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:
Benjamin Admin
2026-02-09 09:51:32 +01:00
parent f7487ee240
commit bfdaf63ba9
2009 changed files with 749983 additions and 1731 deletions

View File

@@ -4,7 +4,8 @@
* SDK Pipeline Sidebar
*
* Floating Action Button mit Drawer zur Visualisierung der SDK-Pipeline.
* Zeigt die 5 Pakete mit Fortschritt und ermoeglicht schnelle Navigation.
* Zeigt die zwei Phasen (Compliance Assessment & Dokumentengenerierung)
* mit Fortschritt und ermöglicht schnelle Navigation.
*
* Features:
* - Desktop (xl+): Fixierte Sidebar rechts
@@ -14,7 +15,7 @@
import Link from 'next/link'
import { useState, useEffect } from 'react'
import { usePathname } from 'next/navigation'
import { useSDK, SDK_STEPS, SDK_PACKAGES, getStepsForPackage, type SDKStep, type SDKPackageId } from '@/lib/sdk'
import { useSDK, SDK_STEPS, getStepsForPhase, type SDKStep } from '@/lib/sdk'
// =============================================================================
// ICONS
@@ -26,12 +27,6 @@ const CheckIcon = () => (
</svg>
)
const LockIcon = () => (
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
</svg>
)
const ArrowIcon = () => (
<svg className="w-3 h-3 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
@@ -50,6 +45,31 @@ const PipelineIcon = () => (
</svg>
)
// Step Icons als Emojis
const STEP_ICONS: Record<string, string> = {
// Phase 1
'use-case-workshop': '📋',
'screening': '🔍',
'modules': '📦',
'requirements': '📜',
'controls': '🛡️',
'evidence': '📎',
'audit-checklist': '✅',
'risks': '⚠️',
// Phase 2
'ai-act': '🤖',
'obligations': '📑',
'dsfa': '📄',
'tom': '🔒',
'einwilligungen': '✍️',
'loeschfristen': '🗑️',
'vvt': '📊',
'consent': '📝',
'cookie-banner': '🍪',
'dsr': '👤',
'escalations': '🚨',
}
// =============================================================================
// STEP ITEM
// =============================================================================
@@ -62,6 +82,8 @@ interface StepItemProps {
}
function StepItem({ step, isActive, isCompleted, onNavigate }: StepItemProps) {
const icon = STEP_ICONS[step.id] || '•'
return (
<Link
href={step.url}
@@ -74,6 +96,7 @@ function StepItem({ step, isActive, isCompleted, onNavigate }: StepItemProps) {
: 'text-slate-600 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-gray-800'
}`}
>
<span className="text-lg flex-shrink-0">{icon}</span>
<span className="flex-1 text-sm truncate">{step.nameShort}</span>
{isCompleted && !isActive && (
<span className="flex-shrink-0 w-4 h-4 bg-green-500 text-white rounded-full flex items-center justify-center">
@@ -88,91 +111,78 @@ function StepItem({ step, isActive, isCompleted, onNavigate }: StepItemProps) {
}
// =============================================================================
// PACKAGE SECTION
// PHASE SECTION
// =============================================================================
interface PackageSectionProps {
pkg: (typeof SDK_PACKAGES)[number]
interface PhaseSectionProps {
phase: 1 | 2
title: string
steps: SDKStep[]
completion: number
currentStepId: string
completedSteps: string[]
isLocked: boolean
onNavigate: () => void
isExpanded: boolean
onToggle: () => void
}
function PackageSection({
pkg,
function PhaseSection({
phase,
title,
steps,
completion,
currentStepId,
completedSteps,
isLocked,
onNavigate,
isExpanded,
onToggle,
}: PackageSectionProps) {
}: PhaseSectionProps) {
return (
<div className="space-y-2">
{/* Package Header */}
{/* Phase Header */}
<button
onClick={onToggle}
disabled={isLocked}
className={`w-full flex items-center justify-between px-3 py-2 rounded-lg transition-colors ${
isLocked
? 'bg-slate-100 dark:bg-gray-800 opacity-50 cursor-not-allowed'
: 'bg-slate-50 dark:bg-gray-800 hover:bg-slate-100 dark:hover:bg-gray-700'
}`}
className="w-full flex items-center justify-between px-3 py-2 rounded-lg bg-slate-50 dark:bg-gray-800 hover:bg-slate-100 dark:hover:bg-gray-700 transition-colors"
>
<div className="flex items-center gap-2">
<div
className={`w-7 h-7 rounded-full flex items-center justify-center text-sm ${
isLocked
? 'bg-gray-200 text-gray-400'
: completion === 100
className={`w-7 h-7 rounded-full flex items-center justify-center text-sm font-bold ${
completion === 100
? 'bg-green-500 text-white'
: 'bg-purple-600 text-white'
}`}
>
{isLocked ? <LockIcon /> : completion === 100 ? <CheckIcon /> : pkg.icon}
{completion === 100 ? <CheckIcon /> : phase}
</div>
<div className="text-left">
<div className={`text-sm font-medium ${isLocked ? 'text-slate-400' : 'text-slate-700 dark:text-slate-200'}`}>
{pkg.order}. {pkg.nameShort}
</div>
<div className="text-xs text-slate-500 dark:text-slate-400">{completion}%</div>
<div className="text-sm font-medium text-slate-700 dark:text-slate-200">{title}</div>
<div className="text-xs text-slate-500 dark:text-slate-400">{completion}% abgeschlossen</div>
</div>
</div>
{!isLocked && (
<svg
className={`w-4 h-4 text-slate-400 transition-transform ${isExpanded ? 'rotate-180' : ''}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
)}
<svg
className={`w-4 h-4 text-slate-400 transition-transform ${isExpanded ? 'rotate-180' : ''}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
{/* Progress Bar */}
{!isLocked && (
<div className="px-3">
<div className="h-1.5 bg-slate-200 dark:bg-gray-700 rounded-full overflow-hidden">
<div
className={`h-full rounded-full transition-all duration-500 ${
completion === 100 ? 'bg-green-500' : 'bg-purple-600'
}`}
style={{ width: `${completion}%` }}
/>
</div>
<div className="px-3">
<div className="h-1.5 bg-slate-200 dark:bg-gray-700 rounded-full overflow-hidden">
<div
className={`h-full rounded-full transition-all duration-500 ${
completion === 100 ? 'bg-green-500' : 'bg-purple-600'
}`}
style={{ width: `${completion}%` }}
/>
</div>
)}
</div>
{/* Steps List */}
{isExpanded && !isLocked && (
{isExpanded && (
<div className="space-y-1 pl-2">
{steps.map(step => (
<StepItem
@@ -200,16 +210,39 @@ function PipelineFlow() {
Datenfluss
</div>
<div className="p-3 bg-slate-50 dark:bg-gray-900 rounded-lg">
<div className="flex flex-col gap-1.5">
{SDK_PACKAGES.map((pkg, idx) => (
<div key={pkg.id} className="flex items-center gap-2 text-xs">
<span className="w-5 h-5 rounded-full bg-purple-100 dark:bg-purple-900/30 flex items-center justify-center">
{pkg.icon}
</span>
<span className="text-slate-600 dark:text-slate-400 flex-1">{pkg.nameShort}</span>
{idx < SDK_PACKAGES.length - 1 && <ArrowIcon />}
<div className="flex flex-col gap-2">
{/* Phase 1 Flow */}
<div className="flex items-center justify-between text-xs">
<span className="text-purple-600 dark:text-purple-400 font-medium">Phase 1</span>
<div className="flex items-center gap-1">
<span title="Use Case">📋</span>
<ArrowIcon />
<span title="Screening">🔍</span>
<ArrowIcon />
<span title="Risiken"></span>
<ArrowIcon />
<span title="Controls">🛡</span>
</div>
))}
</div>
{/* Arrow Down */}
<div className="flex justify-center">
<svg className="w-4 h-4 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 14l-7 7m0 0l-7-7m7 7V3" />
</svg>
</div>
{/* Phase 2 Flow */}
<div className="flex items-center justify-between text-xs">
<span className="text-indigo-600 dark:text-indigo-400 font-medium">Phase 2</span>
<div className="flex items-center gap-1">
<span title="DSFA">📄</span>
<ArrowIcon />
<span title="TOM">🔒</span>
<ArrowIcon />
<span title="VVT">📊</span>
<ArrowIcon />
<span title="Export"></span>
</div>
</div>
</div>
</div>
</div>
@@ -226,72 +259,60 @@ interface SidebarContentProps {
function SidebarContent({ onNavigate }: SidebarContentProps) {
const pathname = usePathname()
const { state, packageCompletion } = useSDK()
const [expandedPackages, setExpandedPackages] = useState<Record<SDKPackageId, boolean>>({
'vorbereitung': true,
'analyse': false,
'dokumentation': false,
'rechtliche-texte': false,
'betrieb': false,
const { state, phase1Completion, phase2Completion } = useSDK()
const [expandedPhases, setExpandedPhases] = useState<Record<number, boolean>>({
1: true,
2: false,
})
const phase1Steps = getStepsForPhase(1)
const phase2Steps = getStepsForPhase(2)
// Find current step
const currentStep = SDK_STEPS.find(s => s.url === pathname)
const currentStepId = currentStep?.id || ''
// Auto-expand current package
// Auto-expand current phase
useEffect(() => {
if (currentStep) {
setExpandedPackages(prev => ({
setExpandedPhases(prev => ({
...prev,
[currentStep.package]: true,
[currentStep.phase]: true,
}))
}
}, [currentStep])
const togglePackage = (packageId: SDKPackageId) => {
setExpandedPackages(prev => ({ ...prev, [packageId]: !prev[packageId] }))
}
const isPackageLocked = (packageId: SDKPackageId): boolean => {
if (state.preferences?.allowParallelWork) return false
const pkg = SDK_PACKAGES.find(p => p.id === packageId)
if (!pkg || pkg.order === 1) return false
const prevPkg = SDK_PACKAGES.find(p => p.order === pkg.order - 1)
if (!prevPkg) return false
return packageCompletion[prevPkg.id] < 100
}
// Get visible steps based on customer type
const getVisibleSteps = (packageId: SDKPackageId): SDKStep[] => {
const steps = getStepsForPackage(packageId)
return steps.filter(step => {
if (step.id === 'import' && state.customerType === 'new') {
return false
}
return true
})
const togglePhase = (phase: number) => {
setExpandedPhases(prev => ({ ...prev, [phase]: !prev[phase] }))
}
return (
<div className="space-y-4">
{/* Packages */}
{SDK_PACKAGES.map(pkg => (
<PackageSection
key={pkg.id}
pkg={pkg}
steps={getVisibleSteps(pkg.id)}
completion={packageCompletion[pkg.id]}
currentStepId={currentStepId}
completedSteps={state.completedSteps}
isLocked={isPackageLocked(pkg.id)}
onNavigate={onNavigate}
isExpanded={expandedPackages[pkg.id]}
onToggle={() => togglePackage(pkg.id)}
/>
))}
{/* Phase 1 */}
<PhaseSection
phase={1}
title="Compliance Assessment"
steps={phase1Steps}
completion={phase1Completion}
currentStepId={currentStepId}
completedSteps={state.completedSteps}
onNavigate={onNavigate}
isExpanded={expandedPhases[1]}
onToggle={() => togglePhase(1)}
/>
{/* Phase 2 */}
<PhaseSection
phase={2}
title="Dokumentengenerierung"
steps={phase2Steps}
completion={phase2Completion}
currentStepId={currentStepId}
completedSteps={state.completedSteps}
onNavigate={onNavigate}
isExpanded={expandedPhases[2]}
onToggle={() => togglePhase(2)}
/>
{/* Pipeline Flow */}
<PipelineFlow />