import { useState } from 'react'; import { motion } from 'framer-motion'; import { BarChart3, TrendingUp } from 'lucide-react'; import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, LineElement, PointElement, ArcElement, Tooltip, Legend, Filler } from 'chart.js'; import { Bar, Line } from 'react-chartjs-2'; ChartJS.register(CategoryScale, LinearScale, BarElement, LineElement, PointElement, ArcElement, Tooltip, Legend, Filler); const COLORS = [ 'rgba(99,102,241,0.85)', // brand indigo 'rgba(6,182,212,0.85)', // cyan 'rgba(167,139,250,0.85)', // violet 'rgba(52,211,153,0.85)', // success (emerald) 'rgba(251,191,36,0.85)', // warning 'rgba(248,113,113,0.85)', // danger ]; function inferType(rows, cols) { return cols.some(c => /date|month|year|day|time|quarter|week/i.test(c)) ? 'line' : 'bar'; } function buildDataset(rows, type) { const cols = Object.keys(rows[0] ?? {}); const numericCols = cols.filter(c => rows.every(r => r[c] == null || !isNaN(Number(r[c])))); const labelCol = cols.find(c => !numericCols.includes(c)) ?? cols[0]; const valueCol = numericCols[0] ?? cols[1] ?? cols[0]; const labels = rows.slice(0, 24).map(r => String(r[labelCol] ?? '').slice(0, 30)); const data = rows.slice(0, 24).map(r => Number(r[valueCol] ?? 0)); return { labels, datasets: [{ label: valueCol.replace(/_/g, ' '), data, borderColor: COLORS[0], backgroundColor: type === 'line' ? 'rgba(99,102,241,0.08)' : COLORS.map(c => c), pointBackgroundColor: COLORS[1], borderWidth: 2, borderRadius: type === 'bar' ? 6 : 0, tension: 0.4, fill: type === 'line', pointRadius: type === 'line' ? 3 : 0, pointHoverRadius: 6, }] }; } const CHART_OPTIONS = { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: true, position: 'top', align: 'end', labels: { color: 'rgba(255,255,255,0.4)', font: { family: 'Inter', size: 11, weight: '500' }, boxWidth: 10, boxHeight: 10, borderRadius: 2, useBorderRadius: true, padding: 12, }, }, tooltip: { backgroundColor: 'var(--surface-05)', borderColor: 'var(--border-1)', borderWidth: 1, titleColor: 'rgba(255,255,255,0.95)', bodyColor: 'rgba(255,255,255,0.7)', padding: 10, cornerRadius: 6, titleFont: { weight: '600', family: 'Inter', size: 11 }, bodyFont: { family: 'Inter', size: 11 }, displayColors: true, boxPadding: 4, }, }, scales: { x: { ticks: { color: 'rgba(255,255,255,0.3)', font: { size: 10, family: 'Inter' }, maxRotation: 45 }, grid: { color: 'rgba(255,255,255,0.02)', drawBorder: false }, }, y: { ticks: { color: 'rgba(255,255,255,0.3)', font: { size: 10, family: 'Inter' } }, grid: { color: 'rgba(255,255,255,0.03)', drawBorder: false }, }, }, }; export default function ChartView({ rows }) { const cols = Object.keys(rows?.[0] ?? {}); const numericCols = cols.filter(c => rows.every(r => r[c] == null || !isNaN(Number(r[c])))); const [type, setType] = useState(() => inferType(rows, cols)); if (!rows?.length || !numericCols.length || cols.length < 2) return null; const dataset = buildDataset(rows, type); return (
Visualization
{[ { key: 'bar', icon: BarChart3, label: 'Bar' }, { key: 'line', icon: TrendingUp, label: 'Line' }, ].map(t => ( ))}
{type === 'bar' ? : }
); }