From c6c7faa979586aadc514d507244f448cb0ffadf2 Mon Sep 17 00:00:00 2001 From: Lixian Zhou Date: Thu, 19 Mar 2026 15:47:34 -0400 Subject: [PATCH] fix --- .../center-calendar/CenterCalendar.js | 99 ++++++++++++++--- .../components/customers/UpdateCustomer.js | 13 ++- .../src/components/events/EventsCalendar.js | 100 +++++++++++++++--- 3 files changed, 177 insertions(+), 35 deletions(-) diff --git a/client/src/components/center-calendar/CenterCalendar.js b/client/src/components/center-calendar/CenterCalendar.js index a30048e..f8c836c 100644 --- a/client/src/components/center-calendar/CenterCalendar.js +++ b/client/src/components/center-calendar/CenterCalendar.js @@ -2,7 +2,7 @@ import React, {useState, useEffect, useRef, useCallback} from "react"; import { useNavigate } from "react-router-dom"; import { AuthService, EventsService, CustomerService, ResourceService, VehicleService, EmployeeService } from "../../services"; import moment from 'moment'; -import { Breadcrumb, Tabs, Tab, Button, Modal, Dropdown, Spinner } from "react-bootstrap"; +import { Breadcrumb, Tabs, Tab, Button, Modal, Dropdown, ProgressBar } from "react-bootstrap"; import { useCalendarApp, ScheduleXCalendar } from '@schedule-x/react'; import { viewMonthGrid, @@ -45,9 +45,21 @@ const EventsCalendar = () => { const [selectedColorFilters, setSelectedColorFilters] = useState([]); const [timeData, setTimeData] = useState([]); const [showFilterDropdown, setShowFilterDropdown] = useState(false); - const eventsServicePlugin = createEventsServicePlugin(); - const eventModalService = createEventModalPlugin(); - const eventRecurrence = createEventRecurrencePlugin(); + const eventsServicePluginRef = useRef(null); + if (!eventsServicePluginRef.current) { + eventsServicePluginRef.current = createEventsServicePlugin(); + } + const eventsServicePlugin = eventsServicePluginRef.current; + const eventModalServiceRef = useRef(null); + if (!eventModalServiceRef.current) { + eventModalServiceRef.current = createEventModalPlugin(); + } + const eventModalService = eventModalServiceRef.current; + const eventRecurrenceRef = useRef(null); + if (!eventRecurrenceRef.current) { + eventRecurrenceRef.current = createEventRecurrencePlugin(); + } + const eventRecurrence = eventRecurrenceRef.current; const [groupedEvents, setGroupedEvents] = useState(new Map()); const [currentRangeStart, setCurrentRangeStart] = useState(null); const [currentRangeEnd, setCurrentRangeEnd] = useState(null); @@ -130,6 +142,10 @@ const EventsCalendar = () => { const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const [deleteTargetId, setDeleteTargetId] = useState(null); const [showSpinner, setShowSpinner] = useState(false); + const [loadingProgress, setLoadingProgress] = useState(12); + const [initialLookupsLoaded, setInitialLookupsLoaded] = useState(false); + const [initialEventsLoaded, setInitialEventsLoaded] = useState(false); + const hasMarkedInitialEventsLoadedRef = useRef(false); const visibleCalendarTabs = calendarTabOrder.filter((tabKey) => AuthService.canViewCalendarTab(tabKey) || AuthService.canEditCalendarTab(tabKey)); const canEditCurrentTab = () => AuthService.canEditCalendarTab(currentTab); @@ -379,6 +395,9 @@ const EventsCalendar = () => { navigate(`/login`); } setShowSpinner(true); + setLoadingProgress(12); + setInitialLookupsLoaded(false); + setInitialEventsLoaded(false); Promise.all([ VehicleService.getAllActiveVehicles().then((data) => { setVehicles(data.data) @@ -396,7 +415,7 @@ const EventsCalendar = () => { setTimeData(data.data); }) ]).finally(() => { - setShowSpinner(false); + setInitialLookupsLoaded(true); }); }, []); @@ -409,10 +428,39 @@ const EventsCalendar = () => { }, [currentTab]); useEffect(() => { - EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }).then(data => setAllEvents(data?.data)); - EventsService.getAllEventRecurrences().then(data => setAllEventRecurrences(data?.data || [])); + Promise.allSettled([ + EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }), + EventsService.getAllEventRecurrences() + ]).then(([eventsResult, recurrencesResult]) => { + setAllEvents(eventsResult?.status === 'fulfilled' ? (eventsResult.value?.data || []) : []); + setAllEventRecurrences(recurrencesResult?.status === 'fulfilled' ? (recurrencesResult.value?.data || []) : []); + if (!hasMarkedInitialEventsLoadedRef.current) { + hasMarkedInitialEventsLoadedRef.current = true; + setInitialEventsLoaded(true); + } + }); }, [fromDate, toDate]); + useEffect(() => { + if (!showSpinner) return; + const timer = setInterval(() => { + setLoadingProgress((prev) => { + if (prev >= 90) return prev; + return prev + Math.max(1, Math.ceil((90 - prev) / 8)); + }); + }, 220); + return () => clearInterval(timer); + }, [showSpinner]); + + useEffect(() => { + if (!initialLookupsLoaded || !initialEventsLoaded) return; + setLoadingProgress(100); + const closeTimer = setTimeout(() => { + setShowSpinner(false); + }, 250); + return () => clearTimeout(closeTimer); + }, [initialLookupsLoaded, initialEventsLoaded]); + // Auto-fill repeat start date from event date when repeat option is selected (new modal) useEffect(() => { if (newEventRecurring && newEventStartDateTime) { @@ -466,9 +514,17 @@ const EventsCalendar = () => { item.totalResource = totalResource; setCurrentTotalResource(item.totalResource); return item; - })?.filter(item => (!showDeletedItems && item.status === 'active') || showDeletedItems) + })?.filter(item => { + const includeDeleted = selectedColorFilters.includes('gray'); + const isDeletedAppt = item?.status !== 'active' || item?.color === 'gray'; + if (!isDeletedAppt) return true; + return includeDeleted; + }) ?.filter(item => { if (selectedColorFilters.length === 0) return true; + if (item?.status !== 'active' || item?.color === 'gray') { + return selectedColorFilters.includes('gray'); + } if (selectedColorFilters.includes(item.color)) return true; // When "Drop-Off Only" (purple) is selected, also show events with no label if (selectedColorFilters.includes('purple') && !item.color) return true; @@ -946,14 +1002,17 @@ const EventsCalendar = () => { const dismissEventModal = (e) => { e?.preventDefault?.(); e?.stopPropagation?.(); + e?.nativeEvent?.stopImmediatePropagation?.(); try { eventModalService?.close?.(); } catch (_e) {} try { calendar?.config?.plugins?.eventModal?.close(); - } catch (e) { - document.querySelectorAll('.sx__event-modal').forEach(el => el.remove()); - } + } catch (_e) {} + setTimeout(() => { + document.querySelectorAll('[class*="sx__event-modal"]').forEach(el => el.remove()); + document.querySelectorAll('[class*="event-modal"]').forEach(el => el.remove()); + }, 0); }; @@ -965,8 +1024,10 @@ const EventsCalendar = () => {