Connect-funnel analytics and off-season connect UX#2
Conversation
June GA data showed 88 users reaching Connect Your League but only ~10 reaching analysis pages. Instrument the funnel (connect_attempt / connect_error, platform + coarse error class only) and close the biggest UX gap: a fresh off-season connect landed silently on the Draft Room's bare setup form or DraftPage's empty state with no confirmation the connect worked, and the ESPN season-1 fallback swapped in last year's data with no explanation. - src/utils/analytics.ts: connect_attempt (platform) and connect_error (platform, error_type) events; error_type derives from useLeague's existing error branching, no league id/username/message text sent. - src/components/LeagueForm.tsx, src/App.tsx: fire connect_attempt on Sleeper/ESPN form submit and Yahoo login clicks (form + header). - src/hooks/useLeague.ts: fire connect_error on a genuine load failure; add seasonFallbackNotice state for the ESPN current-year-404 fallback. - src/components/draftRoom/ConnectedBanner.tsx: dismissible confirmation banner on the Draft Room shown once after a fresh connect routes there because the league has no draft data yet. - src/pages/DraftPage.tsx: off-season context line on the "No Draft Data Available" empty state. - src/components/SeasonFallbackNotice.tsx: small dismissible notice when the ESPN season-1 fallback silently loads last year's data. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b36a38cbcd
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ); | ||
| return loadImpl({ ...credentials, season: fallbackSeason }, { ...options, isFallbackRetry: true }); | ||
| } | ||
| Analytics.connectError(credentials.platform, connectErrorTypeFor(err, credentials.platform)); |
There was a problem hiding this comment.
Only emit connect_error for connect attempts
Because useLeague.load is also used outside the connect form (for example the header refresh and App.handlePickSeason season changes call load(...) without any connect_attempt), this records ordinary refresh/year-switch failures as connect_error. If a connected user picks an unavailable season or refreshes during a transient outage, the new funnel metric will count a failed connect that never happened, skewing the experiment this instrumentation is meant to measure.
Useful? React with 👍 / 👎.
| setSeasonFallbackNotice( | ||
| `Showing ${fallbackSeason} data - the ${credentials.season} season isn't set up yet.`, | ||
| ); | ||
| return loadImpl({ ...credentials, season: fallbackSeason }, { ...options, isFallbackRetry: true }); |
There was a problem hiding this comment.
Defer fallback notice until the retry succeeds
When the ESPN current-year load 404s but the previous-season retry also fails (wrong league ID, brand-new league, expired cookies, etc.), this notice remains set because the recursive retry is marked isFallbackRetry and therefore does not clear it, while the retry error path only sets error/league. The UI can then show “Showing data” next to an error with no league loaded, so set the notice only after the fallback load returns a league or clear it on retry failure.
Useful? React with 👍 / 👎.
| <DraftRoomPage | ||
| key={`${league.platform}:${league.id}:${league.season}:${league.totalTeams}:${league.scoringType}:${league.rosterSlots?.SUPERFLEX ?? 0}`} | ||
| league={league} | ||
| justConnected={!!(location.state as { justConnected?: boolean } | null)?.justConnected} |
There was a problem hiding this comment.
Clear one-shot connect state before rendering guests
location.state is backed by the browser history entry, so it can still be present after a manual refresh of the just-connected /draft-room entry. On that refresh the app has no loaded real league and this route goes through GuestAutoEnter, but this line still passes justConnected, causing the guest Draft Room to display “Your league is connected.” Gate this on a non-guest league or consume/replace the state after first render.
Useful? React with 👍 / 👎.
Summary
connect_attempt(platform) andconnect_error(platform, coarseerror_type) GA events, wired at the Sleeper/ESPN form submit and both Yahoo login entry points;error_typereusesuseLeague's existing error branching and never carries the raw error text, league id, or username.Report only (no code changes)
Per the task scope,
api/yahoo-auth.js,api/yahoo-callback.js, andapi/_cors.jswere reviewed, not changed. All three defaultFRONTEND_URLto the oldkrool.github.ioorigin when the env var is unset - confirmed matchesCLAUDE.md's documented behavior ("in-code default is the old github.io URL, so the Vercel value is what matters"). For Yahoo OAuth to work onfantasyfootballanalyzer.app, Vercel must have:FRONTEND_URL=https://fantasyfootballanalyzer.app(no trailing slash)YAHOO_CLIENT_IDYAHOO_CLIENT_SECRETALLOW_DEV_OAUTHshould stay unset in production (dev-only escape hatch for localhost OAuth redirects)Test plan
npx tsc -bnpm run lintnpm run test:run(667 tests passing, including new analytics/useLeague coverage)🤖 Generated with Claude Code
Co-Authored-By: Claude Fable 5 noreply@anthropic.com