657 lines
22 KiB
TypeScript
657 lines
22 KiB
TypeScript
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 { useScreenSizeDetection } from '@/useScreenSizeDetection'
|
|
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, MoreLinkArg } 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 { Fab } 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/types'
|
|
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 './CustomCalendar.styl'
|
|
import { useCalendarEventHandlers } from './hooks/useCalendarEventHandlers'
|
|
import { useCalendarViewHandlers } from './hooks/useCalendarViewHandlers'
|
|
import { TimezoneSelector } from './TimezoneSelector'
|
|
import {
|
|
eventToFullCalendarFormat,
|
|
extractEvents,
|
|
updateSlotLabelVisibility
|
|
} from './utils/calendarUtils'
|
|
import { CALENDAR_VIEWS } from './utils/constants'
|
|
import Sidebar from './Sidebar/SideBar'
|
|
import TempSearchDialog from './TempSearchDialog'
|
|
import { setIsMobileSearchOpen } from '@/features/Calendars/CalendarSlice'
|
|
import ViewMoreEvents from './ViewMoreEvents'
|
|
|
|
const localeMap: Record<string, LocaleInput | undefined> = {
|
|
fr: frLocale,
|
|
ru: ruLocale,
|
|
vi: viLocale,
|
|
en: undefined
|
|
}
|
|
|
|
interface CalendarAppProps {
|
|
calendarRef: MutableRefObject<CalendarApi | null>
|
|
onDateChange?: (date: Date) => void
|
|
onViewChange: (view: string) => void
|
|
menubarProps?: MenubarProps
|
|
openSidebar: boolean
|
|
onCloseSidebar: () => void
|
|
setCurrentView: (view: string) => void
|
|
currentView: string
|
|
}
|
|
|
|
const CalendarApp: React.FC<CalendarAppProps> = ({
|
|
calendarRef,
|
|
onDateChange,
|
|
onViewChange,
|
|
menubarProps,
|
|
openSidebar,
|
|
onCloseSidebar,
|
|
setCurrentView,
|
|
currentView
|
|
}: CalendarAppProps) => {
|
|
const [selectedDate, setSelectedDate] = useState(new Date())
|
|
const [debouncedDate, setDebouncedDate] = useState(new Date())
|
|
useEffect(() => {
|
|
const t = setTimeout(() => setDebouncedDate(selectedDate), 300)
|
|
return (): void => 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)
|
|
|
|
const hideDeclinedEvents = useAppSelector(
|
|
state => state.settings.hideDeclinedEvents
|
|
)
|
|
|
|
const { isTablet, isTooSmall: isMobile } = useScreenSizeDetection()
|
|
|
|
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])
|
|
|
|
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()
|
|
const [selectedCalendars, setSelectedCalendars] =
|
|
useState<string[]>(storedCalendars)
|
|
|
|
const calendarIdsString = useMemo(
|
|
() =>
|
|
Object.keys(calendars || {})
|
|
.sort()
|
|
.join(','),
|
|
[calendars]
|
|
)
|
|
const calendarIds = useMemo(
|
|
() => (calendarIdsString ? calendarIdsString.split(',') : []),
|
|
[calendarIdsString]
|
|
)
|
|
|
|
const timezone =
|
|
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())
|
|
|
|
useEffect(() => {
|
|
const handler = errorHandler.current
|
|
handler.setErrorCallback(setEventErrors)
|
|
return (): void => handler.setErrorCallback(() => {})
|
|
}, [])
|
|
|
|
const handleErrorClose = (): void => {
|
|
setEventErrors([])
|
|
errorHandler.current.clearAll()
|
|
}
|
|
|
|
useEffect(() => {
|
|
const updateSelectedCalendars = (): void => {
|
|
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
|
|
}
|
|
}
|
|
updateSelectedCalendars()
|
|
}, [calendarIds, calendars, userId])
|
|
|
|
// Save selected cals to cache
|
|
useEffect(() => {
|
|
if (calendarIds.length > 0) {
|
|
setSelectedCalendarsToStorage(selectedCalendars)
|
|
}
|
|
}, [selectedCalendars, calendarIds.length])
|
|
|
|
useEffect(() => {
|
|
const updateSelectedCalendarsOnCalendarChange = (): void => {
|
|
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
|
|
})
|
|
}
|
|
updateSelectedCalendarsOnCalendarChange()
|
|
}, [calendarIds])
|
|
|
|
const sortedSelectedCalendars = useMemo(
|
|
() => [...selectedCalendars].sort(),
|
|
[selectedCalendars]
|
|
)
|
|
|
|
const tempCalendarIdsString = useMemo(
|
|
() =>
|
|
Object.keys(tempcalendars || {})
|
|
.sort()
|
|
.join(','),
|
|
[tempcalendars]
|
|
)
|
|
const tempCalendarIds = useMemo(
|
|
() => (tempCalendarIdsString ? tempCalendarIdsString.split(',') : []),
|
|
[tempCalendarIdsString]
|
|
)
|
|
|
|
useCalendarDataLoader({
|
|
selectedDate: debouncedDate,
|
|
currentView,
|
|
selectedCalendars,
|
|
sortedSelectedCalendars,
|
|
calendarIds,
|
|
calendarIdsString,
|
|
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('')
|
|
|
|
// Listen for eventModalError event to reopen modal on API failure
|
|
useEffect(() => {
|
|
const handleEventModalError = (e: Event): void => {
|
|
const event = e as CustomEvent<{
|
|
type: 'create' | 'update'
|
|
eventId: string
|
|
calId: string
|
|
typeOfAction: string
|
|
}>
|
|
if (event.detail?.type === 'create') {
|
|
// Reopen create event modal
|
|
setAnchorEl(document.body)
|
|
} else if (event.detail?.type === 'update') {
|
|
// Store update modal info to sessionStorage for EventDisplayPreview to pick up
|
|
try {
|
|
sessionStorage.setItem(
|
|
'eventUpdateModalReopen',
|
|
JSON.stringify({
|
|
eventId: event.detail.eventId,
|
|
calId: event.detail.calId,
|
|
typeOfAction: event.detail.typeOfAction,
|
|
timestamp: Date.now()
|
|
})
|
|
)
|
|
|
|
// Open EventDisplayPreview if it's not already open with matching event, so it can pick up the sessionStorage
|
|
if (
|
|
!openEventDisplay ||
|
|
eventDisplayedId !== event.detail.eventId ||
|
|
eventDisplayedCalId !== event.detail.calId
|
|
) {
|
|
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', {
|
|
detail: {
|
|
eventId: event.detail.eventId,
|
|
calId: event.detail.calId,
|
|
typeOfAction: event.detail.typeOfAction
|
|
}
|
|
})
|
|
)
|
|
}
|
|
} catch {
|
|
// Ignore sessionStorage errors
|
|
}
|
|
}
|
|
}
|
|
|
|
window.addEventListener(
|
|
'eventModalError',
|
|
handleEventModalError as EventListener
|
|
)
|
|
return (): void => {
|
|
window.removeEventListener(
|
|
'eventModalError',
|
|
handleEventModalError as EventListener
|
|
)
|
|
}
|
|
}, [openEventDisplay, eventDisplayedId, eventDisplayedCalId])
|
|
|
|
const [openEditModePopup, setOpenEditModePopup] = useState<string | null>(
|
|
null
|
|
)
|
|
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)
|
|
|
|
const [tempUsers, setTempUsers] = useState<User[]>([])
|
|
const [tempEvent, setTempEvent] = useState<CalendarEvent>({} as CalendarEvent)
|
|
|
|
const [isMoreEventsDrawerOpen, setIsMoreEventsDrawerOpen] = useState(false)
|
|
const [moreEvents, setMoreEvents] = useState<EventApi[]>([])
|
|
|
|
const handleMoreLinkClick = (arg: MoreLinkArg): string | void => {
|
|
if (!isMobile) return
|
|
|
|
setMoreEvents(arg.hiddenSegs.map(seg => seg.event))
|
|
setIsMoreEventsDrawerOpen(true)
|
|
|
|
return 'none'
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (view !== 'calendar') return
|
|
const targetView =
|
|
currentView ||
|
|
(isTablet ? CALENDAR_VIEWS.timeGridDay : CALENDAR_VIEWS.timeGridWeek)
|
|
|
|
if (calendarRef.current?.view.type === targetView) return
|
|
const id = requestAnimationFrame(() => {
|
|
if (calendarRef.current?.view.type !== targetView) {
|
|
calendarRef.current?.changeView(targetView)
|
|
}
|
|
})
|
|
return (): void => cancelAnimationFrame(id)
|
|
}, [view, isTablet, currentView, calendarRef])
|
|
// Event handlers
|
|
const eventHandlers = useCalendarEventHandlers({
|
|
setSelectedRange,
|
|
setAnchorEl,
|
|
calendarRef,
|
|
dispatch,
|
|
setOpenEventDisplay,
|
|
setEventDisplayedId,
|
|
setEventDisplayedCalId,
|
|
setEventDisplayedTemp,
|
|
calendars,
|
|
setSelectedEvent,
|
|
setAfterChoiceFunc,
|
|
setOpenEditModePopup,
|
|
tempUsers,
|
|
setTempEvent,
|
|
timezone
|
|
})
|
|
|
|
// View handlers
|
|
const viewHandlers = useCalendarViewHandlers({
|
|
calendarRef,
|
|
setSelectedDate,
|
|
setSelectedMiniDate,
|
|
onViewChange: setCurrentView,
|
|
calendars,
|
|
tempcalendars,
|
|
// Note: To preserve current logic, this will temporarily disable eslint for react-hooks/refs
|
|
// eslint-disable-next-line react-hooks/refs
|
|
errorHandler: errorHandler.current
|
|
})
|
|
|
|
useEffect(() => {
|
|
if (process.env.NODE_ENV === 'test') {
|
|
window.__calendarRef = calendarRef
|
|
}
|
|
}, [calendarRef])
|
|
|
|
const { t, lang } = useI18n()
|
|
|
|
return (
|
|
<main
|
|
className={`main-layout calendar-layout ${menubarProps?.isIframe ? ' isInIframe' : ''}`}
|
|
>
|
|
<Sidebar
|
|
open={openSidebar}
|
|
onClose={onCloseSidebar}
|
|
calendarRef={calendarRef}
|
|
isIframe={menubarProps?.isIframe}
|
|
onCreateEvent={() => eventHandlers.handleDateSelect(null)}
|
|
onViewChange={onViewChange}
|
|
selectedMiniDate={selectedMiniDate}
|
|
setSelectedMiniDate={setSelectedMiniDate}
|
|
selectedCalendars={selectedCalendars}
|
|
setSelectedCalendars={setSelectedCalendars}
|
|
tempUsers={tempUsers}
|
|
setTempUsers={setTempUsers}
|
|
currentView={currentView}
|
|
onDateChange={onDateChange}
|
|
/>
|
|
<div className="calendar">
|
|
<ImportAlert />
|
|
{menubarProps?.isIframe && <Menubar {...menubarProps} />}
|
|
{isTablet && (
|
|
<Fab
|
|
color="primary"
|
|
aria-label={t('event.createEvent')}
|
|
onClick={() => eventHandlers.handleDateSelect(null)}
|
|
sx={{
|
|
position: 'fixed',
|
|
bottom: 24,
|
|
right: 24,
|
|
zIndex: 20,
|
|
borderRadius: '16px'
|
|
}}
|
|
>
|
|
<AddIcon />
|
|
</Fab>
|
|
)}
|
|
{view === 'calendar' && (
|
|
<FullCalendar
|
|
key={hiddenDays.join(',')}
|
|
ref={ref => {
|
|
if (ref) {
|
|
calendarRef.current = ref.getApi()
|
|
}
|
|
}}
|
|
plugins={[
|
|
dayGridPlugin,
|
|
timeGridPlugin,
|
|
interactionPlugin,
|
|
momentTimezonePlugin
|
|
]}
|
|
initialView={
|
|
currentView ||
|
|
(isTablet
|
|
? CALENDAR_VIEWS.timeGridDay
|
|
: CALENDAR_VIEWS.timeGridWeek)
|
|
}
|
|
firstDay={1}
|
|
editable={true}
|
|
locale={localeMap[lang]}
|
|
hiddenDays={hiddenDays}
|
|
selectable={true}
|
|
timeZone={timezone}
|
|
height="100%"
|
|
select={eventHandlers.handleDateSelect}
|
|
longPressDelay={100}
|
|
nowIndicator
|
|
slotLabelClassNames={arg => [
|
|
updateSlotLabelVisibility(new Date(), arg, timezone)
|
|
]}
|
|
nowIndicatorContent={viewHandlers.handleNowIndicatorContent}
|
|
headerToolbar={false}
|
|
views={{
|
|
timeGridWeek: { titleFormat: { month: 'long', year: 'numeric' } }
|
|
}}
|
|
dayMaxEvents={true}
|
|
moreLinkClick={handleMoreLinkClick}
|
|
events={eventToFullCalendarFormat(
|
|
filteredEvents,
|
|
filteredTempEvents,
|
|
userId,
|
|
userData?.email,
|
|
isPending,
|
|
calendars
|
|
)}
|
|
eventOrder={(a: EventApi, b: EventApi) =>
|
|
a.extendedProps.priority - b.extendedProps.priority
|
|
}
|
|
weekNumbers={
|
|
currentView === CALENDAR_VIEWS.timeGridWeek ||
|
|
currentView === CALENDAR_VIEWS.timeGridDay
|
|
}
|
|
weekNumberFormat={{ week: 'long' }}
|
|
weekNumberContent={arg => {
|
|
return (
|
|
<div className="weekSelector">
|
|
{displayWeekNumbers && (
|
|
<div>
|
|
{t('menubar.views.week')} {arg.num}
|
|
</div>
|
|
)}
|
|
<TimezoneSelector
|
|
value={timezone}
|
|
referenceDate={calendarRef.current?.getDate() ?? new Date()}
|
|
onChange={(newTimezone: string) =>
|
|
dispatch(setTimeZone(newTimezone))
|
|
}
|
|
/>
|
|
</div>
|
|
)
|
|
}}
|
|
dayCellContent={arg => {
|
|
const month = arg.date.toLocaleDateString(t('locale'), {
|
|
month: 'short',
|
|
timeZone: timezone
|
|
})
|
|
if (arg.view.type === CALENDAR_VIEWS.dayGridMonth) {
|
|
return (
|
|
<span
|
|
className={`fc-daygrid-day-number ${
|
|
arg.isToday ? 'current-date' : ''
|
|
}`}
|
|
>
|
|
{arg.dayNumberText === '1' ? month : ''} {arg.dayNumberText}
|
|
</span>
|
|
)
|
|
}
|
|
}}
|
|
slotDuration="00:30:00"
|
|
slotLabelInterval="01:00:00"
|
|
scrollTime="12:00:00"
|
|
unselectAuto={false}
|
|
allDayText=""
|
|
slotLabelFormat={{
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
hour12: false
|
|
}}
|
|
datesSet={arg => {
|
|
onViewChange?.(arg.view.type)
|
|
const calendarCurrentDate =
|
|
calendarRef.current?.getDate() || new Date(arg.start)
|
|
setDisplayedDateAndRange(calendarCurrentDate)
|
|
|
|
if (arg.view.type === CALENDAR_VIEWS.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)
|
|
}
|
|
|
|
// Always use the calendar's current date for consistency
|
|
if (onDateChange) {
|
|
onDateChange(calendarCurrentDate)
|
|
}
|
|
|
|
// Notify parent about view change
|
|
setCurrentView(arg.view.type)
|
|
|
|
// Update slot label visibility when view changes
|
|
setTimeout(() => {
|
|
updateSlotLabelVisibility(new Date())
|
|
}, 100)
|
|
}}
|
|
dayHeaderContent={arg => {
|
|
const m = moment.tz(arg.date, timezone)
|
|
|
|
const date = m.date()
|
|
const weekDay = m
|
|
.toDate()
|
|
.toLocaleDateString(t('locale'), {
|
|
weekday: 'short',
|
|
timeZone: timezone
|
|
})
|
|
.toUpperCase()
|
|
|
|
return (
|
|
<div
|
|
className={`fc-daygrid-day-top ${isTablet || isMobile ? 'fc-daygrid-day-top--mobile' : ''}`}
|
|
>
|
|
<small>{weekDay}</small>
|
|
{arg.view.type !== CALENDAR_VIEWS.dayGridMonth && (
|
|
<span
|
|
className={`fc-daygrid-day-number ${arg.isToday ? 'current-date' : ''}`}
|
|
>
|
|
{date}
|
|
</span>
|
|
)}
|
|
</div>
|
|
)
|
|
}}
|
|
dayHeaderDidMount={viewHandlers.handleDayHeaderDidMount}
|
|
dayHeaderWillUnmount={viewHandlers.handleDayHeaderWillUnmount}
|
|
viewDidMount={viewHandlers.handleViewDidMount}
|
|
viewWillUnmount={viewHandlers.handleViewWillUnmount}
|
|
eventClick={eventHandlers.handleEventClick}
|
|
eventAllow={eventHandlers.handleEventAllow}
|
|
eventDrop={arg => {
|
|
void eventHandlers.handleEventDrop(arg)
|
|
}}
|
|
eventResize={arg => {
|
|
void eventHandlers.handleEventResize(arg)
|
|
}}
|
|
eventContent={viewHandlers.handleEventContent}
|
|
eventDidMount={viewHandlers.handleEventDidMount}
|
|
/>
|
|
)}
|
|
{view === 'search' && <SearchResultsPage />}
|
|
<EventPopover
|
|
anchorEl={anchorEl}
|
|
open={Boolean(anchorEl)}
|
|
onClose={eventHandlers.handleClosePopover}
|
|
selectedRange={selectedRange}
|
|
setSelectedRange={setSelectedRange}
|
|
calendarRef={calendarRef}
|
|
event={tempEvent}
|
|
/>
|
|
<EditModeDialog
|
|
type={openEditModePopup}
|
|
setOpen={setOpenEditModePopup}
|
|
eventAction={(type: 'solo' | 'all' | undefined) => {
|
|
setTypeOfAction(type)
|
|
if (afterChoiceFunc) {
|
|
afterChoiceFunc(type)
|
|
}
|
|
}}
|
|
/>
|
|
{openEventDisplay && eventDisplayedId && eventDisplayedCalId && (
|
|
<EventPreviewModal
|
|
eventId={eventDisplayedId}
|
|
calId={eventDisplayedCalId}
|
|
tempEvent={eventDisplayedTemp}
|
|
open={openEventDisplay}
|
|
onClose={eventHandlers.handleCloseEventDisplay}
|
|
/>
|
|
)}
|
|
<EventErrorSnackbar messages={eventErrors} onClose={handleErrorClose} />
|
|
</div>
|
|
|
|
{isMobile && (
|
|
<>
|
|
<TempSearchDialog
|
|
tempUsers={tempUsers}
|
|
setTempUsers={setTempUsers}
|
|
onClose={() => {
|
|
dispatch(setIsMobileSearchOpen(false))
|
|
onCloseSidebar()
|
|
}}
|
|
handleToggleEventPreview={() =>
|
|
eventHandlers.handleDateSelect(null as unknown as DateSelectArg)
|
|
}
|
|
/>
|
|
<ViewMoreEvents
|
|
isOpen={isMoreEventsDrawerOpen}
|
|
onOpen={() => setIsMoreEventsDrawerOpen(true)}
|
|
onClose={() => setIsMoreEventsDrawerOpen(false)}
|
|
moreEvents={moreEvents}
|
|
handleEventClick={eventHandlers.handleEventClick}
|
|
/>
|
|
</>
|
|
)}
|
|
</main>
|
|
)
|
|
}
|
|
|
|
export default CalendarApp
|