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 37s
CI / test-python-backend-compliance (push) Successful in 32s
CI / test-python-document-crawler (push) Successful in 22s
CI / test-python-dsms-gateway (push) Successful in 18s
Phase A: PostgreSQL State Store (sdk_states Tabelle, InMemory-Fallback) Phase B: Modules dynamisch vom Backend, Scope DB-Persistenz, Source Policy State Phase C: UCCA Frontend (3 Seiten, Wizard, RiskScoreGauge), Obligations Live-Daten Phase D: Document Import (PDF/LLM/Gap-Analyse), System Screening (SBOM/OSV.dev) Phase E: Company Profile CRUD mit Audit-Logging Phase F: Tests (Python + TypeScript), flow-data.ts DB-Tabellen aktualisiert Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
86 lines
2.2 KiB
TypeScript
86 lines
2.2 KiB
TypeScript
'use client'
|
|
|
|
interface RiskScoreGaugeProps {
|
|
score: number // 0-100
|
|
riskLevel: string
|
|
size?: 'sm' | 'md' | 'lg'
|
|
}
|
|
|
|
const RISK_COLORS: Record<string, string> = {
|
|
MINIMAL: '#22c55e',
|
|
LOW: '#84cc16',
|
|
MEDIUM: '#eab308',
|
|
HIGH: '#f97316',
|
|
UNACCEPTABLE: '#ef4444',
|
|
}
|
|
|
|
const RISK_LABELS: Record<string, string> = {
|
|
MINIMAL: 'Minimal',
|
|
LOW: 'Niedrig',
|
|
MEDIUM: 'Mittel',
|
|
HIGH: 'Hoch',
|
|
UNACCEPTABLE: 'Unzulaessig',
|
|
}
|
|
|
|
export function RiskScoreGauge({ score, riskLevel, size = 'md' }: RiskScoreGaugeProps) {
|
|
const color = RISK_COLORS[riskLevel] || '#9ca3af'
|
|
const label = RISK_LABELS[riskLevel] || riskLevel
|
|
|
|
const sizes = {
|
|
sm: { w: 80, r: 30, stroke: 6, fontSize: '1rem', labelSize: '0.65rem' },
|
|
md: { w: 120, r: 46, stroke: 8, fontSize: '1.5rem', labelSize: '0.75rem' },
|
|
lg: { w: 160, r: 62, stroke: 10, fontSize: '2rem', labelSize: '0.875rem' },
|
|
}
|
|
|
|
const s = sizes[size]
|
|
const circumference = 2 * Math.PI * s.r
|
|
const dashOffset = circumference - (score / 100) * circumference
|
|
|
|
return (
|
|
<div className="flex flex-col items-center">
|
|
<svg width={s.w} height={s.w} viewBox={`0 0 ${s.w} ${s.w}`}>
|
|
{/* Background circle */}
|
|
<circle
|
|
cx={s.w / 2}
|
|
cy={s.w / 2}
|
|
r={s.r}
|
|
fill="none"
|
|
stroke="#e5e7eb"
|
|
strokeWidth={s.stroke}
|
|
/>
|
|
{/* Score arc */}
|
|
<circle
|
|
cx={s.w / 2}
|
|
cy={s.w / 2}
|
|
r={s.r}
|
|
fill="none"
|
|
stroke={color}
|
|
strokeWidth={s.stroke}
|
|
strokeDasharray={circumference}
|
|
strokeDashoffset={dashOffset}
|
|
strokeLinecap="round"
|
|
transform={`rotate(-90 ${s.w / 2} ${s.w / 2})`}
|
|
className="transition-all duration-500"
|
|
/>
|
|
{/* Score text */}
|
|
<text
|
|
x={s.w / 2}
|
|
y={s.w / 2}
|
|
textAnchor="middle"
|
|
dominantBaseline="central"
|
|
fill={color}
|
|
style={{ fontSize: s.fontSize, fontWeight: 700 }}
|
|
>
|
|
{score}
|
|
</text>
|
|
</svg>
|
|
<span
|
|
className="mt-1 font-medium"
|
|
style={{ color, fontSize: s.labelSize }}
|
|
>
|
|
{label}
|
|
</span>
|
|
</div>
|
|
)
|
|
}
|