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:
257
admin-compliance/app/sdk/sdk-flow/_components/DetailPanel.tsx
Normal file
257
admin-compliance/app/sdk/sdk-flow/_components/DetailPanel.tsx
Normal file
@@ -0,0 +1,257 @@
|
||||
'use client'
|
||||
|
||||
import { SDK_FLOW_STEPS, FLOW_PACKAGES, findProducerStep, type SDKFlowStep } from '../flow-data'
|
||||
import { completionColor, completionLabel } from './helpers'
|
||||
|
||||
export function DetailPanel({
|
||||
step,
|
||||
onClose,
|
||||
}: {
|
||||
step: SDKFlowStep
|
||||
onClose: () => void
|
||||
}) {
|
||||
const pkg = FLOW_PACKAGES[step.package]
|
||||
const baseUrl = 'https://macmini:3007'
|
||||
|
||||
return (
|
||||
<div className="w-80 bg-white border-l border-slate-200 overflow-y-auto">
|
||||
<div className="sticky top-0 bg-white z-10 border-b border-slate-200">
|
||||
<div className="flex items-center justify-between p-4">
|
||||
<h3 className="font-bold text-slate-900">{step.name}</h3>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-slate-400 hover:text-slate-600 text-lg leading-none"
|
||||
>
|
||||
x
|
||||
</button>
|
||||
</div>
|
||||
<div className="px-4 pb-3 flex items-center gap-2">
|
||||
<span
|
||||
className="px-2 py-0.5 rounded text-xs font-medium"
|
||||
style={{ background: pkg.color.bg, color: pkg.color.text }}
|
||||
>
|
||||
{pkg.icon} {pkg.name}
|
||||
</span>
|
||||
<span className="text-xs text-slate-400">seq {step.seq}</span>
|
||||
{step.isOptional && (
|
||||
<span className="px-2 py-0.5 rounded text-xs bg-slate-100 text-slate-500">
|
||||
Optional
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-4 space-y-4">
|
||||
{/* Fertigstellungsgrad */}
|
||||
{step.completion !== undefined && (
|
||||
<div className="bg-slate-50 rounded-lg p-3">
|
||||
<div className="flex items-center justify-between mb-1.5">
|
||||
<span className="text-xs font-semibold text-slate-500 uppercase">
|
||||
Fertigstellungsgrad
|
||||
</span>
|
||||
<span
|
||||
className="text-lg font-bold"
|
||||
style={{ color: completionColor(step.completion) }}
|
||||
>
|
||||
{step.completion}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-2.5 bg-slate-200 rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full rounded-full"
|
||||
style={{
|
||||
width: `${step.completion}%`,
|
||||
background: completionColor(step.completion),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="text-xs mt-1.5 font-medium"
|
||||
style={{ color: completionColor(step.completion) }}
|
||||
>
|
||||
{completionLabel(step.completion)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Beschreibung */}
|
||||
<div>
|
||||
<p className="text-sm text-slate-700 leading-relaxed">{step.description}</p>
|
||||
<p className="text-xs text-slate-500 leading-relaxed mt-2">{step.descriptionLong}</p>
|
||||
{step.legalBasis && (
|
||||
<div className="mt-2 px-2 py-1.5 bg-blue-50 rounded text-xs text-blue-700">
|
||||
<span className="font-medium">Rechtsgrundlage:</span> {step.legalBasis}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Inputs */}
|
||||
{step.inputs.length > 0 && (
|
||||
<div>
|
||||
<h4 className="text-xs font-semibold text-slate-500 uppercase mb-1.5">
|
||||
Inputs (SDKState)
|
||||
</h4>
|
||||
<div className="space-y-1">
|
||||
{step.inputs.map(input => {
|
||||
const producer = findProducerStep(input)
|
||||
return (
|
||||
<div
|
||||
key={input}
|
||||
className="flex items-center justify-between text-sm bg-blue-50 rounded px-2 py-1"
|
||||
>
|
||||
<code className="text-blue-700 text-xs">{input}</code>
|
||||
{producer && (
|
||||
<span className="text-xs text-slate-400">
|
||||
← {producer.nameShort}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Outputs */}
|
||||
{step.outputs.length > 0 && (
|
||||
<div>
|
||||
<h4 className="text-xs font-semibold text-slate-500 uppercase mb-1.5">
|
||||
Outputs (SDKState)
|
||||
</h4>
|
||||
<div className="space-y-1">
|
||||
{step.outputs.map(output => (
|
||||
<div
|
||||
key={output}
|
||||
className="text-sm bg-emerald-50 rounded px-2 py-1"
|
||||
>
|
||||
<code className="text-emerald-700 text-xs">{output}</code>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* DB Tables */}
|
||||
{step.dbTables.length > 0 && (
|
||||
<div>
|
||||
<h4 className="text-xs font-semibold text-slate-500 uppercase mb-1.5">
|
||||
DB-Tabellen
|
||||
</h4>
|
||||
<div className="space-y-1">
|
||||
{step.dbTables.map(table => (
|
||||
<div
|
||||
key={table}
|
||||
className="flex items-center justify-between text-sm bg-slate-100 rounded px-2 py-1"
|
||||
>
|
||||
<code className="text-slate-700 text-xs">{table}</code>
|
||||
<span className="text-xs text-slate-400">{step.dbMode}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* RAG Collections */}
|
||||
{step.ragCollections.length > 0 && (
|
||||
<div>
|
||||
<h4 className="text-xs font-semibold text-slate-500 uppercase mb-1.5">
|
||||
RAG-Collections
|
||||
</h4>
|
||||
<div className="space-y-1">
|
||||
{step.ragCollections.map(rag => (
|
||||
<div
|
||||
key={rag}
|
||||
className="text-sm bg-green-50 rounded px-2 py-1"
|
||||
>
|
||||
<code className="text-green-700 text-xs">{rag}</code>
|
||||
</div>
|
||||
))}
|
||||
{step.ragPurpose && (
|
||||
<p className="text-xs text-slate-500 mt-1">{step.ragPurpose}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Checkpoint */}
|
||||
{step.checkpointId && (
|
||||
<div>
|
||||
<h4 className="text-xs font-semibold text-slate-500 uppercase mb-1.5">
|
||||
Checkpoint
|
||||
</h4>
|
||||
<div className="bg-amber-50 rounded p-2 space-y-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<code className="text-xs font-bold text-amber-800">
|
||||
{step.checkpointId}
|
||||
</code>
|
||||
<span
|
||||
className={`px-1.5 py-0.5 rounded text-xs font-medium ${
|
||||
step.checkpointType === 'REQUIRED'
|
||||
? 'bg-red-100 text-red-700'
|
||||
: 'bg-yellow-100 text-yellow-700'
|
||||
}`}
|
||||
>
|
||||
{step.checkpointType}
|
||||
</span>
|
||||
</div>
|
||||
{step.checkpointReviewer && step.checkpointReviewer !== 'NONE' && (
|
||||
<div className="text-xs text-slate-600">
|
||||
Reviewer: <span className="font-medium">{step.checkpointReviewer}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Generated Docs */}
|
||||
{step.generates && step.generates.length > 0 && (
|
||||
<div>
|
||||
<h4 className="text-xs font-semibold text-slate-500 uppercase mb-1.5">
|
||||
Generierte Dokumente
|
||||
</h4>
|
||||
<div className="space-y-1">
|
||||
{step.generates.map(doc => (
|
||||
<div
|
||||
key={doc}
|
||||
className="text-sm bg-violet-50 rounded px-2 py-1 text-violet-700"
|
||||
>
|
||||
{doc}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Prerequisites */}
|
||||
{step.prerequisiteSteps.length > 0 && (
|
||||
<div>
|
||||
<h4 className="text-xs font-semibold text-slate-500 uppercase mb-1.5">
|
||||
Voraussetzungen
|
||||
</h4>
|
||||
<div className="space-y-1">
|
||||
{step.prerequisiteSteps.map(preId => {
|
||||
const preStep = SDK_FLOW_STEPS.find(s => s.id === preId)
|
||||
return (
|
||||
<div
|
||||
key={preId}
|
||||
className="text-sm text-slate-600 bg-slate-50 rounded px-2 py-1"
|
||||
>
|
||||
{preStep?.name || preId}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Open in SDK */}
|
||||
<button
|
||||
onClick={() => window.open(`${baseUrl}${step.url}`, '_blank')}
|
||||
className="w-full mt-2 px-4 py-2 bg-purple-600 text-white rounded-lg text-sm font-medium hover:bg-purple-700 transition-colors"
|
||||
>
|
||||
Im SDK oeffnen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user