Repository navigation
Conversation
Product events were only reachable through the dashboard widget builder and one tab on Web analytics. /product-analytics gives them their own page under Analyze, beside Web analytics: - Events: every event name in the window with its kind, counts and reach, and a Funnel and Paths action per row. - Funnels: the kit step builder, Count by / Within / Bars or Drop-off, a stat rail, the funnel chart and a steps table with time between steps and what leavers did. With no steps it suggests pairs from the org's events. - Paths: an anchor event or page, direction, steps and branches, the paths chart, and the most common next step and where sequences ended. The definition lives in the URL (shareable); typed step names wait for a pause before they run. The Web analytics filter rail moves to the kit and both pages mount it. Web analytics' Events card links to the new page. /lab/product-analytics renders every tab and state over fixtures.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the
|
Picking Page for the paths anchor made an anchor with no path yet, which the URL leaves out; the navigation then rebuilt the drafts from the URL and the anchor fell back to an event. A navigation now keeps the drafts when it carries the definition they already produce.
🍁 Maple PR previewNote Preview resources were removed when the preview label came off. Final commit |
This comment has been minimized.
This comment has been minimized.
Walking back from the anchor, a sequence that runs out is where it started, not where it ended.
This comment has been minimized.
This comment has been minimized.
Previews and local stacks seeded through /internal/demo/seed had traces, logs and metrics but no session_replays or product_events, so Web analytics showed its setup state and Product analytics had nothing to show. The seed now adds 40 visitors an hour walking a signup funnel.
The row keeps the Paths tab's direction, so it opens paths from the event, after or before.
session_replays and product_events map OrgId from the ingestion key org_id, so the guard saw no org tag on their rows and refused the demo seed's sessions. It now checks org_id like the other two tags: every tag a row carries must be the writing org.
This comment has been minimized.
This comment has been minimized.
The kind filter and a fixed-width search field overflowed a 390px panel; the row now wraps and the field takes the full width until sm.
Switching Drop-off to Bars kept the timing and leaver columns until the new answer arrived, because they followed the rows on screen.
This comment has been minimized.
This comment has been minimized.
Traces from the preview test (
|
| Span | Spans | p50 | p95 |
|---|---|---|---|
QueryEngine.getProductEventNames |
69 | 327 ms | 1.23 s |
QueryEngine.getProductEventsFunnelWidget |
24 | 687 ms | 3.32 s |
QueryEngine.getProductEventsPathsWidget |
14 | 490 ms | 1.21 s |
The funnel p95 comes from three requests in one burst (10:52:01 to :05; 5.48 s, 3.60 s, 1.74 s). The slowest trace, 8f2e84a00186b866ce461822c50dbd48, doesn't spend that time on the server:
FetchProductAnalyticsFunnel (maple-web) 5.48s
POST /_api/internal/query-engine/product-events-funnel 5.48s (browser)
POST /_api/* (maple-web-worker) 134ms
POST /internal/query-engine/product-events-funnel (maple-api) 112ms
QueryEngineService.cachedDirect → EdgeCacheService 12ms
AuditLogService.record 83ms
LoadOnboardingChecklist (maple-web) 5.48s (server: 72ms)
The browser waited about 5.3 s before both requests in that navigation reached the worker, the funnel and the onboarding checklist alike. The same burst then ran under 0.8 s. That points at the test browser's connection queue, not the funnel query. The funnel's own queries (base, timing and leavers, the last two concurrent) are not visible here, because the edge cache answered. No errors on any of these spans.
Not from this PR, but noticed: AuditLogService.record takes 83 ms inside a cached query read.
Back to an entry with the same steps but another window kept the pause, which then wrote the half-typed step into that entry. The page now remembers the URL it asked for; any other route change cancels the pause, while its own write still leaves a newer pause running.
This comment has been minimized.
This comment has been minimized.
…lytics-page # Conflicts: # apps/web/src/page/analytics/view-filters.ts # packages/query-engine/src/execution/executor.ts
Both were cut to six rows while Share divided by the full total. Each is already bounded by Branches and Steps, so they now show them all.
The events read asks for one name past the limit, so a complete 200-name answer no longer reads as capped. The paths tables sit in the kit grid.
This comment has been minimized.
This comment has been minimized.
…e, names trimmed - A Count by or view change no longer draws the previous counts under the new labels: a refetching funnel shows its loading state, as paths does. - Back to another window mid-typing restarts the typing pause, so the typed step lands in the entry now showing instead of never running. - Step and anchor names are trimmed before they reach the query. - The Top custom event tile says when it only saw the top 200 names.
This comment has been minimized.
This comment has been minimized.
Maple review🟢 Confidence 10/10 · safe to merge. New page plus a stricter org-scope guard on ingest, the guard pinned by a foreign Adds a
Production impactOpen errors in the changed files (2)
After this merges, Maple checks whether they stop.
|
Product events were only reachable through the dashboard widget builder and one card on Web analytics, so almost nobody found funnels or paths. This adds a Product analytics page at
/product-analytics, beside Web analytics under Analyze. Web analytics itself is unchanged apart from a link from its Events card.page/kit/analytics-filter-rail.tsand both pages mount it. Product analytics leaves out the Traffic and Event sections, because funnels don't take them.getProductEventNames,getProductEventsFunnelWidget,getProductEventsPathsWidgetandgetWebAnalyticsBreakdowns./lab/product-analytics?state=…renders every tab and state over fixtures./internal/demo/seed, local and preview stacks only) now also writes about 40 visitors an hour assession_replaysrows plus theirproduct_events, walking a signup funnel. Before this, previews had no web or product analytics data at all.org_id. That is the ingestion-JSON key ofsession_replaysandproduct_events; the guard used to refuse them because it saw no org tag. Every tag a row carries must still be the writing org, and the executor test covers a foreignorg_id.Screenshots
Events (dark, light)
Funnels, drop-off view (dark, light), and bars view
Paths
Empty, loading, error and mobile states
New funnel with suggestions
Failed funnel
No events
Loading
Paths with no anchor
Mobile
Verification
bunx vitest run src/page/product-analytics src/page/kit src/page/analytics src/page/dashboards src/route src/shell src/lab/registry.test.ts: 302 tests pass. The new scene tests cover:Navigations are decoded through the app router, so
/product-analyticsURLs are covered too.tsrs --noEmitclean;oxlint -c .oxlintrc.effect.json,ui:index:checkandui:patterns:checkpass.In a browser (Playwright on
/lab/product-analytics): every state above, dark and light, desktop and mobile. Step comboboxes open with the window's event names and counts, and picking one fills the step.Preview: a separate agent tested app-pr-2172 against the seeded data. Every tab, the filter rail and the Web analytics link worked, with no console errors and no failed requests. Results and traces are in the comments.
Not tested on the preview: the dashboard funnel editor (refactor(web): move the funnel step builder into the page kit #2171). Dashboards need live sync, which
preview:webdoesn't run. refactor(web): move the funnel step builder into the page kit #2171's scene tests drive the editor's combobox and kind select.Follow-up: "Add to dashboard" from a funnel or paths view.
Stack: #2171 → this PR