localgreenchain/components/analytics/charts/Gauge.tsx
Claude 816c3b3f2e
Implement Agent 7: Advanced Analytics Dashboard
Add comprehensive analytics system with:
- Analytics data layer (aggregator, metrics, trends, cache)
- 6 API endpoints (overview, plants, transport, farms, sustainability, export)
- 6 chart components (LineChart, BarChart, PieChart, AreaChart, Gauge, Heatmap)
- 5 dashboard widgets (KPICard, TrendIndicator, DataTable, DateRangePicker, FilterPanel)
- 5 dashboard pages (overview, plants, transport, farms, sustainability)
- Export functionality (CSV, JSON)

Dependencies added: recharts, d3, date-fns

Also includes minor fixes:
- Fix EnvironmentalForm spread type error
- Fix AgentOrchestrator Map iteration issues
- Fix next.config.js image domains undefined error
- Add downlevelIteration to tsconfig
2025-11-23 04:02:07 +00:00

91 lines
2.4 KiB
TypeScript

/**
* Gauge Chart Component
* Displays a single value as a gauge/meter
*/
import { PieChart, Pie, Cell, ResponsiveContainer } from 'recharts';
interface GaugeProps {
value: number;
max?: number;
title?: string;
unit?: string;
size?: number;
colors?: { low: string; medium: string; high: string };
thresholds?: { low: number; high: number };
}
const DEFAULT_COLORS = {
low: '#ef4444',
medium: '#f59e0b',
high: '#10b981',
};
export default function Gauge({
value,
max = 100,
title,
unit = '%',
size = 200,
colors = DEFAULT_COLORS,
thresholds = { low: 33, high: 66 },
}: GaugeProps) {
const percentage = Math.min((value / max) * 100, 100);
// Determine color based on thresholds
let color: string;
if (percentage < thresholds.low) {
color = colors.low;
} else if (percentage < thresholds.high) {
color = colors.medium;
} else {
color = colors.high;
}
// Data for semi-circle gauge
const gaugeData = [
{ value: percentage, color },
{ value: 100 - percentage, color: '#e5e7eb' },
];
return (
<div className="bg-white rounded-lg shadow-lg p-6 flex flex-col items-center">
{title && <h3 className="text-lg font-bold text-gray-900 mb-2">{title}</h3>}
<div className="relative" style={{ width: size, height: size / 2 + 20 }}>
<ResponsiveContainer width="100%" height={size}>
<PieChart>
<Pie
data={gaugeData}
cx="50%"
cy="100%"
startAngle={180}
endAngle={0}
innerRadius={size * 0.3}
outerRadius={size * 0.4}
paddingAngle={0}
dataKey="value"
>
{gaugeData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} stroke="none" />
))}
</Pie>
</PieChart>
</ResponsiveContainer>
<div
className="absolute inset-0 flex flex-col items-center justify-end pb-2"
style={{ top: size * 0.2 }}
>
<span className="text-3xl font-bold" style={{ color }}>
{value.toFixed(1)}
</span>
<span className="text-sm text-gray-500">{unit}</span>
</div>
</div>
<div className="flex justify-between w-full mt-2 px-4 text-xs text-gray-500">
<span>0</span>
<span>{max / 2}</span>
<span>{max}</span>
</div>
</div>
);
}