Spike: Player self-service jersey ordering from profile #196

Closed
opened 2026-03-29 22:49:53 +00:00 by forgejo_admin · 4 comments
Contributor

Type

Spike

Lineage

Standalone — discovered during jersey email session 2026-03-29. Review review-690-2026-03-29 confirmed spike needed before decomposition.

Repo

forgejo_admin/westside-landing + forgejo_admin/basketball-api

Question

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. Both routes/jersey.py and routes/checkout.py authenticate via Parent.registration_token with no Keycloak/session path. There is no way for a logged-in parent to reach jersey ordering from within the app.

Investigation Scope

  1. Backend: What changes to routes/jersey.py and routes/checkout.py to 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?
  2. Frontend: Where does the "Order Jersey" entry point go? Player profile page (players/[id]/+page.svelte)? Dashboard? A new nav item?
  3. Player resolution: Token flow resolves parent → player. Auth flow needs to resolve authenticated user → parent → player(s). What if a parent has multiple players?
  4. Blast radius: What breaks if we modify the jersey endpoints to accept both auth methods?

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

  • Backend changes documented with specific file targets and approach
  • Frontend entry point decided with mockup or description
  • Multi-player parent edge case addressed
  • Sub-tickets created on Forgejo and added to board
  • project-westside-basketball
  • forgejo_admin/basketball-api#243 — jersey email (current token-based flow)
### Type Spike ### Lineage Standalone — discovered during jersey email session 2026-03-29. Review `review-690-2026-03-29` confirmed spike needed before decomposition. ### Repo `forgejo_admin/westside-landing` + `forgejo_admin/basketball-api` ### Question 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. Both `routes/jersey.py` and `routes/checkout.py` authenticate via `Parent.registration_token` with no Keycloak/session path. There is no way for a logged-in parent to reach jersey ordering from within the app. ### Investigation Scope 1. **Backend:** What changes to `routes/jersey.py` and `routes/checkout.py` to 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? 2. **Frontend:** Where does the "Order Jersey" entry point go? Player profile page (`players/[id]/+page.svelte`)? Dashboard? A new nav item? 3. **Player resolution:** Token flow resolves parent → player. Auth flow needs to resolve authenticated user → parent → player(s). What if a parent has multiple players? 4. **Blast radius:** What breaks if we modify the jersey endpoints to accept both auth methods? ### 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 - [ ] Backend changes documented with specific file targets and approach - [ ] Frontend entry point decided with mockup or description - [ ] Multi-player parent edge case addressed - [ ] Sub-tickets created on Forgejo and added to board ### Related - `project-westside-basketball` - `forgejo_admin/basketball-api#243` — jersey email (current token-based flow)
Author
Contributor

Scope Review: NEEDS_REFINEMENT

Review note: review-690-2026-03-29
Cross-repo feature with uncertain backend scope — needs decomposition before execution.

  • [BODY] Backend file targets unspecified — routes/jersey.py and routes/checkout.py are both token-only, need auth-aware variants listed
  • [BODY] Undocumented dependency on board item #666 (in_progress, same story/repos)
  • [SCOPE] story:WS-S18 covers email-based jersey ordering only — self-service path needs new or extended story
  • [SCOPE] Architecture notes arch-westside-app and arch-basketball-api do not exist in pal-e-docs
  • [DECOMPOSE] 3+ files across 2 repos with uncertain backend scope. Recommend: spike first, then decompose via skill-decompose-ticket
## Scope Review: NEEDS_REFINEMENT Review note: `review-690-2026-03-29` Cross-repo feature with uncertain backend scope — needs decomposition before execution. - **[BODY]** Backend file targets unspecified — `routes/jersey.py` and `routes/checkout.py` are both token-only, need auth-aware variants listed - **[BODY]** Undocumented dependency on board item #666 (in_progress, same story/repos) - **[SCOPE]** story:WS-S18 covers email-based jersey ordering only — self-service path needs new or extended story - **[SCOPE]** Architecture notes arch-westside-app and arch-basketball-api do not exist in pal-e-docs - **[DECOMPOSE]** 3+ files across 2 repos with uncertain backend scope. Recommend: spike first, then decompose via skill-decompose-ticket
forgejo_admin changed title from Player self-service jersey ordering from profile to Spike: Player self-service jersey ordering from profile 2026-03-29 22:56:36 +00:00
Author
Contributor

Scope 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: 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.
Author
Contributor

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 label type:featuretype:spike
  • [SCOPE] Create architecture notes arch-westside-app and arch-basketball-api (project-wide gap, not spike-specific)
## 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 label `type:feature` → `type:spike` - `[SCOPE]` Create architecture notes `arch-westside-app` and `arch-basketball-api` (project-wide gap, not spike-specific)
Author
Contributor

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.py and checkout.py authenticate exclusively via Parent.registration_token as a ?token= query param. The SPA already has Keycloak JWT auth via get_current_user in auth.py, and account.py line 57 demonstrates resolving a Parent from Keycloak email: Parent.email.ilike(user.email).

Approach: Create a reusable get_parent_dual_auth FastAPI 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 via apiFetch.

3. Multi-Player Parent Edge Case

Current state: Both jersey.py (line 127) and checkout.py (line 147) use parent.players[0] -- always the first player. The account route already returns ALL players.

Approach: Add optional player_id query param to all three endpoints. When provided, validate it belongs to the parent (403 if not). When omitted, default to parent.players[0] for backwards compatibility.

4. Blast Radius

registration_token appears 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:

  1. basketball-api#255 (dual-auth dependency) -- no dependencies, do first
  2. basketball-api#256 (player_id param) -- depends on #255
  3. westside-landing#198 (frontend session auth) -- depends on #255
  4. westside-landing#197 (jersey card on profile) -- depends on #256 and #198

Tickets 2 and 3 can run in parallel after ticket 1 is merged.

Sub-Tickets Created

# Repo Title Board Item
basketball-api#255 basketball-api Add dual-auth dependency for jersey and checkout routes #693
basketball-api#256 basketball-api Add player_id param to jersey/checkout for multi-player parents #694
westside-landing#197 westside-landing Add jersey order card to player profile page #695
westside-landing#198 westside-landing Update jersey/checkout pages to support session auth #696

All items placed in backlog on board-westside-basketball with labels type:feature, story:WS-S18, and appropriate arch: labels.

## 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.py` and `checkout.py` authenticate exclusively via `Parent.registration_token` as a `?token=` query param. The SPA already has Keycloak JWT auth via `get_current_user` in `auth.py`, and `account.py` line 57 demonstrates resolving a Parent from Keycloak email: `Parent.email.ilike(user.email)`. Approach: Create a reusable `get_parent_dual_auth` FastAPI 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 via `apiFetch`. **3. Multi-Player Parent Edge Case** Current state: Both `jersey.py` (line 127) and `checkout.py` (line 147) use `parent.players[0]` -- always the first player. The account route already returns ALL players. Approach: Add optional `player_id` query param to all three endpoints. When provided, validate it belongs to the parent (403 if not). When omitted, default to `parent.players[0]` for backwards compatibility. **4. Blast Radius** `registration_token` appears 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: 1. **basketball-api#255** (dual-auth dependency) -- no dependencies, do first 2. **basketball-api#256** (player_id param) -- depends on #255 3. **westside-landing#198** (frontend session auth) -- depends on #255 4. **westside-landing#197** (jersey card on profile) -- depends on #256 and #198 Tickets 2 and 3 can run in parallel after ticket 1 is merged. ### Sub-Tickets Created | # | Repo | Title | Board Item | |---|------|-------|------------| | [basketball-api#255](https://forgejo.tail5b443a.ts.net/forgejo_admin/basketball-api/issues/255) | basketball-api | Add dual-auth dependency for jersey and checkout routes | #693 | | [basketball-api#256](https://forgejo.tail5b443a.ts.net/forgejo_admin/basketball-api/issues/256) | basketball-api | Add player_id param to jersey/checkout for multi-player parents | #694 | | [westside-landing#197](https://forgejo.tail5b443a.ts.net/forgejo_admin/westside-landing/issues/197) | westside-landing | Add jersey order card to player profile page | #695 | | [westside-landing#198](https://forgejo.tail5b443a.ts.net/forgejo_admin/westside-landing/issues/198) | westside-landing | Update jersey/checkout pages to support session auth | #696 | All items placed in `backlog` on `board-westside-basketball` with labels `type:feature`, `story:WS-S18`, and appropriate `arch:` labels.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ldraney/westside-app#196
No description provided.