import { useEffect, useState } from "react"; import { deleteEventAsync, putEventAsync } from "../Calendars/CalendarSlice"; import { useAppDispatch, useAppSelector } from "../../app/hooks"; import AttendeeSelector from "../../components/Attendees/AttendeeSearch"; import { Popover, Button, Box, Typography, ButtonGroup, Card, CardContent, Divider, IconButton, Avatar, Badge, Modal, TextField, CardHeader, FormControl, InputLabel, MenuItem, Select, SelectChangeEvent, Checkbox, FormControlLabel, CardActions, Link, } from "@mui/material"; import DeleteIcon from "@mui/icons-material/Delete"; import CloseIcon from "@mui/icons-material/Close"; import VideocamIcon from "@mui/icons-material/Videocam"; import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline"; import CircleIcon from "@mui/icons-material/Circle"; import CancelIcon from "@mui/icons-material/Cancel"; import CheckCircleIcon from "@mui/icons-material/CheckCircle"; import { userAttendee } from "../User/userDataTypes"; import { TIMEZONES } from "../../utils/timezone-data"; import { Calendars } from "../Calendars/CalendarTypes"; import { CalendarEvent } from "./EventsTypes"; import { isValidUrl } from "../../utils/apiUtils"; import { formatLocalDateTime } from "./EventModal"; export default function EventDisplayModal({ eventId, calId, open, onClose, }: { eventId: string; calId: string; open: boolean; onClose: (event: {}, reason: "backdropClick" | "escapeKeyDown") => void; }) { 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 = useAppSelector((state) => Object.values(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 ?? ""); const [alarm, setAlarm] = useState(""); const [eventClass, setEventClass] = useState(event?.class ?? "PUBLIC"); const [timezone, setTimezone] = useState(event?.timezone ?? "UTC"); const [calendarid, setCalendarid] = useState( event?.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 ); 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"); } }, [event, calendar, onClose]); if (!event || !calendar) return null; function handleRSVP(rsvp: string) { const newEvent = { ...event, attendee: event.attendee?.map((a) => a.cal_address === user.userData.email ? { ...a, partstat: rsvp } : a ), }; dispatch(putEventAsync({ cal: calendar, newEvent })); onClose({}, "backdropClick"); } const handleSave = () => { const newEventUID = crypto.randomUUID(); const newEvent: CalendarEvent = { calId, title, URL: event.URL ?? `/calendars/${calId}/${newEventUID}.ics`, start: new Date(start), end: new Date(end), allday, uid: event.uid ?? newEventUID, description, location, repetition, class: eventClass, organizer: event.organizer, timezone, attendee: [ { cn: event.organizer?.cn, cal_address: event.organizer?.cal_address ?? "", partstat: "ACCEPTED", rsvp: "FALSE", role: "CHAIR", cutype: "INDIVIDUAL", }, ...attendees, ], transp: "OPAQUE", color: userPersonnalCalendars[calendarid]?.color, }; dispatch( putEventAsync({ cal: userPersonnalCalendars[calendarid], newEvent, }) ); onClose({}, "backdropClick"); }; const calList = event.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 }} /> setAllDay(!allday)} /> } label="All day" /> {/* Description & Location */} setDescription(e.target.value)} size="small" margin="dense" multiline rows={2} /> setLocation(e.target.value)} size="small" margin="dense" /> {/* Video */} {event.x_openpass_videoconference && ( } text="Video conference available" data={event.x_openpass_videoconference} /> )} {/* Attendees */} {event.attendee?.length > 0 && ( Attendees: {organizer && renderAttendeeBadge(organizer, "org", true)} {(showAllAttendees ? attendees : attendees.slice(0, attendeeDisplayLimit) ).map((a, idx) => ( {renderAttendeeBadge(a, idx.toString())} { const newAttendeesList = [...attendees]; setAttendees(newAttendeesList.splice(idx, 1)); console.log(attendees, newAttendeesList); }} > ))} {attendees.length > attendeeDisplayLimit && ( setShowAllAttendees(!showAllAttendees)} > {showAllAttendees ? "Show less" : `Show more (${ attendees.length - attendeeDisplayLimit } more)`} )} )} {/* Extended options */} {showMore && ( <> Repetition Alarm Class is Busy {/* Error */} {event.error && ( } text={event.error} error /> )} )} { onClose({}, "backdropClick"); dispatch( deleteEventAsync({ calId, eventId, eventURL: event.URL }) ); }} > {isOwn && ( )} ); } export function InfoRow({ icon, text, error = false, data, }: { icon: React.ReactNode; text: string; error?: boolean; data?: string; }) { return ( {icon} {isValidUrl(data) && {text}} ); } export function renderAttendeeBadge( a: userAttendee, key: string, isOrganizer?: boolean ) { const classIcon = a.partstat === "ACCEPTED" ? ( ) : a.partstat === "DECLINED" ? ( ) : null; return ( {classIcon} ) } > {a.cn || a.cal_address} {isOrganizer && ( Organizer )} ); } export function stringToColor(string: string) { let hash = 0; for (let i = 0; i < string.length; i++) { hash = string.charCodeAt(i) + ((hash << 5) - hash); } let color = "#"; for (let i = 0; i < 3; i++) { const value = (hash >> (i * 8)) & 0xff; color += `00${value.toString(16)}`.slice(-2); } return color; } export function stringAvatar(name: string) { return { sx: { width: 24, height: 24, fontSize: 18, bgcolor: stringToColor(name) }, children: name[0], }; }