feat: port playground prototypes to SvelteKit dashboards #31

Merged
forgejo_admin merged 1 commit from feature/10f-4-playground-port into main 2026-03-16 00:09:41 +00:00

Summary

  • Replace tryout-day triage UI with post-tryout operations dashboards
  • Port playground CSS design system (589 lines of component classes) to app.css
  • Port player, coach, and admin page layouts from playground HTML prototypes to SvelteKit with live API data bindings

Changes

  • src/app.css: Replaced with playground's unified design system. Adds component classes (.stat-card, .player-card, .badge-*, .list-row, .info-card, .team-card, .schedule-item, .actions-row, etc.). Keeps legacy --color-* aliases for AuthStatus.svelte and landing page compatibility.
  • src/routes/admin/+page.server.js: Replaced tryout triage grouping with payment stats derivation and overdue player filtering. Added fetchTeams() call for teams summary. Removed tryout-specific form actions.
  • src/routes/admin/+page.svelte: Replaced tryout triage buckets with operations dashboard: stats row, payment health bar with collection rate, overdue families list, teams summary cards, quick action buttons.
  • src/routes/coach/+page.svelte: Replaced tryout check-in card grid with player card grid showing parent contact info and payment status badges.
  • src/routes/player/+page.svelte: Replaced tryout card view with info-card layout: player profile, team assignment, payment status, and schedule placeholder.

Test Plan

  • npm run build passes with zero errors
  • npm run check -- no new errors (31 remaining are all pre-existing in untouched files)
  • Player page shows profile info cards (not tryout card grid)
  • Coach page shows player grid with parent contact + payment badges
  • Admin page shows operations dashboard with stats, payment health bar, overdue list, teams summary
  • Landing page unchanged
  • Auth redirects still work (admin->/admin, coach->/coach, player->/player)
  • Mobile layout correct at 390px width

Review Checklist

  • Passed automated review-fix loop
  • No secrets committed
  • No unnecessary file changes
  • Commit messages are descriptive
  • Closes #30
  • plan-2026-03-08-tryout-prep -- Phase 10f-4
## Summary - Replace tryout-day triage UI with post-tryout operations dashboards - Port playground CSS design system (589 lines of component classes) to app.css - Port player, coach, and admin page layouts from playground HTML prototypes to SvelteKit with live API data bindings ## Changes - `src/app.css`: Replaced with playground's unified design system. Adds component classes (`.stat-card`, `.player-card`, `.badge-*`, `.list-row`, `.info-card`, `.team-card`, `.schedule-item`, `.actions-row`, etc.). Keeps legacy `--color-*` aliases for AuthStatus.svelte and landing page compatibility. - `src/routes/admin/+page.server.js`: Replaced tryout triage grouping with payment stats derivation and overdue player filtering. Added `fetchTeams()` call for teams summary. Removed tryout-specific form actions. - `src/routes/admin/+page.svelte`: Replaced tryout triage buckets with operations dashboard: stats row, payment health bar with collection rate, overdue families list, teams summary cards, quick action buttons. - `src/routes/coach/+page.svelte`: Replaced tryout check-in card grid with player card grid showing parent contact info and payment status badges. - `src/routes/player/+page.svelte`: Replaced tryout card view with info-card layout: player profile, team assignment, payment status, and schedule placeholder. ## Test Plan - [x] `npm run build` passes with zero errors - [x] `npm run check` -- no new errors (31 remaining are all pre-existing in untouched files) - [ ] Player page shows profile info cards (not tryout card grid) - [ ] Coach page shows player grid with parent contact + payment badges - [ ] Admin page shows operations dashboard with stats, payment health bar, overdue list, teams summary - [ ] Landing page unchanged - [ ] Auth redirects still work (admin->/admin, coach->/coach, player->/player) - [ ] Mobile layout correct at 390px width ## Review Checklist - [x] Passed automated review-fix loop - [x] No secrets committed - [x] No unnecessary file changes - [x] Commit messages are descriptive ## Related - Closes #30 - `plan-2026-03-08-tryout-prep` -- Phase 10f-4
Replace tryout-day triage UI with post-tryout operations dashboards.
Port playground CSS design system and page layouts to SvelteKit.

Closes #30

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Author
Owner

Self-Review

Reviewed all 5 changed files (100% of diff).

Verified

  • No secrets committed
  • All changes scoped to issue #30 File Targets list
  • CSS variable backward compatibility: legacy --color-* aliases preserved for AuthStatus.svelte and landing page
  • Playground <nav> elements stripped -- AuthStatus.svelte handles navigation
  • Data bindings match server.js load function return shapes
  • Graceful fallbacks for missing data (optional chaining, || '?', || 'Not assigned')
  • JSDoc type annotations on all function parameters -- zero new type errors
  • npm run build passes
  • npm run check -- no new errors (31 remaining are pre-existing in untouched files)

No blocking issues found

## Self-Review Reviewed all 5 changed files (100% of diff). ### Verified - No secrets committed - All changes scoped to issue #30 File Targets list - CSS variable backward compatibility: legacy `--color-*` aliases preserved for AuthStatus.svelte and landing page - Playground `<nav>` elements stripped -- AuthStatus.svelte handles navigation - Data bindings match server.js load function return shapes - Graceful fallbacks for missing data (optional chaining, `|| '?'`, `|| 'Not assigned'`) - JSDoc type annotations on all function parameters -- zero new type errors - `npm run build` passes - `npm run check` -- no new errors (31 remaining are pre-existing in untouched files) ### No blocking issues found
Author
Owner

PR #31 Review

DOMAIN REVIEW

Tech stack: SvelteKit (Svelte 5 runes), JavaScript (no TypeScript), CSS custom properties design system.

What this PR does: Replaces tryout-day UI with post-tryout operations dashboards by porting HTML/CSS prototypes from ~/westside-playground/ to SvelteKit components. Five files changed: src/app.css (design system replacement), player/+page.svelte, coach/+page.svelte, admin/+page.svelte, and admin/+page.server.js (new fetchTeams + payment stats derivation).

Playground fidelity check:

  • src/app.css is a faithful port of westside-playground/shared/app.css. The playground nav bar (.nav, .nav-brand, .nav-logo, .nav-user, .bottom-nav) was intentionally excluded since the SvelteKit app uses AuthStatus.svelte for navigation. Legacy --color-* aliases were correctly added for AuthStatus.svelte and landing page compatibility. The body { min-height: 100vh } addition is a good improvement over the playground source. Alert and empty-state classes added for Svelte error/empty states. Responsive breakpoints match playground.
  • player/+page.svelte faithfully ports parent.html info-card layout. Data-driven via data.myPlayers with proper null guards (player.position || '?'). Schedule section correctly shows placeholder ("Schedule coming soon") since schedule data is not yet API-backed. Graceful fallback for admins/coaches with no player profile.
  • coach/+page.svelte faithfully ports coach.html player-card grid. Search filter is reactive via $derived. Parent contact info with phone formatting. Photo support added (playground was text-only). Empty state handling is thorough (no team, no players, no matches).
  • admin/+page.svelte faithfully ports admin.html operations dashboard. Page-specific stat-bar styles correctly kept in component <style> block (matching playground's inline <style> approach). Stats, payment health bar, overdue families list, team cards, and quick actions all data-driven. Quick action links correctly point to /admin/users and /admin/teams.
  • admin/+page.server.js adds fetchTeams() import and derivePaymentStats() / getOverduePlayers() helper functions. Auth guard is present and correct (admin role required). Promise.all for parallel fetching is good. Error handling returns safe defaults.

Data binding verification (all pass):

  • admin/+page.svelte accesses data.paymentStats.{total,current,overdue,collectionRate}, data.teams, data.overduePlayers, data.apiConnected -- all returned by admin/+page.server.js.
  • coach/+page.svelte accesses data.myTeam, data.players, data.totalPlayers, data.apiConnected -- all returned by coach/+page.server.js.
  • player/+page.svelte accesses data.myPlayers, data.apiConnected, data.roles -- all returned by player/+page.server.js.

Auth guard verification (all pass):

  • admin/+page.server.js checks session?.user (redirect to /signin) and roles.includes('admin') (403).
  • coach/+page.server.js checks session?.user (redirect to /signin) and roles.includes('coach') || roles.includes('admin') (403).
  • player/+page.server.js checks session?.user (redirect to /signin).

API module verification: fetchTeams() exists in src/lib/server/api.js (line 58) and accepts (tenant, token). The admin/+page.server.js calls it as fetchTeams(TENANT, accessToken) which matches the signature. The .catch(() => []) gracefully handles team API failures.

"Do not touch" files verification: Landing page (+page.svelte, +page.server.js), auth routes (signin/, signout/), admin/teams/, admin/users/, AuthStatus.svelte, +layout.svelte, +layout.server.js, hooks.server.js, auth.js, app.html -- all unchanged. Confirmed by reading each file; no modifications detected.

CSS compatibility: The app.html inline styles set background-color: #0a0a0a and color: #f0f0f0 which match the new --color-black and --color-gray-100 design tokens. Legacy aliases (--color-bg, --color-bg-card, --color-text, --color-brand, etc.) preserve compatibility with AuthStatus.svelte and the landing page +page.svelte scoped styles.

XSS check: All dynamic values are rendered via Svelte's {expression} syntax which auto-escapes HTML. No use of {@html}. Phone numbers in href="tel:{player.parent_phone}" are URL-scheme references, not HTML injection vectors. Safe.

BLOCKERS

None found. No build errors expected (no new dependencies, all imports resolve). Auth guards are present on all protected routes. Data bindings match server returns. No XSS vectors. No secrets committed. No "do not touch" files modified. No removed functionality that is still needed.

On test coverage: This project has zero test files (pre-existing condition across the entire repo). The PR ports visual templates from playground HTML to data-driven Svelte components -- it adds server-side data derivation logic (derivePaymentStats, getOverduePlayers) which would benefit from unit tests, but the zero-test baseline is a repo-level gap, not a PR-introduced regression. Flagging as a nit rather than a blocker given the context.

NITS

  1. DRY violation -- utility functions duplicated across 3+ components. getInitials() is defined in player/+page.svelte, coach/+page.svelte, and admin/+page.svelte. getPaymentBadgeClass() and getPaymentBadgeLabel() are identical in all three. formatPhone() is in coach/ and admin/. formatGradClass() is in player/, coach/, and admin/teams/. These should be extracted to src/lib/utils.js or similar. Not a security path, but maintenance risk -- if badge logic changes, three files need updating.

  2. Dead code in coach/+page.server.js. Line 42 computes checkedInCount and line 48 returns it, but the new coach/+page.svelte never references data.checkedInCount. This was used by the old tryout-day check-in UI. Should be removed.

  3. Hardcoded $200/month and $200 amounts. player/+page.svelte line 114 and admin/+page.svelte line 107 hardcode the payment amount. Same as playground source, but when subscription amounts vary per player, these will break. Consider deriving from API data when available.

  4. Hardcoded tenant slug. admin/+page.server.js line 4: const TENANT = 'westside-kings-queens'. Same pattern exists in player/ and coach/ server files. Could be a shared constant in $lib/server/api.js or $lib/config.js.

  5. Admin overdue list shows static "overdue" text. admin/+page.svelte line 108: <div class="list-days">overdue</div>. The playground shows specific days overdue ("12 days overdue", "8 days overdue"). The API likely does not provide this data yet, but the placeholder text is less informative than the playground design. Consider a TODO comment.

  6. Missing <label> for search input. coach/+page.svelte line 83 uses <input type="text" class="search" placeholder="Search players..."> without an associated <label> element or aria-label attribute. Same issue in the playground source, but SvelteKit should be more accessible. A visually-hidden label or aria-label="Search players" would help screen readers.

  7. No test coverage for derivePaymentStats() and getOverduePlayers(). These are pure functions with clear inputs/outputs -- ideal for unit testing. derivePaymentStats handles edge cases (empty array, missing payment_status) which should be verified. Low effort, high value.

  8. Playground min-height: 100vh on body not in playground source. src/app.css line 84 adds min-height: 100vh to body which was not in westside-playground/shared/app.css. This is an improvement (prevents short-page white flash at bottom), but worth noting as a deliberate deviation from playground source.

SOP COMPLIANCE

  • Branch named after issue (feature/10f-4-playground-port references Phase 10f-4, issue #30)
  • PR title follows convention (feat: port playground prototypes to SvelteKit dashboards)
  • Related issue exists (#30 — "Port playground prototypes to SvelteKit (Phase 10f-4)")
  • No secrets committed
  • No unnecessary file changes (scope is tight -- 5 files, all related to the port)
  • PR body template not verifiable (could not parse PR body from review tool output -- single-line JSON exceeded token limits)

PROCESS OBSERVATIONS

  • Deployment frequency: This PR enables the transition from tryout-day UI to post-tryout operations dashboards. It unblocks coach/admin/player daily usage of the app, which should increase deployment frequency as each role's dashboard gets iterated on.
  • Change failure risk: Low. Auth guards are unchanged. Data bindings verified against server returns. CSS design system is a full replacement but with legacy aliases for backward compatibility with existing components. The playground-first approach (prototype in static HTML, then port) de-risks visual regressions.
  • Documentation gap: The DRY violations (5 utility functions x 3-4 files) create a maintenance scaling risk. As more pages are added, the copy-paste pattern will compound. Extracting to $lib/utils.js should happen soon.
  • Test gap: The repo has zero tests. The derivePaymentStats and getOverduePlayers functions added in this PR are pure functions that are easy to test. This would be a good entry point for establishing a test baseline.

VERDICT: APPROVED

The port is faithful to the playground prototypes, data bindings are correct, auth guards are intact, and no "do not touch" files were modified. The DRY violations and dead code are real quality issues but do not block merge -- they should be tracked as follow-up work (ideally as a Phase 10f-5 or similar).

## PR #31 Review ### DOMAIN REVIEW **Tech stack:** SvelteKit (Svelte 5 runes), JavaScript (no TypeScript), CSS custom properties design system. **What this PR does:** Replaces tryout-day UI with post-tryout operations dashboards by porting HTML/CSS prototypes from `~/westside-playground/` to SvelteKit components. Five files changed: `src/app.css` (design system replacement), `player/+page.svelte`, `coach/+page.svelte`, `admin/+page.svelte`, and `admin/+page.server.js` (new `fetchTeams` + payment stats derivation). **Playground fidelity check:** - `src/app.css` is a faithful port of `westside-playground/shared/app.css`. The playground nav bar (`.nav`, `.nav-brand`, `.nav-logo`, `.nav-user`, `.bottom-nav`) was intentionally excluded since the SvelteKit app uses `AuthStatus.svelte` for navigation. Legacy `--color-*` aliases were correctly added for `AuthStatus.svelte` and landing page compatibility. The `body { min-height: 100vh }` addition is a good improvement over the playground source. Alert and empty-state classes added for Svelte error/empty states. Responsive breakpoints match playground. - `player/+page.svelte` faithfully ports `parent.html` info-card layout. Data-driven via `data.myPlayers` with proper null guards (`player.position || '?'`). Schedule section correctly shows placeholder ("Schedule coming soon") since schedule data is not yet API-backed. Graceful fallback for admins/coaches with no player profile. - `coach/+page.svelte` faithfully ports `coach.html` player-card grid. Search filter is reactive via `$derived`. Parent contact info with phone formatting. Photo support added (playground was text-only). Empty state handling is thorough (no team, no players, no matches). - `admin/+page.svelte` faithfully ports `admin.html` operations dashboard. Page-specific stat-bar styles correctly kept in component `<style>` block (matching playground's inline `<style>` approach). Stats, payment health bar, overdue families list, team cards, and quick actions all data-driven. Quick action links correctly point to `/admin/users` and `/admin/teams`. - `admin/+page.server.js` adds `fetchTeams()` import and `derivePaymentStats()` / `getOverduePlayers()` helper functions. Auth guard is present and correct (admin role required). `Promise.all` for parallel fetching is good. Error handling returns safe defaults. **Data binding verification (all pass):** - `admin/+page.svelte` accesses `data.paymentStats.{total,current,overdue,collectionRate}`, `data.teams`, `data.overduePlayers`, `data.apiConnected` -- all returned by `admin/+page.server.js`. - `coach/+page.svelte` accesses `data.myTeam`, `data.players`, `data.totalPlayers`, `data.apiConnected` -- all returned by `coach/+page.server.js`. - `player/+page.svelte` accesses `data.myPlayers`, `data.apiConnected`, `data.roles` -- all returned by `player/+page.server.js`. **Auth guard verification (all pass):** - `admin/+page.server.js` checks `session?.user` (redirect to `/signin`) and `roles.includes('admin')` (403). - `coach/+page.server.js` checks `session?.user` (redirect to `/signin`) and `roles.includes('coach') || roles.includes('admin')` (403). - `player/+page.server.js` checks `session?.user` (redirect to `/signin`). **API module verification:** `fetchTeams()` exists in `src/lib/server/api.js` (line 58) and accepts `(tenant, token)`. The `admin/+page.server.js` calls it as `fetchTeams(TENANT, accessToken)` which matches the signature. The `.catch(() => [])` gracefully handles team API failures. **"Do not touch" files verification:** Landing page (`+page.svelte`, `+page.server.js`), auth routes (`signin/`, `signout/`), `admin/teams/`, `admin/users/`, `AuthStatus.svelte`, `+layout.svelte`, `+layout.server.js`, `hooks.server.js`, `auth.js`, `app.html` -- all unchanged. Confirmed by reading each file; no modifications detected. **CSS compatibility:** The `app.html` inline styles set `background-color: #0a0a0a` and `color: #f0f0f0` which match the new `--color-black` and `--color-gray-100` design tokens. Legacy aliases (`--color-bg`, `--color-bg-card`, `--color-text`, `--color-brand`, etc.) preserve compatibility with `AuthStatus.svelte` and the landing page `+page.svelte` scoped styles. **XSS check:** All dynamic values are rendered via Svelte's `{expression}` syntax which auto-escapes HTML. No use of `{@html}`. Phone numbers in `href="tel:{player.parent_phone}"` are URL-scheme references, not HTML injection vectors. Safe. ### BLOCKERS None found. No build errors expected (no new dependencies, all imports resolve). Auth guards are present on all protected routes. Data bindings match server returns. No XSS vectors. No secrets committed. No "do not touch" files modified. No removed functionality that is still needed. **On test coverage:** This project has zero test files (pre-existing condition across the entire repo). The PR ports visual templates from playground HTML to data-driven Svelte components -- it adds server-side data derivation logic (`derivePaymentStats`, `getOverduePlayers`) which would benefit from unit tests, but the zero-test baseline is a repo-level gap, not a PR-introduced regression. Flagging as a nit rather than a blocker given the context. ### NITS 1. **DRY violation -- utility functions duplicated across 3+ components.** `getInitials()` is defined in `player/+page.svelte`, `coach/+page.svelte`, and `admin/+page.svelte`. `getPaymentBadgeClass()` and `getPaymentBadgeLabel()` are identical in all three. `formatPhone()` is in `coach/` and `admin/`. `formatGradClass()` is in `player/`, `coach/`, and `admin/teams/`. These should be extracted to `src/lib/utils.js` or similar. Not a security path, but maintenance risk -- if badge logic changes, three files need updating. 2. **Dead code in `coach/+page.server.js`.** Line 42 computes `checkedInCount` and line 48 returns it, but the new `coach/+page.svelte` never references `data.checkedInCount`. This was used by the old tryout-day check-in UI. Should be removed. 3. **Hardcoded `$200/month` and `$200` amounts.** `player/+page.svelte` line 114 and `admin/+page.svelte` line 107 hardcode the payment amount. Same as playground source, but when subscription amounts vary per player, these will break. Consider deriving from API data when available. 4. **Hardcoded tenant slug.** `admin/+page.server.js` line 4: `const TENANT = 'westside-kings-queens'`. Same pattern exists in `player/` and `coach/` server files. Could be a shared constant in `$lib/server/api.js` or `$lib/config.js`. 5. **Admin overdue list shows static "overdue" text.** `admin/+page.svelte` line 108: `<div class="list-days">overdue</div>`. The playground shows specific days overdue ("12 days overdue", "8 days overdue"). The API likely does not provide this data yet, but the placeholder text is less informative than the playground design. Consider a TODO comment. 6. **Missing `<label>` for search input.** `coach/+page.svelte` line 83 uses `<input type="text" class="search" placeholder="Search players...">` without an associated `<label>` element or `aria-label` attribute. Same issue in the playground source, but SvelteKit should be more accessible. A visually-hidden label or `aria-label="Search players"` would help screen readers. 7. **No test coverage for `derivePaymentStats()` and `getOverduePlayers()`.** These are pure functions with clear inputs/outputs -- ideal for unit testing. `derivePaymentStats` handles edge cases (empty array, missing `payment_status`) which should be verified. Low effort, high value. 8. **Playground `min-height: 100vh` on body not in playground source.** `src/app.css` line 84 adds `min-height: 100vh` to body which was not in `westside-playground/shared/app.css`. This is an improvement (prevents short-page white flash at bottom), but worth noting as a deliberate deviation from playground source. ### SOP COMPLIANCE - [x] Branch named after issue (`feature/10f-4-playground-port` references Phase 10f-4, issue #30) - [x] PR title follows convention (`feat: port playground prototypes to SvelteKit dashboards`) - [x] Related issue exists (#30 — "Port playground prototypes to SvelteKit (Phase 10f-4)") - [x] No secrets committed - [x] No unnecessary file changes (scope is tight -- 5 files, all related to the port) - [ ] PR body template not verifiable (could not parse PR body from review tool output -- single-line JSON exceeded token limits) ### PROCESS OBSERVATIONS - **Deployment frequency:** This PR enables the transition from tryout-day UI to post-tryout operations dashboards. It unblocks coach/admin/player daily usage of the app, which should increase deployment frequency as each role's dashboard gets iterated on. - **Change failure risk:** Low. Auth guards are unchanged. Data bindings verified against server returns. CSS design system is a full replacement but with legacy aliases for backward compatibility with existing components. The playground-first approach (prototype in static HTML, then port) de-risks visual regressions. - **Documentation gap:** The DRY violations (5 utility functions x 3-4 files) create a maintenance scaling risk. As more pages are added, the copy-paste pattern will compound. Extracting to `$lib/utils.js` should happen soon. - **Test gap:** The repo has zero tests. The `derivePaymentStats` and `getOverduePlayers` functions added in this PR are pure functions that are easy to test. This would be a good entry point for establishing a test baseline. ### VERDICT: APPROVED The port is faithful to the playground prototypes, data bindings are correct, auth guards are intact, and no "do not touch" files were modified. The DRY violations and dead code are real quality issues but do not block merge -- they should be tracked as follow-up work (ideally as a Phase 10f-5 or similar).
forgejo_admin deleted branch feature/10f-4-playground-port 2026-03-16 00:09:41 +00:00
Sign in to join this conversation.
No reviewers
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/westside-landing!31
No description provided.