This commit is contained in:
+55
-2
@@ -54,6 +54,7 @@ export interface OperationRow {
|
||||
id: string;
|
||||
sequence: number;
|
||||
name: string;
|
||||
kind: string;
|
||||
machineId: string | null;
|
||||
machineName: string | null;
|
||||
templateId: string | null;
|
||||
@@ -89,6 +90,7 @@ const STATUS_TONE: Record<string, "slate" | "blue" | "green" | "amber" | "red">
|
||||
in_progress: "blue",
|
||||
partial: "amber",
|
||||
completed: "green",
|
||||
qc_failed: "red",
|
||||
};
|
||||
|
||||
const STATUS_LABEL: Record<string, string> = {
|
||||
@@ -96,6 +98,7 @@ const STATUS_LABEL: Record<string, string> = {
|
||||
in_progress: "In progress",
|
||||
partial: "Partial",
|
||||
completed: "Completed",
|
||||
qc_failed: "QC failed",
|
||||
};
|
||||
|
||||
function formatBytes(n: number) {
|
||||
@@ -295,6 +298,26 @@ function OperationsSection({
|
||||
}
|
||||
}
|
||||
|
||||
async function resetQc(op: OperationRow) {
|
||||
if (
|
||||
!confirm(
|
||||
`Clear QC failure on step ${op.sequence}. ${op.name}? The step will reopen for rework; the failing QC record stays on file.`,
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
setBusyId(op.id);
|
||||
setError(null);
|
||||
try {
|
||||
await apiFetch(`/api/v1/operations/${op.id}/qc-reset`, { method: "POST" });
|
||||
onChange();
|
||||
} catch (err) {
|
||||
setError(err instanceof ApiClientError ? err.message : "Reset failed");
|
||||
} finally {
|
||||
setBusyId(null);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
@@ -322,7 +345,10 @@ function OperationsSection({
|
||||
<tr key={op.id} className="border-b border-slate-100 last:border-0">
|
||||
<td className="px-3 py-3 text-slate-600">{op.sequence}</td>
|
||||
<td className="px-3 py-3">
|
||||
<div className="font-medium">{op.name}</div>
|
||||
<div className="font-medium flex items-center gap-2">
|
||||
<span>{op.name}</span>
|
||||
{op.kind === "qc" ? <Badge tone="blue">QC step</Badge> : null}
|
||||
</div>
|
||||
{op.templateName ? (
|
||||
<div className="text-xs text-slate-500">from {op.templateName}</div>
|
||||
) : null}
|
||||
@@ -376,6 +402,16 @@ function OperationsSection({
|
||||
<Button variant="ghost" size="sm" onClick={() => setEdit(op)} disabled={busyId !== null}>
|
||||
Edit
|
||||
</Button>
|
||||
{op.status === "qc_failed" ? (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => resetQc(op)}
|
||||
disabled={busyId !== null}
|
||||
>
|
||||
Reset QC
|
||||
</Button>
|
||||
) : null}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
@@ -528,6 +564,9 @@ function OperationModal({
|
||||
const editing = !!operation;
|
||||
const [templateId, setTemplateId] = useState(operation?.templateId ?? "");
|
||||
const [name, setName] = useState(operation?.name ?? "");
|
||||
const [kind, setKind] = useState<"work" | "qc">(
|
||||
(operation?.kind as "work" | "qc") ?? "work",
|
||||
);
|
||||
const [machineId, setMachineId] = useState(operation?.machineId ?? "");
|
||||
const [settings, setSettings] = useState(operation?.settings ?? "");
|
||||
const [materialNotes, setMaterialNotes] = useState(operation?.materialNotes ?? "");
|
||||
@@ -563,6 +602,7 @@ function OperationModal({
|
||||
const body = {
|
||||
templateId: templateId || null,
|
||||
name,
|
||||
kind,
|
||||
machineId: machineId || null,
|
||||
settings,
|
||||
materialNotes,
|
||||
@@ -623,6 +663,15 @@ function OperationModal({
|
||||
<Field label="Name" required>
|
||||
<Input value={name} onChange={(e) => setName(e.target.value)} required />
|
||||
</Field>
|
||||
<Field
|
||||
label="Kind"
|
||||
hint='"Work" is a normal production step. "QC" is a dedicated inspection step — close always demands a pass/fail record and unit counts are ignored.'
|
||||
>
|
||||
<Select value={kind} onChange={(e) => setKind(e.target.value as "work" | "qc")}>
|
||||
<option value="work">Work — production step</option>
|
||||
<option value="qc">QC — dedicated inspection</option>
|
||||
</Select>
|
||||
</Field>
|
||||
<Field label="Machine">
|
||||
<Select value={machineId} onChange={(e) => setMachineId(e.target.value)}>
|
||||
<option value="">— none —</option>
|
||||
@@ -669,12 +718,16 @@ function OperationModal({
|
||||
<span>QC check required on close-out</span>
|
||||
</label>
|
||||
{editing && (
|
||||
<Field label="Status">
|
||||
<Field
|
||||
label="Status"
|
||||
hint="Use QC-failed only if you need to block a step out-of-band; the normal path is for the operator's Done/fail to set it."
|
||||
>
|
||||
<Select value={status} onChange={(e) => setStatus(e.target.value)}>
|
||||
<option value="pending">Pending</option>
|
||||
<option value="in_progress">In progress</option>
|
||||
<option value="partial">Partial</option>
|
||||
<option value="completed">Completed</option>
|
||||
<option value="qc_failed">QC failed</option>
|
||||
</Select>
|
||||
</Field>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user