- 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.
138 lines
3.4 KiB
JavaScript
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>
|
|
);
|
|
}
|