add edit task dialog
This commit is contained in:
@@ -8,7 +8,7 @@ import { ProjectProvider } from "../../../../providers/ProjectProvider";
|
||||
import { createSWRFetcher } from "../../../../utils/swrFetcher";
|
||||
import { AddTask } from "./AddTask";
|
||||
import { ProjectOptions } from "./ProjectOptions";
|
||||
import { TaskListProps, TasksList } from "./TasksList";
|
||||
import { TaskListProps, TasksList } from "./TaskList/TasksList";
|
||||
|
||||
export type APIProjectTasksList = {
|
||||
data: TaskListProps["tasks"];
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
TextField,
|
||||
} from "@mui/material";
|
||||
import { SetStateAction, useState } from "react";
|
||||
import { SubmitHandler, useForm } from "react-hook-form";
|
||||
import { useAuth } from "../../../../../hooks/useAuth";
|
||||
import { useProject } from "../../../../../hooks/useProject";
|
||||
import { Task } from "./TasksListItem";
|
||||
|
||||
type EditProjectForm = {
|
||||
description: string;
|
||||
};
|
||||
|
||||
export type EditTaskDialogProps = {
|
||||
open: boolean;
|
||||
setOpen: (value: SetStateAction<boolean>) => void;
|
||||
task: Task;
|
||||
};
|
||||
|
||||
export const EditTaskDialog = ({
|
||||
task,
|
||||
open,
|
||||
setOpen,
|
||||
}: EditTaskDialogProps) => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { apiClient } = useAuth();
|
||||
const { tasksMutate, project } = useProject();
|
||||
const { register, handleSubmit, reset } = useForm<EditProjectForm>({
|
||||
defaultValues: task,
|
||||
});
|
||||
|
||||
const handleClose = () => {
|
||||
reset();
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const onSubmit: SubmitHandler<EditProjectForm> = (data) => {
|
||||
setIsLoading(true);
|
||||
|
||||
apiClient(`projects/${project.id}/tasks/${task.id}`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(data),
|
||||
}).then(() => {
|
||||
tasksMutate();
|
||||
setIsLoading(false);
|
||||
handleClose();
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={handleClose}>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<DialogTitle>Rename task</DialogTitle>
|
||||
<DialogContent>
|
||||
<TextField
|
||||
{...register("description")}
|
||||
disabled={isLoading}
|
||||
autoFocus
|
||||
fullWidth
|
||||
margin="dense"
|
||||
variant="standard"
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button disabled={isLoading} onClick={handleClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit" disabled={isLoading}>
|
||||
Rename
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { List, ListSubheader } from "@mui/material";
|
||||
import { useAuth } from "../../../../hooks/useAuth";
|
||||
import { useProject } from "../../../../hooks/useProject";
|
||||
import { useAuth } from "../../../../../hooks/useAuth";
|
||||
import { useProject } from "../../../../../hooks/useProject";
|
||||
import { Task, TasksListItem } from "./TasksListItem";
|
||||
|
||||
export type TaskListProps = {
|
||||
@@ -0,0 +1,103 @@
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import EditIcon from "@mui/icons-material/Edit";
|
||||
|
||||
import {
|
||||
Checkbox,
|
||||
IconButton,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemText,
|
||||
Tooltip,
|
||||
} from "@mui/material";
|
||||
import { useState } from "react";
|
||||
import { formatDate } from "../../../../../utils/formatDate";
|
||||
import { EditTaskDialog } from "./EditTaskDialog";
|
||||
|
||||
export type Task = {
|
||||
id: number;
|
||||
description: string;
|
||||
createdAt: string;
|
||||
finishedAt?: string;
|
||||
};
|
||||
|
||||
export type TasksListItemProps = {
|
||||
task: Task;
|
||||
onCheck: (taskId: number) => void;
|
||||
onDelete: (taskId: number) => void;
|
||||
};
|
||||
|
||||
export const TasksListItem = ({
|
||||
task,
|
||||
onCheck,
|
||||
onDelete,
|
||||
}: TasksListItemProps) => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [openEditDialog, setOpenEditDialog] = useState(false);
|
||||
const finished = !!task.finishedAt;
|
||||
const blockInteration = finished || isLoading;
|
||||
|
||||
const handleCheck = () => {
|
||||
setIsLoading(true);
|
||||
onCheck(task.id);
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
setIsLoading(true);
|
||||
onDelete(task.id);
|
||||
};
|
||||
|
||||
const handleEdit = () => {
|
||||
setOpenEditDialog(true);
|
||||
};
|
||||
|
||||
const tooltipText = `
|
||||
${task.finishedAt ? `Finished at: ${formatDate(task.finishedAt)} |` : ""}
|
||||
${`Created at: ${formatDate(task.createdAt)}`}
|
||||
`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<EditTaskDialog
|
||||
task={task}
|
||||
open={openEditDialog}
|
||||
setOpen={setOpenEditDialog}
|
||||
/>
|
||||
<Tooltip title={tooltipText} placement="top-start">
|
||||
<ListItem
|
||||
secondaryAction={
|
||||
finished ? null : (
|
||||
<>
|
||||
<IconButton
|
||||
onClick={handleEdit}
|
||||
disabled={blockInteration}
|
||||
edge="end"
|
||||
aria-label="edit"
|
||||
>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
sx={{ ml: 2 }}
|
||||
onClick={handleDelete}
|
||||
disabled={blockInteration}
|
||||
edge="end"
|
||||
aria-label="delete"
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
>
|
||||
<ListItemAvatar>
|
||||
<Checkbox
|
||||
onChange={handleCheck}
|
||||
disabled={blockInteration}
|
||||
checked={blockInteration}
|
||||
/>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={task.description} />
|
||||
</ListItem>
|
||||
</Tooltip>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './TasksList'
|
||||
@@ -1,81 +0,0 @@
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import {
|
||||
Checkbox,
|
||||
IconButton,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemText,
|
||||
Tooltip,
|
||||
} from "@mui/material";
|
||||
import { useState } from "react";
|
||||
import { formatDate } from "../../../../utils/formatDate";
|
||||
|
||||
export type Task = {
|
||||
id: number;
|
||||
description: string;
|
||||
createdAt: string;
|
||||
finishedAt?: string;
|
||||
};
|
||||
|
||||
export type TasksListItemProps = {
|
||||
task: Task;
|
||||
onCheck: (taskId: number) => void;
|
||||
onDelete: (taskId: number) => void;
|
||||
};
|
||||
|
||||
export const TasksListItem = ({
|
||||
task,
|
||||
onCheck,
|
||||
onDelete,
|
||||
}: TasksListItemProps) => {
|
||||
const finished = !!task.finishedAt;
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const handleCheck = () => {
|
||||
setIsLoading(true);
|
||||
onCheck(task.id);
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
setIsLoading(true);
|
||||
onDelete(task.id);
|
||||
};
|
||||
|
||||
const blockInteration = finished || isLoading;
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
title={`
|
||||
${
|
||||
task.finishedAt ? `Finished at: ${formatDate(task.finishedAt)} |` : ""
|
||||
}
|
||||
${`Created at: ${formatDate(task.createdAt)}`}
|
||||
`}
|
||||
placement="top-start"
|
||||
>
|
||||
<ListItem
|
||||
secondaryAction={
|
||||
finished ? null : (
|
||||
<IconButton
|
||||
onClick={handleDelete}
|
||||
disabled={blockInteration}
|
||||
edge="end"
|
||||
aria-label="delete"
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
)
|
||||
}
|
||||
>
|
||||
<ListItemAvatar>
|
||||
<Checkbox
|
||||
onChange={handleCheck}
|
||||
disabled={blockInteration}
|
||||
checked={blockInteration}
|
||||
/>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={task.description} />
|
||||
</ListItem>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user