turash/bugulma/frontend/components/map/SymbiosisLines.tsx
Damir Mukimov 6347f42e20
Consolidate repositories: Remove nested frontend .git and merge into main repository
- Remove nested git repository from bugulma/frontend/.git
- Add all frontend files to main repository tracking
- Convert from separate frontend/backend repos to unified monorepo
- Preserve all frontend code and development history as tracked files
- Eliminate nested repository complexity for simpler development workflow

This creates a proper monorepo structure with frontend and backend
coexisting in the same repository for easier development and deployment.
2025-11-25 06:02:57 +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">
Connection to <strong>{match.org?.Name || 'Unknown'}</strong>
</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);