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
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
# Phase 5 & 6 — Usability, Compliance Hardening, Analytics
|
||||
|
||||
## Context
|
||||
|
||||
Phases 0–4 complete. Gap audit against PRD (`docs/01_PRD_HSE_Incident_Management_System.md`) found 10 specced features missing or partial. User approved all four gap groups: core usability, compliance hardening, evidence UX, and analytics. Split into **Phase 5** (usability + compliance + evidence) and **Phase 6** (analytics/AI), matching the existing phase-plan convention in `docs/superpowers/plans/`.
|
||||
|
||||
Verified current state (Explore audit, 2026-07-11):
|
||||
- `notifications_log` table exists (`supabase/migrations/20260709000006_notifications_audit.sql`) — no UI
|
||||
- `app/(protected)/admin/page.tsx` is a stub
|
||||
- `app/api/incidents/[id]/route.ts` has no PATCH; no closure lock
|
||||
- `app/api/incidents/[id]/jkkp-pdf/route.ts` supports jkkp6/jkkp7 only; `lib/incidents/dosh.ts:45` already computes `requires_jkkp8`
|
||||
- `components/incidents/report-form.tsx` branches only on `injury_involved`
|
||||
- Investigation schema has `alcohol_test_result`, `witness_statement_refs` (`migrations/...004:27-28`) — no UI
|
||||
- `app/(protected)/hse/incidents/page.tsx:18` hard `.limit(100)`, no pagination
|
||||
- `lib/supabase/storage.ts` stores SHA-256 `file_hash`; no thumbnails
|
||||
- Dashboard has type/site breakdown + zone heatmap; no time-series, no root-cause trend
|
||||
|
||||
## Conventions to follow (established, do not deviate)
|
||||
|
||||
- Every DB mutation writes `audit_log`; RLS at DB level for all new tables/columns
|
||||
- AI routes: `claude-opus-4-8`, `thinking: {type:"adaptive"}`, forced `tool_choice`, validate tool output, 503 on model failure; `getApiKey(supabase, key)` (`lib/settings.ts`) before client construction (`lib/claude/client.ts`)
|
||||
- Notifications: reuse `lib/notifications/email.ts`, `lib/notifications/whatsapp.ts`
|
||||
- i18n: add strings to `lib/i18n/locales.ts` (EN/MS/ZH)
|
||||
- Client fetches prefixed `/ims` basePath
|
||||
- TDD per repo practice; tests in `tests/` mirroring lib paths (61 passing currently)
|
||||
|
||||
---
|
||||
|
||||
## Phase 5 — Usability & Compliance Completion
|
||||
|
||||
### Task 1: In-app notification bell + badge
|
||||
- Migration: extend `notifications_log` if needed (add `read_at TIMESTAMPTZ`, index on recipient+read)
|
||||
- `app/api/notifications/route.ts` — GET unread list (RLS: own rows), POST mark-read
|
||||
- `components/notifications/bell.tsx` — badge count, dropdown list; mount in `app/(protected)/layout.tsx`
|
||||
- Write in-app rows at the same points email/WhatsApp fire (new incident, CAPA assign/escalate, verification)
|
||||
|
||||
### Task 2: Admin user + site/zone management
|
||||
- Replace stub `app/(protected)/admin/page.tsx`
|
||||
- User list: invite (Supabase admin API server-side), role assign, deactivate — admin-only RLS + server-side role check via `lib/auth/roles.ts`
|
||||
- Site/zone CRUD against `sites`/`zones` tables (migration 001)
|
||||
- All mutations audit-logged
|
||||
|
||||
### Task 3: Incident closure lock + addenda
|
||||
- Migration: `incident_addenda` table (incident_id, author, body, created_at) with RLS
|
||||
- Add PATCH guard in `app/api/incidents/[id]/route.ts` and triage/investigation/capa routes: reject mutation when `closed_at IS NOT NULL` (DB trigger preferred — belt and braces)
|
||||
- Addenda UI on `components/incidents/incident-detail.tsx` for closed incidents
|
||||
|
||||
### Task 4: JKKP 8 annual register export
|
||||
- `lib/pdf/jkkp8.ts` (or Excel via existing export route pattern `app/api/dashboard/export/route.ts`) — annual register of all reportable incidents for a chosen year
|
||||
- `app/api/reports/jkkp8/route.ts` — HSE/admin only; reuse `lib/incidents/dosh.ts` flags
|
||||
- Download button on HSE dashboard; reminder banner in January while register unsubmitted
|
||||
|
||||
### Task 5: Type-specific intake forms
|
||||
- Extend `components/incidents/report-form.tsx`: conditional field groups per type — environmental (spill volume/substance/containment), MHE/asset (equipment id, LOTO applied), security, fire; near-miss stays minimal by design
|
||||
- Store in existing JSONB detail column if present, else migration adds one
|
||||
- Validate in `lib/incidents/validate.ts`; translate new labels
|
||||
|
||||
### Task 6: Witness statement + alcohol test UI
|
||||
- Add fields to `components/incidents/investigation-form.tsx` wired to existing `alcohol_test_result`, `witness_statement_refs` columns
|
||||
- Witness statement file uploads reuse `components/incidents/file-upload.tsx` with stage tag
|
||||
|
||||
### Task 7: Incident list pagination
|
||||
- Server-side range pagination in `app/(protected)/hse/incidents/page.tsx` + `components/incidents/incident-list.tsx` (searchParams page/pageSize, `.range()`, count)
|
||||
- Same for supervisor incident list
|
||||
|
||||
### Task 8: Evidence thumbnails
|
||||
- Supabase Storage image transform (`getPublicUrl` with `transform: {width}`) for image types in `components/incidents/evidence-gallery.tsx`; icon fallback for video/pdf/doc
|
||||
- No new infra if Supabase transform available on plan; else client-side `next/image` sizing of signed URL
|
||||
|
||||
### Task 9: Housekeeping
|
||||
- Delete stray duplicates `lib/notifications/whatsapp 2.ts`, `tests/lib/notifications/whatsapp.test 2.ts` (verify identical/stale vs canonical first)
|
||||
|
||||
---
|
||||
|
||||
## Phase 6 — Analytics & Predictive Safety
|
||||
|
||||
### Task 1: Dashboard time-series
|
||||
- Extend `app/api/dashboard/stats/route.ts`: monthly incident counts by type (12 mo), leading vs lagging trend, CAPA on-time trend
|
||||
- Chart components on `app/(protected)/hse/dashboard/page.tsx` (lightweight — no heavy chart lib unless one already present)
|
||||
|
||||
### Task 2: Top root cause trended
|
||||
- Aggregate `root_cause_category` (phase-2 RCA data) by month; top-5 table + trend on dashboard
|
||||
|
||||
### Task 3: AI rising-risk heatmap
|
||||
- `app/api/dashboard/ai/risk-flags/route.ts` — server-side Claude call per AI-route conventions; input: 90d near-miss + incident + zone aggregates; output: flagged zones/shifts with rationale (forced tool_use schema)
|
||||
- Panel on management + HSE dashboards, clearly labelled as AI suggestion; log suggestion to `audit_log` per convention
|
||||
|
||||
---
|
||||
|
||||
## Verification
|
||||
|
||||
- `npm test` — all existing 61 tests plus new unit tests per task (escalation-style pattern in `tests/lib/`)
|
||||
- Manual: run app, walk lifecycle — report (each type) → triage → investigate (witness/alcohol) → CAPA → verify → close → confirm lock + addenda; bell badge increments; admin creates user + zone; JKKP 8 downloads; pagination past 100 rows (seed if needed)
|
||||
- `graphify update .` after code changes
|
||||
- Update `docs/superpowers/plans/` with phase-5/6 plan docs and memory `phases.md` on completion
|
||||
|
||||
---
|
||||
|
||||
## Completion Record (2026-07-12)
|
||||
|
||||
All Phase 5 and Phase 6 tasks implemented on branch `phase-5-6`. 112 tests passing (23 files), `tsc --noEmit` clean, `next build` clean.
|
||||
|
||||
Notable deviations/additions vs plan:
|
||||
- Incident **close endpoint did not exist at all** — added `POST /api/incidents/[id]/close` (verification status + all CAPAs verified required) alongside the lock.
|
||||
- In-app notification inserts go through `create_in_app_notification` SECURITY DEFINER RPC (reporters are not covered by the elevated INSERT policy).
|
||||
- Fixed 9 pre-existing missing `/ims` basePath prefixes across fetch calls and download links (similar-incidents-panel, investigation-form ×3, triage-form ×2, verify-form, JKKP PDF links ×2, dashboard/management export links).
|
||||
- New migrations: 20260712000016_in_app_notifications, 20260712000017_closure_lock_addenda, 20260712000018_type_details.
|
||||
Reference in New Issue
Block a user