'use client'; import React, { useEffect, useState, useCallback } from 'react'; import { motion } from 'framer-motion'; import { Settings as SettingsIcon, School, Calendar, BookOpen, FileText, Shield, Download, Trash2, Plus, Pencil, CheckCircle2, Clock, AlertTriangle, Filter, RefreshCw, Users, UserPlus, Mail, Key, Search, Database, Activity, GraduationCap, Heart, UserCheck, Zap, BarChart3, TrendingUp, CheckCircle, Leaf, DatabaseIcon, Upload, FileDown, FileUp, Loader2, ClipboardCheck, CalendarDays, } from 'lucide-react'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; import { Switch } from '@/components/ui/switch'; import { Separator } from '@/components/ui/separator'; import { Skeleton } from '@/components/ui/skeleton'; import { Checkbox } from '@/components/ui/checkbox'; import { useAppStore } from '@/lib/store'; import { t } from '@/lib/i18n'; import { apiGet, apiPost, apiPut, apiDelete } from '@/lib/api'; import { toast } from 'sonner'; import { fetchSchools, updateSchool, fetchSchoolYears, createSchoolYear, fetchSubjects, createSubject, updateSubject, deleteSubject, fetchAuditLog, downloadCsvExport, requestDataErasure, fetchUsers, createUser, updateUser, deleteUser, createStudentUserAccount, bulkCreateStudentAccounts, fetchStudents, type School as SchoolType, type SchoolYear, type Subject, type AuditLogEntry, type UserAccount, } from '@/lib/api'; const containerVariants = { hidden: { opacity: 0 }, visible: { opacity: 1, transition: { staggerChildren: 0.06 } }, }; const itemVariants = { hidden: { opacity: 0, y: 12 }, visible: { opacity: 1, y: 0 }, }; const schoolTypeLabels: Record = { ELEMENTARY: 'Grundschule', MIDDLE: 'Mittelschule', GYMNASIUM: 'Gymnasium', OTHER: 'Weitere', }; const actionColors: Record = { CREATE: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300', UPDATE: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300', DELETE: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300', LOGIN: 'bg-teal-100 text-teal-700 dark:bg-teal-900/30 dark:text-teal-300', EXPORT: 'bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-300', }; export default function SettingsView() { const currentUser = useAppStore((s) => s.currentUser); const [activeTab, setActiveTab] = useState('school'); // School info state const [schools, setSchools] = useState([]); const [selectedSchool, setSelectedSchool] = useState(null); const [schoolForm, setSchoolForm] = useState({ name: '', schoolType: '', country: '', timezone: '' }); const [schoolLoading, setSchoolLoading] = useState(true); const [schoolSaving, setSchoolSaving] = useState(false); // School years state const [schoolYears, setSchoolYears] = useState([]); const [yearsLoading, setYearsLoading] = useState(true); const [showCreateYear, setShowCreateYear] = useState(false); const [yearForm, setYearForm] = useState({ label: '', startDate: '', endDate: '' }); // Subjects state const [subjects, setSubjects] = useState([]); const [subjectsLoading, setSubjectsLoading] = useState(true); const [showCreateSubject, setShowCreateSubject] = useState(false); const [subjectForm, setSubjectForm] = useState({ name: '', gradeLevelMin: 1, gradeLevelMax: 13 }); const [editingSubject, setEditingSubject] = useState(null); const [deleteSubjectId, setDeleteSubjectId] = useState(null); // Audit log state const [auditEntries, setAuditEntries] = useState([]); const [auditLoading, setAuditLoading] = useState(true); const [auditActionFilter, setAuditActionFilter] = useState('all'); const [auditDateFrom, setAuditDateFrom] = useState(''); const [auditDateTo, setAuditDateTo] = useState(''); const [auditSearch, setAuditSearch] = useState(''); // Data erasure state const [showErasureDialog, setShowErasureDialog] = useState(false); const [erasureScope, setErasureScope] = useState('STUDENT'); // Users state const [users, setUsers] = useState([]); const [usersLoading, setUsersLoading] = useState(true); const [showUserDialog, setShowUserDialog] = useState(false); const [editingUser, setEditingUser] = useState(null); const [userForm, setUserForm] = useState({ firstName: '', lastName: '', email: '', password: '', role: 'TEACHER' as 'TEACHER' | 'SCHOOL_ADMIN' | 'SUPER_ADMIN' | 'STUDENT' | 'PARENT', }); const [deleteUserId, setDeleteUserId] = useState(null); // Student account creation state const [showStudentAccountDialog, setShowStudentAccountDialog] = useState(false); const [showBulkStudentDialog, setShowBulkStudentDialog] = useState(false); const [studentAccountForm, setStudentAccountForm] = useState({ firstName: '', lastName: '', email: '', password: 'Schule2025!', studentId: '', }); const [bulkForm, setBulkForm] = useState({ defaultPassword: 'Schule2025!', emailDomain: 'schule.de', selectedStudentIds: [] as string[], }); const [availableStudents, setAvailableStudents] = useState>([]); const [bulkCreating, setBulkCreating] = useState(false); // Demo accounts state const [demoAccounts, setDemoAccounts] = useState>([]); const [demoLoading, setDemoLoading] = useState(true); const [deleteDemoId, setDeleteDemoId] = useState(null); const [deleteAllDemoOpen, setDeleteAllDemoOpen] = useState(false); // Data import state const [importType, setImportType] = useState<'students' | 'assessments' | 'grades'>('students'); const [importFile, setImportFile] = useState(null); const [importing, setImporting] = useState(false); const [importResult, setImportResult] = useState<{ created: number; skipped: number; errorCount: number; errors: string[]; detectedColumns: string[]; } | null>(null); const [dragOver, setDragOver] = useState(false); // Export history const [exportHistory, setExportHistory] = useState>([]); const isAdmin = currentUser?.role === 'SCHOOL_ADMIN' || currentUser?.role === 'SUPER_ADMIN'; const loadSchools = useCallback(async () => { setSchoolLoading(true); try { const data = await fetchSchools(); setSchools(data); if (currentUser?.schoolId) { const mySchool = data.find((s) => s.id === currentUser.schoolId); if (mySchool) { setSelectedSchool(mySchool); setSchoolForm({ name: mySchool.name, schoolType: mySchool.schoolType, country: mySchool.country, timezone: mySchool.timezone, }); } } else if (data.length > 0) { setSelectedSchool(data[0]); setSchoolForm({ name: data[0].name, schoolType: data[0].schoolType, country: data[0].country, timezone: data[0].timezone, }); } } catch { toast.error(t('error.generic')); } finally { setSchoolLoading(false); } }, [currentUser?.schoolId]); const loadSchoolYears = useCallback(async () => { if (!currentUser?.schoolId) return; setYearsLoading(true); try { const data = await fetchSchoolYears(currentUser.schoolId); setSchoolYears(data); } catch { toast.error(t('error.generic')); } finally { setYearsLoading(false); } }, [currentUser?.schoolId]); const loadSubjects = useCallback(async () => { setSubjectsLoading(true); try { const data = await fetchSubjects(currentUser?.schoolId ?? undefined); setSubjects(data); } catch { toast.error(t('error.generic')); } finally { setSubjectsLoading(false); } }, [currentUser?.schoolId]); const loadAuditLog = useCallback(async () => { setAuditLoading(true); try { const data = await fetchAuditLog({ schoolId: currentUser?.schoolId ?? undefined, action: auditActionFilter !== 'all' ? auditActionFilter : undefined, startDate: auditDateFrom || undefined, endDate: auditDateTo || undefined, }); setAuditEntries(data); } catch { toast.error(t('error.generic')); } finally { setAuditLoading(false); } }, [currentUser?.schoolId, auditActionFilter, auditDateFrom, auditDateTo]); const loadUsers = useCallback(async () => { setUsersLoading(true); try { const data = await fetchUsers(currentUser?.schoolId ?? undefined); setUsers(data); } catch { toast.error(t('error.generic')); } finally { setUsersLoading(false); } }, [currentUser?.schoolId]); const loadDemoAccounts = useCallback(async () => { setDemoLoading(true); try { const response = await fetch('/api/demo-accounts'); if (!response.ok) { throw new Error(`API error ${response.status}`); } const data = await response.json(); setDemoAccounts(Array.isArray(data) ? data : []); } catch (err) { console.error('Failed to load demo accounts:', err); setDemoAccounts([]); } finally { setDemoLoading(false); } }, []); // Reload demo accounts when demo tab is selected const handleTabChange = useCallback((tab: string) => { setActiveTab(tab); if (tab === 'demo' && isAdmin) { loadDemoAccounts(); } }, [isAdmin, loadDemoAccounts]); useEffect(() => { loadSchools(); loadSchoolYears(); loadSubjects(); loadAuditLog(); loadUsers(); if (isAdmin) loadDemoAccounts(); }, [loadSchools, loadSchoolYears, loadSubjects, loadAuditLog, loadUsers, isAdmin, loadDemoAccounts]); const handleSaveSchool = async () => { if (!selectedSchool) return; setSchoolSaving(true); try { await updateSchool({ id: selectedSchool.id, ...schoolForm, }); toast.success(t('settings.school_saved')); loadSchools(); } catch { toast.error(t('error.generic')); } finally { setSchoolSaving(false); } }; const handleCreateYear = async () => { if (!currentUser?.schoolId) return; try { await createSchoolYear({ schoolId: currentUser.schoolId, ...yearForm, }); toast.success(t('settings.year_created')); setShowCreateYear(false); setYearForm({ label: '', startDate: '', endDate: '' }); loadSchoolYears(); } catch { toast.error(t('error.generic')); } }; const handleCreateSubject = async () => { try { await createSubject({ schoolId: currentUser?.schoolId ?? null, ...subjectForm, }); toast.success(t('settings.subject_created')); setShowCreateSubject(false); setSubjectForm({ name: '', gradeLevelMin: 1, gradeLevelMax: 13 }); loadSubjects(); } catch { toast.error(t('error.generic')); } }; const handleUpdateSubject = async () => { if (!editingSubject) return; try { await updateSubject({ id: editingSubject.id, name: subjectForm.name, gradeLevelMin: subjectForm.gradeLevelMin, gradeLevelMax: subjectForm.gradeLevelMax, }); toast.success(t('settings.subject_updated')); setEditingSubject(null); setSubjectForm({ name: '', gradeLevelMin: 1, gradeLevelMax: 13 }); loadSubjects(); } catch { toast.error(t('error.generic')); } }; const handleDeleteSubject = async () => { if (!deleteSubjectId) return; try { await deleteSubject(deleteSubjectId); toast.success(t('settings.subject_deleted')); setDeleteSubjectId(null); loadSubjects(); } catch { toast.error(t('error.generic')); } }; const handleCsvExport = (type: 'students' | 'progress' | 'assessments' | 'grades' | 'attendance') => { downloadCsvExport({ type, schoolId: currentUser?.schoolId ?? undefined, schoolYearId: useAppStore.getState().schoolYearId ?? undefined, }); setExportHistory((prev) => [{ type, date: new Date().toLocaleDateString() }, ...prev].slice(0, 20)); toast.success(t('settings.data_export_started')); }; const handleImport = async () => { if (!importFile || !currentUser?.schoolId) return; setImporting(true); setImportResult(null); try { const formData = new FormData(); formData.append('file', importFile); formData.append('type', importType); formData.append('schoolId', currentUser.schoolId); const res = await fetch('/api/data-import', { method: 'POST', body: formData, }); if (!res.ok) { const err = await res.json().catch(() => ({ error: 'Import failed' })); throw new Error(err.error || 'Import failed'); } const result = await res.json(); setImportResult(result); toast.success(t('import.success')); setImportFile(null); } catch (err) { toast.error(err instanceof Error ? err.message : t('import.errors')); } finally { setImporting(false); } }; const handleDownloadSample = (type: 'students' | 'assessments' | 'grades') => { window.open(`/api/data-import?type=${type}`, '_blank'); }; const handleErasureRequest = async () => { try { await requestDataErasure({ scope: erasureScope, }); toast.success(t('settings.data_erasure_requested')); setShowErasureDialog(false); } catch { toast.error(t('error.generic')); } }; // ── Demo account handlers ── const handleToggleDemoAccount = async (id: string, currentDeletedAt: string | null) => { try { const action = currentDeletedAt ? 'enable' : 'disable'; await apiPut(`/api/demo-accounts/${id}`, { action }); toast.success(t('settings.demo_account_toggled')); loadDemoAccounts(); } catch { toast.error(t('error.generic')); } }; const handleDeleteDemoAccount = async (id: string) => { try { await apiDelete(`/api/demo-accounts/${id}`); toast.success(t('settings.demo_account_deleted')); setDeleteDemoId(null); loadDemoAccounts(); } catch { toast.error(t('error.generic')); } }; const handleDisableAllDemo = async () => { try { await apiPut('/api/demo-accounts', { action: 'disable' }); toast.success(t('settings.demo_disabled')); loadDemoAccounts(); } catch { toast.error(t('error.generic')); } }; const handleEnableAllDemo = async () => { try { await apiPut('/api/demo-accounts', { action: 'enable' }); toast.success(t('settings.demo_enabled')); loadDemoAccounts(); } catch { toast.error(t('error.generic')); } }; const handleDeleteAllDemo = async () => { try { await apiDelete('/api/demo-accounts'); toast.success(t('settings.demo_deleted')); setDeleteAllDemoOpen(false); loadDemoAccounts(); } catch { toast.error(t('error.generic')); } }; const getRoleBadgeColor = (role: string) => { switch (role) { case 'SCHOOL_ADMIN': return 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300'; case 'TEACHER': return 'bg-teal-100 text-teal-700 dark:bg-teal-900/30 dark:text-teal-300'; case 'STUDENT': return 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300'; case 'PARENT': return 'bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-300'; case 'SUPER_ADMIN': return 'bg-rose-100 text-rose-700 dark:bg-rose-900/30 dark:text-rose-300'; default: return 'bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-300'; } }; const getRoleIcon = (role: string) => { switch (role) { case 'SCHOOL_ADMIN': return Shield; case 'TEACHER': return GraduationCap; case 'STUDENT': return UserCheck; case 'PARENT': return Heart; default: return Users; } }; const openCreateUserDialog = () => { setEditingUser(null); setUserForm({ firstName: '', lastName: '', email: '', password: '', role: 'TEACHER', }); setShowUserDialog(true); }; const openCreateStudentAccountDialog = async () => { setStudentAccountForm({ firstName: '', lastName: '', email: '', password: 'Schule2025!', studentId: '' }); try { const students = await fetchStudents(currentUser?.schoolId ?? undefined); setAvailableStudents(students.map((s) => ({ id: s.id, firstName: s.firstName, lastName: s.lastName }))); } catch { // ignore } setShowStudentAccountDialog(true); }; const openBulkStudentDialog = async () => { setBulkForm({ defaultPassword: 'Schule2025!', emailDomain: 'schule.de', selectedStudentIds: [] }); try { const students = await fetchStudents(currentUser?.schoolId ?? undefined); setAvailableStudents(students.map((s) => ({ id: s.id, firstName: s.firstName, lastName: s.lastName }))); } catch { // ignore } setShowBulkStudentDialog(true); }; const handleCreateStudentAccount = async () => { try { await createStudentUserAccount({ schoolId: currentUser?.schoolId ?? '', email: studentAccountForm.email, password: studentAccountForm.password, firstName: studentAccountForm.firstName, lastName: studentAccountForm.lastName, role: 'STUDENT', studentId: studentAccountForm.studentId || undefined, }); toast.success(t('settings.student_account_created')); setShowStudentAccountDialog(false); loadUsers(); } catch (err) { const message = err instanceof Error ? err.message : t('error.generic'); toast.error(message); } }; const handleBulkCreateStudents = async () => { if (bulkForm.selectedStudentIds.length === 0) { toast.error(t('error.generic')); return; } setBulkCreating(true); try { const result = await bulkCreateStudentAccounts({ schoolId: currentUser?.schoolId ?? '', defaultPassword: bulkForm.defaultPassword, studentIds: bulkForm.selectedStudentIds, emailDomain: bulkForm.emailDomain, }); toast.success(t('settings.bulk_accounts_created') + ` (${result.count})`); setShowBulkStudentDialog(false); loadUsers(); } catch (err) { const message = err instanceof Error ? err.message : t('error.generic'); toast.error(message); } finally { setBulkCreating(false); } }; const openEditUserDialog = (u: UserAccount) => { setEditingUser(u); setUserForm({ firstName: u.firstName, lastName: u.lastName, email: u.email, password: '', role: u.role as 'TEACHER' | 'SCHOOL_ADMIN' | 'SUPER_ADMIN' | 'STUDENT' | 'PARENT', }); setShowUserDialog(true); }; const handleSaveUser = async () => { try { if (editingUser) { await updateUser({ id: editingUser.id, firstName: userForm.firstName, lastName: userForm.lastName, email: userForm.email, role: userForm.role, }); toast.success(t('users.updated')); } else { if (userForm.password.length < 8) { toast.error(t('users.password_hint')); return; } await createUser({ schoolId: currentUser?.schoolId ?? null, email: userForm.email, password: userForm.password, firstName: userForm.firstName, lastName: userForm.lastName, role: userForm.role, }); toast.success(t('users.created')); } setShowUserDialog(false); setUserForm({ firstName: '', lastName: '', email: '', password: '', role: 'TEACHER' }); loadUsers(); } catch (err) { toast.error(err instanceof Error ? err.message : t('error.generic')); } }; const handleDeleteUser = async () => { if (!deleteUserId) return; try { await deleteUser(deleteUserId); toast.success(t('users.deleted')); setDeleteUserId(null); loadUsers(); } catch (err) { toast.error(err instanceof Error ? err.message : t('error.generic')); } }; const roleBadge = (role: string) => { if (role === 'SUPER_ADMIN') return 'bg-rose-100 text-rose-700 dark:bg-rose-900/30 dark:text-rose-300'; if (role === 'SCHOOL_ADMIN') return 'bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-300'; return 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300'; }; const roleLabel = (role: string) => { if (role === 'SUPER_ADMIN') return t('role.super_admin'); if (role === 'SCHOOL_ADMIN') return t('role.school_admin'); return t('role.teacher'); }; if (!isAdmin) { return (

{t('settings.title')} — {t('role.teacher')}

{t('error.forbidden')}

); } return ( {/* Header */}

{t('settings.title')}

{t('app.subtitle')}

{/* Quick stats banner */}
{[ { icon: School, label: t('polish.quick_stats'), value: schools.length, color: 'from-violet-50 to-violet-100/50 dark:from-violet-900/20 dark:to-violet-800/10', text: 'text-violet-700 dark:text-violet-300', iconBg: 'bg-gradient-to-br from-violet-400 to-violet-500', border: 'border-violet-200/40 dark:border-violet-900/30', tab: 'school' }, { icon: Users, label: t('polish.total_users'), value: users.length, color: 'from-emerald-50 to-emerald-100/50 dark:from-emerald-900/20 dark:to-emerald-800/10', text: 'text-emerald-700 dark:text-emerald-300', iconBg: 'bg-gradient-to-br from-emerald-400 to-teal-500', border: 'border-emerald-200/40 dark:border-emerald-900/30', tab: 'users' }, { icon: BookOpen, label: t('polish.total_subjects'), value: subjects.length, color: 'from-amber-50 to-amber-100/50 dark:from-amber-900/20 dark:to-amber-800/10', text: 'text-amber-700 dark:text-amber-300', iconBg: 'bg-gradient-to-br from-amber-400 to-amber-500', border: 'border-amber-200/40 dark:border-amber-900/30', tab: 'subjects' }, { icon: Calendar, label: t('polish.total_years'), value: schoolYears.length, color: 'from-teal-50 to-teal-100/50 dark:from-teal-900/20 dark:to-teal-800/10', text: 'text-teal-700 dark:text-teal-300', iconBg: 'bg-gradient-to-br from-teal-400 to-emerald-500', border: 'border-teal-200/40 dark:border-teal-900/30', tab: 'years' }, { icon: Activity, label: t('settings.tab_audit'), value: auditEntries.length, color: 'from-slate-50 to-slate-100/50 dark:from-slate-800/40 dark:to-slate-700/10', text: 'text-slate-700 dark:text-slate-300', iconBg: 'bg-gradient-to-br from-slate-400 to-slate-500', border: 'border-slate-200/40 dark:border-slate-700/30', tab: 'audit' }, ].map((stat) => (
setActiveTab(stat.tab)}>

{stat.label}

{stat.value}

))}
{/* Tabs */} {t('settings.tab_school')} {t('settings.tab_years')} {t('settings.tab_subjects')} {t('settings.tab_users')} {t('settings.tab_audit')} {t('settings.tab_data')} {isAdmin && ( {t('settings.demo_accounts')} )} {/* ── School Info Tab ─────────────────────────────────── */}
{t('settings.tab_school')}
{t('settings.school_name')} & {t('settings.school_type')}
{schoolLoading ? (
) : selectedSchool ? (
setSchoolForm({ ...schoolForm, name: e.target.value })} className="border-emerald-200 dark:border-emerald-900/30 focus:border-emerald-500 rounded-xl" />
setSchoolForm({ ...schoolForm, country: e.target.value })} className="border-emerald-200 dark:border-emerald-900/30 focus:border-emerald-500 rounded-xl" />
setSchoolForm({ ...schoolForm, timezone: e.target.value })} className="border-emerald-200 dark:border-emerald-900/30 focus:border-emerald-500 rounded-xl" />
{selectedSchool._count && (
{selectedSchool._count.users} {t('label.teacher') || 'Lehrkräfte'} {selectedSchool._count.classGroups} {t('label.class')} {selectedSchool._count.students} {t('label.student') || 'Schüler'}
)}
) : (

{t('dashboard.no_classes')}

)}
{/* ── School Years Tab ────────────────────────────────── */}
{t('settings.tab_years')}
{t('label.school_year')}
{yearsLoading ? (
{Array.from({ length: 3 }).map((_, i) => )}
) : schoolYears.length === 0 ? (

{t('settings.audit_no_entries')}

) : (
{schoolYears.map((yr) => { const yearWithCount = yr as SchoolYear & { _count?: { classGroups: number; enrollments: number } }; return (

{yr.label}

{new Date(yr.startDate).toLocaleDateString()} — {new Date(yr.endDate).toLocaleDateString()}

{yearWithCount._count && ( <> {yearWithCount._count.classGroups} {t('settings.year_classes')} {yearWithCount._count.enrollments} {t('settings.year_enrollments')} )}
); })}
)} {/* Create Year Dialog */} {t('action.create')} {t('label.school_year')} {t('settings.year_label')}
setYearForm({ ...yearForm, label: e.target.value })} placeholder="2025/2026" className="rounded-xl" />
setYearForm({ ...yearForm, startDate: e.target.value })} className="rounded-xl" />
setYearForm({ ...yearForm, endDate: e.target.value })} className="rounded-xl" />
{/* ── Subjects Tab ────────────────────────────────────── */}
{t('settings.tab_subjects')}
{t('label.subject')}
{subjectsLoading ? (
{Array.from({ length: 3 }).map((_, i) => )}
) : subjects.length === 0 ? (

{t('settings.audit_no_entries')}

) : (
{subjects.map((sub) => (
{sub.name.slice(0, 2).toUpperCase()}

{sub.name}

{t('label.grade')} {sub.gradeLevelMin}–{sub.gradeLevelMax} · {sub.schoolId ? t('settings.subject_school') : t('settings.subject_global')}

))}
)} {/* Create/Edit Subject Dialog */} { if (!open) { setShowCreateSubject(false); setEditingSubject(null); setSubjectForm({ name: '', gradeLevelMin: 1, gradeLevelMax: 13 }); } }}> {editingSubject ? t('action.edit') : t('action.create')} {t('label.subject')}
setSubjectForm({ ...subjectForm, name: e.target.value })} className="rounded-xl" />
setSubjectForm({ ...subjectForm, gradeLevelMin: parseInt(e.target.value) || 1 })} className="rounded-xl" />
setSubjectForm({ ...subjectForm, gradeLevelMax: parseInt(e.target.value) || 13 })} className="rounded-xl" />
{/* Delete Subject Alert */} { if (!open) setDeleteSubjectId(null); }}> {t('action.delete')} {t('label.subject')} {t('settings.subject_deleted')} setDeleteSubjectId(null)}>{t('action.cancel')} {t('action.confirm')}
{/* ── Users Tab ──────────────────────────────────────── */}
{t('users.title')}
{t('users.subtitle')}
{t('users.count', { count: users.length })}
{usersLoading ? (
{Array.from({ length: 4 }).map((_, i) => )}
) : users.length === 0 ? (

{t('users.no_users')}

) : (
{users.map((u) => (
{u.firstName[0]}{u.lastName[0]}

{u.firstName} {u.lastName}

{roleLabel(u.role)} {u.deletedAt && ( {t('users.inactive')} )}
{u.email}
{/* Assigned classes */}
{u.classGroupTeachers && u.classGroupTeachers.length > 0 ? ( u.classGroupTeachers.map((ct) => ( {ct.classGroup.name} ({ct.role === 'HOMEROOM_TEACHER' ? t('classes.homeroom_teacher') : t('classes.subject_teacher')}) )) ) : ( {t('users.no_classes_assigned')} )}
))}
)} {/* Create/Edit User Dialog */} { if (!open) { setShowUserDialog(false); setEditingUser(null); setUserForm({ firstName: '', lastName: '', email: '', password: '', role: 'TEACHER' }); } }}> {editingUser ? t('users.edit_user') : t('users.create_user')} {t('users.subtitle')}
setUserForm({ ...userForm, firstName: e.target.value })} className="rounded-xl border-rose-200/50 dark:border-rose-900/30" />
setUserForm({ ...userForm, lastName: e.target.value })} className="rounded-xl border-rose-200/50 dark:border-rose-900/30" />
setUserForm({ ...userForm, email: e.target.value })} className="rounded-xl border-rose-200/50 dark:border-rose-900/30" />
{!editingUser && (
setUserForm({ ...userForm, password: e.target.value })} placeholder="••••••••" className="rounded-xl border-rose-200/50 dark:border-rose-900/30" />

{t('users.password_hint')}

)}
{/* Delete User Alert */} { if (!open) setDeleteUserId(null); }}> {t('users.delete_user')} {t('users.delete_confirm')} setDeleteUserId(null)}>{t('action.cancel')} {t('action.confirm')} {/* Create Student Account Dialog */} { if (!open) setShowStudentAccountDialog(false); }}> {t('settings.create_student_account')} {t('auth.student_login_desc')}
setStudentAccountForm({ ...studentAccountForm, firstName: e.target.value })} className="rounded-xl" />
setStudentAccountForm({ ...studentAccountForm, lastName: e.target.value })} className="rounded-xl" />
setStudentAccountForm({ ...studentAccountForm, email: e.target.value })} className="rounded-xl" placeholder="name@schule.de" />
setStudentAccountForm({ ...studentAccountForm, password: e.target.value })} className="rounded-xl" />
{availableStudents.length > 0 && (
)}
{/* Bulk Create Student Accounts Dialog */} { if (!open) setShowBulkStudentDialog(false); }}> {t('settings.bulk_create_students')} {t('auth.bulk_create')}
setBulkForm({ ...bulkForm, defaultPassword: e.target.value })} className="rounded-xl" />
setBulkForm({ ...bulkForm, emailDomain: e.target.value })} className="rounded-xl" placeholder="schule.de" />
{availableStudents.map((s) => ( ))}
{/* ── Audit Log Tab ───────────────────────────────────── */}
{t('settings.audit_title')}
{t('settings.audit_action')} & {t('settings.audit_timestamp')}
{/* Search input */}
setAuditSearch(e.target.value)} placeholder={t('polish.audit_search')} className="pl-9 h-9 rounded-lg border-violet-200/50 dark:border-violet-900/30 bg-violet-50/20 dark:bg-violet-900/10" />
{/* Color-coded action filter chips */}
{[ { value: 'all', label: t('polish.all_actions'), color: 'bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300 ring-gray-300 dark:ring-gray-700' }, { value: 'CREATE', label: t('polish.create_action'), color: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300 ring-emerald-300 dark:ring-emerald-700' }, { value: 'UPDATE', label: t('polish.update_action'), color: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300 ring-amber-300 dark:ring-amber-700' }, { value: 'DELETE', label: t('polish.delete_action'), color: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300 ring-red-300 dark:ring-red-700' }, { value: 'LOGIN', label: t('polish.login_action'), color: 'bg-teal-100 text-teal-700 dark:bg-teal-900/30 dark:text-teal-300 ring-teal-300 dark:ring-teal-700' }, { value: 'EXPORT', label: t('action.export'), color: 'bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-300 ring-violet-300 dark:ring-violet-700' }, ].map((chip) => ( ))}
{/* Date filters */}
setAuditDateFrom(e.target.value)} className="h-8 w-36 rounded-lg text-xs border-violet-200 dark:border-violet-900/30" />
setAuditDateTo(e.target.value)} className="h-8 w-36 rounded-lg text-xs border-violet-200 dark:border-violet-900/30" />
{/* Timeline */} {auditLoading ? (
{Array.from({ length: 5 }).map((_, i) => )}
) : auditEntries.length === 0 ? (

{t('settings.audit_no_entries')}

) : (() => { const searchLower = auditSearch.trim().toLowerCase(); const filtered = searchLower ? auditEntries.filter((e) => { const actorName = e.user ? `${e.user.firstName} ${e.user.lastName}`.toLowerCase() : 'system'; return ( e.entityType.toLowerCase().includes(searchLower) || e.action.toLowerCase().includes(searchLower) || actorName.includes(searchLower) || (e.entityId ?? '').toLowerCase().includes(searchLower) ); }) : auditEntries; if (filtered.length === 0) { return (

{t('polish.no_results')}

); } return (
{/* Vertical timeline line */}
{filtered.map((entry, i) => { const colorClass = actionColors[entry.action] || 'bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-300'; const actorName = entry.user ? `${entry.user.firstName} ${entry.user.lastName}` : 'System'; const dotColor = entry.action === 'CREATE' ? 'bg-emerald-500' : entry.action === 'UPDATE' ? 'bg-amber-500' : entry.action === 'DELETE' ? 'bg-red-500' : entry.action === 'LOGIN' ? 'bg-teal-500' : entry.action === 'EXPORT' ? 'bg-violet-500' : 'bg-gray-400'; const iconBg = entry.action === 'CREATE' ? 'bg-emerald-100 dark:bg-emerald-900/30 text-emerald-600 dark:text-emerald-300' : entry.action === 'UPDATE' ? 'bg-amber-100 dark:bg-amber-900/30 text-amber-600 dark:text-amber-300' : entry.action === 'DELETE' ? 'bg-red-100 dark:bg-red-900/30 text-red-600 dark:text-red-300' : entry.action === 'LOGIN' ? 'bg-teal-100 dark:bg-teal-900/30 text-teal-600 dark:text-teal-300' : 'bg-violet-100 dark:bg-violet-900/30 text-violet-600 dark:text-violet-300'; return ( {/* Timeline dot */}
{entry.action === 'CREATE' ? : entry.action === 'UPDATE' ? : entry.action === 'DELETE' ? : entry.action === 'LOGIN' ? : entry.action === 'EXPORT' ? : }
{entry.action} {entry.entityType} {entry.entityId && ( {entry.entityId.slice(0, 8)}… )}
{new Date(entry.timestamp).toLocaleString()}
{actorName === 'System' ? 'S' : actorName.split(' ').map((n) => n[0]).join('').slice(0, 2)}
{actorName}
); })}
); })()} {/* ── Data Management Tab ─────────────────────────────── */}
{/* Data Import */}
{t('import.title')}
{t('import.csv_upload')}
{/* Import type selector */}
{(['students', 'assessments', 'grades'] as const).map((impType) => { const icons = { students: GraduationCap, assessments: ClipboardCheck, grades: BarChart3 }; const Icon = icons[impType]; return ( ); })}
{/* Drag and drop zone */}
{ e.preventDefault(); setDragOver(true); }} onDragLeave={() => setDragOver(false)} onDrop={(e) => { e.preventDefault(); setDragOver(false); const file = e.dataTransfer.files[0]; if (file && file.name.endsWith('.csv')) { setImportFile(file); setImportResult(null); } else { toast.error('Please upload a CSV file'); } }} >

{importFile ? importFile.name : t('import.csv_upload')}

Drag & drop or click to select

{ const file = e.target.files?.[0]; if (file) { setImportFile(file); setImportResult(null); } }} />
{/* Import button */}
{/* Import result */} {importResult && (

{t('import.summary')}

{importResult.created}

{t('import.success')}

{importResult.skipped}

Skipped

{importResult.errorCount}

{t('import.errors')}

{importResult.detectedColumns.length > 0 && (
{importResult.detectedColumns.map((col) => ( {col} ))}
)} {importResult.errors.length > 0 && (
{importResult.errors.slice(0, 5).map((err, i) => (

{err}

))}
)}
)}
{/* CSV Export */}
{t('settings.data_export_csv')}
{t('csv.export_title')}
{[ { type: 'students' as const, iconComponent: GraduationCap, label: t('export.students'), color: 'from-emerald-50/60 to-emerald-100/0 dark:from-emerald-900/15 dark:to-emerald-900/0', border: 'border-emerald-200/40 dark:border-emerald-900/30', iconBg: 'bg-gradient-to-br from-emerald-400 to-emerald-500' }, { type: 'progress' as const, iconComponent: TrendingUp, label: t('export.progress'), color: 'from-amber-50/60 to-amber-100/0 dark:from-amber-900/15 dark:to-amber-900/0', border: 'border-amber-200/40 dark:border-amber-900/30', iconBg: 'bg-gradient-to-br from-amber-400 to-amber-500' }, { type: 'assessments' as const, iconComponent: ClipboardCheck, label: t('export.assessments'), color: 'from-teal-50/60 to-teal-100/0 dark:from-teal-900/15 dark:to-teal-900/0', border: 'border-teal-200/40 dark:border-teal-900/30', iconBg: 'bg-gradient-to-br from-teal-400 to-teal-500' }, { type: 'grades' as const, iconComponent: BarChart3, label: t('export.grades'), color: 'from-violet-50/60 to-violet-100/0 dark:from-violet-900/15 dark:to-violet-900/0', border: 'border-violet-200/40 dark:border-violet-900/30', iconBg: 'bg-gradient-to-br from-violet-400 to-violet-500' }, { type: 'attendance' as const, iconComponent: CalendarDays, label: t('export.attendance'), color: 'from-rose-50/60 to-rose-100/0 dark:from-rose-900/15 dark:to-rose-900/0', border: 'border-rose-200/40 dark:border-rose-900/30', iconBg: 'bg-gradient-to-br from-rose-400 to-rose-500' }, ].map((item) => ( handleCsvExport(item.type)} >

{item.label}

CSV

))}
{/* Export history */} {exportHistory.length > 0 && (

{t('settings.export_history')}

{exportHistory.map((item, idx) => (
{item.type} {item.date}
))}
)}
{/* GDPR Data Erasure — Danger Zone */}

{t('settings.data_erasure')}

{t('settings.data_erasure_desc')}

{t('settings.data_erasure_desc')}

{/* Erasure Dialog */} {t('settings.data_erasure')} {t('settings.data_erasure_desc')}
{t('action.cancel')} {t('settings.data_erasure_confirm')}
{/* ── Demo Accounts Tab ───────────────────────────────── */} {isAdmin && ( {/* Demo Data Danger Zone Card */}
{t('settings.demo_data_title')}
{t('settings.demo_data_desc')}
{/* Demo record count */}

{t('settings.demo_record_count')}

{demoAccounts.length} {t('settings.demo_record_count').toLowerCase()}

{/* Big red Delete All Demo Data button */} {/* Environmental-friendly messaging */}

{t('settings.env_tip')}

{/* Demo Accounts Card */}
{t('settings.demo_accounts')}
{t('settings.demo_accounts_desc')}
{/* Info hint */}

{t('settings.demo_toggle_hint')}

{/* Action buttons */}
{/* Demo accounts list */} {demoLoading ? (
{Array.from({ length: 3 }).map((_, i) => )}
) : demoAccounts.length === 0 ? (

{t('settings.demo_no_accounts')}

) : (
{(demoAccounts ?? []).map((account) => { const isActive = !account.deletedAt; const RoleIcon = getRoleIcon(account.role); return (

{account.firstName} {account.lastName}

{account.email}

{account.role} {isActive ? t('settings.demo_active') : t('settings.demo_inactive')}
handleToggleDemoAccount(account.id, account.deletedAt)} className="data-[state=checked]:bg-emerald-500 data-[state=unchecked]:bg-gray-300 dark:data-[state=unchecked]:bg-gray-600" />
); })}
)} {/* Delete single demo account confirmation */} { if (!open) setDeleteDemoId(null); }}> {t('action.delete')} {t('settings.demo_delete_one_confirm')} {t('action.cancel')} deleteDemoId && handleDeleteDemoAccount(deleteDemoId)} className="bg-rose-600 hover:bg-rose-700 text-white"> {t('action.delete')} {/* Delete all demo accounts confirmation */} {t('settings.demo_delete_all')} {t('settings.demo_delete_confirm')} {t('action.cancel')} {t('action.delete')}
)}
); }