Skip to content

Commit 96037b0

Browse files
adamintCopilot
andcommitted
Remove distracting SVG edge lines from concept map
Keep prerequisites data and Suggested Next section (useful for learning), but remove the SVG bezier curves that visually cluttered the page. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent a60d7cb commit 96037b0

1 file changed

Lines changed: 2 additions & 124 deletions

File tree

AspireAcademy.Web/src/pages/ConceptMapPage.tsx

Lines changed: 2 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useMemo, useRef, useEffect, useCallback } from 'react';
1+
import { useState, useMemo } from 'react';
22
import { useNavigate } from 'react-router-dom';
33
import { useQuery } from '@tanstack/react-query';
44
import { Box, Flex, Text, Input, Heading, SimpleGrid, Button } from '@chakra-ui/react';
@@ -32,14 +32,6 @@ interface ConceptsData {
3232
concepts: ConceptNode[];
3333
}
3434

35-
interface Edge {
36-
fromX: number;
37-
fromY: number;
38-
toX: number;
39-
toY: number;
40-
color: string;
41-
}
42-
4335
// ── Helpers ──
4436

4537
function isLessonCompleted(lessonId: string, worlds: World[]): boolean {
@@ -73,8 +65,6 @@ function isLessonLocked(lessonId: string, worlds: World[]): boolean {
7365
export default function ConceptMapPage() {
7466
const navigate = useNavigate();
7567
const [search, setSearch] = useState('');
76-
const containerRef = useRef<HTMLDivElement>(null);
77-
const [edges, setEdges] = useState<Edge[]>([]);
7868

7969
const { data: worlds } = useQuery<World[]>({
8070
queryKey: ['worlds'],
@@ -110,66 +100,6 @@ export default function ConceptMapPage() {
110100
return map;
111101
}, [searchLower, layerOrder, concepts]);
112102

113-
// Visible concept IDs (for edge filtering)
114-
const visibleConceptIds = useMemo(() => {
115-
const ids = new Set<string>();
116-
for (const arr of conceptsByLayer.values()) {
117-
for (const c of arr) ids.add(c.id);
118-
}
119-
return ids;
120-
}, [conceptsByLayer]);
121-
122-
// Compute edges between prerequisite concepts
123-
const computeEdges = useCallback(() => {
124-
const container = containerRef.current;
125-
if (!container || concepts.length === 0) { setEdges([]); return; }
126-
127-
const containerRect = container.getBoundingClientRect();
128-
const newEdges: Edge[] = [];
129-
130-
for (const concept of concepts) {
131-
if (!concept.prerequisites?.length) continue;
132-
if (!visibleConceptIds.has(concept.id)) continue;
133-
134-
const toEl = container.querySelector(`[data-concept-id="${concept.id}"]`);
135-
if (!toEl) continue;
136-
const toRect = toEl.getBoundingClientRect();
137-
const layerDef = layers[concept.layer];
138-
const color = layerDef?.color ?? '#888';
139-
140-
for (const preReqId of concept.prerequisites) {
141-
if (!visibleConceptIds.has(preReqId)) continue;
142-
const fromEl = container.querySelector(`[data-concept-id="${preReqId}"]`);
143-
if (!fromEl) continue;
144-
const fromRect = fromEl.getBoundingClientRect();
145-
146-
newEdges.push({
147-
fromX: fromRect.left + fromRect.width / 2 - containerRect.left,
148-
fromY: fromRect.top + fromRect.height / 2 - containerRect.top,
149-
toX: toRect.left + toRect.width / 2 - containerRect.left,
150-
toY: toRect.top + toRect.height / 2 - containerRect.top,
151-
color,
152-
});
153-
}
154-
}
155-
156-
setEdges(newEdges);
157-
}, [concepts, visibleConceptIds, layers]);
158-
159-
useEffect(() => {
160-
const timer = setTimeout(computeEdges, 100);
161-
const container = containerRef.current;
162-
let observer: ResizeObserver | undefined;
163-
if (container) {
164-
observer = new ResizeObserver(() => computeEdges());
165-
observer.observe(container);
166-
}
167-
return () => {
168-
clearTimeout(timer);
169-
observer?.disconnect();
170-
};
171-
}, [computeEdges]);
172-
173103
// Suggested next concepts
174104
const suggestedNext = useMemo(() => {
175105
if (!worlds || worlds.length === 0) return [];
@@ -201,14 +131,8 @@ export default function ConceptMapPage() {
201131
});
202132
}, [worlds, concepts]);
203133

204-
// Unique edge colors for SVG markers
205-
const edgeColors = useMemo(() => {
206-
const colorSet = new Set(edges.map(e => e.color));
207-
return Array.from(colorSet);
208-
}, [edges]);
209-
210134
return (
211-
<Box minH="100vh" bg="dark.bg" p={{ base: 3, md: 5 }} ref={containerRef} position="relative">
135+
<Box minH="100vh" bg="dark.bg" p={{ base: 3, md: 5 }} position="relative">
212136
{/* Header */}
213137
<Flex align="center" justify="space-between" wrap="wrap" gap={3} mb={5}>
214138
<Heading
@@ -417,52 +341,6 @@ export default function ConceptMapPage() {
417341
</Flex>
418342
)}
419343

420-
{/* SVG overlay for prerequisite edges */}
421-
{edges.length > 0 && (
422-
<svg
423-
style={{
424-
position: 'absolute',
425-
top: 0,
426-
left: 0,
427-
width: '100%',
428-
height: '100%',
429-
pointerEvents: 'none',
430-
overflow: 'visible',
431-
}}
432-
data-testid="concept-edges-svg"
433-
>
434-
<defs>
435-
{edgeColors.map((color) => (
436-
<marker
437-
key={color}
438-
id={`arrow-${color.replace('#', '')}`}
439-
markerWidth={8}
440-
markerHeight={6}
441-
refX={8}
442-
refY={3}
443-
orient="auto"
444-
>
445-
<path d="M0,0 L8,3 L0,6 Z" fill={color} opacity={0.6} />
446-
</marker>
447-
))}
448-
</defs>
449-
{edges.map((edge, i) => {
450-
const midY = (edge.fromY + edge.toY) / 2;
451-
const d = `M ${edge.fromX},${edge.fromY} C ${edge.fromX},${midY} ${edge.toX},${midY} ${edge.toX},${edge.toY}`;
452-
return (
453-
<path
454-
key={i}
455-
d={d}
456-
fill="none"
457-
stroke={edge.color}
458-
strokeWidth={1.5}
459-
opacity={0.35}
460-
markerEnd={`url(#arrow-${edge.color.replace('#', '')})`}
461-
/>
462-
);
463-
})}
464-
</svg>
465-
)}
466344
</Box>
467345
);
468346
}

0 commit comments

Comments
 (0)