Do todos
This commit is contained in:
1 parent
6bf7f18bb5
commit
ea59db58d3
6 files changed
+302
-165
No files matched your search
Generated
+52
@@ -11,6 +11,7 @@
|
|||||||
"@date-io/dayjs": "^2.13.1",
|
"@date-io/dayjs": "^2.13.1",
|
||||||
"@emotion/react": "^11.9.0",
|
"@emotion/react": "^11.9.0",
|
||||||
"@emotion/styled": "^11.8.1",
|
"@emotion/styled": "^11.8.1",
|
||||||
|
"@mui/icons-material": "^5.6.2",
|
||||||
"@mui/material": "^5.6.2",
|
"@mui/material": "^5.6.2",
|
||||||
"@mui/x-date-pickers": "^5.0.0-alpha.1",
|
"@mui/x-date-pickers": "^5.0.0-alpha.1",
|
||||||
"@testing-library/jest-dom": "^5.16.4",
|
"@testing-library/jest-dom": "^5.16.4",
|
||||||
@@ -20,6 +21,7 @@
|
|||||||
"@types/node": "^16.11.27",
|
"@types/node": "^16.11.27",
|
||||||
"@types/react": "^18.0.6",
|
"@types/react": "^18.0.6",
|
||||||
"@types/react-dom": "^18.0.2",
|
"@types/react-dom": "^18.0.2",
|
||||||
|
"date-fns": "^2.28.0",
|
||||||
"dayjs": "^1.11.1",
|
"dayjs": "^1.11.1",
|
||||||
"react": "^18.0.0",
|
"react": "^18.0.0",
|
||||||
"react-dom": "^18.0.0",
|
"react-dom": "^18.0.0",
|
||||||
@@ -3049,6 +3051,31 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@mui/icons-material": {
|
||||||
|
"version": "5.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.6.2.tgz",
|
||||||
|
"integrity": "sha512-9QdI7axKuBAyaGz4mtdi7Uy1j73/thqFmEuxpJHxNC7O8ADEK1Da3t2veK2tgmsXsUlAHcAG63gg+GvWWeQNqQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.17.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@mui/material": "^5.0.0",
|
||||||
|
"@types/react": "^17.0.0 || ^18.0.0",
|
||||||
|
"react": "^17.0.0 || ^18.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@mui/material": {
|
"node_modules/@mui/material": {
|
||||||
"version": "5.6.2",
|
"version": "5.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.6.2.tgz",
|
||||||
@@ -6410,6 +6437,18 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/date-fns": {
|
||||||
|
"version": "2.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz",
|
||||||
|
"integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.11"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/date-fns"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dayjs": {
|
"node_modules/dayjs": {
|
||||||
"version": "1.11.1",
|
"version": "1.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.1.tgz",
|
||||||
@@ -18733,6 +18772,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@mui/icons-material": {
|
||||||
|
"version": "5.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.6.2.tgz",
|
||||||
|
"integrity": "sha512-9QdI7axKuBAyaGz4mtdi7Uy1j73/thqFmEuxpJHxNC7O8ADEK1Da3t2veK2tgmsXsUlAHcAG63gg+GvWWeQNqQ==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.17.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@mui/material": {
|
"@mui/material": {
|
||||||
"version": "5.6.2",
|
"version": "5.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.6.2.tgz",
|
||||||
@@ -21132,6 +21179,11 @@
|
|||||||
"whatwg-url": "^8.0.0"
|
"whatwg-url": "^8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"date-fns": {
|
||||||
|
"version": "2.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz",
|
||||||
|
"integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw=="
|
||||||
|
},
|
||||||
"dayjs": {
|
"dayjs": {
|
||||||
"version": "1.11.1",
|
"version": "1.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.1.tgz",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
"@date-io/dayjs": "^2.13.1",
|
"@date-io/dayjs": "^2.13.1",
|
||||||
"@emotion/react": "^11.9.0",
|
"@emotion/react": "^11.9.0",
|
||||||
"@emotion/styled": "^11.8.1",
|
"@emotion/styled": "^11.8.1",
|
||||||
|
"@mui/icons-material": "^5.6.2",
|
||||||
"@mui/material": "^5.6.2",
|
"@mui/material": "^5.6.2",
|
||||||
"@mui/x-date-pickers": "^5.0.0-alpha.1",
|
"@mui/x-date-pickers": "^5.0.0-alpha.1",
|
||||||
"@testing-library/jest-dom": "^5.16.4",
|
"@testing-library/jest-dom": "^5.16.4",
|
||||||
@@ -15,6 +16,7 @@
|
|||||||
"@types/node": "^16.11.27",
|
"@types/node": "^16.11.27",
|
||||||
"@types/react": "^18.0.6",
|
"@types/react": "^18.0.6",
|
||||||
"@types/react-dom": "^18.0.2",
|
"@types/react-dom": "^18.0.2",
|
||||||
|
"date-fns": "^2.28.0",
|
||||||
"dayjs": "^1.11.1",
|
"dayjs": "^1.11.1",
|
||||||
"react": "^18.0.0",
|
"react": "^18.0.0",
|
||||||
"react-dom": "^18.0.0",
|
"react-dom": "^18.0.0",
|
||||||
|
|||||||
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en" style="height: 100%">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
@@ -26,9 +26,9 @@
|
|||||||
-->
|
-->
|
||||||
<title>React App</title>
|
<title>React App</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body style="height: 100%">
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<div id="root"></div>
|
<div id="root" style="height: 100%"></div>
|
||||||
<!--
|
<!--
|
||||||
This HTML file is a template.
|
This HTML file is a template.
|
||||||
If you open it directly in the browser, you will see an empty page.
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|||||||
+47
-162
@@ -1,158 +1,36 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import { AppBar, Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, Grid, TextField, Toolbar, Typography } from '@mui/material';
|
import deLocale from 'date-fns/locale/de';
|
||||||
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
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 { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
||||||
import dayjs from 'dayjs';
|
import { Todos } from './components/Todos';
|
||||||
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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function Calendar(){
|
function Calendar(){
|
||||||
return(
|
return(
|
||||||
<Typography variant="h2">Calendar</Typography>
|
<Typography variant="h3">Calendar</Typography>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Dinos(){
|
function Dinos(){
|
||||||
return(
|
return(
|
||||||
<Typography variant="h2">Dinos</Typography>
|
<div>
|
||||||
|
<Typography variant="h3">Dinos</Typography>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Overview(){
|
function Overview(){
|
||||||
return (
|
return (
|
||||||
<div>
|
<div style={{display: "flex", flexGrow: 1, flexDirection: "column", height: "100%", maxHeight: "100%"}}>
|
||||||
<Typography variant="h2">Overview</Typography>
|
<div><Typography variant="h2">Overview</Typography></div>
|
||||||
<Grid
|
<Box sx={{display: "flex", flexGrow: 1, justifyContent: "space-evenly", gap: 4, height: "100%", minHeight: 0}}>
|
||||||
container
|
<Paper sx={{overflowY: "auto", width: "100%", padding: 2, height: "calc(100% - 32px)"}}><Todos/></Paper>
|
||||||
justifyContent="space-evenly"
|
<Box sx={{width: "100%", display: "flex", flexDirection: "column", justifyContent: "space-evenly"}}>
|
||||||
alignItems="flex-start"
|
<Calendar/>
|
||||||
spacing={2}>
|
<Calendar/>
|
||||||
<Todos/>
|
</Box>
|
||||||
<Todos/>
|
</Box>
|
||||||
</Grid>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -163,28 +41,35 @@ function App() {
|
|||||||
const [page,setPage] = useState<string>("Overview");
|
const [page,setPage] = useState<string>("Overview");
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AppBar>
|
<LocalizationProvider dateAdapter={AdapterDateFns} locale={deLocale}>
|
||||||
<Toolbar>
|
<AppBar>
|
||||||
<Typography variant="h5"
|
<Toolbar>
|
||||||
noWrap
|
<Typography variant="h5"
|
||||||
component="div"
|
noWrap
|
||||||
//sx={{ flexGrow: 0, display: { xs: 'flex', md: 'none' } }}
|
component="div"
|
||||||
>
|
//sx={{ flexGrow: 0, display: { xs: 'flex', md: 'none' } }}
|
||||||
ToDinos
|
>
|
||||||
</Typography>
|
ToDinos
|
||||||
<Box sx={{ flexGrow: 1, display: { xs: 'none', md: 'flex' } }}>
|
</Typography>
|
||||||
{pages.map((page) =>
|
<Box sx={{ flexGrow: 1, display: { xs: 'none', md: 'flex' } }}>
|
||||||
<Button key={page} onClick={() => setPage(page)} sx={{ my: 2, color: 'white', display: 'block' }}>
|
{pages.map((page) =>
|
||||||
{page}
|
<Button key={page} onClick={() => setPage(page)} sx={{ my: 2, color: 'white', display: 'block' }}>
|
||||||
</Button>
|
{page}
|
||||||
)}
|
</Button>
|
||||||
</Box>
|
)}
|
||||||
</Toolbar>
|
</Box>
|
||||||
</AppBar>
|
</Toolbar>
|
||||||
{page === "Overview" && <Overview/>}
|
</AppBar>
|
||||||
{page === "Todos" && <Todos/>}
|
<div style={{display: "flex", flexDirection: "column", gap: 2, height: "100%", maxHeight: "100%"}}>
|
||||||
{page === "Calendar" && <Calendar/>}
|
<Toolbar sx={{flexGrow: 1}}/>
|
||||||
{page === "Dinos" && <Dinos/>}
|
<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