'use client' import { useState, useEffect, useCallback } from 'react' import { useRouter, useParams } from 'next/navigation' import { useTheme } from '@/lib/ThemeContext' import { Sidebar } from '@/components/Sidebar' import { ThemeToggle } from '@/components/ThemeToggle' import { LanguageDropdown } from '@/components/LanguageDropdown' import { DocumentViewer, AnnotationLayer, AnnotationToolbar, AnnotationLegend, CriteriaPanel, GutachtenEditor, EHSuggestionPanel, } from '@/components/korrektur' import { korrekturApi } from '@/lib/korrektur/api' import type { Klausur, StudentWork, Annotation, AnnotationType, AnnotationPosition, CriteriaScores, EHSuggestion, } from '../../types' // ============================================================================= // GLASS CARD // ============================================================================= interface GlassCardProps { children: React.ReactNode className?: string } function GlassCard({ children, className = '' }: GlassCardProps) { return (
{children}
) } // ============================================================================= // MAIN PAGE // ============================================================================= export default function StudentWorkspacePage() { const { isDark } = useTheme() const router = useRouter() const params = useParams() const klausurId = params.klausurId as string const studentId = params.studentId as string // State const [klausur, setKlausur] = useState(null) const [student, setStudent] = useState(null) const [students, setStudents] = useState([]) const [annotations, setAnnotations] = useState([]) const [isLoading, setIsLoading] = useState(true) const [error, setError] = useState(null) // Editor state const [currentPage, setCurrentPage] = useState(1) const [totalPages, setTotalPages] = useState(1) const [selectedTool, setSelectedTool] = useState(null) const [selectedAnnotation, setSelectedAnnotation] = useState(null) const [activeTab, setActiveTab] = useState<'kriterien' | 'gutachten' | 'eh'>('kriterien') // Criteria and Gutachten state const [criteriaScores, setCriteriaScores] = useState({}) const [gutachten, setGutachten] = useState('') const [isGeneratingGutachten, setIsGeneratingGutachten] = useState(false) // EH Suggestions state const [ehSuggestions, setEhSuggestions] = useState([]) const [isLoadingEH, setIsLoadingEH] = useState(false) // Saving state const [isSaving, setIsSaving] = useState(false) const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false) // Load data const loadData = useCallback(async () => { if (!klausurId || !studentId) return setIsLoading(true) setError(null) try { const [klausurData, studentData, studentsData, annotationsData] = await Promise.all([ korrekturApi.getKlausur(klausurId), korrekturApi.getStudent(studentId), korrekturApi.getStudents(klausurId), korrekturApi.getAnnotations(studentId), ]) setKlausur(klausurData) setStudent(studentData) setStudents(studentsData) setAnnotations(annotationsData) // Initialize editor state from student data setCriteriaScores(studentData.criteria_scores || {}) setGutachten(studentData.gutachten || '') // Estimate total pages (for images, usually 1; for PDFs, would need backend info) setTotalPages(studentData.file_type === 'pdf' ? 5 : 1) } catch (err) { console.error('Failed to load data:', err) setError(err instanceof Error ? err.message : 'Laden fehlgeschlagen') } finally { setIsLoading(false) } }, [klausurId, studentId]) useEffect(() => { loadData() }, [loadData]) // Get current student index const currentIndex = students.findIndex((s) => s.id === studentId) const prevStudent = currentIndex > 0 ? students[currentIndex - 1] : null const nextStudent = currentIndex < students.length - 1 ? students[currentIndex + 1] : null // Navigation const goToStudent = (id: string) => { if (hasUnsavedChanges) { if (!confirm('Sie haben ungespeicherte Aenderungen. Trotzdem wechseln?')) { return } } router.push(`/korrektur/${klausurId}/${id}`) } // Handle criteria change const handleCriteriaChange = (criterion: string, value: number) => { setCriteriaScores((prev) => ({ ...prev, [criterion]: value, })) setHasUnsavedChanges(true) } // Handle gutachten change const handleGutachtenChange = (value: string) => { setGutachten(value) setHasUnsavedChanges(true) } // Generate gutachten const handleGenerateGutachten = async () => { setIsGeneratingGutachten(true) try { const result = await korrekturApi.generateGutachten(studentId) setGutachten(result.gutachten) setHasUnsavedChanges(true) } catch (err) { console.error('Failed to generate gutachten:', err) setError('Gutachten-Generierung fehlgeschlagen') } finally { setIsGeneratingGutachten(false) } } // Load EH suggestions const handleLoadEHSuggestions = async (criterion?: string) => { setIsLoadingEH(true) try { const suggestions = await korrekturApi.getEHSuggestions(studentId, criterion) setEhSuggestions(suggestions) } catch (err) { console.error('Failed to load EH suggestions:', err) setError('EH-Vorschlaege konnten nicht geladen werden') } finally { setIsLoadingEH(false) } } // Create annotation const handleAnnotationCreate = async (position: AnnotationPosition, type: AnnotationType) => { try { const newAnnotation = await korrekturApi.createAnnotation(studentId, { page: currentPage, position, type, text: '', severity: 'minor', }) setAnnotations((prev) => [...prev, newAnnotation]) setSelectedAnnotation(newAnnotation.id) setSelectedTool(null) } catch (err) { console.error('Failed to create annotation:', err) } } // Delete annotation const handleAnnotationDelete = async (id: string) => { try { await korrekturApi.deleteAnnotation(id) setAnnotations((prev) => prev.filter((a) => a.id !== id)) setSelectedAnnotation(null) } catch (err) { console.error('Failed to delete annotation:', err) } } // Save all changes const handleSave = async () => { setIsSaving(true) try { await Promise.all([ korrekturApi.updateCriteria(studentId, criteriaScores), korrekturApi.updateGutachten(studentId, gutachten), ]) setHasUnsavedChanges(false) } catch (err) { console.error('Failed to save:', err) setError('Speichern fehlgeschlagen') } finally { setIsSaving(false) } } // Insert EH suggestion into gutachten const handleInsertSuggestion = (text: string) => { setGutachten((prev) => prev + '\n\n' + text) setHasUnsavedChanges(true) setActiveTab('gutachten') } // Keyboard shortcuts useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { // Don't trigger shortcuts when typing in inputs if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) { return } if (e.key === 'Escape') { setSelectedTool(null) setSelectedAnnotation(null) } else if (e.key === 'r' || e.key === 'R') { setSelectedTool('rechtschreibung') } else if (e.key === 'g' || e.key === 'G') { setSelectedTool('grammatik') } else if (e.key === 'i' || e.key === 'I') { setSelectedTool('inhalt') } else if (e.key === 's' && e.metaKey) { e.preventDefault() handleSave() } } window.addEventListener('keydown', handleKeyDown) return () => window.removeEventListener('keydown', handleKeyDown) }, [criteriaScores, gutachten]) if (isLoading) { return (
) } return (
{/* Animated Background Blobs */}
{/* Sidebar */}
{/* Main Content */}
{/* Header */}

{student?.anonym_id || 'Student'}

{klausur?.title}

{/* Navigation */}
{currentIndex + 1} / {students.length}
{hasUnsavedChanges && ( Ungespeicherte Aenderungen )}
{/* Error Display */} {error && (
{error}
)} {/* Main Workspace - 2/3 - 1/3 Layout */}
{/* Left: Document Viewer (2/3) */}
a.page === currentPage)} selectedAnnotation={selectedAnnotation} currentTool={selectedTool} onAnnotationCreate={handleAnnotationCreate} onAnnotationSelect={setSelectedAnnotation} onAnnotationDelete={handleAnnotationDelete} /> {/* Annotation Toolbar */}
{/* Right: Criteria/Gutachten Panel (1/3) */}
{/* Tabs */}
{/* Tab Content */}
{activeTab === 'kriterien' && ( { handleLoadEHSuggestions(criterion) setActiveTab('eh') }} /> )} {activeTab === 'gutachten' && ( )} {activeTab === 'eh' && ( )}
) }