6 changed files with 302 additions and 165 deletions
@ -0,0 +1,183 @@ |
|||||
|
import React, {useState } from 'react'; |
||||
|
import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Divider, List, ListItem, TextField, 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 { useLocalStorage } from '../hooks/useLocalStorage'; |
||||
|
|
||||
|
type Todo = { |
||||
|
id: string, |
||||
|
name: string, |
||||
|
done: boolean, |
||||
|
description: string, |
||||
|
from?: string, |
||||
|
to?: string, |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function TodoItem({todo}: {todo: Todo}){ |
||||
|
return ( |
||||
|
<ListItem> |
||||
|
<Box sx={{display: "flex", flexDirection: "column"}}> |
||||
|
<Typography variant="h5">{todo.name}</Typography> |
||||
|
{todo.description && <span>Description: {todo.description}</span>} |
||||
|
{todo.from && <span>From: {(new Date(todo.from)).toLocaleDateString()}</span>} |
||||
|
{todo.to && <span>To: {(new Date(todo.to)).toLocaleDateString()}</span>} |
||||
|
</Box> |
||||
|
</ListItem> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
function ChangeTodo({todo, setTodo, handleCancel}: {todo: Todo, setTodo: (todo: Todo) => void, handleCancel: () => void}){ |
||||
|
|
||||
|
const [name, setName] = React.useState<string>(todo.name) |
||||
|
const handleNameChange = (event: React.ChangeEvent<HTMLInputElement>) => { |
||||
|
setName(event.target.value); |
||||
|
}; |
||||
|
|
||||
|
const [id, setId] = React.useState<string>(todo.id) |
||||
|
|
||||
|
const [description, setDescription] = React.useState<string>(todo.description) |
||||
|
const handleDescChange = (event: React.ChangeEvent<HTMLInputElement>) => { |
||||
|
setDescription(event.target.value); |
||||
|
}; |
||||
|
|
||||
|
const [done, setDone] = React.useState<boolean>(todo.done) |
||||
|
|
||||
|
const [from, setFrom] = React.useState<Date | null>(todo.from ? new Date(todo.from) : null); |
||||
|
const handleFrom = (newValue: Date | null) => { |
||||
|
setFrom(newValue); |
||||
|
}; |
||||
|
|
||||
|
const [to, setTo] = React.useState<Date | null>(todo.to ? new Date(todo.to) : null); |
||||
|
|
||||
|
const handleTo = (newValue: Date | null) => { |
||||
|
setTo(newValue); |
||||
|
}; |
||||
|
|
||||
|
const saveTodo=() => { |
||||
|
const newtodo = { |
||||
|
name: name, |
||||
|
id: id === "" ? Math.random().toString() : id, |
||||
|
done: done, |
||||
|
description: description, |
||||
|
to: to?.toISOString(), |
||||
|
from: from?.toISOString() |
||||
|
}; |
||||
|
setTodo(newtodo); |
||||
|
} |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<DialogContent> |
||||
|
<Box component="form" |
||||
|
sx={{ |
||||
|
'& > :not(style)': { m: 2, width: '50ch' }, |
||||
|
}}> |
||||
|
<TextField |
||||
|
id="name" |
||||
|
value={name} |
||||
|
label="Name" |
||||
|
onChange={handleNameChange} |
||||
|
/> |
||||
|
<TextField |
||||
|
id="description" |
||||
|
value={description} |
||||
|
label="Description" |
||||
|
onChange={handleDescChange} |
||||
|
/> |
||||
|
<DatePicker |
||||
|
mask={'__.__.____'} |
||||
|
label="From:" |
||||
|
value={from} |
||||
|
onChange={handleFrom} |
||||
|
renderInput={(params: any) => <TextField {...params} />} |
||||
|
/> |
||||
|
<DatePicker |
||||
|
mask={'__.__.____'} |
||||
|
label="To:" |
||||
|
value={to} |
||||
|
onChange={handleTo} |
||||
|
renderInput={(params: any) => <TextField {...params} />} |
||||
|
/> |
||||
|
</Box> |
||||
|
</DialogContent> |
||||
|
<DialogActions> |
||||
|
<Button onClick={handleCancel}>Cancel</Button> |
||||
|
<Button onClick={saveTodo}>Save</Button> |
||||
|
</DialogActions> |
||||
|
</> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
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}, |
||||
|
...todos.slice(i+1)]); |
||||
|
|
||||
|
const deleteTodo=(i: number,todo: Todo) => |
||||
|
setTodos([...todos.slice(0,i), |
||||
|
...todos.slice(i+1)]); |
||||
|
|
||||
|
const setTodo= (todo: Todo) => { |
||||
|
handleClose(); |
||||
|
setTodos([...todos, todo]); |
||||
|
} |
||||
|
|
||||
|
const [open, setOpen] = useState(false); |
||||
|
const handleClickOpen = () => { |
||||
|
setOpen(true); |
||||
|
}; |
||||
|
const handleClose = () => { |
||||
|
setOpen(false); |
||||
|
}; |
||||
|
|
||||
|
const [open2, setOpen2] = useState(false); |
||||
|
const [edit, setEdit] = useState<Todo>({name: "", description: "", done: false, id: ""}); |
||||
|
const [editI, setEditI] = useState<number>(0); |
||||
|
const handleClickOpen2 = (todo: Todo, i: number) => { |
||||
|
setOpen2(true); |
||||
|
setEdit(todo); |
||||
|
setEditI(i); |
||||
|
}; |
||||
|
const handleClose2 = () => { |
||||
|
setOpen2(false); |
||||
|
}; |
||||
|
|
||||
|
const editTodo= (todo: Todo) => { |
||||
|
handleClose2(); |
||||
|
setTodos([...todos.slice(0,editI), |
||||
|
todo, |
||||
|
...todos.slice(editI+1)]); |
||||
|
} |
||||
|
|
||||
|
return ( |
||||
|
<div style={{...style, textAlign: "left"}}> |
||||
|
<Typography variant="h3">ToDo's</Typography> |
||||
|
<Button onClick={handleClickOpen}>Add Todo</Button> |
||||
|
<List> |
||||
|
{todos.map((todo, i) => |
||||
|
<> |
||||
|
<Box sx={{display: "flex", flexDirection: "columns"}}> |
||||
|
<TodoItem key={todo.id} todo={todo}/> |
||||
|
<input type="checkbox" checked={todo.done} onChange={(e) => setDone(i, todo,e.target.checked)}></input> |
||||
|
<Button onClick={() => handleClickOpen2(todo, i)}><EditRoundedIcon/></Button> |
||||
|
<Button onClick={() => deleteTodo}><DeleteOutlineRoundedIcon/></Button> |
||||
|
</Box> |
||||
|
{(i + 1) < todos.length && <Divider/>} |
||||
|
</> |
||||
|
)} |
||||
|
</List> |
||||
|
<Dialog open={open} onClose={handleClose}> |
||||
|
<DialogTitle>New Todo</DialogTitle> |
||||
|
<ChangeTodo todo={{id: "", name: "", done: false, description: ""}} setTodo={setTodo} handleCancel={handleClose}></ChangeTodo> |
||||
|
</Dialog> |
||||
|
<Dialog open={open2} onClose={handleClose2}> |
||||
|
<DialogTitle>Edit Todo</DialogTitle> |
||||
|
<ChangeTodo todo={edit} setTodo={editTodo} handleCancel={handleClose2}></ChangeTodo> |
||||
|
</Dialog> |
||||
|
</div> |
||||
|
) |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
import React, { useEffect, useState } from 'react'; |
||||
|
|
||||
|
function useLocalStorage<T>(storageKey: string, fallbackState: T): [T, (val: T) => void] { |
||||
|
const [value, setValue] = useState( |
||||
|
JSON.parse(localStorage.getItem(storageKey)!) ?? fallbackState |
||||
|
); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
localStorage.setItem(storageKey, JSON.stringify(value)) |
||||
|
}, [value, storageKey]); |
||||
|
|
||||
|
return [value, setValue]; |
||||
|
} |
||||
|
|
||||
|
export {useLocalStorage} |
Loading…
Reference in new issue