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 { AuthService, EventsService, CustomerService, ResourceService, VehicleService, EmployeeService } from "../../services";
import moment from 'moment';
@@ -38,9 +38,6 @@ const EventsCalendar = () => {
const [resources, setResources] = useState([]);
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 [currentTotalTranslate1, setCurrentTotalTranslate1] = useState(0);
const [currentTotalTranslate2, setCurrentTotalTranslate2] = useState(0);
const [currentTotalResource, setCurrentTotalResource] = useState(0);
const [showDeletedItems, setShowDeletedItems] = useState(false);
const [selectedColorFilters, setSelectedColorFilters] = useState([]);
const [timeData, setTimeData] = useState([]);
@@ -147,6 +144,7 @@ const EventsCalendar = () => {
const [initialEventsLoaded, setInitialEventsLoaded] = useState(false);
const [isTabTransitionLoading, setIsTabTransitionLoading] = useState(false);
const hasMarkedInitialEventsLoadedRef = useRef(false);
const eventsFetchRequestIdRef = useRef(0);
const visibleCalendarTabs = calendarTabOrder.filter((tabKey) => AuthService.canViewCalendarTab(tabKey) || AuthService.canEditCalendarTab(tabKey));
const canEditCurrentTab = () => AuthService.canEditCalendarTab(currentTab);
@@ -184,6 +182,22 @@ const EventsCalendar = () => {
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
const expandRecurrence = (rule, rangeFrom, rangeTo) => {
const instances = [];
@@ -297,15 +311,15 @@ const EventsCalendar = () => {
plugins: [eventModalService, eventsServicePlugin, eventRecurrence],
callbacks: {
onRangeUpdate(range) {
console.log('new calendar range start date', range.start);
console.log('new calendar range end date', range.end);
setCurrentRangeStart(range.start);
setCurrentRangeEnd(range.end);
// Update fromDate/toDate for API fetching based on the range
const startDate = new Date(range.start);
const endDate = new Date(range.end);
setFromDate(new Date(startDate.getFullYear(), startDate.getMonth(), 1));
setToDate(new Date(endDate.getFullYear(), endDate.getMonth() + 1, 0));
const nextFromDate = new Date(startDate.getFullYear(), startDate.getMonth(), 1);
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) {
if (currentTabRef.current === 'medicalCalendar' || !AuthService.canEditCalendarTab(currentTabRef.current)) return;
@@ -331,8 +345,6 @@ const EventsCalendar = () => {
// Filter events based on current calendar range
const getFilteredEvents = () => {
console.log("CenterCalendar - range:", currentRangeStart, "to", currentRangeEnd, "events:", events.length);
if (!currentRangeStart || !currentRangeEnd) {
// If no range set yet, show all events for current month
const now = moment();
@@ -429,19 +441,34 @@ const EventsCalendar = () => {
}, [currentTab]);
useEffect(() => {
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);
}
});
const requestId = ++eventsFetchRequestIdRef.current;
const timer = setTimeout(() => {
EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) })
.then((eventsResult) => {
if (requestId !== eventsFetchRequestIdRef.current) return;
setAllEvents(eventsResult?.data || []);
if (!hasMarkedInitialEventsLoadedRef.current) {
hasMarkedInitialEventsLoadedRef.current = true;
setInitialEventsLoaded(true);
}
}).catch(() => {
if (requestId !== eventsFetchRequestIdRef.current) return;
setAllEvents([]);
if (!hasMarkedInitialEventsLoadedRef.current) {
hasMarkedInitialEventsLoadedRef.current = true;
setInitialEventsLoaded(true);
}
});
}, 180);
return () => clearTimeout(timer);
}, [fromDate, toDate]);
useEffect(() => {
EventsService.getAllEventRecurrences()
.then((data) => setAllEventRecurrences(data?.data || []))
.catch(() => setAllEventRecurrences([]));
}, []);
useEffect(() => {
if (!showSpinner) return;
const timer = setInterval(() => {
@@ -490,14 +517,16 @@ const EventsCalendar = () => {
if (customers?.length > 0 && resources.length > 0) {
const orignialEvents = [...allEvents];
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 doctorField = item?.data?.resource ? ((resources?.find(r => r.id === item?.data?.resource))?.name || item?.data?.resource_name || '') : (item?.data?.resource_name || '');
const customer = item?.data?.customer ? customerById.get(item?.data?.customer) : null;
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.customer = customerField;
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.contact = item?.data?.resource? ((resources?.find(r => r.id === item?.data?.resource))?.contact || item?.data?.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.phone = resource?.phone || item?.data?.resource_phone || '';
item.contact = resource?.contact || item?.data?.resource_contact || '';
item.address = resource?.address || item?.data?.resource_address || '';
item.translation = item?.data?.interpreter || '';
item.newPatient = item?.data?.new_patient || '';
item.needId = item?.data?.need_id || '';
@@ -519,11 +548,8 @@ const EventsCalendar = () => {
item.maxTranslate2 = maxTranslate2;
item.maxResource = maxResource;
item.totalTranslate1 = totalTranslate1;
setCurrentTotalTranslate1(item.totalTranslate1);
item.totalTranslate2 = totalTranslate2;
setCurrentTotalTranslate2(item.totalTranslate2);
item.totalResource = totalResource;
setCurrentTotalResource(item.totalResource);
return item;
})?.filter(item => {
const includeDeleted = selectedColorFilters.includes('gray');
@@ -598,7 +624,7 @@ const EventsCalendar = () => {
filteredEvents = filteredEvents?.filter(item => {
// If it's a customer-related event, check if the customer is active
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)
return customer && customer.status === 'active' &&
customer.type !== 'discharged' &&
@@ -612,13 +638,12 @@ const EventsCalendar = () => {
setEvents(filteredEvents);
}
}, [customers, resources, timeData, currentTab, allEvents, allEventRecurrences, showDeletedItems, selectedColorFilters])
}, [customers, resources, timeData, currentTab, allEvents, allEventRecurrences, showDeletedItems, selectedColorFilters, customerById, resourceById])
useEffect(() => {
if (events && calendar) {
console.log("CenterCalendar useEffect - events:", events.length, "range:", currentRangeStart, "to", currentRangeEnd);
calendar?.eventsService?.set(events);
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 { AuthService, EventsService, CustomerService, ResourceService } from "../../services";
import moment from 'moment';
@@ -27,9 +27,6 @@ const EventsCalendar = () => {
const [resources, setResources] = useState([]);
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 [currentTotalTranslate1, setCurrentTotalTranslate1] = useState(0);
const [currentTotalTranslate2, setCurrentTotalTranslate2] = useState(0);
const [currentTotalResource, setCurrentTotalResource] = useState(0);
const [selectedColorFilters, setSelectedColorFilters] = useState([]);
const [timeData, setTimeData] = useState([]);
const [showFilterDropdown, setShowFilterDropdown] = useState(false);
@@ -54,6 +51,7 @@ const EventsCalendar = () => {
const [initialLookupsLoaded, setInitialLookupsLoaded] = useState(false);
const [initialEventsLoaded, setInitialEventsLoaded] = useState(false);
const hasMarkedInitialEventsLoadedRef = useRef(false);
const eventsFetchRequestIdRef = useRef(0);
const [newEventStartDateTime, setNewEventStartDateTime] = useState(new Date());
const [newEventEndDateTime, setNewEventEndDateTime] = useState(new Date());
const [newEventColor, setNewEventColor] = useState('');
@@ -90,6 +88,22 @@ const EventsCalendar = () => {
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({
views: [createViewMonthGrid(), createViewDay(), createViewWeek()],
monthGridOptions: {
@@ -106,8 +120,10 @@ const EventsCalendar = () => {
setCurrentRangeEnd(range.end);
const startDate = new Date(range.start);
const endDate = new Date(range.end);
setFromDate(new Date(startDate.getFullYear(), startDate.getMonth(), 1));
setToDate(new Date(endDate.getFullYear(), endDate.getMonth() + 1, 0));
const nextFromDate = new Date(startDate.getFullYear(), startDate.getMonth(), 1);
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) {
// Disabled: prevent creating new event by tapping calendar date
@@ -191,19 +207,25 @@ const EventsCalendar = () => {
}, []);
useEffect(() => {
EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }).then(data => {
setAllEvents(data?.data || []);
if (!hasMarkedInitialEventsLoadedRef.current) {
hasMarkedInitialEventsLoadedRef.current = true;
setInitialEventsLoaded(true);
}
}).catch(() => {
setAllEvents([]);
if (!hasMarkedInitialEventsLoadedRef.current) {
hasMarkedInitialEventsLoadedRef.current = true;
setInitialEventsLoaded(true);
}
});
const requestId = ++eventsFetchRequestIdRef.current;
const timer = setTimeout(() => {
EventsService.getAllEvents({ from: EventsService.formatDate(fromDate), to: EventsService.formatDate(toDate) }).then(data => {
if (requestId !== eventsFetchRequestIdRef.current) return;
setAllEvents(data?.data || []);
if (!hasMarkedInitialEventsLoadedRef.current) {
hasMarkedInitialEventsLoadedRef.current = true;
setInitialEventsLoaded(true);
}
}).catch(() => {
if (requestId !== eventsFetchRequestIdRef.current) return;
setAllEvents([]);
if (!hasMarkedInitialEventsLoadedRef.current) {
hasMarkedInitialEventsLoadedRef.current = true;
setInitialEventsLoaded(true);
}
});
}, 180);
return () => clearTimeout(timer);
}, [fromDate, toDate]);
useEffect(() => {
@@ -230,14 +252,16 @@ const EventsCalendar = () => {
if (customers?.length > 0 && resources.length > 0) {
const orignialEvents = [...allEvents];
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 doctorField = item?.data?.resource ? ((resources?.find(r => r.id === item?.data?.resource))?.name || item?.data?.resource_name || '') : (item?.data?.resource_name || '');
const customer = item?.data?.customer ? customerById.get(item?.data?.customer) : null;
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.customer = customerField;
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.contact = item?.data?.resource? ((resources?.find(r => r.id === item?.data?.resource))?.contact || item?.data?.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.phone = resource?.phone || item?.data?.resource_phone || '';
item.contact = resource?.contact || item?.data?.resource_contact || '';
item.address = resource?.address || item?.data?.resource_address || '';
item.translation = item?.data?.interpreter || '';
item.newPatient = item?.data?.new_patient || '';
item.needId = item?.data?.need_id || '';
@@ -259,11 +283,8 @@ const EventsCalendar = () => {
item.maxTranslate2 = maxTranslate2;
item.maxResource = maxResource;
item.totalTranslate1 = totalTranslate1;
setCurrentTotalTranslate1(item.totalTranslate1);
item.totalTranslate2 = totalTranslate2;
setCurrentTotalTranslate2(item.totalTranslate2);
item.totalResource = totalResource;
setCurrentTotalResource(item.totalResource);
return item;
})?.filter(item => {
const includeDeleted = selectedColorFilters.includes('gray');
@@ -282,7 +303,7 @@ const EventsCalendar = () => {
return false;
}));
}
}, [customers, resources, timeData, allEvents, selectedColorFilters])
}, [customers, resources, timeData, allEvents, selectedColorFilters, customerById, resourceById])