'use client'; import { TrendingUp, TrendingDown, MessageSquare, CheckCircle, AlertTriangle, Star, Activity, } from 'lucide-react'; import type { WidgetConfig, StatCardData, StatCardConfig } from '@/lib/pipeline-types'; import { WidgetWrapper } from './WidgetWrapper'; interface StatCardProps { config: WidgetConfig; data: StatCardData | null; loading: boolean; error?: string; onRefresh?: () => void; } // Icon mapping const ICONS: Record> = { 'message-square': MessageSquare, 'check-circle': CheckCircle, 'alert-triangle': AlertTriangle, star: Star, activity: Activity, }; // Color mapping (light mode optimized for bg-gray-50 background) const COLORS: Record = { blue: 'text-blue-700 bg-blue-100', green: 'text-green-700 bg-green-100', red: 'text-red-700 bg-red-100', yellow: 'text-yellow-700 bg-yellow-100', purple: 'text-purple-700 bg-purple-100', gray: 'text-gray-700 bg-gray-100', }; /** * Format a value according to a format string. * Supports: {value:,} for thousands, {value:.1f} for decimals, {value:.1%} for percentages */ function formatValue(value: number | string, format?: string): string { if (!format) return String(value); const num = typeof value === 'string' ? parseFloat(value) : value; if (isNaN(num)) return String(value); // Simple format parsing if (format.includes(':,}')) { return num.toLocaleString(); } if (format.includes(':.1f}')) { return num.toFixed(1); } if (format.includes(':.2f}')) { return num.toFixed(2); } if (format.includes(':.1%}')) { return (num * 100).toFixed(1) + '%'; } return String(value); } /** * Stat card widget for displaying KPIs. */ export function StatCard({ config, data, loading, error, onRefresh }: StatCardProps) { const widgetConfig = config.config as unknown as StatCardConfig; const Icon = widgetConfig.icon ? ICONS[widgetConfig.icon] : Activity; const colorClass = widgetConfig.color ? COLORS[widgetConfig.color] : COLORS.gray; // Extract value and trend from data const value = data?.[widgetConfig.value_key] ?? 0; const trend = widgetConfig.trend_key ? data?.[widgetConfig.trend_key] : undefined; return (

{formatValue(value, widgetConfig.format)}

{trend !== undefined && (
{Number(trend) >= 0 ? ( ) : ( )} = 0 ? 'text-green-600' : 'text-red-600' }`} > {formatValue(trend, widgetConfig.trend_format || '{value:.1f}')}
)}
{Icon && (
)}
); }