import { useState, useEffect, useRef } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Settings, Trash2, Maximize2, Download } from "lucide-react"; import { AreaChart, Area, BarChart, Bar, LineChart, Line, PieChart, Pie, Cell, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from "recharts"; import { exportToExcel, exportToPDF, exportToCSV } from "../../lib/export-utils"; // Type for dataKey to work around TypeScript restrictions type DataKeyType = string | number | ((obj: any) => any); export interface DataPoint { [key: string]: any; } export interface WidgetProps { id: string; title: string; type: "bar" | "line" | "pie" | "area" | "table" | "number"; data: DataPoint[]; config: { xAxis?: string; yAxis?: string | string[]; dimensions?: string[]; metrics?: string[]; colors?: string[]; showLegend?: boolean; stacked?: boolean; precision?: number; // New chart options showGrid?: boolean; showTooltip?: boolean; enableAnimation?: boolean; valueFormatter?: "none" | "number" | "percent" | "currency"; currencySymbol?: string; minValue?: number | null; maxValue?: number | null; }; onEdit: (id: string) => void; onDelete: (id: string) => void; } const COLORS = [ "#8884d8", "#82ca9d", "#ffc658", "#ff8042", "#0088fe", "#00C49F", "#FFBB28", "#FF8042", "#a4de6c", "#d0ed57" ]; export function DashboardWidget({ id, title, type, data, config, onEdit, onDelete }: WidgetProps) { const [expanded, setExpanded] = useState(false); const contentRef = useRef(null); // Format data for number type const calculateNumber = () => { if (!data || data.length === 0 || !config.metrics || config.metrics.length === 0) return 0; const metric = config.metrics[0]; let value = 0; // Sum all values for the metric data.forEach(item => { if (item[metric] !== undefined && !isNaN(Number(item[metric]))) { value += Number(item[metric]); } }); // Apply precision if specified if (config.precision !== undefined) { return value.toFixed(config.precision); } return value; }; const handleExport = (format: 'excel' | 'pdf' | 'csv') => { switch (format) { case 'excel': exportToExcel(data, title); break; case 'pdf': exportToPDF(data, title, config); break; case 'csv': exportToCSV(data, title); break; } }; // Render a table const renderTable = () => { if (!data || data.length === 0) return

No data available

; // Determine columns from first data point and config const columns = Object.keys(data[0]) .filter(key => { if (config.dimensions && config.metrics) { return [...config.dimensions, ...config.metrics].includes(key); } return true; }); return (
{columns.map((column) => ( ))} {data.map((item, idx) => ( {columns.map((column) => ( ))} ))}
{column}
{item[column] !== undefined ? String(item[column]) : '—'}
); }; // Render different chart types const renderChart = () => { if (!data || data.length === 0) return

No data available

; const { xAxis, yAxis, dimensions, metrics, colors = COLORS, showLegend = true, stacked = false } = config; const xAxisKey = xAxis as string; switch (type) { case 'bar': return ( {showLegend && } {Array.isArray(yAxis) && yAxis.length > 0 ? yAxis.map((axis, index) => ( )) : yAxis ? : null } ); case 'line': return ( {showLegend && } {Array.isArray(yAxis) && yAxis.length > 0 ? yAxis.map((axis, index) => ( )) : yAxis ? : null } ); case 'area': return ( {showLegend && } {Array.isArray(yAxis) && yAxis.length > 0 ? yAxis.map((axis, index) => ( )) : yAxis ? : null } ); case 'pie': // For pie charts, we need to process the data differently const pieData = data.map(item => { const dimensionField = dimensions?.[0] || xAxis || "name"; const metricField = Array.isArray(metrics) && metrics.length > 0 ? metrics[0] : (Array.isArray(yAxis) && yAxis.length > 0 ? yAxis[0] : (typeof yAxis === 'string' ? yAxis : "value")); const name = item[dimensionField as string]; const value = item[metricField as string]; return { name, value: Number(value) }; }).filter(item => !isNaN(item.value)); return ( `${name}: ${(percent * 100).toFixed(0)}%`} outerRadius={80} fill="#8884d8" dataKey="value" > {pieData.map((entry, index) => ( ))} [`${value}`, ""]} /> {showLegend && } ); case 'number': return (

{calculateNumber()}

{config.metrics && config.metrics.length > 0 && (

{config.metrics[0]}

)}
); case 'table': default: return renderTable(); } }; return ( {title}
{renderChart()}
); }