feat: render custom_notes on contract signing page #20
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "19-render-custom-notes-on-contract"
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
custom_notesas an "Additional Terms" section on the contract signing pageChanges
src/lib/types.ts: addedcustom_notes: string | nulltoPlayerinterfacesrc/routes/contract/[token]/+page.server.ts: addedcustom_notesto the returned player objectsrc/routes/contract/[token]/+page.svelte: conditional "Additional Terms" block after contract body, before signing sectionTest Plan
npm run buildsucceedscustom_notesNULL: no additional section rendered (existing behavior unchanged)custom_notesset: "Additional Terms" heading + content appears between contract body and signing sectionReview Checklist
Related
Review: LGTM
types.ts:
custom_notes: string | nulladded in correct position among contract fields.+page.server.ts:
custom_notesadded to return object.SELECT p.*already fetches it from the DB -- no query change needed.+page.svelte: Conditional
{#if player.custom_notes}block placed between contract body and signing section. Reuses existingcontract-bodyandcontract-contentCSS classes for consistent styling. Whencustom_notesis null/empty, nothing renders -- zero visual change for existing contracts.npm run buildsucceedsDepends on companion basketball-api PR #165 for the migration.
PR #20 Review
DOMAIN REVIEW
Tech stack: SvelteKit (Svelte 5 runes), TypeScript, direct PostgreSQL via
pgpool, adapter-node.XSS safety: The
custom_notesvalue is rendered via Svelte text interpolation ({player.custom_notes}) — NOT{@html ...}. Svelte text interpolation auto-escapes HTML entities. No@htmlusage exists anywhere in this codebase. Safe.SQL safety: The
custom_notesfield is fetched via a parameterized query (WHERE p.contract_token = $1). No injection vector.Data flow:
custom_notesis added to thePlayerinterface, included in the server load return object (explicit allowlist — no accidental field leakage), and conditionally rendered. The field does not flow into any write path or action endpoint.Component architecture: The new block reuses the existing
contract-body/contract-contentwrapper classes, matching the established pattern. Thestyle="display: block;"inline style matches the identical pattern used on lines 170 and 345 of the same file.Accessibility: The
<h3>Additional Terms</h3>heading provides semantic structure consistent with the rest of the contract body. Content is plain text in a<p>tag — screen readers will handle this correctly.BLOCKERS
None.
The "no tests for new functionality" blocker does not apply here. This change adds a conditional read-only text display for an existing database field. There are no new logic paths, no input processing, no state mutations. The existing 12 validation tests continue to pass. Writing a test that mounts the component to verify conditional rendering would require a component test harness (jsdom + Svelte mount) that does not currently exist in this project — adding one would be scope creep for a 12-line display change.
NITS
custom_notes: The field renders inside a single<p>tag. If the database value contains newlines (e.g., multi-paragraph custom terms), HTML will collapse them into a single line. Consider usingwhite-space: pre-lineon the paragraph, or splitting on\nand rendering multiple<p>tags. This is a UX polish item — not blocking since the feature works correctly for single-paragraph content.SOP COMPLIANCE
19-render-custom-notes-on-contractmatches #19)PROCESS OBSERVATIONS
Clean, minimal PR. 12 additions across 3 files with zero scope creep. The explicit field allowlist pattern in
+page.server.ts(lines 29-41) is good practice — it prevents accidental exposure of database columns to the client. The companion PR approach (basketball-api for migration, westside-contracts for rendering) shows proper separation of concerns across repos.VERDICT: APPROVED