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>
248 lines
8.4 KiB
TypeScript
248 lines
8.4 KiB
TypeScript
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>
|
||
)
|
||
}
|