feat(purchasing): amber treatment for POs/DNs with pending status

This commit is contained in:
DEls 2026-08-27 00:01:41 +02:00
parent c9f3d61fc6
commit 85f3bdcbab
3 changed files with 59 additions and 6 deletions

View file

@ -89,6 +89,7 @@ export default async function PurchaseOrderDetailPage({
isVerified: !!d.is_verified,
slipImage: d.slip_image,
rawOcrData: d.raw_ocr_data ?? null,
processingStatus: d.processing_status ?? "",
receivedItems: (receivedByDn.get(d.id) ?? []).map((r) => ({
id: r.id,
purchaseOrderItemId: r.purchase_order_item,

View file

@ -46,6 +46,7 @@ export interface PoDetailDn {
isVerified: boolean;
slipImage: string;
rawOcrData: unknown;
processingStatus: string;
receivedItems: {
id: string;
purchaseOrderItemId: string;
@ -317,6 +318,31 @@ export function PoDetailView({ detail }: { detail: PoDetail }) {
);
}
function DnStatusPill({ status }: { status: string }) {
if (status === "needs-review" || status === "pending") {
return (
<Badge
variant="outline"
className="gap-1 border-amber-500/40 bg-amber-500/15 text-amber-700 dark:text-amber-300"
>
<AlertTriangle className="h-3 w-3" />
{status === "needs-review" ? "Needs review" : "Pending"}
</Badge>
);
}
if (status === "processed") {
return (
<Badge
variant="outline"
className="border-border bg-muted/40 text-muted-foreground"
>
Processed
</Badge>
);
}
return null;
}
function DeliveryNoteCard({
dn,
items,
@ -337,9 +363,12 @@ function DeliveryNoteCard({
<div className="p-4">
<div className="flex flex-wrap items-start justify-between gap-2">
<div>
<p className="font-mono text-sm font-semibold">
{dn.deliveryNoteNumber || "(no DN number)"}
</p>
<div className="flex flex-wrap items-center gap-2">
<p className="font-mono text-sm font-semibold">
{dn.deliveryNoteNumber || "(no DN number)"}
</p>
<DnStatusPill status={dn.processingStatus} />
</div>
<div className="mt-0.5 flex flex-wrap items-center gap-x-1.5 text-xs text-muted-foreground">
<span>
{dn.dateReceived

View file

@ -302,17 +302,31 @@ export function PurchasingView({
function PoListItem({ po }: { po: PoRow }) {
const href = `${PO_BASE}/${po.id}`;
const hasPending = po.pendingDnCount > 0;
return (
<li>
<Link
href={href}
className="group flex items-center gap-3 px-4 py-3 transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#F2B165]/40"
className={cn(
"group relative flex items-center gap-3 px-4 py-3 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#F2B165]/40",
hasPending
? "border-l-2 border-amber-500/60 bg-amber-500/5 hover:bg-amber-500/10"
: "hover:bg-muted/40",
)}
>
<span
aria-hidden
className="inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-[#F2B165]/15"
className={cn(
"inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-md",
hasPending ? "bg-amber-500/20" : "bg-[#F2B165]/15",
)}
>
<Package className="h-3.5 w-3.5 text-[#F2B165]" />
<Package
className={cn(
"h-3.5 w-3.5",
hasPending ? "text-amber-700 dark:text-amber-300" : "text-[#F2B165]",
)}
/>
</span>
<div className="min-w-0 flex-1">
<div className="flex flex-wrap items-baseline gap-x-3 gap-y-1">
@ -328,6 +342,15 @@ function PoListItem({ po }: { po: PoRow }) {
Required {format(parseISO(po.requiredByDate), "yyyy-MM-dd")}
</span>
)}
{hasPending && (
<Badge
variant="outline"
className="gap-1 border-amber-500/40 bg-amber-500/15 text-amber-700 dark:text-amber-300"
>
<AlertTriangle className="h-3 w-3" />
Pending DNs: {po.pendingDnCount}
</Badge>
)}
</div>
<p className="mt-0.5 text-xs text-muted-foreground">
{po.itemCount} line item{po.itemCount === 1 ? "" : "s"} ·{" "}