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).
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.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes the three-page split the navbar promised, with zod-typed localStorage persistence, plus deployment config.
Pages
//add/on success/edit/edit?id=<uuid>deep links from the listData layer —
src/lib/students.tsSingle source of truth: zod
studentDraftSchema(trims name, lowercases email, coercesyearLevelto int 1-5, course limited to a known list) ->studentSchema= draft +id+createdAt-> types viaz.infer. Versioned keyit3206.students.v1, validated on read and write; corrupt JSON degrades to an empty list instead of throwing. Typed CRUD helpers +useSyncExternalStorehook (stable snapshot cache, window event +storageevent) so every island and other tabs re-render live.Footer
clear local storagecallsclearStudents()with a confirm dialog and label feedback. Layout moved tomin-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— pinsprovider: node(repo has bothpackage.jsonandpublic/),TZ=UTCStaticfile—root: dist,index_fallback: false. Railpack's default SPA fallback appends/index.htmlto try_files, which answers unknown URLs with the homepage and HTTP 200; disabling it gives a real 404 for this multi-page sitesrc/pages/404.astro->dist/404.htmlbuildnow runsastro check && astro build, so type errors fail the deployVerified with
railpack plan .: bun install ->bun run build-> Caddy servingdist,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. Addszod+ dev deps@astrojs/check,typescript@^6(TS 7 lacks the APIastro checkneeds).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.