|
|
@ -1,10 +1,20 @@ |
|
|
|
import React, {useState } from 'react'; |
|
|
|
import { Box, Button, Checkbox, Dialog, DialogActions, DialogContent, DialogTitle, Divider, List, ListItem, TextField, Typography } from '@mui/material'; |
|
|
|
import { Box, Button, Checkbox, Dialog, DialogActions, DialogContent, DialogTitle, Divider, List, ListItem, TextField, Tooltip, Typography } from '@mui/material'; |
|
|
|
import { DatePicker } from '@mui/x-date-pickers'; |
|
|
|
import DeleteOutlineRoundedIcon from '@mui/icons-material/DeleteOutlineRounded'; |
|
|
|
import EditRoundedIcon from '@mui/icons-material/EditRounded'; |
|
|
|
import AddTaskRoundedIcon from '@mui/icons-material/AddTaskRounded'; |
|
|
|
import { useLocalStorage } from '../hooks/useLocalStorage'; |
|
|
|
|
|
|
|
type Subtodo = { |
|
|
|
id: string, |
|
|
|
name: string, |
|
|
|
done: boolean, |
|
|
|
description: string, |
|
|
|
from?: string, |
|
|
|
to?: string, |
|
|
|
} |
|
|
|
|
|
|
|
type Todo = { |
|
|
|
id: string, |
|
|
|
name: string, |
|
|
@ -12,15 +22,27 @@ type Todo = { |
|
|
|
description: string, |
|
|
|
from?: string, |
|
|
|
to?: string, |
|
|
|
subs?: Subtodo[] |
|
|
|
} |
|
|
|
|
|
|
|
function todoToSubtodo({todo}: {todo: Todo}) { |
|
|
|
const subtodo = { |
|
|
|
name: todo.name, |
|
|
|
id: todo.id, |
|
|
|
done: todo.done, |
|
|
|
description: todo.description, |
|
|
|
from: todo.from, |
|
|
|
to: todo.to |
|
|
|
}; |
|
|
|
return (subtodo) |
|
|
|
} |
|
|
|
|
|
|
|
function TodoItem({todo}: {todo: Todo}){ |
|
|
|
function TodoItem({todo}: {todo: Subtodo}){ |
|
|
|
return ( |
|
|
|
<ListItem> |
|
|
|
<Box sx={{display: "flex", flexDirection: "column"}}> |
|
|
|
<Typography variant="h5">{todo.name}</Typography> |
|
|
|
{todo.description && <span>Description: {todo.description}</span>} |
|
|
|
{todo.description && <span>{todo.description}</span>} |
|
|
|
{todo.from && <span>From: {(new Date(todo.from)).toLocaleDateString()}</span>} |
|
|
|
{todo.to && <span>To: {(new Date(todo.to)).toLocaleDateString()}</span>} |
|
|
|
</Box> |
|
|
@ -58,21 +80,21 @@ function ChangeTodo({todo, setTodo, handleCancel}: {todo: Todo, setTodo: (todo: |
|
|
|
done: todo.done, |
|
|
|
description: description, |
|
|
|
to: to?.toISOString(), |
|
|
|
from: from?.toISOString() |
|
|
|
from: from?.toISOString(), |
|
|
|
subs: [] |
|
|
|
}; |
|
|
|
setTodo(newtodo); |
|
|
|
} |
|
|
|
|
|
|
|
return ( |
|
|
|
<> |
|
|
|
<DialogContent> |
|
|
|
<Box component="form" |
|
|
|
<Box component="form" onSubmit={(e: any) => {e.preventDefault(); saveTodo();}}> |
|
|
|
<DialogContent |
|
|
|
sx={{ |
|
|
|
'& > :not(style)': { m: 2, width: '50ch' }, |
|
|
|
}}> |
|
|
|
<TextField |
|
|
|
autoFocus |
|
|
|
id="name" |
|
|
|
id={`name ${todo.id}`} |
|
|
|
value={name} |
|
|
|
label="Name" |
|
|
|
onChange={handleNameChange} |
|
|
@ -97,13 +119,12 @@ function ChangeTodo({todo, setTodo, handleCancel}: {todo: Todo, setTodo: (todo: |
|
|
|
onChange={handleTo} |
|
|
|
renderInput={(params: any) => <TextField {...params} />} |
|
|
|
/> |
|
|
|
</Box> |
|
|
|
</DialogContent> |
|
|
|
<DialogActions> |
|
|
|
<Button onClick={handleCancel}>Cancel</Button> |
|
|
|
<Button onClick={saveTodo}>Save</Button> |
|
|
|
<Button type="submit">Save</Button> |
|
|
|
</DialogActions> |
|
|
|
</> |
|
|
|
</Box> |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
@ -111,9 +132,25 @@ export function Todos({style}: {style?: any}) { |
|
|
|
const [todos, setTodos] = useLocalStorage<Todo[]>("todos",[]); |
|
|
|
const setDone=(i: number,todo: Todo, done: boolean) => |
|
|
|
setTodos([...todos.slice(0,i), |
|
|
|
{name: todo.name, id: todo.id, done: done, description: todo.description, to: todo.to, from: todo.from}, |
|
|
|
{name: todo.name, id: todo.id, done: done, description: todo.description, to: todo.to, from: todo.from, subs: todo.subs}, |
|
|
|
...todos.slice(i+1)]); |
|
|
|
|
|
|
|
const setDoneSub=(i: number, j: number, stodo: Subtodo, done: boolean) => { |
|
|
|
const todo = todos[i]; |
|
|
|
setTodos([...todos.slice(0,i), |
|
|
|
{name: todo.name, id: todo.id, done: todo.done, description: todo.description, to: todo.to, from: todo.from, |
|
|
|
subs: [...todo.subs!.slice(0,j), |
|
|
|
{name: stodo.name, id: stodo.id, done: done, description: stodo.description, to: stodo.to, from: stodo.from,} |
|
|
|
, ...todo.subs!.slice(j+1)]}, |
|
|
|
...todos.slice(i+1)]);} |
|
|
|
|
|
|
|
const deleteSubtodo=(i: number, j: number) => { |
|
|
|
const todo = todos[i]; |
|
|
|
setTodos([...todos.slice(0,i), |
|
|
|
{name: todo.name, id: todo.id, done: todo.done, description: todo.description, to: todo.to, from: todo.from, |
|
|
|
subs: [...todo.subs!.slice(0,j), ...todo.subs!.slice(j+1)]}, |
|
|
|
...todos.slice(i+1)]);} |
|
|
|
|
|
|
|
const deleteTodo=(i: number) => |
|
|
|
setTodos([...todos.slice(0,i), |
|
|
|
...todos.slice(i+1)]); |
|
|
@ -132,7 +169,7 @@ const handleClose = () => { |
|
|
|
}; |
|
|
|
|
|
|
|
const [open2, setOpen2] = useState(false); |
|
|
|
const [edit, setEdit] = useState<Todo>({name: "", description: "", done: false, id: ""}); |
|
|
|
const [edit, setEdit] = useState<Todo>({name: "", description: "", done: false, id: "", subs: []}); |
|
|
|
const [editI, setEditI] = useState<number>(0); |
|
|
|
const handleClickOpen2 = (todo: Todo, i: number) => { |
|
|
|
setOpen2(true); |
|
|
@ -143,6 +180,30 @@ const handleClose2 = () => { |
|
|
|
setOpen2(false); |
|
|
|
}; |
|
|
|
|
|
|
|
const [open3, setOpen3] = useState(false); |
|
|
|
const [editSub, setEditSub] = useState<Subtodo>({name: "", description: "", done: false, id: ""}); |
|
|
|
const [editJ, setEditJ] = useState<number>(0); |
|
|
|
const handleClickOpenSubtodo = (todo: Subtodo, i: number, j: number) => { |
|
|
|
setOpen3(true); |
|
|
|
setEditSub(todo); |
|
|
|
setEditI(i); |
|
|
|
setEditJ(j > -1 ? j : todos[i].subs!.length); |
|
|
|
}; |
|
|
|
const handleCloseSubtodo = () => { |
|
|
|
setOpen3(false); |
|
|
|
}; |
|
|
|
|
|
|
|
const [open4, setOpen4] = useState(false); |
|
|
|
const handleClickOpenSubtodo2 = (todo: Subtodo, i: number, j: number) => { |
|
|
|
setOpen4(true); |
|
|
|
setEditSub(todo); |
|
|
|
setEditI(i); |
|
|
|
setEditJ(j > -1 ? j : todos[i].subs!.length); |
|
|
|
}; |
|
|
|
const handleCloseSubtodo2 = () => { |
|
|
|
setOpen4(false); |
|
|
|
}; |
|
|
|
|
|
|
|
const editTodo= (todo: Todo) => { |
|
|
|
handleClose2(); |
|
|
|
setTodos([...todos.slice(0,editI), |
|
|
@ -150,6 +211,23 @@ const editTodo= (todo: Todo) => { |
|
|
|
...todos.slice(editI+1)]); |
|
|
|
} |
|
|
|
|
|
|
|
const editSubtodo= (todo: Subtodo) => { |
|
|
|
const subtodos = (typeof todos[editI].subs !== 'undefined') ? [...todos[editI].subs!.slice(0, editJ), todo, ...todos[editI].subs!.slice(editJ+1)] : [todo]; |
|
|
|
// VERY HACKY
|
|
|
|
handleCloseSubtodo(); |
|
|
|
handleCloseSubtodo2(); |
|
|
|
setTodos([...todos.slice(0,editI), |
|
|
|
{name: todos[editI].name, |
|
|
|
id: todos[editI].id, |
|
|
|
description: todos[editI].description, |
|
|
|
from: todos[editI].from, |
|
|
|
to: todos[editI].to, |
|
|
|
done: todos[editI].done, |
|
|
|
subs: subtodos |
|
|
|
}, |
|
|
|
...todos.slice(editI+1)]); |
|
|
|
} |
|
|
|
|
|
|
|
return ( |
|
|
|
<div style={{...style, textAlign: "left"}}> |
|
|
|
<Typography variant="h3">ToDo's</Typography> |
|
|
@ -160,21 +238,55 @@ const editTodo= (todo: Todo) => { |
|
|
|
<Box sx={{display: "flex", flexDirection: "columns"}}> |
|
|
|
<TodoItem key={todo.id} todo={todo}/> |
|
|
|
<Checkbox checked={todo.done} onChange={(e) => setDone(i, todo,e.target.checked)}></Checkbox> |
|
|
|
<Button onClick={() => handleClickOpen2(todo, i)}><EditRoundedIcon/></Button> |
|
|
|
<Button onClick={() => deleteTodo(i)}><DeleteOutlineRoundedIcon/></Button> |
|
|
|
<Tooltip title="Add Subtodo"> |
|
|
|
<Button onClick={() => handleClickOpenSubtodo(todo, i,-1)}><AddTaskRoundedIcon/></Button> |
|
|
|
</Tooltip> |
|
|
|
<Tooltip title="Edit Todo"> |
|
|
|
<Button onClick={() => handleClickOpen2(todo, i)}><EditRoundedIcon/></Button> |
|
|
|
</Tooltip> |
|
|
|
<Tooltip title="Delete Todo"> |
|
|
|
<Button onClick={() => deleteTodo(i)}><DeleteOutlineRoundedIcon/></Button> |
|
|
|
</Tooltip> |
|
|
|
</Box> |
|
|
|
{(todo.subs && todo.subs.length > 0) && |
|
|
|
<List sx={{marginLeft: "20px"}}> |
|
|
|
<Divider/> |
|
|
|
{todo.subs.map((stodo, j) => |
|
|
|
<div key={stodo.id}> |
|
|
|
<Box sx={{display: "flex", flexDirection: "columns"}}> |
|
|
|
<TodoItem key={todo.id} todo={stodo}/> |
|
|
|
<Checkbox checked={stodo.done} onChange={(e) => setDoneSub(i, j, stodo,e.target.checked)}></Checkbox> |
|
|
|
<Tooltip title="Edit Subtodo"> |
|
|
|
<Button onClick={() => handleClickOpenSubtodo2(stodo, i, j)}><EditRoundedIcon/></Button> |
|
|
|
</Tooltip> |
|
|
|
<Tooltip title="Delete Subtodo"> |
|
|
|
<Button onClick={() => deleteSubtodo(i,j)}><DeleteOutlineRoundedIcon/></Button> |
|
|
|
</Tooltip> |
|
|
|
</Box> |
|
|
|
{(j + 1) < todo.subs!.length && <Divider/>} |
|
|
|
</div> |
|
|
|
)} |
|
|
|
</List>} |
|
|
|
{(i + 1) < todos.length && <Divider/>} |
|
|
|
</div> |
|
|
|
)} |
|
|
|
</List> |
|
|
|
<Dialog open={open} onClose={handleClose}> |
|
|
|
<Dialog open={open} onClose={handleClose} disableRestoreFocus> |
|
|
|
<DialogTitle>New Todo</DialogTitle> |
|
|
|
<ChangeTodo todo={{id: "", name: "", done: false, description: ""}} setTodo={setTodo} handleCancel={handleClose}></ChangeTodo> |
|
|
|
<ChangeTodo todo={{id: "", name: "", done: false, description: "", subs: []}} setTodo={setTodo} handleCancel={handleClose}></ChangeTodo> |
|
|
|
</Dialog> |
|
|
|
<Dialog open={open2} onClose={handleClose2}> |
|
|
|
<Dialog open={open2} onClose={handleClose2} disableRestoreFocus> |
|
|
|
<DialogTitle>Edit Todo</DialogTitle> |
|
|
|
<ChangeTodo todo={edit} setTodo={editTodo} handleCancel={handleClose2}></ChangeTodo> |
|
|
|
</Dialog> |
|
|
|
<Dialog open={open3} onClose={handleCloseSubtodo} disableRestoreFocus> |
|
|
|
<DialogTitle>New Subtodo</DialogTitle> |
|
|
|
<ChangeTodo todo={{id: "", name: "", done: false, description: ""}} setTodo={editSubtodo} handleCancel={handleCloseSubtodo}></ChangeTodo> |
|
|
|
</Dialog> |
|
|
|
<Dialog open={open4} onClose={handleCloseSubtodo2} disableRestoreFocus> |
|
|
|
<DialogTitle>Edit Subtodo</DialogTitle> |
|
|
|
<ChangeTodo todo={{...editSub}} setTodo={editSubtodo} handleCancel={handleCloseSubtodo2}></ChangeTodo> |
|
|
|
</Dialog> |
|
|
|
</div> |
|
|
|
) |
|
|
|
} |
|
|
|