Files
PocketVeto/frontend/lib/utils.ts
Jack Levy f3a8c1218a Add chamber color badges, action history fallback, and task status polling
- Add chamberBadgeColor util: amber/gold for Senate, slate/silver for House
- Apply chamber badge to BillCard and bill detail header
- ActionTimeline: show latest_action_date/text as fallback when full history
  not yet fetched, with note that full history loads in background
- Manual Controls: poll task status every 5s after triggering, show spinning
  indicator while running, task ID prefix, and completion/failure state

Authored-By: Jack Levy
2026-03-01 11:29:11 -05:00

68 lines
2.3 KiB
TypeScript

import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
export function formatDate(date?: string | null): string {
if (!date) return "—";
return new Date(date).toLocaleDateString("en-US", {
year: "numeric",
month: "short",
day: "numeric",
});
}
export function billLabel(billType: string, billNumber: number): string {
const labels: Record<string, string> = {
hr: "H.R.",
s: "S.",
hjres: "H.J.Res.",
sjres: "S.J.Res.",
hconres: "H.Con.Res.",
sconres: "S.Con.Res.",
hres: "H.Res.",
sres: "S.Res.",
};
return `${labels[billType?.toLowerCase()] ?? billType?.toUpperCase()} ${billNumber}`;
}
export function partyColor(party?: string): string {
if (!party) return "text-muted-foreground";
const p = party.toLowerCase();
if (p.includes("democrat") || p === "d") return "text-blue-500";
if (p.includes("republican") || p === "r") return "text-red-500";
return "text-yellow-500";
}
export function partyBadgeColor(party?: string): string {
if (!party) return "bg-muted text-muted-foreground";
const p = party.toLowerCase();
if (p.includes("democrat") || p === "d") return "bg-blue-600 text-white";
if (p.includes("republican") || p === "r") return "bg-red-600 text-white";
return "bg-slate-500 text-white";
}
export function congressLabel(congress: number): string {
const lastTwo = congress % 100;
if (lastTwo >= 11 && lastTwo <= 13) return `${congress}th Congress`;
const suffixes: Record<number, string> = { 1: "st", 2: "nd", 3: "rd" };
return `${congress}${suffixes[congress % 10] ?? "th"} Congress`;
}
export function chamberBadgeColor(chamber?: string): string {
if (!chamber) return "bg-muted text-muted-foreground";
const c = chamber.toLowerCase();
if (c === "senate") return "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400";
if (c.startsWith("house")) return "bg-slate-100 text-slate-600 dark:bg-slate-700/50 dark:text-slate-300";
return "bg-muted text-muted-foreground";
}
export function trendColor(score?: number): string {
if (!score) return "text-muted-foreground";
if (score >= 70) return "text-red-500";
if (score >= 40) return "text-yellow-500";
return "text-green-500";
}