Feat(frontend): Create/Edit/Delete todos
This commit is contained in:
61
frontend/src/components/Todo.tsx
Normal file
61
frontend/src/components/Todo.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import Checkbox from "@mui/material/Checkbox";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemButton from "@mui/material/ListItemButton";
|
||||
import ListItemIcon from "@mui/material/ListItemIcon";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import Skeleton from "@mui/material/Skeleton";
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import { format } from "date-fns";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { Todo as TodoModel } from "../models";
|
||||
import { useDeleteTodoMutation } from "../queries";
|
||||
|
||||
interface IProps {
|
||||
todo?: TodoModel;
|
||||
}
|
||||
|
||||
const Todo = ({ todo }: IProps) => {
|
||||
const { mutateAsync: deleteTodo } = useDeleteTodoMutation();
|
||||
let secondary;
|
||||
if (todo === undefined) {
|
||||
secondary = <Skeleton width="200px" />;
|
||||
} else if (todo.due !== null) {
|
||||
secondary = format(todo.due, "P");
|
||||
}
|
||||
return (
|
||||
<ListItem
|
||||
secondaryAction={
|
||||
<IconButton
|
||||
disabled={todo === undefined}
|
||||
edge="end"
|
||||
onClick={() => deleteTodo(todo?.id!)}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<ListItemButton
|
||||
component={Link}
|
||||
disabled={todo === undefined}
|
||||
to={`/todos/${todo?.id}/`}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<Checkbox
|
||||
checked={todo?.complete ?? false}
|
||||
disabled
|
||||
disableRipple
|
||||
edge="start"
|
||||
tabIndex={-1}
|
||||
/>
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={todo?.task ?? <Skeleton />}
|
||||
secondary={secondary}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
);
|
||||
};
|
||||
export default Todo;
|
||||
44
frontend/src/components/TodoForm.tsx
Normal file
44
frontend/src/components/TodoForm.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { Form, Formik } from "formik";
|
||||
import * as yup from "yup";
|
||||
|
||||
import CheckboxField from "../components/CheckboxField";
|
||||
import DateField from "../components/DateField";
|
||||
import FormActions from "../components/FormActions";
|
||||
import TextField from "../components/TextField";
|
||||
import type { ITodoData } from "../queries";
|
||||
|
||||
interface IProps {
|
||||
initialValues: ITodoData;
|
||||
label: string;
|
||||
onSubmit: (data: ITodoData) => Promise<any>;
|
||||
}
|
||||
|
||||
const validationSchema = yup.object({
|
||||
complete: yup.boolean(),
|
||||
due: yup.date().nullable(),
|
||||
task: yup.string().required("Required"),
|
||||
});
|
||||
|
||||
const TodoForm = ({ initialValues, label, onSubmit }: IProps) => (
|
||||
<Formik<ITodoData>
|
||||
initialValues={initialValues}
|
||||
onSubmit={onSubmit}
|
||||
validationSchema={validationSchema}
|
||||
>
|
||||
{({ dirty, isSubmitting }) => (
|
||||
<Form>
|
||||
<TextField fullWidth label="Task" name="task" required />
|
||||
<DateField fullWidth label="Due" name="due" />
|
||||
<CheckboxField fullWidth label="Complete" name="complete" />
|
||||
<FormActions
|
||||
disabled={!dirty}
|
||||
isSubmitting={isSubmitting}
|
||||
label={label}
|
||||
links={[{ label: "Back", to: "/" }]}
|
||||
/>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
);
|
||||
|
||||
export default TodoForm;
|
||||
Reference in New Issue
Block a user