'use client' import { useMemo } from 'react' import { type Node, type Edge, MarkerType } from 'reactflow' import { ARCH_SERVICES, ARCH_EDGES, LAYERS, type ArchService, } from '../architecture-data' import { NODE_WIDTH, LANE_Y_START, getServicePosition, type LayerFilter, } from '../_layout' export function useArchGraph({ layerFilter, showDb, showRag, showApis, selectedService, }: { layerFilter: LayerFilter showDb: boolean showRag: boolean showApis: boolean selectedService: ArchService | null }) { return useMemo(() => { const nodes: Node[] = [] const edges: Edge[] = [] const visibleServices = layerFilter === 'alle' ? ARCH_SERVICES : ARCH_SERVICES.filter(s => s.layer === layerFilter) const visibleIds = new Set(visibleServices.map(s => s.id)) // ── Service Nodes ────────────────────────────────────────────────────── visibleServices.forEach(service => { const layer = LAYERS[service.layer] const pos = getServicePosition(service) const isSelected = selectedService?.id === service.id nodes.push({ id: service.id, type: 'default', position: pos, data: { label: (
{service.nameShort}
{service.tech}
{service.port && (
:{service.port}
)}
), }, style: { background: isSelected ? layer.colorBorder : layer.colorBg, color: isSelected ? 'white' : layer.colorText, border: `2px solid ${layer.colorBorder}`, borderRadius: '10px', padding: '8px 4px', minWidth: `${NODE_WIDTH}px`, maxWidth: `${NODE_WIDTH}px`, cursor: 'pointer', boxShadow: isSelected ? `0 0 16px ${layer.colorBorder}` : '0 1px 3px rgba(0,0,0,0.08)', }, }) }) // ── Connection Edges ─────────────────────────────────────────────────── ARCH_EDGES.forEach(archEdge => { if (visibleIds.has(archEdge.source) && visibleIds.has(archEdge.target)) { const isHighlighted = selectedService?.id === archEdge.source || selectedService?.id === archEdge.target edges.push({ id: `e-${archEdge.source}-${archEdge.target}`, source: archEdge.source, target: archEdge.target, type: 'smoothstep', animated: isHighlighted, label: archEdge.label, labelStyle: { fontSize: 9, fill: isHighlighted ? '#7c3aed' : '#94a3b8' }, style: { stroke: isHighlighted ? '#7c3aed' : '#94a3b8', strokeWidth: isHighlighted ? 2.5 : 1.5, }, markerEnd: { type: MarkerType.ArrowClosed, color: isHighlighted ? '#7c3aed' : '#94a3b8', width: 14, height: 14, }, }) } }) // ── DB Table Nodes ───────────────────────────────────────────────────── if (showDb) { const dbTablesInUse = new Set() visibleServices.forEach(s => s.dbTables.forEach(t => dbTablesInUse.add(t))) let dbIdx = 0 dbTablesInUse.forEach(table => { const nodeId = `db-${table}` nodes.push({ id: nodeId, type: 'default', position: { x: -250, y: LANE_Y_START + dbIdx * 60 }, data: { label: (
{table}
), }, style: { background: '#f1f5f9', color: '#475569', border: '1px solid #94a3b8', borderRadius: '6px', padding: '4px 6px', fontSize: '10px', minWidth: '140px', }, }) visibleServices .filter(s => s.dbTables.includes(table)) .forEach(svc => { edges.push({ id: `e-db-${table}-${svc.id}`, source: nodeId, target: svc.id, type: 'straight', style: { stroke: '#94a3b8', strokeWidth: 1, strokeDasharray: '6 3' }, }) }) dbIdx++ }) } // ── RAG Collection Nodes ─────────────────────────────────────────────── if (showRag) { const ragInUse = new Set() visibleServices.forEach(s => s.ragCollections.forEach(r => ragInUse.add(r))) let ragIdx = 0 ragInUse.forEach(collection => { const nodeId = `rag-${collection}` const rightX = 1200 nodes.push({ id: nodeId, type: 'default', position: { x: rightX, y: LANE_Y_START + ragIdx * 60 }, data: { label: (
{collection.replace('bp_', '')}
), }, style: { background: '#dcfce7', color: '#166534', border: '1px solid #22c55e', borderRadius: '6px', padding: '4px 6px', fontSize: '10px', minWidth: '130px', }, }) visibleServices .filter(s => s.ragCollections.includes(collection)) .forEach(svc => { edges.push({ id: `e-rag-${collection}-${svc.id}`, source: nodeId, target: svc.id, type: 'straight', style: { stroke: '#22c55e', strokeWidth: 1, strokeDasharray: '6 3' }, }) }) ragIdx++ }) } // ── API Endpoint Nodes ───────────────────────────────────────────────── if (showApis) { visibleServices.forEach(svc => { if (svc.apiEndpoints.length === 0) return const svcPos = getServicePosition(svc) svc.apiEndpoints.forEach((ep, idx) => { const nodeId = `api-${svc.id}-${idx}` nodes.push({ id: nodeId, type: 'default', position: { x: svcPos.x + NODE_WIDTH + 30, y: svcPos.y + idx * 32 }, data: { label: (
{ep}
), }, style: { background: '#faf5ff', color: '#7c3aed', border: '1px solid #c4b5fd', borderRadius: '4px', padding: '2px 6px', fontSize: '9px', minWidth: '160px', }, }) edges.push({ id: `e-api-${svc.id}-${idx}`, source: svc.id, target: nodeId, type: 'straight', style: { stroke: '#c4b5fd', strokeWidth: 1 }, }) }) }) } return { nodes, edges } }, [layerFilter, showDb, showRag, showApis, selectedService]) }