p2p/src/components/StatusBadge.tsx
Bhanu Prakash Sai Potteri f5bc1fa119 feat: rebuild the UI on Tailwind v4 + shadcn/ui
Migrates Tailwind v3 -> v4 (CSS-first, theme tokens for brand, workflow
states and charts) and adds 23 shadcn/ui components on Radix.

- lib/workflow.ts is now the single source of truth for state name, colour,
  icon and available actions; "Financial Review" is the canonical served
  name, with "Finance Review" kept as an alias so those rows stop falling
  through to the unstyled fallback and dropping out of every count
- record view: TanStack Table with server-side sort/paginate, company column
  with initials avatar, page totals, density and a URL-synced query state
- dashboard: one cached analytics fetch shared by tiles and charts, donut +
  aging + vendor spend, click-to-filter
- detail view: rebuilt against the live detailview payload — restores the PO
  number and payment terms that the old key heuristics silently dropped,
  parses match_summary into a 3-way-match checklist, and separates the
  department's and reviewer's own words from AI-generated content
- form modal on Radix Dialog (focus trap, scroll lock, escape handling)
2026-07-29 19:47:59 +05:30

51 lines
1.4 KiB
TypeScript

import { cn } from "@/lib/utils";
import { resolveStatus } from "@/lib/workflow";
interface Props {
value: string | null | undefined;
/** `sm` for table cells, `md` for the detail header. */
size?: "sm" | "md";
/** Show the state icon alongside the dot. Off in dense tables. */
withIcon?: boolean;
className?: string;
}
/**
* The one status pill. Colour, label and icon all come from lib/workflow, so a
* state looks identical in a table cell, a detail header and a chart legend.
*/
export default function StatusBadge({ value, size = "sm", withIcon = false, className }: Props) {
const status = resolveStatus(value);
if (!status) return null;
const { label, tone, pulse, icon: Icon } = status;
return (
<span
className={cn(
"inline-flex shrink-0 items-center gap-1.5 rounded-full border font-semibold",
tone.bg,
tone.border,
tone.text,
size === "sm" ? "px-2 py-0.5 text-xs" : "px-2.5 py-1 text-sm",
className,
)}
>
{withIcon ? (
<Icon className={size === "sm" ? "size-3" : "size-3.5"} aria-hidden />
) : (
<span
className={cn(
"size-1.5 shrink-0 rounded-full",
tone.solid,
// Only the in-progress state pulses, so motion means "working".
pulse && "animate-pulse",
)}
aria-hidden
/>
)}
{label}
</span>
);
}