Commit Graph
81 Commits
Author SHA1 Message Date
admin 1879def32c security: P0 fixes — IDOR on incident/CAPA, CAPA non-owner write, timing-safe recheck cron, auth callback open redirect 2026-07-12 20:36:28 +08:00
admin dba79c0f17 fix: drop-if-exists before recreating notifications RLS policies 2026-07-12 17:34:44 +08:00
admin c80091c8d6 security: CAPA privilege escalation, CSV injection, AI rate-limit, prompt injection guard, open redirect, timing-safe cron secret, server-only admin client, notifications RLS 2026-07-12 17:33:58 +08:00
admin c0ec6660ef feat: sticky incident header with top-positioned action buttons (fix #5) 2026-07-12 16:44:42 +08:00
admin f6064b9580 feat: reporter clickable rows, CAPA owner action buttons, triage segmented severity 2026-07-12 16:42:49 +08:00
admin 180b0dc0a3 feat: persistent sidebar nav with role-scoped links, logout, and mobile bottom tab bar 2026-07-12 16:40:57 +08:00
admin ba2cf69a8b feat: add cross-section nav links on admin page for admin role 2026-07-12 16:24:05 +08:00
admin 580a60bbd8 fix: admin role bypasses path prefix guard — can access all protected routes 2026-07-12 15:03:56 +08:00
admin 706a075d73 fix: use nextUrl.clone() for middleware redirects to preserve basePath /ims 2026-07-12 14:39:01 +08:00
admin c1c874b3fe fix: exclude /api/cron from auth middleware so cron jobs run unauthenticated 2026-07-12 13:59:54 +08:00
adminandClaude Fable 5 4242af029c fix: code-review findings — closed-incident embedding 503, hardening, dedup
- /api/incidents/[id]/similar: embedding backfill on a closed incident hit
  the closure-lock trigger and turned the whole request into a 503; now
  skips persistence for closed incidents (vector still used for the query)
- addenda: cap body at 5000 chars; include body text in audit_log entry
- admin users PATCH: 404 when target user does not exist (was silent ok)
- extract shared requireAdmin to lib/auth/require-admin.ts (was duplicated
  in admin users + sites routes)
- extract escapeCsv/rowsToCsv to lib/csv.ts (was duplicated in dashboard
  export route and lib/reports/jkkp8.ts)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPf5Rc8QPx6V8KLEEgfKEQ
2026-07-12 12:45:01 +08:00
adminandClaude Fable 5 e2559d1f59 docs: real README, current phase status in CLAUDE.md, complete env example
- README was untouched create-next-app boilerplate
- CLAUDE.md still claimed "Phase 0 not started"; all phases 0-6 complete
- .env.local.example was missing SUPABASE_SERVICE_ROLE_KEY, Resend, and
  Meta WhatsApp variables added in later phases

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPf5Rc8QPx6V8KLEEgfKEQ
2026-07-12 10:46:44 +08:00
adminandClaude Fable 5 81282fbd8a refactor: make generate-qr testable with injectable deps + entry guard
Extracts buildQrUrl and generateZoneQrs with injectable I/O deps and adds
tests/scripts/generate-qr.test.ts (20 tests). Refactor + tests originated
from a concurrent working session; this commit adds on top:
- import.meta entry guard — the unconditional top-level main() executed on
  test import and wrote (sometimes truncated) PNGs into public/qr
- typed vi.fn generics in makeDeps so strict tsc passes

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPf5Rc8QPx6V8KLEEgfKEQ
2026-07-12 10:40:38 +08:00
adminandClaude Fable 5 646c94be0c chore: commit supabase config, phase 2/4 plan docs, extend gitignore
- supabase/config.toml + supabase/.gitignore from supabase init (needed
  for supabase db push / local dev)
- phase 2 and phase 4 implementation plans referenced by the SDD
  progress ledger but never committed
- ignore local tooling dirs (node_modules.nosync, graphify-out, .claude)
  and iCloud "name 2.ext" sync-conflict copies

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPf5Rc8QPx6V8KLEEgfKEQ
2026-07-12 10:30:06 +08:00
adminandClaude Fable 5 f1d426adf1 chore: add idb dependency missed in Phase 4 offline-capture commit
lib/offline/db.ts has imported idb since c048878 but the dependency
was never committed to package.json.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPf5Rc8QPx6V8KLEEgfKEQ
2026-07-12 10:25:21 +08:00
adminandClaude Fable 5 576557181a feat: Phase 5 & 6 — usability, compliance hardening, analytics
Phase 5 (usability + compliance):
- In-app notification bell/badge: migration 016 adds read state + per-user
  RLS + create_in_app_notification SECURITY DEFINER RPC; /api/notifications;
  wired into incident creation, CAPA assign/verify, escalation cron
- Incident closure: new POST /api/incidents/[id]/close (requires verification
  status + all CAPAs verified); migration 017 locks closed incidents at DB
  level (update/delete triggers) with append-only incident_addenda + UI panel
- Server-side pagination on HSE/supervisor incident inboxes (.range, 25/page)
- Investigation form: alcohol/urine test result + witness statement refs
  (existing schema columns, now editable)
- Type-specific intake fields: migration 018 adds incidents.type_details
  JSONB; whitelist validation; environmental/asset/security/fire field
  groups in report form; EN/MS/ZH labels; offline queue support
- JKKP 8 annual register CSV export (/api/reports/jkkp8) + dashboard button
  + January statutory deadline banner
- Admin page: user invite (service-role client), role/site/active management,
  site + zone CRUD with QR report links — replaces Phase 0 stub
- Evidence gallery thumbnails via Supabase render transform with fallback

Phase 6 (analytics):
- 12-month stacked trend chart (leading/lagging/other) + top root causes
  (lib/dashboard/trends.ts pure helpers)
- AI rising-risk zones: /api/dashboard/ai/risk-flags aggregates 90-day
  zone stats, claude-opus-4-8 forced tool_use, panel on HSE + management
  dashboards, suggestion audit-logged

Also fixes 9 pre-existing missing /ims basePath prefixes in client fetches
and download links.

132 tests passing, tsc clean, next build clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPf5Rc8QPx6V8KLEEgfKEQ
2026-07-12 10:25:08 +08:00
admin 98c38c3716 fix: use setUTCDate in getNextRecheckDate to avoid local-timezone off-by-one 2026-07-11 21:49:43 +08:00
adminandClaude Sonnet 4.6 e4d1da033d fix: prefix all client-side fetch calls with /ims basePath
Client components were calling fetch('/api/...') without the /ims prefix,
causing 404s in production where Next.js serves under basePath: '/ims'.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 21:26:18 +08:00
adminandClaude Sonnet 4.6 448ea4857a fix: offline sync basePath prefix and stable syncNow callback
- fetch URL: /api/incidents → /ims/api/incidents (basePath not auto-prepended in client fetch)
- replace syncing state guard with syncingRef to give syncNow a stable reference, preventing useEffect from re-registering the online listener on every sync cycle
- keep syncing state for UI rendering only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 19:11:16 +08:00
admin c048878600 feat: PWA offline capture — IndexedDB queue, service worker cache, auto-sync on reconnect 2026-07-11 19:07:35 +08:00
admin 8c88118b15 chore: update progress ledger — Phase 3 v2 review complete 2026-07-11 19:04:20 +08:00
admin 871ef7c53a fix: rename on-time rate label, hoist getApiKey above escalation loop, remove broken capa-owner incident link 2026-07-11 19:03:23 +08:00
adminandClaude Sonnet 4.6 87264a5497 feat: i18n — EN/MS/ZH translations with cookie-based locale switching, report form translated
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 19:01:44 +08:00
admin c67c489ab3 chore: mark Phase 3 complete in progress ledger 2026-07-11 18:56:10 +08:00
adminandClaude Sonnet 4.6 b9def9c957 feat: CAPA effectiveness re-check cron — 30/60/90-day email + WhatsApp notifications
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 18:55:27 +08:00
admin 76d9363c3c test: RCA draft route test 2026-07-11 18:54:53 +08:00
adminandClaude Sonnet 4.6 edb0bde4c5 fix: restore phase4 migration (recheck_round column + WhatsApp app_settings rows)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 18:51:44 +08:00
adminandClaude Sonnet 4.6 a47f3aabc9 feat: WhatsApp notifications — new incident alert and CAPA overdue escalation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 18:50:25 +08:00
admin 121433ddf8 Revert "feat: WhatsApp notification helper + phase 4 migration (recheck_round, WhatsApp settings)"
This reverts commit b6845f5bb5.
2026-07-11 18:47:08 +08:00
admin dfca872629 feat: CSV export endpoint for HSE and management dashboards 2026-07-11 18:44:52 +08:00
admin b6845f5bb5 feat: WhatsApp notification helper + phase 4 migration (recheck_round, WhatsApp settings) 2026-07-11 18:43:57 +08:00
admin e631d04aba fix: reporter dashboard — guard reported_at null before new Date() 2026-07-11 18:41:43 +08:00
admin 55b6dac646 feat: reporter dashboard — user-scoped incident submissions with status 2026-07-11 18:39:18 +08:00
admin 070e2d1d5d fix: CAPA owner dashboard — priority null guard, nulls-last due_date sort 2026-07-11 18:22:21 +08:00
admin a2f1be7eaf feat: CAPA owner dashboard — user-scoped active and completed CAPAs 2026-07-11 18:19:41 +08:00
adminandClaude Sonnet 4.6 69df45250c fix: supervisor dashboard — dedup incident ID queries, accurate counts, due_date null guard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V5pD9CZzUjknw5nxzX5abr
2026-07-11 18:16:54 +08:00
admin ceb5c7994a feat: supervisor dashboard — site-scoped open incidents, overdue CAPAs 2026-07-11 18:13:26 +08:00
admin 3dac469314 feat: management dashboard — month delta, LTI, severity distribution, site comparison 2026-07-11 18:10:33 +08:00
admin 16e8f9d01b docs: update Phase 3 plan — AI features already done, focus on remaining dashboards 2026-07-11 18:04:10 +08:00
adminandClaude Sonnet 4.6 b9ab94c9da feat: API key settings page — store ANTHROPIC/VOYAGE keys in DB with admin UI
- Migration: app_settings table with admin-only RLS (ANTHROPIC_API_KEY, VOYAGE_API_KEY)
- lib/settings.ts: getApiKey() reads DB first, falls back to env var
- lib/claude/client.ts: factory createAnthropicClient(apiKey) replaces singleton
- lib/claude/embed.ts: optional apiKey param, falls back to env
- 3 Claude AI routes + similar route: fetch key from settings before calling AI
- incidents/route.ts: fire-and-forget embed reads VOYAGE key from settings
- GET/POST /api/settings: admin-only masked key management endpoint
- /hse/settings page + ApiKeyForm client component

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 17:46:40 +08:00
admin a07c9f0910 docs: Phase 3 design spec — AI features + dashboard upgrade 2026-07-11 17:39:18 +08:00
admin 3297198179 chore: log Phase 3 final fixes in progress ledger 2026-07-11 16:49:18 +08:00
adminandClaude Sonnet 4.6 c07bdb77ad fix: RLS guard in match_incidents + try/catch around AI/embed calls
- Add new migration 20260711000013_match_incidents_auth_guard.sql that
  replaces match_incidents with an inline auth guard: callers without
  hse/admin role receive PGRST301 Forbidden, closing the SECURITY
  DEFINER RLS bypass.
- Wrap anthropic.messages.create() in try/catch returning 503 in all
  four AI routes: quality-check, triage-suggest, rca-draft, similar.
- Wrap JSON.parse(inc.embedding) and embedText() in similar/route.ts
  in a shared try/catch returning 503 Embedding service unavailable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 16:48:20 +08:00
admin 28b4951b68 chore: mark Phase 3 complete in progress ledger 2026-07-11 16:41:17 +08:00
adminandClaude Sonnet 4.6 3473cf8ab7 feat: dashboard — leading/lagging, zone heatmap, CAPA on-time rate, DOSH filing status
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 16:38:57 +08:00
adminandClaude Sonnet 4.6 10e71969dd feat: AI RCA draft assistant in investigation form
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 16:35:12 +08:00
adminandClaude Sonnet 4.6 547cc6e817 feat: pgvector similar-incident retrieval + SimilarIncidentsPanel
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 16:31:02 +08:00
adminandClaude Sonnet 4.6 f10fa071f9 feat: AI triage suggestion — severity + DOSH flags pre-fill
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 16:27:29 +08:00
admin a5933d2cd9 fix: guard JSON parse and validate Claude tool output in quality-check route 2026-07-11 16:23:31 +08:00
adminandClaude Sonnet 4.6 3bec95aaf8 feat: AI report quality check before submission
Adds POST /api/incidents/ai/quality-check using Claude tool_use to score incident descriptions 1-10. Report form blocks submission when score < 6, shows amber feedback with suggestions, and offers a 'Submit anyway' override.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 16:18:39 +08:00
admin ab6299f963 feat: pgvector extension + incidents.embedding column + match_incidents RPC 2026-07-11 16:14:32 +08:00
adminandClaude Sonnet 4.6 ebaa98d9f4 feat: Claude client + Voyage AI embed helper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 16:10:26 +08:00
adminandClaude Opus 4.8 0e53d5c771 chore: mark Phase 2 Tasks 3-8 complete in progress ledger
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 15:24:32 +08:00
adminandClaude Opus 4.8 54332d0297 feat: JKKP 6/7 PDF generation via pdf-lib, download from incident detail
Also excludes node_modules.nosync from vitest test discovery to fix pre-existing bleed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 15:24:18 +08:00
adminandClaude Opus 4.8 75fa2605bf feat: CAPA overdue escalation cron — 4 thresholds, once-per-threshold via notifications_log
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 15:20:15 +08:00
adminandClaude Opus 4.8 804b1fbf33 feat: verification flow — HSE verify/reopen CAPA, auto-transition incident to verification
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 15:18:17 +08:00
adminandClaude Opus 4.8 1780586185 feat: CAPA board — kanban/table view, create CAPA from incident, status tracking
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 15:16:49 +08:00
adminandClaude Opus 4.8 63cb28f520 feat: investigation workspace — 5-Why/fishbone RCA, transitions triaged→investigating→capa_pending
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 15:14:27 +08:00
adminandClaude Opus 4.8 e55c5284a7 feat: triage panel — severity + DOSH checklist, transitions reported→triaged
Also excludes node_modules.nosync from tsconfig to fix pre-existing TS type check bleed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FFDuBhMKvoWjrWT3ZnGmmr
2026-07-11 15:12:35 +08:00
admin 8b00c72014 feat: DOSH obligation pure function with NADOPOD 2004 rules 2026-07-11 14:51:35 +08:00
admin a718a2ddbb fix: drop write_audit_log before recreate to handle existing function with different param names 2026-07-11 14:50:03 +08:00
admin dbbb663611 feat: phase2 migration — triage fields, RCA structured data, write_audit_log RPC 2026-07-11 08:35:51 +08:00
adminandClaude Sonnet 4.6 1fe646d82d fix: extract zone fields explicitly to avoid Supabase join type narrowing to never
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWxyMibCuGGtSQSqfajDQ7
2026-07-11 07:59:07 +08:00
adminandClaude Sonnet 4.6 33875395fb fix: cast Supabase join types through unknown for TSC compatibility
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWxyMibCuGGtSQSqfajDQ7
2026-07-11 07:57:00 +08:00
adminandClaude Sonnet 4.6 7d3f8ec757 fix: export Incident types, replace any casts with typed unknown casts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWxyMibCuGGtSQSqfajDQ7
2026-07-11 07:55:26 +08:00
adminandClaude Sonnet 4.6 ed14885253 fix: replace any casts with typed casts for Supabase join results
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWxyMibCuGGtSQSqfajDQ7
2026-07-11 07:52:53 +08:00
adminandClaude Sonnet 4.6 b281420810 feat: HSE dashboard with incident stats by type and site
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWxyMibCuGGtSQSqfajDQ7
2026-07-11 07:40:22 +08:00
adminandClaude Sonnet 4.6 713556631b feat: incident detail page with evidence gallery for HSE and supervisor roles
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWxyMibCuGGtSQSqfajDQ7
2026-07-11 07:40:06 +08:00
adminandClaude Sonnet 4.6 2f2018f27a feat: incident inbox pages with filter/search for HSE and supervisor roles
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWxyMibCuGGtSQSqfajDQ7
2026-07-11 07:39:43 +08:00
admin 2239b6e2df feat: email notifications via Resend on new incident 2026-07-10 13:24:44 +08:00
admin 3f8da5bd91 feat: incident report form, API route, middleware shared-route + redirect 2026-07-10 13:19:45 +08:00
admin a40a0f7c59 fix: signed URLs for private bucket, 10MB server-side limit, safe auth destructuring 2026-07-10 12:00:52 +08:00
admin 8a9758c8a6 feat: Phase 1 foundation - incident components, storage, and migrations 2026-07-10 10:48:56 +08:00
admin 28957f1e0f fix: patch critical auth and RLS security findings from final review 2026-07-10 06:01:36 +08:00
adminandClaude Sonnet 4.6 152a6602d6 feat: add QR code generation script for seeded zones
Adds scripts/generate-qr.ts that generates one 400px PNG per warehouse
zone into public/qr/ (gitignored). Encodes /report?zone=<token> URL per
zone using NEXT_PUBLIC_APP_URL (default http://localhost:3000). Adds
public/qr/.gitkeep so the output directory is tracked. All 8 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWxyMibCuGGtSQSqfajDQ7
2026-07-09 22:36:50 +08:00
adminandClaude Sonnet 4.6 c3ba543ebb feat: add protected role dashboard placeholders
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWxyMibCuGGtSQSqfajDQ7
2026-07-09 22:32:29 +08:00
adminandClaude Sonnet 4.6 559859470b feat: add login page and auth callback route
- Replace default Next.js home with role-aware root redirect
- Add email/password login form (client component, signInWithPassword)
- Add auth callback route for Supabase code exchange
- Login page at /login, callback at /api/auth/callback

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWxyMibCuGGtSQSqfajDQ7
2026-07-09 22:18:21 +08:00
adminandClaude Sonnet 4.6 05daf70177 feat: add database schema migrations and seed data
8 SQL migration files covering all 10 tables, RLS policies with
SECURITY DEFINER helpers, and seed data for SCW1 site + 3 zones.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWxyMibCuGGtSQSqfajDQ7
2026-07-09 21:44:24 +08:00
adminandClaude Sonnet 4.6 6939a21183 feat: add user role types and auth middleware
- lib/auth/roles.ts: UserRole union, ALL_ROLES, ROLE_HOME, getRoleHome, isValidRole (pure, no Supabase imports)
- __tests__/lib/auth/roles.test.ts: 8 TDD tests (written before implementation)
- middleware.ts: createServerClient with request.cookies pattern, unauthenticated redirect to /login, role-based redirect on / and /login

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWxyMibCuGGtSQSqfajDQ7
2026-07-09 21:39:32 +08:00
admin 9b5b21381f chore: fix layout metadata and mark Task 1 complete in ledger 2026-07-09 21:37:23 +08:00
admin f83686fcca chore: scaffold Next.js 15 + Supabase clients + vitest 2026-07-09 21:33:59 +08:00