[#421] added eslint check to CI (#534)

Co-authored-by: Camille Moussu <cmoussu@linagora.com>
This commit is contained in:
Camille Moussu
2026-02-10 10:41:37 +01:00
committed by GitHub
parent 8a6ec8fc39
commit d6e464afad
116 changed files with 1232 additions and 801 deletions
+2 -2
View File
@@ -15,7 +15,7 @@ export default function UserSearch({
placeholder,
}: {
attendees: userAttendee[];
setAttendees: Function;
setAttendees: (attendees: userAttendee[]) => void;
disabled?: boolean;
inputSlot?: (
params: ExtendedAutocompleteRenderInputParams
@@ -47,7 +47,7 @@ export default function UserSearch({
disabled={disabled}
inputSlot={inputSlot}
placeholder={placeholder}
onChange={(event: any, value: User[]) => {
onChange={(_event, value: User[]) => {
setAttendees(
value.map((attendee: User) =>
createAttendee({
+6 -6
View File
@@ -23,6 +23,7 @@ import {
useEffect,
useState,
type ReactNode,
type SyntheticEvent,
} from "react";
import { useI18n } from "twake-i18n";
@@ -55,7 +56,7 @@ export function PeopleSearch({
customSlotProps,
}: {
selectedUsers: User[];
onChange: (event: any, users: User[]) => void;
onChange: (event: SyntheticEvent, users: User[]) => void;
objectTypes: string[];
disabled?: boolean;
freeSolo?: boolean;
@@ -155,7 +156,7 @@ export function PeopleSearch({
),
};
const enhancedParamsWithInputProps = {
const enhancedParams = {
...params,
InputProps: inputProps,
inputProps: {
@@ -164,8 +165,6 @@ export function PeopleSearch({
},
};
const { InputProps, ...enhancedParams } = enhancedParamsWithInputProps;
const handleEnterKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter" && onToggleEventPreview) {
e.preventDefault();
@@ -193,7 +192,7 @@ export function PeopleSearch({
{t("peopleSearch.label")}
</label>
{inputSlot({
...enhancedParamsWithInputProps,
...enhancedParams,
error: !!inputError,
helperText: inputError,
placeholder: searchPlaceholder,
@@ -218,6 +217,7 @@ export function PeopleSearch({
</>
);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[inputError, t, onToggleEventPreview, loading, searchPlaceholder]
);
@@ -296,7 +296,7 @@ export function PeopleSearch({
}
renderOption={(props, option) => {
if (selectedUsers.find((u) => u.email === option.email)) return null;
const { key, ...otherProps } = props as any;
const { key, ...otherProps } = props;
return (
<ListItem key={key + option?.email} {...otherProps} disableGutters>
<ListItemAvatar>
+1 -1
View File
@@ -22,7 +22,7 @@ import { SnackbarAlert } from "../Loading/SnackBarAlert";
export function AccessTab({ calendar }: { calendar: Calendar }) {
const { t } = useI18n();
const calDAVLink = `${(window as any).CALENDAR_BASE_URL}${calendar.link.replace(".json", "")}`;
const calDAVLink = `${window.CALENDAR_BASE_URL}${calendar.link.replace(".json", "")}`;
const [secretLink, setSecretLink] = useState("");
const [open, setOpen] = useState(false);
+18 -15
View File
@@ -15,6 +15,7 @@ import { extractEventBaseUuid } from "@/utils/extractEventBaseUuid";
import { setSelectedCalendars as setSelectedCalendarsToStorage } from "@/utils/storage/setSelectedCalendars";
import { useSelectedCalendars } from "@/utils/storage/useSelectedCalendars";
import { browserDefaultTimeZone } from "@/utils/timezone";
import type { EventApi, LocaleInput } from "@fullcalendar/core";
import { CalendarApi, DateSelectArg } from "@fullcalendar/core";
import frLocale from "@fullcalendar/core/locales/fr";
import ruLocale from "@fullcalendar/core/locales/ru";
@@ -47,7 +48,7 @@ import {
updateSlotLabelVisibility,
} from "./utils/calendarUtils";
const localeMap: Record<string, any> = {
const localeMap: Record<string, LocaleInput | undefined> = {
fr: frLocale,
ru: ruLocale,
vi: viLocale,
@@ -221,7 +222,7 @@ export default function CalendarApp({
[rangeStart, rangeEnd]
);
let filteredEvents: CalendarEvent[] = extractEvents(
const filteredEvents: CalendarEvent[] = extractEvents(
selectedCalendars,
calendars || {},
userData?.email,
@@ -233,7 +234,7 @@ export default function CalendarApp({
[tempcalendars]
);
let filteredTempEvents: CalendarEvent[] = extractEvents(
const filteredTempEvents: CalendarEvent[] = extractEvents(
tempCalendarIds,
tempcalendars || {},
userData?.email,
@@ -303,7 +304,7 @@ export default function CalendarApp({
},
})
).unwrap();
} catch (error) {
} catch {
fetchedRangesRef.current[id] = "";
}
});
@@ -323,6 +324,7 @@ export default function CalendarApp({
return () => {
cancelled = true;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dispatch, rangeKey, sortedSelectedCalendars, rangeStart, rangeEnd]);
useEffect(() => {
@@ -352,6 +354,7 @@ export default function CalendarApp({
prefetchedCalendarsRef.current[id] = "";
});
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
calendarIdsString,
selectedCalendars,
@@ -458,7 +461,7 @@ export default function CalendarApp({
calType: "temp",
})
).unwrap();
} catch (error) {
} catch {
tempFetchedRangesRef.current[id] = "";
}
});
@@ -521,7 +524,7 @@ export default function CalendarApp({
})
);
}
} catch (err) {
} catch {
// Ignore sessionStorage errors
}
}
@@ -543,10 +546,10 @@ export default function CalendarApp({
null
);
const [, setTypeOfAction] = useState<"solo" | "all" | undefined>(undefined);
const [afterChoiceFunc, setAfterChoiceFunc] = useState<Function>();
const [selectedEvent, setSelectedEvent] = useState<CalendarEvent>(
{} as CalendarEvent
);
const [afterChoiceFunc, setAfterChoiceFunc] = useState<
((type: "solo" | "all" | undefined) => void) | undefined
>();
const [, setSelectedEvent] = useState<CalendarEvent>({} as CalendarEvent);
const [selectedRange, setSelectedRange] = useState<DateSelectArg | null>(
null
);
@@ -587,7 +590,7 @@ export default function CalendarApp({
});
if (process.env.NODE_ENV === "test") {
(window as any).__calendarRef = calendarRef;
window.__calendarRef = calendarRef;
}
const { t, lang } = useI18n();
@@ -635,7 +638,6 @@ export default function CalendarApp({
<MiniCalendar
calendarRef={calendarRef}
selectedDate={selectedMiniDate}
setSelectedDate={setSelectedDate}
setSelectedMiniDate={setSelectedMiniDate}
dottedEvents={dottedEvents}
/>
@@ -697,7 +699,7 @@ export default function CalendarApp({
userData?.email,
isPending
)}
eventOrder={(a: any, b: any) =>
eventOrder={(a: EventApi, b: EventApi) =>
a.extendedProps.priority - b.extendedProps.priority
}
weekNumbers={
@@ -825,10 +827,11 @@ export default function CalendarApp({
<EditModeDialog
type={openEditModePopup}
setOpen={setOpenEditModePopup}
event={selectedEvent}
eventAction={(type: "solo" | "all" | undefined) => {
setTypeOfAction(type);
afterChoiceFunc && afterChoiceFunc(type);
if (afterChoiceFunc) {
afterChoiceFunc(type);
}
}}
/>
{openEventDisplay && eventDisplayedId && eventDisplayedCalId && (
@@ -123,11 +123,11 @@ function ColorPickerBox({
selectedColor ?? { light: "#ffffff", dark: "#808080" }
);
const [color, setColor] = useState(oldColor);
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
const open = Boolean(anchorEl);
const theme = useTheme();
const handleClick = (event: any) => {
const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {
setAnchorEl(event.currentTarget);
};
+2 -1
View File
@@ -1,6 +1,7 @@
import { useAppDispatch, useAppSelector } from "@/app/hooks";
import SettingsPage from "@/features/Settings/SettingsPage";
import { getCalendarRange } from "@/utils/dateUtils";
import type { CalendarApi } from "@fullcalendar/core";
import { useEffect, useRef, useState } from "react";
import { ErrorSnackbar } from "../Error/ErrorSnackbar";
import { refreshCalendars } from "../Event/utils/eventUtils";
@@ -8,7 +9,7 @@ import { Menubar, MenubarProps } from "../Menubar/Menubar";
import CalendarApp from "./Calendar";
export default function CalendarLayout() {
const calendarRef = useRef<any>(null);
const calendarRef = useRef<CalendarApi | null>(null);
const dispatch = useAppDispatch();
const error = useAppSelector((state) => state.calendars.error);
const selectedCalendars = useAppSelector((state) => state.calendars.list);
+7 -5
View File
@@ -138,28 +138,30 @@ function CalendarPopover({
newCalColor,
newCalVisibility
);
importedContent &&
if (importedContent) {
dispatch(
importEventFromFileAsync({
calLink: `/calendar/${userData.openpaasId}/${calId}.json`,
file: importedContent,
})
);
}
}
} else {
importedContent &&
if (importedContent) {
dispatch(
importEventFromFileAsync({
calLink: calendars[importTarget].link,
file: importedContent,
})
);
}
}
handleClose({}, "backdropClick");
};
const handleClose = (
e: {},
e: object | null,
reason: "backdropClick" | "escapeKeyDown"
): void => {
onClose(e, reason);
@@ -182,7 +184,7 @@ function CalendarPopover({
open={open}
onClose={() => handleClose({}, "backdropClick")}
title={
<Tabs value={tab} onChange={(e, v) => setTab(v)}>
<Tabs value={tab} onChange={(_e, v) => setTab(v)}>
<Tab
value="settings"
label={
@@ -203,7 +205,7 @@ function CalendarPopover({
<>
<Button
variant="outlined"
onClick={(e) => handleClose({}, "backdropClick")}
onClick={() => handleClose({}, "backdropClick")}
>
{t("common.cancel")}
</Button>
+10 -9
View File
@@ -21,7 +21,7 @@ import { ColorPicker } from "./CalendarColorPicker";
import { defaultColors, getAccessiblePair } from "./utils/calendarColorsUtils";
interface CalendarWithOwner {
cal: Record<string, any>;
cal: Record<string, unknown>;
owner: User;
}
@@ -117,7 +117,7 @@ function SelectedCalendarsList({
>
>((acc, cal) => {
const exists = Object.values(calendars).some(
(existing: any) =>
(existing: Calendar) =>
existing.id ===
cal.cal?._links?.self?.href
.replace("/calendars/", "")
@@ -190,13 +190,14 @@ function SelectedCalendarsList({
}
export default function CalendarSearch({
anchorEl,
open,
onClose,
}: {
anchorEl: HTMLElement | null;
open: boolean;
onClose: Function;
onClose: (
result?: string[] | Record<string, never>,
reason?: "backdropClick" | "escapeKeyDown"
) => void;
}) {
const dispatch = useAppDispatch();
const theme = useTheme();
@@ -214,7 +215,7 @@ export default function CalendarSearch({
selectedCal.map(async (cal) => {
const calId = crypto.randomUUID();
const exists = Object.values(calendars).some(
(existing: any) =>
(existing: Calendar) =>
existing.id ===
cal.cal?._links?.self?.href
.replace("/calendars/", "")
@@ -279,7 +280,7 @@ export default function CalendarSearch({
objectTypes={["user"]}
selectedUsers={selectedUsers}
inputSlot={(params) => <TextField {...params} size="small" />}
onChange={async (event: any, value: User[]) => {
onChange={async (_event: React.SyntheticEvent, value: User[]) => {
setSelectedUsers(value);
const cals = await Promise.all(
@@ -288,10 +289,10 @@ export default function CalendarSearch({
const cals = (await getCalendars(
user.openpaasId,
"sharedPublic=true&"
)) as Record<string, any>;
)) as Record<string, unknown>;
return cals._embedded?.["dav:calendar"]
? cals._embedded["dav:calendar"].map(
(cal: Record<string, any>) => ({ cal, owner: user })
(cal: Record<string, unknown>) => ({ cal, owner: user })
)
: { cal: undefined, owner: user };
}
+11 -9
View File
@@ -13,12 +13,11 @@ import {
ListItem,
Menu,
MenuItem,
Typography,
} from "@linagora/twake-mui";
import AddIcon from "@mui/icons-material/Add";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import MoreHorizIcon from "@mui/icons-material/MoreHoriz";
import { useEffect, useMemo, useState } from "react";
import { SetStateAction, useEffect, useMemo, useState } from "react";
import { useI18n } from "twake-i18n";
import CalendarPopover from "./CalendarModal";
import CalendarSearch from "./CalendarSearch";
@@ -39,9 +38,9 @@ function CalendarAccordion({
selectedCalendars: string[];
handleToggle: (id: string) => void;
showAddButton?: boolean;
onAddClick?: Function;
onAddClick?: () => void;
defaultExpanded?: boolean;
setOpen: Function;
setOpen: (id: string) => void;
}) {
const allCalendars = useAppSelector((state) => state.calendars.list);
const { t } = useI18n();
@@ -86,8 +85,12 @@ function CalendarAccordion({
<IconButton
component="span"
onClick={(e) => {
expended && e.stopPropagation();
onAddClick && onAddClick();
if (expended) {
e.stopPropagation();
}
if (onAddClick) {
onAddClick();
}
}}
>
<AddIcon />
@@ -116,7 +119,7 @@ export default function CalendarSelection({
setSelectedCalendars,
}: {
selectedCalendars: string[];
setSelectedCalendars: Function;
setSelectedCalendars: (value: SetStateAction<string[]>) => void;
}) {
const { t } = useI18n();
const userId =
@@ -202,7 +205,6 @@ export default function CalendarSelection({
}}
/>
<CalendarSearch
anchorEl={anchorElCalOthers}
open={Boolean(anchorElCalOthers)}
onClose={(newCalIds?: string[]) => {
setAnchorElCalOthers(null);
@@ -228,7 +230,7 @@ function CalendarSelector({
isPersonal: boolean;
selectedCalendars: string[];
handleCalendarToggle: (name: string) => void;
setOpen: Function;
setOpen: () => void;
}) {
const { t } = useI18n();
const dispatch = useAppDispatch();
+7 -7
View File
@@ -24,21 +24,21 @@ export function ImportTab({
}: {
userId: string;
importTarget: string;
setImportTarget: Function;
setImportedContent: Function;
setImportTarget: (target: string) => void;
setImportedContent: (content: File | null) => void;
newCalParams: {
name: string;
setName: Function;
setName: (name: string) => void;
description: string;
setDescription: (d: string) => void;
color: Record<string, string>;
setColor: Function;
setColor: (color: Record<string, string>) => void;
visibility: "public" | "private";
setVisibility: Function;
setVisibility: (visibility: "public" | "private") => void;
};
}) {
const { t } = useI18n();
const [importMode, setImportMode] = useState<"file" | "url">("file");
const [importMode] = useState<"file" | "url">("file");
const [importFile, setImportFile] = useState<File | null>(null);
const [importUrl, setImportUrl] = useState("");
const calendars = useAppSelector((state) => state.calendars.list);
@@ -48,7 +48,7 @@ export function ImportTab({
useEffect(() => {
setImportedContent(importMode === "file" ? importFile : null);
}, [importFile, importUrl, importMode]);
}, [importFile, importUrl, importMode, setImportedContent]);
return (
<>
+5 -4
View File
@@ -13,19 +13,20 @@ import moment from "moment";
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
import { useEffect, useState } from "react";
import { useI18n } from "twake-i18n";
import { CalendarEvent } from "@/features/Events/EventsTypes";
import type { CalendarApi } from "@fullcalendar/core";
export function MiniCalendar({
calendarRef,
selectedDate,
setSelectedDate,
setSelectedMiniDate,
dottedEvents,
}: {
calendarRef: any;
calendarRef: React.MutableRefObject<CalendarApi | null>;
selectedDate: Date;
setSelectedDate: (d: Date) => void;
setSelectedMiniDate: (d: Date) => void;
dottedEvents: any[];
dottedEvents: CalendarEvent[];
}) {
const dispatch = useAppDispatch();
const calendars = useAppSelector((state) => state.calendars);
+3 -3
View File
@@ -27,13 +27,13 @@ export function SettingsTab({
calendar,
}: {
name: string;
setName: Function;
setName: (name: string) => void;
description: string;
setDescription: (d: string) => void;
color: Record<string, string>;
setColor: Function;
setColor: (color: Record<string, string>) => void;
visibility: "public" | "private";
setVisibility: Function;
setVisibility: (visibility: "public" | "private") => void;
calendar?: Calendar;
}) {
const { t } = useI18n();
@@ -22,7 +22,7 @@ export function TempCalendarsInput({
setTempUsers: (users: User[]) => void;
handleToggleEventPreview: () => void;
selectedCalendars: string[];
setSelectedCalendars: Function;
setSelectedCalendars: React.Dispatch<React.SetStateAction<string[]>>;
}) {
const dispatch = useAppDispatch();
const tempcalendars =
@@ -34,7 +34,7 @@ export function TempCalendarsInput({
const prevUsersRef = useRef<User[]>([]);
const userColorsRef = useRef(new Map<string, string>());
const handleUserChange = async (_: any, users: User[]) => {
const handleUserChange = async (_: React.SyntheticEvent, users: User[]) => {
setTempUsers(users);
const prevUsers = prevUsersRef.current;
@@ -13,7 +13,12 @@ import { CalendarEvent } from "@/features/Events/EventsTypes";
import { userAttendee } from "@/features/User/models/attendee";
import { createAttendee } from "@/features/User/models/attendee.mapper";
import { getDeltaInMilliseconds } from "@/utils/dateUtils";
import { CalendarApi, DateSelectArg, EventDropArg } from "@fullcalendar/core";
import {
CalendarApi,
DateSelectArg,
EventClickArg,
EventDropArg,
} from "@fullcalendar/core";
import { EventResizeDoneArg } from "@fullcalendar/interaction";
export interface EventHandlersProps {
@@ -27,7 +32,9 @@ export interface EventHandlersProps {
setEventDisplayedTemp: (temp: boolean) => void;
calendars: Record<string, Calendar>;
setSelectedEvent: (event: CalendarEvent) => void;
setAfterChoiceFunc: (func: Function) => void;
setAfterChoiceFunc: (
func: ((type: "solo" | "all" | undefined) => void) | undefined
) => void;
setOpenEditModePopup: (open: string) => void;
tempUsers: User[];
setTempEvent: (event: CalendarEvent) => void;
@@ -87,7 +94,7 @@ export const createEventHandlers = (props: EventHandlersProps) => {
setOpenEventDisplay(false);
};
const handleEventClick = (info: any) => {
const handleEventClick = (info: EventClickArg) => {
info.jsEvent.preventDefault();
if (info.event.url) {
@@ -1,5 +1,9 @@
interface CalendarHTMLElement extends HTMLElement {
__calendarMouseMoveHandler?: (e: MouseEvent) => void;
__calendarMouseLeaveHandler?: () => void;
}
export interface MouseHandlersProps {
calendarEl: HTMLElement;
calendarEl: CalendarHTMLElement;
}
export const createMouseHandlers = (props: MouseHandlersProps) => {
@@ -75,24 +79,24 @@ export const createMouseHandlers = (props: MouseHandlersProps) => {
calendarEl.addEventListener("mousemove", handleMouseMove);
calendarEl.addEventListener("mouseleave", handleMouseLeave);
(calendarEl as any).__calendarMouseMoveHandler = handleMouseMove;
(calendarEl as any).__calendarMouseLeaveHandler = handleMouseLeave;
calendarEl.__calendarMouseMoveHandler = handleMouseMove;
calendarEl.__calendarMouseLeaveHandler = handleMouseLeave;
};
const removeMouseEventListeners = () => {
if ((calendarEl as any).__calendarMouseMoveHandler) {
if (calendarEl.__calendarMouseMoveHandler) {
calendarEl.removeEventListener(
"mousemove",
(calendarEl as any).__calendarMouseMoveHandler
calendarEl.__calendarMouseMoveHandler
);
delete (calendarEl as any).__calendarMouseMoveHandler;
delete calendarEl.__calendarMouseMoveHandler;
}
if ((calendarEl as any).__calendarMouseLeaveHandler) {
if (calendarEl.__calendarMouseLeaveHandler) {
calendarEl.removeEventListener(
"mouseleave",
(calendarEl as any).__calendarMouseLeaveHandler
calendarEl.__calendarMouseLeaveHandler
);
delete (calendarEl as any).__calendarMouseLeaveHandler;
delete calendarEl.__calendarMouseLeaveHandler;
}
};
@@ -2,7 +2,14 @@ import { EventErrorHandler } from "@/components/Error/EventErrorHandler";
import { EventChip } from "@/components/Event/EventChip/EventChip";
import { Calendar } from "@/features/Calendars/CalendarTypes";
import { userAttendee } from "@/features/User/models/attendee";
import { CalendarApi, NowIndicatorContentArg } from "@fullcalendar/core";
import {
CalendarApi,
DayHeaderMountArg,
EventContentArg,
EventMountArg,
NowIndicatorContentArg,
ViewMountArg,
} from "@fullcalendar/core";
import React from "react";
import { createMouseHandlers } from "./mouseHandlers";
@@ -46,7 +53,7 @@ export const createViewHandlers = (props: ViewHandlersProps) => {
}
};
const handleDayHeaderDidMount = (arg: any) => {
const handleDayHeaderDidMount = (arg: DayHeaderMountArg) => {
if (arg.view.type === "timeGridWeek") {
const headerEl = arg.el;
@@ -61,22 +68,19 @@ export const createViewHandlers = (props: ViewHandlersProps) => {
};
headerEl.addEventListener("click", handleDayHeaderClick);
(headerEl as any).__dayHeaderClickHandler = handleDayHeaderClick;
headerEl.__dayHeaderClickHandler = handleDayHeaderClick;
}
};
const handleDayHeaderWillUnmount = (arg: any) => {
const handleDayHeaderWillUnmount = (arg: DayHeaderMountArg) => {
const headerEl = arg.el;
if ((headerEl as any).__dayHeaderClickHandler) {
headerEl.removeEventListener(
"click",
(headerEl as any).__dayHeaderClickHandler
);
delete (headerEl as any).__dayHeaderClickHandler;
if (headerEl.__dayHeaderClickHandler) {
headerEl.removeEventListener("click", headerEl.__dayHeaderClickHandler);
delete headerEl.__dayHeaderClickHandler;
}
};
const handleViewDidMount = (arg: any) => {
const handleViewDidMount = (arg: ViewMountArg) => {
if (arg.view.type === "timeGridWeek" || arg.view.type === "timeGridDay") {
const calendarEl = document.querySelector(".fc") as HTMLElement;
if (calendarEl) {
@@ -86,15 +90,15 @@ export const createViewHandlers = (props: ViewHandlersProps) => {
}
};
const handleViewWillUnmount = (arg: any) => {
if ((arg.el as any).__timeInterval) {
clearInterval((arg.el as any).__timeInterval);
delete (arg.el as any).__timeInterval;
const handleViewWillUnmount = (arg: ViewMountArg) => {
if (arg.el.__timeInterval) {
clearInterval(arg.el.__timeInterval);
delete arg.el.__timeInterval;
}
if ((arg.el as any).__timeObserver) {
(arg.el as any).__timeObserver.disconnect();
delete (arg.el as any).__timeObserver;
if (arg.el.__timeObserver) {
arg.el.__timeObserver.disconnect();
delete arg.el.__timeObserver;
}
const calendarEl = document.querySelector(".fc") as HTMLElement;
@@ -104,7 +108,7 @@ export const createViewHandlers = (props: ViewHandlersProps) => {
}
};
const handleEventContent = (arg: any) => {
const handleEventContent = (arg: EventContentArg) => {
return React.createElement(EventChip, {
arg,
calendars,
@@ -113,7 +117,7 @@ export const createViewHandlers = (props: ViewHandlersProps) => {
});
};
const handleEventDidMount = (arg: any) => {
const handleEventDidMount = (arg: EventMountArg) => {
const attendees = arg.event._def.extendedProps.attendee || [];
if (!calendars[arg.event._def.extendedProps.calId]) return;
const ownerEmails = new Set(
@@ -1,3 +1,4 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { useCallback } from "react";
import {
createEventHandlers,
@@ -1,3 +1,4 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { useCallback } from "react";
import {
createViewHandlers,
+14 -12
View File
@@ -1,12 +1,12 @@
import { refreshSingularCalendar } from "@/components/Event/utils/eventUtils";
import { AppDispatch } from "@/app/store";
import { Calendar } from "@/features/Calendars/CalendarTypes";
import { getCalendarDetailAsync } from "@/features/Calendars/services";
import { AclEntry } from "@/features/Calendars/types/CalendarData";
import { CalendarEvent } from "@/features/Events/EventsTypes";
import { formatDateToYYYYMMDDTHHMMSS } from "@/utils/dateUtils";
import { extractEventBaseUuid } from "@/utils/extractEventBaseUuid";
import { convertEventDateTimeToISO } from "@/utils/timezone";
import { SlotLabelContentArg } from "@fullcalendar/core";
import { ThunkDispatch } from "@reduxjs/toolkit";
import moment from "moment-timezone";
import { useI18n } from "twake-i18n";
@@ -56,7 +56,10 @@ export const checkIfCurrentWeekOrDay = (): boolean => {
return !!nowIndicator;
};
export function formatEventChipTitle(e: CalendarEvent, t: Function) {
export function formatEventChipTitle(
e: CalendarEvent,
t: (key: string) => string
) {
if (!e.title) {
return t("event.untitled");
}
@@ -72,6 +75,7 @@ export const eventToFullCalendarFormat = (
userAddress: string | undefined,
pending: boolean
) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const { t } = useI18n();
return filteredEvents
.concat(filteredTempEvents.map((e) => ({ ...e, temp: true })))
@@ -83,7 +87,11 @@ export const eventToFullCalendarFormat = (
: true; // if there are no organizer in the event we assume it was organized by the owner
const isPersonnalEvent =
extractEventBaseUuid(e.calId) === userId && isOrganiser;
const convertedEvent: any = {
const convertedEvent: CalendarEvent & {
colors: Record<string, string> | undefined;
editable: boolean;
priority: number;
} = {
...e,
title: formatEventChipTitle(e, t),
colors: e.color,
@@ -147,7 +155,7 @@ export const updateCalsDetails = (
pending: boolean,
rangeKey: string,
previousRangeKey: string,
dispatch: Function,
dispatch: AppDispatch,
calendarRange: { start: Date; end: Date },
calType?: "temp",
controllers?: Map<string, AbortController>
@@ -223,15 +231,9 @@ export const updateCalsDetails = (
}
};
interface AclEntry {
privilege: string;
principal: string;
protected: boolean;
}
export function getCalendarVisibility(acl: AclEntry[]): "private" | "public" {
let hasRead = false;
let hasFreeBusy = false;
// const hasFreeBusy = false;
if (acl) {
for (const entry of acl) {
if (entry.principal !== "{DAV:}authenticated") continue;
+1 -1
View File
@@ -158,7 +158,7 @@ function ResponsiveDialog({
}, [isExpanded]);
const handleClose = (
event: {},
event: unknown,
reason: "backdropClick" | "escapeKeyDown"
) => {
if (isExpanded && reason === "backdropClick") {
-3
View File
@@ -1,4 +1,3 @@
import { CalendarEvent } from "@/features/Events/EventsTypes";
import {
Button,
ButtonGroup,
@@ -16,12 +15,10 @@ import { useI18n } from "twake-i18n";
export function EditModeDialog({
type,
setOpen,
event,
eventAction,
}: {
type: string | null;
setOpen: (e: string | null) => void;
event: CalendarEvent;
eventAction: (type: "solo" | "all" | undefined) => void;
}) {
const { t } = useI18n();
@@ -1,4 +1,5 @@
import { EventErrorHandler } from "@/components/Error/EventErrorHandler";
import { EventContentArg } from "@fullcalendar/core";
import { Card, Typography } from "@linagora/twake-mui";
import { useEffect, useRef } from "react";
@@ -7,18 +8,30 @@ export function ErrorEventChip({
errorHandler,
error,
}: {
event: any;
event: EventContentArg["event"];
errorHandler: EventErrorHandler;
error: any;
error: unknown;
}) {
const hasReported = useRef(false);
useEffect(() => {
if (!hasReported.current) {
const message =
error instanceof Error
? error.message
: `${error.class} error during rendering ${event._def.extendedProps.uid || event.id}`;
let message: string;
if (error instanceof Error) {
message = error.message;
} else if (
typeof error === "object" &&
error !== null &&
"class" in error
) {
message = `${String(
(error as { class: string }).class
)} error during rendering ${event._def.extendedProps.uid || event.id}`;
} else {
message = `Unknown error during rendering ${event._def.extendedProps.uid || event.id}`;
}
errorHandler.reportError(
event._def.extendedProps.uid || event.id,
message
+2 -2
View File
@@ -59,7 +59,7 @@ export function EventChip({
const ownerEmails = new Set(
calendar.ownerEmails?.map((e) => e.toLowerCase())
);
const delegated = calendar.delegated;
// const delegated = calendar.delegated;
// Determine owner attendee
const ownerAttendee = getOwnerAttendee(attendees, ownerEmails);
@@ -258,7 +258,7 @@ export function EventChip({
!event._def.extendedProps.allday &&
event._def.extendedProps.organizer &&
!showCompact &&
(window as any).displayOrgAvatar && (
window.displayOrgAvatar && (
<Avatar
children={OrganizerAvatar.children}
color={OrganizerAvatar.color}
@@ -1,6 +1,7 @@
import { EventErrorHandler } from "@/components/Error/EventErrorHandler";
import { Calendar } from "@/features/Calendars/CalendarTypes";
import { userAttendee } from "@/features/User/models/attendee";
import { EventContentArg } from "@fullcalendar/core";
import { getContrastRatio } from "@linagora/twake-mui";
import CancelIcon from "@mui/icons-material/Cancel";
import HelpOutlineIcon from "@mui/icons-material/HelpOutline";
@@ -12,7 +13,7 @@ import { EVENT_DURATION } from "./EventChip";
const COMPACT_WIDTH_THRESHOLD = 100;
export interface EventChipProps {
arg: any;
arg: EventContentArg["event"];
calendars: Record<string, Calendar>;
tempcalendars: Record<string, Calendar>;
errorHandler: EventErrorHandler;
@@ -23,9 +24,9 @@ export interface IconDisplayConfig {
needAction: boolean;
private: boolean;
}
export function getEventDuration(event: any): number {
return moment(event._instance.range.end).diff(
moment(event._instance.range.start),
export function getEventDuration(event: EventContentArg["event"]): number {
return moment(event._instance?.range.end).diff(
moment(event._instance?.range.start),
"minutes"
);
}
@@ -34,7 +35,10 @@ export function getBestColor(colors: { light: string; dark: string }): string {
const contrastToLight = getContrastRatio(colors?.light, "#fff");
return contrastToDark > contrastToLight ? colors?.dark : colors?.light;
}
export function getEventTimes(event: any, timeZone: string) {
export function getEventTimes(
event: EventContentArg["event"],
timeZone: string
) {
return {
startTime: moment.tz(event.start, timeZone).format("HH:mm"),
endTime: moment.tz(event.end, timeZone).format("HH:mm"),
@@ -52,7 +56,7 @@ export function getOwnerAttendee(
export function getTitleStyle(
bestColor: string,
partstat?: string,
calendar?: any
calendar?: Calendar
): React.CSSProperties {
const baseStyle: React.CSSProperties = {
fontFamily: "Roboto",
@@ -122,7 +126,7 @@ export function getCardStyle(
bestColor: string,
eventLength: number,
partstat?: string,
calendar?: any
calendar?: Calendar
): React.CSSProperties {
const baseStyle: React.CSSProperties = getCardVariantStyle(
getEventVariant(eventLength),
-5
View File
@@ -1,14 +1,9 @@
import { CalendarEvent } from "@/features/Events/EventsTypes";
import { MenuItem } from "@linagora/twake-mui";
import { useI18n } from "twake-i18n";
export default function EventDuplication({
onClose,
event,
onOpenDuplicate,
}: {
onClose: Function;
event: CalendarEvent;
onOpenDuplicate?: () => void;
}) {
const { t } = useI18n();
+2 -6
View File
@@ -21,17 +21,15 @@ import {
ToggleButton,
ToggleButtonGroup,
Typography,
useTheme,
} from "@linagora/twake-mui";
import { alpha } from "@mui/material/styles";
import {
Close as DeleteIcon,
ContentCopy as CopyIcon,
Public as PublicIcon,
LocationOn as LocationIcon,
Public as PublicIcon,
} from "@mui/icons-material";
import SquareRoundedIcon from "@mui/icons-material/SquareRounded";
import LockOutlineIcon from "@mui/icons-material/LockOutline";
import SquareRoundedIcon from "@mui/icons-material/SquareRounded";
import React from "react";
import { useI18n } from "twake-i18n";
import AttendeeSelector from "../Attendees/AttendeeSearch";
@@ -163,7 +161,6 @@ export default function EventFormFields({
onHasEndDateChangedChange,
}: EventFormFieldsProps) {
const { t } = useI18n();
const theme = useTheme();
// Internal state for 4 separate fields
const [startDate, setStartDate] = React.useState("");
@@ -377,7 +374,6 @@ export default function EventFormFields({
showMore,
});
}, [
title,
startDate,
startTime,
endDate,
+1 -1
View File
@@ -25,7 +25,7 @@ export default function RepeatEvent({
}: {
repetition: RepetitionObject;
eventStart: Date;
setRepetition: Function;
setRepetition: (repetition: RepetitionObject) => void;
isOwn?: boolean;
}) {
const { t } = useI18n();
@@ -83,7 +83,6 @@ export const DateTimeFields: React.FC<DateTimeFieldsProps> = ({
showMore,
hasEndDateChanged,
showEndDate,
onToggleEndDate,
validation,
onStartDateChange,
onStartTimeChange,
@@ -88,7 +88,7 @@ export const DateTimeSummary: React.FC<DateTimeSummaryProps> = ({
const offset = getTimezoneOffset(tz, dateForOffset);
const tzName = tz.replace(/_/g, " ");
return `(${offset}) ${tzName}`;
} catch (error) {
} catch {
return tz.replace(/_/g, " ");
}
};
@@ -19,12 +19,12 @@ import { parseTimeInput } from "../utils/dateTimeHelpers";
type FieldType = "date" | "time" | "date-time";
type GenericPickerFieldProps = PickerFieldProps<any, any, any> & {
type GenericPickerFieldProps = PickerFieldProps<Dayjs, false, false> & {
fieldType: FieldType;
validator: (
value: any,
value: Dayjs | null,
context: PickerValidationScope,
adapter: PickerFieldAdapter<any>
adapter: PickerFieldAdapter<Dayjs>
) => string | null;
};
@@ -223,7 +223,7 @@ function EditableTimePickerField(props: GenericPickerFieldProps) {
dispatchCloseOtherPickers();
};
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
const handleBlur = (_e: React.FocusEvent<HTMLInputElement>) => {
// If dropdown is open, don't parse input
// MUI will handle selection and sync value via useEffect
if (pickerContext.open) {
@@ -12,15 +12,16 @@ import {
useValidation,
validateDate,
} from "@mui/x-date-pickers/validation";
import { Dayjs } from "dayjs";
type FieldType = "date" | "time" | "date-time";
type GenericPickerFieldProps = PickerFieldProps<any, any, any> & {
type GenericPickerFieldProps = PickerFieldProps<Dayjs, false, false> & {
fieldType: FieldType;
validator: (
value: any,
value: Dayjs | null,
context: PickerValidationScope,
adapter: PickerFieldAdapter<any>
adapter: PickerFieldAdapter<Dayjs>
) => string | null;
};
@@ -119,28 +119,18 @@ export async function handleRSVP(
export function handleDelete(
isRecurring: boolean,
typeOfAction: "solo" | "all" | undefined,
onClose: (event: {}, reason: "backdropClick" | "escapeKeyDown") => void,
dispatch: Function,
onClose: (event: unknown, reason: "backdropClick" | "escapeKeyDown") => void,
dispatch: AppDispatch,
calendar: Calendar,
event: CalendarEvent,
calId: string,
eventId: string
) {
if (isRecurring) {
onClose({}, "backdropClick");
if (typeOfAction === "solo") {
dispatch(deleteEventInstanceAsync({ cal: calendar, event }));
} else {
dispatch(
deleteEventAsync({
calId,
eventId,
eventURL: event.URL,
})
);
}
onClose({}, "backdropClick");
if (isRecurring && typeOfAction === "solo") {
dispatch(deleteEventInstanceAsync({ cal: calendar, event }));
} else {
onClose({}, "backdropClick");
dispatch(
deleteEventAsync({
calId,
+4 -2
View File
@@ -16,7 +16,7 @@ import CheckCircleIcon from "@mui/icons-material/CheckCircle";
export function renderAttendeeBadge(
a: userAttendee,
key: string,
t: Function,
t: (key: string) => string,
isFull?: boolean,
isOrganizer?: boolean
) {
@@ -110,7 +110,9 @@ export async function refreshCalendars(
) {
if (process.env.NODE_ENV === "test") return;
!calType && (await dispatch(getCalendarsListAsync()));
if (!calType) {
await dispatch(getCalendarsListAsync());
}
const results = await Promise.allSettled(
calendars.map((calendar) =>
+1 -1
View File
@@ -8,7 +8,7 @@ export function SnackbarAlert({
severity = "success",
}: {
open: boolean;
setOpen: Function;
setOpen: (o: boolean) => void;
message: string;
severity?: AlertColor;
}) {
+6 -4
View File
@@ -142,7 +142,7 @@ export default function SearchBar() {
setFilterError(false);
};
const handleContactSelect = (_event: any, contacts: User[]) => {
const handleContactSelect = (contacts: User[]) => {
setSelectedContacts(contacts);
setSearch("");
if (contacts.length > 0) {
@@ -173,7 +173,9 @@ export default function SearchBar() {
dispatch(setView("search"));
setAnchorEl(null);
} else {
filterOpen && setFilterError(true);
if (filterOpen) {
setFilterError(true);
}
}
};
@@ -222,8 +224,8 @@ export default function SearchBar() {
{extended && (
<PeopleSearch
selectedUsers={selectedContacts}
onChange={(event, users) => {
handleContactSelect(event, users);
onChange={(_event, users) => {
handleContactSelect(users);
}}
objectTypes={["user", "contact"]}
onToggleEventPreview={() => {}}
+2 -2
View File
@@ -56,9 +56,9 @@ export function Menubar({
currentView,
onViewChange,
}: MenubarProps) {
const { t, lang } = useI18n(); // deliberately NOT using f()
const { t } = useI18n(); // deliberately NOT using f()
const user = useAppSelector((state) => state.user.userData);
const applist: AppIconProps[] = (window as any).appList ?? [];
const applist: AppIconProps[] = window.appList ?? [];
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const [userMenuAnchorEl, setUserMenuAnchorEl] = useState<null | HTMLElement>(
null