Do todos
This commit is contained in:
1 parent
6bf7f18bb5
commit
ea59db58d3
6 files changed
+302
-165
No files matched your search
+47
-162
@@ -1,158 +1,36 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import './App.css';
|
||||
import { AppBar, Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Grid, TextField, Toolbar, Typography } from '@mui/material';
|
||||
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
||||
import deLocale from 'date-fns/locale/de';
|
||||
import { AppBar, Box, Button, Paper, Toolbar, Typography } from '@mui/material';
|
||||
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
|
||||
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
||||
import dayjs from 'dayjs';
|
||||
import { DatePicker } from '@mui/x-date-pickers';
|
||||
|
||||
type Todo = {
|
||||
name: string,
|
||||
id: string,
|
||||
done: boolean
|
||||
}
|
||||
|
||||
function TodoItem({todo,setDone}: {todo: Todo, setDone: (done: boolean) => void}){
|
||||
return (
|
||||
<li>
|
||||
{todo.name}
|
||||
<input type="checkbox" checked={todo.done} onChange={(e) => setDone(e.target.checked)}></input>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
function ChangeTodo({todo, setTodo, handleCancel}: {todo: Todo, setTodo: (todo: Todo) => void, handleCancel: () => void}){
|
||||
|
||||
const [name, setName] = React.useState<string>(todo.name)
|
||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setName(event.target.value);
|
||||
};
|
||||
|
||||
const [id, setId] = React.useState<string>(todo.id)
|
||||
|
||||
const [done, setDone] = React.useState<boolean>(todo.done)
|
||||
|
||||
const [from, setFrom] = React.useState<Date | null>(
|
||||
new Date('2022-04-18T21:11:54'),
|
||||
);
|
||||
const handleFrom = (newValue: Date | null) => {
|
||||
setFrom(newValue);
|
||||
};
|
||||
|
||||
const [to, setTo] = React.useState<Date | null>(
|
||||
new Date('2022-05-18T21:11:54'),
|
||||
);
|
||||
const handleTo = (newValue: Date | null) => {
|
||||
setTo(newValue);
|
||||
};
|
||||
|
||||
const saveTodo=() => {
|
||||
const newtodo = {
|
||||
name: name,
|
||||
id: id === "" ? Math.random().toString() : id,
|
||||
done: done
|
||||
};
|
||||
setTodo(newtodo);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DialogContent>
|
||||
<TextField
|
||||
id="name"
|
||||
value={name}
|
||||
label="Name"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
id="description"
|
||||
label="Description"
|
||||
/>
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs} locale={dayjs().locale('de')}>
|
||||
<DatePicker
|
||||
mask={'__.__.____'}
|
||||
label="From:"
|
||||
value={from}
|
||||
onChange={handleFrom}
|
||||
renderInput={(params: any) => <TextField {...params} />}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs} locale={dayjs().locale('de')}>
|
||||
<DatePicker
|
||||
mask={'__.__.____'}
|
||||
label="To:"
|
||||
value={to}
|
||||
onChange={handleTo}
|
||||
renderInput={(params: any) => <TextField {...params} />}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleCancel}>Cancel</Button>
|
||||
<Button onClick={saveTodo}>Save</Button>
|
||||
</DialogActions>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function Todos() {
|
||||
const [todos, setTodos] = useState<Todo[]>([]);
|
||||
const setDone=(i: number,todo: Todo) => (done: boolean) => setTodos([...todos.slice(0,i),{name: todo.name, id: todo.id, done: done},...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);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Typography variant="h2">ToDo's</Typography>
|
||||
<ul>
|
||||
{todos.map((todo, i) =>
|
||||
<TodoItem key={todo.id} todo={todo} setDone={setDone(i, todo)}></TodoItem>
|
||||
)}
|
||||
</ul>
|
||||
<Button onClick={handleClickOpen}>Add Todo</Button>
|
||||
<Dialog open={open} onClose={handleClose}>
|
||||
<DialogTitle>New Todo</DialogTitle>
|
||||
<ChangeTodo todo={{id: "", name: "", done: false}} setTodo={setTodo} handleCancel={handleClose}></ChangeTodo>
|
||||
</Dialog>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
import { Todos } from './components/Todos';
|
||||
|
||||
function Calendar(){
|
||||
return(
|
||||
<Typography variant="h2">Calendar</Typography>
|
||||
<Typography variant="h3">Calendar</Typography>
|
||||
)
|
||||
}
|
||||
|
||||
function Dinos(){
|
||||
return(
|
||||
<Typography variant="h2">Dinos</Typography>
|
||||
<div>
|
||||
<Typography variant="h3">Dinos</Typography>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Overview(){
|
||||
return (
|
||||
<div>
|
||||
<Typography variant="h2">Overview</Typography>
|
||||
<Grid
|
||||
container
|
||||
justifyContent="space-evenly"
|
||||
alignItems="flex-start"
|
||||
spacing={2}>
|
||||
<Todos/>
|
||||
<Todos/>
|
||||
</Grid>
|
||||
<div style={{display: "flex", flexGrow: 1, flexDirection: "column", height: "100%", maxHeight: "100%"}}>
|
||||
<div><Typography variant="h2">Overview</Typography></div>
|
||||
<Box sx={{display: "flex", flexGrow: 1, justifyContent: "space-evenly", gap: 4, height: "100%", minHeight: 0}}>
|
||||
<Paper sx={{overflowY: "auto", width: "100%", padding: 2, height: "calc(100% - 32px)"}}><Todos/></Paper>
|
||||
<Box sx={{width: "100%", display: "flex", flexDirection: "column", justifyContent: "space-evenly"}}>
|
||||
<Calendar/>
|
||||
<Calendar/>
|
||||
</Box>
|
||||
</Box>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -163,28 +41,35 @@ function App() {
|
||||
const [page,setPage] = useState<string>("Overview");
|
||||
return (
|
||||
<>
|
||||
<AppBar>
|
||||
<Toolbar>
|
||||
<Typography variant="h5"
|
||||
noWrap
|
||||
component="div"
|
||||
//sx={{ flexGrow: 0, display: { xs: 'flex', md: 'none' } }}
|
||||
>
|
||||
ToDinos
|
||||
</Typography>
|
||||
<Box sx={{ flexGrow: 1, display: { xs: 'none', md: 'flex' } }}>
|
||||
{pages.map((page) =>
|
||||
<Button key={page} onClick={() => setPage(page)} sx={{ my: 2, color: 'white', display: 'block' }}>
|
||||
{page}
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
{page === "Overview" && <Overview/>}
|
||||
{page === "Todos" && <Todos/>}
|
||||
{page === "Calendar" && <Calendar/>}
|
||||
{page === "Dinos" && <Dinos/>}
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns} locale={deLocale}>
|
||||
<AppBar>
|
||||
<Toolbar>
|
||||
<Typography variant="h5"
|
||||
noWrap
|
||||
component="div"
|
||||
//sx={{ flexGrow: 0, display: { xs: 'flex', md: 'none' } }}
|
||||
>
|
||||
ToDinos
|
||||
</Typography>
|
||||
<Box sx={{ flexGrow: 1, display: { xs: 'none', md: 'flex' } }}>
|
||||
{pages.map((page) =>
|
||||
<Button key={page} onClick={() => setPage(page)} sx={{ my: 2, color: 'white', display: 'block' }}>
|
||||
{page}
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<div style={{display: "flex", flexDirection: "column", gap: 2, height: "100%", maxHeight: "100%"}}>
|
||||
<Toolbar sx={{flexGrow: 1}}/>
|
||||
<div style={{marginLeft: "1em", marginRight: "1em", flexGrow: 1, height: "100%", maxHeight: "100%", minHeight: 0}}>
|
||||
{page === "Overview" && <Overview/>}
|
||||
{page === "Todos" && <Todos/>}
|
||||
{page === "Calendar" && <Calendar/>}
|
||||
{page === "Dinos" && <Dinos/>}
|
||||
</div>
|
||||
</div>
|
||||
</LocalizationProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
Reference in new issue
Block a user