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.
This commit is contained in:
137
src/components/ui/TaskStatusChart.js
Normal file
137
src/components/ui/TaskStatusChart.js
Normal file
@@ -0,0 +1,137 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user