Task detail page finalized and added user loading skeleton

This commit is contained in:
Thastertyn committed 2025-06-23 00:09:21 +02:00
1 parent 2204ffeece
commit e5e565430c
7 files changed
+126 -23

No files matched your search

+2 -5
View File
@@ -24,7 +24,7 @@ const TaskComponent: React.FC<TaskComponentProps> = ({ task }) => {
return ( return (
<ListItem <ListItem
className="task-list-item" className={`task-list-item ${task.finished && "finished"}`}
onClick={() => navigate(`/task/${task.id}`)} onClick={() => navigate(`/task/${task.id}`)}
> >
<Grid container spacing={2} alignItems="center" width={"100%"}> <Grid container spacing={2} alignItems="center" width={"100%"}>
@@ -38,14 +38,11 @@ const TaskComponent: React.FC<TaskComponentProps> = ({ task }) => {
Assigned to: {assignedToUser} Assigned to: {assignedToUser}
</Typography> </Typography>
</Grid> </Grid>
<Grid size={3}> <Grid size={4}>
<Typography variant="body1" color="text.secondary"> <Typography variant="body1" color="text.secondary">
Due: {task.due} Due: {task.due}
</Typography> </Typography>
</Grid> </Grid>
<Grid size={1}>
<input type="checkbox" checked={task.finished} disabled={true} />
</Grid>
</Grid> </Grid>
</ListItem> </ListItem>
); );
+95 -1
View File
@@ -1,7 +1,101 @@
import React from "react"; 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 = () => { const TaskDetail: React.FC = () => {
return <div></div>; 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 <div>Task not found</div>;
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 (
<Card>
<CardHeader title={`Task #${taskInQuestion.id}`} />
<CardContent>
<Stack spacing={2}>
<Typography variant="subtitle1" color="text.primary">
{taskInQuestion.text}
</Typography>
<Divider />
<Typography variant="body2" color="text.secondary" align="left">
<b>Assigned to:</b>{" "}
{userInQuestion
? `${userInQuestion.name} <${userInQuestion.email}>`
: "Unknown user"}
</Typography>
<Typography variant="body2" color="text.secondary" align="left">
<b>Due date:</b> {taskInQuestion.due}
</Typography>
<Typography variant="body2" color="text.secondary" align="left">
<b>Created at:</b>{" "}
{taskInQuestion.createdAt.current.toLocaleString()}
</Typography>
<Divider />
<Stack
direction="row"
spacing={2}
justifyContent="space-between"
>
<Button variant="outlined" onClick={() => navigate(-1)}>
Go back
</Button>
<Button
variant="contained"
color="primary"
onClick={markFinished}
>
{!taskInQuestion.finished ? "Mark finished" : "Undo"}
</Button>
</Stack>
</Stack>
</CardContent>
</Card>
);
}; };
export default TaskDetail; export default TaskDetail;
+3 -2
View File
@@ -10,6 +10,7 @@ import {
Button, Button,
Card, Card,
CardHeader, CardHeader,
Skeleton,
TextField, TextField,
} from "@mui/material"; } from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers/DatePicker"; import { DatePicker } from "@mui/x-date-pickers/DatePicker";
@@ -37,6 +38,7 @@ const TaskForm: React.FC = () => {
const creationDate = useRef<Date>(new Date()); const creationDate = useRef<Date>(new Date());
function addTask(data: FormValues) { function addTask(data: FormValues) {
creationDate.current = new Date();
const currentIdMaxId = Math.max(...tasks.map((task) => task.id)) + 1; const currentIdMaxId = Math.max(...tasks.map((task) => task.id)) + 1;
const newId = Math.max(currentIdMaxId, 1); const newId = Math.max(currentIdMaxId, 1);
@@ -53,8 +55,7 @@ const TaskForm: React.FC = () => {
reset(); reset();
} }
// TODO make a skeleton if (loading) return <Skeleton className="task-form-skeleton" />;
if (loading) return <div>Loading...</div>;
// TODO Better error display // TODO Better error display
if (users == null) return <div>{error}</div>; if (users == null) return <div>{error}</div>;
+10 -1
View File
@@ -6,6 +6,15 @@ import { Card, List } from "@mui/material";
const TaskList: React.FC = () => { const TaskList: React.FC = () => {
const { tasks } = useContext(TaskContext); 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 ( return (
<div className="task-list"> <div className="task-list">
<Card> <Card>
@@ -13,7 +22,7 @@ const TaskList: React.FC = () => {
<p>No tasks yet...</p> <p>No tasks yet...</p>
) : ( ) : (
<List> <List>
{tasks.map((task, index) => ( {sortedTasks.map((task, index) => (
<TaskComponent key={index} task={task} /> <TaskComponent key={index} task={task} />
))} ))}
</List> </List>
+11
View File
@@ -23,3 +23,14 @@
.task-list-item:hover { .task-list-item:hover {
background-color: #f0f0f0; background-color: #f0f0f0;
} }
.task-list-item.finished {
text-decoration: line-through;
color: #888;
}
.task-form-skeleton {
width: 50vw;
max-width: 1000px;
height: 100px;
}
+3 -1
View File
@@ -1,7 +1,9 @@
import "@/css/MainPage.css";
import React from "react"; import React from "react";
import TaskForm from "../components/TaskForm"; import TaskForm from "../components/TaskForm";
import TaskList from "../components/TaskList"; import TaskList from "../components/TaskList";
import "@/css/MainPage.css";
const MainPage: React.FC = () => { const MainPage: React.FC = () => {
return ( return (
+2 -13
View File
@@ -1,19 +1,8 @@
import React from "react"; import React from "react";
import { useParams } from "react-router-dom"; import TaskDetail from "../components/TaskDetail";
import { TaskContext } from "../contexts/taskContext";
import { useContext } from "react";
const TaskPage: React.FC = () => { const TaskPage: React.FC = () => {
const { taskId } = useParams(); return <TaskDetail />;
const numerizedTaskId = Number(taskId);
const { tasks } = useContext(TaskContext);
const taskInQuestion = tasks.find((task) => task.id === numerizedTaskId);
if (!taskInQuestion) return <div>Task not found</div>;
return <div>Task {taskId}</div>;
}; };
export default TaskPage; export default TaskPage;