Fetches investigation record (5-Why steps, findings, root cause, alcohol test, witness refs) and renders below incident details. Shows completion status and investigator name. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CPf5Rc8QPx6V8KLEEgfKEQ
131 lines
5.0 KiB
TypeScript
131 lines
5.0 KiB
TypeScript
type FiveWhyStep = { why: string; answer: string }
|
|
|
|
interface Investigation {
|
|
id: string
|
|
method: 'five_why' | 'fishbone' | 'other'
|
|
five_why_steps: FiveWhyStep[] | null
|
|
fishbone_categories: Record<string, string> | null
|
|
findings_text: string | null
|
|
root_cause_summary: string | null
|
|
alcohol_test_result: string | null
|
|
witness_statement_refs: string[]
|
|
completed_at: string | null
|
|
investigator: { name: string | null; email: string } | null
|
|
}
|
|
|
|
interface Props {
|
|
investigation: Investigation
|
|
}
|
|
|
|
const METHOD_LABELS = {
|
|
five_why: '5-Why Analysis',
|
|
fishbone: 'Fishbone (Ishikawa)',
|
|
other: 'Other Method',
|
|
}
|
|
|
|
export function InvestigationPanel({ investigation: inv }: Props) {
|
|
const investigatorName = inv.investigator?.name ?? inv.investigator?.email ?? 'Unknown'
|
|
|
|
return (
|
|
<div className="mt-6 bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
|
<div className="flex items-center justify-between px-5 py-4 border-b border-gray-100">
|
|
<h2 className="text-sm font-semibold text-gray-900">Investigation</h2>
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-xs text-gray-500">{METHOD_LABELS[inv.method] ?? inv.method}</span>
|
|
<span className={`text-xs font-medium px-2 py-0.5 rounded-full ${
|
|
inv.completed_at
|
|
? 'bg-green-100 text-green-700'
|
|
: 'bg-yellow-100 text-yellow-700'
|
|
}`}>
|
|
{inv.completed_at ? 'Completed' : 'In Progress'}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="px-5 py-4 space-y-5">
|
|
<p className="text-xs text-gray-500">
|
|
Investigator: <span className="font-medium text-gray-700">{investigatorName}</span>
|
|
{inv.completed_at && (
|
|
<span className="ml-2">
|
|
· Completed {new Date(inv.completed_at).toLocaleDateString('en-MY', { day: 'numeric', month: 'short', year: 'numeric' })}
|
|
</span>
|
|
)}
|
|
</p>
|
|
|
|
{/* 5-Why steps */}
|
|
{inv.method === 'five_why' && inv.five_why_steps && inv.five_why_steps.length > 0 && (
|
|
<div>
|
|
<h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-3">5-Why Steps</h3>
|
|
<ol className="space-y-3">
|
|
{inv.five_why_steps.map((step, i) => (
|
|
<li key={i} className="flex gap-3">
|
|
<span className="flex-shrink-0 w-6 h-6 rounded-full bg-blue-100 text-blue-700 text-xs font-bold flex items-center justify-center">
|
|
{i + 1}
|
|
</span>
|
|
<div className="flex-1 min-w-0">
|
|
<p className="text-xs font-medium text-gray-700">{step.why}</p>
|
|
{step.answer && (
|
|
<p className="text-sm text-gray-900 mt-0.5">{step.answer}</p>
|
|
)}
|
|
</div>
|
|
</li>
|
|
))}
|
|
</ol>
|
|
</div>
|
|
)}
|
|
|
|
{/* Fishbone categories */}
|
|
{inv.method === 'fishbone' && inv.fishbone_categories && Object.keys(inv.fishbone_categories).length > 0 && (
|
|
<div>
|
|
<h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-3">Fishbone Categories</h3>
|
|
<dl className="space-y-2">
|
|
{Object.entries(inv.fishbone_categories).map(([cat, val]) => (
|
|
<div key={cat}>
|
|
<dt className="text-xs font-medium text-gray-500 capitalize">{cat}</dt>
|
|
<dd className="text-sm text-gray-900">{val}</dd>
|
|
</div>
|
|
))}
|
|
</dl>
|
|
</div>
|
|
)}
|
|
|
|
{/* Findings */}
|
|
{inv.findings_text && (
|
|
<div>
|
|
<h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-1">Findings</h3>
|
|
<p className="text-sm text-gray-900 whitespace-pre-wrap">{inv.findings_text}</p>
|
|
</div>
|
|
)}
|
|
|
|
{/* Root cause */}
|
|
{inv.root_cause_summary && (
|
|
<div className="bg-red-50 border border-red-100 rounded-lg px-4 py-3">
|
|
<h3 className="text-xs font-semibold text-red-600 uppercase tracking-wide mb-1">Root Cause</h3>
|
|
<p className="text-sm text-gray-900 whitespace-pre-wrap">{inv.root_cause_summary}</p>
|
|
</div>
|
|
)}
|
|
|
|
{/* Alcohol test */}
|
|
{inv.alcohol_test_result && (
|
|
<div>
|
|
<h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-1">Alcohol Test Result</h3>
|
|
<p className="text-sm text-gray-900">{inv.alcohol_test_result}</p>
|
|
</div>
|
|
)}
|
|
|
|
{/* Witness refs */}
|
|
{inv.witness_statement_refs && inv.witness_statement_refs.length > 0 && (
|
|
<div>
|
|
<h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-1">Witness Statements</h3>
|
|
<ul className="list-disc list-inside space-y-1">
|
|
{inv.witness_statement_refs.map((ref, i) => (
|
|
<li key={i} className="text-sm text-gray-900">{ref}</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|