export const dynamic = 'force-dynamic' import Link from 'next/link' import { createClient } from '@/lib/supabase/server' import { redirect } from 'next/navigation' import { StatCard } from '@/components/dashboard/stat-card' 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-700', 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 SupervisorPage() { const supabase = await createClient() const { data: { user } } = await supabase.auth.getUser() if (!user) redirect('/login') const { data: profile } = await supabase .from('users') .select('name, site_id, role') .eq('id', user.id) .single() if (!profile || !['supervisor', 'admin'].includes(profile.role)) redirect('/') if (!profile.site_id) { return (

Supervisor Portal

No site assigned — contact your administrator.

) } const { data: siteRow } = await supabase.from('sites').select('name').eq('id', profile.site_id).single() const siteName = (siteRow as unknown as { name: string } | null)?.name ?? 'Your Site' // Resolve incident IDs once to avoid duplicate queries inside Promise.all const { data: siteIncidents } = await supabase .from('incidents') .select('id') .eq('site_id', profile.site_id) const incidentIds = siteIncidents?.map(r => r.id) ?? [] const [ { data: openIncidents }, { count: closedCount }, { count: openCount }, { data: overdueCapas }, { data: allCapas }, ] = await Promise.all([ supabase .from('incidents') .select('id, reference_no, incident_type, status, reported_at') .eq('site_id', profile.site_id) .neq('status', 'closed') .order('reported_at', { ascending: false }) .limit(10), supabase .from('incidents') .select('*', { count: 'exact', head: true }) .eq('site_id', profile.site_id) .eq('status', 'closed'), supabase .from('incidents') .select('*', { count: 'exact', head: true }) .eq('site_id', profile.site_id) .neq('status', 'closed'), supabase .from('capa_actions') .select('id, description, due_date, users (name)') .eq('status', 'overdue') .in('incident_id', incidentIds), supabase .from('capa_actions') .select('id, status') .in('incident_id', incidentIds), ]) const resolvedOpenCount = openCount ?? 0 const resolvedClosedCount = closedCount ?? 0 const overdueCount = overdueCapas?.length ?? 0 const capaRows = allCapas ?? [] const capaOpenCount = capaRows.filter(c => ['open', 'in_progress'].includes(c.status)).length const capaVerifiedCount = capaRows.filter(c => c.status === 'verified').length const TYPE_LABELS: Record = { injury: 'Injury', near_miss: 'Near Miss', hazard: 'Hazard', asset_damage: 'Asset Damage', environmental: 'Environmental', security: 'Security', fire: 'Fire', } return (

Supervisor Portal

{siteName}

All incidents →
{/* Stats */}
0 ? 'yellow' : 'green'} /> 0 ? 'yellow' : 'green'} /> 0 ? 'red' : 'green'} />
{/* Open incidents */}

Open Incidents

{(openIncidents ?? []).length === 0 ? (

No open incidents.

) : (
{(openIncidents ?? []).map(inc => (

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

{TYPE_LABELS[inc.incident_type] ?? inc.incident_type}

{STATUS_LABELS[inc.status] ?? inc.status} ))}
)}
{/* Overdue CAPAs */} {overdueCount > 0 && (

Overdue CAPAs

{(overdueCapas ?? []).map(capa => (

{capa.description}

Owner: {(capa.users as unknown as { name: string } | null)?.name ?? 'Unassigned'}

Due {capa.due_date ? new Date(capa.due_date as string).toLocaleDateString('en-MY') : '—'}
))}

CAPA verification rate: {capaRows.length > 0 ? Math.round((capaVerifiedCount / capaRows.length) * 100) : 'N/A'}%

)}
) }