feat: localStorage CRUD (view/add/edit) + Dokploy Railpack deploy #1

Merged
adrianbonpin merged 2 commits from feat/localstorage-crud into prod 2026-09-16 11:00:54 +00:00
2 Commits
Author SHA1 Message Date
adrianbonpin ee0335345d chore: configure Dokploy Railpack deployment
Railpack's Node provider builds this Astro project as a static site with zero
config (bun install -> bun run build -> Caddy serving dist), verified with
`railpack plan`:

- railpack.json pins provider=node explicitly, since the repo has both a
  package.json and a public/ dir, and sets TZ=UTC
- Staticfile sets root=dist and index_fallback=false. Railpack's default SPA
  fallback appends /index.html to try_files, which answers unknown URLs with the
  homepage and HTTP 200; disabling it yields a real 404 for this multi-page site
- src/pages/404.astro gives the static build a dist/404.html to serve
- build script now runs `astro check && astro build`, so a type error fails the
  deploy instead of shipping

Dokploy side: build type Railpack, container port 80 (the generated Caddyfile
listens on {$PORT:80}), and GET /health returns 200 for healthchecks.
2026-09-16 19:00:18 +08:00
adrianbonpin a21c76f029 feat: localStorage-backed student CRUD with zod schemas
Split the app into the three pages the navbar promises, each with one job:

- / (home)   -> read-only roster, no mutation controls
- /add       -> create only, redirects to / on success
- /edit      -> update + delete only (picker, plus /edit?id=<uuid> deep links)

Data layer (src/lib/students.ts) is the single source of truth:
- zod studentDraftSchema validates form input (trims name, lowercases email,
  coerces yearLevel to an int 1-5, restricts course to a known list)
- studentSchema = draft + id + createdAt, so types come from z.infer
- versioned key it3206.students.v1, validated on read *and* write; corrupt or
  missing data degrades to an empty list instead of throwing
- typed CRUD helpers; useSyncExternalStore hook with a stable snapshot cache,
  a window event and the storage event so every island and other tabs re-render

Footer "clear local storage" is now wired to clearStudents() (confirm dialog,
label feedback) instead of being inert, and the layout switched to min-h-svh +
an in-flow footer so it no longer overlaps long lists. Footer padding clears the
Astro dev toolbar, which was covering the link in dev.

Also adds zod, plus @astrojs/check + typescript@^6 as dev deps so astro check
runs (TS 7 does not expose the API astro check needs yet).
2026-09-16 19:00:18 +08:00