Initial commit: breakpilot-lehrer - Lehrer KI Platform

Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website,
Klausur-Service, School-Service, Voice-Service, Geo-Service,
BreakPilot Drive, Agent-Core

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Boenisch
2026-02-11 23:47:26 +01:00
commit 5a31f52310
1224 changed files with 425430 additions and 0 deletions

View File

@@ -0,0 +1,247 @@
import { useState, useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
import { useKlausur } from '../hooks/useKlausur'
type Step = 'action' | 'type' | 'class'
type Action = 'korrigieren' | 'erstellen'
type ExamType = 'vorabitur' | 'abitur'
interface SchoolClass {
id: string
name: string
student_count: number
}
export default function OnboardingPage() {
const navigate = useNavigate()
const { createKlausur, loadKlausuren, klausuren } = useKlausur()
const [step, setStep] = useState<Step>('action')
const [action, setAction] = useState<Action | null>(null)
const [examType, setExamType] = useState<ExamType | null>(null)
const [selectedClass, setSelectedClass] = useState<SchoolClass | null>(null)
const [classes, setClasses] = useState<SchoolClass[]>([])
const [loading, setLoading] = useState(false)
// Load existing klausuren on mount
useEffect(() => {
loadKlausuren()
}, [loadKlausuren])
// Load classes when reaching step 3
useEffect(() => {
if (step === 'class') {
loadClasses()
}
}, [step])
const loadClasses = async () => {
try {
// Try to load from school-service via backend proxy
const resp = await fetch('/api/school/classes')
if (resp.ok) {
const data = await resp.json()
setClasses(data)
} else {
// If no school service available, use demo data
setClasses([
{ id: 'demo-q1', name: 'Q1 Deutsch GK', student_count: 24 },
{ id: 'demo-q2', name: 'Q2 Deutsch LK', student_count: 18 },
{ id: 'demo-q3', name: 'Q3 Deutsch GK', student_count: 22 }
])
}
} catch (e) {
console.error('Failed to load classes:', e)
// Use demo data on error
setClasses([
{ id: 'demo-q1', name: 'Q1 Deutsch GK', student_count: 24 },
{ id: 'demo-q2', name: 'Q2 Deutsch LK', student_count: 18 },
{ id: 'demo-q3', name: 'Q3 Deutsch GK', student_count: 22 }
])
}
}
const handleSelectAction = (a: Action) => {
setAction(a)
if (a === 'erstellen') {
alert('Die Klausur-Erstellung wird in einer spaeteren Version verfuegbar sein.')
return
}
setStep('type')
}
const handleSelectType = (t: ExamType) => {
setExamType(t)
setStep('class')
}
const handleSelectClass = (c: SchoolClass) => {
setSelectedClass(c)
}
const handleBack = () => {
if (step === 'type') setStep('action')
else if (step === 'class') setStep('type')
}
const handleStartCorrection = async () => {
if (!selectedClass || !examType) return
setLoading(true)
try {
const klausur = await createKlausur({
title: `Klausur ${selectedClass.name}`,
subject: 'Deutsch',
modus: examType === 'abitur' ? 'landes_abitur' : 'vorabitur',
class_id: selectedClass.id,
year: new Date().getFullYear(),
semester: 'Q1'
})
navigate(`/korrektur/${klausur.id}`)
} catch (e) {
console.error('Failed to create klausur:', e)
alert('Fehler beim Erstellen der Klausur')
} finally {
setLoading(false)
}
}
const getStepClasses = (s: Step) => {
const steps: Step[] = ['action', 'type', 'class']
const currentIndex = steps.indexOf(step)
const stepIndex = steps.indexOf(s)
if (stepIndex < currentIndex) return 'onboarding-step completed'
if (stepIndex === currentIndex) return 'onboarding-step active'
return 'onboarding-step'
}
return (
<div className="onboarding">
{step !== 'action' && (
<div className="onboarding-back" onClick={handleBack}>
Zurueck
</div>
)}
<div className="onboarding-content">
<div className="onboarding-steps">
<div className={getStepClasses('action')} />
<div className={getStepClasses('type')} />
<div className={getStepClasses('class')} />
</div>
{step === 'action' && (
<>
<h1 className="onboarding-title">Was moechten Sie tun?</h1>
<p className="onboarding-subtitle">Waehlen Sie eine Option, um fortzufahren</p>
<div className="onboarding-options">
<div
className={`onboarding-card ${action === 'korrigieren' ? 'selected' : ''}`}
onClick={() => handleSelectAction('korrigieren')}
>
<div className="onboarding-card-icon"></div>
<div className="onboarding-card-title">Klausuren korrigieren</div>
<div className="onboarding-card-desc">
Schuelerarbeiten hochladen und mit KI-Unterstuetzung bewerten
</div>
</div>
<div
className={`onboarding-card ${action === 'erstellen' ? 'selected' : ''}`}
onClick={() => handleSelectAction('erstellen')}
>
<div className="onboarding-card-icon">📝</div>
<div className="onboarding-card-title">Klausur erstellen</div>
<div className="onboarding-card-desc">
Neue Klausur mit Erwartungshorizont anlegen
</div>
</div>
</div>
{klausuren.length > 0 && (
<div style={{ marginTop: 40, textAlign: 'center' }}>
<button
className="btn btn-secondary"
onClick={() => navigate('/korrektur')}
>
Zu bestehenden Klausuren ({klausuren.length})
</button>
</div>
)}
</>
)}
{step === 'type' && (
<>
<h1 className="onboarding-title">Welche Art von Klausur?</h1>
<p className="onboarding-subtitle">Waehlen Sie den Klausurtyp</p>
<div className="onboarding-options">
<div
className={`onboarding-card ${examType === 'vorabitur' ? 'selected' : ''}`}
onClick={() => handleSelectType('vorabitur')}
>
<div className="onboarding-card-icon">📋</div>
<div className="onboarding-card-title">Vorabitur / Klausur</div>
<div className="onboarding-card-desc">
Regulaere Klausuren waehrend des Schuljahres
</div>
</div>
<div
className={`onboarding-card ${examType === 'abitur' ? 'selected' : ''}`}
onClick={() => handleSelectType('abitur')}
>
<div className="onboarding-card-icon">🎓</div>
<div className="onboarding-card-title">Abiturklausur</div>
<div className="onboarding-card-desc">
Abiturpruefung mit 15-Punkte-System
</div>
</div>
</div>
</>
)}
{step === 'class' && (
<>
<h1 className="onboarding-title">Klasse waehlen</h1>
<p className="onboarding-subtitle">
Waehlen Sie die Klasse oder legen Sie eine neue Klausur an
</p>
{classes.length === 0 ? (
<div style={{ color: 'var(--bp-text-muted)', padding: '32px', textAlign: 'center' }}>
Keine Klassen gefunden. Bitte legen Sie Klassen im Studio Modul an.
</div>
) : (
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(150px, 1fr))', gap: 16, marginBottom: 24 }}>
{classes.map(c => (
<div
key={c.id}
className={`onboarding-card ${selectedClass?.id === c.id ? 'selected' : ''}`}
style={{ padding: '20px 16px', minWidth: 'auto' }}
onClick={() => handleSelectClass(c)}
>
<div className="onboarding-card-title" style={{ fontSize: 16 }}>{c.name}</div>
<div className="onboarding-card-desc">{c.student_count} Schueler</div>
</div>
))}
</div>
)}
<div style={{ marginTop: 24 }}>
<button
className="btn btn-primary"
disabled={!selectedClass || loading}
onClick={handleStartCorrection}
>
{loading ? 'Wird erstellt...' : 'Weiter zur Korrektur'}
</button>
</div>
</>
)}
</div>
</div>
)
}