feat(collection-confirm): rewrite row card as stacked vertical sections

This commit is contained in:
SJCoetz 2026-07-10 01:07:29 +02:00
parent 9ab4be2666
commit 6d03be2ebc

View file

@ -566,7 +566,7 @@ function ConfirmCard({
disabled={busy}
/>
</div>
<div className="grid min-w-0 flex-1 gap-3 md:grid-cols-[minmax(0,1.2fr)_minmax(0,0.9fr)_auto]">
<div className="min-w-0 flex-1 space-y-1">
<div className="min-w-0">
<p className="truncate text-sm font-semibold">
{row.expand?.purchase_order?.expand?.supplier?.suppliername ?? "Unknown supplier"}
@ -599,90 +599,103 @@ function ConfirmCard({
)}
</div>
</div>
<div className="min-w-0 space-y-1 text-sm">
<div className="flex items-center gap-3">
<span className="text-xs text-muted-foreground">Ordered</span>
<span className="font-mono">{row.order_qty ?? row.quantity ?? 0}</span>
</div>
<div className="flex items-center gap-3">
<span className="text-xs text-muted-foreground">Collected</span>
<span className="font-mono">{row.qty_collected ?? 0}</span>
</div>
{row.problem_flag && (
<div className="inline-flex items-center gap-1 rounded-md bg-amber-100 px-2 py-0.5 text-xs font-medium text-amber-800">
<AlertTriangle className="h-3 w-3" />
Problem: {row.problem_type ?? "flagged"}
</div>
)}
{row.collection_photo && (
<Button size="xs" variant="outline" onClick={() => setPhotoOpen(true)}>
<ImageIcon className="mr-1 h-3 w-3" />
View photo
</Button>
)}
{attachments.length > 0 && (
<div className="flex flex-wrap gap-1.5">
{attachments.map((name) => (
<a
key={name}
href={attachmentUrl(row, name)}
target="_blank"
rel="noreferrer"
className="inline-flex items-center gap-1 rounded-md border bg-background px-2 py-0.5 text-xs hover:bg-accent"
>
<Paperclip className="h-3 w-3" />
{displayFilename(name)}
</a>
))}
</div>
)}
{invoice && (
<div className="inline-flex items-center gap-1 rounded-md bg-emerald-50 px-2 py-0.5 text-xs font-medium text-emerald-700">
Invoice {invoice.number} · {invoice.date}
</div>
)}
</div>
<div className="flex flex-col items-stretch gap-2 lg:min-w-[12rem]">
<Button size="sm" variant="outline" onClick={() => setQuantityOpen(true)} disabled={busy}>
<Pencil className="mr-1 h-3.5 w-3.5" />
Adjust Quantity
</Button>
<Button size="sm" variant="outline" onClick={() => setNoteOpen(true)} disabled={busy}>
<MessageSquare className="mr-1 h-3.5 w-3.5" />
Add Note
</Button>
<Button
size="sm"
variant="outline"
onClick={() => setAttachOpen(true)}
disabled={busy}
aria-label="Attach invoice"
>
<Paperclip className="mr-1 h-3.5 w-3.5" />
Attach
</Button>
<Button size="sm" disabled={busy || !canConfirm} onClick={() => void onConfirm(row)} className="bg-emerald-600 text-white hover:bg-emerald-700">
{busy ? <Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" /> : null}
Confirm for invoicing
</Button>
<Button
size="sm"
variant="outline"
onClick={() => onRequestReturn(row)}
disabled={busy}
>
<Undo2 className="mr-1 h-3.5 w-3.5" />
Return to pool
</Button>
{!canConfirm && (
<p className="text-[11px] text-muted-foreground">Add a confirming note before invoicing this row.</p>
)}
</div>
</div>
</div>
{/* ── Details: qty + problem + photo (left), attachments + invoice (right) ── */}
<div className="grid grid-cols-1 gap-3 border-t bg-muted/20 px-4 py-3 md:grid-cols-2">
<div className="space-y-1 text-sm">
<div className="flex items-center gap-3">
<span className="text-xs text-muted-foreground">Ordered</span>
<span className="font-mono">{row.order_qty ?? row.quantity ?? 0}</span>
</div>
<div className="flex items-center gap-3">
<span className="text-xs text-muted-foreground">Collected</span>
<span className="font-mono">{row.qty_collected ?? 0}</span>
</div>
{row.problem_flag && (
<div className="inline-flex items-center gap-1 rounded-md bg-amber-100 px-2 py-0.5 text-xs font-medium text-amber-800">
<AlertTriangle className="h-3 w-3" />
Problem: {row.problem_type ?? "flagged"}
</div>
)}
{row.collection_photo && (
<Button size="xs" variant="outline" onClick={() => setPhotoOpen(true)}>
<ImageIcon className="mr-1 h-3 w-3" />
View photo
</Button>
)}
</div>
<div className="space-y-1 text-sm">
{attachments.length > 0 && (
<div className="flex flex-wrap gap-1.5">
{attachments.map((name) => (
<a
key={name}
href={attachmentUrl(row, name)}
target="_blank"
rel="noreferrer"
className="inline-flex items-center gap-1 rounded-md border bg-background px-2 py-0.5 text-xs hover:bg-accent"
>
<Paperclip className="h-3 w-3" />
{displayFilename(name)}
</a>
))}
</div>
)}
{invoice && (
<div className="inline-flex items-center gap-1 rounded-md bg-emerald-50 px-2 py-0.5 text-xs font-medium text-emerald-700">
Invoice {invoice.number} · {invoice.date}
</div>
)}
</div>
</div>
{/* ── Actions: 2-up grid on sm, 3-up on lg, primary spans 2 ── */}
<div className="grid grid-cols-1 gap-2 border-t px-4 py-3 sm:grid-cols-2 lg:grid-cols-3">
<Button size="sm" variant="outline" onClick={() => setQuantityOpen(true)} disabled={busy}>
<Pencil className="mr-1 h-3.5 w-3.5" />
Adjust Quantity
</Button>
<Button size="sm" variant="outline" onClick={() => setNoteOpen(true)} disabled={busy}>
<MessageSquare className="mr-1 h-3.5 w-3.5" />
Add Note
</Button>
<Button
size="sm"
variant="outline"
onClick={() => setAttachOpen(true)}
disabled={busy}
aria-label="Attach invoice"
>
<Paperclip className="mr-1 h-3.5 w-3.5" />
Attach
</Button>
<Button
size="sm"
disabled={busy || !canConfirm}
onClick={() => void onConfirm(row)}
className="bg-emerald-600 text-white hover:bg-emerald-700 lg:col-span-2"
>
{busy ? <Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" /> : <CheckCircle2 className="mr-1 h-3.5 w-3.5" />}
Confirm for invoicing
</Button>
<Button
size="sm"
variant="outline"
onClick={() => onRequestReturn(row)}
disabled={busy}
>
<Undo2 className="mr-1 h-3.5 w-3.5" />
Return to pool
</Button>
{!canConfirm && (
<p className="text-[11px] text-muted-foreground sm:col-span-2 lg:col-span-3">
Add a confirming note before invoicing this row.
</p>
)}
</div>
<div className="border-t bg-muted/30 px-4 py-3">
<p className="text-[11px] font-semibold uppercase tracking-widest text-muted-foreground">Notes</p>
{entries.length === 0 ? (