mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-07-27 22:45:03 +00:00
Dropping `<input type="date">` for the dd/mm/yyyy mask also dropped its
calendar, leaving typing as the only way to enter a date. ADR-0019 accepted
that cost but named the remedy: add a picker *behind* `DateInput`, writing the
same ISO value, rather than revert to the native control. That was felt
immediately in use, so this does it.
`DateInput` now renders a calendar button inside the field's right edge and a
Calendar in a Radix popover. Typing and picking are equal routes to the same
value — picking fills the text, typing moves the grid — so neither is
privileged and the two cannot disagree. The trigger is `type="button"` so it
cannot submit the form, and carries an aria-label, a bare icon having no
accessible name.
The calendar wrapper is written by hand rather than taken from the shadcn
registry: the published template targets react-day-picker v8/v9, whose
`classNames` keys differ from the `UI` enum v10 uses. It defaults to the enGB
locale, which carries `weekStartsOn: 1`, so the grid starts on Monday without
being told to.
The delicate part is the `Date` boundary. A calendar works in `Date`s, and
both obvious conversions are wrong: `new Date("2026-03-01")` is midnight UTC,
which is 29 February west of Greenwich, and `toISOString()` shifts the day back
the other way. So `isoToLocalDate` and `localDateToIso` build and read local
parts, and they are the only two functions in `src/utils/dates.ts` that touch
`Date` at all — everything else stays string-only. The round trip is tested
across a full month rather than at a sample day, because the failure only
appears on some days in some zones; the suite was also run under
TZ=America/Los_Angeles and TZ=Pacific/Auckland.
Dependencies: react-day-picker and date-fns, the latter already present
transitively and now direct at v4. `@tremor/react` bundles react-day-picker v8
nested and npm kept it isolated on date-fns v3, so nothing existing changed
version; no Tremor DatePicker is used anywhere in src, so there is no second
copy in the bundle. The alternative was a hand-rolled month grid with no
dependency, rejected because keyboard navigation and ARIA on a calendar are
easy to get subtly wrong and not worth owning.
ADR-0019's "the native calendar picker is gone" consequence was now false and
has been rewritten.
TESTS: tsc --noEmit and ESLint clean; vitest 582 passing (53 files), 24 of them
for the date helpers. NOT verified in a browser — this project has no jsdom or
testing-library, and `next build` was not run because it would statically
render pages against a DATABASE_URL still pointing at production. The new
Cypress case, which picks 17 March from the grid and expects 17/03/2026 in the
field, is unrun like the rest of that spec.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
106 lines
3.7 KiB
JSON
106 lines
3.7 KiB
JSON
{
|
|
"name": "assessment-model",
|
|
"version": "0.1.0",
|
|
"private": true,
|
|
"scripts": {
|
|
"dev": "/workspaces/assessment-model/node_modules/.bin/next dev",
|
|
"build": "next build",
|
|
"start": "next start",
|
|
"lint": "next lint",
|
|
"test": "vitest run",
|
|
"test:watch": "vitest",
|
|
"test:e2e:open": "start-server-and-test dev http://localhost:3000 \"cypress open --e2e\"",
|
|
"test:e2e:run": "cypress run",
|
|
"migration:generate": "drizzle-kit generate",
|
|
"migration:migrate": "drizzle-kit migrate",
|
|
"create_user": "tsx src/app/db/create_user.ts",
|
|
"backfill:recommendation-denormalization": "tsx src/app/db/backfill-recommendation-denormalization.ts",
|
|
"backfill:recommendation-denormalization:pass": "BACKFILL_FINALIZE=false tsx src/app/db/backfill-recommendation-denormalization.ts",
|
|
"db:run-sql": "tsx src/app/db/run-sql.ts",
|
|
"backfill:recommendation-restore": "tsx src/app/db/run-sql.ts src/app/db/sql/rebuild-recommendation-backfill-indexes.sql src/app/db/sql/vacuum-recommendation.sql"
|
|
},
|
|
"dependencies": {
|
|
"@aws-sdk/client-s3": "^3.971.0",
|
|
"@aws-sdk/client-sqs": "^3.864.0",
|
|
"@aws-sdk/s3-request-presigner": "^3.927.0",
|
|
"@dnd-kit/core": "^6.3.1",
|
|
"@dnd-kit/sortable": "^10.0.0",
|
|
"@dnd-kit/utilities": "^3.2.2",
|
|
"@headlessui/react": "^2.2.7",
|
|
"@heroicons/react": "^2.2.0",
|
|
"@hookform/resolvers": "^3.9.1",
|
|
"@hubspot/api-client": "^13.4.0",
|
|
"@radix-ui/react-accordion": "^1.2.12",
|
|
"@radix-ui/react-checkbox": "^1.0.4",
|
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
"@radix-ui/react-dropdown-menu": "^2.0.5",
|
|
"@radix-ui/react-hover-card": "^1.0.6",
|
|
"@radix-ui/react-label": "^2.1.0",
|
|
"@radix-ui/react-navigation-menu": "^1.1.3",
|
|
"@radix-ui/react-popover": "^1.1.15",
|
|
"@radix-ui/react-scroll-area": "^1.2.10",
|
|
"@radix-ui/react-select": "^2.2.6",
|
|
"@radix-ui/react-separator": "^1.0.3",
|
|
"@radix-ui/react-slot": "^1.2.3",
|
|
"@radix-ui/react-tabs": "^1.1.13",
|
|
"@radix-ui/react-toast": "^1.2.2",
|
|
"@radix-ui/react-tooltip": "^1.0.7",
|
|
"@react-google-maps/api": "^2.20.8",
|
|
"@remixicon/react": "^4.2.0",
|
|
"@tanstack/match-sorter-utils": "^8.19.4",
|
|
"@tanstack/react-query": "^4.29.12",
|
|
"@tanstack/react-table": "^8.9.3",
|
|
"@tremor/react": "^3.18.7",
|
|
"@types/node": "20.2.3",
|
|
"@types/react-dom": "18.3.1",
|
|
"@vercel/speed-insights": "^1.2.0",
|
|
"autoprefixer": "10.4.14",
|
|
"aws-sdk": "^2.1415.0",
|
|
"class-variance-authority": "^0.6.1",
|
|
"clsx": "^1.2.1",
|
|
"date-fns": "^4.4.0",
|
|
"drizzle-orm": "^0.44.5",
|
|
"esbuild": "^0.25.8",
|
|
"eslint-config-next": "13.4.3",
|
|
"framer-motion": "^12.23.24",
|
|
"geotiff": "^3.0.5",
|
|
"lucide-react": "^0.233.0",
|
|
"next": "^15.5.7",
|
|
"next-auth": "^4.22.1",
|
|
"next-axiom": "^1.9.2",
|
|
"next-themes": "^0.3.0",
|
|
"nodemailer": "^7.0.11",
|
|
"pg": "^8.11.1",
|
|
"postcss": "^8.5.6",
|
|
"react": "18.3.1",
|
|
"react-confetti": "^6.4.0",
|
|
"react-day-picker": "^10.0.1",
|
|
"react-dom": "18.3.1",
|
|
"react-hook-form": "^7.53.2",
|
|
"recharts": "^3.8.1",
|
|
"tailwind-merge": "^1.13.2",
|
|
"tailwindcss": "^3.4.3",
|
|
"tailwindcss-animate": "^1.0.6",
|
|
"tsx": "^4.20.3",
|
|
"typescript": "5.0.4",
|
|
"vaul": "^1.1.2",
|
|
"xlsx": "^0.18.5",
|
|
"zod": "^3.23.8"
|
|
},
|
|
"devDependencies": {
|
|
"@tailwindcss/forms": "^0.5.10",
|
|
"@testing-library/cypress": "^10.0.3",
|
|
"@types/node": "^24.10.1",
|
|
"@types/nodemailer": "^7.0.2",
|
|
"@types/pg": "^8.10.2",
|
|
"@types/react": "^19.2.7",
|
|
"cypress": "^14.5.3",
|
|
"cypress-social-logins": "^1.14.1",
|
|
"dotenv": "^16.3.1",
|
|
"drizzle-kit": "^0.31.5",
|
|
"eslint": "^8.57.1",
|
|
"prettier": "^3.6.2",
|
|
"start-server-and-test": "^2.0.0",
|
|
"vitest": "^2.1.9"
|
|
}
|
|
}
|