177 lines
4.6 KiB
TypeScript
177 lines
4.6 KiB
TypeScript
'use client';
|
|
|
|
import * as React from 'react';
|
|
import { Loader2 } from 'lucide-react';
|
|
import { Label, Pie, PieChart } from 'recharts';
|
|
|
|
import {
|
|
ChartContainer,
|
|
ChartTooltip,
|
|
ChartTooltipContent,
|
|
type ChartConfig,
|
|
} from '@/components/ui/chart';
|
|
import { DETECTION_TYPES } from '@/constants/detectionModeConfig';
|
|
|
|
interface DetectionDonutChartProps {
|
|
defectedSignboard: number;
|
|
pothole: number;
|
|
roadCrack: number;
|
|
damagedRoadMarking: number;
|
|
// goodSignboard: number;
|
|
drainIssue: number;
|
|
defectiveCulvert: number;
|
|
isLoading?: boolean;
|
|
}
|
|
|
|
const chartConfig = {
|
|
pothole: {
|
|
label: DETECTION_TYPES.pothole.label + 's',
|
|
color: DETECTION_TYPES.pothole.color,
|
|
},
|
|
defectedSignboard: {
|
|
label: DETECTION_TYPES.defected_sign_board.label + 's',
|
|
color: DETECTION_TYPES.defected_sign_board.color,
|
|
},
|
|
roadCrack: {
|
|
label: DETECTION_TYPES.road_crack.label + 's',
|
|
color: DETECTION_TYPES.road_crack.color,
|
|
},
|
|
damagedRoadMarking: {
|
|
label: DETECTION_TYPES.damaged_road_marking.label + 's',
|
|
color: DETECTION_TYPES.damaged_road_marking.color,
|
|
},
|
|
drainIssue: {
|
|
label: DETECTION_TYPES.drain_issue.label + 's',
|
|
color: DETECTION_TYPES.drain_issue.color,
|
|
},
|
|
defectiveCulvert: {
|
|
label: DETECTION_TYPES.defective_culvert.label + 's',
|
|
color: DETECTION_TYPES.defective_culvert.color,
|
|
},
|
|
} satisfies ChartConfig;
|
|
|
|
export function DetectionDonutChart({
|
|
defectedSignboard,
|
|
pothole,
|
|
roadCrack,
|
|
damagedRoadMarking,
|
|
// goodSignboard,
|
|
drainIssue,
|
|
defectiveCulvert,
|
|
isLoading = false,
|
|
}: DetectionDonutChartProps) {
|
|
const chartData = React.useMemo(
|
|
() =>
|
|
[
|
|
{ type: 'pothole', count: pothole, fill: 'var(--color-pothole)' },
|
|
{
|
|
type: 'defectedSignboard',
|
|
count: defectedSignboard,
|
|
fill: 'var(--color-defectedSignboard)',
|
|
},
|
|
{ type: 'roadCrack', count: roadCrack, fill: 'var(--color-roadCrack)' },
|
|
{
|
|
type: 'damagedRoadMarking',
|
|
count: damagedRoadMarking,
|
|
fill: 'var(--color-damagedRoadMarking)',
|
|
},
|
|
/* {
|
|
type: 'goodSignboard',
|
|
count: goodSignboard,
|
|
fill: 'var(--color-goodSignboard)',
|
|
}, */
|
|
{
|
|
type: 'drainIssue',
|
|
count: drainIssue,
|
|
fill: 'var(--color-drainIssue)',
|
|
},
|
|
{
|
|
type: 'defectiveCulvert',
|
|
count: defectiveCulvert,
|
|
fill: 'var(--color-defectiveCulvert)',
|
|
},
|
|
].filter((item) => item.count > 0),
|
|
[
|
|
pothole,
|
|
defectedSignboard,
|
|
roadCrack,
|
|
damagedRoadMarking,
|
|
// goodSignboard,
|
|
drainIssue,
|
|
defectiveCulvert,
|
|
],
|
|
);
|
|
|
|
const totalDetections = React.useMemo(() => {
|
|
return chartData.reduce((acc, curr) => acc + curr.count, 0);
|
|
}, [chartData]);
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="h-[250px] flex items-center justify-center">
|
|
<Loader2 className="h-8 w-8 text-primary/50 animate-spin" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (totalDetections === 0) {
|
|
return (
|
|
<div className="h-[250px] flex flex-col items-center justify-center text-muted-foreground">
|
|
<p className="text-sm">No detections found</p>
|
|
<p className="text-xs mt-1">Process videos to see data</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<ChartContainer
|
|
config={chartConfig}
|
|
className="mx-auto aspect-square max-h-[250px]"
|
|
>
|
|
<PieChart>
|
|
<ChartTooltip
|
|
cursor={false}
|
|
content={<ChartTooltipContent hideLabel />}
|
|
/>
|
|
<Pie
|
|
data={chartData}
|
|
dataKey="count"
|
|
nameKey="type"
|
|
innerRadius={60}
|
|
strokeWidth={5}
|
|
>
|
|
<Label
|
|
content={({ viewBox }) => {
|
|
if (viewBox && 'cx' in viewBox && 'cy' in viewBox) {
|
|
return (
|
|
<text
|
|
x={viewBox.cx}
|
|
y={viewBox.cy}
|
|
textAnchor="middle"
|
|
dominantBaseline="middle"
|
|
>
|
|
<tspan
|
|
x={viewBox.cx}
|
|
y={viewBox.cy}
|
|
className="fill-foreground text-3xl font-bold"
|
|
>
|
|
{totalDetections.toLocaleString()}
|
|
</tspan>
|
|
<tspan
|
|
x={viewBox.cx}
|
|
y={(viewBox.cy || 0) + 24}
|
|
className="fill-muted-foreground"
|
|
>
|
|
Total
|
|
</tspan>
|
|
</text>
|
|
);
|
|
}
|
|
}}
|
|
/>
|
|
</Pie>
|
|
</PieChart>
|
|
</ChartContainer>
|
|
);
|
|
}
|