Skip to content

Commit 54cfa82

Browse files
committed
Add a reminder settings dialog and reminder notes
Signed-off-by: Ryan Brue <ryanbrue.dev@gmail.com>
1 parent 1630b3a commit 54cfa82

3 files changed

Lines changed: 138 additions & 31 deletions

File tree

src/components/Header.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { FormControlLabel, FormGroup, Switch } from '@mui/material';
1717

1818
type HeaderProps = {
1919
showCompleted: boolean;
20-
setShowCompleted: (val: boolean) => void;
20+
setShowCompleted: React.Dispatch<React.SetStateAction<boolean>>;
2121
};
2222

2323
export default function Header({ showCompleted, setShowCompleted }: HeaderProps) {
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { Button, Dialog, DialogActions, DialogContent, DialogTitle, FormGroup, TextField } from '@mui/material';
2+
import * as React from 'react';
3+
import type { ReminderType } from '../pages/ReminderPage';
4+
5+
type ReminderSettingsDialogProps = {
6+
open: string | null;
7+
setOpen: React.Dispatch<React.SetStateAction<string | null>>;
8+
reminders: ReminderType[];
9+
setReminders: React.Dispatch<React.SetStateAction<ReminderType[]>>;
10+
};
11+
12+
export default function ReminderSettingsDialog({ open, setOpen, reminders, setReminders }: ReminderSettingsDialogProps) {
13+
14+
// const handleOpen = () => {
15+
// setOpen(true);
16+
// };
17+
18+
const handleClose = () => {
19+
setOpen(null);
20+
};
21+
22+
const handleSave = () => {
23+
if (draft) {
24+
updateReminder(draft.id, draft);
25+
}
26+
setOpen(null);
27+
}
28+
29+
// Update a reminder
30+
const updateReminder = (id: string, fields: Partial<ReminderType>) => {
31+
setReminders((prev) =>
32+
prev.map((r) => (r.id === id ? { ...r, ...fields } : r))
33+
);
34+
};
35+
36+
37+
const [draft, setDraft] = React.useState<ReminderType | null>(null);
38+
// Update draft
39+
const updateDraft = (fields: Partial<ReminderType>) => {
40+
setDraft((prev) => {
41+
return prev ? { ...prev, ...fields } : prev;
42+
})
43+
}
44+
45+
React.useEffect(() => {
46+
if (open) {
47+
const reminder = reminders.find(r => r.id === open);
48+
if (reminder) {
49+
setDraft({ ...reminder }); // shallow clone
50+
}
51+
} else {
52+
setDraft(null);
53+
}
54+
}, [open, reminders]);
55+
56+
return (
57+
<React.Fragment>
58+
<Dialog open={draft != null} onClose={handleClose} fullWidth>
59+
<DialogTitle>Reminder Settings</DialogTitle>
60+
<DialogContent>
61+
{
62+
draft && (
63+
<FormGroup>
64+
<TextField
65+
variant="filled" label="Reminder Name" multiline
66+
value={draft.text}
67+
onChange={(e) => updateDraft({ text: e.target.value })}
68+
/>
69+
<TextField variant="filled" label="Reminder Notes" multiline
70+
value={draft.notes}
71+
onChange={(e) => updateDraft({ notes: e.target.value })}
72+
/>
73+
</FormGroup>
74+
)
75+
}
76+
</DialogContent>
77+
<DialogActions>
78+
<Button onClick={handleClose}>Cancel</Button>
79+
<Button onClick={handleSave}>
80+
Save
81+
</Button>
82+
</DialogActions>
83+
</Dialog>
84+
</React.Fragment>
85+
)
86+
}

src/pages/ReminderPage.tsx

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { Add, Delete, InfoOutline } from "@mui/icons-material";
2-
import { Checkbox, Container, Fab, IconButton, Stack, TextField } from "@mui/material";
2+
import { Checkbox, Container, Fab, IconButton, Stack, TextField, Typography } from "@mui/material";
33
import { v4 as uuidv4 } from 'uuid';
44
import * as React from "react";
5+
import ReminderSettingsDialog from "../components/ReminderSettingsDialog";
56

6-
type ReminderType = {
7+
export type ReminderType = {
78
id: string;
89
text: string;
10+
notes: string;
911
completed: boolean;
1012
};
1113

@@ -23,6 +25,8 @@ export default function ReminderPage({ showCompleted }: ReminderProps) {
2325
const [focusedId, setFocusedId] = React.useState<string | null>(null);
2426
// Input refs, for forcing focus
2527
const inputRefs = React.useRef<Record<string, HTMLInputElement | null>>({});
28+
// Whether the reminder settings is open
29+
const [settingsOpen, setSettingsOpen] = React.useState<string | null>(null);
2630
// Save to localStorage whenever reminders change
2731
React.useEffect(() => {
2832
localStorage.setItem('reminders', JSON.stringify(reminders));
@@ -38,7 +42,7 @@ export default function ReminderPage({ showCompleted }: ReminderProps) {
3842
const id = uuidv4();
3943
setReminders((prev) => [
4044
...prev,
41-
{ id, text: '', completed: false },
45+
{ id, text: "", notes: "", completed: false },
4246
]);
4347

4448
// Focus after next render
@@ -51,7 +55,7 @@ export default function ReminderPage({ showCompleted }: ReminderProps) {
5155
const id = uuidv4();
5256
setReminders(prev => {
5357
const index = prev.findIndex(r => r.id === afterId);
54-
const newReminder: ReminderType = { id, text: '', completed: false };
58+
const newReminder: ReminderType = { id, text: "", notes: "", completed: false };
5559
const updated = [
5660
...prev.slice(0, index + 1),
5761
newReminder,
@@ -74,6 +78,10 @@ export default function ReminderPage({ showCompleted }: ReminderProps) {
7478
const reminderIsEmpty = (reminder: ReminderType) => {
7579
return reminder.text.length === 0;
7680
}
81+
// Open reminder settings for reminder
82+
const openReminderSettings = (id: string) => {
83+
setSettingsOpen(id);
84+
}
7785

7886
const sortedReminders = [...reminders].sort((a, b) => {
7987
// false (incomplete) comes before true (complete)
@@ -97,35 +105,42 @@ export default function ReminderPage({ showCompleted }: ReminderProps) {
97105
updateReminder(r.id, { completed: e.target.checked })
98106
}
99107
/>
100-
<TextField
101-
fullWidth
102-
multiline
103-
variant="outlined"
104-
size="small"
105-
value={r.text}
106-
inputRef={el => (inputRefs.current[r.id] = el)}
107-
onFocus={() => setFocusedId(r.id)}
108-
onBlur={() => {
109-
setFocusedId(null);
110-
if (reminderIsEmpty(r)) {
111-
deleteReminder(r.id);
112-
}
113-
}}
114-
onChange={(e) => updateReminder(r.id, { text: e.target.value })}
115-
onKeyDown={(e) => {
116-
if (e.key === 'Enter') {
117-
e.preventDefault(); // prevent newline
108+
<Stack sx={{ width: "100%" }}>
109+
<TextField
110+
fullWidth
111+
multiline
112+
variant="outlined"
113+
size="medium"
114+
inputRef={el => (inputRefs.current[r.id] = el)}
115+
onFocus={() => setFocusedId(r.id)}
116+
onBlur={() => {
117+
setFocusedId(null);
118118
if (reminderIsEmpty(r)) {
119-
setFocusedId(null);
120119
deleteReminder(r.id);
121120
}
122-
else {
123-
addReminderAfter(r.id);
121+
}}
122+
value={r.text}
123+
onChange={(e) => updateReminder(r.id, { text: e.target.value })}
124+
onKeyDown={(e) => {
125+
if (e.key === 'Enter') {
126+
e.preventDefault(); // prevent newline
127+
if (reminderIsEmpty(r)) {
128+
setFocusedId(null);
129+
deleteReminder(r.id);
130+
}
131+
else {
132+
addReminderAfter(r.id);
133+
}
124134
}
125-
}
126-
}}
127-
sx={{ textDecoration: r.completed ? 'line-through' : 'none', transition: 'transform 0.2s' }}
128-
/>
135+
}}
136+
sx={{ textDecoration: r.completed ? 'line-through' : 'none', transition: 'transform 0.2s' }}
137+
/>
138+
{
139+
r.notes.length > 0 && (
140+
<Typography variant="body2">{r.notes}</Typography>
141+
)
142+
}
143+
</Stack>
129144
<Stack
130145
direction="row"
131146
spacing={1}
@@ -136,7 +151,7 @@ export default function ReminderPage({ showCompleted }: ReminderProps) {
136151
pointerEvents: focusedId === r.id ? 'auto' : 'none',
137152
}}
138153
>
139-
<IconButton size="small" disabled={focusedId !== r.id} onMouseDown={() => { }}>
154+
<IconButton size="small" disabled={focusedId !== r.id} onMouseDown={() => openReminderSettings(r.id)}>
140155
<InfoOutline />
141156
</IconButton>
142157
<IconButton size="small" disabled={focusedId !== r.id} onMouseDown={() => deleteReminder(r.id)}>
@@ -146,6 +161,12 @@ export default function ReminderPage({ showCompleted }: ReminderProps) {
146161
</Stack>
147162
))}
148163
</Stack>
164+
<ReminderSettingsDialog
165+
open={settingsOpen}
166+
setOpen={setSettingsOpen}
167+
reminders={reminders}
168+
setReminders={setReminders}
169+
/>
149170
</Container>
150171
<Fab color="primary" onClick={addReminder} aria-label="add" sx={{
151172
position: 'fixed', // FIXED floats relative to viewport

0 commit comments

Comments
 (0)