Files
breakpilot-lehrer/klausur-service/frontend/src/pages/OnboardingPage.tsx
Benjamin Boenisch 5a31f52310 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>
2026-02-11 23:47:26 +01:00

248 lines
8.4 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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>
)
}