Spike: audit API response contract mismatches with westside-landing frontend #278

Closed
opened 2026-04-03 16:28:26 +00:00 by forgejo_admin · 2 comments

Type

Spike

Lineage

Related to forgejo_admin/basketball-api#276 and forgejo_admin/westside-landing#203 — parent phone bug revealed a systemic contract mismatch pattern between API response shapes and frontend field expectations.

Repo

Multiple — forgejo_admin/basketball-api + forgejo_admin/westside-landing

Question

How many API response shape mismatches exist between basketball-api endpoints and the westside-landing frontend, and should we fix them at the API layer (flatten responses), the frontend layer (add mappings), or both?

What to Explore

Known mismatches (from #276/#203 investigation):

  • GET /players/{id} returns nested parent: { name, phone, email } but frontend expects flat parent_phone, parent_name, parent_email
  • GET /admin/players was missing parent_phone (fixed in #276). QA noted IncompletePlayerItem and SubscriptionListItem have the same omission.

Investigation approach:

  1. List all Pydantic response models in basketball-api/src/basketball_api/routes/ — identify every model that returns parent, team, or coach data
  2. For each model, compare field names/shapes to how westside-landing consumes them (grep player.parent_, player.team_, player.coach_ in Svelte templates)
  3. Categorize mismatches: (a) missing fields, (b) nested vs flat, (c) naming inconsistencies
  4. For each mismatch, determine if it's causing a visible bug or is latent
  5. Recommend: fix at API (standardize response shapes) or frontend (add mappings) or both

Specific models to check:

  • AdminPlayerItem (admin.py) — partially fixed, check remaining gaps
  • IncompletePlayerItem (admin.py) — QA flagged missing parent_phone
  • SubscriptionListItem (admin.py) — QA flagged missing parent_phone
  • PlayerProfileResponse (players.py) — nested parent object
  • Any coach/team response models

Success Criteria

  • Complete inventory of all response models with parent/team/coach data
  • Each mismatch documented with: field name, API shape, frontend expectation, visible impact (bug vs latent)
  • Recommendation: API-first fix, frontend-first fix, or both
  • Follow-up bug/feature tickets created for each actionable mismatch

Time-box

1 session (agent-driven exploration). If more than 10 mismatches found, batch into logical groups for separate fix tickets.

  • project-westside-basketball
  • forgejo_admin/basketball-api#276 — parent phone fix (API side)
  • forgejo_admin/westside-landing#203 — parent phone fix (frontend mapping)
### Type Spike ### Lineage Related to `forgejo_admin/basketball-api#276` and `forgejo_admin/westside-landing#203` — parent phone bug revealed a systemic contract mismatch pattern between API response shapes and frontend field expectations. ### Repo Multiple — `forgejo_admin/basketball-api` + `forgejo_admin/westside-landing` ### Question How many API response shape mismatches exist between basketball-api endpoints and the westside-landing frontend, and should we fix them at the API layer (flatten responses), the frontend layer (add mappings), or both? ### What to Explore **Known mismatches (from #276/#203 investigation):** - `GET /players/{id}` returns nested `parent: { name, phone, email }` but frontend expects flat `parent_phone`, `parent_name`, `parent_email` - `GET /admin/players` was missing `parent_phone` (fixed in #276). QA noted `IncompletePlayerItem` and `SubscriptionListItem` have the same omission. **Investigation approach:** 1. List all Pydantic response models in `basketball-api/src/basketball_api/routes/` — identify every model that returns parent, team, or coach data 2. For each model, compare field names/shapes to how `westside-landing` consumes them (grep `player.parent_`, `player.team_`, `player.coach_` in Svelte templates) 3. Categorize mismatches: (a) missing fields, (b) nested vs flat, (c) naming inconsistencies 4. For each mismatch, determine if it's causing a visible bug or is latent 5. Recommend: fix at API (standardize response shapes) or frontend (add mappings) or both **Specific models to check:** - `AdminPlayerItem` (admin.py) — partially fixed, check remaining gaps - `IncompletePlayerItem` (admin.py) — QA flagged missing parent_phone - `SubscriptionListItem` (admin.py) — QA flagged missing parent_phone - `PlayerProfileResponse` (players.py) — nested parent object - Any coach/team response models ### Success Criteria - [ ] Complete inventory of all response models with parent/team/coach data - [ ] Each mismatch documented with: field name, API shape, frontend expectation, visible impact (bug vs latent) - [ ] Recommendation: API-first fix, frontend-first fix, or both - [ ] Follow-up bug/feature tickets created for each actionable mismatch ### Time-box 1 session (agent-driven exploration). If more than 10 mismatches found, batch into logical groups for separate fix tickets. ### Related - `project-westside-basketball` - `forgejo_admin/basketball-api#276` — parent phone fix (API side) - `forgejo_admin/westside-landing#203` — parent phone fix (frontend mapping)
Author
Owner

Scope Review: READY

Review note: review-738-2026-04-03
Spike scope is solid — all 8 template sections complete, all 4 named models verified in codebase, traceability triangle intact (story:WS-S12 confirmed on project page).

  • [SCOPE] Missing arch note: create arch-basketball-api in pal-e-docs (documentation gap, not a blocker for spike execution).
## Scope Review: READY Review note: `review-738-2026-04-03` Spike scope is solid — all 8 template sections complete, all 4 named models verified in codebase, traceability triangle intact (story:WS-S12 confirmed on project page). - `[SCOPE]` Missing arch note: create `arch-basketball-api` in pal-e-docs (documentation gap, not a blocker for spike execution).
Author
Owner

Spike Results: API Response Contract Mismatch Audit

Investigation covered all Pydantic response models in basketball-api/src/basketball_api/routes/ and all Svelte templates in westside-app/src/routes/ that consume player, parent, team, or coach data.


1. Complete Inventory of Response Models with Parent/Team/Coach Data

# Model File Endpoint Parent Fields Team Fields Coach Fields
1 PlayerProfileResponse players.py GET /players/{id} parent: ParentInfo (nested: id, name, email, phone) team: TeamInfo (nested: id, name, coach_name) coach_name inside TeamInfo
2 AdminPlayerItem admin.py GET /admin/players parent_name, parent_email (flat, no parent_phone) team_name (flat) none
3 IncompletePlayerItem admin.py GET /admin/players/incomplete parent_email, parent_name (flat, no parent_phone) none none
4 SubscriptionListItem subscriptions.py GET /subscriptions, GET /subscriptions/overview parent_name, parent_email (flat, no parent_phone) none none
5 AccountPlayerResponse account.py GET /account/players none team_name (flat) none
6 TeamPlayerItem admin.py GET /admin/teams parent_name (flat, no parent_email, no parent_phone) team_id, team, team_ids, team_names (flat) none
7 DashboardTeamItem admin.py GET /admin/dashboard none id, name coach_name (flat)
8 TeamResponse / TeamDetail teams.py GET /teams/{id}, GET /teams none id, name, division, age_group coach: CoachBrief (nested: id, name, email)
9 CoachDashboardResponse coaches_api.py GET /coaches/me none teams: [CoachTeamResponse] with nested players: [CoachPlayerBrief] id, name, email, role (flat)
10 CoachPlayerBrief coaches_api.py nested in /coaches/me none none none
11 PlayerInfo roster.py GET /{slug}/roster parent_name, parent_email, parent_phone (flat) none none
12 PublicTeamResponse public.py GET /public/teams none id, name, division, age_group coach_name (flat)
13 CoachProfileResponse coaches_api.py GET /coaches/{id} none teams: [CoachTeamResponse] id, name, email, phone, role, onboarding_status

2. Mismatches Found

MISMATCH 1: GET /players/{id} — Nested parent/team vs flat field expectations (VISIBLE BUG)

API returns:

{
  "parent": { "id": 1, "name": "Jane", "email": "jane@x.com", "phone": "555-1234" },
  "team": { "id": 5, "name": "16U Kings", "coach_name": "KJ Ng" }
}

Frontend expects (players/[id]/+page.svelte):

player.parent_name    // undefined — API sends player.parent.name
player.parent_phone   // undefined — API sends player.parent.phone
player.parent_email   // undefined — API sends player.parent.email
player.team_name      // undefined — API sends player.team.name
player.team_id        // undefined — API sends player.team.id
player.coach_name     // undefined — API sends player.team.coach_name
player.coach_phone    // undefined — API has no coach phone at all

Visible impact: Player profile page shows empty parent contact, empty team name, empty coach name. The "Team & Coach" card appears empty or falls through to "Not assigned to a team yet" even when the player has a team. Admin Actions card shows no parent phone or email. This is the #276/#203 root cause pattern.

Severity: HIGH — core player profile page is broken for all real API data.


MISMATCH 2: GET /admin/players — Missing parent_phone (VISIBLE BUG)

API returns: AdminPlayerItem has parent_name and parent_email but no parent_phone.

Frontend expects (admin/players/+page.svelte, line 297-299):

{#if player.parent_name || player.parent_phone}
    {player.parent_name || ''}{player.parent_phone ? ' · ' + player.parent_phone : ''}
{/if}

Visible impact: Parent phone never displays in the admin CRM player list. The subtitle row shows parent name but never phone. This was flagged in #276 QA but not fixed.

Severity: MEDIUM — data exists in DB (Parent.phone), just not in the response model.


MISMATCH 3: GET /account/players — Missing coach_name and team_id (VISIBLE BUG)

API returns: AccountPlayerResponse has team_name but no coach_name, no team_id, no height.

Frontend expects (my-players/+page.svelte, lines 109-111):

{#if player.coach_name || player.team_name}
    {player.coach_name ? 'Coach ' + player.coach_name : ''}{...}{player.team_name || ''}
{/if}

Also references player.height on line 107 which is not in the response model.

Visible impact: Parent dashboard player cards never show coach name. Height is always empty in the detail line.

Severity: MEDIUM — parent-facing dashboard is missing key info.


MISMATCH 4: GET /coaches/meCoachPlayerBrief missing parent_phone, school, height (VISIBLE BUG)

API returns: CoachPlayerBrief has only id, name, position, division, graduating_class.

Frontend expects (coach/+page.svelte, lines 118-122):

{#if player.parent_phone}
    <span style="color:var(--color-blue);">{player.parent_phone}</span>
{/if}

Also line 118: player.school and player.height.

Visible impact: Coach dashboard roster cards never show parent phone (the phone icon row never renders), school, or height. Coaches need parent phone to contact families — this is a workflow blocker.

Severity: HIGH — coaches cannot contact parents from their dashboard.


MISMATCH 5: GET /admin/players/incompleteIncompletePlayerItem missing parent_phone (LATENT)

API returns: parent_email, parent_name — no parent_phone.

Frontend consumer: No dedicated frontend page currently renders this endpoint's data in a way that references parent_phone, but the pattern is inconsistent with PlayerInfo (roster.py) which does include it. If an incomplete-players admin view is built, it will hit the same gap.

Severity: LOW — latent, but should be fixed for consistency.


MISMATCH 6: GET /subscriptionsSubscriptionListItem missing parent_phone (LATENT)

API returns: parent_name, parent_email — no parent_phone.

Frontend consumer: No dedicated subscription management page exists yet in westside-app.

Severity: LOW — latent, fix for consistency.


MISMATCH 7: GET /admin/teamsTeamPlayerItem missing parent_phone, parent_email (LATENT)

API returns: parent_name only. No parent_phone or parent_email.

Frontend (admin/teams/+page.svelte): Currently does not display parent info in the draft board, so no visible bug. But the model is inconsistent — admin contexts generally need parent contact info.

Severity: LOW — no current visible impact.


MISMATCH 8: GET /coaches/{id} — Frontend expects team_name, title, bio that API doesn't return (VISIBLE BUG)

API returns: CoachProfileResponse with id, name, email, phone, role, onboarding_status, teams[].

Frontend expects (coaches/[id]/+page.svelte):

coach.team_name      // undefined — API returns teams[] array
coach.title          // undefined — API returns role
coach.bio            // undefined — not in model
coach.photo_url      // undefined — not in model

Visible impact: Coach profile page shows empty role label (checks coach.title not coach.role), no team name in header, no bio, no photo. The teams section works because it iterates coach.teams[].

Severity: MEDIUM — coach profile page partially broken.


MISMATCH 9: GET /teams/{id}TeamDetail returns nested coach: CoachBrief but frontend expects flat + extra fields (PARTIAL MISMATCH)

API returns:

{
  "coach": { "id": 1, "name": "KJ", "email": "kj@x.com" },
  "players": [{ "id": 1, "name": "Player", "division": "boys", "position": "PG" }]
}

Frontend expects (teams/[id]/+page.svelte):

  • team.coaches (plural array) — API returns team.coach (singular object)
  • coach.role — API's CoachBrief has no role field
  • coach.phone — API's CoachBrief has no phone field
  • player.height — API's PlayerBrief has no height field
  • player.jersey_number — API's PlayerBrief has no jersey_number field

Visible impact: Team detail page shows no coaches (iterates team.coaches which is undefined), player cards missing height/jersey. The coaches card renders "0 coaches" and an empty list.

Severity: HIGH — team page coaches section completely broken. Players section partially broken (missing height, jersey).


3. Recommendations

Strategy: Fix at API layer (flatten + enrich responses)

The frontend was designed mobile-first with flat field expectations. The API should match. Rationale:

  • Frontend is adapter-static (no server-side data transforms)
  • Every Svelte template already uses player.parent_phone, player.team_name, player.coach_name — changing all templates is higher risk
  • Nested objects add no value for this SPA — no frontend component consumes player.parent as an object

Per-mismatch recommendations:

# Mismatch Fix Location Action
1 PlayerProfileResponse nested parent/team API Add flat aliases: parent_name, parent_phone, parent_email, team_name, team_id, coach_name. Keep nested objects for backwards compat.
2 AdminPlayerItem missing parent_phone API Add parent_phone: str | None to model + query
3 AccountPlayerResponse missing coach_name, team_id, height API Add coach_name, team_id, height fields
4 CoachPlayerBrief missing parent_phone, school, height API Add parent_phone, school, height fields
5 IncompletePlayerItem missing parent_phone API Add parent_phone: str | None
6 SubscriptionListItem missing parent_phone API Add parent_phone: str | None
7 TeamPlayerItem missing parent_phone, parent_email API Add parent_phone, parent_email
8 CoachProfileResponse vs frontend Both API: add photo_url, consider bio. Frontend: use coach.role not coach.title. Use coach.teams[0]?.name for team_name.
9 TeamDetail singular coach vs plural coaches Both API: return coaches: list[CoachBrief] instead of coach: CoachBrief. Add phone, role to CoachBrief. Add height, jersey_number to PlayerBrief.

4. Suggested Follow-Up Ticket Groupings

Ticket Group A — Player Profile Flatten (HIGH, fixes #276 pattern completely)

  • Flatten PlayerProfileResponse to include parent_name, parent_phone, parent_email, team_name, team_id, coach_name
  • Fixes: Mismatch 1
  • Scope: players.py only

Ticket Group B — Admin CRM Missing Fields (MEDIUM)

  • Add parent_phone to AdminPlayerItem, IncompletePlayerItem, TeamPlayerItem
  • Add parent_email to TeamPlayerItem
  • Fixes: Mismatches 2, 5, 7
  • Scope: admin.py only

Ticket Group C — Parent Dashboard Enrichment (MEDIUM)

  • Add coach_name, team_id, height to AccountPlayerResponse
  • Fixes: Mismatch 3
  • Scope: account.py only

Ticket Group D — Coach Dashboard Parent Contact (HIGH)

  • Add parent_phone, school (current_school), height to CoachPlayerBrief
  • Fixes: Mismatch 4
  • Scope: coaches_api.py only

Ticket Group E — Team Detail Page Contract (HIGH)

  • Change TeamDetail.coach from singular to coaches: list[]
  • Add role, phone to CoachBrief
  • Add height, jersey_number to PlayerBrief
  • Fixes: Mismatch 9
  • Scope: teams.py + teams/[id]/+page.svelte

Ticket Group F — Coach Profile Page Contract (MEDIUM)

  • Frontend fix: coach.title -> coach.role
  • Frontend fix: derive team_name from coach.teams[0]?.name
  • Optional API: add photo_url, bio to CoachProfileResponse (if DB supports it)
  • Fixes: Mismatch 8
  • Scope: coaches_api.py + coaches/[id]/+page.svelte

Ticket Group G — Subscription List Consistency (LOW)

  • Add parent_phone to SubscriptionListItem
  • Fixes: Mismatch 6
  • Scope: subscriptions.py only

Summary

  • 9 mismatches found across 9 response models
  • 4 HIGH severity (visible bugs on core pages: player profile, coach dashboard, team detail)
  • 3 MEDIUM severity (partial data missing on admin CRM, parent dashboard, coach profile)
  • 2 LOW severity (latent gaps, no current frontend consumer)
  • 7 ticket groups recommended, can be parallelized (no cross-dependencies except Group E which touches both repos)
  • Root cause pattern: API was built with normalized/nested responses, frontend was built mobile-first expecting flat denormalized fields. Neither side adapted to the other.
## Spike Results: API Response Contract Mismatch Audit Investigation covered all Pydantic response models in `basketball-api/src/basketball_api/routes/` and all Svelte templates in `westside-app/src/routes/` that consume player, parent, team, or coach data. --- ### 1. Complete Inventory of Response Models with Parent/Team/Coach Data | # | Model | File | Endpoint | Parent Fields | Team Fields | Coach Fields | |---|-------|------|----------|---------------|-------------|--------------| | 1 | `PlayerProfileResponse` | `players.py` | `GET /players/{id}` | `parent: ParentInfo` (nested: id, name, email, phone) | `team: TeamInfo` (nested: id, name, coach_name) | coach_name inside TeamInfo | | 2 | `AdminPlayerItem` | `admin.py` | `GET /admin/players` | `parent_name`, `parent_email` (flat, **no parent_phone**) | `team_name` (flat) | none | | 3 | `IncompletePlayerItem` | `admin.py` | `GET /admin/players/incomplete` | `parent_email`, `parent_name` (flat, **no parent_phone**) | none | none | | 4 | `SubscriptionListItem` | `subscriptions.py` | `GET /subscriptions`, `GET /subscriptions/overview` | `parent_name`, `parent_email` (flat, **no parent_phone**) | none | none | | 5 | `AccountPlayerResponse` | `account.py` | `GET /account/players` | none | `team_name` (flat) | none | | 6 | `TeamPlayerItem` | `admin.py` | `GET /admin/teams` | `parent_name` (flat, **no parent_email, no parent_phone**) | `team_id`, `team`, `team_ids`, `team_names` (flat) | none | | 7 | `DashboardTeamItem` | `admin.py` | `GET /admin/dashboard` | none | id, name | `coach_name` (flat) | | 8 | `TeamResponse` / `TeamDetail` | `teams.py` | `GET /teams/{id}`, `GET /teams` | none | id, name, division, age_group | `coach: CoachBrief` (nested: id, name, email) | | 9 | `CoachDashboardResponse` | `coaches_api.py` | `GET /coaches/me` | none | `teams: [CoachTeamResponse]` with nested `players: [CoachPlayerBrief]` | id, name, email, role (flat) | | 10 | `CoachPlayerBrief` | `coaches_api.py` | nested in `/coaches/me` | none | none | none | | 11 | `PlayerInfo` | `roster.py` | `GET /{slug}/roster` | `parent_name`, `parent_email`, `parent_phone` (flat) | none | none | | 12 | `PublicTeamResponse` | `public.py` | `GET /public/teams` | none | id, name, division, age_group | `coach_name` (flat) | | 13 | `CoachProfileResponse` | `coaches_api.py` | `GET /coaches/{id}` | none | `teams: [CoachTeamResponse]` | id, name, email, phone, role, onboarding_status | --- ### 2. Mismatches Found #### MISMATCH 1: `GET /players/{id}` — Nested parent/team vs flat field expectations (VISIBLE BUG) **API returns:** ```json { "parent": { "id": 1, "name": "Jane", "email": "jane@x.com", "phone": "555-1234" }, "team": { "id": 5, "name": "16U Kings", "coach_name": "KJ Ng" } } ``` **Frontend expects (players/[id]/+page.svelte):** ```js player.parent_name // undefined — API sends player.parent.name player.parent_phone // undefined — API sends player.parent.phone player.parent_email // undefined — API sends player.parent.email player.team_name // undefined — API sends player.team.name player.team_id // undefined — API sends player.team.id player.coach_name // undefined — API sends player.team.coach_name player.coach_phone // undefined — API has no coach phone at all ``` **Visible impact:** Player profile page shows empty parent contact, empty team name, empty coach name. The "Team & Coach" card appears empty or falls through to "Not assigned to a team yet" even when the player has a team. Admin Actions card shows no parent phone or email. This is the **#276/#203 root cause pattern**. **Severity:** HIGH — core player profile page is broken for all real API data. --- #### MISMATCH 2: `GET /admin/players` — Missing `parent_phone` (VISIBLE BUG) **API returns:** `AdminPlayerItem` has `parent_name` and `parent_email` but **no `parent_phone`**. **Frontend expects (admin/players/+page.svelte, line 297-299):** ```svelte {#if player.parent_name || player.parent_phone} {player.parent_name || ''}{player.parent_phone ? ' · ' + player.parent_phone : ''} {/if} ``` **Visible impact:** Parent phone never displays in the admin CRM player list. The subtitle row shows parent name but never phone. This was flagged in #276 QA but not fixed. **Severity:** MEDIUM — data exists in DB (Parent.phone), just not in the response model. --- #### MISMATCH 3: `GET /account/players` — Missing `coach_name` and `team_id` (VISIBLE BUG) **API returns:** `AccountPlayerResponse` has `team_name` but **no `coach_name`**, no `team_id`, no `height`. **Frontend expects (my-players/+page.svelte, lines 109-111):** ```svelte {#if player.coach_name || player.team_name} {player.coach_name ? 'Coach ' + player.coach_name : ''}{...}{player.team_name || ''} {/if} ``` Also references `player.height` on line 107 which is not in the response model. **Visible impact:** Parent dashboard player cards never show coach name. Height is always empty in the detail line. **Severity:** MEDIUM — parent-facing dashboard is missing key info. --- #### MISMATCH 4: `GET /coaches/me` — `CoachPlayerBrief` missing `parent_phone`, `school`, `height` (VISIBLE BUG) **API returns:** `CoachPlayerBrief` has only `id, name, position, division, graduating_class`. **Frontend expects (coach/+page.svelte, lines 118-122):** ```svelte {#if player.parent_phone} <span style="color:var(--color-blue);">{player.parent_phone}</span> {/if} ``` Also line 118: `player.school` and `player.height`. **Visible impact:** Coach dashboard roster cards never show parent phone (the phone icon row never renders), school, or height. Coaches need parent phone to contact families — this is a **workflow blocker**. **Severity:** HIGH — coaches cannot contact parents from their dashboard. --- #### MISMATCH 5: `GET /admin/players/incomplete` — `IncompletePlayerItem` missing `parent_phone` (LATENT) **API returns:** `parent_email`, `parent_name` — no `parent_phone`. **Frontend consumer:** No dedicated frontend page currently renders this endpoint's data in a way that references `parent_phone`, but the pattern is inconsistent with `PlayerInfo` (roster.py) which does include it. If an incomplete-players admin view is built, it will hit the same gap. **Severity:** LOW — latent, but should be fixed for consistency. --- #### MISMATCH 6: `GET /subscriptions` — `SubscriptionListItem` missing `parent_phone` (LATENT) **API returns:** `parent_name`, `parent_email` — no `parent_phone`. **Frontend consumer:** No dedicated subscription management page exists yet in westside-app. **Severity:** LOW — latent, fix for consistency. --- #### MISMATCH 7: `GET /admin/teams` — `TeamPlayerItem` missing `parent_phone`, `parent_email` (LATENT) **API returns:** `parent_name` only. No `parent_phone` or `parent_email`. **Frontend (admin/teams/+page.svelte):** Currently does not display parent info in the draft board, so no visible bug. But the model is inconsistent — admin contexts generally need parent contact info. **Severity:** LOW — no current visible impact. --- #### MISMATCH 8: `GET /coaches/{id}` — Frontend expects `team_name`, `title`, `bio` that API doesn't return (VISIBLE BUG) **API returns:** `CoachProfileResponse` with `id, name, email, phone, role, onboarding_status, teams[]`. **Frontend expects (coaches/[id]/+page.svelte):** ```js coach.team_name // undefined — API returns teams[] array coach.title // undefined — API returns role coach.bio // undefined — not in model coach.photo_url // undefined — not in model ``` **Visible impact:** Coach profile page shows empty role label (checks `coach.title` not `coach.role`), no team name in header, no bio, no photo. The teams section works because it iterates `coach.teams[]`. **Severity:** MEDIUM — coach profile page partially broken. --- #### MISMATCH 9: `GET /teams/{id}` — `TeamDetail` returns nested `coach: CoachBrief` but frontend expects flat + extra fields (PARTIAL MISMATCH) **API returns:** ```json { "coach": { "id": 1, "name": "KJ", "email": "kj@x.com" }, "players": [{ "id": 1, "name": "Player", "division": "boys", "position": "PG" }] } ``` **Frontend expects (teams/[id]/+page.svelte):** - `team.coaches` (plural array) — API returns `team.coach` (singular object) - `coach.role` — API's `CoachBrief` has no `role` field - `coach.phone` — API's `CoachBrief` has no `phone` field - `player.height` — API's `PlayerBrief` has no `height` field - `player.jersey_number` — API's `PlayerBrief` has no `jersey_number` field **Visible impact:** Team detail page shows no coaches (iterates `team.coaches` which is undefined), player cards missing height/jersey. The coaches card renders "0 coaches" and an empty list. **Severity:** HIGH — team page coaches section completely broken. Players section partially broken (missing height, jersey). --- ### 3. Recommendations #### Strategy: Fix at API layer (flatten + enrich responses) The frontend was designed mobile-first with flat field expectations. The API should match. Rationale: - Frontend is adapter-static (no server-side data transforms) - Every Svelte template already uses `player.parent_phone`, `player.team_name`, `player.coach_name` — changing all templates is higher risk - Nested objects add no value for this SPA — no frontend component consumes `player.parent` as an object **Per-mismatch recommendations:** | # | Mismatch | Fix Location | Action | |---|----------|-------------|--------| | 1 | PlayerProfileResponse nested parent/team | API | Add flat aliases: `parent_name`, `parent_phone`, `parent_email`, `team_name`, `team_id`, `coach_name`. Keep nested objects for backwards compat. | | 2 | AdminPlayerItem missing parent_phone | API | Add `parent_phone: str \| None` to model + query | | 3 | AccountPlayerResponse missing coach_name, team_id, height | API | Add `coach_name`, `team_id`, `height` fields | | 4 | CoachPlayerBrief missing parent_phone, school, height | API | Add `parent_phone`, `school`, `height` fields | | 5 | IncompletePlayerItem missing parent_phone | API | Add `parent_phone: str \| None` | | 6 | SubscriptionListItem missing parent_phone | API | Add `parent_phone: str \| None` | | 7 | TeamPlayerItem missing parent_phone, parent_email | API | Add `parent_phone`, `parent_email` | | 8 | CoachProfileResponse vs frontend | Both | API: add `photo_url`, consider `bio`. Frontend: use `coach.role` not `coach.title`. Use `coach.teams[0]?.name` for `team_name`. | | 9 | TeamDetail singular coach vs plural coaches | Both | API: return `coaches: list[CoachBrief]` instead of `coach: CoachBrief`. Add `phone`, `role` to `CoachBrief`. Add `height`, `jersey_number` to `PlayerBrief`. | --- ### 4. Suggested Follow-Up Ticket Groupings **Ticket Group A — Player Profile Flatten (HIGH, fixes #276 pattern completely)** - Flatten `PlayerProfileResponse` to include `parent_name`, `parent_phone`, `parent_email`, `team_name`, `team_id`, `coach_name` - Fixes: Mismatch 1 - Scope: `players.py` only **Ticket Group B — Admin CRM Missing Fields (MEDIUM)** - Add `parent_phone` to `AdminPlayerItem`, `IncompletePlayerItem`, `TeamPlayerItem` - Add `parent_email` to `TeamPlayerItem` - Fixes: Mismatches 2, 5, 7 - Scope: `admin.py` only **Ticket Group C — Parent Dashboard Enrichment (MEDIUM)** - Add `coach_name`, `team_id`, `height` to `AccountPlayerResponse` - Fixes: Mismatch 3 - Scope: `account.py` only **Ticket Group D — Coach Dashboard Parent Contact (HIGH)** - Add `parent_phone`, `school` (`current_school`), `height` to `CoachPlayerBrief` - Fixes: Mismatch 4 - Scope: `coaches_api.py` only **Ticket Group E — Team Detail Page Contract (HIGH)** - Change `TeamDetail.coach` from singular to `coaches: list[]` - Add `role`, `phone` to `CoachBrief` - Add `height`, `jersey_number` to `PlayerBrief` - Fixes: Mismatch 9 - Scope: `teams.py` + `teams/[id]/+page.svelte` **Ticket Group F — Coach Profile Page Contract (MEDIUM)** - Frontend fix: `coach.title` -> `coach.role` - Frontend fix: derive `team_name` from `coach.teams[0]?.name` - Optional API: add `photo_url`, `bio` to `CoachProfileResponse` (if DB supports it) - Fixes: Mismatch 8 - Scope: `coaches_api.py` + `coaches/[id]/+page.svelte` **Ticket Group G — Subscription List Consistency (LOW)** - Add `parent_phone` to `SubscriptionListItem` - Fixes: Mismatch 6 - Scope: `subscriptions.py` only --- ### Summary - **9 mismatches** found across 9 response models - **4 HIGH severity** (visible bugs on core pages: player profile, coach dashboard, team detail) - **3 MEDIUM severity** (partial data missing on admin CRM, parent dashboard, coach profile) - **2 LOW severity** (latent gaps, no current frontend consumer) - **7 ticket groups** recommended, can be parallelized (no cross-dependencies except Group E which touches both repos) - **Root cause pattern:** API was built with normalized/nested responses, frontend was built mobile-first expecting flat denormalized fields. Neither side adapted to the other.
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
forgejo_admin/basketball-api#278
No description provided.