diff --git a/src/components/map/CableMap.tsx b/src/components/map/CableMap.tsx index 75bc1e6..5bf1cfc 100644 --- a/src/components/map/CableMap.tsx +++ b/src/components/map/CableMap.tsx @@ -64,40 +64,59 @@ export default function CableMap({ locations }: CableMapProps) { [locations] ); - const initialEdges = useMemo( - () => - locations.flatMap((location) => - location.startCables.flatMap((cable) => { - const route = [ - { id: String(cable.startLocationId), name: location.name }, - ...cable.intermediateStops.map((stop) => ({ - id: String(stop.location.id), - name: stop.location.name, - })), - { id: String(cable.endLocationId), name: cable.endLocation.name }, - ]; + const initialEdges = useMemo(() => { + const edges = locations.flatMap((location) => + location.startCables.flatMap((cable) => { + const route = [ + { id: String(cable.startLocationId), name: location.name }, + ...cable.intermediateStops.map((stop) => ({ + id: String(stop.location.id), + name: stop.location.name, + })), + { id: String(cable.endLocationId), name: cable.endLocation.name }, + ]; - return route.slice(1).map((target, index) => { - const source = route[index]; - return { - id: `cable-${cable.id}-${index}`, - type: "cable", - source: source.id, - target: target.id, - data: { - label: cable.identifier, - coreCount: cable._count.cores, - cableId: cable.id, - sourceName: source.name, - targetName: target.name, - onEdgeClick: () => setSelectedEdgeId(cable.id), - }, - }; - }); - }) - ), - [locations] - ); + return route.slice(1).map((target, index) => { + const source = route[index]; + return { + id: `cable-${cable.id}-${index}`, + type: "cable", + source: source.id, + target: target.id, + data: { + label: cable.identifier, + coreCount: cable._count.cores, + cableId: cable.id, + sourceName: source.name, + targetName: target.name, + onEdgeClick: () => setSelectedEdgeId(cable.id), + }, + }; + }); + }) + ); + + const counts = new Map(); + edges.forEach((edge) => { + const key = `${edge.source}->${edge.target}`; + counts.set(key, (counts.get(key) ?? 0) + 1); + }); + + const indexes = new Map(); + return edges.map((edge) => { + const key = `${edge.source}->${edge.target}`; + const index = indexes.get(key) ?? 0; + indexes.set(key, index + 1); + return { + ...edge, + data: { + ...edge.data, + parallelIndex: index, + parallelCount: counts.get(key) ?? 1, + }, + }; + }); + }, [locations]); const [nodes,, onNodesChange] = useNodesState(initialNodes); const [edges,, onEdgesChange] = useEdgesState(initialEdges); diff --git a/src/components/map/CableMapEdge.tsx b/src/components/map/CableMapEdge.tsx index 26b3674..e89a8e0 100644 --- a/src/components/map/CableMapEdge.tsx +++ b/src/components/map/CableMapEdge.tsx @@ -9,6 +9,8 @@ type CableEdgeData = { sourceName?: string; targetName?: string; onEdgeClick?: () => void; + parallelIndex?: number; + parallelCount?: number; }; export default function CableMapEdge({ @@ -33,6 +35,9 @@ export default function CableMapEdge({ const edgeId = `edge-${id}`; const strokeWidth = 1 + Math.min(data.coreCount, 8) * 0.4; const strokeColor = selected ? "#1d4ed8" : "#64748b"; + const parallelCount = typeof data.parallelCount === "number" ? data.parallelCount : 1; + const parallelIndex = typeof data.parallelIndex === "number" ? data.parallelIndex : 0; + const startOffset = `${((parallelIndex + 1) / (parallelCount + 1)) * 100}%`; return ( <> @@ -46,7 +51,12 @@ export default function CableMapEdge({ onClick={() => data.onEdgeClick?.()} /> - + {data.label}