import { DemandItem, DemandSignal } from '../../lib/demand/types'; interface SupplyGapChartProps { demandSignal: DemandSignal; showTopN?: number; } function formatCategory(category: string): string { return category.replace(/_/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase()); } export default function SupplyGapChart({ demandSignal, showTopN = 10 }: SupplyGapChartProps) { // Sort items by gap (largest gaps first) const sortedItems = [...demandSignal.demandItems] .filter((item) => item.gapKg > 0) .sort((a, b) => b.gapKg - a.gapKg) .slice(0, showTopN); // Find the max for scaling const maxDemand = Math.max(...demandSignal.demandItems.map((item) => item.weeklyDemandKg)); // Calculate overall stats const totalDemand = demandSignal.totalWeeklyDemandKg; const totalSupply = demandSignal.currentSupplyKg; const totalGap = demandSignal.supplyGapKg; const coveragePercentage = totalDemand > 0 ? ((totalSupply / totalDemand) * 100) : 100; if (demandSignal.demandItems.length === 0) { return (

Supply vs Demand

📊

No demand data available.

); } return (

Supply vs Demand

{demandSignal.region.name} - {demandSignal.seasonalPeriod}

{/* Overall summary */}

{totalDemand.toFixed(0)}

Weekly Demand (kg)

{totalSupply.toFixed(0)}

Current Supply (kg)

0 ? 'text-red-600' : 'text-green-600'}`}> {totalGap.toFixed(0)}

Gap (kg)

= 100 ? 'text-green-600' : coveragePercentage >= 80 ? 'text-yellow-600' : 'text-red-600' }`}> {coveragePercentage.toFixed(0)}%

Coverage

{/* Coverage bar */}
= 100 ? 'bg-green-500' : coveragePercentage >= 80 ? 'bg-yellow-500' : 'bg-red-500' }`} style={{ width: `${Math.min(coveragePercentage, 100)}%` }} >
{/* Gap items chart */} {sortedItems.length > 0 && (

Largest Supply Gaps

{sortedItems.map((item, index) => ( ))}
)} {/* Items with surplus */} {demandSignal.demandItems.filter(item => item.gapKg <= 0).length > 0 && (

Well Supplied Items

{demandSignal.demandItems .filter(item => item.gapKg <= 0) .slice(0, 8) .map((item, index) => ( {item.produceType} ))}
)} {/* Legend */}
Demand
Supply
Gap
); } function SupplyGapBar({ item, maxDemand }: { item: DemandItem; maxDemand: number }) { const demandWidth = (item.weeklyDemandKg / maxDemand) * 100; const supplyWidth = (item.matchedSupply / maxDemand) * 100; const gapPercentage = item.weeklyDemandKg > 0 ? ((item.gapKg / item.weeklyDemandKg) * 100) : 0; return (
{item.produceType} {formatCategory(item.category)}
50 ? 'text-red-600' : 'text-yellow-600'}`}> -{item.gapKg.toFixed(1)} kg ({gapPercentage.toFixed(0)}% gap)
{/* Stacked bar */}
{/* Supply (filled portion) */}
{/* Gap (unfilled portion shown as demand outline) */}
{/* Gap indicator */}
{/* Values */}
Supply: {item.matchedSupply.toFixed(0)} kg Demand: {item.weeklyDemandKg.toFixed(0)} kg
); }