Files
fdbck/package.json
mAi 1ab6ef7f22 feat(questions): date_ranked_choice module — closes the validation gap
Registry slot 7/7. The big one. Closes the server-side required-validation
gap the audit (§3.A) flagged: legacy submit/+server.ts only matched empty
strings and empty arrays, so a date_ranked_choice answer of `{}` (object
exists, but no options rated) passed the gate even when the question was
required. The client-side validator caught it; the server didn't.

THE source of truth for the gap is now `isAnswerEmpty(q, answer)`:

  - undefined / null → empty
  - empty object → empty (this is the case the legacy gate missed)
  - object where every value is null → empty
  - object with at least one finite integer 1..5 → not empty
  - out-of-range / non-integer / wrong-shape → empty

After the wiring step (commit 11) flips submit/+server.ts to call
`getQuestion(q.type).isAnswerEmpty(q, answer)`, the gap closes by
construction — one rule, two callers, no drift.

Files added:

- date_ranked_choice.ts — schema (extends base with options[2..50] of
  {id, start, end?, label?}, optional scale.{min,max}_label, optional
  allow_partial), defaultStub (two slots starting at the next hour
  + 24h), isAnswerEmpty (the gap closer above), emptyStats with
  per-option OptStatsWip accumulators, ingest (per-option counting,
  range-checking, _sum), finalise (mean = _sum/count, sort by mean
  desc with tiebreaks on 5-count / 4-count / count / id), CSV (one
  column per option, header format <qid>[<optId>]), adminCellSummary
  ("X avg (N rated)").
- date_ranked_choice.input.svelte — per-option row with date display,
  1..5 rating buttons, skip button. Same markup the participant page
  renders today.
- date_ranked_choice.builder.svelte — date/time options list with add /
  remove, scale labels (rating-1 / rating-5 captions), allow_partial
  toggle. Includes the renamed `setDateRankedScaleLabel` from the
  papercut commit. Date-handling helpers (isoToLocalInput,
  localInputToIso, defaultStartIso, optUid) live here.
- date_ranked_choice.results.svelte — full calendar + bars view with
  view-toggle (Kalender / Balken). All helper logic — buildCalendar,
  cellTitle, colorForRating, colorForMean, fmtTimeRange, fmtDateOption,
  fmtMean, mixHex — is now per-module instead of in Results.svelte.
- date_ranked_choice.test.ts — 22 cases covering schema (duplicate ids,
  fewer than 2 options, malformed ISO, disallowed id chars), the seven
  isAnswerEmpty rules above, ingest+finalise (sort, _sum drop, range
  filtering, missing-answer handling), CSV (one column per option,
  cell extraction, wrong-shape passthrough), adminCellSummary.

123 server tests pass (was 101). svelte-check + bun run build clean.

All seven types now in QUESTION_MODULES. The wiring step (next commit)
flips the legacy callers — schemas.ts assembles its discriminated union
from the registry, FormBuilder mounts BuilderEditor by type, participant
page mounts ParticipantInput, Results.svelte mounts ResultsBlock, the
submit endpoint calls isAnswerEmpty per type, the export endpoint calls
csvColumns + csvCellFor per type. After that, the legacy `q.type === '...'`
strips disappear.
2026-05-07 20:22:52 +02:00

36 lines
1.3 KiB
JSON

{
"name": "fdbck",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"start": "node build/index.js",
"test:server": "bun test ./src/lib/server/rate-limit.test.ts ./src/lib/server/public-scope.test.ts ./src/lib/server/results.test.ts ./src/lib/server/admin-route.test.ts ./src/lib/server/feedback-pure.test.ts ./src/lib/questions/registry.test.ts ./src/lib/questions/boolean.test.ts ./src/lib/questions/text.test.ts ./src/lib/questions/scale.test.ts ./src/lib/questions/choice.test.ts ./src/lib/questions/date_ranked_choice.test.ts",
"test:components": "bun --bun vitest run --config vitest.config.ts",
"test": "bun run test:server && bun run test:components"
},
"devDependencies": {
"@sveltejs/adapter-node": "^5.5.4",
"@sveltejs/kit": "^2.15.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/svelte": "^5.3.1",
"@types/bun": "^1.3.13",
"jsdom": "^29.1.1",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"typescript": "^5.0.0",
"vite": "^6.0.0",
"vitest": "^4.1.5"
},
"dependencies": {
"@supabase/supabase-js": "^2.104.1",
"postgres": "^3.4.9",
"zod": "^4.3.6"
}
}