fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,264 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import { useCompliance } from '../provider'
|
||||
import { ComplianceScore } from './ComplianceScore'
|
||||
import { RiskMatrix } from './RiskMatrix'
|
||||
|
||||
export interface ComplianceDashboardProps {
|
||||
showScore?: boolean
|
||||
showRisks?: boolean
|
||||
showProgress?: boolean
|
||||
showObligations?: boolean
|
||||
className?: string
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
|
||||
export function ComplianceDashboard({
|
||||
showScore = true,
|
||||
showRisks = true,
|
||||
showProgress = true,
|
||||
showObligations = true,
|
||||
className,
|
||||
style,
|
||||
}: ComplianceDashboardProps) {
|
||||
const { state, compliance, completionPercentage, phase1Completion, phase2Completion } =
|
||||
useCompliance()
|
||||
|
||||
const score = compliance.calculateComplianceScore()
|
||||
const criticalRisks = compliance.getCriticalRisks()
|
||||
const upcomingObligations = compliance.getUpcomingObligations()
|
||||
const overdueObligations = compliance.getOverdueObligations()
|
||||
|
||||
const containerStyle: React.CSSProperties = {
|
||||
fontFamily: 'system-ui, -apple-system, sans-serif',
|
||||
...style,
|
||||
}
|
||||
|
||||
const cardStyle: React.CSSProperties = {
|
||||
backgroundColor: '#fff',
|
||||
borderRadius: '8px',
|
||||
padding: '20px',
|
||||
boxShadow: '0 1px 3px rgba(0, 0, 0, 0.1)',
|
||||
marginBottom: '20px',
|
||||
}
|
||||
|
||||
const gridStyle: React.CSSProperties = {
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(250px, 1fr))',
|
||||
gap: '20px',
|
||||
marginBottom: '20px',
|
||||
}
|
||||
|
||||
const statStyle: React.CSSProperties = {
|
||||
...cardStyle,
|
||||
textAlign: 'center',
|
||||
}
|
||||
|
||||
const statValueStyle: React.CSSProperties = {
|
||||
fontSize: '36px',
|
||||
fontWeight: 700,
|
||||
margin: '10px 0',
|
||||
}
|
||||
|
||||
const statLabelStyle: React.CSSProperties = {
|
||||
fontSize: '14px',
|
||||
color: '#666',
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={containerStyle} className={className}>
|
||||
<h1 style={{ margin: '0 0 20px' }}>Compliance Dashboard</h1>
|
||||
|
||||
{/* Stats Grid */}
|
||||
<div style={gridStyle}>
|
||||
{showScore && (
|
||||
<div style={statStyle}>
|
||||
<div style={statLabelStyle}>Compliance Score</div>
|
||||
<div style={{ ...statValueStyle, color: score.overall >= 70 ? '#16a34a' : '#dc2626' }}>
|
||||
{score.overall}%
|
||||
</div>
|
||||
<div style={statLabelStyle}>
|
||||
Trend:{' '}
|
||||
{score.trend === 'UP' ? '↑ Steigend' : score.trend === 'DOWN' ? '↓ Fallend' : '→ Stabil'}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showProgress && (
|
||||
<>
|
||||
<div style={statStyle}>
|
||||
<div style={statLabelStyle}>Gesamtfortschritt</div>
|
||||
<div style={statValueStyle}>{completionPercentage}%</div>
|
||||
<div style={statLabelStyle}>
|
||||
{state.completedSteps.length} von 19 Schritten abgeschlossen
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={statStyle}>
|
||||
<div style={statLabelStyle}>Phase 1: Assessment</div>
|
||||
<div style={statValueStyle}>{phase1Completion}%</div>
|
||||
<div style={{ height: '8px', backgroundColor: '#e5e5e5', borderRadius: '4px' }}>
|
||||
<div
|
||||
style={{
|
||||
height: '100%',
|
||||
width: `${phase1Completion}%`,
|
||||
backgroundColor: '#3b82f6',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={statStyle}>
|
||||
<div style={statLabelStyle}>Phase 2: Dokumentation</div>
|
||||
<div style={statValueStyle}>{phase2Completion}%</div>
|
||||
<div style={{ height: '8px', backgroundColor: '#e5e5e5', borderRadius: '4px' }}>
|
||||
<div
|
||||
style={{
|
||||
height: '100%',
|
||||
width: `${phase2Completion}%`,
|
||||
backgroundColor: '#8b5cf6',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Main Content Grid */}
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px' }}>
|
||||
{/* Score Breakdown */}
|
||||
{showScore && (
|
||||
<div style={cardStyle}>
|
||||
<h3 style={{ margin: '0 0 15px' }}>Score nach Regulierung</h3>
|
||||
{Object.entries(score.byRegulation).map(([reg, value]) => (
|
||||
<div
|
||||
key={reg}
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
padding: '10px 0',
|
||||
borderBottom: '1px solid #eee',
|
||||
}}
|
||||
>
|
||||
<span>{reg}</span>
|
||||
<span style={{ fontWeight: 600, color: value >= 70 ? '#16a34a' : '#dc2626' }}>
|
||||
{value}%
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Obligations */}
|
||||
{showObligations && (
|
||||
<div style={cardStyle}>
|
||||
<h3 style={{ margin: '0 0 15px' }}>Anstehende Pflichten</h3>
|
||||
|
||||
{overdueObligations.length > 0 && (
|
||||
<div
|
||||
style={{
|
||||
padding: '10px',
|
||||
backgroundColor: '#fef2f2',
|
||||
borderRadius: '4px',
|
||||
marginBottom: '10px',
|
||||
}}
|
||||
>
|
||||
<strong style={{ color: '#dc2626' }}>
|
||||
{overdueObligations.length} überfällige Pflichten!
|
||||
</strong>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{upcomingObligations.length === 0 && overdueObligations.length === 0 ? (
|
||||
<p style={{ color: '#666' }}>Keine anstehenden Pflichten in den nächsten 30 Tagen.</p>
|
||||
) : (
|
||||
<div>
|
||||
{[...overdueObligations, ...upcomingObligations].slice(0, 5).map(o => (
|
||||
<div
|
||||
key={o.id}
|
||||
style={{
|
||||
padding: '10px',
|
||||
borderBottom: '1px solid #eee',
|
||||
}}
|
||||
>
|
||||
<div style={{ fontWeight: 500 }}>{o.title}</div>
|
||||
<div style={{ fontSize: '12px', color: '#666' }}>
|
||||
{o.regulationCode} Art. {o.article}
|
||||
</div>
|
||||
{o.deadline && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: '12px',
|
||||
color: new Date(o.deadline) < new Date() ? '#dc2626' : '#666',
|
||||
}}
|
||||
>
|
||||
Frist: {new Date(o.deadline).toLocaleDateString('de-DE')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Risk Matrix */}
|
||||
{showRisks && state.risks.length > 0 && (
|
||||
<div style={cardStyle}>
|
||||
<h3 style={{ margin: '0 0 15px' }}>Risikomatrix</h3>
|
||||
<RiskMatrix risks={state.risks} />
|
||||
|
||||
{criticalRisks.length > 0 && (
|
||||
<div
|
||||
style={{
|
||||
marginTop: '15px',
|
||||
padding: '10px',
|
||||
backgroundColor: '#fef2f2',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
>
|
||||
<strong style={{ color: '#dc2626' }}>
|
||||
{criticalRisks.length} kritische/hohe Risiken ohne Mitigation
|
||||
</strong>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Controls Summary */}
|
||||
<div style={cardStyle}>
|
||||
<h3 style={{ margin: '0 0 15px' }}>Controls Übersicht</h3>
|
||||
<div style={gridStyle}>
|
||||
<div>
|
||||
<div style={{ fontSize: '24px', fontWeight: 700 }}>{state.controls.length}</div>
|
||||
<div style={{ color: '#666' }}>Gesamt</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ fontSize: '24px', fontWeight: 700, color: '#16a34a' }}>
|
||||
{state.controls.filter(c => c.implementationStatus === 'IMPLEMENTED').length}
|
||||
</div>
|
||||
<div style={{ color: '#666' }}>Implementiert</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ fontSize: '24px', fontWeight: 700, color: '#f59e0b' }}>
|
||||
{state.controls.filter(c => c.implementationStatus === 'PARTIAL').length}
|
||||
</div>
|
||||
<div style={{ color: '#666' }}>Teilweise</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ fontSize: '24px', fontWeight: 700, color: '#dc2626' }}>
|
||||
{state.controls.filter(c => c.implementationStatus === 'NOT_IMPLEMENTED').length}
|
||||
</div>
|
||||
<div style={{ color: '#666' }}>Offen</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user