feat: complete dashboard widgets, fix tooltips, integrate chatbot, and update docs
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user