import React from "react"; import { Box, Button, Checkbox, FormControl, FormControlLabel, IconButton, InputLabel, MenuItem, Select, SelectChangeEvent, TextField, Typography, ToggleButtonGroup, ToggleButton, } from "@mui/material"; import { Description as DescriptionIcon, Public as PublicIcon, Lock as LockIcon, CameraAlt as VideocamIcon, ContentCopy as CopyIcon, Close as DeleteIcon, } from "@mui/icons-material"; import AttendeeSelector from "../Attendees/AttendeeSearch"; import RepeatEvent from "./EventRepeat"; import { RepetitionObject } from "../../features/Events/EventsTypes"; import { userAttendee } from "../../features/User/userDataTypes"; import { Calendars } from "../../features/Calendars/CalendarTypes"; import { generateMeetingLink, addVideoConferenceToDescription, } from "../../utils/videoConferenceUtils"; // Helper component for field with label export const FieldWithLabel = React.memo( ({ label, isExpanded, children, }: { label: string; isExpanded: boolean; children: React.ReactNode; }) => { if (!isExpanded) { // Normal mode: label on top return ( {label} {children} ); } // Extended mode: label on left return ( {label} {children} ); } ); FieldWithLabel.displayName = "FieldWithLabel"; interface EventFormFieldsProps { // Form state title: string; setTitle: (title: string) => void; description: string; setDescription: (description: string) => void; location: string; setLocation: (location: string) => void; start: string; setStart: (start: string) => void; end: string; setEnd: (end: string) => void; allday: boolean; setAllDay: (allday: boolean) => void; repetition: RepetitionObject; setRepetition: (repetition: RepetitionObject) => void; typeOfAction?: "solo" | "all"; attendees: userAttendee[]; setAttendees: (attendees: userAttendee[]) => void; alarm: string; setAlarm: (alarm: string) => void; busy: string; setBusy: (busy: string) => void; eventClass: string; setEventClass: (eventClass: string) => void; timezone: string; setTimezone: (timezone: string) => void; calendarid: number; setCalendarid: (calendarid: number) => void; hasVideoConference: boolean; setHasVideoConference: (hasVideoConference: boolean) => void; meetingLink: string | null; setMeetingLink: (meetingLink: string | null) => void; // UI state showMore: boolean; showDescription: boolean; setShowDescription: (showDescription: boolean) => void; showRepeat: boolean; setShowRepeat: (showRepeat: boolean) => void; // Data userPersonnalCalendars: Calendars[]; timezoneList: { zones: string[]; browserTz: string; getTimezoneOffset: (tzName: string) => string; }; // Event handlers onStartChange?: (newStart: string) => void; onEndChange?: (newEnd: string) => void; onAllDayChange?: (newAllDay: boolean) => void; onCalendarChange?: (newCalendarId: number) => void; } export default function EventFormFields({ title, setTitle, description, setDescription, location, setLocation, start, setStart, end, setEnd, allday, setAllDay, repetition, setRepetition, typeOfAction, attendees, setAttendees, alarm, setAlarm, busy, setBusy, eventClass, setEventClass, timezone, setTimezone, calendarid, setCalendarid, hasVideoConference, setHasVideoConference, meetingLink, setMeetingLink, showMore, showDescription, setShowDescription, showRepeat, setShowRepeat, userPersonnalCalendars, timezoneList, onStartChange, onEndChange, onAllDayChange, onCalendarChange, }: EventFormFieldsProps) { const handleAddVideoConference = () => { const newMeetingLink = generateMeetingLink(); const updatedDescription = addVideoConferenceToDescription( description, newMeetingLink ); setDescription(updatedDescription); setHasVideoConference(true); setMeetingLink(newMeetingLink); }; const handleCopyMeetingLink = async () => { if (meetingLink) { try { await navigator.clipboard.writeText(meetingLink); } catch (err) { console.error("Failed to copy link:", err); } } }; const handleDeleteVideoConference = () => { // Remove video conference footer from description const updatedDescription = description.replace( /\nVisio: https?:\/\/[^\s]+/, "" ); setDescription(updatedDescription); setHasVideoConference(false); setMeetingLink(null); }; const handleStartChange = (newStart: string) => { setStart(newStart); onStartChange?.(newStart); }; const handleEndChange = (newEnd: string) => { setEnd(newEnd); onEndChange?.(newEnd); }; const handleAllDayChange = (newAllDay: boolean) => { setAllDay(newAllDay); onAllDayChange?.(newAllDay); }; const handleCalendarChange = (newCalendarId: number) => { setCalendarid(newCalendarId); onCalendarChange?.(newCalendarId); }; return ( <> setTitle(e.target.value)} size="small" margin="dense" /> {showDescription && ( setDescription(e.target.value)} size="small" margin="dense" multiline rows={2} /> )} {showMore && ( Start )} handleStartChange(e.target.value)} size="small" margin="dense" InputLabelProps={{ shrink: true }} /> {showMore && ( End )} handleEndChange(e.target.value)} size="small" margin="dense" InputLabelProps={{ shrink: true }} /> { const endDate = new Date(end); const startDate = new Date(start); const newAllDay = !allday; setAllDay(newAllDay); if (endDate.getDate() === startDate.getDate()) { endDate.setDate(startDate.getDate() + 1); setEnd(formatLocalDateTime(endDate)); } handleAllDayChange(newAllDay); }} /> } label="All day" /> { const newShowRepeat = !showRepeat; setShowRepeat(newShowRepeat); if (newShowRepeat) { setRepetition({ freq: "daily", interval: 1, occurrences: 0, endDate: "", byday: null, } as RepetitionObject); } else { setRepetition({ freq: "", interval: 1, occurrences: 0, endDate: "", byday: null, } as RepetitionObject); } }} /> } label="Repeat" /> {showRepeat && ( )} {hasVideoConference && meetingLink && ( <> )} setLocation(e.target.value)} size="small" margin="dense" /> {!showMore && ( Calendar )} {/* Extended options */} {showMore && ( <> { if (newValue !== null) { setEventClass(newValue); } }} size="small" > All Participants )} ); } export function formatLocalDateTime(date: Date): string { const pad = (n: number) => n.toString().padStart(2, "0"); return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad( date.getDate() )}T${pad(date.getHours())}:${pad(date.getMinutes())}`; } export function formatDateTimeInTimezone( isoString: string, timezone: string ): string { // Parse the ISO string as UTC const utcDate = new Date(isoString); // Format the date in the target timezone const formatter = new Intl.DateTimeFormat("en-CA", { timeZone: timezone, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", hour12: false, }); const parts = formatter.formatToParts(utcDate); const getValue = (type: string) => parts.find((p) => p.type === type)?.value || ""; return `${getValue("year")}-${getValue("month")}-${getValue("day")}T${getValue("hour")}:${getValue("minute")}`; }