Files
whyrating-engine-legacy/web/components/reviewiq/kpi/KPICard.tsx
Alejandro Gutiérrez c8ecb4b98f feat(reviewiq): Add AI synthesis support to dashboard components
Frontend:
- Add Synthesis type with action plan, insights, annotations
- ExecutiveSummary: Accept synthesis prop for AI narrative
- SentimentPie: Accept insight prop for contextual explanation
- IntensityHeatmap: Accept insight + highlightDomain props
- TimelineChart: Accept insight + annotations props
- All components gracefully degrade when synthesis is null

Backend:
- Add Stage 4: Synthesize for generating AI narratives
- Gathers context from classified spans
- Generates executive narrative, section insights, action plan
- Produces timeline annotations and marketing angles
- Stores synthesis in pipeline.executions table

Components show AI insights with purple gradient styling when available,
fall back to existing behavior when synthesis is not yet generated.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 02:59:47 +00:00

64 lines
1.5 KiB
TypeScript

'use client';
import { LucideIcon } from 'lucide-react';
interface KPICardProps {
title: string;
value: string | number;
subtitle?: string;
icon: LucideIcon;
colorClass: string;
onClick?: () => void;
isActive?: boolean;
}
/**
* Clickable KPI card component for the dashboard.
*/
export function KPICard({
title,
value,
subtitle,
icon: Icon,
colorClass,
onClick,
isActive = false,
}: KPICardProps) {
const baseClasses = `
rounded-xl p-4 shadow-md hover:shadow-lg transition-all cursor-pointer
border-2 ${colorClass}
`;
const activeClasses = isActive
? 'ring-2 ring-offset-2 ring-blue-500 scale-[1.02]'
: '';
return (
<div
className={`${baseClasses} ${activeClasses}`}
onClick={onClick}
role="button"
tabIndex={0}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
onClick?.();
}
}}
>
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2">
<Icon className="w-5 h-5" />
<span className="text-sm font-bold">{title}</span>
</div>
{isActive && (
<span className="px-2 py-0.5 bg-blue-600 text-white text-[10px] font-bold rounded-full">
ACTIVE
</span>
)}
</div>
<div className="text-3xl font-bold">{value}</div>
{subtitle && <div className="text-xs mt-1 font-medium opacity-80">{subtitle}</div>}
</div>
);
}