Files
breakpilot-compliance/admin-compliance/app/sdk/sdk-flow/_components/FlowCanvas.tsx
Sharang Parnerkar 2b818c6fb3 refactor(admin): split sdk-flow page.tsx into colocated components
Extract BetriebOverviewPanel, DetailPanel, FlowCanvas, FlowToolbar,
StepTable, useFlowGraph hook and helpers into _components/ so page.tsx
drops from 1019 to 156 LOC (under the 300 soft target).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:49:33 +02:00

143 lines
4.6 KiB
TypeScript

'use client'
import ReactFlow, {
Node,
Controls,
Background,
MiniMap,
BackgroundVariant,
Panel,
OnNodesChange,
OnEdgesChange,
Node as RFNode,
Edge as RFEdge,
} from 'reactflow'
import { SDK_FLOW_STEPS, FLOW_PACKAGES, type SDKFlowStep } from '../flow-data'
import { DetailPanel } from './DetailPanel'
import { PACKAGE_ORDER, type PackageFilter } from './helpers'
export function FlowCanvas({
nodes,
edges,
onNodesChange,
onEdgesChange,
onNodeClick,
onPaneClick,
packageFilter,
selectedStep,
setSelectedStep,
}: {
nodes: RFNode[]
edges: RFEdge[]
onNodesChange: OnNodesChange
onEdgesChange: OnEdgesChange
onNodeClick: (e: React.MouseEvent, node: Node) => void
onPaneClick: () => void
packageFilter: PackageFilter
selectedStep: SDKFlowStep | null
setSelectedStep: (s: SDKFlowStep | null) => void
}) {
return (
<div className="flex bg-white rounded-xl border border-slate-200 shadow-sm overflow-hidden" style={{ height: '700px' }}>
{/* Canvas */}
<div className="flex-1 relative">
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onNodeClick={onNodeClick}
onPaneClick={onPaneClick}
fitView
fitViewOptions={{ padding: 0.15 }}
attributionPosition="bottom-left"
>
<Controls />
<MiniMap
nodeColor={node => {
if (node.id.startsWith('db-')) return '#94a3b8'
if (node.id.startsWith('rag-')) return '#22c55e'
const step = SDK_FLOW_STEPS.find(s => s.id === node.id)
return step ? FLOW_PACKAGES[step.package].color.border : '#94a3b8'
}}
maskColor="rgba(0,0,0,0.08)"
/>
<Background variant={BackgroundVariant.Dots} gap={16} size={1} />
{/* Legende */}
<Panel
position="bottom-right"
className="bg-white/95 p-3 rounded-lg shadow-lg text-xs"
>
<div className="font-medium text-slate-700 mb-2">Legende</div>
<div className="space-y-1">
{PACKAGE_ORDER.map(pkgId => {
const pkg = FLOW_PACKAGES[pkgId]
return (
<div key={pkgId} className="flex items-center gap-2">
<span
className="w-3 h-3 rounded"
style={{
background: pkg.color.bg,
border: `1px solid ${pkg.color.border}`,
}}
/>
<span className="text-slate-600">{pkg.name}</span>
</div>
)
})}
<div className="border-t border-slate-200 my-1.5 pt-1.5">
<div className="flex items-center gap-2">
<span className="w-3 h-3 rounded-full bg-slate-200 border border-slate-400" />
<span className="text-slate-500">DB-Tabelle</span>
</div>
<div className="flex items-center gap-2 mt-0.5">
<span className="w-3 h-3 rounded-full bg-green-100 border border-green-500" />
<span className="text-slate-500">RAG-Collection</span>
</div>
</div>
<div className="border-t border-slate-200 my-1.5 pt-1.5 text-slate-400">
<div>* = REQUIRED</div>
<div>~ = RECOMMENDED</div>
<div>--- = gestrichelte Border: Optional</div>
</div>
</div>
</Panel>
{/* Package Headers */}
{packageFilter === 'alle' && (
<Panel position="top-center" className="flex gap-2 pointer-events-none">
{PACKAGE_ORDER.map((pkgId) => {
const pkg = FLOW_PACKAGES[pkgId]
return (
<div
key={pkgId}
className="px-3 py-1 rounded-lg text-xs font-medium opacity-60"
style={{
background: pkg.color.bg,
color: pkg.color.text,
border: `1px solid ${pkg.color.border}`,
minWidth: '200px',
textAlign: 'center',
}}
>
{pkg.icon} {pkg.name}
</div>
)
})}
</Panel>
)}
</ReactFlow>
</div>
{/* Detail Panel */}
{selectedStep && (
<DetailPanel
step={selectedStep}
onClose={() => setSelectedStep(null)}
/>
)}
</div>
)
}