diff --git a/app/(protected)/capa-owner/page.tsx b/app/(protected)/capa-owner/page.tsx index 87e040c..2d2bc2d 100644 --- a/app/(protected)/capa-owner/page.tsx +++ b/app/(protected)/capa-owner/page.tsx @@ -4,6 +4,7 @@ import Link from 'next/link' import { createClient } from '@/lib/supabase/server' import { redirect } from 'next/navigation' import { StatCard } from '@/components/dashboard/stat-card' +import { CapaOwnerActions } from '@/components/capa/capa-owner-actions' const STATUS_LABELS: Record = { open: 'Open', @@ -78,21 +79,24 @@ export default async function CapaOwnerPage() {

{capa.description}

{incRef && ( -

{incRef}

+ + {incRef} + )}
-
+
{STATUS_LABELS[capa.status] ?? capa.status} -

+

{capa.due_date ? `Due ${new Date(capa.due_date as string).toLocaleDateString('en-MY')}` : 'No due date'}

{capa.priority && ( -

+

{(capa.priority as string).toUpperCase()} priority

)} +
diff --git a/app/(protected)/reporter/incidents/[id]/page.tsx b/app/(protected)/reporter/incidents/[id]/page.tsx new file mode 100644 index 0000000..bb4889f --- /dev/null +++ b/app/(protected)/reporter/incidents/[id]/page.tsx @@ -0,0 +1,107 @@ +export const dynamic = 'force-dynamic' + +import Link from 'next/link' +import { createClient } from '@/lib/supabase/server' +import { redirect, notFound } from 'next/navigation' + +const STATUS_LABELS: Record = { + reported: 'Reported', triaged: 'Triaged', investigating: 'Investigating', + capa_pending: 'CAPA Pending', verification: 'Verification', closed: 'Closed', +} +const STATUS_COLORS: Record = { + reported: 'bg-gray-100 text-gray-600', triaged: 'bg-yellow-100 text-yellow-800', + investigating: 'bg-blue-100 text-blue-700', capa_pending: 'bg-orange-100 text-orange-700', + verification: 'bg-purple-100 text-purple-700', closed: 'bg-green-100 text-green-700', +} + +export default async function ReporterIncidentDetail({ + params, +}: { + params: Promise<{ id: string }> +}) { + const { id } = await params + const supabase = await createClient() + const { data: { user } } = await supabase.auth.getUser() + if (!user) redirect('/login') + + const { data: inc } = await supabase + .from('incidents') + .select(` + id, reference_no, incident_type, status, severity, reported_at, closed_at, + description, injury_involved, medical_status, lost_days, + sites (name), zones (name), + capa_actions (id, description, status, due_date) + `) + .eq('id', id) + .eq('reported_by', user.id) + .single() + + if (!inc) notFound() + + const siteName = (inc.sites as unknown as { name: string } | null)?.name + const zoneName = (inc.zones as unknown as { name: string } | null)?.name + const capas = (inc.capa_actions as unknown as Array<{ id: string; description: string; status: string; due_date: string | null }>) ?? [] + + return ( +
+
+ ← My Reports +
+ +
+
+
+

{inc.reference_no ?? inc.id.slice(0, 8)}

+

{inc.incident_type?.replace(/_/g, ' ')}

+
+ + {STATUS_LABELS[inc.status] ?? inc.status} + +
+ +
+ {siteName &&

Site

{siteName}

} + {zoneName &&

Zone

{zoneName}

} + {inc.severity &&

Severity

{inc.severity} / 5

} + {inc.reported_at && ( +
+

Reported

+

{new Date(inc.reported_at as string).toLocaleDateString('en-MY')}

+
+ )} + {inc.closed_at && ( +
+

Closed

+

{new Date(inc.closed_at as string).toLocaleDateString('en-MY')}

+
+ )} +
+ + {inc.description && ( +
+

Description

+

{inc.description}

+
+ )} +
+ + {capas.length > 0 && ( +
+

Corrective Actions ({capas.length})

+
+ {capas.map(c => ( +
+

{c.description}

+ {c.status.replace(/_/g, ' ')} +
+ ))} +
+
+ )} +
+ ) +} diff --git a/app/(protected)/reporter/page.tsx b/app/(protected)/reporter/page.tsx index d7a2e9b..9b174a8 100644 --- a/app/(protected)/reporter/page.tsx +++ b/app/(protected)/reporter/page.tsx @@ -84,7 +84,7 @@ export default async function ReporterPage() { {rows.map(inc => { const siteName = (inc.sites as unknown as { name: string } | null)?.name return ( -
+

@@ -107,7 +107,7 @@ export default async function ReporterPage() { {STATUS_LABELS[inc.status] ?? inc.status}

-
+ ) })}
diff --git a/components/capa/capa-owner-actions.tsx b/components/capa/capa-owner-actions.tsx new file mode 100644 index 0000000..6f4433f --- /dev/null +++ b/components/capa/capa-owner-actions.tsx @@ -0,0 +1,54 @@ +'use client' + +import { useState } from 'react' +import { useRouter } from 'next/navigation' + +interface Props { + capaId: string + currentStatus: string +} + +export function CapaOwnerActions({ capaId, currentStatus }: Props) { + const router = useRouter() + const [loading, setLoading] = useState(false) + + async function updateStatus(status: string) { + setLoading(true) + try { + await fetch(`/ims/api/capa/${capaId}`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ status }), + }) + router.refresh() + } finally { + setLoading(false) + } + } + + if (currentStatus === 'open' || currentStatus === 'reopened') { + return ( + + ) + } + + if (currentStatus === 'in_progress') { + return ( + + ) + } + + return null +} diff --git a/components/dashboard/dashboard-tabs.tsx b/components/dashboard/dashboard-tabs.tsx new file mode 100644 index 0000000..65a9016 --- /dev/null +++ b/components/dashboard/dashboard-tabs.tsx @@ -0,0 +1,42 @@ +'use client' + +import { useRouter, usePathname } from 'next/navigation' +import { Suspense } from 'react' + +const TABS = [ + { key: 'overview', label: 'Overview' }, + { key: 'trends', label: 'Trends' }, + { key: 'zones', label: 'Zones & Types' }, + { key: 'ai', label: 'AI Insights' }, +] + +function TabsInner({ activeTab }: { activeTab: string }) { + const router = useRouter() + const pathname = usePathname() + + return ( +
+ {TABS.map(tab => ( + + ))} +
+ ) +} + +export function DashboardTabs({ activeTab }: { activeTab: string }) { + return ( + }> + + + ) +} diff --git a/components/incidents/incident-filters.tsx b/components/incidents/incident-filters.tsx new file mode 100644 index 0000000..06658f1 --- /dev/null +++ b/components/incidents/incident-filters.tsx @@ -0,0 +1,126 @@ +'use client' + +import { useRouter, useSearchParams, usePathname } from 'next/navigation' +import { useCallback, useState, useTransition, Suspense } from 'react' + +const STATUS_OPTIONS = [ + { value: '', label: 'All Statuses' }, + { value: 'reported', label: 'Reported' }, + { value: 'triaged', label: 'Triaged' }, + { value: 'investigating', label: 'Investigating' }, + { value: 'capa_pending', label: 'CAPA Pending' }, + { value: 'verification', label: 'Pending Verification' }, + { value: 'closed', label: 'Closed' }, +] + +const TYPE_OPTIONS = [ + { value: '', label: 'All Types' }, + { value: 'injury', label: 'Injury' }, + { value: 'near_miss', label: 'Near Miss' }, + { value: 'dangerous_occurrence', label: 'Dangerous Occurrence' }, + { value: 'occupational_disease', label: 'Occupational Disease' }, + { value: 'environmental', label: 'Environmental' }, + { value: 'mhe_asset', label: 'MHE / Asset' }, + { value: 'security', label: 'Security' }, + { value: 'fire', label: 'Fire' }, +] + +export interface SiteOption { id: string; name: string } + +interface FiltersProps { + sites: SiteOption[] +} + +function FiltersInner({ sites }: FiltersProps) { + const router = useRouter() + const pathname = usePathname() + const searchParams = useSearchParams() + const [, startTransition] = useTransition() + const [search, setSearch] = useState(searchParams.get('q') ?? '') + + const updateParam = useCallback((key: string, value: string) => { + const params = new URLSearchParams(searchParams.toString()) + if (value) { + params.set(key, value) + } else { + params.delete(key) + } + params.delete('page') + startTransition(() => { + router.replace(`${pathname}?${params.toString()}`) + }) + }, [router, pathname, searchParams]) + + const handleSearch = useCallback((value: string) => { + setSearch(value) + const timer = setTimeout(() => updateParam('q', value), 300) + return () => clearTimeout(timer) + }, [updateParam]) + + const hasFilters = searchParams.get('q') || searchParams.get('status') || + searchParams.get('type') || searchParams.get('site_id') + + return ( +
+
+ + + + handleSearch(e.target.value)} + className="w-full pl-8 pr-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" + /> +
+ + + {sites.length > 0 && ( + + )} + {hasFilters && ( + + )} +
+ ) +} + +export function IncidentFilters({ sites }: FiltersProps) { + return ( + }> + + + ) +} diff --git a/components/incidents/triage-form.tsx b/components/incidents/triage-form.tsx index 6a8baa4..21be4ee 100644 --- a/components/incidents/triage-form.tsx +++ b/components/incidents/triage-form.tsx @@ -94,16 +94,23 @@ export function TriageForm({ incidentId, currentSeverity }: Props) { return (
- - setSeverity(Number(e.target.value))} - className="w-full accent-blue-600" - /> -
- 1 Minor3 Moderate5 Critical + +
+ {([1, 2, 3, 4, 5] as const).map(v => ( + + ))}
diff --git a/docs/BRD_HSE_IMS.docx b/docs/BRD_HSE_IMS.docx new file mode 100644 index 0000000..94a6583 Binary files /dev/null and b/docs/BRD_HSE_IMS.docx differ diff --git a/docs/BRD_HSE_IMS.md b/docs/BRD_HSE_IMS.md new file mode 100644 index 0000000..f7dc69a --- /dev/null +++ b/docs/BRD_HSE_IMS.md @@ -0,0 +1,338 @@ +# Business Requirement Document + +## Centralized AI-Powered HSE Incident Management & Reporting System + +**Client:** Setia Corporation — 3PL Warehouse Operations +**Prepared by:** Wee Ihan Yap +**Version:** 1.0 +**Date:** 12 July 2026 +**Status:** Final + +--- + +## 1. Executive Summary + +Setia Corporation currently manages Health, Safety and Environment (HSE) incident reporting through a combination of WhatsApp messages, Excel spreadsheets, and paper-based forms. This process creates significant gaps: delayed notifications, incomplete evidence, inconsistent classification, and manual workload for statutory DOSH reporting. + +This document defines the business requirements for a centralized, AI-powered HSE Incident Management System (IMS) that replaces these manual processes. The system captures every incident digitally, runs a structured workflow from report through closure, tracks corrective actions, and generates statutory JKKP reports automatically — eliminating the administrative burden on HSE staff and ensuring zero missed regulatory deadlines. + +--- + +## 2. Business Objectives + +1. Replace WhatsApp/Excel/paper incident reporting with a single digital system capturing all incident types. +2. Enforce a structured, auditable workflow: report → triage → investigation → CAPA → verification → closure. +3. Track all Corrective and Preventive Actions (CAPAs) to completion with automated escalation for overdue items. +4. Provide management with a live dashboard replacing monthly manual report compilation. +5. Ensure full compliance with Malaysian DOSH reporting obligations (NADOPOD 2004) without manual form-filling. +6. Use AI (Claude API) to reduce HSE admin burden: drafting summaries, suggesting root causes, flagging incomplete reports, and detecting risk patterns. + +--- + +## 3. Stakeholders + +| Role | Representative(s) | Responsibility | +|---|---|---| +| Sponsor / Business Owner | Ms. Agnes, Mr. Terence | Approve requirements, budget, go-live sign-off | +| HSE Lead | Mr. Yap | Primary system user; investigation, CAPA, DOSH filing | +| Operations | Mr. Jensen | Supervisor workflow, CAPA ownership | +| Floor Staff / Reporters | All employees, guards | Incident submission via QR scan | +| System Developer | Wee Ihan Yap | Design, build, deployment | + +--- + +## 4. User Roles & Access + +| Role | Who | Access Level | Primary Responsibility | +|---|---|---|---| +| Reporter | Any employee, supervisor, witness, security guard | Submit reports via QR/app; view own reports only | Report incidents and hazards immediately | +| Operation Supervisor | Warehouse/shift supervisor | Full access to incidents in their site/zone | Secure area, first response, initial classification | +| HSE Officer | Safety Assistant / HSE team | Full access across all sites | Investigation, RCA, CAPA assignment, verification, closure, DOSH filing | +| CAPA Owner | Department head (Ops, Maintenance, HR, etc.) | CAPA items assigned to their department only | Complete corrective actions, upload proof | +| Management | Ms. Agnes, Mr. Terence, Mr. Yap, Mr. Jensen | Read-only dashboard across all sites | Review trends, approve budgets, audit readiness | +| System Admin | IT / appointed super-user | Full configuration access | User management, site/zone setup, form configuration | + +Every action must be attributable to a logged-in user — no anonymous edits. + +--- + +## 5. Scope + +### 5.1 In Scope + +- Digital incident capture for all seven incident types (see §6) +- Full incident lifecycle: report → triage → investigation → CAPA → verification → closure +- Evidence management: photos, videos, documents at every lifecycle stage +- CAPA board with auto-escalation and effectiveness re-check +- Email and WhatsApp Business notifications +- In-app notification bell and badge +- DOSH compliance: automated JKKP 6, JKKP 7, JKKP 8 PDF and CSV generation +- AI-assisted features: quality check, severity suggestion, similar incident retrieval, RCA/CAPA drafting, risk heatmap +- Multi-site, multi-zone QR-based reporting +- Multi-language UI (English, Bahasa Malaysia, Mandarin) +- Mobile-first, offline capture with auto-sync on reconnect +- Dashboard: leading/lagging indicators, site/zone heatmap, CAPA on-time rate, trend charts +- Admin panel: user management, site/zone CRUD, role assignment +- Full audit trail (every action logged with user + timestamp) +- 5-year data retention per DOSH requirements + +### 5.2 Out of Scope + +- WMS (Warehouse Management System) integration (optional future phase) +- Tamil language support (deferred unless workforce requires it) +- Third-party EHS platform integration +- External audit portal access + +--- + +## 6. Incident Types + +The system must support all seven incident types, each with a type-specific intake form: + +1. **Injury / Medical Treatment Case** — LTI and non-LTI +2. **Near Miss** — fast, low-friction form (near-miss volume is the primary leading safety indicator) +3. **Unsafe Condition / Hazard Observation** — proactive, not tied to an event +4. **Property / Asset / MHE Damage** — forklift, racking, dock equipment +5. **Environmental Incident** — spill, leak, chemical release, waste +6. **Security Incident** — theft, unauthorized access +7. **Fire / Emergency Incident** + +--- + +## 7. Incident Reference Format + +Every incident auto-generates a unique reference number: + +``` +SITE-YYYYMM-#### +``` + +Example: `KL01-202607-0042` + +--- + +## 8. Functional Requirements + +### 8.1 Incident Reporting + +- Reporter scans a site/zone-specific QR code or opens the app +- System pre-fills site and zone from QR token (no manual entry) +- Reporter selects incident type and completes type-specific intake form +- Minimum one photo required for injury reports; video and documents optional +- System auto-generates incident reference number and timestamps report +- Automatic notification fires immediately to the relevant Supervisor and HSE Officer +- Offline capture supported: form data queued in browser (IndexedDB) and synced on reconnect + +### 8.2 Triage & Initial Response + +- Supervisor or HSE Officer confirms or reclassifies incident type +- Assigns severity level (1–5); AI may suggest a level but a human always confirms +- Workflow branches by type: + - Injury: medical/first-aid path, LTI/non-LTI classification, lost-day tracking + - Asset/MHE: emergency shutdown/LOTO, operator-error check, HR/discipline path if applicable + - Environmental: containment steps, spill-kit deployment, environmental authority check + - Near miss / hazard: skip to root-cause and CAPA directly +- System automatically evaluates incident data against NADOPOD 2004 rules and presents the applicable obligation (immediate DOSH notification / JKKP 6 / JKKP 7 / JKKP 8) as a checklist for the HSE Officer + +### 8.3 Investigation & CAPA + +- HSE enters witness statements, alcohol/urine test result (if applicable), evidence +- Structured root-cause analysis via selectable template: 5-Why or Fishbone (not free text only) +- Every CAPA item must record: description, responsible department/owner, due date, priority, and root-cause linkage +- Auto-escalation ladder: + - 3 days before due date: reminder to owner + - On due date: notify owner + - 3 days overdue: notify owner's manager + - 7 days overdue: notify HSE Officer, flag red on dashboard + +### 8.4 Verification & Closure + +- CAPA owner uploads completion evidence (photo/document proof) before marking CAPA as done +- HSE verifies effectiveness; if not effective, CAPA **reopens** (not closed with open gap) +- Once all CAPAs verified, HSE closes incident +- On closure: record locks against further edits; only addenda can be appended +- Closed incident automatically enters JKKP 8 annual register + +### 8.5 Evidence Management + +Evidence must be attachable at every lifecycle stage, not only at initial report: + +| Stage | Expected Evidence | +|---|---| +| Report | Scene photo/video, hazard photo | +| Response | LOTO tag photo, first-aid record, medical referral letter | +| Investigation | Witness statement scans, CCTV export, equipment inspection report, alcohol/urine test result | +| CAPA | Before/after photos, purchase receipts, training attendance sheets, updated SOP | +| Verification | Final confirmation photo/video that corrective action is in place and effective | + +Requirements: +- Accepted formats: JPG, PNG, HEIC, MP4, MOV, PDF, DOCX, XLSX +- Max file size: configurable (recommended 200 MB for video; compress on upload) +- Every file records: uploader, timestamp, incident ID, stage, immutable SHA-256 file hash +- Files retained minimum 5 years; never auto-deleted +- Thumbnail/preview generation so HSE can review without downloading + +### 8.6 Notifications + +| Channel | Use | +|---|---| +| Email | Formal records: investigation assignment, CAPA assignment, closure notifications | +| WhatsApp Business API | Time-critical alerts: new serious incident, CAPA overdue escalation | +| In-app notification bell | All events for all users; unread badge count; dropdown list | + +### 8.7 Dashboard & Analytics + +- Total incidents, near misses, severity rate, open vs. closed counts +- **Leading vs. lagging indicator split** (near miss/hazard = leading; injury/LTI = lagging) +- Site/zone/shift heatmap — critical for multi-warehouse operations +- CAPA on-time completion rate (%) +- Top incident category and top root cause, trended over 12 months +- DOSH-reportable incident count and filing status (filed / pending / overdue) +- AI rising-risk zone flags: zones with statistically increasing incident frequency +- Export to PDF/Excel for board reporting + +### 8.8 AI-Assisted Capabilities + +All AI outputs are suggestions that a human reviews and approves — never auto-submitted to DOSH and never auto-closed without human sign-off. + +| Capability | Description | Business Value | +|---|---|---| +| Report quality check | Flags incomplete reports before submission (missing photo on injury report, vague description) | Fixes delayed/incomplete information problem | +| Severity/category suggestion | Suggests severity level (1–5) and incident category from free-text description | Speeds triage, reduces classification inconsistency | +| Similar incident retrieval | Surfaces top-5 past incidents with similar description/location/equipment via vector similarity | Reveals recurring hazards; supports trend detection | +| RCA/CAPA drafting assistant | Suggests likely root causes and draft corrective actions from investigation notes | Cuts write-up time, improves CAPA consistency | +| JKKP form auto-fill | Generates JKKP 6/7 PDF drafts and JKKP 8 annual register from stored data | Removes single biggest admin burden | +| Risk heatmap / prediction | Combines near-miss, incident, and hazard data by site/zone/shift to flag rising-risk areas | Predictive safety capability tuned to Setia's own warehouses | + +All AI suggestions logged to audit trail: what was suggested and what the human ultimately chose. + +### 8.9 DOSH Compliance (NADOPOD 2004) + +The system encodes Malaysia-specific statutory reporting rules and automatically determines the applicable obligation: + +| Situation | Obligation | +|---|---| +| Fatality or serious bodily injury (NADOPOD First Schedule: fracture, amputation, loss of sight) | Notify nearest DOSH office immediately; submit JKKP 6 within 7 days | +| Dangerous occurrence (Second Schedule: boiler explosion, structural collapse) regardless of injury | Notify DOSH immediately; submit JKKP 6 within 7 days | +| Other injury causing incapacity for more than 4 consecutive days | Submit JKKP 6 within 7 days | +| Occupational poisoning or disease (Third Schedule) | Submit JKKP 7 within 7 days | +| Any of the above | Also logged in JKKP 8 annual register; retained on-site 5 years; submitted to DOSH before 31 January each year | + +### 8.10 Admin Management + +- User management: invite by email, assign role and site, activate/deactivate +- Site CRUD: name, address, region, active flag +- Zone CRUD: name per site, QR code generation and download per zone +- All admin actions logged to audit trail + +--- + +## 9. Non-Functional Requirements + +| Category | Requirement | +|---|---| +| Mobile-first | Must work on low-end Android phones common on warehouse floors | +| Offline | Incident form submittable offline; data queues locally (IndexedDB) and auto-syncs on reconnect | +| Multi-language | English, Bahasa Malaysia, Mandarin (all three available at all times via language switcher) | +| Multi-site | Site and zone are first-class fields on every record from day one | +| Access control | Role-based access enforced at the database level (Supabase RLS), not only in UI | +| Audit trail | Every create/edit/status-change/file-upload logged with user and timestamp; immutable | +| Data retention | Minimum 5 years per DOSH JKKP 8 requirement; evidence files never hard-deleted | +| Performance | Incident list uses server-side pagination; no unbounded queries | +| Security | API keys server-side only; never exposed to client; RLS on all tables | + +--- + +## 10. System Architecture Summary + +**Stack:** + +| Layer | Technology | +|---|---| +| Frontend + API routes | Next.js 15 (App Router) | +| Database + Auth + Storage | Supabase (Postgres + RLS + pgvector) | +| Hosting | Vercel (frontend) + Supabase cloud | +| AI | Claude API (Anthropic) — server-side only | +| Email | Resend | +| WhatsApp | Meta WhatsApp Business Cloud API | +| PDF generation | pdf-lib (JKKP 6/7 form fill) | +| QR codes | qrcode npm package | + +**Deployment:** Vercel (frontend) + Supabase cloud. Custom domain `hse.setiacorp.com` once MVP validated. All secrets stored as environment variables — never committed to code. + +--- + +## 11. Key Database Entities + +| Entity | Purpose | +|---|---| +| sites | Warehouse locations | +| zones | Named areas within a site, each with a unique QR token | +| users | All system users with role, department, site assignment | +| incidents | Core record: type, site, zone, severity, status, lifecycle timestamps | +| evidence_files | Files attached at each lifecycle stage with immutable hash | +| investigations | RCA method, findings, root cause summary, test results | +| capa_actions | CAPA items with owner, due date, priority, status, effectiveness recheck date | +| dosh_reports | JKKP 6/7/8 records with filing status and generated PDF link | +| notifications_log | All notifications sent across all channels | +| audit_log | Immutable record of every system action | + +--- + +## 12. Development Phases + +| Phase | Scope | Status | +|---|---|---| +| 0 | Foundation: scaffold, auth, DB migrations, QR codes | Complete | +| 1 | Core reporting: incident form, inbox, evidence upload, email notification | Complete | +| 2 | Investigation + CAPA: triage, 5-Why/fishbone, CAPA board, verification, JKKP 6/7 PDF | Complete | +| 3 | AI features + dashboards: Claude integration, pgvector similar incidents, role dashboards, CSV export | Complete | +| 4 | Scale & polish: WhatsApp notifications, i18n EN/MS/ZH, PWA offline capture, CAPA effectiveness recheck | Complete | +| 5 | Usability & compliance: notification bell, closure lock, pagination, type-specific intake, witness/alcohol UI, JKKP 8, admin management, evidence thumbnails | Complete | +| 6 | Analytics & predictive: 12-month trend chart, top root causes trended, AI rising-risk zone flags | Complete | + +--- + +## 13. Success Metrics + +| Metric | Target | +|---|---| +| Digital incident capture rate | 100% within 30 days of launch (zero WhatsApp-only reports) | +| CAPA on-time closure rate | Above 85% within 3 months | +| Incident-to-HSE notification time | Under 2 minutes (vs. current WhatsApp-dependent manual forwarding) | +| Monthly HSE report preparation time | Reduced from days to minutes (auto-generated from dashboard) | +| Missed DOSH statutory reporting deadlines | Zero | + +--- + +## 14. Assumptions & Constraints + +- All warehouse sites have internet access sufficient for mobile web browsing; offline mode covers low-signal periods only. +- Users are expected to have a smartphone (Android minimum); desktop access available for HSE Officers and management. +- Supabase free/starter tier sufficient for initial rollout; video-heavy storage may require upgrade to Supabase Pro or Cloudflare R2 migration at scale. +- `SUPABASE_SERVICE_ROLE_KEY` must be set in production environment for admin user-invite functionality. +- WhatsApp Business API requires approved Meta Business account and message templates before production use. +- VPS cron jobs must be registered for CAPA escalation and effectiveness recheck automation post-deployment. +- Evidence files must never be deleted; storage cost is a known ongoing operational expense. + +--- + +## 15. Glossary + +| Term | Definition | +|---|---| +| CAPA | Corrective and Preventive Action | +| DOSH | Department of Occupational Safety and Health (Malaysia) | +| HSE | Health, Safety and Environment | +| JKKP 6 | Malaysia statutory form — notification of accident/dangerous occurrence | +| JKKP 7 | Malaysia statutory form — notification of occupational poisoning/disease | +| JKKP 8 | Malaysia statutory form — annual register of accidents/occupational diseases | +| LTI | Lost Time Injury (injury resulting in at least one day away from work) | +| LOTO | Lockout/Tagout (energy isolation safety procedure) | +| MHE | Material Handling Equipment (forklifts, pallet jacks, etc.) | +| NADOPOD 2004 | Notification of Accident, Dangerous Occurrence, Occupational Poisoning and Occupational Disease Regulations 2004 | +| pgvector | Postgres extension for vector similarity search (used for similar incident retrieval) | +| QR | Quick Response code (used for site/zone identification on incident report form) | +| RCA | Root Cause Analysis | +| RLS | Row-Level Security (Supabase/Postgres feature enforcing data access at DB level) | +| 3PL | Third-Party Logistics | diff --git a/docs/BRD_HSE_IMS_Formatted.docx b/docs/BRD_HSE_IMS_Formatted.docx new file mode 100644 index 0000000..0c3785e Binary files /dev/null and b/docs/BRD_HSE_IMS_Formatted.docx differ diff --git a/docs/~$D_HSE_IMS_Formatted.docx b/docs/~$D_HSE_IMS_Formatted.docx new file mode 100644 index 0000000..dba0200 Binary files /dev/null and b/docs/~$D_HSE_IMS_Formatted.docx differ