feat: data-driven +page.server.ts with config merge logic #36

Closed
opened 2026-04-03 23:51:22 +00:00 by forgejo_admin · 2 comments
Contributor

Type

Feature

Lineage

Sub-ticket of forgejo_admin/westside-contracts#34 — data-driven contract rendering system. Wave 2 (depends on basketball-api migrations).

Repo

forgejo_admin/westside-contracts

User Story

As the contract page, I need to load structured contract configuration from the database and merge team defaults with player overrides so that components can render from data instead of hardcoded HTML.

Context

The current +page.server.ts runs a simple query joining players/teams/parents and derives isLocal/isGirls from the team name. After the basketball-api migrations add teams.contract_config and players.contract_overrides JSONB columns, this file needs to load both, merge them (team base + player overrides), and pass a structured config object to the page.

Merge rules:

  • monthly_fee: player column → team config default → 200
  • tournaments: if override has tournament IDs array, filter team tournaments to only those IDs
  • practices: override replaces entirely
  • payments: override replaces entirely
  • sections: shallow merge (override wins per key)
  • note: from override (player-level custom note)
  • If contract_config is NULL: return null config, page falls back to hardcoded rendering

File Targets

  • src/routes/contract/[token]/+page.server.ts — update query to SELECT contract_config and contract_overrides, add merge logic
  • src/lib/types.ts — add ContractConfig, ContractOverrides, MergedConfig TypeScript interfaces

Files NOT to touch:

  • src/lib/db.ts, src/lib/minio.ts, src/lib/validation.ts
  • src/routes/contract/[token]/sign/+server.ts — separate ticket
  • src/app.css

Acceptance Criteria

  • SQL query SELECTs t.contract_config and p.contract_overrides
  • Merge function correctly combines team config with player overrides
  • When contract_config is NULL, config is null (triggers fallback rendering)
  • When contract_overrides is NULL, team defaults pass through unchanged
  • Tournament filtering works: override ["mesa-az", "nike-vegas"] returns only those 2 tournaments from team's 5
  • Practice override replaces team practices entirely
  • Section flags merge: override {roster_flexibility: false} disables that section while keeping others
  • TypeScript types are correct and npm run check passes

Test Expectations

  • Unit test: mergeContractConfig with full team config, no overrides → returns team config
  • Unit test: mergeContractConfig with tournament filter → returns filtered tournaments
  • Unit test: mergeContractConfig with practice override → replaces practices
  • Unit test: mergeContractConfig with null team config → returns null
  • Unit test: mergeContractConfig with section overrides → shallow merge
  • Run command: npm test && npm run check

Constraints

  • Merge function should be a pure function in a separate file (e.g. src/lib/contract-config.ts) for testability
  • Must be backwards compatible — NULL contract_config means hardcoded fallback
  • TypeScript interfaces must match the JSONB schema defined in the spec

Checklist

  • PR opened
  • Tests pass
  • No unrelated changes
  • westside-basketball — parent project
  • forgejo_admin/westside-contracts#34 — parent issue
  • Depends on: basketball-api #319 (contract_config), basketball-api #321 (contract_overrides)
### Type Feature ### Lineage Sub-ticket of `forgejo_admin/westside-contracts#34` — data-driven contract rendering system. Wave 2 (depends on basketball-api migrations). ### Repo `forgejo_admin/westside-contracts` ### User Story As the contract page, I need to load structured contract configuration from the database and merge team defaults with player overrides so that components can render from data instead of hardcoded HTML. ### Context The current +page.server.ts runs a simple query joining players/teams/parents and derives `isLocal`/`isGirls` from the team name. After the basketball-api migrations add `teams.contract_config` and `players.contract_overrides` JSONB columns, this file needs to load both, merge them (team base + player overrides), and pass a structured config object to the page. Merge rules: - `monthly_fee`: player column → team config default → 200 - `tournaments`: if override has tournament IDs array, filter team tournaments to only those IDs - `practices`: override replaces entirely - `payments`: override replaces entirely - `sections`: shallow merge (override wins per key) - `note`: from override (player-level custom note) - If `contract_config` is NULL: return null config, page falls back to hardcoded rendering ### File Targets - `src/routes/contract/[token]/+page.server.ts` — update query to SELECT contract_config and contract_overrides, add merge logic - `src/lib/types.ts` — add ContractConfig, ContractOverrides, MergedConfig TypeScript interfaces Files NOT to touch: - `src/lib/db.ts`, `src/lib/minio.ts`, `src/lib/validation.ts` - `src/routes/contract/[token]/sign/+server.ts` — separate ticket - `src/app.css` ### Acceptance Criteria - [ ] SQL query SELECTs `t.contract_config` and `p.contract_overrides` - [ ] Merge function correctly combines team config with player overrides - [ ] When contract_config is NULL, `config` is null (triggers fallback rendering) - [ ] When contract_overrides is NULL, team defaults pass through unchanged - [ ] Tournament filtering works: override `["mesa-az", "nike-vegas"]` returns only those 2 tournaments from team's 5 - [ ] Practice override replaces team practices entirely - [ ] Section flags merge: override `{roster_flexibility: false}` disables that section while keeping others - [ ] TypeScript types are correct and `npm run check` passes ### Test Expectations - [ ] Unit test: mergeContractConfig with full team config, no overrides → returns team config - [ ] Unit test: mergeContractConfig with tournament filter → returns filtered tournaments - [ ] Unit test: mergeContractConfig with practice override → replaces practices - [ ] Unit test: mergeContractConfig with null team config → returns null - [ ] Unit test: mergeContractConfig with section overrides → shallow merge - Run command: `npm test && npm run check` ### Constraints - Merge function should be a pure function in a separate file (e.g. `src/lib/contract-config.ts`) for testability - Must be backwards compatible — NULL contract_config means hardcoded fallback - TypeScript interfaces must match the JSONB schema defined in the spec ### Checklist - [ ] PR opened - [ ] Tests pass - [ ] No unrelated changes ### Related - `westside-basketball` — parent project - `forgejo_admin/westside-contracts#34` — parent issue - Depends on: basketball-api #319 (contract_config), basketball-api #321 (contract_overrides)
Author
Contributor

Scope Review: READY

Review note: review-776-2026-04-03
Scope is solid — all template sections present, traceability complete (story:WS-S23 verified on project page, arch note exists), both file targets verified in codebase, dependencies documented and wave-ordered correctly. No decomposition needed.

## Scope Review: READY Review note: `review-776-2026-04-03` Scope is solid — all template sections present, traceability complete (story:WS-S23 verified on project page, arch note exists), both file targets verified in codebase, dependencies documented and wave-ordered correctly. No decomposition needed.
Author
Contributor

Agent picked up this ticket.

Agent picked up this ticket.
Sign in to join this conversation.
No labels
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-contracts#36
No description provided.