# League Admin MVP Plan (Admin Acquisition) ## Goal Finish all league-management tools needed to attract and retain league admins by making three workflows fully functional, permission-correct, and data-backed: - Schedule builder + publishing - Roster + join requests + roles - Results import + standings recompute This plan prioritizes fixing auth/permissions and API contracts first, because they block all three workflows. --- ## What we observed (current state) ### Website gaps (data + correctness) - Schedule UI assumes a rich race object but contract is effectively `unknown[]` and uses `Date` methods; this is unsafe and likely to break on real API responses. - Standings page uses real standings + memberships, but fills missing stats fields with placeholders (avgFinish, penaltyPoints, bonusPoints, racesStarted). - League settings page checks admin via a membership cache, which can falsely deny access if cache isn’t hydrated. - Stewarding data fetch is N+1: for each race, fetch protests + penalties, which won’t scale. - Wallet page is explicitly demo/prototype: hardcoded season/account and static breakdown data; also not admin-gated. ### API gaps (permissions + contract + admin tooling) - Actor identity is inconsistent: - Some operations take a performer/admin ID from request data. - Some operations hardcode an admin ID. - Some areas infer identity from session. - Swagger/OpenAPI generation exists in code, but the committed OpenAPI artifact is stale/empty, so it cannot serve as a reliable contract source right now. - League schedule endpoint exists, but it does not appear to deliver a typed, UI-ready schedule contract that the website expects. - League admin tooling endpoints exist in fragments (join requests, memberships, config, wallet, protests), but are missing end-to-end admin workflows (schedule editing/publishing, results import flows, etc.) and consistent authorization. --- ## Definition of Done (MVP-wide) 1. Every admin action is authorized server-side based on the authenticated session identity (no client-supplied performer IDs; no hardcoded admin IDs). 2. Website uses stable, generated types for API DTOs; no `unknown[]` schedule data. 3. Admin can: - Create and publish a season schedule (add/edit/remove races). - Manage roster and join requests (approve/reject, roles, remove members; enforce capacity). - Import results and see standings update per season. 4. Performance guardrails: - No N+1 requests for league stewarding over races; provide aggregate endpoint(s). 5. Quality gates pass in implementation phase: lint, typecheck, tests. --- ## Gap matrix (workflow → missing pieces) ### 1) Auth/Permissions (cross-cutting) Missing / must improve: - A single canonical “actor” model (session userId vs driverId mapping). - Consistent admin/owner authorization checks for all league write operations. - Removal of performer IDs from all public contracts. Dependencies: - Session endpoint exists; need to decide how driver identity is represented in session and how it’s resolved. Deliverable: - A short doc describing actor model and permission rules, then code changes that enforce them. ### 2) Schedule builder + publishing Missing / must improve: - Contract: schedule DTO must be typed and use ISO strings for dates; website parses to Date in view models. - Admin endpoints: create/update/delete schedule races, associate to season, publish/unpublish. - UI: schedule admin interface for managing races. - Driver registration status: schedule should reflect registration per driver without relying on ad-hoc “isRegistered” fields. Deliverable: - Season-aware schedule read endpoint + schedule write endpoints + website schedule editor. ### 3) Roster + join requests + roles Missing / must improve: - Join requests list exists, but approval/rejection must be permission-correct and actor-derived. - Role changes and member removal must be actor-derived. - UI: admin roster page (requests inbox + members list + role controls + remove). - Capacity/status enforcement at the API layer. Deliverable: - A single roster admin experience that matches API rules. ### 4) Results import + standings recompute Missing / must improve: - Results import UX in website (admin flow) and stable API contract(s) for import + recompute. - Standings should be season-aware and include fields the UI currently fakes or omits. - Ensure penalties/protests can affect standings where applicable. Deliverable: - Admin results import page + standings page backed by season-aware API. ### 5) Stewarding Missing / must improve: - Aggregate league stewarding endpoint (races + protests + penalties) to avoid N+1 behavior. - Confirm admin-only access and correct actor inference for review/apply penalty. Deliverable: - Single endpoint powering stewarding page, plus minimal UI updates. ### 6) Wallet (scope decision required) Recommendation: - Keep wallet “demo-only” for MVP, but make it permission-correct and remove hardcoded season/account IDs. - Replace static breakdown sections with values derived from the wallet endpoint, or hide them behind a “coming soon” section. Deliverable: - Admin-only wallet access + remove hardcoded values + clearly labeled non-MVP parts. --- ## Proposed execution plan (implementation-ready) ### Phase 0 — Contract & identity foundation (must be first) - Define the actor model: - What the session contains (userId, driverId, roles). - How userId maps to driverId (1:1 or indirect). - What “league admin” means and where it’s validated. - Update all league write endpoints to infer actor from session and enforce permissions. - Remove any hardcoded actor IDs in services. - Make OpenAPI generation reliable and used as contract source. Acceptance criteria: - No API route accepts performer/admin IDs for authorization. - OpenAPI doc contains real paths and is regeneratable in CI/local. ### Phase 1 — Normalize DTOs + website type safety - Fix website type generation flow so generated DTOs exist and match API. - Fix schedule DTO contract: - Race schedule entries use ISO strings. - Website parses and derives isPast/isUpcoming deterministically. - Registration state is returned explicitly or derived via a separate endpoint. Acceptance criteria: - No schedule-related `unknown` casts remain. - Schedule page renders with real API data and correct date handling. ### Phase 2 — Admin schedule management - Implement schedule CRUD endpoints for admins (season-scoped). - Build schedule editor UI (create/edit/delete/publish). - Ensure driver registration still works. Acceptance criteria: - Admin can publish a schedule and members see it immediately. ### Phase 3 — Roster and join requests - Ensure join request approve/reject is actor-derived and permission-checked. - Provide endpoints for roster listing, role changes, and member removal. - Build roster admin UI. Acceptance criteria: - Admin can manage roster end-to-end without passing performer IDs. ### Phase 4 — Results import and standings - Implement results import endpoints and recompute trigger behavior (manual + after import). - Make standings season-aware and include additional fields needed by the UI (or simplify UI to match real data). - Build results import UI and update standings UI accordingly. Acceptance criteria: - Import updates standings deterministically and is visible in UI. ### Phase 5 — Stewarding performance + wallet cleanup - Replace N+1 stewarding fetch with aggregate endpoint; update UI to use it. - Wallet: admin-only gating; remove hardcoded season/account; hide or compute static sections. Acceptance criteria: - Stewarding loads in bounded requests. - Wallet page does not contain hardcoded season/account IDs. ### Phase 6 — Quality gates - Run lint, typecheck, and tests until clean for all changes introduced. --- ## Key decisions / assumptions (explicit) - “Admin acquisition” MVP includes all three workflows and therefore requires solid permissions and contracts first. - Wallet is not a blocker for admin acquisition but must not mislead; keep demo-only unless you want it fully MVP.