'use client' import React, { useState, useEffect, useMemo } from 'react' import Link from 'next/link' import { useSDK } from '@/lib/sdk' import { StepHeader, STEP_EXPLANATIONS } from '@/components/sdk/StepHeader' import { Course, CourseCategory, Enrollment, EnrollmentStatus, AcademyStatistics, COURSE_CATEGORY_INFO, ENROLLMENT_STATUS_INFO, isEnrollmentOverdue, getDaysUntilDeadline } from '@/lib/sdk/academy/types' import { fetchSDKAcademyList, generateAllCourses } from '@/lib/sdk/academy/api' // ============================================================================= // TYPES // ============================================================================= type TabId = 'overview' | 'courses' | 'enrollments' | 'certificates' | 'settings' interface Tab { id: TabId label: string count?: number countColor?: string } // ============================================================================= // COMPONENTS // ============================================================================= function TabNavigation({ tabs, activeTab, onTabChange }: { tabs: Tab[] activeTab: TabId onTabChange: (tab: TabId) => void }) { return (
) } function StatCard({ label, value, color = 'gray', icon, trend }: { label: string value: number | string color?: 'gray' | 'blue' | 'yellow' | 'red' | 'green' | 'purple' icon?: React.ReactNode trend?: { value: number; label: string } }) { const colorClasses = { gray: 'border-gray-200 text-gray-900', blue: 'border-blue-200 text-blue-600', yellow: 'border-yellow-200 text-yellow-600', red: 'border-red-200 text-red-600', green: 'border-green-200 text-green-600', purple: 'border-purple-200 text-purple-600' } return (
{label}
{value}
{trend && (
= 0 ? 'text-green-600' : 'text-red-600'}`}> {trend.value >= 0 ? '+' : ''}{trend.value} {trend.label}
)}
{icon && (
{icon}
)}
) } function CourseCard({ course, enrollmentCount }: { course: Course; enrollmentCount: number }) { const categoryInfo = COURSE_CATEGORY_INFO[course.category] || COURSE_CATEGORY_INFO['custom'] return (
{/* Header Badges */}
{categoryInfo.label}
{/* Course Title */}

{course.title}

{course.description}

{/* Course Meta */}
{course.lessons.length} Lektionen {course.durationMinutes} Min. {enrollmentCount} Teilnehmer
{/* Right Side - Roles */}
{course.requiredForRoles.includes('all') ? 'Pflicht fuer alle' : `${course.requiredForRoles.length} Rollen`}
{new Date(course.updatedAt).toLocaleDateString('de-DE')}
{/* Footer */}
Erstellt: {new Date(course.createdAt).toLocaleDateString('de-DE')}
Details
) } function EnrollmentCard({ enrollment, courseName }: { enrollment: Enrollment; courseName: string }) { const statusInfo = ENROLLMENT_STATUS_INFO[enrollment.status] const overdue = isEnrollmentOverdue(enrollment) const daysUntil = getDaysUntilDeadline(enrollment.deadline) return (
{/* Status Badge */}
{statusInfo.label} {overdue && ( Ueberfaellig )}
{/* User Info */}

{enrollment.userName}

{enrollment.userEmail}

{courseName}

{/* Progress Bar */}
Fortschritt {enrollment.progress}%
{/* Right Side - Deadline */}
{enrollment.status === 'completed' ? 'Abgeschlossen' : overdue ? `${Math.abs(daysUntil)} Tage ueberfaellig` : `${daysUntil} Tage verbleibend` }
Frist: {new Date(enrollment.deadline).toLocaleDateString('de-DE')}
{/* Footer */}
Gestartet: {new Date(enrollment.startedAt).toLocaleDateString('de-DE')}
{enrollment.completedAt && (
Abgeschlossen: {new Date(enrollment.completedAt).toLocaleDateString('de-DE')}
)}
) } function FilterBar({ selectedCategory, selectedStatus, onCategoryChange, onStatusChange, onClear }: { selectedCategory: CourseCategory | 'all' selectedStatus: EnrollmentStatus | 'all' onCategoryChange: (category: CourseCategory | 'all') => void onStatusChange: (status: EnrollmentStatus | 'all') => void onClear: () => void }) { const hasFilters = selectedCategory !== 'all' || selectedStatus !== 'all' return (
Filter: {/* Category Filter */} {/* Enrollment Status Filter */} {/* Clear Filters */} {hasFilters && ( )}
) } // ============================================================================= // MAIN PAGE // ============================================================================= export default function AcademyPage() { const { state } = useSDK() const [activeTab, setActiveTab] = useState('overview') const [courses, setCourses] = useState([]) const [enrollments, setEnrollments] = useState([]) const [statistics, setStatistics] = useState(null) const [isLoading, setIsLoading] = useState(true) const [isGenerating, setIsGenerating] = useState(false) const [generateResult, setGenerateResult] = useState<{ generated: number; skipped: number; errors: string[] } | null>(null) // Filters const [selectedCategory, setSelectedCategory] = useState('all') const [selectedStatus, setSelectedStatus] = useState('all') // Load data from SDK backend useEffect(() => { loadData() }, []) // Calculate tab counts const tabCounts = useMemo(() => { return { courses: courses.length, enrollments: enrollments.filter(e => e.status !== 'completed').length, certificates: enrollments.filter(e => e.certificateId).length, overdue: enrollments.filter(e => isEnrollmentOverdue(e)).length } }, [courses, enrollments]) // Filtered courses const filteredCourses = useMemo(() => { let filtered = [...courses] if (selectedCategory !== 'all') { filtered = filtered.filter(c => c.category === selectedCategory) } return filtered.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()) }, [courses, selectedCategory]) // Filtered enrollments const filteredEnrollments = useMemo(() => { let filtered = [...enrollments] if (selectedStatus !== 'all') { filtered = filtered.filter(e => e.status === selectedStatus) } // Sort: overdue first, then by deadline return filtered.sort((a, b) => { const aOverdue = isEnrollmentOverdue(a) ? -1 : 0 const bOverdue = isEnrollmentOverdue(b) ? -1 : 0 if (aOverdue !== bOverdue) return aOverdue - bOverdue return getDaysUntilDeadline(a.deadline) - getDaysUntilDeadline(b.deadline) }) }, [enrollments, selectedStatus]) // Enrollment counts per course const enrollmentCountByCourseId = useMemo(() => { const counts: Record = {} enrollments.forEach(e => { counts[e.courseId] = (counts[e.courseId] || 0) + 1 }) return counts }, [enrollments]) // Course name lookup const courseNameById = useMemo(() => { const map: Record = {} courses.forEach(c => { map[c.id] = c.title }) return map }, [courses]) const tabs: Tab[] = [ { id: 'overview', label: 'Uebersicht' }, { id: 'courses', label: 'Kurse', count: tabCounts.courses, countColor: 'bg-blue-100 text-blue-600' }, { id: 'enrollments', label: 'Einschreibungen', count: tabCounts.enrollments, countColor: 'bg-yellow-100 text-yellow-600' }, { id: 'certificates', label: 'Zertifikate', count: tabCounts.certificates, countColor: 'bg-green-100 text-green-600' }, { id: 'settings', label: 'Einstellungen' } ] const stepInfo = STEP_EXPLANATIONS['academy'] const loadData = async () => { setIsLoading(true) try { const data = await fetchSDKAcademyList() setCourses(data.courses) setEnrollments(data.enrollments) setStatistics(data.statistics) } catch (error) { console.error('Failed to load Academy data:', error) } finally { setIsLoading(false) } } const handleGenerateAll = async () => { setIsGenerating(true) setGenerateResult(null) try { const result = await generateAllCourses() setGenerateResult({ generated: result.generated, skipped: result.skipped, errors: result.errors || [] }) // Reload data to show new courses await loadData() } catch (error) { console.error('Failed to generate courses:', error) setGenerateResult({ generated: 0, skipped: 0, errors: [error instanceof Error ? error.message : 'Fehler bei der Generierung'] }) } finally { setIsGenerating(false) } } const clearFilters = () => { setSelectedCategory('all') setSelectedStatus('all') } return (
{/* Step Header */}
Kurs erstellen
{/* Generation Result */} {generateResult && (
0 ? 'bg-yellow-50 border-yellow-200' : 'bg-green-50 border-green-200'}`}>
{generateResult.generated} Kurse generiert {generateResult.skipped} uebersprungen {generateResult.errors.length > 0 && ( {generateResult.errors.length} Fehler )}
{generateResult.errors.length > 0 && (
{generateResult.errors.map((err, i) =>
{err}
)}
)}
)} {/* Tab Navigation */} {/* Loading State */} {isLoading ? (
) : activeTab === 'settings' ? ( /* Settings Tab */

Einstellungen

Academy-Einstellungen, E-Mail-Benachrichtigungen und Kurs-Vorlagen werden in einer spaeteren Version verfuegbar sein.

) : activeTab === 'certificates' ? ( /* Certificates Tab Placeholder */

Zertifikate

Zertifikate werden automatisch nach erfolgreichem Kursabschluss generiert. Die Zertifikatsverwaltung wird in einer spaeteren Version verfuegbar sein.

{tabCounts.certificates > 0 && (

{tabCounts.certificates} Zertifikat(e) vorhanden

)}
) : ( <> {/* Statistics (Overview Tab) */} {activeTab === 'overview' && statistics && (
0 ? 'red' : 'green'} />
)} {/* Overdue Alert */} {tabCounts.overdue > 0 && (

Achtung: {tabCounts.overdue} ueberfaellige Schulung(en)

Mitarbeiter haben Pflichtschulungen nicht fristgerecht abgeschlossen. Handeln Sie umgehend.

)} {/* Info Box (Overview Tab) */} {activeTab === 'overview' && (

Schulungspflicht nach Art. 39 DSGVO

Gemaess Art. 39 Abs. 1 lit. b DSGVO gehoert die Sensibilisierung und Schulung der an den Verarbeitungsvorgaengen beteiligten Mitarbeiter zu den Aufgaben des Datenschutzbeauftragten. Nachweisbare Compliance-Schulungen sind Pflicht und sollten mindestens jaehrlich aufgefrischt werden.

)} {/* Filters */} {/* Courses Tab */} {(activeTab === 'overview' || activeTab === 'courses') && (
{activeTab === 'courses' && (

Kurse ({filteredCourses.length})

)} {filteredCourses.map(course => ( ))}
)} {/* Enrollments Tab */} {activeTab === 'enrollments' && (

Einschreibungen ({filteredEnrollments.length})

{filteredEnrollments.map(enrollment => ( ))}
)} {/* Empty States */} {activeTab === 'courses' && filteredCourses.length === 0 && (

Keine Kurse gefunden

{selectedCategory !== 'all' ? 'Passen Sie die Filter an oder' : 'Es sind noch keine Kurse vorhanden.' }

{selectedCategory !== 'all' ? ( ) : ( Ersten Kurs erstellen )}
)} {activeTab === 'enrollments' && filteredEnrollments.length === 0 && (

Keine Einschreibungen gefunden

{selectedStatus !== 'all' ? 'Passen Sie die Filter an.' : 'Es sind noch keine Mitarbeiter in Kurse eingeschrieben.' }

{selectedStatus !== 'all' && ( )}
)} )}
) }