import HistoryIcon from "@mui/icons-material/History"; import LockIcon from "@mui/icons-material/Lock"; import LockOpenIcon from "@mui/icons-material/LockOpen"; import PictureAsPdfIcon from "@mui/icons-material/PictureAsPdf"; import { Alert, Box, Button, Card, CardContent, CardHeader, Chip, CircularProgress, Divider, Grid, Stack, Tab, Table, TableBody, TableCell, TableHead, TableRow, Tabs, Typography, } from "@mui/material"; import { useState } from "react"; import { useParams } from "react-router-dom"; import { openBlob } from "../api/client"; import { useMe, useNcr, useNcrAudit } from "../api/hooks"; import type { NcrDetail } from "../api/types"; import { STAGE_LABELS } from "../api/types"; import { AttachmentSection } from "../components/AttachmentSection"; import { FieldRow } from "../components/FieldRow"; import { RichTextView } from "../components/RichTextView"; import { StageChip } from "../components/StageChip"; import { StageStepper } from "../components/StageStepper"; import { useToast } from "../components/Toast"; import { CapaSection } from "./CapaSection"; import { CostingForm, InitialDispositionForm, InspectionForm, OperationsForm, ReopenDialog, SecondaryDispositionForm, } from "./StageForms"; function fmt(iso: string | null): string { return iso ? new Date(iso).toLocaleString() : "—"; } function money(v: string | null): string { return v === null ? "—" : Number(v).toLocaleString(undefined, { style: "currency", currency: "USD" }); } function SectionCard({ title, action, children, }: { title: string; action?: React.ReactNode; children: React.ReactNode; }) { return ( {children} ); } function AuditTab({ ncrId }: { ncrId: number }) { const audit = useNcrAudit(ncrId, true); if (audit.isLoading) return ; if (audit.isError) return {(audit.error as Error).message}; const items = audit.data?.items ?? []; return ( When Who Action Field Before After {items.map((a) => ( {fmt(a.created_at)} {a.user.display_name} {a.detail && ( {a.detail} )} {a.field_name ?? ""} {a.old_value ?? ""} {a.new_value ?? ""} ))}
); } function DetailBody({ ncr }: { ncr: NcrDetail }) { const actions = ncr.available_actions; const closed = ncr.stage === "closed"; return ( <> {closed && ( } severity="info" sx={{ mb: 2 }}> This NCR is closed and locked. Closed on {fmt(ncr.closed_at)} by{" "} {ncr.closed_by?.display_name}. Only an Admin can reopen it. )} {ncr.job_number} {new Date(ncr.created_at).toLocaleDateString()} {ncr.department} {ncr.deviation_category} {ncr.requester.display_name} {ncr.disposition_authority.display_name} {ncr.job_info && ( <> {[ncr.job_info.part_id, ncr.job_info.part_description] .filter(Boolean) .join(" — ")} {ncr.job_info.customer_name} {ncr.job_info.work_order_status} )} Deviation Detail {ncr.deviation_detail} Attachments {ncr.stage === "new_request" && !actions.includes("initial_disposition") ? ( Awaiting initial disposition by {ncr.disposition_authority.display_name}. ) : ( {ncr.qc_authority} {ncr.work_order} {ncr.secondary_review_needed === null ? "—" : ncr.secondary_review_needed ? `Yes — ${ncr.secondary_authorities.map((u) => u.display_name).join(", ") || "unassigned"}` : "No"} {ncr.disposition_notes && ( Disposition Notes )} )} {actions.includes("initial_disposition") && ( <> )} {actions.includes("secondary_disposition") && ( <> )} {ncr.operations_complete ? "Yes" : "Pending"} {ncr.operations_completed_by?.display_name} {fmt(ncr.operations_completed_at)} {actions.includes("operations_complete") && ( <> )} {ncr.qc_approval === null ? "—" : ncr.qc_approval === "yes" ? "Yes" : "No"} {ncr.qc_closed ? `Yes — ${ncr.qc_closed_by?.display_name}, ${fmt(ncr.qc_closed_at)}` : "Pending"} {ncr.inspection_notes && ( Inspection Notes {ncr.inspection_notes} )} {actions.includes("inspection") && ( <> )} {money(ncr.labor_cost)} {money(ncr.material_cost)} {money(ncr.service_cost)} {money(ncr.other_cost)} {money(ncr.total_cost)} {ncr.costing_completed_by ? `${ncr.costing_completed_by.display_name}, ${fmt(ncr.costing_completed_at)}` : null} {actions.includes("costing") && ( <> )} When Action From To By {ncr.transitions.map((t) => ( {fmt(t.acted_at)} {t.action.replace(/_/g, " ")} {t.note && ( {t.note} )} {t.from_stage ? STAGE_LABELS[t.from_stage] : "—"} {STAGE_LABELS[t.to_stage]} {t.acted_by.display_name} ))}
); } export function NcrDetailPage() { // URL carries the NCR business number (e.g. /ncrs/NCR-2026-0021); numeric // ids from older links still resolve server-side. const { id } = useParams(); const ncrQuery = useNcr(id); const me = useMe(); const { toast } = useToast(); const [tab, setTab] = useState(0); const [reopenOpen, setReopenOpen] = useState(false); if (ncrQuery.isLoading) { return ( ); } if (ncrQuery.isError || !ncrQuery.data) { return ( {(ncrQuery.error as Error | undefined)?.message ?? "NCR not found."} ); } const ncr = ncrQuery.data; const canAudit = ncr.available_actions.includes("view_audit") || (me.data?.roles.includes("admin") ?? false); return ( {ncr.ncr_number} {ncr.days_in_stage}d in stage {ncr.available_actions.includes("reopen") && ( )} {canAudit ? ( <> setTab(v)} sx={{ mb: 2 }}> } iconPosition="start" label="Audit History" /> {tab === 0 ? ( ) : ( )} ) : ( )} setReopenOpen(false)} /> ); }