Files
ims/docs/superpowers/plans/2026-07-11-phase-5-6-usability-compliance-analytics.md
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

108 lines
7.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 5 & 6 — Usability, Compliance Hardening, Analytics
## Context
Phases 04 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.