Files
road-monitoring-ui/src/components/dashboard/detection-donut-chart.tsx

157 lines
3.8 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"
interface DetectionDonutChartProps {
defectedSignboard: number
pothole: number
roadCrack: number
damagedRoadMarking: number
goodSignboard: number
isLoading?: boolean
}
const chartConfig = {
pothole: {
label: "Potholes",
color: "var(--chart-1)",
},
defectedSignboard: {
label: "Defected Signboards",
color: "var(--chart-2)",
},
roadCrack: {
label: "Road Cracks",
color: "var(--chart-3)",
},
damagedRoadMarking: {
label: "Damaged Markings",
color: "var(--chart-4)",
},
goodSignboard: {
label: "Good Signboards",
color: "var(--chart-5)",
},
} satisfies ChartConfig
export function DetectionDonutChart({
defectedSignboard,
pothole,
roadCrack,
damagedRoadMarking,
goodSignboard,
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)",
},
].filter((item) => item.count > 0),
[
pothole,
defectedSignboard,
roadCrack,
damagedRoadMarking,
goodSignboard,
]
)
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>
)
}