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 21a844cb8a
1986 changed files with 744143 additions and 1731 deletions

View File

@@ -0,0 +1,180 @@
"""
School Module - Parent Onboarding Page
QR code landing page for parent registration
"""
from ..styles import SCHOOL_BASE_STYLES, ONBOARDING_STYLES
from ..templates import COMMON_SCRIPTS
def parent_onboarding() -> str:
"""Parent onboarding page (QR code landing)"""
# Onboarding page uses its own simplified styles without sidebar
styles = f"""
:root {{
--bp-primary: #6C1B1B;
--bp-bg: #F8F8F8;
--bp-surface: #FFFFFF;
--bp-text: #4A4A4A;
--bp-text-muted: #6B6B6B;
--bp-accent: #5ABF60;
--bp-border: #E0E0E0;
}}
* {{ box-sizing: border-box; margin: 0; padding: 0; }}
{ONBOARDING_STYLES}
"""
content = '''
<div class="onboarding-container">
<div class="logo">
<div class="logo-icon">BP</div>
<span class="logo-text">BreakPilot</span>
</div>
<div id="loading">
<div class="spinner"></div>
<p>QR-Code wird validiert...</p>
</div>
<div id="content" style="display: none;">
<h1>Eltern-Registrierung</h1>
<p class="subtitle">Sie wurden eingeladen, sich für die Schulkommunikation zu registrieren.</p>
<div id="error" class="error" style="display: none;"></div>
<div id="success" class="success" style="display: none;"></div>
<div class="info-card">
<div class="info-row">
<span class="info-label">Schule</span>
<span class="info-value" id="school-name">-</span>
</div>
<div class="info-row">
<span class="info-label">Klasse</span>
<span class="info-value" id="class-name">-</span>
</div>
<div class="info-row">
<span class="info-label">Kind</span>
<span class="info-value" id="student-name">-</span>
</div>
<div class="info-row">
<span class="info-label">Ihre Rolle</span>
<span class="info-value" id="role">Elternteil</span>
</div>
</div>
<div class="checkbox-group">
<div class="checkbox-item">
<input type="checkbox" id="consent-terms" required>
<label class="checkbox-label" for="consent-terms">
Ich habe die <a href="/terms" target="_blank">Nutzungsbedingungen</a> gelesen und akzeptiere diese.
</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="consent-privacy" required>
<label class="checkbox-label" for="consent-privacy">
Ich habe die <a href="/privacy" target="_blank">Datenschutzerklärung</a> gelesen und stimme der Verarbeitung meiner Daten zu.
</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="consent-custody" required>
<label class="checkbox-label" for="consent-custody">
Ich bestätige, dass ich sorgeberechtigt für das oben genannte Kind bin.
</label>
</div>
</div>
<button class="btn btn-primary" id="submit-btn" onclick="completeOnboarding()" disabled>
Registrierung abschließen
</button>
</div>
</div>'''
scripts = '''
<script>
const API_BASE = '/api/v1';
let tokenData = null;
const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get('token');
async function validateToken() {
if (!token) {
showError('Kein gültiger QR-Code. Bitte scannen Sie den QR-Code erneut.');
return;
}
try {
const response = await fetch(API_BASE + '/onboarding/validate?token=' + token);
const data = await response.json();
if (!response.ok || !data.valid) {
showError('Der QR-Code ist ungültig oder abgelaufen. Bitte wenden Sie sich an den Klassenlehrer.');
return;
}
tokenData = data;
document.getElementById('school-name').textContent = data.school_name;
document.getElementById('class-name').textContent = data.class_name;
document.getElementById('student-name').textContent = data.student_name;
document.getElementById('role').textContent = data.role === 'parent_representative' ? 'Elternvertreter' : 'Elternteil';
document.getElementById('loading').style.display = 'none';
document.getElementById('content').style.display = 'block';
} catch (error) {
showError('Ein Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.');
}
}
function showError(message) {
document.getElementById('loading').style.display = 'none';
document.getElementById('content').style.display = 'block';
document.getElementById('error').textContent = message;
document.getElementById('error').style.display = 'block';
}
document.querySelectorAll('input[type="checkbox"]').forEach(cb => {
cb.addEventListener('change', () => {
const allChecked = document.querySelectorAll('input[type="checkbox"]:checked').length === 3;
document.getElementById('submit-btn').disabled = !allChecked;
});
});
async function completeOnboarding() {
const btn = document.getElementById('submit-btn');
btn.disabled = true;
btn.textContent = 'Wird verarbeitet...';
try {
const returnUrl = encodeURIComponent(window.location.href);
window.location.href = '/app/login?return_to=' + returnUrl + '&onboarding=true';
} catch (error) {
showError('Ein Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.');
btn.disabled = false;
btn.textContent = 'Registrierung abschließen';
}
}
validateToken();
</script>'''
# This page doesn't use the standard base template with sidebar
return f'''<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>BreakPilot Eltern-Onboarding</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
{styles}
</style>
</head>
<body>
{content}
{scripts}
</body>
</html>'''