diff --git a/src/components/item-history-popover.tsx b/src/components/item-history-popover.tsx index 742d2c7..60fe097 100644 --- a/src/components/item-history-popover.tsx +++ b/src/components/item-history-popover.tsx @@ -57,13 +57,13 @@ export function ItemHistoryPopover({ data, loading, error }: Props) { ); } -function FreshnessBadge({ label, ts, variant }: { label: string; ts: string | null; variant: "indigo" | "amber" }) { +function FreshnessBadge({ label, ts, variant }: { label: string; ts: string | null; variant: "indigo" | "warn" }) { const cls = variant === "indigo" - ? "bg-indigo-500/10 text-indigo-700 dark:text-indigo-300" - : "bg-amber-500/10 text-amber-700 dark:text-amber-300"; + ? "bg-indigo-500/15 text-indigo-700 dark:text-indigo-300 ring-1 ring-inset ring-indigo-500/20" + : "bg-warn/15 text-warn-foreground ring-1 ring-inset ring-warn/25"; return ( - + {label} {ts ? relTime(ts) : ""} ); @@ -72,7 +72,7 @@ function FreshnessBadge({ label, ts, variant }: { label: string; ts: string | nu function Section({ title, children }: { title: string; children: React.ReactNode }) { return (
-
{title}
+
{title}
{children}
); @@ -87,34 +87,36 @@ function DeterministicCard({ }) { const fuzzy = data.match.match_kind === "description_fuzzy"; return ( -
+
- - PB-derived + + + + PB-derived
{fuzzy && ( -
+
⚠ best-guess match · {Math.round(data.match.match_confidence * 100)}% confidence
)} {data.cheapest && ( -
+
↓ Cheapest: {data.cheapest.supplier_name} R{data.cheapest.last_cost}
)} {data.suppliers.length > 0 && (
-
    +
      {data.suppliers.map((s) => ( -
    • +
    • {s.supplier_name} - R{s.last_cost} + R{s.last_cost} {relTime(s.last_bought_at)} (×{s.times_bought})
    • ))} @@ -124,9 +126,9 @@ function DeterministicCard({ {data.other_quotes.length > 0 && (
      -
        +
          {data.other_quotes.map((q) => ( -
        • +
        • {q.quote_number} R{q.selling_price} {q.status} · {q.days_ago}d @@ -159,21 +161,23 @@ function AiCard({ data, updatedAt }: { data: Record; updatedAt: // a `summary` (or any *_summary / first long string) as the headline // and turns named arrays (top_historical_matches with // matched_description, etc.) into structured rows. We only wrap it - // here to keep the amber framing + freshness badge + Raw data - // fallback that operators expect. + // here to keep the warm warn-toned framing + freshness badge + + // Raw data fallback that operators expect. return ( -
          +
          - - AI snapshot + + + + AI snapshot
          - +
          -
          - Raw AI data -
          +      
          + Raw AI data +
                     {JSON.stringify(data, null, 2)}
                   
          diff --git a/src/components/order-sprint-process-modal.tsx b/src/components/order-sprint-process-modal.tsx index bf77bdc..92d7b32 100644 --- a/src/components/order-sprint-process-modal.tsx +++ b/src/components/order-sprint-process-modal.tsx @@ -303,7 +303,7 @@ export function OrderSprintProcessModal({ item, suppliers, open, onClose, onProc aria-modal="true" onClick={(e) => { if (e.target === e.currentTarget) onClose(); }} > - +