Skip to content

Connect-funnel analytics and off-season connect UX#2

Merged
Krool merged 1 commit into
masterfrom
connect-funnel-analytics-and-offseason-ux
Jul 2, 2026
Merged

Connect-funnel analytics and off-season connect UX#2
Krool merged 1 commit into
masterfrom
connect-funnel-analytics-and-offseason-ux

Conversation

@Krool

@Krool Krool commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • June GA data showed 88 users reaching Connect Your League but only ~10 reaching analysis pages. This adds funnel instrumentation and closes the biggest silent-failure gap: an off-season connect landed on a bare Draft Room setup form or DraftPage's empty state with no sign the connect worked.
  • New connect_attempt (platform) and connect_error (platform, coarse error_type) GA events, wired at the Sleeper/ESPN form submit and both Yahoo login entry points; error_type reuses useLeague's existing error branching and never carries the raw error text, league id, or username.
  • Dismissible "Connected" banner on the Draft Room when a fresh connect routes there because the league has no draft data yet (off-season), plus a one-line off-season note on DraftPage's empty state.
  • Small dismissible notice when the ESPN current-year-404 fallback silently loads last season's data instead.

Report only (no code changes)

Per the task scope, api/yahoo-auth.js, api/yahoo-callback.js, and api/_cors.js were reviewed, not changed. All three default FRONTEND_URL to the old krool.github.io origin when the env var is unset - confirmed matches CLAUDE.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 on fantasyfootballanalyzer.app, Vercel must have:

  • FRONTEND_URL = https://fantasyfootballanalyzer.app (no trailing slash)
  • YAHOO_CLIENT_ID
  • YAHOO_CLIENT_SECRET
  • ALLOW_DEV_OAUTH should stay unset in production (dev-only escape hatch for localhost OAuth redirects)

Test plan

  • npx tsc -b
  • npm run lint
  • npm run test:run (667 tests passing, including new analytics/useLeague coverage)

🤖 Generated with Claude Code

Co-Authored-By: Claude Fable 5 noreply@anthropic.com

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>
@vercel

vercel Bot commented Jul 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
fantasy-football-analyzer Building Building Preview Jul 2, 2026 6:18pm

@Krool Krool merged commit 5e9007f into master Jul 2, 2026
3 checks passed
@Krool Krool deleted the connect-funnel-analytics-and-offseason-ux branch July 2, 2026 18:19

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/hooks/useLeague.ts
);
return loadImpl({ ...credentials, season: fallbackSeason }, { ...options, isFallbackRetry: true });
}
Analytics.connectError(credentials.platform, connectErrorTypeFor(err, credentials.platform));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread src/hooks/useLeague.ts
Comment on lines +209 to +212
setSeasonFallbackNotice(
`Showing ${fallbackSeason} data - the ${credentials.season} season isn't set up yet.`,
);
return loadImpl({ ...credentials, season: fallbackSeason }, { ...options, isFallbackRetry: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread src/App.tsx
<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}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant