* #708 apply strictier linting rules and fix simple eslint bugs * #708 fix eslint errors relate to promise * #708 fix eslint import/no-extraneous-dependencies * #708 fix eslint errors of react-hook * #708 enable eslint check for typescript --------- Co-authored-by: lethemanh <lethemanh@lethemanhs-MacBook-Pro.local>
This commit is contained in:
@@ -1,195 +1,199 @@
|
||||
import { useAppDispatch, useAppSelector } from "@/app/hooks";
|
||||
import EventPopover from "@/features/Events/EventModal";
|
||||
import EventPreviewModal from "@/features/Events/EventPreview";
|
||||
import { CalendarEvent } from "@/features/Events/EventsTypes";
|
||||
import ImportAlert from "@/features/Events/ImportAlert";
|
||||
import SearchResultsPage from "@/features/Search/SearchResultsPage";
|
||||
import { setTimeZone } from "@/features/Settings/SettingsSlice";
|
||||
import { setDisplayedDateAndRange } from "@/utils/CalendarRangeManager";
|
||||
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";
|
||||
import viLocale from "@fullcalendar/core/locales/vi";
|
||||
import dayGridPlugin from "@fullcalendar/daygrid";
|
||||
import interactionPlugin from "@fullcalendar/interaction";
|
||||
import momentTimezonePlugin from "@fullcalendar/moment-timezone";
|
||||
import FullCalendar from "@fullcalendar/react";
|
||||
import timeGridPlugin from "@fullcalendar/timegrid";
|
||||
import { Box, Button, radius } from "@linagora/twake-mui";
|
||||
import AddIcon from "@mui/icons-material/Add";
|
||||
import moment from "moment-timezone";
|
||||
import { MutableRefObject, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useI18n } from "twake-i18n";
|
||||
import { useCalendarDataLoader } from "../../features/Calendars/useCalendarLoader";
|
||||
import { User } from "../Attendees/PeopleSearch";
|
||||
import { EventErrorSnackbar } from "../Error/ErrorSnackbar";
|
||||
import { EventErrorHandler } from "../Error/EventErrorHandler";
|
||||
import { EditModeDialog } from "../Event/EditModeDialog";
|
||||
import { Menubar, MenubarProps } from "../Menubar/Menubar";
|
||||
import "./Calendar.styl";
|
||||
import CalendarSelection from "./CalendarSelection";
|
||||
import "./CustomCalendar.styl";
|
||||
import { useCalendarEventHandlers } from "./hooks/useCalendarEventHandlers";
|
||||
import { useCalendarViewHandlers } from "./hooks/useCalendarViewHandlers";
|
||||
import { MiniCalendar } from "./MiniCalendar";
|
||||
import { TempCalendarsInput } from "./TempCalendarsInput";
|
||||
import { TimezoneSelector } from "./TimezoneSelector";
|
||||
import { useAppDispatch, useAppSelector } from '@/app/hooks'
|
||||
import EventPopover from '@/features/Events/EventModal'
|
||||
import EventPreviewModal from '@/features/Events/EventPreview'
|
||||
import { CalendarEvent } from '@/features/Events/EventsTypes'
|
||||
import ImportAlert from '@/features/Events/ImportAlert'
|
||||
import SearchResultsPage from '@/features/Search/SearchResultsPage'
|
||||
import { setTimeZone } from '@/features/Settings/SettingsSlice'
|
||||
import { setDisplayedDateAndRange } from '@/utils/CalendarRangeManager'
|
||||
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'
|
||||
import viLocale from '@fullcalendar/core/locales/vi'
|
||||
import dayGridPlugin from '@fullcalendar/daygrid'
|
||||
import interactionPlugin from '@fullcalendar/interaction'
|
||||
import momentTimezonePlugin from '@fullcalendar/moment-timezone'
|
||||
import FullCalendar from '@fullcalendar/react'
|
||||
import timeGridPlugin from '@fullcalendar/timegrid'
|
||||
import { Box, Button, radius } from '@linagora/twake-mui'
|
||||
import AddIcon from '@mui/icons-material/Add'
|
||||
import moment from 'moment-timezone'
|
||||
import { MutableRefObject, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useI18n } from 'twake-i18n'
|
||||
import { useCalendarDataLoader } from '../../features/Calendars/useCalendarLoader'
|
||||
import { User } from '../Attendees/PeopleSearch'
|
||||
import { EventErrorSnackbar } from '../Error/ErrorSnackbar'
|
||||
import { EventErrorHandler } from '../Error/EventErrorHandler'
|
||||
import { EditModeDialog } from '../Event/EditModeDialog'
|
||||
import { Menubar, MenubarProps } from '../Menubar/Menubar'
|
||||
import './Calendar.styl'
|
||||
import CalendarSelection from './CalendarSelection'
|
||||
import './CustomCalendar.styl'
|
||||
import { useCalendarEventHandlers } from './hooks/useCalendarEventHandlers'
|
||||
import { useCalendarViewHandlers } from './hooks/useCalendarViewHandlers'
|
||||
import { MiniCalendar } from './MiniCalendar'
|
||||
import { TempCalendarsInput } from './TempCalendarsInput'
|
||||
import { TimezoneSelector } from './TimezoneSelector'
|
||||
import {
|
||||
eventToFullCalendarFormat,
|
||||
extractEvents,
|
||||
updateSlotLabelVisibility,
|
||||
} from "./utils/calendarUtils";
|
||||
updateSlotLabelVisibility
|
||||
} from './utils/calendarUtils'
|
||||
|
||||
const localeMap: Record<string, LocaleInput | undefined> = {
|
||||
fr: frLocale,
|
||||
ru: ruLocale,
|
||||
vi: viLocale,
|
||||
en: undefined,
|
||||
};
|
||||
en: undefined
|
||||
}
|
||||
|
||||
interface CalendarAppProps {
|
||||
calendarRef: MutableRefObject<CalendarApi | null>;
|
||||
onDateChange?: (date: Date) => void;
|
||||
onViewChange?: (view: string) => void;
|
||||
menubarProps?: MenubarProps;
|
||||
calendarRef: MutableRefObject<CalendarApi | null>
|
||||
onDateChange?: (date: Date) => void
|
||||
onViewChange?: (view: string) => void
|
||||
menubarProps?: MenubarProps
|
||||
}
|
||||
|
||||
export default function CalendarApp({
|
||||
calendarRef,
|
||||
onDateChange,
|
||||
onViewChange,
|
||||
menubarProps,
|
||||
menubarProps
|
||||
}: CalendarAppProps) {
|
||||
const [selectedDate, setSelectedDate] = useState(new Date());
|
||||
const [debouncedDate, setDebouncedDate] = useState(new Date());
|
||||
const [selectedDate, setSelectedDate] = useState(new Date())
|
||||
const [debouncedDate, setDebouncedDate] = useState(new Date())
|
||||
useEffect(() => {
|
||||
const t = setTimeout(() => setDebouncedDate(selectedDate), 300);
|
||||
return () => clearTimeout(t);
|
||||
}, [selectedDate]);
|
||||
const [selectedMiniDate, setSelectedMiniDate] = useState(new Date());
|
||||
const userId =
|
||||
useAppSelector((state) => state.user.userData?.openpaasId) ?? "";
|
||||
const dispatch = useAppDispatch();
|
||||
const view = useAppSelector((state) => state.settings.view);
|
||||
const userData = useAppSelector((state) => state.user.userData);
|
||||
const t = setTimeout(() => setDebouncedDate(selectedDate), 300)
|
||||
return () => clearTimeout(t)
|
||||
}, [selectedDate])
|
||||
const [selectedMiniDate, setSelectedMiniDate] = useState(new Date())
|
||||
const userId = useAppSelector(state => state.user.userData?.openpaasId) ?? ''
|
||||
const dispatch = useAppDispatch()
|
||||
const view = useAppSelector(state => state.settings.view)
|
||||
const userData = useAppSelector(state => state.user.userData)
|
||||
const workingDays = useAppSelector(
|
||||
(state) => state.settings.businessHours?.daysOfWeek
|
||||
);
|
||||
const hideWorkingDays = useAppSelector((state) => state.settings.workingDays);
|
||||
state => state.settings.businessHours?.daysOfWeek
|
||||
)
|
||||
const hideWorkingDays = useAppSelector(state => state.settings.workingDays)
|
||||
|
||||
const hideDeclinedEvents = useAppSelector(
|
||||
(state) => state.settings.hideDeclinedEvents
|
||||
);
|
||||
state => state.settings.hideDeclinedEvents
|
||||
)
|
||||
const hiddenDays = useMemo(() => {
|
||||
if (!hideWorkingDays || !workingDays || workingDays.length === 0) return [];
|
||||
const validWorkingDays = workingDays.filter((d) => d >= 0 && d <= 6);
|
||||
if (validWorkingDays.length === 0) return [];
|
||||
return [0, 1, 2, 3, 4, 5, 6].filter((d) => !validWorkingDays.includes(d));
|
||||
}, [hideWorkingDays, workingDays]);
|
||||
if (!hideWorkingDays || !workingDays || workingDays.length === 0) return []
|
||||
const validWorkingDays = workingDays.filter(d => d >= 0 && d <= 6)
|
||||
if (validWorkingDays.length === 0) return []
|
||||
return [0, 1, 2, 3, 4, 5, 6].filter(d => !validWorkingDays.includes(d))
|
||||
}, [hideWorkingDays, workingDays])
|
||||
|
||||
const calendars = useAppSelector((state) => state.calendars.list);
|
||||
const isPending = useAppSelector((state) => state.calendars.pending);
|
||||
const calendars = useAppSelector(state => state.calendars.list)
|
||||
const isPending = useAppSelector(state => state.calendars.pending)
|
||||
const displayWeekNumbers = useAppSelector(
|
||||
(state) => state.settings.displayWeekNumbers
|
||||
);
|
||||
const tempcalendars = useAppSelector((state) => state.calendars.templist);
|
||||
const storedCalendars = useSelectedCalendars();
|
||||
state => state.settings.displayWeekNumbers
|
||||
)
|
||||
const tempcalendars = useAppSelector(state => state.calendars.templist)
|
||||
const storedCalendars = useSelectedCalendars()
|
||||
const [selectedCalendars, setSelectedCalendars] =
|
||||
useState<string[]>(storedCalendars);
|
||||
useState<string[]>(storedCalendars)
|
||||
|
||||
const calendarIdsString = useMemo(
|
||||
() =>
|
||||
Object.keys(calendars || {})
|
||||
.sort()
|
||||
.join(","),
|
||||
.join(','),
|
||||
[calendars]
|
||||
);
|
||||
)
|
||||
const calendarIds = useMemo(
|
||||
() => (calendarIdsString ? calendarIdsString.split(",") : []),
|
||||
() => (calendarIdsString ? calendarIdsString.split(',') : []),
|
||||
[calendarIdsString]
|
||||
);
|
||||
)
|
||||
|
||||
const [currentView, setCurrentView] = useState("timeGridWeek");
|
||||
const [currentView, setCurrentView] = useState('timeGridWeek')
|
||||
const timezone =
|
||||
useAppSelector((state) => state.settings.timeZone) ??
|
||||
browserDefaultTimeZone;
|
||||
useAppSelector(state => state.settings.timeZone) ?? browserDefaultTimeZone
|
||||
|
||||
// Auto-select personal calendars when first loaded
|
||||
const initialLoadRef = useRef(true);
|
||||
const [eventErrors, setEventErrors] = useState<string[]>([]);
|
||||
const errorHandler = useRef(new EventErrorHandler());
|
||||
const initialLoadRef = useRef(true)
|
||||
const [eventErrors, setEventErrors] = useState<string[]>([])
|
||||
const errorHandler = useRef(new EventErrorHandler())
|
||||
|
||||
useEffect(() => {
|
||||
const handler = errorHandler.current;
|
||||
handler.setErrorCallback(setEventErrors);
|
||||
return () => handler.setErrorCallback(() => {});
|
||||
}, []);
|
||||
const handler = errorHandler.current
|
||||
handler.setErrorCallback(setEventErrors)
|
||||
return () => handler.setErrorCallback(() => {})
|
||||
}, [])
|
||||
|
||||
const handleErrorClose = () => {
|
||||
setEventErrors([]);
|
||||
errorHandler.current.clearAll();
|
||||
};
|
||||
setEventErrors([])
|
||||
errorHandler.current.clearAll()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (initialLoadRef.current && calendarIds.length > 0 && userId) {
|
||||
const cached = localStorage.getItem("selectedCalendars");
|
||||
if (cached && cached.length > 0) {
|
||||
const parsed = JSON.parse(cached) as string[];
|
||||
const valid = parsed.filter((id) => calendars[id]);
|
||||
setSelectedCalendars(valid);
|
||||
} else {
|
||||
const personalCalendarIds = calendarIds.filter(
|
||||
(id) => extractEventBaseUuid(id) === userId
|
||||
);
|
||||
setSelectedCalendars(personalCalendarIds);
|
||||
const updateSelectedCalendars = () => {
|
||||
if (initialLoadRef.current && calendarIds.length > 0 && userId) {
|
||||
const cached = localStorage.getItem('selectedCalendars')
|
||||
if (cached && cached.length > 0) {
|
||||
const parsed = JSON.parse(cached) as string[]
|
||||
const valid = parsed.filter(id => calendars[id])
|
||||
setSelectedCalendars(valid)
|
||||
} else {
|
||||
const personalCalendarIds = calendarIds.filter(
|
||||
id => extractEventBaseUuid(id) === userId
|
||||
)
|
||||
setSelectedCalendars(personalCalendarIds)
|
||||
}
|
||||
initialLoadRef.current = false
|
||||
}
|
||||
initialLoadRef.current = false;
|
||||
}
|
||||
}, [calendarIds, calendars, userId]);
|
||||
updateSelectedCalendars()
|
||||
}, [calendarIds, calendars, userId])
|
||||
|
||||
// Save selected cals to cache
|
||||
useEffect(() => {
|
||||
if (calendarIds.length > 0) {
|
||||
setSelectedCalendarsToStorage(selectedCalendars);
|
||||
setSelectedCalendarsToStorage(selectedCalendars)
|
||||
}
|
||||
}, [selectedCalendars, calendarIds.length]);
|
||||
}, [selectedCalendars, calendarIds.length])
|
||||
|
||||
useEffect(() => {
|
||||
if (calendarIds.length === 0) return;
|
||||
const validCalendarIds = new Set(calendarIds);
|
||||
setSelectedCalendars((prev) => {
|
||||
const filtered = prev.filter((calId) => validCalendarIds.has(calId));
|
||||
if (filtered.length === prev.length) {
|
||||
const unchanged = filtered.every((id, index) => id === prev[index]);
|
||||
if (unchanged) {
|
||||
return prev;
|
||||
const updateSelectedCalendarsOnCalendarChange = () => {
|
||||
if (calendarIds.length === 0) return
|
||||
const validCalendarIds = new Set(calendarIds)
|
||||
setSelectedCalendars(prev => {
|
||||
const filtered = prev.filter(calId => validCalendarIds.has(calId))
|
||||
if (filtered.length === prev.length) {
|
||||
const unchanged = filtered.every((id, index) => id === prev[index])
|
||||
if (unchanged) {
|
||||
return prev
|
||||
}
|
||||
}
|
||||
}
|
||||
return filtered;
|
||||
});
|
||||
}, [calendarIds]);
|
||||
return filtered
|
||||
})
|
||||
}
|
||||
updateSelectedCalendarsOnCalendarChange()
|
||||
}, [calendarIds])
|
||||
|
||||
const sortedSelectedCalendars = useMemo(
|
||||
() => [...selectedCalendars].sort(),
|
||||
[selectedCalendars]
|
||||
);
|
||||
)
|
||||
|
||||
const tempCalendarIdsString = useMemo(
|
||||
() =>
|
||||
Object.keys(tempcalendars || {})
|
||||
.sort()
|
||||
.join(","),
|
||||
.join(','),
|
||||
[tempcalendars]
|
||||
);
|
||||
)
|
||||
const tempCalendarIds = useMemo(
|
||||
() => (tempCalendarIdsString ? tempCalendarIdsString.split(",") : []),
|
||||
() => (tempCalendarIdsString ? tempCalendarIdsString.split(',') : []),
|
||||
[tempCalendarIdsString]
|
||||
);
|
||||
)
|
||||
|
||||
useCalendarDataLoader({
|
||||
selectedDate: debouncedDate,
|
||||
@@ -198,47 +202,47 @@ export default function CalendarApp({
|
||||
sortedSelectedCalendars,
|
||||
calendarIds,
|
||||
calendarIdsString,
|
||||
tempCalendarIds,
|
||||
});
|
||||
tempCalendarIds
|
||||
})
|
||||
|
||||
const filteredEvents: CalendarEvent[] = extractEvents(
|
||||
selectedCalendars,
|
||||
calendars || {},
|
||||
userData?.email,
|
||||
hideDeclinedEvents
|
||||
);
|
||||
)
|
||||
|
||||
const filteredTempEvents: CalendarEvent[] = extractEvents(
|
||||
tempCalendarIds,
|
||||
tempcalendars || {},
|
||||
userData?.email,
|
||||
hideDeclinedEvents
|
||||
);
|
||||
)
|
||||
|
||||
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
|
||||
const [openEventDisplay, setOpenEventDisplay] = useState(false);
|
||||
const [eventDisplayedId, setEventDisplayedId] = useState("");
|
||||
const [eventDisplayedTemp, setEventDisplayedTemp] = useState(false);
|
||||
const [eventDisplayedCalId, setEventDisplayedCalId] = useState("");
|
||||
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null)
|
||||
const [openEventDisplay, setOpenEventDisplay] = useState(false)
|
||||
const [eventDisplayedId, setEventDisplayedId] = useState('')
|
||||
const [eventDisplayedTemp, setEventDisplayedTemp] = useState(false)
|
||||
const [eventDisplayedCalId, setEventDisplayedCalId] = useState('')
|
||||
|
||||
// Listen for eventModalError event to reopen modal on API failure
|
||||
useEffect(() => {
|
||||
const handleEventModalError = (event: CustomEvent) => {
|
||||
if (event.detail?.type === "create") {
|
||||
if (event.detail?.type === 'create') {
|
||||
// Reopen create event modal
|
||||
setAnchorEl(document.body);
|
||||
} else if (event.detail?.type === "update") {
|
||||
setAnchorEl(document.body)
|
||||
} else if (event.detail?.type === 'update') {
|
||||
// Store update modal info to sessionStorage for EventDisplayPreview to pick up
|
||||
try {
|
||||
sessionStorage.setItem(
|
||||
"eventUpdateModalReopen",
|
||||
'eventUpdateModalReopen',
|
||||
JSON.stringify({
|
||||
eventId: event.detail.eventId,
|
||||
calId: event.detail.calId,
|
||||
typeOfAction: event.detail.typeOfAction,
|
||||
timestamp: Date.now(),
|
||||
timestamp: Date.now()
|
||||
})
|
||||
);
|
||||
)
|
||||
|
||||
// Open EventDisplayPreview if it's not already open with matching event, so it can pick up the sessionStorage
|
||||
if (
|
||||
@@ -246,56 +250,52 @@ export default function CalendarApp({
|
||||
eventDisplayedId !== event.detail.eventId ||
|
||||
eventDisplayedCalId !== event.detail.calId
|
||||
) {
|
||||
setEventDisplayedId(event.detail.eventId);
|
||||
setEventDisplayedCalId(event.detail.calId);
|
||||
setEventDisplayedTemp(false);
|
||||
setOpenEventDisplay(true);
|
||||
setEventDisplayedId(event.detail.eventId)
|
||||
setEventDisplayedCalId(event.detail.calId)
|
||||
setEventDisplayedTemp(false)
|
||||
setOpenEventDisplay(true)
|
||||
} else {
|
||||
// If EventDisplayPreview is already open, trigger reopen by dispatching a custom event
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("eventUpdateModalReopen", {
|
||||
new CustomEvent('eventUpdateModalReopen', {
|
||||
detail: {
|
||||
eventId: event.detail.eventId,
|
||||
calId: event.detail.calId,
|
||||
typeOfAction: event.detail.typeOfAction,
|
||||
},
|
||||
typeOfAction: event.detail.typeOfAction
|
||||
}
|
||||
})
|
||||
);
|
||||
)
|
||||
}
|
||||
} catch {
|
||||
// Ignore sessionStorage errors
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
window.addEventListener(
|
||||
"eventModalError",
|
||||
'eventModalError',
|
||||
handleEventModalError as EventListener
|
||||
);
|
||||
)
|
||||
return () => {
|
||||
window.removeEventListener(
|
||||
"eventModalError",
|
||||
'eventModalError',
|
||||
handleEventModalError as EventListener
|
||||
);
|
||||
};
|
||||
}, [openEventDisplay, eventDisplayedId, eventDisplayedCalId]);
|
||||
)
|
||||
}
|
||||
}, [openEventDisplay, eventDisplayedId, eventDisplayedCalId])
|
||||
|
||||
const [openEditModePopup, setOpenEditModePopup] = useState<string | null>(
|
||||
null
|
||||
);
|
||||
const [, setTypeOfAction] = useState<"solo" | "all" | undefined>(undefined);
|
||||
)
|
||||
const [, setTypeOfAction] = useState<'solo' | 'all' | undefined>(undefined)
|
||||
const [afterChoiceFunc, setAfterChoiceFunc] = useState<
|
||||
((type: "solo" | "all" | undefined) => void) | undefined
|
||||
>();
|
||||
const [, setSelectedEvent] = useState<CalendarEvent>({} as CalendarEvent);
|
||||
const [selectedRange, setSelectedRange] = useState<DateSelectArg | null>(
|
||||
null
|
||||
);
|
||||
((type: 'solo' | 'all' | undefined) => void) | undefined
|
||||
>()
|
||||
const [, setSelectedEvent] = useState<CalendarEvent>({} as CalendarEvent)
|
||||
const [selectedRange, setSelectedRange] = useState<DateSelectArg | null>(null)
|
||||
|
||||
const [tempUsers, setTempUsers] = useState<User[]>([]);
|
||||
const [tempEvent, setTempEvent] = useState<CalendarEvent>(
|
||||
{} as CalendarEvent
|
||||
);
|
||||
const [tempUsers, setTempUsers] = useState<User[]>([])
|
||||
const [tempEvent, setTempEvent] = useState<CalendarEvent>({} as CalendarEvent)
|
||||
|
||||
// Event handlers
|
||||
const eventHandlers = useCalendarEventHandlers({
|
||||
@@ -313,8 +313,8 @@ export default function CalendarApp({
|
||||
setOpenEditModePopup,
|
||||
tempUsers,
|
||||
setTempEvent,
|
||||
timezone,
|
||||
});
|
||||
timezone
|
||||
})
|
||||
|
||||
// View handlers
|
||||
const viewHandlers = useCalendarViewHandlers({
|
||||
@@ -324,18 +324,22 @@ export default function CalendarApp({
|
||||
onViewChange,
|
||||
calendars,
|
||||
tempcalendars,
|
||||
errorHandler: errorHandler.current,
|
||||
});
|
||||
// Note: To preserve current logic, this will temporarily disable eslint for react-hooks/refs
|
||||
// eslint-disable-next-line react-hooks/refs
|
||||
errorHandler: errorHandler.current
|
||||
})
|
||||
|
||||
if (process.env.NODE_ENV === "test") {
|
||||
window.__calendarRef = calendarRef;
|
||||
}
|
||||
useEffect(() => {
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
window.__calendarRef = calendarRef
|
||||
}
|
||||
}, [calendarRef])
|
||||
|
||||
const { t, lang } = useI18n();
|
||||
const { t, lang } = useI18n()
|
||||
|
||||
return (
|
||||
<main
|
||||
className={`main-layout calendar-layout ${menubarProps?.isIframe ? " isInIframe" : ""}`}
|
||||
className={`main-layout calendar-layout ${menubarProps?.isIframe ? ' isInIframe' : ''}`}
|
||||
>
|
||||
<Box
|
||||
className="sidebar"
|
||||
@@ -344,16 +348,16 @@ export default function CalendarApp({
|
||||
paddingBottom: 3,
|
||||
paddingLeft: 3,
|
||||
paddingRight: 2,
|
||||
width: "270px",
|
||||
width: '270px'
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
position: "sticky",
|
||||
position: 'sticky',
|
||||
top: 0,
|
||||
zIndex: 10,
|
||||
backgroundColor: "#fff",
|
||||
paddingTop: menubarProps?.isIframe ? "10px" : 3,
|
||||
backgroundColor: '#fff',
|
||||
paddingTop: menubarProps?.isIframe ? '10px' : 3
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
@@ -365,13 +369,13 @@ export default function CalendarApp({
|
||||
}
|
||||
sx={{
|
||||
borderRadius: radius.lg,
|
||||
fontSize: "16px",
|
||||
fontSize: '16px',
|
||||
fontWeight: 500,
|
||||
lineHeight: "normal",
|
||||
lineHeight: 'normal'
|
||||
}}
|
||||
>
|
||||
<AddIcon sx={{ marginRight: 0.5, fontSize: "20px" }} />{" "}
|
||||
{t("event.createEvent")}
|
||||
<AddIcon sx={{ marginRight: 0.5, fontSize: '20px' }} />{' '}
|
||||
{t('event.createEvent')}
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
@@ -385,7 +389,7 @@ export default function CalendarApp({
|
||||
tempUsers={tempUsers}
|
||||
setTempUsers={setTempUsers}
|
||||
handleToggleEventPreview={() => {
|
||||
eventHandlers.handleDateSelect(null as unknown as DateSelectArg);
|
||||
eventHandlers.handleDateSelect(null as unknown as DateSelectArg)
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
@@ -399,19 +403,19 @@ export default function CalendarApp({
|
||||
<div className="calendar">
|
||||
<ImportAlert />
|
||||
{menubarProps?.isIframe && <Menubar {...menubarProps} />}
|
||||
{view === "calendar" && (
|
||||
{view === 'calendar' && (
|
||||
<FullCalendar
|
||||
key={hiddenDays.join(",")}
|
||||
ref={(ref) => {
|
||||
key={hiddenDays.join(',')}
|
||||
ref={ref => {
|
||||
if (ref) {
|
||||
calendarRef.current = ref.getApi();
|
||||
calendarRef.current = ref.getApi()
|
||||
}
|
||||
}}
|
||||
plugins={[
|
||||
dayGridPlugin,
|
||||
timeGridPlugin,
|
||||
interactionPlugin,
|
||||
momentTimezonePlugin,
|
||||
momentTimezonePlugin
|
||||
]}
|
||||
initialView="timeGridWeek"
|
||||
firstDay={1}
|
||||
@@ -420,16 +424,16 @@ export default function CalendarApp({
|
||||
hiddenDays={hiddenDays}
|
||||
selectable={true}
|
||||
timeZone={timezone}
|
||||
height={"100%"}
|
||||
height="100%"
|
||||
select={eventHandlers.handleDateSelect}
|
||||
nowIndicator
|
||||
slotLabelClassNames={(arg) => [
|
||||
updateSlotLabelVisibility(new Date(), arg, timezone),
|
||||
slotLabelClassNames={arg => [
|
||||
updateSlotLabelVisibility(new Date(), arg, timezone)
|
||||
]}
|
||||
nowIndicatorContent={viewHandlers.handleNowIndicatorContent}
|
||||
headerToolbar={false}
|
||||
views={{
|
||||
timeGridWeek: { titleFormat: { month: "long", year: "numeric" } },
|
||||
timeGridWeek: { titleFormat: { month: 'long', year: 'numeric' } }
|
||||
}}
|
||||
dayMaxEvents={true}
|
||||
events={eventToFullCalendarFormat(
|
||||
@@ -444,15 +448,15 @@ export default function CalendarApp({
|
||||
a.extendedProps.priority - b.extendedProps.priority
|
||||
}
|
||||
weekNumbers={
|
||||
currentView === "timeGridWeek" || currentView === "timeGridDay"
|
||||
currentView === 'timeGridWeek' || currentView === 'timeGridDay'
|
||||
}
|
||||
weekNumberFormat={{ week: "long" }}
|
||||
weekNumberContent={(arg) => {
|
||||
weekNumberFormat={{ week: 'long' }}
|
||||
weekNumberContent={arg => {
|
||||
return (
|
||||
<div className="weekSelector">
|
||||
{displayWeekNumbers && (
|
||||
<div>
|
||||
{t("menubar.views.week")} {arg.num}
|
||||
{t('menubar.views.week')} {arg.num}
|
||||
</div>
|
||||
)}
|
||||
<TimezoneSelector
|
||||
@@ -463,91 +467,91 @@ export default function CalendarApp({
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}}
|
||||
dayCellContent={(arg) => {
|
||||
const month = arg.date.toLocaleDateString(t("locale"), {
|
||||
month: "short",
|
||||
timeZone: timezone,
|
||||
});
|
||||
if (arg.view.type === "dayGridMonth") {
|
||||
dayCellContent={arg => {
|
||||
const month = arg.date.toLocaleDateString(t('locale'), {
|
||||
month: 'short',
|
||||
timeZone: timezone
|
||||
})
|
||||
if (arg.view.type === 'dayGridMonth') {
|
||||
return (
|
||||
<span
|
||||
className={`fc-daygrid-day-number ${
|
||||
arg.isToday ? "current-date" : ""
|
||||
arg.isToday ? 'current-date' : ''
|
||||
}`}
|
||||
>
|
||||
{arg.dayNumberText === "1" ? month : ""} {arg.dayNumberText}
|
||||
{arg.dayNumberText === '1' ? month : ''} {arg.dayNumberText}
|
||||
</span>
|
||||
);
|
||||
)
|
||||
}
|
||||
}}
|
||||
slotDuration={"00:30:00"}
|
||||
slotLabelInterval={"01:00:00"}
|
||||
slotDuration="00:30:00"
|
||||
slotLabelInterval="01:00:00"
|
||||
scrollTime="12:00:00"
|
||||
unselectAuto={false}
|
||||
allDayText=""
|
||||
slotLabelFormat={{
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: false,
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
}}
|
||||
datesSet={(arg) => {
|
||||
setCurrentView(arg.view.type);
|
||||
datesSet={arg => {
|
||||
setCurrentView(arg.view.type)
|
||||
const calendarCurrentDate =
|
||||
calendarRef.current?.getDate() || new Date(arg.start);
|
||||
setDisplayedDateAndRange(calendarCurrentDate);
|
||||
calendarRef.current?.getDate() || new Date(arg.start)
|
||||
setDisplayedDateAndRange(calendarCurrentDate)
|
||||
|
||||
if (arg.view.type === "dayGridMonth") {
|
||||
const start = new Date(arg.start).getTime();
|
||||
const end = new Date(arg.end).getTime();
|
||||
const middle = start + (end - start) / 2;
|
||||
setSelectedDate(new Date(middle));
|
||||
setSelectedMiniDate(calendarCurrentDate);
|
||||
if (arg.view.type === 'dayGridMonth') {
|
||||
const start = new Date(arg.start).getTime()
|
||||
const end = new Date(arg.end).getTime()
|
||||
const middle = start + (end - start) / 2
|
||||
setSelectedDate(new Date(middle))
|
||||
setSelectedMiniDate(calendarCurrentDate)
|
||||
} else {
|
||||
setSelectedDate(calendarCurrentDate);
|
||||
setSelectedMiniDate(calendarCurrentDate);
|
||||
setSelectedDate(calendarCurrentDate)
|
||||
setSelectedMiniDate(calendarCurrentDate)
|
||||
}
|
||||
|
||||
// Always use the calendar's current date for consistency
|
||||
if (onDateChange) {
|
||||
onDateChange(calendarCurrentDate);
|
||||
onDateChange(calendarCurrentDate)
|
||||
}
|
||||
|
||||
// Notify parent about view change
|
||||
if (onViewChange) {
|
||||
onViewChange(arg.view.type);
|
||||
onViewChange(arg.view.type)
|
||||
}
|
||||
|
||||
// Update slot label visibility when view changes
|
||||
setTimeout(() => {
|
||||
updateSlotLabelVisibility(new Date());
|
||||
}, 100);
|
||||
updateSlotLabelVisibility(new Date())
|
||||
}, 100)
|
||||
}}
|
||||
dayHeaderContent={(arg) => {
|
||||
const m = moment.tz(arg.date, timezone);
|
||||
dayHeaderContent={arg => {
|
||||
const m = moment.tz(arg.date, timezone)
|
||||
|
||||
const date = m.date();
|
||||
const date = m.date()
|
||||
const weekDay = m
|
||||
.toDate()
|
||||
.toLocaleDateString(t("locale"), {
|
||||
weekday: "short",
|
||||
timeZone: timezone,
|
||||
.toLocaleDateString(t('locale'), {
|
||||
weekday: 'short',
|
||||
timeZone: timezone
|
||||
})
|
||||
.toUpperCase();
|
||||
.toUpperCase()
|
||||
|
||||
return (
|
||||
<div className="fc-daygrid-day-top">
|
||||
<small>{weekDay}</small>
|
||||
{arg.view.type !== "dayGridMonth" && (
|
||||
{arg.view.type !== 'dayGridMonth' && (
|
||||
<span
|
||||
className={`fc-daygrid-day-number ${arg.isToday ? "current-date" : ""}`}
|
||||
className={`fc-daygrid-day-number ${arg.isToday ? 'current-date' : ''}`}
|
||||
>
|
||||
{date}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}}
|
||||
dayHeaderDidMount={viewHandlers.handleDayHeaderDidMount}
|
||||
dayHeaderWillUnmount={viewHandlers.handleDayHeaderWillUnmount}
|
||||
@@ -561,7 +565,7 @@ export default function CalendarApp({
|
||||
eventDidMount={viewHandlers.handleEventDidMount}
|
||||
/>
|
||||
)}
|
||||
{view === "search" && <SearchResultsPage />}
|
||||
{view === 'search' && <SearchResultsPage />}
|
||||
<EventPopover
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
@@ -574,10 +578,10 @@ export default function CalendarApp({
|
||||
<EditModeDialog
|
||||
type={openEditModePopup}
|
||||
setOpen={setOpenEditModePopup}
|
||||
eventAction={(type: "solo" | "all" | undefined) => {
|
||||
setTypeOfAction(type);
|
||||
eventAction={(type: 'solo' | 'all' | undefined) => {
|
||||
setTypeOfAction(type)
|
||||
if (afterChoiceFunc) {
|
||||
afterChoiceFunc(type);
|
||||
afterChoiceFunc(type)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@@ -593,5 +597,5 @@ export default function CalendarApp({
|
||||
<EventErrorSnackbar messages={eventErrors} onClose={handleErrorClose} />
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user