fix
All checks were successful
Build And Deploy Main / build-and-deploy (push) Successful in 37s

This commit is contained in:
2026-03-19 16:26:10 -04:00
parent 45079b0793
commit 0b72b72fcc
2 changed files with 106 additions and 60 deletions

View File

@@ -1,4 +1,4 @@
import React, {useState, useEffect, useRef, useCallback} from "react"; import React, {useState, useEffect, useMemo, useRef, useCallback} from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { AuthService, EventsService, CustomerService, ResourceService, VehicleService, EmployeeService } from "../../services"; import { AuthService, EventsService, CustomerService, ResourceService, VehicleService, EmployeeService } from "../../services";
import moment from 'moment'; import moment from 'moment';
@@ -38,9 +38,6 @@ const EventsCalendar = () => {
const [resources, setResources] = useState([]); const [resources, setResources] = useState([]);
const [fromDate, setFromDate] = useState(new Date(new Date().getFullYear(), new Date().getMonth(), 1)); const [fromDate, setFromDate] = useState(new Date(new Date().getFullYear(), new Date().getMonth(), 1));
const [toDate, setToDate] = useState(new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0)); const [toDate, setToDate] = useState(new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0));
const [currentTotalTranslate1, setCurrentTotalTranslate1] = useState(0);
const [currentTotalTranslate2, setCurrentTotalTranslate2] = useState(0);
const [currentTotalResource, setCurrentTotalResource] = useState(0);
const [showDeletedItems, setShowDeletedItems] = useState(false); const [showDeletedItems, setShowDeletedItems] = useState(false);
const [selectedColorFilters, setSelectedColorFilters] = useState([]); const [selectedColorFilters, setSelectedColorFilters] = useState([]);
const [timeData, setTimeData] = useState([]); const [timeData, setTimeData] = useState([]);
@@ -147,6 +144,7 @@ const EventsCalendar = () => {
const [initialEventsLoaded, setInitialEventsLoaded] = useState(false); const [initialEventsLoaded, setInitialEventsLoaded] = useState(false);
const [isTabTransitionLoading, setIsTabTransitionLoading] = useState(false); const [isTabTransitionLoading, setIsTabTransitionLoading] = useState(false);
const hasMarkedInitialEventsLoadedRef = useRef(false); const hasMarkedInitialEventsLoadedRef = useRef(false);
const eventsFetchRequestIdRef = useRef(0);
const visibleCalendarTabs = calendarTabOrder.filter((tabKey) => AuthService.canViewCalendarTab(tabKey) || AuthService.canEditCalendarTab(tabKey)); const visibleCalendarTabs = calendarTabOrder.filter((tabKey) => AuthService.canViewCalendarTab(tabKey) || AuthService.canEditCalendarTab(tabKey));
const canEditCurrentTab = () => AuthService.canEditCalendarTab(currentTab); const canEditCurrentTab = () => AuthService.canEditCalendarTab(currentTab);
@@ -184,6 +182,22 @@ const EventsCalendar = () => {
return doctorName ? `${fullName} - ${doctorName}` : fullName; return doctorName ? `${fullName} - ${doctorName}` : fullName;
}; };
const customerById = useMemo(() => {
const map = new Map();
(customers || []).forEach((customer) => {
if (customer?.id) map.set(customer.id, customer);
});
return map;
}, [customers]);
const resourceById = useMemo(() => {
const map = new Map();
(resources || []).forEach((resource) => {
if (resource?.id) map.set(resource.id, resource);
});
return map;
}, [resources]);
// Helper: expand a recurring rule into individual event instances for a date range // Helper: expand a recurring rule into individual event instances for a date range
const expandRecurrence = (rule, rangeFrom, rangeTo) => { const expandRecurrence = (rule, rangeFrom, rangeTo) => {
const instances = []; const instances = [];
@@ -297,15 +311,15 @@ const EventsCalendar = () => {
plugins: [eventModalService, eventsServicePlugin, eventRecurrence], plugins: [eventModalService, eventsServicePlugin, eventRecurrence],
callbacks: { callbacks: {
onRangeUpdate(range) { onRangeUpdate(range) {
console.log('new calendar range start date', range.start);
console.log('new calendar range end date', range.end);
setCurrentRangeStart(range.start); setCurrentRangeStart(range.start);
setCurrentRangeEnd(range.end); setCurrentRangeEnd(range.end);
// Update fromDate/toDate for API fetching based on the range // Update fromDate/toDate for API fetching based on the range
const startDate = new Date(range.start); const startDate = new Date(range.start);
const endDate = new Date(range.end); const endDate = new Date(range.end);
setFromDate(new Date(startDate.getFullYear(), startDate.getMonth(), 1)); const nextFromDate = new Date(startDate.getFullYear(), startDate.getMonth(), 1);
setToDate(new Date(endDate.getFullYear(), endDate.getMonth() + 1, 0)); const nextToDate = new Date(endDate.getFullYear(), endDate.getMonth() + 1, 0);
setFromDate((prev) => (prev?.getTime?.() === nextFromDate.getTime() ? prev : nextFromDate));
setToDate((prev) => (prev?.getTime?.() === nextToDate.getTime() ? prev : nextToDate));
}, },
onClickDate(date) { onClickDate(date) {
if (currentTabRef.current === 'medicalCalendar' || !AuthService.canEditCalendarTab(currentTabRef.current)) return; if (currentTabRef.current === 'medicalCalendar' || !AuthService.canEditCalendarTab(currentTabRef.current)) return;
@@ -331,8 +345,6 @@ const EventsCalendar = () => {
// Filter events based on current calendar range // Filter events based on current calendar range
const getFilteredEvents = () => { const getFilteredEvents = () => {
console.log("CenterCalendar - range:", currentRangeStart, "to", currentRangeEnd, "events:", events.length);
if (!currentRangeStart || !currentRangeEnd) { if (!currentRangeStart || !currentRangeEnd) {
// If no range set yet, show all events for current month // If no range set yet, show all events for current month
const now = moment(); const now = moment();
@@ -429,19 +441,34 @@ const EventsCalendar = () => {
}, [currentTab]); }, [currentTab]);
useEffect(() => { useEffect(() => {
Promise.allSettled([ const requestId = ++eventsFetchRequestIdRef.current;
EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }), const timer = setTimeout(() => {
EventsService.getAllEventRecurrences() EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) })
]).then(([eventsResult, recurrencesResult]) => { .then((eventsResult) => {
setAllEvents(eventsResult?.status === 'fulfilled' ? (eventsResult.value?.data || []) : []); if (requestId !== eventsFetchRequestIdRef.current) return;
setAllEventRecurrences(recurrencesResult?.status === 'fulfilled' ? (recurrencesResult.value?.data || []) : []); setAllEvents(eventsResult?.data || []);
if (!hasMarkedInitialEventsLoadedRef.current) { if (!hasMarkedInitialEventsLoadedRef.current) {
hasMarkedInitialEventsLoadedRef.current = true; hasMarkedInitialEventsLoadedRef.current = true;
setInitialEventsLoaded(true); setInitialEventsLoaded(true);
} }
}); }).catch(() => {
if (requestId !== eventsFetchRequestIdRef.current) return;
setAllEvents([]);
if (!hasMarkedInitialEventsLoadedRef.current) {
hasMarkedInitialEventsLoadedRef.current = true;
setInitialEventsLoaded(true);
}
});
}, 180);
return () => clearTimeout(timer);
}, [fromDate, toDate]); }, [fromDate, toDate]);
useEffect(() => {
EventsService.getAllEventRecurrences()
.then((data) => setAllEventRecurrences(data?.data || []))
.catch(() => setAllEventRecurrences([]));
}, []);
useEffect(() => { useEffect(() => {
if (!showSpinner) return; if (!showSpinner) return;
const timer = setInterval(() => { const timer = setInterval(() => {
@@ -490,14 +517,16 @@ const EventsCalendar = () => {
if (customers?.length > 0 && resources.length > 0) { if (customers?.length > 0 && resources.length > 0) {
const orignialEvents = [...allEvents]; const orignialEvents = [...allEvents];
setEvents(orignialEvents?.filter(item => item.type === 'medical')?.map((item) => { setEvents(orignialEvents?.filter(item => item.type === 'medical')?.map((item) => {
const customerField = item?.data?.customer ? (customers?.find(c => c.id === item?.data?.customer)?.name || item?.data?.client_name || '') : (item?.data?.client_name || ''); const customer = item?.data?.customer ? customerById.get(item?.data?.customer) : null;
const doctorField = item?.data?.resource ? ((resources?.find(r => r.id === item?.data?.resource))?.name || item?.data?.resource_name || '') : (item?.data?.resource_name || ''); const resource = item?.data?.resource ? resourceById.get(item?.data?.resource) : null;
const customerField = customer?.name || item?.data?.client_name || '';
const doctorField = resource?.name || item?.data?.resource_name || '';
item.event_id = item.id; item.event_id = item.id;
item.customer = customerField; item.customer = customerField;
item.doctor = doctorField; item.doctor = doctorField;
item.phone = item?.data?.resource ? ((resources?.find(r => r.id === item?.data?.resource))?.phone || item?.data?.resource_phone || '') : (item?.data?.resource_phone || ''); item.phone = resource?.phone || item?.data?.resource_phone || '';
item.contact = item?.data?.resource? ((resources?.find(r => r.id === item?.data?.resource))?.contact || item?.data?.resource_contact || '') : (item?.data?.resource_contact || '') item.contact = resource?.contact || item?.data?.resource_contact || '';
item.address = item?.data?.resource ? ((resources?.find(r => r.id === item?.data?.resource))?.address || item?.data?.resource_address || '') : (item?.data?.resource_address || ''); item.address = resource?.address || item?.data?.resource_address || '';
item.translation = item?.data?.interpreter || ''; item.translation = item?.data?.interpreter || '';
item.newPatient = item?.data?.new_patient || ''; item.newPatient = item?.data?.new_patient || '';
item.needId = item?.data?.need_id || ''; item.needId = item?.data?.need_id || '';
@@ -519,11 +548,8 @@ const EventsCalendar = () => {
item.maxTranslate2 = maxTranslate2; item.maxTranslate2 = maxTranslate2;
item.maxResource = maxResource; item.maxResource = maxResource;
item.totalTranslate1 = totalTranslate1; item.totalTranslate1 = totalTranslate1;
setCurrentTotalTranslate1(item.totalTranslate1);
item.totalTranslate2 = totalTranslate2; item.totalTranslate2 = totalTranslate2;
setCurrentTotalTranslate2(item.totalTranslate2);
item.totalResource = totalResource; item.totalResource = totalResource;
setCurrentTotalResource(item.totalResource);
return item; return item;
})?.filter(item => { })?.filter(item => {
const includeDeleted = selectedColorFilters.includes('gray'); const includeDeleted = selectedColorFilters.includes('gray');
@@ -598,7 +624,7 @@ const EventsCalendar = () => {
filteredEvents = filteredEvents?.filter(item => { filteredEvents = filteredEvents?.filter(item => {
// If it's a customer-related event, check if the customer is active // If it's a customer-related event, check if the customer is active
if (item.target_type === 'customer' && item.target_uuid) { if (item.target_type === 'customer' && item.target_uuid) {
const customer = customers.find(c => c.id === item.target_uuid); const customer = customerById.get(item.target_uuid);
// Only show if customer exists and is active (not discharged/transferred/deceased) // Only show if customer exists and is active (not discharged/transferred/deceased)
return customer && customer.status === 'active' && return customer && customer.status === 'active' &&
customer.type !== 'discharged' && customer.type !== 'discharged' &&
@@ -612,13 +638,12 @@ const EventsCalendar = () => {
setEvents(filteredEvents); setEvents(filteredEvents);
} }
}, [customers, resources, timeData, currentTab, allEvents, allEventRecurrences, showDeletedItems, selectedColorFilters]) }, [customers, resources, timeData, currentTab, allEvents, allEventRecurrences, showDeletedItems, selectedColorFilters, customerById, resourceById])
useEffect(() => { useEffect(() => {
if (events && calendar) { if (events && calendar) {
console.log("CenterCalendar useEffect - events:", events.length, "range:", currentRangeStart, "to", currentRangeEnd);
calendar?.eventsService?.set(events); calendar?.eventsService?.set(events);
setGroupedEvents(getGroupedEvents()); setGroupedEvents(getGroupedEvents());
} }

View File

@@ -1,4 +1,4 @@
import React, {useState, useEffect, useRef} from "react"; import React, {useState, useEffect, useMemo, useRef} from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { AuthService, EventsService, CustomerService, ResourceService } from "../../services"; import { AuthService, EventsService, CustomerService, ResourceService } from "../../services";
import moment from 'moment'; import moment from 'moment';
@@ -27,9 +27,6 @@ const EventsCalendar = () => {
const [resources, setResources] = useState([]); const [resources, setResources] = useState([]);
const [fromDate, setFromDate] = useState(new Date(new Date().getFullYear(), new Date().getMonth(), 1)); const [fromDate, setFromDate] = useState(new Date(new Date().getFullYear(), new Date().getMonth(), 1));
const [toDate, setToDate] = useState(new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0)); const [toDate, setToDate] = useState(new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0));
const [currentTotalTranslate1, setCurrentTotalTranslate1] = useState(0);
const [currentTotalTranslate2, setCurrentTotalTranslate2] = useState(0);
const [currentTotalResource, setCurrentTotalResource] = useState(0);
const [selectedColorFilters, setSelectedColorFilters] = useState([]); const [selectedColorFilters, setSelectedColorFilters] = useState([]);
const [timeData, setTimeData] = useState([]); const [timeData, setTimeData] = useState([]);
const [showFilterDropdown, setShowFilterDropdown] = useState(false); const [showFilterDropdown, setShowFilterDropdown] = useState(false);
@@ -54,6 +51,7 @@ const EventsCalendar = () => {
const [initialLookupsLoaded, setInitialLookupsLoaded] = useState(false); const [initialLookupsLoaded, setInitialLookupsLoaded] = useState(false);
const [initialEventsLoaded, setInitialEventsLoaded] = useState(false); const [initialEventsLoaded, setInitialEventsLoaded] = useState(false);
const hasMarkedInitialEventsLoadedRef = useRef(false); const hasMarkedInitialEventsLoadedRef = useRef(false);
const eventsFetchRequestIdRef = useRef(0);
const [newEventStartDateTime, setNewEventStartDateTime] = useState(new Date()); const [newEventStartDateTime, setNewEventStartDateTime] = useState(new Date());
const [newEventEndDateTime, setNewEventEndDateTime] = useState(new Date()); const [newEventEndDateTime, setNewEventEndDateTime] = useState(new Date());
const [newEventColor, setNewEventColor] = useState(''); const [newEventColor, setNewEventColor] = useState('');
@@ -90,6 +88,22 @@ const EventsCalendar = () => {
return doctorName ? `${fullName} - ${doctorName}` : fullName; return doctorName ? `${fullName} - ${doctorName}` : fullName;
}; };
const customerById = useMemo(() => {
const map = new Map();
(customers || []).forEach((customer) => {
if (customer?.id) map.set(customer.id, customer);
});
return map;
}, [customers]);
const resourceById = useMemo(() => {
const map = new Map();
(resources || []).forEach((resource) => {
if (resource?.id) map.set(resource.id, resource);
});
return map;
}, [resources]);
const calendar = useCalendarApp({ const calendar = useCalendarApp({
views: [createViewMonthGrid(), createViewDay(), createViewWeek()], views: [createViewMonthGrid(), createViewDay(), createViewWeek()],
monthGridOptions: { monthGridOptions: {
@@ -106,8 +120,10 @@ const EventsCalendar = () => {
setCurrentRangeEnd(range.end); setCurrentRangeEnd(range.end);
const startDate = new Date(range.start); const startDate = new Date(range.start);
const endDate = new Date(range.end); const endDate = new Date(range.end);
setFromDate(new Date(startDate.getFullYear(), startDate.getMonth(), 1)); const nextFromDate = new Date(startDate.getFullYear(), startDate.getMonth(), 1);
setToDate(new Date(endDate.getFullYear(), endDate.getMonth() + 1, 0)); const nextToDate = new Date(endDate.getFullYear(), endDate.getMonth() + 1, 0);
setFromDate((prev) => (prev?.getTime?.() === nextFromDate.getTime() ? prev : nextFromDate));
setToDate((prev) => (prev?.getTime?.() === nextToDate.getTime() ? prev : nextToDate));
}, },
onClickDate(date) { onClickDate(date) {
// Disabled: prevent creating new event by tapping calendar date // Disabled: prevent creating new event by tapping calendar date
@@ -191,19 +207,25 @@ const EventsCalendar = () => {
}, []); }, []);
useEffect(() => { useEffect(() => {
EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }).then(data => { const requestId = ++eventsFetchRequestIdRef.current;
setAllEvents(data?.data || []); const timer = setTimeout(() => {
if (!hasMarkedInitialEventsLoadedRef.current) { EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }).then(data => {
hasMarkedInitialEventsLoadedRef.current = true; if (requestId !== eventsFetchRequestIdRef.current) return;
setInitialEventsLoaded(true); setAllEvents(data?.data || []);
} if (!hasMarkedInitialEventsLoadedRef.current) {
}).catch(() => { hasMarkedInitialEventsLoadedRef.current = true;
setAllEvents([]); setInitialEventsLoaded(true);
if (!hasMarkedInitialEventsLoadedRef.current) { }
hasMarkedInitialEventsLoadedRef.current = true; }).catch(() => {
setInitialEventsLoaded(true); if (requestId !== eventsFetchRequestIdRef.current) return;
} setAllEvents([]);
}); if (!hasMarkedInitialEventsLoadedRef.current) {
hasMarkedInitialEventsLoadedRef.current = true;
setInitialEventsLoaded(true);
}
});
}, 180);
return () => clearTimeout(timer);
}, [fromDate, toDate]); }, [fromDate, toDate]);
useEffect(() => { useEffect(() => {
@@ -230,14 +252,16 @@ const EventsCalendar = () => {
if (customers?.length > 0 && resources.length > 0) { if (customers?.length > 0 && resources.length > 0) {
const orignialEvents = [...allEvents]; const orignialEvents = [...allEvents];
setEvents(orignialEvents?.filter(item => item.type === 'medical')?.map((item) => { setEvents(orignialEvents?.filter(item => item.type === 'medical')?.map((item) => {
const customerField = item?.data?.customer ? (customers?.find(c => c.id === item?.data?.customer)?.name || item?.data?.client_name || '') : (item?.data?.client_name || ''); const customer = item?.data?.customer ? customerById.get(item?.data?.customer) : null;
const doctorField = item?.data?.resource ? ((resources?.find(r => r.id === item?.data?.resource))?.name || item?.data?.resource_name || '') : (item?.data?.resource_name || ''); const resource = item?.data?.resource ? resourceById.get(item?.data?.resource) : null;
const customerField = customer?.name || item?.data?.client_name || '';
const doctorField = resource?.name || item?.data?.resource_name || '';
item.event_id = item.id; item.event_id = item.id;
item.customer = customerField; item.customer = customerField;
item.doctor = doctorField; item.doctor = doctorField;
item.phone = item?.data?.resource ? ((resources?.find(r => r.id === item?.data?.resource))?.phone || item?.data?.resource_phone || '') : (item?.data?.resource_phone || ''); item.phone = resource?.phone || item?.data?.resource_phone || '';
item.contact = item?.data?.resource? ((resources?.find(r => r.id === item?.data?.resource))?.contact || item?.data?.resource_contact || '') : (item?.data?.resource_contact || '') item.contact = resource?.contact || item?.data?.resource_contact || '';
item.address = item?.data?.resource ? ((resources?.find(r => r.id === item?.data?.resource))?.address || item?.data?.resource_address || '') : (item?.data?.resource_address || ''); item.address = resource?.address || item?.data?.resource_address || '';
item.translation = item?.data?.interpreter || ''; item.translation = item?.data?.interpreter || '';
item.newPatient = item?.data?.new_patient || ''; item.newPatient = item?.data?.new_patient || '';
item.needId = item?.data?.need_id || ''; item.needId = item?.data?.need_id || '';
@@ -259,11 +283,8 @@ const EventsCalendar = () => {
item.maxTranslate2 = maxTranslate2; item.maxTranslate2 = maxTranslate2;
item.maxResource = maxResource; item.maxResource = maxResource;
item.totalTranslate1 = totalTranslate1; item.totalTranslate1 = totalTranslate1;
setCurrentTotalTranslate1(item.totalTranslate1);
item.totalTranslate2 = totalTranslate2; item.totalTranslate2 = totalTranslate2;
setCurrentTotalTranslate2(item.totalTranslate2);
item.totalResource = totalResource; item.totalResource = totalResource;
setCurrentTotalResource(item.totalResource);
return item; return item;
})?.filter(item => { })?.filter(item => {
const includeDeleted = selectedColorFilters.includes('gray'); const includeDeleted = selectedColorFilters.includes('gray');
@@ -282,7 +303,7 @@ const EventsCalendar = () => {
return false; return false;
})); }));
} }
}, [customers, resources, timeData, allEvents, selectedColorFilters]) }, [customers, resources, timeData, allEvents, selectedColorFilters, customerById, resourceById])