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>
514 lines
17 KiB
TypeScript
514 lines
17 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useEffect } from 'react'
|
|
import { useLanguage } from '@/lib/LanguageContext'
|
|
import { useTheme } from '@/lib/ThemeContext'
|
|
import { GermanyMap, bundeslaender } from './GermanyMap'
|
|
import { CityMap } from './CityMap'
|
|
import { SchoolSearch } from './SchoolSearch'
|
|
import { BPIcon } from './Logo'
|
|
|
|
interface OnboardingWizardProps {
|
|
onComplete: (data: OnboardingData) => void
|
|
}
|
|
|
|
export interface OnboardingData {
|
|
bundesland: string
|
|
bundeslandName: string
|
|
city: string
|
|
cityLat?: number
|
|
cityLng?: number
|
|
schoolName: string
|
|
schoolType: string
|
|
}
|
|
|
|
// Schulformen mit Icons und Beschreibungen
|
|
const schulformen = [
|
|
// Allgemeinbildende Schulen
|
|
{
|
|
id: 'gymnasium',
|
|
name: 'Gymnasium',
|
|
icon: '🎓',
|
|
description: 'Allgemeinbildend bis Abitur',
|
|
category: 'allgemein'
|
|
},
|
|
{
|
|
id: 'gesamtschule',
|
|
name: 'Gesamtschule',
|
|
icon: '🏫',
|
|
description: 'Integriert/kooperativ',
|
|
category: 'allgemein'
|
|
},
|
|
{
|
|
id: 'realschule',
|
|
name: 'Realschule',
|
|
icon: '📚',
|
|
description: 'Mittlerer Abschluss',
|
|
category: 'allgemein'
|
|
},
|
|
{
|
|
id: 'hauptschule',
|
|
name: 'Hauptschule',
|
|
icon: '📖',
|
|
description: 'Erster Abschluss',
|
|
category: 'allgemein'
|
|
},
|
|
{
|
|
id: 'mittelschule',
|
|
name: 'Mittelschule',
|
|
icon: '📝',
|
|
description: 'Bayern/Sachsen',
|
|
category: 'allgemein'
|
|
},
|
|
{
|
|
id: 'oberschule',
|
|
name: 'Oberschule',
|
|
icon: '🏛️',
|
|
description: 'Sachsen/Brandenburg',
|
|
category: 'allgemein'
|
|
},
|
|
{
|
|
id: 'stadtteilschule',
|
|
name: 'Stadtteilschule',
|
|
icon: '🌆',
|
|
description: 'Hamburg',
|
|
category: 'allgemein'
|
|
},
|
|
{
|
|
id: 'gemeinschaftsschule',
|
|
name: 'Gemeinschaftsschule',
|
|
icon: '🤲',
|
|
description: 'BW/SH/TH/SL/BE',
|
|
category: 'allgemein'
|
|
},
|
|
// Berufliche Schulen
|
|
{
|
|
id: 'berufsschule',
|
|
name: 'Berufsschule',
|
|
icon: '🔧',
|
|
description: 'Duale Ausbildung',
|
|
category: 'beruflich'
|
|
},
|
|
{
|
|
id: 'berufliches_gymnasium',
|
|
name: 'Berufl. Gymnasium',
|
|
icon: '💼',
|
|
description: 'Fachgebundenes Abitur',
|
|
category: 'beruflich'
|
|
},
|
|
{
|
|
id: 'fachoberschule',
|
|
name: 'Fachoberschule',
|
|
icon: '📊',
|
|
description: 'Fachhochschulreife',
|
|
category: 'beruflich'
|
|
},
|
|
{
|
|
id: 'berufsfachschule',
|
|
name: 'Berufsfachschule',
|
|
icon: '🛠️',
|
|
description: 'Vollzeitberufliche Bildung',
|
|
category: 'beruflich'
|
|
},
|
|
// Sonder- und Förderschulen
|
|
{
|
|
id: 'foerderschule',
|
|
name: 'Förderschule',
|
|
icon: '🤝',
|
|
description: 'Sonderpädagogisch',
|
|
category: 'foerder'
|
|
},
|
|
{
|
|
id: 'foerderzentrum',
|
|
name: 'Förderzentrum',
|
|
icon: '💚',
|
|
description: 'Inklusiv/integriert',
|
|
category: 'foerder'
|
|
},
|
|
// Privatschulen & Besondere Formen
|
|
{
|
|
id: 'privatschule',
|
|
name: 'Privatschule',
|
|
icon: '🏰',
|
|
description: 'Freier Träger',
|
|
category: 'privat'
|
|
},
|
|
{
|
|
id: 'internat',
|
|
name: 'Internat',
|
|
icon: '🛏️',
|
|
description: 'Mit Unterbringung',
|
|
category: 'privat'
|
|
},
|
|
{
|
|
id: 'waldorfschule',
|
|
name: 'Waldorfschule',
|
|
icon: '🌿',
|
|
description: 'Anthroposophisch',
|
|
category: 'alternativ'
|
|
},
|
|
{
|
|
id: 'montessori',
|
|
name: 'Montessori-Schule',
|
|
icon: '🧒',
|
|
description: 'Montessori-Pädagogik',
|
|
category: 'alternativ'
|
|
},
|
|
// Grundschulen
|
|
{
|
|
id: 'grundschule',
|
|
name: 'Grundschule',
|
|
icon: '🏠',
|
|
description: 'Klasse 1-4',
|
|
category: 'grund'
|
|
},
|
|
// Internationale
|
|
{
|
|
id: 'internationale_schule',
|
|
name: 'Internationale Schule',
|
|
icon: '🌍',
|
|
description: 'IB/Cambridge',
|
|
category: 'international'
|
|
},
|
|
{
|
|
id: 'europaeische_schule',
|
|
name: 'Europäische Schule',
|
|
icon: '🇪🇺',
|
|
description: 'EU-Curriculum',
|
|
category: 'international'
|
|
},
|
|
]
|
|
|
|
// Kategorien für die Anzeige
|
|
const schulformKategorien = [
|
|
{ id: 'allgemein', name: 'Allgemeinbildend', icon: '📚' },
|
|
{ id: 'beruflich', name: 'Berufsbildend', icon: '💼' },
|
|
{ id: 'foerder', name: 'Förderschulen', icon: '💚' },
|
|
{ id: 'privat', name: 'Privat & Internat', icon: '🏰' },
|
|
{ id: 'alternativ', name: 'Alternative Pädagogik', icon: '🌿' },
|
|
{ id: 'grund', name: 'Primarstufe', icon: '🏠' },
|
|
{ id: 'international', name: 'International', icon: '🌍' },
|
|
]
|
|
|
|
export function OnboardingWizard({ onComplete }: OnboardingWizardProps) {
|
|
const { t } = useLanguage()
|
|
const { isDark } = useTheme()
|
|
|
|
const [step, setStep] = useState(1)
|
|
const [data, setData] = useState<Partial<OnboardingData>>({})
|
|
const [citySearch, setCitySearch] = useState('')
|
|
const [schoolSearch, setSchoolSearch] = useState('')
|
|
|
|
const totalSteps = 4
|
|
|
|
const handleNext = () => {
|
|
if (step < totalSteps) {
|
|
setStep(step + 1)
|
|
} else {
|
|
// Abschluss
|
|
onComplete(data as OnboardingData)
|
|
}
|
|
}
|
|
|
|
const handleBack = () => {
|
|
if (step > 1) {
|
|
setStep(step - 1)
|
|
}
|
|
}
|
|
|
|
const canProceed = () => {
|
|
switch (step) {
|
|
case 1:
|
|
return !!data.bundesland
|
|
case 2:
|
|
return !!data.city && data.city.trim().length > 0
|
|
case 3:
|
|
return !!data.schoolName && data.schoolName.trim().length > 0
|
|
case 4:
|
|
return !!data.schoolType
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className={`min-h-screen flex flex-col ${
|
|
isDark
|
|
? 'bg-gradient-to-br from-indigo-900 via-purple-900 to-pink-800'
|
|
: 'bg-gradient-to-br from-slate-100 via-blue-50 to-indigo-100'
|
|
}`}>
|
|
{/* Animated Background */}
|
|
<div className="absolute inset-0 overflow-hidden pointer-events-none">
|
|
<div className={`absolute -top-40 -right-40 w-80 h-80 rounded-full mix-blend-multiply filter blur-3xl animate-pulse ${
|
|
isDark ? 'bg-purple-500 opacity-50' : 'bg-purple-300 opacity-30'
|
|
}`} />
|
|
<div className={`absolute -bottom-40 -left-40 w-80 h-80 rounded-full mix-blend-multiply filter blur-3xl animate-pulse ${
|
|
isDark ? 'bg-blue-500 opacity-50' : 'bg-blue-300 opacity-30'
|
|
}`} style={{ animationDelay: '1s' }} />
|
|
</div>
|
|
|
|
<div className="relative z-10 flex-1 flex flex-col items-center justify-center p-8">
|
|
{/* Logo & Willkommen */}
|
|
<div className="text-center mb-8">
|
|
<div className="flex items-center justify-center gap-3 mb-4">
|
|
<BPIcon variant="cupertino" size={56} />
|
|
<div className="text-left">
|
|
<h1 className={`text-2xl font-bold ${isDark ? 'text-white' : 'text-slate-900'}`}>
|
|
BreakPilot Studio
|
|
</h1>
|
|
<p className={`text-sm ${isDark ? 'text-white/60' : 'text-slate-500'}`}>
|
|
Willkommen! Lassen Sie uns loslegen.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Progress Bar */}
|
|
<div className="w-full max-w-2xl mb-8">
|
|
<div className="flex items-center justify-between mb-2">
|
|
{[1, 2, 3, 4].map((s) => (
|
|
<div
|
|
key={s}
|
|
className={`w-10 h-10 rounded-full flex items-center justify-center font-medium transition-all ${
|
|
s === step
|
|
? 'bg-gradient-to-br from-blue-500 to-purple-500 text-white scale-110 shadow-lg'
|
|
: s < step
|
|
? isDark
|
|
? 'bg-green-500/30 text-green-300'
|
|
: 'bg-green-100 text-green-700'
|
|
: isDark
|
|
? 'bg-white/10 text-white/40'
|
|
: 'bg-slate-200 text-slate-400'
|
|
}`}
|
|
>
|
|
{s < step ? '✓' : s}
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className={`h-2 rounded-full overflow-hidden ${isDark ? 'bg-white/10' : 'bg-slate-200'}`}>
|
|
<div
|
|
className="h-full bg-gradient-to-r from-blue-500 to-purple-500 transition-all duration-500"
|
|
style={{ width: `${((step - 1) / (totalSteps - 1)) * 100}%` }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Main Card */}
|
|
<div className={`w-full max-w-2xl backdrop-blur-xl border rounded-3xl p-8 ${
|
|
isDark
|
|
? 'bg-white/10 border-white/20'
|
|
: 'bg-white/80 border-black/10 shadow-xl'
|
|
}`}>
|
|
{/* Step 1: Bundesland */}
|
|
{step === 1 && (
|
|
<div className="text-center">
|
|
<h2 className={`text-2xl font-bold mb-2 ${isDark ? 'text-white' : 'text-slate-900'}`}>
|
|
In welchem Bundesland unterrichten Sie?
|
|
</h2>
|
|
<p className={`mb-6 ${isDark ? 'text-white/60' : 'text-slate-600'}`}>
|
|
Klicken Sie auf Ihr Bundesland in der Karte
|
|
</p>
|
|
<GermanyMap
|
|
selectedState={data.bundesland || null}
|
|
suggestedState="HH"
|
|
onSelectState={(stateId) => setData({
|
|
...data,
|
|
bundesland: stateId,
|
|
bundeslandName: bundeslaender[stateId as keyof typeof bundeslaender]
|
|
})}
|
|
className="mx-auto max-w-md"
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{/* Step 2: Stadt */}
|
|
{step === 2 && (
|
|
<div className="text-center">
|
|
<h2 className={`text-2xl font-bold mb-2 ${isDark ? 'text-white' : 'text-slate-900'}`}>
|
|
In welcher Stadt arbeiten Sie?
|
|
</h2>
|
|
<p className={`mb-4 ${isDark ? 'text-white/60' : 'text-slate-600'}`}>
|
|
Suchen Sie Ihre Stadt oder klicken Sie auf die Karte
|
|
</p>
|
|
|
|
{/* Info Box - Bundesland */}
|
|
<div className={`inline-flex items-center gap-2 px-4 py-2 rounded-full mb-4 ${
|
|
isDark ? 'bg-white/10' : 'bg-slate-100'
|
|
}`}>
|
|
<span className="text-lg">📍</span>
|
|
<span className={`text-sm font-medium ${isDark ? 'text-white/80' : 'text-slate-700'}`}>
|
|
{data.bundeslandName}
|
|
</span>
|
|
</div>
|
|
|
|
{/* CityMap Komponente */}
|
|
<CityMap
|
|
bundesland={data.bundesland || 'HH'}
|
|
bundeslandName={data.bundeslandName || 'Hamburg'}
|
|
selectedCity={data.city || ''}
|
|
onSelectCity={(city, lat, lng) => setData({
|
|
...data,
|
|
city,
|
|
cityLat: lat,
|
|
cityLng: lng
|
|
})}
|
|
className="max-w-lg mx-auto"
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{/* Step 3: Schule */}
|
|
{step === 3 && (
|
|
<div className="text-center">
|
|
<h2 className={`text-2xl font-bold mb-2 ${isDark ? 'text-white' : 'text-slate-900'}`}>
|
|
Wie heißt Ihre Schule?
|
|
</h2>
|
|
<p className={`mb-4 ${isDark ? 'text-white/60' : 'text-slate-600'}`}>
|
|
Suchen Sie Ihre Schule oder geben Sie den Namen ein
|
|
</p>
|
|
|
|
{/* SchoolSearch Komponente mit Autocomplete */}
|
|
<SchoolSearch
|
|
city={data.city || ''}
|
|
bundesland={data.bundesland || 'HH'}
|
|
bundeslandName={data.bundeslandName || 'Hamburg'}
|
|
selectedSchool={data.schoolName || ''}
|
|
onSelectSchool={(schoolName, schoolId) => setData({
|
|
...data,
|
|
schoolName
|
|
})}
|
|
className="max-w-lg mx-auto"
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{/* Step 4: Schulform */}
|
|
{step === 4 && (
|
|
<div className="text-center">
|
|
<h2 className={`text-2xl font-bold mb-2 ${isDark ? 'text-white' : 'text-slate-900'}`}>
|
|
Welche Schulform ist es?
|
|
</h2>
|
|
<p className={`mb-6 ${isDark ? 'text-white/60' : 'text-slate-600'}`}>
|
|
Wählen Sie die passende Schulform
|
|
</p>
|
|
|
|
{/* Scrollbarer Bereich mit Kategorien */}
|
|
<div className="max-h-[400px] overflow-y-auto pr-2 space-y-6">
|
|
{schulformKategorien.map((kategorie) => {
|
|
const formenInKategorie = schulformen.filter(f => f.category === kategorie.id)
|
|
if (formenInKategorie.length === 0) return null
|
|
|
|
return (
|
|
<div key={kategorie.id}>
|
|
{/* Kategorie-Header */}
|
|
<div className={`flex items-center gap-2 mb-3 ${isDark ? 'text-white/70' : 'text-slate-600'}`}>
|
|
<span>{kategorie.icon}</span>
|
|
<span className="text-sm font-medium">{kategorie.name}</span>
|
|
<div className={`flex-1 h-px ${isDark ? 'bg-white/10' : 'bg-slate-200'}`} />
|
|
</div>
|
|
|
|
{/* Schulformen in dieser Kategorie */}
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
|
{formenInKategorie.map((form) => {
|
|
const isSelected = data.schoolType === form.id
|
|
return (
|
|
<button
|
|
key={form.id}
|
|
onClick={() => setData({ ...data, schoolType: form.id })}
|
|
className={`p-3 rounded-xl border-2 transition-all hover:scale-105 text-left ${
|
|
isSelected
|
|
? 'border-blue-500 bg-blue-500/20 shadow-lg'
|
|
: isDark
|
|
? 'border-white/10 bg-white/5 hover:bg-white/10 hover:border-white/20'
|
|
: 'border-slate-200 bg-white hover:bg-slate-50 hover:border-slate-300'
|
|
}`}
|
|
>
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-xl">{form.icon}</span>
|
|
<div className="flex-1 min-w-0">
|
|
<p className={`font-medium text-sm truncate ${
|
|
isSelected
|
|
? isDark ? 'text-blue-300' : 'text-blue-700'
|
|
: isDark ? 'text-white' : 'text-slate-900'
|
|
}`}>
|
|
{form.name}
|
|
</p>
|
|
<p className={`text-xs truncate ${isDark ? 'text-white/40' : 'text-slate-400'}`}>
|
|
{form.description}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</button>
|
|
)
|
|
})}
|
|
</div>
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
|
|
{/* Summary */}
|
|
{data.schoolType && (
|
|
<div className={`mt-6 p-4 rounded-xl ${
|
|
isDark ? 'bg-white/5' : 'bg-slate-50'
|
|
}`}>
|
|
<p className={`text-sm ${isDark ? 'text-white/80' : 'text-slate-700'}`}>
|
|
<strong>{data.schoolName}</strong>
|
|
</p>
|
|
<p className={`text-xs ${isDark ? 'text-white/50' : 'text-slate-500'}`}>
|
|
{schulformen.find(f => f.id === data.schoolType)?.name} in {data.city}, {data.bundeslandName}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Navigation Buttons */}
|
|
<div className="flex items-center gap-4 mt-8">
|
|
{step > 1 && (
|
|
<button
|
|
onClick={handleBack}
|
|
className={`px-6 py-3 rounded-xl font-medium transition-all ${
|
|
isDark
|
|
? 'bg-white/10 text-white hover:bg-white/20'
|
|
: 'bg-slate-200 text-slate-700 hover:bg-slate-300'
|
|
}`}
|
|
>
|
|
← Zurück
|
|
</button>
|
|
)}
|
|
|
|
<button
|
|
onClick={handleNext}
|
|
disabled={!canProceed()}
|
|
className={`px-8 py-3 rounded-xl font-medium transition-all ${
|
|
canProceed()
|
|
? 'bg-gradient-to-r from-blue-500 to-purple-500 text-white hover:shadow-xl hover:shadow-purple-500/30 hover:scale-105'
|
|
: isDark
|
|
? 'bg-white/10 text-white/30 cursor-not-allowed'
|
|
: 'bg-slate-200 text-slate-400 cursor-not-allowed'
|
|
}`}
|
|
>
|
|
{step === totalSteps ? 'Los geht\'s! →' : 'Weiter →'}
|
|
</button>
|
|
</div>
|
|
|
|
{/* Skip Option */}
|
|
<button
|
|
onClick={() => onComplete({
|
|
bundesland: data.bundesland || 'NI',
|
|
bundeslandName: data.bundeslandName || 'Niedersachsen',
|
|
city: data.city || 'Unbekannt',
|
|
schoolName: data.schoolName || 'Meine Schule',
|
|
schoolType: data.schoolType || 'gymnasium'
|
|
})}
|
|
className={`mt-4 text-sm ${isDark ? 'text-white/40 hover:text-white/60' : 'text-slate-400 hover:text-slate-600'}`}
|
|
>
|
|
Überspringen (später einrichten)
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|