turash/bugulma/frontend/components/map/SymbiosisLines.tsx
Damir Mukimov f24628a248
Some checks failed
CI/CD Pipeline / backend-build (push) Has been skipped
CI/CD Pipeline / backend-lint (push) Failing after 31s
CI/CD Pipeline / frontend-lint (push) Failing after 1m26s
CI/CD Pipeline / frontend-build (push) Has been skipped
CI/CD Pipeline / e2e-test (push) Has been skipped
fix: resolve remaining linting and React Compiler errors
- Fix prettier formatting issues in multiple components
- Fix React Compiler memoization issues in ProductServiceMarkers.tsx
- Replace literal strings with i18n keys across components
- Address i18n issues in heritage, network graph, and match components
- Fix dependency arrays in useMemo hooks to match React Compiler expectations
2025-12-25 00:25:51 +01:00

110 lines
2.9 KiB
TypeScript

import { LatLngTuple } from 'leaflet';
import React, { useMemo } from 'react';
import { Polyline, Popup } from 'react-leaflet';
import { useOrganizationSites } from '@/hooks/map/useOrganizationSites.ts';
import { Organization, SymbiosisMatch } from '@/types.ts';
interface SymbiosisLinesProps {
matches: SymbiosisMatch[];
hoveredOrgId: string | null;
selectedOrg: Organization;
}
/**
* Individual symbiosis line component memoized to prevent unnecessary re-renders
*/
const SymbiosisLine = React.memo<{
match: SymbiosisMatch;
selectedOrgSite: { Latitude: number; Longitude: number };
matchOrgSite: { Latitude: number; Longitude: number };
isHovered: boolean;
}>(({ match, selectedOrgSite, matchOrgSite, isHovered }) => {
const positions: LatLngTuple[] = useMemo(
() => [
[selectedOrgSite.Latitude, selectedOrgSite.Longitude],
[matchOrgSite.Latitude, matchOrgSite.Longitude],
],
[selectedOrgSite, matchOrgSite]
);
return (
<Polyline
positions={positions}
pathOptions={{
color: 'hsl(var(--primary))',
weight: isHovered ? 4 : 2,
opacity: isHovered ? 1 : 0.5,
}}
>
<Popup>
<div>
<p className="text-sm">
{t('symbiosis.connectionTo', { name: match.org?.Name || 'Unknown' })}
</p>
</div>
</Popup>
</Polyline>
);
});
SymbiosisLine.displayName = 'SymbiosisLine';
const SymbiosisLines: React.FC<SymbiosisLinesProps> = ({ matches, hoveredOrgId, selectedOrg }) => {
// Get sites for selected org and matched orgs
const allOrgs = useMemo(
() => [selectedOrg, ...matches.map((m) => m.org).filter(Boolean)],
[selectedOrg, matches]
);
const { orgSitesMap } = useOrganizationSites(allOrgs);
const selectedOrgSite = orgSitesMap.get(selectedOrg.ID);
// Memoize valid matches with sites to prevent unnecessary recalculations
const validMatches = useMemo(() => {
if (!selectedOrgSite) return [];
return matches
.map((match) => {
if (!match.org) return null;
const matchOrgSite = orgSitesMap.get(match.org.ID);
if (!matchOrgSite) return null;
return {
match,
matchOrgSite,
};
})
.filter(
(
item
): item is {
match: SymbiosisMatch;
matchOrgSite: NonNullable<typeof item>['matchOrgSite'];
} => item !== null
);
}, [matches, orgSitesMap, selectedOrgSite]);
if (!selectedOrgSite) return null;
return (
<>
{validMatches.map(({ match, matchOrgSite }) => {
const isHovered = hoveredOrgId === match.id;
return (
<SymbiosisLine
key={match.id}
match={match}
selectedOrgSite={selectedOrgSite}
matchOrgSite={matchOrgSite}
isHovered={isHovered}
/>
);
})}
</>
);
};
export default React.memo(SymbiosisLines);