Files
panel/src/components/ui/TaskStatusChart.js
Chop 27c5feb6dc feat: add TaskStatusChart component and integrate it into the home page
- Added recharts library to package.json for charting capabilities.
- Created TaskStatusChart component to visualize task statuses with a pie chart.
- Replaced the previous task status display with the new TaskStatusChart in the home page.
2025-06-19 23:51:45 +02:00

138 lines
3.4 KiB
JavaScript

"use client";
import {
PieChart,
Pie,
Cell,
ResponsiveContainer,
Legend,
Tooltip,
} from "recharts";
const COLORS = {
pending: "#eab308", // yellow-500
in_progress: "#3b82f6", // blue-500
completed: "#22c55e", // green-500
overdue: "#ef4444", // red-500
};
const STATUS_LABELS = {
pending: "Pending",
in_progress: "In Progress",
completed: "Completed",
overdue: "Overdue",
};
const CustomTooltip = ({ active, payload }) => {
if (active && payload && payload.length) {
const data = payload[0].payload;
return (
<div className="bg-white p-3 border border-gray-200 rounded-lg shadow-lg">
<p className="text-sm font-medium text-gray-900">
{STATUS_LABELS[data.status]}
</p>
<p className="text-sm text-gray-600">
{data.count} tasks ({data.percentage}%)
</p>
</div>
);
}
return null;
};
const CustomLegend = ({ payload }) => {
return (
<ul className="flex flex-wrap justify-center gap-4 mt-4">
{payload.map((entry, index) => (
<li key={index} className="flex items-center gap-2">
<div
className="w-3 h-3 rounded-full"
style={{ backgroundColor: entry.color }}
/>
<span className="text-xs text-gray-700">
{STATUS_LABELS[entry.payload.status]} ({entry.payload.count})
</span>
</li>
))}
</ul>
);
};
export default function TaskStatusChart({ data, completionRate }) {
// Calculate total and percentages
const total = data.reduce((sum, item) => sum + item.count, 0);
const chartData = data.map((item) => ({
...item,
percentage: total > 0 ? Math.round((item.count / total) * 100) : 0,
}));
if (total === 0) {
return (
<div className="flex items-center justify-center h-48 text-gray-500">
<div className="text-center">
<svg
className="w-12 h-12 mx-auto mb-2 text-gray-300"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
/>
</svg>
<p className="text-sm">No tasks available</p>
</div>
</div>
);
}
return (
<div className="w-full">
<ResponsiveContainer width="100%" height={200}>
<PieChart>
<Pie
data={chartData}
cx="50%"
cy="50%"
innerRadius={40}
outerRadius={80}
paddingAngle={2}
dataKey="count"
>
{chartData.map((entry, index) => (
<Cell
key={`cell-${index}`}
fill={COLORS[entry.status]}
stroke="#fff"
strokeWidth={2}
/>
))}
</Pie>
<Tooltip content={<CustomTooltip />} />
</PieChart>
</ResponsiveContainer>
<CustomLegend
payload={chartData.map((item) => ({
color: COLORS[item.status],
payload: item,
}))}
/>
<div className="mt-4 pt-4 border-t border-gray-200">
<div className="flex items-center justify-between text-sm">
<span className="text-gray-600">Total Tasks</span>
<span className="font-bold text-gray-900">{total}</span>
</div>
<div className="flex items-center justify-between text-sm mt-1">
<span className="text-gray-600">Completion Rate</span>
<span className="font-bold text-green-600">{completionRate}%</span>
</div>
</div>
</div>
);
}