'use client'; import { useState } from 'react'; /** * ChartCard — Card wrapper for chart widgets. * * Features: * - Title with auto-selected contextual icon based on keywords * - Optional tabbed header for multi-view chart cards * - Optional legend toggles for showing/hiding data series * - Smooth fade-in animation on initial render * - Falls back to rendering children directly when no tabs provided * * Requirements: 5.1, 5.2, 5.3, 5.4 */ export interface ChartTab { key: string; label: string; content: React.ReactNode; } export interface LegendToggle { key: string; label: string; color: string; enabled: boolean; } export interface ChartCardProps { title: string; children: React.ReactNode; className?: string; tabs?: ChartTab[]; legendItems?: LegendToggle[]; onLegendToggle?: (key: string) => void; } const iconClass = 'h-4 w-4 text-divisi-accent flex-shrink-0'; /** Pick an SVG icon based on keywords in the chart title. */ function TitleIcon({ title }: { title: string }) { const t = title.toLowerCase(); // Time / trend / over time → line chart if (t.includes('over time') || t.includes('trend') || t.includes('monthly') || t.includes('timeline')) { return ( ); } // Currency → dollar sign if (t.includes('currency')) { return ( ); } // Territory / region / country / geography → globe if (t.includes('territory') || t.includes('territorio') || t.includes('region') || t.includes('country') || t.includes('geography')) { return ( ); } // Top / ranking / works → trophy/star if (t.includes('top ') || t.includes('ranking') || t.includes('best')) { return ( ); } // Media / type / category / genre / source → layers/tag if (t.includes('media') || t.includes('type') || t.includes('category') || t.includes('genre') || t.includes('source') || t.includes('match')) { return ( ); } // Status / active / inactive / pending → pie/donut if (t.includes('status') || t.includes('active') || t.includes('inactive') || t.includes('by status')) { return ( ); } // Income / revenue / costs / amount / payable → dollar if (t.includes('income') || t.includes('revenue') || t.includes('cost') || t.includes('amount') || t.includes('payable') || t.includes('earning')) { return ( ); } // Entity / event / audit / log → shield/clipboard if (t.includes('entity') || t.includes('event') || t.includes('audit') || t.includes('log')) { return ( ); } // Payee / user / composer / people → users if (t.includes('payee') || t.includes('composer') || t.includes('user') || t.includes('people') || t.includes('contact')) { return ( ); } // Contract / document / statement → file if (t.includes('contract') || t.includes('document') || t.includes('statement')) { return ( ); } // Transaction / transfer → arrows if (t.includes('transaction') || t.includes('transfer')) { return ( ); } // Suspense / pending / unmatched → alert circle if (t.includes('suspense') || t.includes('unmatched') || t.includes('pending')) { return ( ); } // Works / music → music note if (t.includes('work') || t.includes('music') || t.includes('song')) { return ( ); } // Default fallback → bar chart return ( ); } export default function ChartCard({ title, children, className = '', tabs, legendItems, onLegendToggle, }: ChartCardProps) { const [activeTabIndex, setActiveTabIndex] = useState(0); const hasTabs = tabs && tabs.length > 0; return (