* feat: Add new API handlers for user, project, and task management; update package dependencies * feat: Update .gitignore, add Lambda layer configuration, and refactor DynamoDB handlers to use AWS SDK v3 * feat: Update serverless configuration and refactor API handlers to improve error handling and response structure * feat: Add Cognito user pool name parameter and update API handlers to include CORS headers * feat: Update task and project ID formats, add populateSeedData function, and enhance user ID handling * feat: Update image source paths to use S3 public URL for profile and task attachments
210 lines
6.2 KiB
TypeScript
210 lines
6.2 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
"use client";
|
|
|
|
import {
|
|
Priority,
|
|
Project,
|
|
Task,
|
|
useGetAuthUserQuery,
|
|
useGetProjectsQuery,
|
|
useGetTasksByUserQuery,
|
|
} from "@/state/api";
|
|
import React from "react";
|
|
import { useAppSelector } from "../redux";
|
|
import { DataGrid, GridColDef } from "@mui/x-data-grid";
|
|
import Header from "@/app/components/Header";
|
|
import {
|
|
Bar,
|
|
BarChart,
|
|
CartesianGrid,
|
|
Cell,
|
|
Legend,
|
|
Pie,
|
|
PieChart,
|
|
ResponsiveContainer,
|
|
Tooltip,
|
|
XAxis,
|
|
YAxis,
|
|
} from "recharts";
|
|
import { dataGridSxStyles } from "@/lib/utils";
|
|
|
|
const taskColumns: GridColDef[] = [
|
|
{ field: "title", headerName: "Title", width: 200 },
|
|
{
|
|
field: "status",
|
|
headerName: "Status",
|
|
width: 150,
|
|
renderCell: (params) => (
|
|
<span
|
|
className={`inline-flex rounded-full px-2 text-xs font-semibold leading-5 ${
|
|
params.value === "In Progress"
|
|
? "bg-green-200 text-green-600"
|
|
: params.value === "Test/Review"
|
|
? "bg-green-200 text-green-600"
|
|
: params.value === "Done"
|
|
? "bg-green-400 text-green-800"
|
|
: "bg-gray-400 text-gray-800"
|
|
}`}
|
|
>
|
|
{params.value}
|
|
</span>
|
|
),
|
|
},
|
|
{
|
|
field: "priority",
|
|
headerName: "Priority",
|
|
width: 150,
|
|
renderCell: (params) => (
|
|
<span
|
|
className={`inline-flex rounded-full px-2 text-xs font-semibold leading-5 ${
|
|
params.value === "Urgent"
|
|
? "bg-red-200 text-red-700"
|
|
: params.value === "High"
|
|
? "bg-yellow-200 text-yellow-700"
|
|
: params.value === "Medium"
|
|
? "bg-green-200 text-green-700"
|
|
: params.value === "Low"
|
|
? "bg-blue-200 text-blue-700"
|
|
: "bg-gray-200 text-gray-700"
|
|
}`}
|
|
>
|
|
{params.value}
|
|
</span>
|
|
),
|
|
},
|
|
{ field: "dueDate", headerName: "Due Date", width: 150 },
|
|
];
|
|
|
|
const statusColors = ["#0088FE", "#00C49F", "#FFBB28", "#FF8042"];
|
|
|
|
const HomePage = () => {
|
|
const { data: currentUser } = useGetAuthUserQuery({});
|
|
const userId = currentUser?.userDetails?.userId ?? null;
|
|
const {
|
|
data: tasks,
|
|
isLoading: tasksLoading,
|
|
isError: tasksError,
|
|
} = useGetTasksByUserQuery(userId || "", {
|
|
skip: userId === null,
|
|
});
|
|
const { data: projects, isLoading: isProjectsLoading } =
|
|
useGetProjectsQuery();
|
|
|
|
const isDarkMode = useAppSelector((state) => state.global.isDarkMode);
|
|
|
|
if (tasksLoading || isProjectsLoading) return <div>Loading..</div>;
|
|
if (tasksError || !tasks || !projects) return <div>Error fetching data</div>;
|
|
|
|
const priorityCount = tasks.reduce(
|
|
(acc: Record<string, number>, task: Task) => {
|
|
const { priority } = task;
|
|
acc[priority as Priority] = (acc[priority as Priority] || 0) + 1;
|
|
return acc;
|
|
},
|
|
{},
|
|
);
|
|
|
|
const taskDistribution = Object.keys(priorityCount).map((key) => ({
|
|
name: key,
|
|
count: priorityCount[key],
|
|
}));
|
|
|
|
const statusCount = projects.reduce(
|
|
(acc: Record<string, number>, project: Project) => {
|
|
const status = project.endDate ? "Completed" : "Active";
|
|
acc[status] = (acc[status] || 0) + 1;
|
|
return acc;
|
|
},
|
|
{},
|
|
);
|
|
|
|
const projectStatus = Object.keys(statusCount).map((key) => ({
|
|
name: key,
|
|
count: statusCount[key],
|
|
}));
|
|
|
|
const chartColors = isDarkMode
|
|
? {
|
|
bar: "#8884d8",
|
|
barGrid: "#303030",
|
|
pieFill: "#4A90E2",
|
|
text: "#FFFFFF",
|
|
}
|
|
: {
|
|
bar: "#8884d8",
|
|
barGrid: "#E0E0E0",
|
|
pieFill: "#82ca9d",
|
|
text: "#000000",
|
|
};
|
|
|
|
return (
|
|
<div className="container h-full w-[100%] bg-gray-100 bg-transparent p-8">
|
|
<Header name="Project Management Dashboard" />
|
|
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
<div className="rounded-lg bg-white p-4 shadow dark:bg-dark-secondary">
|
|
<h3 className="mb-4 text-lg font-semibold dark:text-white">
|
|
Task Priority Distribution
|
|
</h3>
|
|
<ResponsiveContainer width="100%" height={300}>
|
|
<BarChart data={taskDistribution}>
|
|
<CartesianGrid
|
|
strokeDasharray="3 3"
|
|
stroke={chartColors.barGrid}
|
|
/>
|
|
<XAxis dataKey="name" stroke={chartColors.text} />
|
|
<YAxis stroke={chartColors.text} />
|
|
<Tooltip
|
|
contentStyle={{
|
|
width: "min-content",
|
|
height: "min-content",
|
|
}}
|
|
/>
|
|
<Legend />
|
|
<Bar dataKey="count" fill={chartColors.bar} />
|
|
</BarChart>
|
|
</ResponsiveContainer>
|
|
</div>
|
|
<div className="rounded-lg bg-white p-4 shadow dark:bg-dark-secondary">
|
|
<h3 className="mb-4 text-lg font-semibold dark:text-white">
|
|
Project Status
|
|
</h3>
|
|
<ResponsiveContainer width="100%" height={300}>
|
|
<PieChart>
|
|
<Pie dataKey="count" data={projectStatus} fill="#82ca9d" label>
|
|
{projectStatus.map((entry, index) => (
|
|
<Cell
|
|
key={`cell-${index}`}
|
|
fill={statusColors[index % statusColors.length]}
|
|
/>
|
|
))}
|
|
</Pie>
|
|
<Tooltip />
|
|
<Legend />
|
|
</PieChart>
|
|
</ResponsiveContainer>
|
|
</div>
|
|
<div className="rounded-lg bg-white p-4 shadow dark:bg-dark-secondary md:col-span-2">
|
|
<h3 className="mb-4 text-lg font-semibold dark:text-white">
|
|
Your Tasks
|
|
</h3>
|
|
<div style={{ height: 400, width: "100%" }}>
|
|
<DataGrid
|
|
rows={tasks}
|
|
columns={taskColumns}
|
|
checkboxSelection
|
|
loading={tasksLoading}
|
|
getRowId={(row) => row.taskId}
|
|
getRowClassName={() => "data-grid-row"}
|
|
getCellClassName={() => "data-grid-cell"}
|
|
className="border border-gray-200 bg-white shadow dark:border-stroke-dark dark:bg-dark-secondary dark:text-gray-200"
|
|
sx={dataGridSxStyles(isDarkMode)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default HomePage;
|