import { useTranslation } from '@/hooks/useI18n'; import type { BackendMatch } from '@/schemas/backend/match'; import L from 'leaflet'; import React, { useMemo } from 'react'; import { Marker, Popup } from 'react-leaflet'; import MarkerClusterGroup from 'react-leaflet-markercluster'; import { formatCurrency } from '../../lib/fin'; // Resource type color mapping const resourceTypeColors: Record = { heat: '#FF6B6B', water: '#4ECDC4', steam: '#95E1D3', CO2: '#45B7D1', biowaste: '#96CEB4', cooling: '#FECA57', logistics: '#FF9FF3', materials: '#54A0FF', service: '#5F27CD', }; interface ResourceFlowMarkersProps { matches: BackendMatch[]; selectedMatchId: string | null; onMatchSelect: (matchId: string) => void; } /** * Individual resource flow marker component */ const ResourceFlowMarker = React.memo<{ position: [number, number]; resourceType: string; direction: string; match: BackendMatch; isInSelectedMatch: boolean; onClick: () => void; }>(({ position, resourceType, direction, match, isInSelectedMatch, onClick }) => { const { t } = useTranslation(); const color = resourceTypeColors[resourceType] || '#6C757D'; // Create custom icon const icon = useMemo(() => { const markerHtml = `
`; return L.divIcon({ html: markerHtml, className: 'custom-resource-flow-marker', iconSize: [26, 26], iconAnchor: [13, 13], }); }, [color, isInSelectedMatch]); const formatScore = (score: number) => { return `${(score * 100).toFixed(1)}%`; }; // use central fin utility return (

{t(`resourceTypes.${resourceType}`, { defaultValue: resourceType })}{' '} {t('matchesMap.flow')}

{direction === 'input' ? t('matchesMap.input') : t('matchesMap.output')}
{t('matchesMap.matchId')}:
{match.ID}
{t('matchesMap.compatibility')}:
{formatScore(match.CompatibilityScore)}
{t('matchesMap.distance')}:
{t('matchesMap.distanceValue', { distance: match.DistanceKm.toFixed(1) })}
{t('matchesMap.economicValue')}:
{formatCurrency(match.EconomicValue)}
{t('matchDetail.updateStatus')}: {t(`matchStatus.${match.Status}`, { defaultValue: match.Status })}
); }); ResourceFlowMarker.displayName = 'ResourceFlowMarker'; const ResourceFlowMarkers: React.FC = ({ matches, selectedMatchId, onMatchSelect, }) => { // Transform matches into resource flow markers const resourceFlowMarkers = useMemo(() => { const markers: Array<{ id: string; position: [number, number]; resourceType: string; direction: string; match: BackendMatch; }> = []; matches.forEach((match) => { // For now, we'll generate mock positions based on match ID // In a real implementation, we'd get coordinates from site data const baseLat = 55.1644; // Bugulma center const baseLng = 50.205; const hash = match.ID.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0); const lat1 = baseLat + ((hash % 100) - 50) * 0.01; const lng1 = baseLng + (((hash * 7) % 100) - 50) * 0.01; const lat2 = baseLat + (((hash * 13) % 100) - 50) * 0.01; const lng2 = baseLng + (((hash * 17) % 100) - 50) * 0.01; // Add source flow marker (assuming it's an output) markers.push({ id: `${match.ID}-source`, position: [lat1, lng1], resourceType: 'heat', // Would come from match data direction: 'output', match, }); // Add target flow marker (assuming it's an input) markers.push({ id: `${match.ID}-target`, position: [lat2, lng2], resourceType: 'heat', // Would come from match data direction: 'input', match, }); }); return markers; }, [matches]); return ( { const count = cluster.getChildCount(); const size = count < 10 ? 'small' : count < 100 ? 'medium' : 'large'; const className = `marker-cluster marker-cluster-${size}`; return L.divIcon({ html: `
${count}
`, className, iconSize: [40, 40], }); }} > {resourceFlowMarkers.map((marker) => ( onMatchSelect(marker.match.ID)} /> ))}
); }; export default React.memo(ResourceFlowMarkers);