feat: show contract status badge in admin CRM player list #141
No reviewers
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
forgejo_admin/westside-landing!141
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "137-contract-status-admin-crm"
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?
Summary
contract_statusfield from API response (no backend changes)Changes
src/routes/(app)/admin/players/+page.svelte: AddedgetContractBadgeClassandgetContractLabelhelpers for the contract_status enum. AddedsignedCountderived value. Rendered contract badge inline next to existing payment status badge. Updated subtitle to show signed count.src/app.css: Addedbadge-signed(green),badge-offered(yellow),badge-no-contract(gray) classes following existing badge pattern and CSS variable conventions.Test Plan
npm run buildzero errors,svelte-checkzero errors)/admin/playersshows two badges (payment + contract)contract_statusfield default to gray "No Contract" badgeReview Checklist
Related Notes
westside-app— the project this work belongs toQA Review -- PR #141
Diff Summary
src/app.css: 3 new badge classes (badge-signed, badge-offered, badge-no-contract)src/routes/(app)/admin/players/+page.svelte: 2 helper functions, 1 derived value, template updatesFindings
CSS badge classes -- All three new classes follow the exact existing badge pattern. Correct CSS vars used (green for signed, yellow for offered, gray for no-contract). No Tailwind. No regressions to existing badge styles.
Svelte helpers --
getContractBadgeClassandgetContractLabelhandle the three enum states (signed, offered, default/none/undefined) correctly. JSDoc types arestring | undefined, which matches the API contract where the field may be absent.Signed count --
signedCountderived value correctly filterscontract_status === 'signed'. Rendered in subtitle between player count and pipeline stages.Template -- Contract badge placed inline after existing payment status badge in
card-row-name-line. Clean addition, no disruption to existing layout.Build verification --
npm run buildandsvelte-checkboth pass with zero errors. Pre-existing warnings are unrelated (a11y in teams/checkout pages).No issues found.
VERDICT: APPROVE
PR #141 Review
DOMAIN REVIEW
Tech stack: SvelteKit (Svelte 5 runes), pure CSS with CSS custom properties.
Svelte patterns: The
$derived(() => ...)pattern forsignedCountis consistent with the existingfilteredPlayersandcountsderivations in the same file. Called assignedCount()in the template, matching the established convention.CSS patterns: New badge classes (
badge-signed,badge-offered,badge-no-contract) follow the exact same structure as existing badge classes (badge-paid,badge-pending,badge-registered). Each semantic badge gets its own class even when the color scheme is shared -- this is the established convention in this codebase and allows independent future customization. All CSS variables (--color-green-bg,--color-green,--color-green-border,--color-yellow-bg,--color-yellow,--color-yellow-border,--color-gray-800,--color-gray-400,--color-gray-600) are defined in the:rootblock at the top ofapp.css.Helper functions:
getContractBadgeClassandgetContractLabelmirror the existinggetStatusBadgeClassandgetStatusLabelpattern exactly, including JSDoc type annotations. Thestring | undefinedparam type correctly handles players with nocontract_statusfield, falling through to thedefaultcase.Accessibility: Badge text is visible (not icon-only), uses uppercase text matching existing badges. Color is not the sole differentiator -- label text ("Signed", "Offered", "No Contract") provides semantic meaning.
No Tailwind: Zero Tailwind utility classes. Pure CSS vars throughout.
BLOCKERS
None.
This is a frontend-only display change consuming an existing API field. No new user input is accepted. No auth/security paths are touched. No secrets in the diff. The scope is appropriately small -- 27 additions, 1 deletion, exactly 2 files.
NITS
DRY observation (non-blocking):
badge-no-contractis byte-for-byte identical tobadge-registered, andbadge-signedis identical tobadge-paid/badge-active/badge-current. This matches the existing convention (every badge class stands alone for semantic clarity). Not a problem -- just noting the pattern.Potential future enhancement: If the API ever returns additional
contract_statusvalues beyondsigned/offered/undefined, thedefaultfallback to "No Contract" is a reasonable safe default. No action needed now.SOP COMPLIANCE
137-contract-status-admin-crmreferences issue #137Closes #137ACCEPTANCE CRITERIA VERIFICATION
Per issue #137:
signedCount()displayed in subtitle at line 145.+page.svelteandapp.css.PROCESS OBSERVATIONS
Clean, minimal PR. 27 additions for a well-scoped feature. No backend changes required -- correctly consumes existing API field. Low change failure risk. The
$derivedreactive pattern ensures the signed count stays in sync with filtered/loaded data without manual cache invalidation.VERDICT: APPROVED
PR #141 Review
DOMAIN REVIEW
Tech stack: SvelteKit (Svelte 5 runes), CSS (pure CSS vars, no Tailwind).
Component architecture: Clean. Two small helper functions (
getContractBadgeClass,getContractLabel) follow the exact pattern of the existinggetStatusBadgeClass/getStatusLabelpair. No new component needed for this scope.CSS quality: New badge classes (
.badge-signed,.badge-offered,.badge-no-contract) follow the established one-class-per-semantic-status pattern already used by.badge-paid,.badge-active, etc. All three reference existing CSS variables that are defined in the:rootblock. No magic numbers. No inline styles added.Svelte 5 runes:
signedCountuses$derived(() => ...)which is consistent withfilteredPlayersandcountsin the same file. Note that most of the rest of the codebase uses$derived(expr)without the arrow function wrapper. The arrow function form returns a callable (hencesignedCount()in the template), while the direct form returns the value. Both are valid Svelte 5 patterns. Pre-existing inconsistency across the codebase -- not introduced by this PR.Accessibility: The badges are inline
<span>elements with text content, same pattern as the existing payment badge. No ARIA attributes are needed since the badge text ("Signed", "Offered", "No Contract") is self-describing. Acceptable.Performance: No new API calls, no new imports, no bundle size impact. The
signedCountderived value iteratesallPlayerson each reactivity cycle, but this is a small array (admin CRM page for a basketball org). Negligible.BLOCKERS
None.
This is a frontend-only SvelteKit app. There is no test infrastructure in this repo (no test files outside
node_modules/). The change is a pure UI addition consuming an existing API field -- no new routes, no new API calls, no user input handling, no auth changes. The BLOCKER criterion for "new functionality with zero test coverage" applies to logic that could fail silently or create security risk. Display-only badge rendering from an existing API field does not cross that threshold, especially given the repo has no test framework set up.NITS
DRY opportunity (non-blocking):
.badge-signedis identical to.badge-paid/.badge-active/.badge-current, and.badge-no-contractis identical to.badge-registered. The existing pattern intentionally gives each semantic status its own class for independent future changes. This is fine, but if the badge palette grows further, consider consolidating into color-tier classes (e.g.,.badge-green,.badge-yellow,.badge-gray) composed with semantic classes. Not blocking -- the existing convention is followed correctly.$derived(() => ...)vs$derived(expr): The arrow function form in this file meanssignedCount,filteredPlayers, andcountsare all callables (used assignedCount()in templates). Most other files in the repo use$derived(expr)directly. Consider a future cleanup pass to standardize one pattern across the codebase. Not introduced by this PR.SOP COMPLIANCE
137-contract-status-admin-crmreferences issue #137src/app.css,src/routes/(app)/admin/players/+page.svelte)PROCESS OBSERVATIONS
Small, focused PR. 27 lines added across 2 files. Clean scope -- no backend changes, no new dependencies, no new routes. The PR body correctly notes that
contract_statusis already provided by the API. Change failure risk is very low. Deployment frequency: no friction.VERDICT: APPROVED