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/EventFormFields"; 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); const [repetition, setRepetition] = useState( 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) => ( {userPersonnalCalendars[index].name} )) : Object.keys(calendars).map((calendar, index) => ( {calendars[index].name} - {calendars[index].owner} )); return ( {/* Close button */} onClose({}, "backdropClick")} > {/* Title */} setTitle(e.target.value)} size="small" margin="dense" /> {/* RSVP */} {currentUserAttendee && isOwnCal && ( )} {/* Calendar selector */} Calendar {/* Dates */} setStart(formatLocalDateTime(new Date(e.target.value))) } size="small" margin="dense" InputLabelProps={{ shrink: true }} /> setEnd(formatLocalDateTime(new Date(e.target.value))) } size="small" margin="dense" InputLabelProps={{ shrink: true }} /> { 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 */} setDescription(e.target.value)} size="small" margin="dense" multiline rows={2} /> {isOwn && ( )} setLocation(e.target.value)} size="small" margin="dense" /> {/* Video */} {event.x_openpass_videoconference && ( } content={ } /> )} {/* Attendees */} {event.attendee?.length > 0 && ( Attendees: {organizer.cal_address && renderAttendeeBadge(organizer, "org", true, true)} {(showAllAttendees ? attendees : attendees.slice(0, attendeeDisplayLimit) ).map((a, idx) => ( {renderAttendeeBadge(a, idx.toString(), true)} {isOwn && ( { const newAttendeesList = [...attendees]; newAttendeesList.splice(idx, 1); setAttendees(newAttendeesList); }} > )} ))} {attendees.length > attendeeDisplayLimit && ( setShowAllAttendees(!showAllAttendees)} > {showAllAttendees ? "Show less" : `Show more (${ attendees.length - attendeeDisplayLimit } more)`} )} )} {/* Extended options */} {showMore && ( <> {isOwn && ( )} Notification Visibility is Busy {/* Error */} {event.error && ( } text={event.error} error /> )} )} {isOwn && ( handleDelete( isRecurring, typeOfAction, onClose, dispatch, calendar, event, calId, eventId ) } > )} {isOwn && ( )} ); }