/** Stage 11 views: the 8-hypothesis Root Cause Board (auto + human * assessments, evidence links that jump to stage findings) and the decision * tree with the live path highlighted. */ import { useState } from 'react'; import Box from '@mui/material/Box'; import Chip from '@mui/material/Chip'; import IconButton from '@mui/material/IconButton'; import MenuItem from '@mui/material/MenuItem'; import Select from '@mui/material/Select'; import TextField from '@mui/material/TextField'; import Tooltip from '@mui/material/Tooltip'; import Typography from '@mui/material/Typography'; import { ChevronDown, ChevronRight, Link2 } from 'lucide-react'; import type { Confidence, DecisionTree, EvidenceLink, RootCause, Scoreboard } from '../../types/rca'; import { BORDER, CONFIDENCE_COLORS, Explainer, SectionCard, SEVERITY_COLORS, tableSx } from './common'; function ConfidenceChip({ c }: { c: Confidence | null }) { if (!c) return —; return ( ); } function EvidenceList({ items, sign, onJump }: { items: EvidenceLink[]; sign: '+' | '−'; onJump: (stage: string) => void; }) { if (!items.length) return none; return ( {items.map((e) => ( {sign}{e.weight.toFixed(1)} } label={e.title} onClick={() => onJump(e.stage)} sx={{ height: 19, fontSize: 10.5, maxWidth: 340, bgcolor: '#232a31', color: e.severity === 'CRITICAL' ? SEVERITY_COLORS.CRITICAL : '#cfd8e0', '.MuiChip-icon': { color: '#8a949e', ml: 0.5 }, }} /> ))} ); } export function ScoreBoardView({ board, onJumpToStage, onAssess }: { board: Scoreboard; onJumpToStage: (stageKey: string) => void; onAssess: (hypothesis: RootCause, confidence: Confidence, note: string) => void; }) { const [open, setOpen] = useState>({ [board.rows[0]?.hypothesis]: true }); const [notes, setNotes] = useState>({}); return ( <> #HypothesisScore Auto confidenceYour assessmentNext discriminating test {board.rows.map((r) => ( <> setOpen((o) => ({ ...o, [r.hypothesis]: !o[r.hypothesis] }))}> {open[r.hypothesis] ? : } {r.rank} {r.hypothesis} {r.label} 0 ? '#a5d6a7' : '#8a949e' }}> {r.score.toFixed(1)} {r.human_confidence ? : null} {r.next_discriminating_test} {open[r.hypothesis] ? ( EVIDENCE FOR EVIDENCE AGAINST YOUR NOTE setNotes((n) => ({ ...n, [r.hypothesis]: e.target.value }))} onBlur={(e) => { if (r.human_confidence && e.target.value !== r.human_note) { onAssess(r.hypothesis, r.human_confidence, e.target.value); } }} sx={{ '.MuiInputBase-input': { fontSize: 11.5 } }} /> ) : null} ))} ); } // --------------------------------------------------------------- decision tree const ANSWER_COLORS = { yes: '#66bb6a', no: '#ef5350', unknown: '#ffb74d' }; export function DecisionTreeView({ tree }: { tree: DecisionTree }) { const onPath = new Set(tree.path); const terminal = tree.path[tree.path.length - 1]; return ( <> {tree.nodes.map((n, i) => { const active = onPath.has(n.id); const isTerminal = n.id === terminal; const stoppedHere = isTerminal && (n.answer === 'no' || n.id === 'significant'); return ( {i < tree.nodes.length - 1 ? ( ) : null} {n.question} {n.basis.map((b) => ( {b} ))} {n.answer === 'no' && n.conclusion_if_no && active ? ( ) : null} ); })} CHAIN CONCLUSION {tree.conclusion_kind === 'insufficient_evidence' ? `INSUFFICIENT EVIDENCE (leaning ${tree.conclusion})` : tree.conclusion ?? '—'} ); }