feat: add Commerce admin page for jersey/subscription/contract visibility #228

Merged
forgejo_admin merged 1 commit from 226-add-commerce-admin-page into main 2026-04-06 23:35:01 +00:00
Contributor

Summary

Promotes the approved playground prototype (commerce.html from westside-playground#50) to production Svelte. Adds a Commerce page to the admin nav with three tabbed views (Jerseys, Subscriptions, Contracts), each with summary stat cards, status-based filtering, and responsive data tables — all powered by the existing /admin/players API endpoint.

Changes

  • src/routes/(app)/admin/commerce/+page.svelte — New Commerce page with three tabs, $derived aggregations for summary counts, reactive filter buttons, and {#each} data tables. Uses onMount + apiFetch('/admin/players') consistent with other admin pages.
  • src/routes/(app)/+layout.svelte — Added Commerce nav item ($ icon) between CRM and Teams in the admin bottom nav.
  • src/app.css — Added commerce CSS classes (tab-bar, tab-btn, summary-row, summary-stat, data-table, badge-shipped, badge-past-due, badge-canceled) copied from playground shared/style.css. Includes mobile-responsive table stacking at 600px.

Test Plan

  • Log in as Marcus (admin), verify bottom nav shows: Dashboard | CRM | Commerce | Teams | Schedule | Sign Out
  • Navigate to Commerce, verify Jerseys tab loads with summary counts matching real player data
  • Switch to Subscriptions and Contracts tabs, verify correct columns and counts
  • Test filter buttons on each tab — verify row counts match filter badge counts
  • Test at 320px viewport width — verify bottom nav does not overflow
  • npm run build passes with no errors

Review Checklist

  • npm run build passes
  • No scoped <style> blocks — all CSS in global app.css
  • No Tailwind — pure CSS vars
  • No new API endpoints — reuses /admin/players
  • Svelte 5 runes ($state, $derived) consistent with other admin pages
  • Mobile-responsive data tables with data-label attributes
  • No unrelated changes
  • Closes #226
  • Upstream: forgejo_admin/westside-playground#50 (playground prototype)
  • story:WS-S9 — "As an admin, I want to track payment status per player so that I know who owes what."
  • sop-capacitor-mobile-lifecycle — playground-to-Svelte promotion SOP
## Summary Promotes the approved playground prototype (`commerce.html` from `westside-playground#50`) to production Svelte. Adds a Commerce page to the admin nav with three tabbed views (Jerseys, Subscriptions, Contracts), each with summary stat cards, status-based filtering, and responsive data tables — all powered by the existing `/admin/players` API endpoint. ## Changes - **`src/routes/(app)/admin/commerce/+page.svelte`** — New Commerce page with three tabs, `$derived` aggregations for summary counts, reactive filter buttons, and `{#each}` data tables. Uses `onMount` + `apiFetch('/admin/players')` consistent with other admin pages. - **`src/routes/(app)/+layout.svelte`** — Added Commerce nav item (`$` icon) between CRM and Teams in the admin bottom nav. - **`src/app.css`** — Added commerce CSS classes (tab-bar, tab-btn, summary-row, summary-stat, data-table, badge-shipped, badge-past-due, badge-canceled) copied from playground `shared/style.css`. Includes mobile-responsive table stacking at 600px. ## Test Plan - Log in as Marcus (admin), verify bottom nav shows: Dashboard | CRM | Commerce | Teams | Schedule | Sign Out - Navigate to Commerce, verify Jerseys tab loads with summary counts matching real player data - Switch to Subscriptions and Contracts tabs, verify correct columns and counts - Test filter buttons on each tab — verify row counts match filter badge counts - Test at 320px viewport width — verify bottom nav does not overflow - `npm run build` passes with no errors ## Review Checklist - [x] `npm run build` passes - [x] No scoped `<style>` blocks — all CSS in global app.css - [x] No Tailwind — pure CSS vars - [x] No new API endpoints — reuses `/admin/players` - [x] Svelte 5 runes (`$state`, `$derived`) consistent with other admin pages - [x] Mobile-responsive data tables with `data-label` attributes - [x] No unrelated changes ## Related - Closes #226 - Upstream: `forgejo_admin/westside-playground#50` (playground prototype) ## Related Notes - `story:WS-S9` — "As an admin, I want to track payment status per player so that I know who owes what." - `sop-capacitor-mobile-lifecycle` — playground-to-Svelte promotion SOP
feat: add Commerce admin page with jersey/subscription/contract tabs
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
d47b74ca5d
Promotes the approved playground prototype (commerce.html) to production
Svelte. Three tabbed views with reactive filtering and summary counts,
all powered by the existing /admin/players API endpoint.

Closes #226

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

PR #228 Review

DOMAIN REVIEW

Tech stack: SvelteKit (Svelte 5 runes), global CSS with CSS custom properties, no Tailwind.

Svelte 5 runes usage -- CORRECT

  • $state for reactive primitives (allPlayers, loading, error, activeTab, filter states) -- matches codebase convention.
  • $derived(() => ...) for filteredJerseyPlayers, filteredSubPlayers, filteredContractPlayers -- returns a function, invoked as filteredJerseyPlayers() in {#each} blocks. This matches the established pattern in admin/players/+page.svelte (line 170) and admin/users/+page.svelte (line 64).
  • $derived(expr) for count aggregations (jerseyPaidCount, etc.) -- direct expression style, also matches codebase convention (admin/schedule/+page.svelte lines 26-27, admin/teams/+page.svelte lines 40-42).
  • onMount + apiFetch pattern is consistent with every other admin page.

CSS conventions -- CORRECT

  • All styles in global app.css, no scoped <style> blocks.
  • Uses CSS custom properties (--space-*, --color-*, --font-size-*, --radius, --transition-fast).
  • Mobile responsive table stacking at 600px with data-label attributes for pseudo-element headers.
  • No Tailwind, no magic pixel values (except min-width: 80px for summary cards and 2px padding in mobile table cells -- minor).

Nav integration -- CORRECT

  • Commerce link added between CRM and Teams in admin bottom nav.
  • Uses class:active={currentPath === '/admin/commerce'} consistent with other nav items.
  • HTML entity &#36; ($) as icon -- consistent with how other nav items use HTML entities.

API contract alignment

  • Fields name, team_name, jersey_order_status, jersey_option, jersey_size, jersey_number, parent_name, parent_phone, subscription_status, contract_status, contract_signed_at -- all confirmed used in existing admin/players/+page.svelte and admin/players/[id]/+page.svelte.
  • Unverified fields: monthly_fee and contract_signed_by are referenced in the commerce page but are NOT used anywhere else in the codebase. If the API does not return these fields, the page will display -- (graceful fallback via || '--'), so this is not a crash risk, but the columns may always show dashes.

BLOCKERS

None.

  • No test coverage: This project has zero test infrastructure (no test files, no test runner configured). This is a pre-existing project-wide gap, not introduced by this PR. Not blocking this PR for it, but flagging the systemic risk.
  • No secrets or credentials committed.
  • No unvalidated user input -- this is a read-only display page.
  • No DRY violation in auth/security paths -- auth is handled by the layout's Keycloak guard.

NITS

  1. DRY: Duplicate badge classes -- The PR adds badge-shipped, badge-past-due, and badge-canceled to app.css. The codebase already has badge-jersey-shipped (line 1384, identical styling to badge-shipped) and could potentially reuse it. Consider whether to consolidate or keep them separate for semantic clarity. Not blocking since the naming is domain-specific ("commerce badges" vs "CRM jersey badges"), but worth noting.

  2. Unverified API fields -- monthly_fee and contract_signed_by are not referenced in any other frontend code. Confirm the API actually returns these fields. If not, the Subscriptions "Monthly Fee" column and Contracts "Signed By" column will permanently show --.

  3. Error message placement -- The {#if error} block is placed AFTER all tab content (line 338 in the new file). If the API call fails, the user sees an empty tab with an empty table, then the error message below. Consider moving the error display above the tab bar, or replacing the tab content entirely when error is set. This matches how other admin pages handle it -- but it is a minor UX concern shared across the codebase.

  4. Mobile nav density -- Adding Commerce brings the admin bottom nav to 6 items (Dashboard, CRM, Commerce, Teams, Schedule, Sign Out). The PR body says to test at 320px. The bottom-nav uses flexbox with no overflow-x or wrapping, so very narrow screens may see cramped labels. Not blocking since the test plan explicitly covers 320px verification.

  5. Minor CSS hardcodes -- padding: 2px var(--space-sm) in the mobile table cell rule and min-width: 80px on .summary-stat use pixel values instead of CSS custom properties. Extremely minor.

SOP COMPLIANCE

  • Branch named after issue: 226-add-commerce-admin-page follows {issue-number}-{kebab-case-purpose} convention
  • PR body follows template: Summary, Changes, Test Plan, Review Checklist, Related sections all present
  • Related references plan: References story:WS-S9 and sop-capacitor-mobile-lifecycle
  • No secrets committed
  • No unrelated file changes (3 files, all directly related to the feature)
  • Commit messages -- single feature commit implied by the PR

PROCESS OBSERVATIONS

  • Change failure risk: Low. Read-only display page, no mutations, no new API endpoints. Worst case is empty data columns if monthly_fee/contract_signed_by fields do not exist in the API response.
  • Deployment frequency: No impact -- standard feature addition.
  • Documentation: PR body is thorough. Test plan covers the right scenarios including mobile viewport.
  • Systemic gap: Zero test infrastructure project-wide. This should be addressed at the platform level, not per-PR.

VERDICT: APPROVED

Clean playground-to-production promotion. Svelte 5 runes usage is correct. CSS follows project conventions. API contract is mostly confirmed with two unverified fields that degrade gracefully. No blockers.

## PR #228 Review ### DOMAIN REVIEW **Tech stack**: SvelteKit (Svelte 5 runes), global CSS with CSS custom properties, no Tailwind. **Svelte 5 runes usage -- CORRECT** - `$state` for reactive primitives (`allPlayers`, `loading`, `error`, `activeTab`, filter states) -- matches codebase convention. - `$derived(() => ...)` for `filteredJerseyPlayers`, `filteredSubPlayers`, `filteredContractPlayers` -- returns a function, invoked as `filteredJerseyPlayers()` in `{#each}` blocks. This matches the established pattern in `admin/players/+page.svelte` (line 170) and `admin/users/+page.svelte` (line 64). - `$derived(expr)` for count aggregations (`jerseyPaidCount`, etc.) -- direct expression style, also matches codebase convention (`admin/schedule/+page.svelte` lines 26-27, `admin/teams/+page.svelte` lines 40-42). - `onMount` + `apiFetch` pattern is consistent with every other admin page. **CSS conventions -- CORRECT** - All styles in global `app.css`, no scoped `<style>` blocks. - Uses CSS custom properties (`--space-*`, `--color-*`, `--font-size-*`, `--radius`, `--transition-fast`). - Mobile responsive table stacking at 600px with `data-label` attributes for pseudo-element headers. - No Tailwind, no magic pixel values (except `min-width: 80px` for summary cards and `2px` padding in mobile table cells -- minor). **Nav integration -- CORRECT** - Commerce link added between CRM and Teams in admin bottom nav. - Uses `class:active={currentPath === '/admin/commerce'}` consistent with other nav items. - HTML entity `&#36;` ($) as icon -- consistent with how other nav items use HTML entities. **API contract alignment** - Fields `name`, `team_name`, `jersey_order_status`, `jersey_option`, `jersey_size`, `jersey_number`, `parent_name`, `parent_phone`, `subscription_status`, `contract_status`, `contract_signed_at` -- all confirmed used in existing `admin/players/+page.svelte` and `admin/players/[id]/+page.svelte`. - **Unverified fields**: `monthly_fee` and `contract_signed_by` are referenced in the commerce page but are NOT used anywhere else in the codebase. If the API does not return these fields, the page will display `--` (graceful fallback via `|| '--'`), so this is not a crash risk, but the columns may always show dashes. ### BLOCKERS None. - **No test coverage**: This project has zero test infrastructure (no test files, no test runner configured). This is a pre-existing project-wide gap, not introduced by this PR. Not blocking this PR for it, but flagging the systemic risk. - **No secrets or credentials committed**. - **No unvalidated user input** -- this is a read-only display page. - **No DRY violation in auth/security paths** -- auth is handled by the layout's Keycloak guard. ### NITS 1. **DRY: Duplicate badge classes** -- The PR adds `badge-shipped`, `badge-past-due`, and `badge-canceled` to `app.css`. The codebase already has `badge-jersey-shipped` (line 1384, identical styling to `badge-shipped`) and could potentially reuse it. Consider whether to consolidate or keep them separate for semantic clarity. Not blocking since the naming is domain-specific ("commerce badges" vs "CRM jersey badges"), but worth noting. 2. **Unverified API fields** -- `monthly_fee` and `contract_signed_by` are not referenced in any other frontend code. Confirm the API actually returns these fields. If not, the Subscriptions "Monthly Fee" column and Contracts "Signed By" column will permanently show `--`. 3. **Error message placement** -- The `{#if error}` block is placed AFTER all tab content (line 338 in the new file). If the API call fails, the user sees an empty tab with an empty table, then the error message below. Consider moving the error display above the tab bar, or replacing the tab content entirely when error is set. This matches how other admin pages handle it -- but it is a minor UX concern shared across the codebase. 4. **Mobile nav density** -- Adding Commerce brings the admin bottom nav to 6 items (Dashboard, CRM, Commerce, Teams, Schedule, Sign Out). The PR body says to test at 320px. The `bottom-nav` uses flexbox with no `overflow-x` or wrapping, so very narrow screens may see cramped labels. Not blocking since the test plan explicitly covers 320px verification. 5. **Minor CSS hardcodes** -- `padding: 2px var(--space-sm)` in the mobile table cell rule and `min-width: 80px` on `.summary-stat` use pixel values instead of CSS custom properties. Extremely minor. ### SOP COMPLIANCE - [x] Branch named after issue: `226-add-commerce-admin-page` follows `{issue-number}-{kebab-case-purpose}` convention - [x] PR body follows template: Summary, Changes, Test Plan, Review Checklist, Related sections all present - [x] Related references plan: References `story:WS-S9` and `sop-capacitor-mobile-lifecycle` - [x] No secrets committed - [x] No unrelated file changes (3 files, all directly related to the feature) - [x] Commit messages -- single feature commit implied by the PR ### PROCESS OBSERVATIONS - **Change failure risk**: Low. Read-only display page, no mutations, no new API endpoints. Worst case is empty data columns if `monthly_fee`/`contract_signed_by` fields do not exist in the API response. - **Deployment frequency**: No impact -- standard feature addition. - **Documentation**: PR body is thorough. Test plan covers the right scenarios including mobile viewport. - **Systemic gap**: Zero test infrastructure project-wide. This should be addressed at the platform level, not per-PR. ### VERDICT: APPROVED Clean playground-to-production promotion. Svelte 5 runes usage is correct. CSS follows project conventions. API contract is mostly confirmed with two unverified fields that degrade gracefully. No blockers.
forgejo_admin deleted branch 226-add-commerce-admin-page 2026-04-06 23:35:01 +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
ldraney/westside-app!228
No description provided.