From 39d80857667f44ce7e8e34123c2b85dfcbafc695 Mon Sep 17 00:00:00 2001 From: Camille Moussu <66134347+Eriikah@users.noreply.github.com> Date: Fri, 31 Oct 2025 04:44:54 +0100 Subject: [PATCH] [#205] added calendar list component (#258) Co-authored-by: Camille Moussu --- src/components/Calendar/CalendarItemList.tsx | 14 +++++++++ src/components/Calendar/CalendarName.tsx | 17 ++++++++++ src/components/Calendar/ImportTab.tsx | 11 +++---- src/components/Event/EventFormFields.tsx | 17 +++++----- src/features/Events/EventDisplayPreview.tsx | 33 +++++++------------- src/features/Events/EventModal.tsx | 25 ++++++--------- src/features/Events/EventUpdateModal.tsx | 16 +++++----- 7 files changed, 71 insertions(+), 62 deletions(-) create mode 100644 src/components/Calendar/CalendarItemList.tsx create mode 100644 src/components/Calendar/CalendarName.tsx diff --git a/src/components/Calendar/CalendarItemList.tsx b/src/components/Calendar/CalendarItemList.tsx new file mode 100644 index 0000000..955a261 --- /dev/null +++ b/src/components/Calendar/CalendarItemList.tsx @@ -0,0 +1,14 @@ +import React from "react"; +import { MenuItem } from "@mui/material"; +import { Calendars } from "../../features/Calendars/CalendarTypes"; +import { CalendarName } from "./CalendarName"; + +export function CalendarItemList( + userPersonnalCalendars: Calendars[] +): React.ReactNode { + return Object.values(userPersonnalCalendars).map((calendar) => ( + + + + )); +} diff --git a/src/components/Calendar/CalendarName.tsx b/src/components/Calendar/CalendarName.tsx new file mode 100644 index 0000000..1f7d8bd --- /dev/null +++ b/src/components/Calendar/CalendarName.tsx @@ -0,0 +1,17 @@ +import { Box, Typography } from "@mui/material"; +import { Calendars } from "../../features/Calendars/CalendarTypes"; +import SquareRoundedIcon from "@mui/icons-material/SquareRounded"; +export function CalendarName({ calendar }: { calendar: Calendars }) { + return ( + + + {calendar.name} + + ); +} diff --git a/src/components/Calendar/ImportTab.tsx b/src/components/Calendar/ImportTab.tsx index 7ce2d8a..f0d0bd3 100644 --- a/src/components/Calendar/ImportTab.tsx +++ b/src/components/Calendar/ImportTab.tsx @@ -12,6 +12,7 @@ import { } from "@mui/material"; import { useEffect, useState } from "react"; import { useAppSelector } from "../../app/hooks"; +import { CalendarItemList } from "./CalendarItemList"; import { SettingsTab } from "./SettingsTab"; export function ImportTab({ @@ -40,8 +41,8 @@ export function ImportTab({ const [importFile, setImportFile] = useState(null); const [importUrl, setImportUrl] = useState(""); const calendars = useAppSelector((state) => state.calendars.list); - const personnalCalendars = Object.keys(calendars).filter( - (id) => id.split("/")[0] === userId + const personnalCalendars = Object.values(calendars).filter( + (cal) => cal.id.split("/")[0] === userId ); useEffect(() => { @@ -109,11 +110,7 @@ export function ImportTab({ onChange={(e) => setImportTarget(e.target.value)} > New calendar - {personnalCalendars.map((id) => ( - - {calendars[id].name} - - ))} + {CalendarItemList(personnalCalendars)} diff --git a/src/components/Event/EventFormFields.tsx b/src/components/Event/EventFormFields.tsx index 4ab9db9..0cef472 100644 --- a/src/components/Event/EventFormFields.tsx +++ b/src/components/Event/EventFormFields.tsx @@ -33,6 +33,7 @@ import { addVideoConferenceToDescription, } from "../../utils/videoConferenceUtils"; import { TimezoneAutocomplete } from "../Timezone/TimezoneAutocomplete"; +import { CalendarItemList } from "../Calendar/CalendarItemList"; // Helper component for field with label export const FieldWithLabel = React.memo( @@ -113,8 +114,8 @@ interface EventFormFieldsProps { setEventClass: (eventClass: string) => void; timezone: string; setTimezone: (timezone: string) => void; - calendarid: number; - setCalendarid: (calendarid: number) => void; + calendarid: string; + setCalendarid: (calendarid: string) => void; hasVideoConference: boolean; setHasVideoConference: (hasVideoConference: boolean) => void; meetingLink: string | null; @@ -144,7 +145,7 @@ interface EventFormFieldsProps { newStart: string, newEnd: string ) => void; - onCalendarChange?: (newCalendarId: number) => void; + onCalendarChange?: (newCalendarId: string) => void; // Validation onValidationChange?: (isValid: boolean) => void; @@ -370,7 +371,7 @@ export default function EventFormFields({ onAllDayChange?.(newAllDay, newStart, newEnd); }; - const handleCalendarChange = (newCalendarId: number) => { + const handleCalendarChange = (newCalendarId: string) => { setCalendarid(newCalendarId); onCalendarChange?.(newCalendarId); }; @@ -738,14 +739,10 @@ export default function EventFormFields({ label={!showMore ? "Calendar" : ""} displayEmpty onChange={(e: SelectChangeEvent) => - handleCalendarChange(Number(e.target.value)) + handleCalendarChange(e.target.value) } > - {Object.keys(userPersonnalCalendars).map((calendar, index) => ( - - {userPersonnalCalendars[index].name} - - ))} + {CalendarItemList(userPersonnalCalendars)} diff --git a/src/features/Events/EventDisplayPreview.tsx b/src/features/Events/EventDisplayPreview.tsx index 764986d..1abaf4a 100644 --- a/src/features/Events/EventDisplayPreview.tsx +++ b/src/features/Events/EventDisplayPreview.tsx @@ -1,3 +1,4 @@ +import { DateSelectArg } from "@fullcalendar/core"; import CalendarTodayIcon from "@mui/icons-material/CalendarToday"; import CircleIcon from "@mui/icons-material/Circle"; import CloseIcon from "@mui/icons-material/Close"; @@ -5,17 +6,16 @@ import EditIcon from "@mui/icons-material/Edit"; import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline"; import FileDownloadOutlinedIcon from "@mui/icons-material/FileDownloadOutlined"; import LocationOnOutlinedIcon from "@mui/icons-material/LocationOnOutlined"; +import LockOutlineIcon from "@mui/icons-material/LockOutline"; import MoreVertIcon from "@mui/icons-material/MoreVert"; import NotificationsNoneIcon from "@mui/icons-material/NotificationsNone"; import PeopleAltOutlinedIcon from "@mui/icons-material/PeopleAltOutlined"; +import RepeatIcon from "@mui/icons-material/Repeat"; import SubjectIcon from "@mui/icons-material/Subject"; import VideocamIcon from "@mui/icons-material/Videocam"; -import RepeatIcon from "@mui/icons-material/Repeat"; -import LockOutlineIcon from "@mui/icons-material/LockOutline"; +import { Box, Typography } from "@mui/material"; import EventPopover from "./EventModal"; -import { DateSelectArg } from "@fullcalendar/core"; import { - Box, Button, Chip, DialogActions, @@ -23,15 +23,13 @@ import { Menu, MenuItem, Tooltip, - Typography, } from "@mui/material"; import AvatarGroup from "@mui/material/AvatarGroup"; import { useEffect, useState } from "react"; import { useAppDispatch, useAppSelector } from "../../app/hooks"; -import { deleteEventAsync } from "../Calendars/CalendarSlice"; -import { dlEvent } from "./EventApi"; -import EventDisplayModal from "./EventDisplay"; -import EventUpdateModal from "./EventUpdateModal"; +import { CalendarName } from "../../components/Calendar/CalendarName"; +import { getTimezoneOffset } from "../../components/Calendar/TimezoneSelector"; +import { updateTempCalendar } from "../../components/Calendar/utils/calendarUtils"; import ResponsiveDialog from "../../components/Dialog/ResponsiveDialog"; import { EditModeDialog } from "../../components/Event/EditModeDialog"; import EventDuplication from "../../components/Event/EventDuplicate"; @@ -41,10 +39,12 @@ import { } from "../../components/Event/eventHandlers/eventHandlers"; import { InfoRow } from "../../components/Event/InfoRow"; import { renderAttendeeBadge } from "../../components/Event/utils/eventUtils"; -import { getTimezoneOffset } from "../../components/Calendar/TimezoneSelector"; import { getCalendarRange } from "../../utils/dateUtils"; -import { updateTempCalendar } from "../../components/Calendar/utils/calendarUtils"; +import { deleteEventAsync } from "../Calendars/CalendarSlice"; +import { dlEvent } from "./EventApi"; +import EventDisplayModal from "./EventDisplay"; import { CalendarEvent } from "./EventsTypes"; +import EventUpdateModal from "./EventUpdateModal"; export default function EventPreviewModal({ eventId, calId, @@ -648,16 +648,7 @@ export default function EventPreviewModal({ - - - {calendar.name} - + state.user.userData?.openpaasId) ?? ""; const tempList = useAppSelector((state) => state.calendars.templist); + const calList = useAppSelector((state) => state.calendars.list); const selectPersonnalCalendars = createSelector( (state: any) => state.calendars, (calendars: any) => @@ -94,9 +95,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.findIndex((e) => e.id === event?.calId) - : 0 + event?.calId ?? userPersonnalCalendars[0]?.id ); const [allday, setAllDay] = useState(event?.allday ?? false); const [repetition, setRepetition] = useState( @@ -141,7 +140,7 @@ function EventPopover({ setLocation(""); setStart(""); setEnd(""); - setCalendarid(0); + setCalendarid(userPersonnalCalendars[0].id); setAllDay(false); setRepetition({} as RepetitionObject); setAlarm(""); @@ -293,13 +292,7 @@ function EventPopover({ setEnd(""); } - setCalendarid( - event.calId - ? userPersonnalCalendarsRef.current.findIndex( - (e) => e.id === event.calId - ) - : 0 - ); + setCalendarid(userPersonnalCalendars[0].id); setRepetition(event.repetition ?? ({} as RepetitionObject)); setShowRepeat(event.repetition?.freq ? true : false); setAttendees( @@ -354,7 +347,7 @@ function EventPopover({ setDescription(""); setAttendees([]); setLocation(""); - setCalendarid(0); + setCalendarid(userPersonnalCalendars[0].id); setAllDay(false); setRepetition({} as RepetitionObject); setAlarm(""); @@ -468,9 +461,9 @@ function EventPopover({ const newEventUID = crypto.randomUUID(); const newEvent: CalendarEvent = { - calId: userPersonnalCalendars[calendarid].id, + calId: calList[calendarid].id, title, - URL: `/calendars/${userPersonnalCalendars[calendarid].id}/${newEventUID}.ics`, + URL: `/calendars/${calList[calendarid].id}/${newEventUID}.ics`, start: new Date(start).toISOString(), allday, uid: newEventUID, @@ -491,7 +484,7 @@ function EventPopover({ }, ], transp: busy, - color: userPersonnalCalendars[calendarid]?.color, + color: calList[calendarid]?.color, alarm: { trigger: alarm, action: "EMAIL" }, x_openpass_videoconference: meetingLink || undefined, }; @@ -515,7 +508,7 @@ function EventPopover({ // Save to API in background await dispatch( putEventAsync({ - cal: userPersonnalCalendars[calendarid], + cal: calList[calendarid], newEvent, }) ); diff --git a/src/features/Events/EventUpdateModal.tsx b/src/features/Events/EventUpdateModal.tsx index 95bedf7..5ef98dc 100644 --- a/src/features/Events/EventUpdateModal.tsx +++ b/src/features/Events/EventUpdateModal.tsx @@ -52,6 +52,7 @@ function EventUpdateModal({ }) { const dispatch = useAppDispatch(); const tempList = useAppSelector((state) => state.calendars.templist); + const calList = useAppSelector((state) => state.calendars.list); // Get event from Redux store (cached data) as fallback const cachedEvent = useAppSelector( (state) => state.calendars.list[calId]?.events[eventId] @@ -153,7 +154,9 @@ function EventUpdateModal({ resolveTimezone(Intl.DateTimeFormat().resolvedOptions().timeZone) ); const [newCalId, setNewCalId] = useState(calId); - const [calendarid, setCalendarid] = useState(0); + const [calendarid, setCalendarid] = useState( + calId ?? userPersonnalCalendars[0]?.id + ); const [attendees, setAttendees] = useState([]); const [hasVideoConference, setHasVideoConference] = useState(false); @@ -171,7 +174,7 @@ function EventUpdateModal({ setLocation(""); setStart(""); setEnd(""); - setCalendarid(0); + setCalendarid(userPersonnalCalendars[0].id); setAllDay(false); setRepetition({} as RepetitionObject); setAlarm(""); @@ -235,10 +238,7 @@ function EventUpdateModal({ } // Find correct calendar index - const currentCalIndex = userPersonnalCalendars.findIndex( - (cal) => cal.id === calId - ); - setCalendarid(currentCalIndex >= 0 ? currentCalIndex : 0); + setCalendarid(calId); // Handle repetition properly - check both current event and base event const baseEventId = event.uid.split("/")[0]; @@ -327,7 +327,7 @@ function EventUpdateModal({ const organizer = event.organizer; - const targetCalendar = userPersonnalCalendars[calendarid]; + const targetCalendar = calList[calendarid]; if (!targetCalendar) { console.error("Target calendar not found"); return; @@ -747,7 +747,7 @@ function EventUpdateModal({ userPersonnalCalendars={userPersonnalCalendars} timezoneList={timezoneList} onCalendarChange={(newCalendarId) => { - const selectedCalendar = userPersonnalCalendars[newCalendarId]; + const selectedCalendar = calList[newCalendarId]; if (selectedCalendar) { setNewCalId(selectedCalendar.id); }