feat: per-line material override in product order wizard Step 3
- Added "Mat Override" column to the review table - Each line has its own dropdown (per-line takes priority over global) - Default shows global override if set, otherwise "No override" - "Clear" option to explicitly remove override on a line when global is set - Amber background when override is active Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -50,6 +50,7 @@ export default function NewProductOrderPage() {
|
||||
const [globalPositionSelections, setGlobalPositionSelections] = useState<GlobalPositionSelections>({})
|
||||
const [notes, setNotes] = useState('')
|
||||
const [materialOverride, setMaterialOverride] = useState<string>('')
|
||||
const [lineOverrides, setLineOverrides] = useState<Record<string, string>>({})
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
|
||||
// ---- Step 1: load products with STEP files ----
|
||||
@@ -383,13 +384,17 @@ export default function NewProductOrderPage() {
|
||||
try {
|
||||
const result = await createOrder({
|
||||
notes: notes || undefined,
|
||||
lines: orderLines.map((l) => ({
|
||||
product_id: l.product.id,
|
||||
output_type_id: l.outputType.id,
|
||||
render_position_id: l.position?.id ?? null,
|
||||
global_render_position_id: l.globalPosition?.id ?? null,
|
||||
material_override: materialOverride || null,
|
||||
})),
|
||||
lines: orderLines.map((l) => {
|
||||
const lineOv = lineOverrides[l.key]
|
||||
const override = lineOv === '__none__' ? null : (lineOv || materialOverride || null)
|
||||
return {
|
||||
product_id: l.product.id,
|
||||
output_type_id: l.outputType.id,
|
||||
render_position_id: l.position?.id ?? null,
|
||||
global_render_position_id: l.globalPosition?.id ?? null,
|
||||
material_override: override,
|
||||
}
|
||||
}),
|
||||
})
|
||||
toast.success(`Draft order ${result.order_number} created — review and submit`)
|
||||
navigate(`/orders/${result.id}`)
|
||||
@@ -773,6 +778,7 @@ export default function NewProductOrderPage() {
|
||||
<th className="px-4 py-3 font-medium text-content-secondary">Position</th>
|
||||
<th className="px-4 py-3 font-medium text-content-secondary">Renderer</th>
|
||||
<th className="px-4 py-3 font-medium text-content-secondary">Format</th>
|
||||
<th className="px-4 py-3 font-medium text-content-secondary">Mat Override</th>
|
||||
<th className="px-4 py-3 font-medium text-content-secondary text-right">Price</th>
|
||||
<th className="px-4 py-3 font-medium text-content-secondary w-12"></th>
|
||||
</tr>
|
||||
@@ -809,6 +815,20 @@ export default function NewProductOrderPage() {
|
||||
</td>
|
||||
<td className="px-4 py-3 text-content-muted">{line.outputType.renderer}</td>
|
||||
<td className="px-4 py-3 text-content-muted uppercase">{line.outputType.output_format}</td>
|
||||
<td className="px-4 py-3">
|
||||
<select
|
||||
className="text-xs border border-border-default rounded px-1.5 py-1 w-full"
|
||||
style={{ backgroundColor: (lineOverrides[line.key] || materialOverride) ? 'rgba(245, 158, 11, 0.1)' : 'var(--color-bg-surface)' }}
|
||||
value={lineOverrides[line.key] ?? ''}
|
||||
onChange={(e) => setLineOverrides((prev) => ({ ...prev, [line.key]: e.target.value }))}
|
||||
>
|
||||
<option value="">{materialOverride ? `Global: ${materialOverride.replace('SCHAEFFLER_', '').replace(/_/g, ' ')}` : 'No override'}</option>
|
||||
{materialOverride && <option value="__none__">— No override (clear) —</option>}
|
||||
{libMaterials.map((m: Material) => (
|
||||
<option key={m.id} value={m.name}>{m.name.replace('SCHAEFFLER_', '').replace(/_/g, ' ')}</option>
|
||||
))}
|
||||
</select>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right">
|
||||
{(() => {
|
||||
const price = getLinePrice(line.product.id, line.outputType.id)
|
||||
|
||||
Reference in New Issue
Block a user