'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' && (
)}
)
}