'use client' /** * Magic Help Admin Page * * Comprehensive admin interface for TrOCR Handwriting Recognition and Exam Correction. * Features: * - Model status monitoring * - OCR testing with image upload * - Training data management * - Fine-tuning controls * - Architecture documentation * - Configuration settings */ import { useState, useEffect, useCallback } from 'react' import AdminLayout from '@/components/admin/AdminLayout' type TabId = 'overview' | 'test' | 'training' | 'architecture' | 'settings' interface TrOCRStatus { status: 'available' | 'not_installed' | 'error' model_name?: string model_id?: string device?: string is_loaded?: boolean has_lora_adapter?: boolean training_examples_count?: number error?: string install_command?: string } interface OCRResult { text: string confidence: number processing_time_ms: number model: string has_lora_adapter: boolean } interface TrainingExample { image_path: string ground_truth: string teacher_id: string created_at: string } interface MagicSettings { autoDetectLines: boolean confidenceThreshold: number maxImageSize: number loraRank: number loraAlpha: number learningRate: number epochs: number batchSize: number enableCache: boolean cacheMaxAge: number } const DEFAULT_SETTINGS: MagicSettings = { autoDetectLines: true, confidenceThreshold: 0.7, maxImageSize: 4096, loraRank: 8, loraAlpha: 32, learningRate: 0.00005, epochs: 3, batchSize: 4, enableCache: true, cacheMaxAge: 3600, } export default function MagicHelpPage() { const [activeTab, setActiveTab] = useState('overview') const [status, setStatus] = useState(null) const [loading, setLoading] = useState(true) const [ocrResult, setOcrResult] = useState(null) const [ocrLoading, setOcrLoading] = useState(false) const [examples, setExamples] = useState([]) const [trainingImage, setTrainingImage] = useState(null) const [trainingText, setTrainingText] = useState('') const [fineTuning, setFineTuning] = useState(false) const [settings, setSettings] = useState(DEFAULT_SETTINGS) const [settingsSaved, setSettingsSaved] = useState(false) const fetchStatus = useCallback(async () => { try { const res = await fetch('/api/klausur/trocr/status') const data = await res.json() setStatus(data) } catch { setStatus({ status: 'error', error: 'Failed to fetch status' }) } finally { setLoading(false) } }, []) const fetchExamples = useCallback(async () => { try { const res = await fetch('/api/klausur/trocr/training/examples') const data = await res.json() setExamples(data.examples || []) } catch (error) { console.error('Failed to fetch examples:', error) } }, []) useEffect(() => { fetchStatus() fetchExamples() // Load settings from localStorage const saved = localStorage.getItem('magic-help-settings') if (saved) { try { setSettings(JSON.parse(saved)) } catch { // ignore parse errors } } }, [fetchStatus, fetchExamples]) const handleFileUpload = async (file: File) => { setOcrLoading(true) setOcrResult(null) const formData = new FormData() formData.append('file', file) try { const res = await fetch(`/api/klausur/trocr/extract?detect_lines=${settings.autoDetectLines}`, { method: 'POST', body: formData, }) const data = await res.json() if (data.text !== undefined) { setOcrResult(data) } else { setOcrResult({ text: `Error: ${data.detail || 'Unknown error'}`, confidence: 0, processing_time_ms: 0, model: '', has_lora_adapter: false }) } } catch (error) { setOcrResult({ text: `Error: ${error}`, confidence: 0, processing_time_ms: 0, model: '', has_lora_adapter: false }) } finally { setOcrLoading(false) } } const handleAddTrainingExample = async () => { if (!trainingImage || !trainingText.trim()) { alert('Please provide both an image and the correct text') return } const formData = new FormData() formData.append('file', trainingImage) try { const res = await fetch(`/api/klausur/trocr/training/add?ground_truth=${encodeURIComponent(trainingText)}`, { method: 'POST', body: formData, }) const data = await res.json() if (data.example_id) { alert(`Training example added! Total: ${data.total_examples}`) setTrainingImage(null) setTrainingText('') fetchStatus() fetchExamples() } else { alert(`Error: ${data.detail || 'Unknown error'}`) } } catch (error) { alert(`Error: ${error}`) } } const handleFineTune = async () => { if (!confirm('Start fine-tuning? This may take several minutes.')) return setFineTuning(true) try { const res = await fetch('/api/klausur/trocr/training/fine-tune', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ epochs: settings.epochs, learning_rate: settings.learningRate, lora_rank: settings.loraRank, lora_alpha: settings.loraAlpha, }), }) const data = await res.json() if (data.status === 'success') { alert(`Fine-tuning successful!\nExamples used: ${data.examples_used}\nEpochs: ${data.epochs}`) fetchStatus() } else { alert(`Fine-tuning failed: ${data.message}`) } } catch (error) { alert(`Error: ${error}`) } finally { setFineTuning(false) } } const saveSettings = () => { localStorage.setItem('magic-help-settings', JSON.stringify(settings)) setSettingsSaved(true) setTimeout(() => setSettingsSaved(false), 2000) } const getStatusBadge = () => { if (!status) return null switch (status.status) { case 'available': return Available case 'not_installed': return Not Installed case 'error': return Error } } const tabs = [ { id: 'overview' as TabId, label: 'Übersicht', icon: 'πŸ“Š' }, { id: 'test' as TabId, label: 'OCR Test', icon: 'πŸ”' }, { id: 'training' as TabId, label: 'Training', icon: '🎯' }, { id: 'architecture' as TabId, label: 'Architektur', icon: 'πŸ—οΈ' }, { id: 'settings' as TabId, label: 'Einstellungen', icon: 'βš™οΈ' }, ] return (
{/* Header */}

✨ Magic Help - Handschrifterkennung

KI-gestΓΌtzte Klausurkorrektur mit TrOCR und Privacy-by-Design

{getStatusBadge()}
{/* Tabs */}
{tabs.map((tab) => ( ))}
{/* Tab Content */} {activeTab === 'overview' && (
{/* Status Card */}

Systemstatus

{loading ? (
Lade Status...
) : status?.status === 'available' ? (
{status.model_name || 'trocr-base'}
Modell
{status.device || 'CPU'}
GerΓ€t
{status.training_examples_count || 0}
Trainingsbeispiele
{status.has_lora_adapter ? 'Aktiv' : 'Keiner'}
LoRA Adapter
) : status?.status === 'not_installed' ? (

TrOCR ist nicht installiert. FΓΌhre aus:

{status.install_command}
) : (
{status?.error || 'Unbekannter Fehler'}
)}
{/* Quick Overview Cards */}
🎯

Handschrifterkennung

TrOCR erkennt automatisch handgeschriebenen Text in Klausuren. Das Modell wurde speziell fΓΌr deutsche Handschriften optimiert.

πŸ”’

Privacy by Design

Alle Daten werden lokal verarbeitet. SchΓΌlernamen werden durch QR-Codes pseudonymisiert - DSGVO-konform.

πŸ“ˆ

Kontinuierliches Lernen

Mit LoRA Fine-Tuning passt sich das Modell an individuelle Handschriften an - ohne das Basismodell zu verΓ€ndern.

{/* Workflow Overview */}

Magic Onboarding Workflow

πŸ“„
1. Upload
25 Klausuren hochladen
β†’
πŸ”
2. Analyse
Lokale OCR in 5-10 Sek
β†’
βœ…
3. BestΓ€tigung
Klasse, SchΓΌler, Fach
β†’
πŸ€–
4. KI-Korrektur
Cloud mit Pseudonymisierung
β†’
πŸ“Š
5. Integration
Notenbuch, Zeugnisse
)} {activeTab === 'test' && (
{/* OCR Test */}

OCR Test

Teste die Handschrifterkennung mit einem eigenen Bild. Das Ergebnis zeigt den erkannten Text, Konfidenz und Verarbeitungszeit.

document.getElementById('ocr-file-input')?.click()} onDragOver={(e) => { e.preventDefault(); e.currentTarget.classList.add('border-blue-500') }} onDragLeave={(e) => { e.currentTarget.classList.remove('border-blue-500') }} onDrop={(e) => { e.preventDefault() e.currentTarget.classList.remove('border-blue-500') const file = e.dataTransfer.files[0] if (file?.type.startsWith('image/')) handleFileUpload(file) }} >
πŸ“„
Bild hierher ziehen oder klicken zum Hochladen
PNG, JPG - Handgeschriebener Text
{ const file = e.target.files?.[0] if (file) handleFileUpload(file) }} /> {ocrLoading && (
Analysiere Bild...
)} {ocrResult && (

Erkannter Text:

                    {ocrResult.text || '(Kein Text erkannt)'}
                  
Konfidenz
{(ocrResult.confidence * 100).toFixed(1)}%
Verarbeitungszeit
{ocrResult.processing_time_ms}ms
Modell
{ocrResult.model || 'TrOCR'}
LoRA Adapter
{ocrResult.has_lora_adapter ? 'Ja' : 'Nein'}
)}
{/* Confidence Interpretation */}

Konfidenz-Interpretation

90-100%
Sehr hohe Sicherheit - Text kann direkt ΓΌbernommen werden
70-90%
Gute Sicherheit - manuelle Überprüfung empfohlen
< 70%
Niedrige Sicherheit - manuelle Eingabe erforderlich
)} {activeTab === 'training' && (
{/* Training Overview */}

Training mit LoRA

LoRA (Low-Rank Adaptation) ermΓΆglicht effizientes Fine-Tuning ohne das Basismodell zu verΓ€ndern. Das Training erfolgt lokal auf Ihrem System.

{status?.training_examples_count || 0}
Trainingsbeispiele
10
Minimum benΓΆtigt
{settings.loraRank}
LoRA Rank
{status?.has_lora_adapter ? 'βœ“' : 'βœ—'}
Adapter aktiv
{/* Progress Bar */}
Fortschritt zum Fine-Tuning {Math.min(100, ((status?.training_examples_count || 0) / 10) * 100).toFixed(0)}%
{/* Add Training Example */}

Trainingsbeispiel hinzufΓΌgen

Lade ein Bild mit handgeschriebenem Text hoch und gib die korrekte Transkription ein.

setTrainingImage(e.target.files?.[0] || null)} />