refactor(repairs): one status vocabulary, and the supplier scope input

This commit is contained in:
Dave 2026-11-06 08:00:14 +02:00
parent c4936613e1
commit 283d970cb0
2 changed files with 54 additions and 53 deletions

View file

@ -48,31 +48,12 @@ import {
documentStageStyle,
type DocumentStageTone,
} from "@/lib/repair-document-stage";
import {
REPAIR_STATUS_LABEL,
REPAIR_STATUS_VARIANT,
} from "@/lib/repair-status";
import type { Customer, Supplier, Repair } from "@/lib/types";
const statusColors: Record<
string,
"default" | "secondary" | "destructive" | "outline"
> = {
new: "outline",
assessing: "secondary",
quoted: "secondary",
approved: "default",
in_progress: "secondary",
completed: "default",
cancelled: "destructive",
};
const statusLabels: Record<string, string> = {
new: "New",
assessing: "Sent to supplier",
quoted: "Quote received",
approved: "Approved",
in_progress: "In progress",
completed: "Completed",
cancelled: "Cancelled",
};
const ALL_STATUSES = [
"new",
"assessing",
@ -123,6 +104,7 @@ export default function RepairDetailPage({
serial_no: "",
item_description: "",
fault_description: "",
supplier_scope: "",
notes: "",
status: "new" as (typeof ALL_STATUSES)[number],
});
@ -188,6 +170,7 @@ export default function RepairDetailPage({
serial_no: data.serial_no ?? "",
item_description: data.item_description ?? "",
fault_description: data.fault_description ?? "",
supplier_scope: data.supplier_scope ?? "",
notes: data.notes ?? "",
status: (data.status ?? "new") as (typeof ALL_STATUSES)[number],
});
@ -234,6 +217,7 @@ export default function RepairDetailPage({
serial_no: edit.serial_no || null,
item_description: edit.item_description,
fault_description: edit.fault_description,
supplier_scope: edit.supplier_scope,
notes: edit.notes,
status: edit.status,
}),
@ -580,8 +564,8 @@ export default function RepairDetailPage({
<h1 className="text-2xl font-bold tracking-tight">
{repair.repair_number}
</h1>
<Badge variant={statusColors[repair.status] || "secondary"}>
{statusLabels[repair.status] || repair.status}
<Badge variant={REPAIR_STATUS_VARIANT[repair.status] || "secondary"}>
{REPAIR_STATUS_LABEL[repair.status] || repair.status}
</Badge>
</div>
<p className="text-muted-foreground">{repair.item_description}</p>
@ -718,6 +702,19 @@ export default function RepairDetailPage({
}
/>
</div>
<div className="space-y-1.5">
<Label htmlFor="supplier_scope">
What the supplier must look at
</Label>
<Textarea
id="supplier_scope"
placeholder="Strip and quote the strike mechanism"
value={edit.supplier_scope}
onChange={(e) =>
setEdit((p) => ({ ...p, supplier_scope: e.target.value }))
}
/>
</div>
<div className="space-y-1.5">
<Label>Status</Label>
<Select
@ -732,7 +729,7 @@ export default function RepairDetailPage({
<SelectContent>
{ALL_STATUSES.map((s) => (
<SelectItem key={s} value={s}>
{statusLabels[s]}
{REPAIR_STATUS_LABEL[s]}
</SelectItem>
))}
</SelectContent>
@ -763,6 +760,12 @@ export default function RepairDetailPage({
<div className="col-span-2">
<DetailRow label="Fault" value={repair.fault_description} />
</div>
<div className="col-span-2">
<DetailRow
label="Supplier to look at"
value={repair.supplier_scope}
/>
</div>
</div>
)}
</CardContent>

View file

@ -22,6 +22,10 @@ import { CustomerSearch } from "@/components/ui/customer-search";
import { SupplierSearch } from "@/components/ui/supplier-search";
import { OrderSearch } from "@/components/ui/order-search";
import { Camera, X as XIcon } from "lucide-react";
import {
REPAIR_STATUS_LABEL,
REPAIR_STATUS_VARIANT,
} from "@/lib/repair-status";
import type { Customer, Supplier, Repair } from "@/lib/types";
const ALLOWED_PHOTO_TYPES = [
@ -32,29 +36,6 @@ const ALLOWED_PHOTO_TYPES = [
];
const MAX_PHOTO_BYTES = 8 * 1024 * 1024;
const statusColors: Record<
string,
"default" | "secondary" | "destructive" | "outline"
> = {
new: "outline",
assessing: "secondary",
quoted: "secondary",
approved: "default",
in_progress: "secondary",
completed: "default",
cancelled: "destructive",
};
const statusLabels: Record<string, string> = {
new: "New",
assessing: "Sent to supplier",
quoted: "Quote received",
approved: "Approved",
in_progress: "In progress",
completed: "Completed",
cancelled: "Cancelled",
};
type RepairsListItem = Repair & {
expand?: {
customer?: Customer;
@ -103,6 +84,7 @@ export default function RepairsPage() {
serial_no: "",
item_description: "",
fault_description: "",
supplier_scope: "",
notes: "",
});
const [photoPreviews, setPhotoPreviews] = useState<PhotoPreview[]>([]);
@ -204,6 +186,7 @@ export default function RepairsPage() {
serial_no: "",
item_description: "",
fault_description: "",
supplier_scope: "",
notes: "",
});
setPhotoPreviews((prev) => {
@ -232,6 +215,7 @@ export default function RepairsPage() {
serial_no: form.serial_no || null,
item_description: form.item_description.trim(),
fault_description: form.fault_description.trim(),
supplier_scope: form.supplier_scope.trim(),
notes: form.notes.trim(),
}),
});
@ -331,8 +315,8 @@ export default function RepairsPage() {
key: "status",
label: "Status",
render: (item: RepairsListItem) => (
<Badge variant={statusColors[item.status] || "secondary"}>
{statusLabels[item.status] || item.status}
<Badge variant={REPAIR_STATUS_VARIANT[item.status] || "secondary"}>
{REPAIR_STATUS_LABEL[item.status] || item.status}
</Badge>
),
},
@ -406,8 +390,8 @@ export default function RepairsPage() {
</p>
) : null}
</div>
<Badge variant={statusColors[item.status] || "secondary"}>
{statusLabels[item.status] || item.status}
<Badge variant={REPAIR_STATUS_VARIANT[item.status] || "secondary"}>
{REPAIR_STATUS_LABEL[item.status] || item.status}
</Badge>
</div>
<p className="text-sm">{item.item_description}</p>
@ -502,6 +486,20 @@ export default function RepairsPage() {
rows={3}
/>
</div>
<div className="space-y-1.5">
<Label htmlFor="supplier_scope">
What the supplier must look at
</Label>
<Textarea
id="supplier_scope"
placeholder="Strip and quote the strike mechanism"
value={form.supplier_scope}
onChange={(e) =>
setForm((p) => ({ ...p, supplier_scope: e.target.value }))
}
rows={3}
/>
</div>
<div className="space-y-1.5">
<Label>Notes</Label>
<Textarea