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
Owner

Closes the three-page split the navbar promised, with zod-typed localStorage persistence, plus deployment config.

Pages

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

Data layer — src/lib/students.ts

Single source of truth: zod studentDraftSchema (trims name, lowercases email, coerces yearLevel to int 1-5, course limited to a known list) -> studentSchema = draft + id + createdAt -> types via z.infer. Versioned key it3206.students.v1, validated on read and write; corrupt JSON degrades to an empty list instead of throwing. Typed CRUD helpers + useSyncExternalStore hook (stable snapshot cache, window event + storage event) so every island and other tabs re-render live.

Footer

clear local storage calls clearStudents() with a confirm dialog and label feedback. Layout moved to min-h-svh + in-flow footer so it no longer overlaps long lists, with enough bottom padding to clear the Astro dev toolbar (it was covering the link in dev).

Deploy (Dokploy / Railpack)

  • railpack.json — pins provider: node (repo has both package.json and public/), TZ=UTC
  • Staticfile — root: dist, 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 gives a real 404 for this multi-page site
  • src/pages/404.astro -> dist/404.html
  • build now runs astro check && astro build, so type errors fail the deploy

Verified with railpack plan .: bun install -> bun run build -> Caddy serving dist, try_files {path} {path}.html {path}/index.html (no fallback). Dokploy config: build type Railpack, container port 80 (Caddy listens on {$PORT:80}), GET /health -> 200.

Verification

astro check: 0 errors / 0 warnings. bun run build: 4 pages. Browser-verified on the dev server: create -> redirect, invalid input -> per-field zod messages, edit deep link, update, delete -> redirect, footer clear -> live empty list. Adds zod + dev deps @astrojs/check, typescript@^6 (TS 7 lacks the API astro check needs).

Closes the three-page split the navbar promised, with zod-typed localStorage persistence, plus deployment config. ## Pages | Route | Job | |---|---| | `/` | read-only roster (no create/mutation controls) | | `/add` | create only -> redirects to `/` on success | | `/edit` | update + delete only, picker + `/edit?id=<uuid>` deep links from the list | ## Data layer — `src/lib/students.ts` Single source of truth: zod `studentDraftSchema` (trims name, lowercases email, coerces `yearLevel` to int 1-5, course limited to a known list) -> `studentSchema` = draft + `id` + `createdAt` -> types via `z.infer`. Versioned key `it3206.students.v1`, validated on read **and** write; corrupt JSON degrades to an empty list instead of throwing. Typed CRUD helpers + `useSyncExternalStore` hook (stable snapshot cache, window event + `storage` event) so every island and other tabs re-render live. ## Footer `clear local storage` calls `clearStudents()` with a confirm dialog and label feedback. Layout moved to `min-h-svh` + in-flow footer so it no longer overlaps long lists, with enough bottom padding to clear the Astro dev toolbar (it was covering the link in dev). ## Deploy (Dokploy / Railpack) - `railpack.json` — pins `provider: node` (repo has both `package.json` and `public/`), `TZ=UTC` - `Staticfile` — `root: dist`, `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 gives a real 404 for this multi-page site - `src/pages/404.astro` -> `dist/404.html` - `build` now runs `astro check && astro build`, so type errors fail the deploy Verified with `railpack plan .`: bun install -> `bun run build` -> Caddy serving `dist`, `try_files {path} {path}.html {path}/index.html` (no fallback). Dokploy config: build type Railpack, container port **80** (Caddy listens on `{$PORT:80}`), `GET /health` -> 200. ## Verification `astro check`: 0 errors / 0 warnings. `bun run build`: 4 pages. Browser-verified on the dev server: create -> redirect, invalid input -> per-field zod messages, edit deep link, update, delete -> redirect, footer clear -> live empty list. Adds `zod` + dev deps `@astrojs/check`, `typescript@^6` (TS 7 lacks the API `astro check` needs).
adrianbonpin added 2 commits 2026-09-16 11:00:40 +00:00
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).
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.
adrianbonpin merged commit 8ebd9d7610 into prod 2026-09-16 11:00:54 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: adrianbonpin/it3206-minisys#1