import L, { LatLngTuple } from 'leaflet';
import React, { useCallback, useMemo } from 'react';
import { Marker, Popup } from 'react-leaflet';
import { getSectorDisplay } from '@/constants.tsx';
import { useMapActions, useMapInteraction } from '@/contexts/MapContexts.tsx';
import { useOrganizationSites } from '@/hooks/map/useOrganizationSites.ts';
import { mapBackendSectorToTranslationKey } from '@/lib/sector-mapper.ts';
import { Organization } from '@/types.ts';
import { getCachedOrganizationIcon } from '@/utils/map/iconCache.ts';
interface OrganizationMarkersProps {
organizations: Organization[];
selectedOrg: Organization | null;
hoveredOrgId: string | null;
}
/**
* Individual marker component memoized to prevent unnecessary re-renders
*/
const OrganizationMarker = React.memo<{
org: Organization;
site: { Latitude: number; Longitude: number };
sector: { icon?: React.ReactElement; colorKey?: string } | null;
isSelected: boolean;
isHovered: boolean;
onSelect: (org: Organization) => void;
onHover: (orgId: string | null) => void;
}>(
({ org, site, sector, isSelected, isHovered, onSelect, onHover }) => {
const position: LatLngTuple = useMemo(
() => [site.Latitude, site.Longitude],
[site.Latitude, site.Longitude]
);
const icon = useMemo(
() => getCachedOrganizationIcon(org.ID, org, sector, isSelected, isHovered),
[org.ID, org, sector, isSelected, isHovered]
);
const handleClick = useCallback(() => {
onSelect(org);
}, [org, onSelect]);
const handleMouseOver = useCallback(() => {
onHover(org.ID);
}, [org.ID, onHover]);
const handleMouseOut = useCallback(() => {
onHover(null);
}, [onHover]);
// Don't render if coordinates are invalid
if (!site.Latitude || !site.Longitude || site.Latitude === 0 || site.Longitude === 0) {
return null;
}
return (
{org.Name}
{org.Sector &&
{org.Sector}
}
{org.Description && (
{org.Description}
)}
);
},
(prevProps, nextProps) => {
// Custom comparison function for React.memo
return (
prevProps.org.ID === nextProps.org.ID &&
prevProps.site.Latitude === nextProps.site.Latitude &&
prevProps.site.Longitude === nextProps.site.Longitude &&
prevProps.isSelected === nextProps.isSelected &&
prevProps.isHovered === nextProps.isHovered &&
prevProps.sector?.colorKey === nextProps.sector?.colorKey
);
}
);
OrganizationMarker.displayName = 'OrganizationMarker';
const OrganizationMarkers: React.FC = ({
organizations,
selectedOrg,
hoveredOrgId,
}) => {
const { handleSelectOrg } = useMapActions();
const { setHoveredOrgId } = useMapInteraction();
const { orgSitesMap } = useOrganizationSites(organizations);
// No need for sector map - using getSectorDisplay directly
// Filter organizations that have valid coordinates (from site or organization)
const organizationsWithCoordinates = useMemo(() => {
const result = organizations
.filter((org) => org.ID && org.ID.trim() !== '')
.map((org) => {
const site = orgSitesMap.get(org.ID);
// Use site coordinates if available and valid
if (
site &&
site.Latitude &&
site.Longitude &&
site.Latitude !== 0 &&
site.Longitude !== 0
) {
return { org, site };
}
// Fallback to organization coordinates if available and valid
if (org.Latitude && org.Longitude && org.Latitude !== 0 && org.Longitude !== 0) {
return { org, site: { Latitude: org.Latitude, Longitude: org.Longitude } };
}
return null;
})
.filter(
(item): item is { org: Organization; site: { Latitude: number; Longitude: number } } =>
item !== null
);
return result;
}, [organizations, orgSitesMap]);
// Debug: Log marker count and details in development
if (process.env.NODE_ENV === 'development') {
console.log(`[OrganizationMarkers] Rendering ${organizationsWithCoordinates.length} markers`, {
totalOrganizations: organizations.length,
withCoordinates: organizationsWithCoordinates.length,
organizationsSample: organizations.slice(0, 3).map((org) => ({
id: org.ID,
name: org.Name,
coords: [org.Latitude || 0, org.Longitude || 0],
})),
sample: organizationsWithCoordinates.slice(0, 3).map(({ org, site }) => ({
name: org.Name,
coords: site ? [site.Latitude, site.Longitude] : null,
hasLogo: !!org.LogoURL,
})),
});
// Additional debug info
if (organizations.length === 0) {
console.warn(
'[OrganizationMarkers] No organizations received! Check API connection and data flow.'
);
}
}
return (
<>
{organizationsWithCoordinates.map(({ org, site }) => {
const sectorDisplay = getSectorDisplay(org.Sector);
const isSelected = selectedOrg?.ID === org.ID;
const isHovered = hoveredOrgId === org.ID;
// Skip rendering if coordinates are invalid
if (!site.Latitude || !site.Longitude || site.Latitude === 0 || site.Longitude === 0) {
return null;
}
return (
);
})}
>
);
};
export default React.memo(OrganizationMarkers);