From e5e565430cb2af13155f7427c584bc4e3d0d90f1 Mon Sep 17 00:00:00 2001 From: Thastertyn Date: Mon, 23 Jun 2025 00:09:21 +0200 Subject: [PATCH] Task detail page finalized and added user loading skeleton --- src/components/Task.tsx | 7 +-- src/components/TaskDetail.tsx | 96 ++++++++++++++++++++++++++++++++++- src/components/TaskForm.tsx | 5 +- src/components/TaskList.tsx | 11 +++- src/css/MainPage.css | 11 ++++ src/pages/MainPage.tsx | 4 +- src/pages/TaskPage.tsx | 15 +----- 7 files changed, 126 insertions(+), 23 deletions(-) diff --git a/src/components/Task.tsx b/src/components/Task.tsx index 46b5147..5ee443c 100644 --- a/src/components/Task.tsx +++ b/src/components/Task.tsx @@ -24,7 +24,7 @@ const TaskComponent: React.FC = ({ task }) => { return ( navigate(`/task/${task.id}`)} > @@ -38,14 +38,11 @@ const TaskComponent: React.FC = ({ task }) => { Assigned to: {assignedToUser} - + Due: {task.due} - - - ); diff --git a/src/components/TaskDetail.tsx b/src/components/TaskDetail.tsx index 1d32ef9..1323b75 100644 --- a/src/components/TaskDetail.tsx +++ b/src/components/TaskDetail.tsx @@ -1,7 +1,101 @@ import React from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { TaskContext } from "../contexts/taskContext"; +import { useContext } from "react"; +import { + Button, + Card, + CardContent, + CardHeader, + Divider, + Stack, + Typography, +} from "@mui/material"; +import { UserContext } from "../contexts/userContext"; +import type { User } from "../utils/types"; const TaskDetail: React.FC = () => { - return
; + const navigate = useNavigate(); + + const { taskId } = useParams(); + + const { tasks, setTasks } = useContext(TaskContext); + const { users, loading } = useContext(UserContext); + + const numerizedTaskId = Number(taskId); + + const taskInQuestion = tasks.find((task) => task.id === numerizedTaskId); + + if (!taskInQuestion) return
Task not found
; + + let userInQuestion: User | undefined; + if (users && !loading) { + userInQuestion = users.find( + (user) => user.id === taskInQuestion?.assignedTo, + ); + } + + function markFinished() { + setTasks((prevTasks) => + prevTasks.map((task) => + // @ts-expect-error The function is called after the check. It's safe to assume taskInQuestion is not undefined + task.id === taskInQuestion.id + ? { ...task, finished: !task.finished } + : task, + ), + ); + navigate(-1); + } + + return ( + + + + + + {taskInQuestion.text} + + + + + + Assigned to:{" "} + {userInQuestion + ? `${userInQuestion.name} <${userInQuestion.email}>` + : "Unknown user"} + + + + Due date: {taskInQuestion.due} + + + + Created at:{" "} + {taskInQuestion.createdAt.current.toLocaleString()} + + + + + + + + + + + + ); }; export default TaskDetail; diff --git a/src/components/TaskForm.tsx b/src/components/TaskForm.tsx index eb2a3a2..f1da70f 100644 --- a/src/components/TaskForm.tsx +++ b/src/components/TaskForm.tsx @@ -10,6 +10,7 @@ import { Button, Card, CardHeader, + Skeleton, TextField, } from "@mui/material"; import { DatePicker } from "@mui/x-date-pickers/DatePicker"; @@ -37,6 +38,7 @@ const TaskForm: React.FC = () => { const creationDate = useRef(new Date()); function addTask(data: FormValues) { + creationDate.current = new Date(); const currentIdMaxId = Math.max(...tasks.map((task) => task.id)) + 1; const newId = Math.max(currentIdMaxId, 1); @@ -53,8 +55,7 @@ const TaskForm: React.FC = () => { reset(); } - // TODO make a skeleton - if (loading) return
Loading...
; + if (loading) return ; // TODO Better error display if (users == null) return
{error}
; diff --git a/src/components/TaskList.tsx b/src/components/TaskList.tsx index 1d0144d..b00bd91 100644 --- a/src/components/TaskList.tsx +++ b/src/components/TaskList.tsx @@ -6,6 +6,15 @@ import { Card, List } from "@mui/material"; const TaskList: React.FC = () => { const { tasks } = useContext(TaskContext); + // show unfinished tasks later and those with closer due date higher + const sortedTasks = tasks.sort((a, b) => { + if (a.finished == b.finished) { + if (a.due === b.due) return 0; + return a.due < b.due ? -1 : 1; + } + return a.finished ? 1 : -1; + }); + return (
@@ -13,7 +22,7 @@ const TaskList: React.FC = () => {

No tasks yet...

) : ( - {tasks.map((task, index) => ( + {sortedTasks.map((task, index) => ( ))} diff --git a/src/css/MainPage.css b/src/css/MainPage.css index 5247534..d4a9ab1 100644 --- a/src/css/MainPage.css +++ b/src/css/MainPage.css @@ -23,3 +23,14 @@ .task-list-item:hover { background-color: #f0f0f0; } + +.task-list-item.finished { + text-decoration: line-through; + color: #888; +} + +.task-form-skeleton { + width: 50vw; + max-width: 1000px; + height: 100px; +} diff --git a/src/pages/MainPage.tsx b/src/pages/MainPage.tsx index 7d16986..d810a46 100644 --- a/src/pages/MainPage.tsx +++ b/src/pages/MainPage.tsx @@ -1,7 +1,9 @@ +import "@/css/MainPage.css"; + import React from "react"; + import TaskForm from "../components/TaskForm"; import TaskList from "../components/TaskList"; -import "@/css/MainPage.css"; const MainPage: React.FC = () => { return ( diff --git a/src/pages/TaskPage.tsx b/src/pages/TaskPage.tsx index 5c0b608..b09b854 100644 --- a/src/pages/TaskPage.tsx +++ b/src/pages/TaskPage.tsx @@ -1,19 +1,8 @@ import React from "react"; -import { useParams } from "react-router-dom"; -import { TaskContext } from "../contexts/taskContext"; -import { useContext } from "react"; +import TaskDetail from "../components/TaskDetail"; const TaskPage: React.FC = () => { - const { taskId } = useParams(); - const numerizedTaskId = Number(taskId); - - const { tasks } = useContext(TaskContext); - - const taskInQuestion = tasks.find((task) => task.id === numerizedTaskId); - - if (!taskInQuestion) return
Task not found
; - - return
Task {taskId}
; + return ; }; export default TaskPage;