feat: custom analytics events for signup, checkout, and report views - #1907
feat: custom analytics events for signup, checkout, and report views#1907sweetmantech wants to merge 1 commit into
Conversation
chat.recoupable.dev had @vercel/analytics installed but emitted zero custom events, so the signup/checkout funnel was unmeasurable (#1902 C5). Adds a thin trackEvent wrapper (single door, never throws, no PII) and four funnel events: - signup_started (source: menu | modal_auto | signin_page | start_button) fired via trackSignupStarted before every programmatic Privy login() - signup_completed (is_new_user, login_method) via Privy useLogin onComplete, skipping already-authenticated session restores - checkout_opened when a Stripe checkout session URL is received - valuation_report_viewed (catalog_id) on catalog report page mount Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
Next review available in: 52 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (10)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bcc854b9b9
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| return; | ||
| } | ||
|
|
||
| trackSignupStarted("signin_page"); |
There was a problem hiding this comment.
Avoid double-counting sign-in starts
When an anonymous user lands on /signin, app/layout.tsx still wraps that page in Providers -> UserProvider, so UserAutoLogin runs useAutoLogin and already fires signup_started with source=modal_auto before its login(). This added signin_page event runs in the same mount for the same Privy login flow, so /signin sessions produce two signup_started events and inflate the funnel/source split; gate auto-login off the signin route or choose one source.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
2 issues found across 11 files
Confidence score: 3/5
- In
hooks/useTrackSignupCompleted.tsx,signup_completedis wired touseLogin({ onComplete })but the callback only runs for that hook instance’s scopedlogin()call, so new signups may never emit completion and conversion funnels will be undercounted — trigger the event from the signup success path that actually executes for new registrations (or share the samelogin()invocation path). - In
components/SignInPage/SignInPage.tsx,signup_startedcan fire on/signinwhile app-wideUserAutoLoginalso emitssignup_startedin the same mount cycle, which can double-count starts and skew source attribution (signin_pagevsmodal_auto) — add a dedupe/guard so only one source event is sent per session/flow.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="components/SignInPage/SignInPage.tsx">
<violation number="1" location="components/SignInPage/SignInPage.tsx:19">
P2: On the /signin route, this call fires signup_started with source=signin_page in the same mount where the app-wide UserAutoLogin (mounted via UserProvider) already fires signup_started with source=modal_auto before calling login(). This double-counts signup starts for /signin sessions and skews the funnel/source breakdown. Consider gating auto-login off this route or picking a single source of truth for the event.</violation>
</file>
<file name="hooks/useTrackSignupCompleted.tsx">
<violation number="1" location="hooks/useTrackSignupCompleted.tsx:12">
P1: The `signup_completed` custom event will never fire for new signups. The `useLogin({ onComplete })` hook creates a scoped `login()` function, and the `onComplete` callback fires only when that specific `login()` is called. Since the returned `login()` is never invoked anywhere — all logins use `usePrivy().login()` instead — the callback never executes for actual authentication flows. To fix this, either use `usePrivy` with a `useEffect` that watches the `user` or `authenticated` state and fires the event once when it transitions from unauthenticated to authenticated, or wire the `onComplete` through each component that calls `login()`.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| * No PII in props (chat#1902 C5). | ||
| */ | ||
| export function useTrackSignupCompleted() { | ||
| useLogin({ |
There was a problem hiding this comment.
P1: The signup_completed custom event will never fire for new signups. The useLogin({ onComplete }) hook creates a scoped login() function, and the onComplete callback fires only when that specific login() is called. Since the returned login() is never invoked anywhere — all logins use usePrivy().login() instead — the callback never executes for actual authentication flows. To fix this, either use usePrivy with a useEffect that watches the user or authenticated state and fires the event once when it transitions from unauthenticated to authenticated, or wire the onComplete through each component that calls login().
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At hooks/useTrackSignupCompleted.tsx, line 12:
<comment>The `signup_completed` custom event will never fire for new signups. The `useLogin({ onComplete })` hook creates a scoped `login()` function, and the `onComplete` callback fires only when that specific `login()` is called. Since the returned `login()` is never invoked anywhere — all logins use `usePrivy().login()` instead — the callback never executes for actual authentication flows. To fix this, either use `usePrivy` with a `useEffect` that watches the `user` or `authenticated` state and fires the event once when it transitions from unauthenticated to authenticated, or wire the `onComplete` through each component that calls `login()`.</comment>
<file context>
@@ -0,0 +1,23 @@
+ * No PII in props (chat#1902 C5).
+ */
+export function useTrackSignupCompleted() {
+ useLogin({
+ onComplete: ({ isNewUser, wasAlreadyAuthenticated, loginMethod }) => {
+ if (wasAlreadyAuthenticated) return;
</file context>
| return; | ||
| } | ||
|
|
||
| trackSignupStarted("signin_page"); |
There was a problem hiding this comment.
P2: On the /signin route, this call fires signup_started with source=signin_page in the same mount where the app-wide UserAutoLogin (mounted via UserProvider) already fires signup_started with source=modal_auto before calling login(). This double-counts signup starts for /signin sessions and skews the funnel/source breakdown. Consider gating auto-login off this route or picking a single source of truth for the event.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At components/SignInPage/SignInPage.tsx, line 19:
<comment>On the /signin route, this call fires signup_started with source=signin_page in the same mount where the app-wide UserAutoLogin (mounted via UserProvider) already fires signup_started with source=modal_auto before calling login(). This double-counts signup starts for /signin sessions and skews the funnel/source breakdown. Consider gating auto-login off this route or picking a single source of truth for the event.</comment>
<file context>
@@ -15,6 +16,7 @@ export default function SignInPage() {
return;
}
+ trackSignupStarted("signin_page");
login();
// eslint-disable-next-line
</file context>
Part of #1902 (C5 — web UX conversion audit).
chat.recoupable.dev has
@vercel/analyticsinstalled (<Analytics/>inapp/layout.tsx) but emitted zero custom events, so the signup/checkout funnel was unmeasurable. This PR adds a thinlib/analytics/trackEvent.tswrapper (single door for all future events; forwards name + props to@vercel/analyticstrack; never throws; no PII in props) and instruments four funnel events.Events
signup_startedlogin()call, via thetrackSignupStarted(source)helpersource:menu(sidebar/menu action gate inuseUser.isPrepared),modal_auto(useAutoLoginauto-prompt),signin_page(/signin),start_button(shared StartButton)signup_completeduseLoginonComplete(mounted underUserProvider); skipped whenwasAlreadyAuthenticated(session restore)is_new_user(boolean),login_method(string)checkout_openedlib/stripe/createClientCheckoutSession.ts, just before the checkout tab opensvaluation_report_viewedcomponents/Catalog/report/CatalogReportPage.tsx, serving/catalogs/[catalogId])catalog_idNo PII (emails, names, wallet addresses) is included in any event props.
Files
lib/analytics/trackEvent.ts— new thin wrapper (one exported function)lib/analytics/trackSignupStarted.ts— newsignup_startedhelper with typedsourcelib/analytics/__tests__/trackEvent.test.ts— new unit tests (TDD, red then green)hooks/useTrackSignupCompleted.tsx— new hook on PrivyuseLoginonCompleteproviders/UserProvder.tsx— mounts the completion hook next to auto-loginhooks/useAutoLogin.tsx,components/SignInPage/SignInPage.tsx,components/shared/StartButton.tsx,hooks/useUser.tsx— onetrackSignupStarted(...)line before eachlogin()lib/stripe/createClientCheckoutSession.ts— one additivetrackEvent("checkout_opened")line (kept minimal; sibling PRs touch this file)components/Catalog/report/CatalogReportPage.tsx— mount effect firingvaluation_report_viewedTesting
pnpm exec vitest run— 75 files / 279 tests pass (includes the 3 newtrackEventtests: forwards name+props, forwards name alone, never throws whentrackthrows)pnpm exec tsc --noEmit— no errors in changed files (8 pre-existing baseline errors inSpotifyDeepResearchResult.tsx/extractSendEmailResults.test.ts, untouched by this PR)How to verify in the Vercel Web Analytics dashboard
chatproject → Analytics → Events tab (custom events).signup_startedwithsource=modal_auto), complete the Privy email login (signup_completed), open a catalog report at/catalogs/[catalogId](valuation_report_viewedwith thecatalog_idprop), and click Subscribe as a non-subscribed user (checkout_opened).signup_starteddown bysource.Note: custom events require the Web Analytics Plus/Pro events feature to be enabled on the project; events fired from preview deployments show under that deployment's environment filter.
Preview verification to follow in a PR comment.
🤖 Generated with Claude Code
Summary by cubic
Adds custom analytics events using
@vercel/analyticsto track signup, checkout, and report views. Supports #1902 conversion audit by making the funnel measurable.trackEventfor@vercel/analytics(single entry point, never throws, no PII).signup_started(sources:menu,modal_auto,signin_page,start_button),signup_completed(props:is_new_user,login_method),checkout_opened,valuation_report_viewed(prop:catalog_id).Written for commit bcc854b. Summary will update on new commits.