feat: contract signing UI + login redirect fix #40
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-app!40
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "39-contract-flow-ui"
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
Add the Program Contract signing flow UI and fix the post-login redirect.
Closes #39
Changes
src/routes/players/[id]/+page.svelte— contract terms (7 sections), checkbox, digital signature, sign button. Status transitions offered→active on sign. Mock data for player ID 999.src/routes/my-players/+page.svelte— "Team Offer" card with yellow badge and "Review & Sign Contract" CTA when player status isoffered. Mock data via?mock=offeredquery param.src/app.css—.badge-offered,.contract-offer-card,.contract-card,.contract-terms,.contract-sign,.contract-successstyles.src/lib/keycloak.js— fix login redirect from hardcoded/my-playersto/(home page handles role-based redirect viagetRoleRedirectPath()).Test Plan
/my-players?mock=offered— see Team Offer card with CTA/players/999with full contract/admin(not/my-players)/coachReview Checklist
getRoleRedirectPath()functionRelated
plan-wkq→ Phase 14 (Billing Tiers & Contracts)phase-wkq-15-production-port— SPA rebuild contextwestside-contract-offer-card.png,westside-contract-signing-page.png,westside-contract-signed-success.pngPR #40 Review
DOMAIN REVIEW
Tech stack: SvelteKit (Svelte 5 runes), CSS (global app.css with design tokens), Keycloak-js auth.
4 files changed, 184 additions, 23 deletions.
Login redirect fix (
src/lib/keycloak.js): Correct. The hardcodedredirectUrito/my-playersis replaced with/. The home page (src/routes/+page.svelte) already handles authenticated users by callinggetRoleRedirectPath(), which routes admin to/admin, coach to/coach, and player to/my-players. This properly delegates redirect logic to the existing function instead of hardcoding a single role's destination.Contract offer card (
src/routes/my-players/+page.svelte): Clean implementation. Players withstatus === 'offered'get a prominent yellow-bordered CTA card instead of the regular card row. The{#if}/{:else}branching is well-structured. Mock data is gated behind?mock=offeredquery param, clearly marked with a comment.Contract signing page (
src/routes/players/[id]/+page.svelte): Full 7-section contract with checkbox acceptance + typed digital signature. State management is clean --contractAccepted,contractSignature,contractEnabled(derived),contractSubmitting,contractSigned. ThehandleSignContract()function properly guards withcontractEnabledandcontractSubmittingchecks, POSTs to/players/{id}/contractwith JSON body, and gracefully falls through to success on API error (intentional for pre-API demo phase).CSS (
src/app.css): All new styles use design tokens consistently (--color-yellow-*,--space-*,--radius-*). No magic numbers. Yellow theme tokens are pre-existing. New classes follow the established naming convention (contract-card,contract-terms,contract-sign,contract-success).Accessibility: Checkbox has proper
id="contract-check"/for="contract-check"pairing. Signature input has a visible label. Button hasdisabledbinding. Contract terms area hasmax-height: 400px; overflow-y: autofor scrollability on small screens.BLOCKERS
None.
Test coverage: No test framework is configured in this project (
package.jsonhas no vitest, playwright, or jest dependency). Zero tests exist across the entire codebase. This PR cannot be held to a standard the project does not yet have. The PR body documents a manual Test Plan with 6 verification steps and notes Playwright screenshot validation. Not blocking, but the project will need a test framework eventually.Security: No secrets committed. Signature data is sent as a JSON field via
JSON.stringify, not interpolated into HTML or URLs. TheapiFetchhelper properly attaches Bearer tokens. No XSS or injection vectors.NITS
Unused import:
{ page }is imported from$app/storesinsrc/routes/my-players/+page.svelte(line 3) but never used. The mock query param is read viawindow.location.searchdirectly. Remove the import or use$page.url.searchParamsinstead.Catch-swallows-error in
handleSignContract(): The catch block at line 130 silently succeeds on API failure (contractSigned = true; player.status = 'active'). This is documented as intentional for demo phase, but the comment should include a TODO or link to the issue that will wire the real endpoint, so it doesn't survive into production.Hardcoded mock ID: Player ID
999is used as the mock trigger in+page.svelte(line 46:if (id === '999')). If the API ever assigns ID 999 to a real player, the mock would intercept. Consider using a non-numeric sentinel or environment-gated flag instead. Low risk now, but worth a TODO.Repeated
$200/monthtext: The dollar amount appears in the contract terms (section 1), the checkbox label, and the success message. When billing tiers become dynamic (Phase 14 scope), these will need to be driven by data. Not blocking for a UI-first phase, but noting for traceability.badge-offeredduplicatesbadge-pendingstyling: Both classes at lines 283 and 288 ofapp.csshave identicalbackground,color, andbordervalues. Consider whether these should share a base class or if the duplication is intentional (semantic separation for future divergence).SOP COMPLIANCE
39-contract-flow-uireferences issue #39plan-wkqPhase 14feat: contract signing UI + login redirect fixPROCESS OBSERVATIONS
/->getRoleRedirectPath()) is well-established. All contract UI is additive and gated behindstatus === 'offered'.VERDICT: APPROVED