feat(orders): surface order age as colour-coded badge
Replaces the bare formatDate(order.created) string in the order card metadata slot with an OrderAgeBadge component. The badge shows 'today' or 'Xd old' with colour coding by bucket (green/amber/orange/red). The absolute date is preserved in the badge's title= tooltip. Bucket math lives in src/lib/order-age.ts (covered by 14 unit tests). This change is rendering-only — no server or schema impact. Note: dev-server smoke check was blocked by a pre-existing data bug where the /api/v1/orders/list endpoint returns 0 orders across all tabs for both admin and david.els users, despite the tab counts showing 22/18/14/17/168/0 — investigated, unrelated to this change. The OrderAgeBadge will render correctly once that bug is fixed. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
27efc8628c
commit
dc8436e832
1 changed files with 38 additions and 4 deletions
|
|
@ -24,7 +24,8 @@ import {
|
|||
Copy,
|
||||
} from "lucide-react";
|
||||
import getPocketBase from "@/lib/pocketbase";
|
||||
import { displayContactName } from "@/lib/utils";
|
||||
import { cn, displayContactName } from "@/lib/utils";
|
||||
import { getOrderAge, bucketToBadgeClass } from "@/lib/order-age";
|
||||
import { isOrderComplete } from "@/lib/server/order-completion";
|
||||
import { useAuth } from "@/components/auth-provider";
|
||||
import { isRepUser } from "@/lib/rep-visibility";
|
||||
|
|
@ -96,6 +97,38 @@ function formatDate(dateStr: string): string {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline age badge for the order card metadata slot. Kept local to
|
||||
* this file because it's only used here; hoist to
|
||||
* `src/components/order-age-badge.tsx` if a second page needs it.
|
||||
*
|
||||
* The badge text conveys both the relative age ("3d old") and the
|
||||
* bucket colour, and the `title=` preserves the absolute date for
|
||||
* operators who want to know the exact day.
|
||||
*/
|
||||
function OrderAgeBadge({
|
||||
created,
|
||||
className,
|
||||
}: {
|
||||
created: string;
|
||||
className?: string;
|
||||
}) {
|
||||
const age = getOrderAge(created);
|
||||
return (
|
||||
<span
|
||||
data-testid={`order-age-badge-${age.bucket}`}
|
||||
className={cn(
|
||||
"rounded-full px-2 py-0.5 text-[10px] font-medium tabular-nums",
|
||||
bucketToBadgeClass(age.bucket),
|
||||
className,
|
||||
)}
|
||||
title={`Created ${formatDate(created)}`}
|
||||
>
|
||||
{age.label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
async function fetchDashboardJson<T>(url: string): Promise<T> {
|
||||
const token = getPocketBase().authStore.token;
|
||||
if (!token) throw new Error("Your session has expired. Please sign in again.");
|
||||
|
|
@ -862,9 +895,10 @@ function OrderCard({
|
|||
</span>
|
||||
</>
|
||||
)}
|
||||
<span className="ml-auto text-xs text-muted-foreground hidden sm:inline">
|
||||
{formatDate(order.created)}
|
||||
</span>
|
||||
<OrderAgeBadge
|
||||
created={order.created}
|
||||
className="ml-auto hidden sm:inline-flex"
|
||||
/>
|
||||
</div>
|
||||
{/* Per-tab item-count summary — the operator's at-a-glance
|
||||
scan line when cards are collapsed. */}
|
||||
|
|
|
|||
Loading…
Reference in a new issue