Co-authored-by: Camille Moussu <cmoussu@linagora.com>
This commit is contained in:
@@ -1,640 +0,0 @@
|
||||
import CircleIcon from "@mui/icons-material/Circle";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
|
||||
import VideocamIcon from "@mui/icons-material/Videocam";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Card,
|
||||
CardActions,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
Checkbox,
|
||||
Divider,
|
||||
FormControl,
|
||||
FormControlLabel,
|
||||
IconButton,
|
||||
InputLabel,
|
||||
MenuItem,
|
||||
Modal,
|
||||
Select,
|
||||
SelectChangeEvent,
|
||||
TextField,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||
import AttendeeSelector from "../../components/Attendees/AttendeeSearch";
|
||||
import {
|
||||
handleDelete,
|
||||
handleRSVP,
|
||||
} from "../../components/Event/eventHandlers/eventHandlers";
|
||||
import RepeatEvent from "../../components/Event/EventRepeat";
|
||||
import { InfoRow } from "../../components/Event/InfoRow";
|
||||
import { refreshCalendars } from "../../components/Event/utils/eventUtils";
|
||||
import { renderAttendeeBadge } from "../../components/Event/utils/eventUtils";
|
||||
import { getCalendarRange } from "../../utils/dateUtils";
|
||||
import {
|
||||
moveEventAsync,
|
||||
putEventAsync,
|
||||
removeEvent,
|
||||
updateEventInstanceAsync,
|
||||
updateSeriesAsync,
|
||||
} from "../Calendars/CalendarSlice";
|
||||
import { Calendars } from "../Calendars/CalendarTypes";
|
||||
import { userAttendee } from "../User/userDataTypes";
|
||||
import { getEvent } from "./EventApi";
|
||||
import { formatLocalDateTime } from "../../components/Event/utils/dateTimeFormatters";
|
||||
import { CalendarEvent, RepetitionObject } from "./EventsTypes";
|
||||
|
||||
export default function EventDisplayModal({
|
||||
eventId,
|
||||
calId,
|
||||
open,
|
||||
onClose,
|
||||
typeOfAction,
|
||||
}: {
|
||||
eventId: string;
|
||||
calId: string;
|
||||
open: boolean;
|
||||
onClose: (event: {}, reason: "backdropClick" | "escapeKeyDown") => void;
|
||||
typeOfAction?: "solo" | "all";
|
||||
}) {
|
||||
const dispatch = useAppDispatch();
|
||||
const calendar = useAppSelector((state) => state.calendars.list[calId]);
|
||||
const event = useAppSelector(
|
||||
(state) => state.calendars.list[calId]?.events[eventId]
|
||||
);
|
||||
const user = useAppSelector((state) => state.user);
|
||||
|
||||
const [showAllAttendees, setShowAllAttendees] = useState(false);
|
||||
const [showMore, setShowMore] = useState(false);
|
||||
|
||||
const calendars = Object.values(
|
||||
useAppSelector((state) => state.calendars.list)
|
||||
);
|
||||
|
||||
const userPersonnalCalendars: Calendars[] = calendars.filter(
|
||||
(c) => c.id?.split("/")[0] === user.userData?.openpaasId
|
||||
);
|
||||
|
||||
// Form state
|
||||
const [title, setTitle] = useState(event?.title ?? "");
|
||||
const [description, setDescription] = useState(event?.description ?? "");
|
||||
const [location, setLocation] = useState(event?.location ?? "");
|
||||
const [start, setStart] = useState(
|
||||
formatLocalDateTime(new Date(event?.start ?? Date.now()))
|
||||
);
|
||||
const [end, setEnd] = useState(
|
||||
formatLocalDateTime(new Date(event?.end ?? Date.now()))
|
||||
);
|
||||
const [allday, setAllDay] = useState(event?.allday ?? false);
|
||||
const [repetition, setRepetition] = useState<RepetitionObject>(
|
||||
event?.repetition ?? ({} as RepetitionObject)
|
||||
);
|
||||
const [alarm, setAlarm] = useState(event?.alarm?.trigger ?? "");
|
||||
const [busy, setBusy] = useState(event?.transp ?? "OPAQUE");
|
||||
const [eventClass, setEventClass] = useState(event?.class ?? "PUBLIC");
|
||||
const [timezone, setTimezone] = useState(event?.timezone ?? "UTC");
|
||||
const [newCalId, setNewCalId] = useState(event?.calId);
|
||||
const [calendarid, setCalendarid] = useState(
|
||||
calId.split("/")[0] === user.userData?.openpaasId
|
||||
? userPersonnalCalendars.findIndex((cal) => cal.id === calId)
|
||||
: calendars.findIndex((cal) => cal.id === calId)
|
||||
);
|
||||
|
||||
const [attendees, setAttendees] = useState(
|
||||
(event?.attendee || []).filter(
|
||||
(a) => a.cal_address !== event?.organizer?.cal_address
|
||||
)
|
||||
);
|
||||
const currentUserAttendee = event?.attendee?.find(
|
||||
(person) => person.cal_address === user.userData.email
|
||||
);
|
||||
|
||||
const organizer =
|
||||
event?.attendee?.find(
|
||||
(a) => a.cal_address === event?.organizer?.cal_address
|
||||
) ?? ({} as userAttendee);
|
||||
|
||||
const isOwn = organizer?.cal_address === user.userData.email;
|
||||
const isOwnCal = userPersonnalCalendars.find((cal) => cal.id === calId);
|
||||
const attendeeDisplayLimit = 3;
|
||||
|
||||
useEffect(() => {
|
||||
if (!event || !calendar) {
|
||||
onClose({}, "backdropClick");
|
||||
}
|
||||
setRepetition(event?.repetition ?? ({} as RepetitionObject));
|
||||
}, [open, eventId, dispatch, onClose, event, calendar]);
|
||||
useEffect(() => {
|
||||
const fetchMasterEvent = async () => {
|
||||
const masterEvent = await getEvent(event);
|
||||
|
||||
setTitle(masterEvent.title ?? "");
|
||||
setDescription(masterEvent.description ?? "");
|
||||
setLocation(masterEvent.location ?? "");
|
||||
setStart(formatLocalDateTime(new Date(masterEvent?.start ?? Date.now())));
|
||||
setEnd(formatLocalDateTime(new Date(masterEvent?.end ?? Date.now())));
|
||||
setAllDay(masterEvent.allday ?? false);
|
||||
setRepetition(masterEvent?.repetition ?? ({} as RepetitionObject));
|
||||
setAlarm(masterEvent?.alarm?.trigger ?? "");
|
||||
setBusy(masterEvent?.transp ?? "OPAQUE");
|
||||
setEventClass(masterEvent?.class ?? "PUBLIC");
|
||||
setTimezone(masterEvent.timezone ?? "UTC");
|
||||
};
|
||||
if (typeOfAction === "all") {
|
||||
fetchMasterEvent();
|
||||
}
|
||||
}, [typeOfAction, event]);
|
||||
|
||||
if (!event || !calendar) return null;
|
||||
const isRecurring = event.uid?.includes("/");
|
||||
|
||||
const handleSave = async () => {
|
||||
const newEventUID = crypto.randomUUID();
|
||||
|
||||
const newEvent: CalendarEvent = {
|
||||
calId,
|
||||
title,
|
||||
URL: event.URL ?? `/calendars/${calId}/${newEventUID}.ics`,
|
||||
start: start,
|
||||
end: end,
|
||||
allday,
|
||||
uid: event.uid ?? newEventUID,
|
||||
description,
|
||||
location,
|
||||
repetition,
|
||||
class: eventClass,
|
||||
organizer: event.organizer,
|
||||
timezone,
|
||||
attendee: [organizer, ...attendees],
|
||||
transp: busy,
|
||||
color: userPersonnalCalendars[calendarid]?.color,
|
||||
alarm: { trigger: alarm, action: "EMAIL" },
|
||||
};
|
||||
|
||||
const [baseId, recurrenceId] = event.uid.split("/");
|
||||
const calendarRange = getCalendarRange(new Date(start));
|
||||
|
||||
if (typeOfAction === "solo") {
|
||||
dispatch(
|
||||
updateEventInstanceAsync({
|
||||
cal: userPersonnalCalendars[calendarid],
|
||||
event: { ...newEvent, recurrenceId: recurrenceId },
|
||||
})
|
||||
);
|
||||
} else if (typeOfAction === "all") {
|
||||
dispatch(
|
||||
updateSeriesAsync({
|
||||
cal: userPersonnalCalendars[calendarid],
|
||||
event: { ...newEvent, recurrenceId: recurrenceId },
|
||||
})
|
||||
);
|
||||
await refreshCalendars(dispatch, calendars, calendarRange);
|
||||
} else {
|
||||
dispatch(
|
||||
putEventAsync({ cal: userPersonnalCalendars[calendarid], newEvent })
|
||||
);
|
||||
}
|
||||
|
||||
if (newCalId !== calId) {
|
||||
dispatch(
|
||||
moveEventAsync({
|
||||
cal: userPersonnalCalendars[calendarid],
|
||||
newEvent,
|
||||
newURL: `/calendars/${newCalId}/${baseId}.ics`,
|
||||
})
|
||||
);
|
||||
dispatch(removeEvent({ calendarUid: calId, eventUid: event.uid }));
|
||||
}
|
||||
onClose({}, "backdropClick");
|
||||
};
|
||||
|
||||
const handleToggleShowMore = async () => {
|
||||
setShowMore(!showMore);
|
||||
};
|
||||
|
||||
const calList =
|
||||
calId.split("/")[0] === user.userData?.openpaasId
|
||||
? Object.keys(userPersonnalCalendars).map((calendar, index) => (
|
||||
<MenuItem key={index} value={index}>
|
||||
<Typography variant="body2">
|
||||
<CircleIcon
|
||||
style={{
|
||||
color:
|
||||
userPersonnalCalendars[index].color?.light ?? "#3788D8",
|
||||
width: 12,
|
||||
height: 12,
|
||||
}}
|
||||
/>
|
||||
{userPersonnalCalendars[index].name}
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
))
|
||||
: Object.keys(calendars).map((calendar, index) => (
|
||||
<MenuItem key={index} value={index}>
|
||||
<Typography variant="body2">
|
||||
<CircleIcon
|
||||
style={{
|
||||
color: calendars[index].color?.light ?? "#3788D8",
|
||||
width: 12,
|
||||
height: 12,
|
||||
}}
|
||||
/>
|
||||
{calendars[index].name} - {calendars[index].owner}
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
));
|
||||
|
||||
return (
|
||||
<Modal open={open} onClose={onClose}>
|
||||
<Box
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "5vh",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: "50vw",
|
||||
maxHeight: "80vh",
|
||||
}}
|
||||
>
|
||||
<Card style={{ padding: 16, position: "absolute" }}>
|
||||
{/* Close button */}
|
||||
<Box style={{ position: "absolute", top: 8, right: 8 }}>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => onClose({}, "backdropClick")}
|
||||
>
|
||||
<CloseIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
<CardHeader title={isOwn ? "Edit Event" : "Event Details"} />
|
||||
|
||||
<CardContent style={{ maxHeight: "75vh", overflow: "auto" }}>
|
||||
{/* Title */}
|
||||
<TextField
|
||||
fullWidth
|
||||
disabled={!isOwn}
|
||||
label="Title"
|
||||
value={title ?? ""}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
size="small"
|
||||
margin="dense"
|
||||
/>
|
||||
|
||||
{/* RSVP */}
|
||||
{currentUserAttendee && isOwnCal && (
|
||||
<Card style={{ margin: "8px 0" }}>
|
||||
<ButtonGroup size="small" fullWidth>
|
||||
<Button
|
||||
color={
|
||||
currentUserAttendee.partstat === "ACCEPTED"
|
||||
? "success"
|
||||
: "primary"
|
||||
}
|
||||
onClick={() =>
|
||||
handleRSVP(
|
||||
dispatch,
|
||||
calendar,
|
||||
user,
|
||||
event,
|
||||
"ACCEPTED",
|
||||
undefined,
|
||||
isRecurring ? typeOfAction : undefined
|
||||
)
|
||||
}
|
||||
>
|
||||
Accept
|
||||
</Button>
|
||||
<Button
|
||||
color={
|
||||
currentUserAttendee.partstat === "TENTATIVE"
|
||||
? "warning"
|
||||
: "primary"
|
||||
}
|
||||
onClick={() =>
|
||||
handleRSVP(
|
||||
dispatch,
|
||||
calendar,
|
||||
user,
|
||||
event,
|
||||
"TENTATIVE",
|
||||
undefined,
|
||||
isRecurring ? typeOfAction : undefined
|
||||
)
|
||||
}
|
||||
>
|
||||
Maybe
|
||||
</Button>
|
||||
<Button
|
||||
color={
|
||||
currentUserAttendee.partstat === "DECLINED"
|
||||
? "error"
|
||||
: "primary"
|
||||
}
|
||||
onClick={() =>
|
||||
handleRSVP(
|
||||
dispatch,
|
||||
calendar,
|
||||
user,
|
||||
event,
|
||||
"DECLINED",
|
||||
undefined,
|
||||
isRecurring ? typeOfAction : undefined
|
||||
)
|
||||
}
|
||||
>
|
||||
Decline
|
||||
</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() => console.log("proposenewtime")}
|
||||
>
|
||||
Propose new time
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Calendar selector */}
|
||||
<FormControl fullWidth margin="dense" size="small">
|
||||
<InputLabel id="calendar-select-label">Calendar</InputLabel>
|
||||
<Select
|
||||
disabled={!isOwn}
|
||||
labelId="calendar-select-label"
|
||||
value={calendarid.toString() ?? ""}
|
||||
label="Calendar"
|
||||
onChange={(e: SelectChangeEvent) => {
|
||||
const newId = Number(e.target.value);
|
||||
setCalendarid(newId);
|
||||
setNewCalId(userPersonnalCalendars[newId].id);
|
||||
}}
|
||||
>
|
||||
{calList}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
{/* Dates */}
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Start"
|
||||
disabled={!isOwn}
|
||||
type={allday ? "date" : "datetime-local"}
|
||||
value={allday ? start.split("T")[0] : start.slice(0, 16)}
|
||||
onChange={(e) =>
|
||||
setStart(formatLocalDateTime(new Date(e.target.value)))
|
||||
}
|
||||
size="small"
|
||||
margin="dense"
|
||||
InputLabelProps={{ shrink: true }}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
disabled={!isOwn}
|
||||
label="End"
|
||||
type={allday ? "date" : "datetime-local"}
|
||||
value={allday ? end.split("T")[0] : end.slice(0, 16)}
|
||||
onChange={(e) =>
|
||||
setEnd(formatLocalDateTime(new Date(e.target.value)))
|
||||
}
|
||||
size="small"
|
||||
margin="dense"
|
||||
InputLabelProps={{ shrink: true }}
|
||||
/>
|
||||
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
disabled={!isOwn}
|
||||
checked={allday}
|
||||
onChange={() => {
|
||||
const endDate = new Date(end);
|
||||
const startDate = new Date(start);
|
||||
setAllDay(!allday);
|
||||
if (endDate.getDate() === startDate.getDate()) {
|
||||
endDate.setDate(startDate.getDate() + 1);
|
||||
setEnd(formatLocalDateTime(endDate));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label="All day"
|
||||
/>
|
||||
|
||||
{/* Description & Location */}
|
||||
<TextField
|
||||
fullWidth
|
||||
disabled={!isOwn}
|
||||
label="Description"
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
size="small"
|
||||
margin="dense"
|
||||
multiline
|
||||
rows={2}
|
||||
/>
|
||||
|
||||
{isOwn && (
|
||||
<AttendeeSelector
|
||||
attendees={attendees}
|
||||
setAttendees={setAttendees}
|
||||
/>
|
||||
)}
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Location"
|
||||
disabled={!isOwn}
|
||||
value={location}
|
||||
onChange={(e) => setLocation(e.target.value)}
|
||||
size="small"
|
||||
margin="dense"
|
||||
/>
|
||||
|
||||
{/* Video */}
|
||||
{event.x_openpass_videoconference && (
|
||||
<InfoRow
|
||||
icon={<VideocamIcon style={{ fontSize: 18 }} />}
|
||||
content={
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() =>
|
||||
window.open(event.x_openpass_videoconference)
|
||||
}
|
||||
>
|
||||
Join the video conference
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Attendees */}
|
||||
{event.attendee?.length > 0 && (
|
||||
<Box style={{ marginBottom: 8 }}>
|
||||
<Typography variant="subtitle2">Attendees:</Typography>
|
||||
{organizer.cal_address &&
|
||||
renderAttendeeBadge(organizer, "org", true, true)}
|
||||
{(showAllAttendees
|
||||
? attendees
|
||||
: attendees.slice(0, attendeeDisplayLimit)
|
||||
).map((a, idx) => (
|
||||
<Box key={a.cal_address}>
|
||||
{renderAttendeeBadge(a, idx.toString(), true)}
|
||||
{isOwn && (
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => {
|
||||
const newAttendeesList = [...attendees];
|
||||
newAttendeesList.splice(idx, 1);
|
||||
setAttendees(newAttendeesList);
|
||||
}}
|
||||
>
|
||||
<CloseIcon fontSize="small" />
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
))}
|
||||
{attendees.length > attendeeDisplayLimit && (
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="primary"
|
||||
style={{ cursor: "pointer", marginTop: 4 }}
|
||||
onClick={() => setShowAllAttendees(!showAllAttendees)}
|
||||
>
|
||||
{showAllAttendees
|
||||
? "Show less"
|
||||
: `Show more (${
|
||||
attendees.length - attendeeDisplayLimit
|
||||
} more)`}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Divider style={{ margin: "8px 0" }} />
|
||||
|
||||
{/* Extended options */}
|
||||
{showMore && (
|
||||
<>
|
||||
{isOwn && (
|
||||
<RepeatEvent
|
||||
repetition={repetition}
|
||||
eventStart={new Date(event.start)}
|
||||
setRepetition={setRepetition}
|
||||
isOwn={isOwn && typeOfAction !== "solo"}
|
||||
/>
|
||||
)}
|
||||
<FormControl fullWidth margin="dense" size="small">
|
||||
<InputLabel id="notification">Notification</InputLabel>
|
||||
<Select
|
||||
labelId="notification"
|
||||
label="Notification"
|
||||
value={alarm}
|
||||
disabled={!isOwn}
|
||||
onChange={(e: SelectChangeEvent) =>
|
||||
setAlarm(e.target.value)
|
||||
}
|
||||
>
|
||||
<MenuItem value={""}>No Notification</MenuItem>
|
||||
<MenuItem value={"-PT1M"}>1 minute</MenuItem>
|
||||
<MenuItem value={"-PT5M"}>2 minutes</MenuItem>
|
||||
<MenuItem value={"-PT10M"}>10 minutes</MenuItem>
|
||||
<MenuItem value={"-PT15M"}>15 minutes</MenuItem>
|
||||
<MenuItem value={"-PT30M"}>30 minutes</MenuItem>
|
||||
<MenuItem value={"-PT1H"}>1 hours</MenuItem>
|
||||
<MenuItem value={"-PT2H"}>2 hours</MenuItem>
|
||||
<MenuItem value={"-PT5H"}>5 hours</MenuItem>
|
||||
<MenuItem value={"-PT12H"}>12 hours</MenuItem>
|
||||
<MenuItem value={"-PT1D"}>1 day</MenuItem>
|
||||
<MenuItem value={"-PT2D"}>2 days</MenuItem>
|
||||
<MenuItem value={"-PT1W"}>1 week</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormControl fullWidth margin="dense" size="small">
|
||||
<InputLabel id="Visibility">Visibility</InputLabel>
|
||||
<Select
|
||||
labelId="Visibility"
|
||||
label="Visibility"
|
||||
value={eventClass}
|
||||
disabled={!isOwn}
|
||||
onChange={(e: SelectChangeEvent) =>
|
||||
setEventClass(e.target.value)
|
||||
}
|
||||
>
|
||||
<MenuItem value={"PUBLIC"}>Public</MenuItem>
|
||||
<MenuItem value={"CONFIDENTIAL"}>Show time only</MenuItem>
|
||||
<MenuItem value={"PRIVATE"}>Private</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormControl fullWidth margin="dense" size="small">
|
||||
<InputLabel id="busy">is Busy</InputLabel>
|
||||
<Select
|
||||
labelId="busy"
|
||||
value={busy}
|
||||
disabled={!isOwn}
|
||||
label="is busy"
|
||||
onChange={(e: SelectChangeEvent) => setBusy(e.target.value)}
|
||||
>
|
||||
<MenuItem value={"TRANSPARENT"}>Free</MenuItem>
|
||||
<MenuItem value={"OPAQUE"}>Busy </MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
{/* Error */}
|
||||
{event.error && (
|
||||
<InfoRow
|
||||
icon={
|
||||
<ErrorOutlineIcon
|
||||
color="error"
|
||||
style={{ fontSize: 18 }}
|
||||
/>
|
||||
}
|
||||
text={event.error}
|
||||
error
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
|
||||
<CardActions>
|
||||
<ButtonGroup>
|
||||
{isOwn && (
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() =>
|
||||
handleDelete(
|
||||
isRecurring,
|
||||
typeOfAction,
|
||||
onClose,
|
||||
dispatch,
|
||||
calendar,
|
||||
event,
|
||||
calId,
|
||||
eventId
|
||||
)
|
||||
}
|
||||
>
|
||||
<DeleteIcon fontSize="small" />
|
||||
</IconButton>
|
||||
)}
|
||||
<Button size="small" onClick={handleToggleShowMore}>
|
||||
{showMore ? "Show Less" : "Show More"}
|
||||
</Button>
|
||||
|
||||
{isOwn && (
|
||||
<Button size="small" onClick={handleSave}>
|
||||
Save
|
||||
</Button>
|
||||
)}
|
||||
</ButtonGroup>
|
||||
</CardActions>
|
||||
</Card>
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -42,9 +42,9 @@ import { renderAttendeeBadge } from "../../components/Event/utils/eventUtils";
|
||||
import { getCalendarRange } from "../../utils/dateUtils";
|
||||
import { deleteEventAsync } from "../Calendars/CalendarSlice";
|
||||
import { dlEvent } from "./EventApi";
|
||||
import EventDisplayModal from "./EventDisplay";
|
||||
import { CalendarEvent } from "./EventsTypes";
|
||||
import EventUpdateModal from "./EventUpdateModal";
|
||||
import { useI18n } from "cozy-ui/transpiled/react/providers/I18n";
|
||||
export default function EventPreviewModal({
|
||||
eventId,
|
||||
calId,
|
||||
@@ -58,6 +58,7 @@ export default function EventPreviewModal({
|
||||
open: boolean;
|
||||
onClose: (event: {}, reason: "backdropClick" | "escapeKeyDown") => void;
|
||||
}) {
|
||||
const { t } = useI18n();
|
||||
const dispatch = useAppDispatch();
|
||||
const calendars = useAppSelector((state) => state.calendars);
|
||||
const timezone =
|
||||
@@ -75,7 +76,6 @@ export default function EventPreviewModal({
|
||||
const isRecurring = event?.uid?.includes("/");
|
||||
const isOwn = calendar.ownerEmails?.includes(user.userData.email);
|
||||
const [showAllAttendees, setShowAllAttendees] = useState(false);
|
||||
const [openFullDisplay, setOpenFullDisplay] = useState(false);
|
||||
const [openUpdateModal, setOpenUpdateModal] = useState(false);
|
||||
const [openDuplicateModal, setOpenDuplicateModal] = useState(false);
|
||||
const [hidePreview, setHidePreview] = useState(false);
|
||||
@@ -210,7 +210,7 @@ export default function EventPreviewModal({
|
||||
)
|
||||
}
|
||||
>
|
||||
Email attendees
|
||||
{t("eventPreview.emailAttendees")}
|
||||
</MenuItem>
|
||||
)}
|
||||
<EventDuplication
|
||||
@@ -254,7 +254,7 @@ export default function EventPreviewModal({
|
||||
updateTempList();
|
||||
}}
|
||||
>
|
||||
Delete event
|
||||
{t("eventPreview.deleteEvent")}
|
||||
</MenuItem>
|
||||
)}
|
||||
</Menu>
|
||||
@@ -270,9 +270,7 @@ export default function EventPreviewModal({
|
||||
{event.class === "PRIVATE" &&
|
||||
(isOwn ? (
|
||||
<Tooltip
|
||||
title={
|
||||
"Only you and attendees can see the details of this event."
|
||||
}
|
||||
title={t("eventPreview.privateEvent.tooltipOwn")}
|
||||
placement="top"
|
||||
>
|
||||
<LockOutlineIcon />
|
||||
@@ -294,23 +292,21 @@ export default function EventPreviewModal({
|
||||
</Typography>
|
||||
{event.transp === "TRANSPARENT" && (
|
||||
<Tooltip
|
||||
title={
|
||||
"Others see you as available during the time range of this event."
|
||||
}
|
||||
title={t("eventPreview.free.tooltip")}
|
||||
placement="top"
|
||||
>
|
||||
<Chip
|
||||
icon={<CircleIcon color="success" fontSize="small" />}
|
||||
label="Free"
|
||||
label={t("eventPreview.free.label")}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Box>
|
||||
<Typography color="text.secondaryContainer" gutterBottom>
|
||||
{formatDate(event.start, event.allday)}
|
||||
{formatDate(event.start, t, event.allday)}
|
||||
{event.end &&
|
||||
formatEnd(event.start, event.end, event.allday) &&
|
||||
` – ${formatEnd(event.start, event.end, event.allday)} ${!event.allday && getTimezoneOffset(timezone)}`}
|
||||
formatEnd(event.start, event.end, t, event.allday) &&
|
||||
` – ${formatEnd(event.start, event.end, t, event.allday)} ${!event.allday && getTimezoneOffset(timezone)}`}
|
||||
</Typography>
|
||||
</>
|
||||
)
|
||||
@@ -319,7 +315,9 @@ export default function EventPreviewModal({
|
||||
<>
|
||||
{currentUserAttendee && (
|
||||
<>
|
||||
<Typography sx={{ marginRight: 2 }}>Attending?</Typography>
|
||||
<Typography sx={{ marginRight: 2 }}>
|
||||
{t("eventPreview.attendingQuestion")}
|
||||
</Typography>
|
||||
<Box display="flex" gap="15px" alignItems="center">
|
||||
<Button
|
||||
variant={
|
||||
@@ -344,7 +342,6 @@ export default function EventPreviewModal({
|
||||
user,
|
||||
event,
|
||||
"ACCEPTED",
|
||||
|
||||
onClose,
|
||||
type,
|
||||
calendarList
|
||||
@@ -363,7 +360,7 @@ export default function EventPreviewModal({
|
||||
}
|
||||
}}
|
||||
>
|
||||
Accept
|
||||
{t("eventPreview.accept")}
|
||||
</Button>
|
||||
<Button
|
||||
variant={
|
||||
@@ -406,7 +403,7 @@ export default function EventPreviewModal({
|
||||
}
|
||||
}}
|
||||
>
|
||||
Maybe
|
||||
{t("eventPreview.maybe")}
|
||||
</Button>
|
||||
<Button
|
||||
variant={
|
||||
@@ -449,7 +446,7 @@ export default function EventPreviewModal({
|
||||
}
|
||||
}}
|
||||
>
|
||||
Decline
|
||||
{t("eventPreview.decline")}
|
||||
</Button>
|
||||
</Box>
|
||||
</>
|
||||
@@ -474,7 +471,7 @@ export default function EventPreviewModal({
|
||||
window.open(event.x_openpass_videoconference)
|
||||
}
|
||||
>
|
||||
Join the video conference
|
||||
{t("eventPreview.joinVideo")}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
@@ -497,20 +494,25 @@ export default function EventPreviewModal({
|
||||
}}
|
||||
>
|
||||
<Box sx={{ marginRight: 2 }}>
|
||||
<Typography>{attendees.length} guests</Typography>
|
||||
<Typography>
|
||||
{t("eventPreview.guests", {
|
||||
count: attendees.length,
|
||||
})}
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{ fontSize: "13px", color: "text.secondary" }}
|
||||
>
|
||||
{
|
||||
attendees.filter((a) => a.partstat === "ACCEPTED")
|
||||
.length
|
||||
}{" "}
|
||||
yes,{" "}
|
||||
{
|
||||
attendees.filter((a) => a.partstat === "DECLINED")
|
||||
.length
|
||||
}{" "}
|
||||
no
|
||||
{t("eventPreview.yesCount", {
|
||||
count: attendees.filter(
|
||||
(a) => a.partstat === "ACCEPTED"
|
||||
).length,
|
||||
})}
|
||||
,{" "}
|
||||
{t("eventPreview.noCount", {
|
||||
count: attendees.filter(
|
||||
(a) => a.partstat === "DECLINED"
|
||||
).length,
|
||||
})}
|
||||
</Typography>
|
||||
</Box>
|
||||
{!showAllAttendees && (
|
||||
@@ -519,6 +521,7 @@ export default function EventPreviewModal({
|
||||
renderAttendeeBadge(
|
||||
organizer,
|
||||
"org",
|
||||
t,
|
||||
showAllAttendees,
|
||||
true
|
||||
)}
|
||||
@@ -526,6 +529,7 @@ export default function EventPreviewModal({
|
||||
renderAttendeeBadge(
|
||||
a,
|
||||
idx.toString(),
|
||||
t,
|
||||
showAllAttendees
|
||||
)
|
||||
)}
|
||||
@@ -541,7 +545,9 @@ export default function EventPreviewModal({
|
||||
}}
|
||||
onClick={() => setShowAllAttendees(!showAllAttendees)}
|
||||
>
|
||||
{showAllAttendees ? "Show less" : `Show more `}
|
||||
{showAllAttendees
|
||||
? t("eventPreview.showLess")
|
||||
: t("eventPreview.showMore")}
|
||||
</Typography>
|
||||
</Box>
|
||||
}
|
||||
@@ -550,10 +556,10 @@ export default function EventPreviewModal({
|
||||
)}
|
||||
{showAllAttendees &&
|
||||
organizer &&
|
||||
renderAttendeeBadge(organizer, "org", showAllAttendees, true)}
|
||||
renderAttendeeBadge(organizer, "org", t, showAllAttendees, true)}
|
||||
{showAllAttendees &&
|
||||
visibleAttendees.map((a, idx) =>
|
||||
renderAttendeeBadge(a, idx.toString(), showAllAttendees)
|
||||
renderAttendeeBadge(a, idx.toString(), t, showAllAttendees)
|
||||
)}
|
||||
{/* Location */}
|
||||
{event.location && (
|
||||
@@ -593,7 +599,10 @@ export default function EventPreviewModal({
|
||||
<NotificationsNoneIcon />
|
||||
</Box>
|
||||
}
|
||||
text={`${event.alarm.trigger} before by ${event.alarm.action}`}
|
||||
text={t("eventPreview.alarmText", {
|
||||
trigger: t(`event.form.notifications.${event.alarm.trigger}`),
|
||||
action: t(`event.form.notifications.${event.alarm.action}`),
|
||||
})}
|
||||
style={{
|
||||
fontSize: "16px",
|
||||
fontFamily: "'Inter', sans-serif",
|
||||
@@ -608,7 +617,7 @@ export default function EventPreviewModal({
|
||||
<RepeatIcon />
|
||||
</Box>
|
||||
}
|
||||
text={makeRecurrenceString(event)}
|
||||
text={makeRecurrenceString(event, t)}
|
||||
style={{
|
||||
fontSize: "16px",
|
||||
fontFamily: "'Inter', sans-serif",
|
||||
@@ -636,7 +645,7 @@ export default function EventPreviewModal({
|
||||
letterSpacing={"0.5px"}
|
||||
textAlign={"center"}
|
||||
>
|
||||
This is a private event. Details are hidden.
|
||||
{t("eventPreview.privateEvent.hiddenDetails")}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
@@ -680,13 +689,6 @@ export default function EventPreviewModal({
|
||||
afterChoiceFunc && afterChoiceFunc(type);
|
||||
}}
|
||||
/>
|
||||
<EventDisplayModal
|
||||
open={openFullDisplay}
|
||||
onClose={() => setOpenFullDisplay(false)}
|
||||
eventId={eventId}
|
||||
calId={calId}
|
||||
typeOfAction={typeOfAction}
|
||||
/>
|
||||
<EventUpdateModal
|
||||
open={openUpdateModal}
|
||||
onClose={() => {
|
||||
@@ -725,43 +727,76 @@ export default function EventPreviewModal({
|
||||
);
|
||||
}
|
||||
|
||||
function makeRecurrenceString(event: CalendarEvent): string | undefined {
|
||||
if (!event.repetition) {
|
||||
return;
|
||||
}
|
||||
const recur = [`Reccurent Event · ${event.repetition.freq}`];
|
||||
function makeRecurrenceString(
|
||||
event: CalendarEvent,
|
||||
t: Function
|
||||
): string | undefined {
|
||||
if (!event.repetition) return;
|
||||
|
||||
const recur: string[] = [
|
||||
`${t("eventPreview.recurrentEvent")} · ${t(
|
||||
`eventPreview.freq.${event.repetition.freq}`,
|
||||
event.repetition.freq
|
||||
)}`,
|
||||
];
|
||||
|
||||
const recurType: Record<string, string> = {
|
||||
daily: "days",
|
||||
monthly: "months",
|
||||
yearly: "years",
|
||||
daily: t("eventPreview.days"),
|
||||
monthly: t("eventPreview.months"),
|
||||
yearly: t("eventPreview.years"),
|
||||
};
|
||||
|
||||
if (event.repetition.byday) {
|
||||
recur.push(`on ${event.repetition.byday.join(", ")}`);
|
||||
recur.push(
|
||||
t("eventPreview.recurrenceOnDays", {
|
||||
days: event.repetition.byday
|
||||
.map((s) => t(`eventPreview.onDays.${s}`))
|
||||
.join(", "),
|
||||
})
|
||||
);
|
||||
}
|
||||
if (event.repetition.interval && event.repetition.interval > 1) {
|
||||
recur.push(
|
||||
`every ${event.repetition.interval} ${recurType[event.repetition.freq]}`
|
||||
t("eventPreview.everyInterval", {
|
||||
interval: event.repetition.interval,
|
||||
unit: recurType[event.repetition.freq],
|
||||
})
|
||||
);
|
||||
}
|
||||
if (event.repetition.occurrences) {
|
||||
recur.push(`for ${event.repetition.occurrences} occurences`);
|
||||
recur.push(
|
||||
t("eventPreview.forOccurrences", {
|
||||
count: event.repetition.occurrences,
|
||||
})
|
||||
);
|
||||
}
|
||||
if (event.repetition.endDate) {
|
||||
recur.push(`until ${event.repetition.endDate}`);
|
||||
recur.push(
|
||||
t("eventPreview.until", {
|
||||
date: new Date(event.repetition.endDate).toLocaleDateString(
|
||||
t("locale"),
|
||||
{
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
}
|
||||
),
|
||||
})
|
||||
);
|
||||
}
|
||||
return recur.join(", ");
|
||||
}
|
||||
|
||||
function formatDate(date: Date | string, allday?: boolean) {
|
||||
function formatDate(date: Date | string, t: Function, allday?: boolean) {
|
||||
if (allday) {
|
||||
return new Date(date).toLocaleDateString(undefined, {
|
||||
return new Date(date).toLocaleDateString(t("locale"), {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
weekday: "long",
|
||||
day: "numeric",
|
||||
});
|
||||
} else {
|
||||
return new Date(date).toLocaleString(undefined, {
|
||||
return new Date(date).toLocaleString(t("locale"), {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
weekday: "long",
|
||||
@@ -772,7 +807,12 @@ function formatDate(date: Date | string, allday?: boolean) {
|
||||
}
|
||||
}
|
||||
|
||||
function formatEnd(start: Date | string, end: Date | string, allday?: boolean) {
|
||||
function formatEnd(
|
||||
start: Date | string,
|
||||
end: Date | string,
|
||||
t: Function,
|
||||
allday?: boolean
|
||||
) {
|
||||
const startDate = new Date(start);
|
||||
const endDate = new Date(end);
|
||||
|
||||
@@ -784,19 +824,19 @@ function formatEnd(start: Date | string, end: Date | string, allday?: boolean) {
|
||||
if (allday) {
|
||||
return sameDay
|
||||
? null
|
||||
: endDate.toLocaleDateString(undefined, {
|
||||
: endDate.toLocaleDateString(t("locale"), {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
});
|
||||
} else {
|
||||
if (sameDay) {
|
||||
return endDate.toLocaleTimeString(undefined, {
|
||||
return endDate.toLocaleTimeString(t("locale"), {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
});
|
||||
}
|
||||
return endDate.toLocaleString(undefined, {
|
||||
return endDate.toLocaleString(t("locale"), {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
formatDateTimeInTimezone,
|
||||
} from "../../components/Event/utils/dateTimeFormatters";
|
||||
import { addDays } from "../../components/Event/utils/dateRules";
|
||||
import { useI18n } from "cozy-ui/transpiled/react/providers/I18n";
|
||||
|
||||
function EventPopover({
|
||||
anchorEl,
|
||||
@@ -55,7 +56,7 @@ function EventPopover({
|
||||
useAppSelector((state) => state.user.userData?.openpaasId) ?? "";
|
||||
const tempList = useAppSelector((state) => state.calendars.templist);
|
||||
const calList = useAppSelector((state) => state.calendars.list);
|
||||
const selectPersonnalCalendars = createSelector(
|
||||
const selectPersonalCalendars = createSelector(
|
||||
(state: any) => state.calendars,
|
||||
(calendars: any) =>
|
||||
Object.keys(calendars.list)
|
||||
@@ -67,8 +68,8 @@ function EventPopover({
|
||||
})
|
||||
.filter((calendar) => calendar.id)
|
||||
);
|
||||
const userPersonnalCalendars: Calendars[] = useAppSelector(
|
||||
selectPersonnalCalendars
|
||||
const userPersonalCalendars: Calendars[] = useAppSelector(
|
||||
selectPersonalCalendars
|
||||
);
|
||||
|
||||
const timezoneList = useMemo(() => {
|
||||
@@ -97,7 +98,7 @@ function EventPopover({
|
||||
const [start, setStart] = useState(event?.start ? event.start : "");
|
||||
const [end, setEnd] = useState(event?.end ? event.end : "");
|
||||
const [calendarid, setCalendarid] = useState(
|
||||
event?.calId ?? userPersonnalCalendars[0]?.id ?? ""
|
||||
event?.calId ?? userPersonalCalendars[0]?.id ?? ""
|
||||
);
|
||||
const [allday, setAllDay] = useState(event?.allday ?? false);
|
||||
const [repetition, setRepetition] = useState<RepetitionObject>(
|
||||
@@ -126,18 +127,18 @@ function EventPopover({
|
||||
|
||||
// Use ref to track if we've already initialized to avoid infinite loop
|
||||
const isInitializedRef = useRef(false);
|
||||
const userPersonnalCalendarsRef = useRef(userPersonnalCalendars);
|
||||
const userPersonalCalendarsRef = useRef(userPersonalCalendars);
|
||||
|
||||
// Update ref when userPersonnalCalendars changes
|
||||
// Update ref when userPersonalCalendars changes
|
||||
useEffect(() => {
|
||||
userPersonnalCalendarsRef.current = userPersonnalCalendars;
|
||||
}, [userPersonnalCalendars]);
|
||||
userPersonalCalendarsRef.current = userPersonalCalendars;
|
||||
}, [userPersonalCalendars]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!calendarid && userPersonnalCalendars.length > 0) {
|
||||
setCalendarid(userPersonnalCalendars[0].id);
|
||||
if (!calendarid && userPersonalCalendars.length > 0) {
|
||||
setCalendarid(userPersonalCalendars[0].id);
|
||||
}
|
||||
}, [calendarid, userPersonnalCalendars]);
|
||||
}, [calendarid, userPersonalCalendars]);
|
||||
|
||||
const resetAllStateToDefault = useCallback(() => {
|
||||
setShowMore(false);
|
||||
@@ -150,11 +151,11 @@ function EventPopover({
|
||||
setStart("");
|
||||
setEnd("");
|
||||
if (
|
||||
userPersonnalCalendars &&
|
||||
userPersonnalCalendars.length > 0 &&
|
||||
userPersonnalCalendars[0]?.id
|
||||
userPersonalCalendars &&
|
||||
userPersonalCalendars.length > 0 &&
|
||||
userPersonalCalendars[0]?.id
|
||||
) {
|
||||
setCalendarid(userPersonnalCalendars[0].id);
|
||||
setCalendarid(userPersonalCalendars[0].id);
|
||||
}
|
||||
setAllDay(false);
|
||||
setRepetition({} as RepetitionObject);
|
||||
@@ -164,7 +165,7 @@ function EventPopover({
|
||||
setTimezone(calendarTimezone);
|
||||
setHasVideoConference(false);
|
||||
setMeetingLink(null);
|
||||
}, [calendarTimezone, userPersonnalCalendars]);
|
||||
}, [calendarTimezone, userPersonalCalendars]);
|
||||
|
||||
// Track if we should sync from selectedRange (only on initial selection, not on toggle)
|
||||
const shouldSyncFromRangeRef = useRef(true);
|
||||
@@ -396,11 +397,11 @@ function EventPopover({
|
||||
}
|
||||
|
||||
if (
|
||||
userPersonnalCalendars &&
|
||||
userPersonnalCalendars.length > 0 &&
|
||||
userPersonnalCalendars[0]?.id
|
||||
userPersonalCalendars &&
|
||||
userPersonalCalendars.length > 0 &&
|
||||
userPersonalCalendars[0]?.id
|
||||
) {
|
||||
setCalendarid(userPersonnalCalendars[0].id);
|
||||
setCalendarid(userPersonalCalendars[0].id);
|
||||
}
|
||||
setRepetition(event.repetition ?? ({} as RepetitionObject));
|
||||
setShowRepeat(event.repetition?.freq ? true : false);
|
||||
@@ -457,11 +458,11 @@ function EventPopover({
|
||||
setAttendees([]);
|
||||
setLocation("");
|
||||
if (
|
||||
userPersonnalCalendars &&
|
||||
userPersonnalCalendars.length > 0 &&
|
||||
userPersonnalCalendars[0]?.id
|
||||
userPersonalCalendars &&
|
||||
userPersonalCalendars.length > 0 &&
|
||||
userPersonalCalendars[0]?.id
|
||||
) {
|
||||
setCalendarid(userPersonnalCalendars[0].id);
|
||||
setCalendarid(userPersonalCalendars[0].id);
|
||||
}
|
||||
setAllDay(false);
|
||||
setRepetition({} as RepetitionObject);
|
||||
@@ -584,7 +585,7 @@ function EventPopover({
|
||||
// Resolve target calendar safely
|
||||
const targetCalendar: Calendars | undefined =
|
||||
calList[calendarid] ||
|
||||
userPersonnalCalendars[0] ||
|
||||
userPersonalCalendars[0] ||
|
||||
(Object.values(calList)[0] as Calendars | undefined);
|
||||
if (!targetCalendar || !targetCalendar.id) {
|
||||
console.error("No target calendar available to save event");
|
||||
@@ -672,22 +673,23 @@ function EventPopover({
|
||||
await updateTempCalendar(tempList, newEvent, dispatch, calendarRange);
|
||||
}
|
||||
};
|
||||
const { t } = useI18n();
|
||||
|
||||
const dialogActions = (
|
||||
<Box display="flex" justifyContent="space-between" width="100%" px={2}>
|
||||
{!showMore && (
|
||||
<Button startIcon={<AddIcon />} onClick={() => setShowMore(!showMore)}>
|
||||
More options
|
||||
{t("common.moreOptions")}
|
||||
</Button>
|
||||
)}
|
||||
<Box display="flex" gap={1} ml={showMore ? "auto" : 0}>
|
||||
{showMore && (
|
||||
<Button variant="outlined" onClick={handleClose}>
|
||||
Cancel
|
||||
{t("common.cancel")}
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="contained" onClick={handleSave}>
|
||||
Save
|
||||
{t("actions.save")}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -697,7 +699,11 @@ function EventPopover({
|
||||
<ResponsiveDialog
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
title={event?.uid ? "Duplicate Event" : "Create Event"}
|
||||
title={
|
||||
event?.uid
|
||||
? t("eventDuplication.duplicateEvent")
|
||||
: t("event.createEvent")
|
||||
}
|
||||
isExpanded={showMore}
|
||||
onExpandToggle={() => setShowMore(!showMore)}
|
||||
actions={dialogActions}
|
||||
@@ -739,7 +745,7 @@ function EventPopover({
|
||||
showRepeat={showRepeat}
|
||||
setShowRepeat={setShowRepeat}
|
||||
isOpen={open}
|
||||
userPersonnalCalendars={userPersonnalCalendars}
|
||||
userPersonalCalendars={userPersonalCalendars}
|
||||
timezoneList={timezoneList}
|
||||
onStartChange={handleStartChange}
|
||||
onEndChange={handleEndChange}
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
detectRecurringEventChanges,
|
||||
} from "./eventUtils";
|
||||
import { updateTempCalendar } from "../../components/Calendar/utils/calendarUtils";
|
||||
import { useI18n } from "cozy-ui/transpiled/react/providers/I18n";
|
||||
|
||||
const showErrorNotification = (message: string) => {
|
||||
console.error(`[ERROR] ${message}`);
|
||||
@@ -50,6 +51,7 @@ function EventUpdateModal({
|
||||
eventData?: CalendarEvent | null;
|
||||
typeOfAction?: "solo" | "all";
|
||||
}) {
|
||||
const { t } = useI18n();
|
||||
const dispatch = useAppDispatch();
|
||||
const tempList = useAppSelector((state) => state.calendars.templist);
|
||||
const calList = useAppSelector((state) => state.calendars.list);
|
||||
@@ -84,7 +86,7 @@ function EventUpdateModal({
|
||||
|
||||
const calendarsList = useAppSelector((state) => state.calendars.list);
|
||||
|
||||
const userPersonnalCalendars: Calendars[] = useMemo(() => {
|
||||
const userPersonalCalendars: Calendars[] = useMemo(() => {
|
||||
const allCalendars = Object.values(calendarsList) as Calendars[];
|
||||
return allCalendars.filter(
|
||||
(c: Calendars) => c.id?.split("/")[0] === user.userData?.openpaasId
|
||||
@@ -155,7 +157,7 @@ function EventUpdateModal({
|
||||
);
|
||||
const [newCalId, setNewCalId] = useState(calId);
|
||||
const [calendarid, setCalendarid] = useState(
|
||||
calId ?? userPersonnalCalendars[0]?.id ?? ""
|
||||
calId ?? userPersonalCalendars[0]?.id ?? ""
|
||||
);
|
||||
|
||||
const [attendees, setAttendees] = useState<userAttendee[]>([]);
|
||||
@@ -175,7 +177,7 @@ function EventUpdateModal({
|
||||
setLocation("");
|
||||
setStart("");
|
||||
setEnd("");
|
||||
setCalendarid(userPersonnalCalendars[0].id);
|
||||
setCalendarid(userPersonalCalendars[0].id);
|
||||
setAllDay(false);
|
||||
setRepetition({} as RepetitionObject);
|
||||
setAlarm("");
|
||||
@@ -296,7 +298,7 @@ function EventUpdateModal({
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [open, event, calId, userPersonnalCalendars, calendarsList]);
|
||||
}, [open, event, calId, userPersonalCalendars, calendarsList]);
|
||||
|
||||
// Helper to close modal(s) - use onCloseAll if available to close preview modal too
|
||||
const closeModal = () => {
|
||||
@@ -682,7 +684,7 @@ function EventUpdateModal({
|
||||
moveEventAsync({
|
||||
cal: targetCalendar,
|
||||
newEvent,
|
||||
newURL: `/calendars/${newCalId}/${event.uid}.ics`,
|
||||
newURL: `/calendars/${newCalId}/${event.uid.split("/")[0]}.ics`,
|
||||
})
|
||||
);
|
||||
dispatch(removeEvent({ calendarUid: calId, eventUid: event.uid }));
|
||||
@@ -697,15 +699,15 @@ function EventUpdateModal({
|
||||
<Box display="flex" justifyContent="space-between" width="100%" px={2}>
|
||||
{!showMore && (
|
||||
<Button startIcon={<AddIcon />} onClick={() => setShowMore(!showMore)}>
|
||||
More options
|
||||
{t("common.moreOptions")}
|
||||
</Button>
|
||||
)}
|
||||
<Box display="flex" gap={1} ml={showMore ? "auto" : 0}>
|
||||
<Button variant="outlined" onClick={handleClose}>
|
||||
Cancel
|
||||
{t("common.cancel")}
|
||||
</Button>
|
||||
<Button variant="contained" onClick={handleSave}>
|
||||
Save
|
||||
{t("actions.save")}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -717,7 +719,7 @@ function EventUpdateModal({
|
||||
<ResponsiveDialog
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
title="Update Event"
|
||||
title={t("event.updateEvent")}
|
||||
isExpanded={showMore}
|
||||
onExpandToggle={() => setShowMore(!showMore)}
|
||||
actions={dialogActions}
|
||||
@@ -760,7 +762,7 @@ function EventUpdateModal({
|
||||
showRepeat={typeOfAction !== "solo" && showRepeat}
|
||||
setShowRepeat={setShowRepeat}
|
||||
isOpen={open}
|
||||
userPersonnalCalendars={userPersonnalCalendars}
|
||||
userPersonalCalendars={userPersonalCalendars}
|
||||
timezoneList={timezoneList}
|
||||
onCalendarChange={(newCalendarId) => {
|
||||
const selectedCalendar = calList[newCalendarId];
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
|
||||
export interface SettingsState {
|
||||
language: string;
|
||||
}
|
||||
|
||||
const savedLang = localStorage.getItem("lang");
|
||||
const defaultLang = savedLang ?? (window as any).LANG ?? "en";
|
||||
|
||||
const initialState: SettingsState = {
|
||||
language: defaultLang,
|
||||
};
|
||||
|
||||
export const settingsSlice = createSlice({
|
||||
name: "settings",
|
||||
initialState,
|
||||
reducers: {
|
||||
setLanguage: (state, action: PayloadAction<string>) => {
|
||||
state.language = action.payload;
|
||||
localStorage.setItem("lang", action.payload);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setLanguage } = settingsSlice.actions;
|
||||
export default settingsSlice.reducer;
|
||||
Reference in New Issue
Block a user