feat: Guided Tours Completion Metrics#2412
Conversation
🎩 PreviewA preview build has been created at: |
2cfbe6e to
586e87d
Compare
61e99a7 to
e5de95e
Compare
586e87d to
6bffb2c
Compare
e5de95e to
9551233
Compare
6bffb2c to
18c2dd3
Compare
9551233 to
1e6793c
Compare
18c2dd3 to
0e1b241
Compare
1e6793c to
f89c2a4
Compare
0e1b241 to
7f552ac
Compare
f89c2a4 to
c487762
Compare
3b6725f to
cf9670d
Compare
46e18c1 to
e3b0dd6
Compare
b8c7156 to
4d74024
Compare
ad98205 to
723fee3
Compare
9954dd1 to
68c9054
Compare
97312b6 to
f386c63
Compare
20ce5e1 to
5120618
Compare
f386c63 to
ceded8e
Compare
5120618 to
8c80eaf
Compare
8c80eaf to
612ade1
Compare
ceded8e to
4c1ae33
Compare
f048e6a to
9585c4f
Compare
72758f7 to
076c6dd
Compare
076c6dd to
9195f97
Compare
96f0bf2 to
1632bef
Compare
9195f97 to
4fd1034
Compare
1632bef to
19bac1e
Compare
| @@ -70,6 +70,7 @@ export const REACT_COMPILER_ENABLED_DIRS = [ | |||
| "src/components/ui/typography.tsx", | |||
|
|
|||
| "src/providers/DialogProvider", | |||
There was a problem hiding this comment.
🤖 This is an AI-generated code review comment.
This PR adds the new React component src/providers/TourProvider/TourTelemetryBridge.tsx, but the compiler config only registers src/providers/TourProvider/tourCompletion.ts. src/providers is not enabled as a directory, so the new bridge component is left outside React Compiler coverage.
Suggestion: Add src/providers/TourProvider/TourTelemetryBridge.tsx to REACT_COMPILER_ENABLED_DIRS as part of this PR, or enable a cleaned-up TourProvider subdirectory if appropriate.
Rule: tangle-ui-review: React Compiler adoption; .claude/skills/review: new components/hooks must be covered by react-compiler.config.js
|
|
||
| return (tourId: string): RecordedCompletion => { | ||
| const key = queryKey(backendUrl); | ||
| const current = queryClient.getQueryData<TourCompletionMap>(key) ?? {}; |
There was a problem hiding this comment.
🤖 This is an AI-generated code review comment.
useRecordTourCompletion falls back to {} when the tourCompletions query has not loaded yet, then PATCHes that map as the full completed_tours setting. If a user completes a tour before the settings query resolves, or after it failed, the PATCH can replace previously saved completions with only the current tour.
Suggestion: Do not persist from an empty cache unless the query is known to be loaded. Fetch/ensure the current completed_tours map before merging, move the read-merge-write into the mutation, or gate persistence until the query succeeds and invalidate/refetch after mutation failures.
Rule: .cursorrules: use TanStack Query for server state and proper error/cache handling; tangle-ui-review: General quality

Description
Tours can now be marked as completed and restarted, and we capture progression telemetry so we can see how tours are used and where people bounce off.
Completion state is persisted per user on the backend via
/api/users/me/settings(key:completed_tours) — read with TanStack Query and updated with a read-modify-write PATCH, so it survives across sessions/devices and sits alongside any other user settings without overwriting them. There is no local-storage layer: when no backend is available (e.g. offline / GitHub Pages) completion state simply doesn't surface, which is acceptable for this low-stakes feature and can be revisited later.We deliberately do not resume partially-finished tours — restarting always begins from the first step — so no in-progress state is surfaced.
User-facing
Telemetry (analytics events, independent of the settings store)
learning_hub.tours.start— now carriesis_restartto distinguish redos from first runs.learning_hub.tours.step_viewed— fired once per step reached (step_index,step_count,interaction); gives a step-by-step drop-off funnel.learning_hub.tours.completed— on reaching the final step (step_count,completion_count,previously_completed,duration_ms).learning_hub.tours.exited— on leaving before completion (furthest_step,percent_complete,duration_ms,previously_completed) — the "bounce" signal.Together these cover issue 640's item 2 (how many started/completed, and how far users get before exiting).
Related Issue and Pull requests
Closes https://github.com/Shopify/oasis-frontend/issues/640
Note
Cross-device persistence depends on the backend
/api/users/me/settingsroute doing a key-level merge of thesettingsdict (confirmed) socompleted_toursis appended alongside other keys rather than replacing them.Type of Change
Checklist
Screenshots (if applicable)
Test Instructions
/api/users/me/settings). In DevTools, theGET .../api/users/me/settings?setting_names=completed_toursreturns thecompleted_toursmap; completing a tour fires aPATCHthat includes the full map undersettings.completed_tours.learning_hub.tours.exitedevent fires withfurthest_step. Walk a tour to the end →learning_hub.tours.completedfires; each step emitsstep_viewed.Additional Comments
completed_tourswrite is last-write-wins against the cached map within a session — a completion added by another tab/device between our GET and PATCH could be missed. Negligible here; true per-entry safety would require the backend to deep-mergecompleted_tours.