Spike: Player self-service jersey ordering from profile #196
Labels
No labels
domain:backend
domain:devops
domain:frontend
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ldraney/westside-app#196
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type
Spike
Lineage
Standalone — discovered during jersey email session 2026-03-29. Review
review-690-2026-03-29confirmed spike needed before decomposition.Repo
forgejo_admin/westside-landing+forgejo_admin/basketball-apiQuestion
What backend and frontend changes are needed to let a logged-in parent order a jersey from their player's profile, without requiring the email token link?
Context
Current jersey flow is token-only: email →
/jersey?token={token}→ checkout. Bothroutes/jersey.pyandroutes/checkout.pyauthenticate viaParent.registration_tokenwith no Keycloak/session path. There is no way for a logged-in parent to reach jersey ordering from within the app.Investigation Scope
routes/jersey.pyandroutes/checkout.pyto support Keycloak session auth alongside token auth? Can we reuse existing endpoints with an OR condition (token OR JWT), or do we need new endpoints?players/[id]/+page.svelte)? Dashboard? A new nav item?Deliverable
A decomposed set of sub-tickets (backend + frontend) with concrete file targets, ready for review.
Time Box
1 hour investigation, output is tickets not code.
Acceptance Criteria
Related
project-westside-basketballforgejo_admin/basketball-api#243— jersey email (current token-based flow)Scope Review: NEEDS_REFINEMENT
Review note:
review-690-2026-03-29Cross-repo feature with uncertain backend scope — needs decomposition before execution.
routes/jersey.pyandroutes/checkout.pyare both token-only, need auth-aware variants listedPlayer self-service jersey ordering from profileto Spike: Player self-service jersey ordering from profileScope review: NEEDS_REFINEMENT → converted to Spike
Per review
review-690-2026-03-29: backend is token-only, file targets uncertain, crosses 2 repos. Converted from Feature to Spike per recommendation. Deliverable is sub-tickets, not code.Scope Review: APPROVED
Review note:
review-690-2026-03-29(updated — re-review after Feature→Spike conversion)Spike template fully compliant. All investigation scope areas are concrete, file references verified against codebase, 1-hour time box appropriate. Previous NEEDS_REFINEMENT issues resolved by type conversion.
Non-blocking recommendations:
[LABEL]Change board item labeltype:feature→type:spike[SCOPE]Create architecture notesarch-westside-appandarch-basketball-api(project-wide gap, not spike-specific)Spike Complete: Player Self-Service Jersey Ordering
Investigation Summary
Investigated the backend and frontend codebases to determine what changes are needed for a logged-in parent to order a jersey from their profile without the email token link.
Findings
1. Backend Dual-Auth
Current state:
jersey.pyandcheckout.pyauthenticate exclusively viaParent.registration_tokenas a?token=query param. The SPA already has Keycloak JWT auth viaget_current_userinauth.py, andaccount.pyline 57 demonstrates resolving a Parent from Keycloak email:Parent.email.ilike(user.email).Approach: Create a reusable
get_parent_dual_authFastAPI dependency that accepts EITHER a registration token OR a Keycloak Bearer token. Token-first for backwards compatibility, JWT fallback for session auth.2. Frontend Entry Point
Current state: Player profile page (
players/[id]/+page.svelte) has cards for contract, info, team/coach, payment, stats -- but no jersey section. Jersey page (jersey/+page.svelte) is 100% token-driven and shows "Invalid Link" if no?token=param.Approach: Add a jersey card to the player profile (between team/coach and payment cards). Shows current status if ordered, "Order Jersey" CTA if not. CTA navigates to
/jersey?player_id={id}. The jersey and checkout pages need a second entry mode that accepts?player_id=with Keycloak session auth viaapiFetch.3. Multi-Player Parent Edge Case
Current state: Both
jersey.py(line 127) andcheckout.py(line 147) useparent.players[0]-- always the first player. The account route already returns ALL players.Approach: Add optional
player_idquery param to all three endpoints. When provided, validate it belongs to the parent (403 if not). When omitted, default toparent.players[0]for backwards compatibility.4. Blast Radius
registration_tokenappears in:jersey.py(2 refs),checkout.py(1 ref),register.py(2 refs),tryouts.py(3 refs). The jersey/checkout changes are isolated -- register.py and tryouts.py use the token in completely separate code paths with no shared helper functions. Zero blast radius.Execution Order
The tickets have a dependency chain:
Tickets 2 and 3 can run in parallel after ticket 1 is merged.
Sub-Tickets Created
All items placed in
backlogonboard-westside-basketballwith labelstype:feature,story:WS-S18, and appropriatearch:labels.