feat: add jersey number picker to checkout page #70
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!70
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "67-jersey-number-picker"
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
Adds a jersey number input field to each non-opt-out card on the
/jerseycheckout page. Numbers are validated (0, 00, 1-99), checked against already-taken numbers in the player's division, and sent to the checkout API. The Order button is disabled until both size and number are filled and valid.Changes
src/routes/jersey/+page.svelte-- added number input state, validation logic, taken-numbers fetch via player division, number field in each non-opt-out card, and number param in checkout POST bodysrc/app.css-- added.jersey-number-*styles matching existing dark theme with red accentsTest Plan
npm run build-- passes cleannpm run check-- no new warnings or errors/jersey?token=..., see number input below size dropdownnumberfieldReview Checklist
Related
plan-wkq-- Phase 11 (Girls Tryout)14dc649(Keycloak console links) 7461d03a63Self-review: PASS
Reviewed the full diff (2 files, +179/-10 lines). Findings:
npm run buildpasses cleannpm run checkpasses with no new warnings+page.svelteandapp.csstouched -- no unrelated changesVALID_NUMBERSset andtakenNumbersfrom API.jersey-number-*classes match existing.jersey-size-*patterns (same font sizes, colors, spacing, CSS vars)player-infofetched in parallel with options/sizes;taken-numbersfetched sequentially after division is known;numbersent in checkout POST bodyplayer-infoendpoint returns non-ok, page still works (just no taken-number warnings). Iftaken-numbersfetch fails, page still works.Dependency note: Requires basketball-api PR #148 (
GET /jersey/player-info) to be merged and deployed for full functionality. Without it, the number input still works but won't show taken-number warnings.PR #70 Review
DOMAIN REVIEW
Tech stack: SvelteKit 5 (runes mode with
$state), vanilla CSS, k8s deployment manifest.SvelteKit / Component Quality
VALID_NUMBERSset correctly covers 0, 00, 1-99. ThehandleNumberInputfunction strips non-digits and enforces max 2 chars before validating -- good defensive approach.validateNumbercorrectly cascades: empty = clear error, invalid format = format error, taken = taken error, otherwise clear. No missed states.initSelections()properly coversselectedNumbersandnumberErrorsalongsideselectedSizesfor all non-opt-out options.isOrderDisabledandhandleSelectguard clauses mirror each other correctly -- both check for empty number and for active error. This prevents submission with invalid data.{playerName}(auto-escaped) -- no XSS risk from{@html}.inputmode="numeric"is the correct mobile keyboard hint for this input type.<label>elements withforattributes matching inputidvalues -- good accessibility.CSS Quality
.jersey-number-*classes follow the existing naming convention (jersey-size-*pattern).--color-black,--color-gray-800,--color-red,--color-danger,--radius,--font-family) -- no magic colors or hardcoded values..jersey-number-labelstyles are an exact duplicate of.jersey-size-label(same font-size, weight, color, text-transform, letter-spacing, margin-bottom). This could be a shared class, but it is a CSS nit, not a logic DRY violation.!importanton.jersey-number-input-errorborder-color is needed to override the:focusborder -- acceptable in this context.k8s Deployment
1d100acto14dc649-- this is a commit SHA tag, which is correct for immutable image references.Potential Issue: Missing
encodeURIComponentondivisionquery paramLine 161:
fetch(`${API_BASE}/jersey/taken-numbers?division=${division}`)The
divisionvalue comes from the API (playerInfo.division) and is a human-readable string (e.g., "Boys 10U", "Girls 12U"). If the value contains spaces or special characters, the URL will be malformed. This should beencodeURIComponent(division).The same pattern exists for
tokenat line 123, buttokenis likely a UUID/hash and was pre-existing code, not introduced by this PR.BLOCKERS
1. No test coverage for new functionality
This PR adds substantial new client-side logic: number validation (format checking, taken-numbers checking), input sanitization (non-digit stripping, length enforcement), conditional UI rendering, and a new API integration (player-info + taken-numbers endpoints). None of this has automated tests.
The repo has no test framework installed (
package.jsonhas no vitest/playwright/jest). The Test Plan in the PR body lists only manual checks. While the absence of a test framework is a pre-existing gap, new validation logic -- especially input validation that gates a financial transaction (jersey checkout) -- meets the BLOCKER criteria: "New functionality with zero test coverage."Recommendation: At minimum, add vitest and unit-test the pure functions:
validateNumber(empty, valid, invalid format, taken number) and the input sanitization inhandleNumberInput(non-digit stripping, length truncation). These are easily testable without DOM mocking. Alternatively, if the team has a documented exception for this repo's test posture, note it explicitly.2. Unencoded
divisionin URL query parameterdivisionis a human-readable string interpolated directly into a URL withoutencodeURIComponent(). A division like "Boys 10U" will produce an invalid URL. This is an input validation issue that will cause runtime failures for divisions with spaces.NITS
CSS DRY:
.jersey-number-labeland.jersey-size-labelhave identical style declarations. Consider extracting a shared.jersey-field-labelclass. Non-blocking since CSS duplication doesn't carry divergence risk.Error role for screen readers: The
<p class="jersey-number-error">element would benefit fromrole="alert"oraria-live="assertive"so screen readers announce validation errors as they appear.Taken-number cache staleness:
takenNumbersis fetched once at page load. If two players on the same team are ordering simultaneously, one could select a number that becomes taken between page load and checkout submission. The API should enforce uniqueness server-side (and likely does), but the client will show a stale "available" state. Consider adding a note in the code comment, or re-fetching on submit. Low risk for current scale.SOP COMPLIANCE
67-jersey-number-pickerreferences issue #67plan-wkqfeat:)PROCESS OBSERVATIONS
VERDICT: NOT APPROVED
Two blockers must be addressed:
validateNumberand the input sanitization logic, or document an explicit team-level exception for this repo's test posture.divisioninencodeURIComponent()at line 161 to prevent malformed URLs for divisions with spaces.