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>
This commit is contained in:
142
admin-compliance/app/sdk/sdk-flow/_components/FlowCanvas.tsx
Normal file
142
admin-compliance/app/sdk/sdk-flow/_components/FlowCanvas.tsx
Normal file
@@ -0,0 +1,142 @@
|
||||
'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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user