From 4c428fd9da5f5e792063273e2f34cf2294a4fe7c Mon Sep 17 00:00:00 2001 From: Tyler Date: Wed, 17 Jun 2026 14:52:45 -0600 Subject: [PATCH] =?UTF-8?q?feat(admin):=20add=20v2=20order=20detail=20fulf?= =?UTF-8?q?illment=20timeline=20(Placed=20=E2=86=92=20Ready=20=E2=86=92=20?= =?UTF-8?q?Picked=20up)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/admin/v2/orders/[id]/page.tsx | 7 + .../admin/orders/FulfillmentTimeline.tsx | 150 ++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 src/components/admin/orders/FulfillmentTimeline.tsx diff --git a/src/app/admin/v2/orders/[id]/page.tsx b/src/app/admin/v2/orders/[id]/page.tsx index 03bcae7..71b9e44 100644 --- a/src/app/admin/v2/orders/[id]/page.tsx +++ b/src/app/admin/v2/orders/[id]/page.tsx @@ -6,6 +6,7 @@ import PageHeader from "@/components/admin/PageHeader"; import StatusPill, { type StatusKind } from "@/components/admin/StatusPill"; import StickyActionBar from "@/components/admin/StickyActionBar"; import OrderActionButtons from "@/components/admin/orders/OrderActionButtons"; +import FulfillmentTimeline from "@/components/admin/orders/FulfillmentTimeline"; export const dynamic = "force-dynamic"; @@ -109,6 +110,12 @@ export default async function OrderDetailV2Page({ )} + +
s.key === step); + const currentIdx = STEPS.findIndex((s) => s.key === current); + if (currentIdx === -1) return "upcoming"; + if (order < currentIdx) return "done"; + if (order === currentIdx) return "current"; + return "upcoming"; +} + +export function FulfillmentTimeline({ + status, + placedAt, + pickedUpAt, +}: FulfillmentTimelineProps) { + if (status === "cancelled") { + return ( +
+

+ STATUS +

+
+ Cancelled +
+
+ ); + } + + return ( +
+

+ FULFILLMENT +

+
    + {STEPS.map((step, i) => { + const state = stepState(step.key, status); + const isLast = i === STEPS.length - 1; + // Pick the right timestamp to display. The "Ready" step has no + // dedicated column in the legacy schema, so we show "—" — the + // user can read the mark-ready time from the audit log. + const ts = + step.key === "placed" + ? placedAt + : step.key === "picked-up" + ? pickedUpAt + : null; + const dotBg = + state === "done" + ? "var(--color-accent)" + : state === "current" + ? "var(--color-accent-2)" + : "var(--color-surface-3)"; + const labelColor = + state === "upcoming" ? "var(--color-text-faint)" : "var(--color-text)"; + return ( +
  1. +
    +
    +
    + {step.label} +
    +
    + {ts ? formatDateTime(ts) : "—"} +
    +
  2. + ); + })} +
+
+ ); +} + +export default FulfillmentTimeline;