diff --git a/src/components/generator/Calendar/CalendarComponent.jsx b/src/components/generator/Calendar/CalendarComponent.jsx index 9aac6a0..d3e5b53 100644 --- a/src/components/generator/Calendar/CalendarComponent.jsx +++ b/src/components/generator/Calendar/CalendarComponent.jsx @@ -39,6 +39,7 @@ import { getCalendarViewNotificationMessage, getVisibleTimetables, } from "./utils/calendarViewUtils.js"; +import { buildSelectionPreviewEvents } from "./utils/selectionUtils.js"; import { getFullCalendarConfig } from "./utils/calendarConfigUtils.js"; import MultiLineSnackbar from "@/components/sitewide/MultiLineSnackbar"; import { useIsMobile } from "@/lib/utils/screenSizeUtils"; @@ -71,11 +72,19 @@ export default function CalendarComponent({ const [renameAnchorEl, setRenameAnchorEl] = useState(null); const [renameAnchorPosition, setRenameAnchorPosition] = useState(null); const [coursesForTimeline, setCoursesForTimeline] = useState([]); + const [selectionPreviewEvents, setSelectionPreviewEvents] = useState([]); + const selectionPreviewKeyRef = React.useRef(""); const visibleTimetables = useMemo( () => getVisibleTimetables(timetables, viewRange), [timetables, viewRange], ); + const calendarEvents = useMemo(() => { + if (selectionPreviewEvents.length === 0) { + return events; + } + return [...events, ...selectionPreviewEvents]; + }, [events, selectionPreviewEvents]); // Screen size detection const isMobile = useIsMobile(); @@ -417,6 +426,7 @@ export default function CalendarComponent({ }; const handleSelect = (selectInfo) => { + clearSelectionPreview(); handleCalendarSelection( selectInfo, setCurrentTimetableIndex, @@ -429,9 +439,51 @@ export default function CalendarComponent({ ); }; - const handleSelectAllow = (selectionInfo) => { - return true; - }; + const clearSelectionPreview = useCallback(() => { + if ( + selectionPreviewKeyRef.current !== "" || + selectionPreviewEvents.length + ) { + selectionPreviewKeyRef.current = ""; + setSelectionPreviewEvents([]); + } + }, [selectionPreviewEvents.length]); + + const handleSelectAllow = useCallback( + (selectionInfo) => { + const start = selectionInfo?.start; + const end = selectionInfo?.end; + + if (!start || !end) { + clearSelectionPreview(); + return true; + } + + const previewEvents = buildSelectionPreviewEvents(start, end); + if (previewEvents.length === 0) { + clearSelectionPreview(); + return true; + } + + const previewKey = previewEvents + .map( + (event) => `${event.start.toISOString()}-${event.end.toISOString()}`, + ) + .join("|"); + + if (previewKey !== selectionPreviewKeyRef.current) { + selectionPreviewKeyRef.current = previewKey; + setSelectionPreviewEvents(previewEvents); + } + + return true; + }, + [clearSelectionPreview], + ); + + const handleUnselect = useCallback(() => { + clearSelectionPreview(); + }, [clearSelectionPreview]); // Function to navigate the calendar to a specific date const navigateToDate = useCallback( @@ -492,11 +544,12 @@ export default function CalendarComponent({ {...getFullCalendarConfig({ calendarRef, showWeekends, - events, + events: calendarEvents, handleDatesSet, handleEventClick, handleSelect, handleSelectAllow, + handleUnselect, handleEventMouseEnter, handleEventMouseLeave, isMobile, diff --git a/src/components/generator/Calendar/utils/calendarConfigUtils.js b/src/components/generator/Calendar/utils/calendarConfigUtils.js index 3c595b5..b951e94 100644 --- a/src/components/generator/Calendar/utils/calendarConfigUtils.js +++ b/src/components/generator/Calendar/utils/calendarConfigUtils.js @@ -10,6 +10,7 @@ export const getFullCalendarConfig = ({ handleEventClick, handleSelect, handleSelectAllow, + handleUnselect, handleEventMouseEnter, handleEventMouseLeave, isMobile = false, @@ -21,6 +22,7 @@ export const getFullCalendarConfig = ({ headerToolbar: false, height: 835, dayHeaderFormat: { weekday: "short" }, + dayHeaderContent: (arg) => arg.text.toUpperCase(), dayCellClassNames: (arg) => arg.date.getDay() === new Date().getDay() ? "fc-day-today" : "", slotMinTime: "08:00:00", @@ -29,6 +31,15 @@ export const getFullCalendarConfig = ({ allDaySlot: true, allDayText: "ONLINE", eventContent: (eventInfo) => renderEventContent(eventInfo, isMobile), + eventClassNames: (arg) => + arg.event.extendedProps?.isPinned ? ["fc-event-pinned"] : [], + eventDidMount: (arg) => { + if (arg.event.extendedProps?.isPinned) { + arg.el.style.borderColor = "transparent"; + arg.el.style.borderWidth = "1px"; + arg.el.style.borderStyle = "solid"; + } + }, eventClick: handleEventClick, eventMouseEnter: handleEventMouseEnter, eventMouseLeave: handleEventMouseLeave, @@ -37,6 +48,7 @@ export const getFullCalendarConfig = ({ selectMinDistance: 25, select: handleSelect, selectAllow: handleSelectAllow, + unselect: handleUnselect, longPressDelay: 0, selectLongPressDelay: 500, firstDay: 1, diff --git a/src/components/generator/Calendar/utils/calendarUtils.jsx b/src/components/generator/Calendar/utils/calendarUtils.jsx index 8056698..77225cd 100644 --- a/src/components/generator/Calendar/utils/calendarUtils.jsx +++ b/src/components/generator/Calendar/utils/calendarUtils.jsx @@ -19,7 +19,12 @@ export const renderEventContent = (eventInfo, isMobile = false) => { return (