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>
258 lines
8.7 KiB
TypeScript
258 lines
8.7 KiB
TypeScript
'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>
|
|
)
|
|
}
|