import { Card } from './Card';
import {
BarChart,
Bar,
LineChart,
Line,
AreaChart,
Area,
PieChart,
Pie,
Cell,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer,
Legend
} from 'recharts';
export interface ChartRow {
dimension: string;
value: number;
series?: string;
}
export interface ChartConfig {
chart_uid: string;
key: string;
rows: ChartRow[];
}
export interface AnalyticsChartProps {
data?: unknown[];
gridCols?: number;
}
export function AnalyticsChart({ data, gridCols }: AnalyticsChartProps) {
if (!data || !Array.isArray(data) || data.length === 0) return null;
const charts = data as ChartConfig[];
const colors = ['#3B82F6', '#10B981', '#F59E0B', '#8B5CF6', '#EC4899', '#14B8A6'];
const gridClass = gridCols === 1
? "grid grid-cols-1 gap-6 w-full"
: gridCols === 2
? "grid grid-cols-1 lg:grid-cols-2 gap-6 w-full"
: "grid grid-cols-1 lg:grid-cols-3 gap-6 w-full";
return (
{charts.map((chart, idx) => {
const title = (chart.key || `Chart ${idx + 1}`)
.replace(/_/g, ' ')
.replace(/\b\w/g, c => c.toUpperCase());
const hasSeries = chart.rows?.some(r => r.series);
let finalData: any[] = [];
let seriesKeys: string[] = [];
if (hasSeries) {
const grouped = new Map
();
const sKeys = new Set();
chart.rows?.forEach(r => {
const dim = String(r.dimension || '').trim() || 'Unknown';
const s = String(r.series || '').trim() || 'Unknown';
sKeys.add(s);
if (!grouped.has(dim)) {
grouped.set(dim, { dimension: dim });
}
const entry = grouped.get(dim);
entry[s] = Number(r.value || 0);
});
finalData = Array.from(grouped.values());
seriesKeys = Array.from(sKeys).sort(); // Sort series keys for consistency
} else {
finalData = (chart.rows || []).map(r => ({
...r,
dimension: String(r.dimension || '').trim() || 'Unknown',
value: Number(r.value || 0)
}));
seriesKeys = ['value'];
}
if (finalData.length === 0) return null;
const lowerKey = String(chart.key || '').toLowerCase();
let chartType = idx % 3; // 0 = Bar, 1 = Line, 2 = Area
if (lowerKey.includes('status')) {
chartType = 3; // Pie
} else if (lowerKey.includes('route')) {
chartType = 4; // Donut
} else if (lowerKey.includes('brand') || lowerKey.includes('product')) {
chartType = 5; // Table
} else if (lowerKey.includes('monthly') || lowerKey.includes('yearly')) {
chartType = 0; // Bar
}
// Determine if it should span full width
const isFullWidth = (gridCols === 1) || (!gridCols && charts.length <= 2) || hasSeries || lowerKey.includes('monthly') || lowerKey.includes('yearly') || finalData.length > 8;
const fullSpanClass = gridCols === 2 ? 'lg:col-span-2' : 'lg:col-span-3';
// Calculate a dynamic width if there's a lot of data to allow horizontal scrolling
const minChartWidth = chartType === 0 || chartType === 1 || chartType === 2
? Math.max(100, finalData.length * (hasSeries ? seriesKeys.length * 15 + 40 : 60))
: 100;
const renderChartContent = () => {
return (
<>
{hasSeries && }
{seriesKeys.map((key, i) => {
const color = colors[i % colors.length];
if (chartType === 1) {
return (
);
} else if (chartType === 2) {
return (
);
} else {
const barFill = i === 0 ? `url(#barGradient-${idx}-0)` : (i === 1 ? `url(#barGradient-${idx}-1)` : color);
return (
);
}
})}
>
);
};
return (
100 ? 'overflow-x-auto scrollbar-slim' : ''}`}>
{chartType === 5 ? (
| Dimension |
{seriesKeys.map(k => (
{hasSeries ? k : 'Value'} |
))}
{finalData.filter(row => seriesKeys.some(k => row[k] > 0)).map((row, rIdx) => (
| {row.dimension} |
{seriesKeys.map(k => (
{row[k]} |
))}
))}
) : (
100 ? `${minChartWidth}px` : '100%', height: '100%' }}>
{chartType === 3 || chartType === 4 ? (
{finalData.map((_, index) => (
|
))}
) : chartType === 1 ? (
{renderChartContent()}
) : chartType === 2 ? (
{renderChartContent()}
) : (
{renderChartContent()}
)}
)}
);
})}
);
}