'use client'; import React, { useState, useMemo } from 'react'; import { motion, AnimatePresence, type Variants } from 'framer-motion'; import { BookOpen, Mail, Lock, User, ArrowRight, GraduationCap, Heart, Sparkles, Flower2, Eye, EyeOff, KeyRound, Info, Shield, Leaf, Users as UsersIcon, HelpCircle, LockKeyhole, GitBranch, CheckCircle2, XCircle } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Checkbox } from '@/components/ui/checkbox'; import { Badge } from '@/components/ui/badge'; import { useAppStore } from '@/lib/store'; import { t } from '@/lib/i18n'; import { login, register } from '@/lib/api'; import { toast } from 'sonner'; type LoginRole = 'teacher' | 'student' | 'parent'; const DEMO_ACCOUNTS = [ { email: 'demo@competencetrack.org', password: 'Demo2025!', role: 'SCHOOL_ADMIN', labelKey: 'auth.demo_admin', icon: Shield, colorClass: 'from-emerald-400 to-emerald-600 hover:from-emerald-500 hover:to-emerald-700 shadow-emerald-300/40 dark:shadow-emerald-900/40' }, { email: 'demo.vice@competencetrack.org', password: 'Demo2025!', role: 'VICE_PRINCIPAL', labelKey: 'auth.demo_vice_principal', icon: Shield, colorClass: 'from-emerald-400 to-emerald-600 hover:from-emerald-500 hover:to-emerald-700 shadow-emerald-300/40 dark:shadow-emerald-900/40' }, { email: 'demo.teacher@competencetrack.org', password: 'Demo2025!', role: 'TEACHER', labelKey: 'auth.demo_teacher', icon: GraduationCap, colorClass: 'from-teal-400 to-teal-600 hover:from-teal-500 hover:to-teal-700 shadow-teal-300/40 dark:shadow-teal-900/40' }, { email: 'demo.student@competencetrack.org', password: 'Demo2025!', role: 'STUDENT', labelKey: 'auth.demo_student', icon: User, colorClass: 'from-amber-400 to-amber-600 hover:from-amber-500 hover:to-amber-700 shadow-amber-300/40 dark:shadow-amber-900/40' }, { email: 'demo.parent@competencetrack.org', password: 'Demo2025!', role: 'PARENT', labelKey: 'auth.demo_parent', icon: Heart, colorClass: 'from-violet-400 to-violet-600 hover:from-violet-500 hover:to-violet-700 shadow-violet-300/40 dark:shadow-violet-900/40' }, ]; /* ─── Staggered entrance helpers ────────────────────────────── */ const containerVariants = { hidden: { opacity: 0 }, visible: { opacity: 1, transition: { staggerChildren: 0.07, delayChildren: 0.1 }, }, }; const itemVariants: Variants = { hidden: { opacity: 0, y: 14 }, visible: { opacity: 1, y: 0, transition: { duration: 0.4, ease: 'easeOut' } }, }; /* ─── Floating particles ────────────────────────────────────── */ function FloatingParticles() { const particles = useMemo(() => [ { id: 'p1', x: 8, y: 12, size: 6, opacity: 0.25, color: 'emerald', delay: 0, duration: 14 }, { id: 'p2', x: 85, y: 18, size: 4, opacity: 0.2, color: 'teal', delay: 2, duration: 18 }, { id: 'p3', x: 22, y: 65, size: 5, opacity: 0.18, color: 'amber', delay: 4, duration: 16 }, { id: 'p4', x: 70, y: 75, size: 3, opacity: 0.22, color: 'violet', delay: 1, duration: 20 }, { id: 'p5', x: 45, y: 88, size: 4, opacity: 0.15, color: 'emerald', delay: 3, duration: 12 }, { id: 'p6', x: 92, y: 45, size: 5, opacity: 0.2, color: 'teal', delay: 5, duration: 15 }, { id: 'p7', x: 15, y: 40, size: 3, opacity: 0.18, color: 'amber', delay: 6, duration: 22 }, { id: 'p8', x: 55, y: 10, size: 4, opacity: 0.15, color: 'violet', delay: 2.5, duration: 17 }, { id: 'p9', x: 35, y: 50, size: 2, opacity: 0.12, color: 'emerald', delay: 7, duration: 19 }, { id: 'p10', x: 78, y: 55, size: 3, opacity: 0.16, color: 'teal', delay: 1.5, duration: 13 }, { id: 'p11', x: 60, y: 30, size: 2, opacity: 0.14, color: 'amber', delay: 4.5, duration: 21 }, { id: 'p12', x: 5, y: 80, size: 4, opacity: 0.2, color: 'violet', delay: 3.5, duration: 16 }, ], []); const colorMap: Record = { emerald: 'bg-emerald-400 dark:bg-emerald-500', teal: 'bg-teal-400 dark:bg-teal-500', amber: 'bg-amber-400 dark:bg-amber-500', violet: 'bg-violet-400 dark:bg-violet-500', }; return (
{particles.map((p) => ( ))}
); } /* ─── Decorative geometric shapes ───────────────────────────── */ function FloatingShapes() { return (
{/* Large circles */}
{/* Rotated squares / diamonds */} {/* Hexagonal shapes (approximated with rounded corners) */} {/* Dot grid overlay */}
); } /* ─── Left-side illustration panel ──────────────────────────── */ function LeftIllustration({ loginRole }: { loginRole: LoginRole }) { const features = [ { icon: Sparkles, labelKey: 'auth.feature_competencies', color: 'emerald' }, { icon: Heart, labelKey: 'auth.feature_student_centered', color: 'teal' }, { icon: BookOpen, labelKey: 'auth.feature_open_source', color: 'amber' }, { icon: GraduationCap, labelKey: 'auth.feature_reports', color: 'violet' }, { icon: Flower2, labelKey: 'auth.feature_flower', color: 'rose' }, ]; const colorClasses: Record = { emerald: { bg: 'bg-emerald-100 dark:bg-emerald-900/30', text: 'text-emerald-600 dark:text-emerald-400', ring: 'ring-emerald-200/50 dark:ring-emerald-800/30' }, teal: { bg: 'bg-teal-100 dark:bg-teal-900/30', text: 'text-teal-600 dark:text-teal-400', ring: 'ring-teal-200/50 dark:ring-teal-800/30' }, amber: { bg: 'bg-amber-100 dark:bg-amber-900/30', text: 'text-amber-600 dark:text-amber-400', ring: 'ring-amber-200/50 dark:ring-amber-800/30' }, violet: { bg: 'bg-violet-100 dark:bg-violet-900/30', text: 'text-violet-600 dark:text-violet-400', ring: 'ring-violet-200/50 dark:ring-violet-800/30' }, rose: { bg: 'bg-rose-100 dark:bg-rose-900/30', text: 'text-rose-600 dark:text-rose-400', ring: 'ring-rose-200/50 dark:ring-rose-800/30' }, }; return (
{/* Decorative orbiting circles */} {/* Main icon with role-based animation */} {loginRole === 'teacher' && ( )} {loginRole === 'student' && ( )} {loginRole === 'parent' && ( )}
{/* Feature highlights */}

SchulOS

{features.map((f, i) => { const c = colorClasses[f.color]; return (

{t(f.labelKey)}

); })}
{/* Mockup dashboard preview */}
{[0, 1, 2, 3, 4, 5].map((i) => (
))}
); } /* ─── Main Auth View ────────────────────────────────────────── */ export default function AuthView() { const [mode, setMode] = useState<'login' | 'register'>('login'); const [loginRole, setLoginRole] = useState('teacher'); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); const [schoolIdInput, setSchoolIdInput] = useState(''); const [loading, setLoading] = useState(false); const [demoLoading, setDemoLoading] = useState(false); const [showPassword, setShowPassword] = useState(false); const [rememberMe, setRememberMe] = useState(true); const [showForgotInfo, setShowForgotInfo] = useState(false); const [showForgotModal, setShowForgotModal] = useState(false); const [forgotEmail, setForgotEmail] = useState(''); const [forgotSent, setForgotSent] = useState(false); const setCurrentUser = useAppStore((s) => s.setCurrentUser); // Input validation state const emailValid = email.length > 0 && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); const emailTouched = email.length > 0; const passwordValid = password.length >= 6; const passwordTouched = password.length > 0; const firstNameValid = firstName.length > 0; const lastNameValid = lastName.length > 0; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setLoading(true); try { if (mode === 'login') { const user = await login(email, password); setCurrentUser(user); if (user.role === 'STUDENT') { toast.success(t('auth.student_welcome')); } else if (user.role === 'PARENT') { toast.success(t('auth.parent_welcome')); } else { toast.success(t('auth.welcome_back')); } } else { const user = await register({ email, password, firstName, lastName, }); setCurrentUser(user); toast.success(t('toast.created')); } } catch (err) { const message = err instanceof Error ? err.message : t('auth.error.generic'); if (message.includes('credentials') || message.includes('Invalid')) { toast.error(t('auth.error.invalid_credentials')); } else if (message.includes('already') || message.includes('registered')) { toast.error(t('auth.error.email_exists')); } else { toast.error(message); } } finally { setLoading(false); } }; const toggleMode = () => { setMode(mode === 'login' ? 'register' : 'login'); setEmail(''); setPassword(''); setFirstName(''); setLastName(''); setSchoolIdInput(''); }; const handleDemoLogin = async (demoEmail: string, demoPassword: string) => { setDemoLoading(true); setMode('login'); setEmail(demoEmail); setPassword(demoPassword); try { const user = await login(demoEmail, demoPassword); setCurrentUser(user); if (user.role === 'STUDENT') { toast.success(t('auth.student_welcome')); } else if (user.role === 'PARENT') { toast.success(t('auth.parent_welcome')); } else { toast.success(t('auth.welcome_back')); } } catch (err) { const message = err instanceof Error ? err.message : t('auth.error.generic'); if (message.includes('credentials') || message.includes('Invalid')) { toast.error(t('auth.error.invalid_credentials')); } else { toast.error(message); } } finally { setDemoLoading(false); } }; // Role tab config with enhanced visual distinction const roleTabs: Array<{ key: LoginRole; icon: React.ElementType; labelKey: string; color: string; iconColor: string; activeColor: string; activeBg: string; bgGradient: string; hoverBg: string; indicatorColor: string; glowColor: string; }> = [ { key: 'teacher', icon: GraduationCap, labelKey: 'auth.login', color: 'text-emerald-600 dark:text-emerald-400', iconColor: 'text-emerald-600 dark:text-emerald-400', activeColor: 'text-white', activeBg: 'bg-gradient-to-r from-emerald-500 to-teal-500', bgGradient: 'from-emerald-50/50 to-teal-50/50 dark:from-emerald-950/10 dark:to-teal-950/10', hoverBg: 'hover:bg-emerald-50/60 dark:hover:bg-emerald-950/20', indicatorColor: 'bg-emerald-500', glowColor: 'shadow-emerald-200/50 dark:shadow-emerald-900/30', }, { key: 'student', icon: UsersIcon, labelKey: 'auth.student_login', color: 'text-emerald-600 dark:text-emerald-400', iconColor: 'text-emerald-600 dark:text-emerald-400', activeColor: 'text-white', activeBg: 'bg-gradient-to-r from-amber-500 to-orange-500', bgGradient: 'from-amber-50/50 to-orange-50/50 dark:from-amber-950/10 dark:to-orange-950/10', hoverBg: 'hover:bg-amber-50/60 dark:hover:bg-amber-950/20', indicatorColor: 'bg-amber-500', glowColor: 'shadow-amber-200/50 dark:shadow-amber-900/30', }, { key: 'parent', icon: Heart, labelKey: 'auth.parent_login', color: 'text-emerald-600 dark:text-emerald-400', iconColor: 'text-emerald-600 dark:text-emerald-400', activeColor: 'text-white', activeBg: 'bg-gradient-to-r from-violet-500 to-purple-500', bgGradient: 'from-violet-50/50 to-purple-50/50 dark:from-violet-950/10 dark:to-purple-950/10', hoverBg: 'hover:bg-violet-50/60 dark:hover:bg-violet-950/20', indicatorColor: 'bg-violet-500', glowColor: 'shadow-violet-200/50 dark:shadow-violet-900/30', }, ]; const activeRoleTab = roleTabs.find((r) => r.key === loginRole)!; return (
{/* Animated gradient background overlay */}
{/* Floating geometric shapes */} {/* Floating particles */} {/* Left illustration side (hidden on small screens) */} {/* Auth form */} {/* Branding (mobile only) */}
{t('app.name')}
{/* Auth Card - Glassmorphism */} {/* Role tabs with enhanced visual distinction */}
{roleTabs.map((tab, i) => ( { setLoginRole(tab.key); setShowForgotInfo(false); }} className={`relative flex-1 min-h-[48px] flex items-center justify-center gap-2 px-3 py-3.5 text-sm font-semibold transition-all duration-300 ${ loginRole === tab.key ? `${tab.activeBg} ${tab.activeColor} shadow-sm ${tab.glowColor}` : `${tab.color} bg-transparent ${tab.hoverBg}` }`} initial={{ opacity: 0, y: -8 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.3, delay: 0.2 + i * 0.05 }} whileHover={{ scale: loginRole === tab.key ? 1 : 1.02 }} whileTap={{ scale: 0.98 }} > {t(tab.labelKey)} {/* Active indicator */} {loginRole === tab.key && ( )} ))}
{loginRole === 'teacher' ? (mode === 'login' ? t('auth.login') : t('auth.register_title')) : loginRole === 'student' ? t('auth.student_login') : t('auth.parent_login')}

{loginRole === 'teacher' ? (mode === 'login' ? t('auth.login_subtitle') : t('auth.register_subtitle')) : loginRole === 'student' ? t('auth.student_login_desc') : t('auth.parent_login_desc')}

{/* Name fields for register (teacher only) */} {mode === 'register' && loginRole === 'teacher' && (
setFirstName(e.target.value)} className={`pl-10 pr-9 h-12 min-h-[44px] bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 placeholder:text-gray-500 dark:placeholder:text-gray-400 transition-all duration-200 ${ firstName.length > 0 && firstNameValid ? 'input-valid border-emerald-400 dark:border-emerald-500' : firstName.length > 0 && !firstNameValid ? 'input-invalid border-red-300 dark:border-red-700' : 'border-emerald-200 dark:border-emerald-800 focus:border-emerald-400 focus:ring-emerald-400/20' }`} placeholder={t('auth.firstName')} required /> {firstName.length > 0 && ( {firstNameValid ? : } )}
setLastName(e.target.value)} className={`pr-9 h-12 min-h-[44px] bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 placeholder:text-gray-500 dark:placeholder:text-gray-400 transition-all duration-200 ${ lastName.length > 0 && lastNameValid ? 'input-valid border-emerald-400 dark:border-emerald-500' : lastName.length > 0 && !lastNameValid ? 'input-invalid border-red-300 dark:border-red-700' : 'border-emerald-200 dark:border-emerald-800 focus:border-emerald-400 focus:ring-emerald-400/20' }`} placeholder={t('auth.lastName')} required /> {lastName.length > 0 && ( {lastNameValid ? : } )}
)} {/* Student-specific: School ID field */} {loginRole === 'student' && (
setSchoolIdInput(e.target.value)} className="pl-10 h-12 min-h-[44px] border-emerald-200 dark:border-emerald-800 focus:border-emerald-400 focus:ring-emerald-400/20 text-base bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 placeholder:text-gray-500 dark:placeholder:text-gray-400" placeholder={t('auth.school_id')} />
)} {/* Parent-specific: name fields on login */} {loginRole === 'parent' && mode === 'login' && (

{t('auth.parent_login_desc')}

)}
{/* Email field (always shown) with validation feedback */}
setEmail(e.target.value)} className={`pl-10 pr-10 h-12 min-h-[44px] text-base bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 placeholder:text-gray-500 dark:placeholder:text-gray-400 transition-all duration-200 ${ emailTouched && emailValid ? 'input-valid border-emerald-400 dark:border-emerald-500 focus:border-emerald-500 focus:ring-emerald-400/20' : emailTouched && !emailValid ? 'input-invalid border-red-300 dark:border-red-700 focus:border-red-400 focus:ring-red-400/20' : 'border-emerald-200 dark:border-emerald-800 focus:border-emerald-400 focus:ring-emerald-400/20' } ${ loginRole === 'student' ? 'focus:border-amber-400 focus:ring-amber-400/20' : loginRole === 'parent' ? 'focus:border-violet-400 focus:ring-violet-400/20' : '' }`} placeholder={loginRole === 'student' ? 'name@schule.de' : loginRole === 'parent' ? 'parent@email.de' : 'name@schule.de'} required /> {/* Validation icon */} {emailTouched && ( {emailValid ? ( ) : ( )} )}
{/* Password field with validation feedback */}
{mode === 'login' && ( setShowForgotModal(true)} className="text-xs text-emerald-600 hover:text-emerald-700 dark:text-emerald-400 dark:hover:text-emerald-300 font-medium transition-colors" whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} > {t('auth.forgot_password')} )}
setPassword(e.target.value)} className={`pl-9 pr-12 h-12 min-h-[44px] bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 placeholder:text-gray-500 dark:placeholder:text-gray-400 transition-all duration-200 ${ passwordTouched && passwordValid ? 'input-valid border-emerald-400 dark:border-emerald-500 focus:border-emerald-500 focus:ring-emerald-400/20' : passwordTouched && !passwordValid ? 'input-invalid border-red-300 dark:border-red-700 focus:border-red-400 focus:ring-red-400/20' : 'border-emerald-200 dark:border-emerald-800 focus:border-emerald-400 focus:ring-emerald-400/20' }`} placeholder="••••••" minLength={6} required /> {/* Validation icon */} {passwordTouched && ( {passwordValid ? ( ) : ( )} )} setShowPassword(!showPassword)} className="absolute right-3.5 top-1/2 -translate-y-1/2 text-emerald-400 hover:text-emerald-600 dark:hover:text-emerald-300 transition-colors w-6 h-6 flex items-center justify-center" tabIndex={-1} aria-label={showPassword ? t('polish.hide_password') : t('polish.show_password')} whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }} > {showPassword ? : }
{/* Password strength indicator (register mode) */} {mode === 'register' && passwordTouched && (
{[1, 2, 3, 4].map((level) => (
= level * 2 ? level <= 1 ? 'bg-red-400' : level <= 2 ? 'bg-amber-400' : level <= 3 ? 'bg-emerald-400' : 'bg-teal-400' : 'bg-gray-200 dark:bg-gray-700' }`} /> ))}

{password.length < 4 ? t('auth.password_weak') || 'Weak' : password.length < 6 ? t('auth.password_fair') || 'Fair' : password.length < 8 ? t('auth.password_good') || 'Good' : t('auth.password_strong') || 'Strong'}

)} {/* Forgot Password info */} {showForgotInfo && (

{loginRole === 'student' ? (t('polish.demo_email') + ': demo.student@competencetrack.org / ' + t('polish.demo_password') + ': Demo2025!') : loginRole === 'parent' ? (t('polish.demo_email') + ': demo.parent@competencetrack.org / ' + t('polish.demo_password') + ': Demo2025!') : (t('polish.demo_email') + ': demo@competencetrack.org / ' + t('polish.demo_password') + ': Demo2025!') }

)}
{/* Remember me (login mode only) */} {mode === 'login' && ( setRememberMe(v === true)} className="border-emerald-300 dark:border-emerald-700 data-[state=checked]:bg-emerald-500 data-[state=checked]:border-emerald-500" /> )} {/* Submit button with glow effect */} {/* Role-specific demo login buttons */} {mode === 'login' && (

{t('auth.demo_section_title')}

{t('badge.demo')}

{t('auth.demo_section_subtitle')}

{DEMO_ACCOUNTS.map((demo, i) => ( ))}
)} {/* Toggle register/login (only for teacher role) */} {loginRole === 'teacher' && ( {mode === 'login' ? t('auth.no_account') : t('auth.has_account')} )} {/* Student info: accounts created by teachers */} {loginRole === 'student' && mode === 'login' && (

{t('auth.create_student_account')} — {t('auth.student_login_desc')}

{/* Jugendschutz notice for students */}

{t('dsgvo.jugendschutz_login_notice')}

)} {/* Parent info */} {loginRole === 'parent' && mode === 'login' && (

{t('parent.my_children')} — {t('auth.parent_login_desc')}

)} {/* Footer */}

{t('app.tagline')}

{/* Forgot Password Modal */}
{t('auth.forgot_password')}
{t('auth.forgot_password_desc') || 'Enter your email address and we will send you a link to reset your password.'}
{forgotSent ? (

{t('auth.forgot_email_sent') || 'Reset link sent!'}

{t('auth.forgot_check_inbox') || 'Check your inbox for the password reset link.'}

) : ( <>
setForgotEmail(e.target.value)} className="pl-10 h-12 border-emerald-200/50 dark:border-emerald-900/30 focus:border-emerald-400 focus:ring-emerald-400/20 bg-white/60 dark:bg-gray-900/40 backdrop-blur-sm" placeholder="name@schule.de" type="email" />
{/* Demo credentials reminder */}

{t('polish.demo_email')}: demo@competencetrack.org / {t('polish.demo_password')}: Demo2025!

)}
{forgotSent ? ( ) : ( <> )}
); }