/** * Active Map Page * * Displays hospitals and PHCC locations on an interactive map with markers showing: * - Asset counts * - Work Order counts (Normal/Urgent, by status) * - Maintenance Log counts (Planned/Completed/Overdue) * * Supports both Hospital and PHCC location types with different field mappings: * - Hospital: company field for assets/work orders, custom_hospital_name for maintenance * - PHCC: custom_site for assets, site_name for work orders, asset-based for maintenance */ import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import { MapContainer, TileLayer, Marker, Popup, Tooltip, useMap } from 'react-leaflet'; import L from 'leaflet'; import 'leaflet/dist/leaflet.css'; import apiService from '../services/apiService'; import LinkField from '../components/LinkField'; import useUserDashboardFilters from '../hooks/useUserDashboardFilters'; import { isSiteEnabledHospital, buildMobileTeamSiteFilters } from '../utils/hospitalUtils'; // Fix for default marker icons in React-Leaflet import icon from 'leaflet/dist/images/marker-icon.png'; import iconShadow from 'leaflet/dist/images/marker-shadow.png'; const DefaultIcon = L.icon({ iconUrl: icon, shadowUrl: iconShadow, iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34], tooltipAnchor: [16, -28], shadowSize: [41, 41] }); L.Marker.prototype.options.icon = DefaultIcon; interface LocationData { name: string; latitude: number; longitude: number; location_type: 'hospital' | 'phcc'; assets: number; normal_work_orders: number; urgent_work_orders: number; planned_maintenance: number; completed_maintenance: number; overdue_maintenance: number; wo_open: number; wo_progress: number; wo_review: number; wo_completed: number; wo_closed: number; phcc_asset_names?: string[]; } // Component to handle map bounds fitting const MapBounds: React.FC<{ locations: LocationData[] }> = ({ locations }) => { const map = useMap(); useEffect(() => { if (locations.length > 0 && locations.some(l => l.latitude && l.longitude)) { const bounds = L.latLngBounds( locations .filter(l => l.latitude && l.longitude) .map(l => [l.latitude, l.longitude] as [number, number]) ); map.fitBounds(bounds, { padding: [30, 30], maxZoom: 8 }); } else { // Fallback: show Saudi Arabia center map.setView([24.8, 45.5], 6); } }, [locations, map]); return null; }; const ActiveMap: React.FC = () => { const navigate = useNavigate(); const [selectedPHCC, setSelectedPHCC] = useState(''); const [locations, setLocations] = useState([]); const [loading, setLoading] = useState(true); const markersRef = useRef>({}); const { filterHospital, filterSiteName, hospitalLocked, siteLocked, allowedHospitals, loading: filtersLoading, handleHospitalChange, handleSiteChange, } = useUserDashboardFilters(); const showSiteFilter = isSiteEnabledHospital(filterHospital); const hospitalLocationLinkFilters = useMemo(() => { const filters: Record = { custom_is_hospital: 1 }; if (allowedHospitals.length > 0) { filters.name = ['in', allowedHospitals]; } return filters; }, [allowedHospitals]); const mobileTeamSiteFilters = useMemo( () => buildMobileTeamSiteFilters(filterHospital, siteLocked ? filterSiteName : undefined), [filterHospital, filterSiteName, siteLocked] ); const phccLocationLinkFilters = useMemo(() => { const filters: Record = { custom_is_phcc: 1 }; if (siteLocked && filterSiteName) { filters.name = filterSiteName; } return filters; }, [siteLocked, filterSiteName]); const applyHospitalSiteFilters = useCallback( ( filters: Record, locationType: 'hospital' | 'phcc', siteField: 'site_name' | 'custom_site', hospitalName?: string ) => { const hospital = hospitalName || filterHospital; if ( locationType === 'hospital' && filterSiteName && hospital && isSiteEnabledHospital(hospital) ) { filters[siteField] = filterSiteName; } return filters; }, [filterHospital, filterSiteName] ); // Fetch location counts based on location type const fetchLocationCounts = async ( location: { name: string; latitude: string | number; longitude: string | number }, locationType: 'hospital' | 'phcc' ): Promise => { const isPhcc = locationType === 'phcc'; const assetFilterField = isPhcc ? 'custom_site' : 'company'; const woFilterField = isPhcc ? 'site_name' : 'company'; const counts: Partial = { assets: 0, normal_work_orders: 0, urgent_work_orders: 0, planned_maintenance: 0, completed_maintenance: 0, overdue_maintenance: 0, wo_open: 0, wo_progress: 0, wo_review: 0, wo_completed: 0, wo_closed: 0, phcc_asset_names: [] }; const hospitalName = locationType === 'hospital' ? location.name : undefined; try { const assetFilters = applyHospitalSiteFilters( { [assetFilterField]: location.name }, locationType, 'custom_site', hospitalName ); // Fetch Asset count const assetsResponse = await apiService.apiCall( `/api/resource/Asset?filters=${encodeURIComponent(JSON.stringify(assetFilters))}&fields=["name"]&limit_page_length=0` ); const assetList = assetsResponse?.data || []; counts.assets = assetList.length; // Store asset names for PHCC (needed for maintenance log queries) if (isPhcc) { counts.phcc_asset_names = assetList.map((a: any) => a.name); } const normalWOFilters = applyHospitalSiteFilters( { [woFilterField]: location.name, custom_priority_: 'Normal', repair_status: ['in', ['Open', 'Work In Progress']], }, locationType, 'site_name', hospitalName ); // Fetch Normal Work Orders const normalWOResponse = await apiService.apiCall( `/api/resource/Work_Order?filters=${encodeURIComponent(JSON.stringify(normalWOFilters))}&fields=["name"]` ); counts.normal_work_orders = normalWOResponse?.data?.length || 0; const urgentWOFilters = applyHospitalSiteFilters( { [woFilterField]: location.name, custom_priority_: 'Urgent', repair_status: ['in', ['Open', 'Work In Progress']], }, locationType, 'site_name', hospitalName ); // Fetch Urgent Work Orders const urgentWOResponse = await apiService.apiCall( `/api/resource/Work_Order?filters=${encodeURIComponent(JSON.stringify(urgentWOFilters))}&fields=["name"]` ); counts.urgent_work_orders = urgentWOResponse?.data?.length || 0; const buildWoStatusFilters = (repairStatus: string) => applyHospitalSiteFilters( { [woFilterField]: location.name, repair_status: repairStatus, }, locationType, 'site_name', hospitalName ); // Fetch WO Status counts const [woOpen, woProgress, woReview, woCompleted, woClosed] = await Promise.all([ apiService.apiCall( `/api/resource/Work_Order?filters=${encodeURIComponent(JSON.stringify(buildWoStatusFilters('Open')))}&fields=["name"]` ), apiService.apiCall( `/api/resource/Work_Order?filters=${encodeURIComponent(JSON.stringify(buildWoStatusFilters('Work In Progress')))}&fields=["name"]` ), apiService.apiCall( `/api/resource/Work_Order?filters=${encodeURIComponent(JSON.stringify(buildWoStatusFilters('Pending Review')))}&fields=["name"]` ), apiService.apiCall( `/api/resource/Work_Order?filters=${encodeURIComponent(JSON.stringify(buildWoStatusFilters('Completed')))}&fields=["name"]` ), apiService.apiCall( `/api/resource/Work_Order?filters=${encodeURIComponent(JSON.stringify(buildWoStatusFilters('Closed')))}&fields=["name"]` ), ]); counts.wo_open = woOpen?.data?.length || 0; counts.wo_progress = woProgress?.data?.length || 0; counts.wo_review = woReview?.data?.length || 0; counts.wo_completed = woCompleted?.data?.length || 0; counts.wo_closed = woClosed?.data?.length || 0; // Fetch Maintenance counts - different logic for PHCC vs Hospital if (isPhcc && counts.phcc_asset_names && counts.phcc_asset_names.length > 0) { // For PHCC, filter by asset_name const [plannedPM, completedPM, overduePM] = await Promise.all([ apiService.apiCall( `/api/resource/Asset Maintenance Log?filters=${encodeURIComponent(JSON.stringify({ asset_name: ['in', counts.phcc_asset_names], maintenance_status: 'Planned' }))}&fields=["name"]` ), apiService.apiCall( `/api/resource/Asset Maintenance Log?filters=${encodeURIComponent(JSON.stringify({ asset_name: ['in', counts.phcc_asset_names], maintenance_status: 'Completed' }))}&fields=["name"]` ), apiService.apiCall( `/api/resource/Asset Maintenance Log?filters=${encodeURIComponent(JSON.stringify({ asset_name: ['in', counts.phcc_asset_names], maintenance_status: 'Overdue' }))}&fields=["name"]` ) ]); counts.planned_maintenance = plannedPM?.data?.length || 0; counts.completed_maintenance = completedPM?.data?.length || 0; counts.overdue_maintenance = overduePM?.data?.length || 0; } else if (!isPhcc) { const buildMaintenanceFilters = (maintenanceStatus: string) => applyHospitalSiteFilters( { custom_hospital_name: location.name, maintenance_status: maintenanceStatus, }, locationType, 'site_name', hospitalName ); // For Hospital, filter by custom_hospital_name const [plannedPM, completedPM, overduePM] = await Promise.all([ apiService.apiCall( `/api/resource/Asset Maintenance Log?filters=${encodeURIComponent(JSON.stringify(buildMaintenanceFilters('Planned')))}&fields=["name"]` ), apiService.apiCall( `/api/resource/Asset Maintenance Log?filters=${encodeURIComponent(JSON.stringify(buildMaintenanceFilters('Completed')))}&fields=["name"]` ), apiService.apiCall( `/api/resource/Asset Maintenance Log?filters=${encodeURIComponent(JSON.stringify(buildMaintenanceFilters('Overdue')))}&fields=["name"]` ), ]); counts.planned_maintenance = plannedPM?.data?.length || 0; counts.completed_maintenance = completedPM?.data?.length || 0; counts.overdue_maintenance = overduePM?.data?.length || 0; } } catch (err) { console.error(`Error fetching counts for ${location.name}:`, err); } return { name: location.name, latitude: parseFloat(location.latitude), longitude: parseFloat(location.longitude), location_type: locationType, ...counts } as LocationData; }; // Fetch locations and their counts const fetchAndRenderData = async () => { setLoading(true); try { let allLocations: LocationData[] = []; const fetchPromises: Promise[] = []; const effectivePhcc = siteLocked && filterSiteName ? filterSiteName : selectedPHCC; // Fetch Hospital locations (if no PHCC is specifically selected, or if hospital is selected) if (!effectivePhcc || filterHospital) { const hospitalFilters: Record = { latitude: ['!=', ''], longitude: ['!=', ''], custom_is_hospital: 1, }; if (filterHospital) { hospitalFilters.name = filterHospital; } else if (allowedHospitals.length > 0) { hospitalFilters.name = ['in', allowedHospitals]; } fetchPromises.push( (async () => { const locationsResponse = await apiService.apiCall( `/api/resource/Location?filters=${encodeURIComponent(JSON.stringify(hospitalFilters))}&fields=["name","latitude","longitude"]&limit_page_length=0` ); const locationList = locationsResponse?.data || []; const locationPromises = locationList.map((loc: { name: string; latitude: string; longitude: string }) => fetchLocationCounts(loc, 'hospital') ); return Promise.all(locationPromises); })() ); } // Fetch PHCC locations (if no hospital is specifically selected, or if PHCC is selected) if (!filterHospital || effectivePhcc) { const phccFilters: Record = { latitude: ['!=', ''], longitude: ['!=', ''], custom_is_phcc: 1, }; if (effectivePhcc) { phccFilters.name = effectivePhcc; } fetchPromises.push( (async () => { const locationsResponse = await apiService.apiCall( `/api/resource/Location?filters=${encodeURIComponent(JSON.stringify(phccFilters))}&fields=["name","latitude","longitude"]&limit_page_length=0` ); const locationList = locationsResponse?.data || []; const locationPromises = locationList.map((loc: { name: string; latitude: string; longitude: string }) => fetchLocationCounts(loc, 'phcc') ); return Promise.all(locationPromises); })() ); } const results = await Promise.all(fetchPromises); allLocations = results.flat().filter((l) => !isNaN(l.latitude) && !isNaN(l.longitude)); setLocations(allLocations); } catch (error) { console.error('Error fetching map data:', error); } finally { setLoading(false); } }; useEffect(() => { if (filtersLoading) return; if (siteLocked && filterSiteName) { setSelectedPHCC(filterSiteName); } fetchAndRenderData(); }, [filterHospital, filterSiteName, selectedPHCC, filtersLoading, siteLocked, allowedHospitals]); // Navigate to list view with filters const navigateToWorkOrders = (location: LocationData, priority?: string, status?: string) => { const params = new URLSearchParams(); if (location.location_type === 'phcc') { params.set('site_name', location.name); } else { params.set('company', location.name); if (filterSiteName && isSiteEnabledHospital(location.name)) { params.set('site_name', filterSiteName); } } if (priority) params.set('priority', priority); if (status) params.set('status', status); navigate(`/work-orders?${params.toString()}`); }; const navigateToAssets = (location: LocationData) => { const params = new URLSearchParams(); const filterField = location.location_type === 'phcc' ? 'custom_site' : 'company'; params.set(filterField, location.name); navigate(`/assets?${params.toString()}`); }; const navigateToMaintenanceCalendar = (location: LocationData, status?: string) => { const params = new URLSearchParams(); if (location.location_type === 'phcc') { // For PHCC, we need to pass asset names or use a different approach params.set('phcc', location.name); } else { params.set('hospital', location.name); } if (status) params.set('status', status); navigate(`/maintenance-calendar?${params.toString()}`); }; // Create popup content with modern UI matching the application const createPopupContent = (location: LocationData) => { const isPhcc = location.location_type === 'phcc'; const typeBadge = isPhcc ? ( PHCC ) : ( Hospital ); return (
{/* Location Name Header */}

{location.name} {typeBadge}

Total Assets: {location.assets}

{/* Work Order Status Section */}

Work Order Status

{/* Status Table */}
Status Count
Open
Work In Progress
Pending Review
Completed
Closed
{/* Preventive Maintenance Section */}

Preventive Maintenance

{/* Action Buttons */}
); }; return (

Active Map

{/* Filter Container */}
{/* Hospital Filter — Location name matches Company/Hospital name */}
{showSiteFilter && (
)} {/* PHCC Filter */}
{/* Map Container */}
{loading && (
{filtersLoading ? 'Loading filters...' : 'Loading map data...'}
)} {locations.map((location) => { const urgentIndicator = location.urgent_work_orders > 0 ? '🚨 URGENT! ' : ''; const isPhcc = location.location_type === 'phcc'; const typeIndicator = isPhcc ? '🏥 PHCC' : '🏨 Hospital'; const markerKey = `${location.name}-${location.latitude}-${location.longitude}`; return ( { if (ref) { markersRef.current[markerKey] = ref; // Apply marker styling based on location type and urgency setTimeout(() => { const markerElement = ref.getElement(); if (markerElement) { // Remove all custom classes first markerElement.classList.remove('urgent-marker', 'red-marker', 'phcc-marker'); if (location.urgent_work_orders > 0) { // Same red flashing for both Hospital and PHCC urgent markers markerElement.classList.add('urgent-marker', 'red-marker'); } else if (isPhcc) { // Green marker for non-urgent PHCC markerElement.classList.add('phcc-marker'); } // Non-urgent hospitals use default blue marker } }, 100); } }} >

{urgentIndicator}{location.name}

{typeIndicator}

Assets: {location.assets}

Normal WOs: {location.normal_work_orders}
Urgent WOs: {location.urgent_work_orders}
Planned PMs: {location.planned_maintenance}
Completed PMs: {location.completed_maintenance}
{createPopupContent(location)}
); })}
{/* Custom Styles */}
); }; export default ActiveMap;