99 lines
3.5 KiB
React
99 lines
3.5 KiB
React
import React, { useMemo } from 'react';
|
|
import { PieChart, Pie, Cell, Tooltip, Legend, ResponsiveContainer } from 'recharts';
|
|
import { SpotlightCard } from '../SpotlightCard';
|
|
import { InfoTooltip } from '../InfoTooltip';
|
|
|
|
const COLORS = {
|
|
'Excellent': '#10b981', // emerald-500
|
|
'Good': '#3b82f6', // blue-500
|
|
'Fair': '#eab308', // yellow-500
|
|
'Needs Repair': '#ef4444' // red-500
|
|
};
|
|
|
|
const CustomTooltip = ({ active, payload }) => {
|
|
if (active && payload && payload.length) {
|
|
return (
|
|
<div className="bg-zinc-900/90 border border-zinc-700/50 p-3 rounded-lg shadow-xl backdrop-blur-md">
|
|
<p className="text-zinc-100 font-bold mb-1">{payload[0].name}</p>
|
|
<p className="text-zinc-400 text-xs">
|
|
{payload[0].value} Properties ({((payload[0].payload.percent || 0) * 100).toFixed(0)}%)
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|
|
return null;
|
|
};
|
|
|
|
export const RoofConditionChart = ({ properties }) => {
|
|
const data = useMemo(() => {
|
|
const counts = properties.reduce((acc, p) => {
|
|
const condition = p.propertyData.roofCondition || 'Unknown';
|
|
acc[condition] = (acc[condition] || 0) + 1;
|
|
return acc;
|
|
}, {});
|
|
|
|
const total = properties.length;
|
|
|
|
return Object.keys(counts).map(key => ({
|
|
name: key,
|
|
value: counts[key],
|
|
percent: counts[key] / total
|
|
}));
|
|
}, [properties]);
|
|
|
|
return (
|
|
<SpotlightCard className="h-full flex flex-col">
|
|
<div className="p-6 flex-1 flex flex-col">
|
|
<div className="flex items-center justify-between mb-2">
|
|
<div>
|
|
<h3 className="text-lg font-bold text-zinc-900 dark:text-white flex items-center">
|
|
Roof Condition
|
|
<InfoTooltip text="Breakdown of roof health across all properties. Data derived from recent inspections and aerial imagery." />
|
|
</h3>
|
|
<p className="text-xs text-zinc-500 dark:text-zinc-400">Territory health snapshot</p>
|
|
</div>
|
|
{/* Legend / Key - simpler than Recharts legend */}
|
|
<div className="flex gap-2">
|
|
<div className="flex items-center gap-1 text-[10px] text-zinc-500">
|
|
<div className="w-2 h-2 rounded-full bg-emerald-500"></div> Good
|
|
</div>
|
|
<div className="flex items-center gap-1 text-[10px] text-zinc-500">
|
|
<div className="w-2 h-2 rounded-full bg-red-500"></div> Bad
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex-1 w-full min-h-[250px] relative">
|
|
<ResponsiveContainer width="100%" height="100%">
|
|
<PieChart>
|
|
<Pie
|
|
data={data}
|
|
cx="50%"
|
|
cy="50%"
|
|
innerRadius={60}
|
|
outerRadius={80}
|
|
paddingAngle={5}
|
|
dataKey="value"
|
|
stroke="none"
|
|
>
|
|
{data.map((entry, index) => (
|
|
<Cell key={`cell-${index}`} fill={COLORS[entry.name] || '#6b7280'} />
|
|
))}
|
|
</Pie>
|
|
<Tooltip content={<CustomTooltip />} />
|
|
</PieChart>
|
|
</ResponsiveContainer>
|
|
|
|
{/* Center Text */}
|
|
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
|
|
<div className="text-center">
|
|
<span className="block text-3xl font-black text-zinc-900 dark:text-white">{properties.length}</span>
|
|
<span className="text-[10px] uppercase font-bold text-zinc-500 tracking-wider">Total</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</SpotlightCard>
|
|
);
|
|
};
|