[#77] added repetition proper display and editing

This commit is contained in:
Camille Moussu
2025-09-04 15:02:55 +02:00
parent ce41bdd23c
commit 80028b3f26
3 changed files with 400 additions and 328 deletions
+78 -26
View File
@@ -6,18 +6,15 @@ import {
MenuItem, MenuItem,
Box, Box,
Stack, Stack,
Paper,
Typography, Typography,
TextField, TextField,
Checkbox, Checkbox,
List,
ListItem,
FormControlLabel, FormControlLabel,
FormGroup, FormGroup,
Radio, Radio,
RadioGroup, RadioGroup,
} from "@mui/material"; } from "@mui/material";
import { useState } from "react"; import { useEffect, useState } from "react";
import { RepetitionObject } from "../../features/Events/EventsTypes"; import { RepetitionObject } from "../../features/Events/EventsTypes";
export default function RepeatEvent({ export default function RepeatEvent({
@@ -29,23 +26,31 @@ export default function RepeatEvent({
setRepetition: Function; setRepetition: Function;
isOwn?: boolean; isOwn?: boolean;
}) { }) {
console.log(JSON.stringify(repetition));
const repetitionValues = ["day", "week", "month", "year"]; const repetitionValues = ["day", "week", "month", "year"];
const [interval, setInterval] = useState(repetition.interval ?? 0);
const [selectedDays, setSelectedDays] = useState<string[]>(
repetition.selectedDays ?? []
);
const [endOption, setEndOption] = useState("");
const [occurrences, setOccurrences] = useState(repetition.occurrences) ?? 0;
const [endDate, setEndDate] = useState(repetition.endDate ?? "");
const days = ["MO", "TU", "WE", "TH", "FR", "SA", "SU"]; const days = ["MO", "TU", "WE", "TH", "FR", "SA", "SU"];
const handleDayChange = (day: string) => { // derive endOption based on repetition
setSelectedDays((prev: string[]) => const getEndOption = () => {
prev.includes(day) ? prev.filter((d) => d !== day) : [...prev, day] if (repetition.occurrences && repetition.occurrences > 0) return "after";
); if (repetition.endDate) return "on";
return "never";
}; };
const [endOption, setEndOption] = useState(getEndOption());
// keep endOption in sync if repetition changes from parent
useEffect(() => {
setEndOption(getEndOption());
}, [repetition.occurrences, repetition.endDate]);
const handleDayChange = (day: string) => {
const updatedDays = repetition.selectedDays?.includes(day)
? repetition.selectedDays.filter((d) => d !== day)
: [...(repetition.selectedDays ?? []), day];
setRepetition({ ...repetition, selectedDays: updatedDays });
};
return ( return (
<FormControl fullWidth margin="dense" size="small"> <FormControl fullWidth margin="dense" size="small">
<InputLabel id="repeat">Repetition</InputLabel> <InputLabel id="repeat">Repetition</InputLabel>
@@ -64,18 +69,24 @@ export default function RepeatEvent({
<MenuItem value={"monthly"}>Repeat monthly</MenuItem> <MenuItem value={"monthly"}>Repeat monthly</MenuItem>
<MenuItem value={"yearly"}>Repeat yearly</MenuItem> <MenuItem value={"yearly"}>Repeat yearly</MenuItem>
</Select> </Select>
{repetition.freq && ( {repetition.freq && (
<Stack> <Stack>
{/* Interval */}
<Box display="flex" alignItems="center" gap={2} mb={2}> <Box display="flex" alignItems="center" gap={2} mb={2}>
<Typography>Interval:</Typography> <Typography>Interval:</Typography>
<TextField <TextField
type="number" type="number"
value={interval} value={repetition.interval ?? 0}
onChange={(e) => setInterval(Number(e.target.value))} onChange={(e) =>
setRepetition({
...repetition,
interval: Number(e.target.value),
})
}
size="small" size="small"
sx={{ width: 80 }} sx={{ width: 80 }}
/> />
<Typography> <Typography>
{ {
repetitionValues[ repetitionValues[
@@ -84,6 +95,8 @@ export default function RepeatEvent({
} }
</Typography> </Typography>
</Box> </Box>
{/* Weekly selection */}
{repetition.freq === "weekly" && ( {repetition.freq === "weekly" && (
<Box> <Box>
<Typography variant="body2" gutterBottom> <Typography variant="body2" gutterBottom>
@@ -95,7 +108,9 @@ export default function RepeatEvent({
key={day} key={day}
control={ control={
<Checkbox <Checkbox
checked={selectedDays.includes(day)} checked={
repetition.selectedDays?.includes(day) ?? false
}
onChange={() => handleDayChange(day)} onChange={() => handleDayChange(day)}
/> />
} }
@@ -105,13 +120,38 @@ export default function RepeatEvent({
</FormGroup> </FormGroup>
</Box> </Box>
)} )}
{/* End options */}
<Box> <Box>
<Typography variant="body2" gutterBottom sx={{ mt: 2 }}> <Typography variant="body2" gutterBottom sx={{ mt: 2 }}>
End: End:
</Typography> </Typography>
<RadioGroup <RadioGroup
value={endOption} value={endOption}
onChange={(e) => setEndOption(e.target.value)} onChange={(e) => {
const value = e.target.value;
setEndOption(value);
if (value === "never") {
setRepetition({ ...repetition, occurrences: 0, endDate: "" });
}
if (value === "after") {
setRepetition({
freq: repetition.freq,
interval: repetition.interval,
occurence: Number(e.target.value),
});
}
if (value === "on") {
setRepetition({
freq: repetition.freq,
interval: repetition.interval,
endDate: new Date(e.target.value)
.toISOString()
.slice(0, 16),
});
}
}}
> >
<FormControlLabel <FormControlLabel
value="never" value="never"
@@ -128,8 +168,14 @@ export default function RepeatEvent({
<TextField <TextField
type="number" type="number"
size="small" size="small"
value={occurrences} value={repetition.occurrences ?? 0}
onChange={(e) => setOccurrences(Number(e.target.value))} onChange={(e) =>
setRepetition({
freq: repetition.freq,
interval: repetition.interval,
occurence: Number(e.target.value),
})
}
sx={{ width: 100 }} sx={{ width: 100 }}
inputProps={{ min: 1 }} inputProps={{ min: 1 }}
disabled={endOption !== "after"} disabled={endOption !== "after"}
@@ -148,8 +194,14 @@ export default function RepeatEvent({
<TextField <TextField
type="date" type="date"
size="small" size="small"
value={endDate} value={repetition.endDate ?? ""}
onChange={(e) => setEndDate(e.target.value)} onChange={(e) =>
setRepetition({
freq: repetition.freq,
interval: repetition.interval,
endDate: e.target.value,
})
}
disabled={endOption !== "on"} disabled={endOption !== "on"}
/> />
</Box> </Box>
+22 -5
View File
@@ -124,7 +124,8 @@ export default function EventDisplayModal({
if (!event || !calendar) { if (!event || !calendar) {
onClose({}, "backdropClick"); onClose({}, "backdropClick");
} }
}, [open, eventId, dispatch, onClose]); setRepetition(event.repetition ?? ({} as RepetitionObject));
}, [open, eventId, dispatch, onClose, event]);
if (!event || !calendar) return null; if (!event || !calendar) return null;
@@ -224,17 +225,30 @@ export default function EventDisplayModal({
return ( return (
<Modal open={open} onClose={onClose}> <Modal open={open} onClose={onClose}>
<Card sx={{ minWidth: 300, width: "50vw", p: 2, position: "absolute" }}> <Box
sx={{
position: "absolute",
top: "5vh",
left: "50%",
transform: "translate(-50%, -50%)",
width: "50vw",
maxHeight: "80vh",
}}
>
<Card sx={{ p: 2, position: "absolute" }}>
{/* Close button */} {/* Close button */}
<Box sx={{ position: "absolute", top: 8, right: 8 }}> <Box sx={{ position: "absolute", top: 8, right: 8 }}>
<IconButton size="small" onClick={() => onClose({}, "backdropClick")}> <IconButton
size="small"
onClick={() => onClose({}, "backdropClick")}
>
<CloseIcon fontSize="small" /> <CloseIcon fontSize="small" />
</IconButton> </IconButton>
</Box> </Box>
<CardHeader title={isOwn ? "Edit Event" : "Event Details"} /> <CardHeader title={isOwn ? "Edit Event" : "Event Details"} />
<CardContent sx={{ overflow: "auto" }}> <CardContent sx={{ maxHeight: "75vh", overflow: "auto" }}>
{/* Title */} {/* Title */}
<TextField <TextField
fullWidth fullWidth
@@ -458,7 +472,9 @@ export default function EventDisplayModal({
labelId="alarm" labelId="alarm"
value={alarm} value={alarm}
disabled={!isOwn} disabled={!isOwn}
onChange={(e: SelectChangeEvent) => setAlarm(e.target.value)} onChange={(e: SelectChangeEvent) =>
setAlarm(e.target.value)
}
> >
<MenuItem value={""}>No Alarm</MenuItem> <MenuItem value={""}>No Alarm</MenuItem>
<MenuItem value={"-PT1M"}>1 minute</MenuItem> <MenuItem value={"-PT1M"}>1 minute</MenuItem>
@@ -533,6 +549,7 @@ export default function EventDisplayModal({
</ButtonGroup> </ButtonGroup>
</CardActions> </CardActions>
</Card> </Card>
</Box>
</Modal> </Modal>
); );
} }
+5 -2
View File
@@ -84,18 +84,21 @@ export function parseCalendarEvent(
break; break;
case "rrule": case "rrule":
event.repetition = { freq: value.freq.toLowerCase() }; event.repetition = { freq: value.freq.toLowerCase() };
console.log(value);
if (value.byday) { if (value.byday) {
console.log(value.byday);
event.repetition.selectedDays = value.byday; event.repetition.selectedDays = value.byday;
} }
if (value.until) { if (value.until) {
event.repetition.selectedDays = value.endDate; event.repetition.endDate = value.until;
} }
if (value.count) { if (value.count) {
event.repetition.selectedDays = value.occurrences; event.repetition.occurrences = value.count;
} }
if (value.interval) { if (value.interval) {
event.repetition.interval = value.interval; event.repetition.interval = value.interval;
} }
console.log(event.repetition);
break; break;
} }
} }