06bfbd1dca
Build + Deploy / build-admin-compliance (push) Successful in 2m46s
Build + Deploy / build-backend-compliance (push) Successful in 26s
Build + Deploy / build-ai-sdk (push) Successful in 52s
Build + Deploy / build-developer-portal (push) Successful in 22s
Build + Deploy / build-tts (push) Successful in 16s
Build + Deploy / build-document-crawler (push) Successful in 12s
Build + Deploy / build-dsms-gateway (push) Successful in 20s
Build + Deploy / build-dsms-node (push) Successful in 16s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / loc-budget (push) Failing after 18s
CI / secret-scan (push) Has been skipped
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 3m16s
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / test-go (push) Successful in 1m0s
CI / test-python-backend (push) Successful in 41s
CI / test-python-document-crawler (push) Successful in 29s
CI / test-python-dsms-gateway (push) Successful in 23s
CI / validate-canonical-controls (push) Successful in 16s
Build + Deploy / trigger-orca (push) Successful in 2m36s
Implements the Use-Case Compiler that turns Master Controls into interactive compliance audits. 5 templates (Vendor Check, SAST/DAST, DSGVO, NIS2, CRA), deterministic + LLM question generation, scoring engine with regulation/severity breakdown, and gap detection. - Backend: 9 API endpoints, 22 unit tests (all pass) - Frontend: Template selector, questionnaire, result dashboard - Migration 027: usecase_audits + usecase_answers tables Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
143 lines
5.1 KiB
TypeScript
143 lines
5.1 KiB
TypeScript
'use client'
|
|
|
|
import React from 'react'
|
|
|
|
interface Audit {
|
|
id: string
|
|
name: string
|
|
target_name: string
|
|
total_questions: number
|
|
answered_questions: number
|
|
}
|
|
|
|
interface ScoreResult {
|
|
audit_id: string
|
|
total_questions: number
|
|
answered: number
|
|
passed: number
|
|
failed: number
|
|
skipped: number
|
|
compliance_score: number
|
|
by_regulation: Record<string, { total: number; passed: number; score: number }>
|
|
by_severity: Record<string, { total: number; passed: number; failed: number }>
|
|
}
|
|
|
|
interface Props {
|
|
audit: Audit
|
|
score: ScoreResult
|
|
}
|
|
|
|
function scoreColor(score: number): string {
|
|
if (score >= 80) return 'text-green-600'
|
|
if (score >= 50) return 'text-yellow-600'
|
|
return 'text-red-600'
|
|
}
|
|
|
|
function scoreBg(score: number): string {
|
|
if (score >= 80) return 'bg-green-100 border-green-200'
|
|
if (score >= 50) return 'bg-yellow-100 border-yellow-200'
|
|
return 'bg-red-100 border-red-200'
|
|
}
|
|
|
|
export function AuditResult({ audit, score }: Props) {
|
|
const regEntries = Object.entries(score.by_regulation)
|
|
const sevEntries = Object.entries(score.by_severity)
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Score Overview */}
|
|
<div className={`rounded-xl border p-6 text-center ${scoreBg(score.compliance_score)}`}>
|
|
<div className={`text-5xl font-bold ${scoreColor(score.compliance_score)}`}>
|
|
{Math.round(score.compliance_score)}%
|
|
</div>
|
|
<div className="text-gray-600 mt-2">Compliance Score</div>
|
|
<div className="text-sm text-gray-500 mt-1">
|
|
{audit.name}{audit.target_name ? ` — ${audit.target_name}` : ''}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Summary Stats */}
|
|
<div className="grid grid-cols-4 gap-4">
|
|
<div className="bg-white rounded-xl border border-gray-200 p-4 text-center">
|
|
<div className="text-2xl font-bold text-gray-900">{score.answered}</div>
|
|
<div className="text-xs text-gray-500 mt-1">Beantwortet</div>
|
|
</div>
|
|
<div className="bg-white rounded-xl border border-gray-200 p-4 text-center">
|
|
<div className="text-2xl font-bold text-green-600">{score.passed}</div>
|
|
<div className="text-xs text-gray-500 mt-1">Bestanden</div>
|
|
</div>
|
|
<div className="bg-white rounded-xl border border-gray-200 p-4 text-center">
|
|
<div className="text-2xl font-bold text-red-600">{score.failed}</div>
|
|
<div className="text-xs text-gray-500 mt-1">Nicht bestanden</div>
|
|
</div>
|
|
<div className="bg-white rounded-xl border border-gray-200 p-4 text-center">
|
|
<div className="text-2xl font-bold text-gray-400">{score.skipped}</div>
|
|
<div className="text-xs text-gray-500 mt-1">Uebersprungen</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* By Regulation */}
|
|
{regEntries.length > 0 && (
|
|
<div className="bg-white rounded-xl border border-gray-200 p-5">
|
|
<h3 className="font-semibold text-gray-800 mb-4">Nach Regulierung</h3>
|
|
<div className="space-y-3">
|
|
{regEntries.map(([reg, data]) => (
|
|
<div key={reg} className="flex items-center gap-3">
|
|
<span className="text-sm font-medium text-gray-700 w-32 truncate">{reg}</span>
|
|
<div className="flex-1 h-3 bg-gray-100 rounded-full overflow-hidden">
|
|
<div
|
|
className={`h-full rounded-full transition-all ${
|
|
data.score >= 80 ? 'bg-green-500' :
|
|
data.score >= 50 ? 'bg-yellow-500' : 'bg-red-500'
|
|
}`}
|
|
style={{ width: `${data.score}%` }}
|
|
/>
|
|
</div>
|
|
<span className={`text-sm font-bold w-12 text-right ${scoreColor(data.score)}`}>
|
|
{Math.round(data.score)}%
|
|
</span>
|
|
<span className="text-xs text-gray-400 w-16 text-right">
|
|
{data.passed}/{data.total}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* By Severity */}
|
|
{sevEntries.length > 0 && (
|
|
<div className="bg-white rounded-xl border border-gray-200 p-5">
|
|
<h3 className="font-semibold text-gray-800 mb-4">Nach Schweregrad</h3>
|
|
<div className="grid grid-cols-3 gap-4">
|
|
{sevEntries.map(([sev, data]) => (
|
|
<div key={sev} className="text-center">
|
|
<div className={`text-sm font-medium mb-2 ${
|
|
sev === 'HIGH' ? 'text-red-600' :
|
|
sev === 'MEDIUM' ? 'text-yellow-600' : 'text-green-600'
|
|
}`}>
|
|
{sev}
|
|
</div>
|
|
<div className="text-lg font-bold text-gray-900">{data.passed}/{data.total}</div>
|
|
<div className="text-xs text-gray-400 mt-0.5">
|
|
{data.failed} nicht bestanden
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Actions */}
|
|
<div className="flex items-center gap-3">
|
|
<a
|
|
href="/sdk/use-case-audit"
|
|
className="px-4 py-2.5 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 text-sm font-medium"
|
|
>
|
|
Zurueck zur Liste
|
|
</a>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|