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
This commit is contained in:
Jack Levy
2026-03-01 11:29:11 -05:00
parent 5eebc2f196
commit f3a8c1218a
5 changed files with 101 additions and 26 deletions

View File

@@ -4,10 +4,15 @@ import { formatDate } from "@/lib/utils";
interface ActionTimelineProps {
actions: BillAction[];
latestActionDate?: string;
latestActionText?: string;
}
export function ActionTimeline({ actions }: ActionTimelineProps) {
if (!actions || actions.length === 0) {
export function ActionTimeline({ actions, latestActionDate, latestActionText }: ActionTimelineProps) {
const hasActions = actions && actions.length > 0;
const hasFallback = !hasActions && latestActionText;
if (!hasActions && !hasFallback) {
return (
<div className="bg-card border border-border rounded-lg p-6">
<h2 className="font-semibold mb-3 flex items-center gap-2">
@@ -24,22 +29,38 @@ export function ActionTimeline({ actions }: ActionTimelineProps) {
<h2 className="font-semibold mb-4 flex items-center gap-2">
<Clock className="w-4 h-4" />
Action History
<span className="text-xs text-muted-foreground font-normal">({actions.length})</span>
{hasActions && (
<span className="text-xs text-muted-foreground font-normal">({actions.length})</span>
)}
</h2>
<div className="relative">
<div className="absolute left-2 top-0 bottom-0 w-px bg-border" />
<ul className="space-y-4 pl-7">
{actions.map((action, i) => (
<li key={action.id} className="relative">
<div className="absolute -left-5 top-1.5 w-2 h-2 rounded-full bg-primary/60 border-2 border-background" />
{hasActions ? (
actions.map((action) => (
<li key={action.id} className="relative">
<div className="absolute -left-5 top-1.5 w-2 h-2 rounded-full bg-primary/60 border-2 border-background" />
<div className="text-xs text-muted-foreground mb-0.5">
{formatDate(action.action_date)}
{action.chamber && ` · ${action.chamber}`}
</div>
<p className="text-sm leading-snug">{action.action_text}</p>
</li>
))
) : (
<li className="relative">
<div className="absolute -left-5 top-1.5 w-2 h-2 rounded-full bg-muted-foreground/40 border-2 border-background" />
<div className="text-xs text-muted-foreground mb-0.5">
{formatDate(action.action_date)}
{action.chamber && ` · ${action.chamber}`}
{formatDate(latestActionDate)}
<span className="ml-1.5 italic">· latest known action</span>
</div>
<p className="text-sm leading-snug">{action.action_text}</p>
<p className="text-sm leading-snug">{latestActionText}</p>
<p className="text-xs text-muted-foreground mt-1 italic">
Full history loads in the background refresh to see all actions.
</p>
</li>
))}
)}
</ul>
</div>
</div>