feat(store): tribe stats page in the Tribes tab - #4823
Conversation
…/:name
Each active tribe name row gets a Stats button that swaps the tab to an
in-modal tribe page (profile-modal style back button) showing the owner,
active boost multiplier, and games/player-reach figures for both the
rolling 30-day leaderboard window and lifetime.
Rejected/revoked rows get no button: the public endpoint 404s for them
on purpose. Reach copy follows the API rule ("appeared in games with N
players" — impressions, not distinct players).
The endpoint ships with infra#489; until that deploys the page shows
its error state.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
WalkthroughAdds a public tribe statistics API and schema, a Lit statistics view, and TribesPanel navigation. The view displays validated metrics, owner data, reporting windows, and active boosts with localized English strings. ChangesTribe statistics
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Sequence Diagram(s)sequenceDiagram
participant TribeUser
participant TribesPanel
participant TribeStatsView
participant Api
participant PublicTribeEndpoint
TribeUser->>TribesPanel: Select active tribe statistics
TribesPanel->>TribeStatsView: Set tribeName
TribeStatsView->>Api: fetchTribeStats(name)
Api->>PublicTribeEndpoint: GET encoded tribe name
PublicTribeEndpoint-->>Api: Statistics response
Api-->>TribeStatsView: Validated response or false
TribeStatsView-->>TribeUser: Render statistics or error
Suggested labels: Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
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.
🧹 Nitpick comments (2)
tests/ApiSchemas.test.ts (1)
1061-1068: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd
ownerUsernameandactiveBooststo the required-field rejection test.The sibling test for
TribeLeaderboardResponseSchemachecksownerPublicId,ownerUsername, andactiveBoostsfor required-field rejection. The newTribeStatsResponseSchematest only checksname,ownerPublicId,lifetime, andwindow. BothownerUsernameandactiveBoostsare required fields onTribeStatsResponseSchematoo. Add them to keep test coverage consistent with the leaderboard schema's test.✅ Proposed test coverage addition
- it.each(["name", "ownerPublicId", "lifetime", "window"])( + it.each(["name", "ownerPublicId", "ownerUsername", "activeBoosts", "lifetime", "window"])( "rejects a response missing %s", (field) => { const body: Record<string, unknown> = { ...base }; delete body[field]; expect(TribeStatsResponseSchema.safeParse(body).success).toBe(false); }, );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/ApiSchemas.test.ts` around lines 1061 - 1068, Extend the parameter list for the TribeStatsResponseSchema required-field test to include ownerUsername and activeBoosts alongside the existing fields, ensuring each required field is individually deleted and rejected by safeParse.src/client/components/TribeStatsView.ts (1)
37-43: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider adding a test for the stale-response guard.
load()usesloadSeqto discard stale responses whentribeNamechanges quickly. This asynchronous race logic has no test coverage in the provided files. Add a test that triggers two rapidtribeNamechanges and asserts the view renders the second name's data, not the first. Based on learnings, focused client-component tests can instantiate the custom element directly without thesetup()helper, since this component does not exercise game/simulation infrastructure.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/client/components/TribeStatsView.ts` around lines 37 - 43, Add focused client-component coverage for TribeStatsView.load by instantiating the custom element directly, triggering two rapid tribeName changes with controlled fetch responses, and resolving the second request before the first. Assert the rendered state uses the second tribe’s data, confirming the loadSeq stale-response guard discards the first result.Source: Learnings
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/client/components/TribeStatsView.ts`:
- Around line 37-43: Add focused client-component coverage for
TribeStatsView.load by instantiating the custom element directly, triggering two
rapid tribeName changes with controlled fetch responses, and resolving the
second request before the first. Assert the rendered state uses the second
tribe’s data, confirming the loadSeq stale-response guard discards the first
result.
In `@tests/ApiSchemas.test.ts`:
- Around line 1061-1068: Extend the parameter list for the
TribeStatsResponseSchema required-field test to include ownerUsername and
activeBoosts alongside the existing fields, ensuring each required field is
individually deleted and rejected by safeParse.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: dc996d65-0902-40dd-80fa-eb7e2cf89b98
📒 Files selected for processing (6)
resources/lang/en.jsonsrc/client/Api.tssrc/client/components/TribeStatsView.tssrc/client/components/TribesPanel.tssrc/core/ApiSchemas.tstests/ApiSchemas.test.ts
What
Adds a Stats button to each active tribe name row in the store's Tribes tab. Clicking it swaps the tab content to the tribe page — same modal, with a
modalHeaderback button returning to the list (the profile-modal ← leaderboard pattern) — showing live figures from the new public API endpoint:<player-name>: account username with verified badge, publicId fallback)How
TribeStatsResponseSchema(src/core/ApiSchemas.ts): coercedactiveBoosts, plain-string window bounds (same display-only tolerance as the leaderboard's), impressions warning onplayerReachfetchTribeStats()(src/client/Api.ts):GET /public/tribe/:name, public/no-auth, name URL-encoded; 404 folds into the generic failure since callers can't act on the difference<tribe-stats-view>(new): fetches on open, canonical display name takes over the heading once loaded, window heading falls back to a dateless form if the date format wobblesCopy rules honored
Deploy ordering
Tests
TribeStatsResponseSchematests (null owner username, zeroed new-name response, coerced boost count, non-YYYY-MM-DDwindow bounds, missing-field rejections)tsc --noEmit, ESLint, en.json sort check all clean🤖 Generated with Claude Code