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
}
variant="outlined"
onClick={() =>
void openBlob(`/api/ncrs/${ncr.id}/pdf`, `${ncr.ncr_number}.pdf`, "open").catch(
(e) => toast(e.message, "error"),
)
}
>
Print / Download PDF
{ncr.available_actions.includes("reopen") && (
}
variant="outlined"
color="warning"
onClick={() => setReopenOpen(true)}
>
Reopen
)}
{canAudit ? (
<>
setTab(v)} sx={{ mb: 2 }}>
} iconPosition="start" label="Audit History" />
{tab === 0 ? (
) : (
)}
>
) : (
)}
setReopenOpen(false)} />
);
}