feat(use-case-workshop): UCCA-Ergebnis-Panel nach Abschluss anzeigen
Some checks failed
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) Failing after 35s
CI / test-python-backend-compliance (push) Successful in 34s
CI / test-python-document-crawler (push) Successful in 25s
CI / test-python-dsms-gateway (push) Successful in 19s
Some checks failed
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) Failing after 35s
CI / test-python-backend-compliance (push) Successful in 34s
CI / test-python-document-crawler (push) Successful in 25s
CI / test-python-dsms-gateway (push) Successful in 19s
Nach Wizard-Abschluss wird ein Ergebnis-Panel angezeigt: - Bei UCCA-API-Erfolg: AssessmentResultCard mit Regeln, Kontrollen, Architektur - Bei API-Fehler: Lokale Risikobewertung mit Score, Massnahmen, Regulations - Badge zeigt Quelle (API vs Lokal) - Nutzer kann Ergebnis pruefen bevor "Use Case speichern" geklickt wird Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useSDK, UseCaseAssessment, UseCaseIntake } from '@/lib/sdk'
|
import { useSDK, UseCaseAssessment, UseCaseIntake } from '@/lib/sdk'
|
||||||
|
import { AssessmentResultCard } from '@/components/sdk/use-case-assessment/AssessmentResultCard'
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// WIZARD STEPS
|
// WIZARD STEPS
|
||||||
@@ -232,6 +233,9 @@ function UseCaseWizard({
|
|||||||
const [currentStep, setCurrentStep] = useState(1)
|
const [currentStep, setCurrentStep] = useState(1)
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||||
const [uccaError, setUccaError] = useState<string | null>(null)
|
const [uccaError, setUccaError] = useState<string | null>(null)
|
||||||
|
const [uccaResult, setUccaResult] = useState<Record<string, unknown> | null>(null)
|
||||||
|
const [completedUseCase, setCompletedUseCase] = useState<UseCaseAssessment | null>(null)
|
||||||
|
const [resultSource, setResultSource] = useState<'api' | 'local'>('local')
|
||||||
const [formData, setFormData] = useState<WizardFormData>({
|
const [formData, setFormData] = useState<WizardFormData>({
|
||||||
name: '',
|
name: '',
|
||||||
description: '',
|
description: '',
|
||||||
@@ -425,16 +429,20 @@ function UseCaseWizard({
|
|||||||
riskLevel: data.result.risk_level || newUseCase.assessmentResult!.riskLevel,
|
riskLevel: data.result.risk_level || newUseCase.assessmentResult!.riskLevel,
|
||||||
dsfaRequired: data.result.dsfa_required ?? newUseCase.assessmentResult!.dsfaRequired,
|
dsfaRequired: data.result.dsfa_required ?? newUseCase.assessmentResult!.dsfaRequired,
|
||||||
}
|
}
|
||||||
|
setUccaResult(data.result)
|
||||||
|
setResultSource('api')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setUccaError('UCCA-API nicht erreichbar — lokale Risikobewertung wird verwendet.')
|
setUccaError('UCCA-API nicht erreichbar — lokale Risikobewertung wird verwendet.')
|
||||||
|
setResultSource('local')
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
setUccaError('UCCA-API nicht erreichbar — lokale Risikobewertung wird verwendet.')
|
setUccaError('UCCA-API nicht erreichbar — lokale Risikobewertung wird verwendet.')
|
||||||
|
setResultSource('local')
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsSubmitting(false)
|
setIsSubmitting(false)
|
||||||
onComplete(newUseCase)
|
setCompletedUseCase(newUseCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleBack = () => {
|
const handleBack = () => {
|
||||||
@@ -443,6 +451,115 @@ function UseCaseWizard({
|
|||||||
|
|
||||||
const risk = calculateRiskScore(formData)
|
const risk = calculateRiskScore(formData)
|
||||||
|
|
||||||
|
// ========== RESULT VIEW (after completion) ==========
|
||||||
|
if (completedUseCase) {
|
||||||
|
return (
|
||||||
|
<div className="space-y-6">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-xl font-bold text-gray-900">Assessment-Ergebnis: {completedUseCase.name}</h2>
|
||||||
|
<p className="mt-1 text-sm text-gray-500">
|
||||||
|
{resultSource === 'api'
|
||||||
|
? 'Regelbasierte Bewertung durch UCCA Policy Engine'
|
||||||
|
: 'Lokale Risikobewertung (UCCA-API nicht verfuegbar)'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className={`px-3 py-1 text-xs rounded-full font-medium ${
|
||||||
|
resultSource === 'api' ? 'bg-green-100 text-green-700' : 'bg-yellow-100 text-yellow-700'
|
||||||
|
}`}>
|
||||||
|
{resultSource === 'api' ? 'UCCA API' : 'Lokal'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* UCCA API Result (detailed) */}
|
||||||
|
{uccaResult && (
|
||||||
|
<AssessmentResultCard result={uccaResult as Parameters<typeof AssessmentResultCard>[0]['result']} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Local result fallback (when API not available) */}
|
||||||
|
{!uccaResult && completedUseCase.assessmentResult && (
|
||||||
|
<div className="bg-white rounded-xl border border-gray-200 p-6 space-y-4">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className={`w-16 h-16 rounded-full flex items-center justify-center text-lg font-bold ${
|
||||||
|
completedUseCase.assessmentResult.riskLevel === 'CRITICAL' ? 'bg-red-100 text-red-700' :
|
||||||
|
completedUseCase.assessmentResult.riskLevel === 'HIGH' ? 'bg-orange-100 text-orange-700' :
|
||||||
|
completedUseCase.assessmentResult.riskLevel === 'MEDIUM' ? 'bg-yellow-100 text-yellow-700' :
|
||||||
|
'bg-green-100 text-green-700'
|
||||||
|
}`}>
|
||||||
|
{risk.score}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="text-lg font-semibold text-gray-900">
|
||||||
|
Risikostufe: {completedUseCase.assessmentResult.riskLevel}
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-gray-500">
|
||||||
|
AI Act: {completedUseCase.assessmentResult.aiActClassification}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{completedUseCase.assessmentResult.dsfaRequired && (
|
||||||
|
<span className="px-3 py-1 text-sm bg-orange-100 text-orange-700 rounded-full ml-auto">
|
||||||
|
DSFA empfohlen
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Recommended Controls */}
|
||||||
|
{completedUseCase.assessmentResult.recommendedControls.length > 0 && (
|
||||||
|
<div>
|
||||||
|
<h4 className="text-sm font-medium text-gray-700 mb-2">Empfohlene Massnahmen</h4>
|
||||||
|
<div className="space-y-1">
|
||||||
|
{completedUseCase.assessmentResult.recommendedControls.map((ctrl, i) => (
|
||||||
|
<div key={i} className="flex items-center gap-2 text-sm text-gray-600">
|
||||||
|
<svg className="w-4 h-4 text-purple-500 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||||
|
</svg>
|
||||||
|
{ctrl}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Applicable Regulations */}
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{completedUseCase.assessmentResult.applicableRegulations.map(reg => (
|
||||||
|
<span key={reg} className="px-2 py-1 text-xs bg-purple-100 text-purple-700 rounded-full">
|
||||||
|
{reg}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* UCCA Error info */}
|
||||||
|
{uccaError && (
|
||||||
|
<div className="bg-blue-50 border border-blue-200 rounded-lg p-3 text-sm text-blue-700">
|
||||||
|
{uccaError}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Action buttons */}
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<button
|
||||||
|
onClick={onCancel}
|
||||||
|
className="px-4 py-2 text-gray-600 hover:text-gray-900"
|
||||||
|
>
|
||||||
|
Schliessen
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => onComplete(completedUseCase)}
|
||||||
|
className="px-6 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 font-medium transition-colors"
|
||||||
|
>
|
||||||
|
Use Case speichern
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
|
|||||||
Reference in New Issue
Block a user