Co-authored-by: Camille Moussu <cmoussu@linagora.com>
This commit is contained in:
@@ -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) => (
|
||||||
|
<MenuItem key={calendar.id} value={calendar.id}>
|
||||||
|
<CalendarName calendar={calendar} />
|
||||||
|
</MenuItem>
|
||||||
|
));
|
||||||
|
}
|
||||||
@@ -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 (
|
||||||
|
<Box style={{ display: "flex", flexDirection: "row", gap: 8 }}>
|
||||||
|
<SquareRoundedIcon
|
||||||
|
style={{
|
||||||
|
color: calendar.color?.light ?? "#3788D8",
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography sx={{ wordBreak: "break-word" }}>{calendar.name}</Typography>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useAppSelector } from "../../app/hooks";
|
import { useAppSelector } from "../../app/hooks";
|
||||||
|
import { CalendarItemList } from "./CalendarItemList";
|
||||||
import { SettingsTab } from "./SettingsTab";
|
import { SettingsTab } from "./SettingsTab";
|
||||||
|
|
||||||
export function ImportTab({
|
export function ImportTab({
|
||||||
@@ -40,8 +41,8 @@ export function ImportTab({
|
|||||||
const [importFile, setImportFile] = useState<File | null>(null);
|
const [importFile, setImportFile] = useState<File | null>(null);
|
||||||
const [importUrl, setImportUrl] = useState("");
|
const [importUrl, setImportUrl] = useState("");
|
||||||
const calendars = useAppSelector((state) => state.calendars.list);
|
const calendars = useAppSelector((state) => state.calendars.list);
|
||||||
const personnalCalendars = Object.keys(calendars).filter(
|
const personnalCalendars = Object.values(calendars).filter(
|
||||||
(id) => id.split("/")[0] === userId
|
(cal) => cal.id.split("/")[0] === userId
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -109,11 +110,7 @@ export function ImportTab({
|
|||||||
onChange={(e) => setImportTarget(e.target.value)}
|
onChange={(e) => setImportTarget(e.target.value)}
|
||||||
>
|
>
|
||||||
<MenuItem value="new">New calendar</MenuItem>
|
<MenuItem value="new">New calendar</MenuItem>
|
||||||
{personnalCalendars.map((id) => (
|
{CalendarItemList(personnalCalendars)}
|
||||||
<MenuItem key={id} value={id}>
|
|
||||||
{calendars[id].name}
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import {
|
|||||||
addVideoConferenceToDescription,
|
addVideoConferenceToDescription,
|
||||||
} from "../../utils/videoConferenceUtils";
|
} from "../../utils/videoConferenceUtils";
|
||||||
import { TimezoneAutocomplete } from "../Timezone/TimezoneAutocomplete";
|
import { TimezoneAutocomplete } from "../Timezone/TimezoneAutocomplete";
|
||||||
|
import { CalendarItemList } from "../Calendar/CalendarItemList";
|
||||||
|
|
||||||
// Helper component for field with label
|
// Helper component for field with label
|
||||||
export const FieldWithLabel = React.memo(
|
export const FieldWithLabel = React.memo(
|
||||||
@@ -113,8 +114,8 @@ interface EventFormFieldsProps {
|
|||||||
setEventClass: (eventClass: string) => void;
|
setEventClass: (eventClass: string) => void;
|
||||||
timezone: string;
|
timezone: string;
|
||||||
setTimezone: (timezone: string) => void;
|
setTimezone: (timezone: string) => void;
|
||||||
calendarid: number;
|
calendarid: string;
|
||||||
setCalendarid: (calendarid: number) => void;
|
setCalendarid: (calendarid: string) => void;
|
||||||
hasVideoConference: boolean;
|
hasVideoConference: boolean;
|
||||||
setHasVideoConference: (hasVideoConference: boolean) => void;
|
setHasVideoConference: (hasVideoConference: boolean) => void;
|
||||||
meetingLink: string | null;
|
meetingLink: string | null;
|
||||||
@@ -144,7 +145,7 @@ interface EventFormFieldsProps {
|
|||||||
newStart: string,
|
newStart: string,
|
||||||
newEnd: string
|
newEnd: string
|
||||||
) => void;
|
) => void;
|
||||||
onCalendarChange?: (newCalendarId: number) => void;
|
onCalendarChange?: (newCalendarId: string) => void;
|
||||||
|
|
||||||
// Validation
|
// Validation
|
||||||
onValidationChange?: (isValid: boolean) => void;
|
onValidationChange?: (isValid: boolean) => void;
|
||||||
@@ -370,7 +371,7 @@ export default function EventFormFields({
|
|||||||
onAllDayChange?.(newAllDay, newStart, newEnd);
|
onAllDayChange?.(newAllDay, newStart, newEnd);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCalendarChange = (newCalendarId: number) => {
|
const handleCalendarChange = (newCalendarId: string) => {
|
||||||
setCalendarid(newCalendarId);
|
setCalendarid(newCalendarId);
|
||||||
onCalendarChange?.(newCalendarId);
|
onCalendarChange?.(newCalendarId);
|
||||||
};
|
};
|
||||||
@@ -738,14 +739,10 @@ export default function EventFormFields({
|
|||||||
label={!showMore ? "Calendar" : ""}
|
label={!showMore ? "Calendar" : ""}
|
||||||
displayEmpty
|
displayEmpty
|
||||||
onChange={(e: SelectChangeEvent) =>
|
onChange={(e: SelectChangeEvent) =>
|
||||||
handleCalendarChange(Number(e.target.value))
|
handleCalendarChange(e.target.value)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{Object.keys(userPersonnalCalendars).map((calendar, index) => (
|
{CalendarItemList(userPersonnalCalendars)}
|
||||||
<MenuItem key={index} value={index}>
|
|
||||||
{userPersonnalCalendars[index].name}
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</FieldWithLabel>
|
</FieldWithLabel>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { DateSelectArg } from "@fullcalendar/core";
|
||||||
import CalendarTodayIcon from "@mui/icons-material/CalendarToday";
|
import CalendarTodayIcon from "@mui/icons-material/CalendarToday";
|
||||||
import CircleIcon from "@mui/icons-material/Circle";
|
import CircleIcon from "@mui/icons-material/Circle";
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
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 ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
|
||||||
import FileDownloadOutlinedIcon from "@mui/icons-material/FileDownloadOutlined";
|
import FileDownloadOutlinedIcon from "@mui/icons-material/FileDownloadOutlined";
|
||||||
import LocationOnOutlinedIcon from "@mui/icons-material/LocationOnOutlined";
|
import LocationOnOutlinedIcon from "@mui/icons-material/LocationOnOutlined";
|
||||||
|
import LockOutlineIcon from "@mui/icons-material/LockOutline";
|
||||||
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
import MoreVertIcon from "@mui/icons-material/MoreVert";
|
||||||
import NotificationsNoneIcon from "@mui/icons-material/NotificationsNone";
|
import NotificationsNoneIcon from "@mui/icons-material/NotificationsNone";
|
||||||
import PeopleAltOutlinedIcon from "@mui/icons-material/PeopleAltOutlined";
|
import PeopleAltOutlinedIcon from "@mui/icons-material/PeopleAltOutlined";
|
||||||
|
import RepeatIcon from "@mui/icons-material/Repeat";
|
||||||
import SubjectIcon from "@mui/icons-material/Subject";
|
import SubjectIcon from "@mui/icons-material/Subject";
|
||||||
import VideocamIcon from "@mui/icons-material/Videocam";
|
import VideocamIcon from "@mui/icons-material/Videocam";
|
||||||
import RepeatIcon from "@mui/icons-material/Repeat";
|
import { Box, Typography } from "@mui/material";
|
||||||
import LockOutlineIcon from "@mui/icons-material/LockOutline";
|
|
||||||
import EventPopover from "./EventModal";
|
import EventPopover from "./EventModal";
|
||||||
import { DateSelectArg } from "@fullcalendar/core";
|
|
||||||
import {
|
import {
|
||||||
Box,
|
|
||||||
Button,
|
Button,
|
||||||
Chip,
|
Chip,
|
||||||
DialogActions,
|
DialogActions,
|
||||||
@@ -23,15 +23,13 @@ import {
|
|||||||
Menu,
|
Menu,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Typography,
|
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import AvatarGroup from "@mui/material/AvatarGroup";
|
import AvatarGroup from "@mui/material/AvatarGroup";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
import { useAppDispatch, useAppSelector } from "../../app/hooks";
|
||||||
import { deleteEventAsync } from "../Calendars/CalendarSlice";
|
import { CalendarName } from "../../components/Calendar/CalendarName";
|
||||||
import { dlEvent } from "./EventApi";
|
import { getTimezoneOffset } from "../../components/Calendar/TimezoneSelector";
|
||||||
import EventDisplayModal from "./EventDisplay";
|
import { updateTempCalendar } from "../../components/Calendar/utils/calendarUtils";
|
||||||
import EventUpdateModal from "./EventUpdateModal";
|
|
||||||
import ResponsiveDialog from "../../components/Dialog/ResponsiveDialog";
|
import ResponsiveDialog from "../../components/Dialog/ResponsiveDialog";
|
||||||
import { EditModeDialog } from "../../components/Event/EditModeDialog";
|
import { EditModeDialog } from "../../components/Event/EditModeDialog";
|
||||||
import EventDuplication from "../../components/Event/EventDuplicate";
|
import EventDuplication from "../../components/Event/EventDuplicate";
|
||||||
@@ -41,10 +39,12 @@ import {
|
|||||||
} from "../../components/Event/eventHandlers/eventHandlers";
|
} from "../../components/Event/eventHandlers/eventHandlers";
|
||||||
import { InfoRow } from "../../components/Event/InfoRow";
|
import { InfoRow } from "../../components/Event/InfoRow";
|
||||||
import { renderAttendeeBadge } from "../../components/Event/utils/eventUtils";
|
import { renderAttendeeBadge } from "../../components/Event/utils/eventUtils";
|
||||||
import { getTimezoneOffset } from "../../components/Calendar/TimezoneSelector";
|
|
||||||
import { getCalendarRange } from "../../utils/dateUtils";
|
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 { CalendarEvent } from "./EventsTypes";
|
||||||
|
import EventUpdateModal from "./EventUpdateModal";
|
||||||
export default function EventPreviewModal({
|
export default function EventPreviewModal({
|
||||||
eventId,
|
eventId,
|
||||||
calId,
|
calId,
|
||||||
@@ -648,16 +648,7 @@ export default function EventPreviewModal({
|
|||||||
<Box sx={{ minWidth: "25px", marginRight: 2 }}>
|
<Box sx={{ minWidth: "25px", marginRight: 2 }}>
|
||||||
<CalendarTodayIcon />
|
<CalendarTodayIcon />
|
||||||
</Box>
|
</Box>
|
||||||
<CircleIcon
|
<CalendarName calendar={calendar} />
|
||||||
style={{
|
|
||||||
color: calendar.color?.light ?? "#3788D8",
|
|
||||||
width: 12,
|
|
||||||
height: 12,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Typography sx={{ wordBreak: "break-word" }}>
|
|
||||||
{calendar.name}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
</Box>
|
||||||
</ResponsiveDialog>
|
</ResponsiveDialog>
|
||||||
<EditModeDialog
|
<EditModeDialog
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ function EventPopover({
|
|||||||
const userId =
|
const userId =
|
||||||
useAppSelector((state) => state.user.userData?.openpaasId) ?? "";
|
useAppSelector((state) => state.user.userData?.openpaasId) ?? "";
|
||||||
const tempList = useAppSelector((state) => state.calendars.templist);
|
const tempList = useAppSelector((state) => state.calendars.templist);
|
||||||
|
const calList = useAppSelector((state) => state.calendars.list);
|
||||||
const selectPersonnalCalendars = createSelector(
|
const selectPersonnalCalendars = createSelector(
|
||||||
(state: any) => state.calendars,
|
(state: any) => state.calendars,
|
||||||
(calendars: any) =>
|
(calendars: any) =>
|
||||||
@@ -94,9 +95,7 @@ function EventPopover({
|
|||||||
const [start, setStart] = useState(event?.start ? event.start : "");
|
const [start, setStart] = useState(event?.start ? event.start : "");
|
||||||
const [end, setEnd] = useState(event?.end ? event.end : "");
|
const [end, setEnd] = useState(event?.end ? event.end : "");
|
||||||
const [calendarid, setCalendarid] = useState(
|
const [calendarid, setCalendarid] = useState(
|
||||||
event?.calId
|
event?.calId ?? userPersonnalCalendars[0]?.id
|
||||||
? userPersonnalCalendars.findIndex((e) => e.id === event?.calId)
|
|
||||||
: 0
|
|
||||||
);
|
);
|
||||||
const [allday, setAllDay] = useState(event?.allday ?? false);
|
const [allday, setAllDay] = useState(event?.allday ?? false);
|
||||||
const [repetition, setRepetition] = useState<RepetitionObject>(
|
const [repetition, setRepetition] = useState<RepetitionObject>(
|
||||||
@@ -141,7 +140,7 @@ function EventPopover({
|
|||||||
setLocation("");
|
setLocation("");
|
||||||
setStart("");
|
setStart("");
|
||||||
setEnd("");
|
setEnd("");
|
||||||
setCalendarid(0);
|
setCalendarid(userPersonnalCalendars[0].id);
|
||||||
setAllDay(false);
|
setAllDay(false);
|
||||||
setRepetition({} as RepetitionObject);
|
setRepetition({} as RepetitionObject);
|
||||||
setAlarm("");
|
setAlarm("");
|
||||||
@@ -293,13 +292,7 @@ function EventPopover({
|
|||||||
setEnd("");
|
setEnd("");
|
||||||
}
|
}
|
||||||
|
|
||||||
setCalendarid(
|
setCalendarid(userPersonnalCalendars[0].id);
|
||||||
event.calId
|
|
||||||
? userPersonnalCalendarsRef.current.findIndex(
|
|
||||||
(e) => e.id === event.calId
|
|
||||||
)
|
|
||||||
: 0
|
|
||||||
);
|
|
||||||
setRepetition(event.repetition ?? ({} as RepetitionObject));
|
setRepetition(event.repetition ?? ({} as RepetitionObject));
|
||||||
setShowRepeat(event.repetition?.freq ? true : false);
|
setShowRepeat(event.repetition?.freq ? true : false);
|
||||||
setAttendees(
|
setAttendees(
|
||||||
@@ -354,7 +347,7 @@ function EventPopover({
|
|||||||
setDescription("");
|
setDescription("");
|
||||||
setAttendees([]);
|
setAttendees([]);
|
||||||
setLocation("");
|
setLocation("");
|
||||||
setCalendarid(0);
|
setCalendarid(userPersonnalCalendars[0].id);
|
||||||
setAllDay(false);
|
setAllDay(false);
|
||||||
setRepetition({} as RepetitionObject);
|
setRepetition({} as RepetitionObject);
|
||||||
setAlarm("");
|
setAlarm("");
|
||||||
@@ -468,9 +461,9 @@ function EventPopover({
|
|||||||
const newEventUID = crypto.randomUUID();
|
const newEventUID = crypto.randomUUID();
|
||||||
|
|
||||||
const newEvent: CalendarEvent = {
|
const newEvent: CalendarEvent = {
|
||||||
calId: userPersonnalCalendars[calendarid].id,
|
calId: calList[calendarid].id,
|
||||||
title,
|
title,
|
||||||
URL: `/calendars/${userPersonnalCalendars[calendarid].id}/${newEventUID}.ics`,
|
URL: `/calendars/${calList[calendarid].id}/${newEventUID}.ics`,
|
||||||
start: new Date(start).toISOString(),
|
start: new Date(start).toISOString(),
|
||||||
allday,
|
allday,
|
||||||
uid: newEventUID,
|
uid: newEventUID,
|
||||||
@@ -491,7 +484,7 @@ function EventPopover({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
transp: busy,
|
transp: busy,
|
||||||
color: userPersonnalCalendars[calendarid]?.color,
|
color: calList[calendarid]?.color,
|
||||||
alarm: { trigger: alarm, action: "EMAIL" },
|
alarm: { trigger: alarm, action: "EMAIL" },
|
||||||
x_openpass_videoconference: meetingLink || undefined,
|
x_openpass_videoconference: meetingLink || undefined,
|
||||||
};
|
};
|
||||||
@@ -515,7 +508,7 @@ function EventPopover({
|
|||||||
// Save to API in background
|
// Save to API in background
|
||||||
await dispatch(
|
await dispatch(
|
||||||
putEventAsync({
|
putEventAsync({
|
||||||
cal: userPersonnalCalendars[calendarid],
|
cal: calList[calendarid],
|
||||||
newEvent,
|
newEvent,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ function EventUpdateModal({
|
|||||||
}) {
|
}) {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const tempList = useAppSelector((state) => state.calendars.templist);
|
const tempList = useAppSelector((state) => state.calendars.templist);
|
||||||
|
const calList = useAppSelector((state) => state.calendars.list);
|
||||||
// Get event from Redux store (cached data) as fallback
|
// Get event from Redux store (cached data) as fallback
|
||||||
const cachedEvent = useAppSelector(
|
const cachedEvent = useAppSelector(
|
||||||
(state) => state.calendars.list[calId]?.events[eventId]
|
(state) => state.calendars.list[calId]?.events[eventId]
|
||||||
@@ -153,7 +154,9 @@ function EventUpdateModal({
|
|||||||
resolveTimezone(Intl.DateTimeFormat().resolvedOptions().timeZone)
|
resolveTimezone(Intl.DateTimeFormat().resolvedOptions().timeZone)
|
||||||
);
|
);
|
||||||
const [newCalId, setNewCalId] = useState(calId);
|
const [newCalId, setNewCalId] = useState(calId);
|
||||||
const [calendarid, setCalendarid] = useState(0);
|
const [calendarid, setCalendarid] = useState(
|
||||||
|
calId ?? userPersonnalCalendars[0]?.id
|
||||||
|
);
|
||||||
|
|
||||||
const [attendees, setAttendees] = useState<userAttendee[]>([]);
|
const [attendees, setAttendees] = useState<userAttendee[]>([]);
|
||||||
const [hasVideoConference, setHasVideoConference] = useState(false);
|
const [hasVideoConference, setHasVideoConference] = useState(false);
|
||||||
@@ -171,7 +174,7 @@ function EventUpdateModal({
|
|||||||
setLocation("");
|
setLocation("");
|
||||||
setStart("");
|
setStart("");
|
||||||
setEnd("");
|
setEnd("");
|
||||||
setCalendarid(0);
|
setCalendarid(userPersonnalCalendars[0].id);
|
||||||
setAllDay(false);
|
setAllDay(false);
|
||||||
setRepetition({} as RepetitionObject);
|
setRepetition({} as RepetitionObject);
|
||||||
setAlarm("");
|
setAlarm("");
|
||||||
@@ -235,10 +238,7 @@ function EventUpdateModal({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find correct calendar index
|
// Find correct calendar index
|
||||||
const currentCalIndex = userPersonnalCalendars.findIndex(
|
setCalendarid(calId);
|
||||||
(cal) => cal.id === calId
|
|
||||||
);
|
|
||||||
setCalendarid(currentCalIndex >= 0 ? currentCalIndex : 0);
|
|
||||||
|
|
||||||
// Handle repetition properly - check both current event and base event
|
// Handle repetition properly - check both current event and base event
|
||||||
const baseEventId = event.uid.split("/")[0];
|
const baseEventId = event.uid.split("/")[0];
|
||||||
@@ -327,7 +327,7 @@ function EventUpdateModal({
|
|||||||
|
|
||||||
const organizer = event.organizer;
|
const organizer = event.organizer;
|
||||||
|
|
||||||
const targetCalendar = userPersonnalCalendars[calendarid];
|
const targetCalendar = calList[calendarid];
|
||||||
if (!targetCalendar) {
|
if (!targetCalendar) {
|
||||||
console.error("Target calendar not found");
|
console.error("Target calendar not found");
|
||||||
return;
|
return;
|
||||||
@@ -747,7 +747,7 @@ function EventUpdateModal({
|
|||||||
userPersonnalCalendars={userPersonnalCalendars}
|
userPersonnalCalendars={userPersonnalCalendars}
|
||||||
timezoneList={timezoneList}
|
timezoneList={timezoneList}
|
||||||
onCalendarChange={(newCalendarId) => {
|
onCalendarChange={(newCalendarId) => {
|
||||||
const selectedCalendar = userPersonnalCalendars[newCalendarId];
|
const selectedCalendar = calList[newCalendarId];
|
||||||
if (selectedCalendar) {
|
if (selectedCalendar) {
|
||||||
setNewCalId(selectedCalendar.id);
|
setNewCalId(selectedCalendar.id);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user