feat: batch material override — apply to all lines in an order at once
- POST /orders/{id}/batch-material-override endpoint
- Dropdown above the lines table: "Apply to all lines…"
- Options: clear all overrides, or select a library material
- Updates all order lines in one request
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,7 @@ import {
|
||||
XCircle, RotateCw, Info,
|
||||
} from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
import { getOrder, submitOrder, deleteOrder, unlinkCadFile, regenerateItemThumbnail, patchOrderItem, removeOrderLine, dispatchRenders, cancelLineRender, dispatchLineRender, cancelOrderRenders, splitMissingStep, generateLinesFromItems, downloadOrderRenders, rejectOrder, resubmitOrder, rejectOrderLine, patchOrderLine } from '../api/orders'
|
||||
import { getOrder, submitOrder, deleteOrder, unlinkCadFile, regenerateItemThumbnail, patchOrderItem, removeOrderLine, dispatchRenders, cancelLineRender, dispatchLineRender, cancelOrderRenders, splitMissingStep, generateLinesFromItems, downloadOrderRenders, rejectOrder, resubmitOrder, rejectOrderLine, patchOrderLine, batchMaterialOverride } from '../api/orders'
|
||||
import { checkOrderMaterials, listMaterials, type UnmappedMaterial, type Material } from '../api/materials'
|
||||
import UnmappedMaterialsDialog from '../components/orders/UnmappedMaterialsDialog'
|
||||
import type { OrderItem, OrderLine } from '../api/orders'
|
||||
@@ -128,6 +128,18 @@ export default function OrderDetailPage() {
|
||||
}
|
||||
}
|
||||
|
||||
const { data: matList } = useQuery({ queryKey: ['materials'], queryFn: listMaterials })
|
||||
const orderLibMats = (matList ?? []).filter((m: Material) => m.schaeffler_code !== null).sort((a: Material, b: Material) => a.name.localeCompare(b.name))
|
||||
|
||||
const batchOverrideMut = useMutation({
|
||||
mutationFn: (val: string | null) => batchMaterialOverride(id!, val),
|
||||
onSuccess: (data) => {
|
||||
toast.success(`Material override ${data.material_override ? 'set' : 'cleared'} on ${data.updated} lines`)
|
||||
qc.invalidateQueries({ queryKey: ['order', id] })
|
||||
},
|
||||
onError: (e: any) => toast.error(e.response?.data?.detail || 'Failed'),
|
||||
})
|
||||
|
||||
const cancelAllMut = useMutation({
|
||||
mutationFn: () => cancelOrderRenders(id!),
|
||||
onSuccess: (data) => {
|
||||
@@ -606,6 +618,30 @@ export default function OrderDetailPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(order.lines?.length ?? 0) > 0 && isPrivileged && (
|
||||
<div className="flex items-center gap-2 mb-2 px-1">
|
||||
<span className="text-xs text-content-muted">Batch material override:</span>
|
||||
<select
|
||||
className="text-xs border border-border-default rounded px-2 py-1"
|
||||
style={{ backgroundColor: 'var(--color-bg-surface)' }}
|
||||
value=""
|
||||
onChange={(e) => {
|
||||
const val = e.target.value
|
||||
if (val === '__clear__') batchOverrideMut.mutate(null)
|
||||
else if (val) batchOverrideMut.mutate(val)
|
||||
}}
|
||||
disabled={batchOverrideMut.isPending}
|
||||
>
|
||||
<option value="">Apply to all lines…</option>
|
||||
<option value="__clear__">— Clear all overrides —</option>
|
||||
{orderLibMats.map((m: Material) => (
|
||||
<option key={m.id} value={m.name}>{m.name.replace('SCHAEFFLER_', '').replace(/_/g, ' ')}</option>
|
||||
))}
|
||||
</select>
|
||||
{batchOverrideMut.isPending && <Loader2 size={12} className="animate-spin text-accent" />}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(order.lines?.length ?? 0) > 0 && (
|
||||
<div className="overflow-auto">
|
||||
<table className="w-full text-sm">
|
||||
|
||||
Reference in New Issue
Block a user