feat: Fertigstellungsgrad für Betrieb-Module im SDK Flow
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 35s
CI / test-python-backend-compliance (push) Successful in 30s
CI / test-python-document-crawler (push) Successful in 22s
CI / test-python-dsms-gateway (push) Successful in 17s
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 35s
CI / test-python-backend-compliance (push) Successful in 30s
CI / test-python-document-crawler (push) Successful in 22s
CI / test-python-dsms-gateway (push) Successful in 17s
flow-data.ts: - completion?: number Feld im SDKFlowStep Interface - Completion-Werte für 7 Betrieb-Module (Academy/Training ausgenommen): consent-management: 75%, dsr: 65%, whistleblower: 60%, incidents: 55%, notfallplan: 50%, vendor-compliance: 35%, escalations: 30% - Bewertungsbasis: Frontend-Reifegrad + API-Abdeckung + Backend-Persistenz page.tsx: - completionColor() + completionLabel() Hilfsfunktionen (4-stufige Farbskala) - BetriebOverviewPanel: Übersicht mit Balken + Ø-Wert (erscheint bei Filter=Betrieb) - DetailPanel: Fertigstellungsgrad-Abschnitt ganz oben (Balken + Label) - ReactFlow-Nodes: % + Mini-Fortschrittsbalken im Node-Label - Step-Tabelle: 24px-Fortschrittsbalken-Spalte für alle Steps mit completion Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,13 @@ export interface SDKFlowStep {
|
|||||||
generates?: string[]
|
generates?: string[]
|
||||||
isOptional?: boolean
|
isOptional?: boolean
|
||||||
url: string
|
url: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fertigstellungsgrad in Prozent (0–100).
|
||||||
|
* Nur für Betrieb-Module gesetzt (außer academy/training).
|
||||||
|
* Berechnung: Frontend-Reifegrad + Backend-Persistenz + API-Abdeckung.
|
||||||
|
*/
|
||||||
|
completion?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
@@ -632,6 +639,7 @@ export const SDK_FLOW_STEPS: SDKFlowStep[] = [
|
|||||||
ragPurpose: 'Art. 15-21 DSGVO Betroffenenrechte',
|
ragPurpose: 'Art. 15-21 DSGVO Betroffenenrechte',
|
||||||
isOptional: false,
|
isOptional: false,
|
||||||
url: '/sdk/dsr',
|
url: '/sdk/dsr',
|
||||||
|
completion: 65,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'escalations',
|
id: 'escalations',
|
||||||
@@ -653,6 +661,7 @@ export const SDK_FLOW_STEPS: SDKFlowStep[] = [
|
|||||||
ragCollections: [],
|
ragCollections: [],
|
||||||
isOptional: false,
|
isOptional: false,
|
||||||
url: '/sdk/escalations',
|
url: '/sdk/escalations',
|
||||||
|
completion: 30,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'vendor-compliance',
|
id: 'vendor-compliance',
|
||||||
@@ -675,6 +684,7 @@ export const SDK_FLOW_STEPS: SDKFlowStep[] = [
|
|||||||
ragPurpose: 'AVV-Vorlagen und Pruefkataloge',
|
ragPurpose: 'AVV-Vorlagen und Pruefkataloge',
|
||||||
isOptional: false,
|
isOptional: false,
|
||||||
url: '/sdk/vendor-compliance',
|
url: '/sdk/vendor-compliance',
|
||||||
|
completion: 35,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'consent-management',
|
id: 'consent-management',
|
||||||
@@ -696,6 +706,7 @@ export const SDK_FLOW_STEPS: SDKFlowStep[] = [
|
|||||||
ragCollections: [],
|
ragCollections: [],
|
||||||
isOptional: false,
|
isOptional: false,
|
||||||
url: '/sdk/consent-management',
|
url: '/sdk/consent-management',
|
||||||
|
completion: 75,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'notfallplan',
|
id: 'notfallplan',
|
||||||
@@ -718,6 +729,7 @@ export const SDK_FLOW_STEPS: SDKFlowStep[] = [
|
|||||||
generates: ['Notfallplan (PDF)'],
|
generates: ['Notfallplan (PDF)'],
|
||||||
isOptional: false,
|
isOptional: false,
|
||||||
url: '/sdk/notfallplan',
|
url: '/sdk/notfallplan',
|
||||||
|
completion: 50,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'incidents',
|
id: 'incidents',
|
||||||
@@ -739,6 +751,7 @@ export const SDK_FLOW_STEPS: SDKFlowStep[] = [
|
|||||||
ragCollections: [],
|
ragCollections: [],
|
||||||
isOptional: false,
|
isOptional: false,
|
||||||
url: '/sdk/incidents',
|
url: '/sdk/incidents',
|
||||||
|
completion: 55,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'whistleblower',
|
id: 'whistleblower',
|
||||||
@@ -760,6 +773,7 @@ export const SDK_FLOW_STEPS: SDKFlowStep[] = [
|
|||||||
ragCollections: [],
|
ragCollections: [],
|
||||||
isOptional: false,
|
isOptional: false,
|
||||||
url: '/sdk/whistleblower',
|
url: '/sdk/whistleblower',
|
||||||
|
completion: 60,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'academy',
|
id: 'academy',
|
||||||
|
|||||||
@@ -77,6 +77,98 @@ function getStepPosition(step: SDKFlowStep): { x: number; y: number } {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// HELPERS — FERTIGSTELLUNGSGRAD
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
function completionColor(pct: number): string {
|
||||||
|
if (pct >= 70) return '#22c55e' // green
|
||||||
|
if (pct >= 50) return '#84cc16' // lime
|
||||||
|
if (pct >= 35) return '#f59e0b' // amber
|
||||||
|
return '#ef4444' // red
|
||||||
|
}
|
||||||
|
|
||||||
|
function completionLabel(pct: number): string {
|
||||||
|
if (pct >= 70) return 'Fortgeschritten'
|
||||||
|
if (pct >= 50) return 'In Entwicklung'
|
||||||
|
if (pct >= 35) return 'Begonnen'
|
||||||
|
return 'Frühe Phase'
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// BETRIEB OVERVIEW PANEL
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
function BetriebOverviewPanel() {
|
||||||
|
const betriebWithCompletion = SDK_FLOW_STEPS.filter(
|
||||||
|
s => s.package === 'betrieb' && s.completion !== undefined
|
||||||
|
).sort((a, b) => (b.completion ?? 0) - (a.completion ?? 0))
|
||||||
|
|
||||||
|
const avg = Math.round(
|
||||||
|
betriebWithCompletion.reduce((sum, s) => sum + (s.completion ?? 0), 0) /
|
||||||
|
betriebWithCompletion.length
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-white rounded-xl border border-slate-200 shadow-sm p-5">
|
||||||
|
<div className="flex items-center justify-between mb-4">
|
||||||
|
<div>
|
||||||
|
<h3 className="font-semibold text-slate-800">
|
||||||
|
⚙️ Betrieb — Fertigstellungsgrad
|
||||||
|
</h3>
|
||||||
|
<p className="text-xs text-slate-400 mt-0.5">
|
||||||
|
{betriebWithCompletion.length} Module bewertet · Academy & Training ausgeschlossen
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="text-right">
|
||||||
|
<div className="text-2xl font-bold" style={{ color: completionColor(avg) }}>
|
||||||
|
{avg}%
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-slate-400">Ø Fertigstellung</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-3">
|
||||||
|
{betriebWithCompletion.map(step => {
|
||||||
|
const pct = step.completion ?? 0
|
||||||
|
const color = completionColor(pct)
|
||||||
|
return (
|
||||||
|
<div key={step.id}>
|
||||||
|
<div className="flex items-center justify-between mb-1">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-sm font-medium text-slate-700">{step.name}</span>
|
||||||
|
<span
|
||||||
|
className="px-1.5 py-0.5 rounded text-[10px] font-medium"
|
||||||
|
style={{ background: color + '22', color }}
|
||||||
|
>
|
||||||
|
{completionLabel(pct)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-sm font-bold" style={{ color }}>{pct}%</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-2 bg-slate-100 rounded-full overflow-hidden">
|
||||||
|
<div
|
||||||
|
className="h-full rounded-full transition-all"
|
||||||
|
style={{ width: `${pct}%`, background: color }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3 mt-1 text-[10px] text-slate-400">
|
||||||
|
{step.checkpointId && (
|
||||||
|
<span className="bg-red-50 text-red-500 px-1 rounded">{step.checkpointId}</span>
|
||||||
|
)}
|
||||||
|
<span>{step.dbTables.length > 0 ? `DB: ${step.dbTables.length} Tabellen` : 'Kein DB-Backend'}</span>
|
||||||
|
{step.ragCollections.length > 0 && (
|
||||||
|
<span className="text-green-600">RAG: {step.ragCollections.length}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// DETAIL PANEL
|
// DETAIL PANEL
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
@@ -120,6 +212,38 @@ function DetailPanel({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="p-4 space-y-4">
|
<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 */}
|
{/* Beschreibung */}
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-slate-700 leading-relaxed">{step.description}</p>
|
<p className="text-sm text-slate-700 leading-relaxed">{step.description}</p>
|
||||||
@@ -359,6 +483,28 @@ export default function SDKFlowPage() {
|
|||||||
{step.checkpointId || ''}
|
{step.checkpointId || ''}
|
||||||
{badge}
|
{badge}
|
||||||
</div>
|
</div>
|
||||||
|
{step.completion !== undefined && (
|
||||||
|
<div className="mt-1.5 px-1">
|
||||||
|
<div
|
||||||
|
className="flex items-center justify-center gap-1 text-[9px] font-bold mb-0.5"
|
||||||
|
style={{ color: isSelected ? 'rgba(255,255,255,0.9)' : completionColor(step.completion) }}
|
||||||
|
>
|
||||||
|
{step.completion}%
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="h-1 rounded-full overflow-hidden"
|
||||||
|
style={{ background: isSelected ? 'rgba(255,255,255,0.25)' : 'rgba(0,0,0,0.1)' }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="h-full rounded-full"
|
||||||
|
style={{
|
||||||
|
width: `${step.completion}%`,
|
||||||
|
background: isSelected ? 'rgba(255,255,255,0.8)' : completionColor(step.completion),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -771,6 +917,9 @@ export default function SDKFlowPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Betrieb Fertigstellungsgrad Übersicht */}
|
||||||
|
{packageFilter === 'betrieb' && <BetriebOverviewPanel />}
|
||||||
|
|
||||||
{/* Step Table (below flow) */}
|
{/* Step Table (below flow) */}
|
||||||
<div className="bg-white rounded-xl border border-slate-200 shadow-sm overflow-hidden">
|
<div className="bg-white rounded-xl border border-slate-200 shadow-sm overflow-hidden">
|
||||||
<div className="px-4 py-3 bg-slate-50 border-b">
|
<div className="px-4 py-3 bg-slate-50 border-b">
|
||||||
@@ -831,6 +980,29 @@ export default function SDKFlowPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{step.completion !== undefined ? (
|
||||||
|
<div className="shrink-0 w-24 mr-2">
|
||||||
|
<div className="flex items-center justify-between mb-0.5">
|
||||||
|
<span
|
||||||
|
className="text-[10px] font-bold"
|
||||||
|
style={{ color: completionColor(step.completion) }}
|
||||||
|
>
|
||||||
|
{step.completion}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-1.5 bg-slate-100 rounded-full overflow-hidden">
|
||||||
|
<div
|
||||||
|
className="h-full rounded-full"
|
||||||
|
style={{
|
||||||
|
width: `${step.completion}%`,
|
||||||
|
background: completionColor(step.completion),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="shrink-0 w-24 mr-2" />
|
||||||
|
)}
|
||||||
<span
|
<span
|
||||||
className="px-2 py-0.5 rounded text-[10px] font-medium shrink-0"
|
className="px-2 py-0.5 rounded text-[10px] font-medium shrink-0"
|
||||||
style={{ background: pkg.color.bg, color: pkg.color.text }}
|
style={{ background: pkg.color.bg, color: pkg.color.text }}
|
||||||
|
|||||||
Reference in New Issue
Block a user