Files
MOPC-Portal/src/components/charts/status-breakdown.tsx

132 lines
3.3 KiB
TypeScript
Raw Normal View History

'use client'
import {
PieChart,
Pie,
Cell,
Tooltip,
Legend,
ResponsiveContainer,
} from 'recharts'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
interface StatusDataPoint {
status: string
count: number
}
interface StatusBreakdownProps {
data: StatusDataPoint[]
}
const STATUS_COLORS: Record<string, string> = {
PENDING: '#8884d8',
UNDER_REVIEW: '#82ca9d',
SHORTLISTED: '#ffc658',
SEMIFINALIST: '#ff7300',
FINALIST: '#00C49F',
WINNER: '#0088FE',
ELIMINATED: '#de0f1e',
WITHDRAWN: '#999999',
}
const renderCustomLabel = ({
cx,
cy,
midAngle,
innerRadius,
outerRadius,
percent,
}: {
cx?: number
cy?: number
midAngle?: number
innerRadius?: number
outerRadius?: number
percent?: number
}) => {
if (cx === undefined || cy === undefined || midAngle === undefined ||
innerRadius === undefined || outerRadius === undefined || percent === undefined) {
return null
}
if (percent < 0.05) return null // Don't show labels for small slices
const RADIAN = Math.PI / 180
const radius = innerRadius + (outerRadius - innerRadius) * 0.5
const x = cx + radius * Math.cos(-midAngle * RADIAN)
const y = cy + radius * Math.sin(-midAngle * RADIAN)
return (
<text
x={x}
y={y}
fill="white"
textAnchor={x > cx ? 'start' : 'end'}
dominantBaseline="central"
fontSize={12}
fontWeight={600}
>
{`${(percent * 100).toFixed(0)}%`}
</text>
)
}
export function StatusBreakdownChart({ data }: StatusBreakdownProps) {
const total = data.reduce((sum, item) => sum + item.count, 0)
// Format status for display
const formattedData = data.map((d) => ({
...d,
name: d.status.replace(/_/g, ' '),
color: STATUS_COLORS[d.status] || '#8884d8',
}))
return (
<Card>
<CardHeader>
<CardTitle className="flex items-center justify-between">
<span>Project Status Distribution</span>
<span className="text-sm font-normal text-muted-foreground">
{total} projects
</span>
</CardTitle>
</CardHeader>
<CardContent>
<div className="h-[300px]">
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Pie
data={formattedData}
cx="50%"
cy="50%"
labelLine={false}
label={renderCustomLabel}
outerRadius={100}
innerRadius={50}
fill="#8884d8"
dataKey="count"
nameKey="name"
>
{formattedData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
<Tooltip
contentStyle={{
backgroundColor: 'hsl(var(--card))',
border: '1px solid hsl(var(--border))',
borderRadius: '6px',
}}
formatter={(value: number | undefined, name: string | undefined) => [
`${value ?? 0} (${(((value ?? 0) / total) * 100).toFixed(1)}%)`,
name ?? '',
]}
/>
<Legend />
</PieChart>
</ResponsiveContainer>
</div>
</CardContent>
</Card>
)
}