'use client' import React, { useState, useEffect, useMemo } from 'react' import { useParams, useRouter } from 'next/navigation' import Link from 'next/link' import { Course, Lesson, Enrollment, QuizQuestion, COURSE_CATEGORY_INFO, ENROLLMENT_STATUS_INFO, isEnrollmentOverdue, getDaysUntilDeadline } from '@/lib/sdk/academy/types' import { fetchCourse, fetchEnrollments, deleteCourse, submitQuiz, generateVideos, getVideoStatus } from '@/lib/sdk/academy/api' type TabId = 'overview' | 'lessons' | 'enrollments' | 'videos' export default function CourseDetailPage() { const params = useParams() const router = useRouter() const courseId = params.id as string const [course, setCourse] = useState(null) const [enrollments, setEnrollments] = useState([]) const [activeTab, setActiveTab] = useState('overview') const [isLoading, setIsLoading] = useState(true) const [selectedLesson, setSelectedLesson] = useState(null) const [quizAnswers, setQuizAnswers] = useState>({}) const [quizResult, setQuizResult] = useState(null) const [isSubmittingQuiz, setIsSubmittingQuiz] = useState(false) const [videoStatus, setVideoStatus] = useState(null) const [isGeneratingVideos, setIsGeneratingVideos] = useState(false) useEffect(() => { const loadData = async () => { setIsLoading(true) try { const [courseData, enrollmentData] = await Promise.all([ fetchCourse(courseId).catch(() => null), fetchEnrollments(courseId).catch(() => []) ]) setCourse(courseData) setEnrollments(Array.isArray(enrollmentData) ? enrollmentData : []) if (courseData && courseData.lessons && courseData.lessons.length > 0) { setSelectedLesson(courseData.lessons[0]) } } catch (error) { console.error('Failed to load course:', error) } finally { setIsLoading(false) } } loadData() }, [courseId]) const handleDeleteCourse = async () => { if (!confirm('Sind Sie sicher, dass Sie diesen Kurs loeschen moechten? Diese Aktion kann nicht rueckgaengig gemacht werden.')) return try { await deleteCourse(courseId) router.push('/sdk/academy') } catch (error) { console.error('Failed to delete course:', error) } } const handleSubmitQuiz = async () => { if (!selectedLesson) return const questions = selectedLesson.quizQuestions || [] const answers = questions.map((q: QuizQuestion) => quizAnswers[q.id] ?? -1) setIsSubmittingQuiz(true) try { const result = await submitQuiz(selectedLesson.id, { answers }) setQuizResult(result) } catch (error: any) { console.error('Quiz submission failed:', error) setQuizResult({ error: error.message || 'Fehler bei der Auswertung' }) } finally { setIsSubmittingQuiz(false) } } const handleGenerateVideos = async () => { setIsGeneratingVideos(true) try { const status = await generateVideos(courseId) setVideoStatus(status) } catch (error) { console.error('Video generation failed:', error) } finally { setIsGeneratingVideos(false) } } const handleCheckVideoStatus = async () => { try { const status = await getVideoStatus(courseId) setVideoStatus(status) } catch (error) { console.error('Failed to check video status:', error) } } if (isLoading) { return (
) } if (!course) { return (

Kurs nicht gefunden

Zurueck zur Uebersicht
) } const categoryInfo = COURSE_CATEGORY_INFO[course.category] || COURSE_CATEGORY_INFO['custom'] const sortedLessons = [...(course.lessons || [])].sort((a, b) => a.order - b.order) const completedEnrollments = enrollments.filter(e => e.status === 'completed').length const overdueEnrollments = enrollments.filter(e => isEnrollmentOverdue(e)).length return (
{/* Header */}
{categoryInfo.label} {course.status === 'published' ? 'Veroeffentlicht' : 'Entwurf'}

{course.title}

{course.description}

{/* Stats Row */}
Lektionen
{sortedLessons.length}
Dauer
{course.durationMinutes} Min.
Teilnehmer
{enrollments.length}
Abgeschlossen
{completedEnrollments}
{/* Tabs */}
{/* Overview Tab */} {activeTab === 'overview' && (

Kurs-Details

Bestehensgrenze
{course.passingScore}%
Pflicht fuer
{course.requiredForRoles?.join(', ') || 'Alle'}
Erstellt am
{new Date(course.createdAt).toLocaleDateString('de-DE')}
Aktualisiert am
{new Date(course.updatedAt).toLocaleDateString('de-DE')}
{/* Lesson List Preview */}

Lektionen ({sortedLessons.length})

{sortedLessons.map((lesson, i) => (
{i + 1}
{lesson.title}
{lesson.durationMinutes} Min. | {lesson.type === 'video' ? 'Video' : lesson.type === 'quiz' ? 'Quiz' : 'Text'}
{lesson.type === 'quiz' ? 'Quiz' : lesson.type === 'video' ? 'Video' : 'Text'}
))}
)} {/* Lessons Tab - with content viewer and quiz player */} {activeTab === 'lessons' && (
{/* Lesson Navigation */}

Lektionen

{sortedLessons.map((lesson, i) => ( ))}
{/* Lesson Content */}
{selectedLesson ? (

{selectedLesson.title}

{selectedLesson.type === 'quiz' ? 'Quiz' : selectedLesson.type === 'video' ? 'Video' : 'Text'}
{/* Video Player */} {selectedLesson.type === 'video' && selectedLesson.videoUrl && (
)} {/* Text Content */} {(selectedLesson.type === 'text' || selectedLesson.type === 'video') && selectedLesson.contentMarkdown && (
{selectedLesson.contentMarkdown.split('\n').map((line, i) => { if (line.startsWith('# ')) return

{line.slice(2)}

if (line.startsWith('## ')) return

{line.slice(3)}

if (line.startsWith('### ')) return

{line.slice(4)}

if (line.startsWith('- **')) { const parts = line.slice(2).split('**') return
  • {parts[1]}{parts[2] || ''}
  • } if (line.startsWith('- ')) return
  • {line.slice(2)}
  • if (line.trim() === '') return
    return

    {line}

    })}
    )} {/* Quiz Player */} {selectedLesson.type === 'quiz' && selectedLesson.quizQuestions && (
    {selectedLesson.quizQuestions.map((q: QuizQuestion, qi: number) => (

    Frage {qi + 1}: {q.question}

    {q.options.map((option: string, oi: number) => { const isSelected = quizAnswers[q.id] === oi const showResult = quizResult && !quizResult.error const isCorrect = showResult && quizResult.results?.[qi]?.correct const wasSelected = showResult && isSelected let bgClass = 'bg-white border-gray-200 hover:border-purple-300' if (isSelected && !showResult) bgClass = 'bg-purple-50 border-purple-500' if (showResult && oi === q.correctOptionIndex) bgClass = 'bg-green-50 border-green-500' if (showResult && wasSelected && !isCorrect) bgClass = 'bg-red-50 border-red-500' return ( ) })}
    {quizResult && !quizResult.error && quizResult.results?.[qi] && (
    {quizResult.results[qi].correct ? 'Richtig!' : 'Falsch.'} {q.explanation}
    )}
    ))} {/* Quiz Submit / Result */} {!quizResult ? ( ) : quizResult.error ? (
    {quizResult.error}
    ) : (
    {quizResult.score}%
    {quizResult.passed ? 'Bestanden!' : 'Nicht bestanden'} — {quizResult.correctAnswers}/{quizResult.totalQuestions} richtig
    )}
    )}
    ) : (

    Waehlen Sie eine Lektion aus.

    )}
    )} {/* Enrollments Tab */} {activeTab === 'enrollments' && (
    {overdueEnrollments > 0 && (
    {overdueEnrollments} ueberfaellige Einschreibung(en)
    )} {enrollments.length === 0 ? (

    Noch keine Einschreibungen fuer diesen Kurs.

    ) : ( enrollments.map(enrollment => { const statusInfo = ENROLLMENT_STATUS_INFO[enrollment.status] const overdue = isEnrollmentOverdue(enrollment) const daysUntil = getDaysUntilDeadline(enrollment.deadline) return (
    {statusInfo?.label} {overdue && Ueberfaellig}
    {enrollment.userName}
    {enrollment.userEmail}
    {enrollment.progress}%
    {enrollment.status === 'completed' ? 'Abgeschlossen' : `${daysUntil > 0 ? daysUntil + ' Tage verbleibend' : Math.abs(daysUntil) + ' Tage ueberfaellig'}`}
    ) }) )}
    )} {/* Videos Tab */} {activeTab === 'videos' && (

    Video-Generierung

    Videos werden mit ElevenLabs (Stimme) und HeyGen (Avatar) generiert. Konfigurieren Sie die API-Keys in den Umgebungsvariablen.
    {videoStatus && (
    Gesamtstatus: {videoStatus.status}
    {videoStatus.lessons?.map((ls: any) => (
    Lektion {ls.lessonId.slice(-4)} {ls.status}
    ))}
    )} {!videoStatus && (

    Klicken Sie auf "Videos generieren" um den Prozess zu starten.

    )}
    )}
    ) }