@@ -12,6 +12,8 @@ import {
12
12
IconButton ,
13
13
Checkbox ,
14
14
CircularProgress ,
15
+ DialogContent ,
16
+ Stack ,
15
17
} from "@mui/material" ;
16
18
import DeleteIcon from "@mui/icons-material/Delete" ;
17
19
import Snackbar , { SnackbarCloseReason } from "@mui/material/Snackbar" ;
@@ -21,7 +23,10 @@ import Select from "@mui/material/Select";
21
23
import MenuItem from "@mui/material/MenuItem" ;
22
24
import Box from "@mui/material/Box" ;
23
25
import Grid from "@mui/material/Grid2" ;
24
- import Divider from '@mui/material/Divider' ;
26
+ import Divider from "@mui/material/Divider" ;
27
+ import Dialog from "@mui/material/Dialog" ;
28
+ import DialogTitle from "@mui/material/DialogTitle" ;
29
+ import { format , formatDistance , formatRelative , subDays } from "date-fns" ;
25
30
26
31
interface Task {
27
32
_id : string ;
@@ -31,15 +36,136 @@ interface Task {
31
36
priority : "Low" | "Medium" | "High" ;
32
37
}
33
38
34
- export default function Home ( ) {
35
- const [ tasks , setTasks ] = useState < Task [ ] > ( [ ] ) ;
36
- const [ newTask , setNewTask ] = useState ( {
39
+ export interface SimpleDialogProps {
40
+ open : boolean ;
41
+ selectedValue ?: Task ;
42
+ onClose : ( value : string ) => void ;
43
+ }
44
+
45
+ function AddTaskDialog ( props : SimpleDialogProps ) {
46
+ const { onClose, selectedValue, open } = props ;
47
+
48
+ const newTaskProp = {
37
49
title : "" ,
38
- dueDate : "" ,
50
+ dueDate : new Date ( ) ,
39
51
priority : "Medium" ,
40
- } ) ;
41
-
52
+ } ;
42
53
const [ loadingState , setLoadingState ] = useState ( false ) ;
54
+ const [ newTask , setNewTask ] = useState ( newTaskProp ) ;
55
+
56
+ const handleClose = ( ) => {
57
+ onClose ( "" ) ;
58
+ } ;
59
+
60
+ const handleListItemClick = ( value : string ) => {
61
+ onClose ( value ) ;
62
+ } ;
63
+ const addTask = async ( ) => {
64
+ if ( ! newTask . title || ! newTask . dueDate ) {
65
+ console . error ( "Validation error" ) ;
66
+ return ;
67
+ }
68
+ setLoadingState ( true ) ;
69
+ const res = await axios . post < Task > ( "/api/tasks" , newTask ) ;
70
+ // setTasks([...tasks, res.data]);
71
+ setNewTask ( newTaskProp ) ;
72
+ // showSnackBar("Task added!");
73
+ setLoadingState ( false ) ;
74
+ onClose ( "Task added!" ) ;
75
+ } ;
76
+ return (
77
+ < Dialog onClose = { handleClose } open = { open } maxWidth = "sm" fullWidth >
78
+ < DialogTitle > Add Task</ DialogTitle >
79
+ < Divider />
80
+ < DialogContent >
81
+ < TextField
82
+ fullWidth
83
+ label = "New Task"
84
+ variant = "standard"
85
+ value = { newTask . title }
86
+ onChange = { ( e ) => setNewTask ( { ...newTask , title : e . target . value } ) }
87
+ // sx={{ mb: 2 }}
88
+ />
89
+
90
+ < Grid container spacing = { 2 } mb = { 2 } >
91
+ < Grid size = { 6 } >
92
+ < TextField
93
+ fullWidth
94
+ type = "date"
95
+ size = "small"
96
+ value = { newTask . dueDate }
97
+ onChange = { ( e ) =>
98
+ setNewTask ( { ...newTask , dueDate : e . target . value } )
99
+ }
100
+ sx = { { mt : 2 } }
101
+ />
102
+ </ Grid >
103
+ < Grid size = { 6 } >
104
+ < Select
105
+ fullWidth
106
+ size = "small"
107
+ value = { newTask . priority }
108
+ onChange = { ( e ) =>
109
+ setNewTask ( {
110
+ ...newTask ,
111
+ priority : e . target . value as "Low" | "Medium" | "High" ,
112
+ } )
113
+ }
114
+ sx = { { mt : 2 } }
115
+ >
116
+ < MenuItem value = "Low" > Low</ MenuItem >
117
+ < MenuItem value = "Medium" > Medium</ MenuItem >
118
+ < MenuItem value = "High" > High</ MenuItem >
119
+ </ Select >
120
+ </ Grid >
121
+ </ Grid >
122
+
123
+ < Button variant = "contained" onClick = { addTask } loading = { loadingState } >
124
+ Add Task
125
+ </ Button >
126
+ </ DialogContent >
127
+ </ Dialog >
128
+ ) ;
129
+ }
130
+
131
+ export function TaskHeader ( props ) {
132
+ const [ open , setOpen ] = useState ( false ) ;
133
+ const [ selectedValue , setSelectedValue ] = useState ( ) ;
134
+
135
+ const handleClickOpen = ( ) => {
136
+ setOpen ( true ) ;
137
+ } ;
138
+
139
+ const handleClose = ( value : string ) => {
140
+ setOpen ( false ) ;
141
+ props . onClose ( value ) ;
142
+ } ;
143
+
144
+ return (
145
+ < Stack direction = "row" alignItems = "center" justifyContent = "space-between" >
146
+ < Typography variant = "h5" gutterBottom >
147
+ Tasks
148
+ < Badge
149
+ badgeContent = { props ?. tasks ?. length }
150
+ color = "info"
151
+ sx = { { ml : 2 } }
152
+ > </ Badge >
153
+ </ Typography >
154
+ < Button variant = "contained" onClick = { handleClickOpen } >
155
+ Add Task
156
+ </ Button >
157
+ < AddTaskDialog
158
+ selectedValue = { selectedValue }
159
+ open = { open }
160
+ onClose = { handleClose }
161
+ />
162
+ </ Stack >
163
+ ) ;
164
+ }
165
+
166
+ export default function Home ( ) {
167
+ const [ tasks , setTasks ] = useState < Task [ ] > ( [ ] ) ;
168
+
43
169
const [ initialLoading , setInitialLoading ] = useState ( true ) ; // Add initial loading state
44
170
const [ openSnackBar , setOpenSnackBar ] = useState ( false ) ;
45
171
const [ snackBarMsg , setSnackBarMsg ] = useState ( "" ) ;
@@ -54,25 +180,29 @@ export default function Home() {
54
180
setOpenSnackBar ( false ) ;
55
181
} ;
56
182
57
- useEffect ( ( ) => {
183
+ const loadList = ( ) => {
58
184
axios . get ( "/api/tasks" ) . then ( ( res ) => {
59
185
setTasks ( res . data ) ;
60
186
setInitialLoading ( false ) ; // Set initial loading to false after tasks are loaded
61
187
} ) ;
188
+ }
189
+
190
+ useEffect ( ( ) => {
191
+ loadList ( ) ;
62
192
} , [ ] ) ;
63
193
64
- const addTask = async ( ) => {
65
- if ( ! newTask . title || ! newTask . dueDate ) {
66
- console . error ( "Validation error" ) ;
67
- return ;
68
- }
69
- setLoadingState ( true ) ;
70
- const res = await axios . post < Task > ( "/api/tasks" , newTask ) ;
71
- setTasks ( [ ...tasks , res . data ] ) ;
72
- setNewTask ( { title : "" , dueDate : "" , priority : "Medium" } ) ;
73
- showSnackBar ( "Task added!" ) ;
74
- setLoadingState ( false ) ;
75
- } ;
194
+ // const addTask = async () => {
195
+ // if (!newTask.title || !newTask.dueDate) {
196
+ // console.error("Validation error");
197
+ // return;
198
+ // }
199
+ // setLoadingState(true);
200
+ // const res = await axios.post<Task>("/api/tasks", newTask);
201
+ // setTasks([...tasks, res.data]);
202
+ // setNewTask({ title: "", dueDate: "", priority: "Medium" });
203
+ // showSnackBar("Task added!");
204
+ // setLoadingState(false);
205
+ // };
76
206
77
207
const toggleTask = async ( task : Task ) => {
78
208
const res = await axios . put < Task > ( `/api/task?id=${ task . _id } ` , {
@@ -100,70 +230,20 @@ export default function Home() {
100
230
hideSnackBar ( ) ;
101
231
} ;
102
232
233
+ const onDialogClose = ( msg : string ) => {
234
+ showSnackBar ( msg ) ;
235
+ loadList ( ) ;
236
+ } ;
103
237
return (
104
238
< Container maxWidth = "md" sx = { { mt : 1 } } >
105
239
< Paper elevation = { 3 } sx = { { p : 4 } } >
106
- < Typography variant = "h5" gutterBottom >
107
- Tasks
108
- < Badge
109
- badgeContent = { tasks ?. length }
110
- color = "info"
111
- sx = { { ml : 2 } }
112
- > </ Badge >
113
- </ Typography >
114
- < TextField
115
- fullWidth
116
- label = "New Task"
117
- variant = "standard"
118
- value = { newTask . title }
119
- onChange = { ( e ) => setNewTask ( { ...newTask , title : e . target . value } ) }
120
- // sx={{ mb: 2 }}
121
- />
122
- < Box sx = { { flexGrow : 1 , mb : 2 } } >
123
- < Grid container spacing = { 2 } >
124
- < Grid size = { 3 } >
125
- < TextField
126
- fullWidth
127
- type = "date"
128
- size = "small"
129
- value = { newTask . dueDate }
130
- onChange = { ( e ) =>
131
- setNewTask ( { ...newTask , dueDate : e . target . value } )
132
- }
133
- sx = { { mt : 2 } }
134
- />
135
- </ Grid >
136
- < Grid size = { 3 } >
137
- < Select
138
- fullWidth
139
- size = "small"
140
- value = { newTask . priority }
141
- onChange = { ( e ) =>
142
- setNewTask ( {
143
- ...newTask ,
144
- priority : e . target . value as "Low" | "Medium" | "High" ,
145
- } )
146
- }
147
- sx = { { mt : 2 } }
148
- >
149
- < MenuItem value = "Low" > Low</ MenuItem >
150
- < MenuItem value = "Medium" > Medium</ MenuItem >
151
- < MenuItem value = "High" > High</ MenuItem >
152
- </ Select >
153
- </ Grid >
154
- </ Grid >
155
- </ Box >
156
-
157
- < Button variant = "contained" onClick = { addTask } loading = { loadingState } >
158
- Add Task
159
- </ Button >
160
- { /* <Divider sx={{mt:2}}/> */ }
240
+ < TaskHeader { ...{ tasks, onClose : onDialogClose } } />
161
241
162
242
{ initialLoading ? ( // Show loading icon if initial loading is true
163
243
< Box display = "flex" justifyContent = "center" sx = { { mt : 2 } } >
164
244
< CircularProgress />
165
- </ Box >
166
- ) : (
245
+ </ Box >
246
+ ) : (
167
247
< List sx = { { mt : 1 , mx : - 3 } } >
168
248
{ tasks . map ( ( task ) => (
169
249
< ListItem
@@ -179,7 +259,7 @@ export default function Home() {
179
259
onChange = { ( ) => toggleTask ( task ) }
180
260
/>
181
261
< ListItemText
182
- primary = { `${ task . title } (Due: ${ task . dueDate } )` }
262
+ primary = { `${ task . title } (Due: ${ format ( task . dueDate ?? new Date ( ) , "dd MMM" ) } )` }
183
263
secondary = { `Priority: ${ task . priority } ` }
184
264
sx = { {
185
265
textDecoration : task . completed ? "line-through" : "none" ,
0 commit comments