From a317bd616437d903a59a83c78531e4f02ac1f162 Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Sat, 25 Apr 2026 15:52:19 +0200 Subject: [PATCH] [interface-change] Phase 4: Extract shared types + fix Docker context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shared types extracted to shared/types/: - companion.ts (33+ types, was 100% duplicated admin-lehrer ↔ studio-v2) - klausur.ts (18+ types, was 95% duplicated across 4 locations) - ocr-labeling.ts (11 types, was 100% duplicated admin-lehrer ↔ website) Original type files replaced with re-exports for backward compat. tsconfig.json paths updated with @shared/* alias in all 3 services. Docker: Changed build context from ./service to . (root) so shared/ is accessible. Dockerfiles updated to COPY service/ + shared/. Co-Authored-By: Claude Opus 4.6 (1M context) --- admin-lehrer/Dockerfile | 7 +- .../app/(admin)/ai/ocr-labeling/types.ts | 124 +---- .../_components/workspace-types.ts | 101 +--- .../klausur-korrektur/_components/types.ts | 39 +- .../education/klausur-korrektur/types.ts | 196 +------- admin-lehrer/lib/companion/types.ts | 330 +------------ admin-lehrer/tsconfig.json | 3 + docker-compose.yml | 12 +- shared/types/companion.ts | 329 +++++++++++++ shared/types/index.ts | 3 + shared/types/klausur.ts | 432 ++++++++++++++++++ shared/types/ocr-labeling.ts | 127 +++++ studio-v2/Dockerfile | 7 +- studio-v2/app/korrektur/types.ts | 258 +---------- studio-v2/lib/companion/types.ts | 330 +------------ studio-v2/tsconfig.json | 3 + website/Dockerfile | 7 +- website/app/admin/klausur-korrektur/types.ts | 196 +------- website/app/admin/ocr-labeling/types.ts | 124 +---- website/app/lehrer/klausur-korrektur/types.ts | 196 +------- .../klausur-korrektur/list-types.ts | 44 +- .../klausur-korrektur/workspace-types.ts | 88 +--- website/tsconfig.json | 3 + 23 files changed, 976 insertions(+), 1983 deletions(-) create mode 100644 shared/types/companion.ts create mode 100644 shared/types/index.ts create mode 100644 shared/types/klausur.ts create mode 100644 shared/types/ocr-labeling.ts diff --git a/admin-lehrer/Dockerfile b/admin-lehrer/Dockerfile index 7fda5b0..dd0590b 100644 --- a/admin-lehrer/Dockerfile +++ b/admin-lehrer/Dockerfile @@ -4,13 +4,14 @@ FROM node:20-alpine AS builder WORKDIR /app # Copy package files -COPY package.json package-lock.json* ./ +COPY admin-lehrer/package.json admin-lehrer/package-lock.json* ./ # Install dependencies RUN npm install -# Copy source code -COPY . . +# Copy source code + shared types +COPY admin-lehrer/ . +COPY shared/ /shared/ # Build arguments for environment variables ARG NEXT_PUBLIC_API_URL diff --git a/admin-lehrer/app/(admin)/ai/ocr-labeling/types.ts b/admin-lehrer/app/(admin)/ai/ocr-labeling/types.ts index f73a28a..a09849b 100644 --- a/admin-lehrer/app/(admin)/ai/ocr-labeling/types.ts +++ b/admin-lehrer/app/(admin)/ai/ocr-labeling/types.ts @@ -1,123 +1 @@ -/** - * TypeScript types for OCR Labeling UI - */ - -/** - * Available OCR Models - * - * - llama3.2-vision:11b: Vision LLM, beste Qualitaet bei Handschrift (Standard) - * - trocr: Microsoft TrOCR, schnell bei gedrucktem Text - * - paddleocr: PaddleOCR + LLM, 4x schneller durch Hybrid-Ansatz - * - donut: Document Understanding Transformer, strukturierte Dokumente - */ -export type OCRModel = 'llama3.2-vision:11b' | 'trocr' | 'paddleocr' | 'donut' - -export const OCR_MODEL_INFO: Record = { - 'llama3.2-vision:11b': { - label: 'Vision LLM', - description: 'Beste Qualitaet bei Handschrift', - speed: 'langsam', - }, - trocr: { - label: 'Microsoft TrOCR', - description: 'Schnell bei gedrucktem Text', - speed: 'schnell', - }, - paddleocr: { - label: 'PaddleOCR + LLM', - description: 'Hybrid-Ansatz: OCR + Strukturierung', - speed: 'sehr schnell', - }, - donut: { - label: 'Donut', - description: 'Document Understanding fuer Tabellen/Formulare', - speed: 'mittel', - }, -} - -export interface OCRSession { - id: string - name: string - source_type: 'klausur' | 'handwriting_sample' | 'scan' - description?: string - ocr_model?: OCRModel - total_items: number - labeled_items: number - confirmed_items: number - corrected_items: number - skipped_items: number - created_at: string -} - -export interface OCRItem { - id: string - session_id: string - session_name: string - image_path: string - image_url?: string - ocr_text?: string - ocr_confidence?: number - ground_truth?: string - status: 'pending' | 'confirmed' | 'corrected' | 'skipped' - metadata?: Record - created_at: string -} - -export interface OCRStats { - total_sessions?: number - session_id?: string - name?: string - total_items: number - labeled_items: number - confirmed_items: number - corrected_items: number - skipped_items?: number - pending_items: number - exportable_items?: number - accuracy_rate: number - avg_label_time_seconds?: number - progress_percent?: number -} - -export interface TrainingSample { - id: string - image_path: string - ground_truth: string - export_format: 'generic' | 'trocr' | 'llama_vision' - training_batch: string - exported_at?: string -} - -export interface CreateSessionRequest { - name: string - source_type: 'klausur' | 'handwriting_sample' | 'scan' - description?: string - ocr_model?: OCRModel -} - -export interface ConfirmRequest { - item_id: string - label_time_seconds?: number -} - -export interface CorrectRequest { - item_id: string - ground_truth: string - label_time_seconds?: number -} - -export interface ExportRequest { - export_format: 'generic' | 'trocr' | 'llama_vision' - session_id?: string - batch_id?: string -} - -export interface UploadResult { - id: string - filename: string - image_path: string - image_hash: string - ocr_text?: string - ocr_confidence?: number - status: string -} +export * from '../../../../../shared/types/ocr-labeling' diff --git a/admin-lehrer/app/(admin)/education/klausur-korrektur/[klausurId]/[studentId]/_components/workspace-types.ts b/admin-lehrer/app/(admin)/education/klausur-korrektur/[klausurId]/[studentId]/_components/workspace-types.ts index b8e7485..3ec58e5 100644 --- a/admin-lehrer/app/(admin)/education/klausur-korrektur/[klausurId]/[studentId]/_components/workspace-types.ts +++ b/admin-lehrer/app/(admin)/education/klausur-korrektur/[klausurId]/[studentId]/_components/workspace-types.ts @@ -1,93 +1,24 @@ /** * Types and constants for the Korrektur-Workspace page. + * + * Domain types are re-exported from the shared module. + * Only the API_BASE constant remains local (uses Next.js rewrite proxy). */ -import type { CriteriaScores } from '../../../types' +export type { + ExaminerInfo, + ExaminerResult, + ExaminerWorkflow, + ActiveTab, + GradeTotals, + CriteriaScores, +} from '../../../../../../../../shared/types/klausur' -// ---- Examiner workflow types ---- - -export interface ExaminerInfo { - id: string - assigned_at: string - notes?: string -} - -export interface ExaminerResult { - grade_points: number - criteria_scores?: CriteriaScores - notes?: string - submitted_at: string -} - -export interface ExaminerWorkflow { - student_id: string - workflow_status: string - visibility_mode: string - user_role: 'ek' | 'zk' | 'dk' | 'viewer' - first_examiner?: ExaminerInfo - second_examiner?: ExaminerInfo - third_examiner?: ExaminerInfo - first_result?: ExaminerResult - first_result_visible?: boolean - second_result?: ExaminerResult - third_result?: ExaminerResult - grade_difference?: number - final_grade?: number - consensus_reached?: boolean - consensus_type?: string - einigung?: { - final_grade: number - notes: string - type: string - submitted_by: string - submitted_at: string - ek_grade: number - zk_grade: number - } - drittkorrektur_reason?: string -} - -// ---- Active tab ---- - -export type ActiveTab = 'kriterien' | 'gutachten' | 'annotationen' | 'eh-vorschlaege' - -// ---- Totals from grade calculation ---- - -export interface GradeTotals { - raw: number - weighted: number - gradePoints: number -} - -// ---- Constants ---- +export { + WORKFLOW_STATUS_LABELS, + ROLE_LABELS, + GRADE_LABELS, +} from '../../../../../../../../shared/types/klausur' /** Same-origin proxy to avoid CORS issues */ export const API_BASE = '/klausur-api' - -export const GRADE_LABELS: Record = { - 15: '1+', 14: '1', 13: '1-', 12: '2+', 11: '2', 10: '2-', - 9: '3+', 8: '3', 7: '3-', 6: '4+', 5: '4', 4: '4-', - 3: '5+', 2: '5', 1: '5-', 0: '6', -} - -export const WORKFLOW_STATUS_LABELS: Record = { - not_started: { label: 'Nicht gestartet', color: 'bg-slate-100 text-slate-700' }, - ek_in_progress: { label: 'EK in Arbeit', color: 'bg-blue-100 text-blue-700' }, - ek_completed: { label: 'EK abgeschlossen', color: 'bg-blue-200 text-blue-800' }, - zk_assigned: { label: 'ZK zugewiesen', color: 'bg-amber-100 text-amber-700' }, - zk_in_progress: { label: 'ZK in Arbeit', color: 'bg-amber-200 text-amber-800' }, - zk_completed: { label: 'ZK abgeschlossen', color: 'bg-amber-300 text-amber-900' }, - einigung_required: { label: 'Einigung erforderlich', color: 'bg-orange-100 text-orange-700' }, - einigung_completed: { label: 'Einigung abgeschlossen', color: 'bg-green-100 text-green-700' }, - drittkorrektur_required: { label: 'DK erforderlich', color: 'bg-red-100 text-red-700' }, - drittkorrektur_assigned: { label: 'DK zugewiesen', color: 'bg-red-200 text-red-800' }, - drittkorrektur_in_progress: { label: 'DK in Arbeit', color: 'bg-red-300 text-red-900' }, - completed: { label: 'Abgeschlossen', color: 'bg-green-200 text-green-800' }, -} - -export const ROLE_LABELS: Record = { - ek: { label: 'Erstkorrektor', color: 'bg-blue-500' }, - zk: { label: 'Zweitkorrektor', color: 'bg-amber-500' }, - dk: { label: 'Drittkorrektor', color: 'bg-purple-500' }, - viewer: { label: 'Betrachter', color: 'bg-slate-500' }, -} diff --git a/admin-lehrer/app/(admin)/education/klausur-korrektur/_components/types.ts b/admin-lehrer/app/(admin)/education/klausur-korrektur/_components/types.ts index 1820fe7..edcd4c6 100644 --- a/admin-lehrer/app/(admin)/education/klausur-korrektur/_components/types.ts +++ b/admin-lehrer/app/(admin)/education/klausur-korrektur/_components/types.ts @@ -1,34 +1,11 @@ /** - * Local form types for Klausur-Korrektur page + * Local form types for Klausur-Korrektur page. + * Re-exported from the shared module. */ -export interface CreateKlausurForm { - title: string - subject: string - year: number - semester: string - modus: 'abitur' | 'vorabitur' -} - -export interface VorabiturEHForm { - aufgabentyp: string - titel: string - text_titel: string - text_autor: string - aufgabenstellung: string -} - -export interface EHTemplate { - aufgabentyp: string - name: string - description: string - category: string -} - -export interface DirektuploadForm { - files: File[] - ehFile: File | null - ehText: string - aufgabentyp: string - klausurTitle: string -} +export type { + CreateKlausurForm, + VorabiturEHForm, + EHTemplate, + DirektuploadForm, +} from '../../../../../../shared/types/klausur' diff --git a/admin-lehrer/app/(admin)/education/klausur-korrektur/types.ts b/admin-lehrer/app/(admin)/education/klausur-korrektur/types.ts index 4bb113c..2d9c2ce 100644 --- a/admin-lehrer/app/(admin)/education/klausur-korrektur/types.ts +++ b/admin-lehrer/app/(admin)/education/klausur-korrektur/types.ts @@ -1,195 +1 @@ -// TypeScript Interfaces für Klausur-Korrektur - -export interface Klausur { - id: string - title: string - subject: string - year: number - semester: string - modus: 'abitur' | 'vorabitur' - eh_id?: string - created_at: string - student_count?: number - completed_count?: number - status?: 'draft' | 'in_progress' | 'completed' -} - -export interface StudentWork { - id: string - klausur_id: string - anonym_id: string - file_path: string - file_type: 'pdf' | 'image' - ocr_text: string - criteria_scores: CriteriaScores - gutachten: string - status: StudentStatus - raw_points: number - grade_points: number - grade_label?: string - created_at: string - examiner_id?: string - second_examiner_id?: string - second_examiner_grade?: number -} - -export type StudentStatus = - | 'UPLOADED' - | 'OCR_PROCESSING' - | 'OCR_COMPLETE' - | 'ANALYZING' - | 'FIRST_EXAMINER' - | 'SECOND_EXAMINER' - | 'COMPLETED' - | 'ERROR' - -export interface CriteriaScores { - rechtschreibung?: number - grammatik?: number - inhalt?: number - struktur?: number - stil?: number - [key: string]: number | undefined -} - -export interface Criterion { - id: string - name: string - weight: number - description?: string -} - -export interface GradeInfo { - thresholds: Record - labels: Record - criteria: Record -} - -export interface Annotation { - id: string - student_work_id: string - page: number - position: AnnotationPosition - type: AnnotationType - text: string - severity: 'minor' | 'major' | 'critical' - suggestion?: string - created_by: string - created_at: string - role: 'first_examiner' | 'second_examiner' - linked_criterion?: string -} - -export interface AnnotationPosition { - x: number // Prozent (0-100) - y: number // Prozent (0-100) - width: number // Prozent (0-100) - height: number // Prozent (0-100) -} - -export type AnnotationType = - | 'rechtschreibung' - | 'grammatik' - | 'inhalt' - | 'struktur' - | 'stil' - | 'comment' - | 'highlight' - -export interface FairnessAnalysis { - klausur_id: string - student_count: number - average_grade: number - std_deviation: number - spread: number - outliers: OutlierInfo[] - criteria_analysis: Record - fairness_score: number - warnings: string[] -} - -export interface OutlierInfo { - student_id: string - anonym_id: string - grade_points: number - deviation: number - reason: string -} - -export interface CriteriaStats { - min: number - max: number - average: number - std_deviation: number -} - -export interface EHSuggestion { - criterion: string - excerpt: string - relevance_score: number - source_chunk_id: string -} - -export interface GutachtenSection { - title: string - content: string - evidence_links?: string[] -} - -export interface Gutachten { - einleitung: string - hauptteil: string - fazit: string - staerken: string[] - schwaechen: string[] - generated_at?: string -} - -// API Response Types -export interface KlausurenResponse { - klausuren: Klausur[] - total: number -} - -export interface StudentsResponse { - students: StudentWork[] - total: number -} - -export interface AnnotationsResponse { - annotations: Annotation[] -} - -// Color mapping for annotation types -export const ANNOTATION_COLORS: Record = { - rechtschreibung: '#dc2626', // Red - grammatik: '#2563eb', // Blue - inhalt: '#16a34a', // Green - struktur: '#9333ea', // Purple - stil: '#ea580c', // Orange - comment: '#6b7280', // Gray - highlight: '#eab308', // Yellow -} - -// Status colors -export const STATUS_COLORS: Record = { - UPLOADED: '#6b7280', - OCR_PROCESSING: '#eab308', - OCR_COMPLETE: '#3b82f6', - ANALYZING: '#8b5cf6', - FIRST_EXAMINER: '#f97316', - SECOND_EXAMINER: '#06b6d4', - COMPLETED: '#22c55e', - ERROR: '#ef4444', -} - -export const STATUS_LABELS: Record = { - UPLOADED: 'Hochgeladen', - OCR_PROCESSING: 'OCR laeuft', - OCR_COMPLETE: 'OCR fertig', - ANALYZING: 'Analyse laeuft', - FIRST_EXAMINER: 'Erstkorrektur', - SECOND_EXAMINER: 'Zweitkorrektur', - COMPLETED: 'Abgeschlossen', - ERROR: 'Fehler', -} +export * from '../../../../../shared/types/klausur' diff --git a/admin-lehrer/lib/companion/types.ts b/admin-lehrer/lib/companion/types.ts index 5a4405b..fe2de9c 100644 --- a/admin-lehrer/lib/companion/types.ts +++ b/admin-lehrer/lib/companion/types.ts @@ -1,329 +1 @@ -/** - * TypeScript Types for Companion Module - * Migration from Flask companion.py/companion_js.py - */ - -// ============================================================================ -// Phase System -// ============================================================================ - -export type PhaseId = 'einstieg' | 'erarbeitung' | 'sicherung' | 'transfer' | 'reflexion' - -export interface Phase { - id: PhaseId - shortName: string // E, A, S, T, R - displayName: string - duration: number // minutes - status: 'planned' | 'active' | 'completed' - actualTime?: number // seconds (actual time spent) - color: string // hex color -} - -export interface PhaseContext { - currentPhase: PhaseId - phaseDisplayName: string -} - -// ============================================================================ -// Dashboard / Companion Mode -// ============================================================================ - -export interface CompanionStats { - classesCount: number - studentsCount: number - learningUnitsCreated: number - gradesEntered: number -} - -export interface Progress { - percentage: number - completed: number - total: number -} - -export type SuggestionPriority = 'urgent' | 'high' | 'medium' | 'low' - -export interface Suggestion { - id: string - title: string - description: string - priority: SuggestionPriority - icon: string // lucide icon name - actionTarget: string // navigation path - estimatedTime: number // minutes -} - -export type EventType = 'exam' | 'parent_meeting' | 'deadline' | 'other' - -export interface UpcomingEvent { - id: string - title: string - date: string // ISO date string - type: EventType - inDays: number -} - -export interface CompanionData { - context: PhaseContext - stats: CompanionStats - phases: Phase[] - progress: Progress - suggestions: Suggestion[] - upcomingEvents: UpcomingEvent[] -} - -// ============================================================================ -// Lesson Mode -// ============================================================================ - -export type LessonStatus = - | 'not_started' - | 'in_progress' - | 'paused' - | 'completed' - | 'overtime' - -export interface LessonPhase { - phase: PhaseId - duration: number // planned duration in minutes - status: 'planned' | 'active' | 'completed' | 'skipped' - actualTime: number // actual time spent in seconds - startedAt?: string // ISO timestamp - completedAt?: string // ISO timestamp -} - -export interface Homework { - id: string - title: string - description?: string - dueDate: string // ISO date - attachments?: string[] - completed?: boolean -} - -export interface Material { - id: string - title: string - type: 'document' | 'video' | 'presentation' | 'link' | 'other' - url?: string - fileName?: string -} - -export interface LessonReflection { - rating: number // 1-5 stars - notes: string - nextSteps: string - savedAt?: string -} - -export interface LessonSession { - sessionId: string - classId: string - className: string - subject: string - topic?: string - startTime: string // ISO timestamp - endTime?: string // ISO timestamp - phases: LessonPhase[] - totalPlannedDuration: number // minutes - currentPhaseIndex: number - elapsedTime: number // seconds - isPaused: boolean - pausedAt?: string - pauseDuration: number // total pause time in seconds - overtimeMinutes: number - status: LessonStatus - homeworkList: Homework[] - materials: Material[] - reflection?: LessonReflection -} - -// ============================================================================ -// Lesson Templates -// ============================================================================ - -export interface PhaseDurations { - einstieg: number - erarbeitung: number - sicherung: number - transfer: number - reflexion: number -} - -export interface LessonTemplate { - templateId: string - name: string - description?: string - subject?: string - durations: PhaseDurations - isSystemTemplate: boolean - createdBy?: string - createdAt?: string -} - -// ============================================================================ -// Settings -// ============================================================================ - -export interface TeacherSettings { - defaultPhaseDurations: PhaseDurations - preferredLessonLength: number // minutes (default 45) - autoAdvancePhases: boolean - soundNotifications: boolean - showKeyboardShortcuts: boolean - highContrastMode: boolean - onboardingCompleted: boolean - selectedTemplateId?: string -} - -// ============================================================================ -// Timer State -// ============================================================================ - -export type TimerColorStatus = 'plenty' | 'warning' | 'critical' | 'overtime' - -export interface TimerState { - isRunning: boolean - isPaused: boolean - elapsedSeconds: number - remainingSeconds: number - totalSeconds: number - progress: number // 0-1 - colorStatus: TimerColorStatus - currentPhase: LessonPhase | null -} - -// ============================================================================ -// Forms -// ============================================================================ - -export interface LessonStartFormData { - classId: string - subject: string - topic?: string - templateId?: string - customDurations?: PhaseDurations -} - -export interface Class { - id: string - name: string - grade: string - studentCount: number -} - -// ============================================================================ -// Feedback -// ============================================================================ - -export type FeedbackType = 'bug' | 'feature' | 'feedback' - -export interface FeedbackSubmission { - type: FeedbackType - title: string - description: string - screenshot?: string // base64 - sessionId?: string - metadata?: Record -} - -// ============================================================================ -// Onboarding -// ============================================================================ - -export interface OnboardingStep { - step: number - title: string - description: string - completed: boolean -} - -export interface OnboardingState { - currentStep: number - totalSteps: number - steps: OnboardingStep[] - selectedState?: string // Bundesland - selectedSchoolType?: string - completed: boolean -} - -// ============================================================================ -// WebSocket Messages -// ============================================================================ - -export type WSMessageType = - | 'phase_update' - | 'timer_tick' - | 'overtime_warning' - | 'pause_toggle' - | 'session_end' - | 'sync_request' - -export interface WSMessage { - type: WSMessageType - payload: { - sessionId: string - phase?: number - elapsed?: number - isPaused?: boolean - overtimeMinutes?: number - [key: string]: unknown - } - timestamp: string -} - -// ============================================================================ -// API Responses -// ============================================================================ - -export interface APIResponse { - success: boolean - data?: T - error?: string - message?: string -} - -export interface DashboardResponse extends APIResponse {} - -export interface LessonResponse extends APIResponse {} - -export interface TemplatesResponse extends APIResponse<{ templates: LessonTemplate[] }> {} - -export interface SettingsResponse extends APIResponse {} - -// ============================================================================ -// Component Props -// ============================================================================ - -export type CompanionMode = 'companion' | 'lesson' | 'classic' - -export interface ModeToggleProps { - currentMode: CompanionMode - onModeChange: (mode: CompanionMode) => void -} - -export interface PhaseTimelineProps { - phases: Phase[] - currentPhaseIndex: number - onPhaseClick?: (index: number) => void -} - -export interface VisualPieTimerProps { - progress: number // 0-1 - remainingSeconds: number - totalSeconds: number - colorStatus: TimerColorStatus - isPaused: boolean - currentPhaseName: string - phaseColor: string -} - -export interface QuickActionsBarProps { - onExtend: (minutes: number) => void - onPause: () => void - onResume: () => void - onSkip: () => void - isPaused: boolean - isLastPhase: boolean - disabled?: boolean -} +export * from '../../../shared/types/companion' diff --git a/admin-lehrer/tsconfig.json b/admin-lehrer/tsconfig.json index b66a30f..2b1ea82 100644 --- a/admin-lehrer/tsconfig.json +++ b/admin-lehrer/tsconfig.json @@ -24,6 +24,9 @@ "paths": { "@/*": [ "./*" + ], + "@shared/*": [ + "../shared/*" ] }, "target": "ES2017" diff --git a/docker-compose.yml b/docker-compose.yml index 6dac132..25ce8c3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -60,8 +60,8 @@ services: # ========================================================= admin-lehrer: build: - context: ./admin-lehrer - dockerfile: Dockerfile + context: . + dockerfile: admin-lehrer/Dockerfile args: NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-https://macmini:8001} NEXT_PUBLIC_OLD_ADMIN_URL: ${NEXT_PUBLIC_OLD_ADMIN_URL:-http://macmini:3000/admin} @@ -95,8 +95,8 @@ services: studio-v2: build: - context: ./studio-v2 - dockerfile: Dockerfile + context: . + dockerfile: studio-v2/Dockerfile args: NEXT_PUBLIC_VOICE_SERVICE_URL: ${NEXT_PUBLIC_VOICE_SERVICE_URL:-wss://macmini:8091} NEXT_PUBLIC_KLAUSUR_SERVICE_URL: ${NEXT_PUBLIC_KLAUSUR_SERVICE_URL:-https://macmini:8086} @@ -116,8 +116,8 @@ services: website: build: - context: ./website - dockerfile: Dockerfile + context: . + dockerfile: website/Dockerfile args: NEXT_PUBLIC_BILLING_API_URL: ${NEXT_PUBLIC_BILLING_API_URL:-https://macmini:8083} NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL:-https://macmini} diff --git a/shared/types/companion.ts b/shared/types/companion.ts new file mode 100644 index 0000000..5a4405b --- /dev/null +++ b/shared/types/companion.ts @@ -0,0 +1,329 @@ +/** + * TypeScript Types for Companion Module + * Migration from Flask companion.py/companion_js.py + */ + +// ============================================================================ +// Phase System +// ============================================================================ + +export type PhaseId = 'einstieg' | 'erarbeitung' | 'sicherung' | 'transfer' | 'reflexion' + +export interface Phase { + id: PhaseId + shortName: string // E, A, S, T, R + displayName: string + duration: number // minutes + status: 'planned' | 'active' | 'completed' + actualTime?: number // seconds (actual time spent) + color: string // hex color +} + +export interface PhaseContext { + currentPhase: PhaseId + phaseDisplayName: string +} + +// ============================================================================ +// Dashboard / Companion Mode +// ============================================================================ + +export interface CompanionStats { + classesCount: number + studentsCount: number + learningUnitsCreated: number + gradesEntered: number +} + +export interface Progress { + percentage: number + completed: number + total: number +} + +export type SuggestionPriority = 'urgent' | 'high' | 'medium' | 'low' + +export interface Suggestion { + id: string + title: string + description: string + priority: SuggestionPriority + icon: string // lucide icon name + actionTarget: string // navigation path + estimatedTime: number // minutes +} + +export type EventType = 'exam' | 'parent_meeting' | 'deadline' | 'other' + +export interface UpcomingEvent { + id: string + title: string + date: string // ISO date string + type: EventType + inDays: number +} + +export interface CompanionData { + context: PhaseContext + stats: CompanionStats + phases: Phase[] + progress: Progress + suggestions: Suggestion[] + upcomingEvents: UpcomingEvent[] +} + +// ============================================================================ +// Lesson Mode +// ============================================================================ + +export type LessonStatus = + | 'not_started' + | 'in_progress' + | 'paused' + | 'completed' + | 'overtime' + +export interface LessonPhase { + phase: PhaseId + duration: number // planned duration in minutes + status: 'planned' | 'active' | 'completed' | 'skipped' + actualTime: number // actual time spent in seconds + startedAt?: string // ISO timestamp + completedAt?: string // ISO timestamp +} + +export interface Homework { + id: string + title: string + description?: string + dueDate: string // ISO date + attachments?: string[] + completed?: boolean +} + +export interface Material { + id: string + title: string + type: 'document' | 'video' | 'presentation' | 'link' | 'other' + url?: string + fileName?: string +} + +export interface LessonReflection { + rating: number // 1-5 stars + notes: string + nextSteps: string + savedAt?: string +} + +export interface LessonSession { + sessionId: string + classId: string + className: string + subject: string + topic?: string + startTime: string // ISO timestamp + endTime?: string // ISO timestamp + phases: LessonPhase[] + totalPlannedDuration: number // minutes + currentPhaseIndex: number + elapsedTime: number // seconds + isPaused: boolean + pausedAt?: string + pauseDuration: number // total pause time in seconds + overtimeMinutes: number + status: LessonStatus + homeworkList: Homework[] + materials: Material[] + reflection?: LessonReflection +} + +// ============================================================================ +// Lesson Templates +// ============================================================================ + +export interface PhaseDurations { + einstieg: number + erarbeitung: number + sicherung: number + transfer: number + reflexion: number +} + +export interface LessonTemplate { + templateId: string + name: string + description?: string + subject?: string + durations: PhaseDurations + isSystemTemplate: boolean + createdBy?: string + createdAt?: string +} + +// ============================================================================ +// Settings +// ============================================================================ + +export interface TeacherSettings { + defaultPhaseDurations: PhaseDurations + preferredLessonLength: number // minutes (default 45) + autoAdvancePhases: boolean + soundNotifications: boolean + showKeyboardShortcuts: boolean + highContrastMode: boolean + onboardingCompleted: boolean + selectedTemplateId?: string +} + +// ============================================================================ +// Timer State +// ============================================================================ + +export type TimerColorStatus = 'plenty' | 'warning' | 'critical' | 'overtime' + +export interface TimerState { + isRunning: boolean + isPaused: boolean + elapsedSeconds: number + remainingSeconds: number + totalSeconds: number + progress: number // 0-1 + colorStatus: TimerColorStatus + currentPhase: LessonPhase | null +} + +// ============================================================================ +// Forms +// ============================================================================ + +export interface LessonStartFormData { + classId: string + subject: string + topic?: string + templateId?: string + customDurations?: PhaseDurations +} + +export interface Class { + id: string + name: string + grade: string + studentCount: number +} + +// ============================================================================ +// Feedback +// ============================================================================ + +export type FeedbackType = 'bug' | 'feature' | 'feedback' + +export interface FeedbackSubmission { + type: FeedbackType + title: string + description: string + screenshot?: string // base64 + sessionId?: string + metadata?: Record +} + +// ============================================================================ +// Onboarding +// ============================================================================ + +export interface OnboardingStep { + step: number + title: string + description: string + completed: boolean +} + +export interface OnboardingState { + currentStep: number + totalSteps: number + steps: OnboardingStep[] + selectedState?: string // Bundesland + selectedSchoolType?: string + completed: boolean +} + +// ============================================================================ +// WebSocket Messages +// ============================================================================ + +export type WSMessageType = + | 'phase_update' + | 'timer_tick' + | 'overtime_warning' + | 'pause_toggle' + | 'session_end' + | 'sync_request' + +export interface WSMessage { + type: WSMessageType + payload: { + sessionId: string + phase?: number + elapsed?: number + isPaused?: boolean + overtimeMinutes?: number + [key: string]: unknown + } + timestamp: string +} + +// ============================================================================ +// API Responses +// ============================================================================ + +export interface APIResponse { + success: boolean + data?: T + error?: string + message?: string +} + +export interface DashboardResponse extends APIResponse {} + +export interface LessonResponse extends APIResponse {} + +export interface TemplatesResponse extends APIResponse<{ templates: LessonTemplate[] }> {} + +export interface SettingsResponse extends APIResponse {} + +// ============================================================================ +// Component Props +// ============================================================================ + +export type CompanionMode = 'companion' | 'lesson' | 'classic' + +export interface ModeToggleProps { + currentMode: CompanionMode + onModeChange: (mode: CompanionMode) => void +} + +export interface PhaseTimelineProps { + phases: Phase[] + currentPhaseIndex: number + onPhaseClick?: (index: number) => void +} + +export interface VisualPieTimerProps { + progress: number // 0-1 + remainingSeconds: number + totalSeconds: number + colorStatus: TimerColorStatus + isPaused: boolean + currentPhaseName: string + phaseColor: string +} + +export interface QuickActionsBarProps { + onExtend: (minutes: number) => void + onPause: () => void + onResume: () => void + onSkip: () => void + isPaused: boolean + isLastPhase: boolean + disabled?: boolean +} diff --git a/shared/types/index.ts b/shared/types/index.ts new file mode 100644 index 0000000..33ed764 --- /dev/null +++ b/shared/types/index.ts @@ -0,0 +1,3 @@ +export * from './companion' +export * from './klausur' +export * from './ocr-labeling' diff --git a/shared/types/klausur.ts b/shared/types/klausur.ts new file mode 100644 index 0000000..e867255 --- /dev/null +++ b/shared/types/klausur.ts @@ -0,0 +1,432 @@ +/** + * Shared Klausur-Korrektur types and constants. + * + * This is the single source of truth used by: + * - admin-lehrer (education/klausur-korrektur) + * - studio-v2 (korrektur) + * - website/admin (klausur-korrektur) + * - website/lehrer (klausur-korrektur) + */ + +// --------------------------------------------------------------------------- +// Core domain interfaces +// --------------------------------------------------------------------------- + +export interface Klausur { + id: string + title: string + subject: string + year: number + semester: string + modus: KlausurModus + eh_id?: string + created_at: string + student_count?: number + completed_count?: number + status?: 'draft' | 'in_progress' | 'completed' +} + +/** Union of all modus values used across services */ +export type KlausurModus = 'abitur' | 'vorabitur' | 'landes_abitur' + +export interface StudentWork { + id: string + klausur_id: string + anonym_id: string + file_path: string + file_type: 'pdf' | 'image' + ocr_text: string + criteria_scores: CriteriaScores + gutachten: string + status: StudentStatus + raw_points: number + grade_points: number + grade_label?: string + created_at: string + examiner_id?: string + second_examiner_id?: string + second_examiner_grade?: number +} + +export type StudentStatus = + | 'UPLOADED' + | 'OCR_PROCESSING' + | 'OCR_COMPLETE' + | 'ANALYZING' + | 'FIRST_EXAMINER' + | 'SECOND_EXAMINER' + | 'COMPLETED' + | 'ERROR' + +export interface CriteriaScores { + rechtschreibung?: number + grammatik?: number + inhalt?: number + struktur?: number + stil?: number + [key: string]: number | undefined +} + +export interface Criterion { + id: string + name: string + weight: number + description?: string +} + +export interface GradeInfo { + thresholds: Record + labels: Record + criteria: Record +} + +// --------------------------------------------------------------------------- +// Annotations +// --------------------------------------------------------------------------- + +export interface Annotation { + id: string + student_work_id: string + page: number + position: AnnotationPosition + type: AnnotationType + text: string + severity: 'minor' | 'major' | 'critical' + suggestion?: string + created_by: string + created_at: string + role: 'first_examiner' | 'second_examiner' + linked_criterion?: string +} + +export interface AnnotationPosition { + x: number // Prozent (0-100) + y: number // Prozent (0-100) + width: number // Prozent (0-100) + height: number // Prozent (0-100) +} + +export type AnnotationType = + | 'rechtschreibung' + | 'grammatik' + | 'inhalt' + | 'struktur' + | 'stil' + | 'comment' + | 'highlight' + +// --------------------------------------------------------------------------- +// Fairness analysis +// --------------------------------------------------------------------------- + +export interface FairnessAnalysis { + klausur_id: string + student_count: number + average_grade: number + std_deviation: number + spread: number + outliers: OutlierInfo[] + criteria_analysis: Record + fairness_score: number + warnings: string[] +} + +export interface OutlierInfo { + student_id: string + anonym_id: string + grade_points: number + deviation: number + reason: string +} + +export interface CriteriaStats { + min: number + max: number + average: number + std_deviation: number +} + +// --------------------------------------------------------------------------- +// EH suggestions +// --------------------------------------------------------------------------- + +export interface EHSuggestion { + criterion: string + excerpt: string + relevance_score: number + source_chunk_id: string + // Attribution fields (CTRL-SRC-002) + source_document?: string + source_url?: string + license?: string + license_url?: string + publisher?: string +} + +/** Default Attribution for NiBiS documents (CTRL-SRC-002) */ +export const NIBIS_ATTRIBUTION = { + publisher: 'Niedersaechsischer Bildungsserver (NiBiS)', + license: 'DL-DE-BY-2.0', + license_url: 'https://www.govdata.de/dl-de/by-2-0', + source_url: 'https://nibis.de', +} as const + +// --------------------------------------------------------------------------- +// Gutachten +// --------------------------------------------------------------------------- + +export interface GutachtenSection { + title: string + content: string + evidence_links?: string[] +} + +export interface Gutachten { + einleitung: string + hauptteil: string + fazit: string + staerken: string[] + schwaechen: string[] + generated_at?: string +} + +// --------------------------------------------------------------------------- +// API response types +// --------------------------------------------------------------------------- + +export interface KlausurenResponse { + klausuren: Klausur[] + total: number +} + +export interface StudentsResponse { + students: StudentWork[] + total: number +} + +export interface AnnotationsResponse { + annotations: Annotation[] +} + +// --------------------------------------------------------------------------- +// Create / update types +// --------------------------------------------------------------------------- + +export interface CreateKlausurData { + title: string + subject?: string + year?: number + semester?: string + modus?: KlausurModus +} + +// --------------------------------------------------------------------------- +// Constants — annotation colors +// --------------------------------------------------------------------------- + +export const ANNOTATION_COLORS: Record = { + rechtschreibung: '#dc2626', // Red + grammatik: '#2563eb', // Blue + inhalt: '#16a34a', // Green + struktur: '#9333ea', // Purple + stil: '#ea580c', // Orange + comment: '#6b7280', // Gray + highlight: '#eab308', // Yellow +} + +// --------------------------------------------------------------------------- +// Constants — status colors & labels +// --------------------------------------------------------------------------- + +export const STATUS_COLORS: Record = { + UPLOADED: '#6b7280', + OCR_PROCESSING: '#eab308', + OCR_COMPLETE: '#3b82f6', + ANALYZING: '#8b5cf6', + FIRST_EXAMINER: '#f97316', + SECOND_EXAMINER: '#06b6d4', + COMPLETED: '#22c55e', + ERROR: '#ef4444', +} + +export const STATUS_LABELS: Record = { + UPLOADED: 'Hochgeladen', + OCR_PROCESSING: 'OCR laeuft', + OCR_COMPLETE: 'OCR fertig', + ANALYZING: 'Analyse laeuft', + FIRST_EXAMINER: 'Erstkorrektur', + SECOND_EXAMINER: 'Zweitkorrektur', + COMPLETED: 'Abgeschlossen', + ERROR: 'Fehler', +} + +// --------------------------------------------------------------------------- +// Constants — criteria & grades +// --------------------------------------------------------------------------- + +/** Default criteria with weights (Niedersachsen standard) */ +export const DEFAULT_CRITERIA: Record = { + rechtschreibung: { name: 'Rechtschreibung', weight: 15 }, + grammatik: { name: 'Grammatik', weight: 15 }, + inhalt: { name: 'Inhalt', weight: 40 }, + struktur: { name: 'Struktur', weight: 15 }, + stil: { name: 'Stil', weight: 15 }, +} + +/** Grade thresholds (15-point system) */ +export const GRADE_THRESHOLDS: Record = { + 15: 95, 14: 90, 13: 85, 12: 80, 11: 75, + 10: 70, 9: 65, 8: 60, 7: 55, 6: 50, + 5: 45, 4: 40, 3: 33, 2: 27, 1: 20, 0: 0, +} + +// --------------------------------------------------------------------------- +// Helper functions +// --------------------------------------------------------------------------- + +/** Calculate grade points from a percentage (0-100). */ +export function calculateGrade(percentage: number): number { + for (const [grade, threshold] of Object.entries(GRADE_THRESHOLDS).sort( + (a, b) => Number(b[0]) - Number(a[0]), + )) { + if (percentage >= threshold) { + return Number(grade) + } + } + return 0 +} + +/** Human-readable label for a 15-point grade value. */ +export function getGradeLabel(points: number): string { + const labels: Record = { + 15: '1+', 14: '1', 13: '1-', + 12: '2+', 11: '2', 10: '2-', + 9: '3+', 8: '3', 7: '3-', + 6: '4+', 5: '4', 4: '4-', + 3: '5+', 2: '5', 1: '5-', + 0: '6', + } + return labels[points] || String(points) +} + +// --------------------------------------------------------------------------- +// Examiner workflow types (workspace) +// --------------------------------------------------------------------------- + +export interface ExaminerInfo { + id: string + assigned_at: string + notes?: string +} + +export interface ExaminerResult { + grade_points: number + criteria_scores?: CriteriaScores + notes?: string + submitted_at: string +} + +export interface ExaminerWorkflow { + student_id: string + workflow_status: string + visibility_mode: string + user_role: 'ek' | 'zk' | 'dk' | 'viewer' + first_examiner?: ExaminerInfo + second_examiner?: ExaminerInfo + third_examiner?: ExaminerInfo + first_result?: ExaminerResult + first_result_visible?: boolean + second_result?: ExaminerResult + third_result?: ExaminerResult + grade_difference?: number + final_grade?: number + consensus_reached?: boolean + consensus_type?: string + einigung?: { + final_grade: number + notes: string + type: string + submitted_by: string + submitted_at: string + ek_grade: number + zk_grade: number + } + drittkorrektur_reason?: string +} + +export type ActiveTab = 'kriterien' | 'gutachten' | 'annotationen' | 'eh-vorschlaege' + +export interface GradeTotals { + raw: number + weighted: number + gradePoints: number +} + +// --------------------------------------------------------------------------- +// Constants — workflow status & roles +// --------------------------------------------------------------------------- + +export const GRADE_LABELS: Record = { + 15: '1+', 14: '1', 13: '1-', 12: '2+', 11: '2', 10: '2-', + 9: '3+', 8: '3', 7: '3-', 6: '4+', 5: '4', 4: '4-', + 3: '5+', 2: '5', 1: '5-', 0: '6', +} + +export const WORKFLOW_STATUS_LABELS: Record = { + not_started: { label: 'Nicht gestartet', color: 'bg-slate-100 text-slate-700' }, + ek_in_progress: { label: 'EK in Arbeit', color: 'bg-blue-100 text-blue-700' }, + ek_completed: { label: 'EK abgeschlossen', color: 'bg-blue-200 text-blue-800' }, + zk_assigned: { label: 'ZK zugewiesen', color: 'bg-amber-100 text-amber-700' }, + zk_in_progress: { label: 'ZK in Arbeit', color: 'bg-amber-200 text-amber-800' }, + zk_completed: { label: 'ZK abgeschlossen', color: 'bg-amber-300 text-amber-900' }, + einigung_required: { label: 'Einigung erforderlich', color: 'bg-orange-100 text-orange-700' }, + einigung_completed: { label: 'Einigung abgeschlossen', color: 'bg-green-100 text-green-700' }, + drittkorrektur_required: { label: 'DK erforderlich', color: 'bg-red-100 text-red-700' }, + drittkorrektur_assigned: { label: 'DK zugewiesen', color: 'bg-red-200 text-red-800' }, + drittkorrektur_in_progress: { label: 'DK in Arbeit', color: 'bg-red-300 text-red-900' }, + completed: { label: 'Abgeschlossen', color: 'bg-green-200 text-green-800' }, +} + +export const ROLE_LABELS: Record = { + ek: { label: 'Erstkorrektor', color: 'bg-blue-500' }, + zk: { label: 'Zweitkorrektor', color: 'bg-amber-500' }, + dk: { label: 'Drittkorrektor', color: 'bg-purple-500' }, + viewer: { label: 'Betrachter', color: 'bg-slate-500' }, +} + +// --------------------------------------------------------------------------- +// Form types (create / upload) +// --------------------------------------------------------------------------- + +export interface CreateKlausurForm { + title: string + subject: string + year: number + semester: string + modus: 'abitur' | 'vorabitur' +} + +export interface VorabiturEHForm { + aufgabentyp: string + titel: string + text_titel: string + text_autor: string + aufgabenstellung: string +} + +export interface EHTemplate { + aufgabentyp: string + name: string + description: string + category: string +} + +export interface DirektuploadForm { + files: File[] + ehFile: File | null + ehText: string + aufgabentyp: string + klausurTitle: string +} + +export type TabId = 'willkommen' | 'klausuren' | 'erstellen' | 'direktupload' | 'statistiken' diff --git a/shared/types/ocr-labeling.ts b/shared/types/ocr-labeling.ts new file mode 100644 index 0000000..3061055 --- /dev/null +++ b/shared/types/ocr-labeling.ts @@ -0,0 +1,127 @@ +/** + * Shared TypeScript types for OCR Labeling UI. + * + * Single source of truth used by: + * - admin-lehrer (ai/ocr-labeling) + * - website (admin/ocr-labeling) + */ + +/** + * Available OCR Models + * + * - llama3.2-vision:11b: Vision LLM, beste Qualitaet bei Handschrift (Standard) + * - trocr: Microsoft TrOCR, schnell bei gedrucktem Text + * - paddleocr: PaddleOCR + LLM, 4x schneller durch Hybrid-Ansatz + * - donut: Document Understanding Transformer, strukturierte Dokumente + */ +export type OCRModel = 'llama3.2-vision:11b' | 'trocr' | 'paddleocr' | 'donut' + +export const OCR_MODEL_INFO: Record = { + 'llama3.2-vision:11b': { + label: 'Vision LLM', + description: 'Beste Qualitaet bei Handschrift', + speed: 'langsam', + }, + trocr: { + label: 'Microsoft TrOCR', + description: 'Schnell bei gedrucktem Text', + speed: 'schnell', + }, + paddleocr: { + label: 'PaddleOCR + LLM', + description: 'Hybrid-Ansatz: OCR + Strukturierung', + speed: 'sehr schnell', + }, + donut: { + label: 'Donut', + description: 'Document Understanding fuer Tabellen/Formulare', + speed: 'mittel', + }, +} + +export interface OCRSession { + id: string + name: string + source_type: 'klausur' | 'handwriting_sample' | 'scan' + description?: string + ocr_model?: OCRModel + total_items: number + labeled_items: number + confirmed_items: number + corrected_items: number + skipped_items: number + created_at: string +} + +export interface OCRItem { + id: string + session_id: string + session_name: string + image_path: string + image_url?: string + ocr_text?: string + ocr_confidence?: number + ground_truth?: string + status: 'pending' | 'confirmed' | 'corrected' | 'skipped' + metadata?: Record + created_at: string +} + +export interface OCRStats { + total_sessions?: number + session_id?: string + name?: string + total_items: number + labeled_items: number + confirmed_items: number + corrected_items: number + skipped_items?: number + pending_items: number + exportable_items?: number + accuracy_rate: number + avg_label_time_seconds?: number + progress_percent?: number +} + +export interface TrainingSample { + id: string + image_path: string + ground_truth: string + export_format: 'generic' | 'trocr' | 'llama_vision' + training_batch: string + exported_at?: string +} + +export interface CreateSessionRequest { + name: string + source_type: 'klausur' | 'handwriting_sample' | 'scan' + description?: string + ocr_model?: OCRModel +} + +export interface ConfirmRequest { + item_id: string + label_time_seconds?: number +} + +export interface CorrectRequest { + item_id: string + ground_truth: string + label_time_seconds?: number +} + +export interface ExportRequest { + export_format: 'generic' | 'trocr' | 'llama_vision' + session_id?: string + batch_id?: string +} + +export interface UploadResult { + id: string + filename: string + image_path: string + image_hash: string + ocr_text?: string + ocr_confidence?: number + status: string +} diff --git a/studio-v2/Dockerfile b/studio-v2/Dockerfile index 5937223..baf95aa 100644 --- a/studio-v2/Dockerfile +++ b/studio-v2/Dockerfile @@ -4,13 +4,14 @@ FROM node:20-alpine AS builder WORKDIR /app # Copy package files -COPY package.json package-lock.json* ./ +COPY studio-v2/package.json studio-v2/package-lock.json* ./ # Install dependencies RUN npm install -# Copy source files -COPY . . +# Copy source files + shared types +COPY studio-v2/ . +COPY shared/ /shared/ # Build arguments for environment variables (needed at build time for Next.js) ARG NEXT_PUBLIC_VOICE_SERVICE_URL diff --git a/studio-v2/app/korrektur/types.ts b/studio-v2/app/korrektur/types.ts index 3efcfa6..63f5f0d 100644 --- a/studio-v2/app/korrektur/types.ts +++ b/studio-v2/app/korrektur/types.ts @@ -1,257 +1 @@ -// TypeScript Interfaces fuer Korrekturplattform (Studio v2) - -export interface Klausur { - id: string - title: string - subject: string - year: number - semester: string - modus: 'landes_abitur' | 'vorabitur' - eh_id?: string - created_at: string - student_count?: number - completed_count?: number - status?: 'draft' | 'in_progress' | 'completed' -} - -export interface StudentWork { - id: string - klausur_id: string - anonym_id: string - file_path: string - file_type: 'pdf' | 'image' - ocr_text: string - criteria_scores: CriteriaScores - gutachten: string - status: StudentStatus - raw_points: number - grade_points: number - grade_label?: string - created_at: string - examiner_id?: string - second_examiner_id?: string - second_examiner_grade?: number -} - -export type StudentStatus = - | 'UPLOADED' - | 'OCR_PROCESSING' - | 'OCR_COMPLETE' - | 'ANALYZING' - | 'FIRST_EXAMINER' - | 'SECOND_EXAMINER' - | 'COMPLETED' - | 'ERROR' - -export interface CriteriaScores { - rechtschreibung?: number - grammatik?: number - inhalt?: number - struktur?: number - stil?: number - [key: string]: number | undefined -} - -export interface Criterion { - id: string - name: string - weight: number - description?: string -} - -export interface GradeInfo { - thresholds: Record - labels: Record - criteria: Record -} - -export interface Annotation { - id: string - student_work_id: string - page: number - position: AnnotationPosition - type: AnnotationType - text: string - severity: 'minor' | 'major' | 'critical' - suggestion?: string - created_by: string - created_at: string - role: 'first_examiner' | 'second_examiner' - linked_criterion?: string -} - -export interface AnnotationPosition { - x: number // Prozent (0-100) - y: number // Prozent (0-100) - width: number // Prozent (0-100) - height: number // Prozent (0-100) -} - -export type AnnotationType = - | 'rechtschreibung' - | 'grammatik' - | 'inhalt' - | 'struktur' - | 'stil' - | 'comment' - | 'highlight' - -export interface FairnessAnalysis { - klausur_id: string - student_count: number - average_grade: number - std_deviation: number - spread: number - outliers: OutlierInfo[] - criteria_analysis: Record - fairness_score: number - warnings: string[] -} - -export interface OutlierInfo { - student_id: string - anonym_id: string - grade_points: number - deviation: number - reason: string -} - -export interface CriteriaStats { - min: number - max: number - average: number - std_deviation: number -} - -export interface EHSuggestion { - criterion: string - excerpt: string - relevance_score: number - source_chunk_id: string - // Attribution fields (CTRL-SRC-002) - source_document?: string - source_url?: string - license?: string - license_url?: string - publisher?: string -} - -// Default Attribution for NiBiS documents (CTRL-SRC-002) -export const NIBIS_ATTRIBUTION = { - publisher: 'Niedersaechsischer Bildungsserver (NiBiS)', - license: 'DL-DE-BY-2.0', - license_url: 'https://www.govdata.de/dl-de/by-2-0', - source_url: 'https://nibis.de', -} - -export interface GutachtenSection { - title: string - content: string - evidence_links?: string[] -} - -export interface Gutachten { - einleitung: string - hauptteil: string - fazit: string - staerken: string[] - schwaechen: string[] - generated_at?: string -} - -// API Response Types -export interface KlausurenResponse { - klausuren: Klausur[] - total: number -} - -export interface StudentsResponse { - students: StudentWork[] - total: number -} - -export interface AnnotationsResponse { - annotations: Annotation[] -} - -// Create/Update Types -export interface CreateKlausurData { - title: string - subject?: string - year?: number - semester?: string - modus?: 'landes_abitur' | 'vorabitur' -} - -// Color mapping for annotation types -export const ANNOTATION_COLORS: Record = { - rechtschreibung: '#dc2626', // Red - grammatik: '#2563eb', // Blue - inhalt: '#16a34a', // Green - struktur: '#9333ea', // Purple - stil: '#ea580c', // Orange - comment: '#6b7280', // Gray - highlight: '#eab308', // Yellow -} - -// Status colors -export const STATUS_COLORS: Record = { - UPLOADED: '#6b7280', - OCR_PROCESSING: '#eab308', - OCR_COMPLETE: '#3b82f6', - ANALYZING: '#8b5cf6', - FIRST_EXAMINER: '#f97316', - SECOND_EXAMINER: '#06b6d4', - COMPLETED: '#22c55e', - ERROR: '#ef4444', -} - -export const STATUS_LABELS: Record = { - UPLOADED: 'Hochgeladen', - OCR_PROCESSING: 'OCR laeuft', - OCR_COMPLETE: 'OCR fertig', - ANALYZING: 'Analyse laeuft', - FIRST_EXAMINER: 'Erstkorrektur', - SECOND_EXAMINER: 'Zweitkorrektur', - COMPLETED: 'Abgeschlossen', - ERROR: 'Fehler', -} - -// Default criteria with weights (NI standard) -export const DEFAULT_CRITERIA: Record = { - rechtschreibung: { name: 'Rechtschreibung', weight: 15 }, - grammatik: { name: 'Grammatik', weight: 15 }, - inhalt: { name: 'Inhalt', weight: 40 }, - struktur: { name: 'Struktur', weight: 15 }, - stil: { name: 'Stil', weight: 15 }, -} - -// Grade thresholds (15-point system) -export const GRADE_THRESHOLDS: Record = { - 15: 95, 14: 90, 13: 85, 12: 80, 11: 75, - 10: 70, 9: 65, 8: 60, 7: 55, 6: 50, - 5: 45, 4: 40, 3: 33, 2: 27, 1: 20, 0: 0 -} - -// Helper function to calculate grade from percentage -export function calculateGrade(percentage: number): number { - for (const [grade, threshold] of Object.entries(GRADE_THRESHOLDS).sort((a, b) => Number(b[0]) - Number(a[0]))) { - if (percentage >= threshold) { - return Number(grade) - } - } - return 0 -} - -// Helper function to get grade label -export function getGradeLabel(points: number): string { - const labels: Record = { - 15: '1+', 14: '1', 13: '1-', - 12: '2+', 11: '2', 10: '2-', - 9: '3+', 8: '3', 7: '3-', - 6: '4+', 5: '4', 4: '4-', - 3: '5+', 2: '5', 1: '5-', - 0: '6' - } - return labels[points] || String(points) -} +export * from '../../../shared/types/klausur' diff --git a/studio-v2/lib/companion/types.ts b/studio-v2/lib/companion/types.ts index 5a4405b..fe2de9c 100644 --- a/studio-v2/lib/companion/types.ts +++ b/studio-v2/lib/companion/types.ts @@ -1,329 +1 @@ -/** - * TypeScript Types for Companion Module - * Migration from Flask companion.py/companion_js.py - */ - -// ============================================================================ -// Phase System -// ============================================================================ - -export type PhaseId = 'einstieg' | 'erarbeitung' | 'sicherung' | 'transfer' | 'reflexion' - -export interface Phase { - id: PhaseId - shortName: string // E, A, S, T, R - displayName: string - duration: number // minutes - status: 'planned' | 'active' | 'completed' - actualTime?: number // seconds (actual time spent) - color: string // hex color -} - -export interface PhaseContext { - currentPhase: PhaseId - phaseDisplayName: string -} - -// ============================================================================ -// Dashboard / Companion Mode -// ============================================================================ - -export interface CompanionStats { - classesCount: number - studentsCount: number - learningUnitsCreated: number - gradesEntered: number -} - -export interface Progress { - percentage: number - completed: number - total: number -} - -export type SuggestionPriority = 'urgent' | 'high' | 'medium' | 'low' - -export interface Suggestion { - id: string - title: string - description: string - priority: SuggestionPriority - icon: string // lucide icon name - actionTarget: string // navigation path - estimatedTime: number // minutes -} - -export type EventType = 'exam' | 'parent_meeting' | 'deadline' | 'other' - -export interface UpcomingEvent { - id: string - title: string - date: string // ISO date string - type: EventType - inDays: number -} - -export interface CompanionData { - context: PhaseContext - stats: CompanionStats - phases: Phase[] - progress: Progress - suggestions: Suggestion[] - upcomingEvents: UpcomingEvent[] -} - -// ============================================================================ -// Lesson Mode -// ============================================================================ - -export type LessonStatus = - | 'not_started' - | 'in_progress' - | 'paused' - | 'completed' - | 'overtime' - -export interface LessonPhase { - phase: PhaseId - duration: number // planned duration in minutes - status: 'planned' | 'active' | 'completed' | 'skipped' - actualTime: number // actual time spent in seconds - startedAt?: string // ISO timestamp - completedAt?: string // ISO timestamp -} - -export interface Homework { - id: string - title: string - description?: string - dueDate: string // ISO date - attachments?: string[] - completed?: boolean -} - -export interface Material { - id: string - title: string - type: 'document' | 'video' | 'presentation' | 'link' | 'other' - url?: string - fileName?: string -} - -export interface LessonReflection { - rating: number // 1-5 stars - notes: string - nextSteps: string - savedAt?: string -} - -export interface LessonSession { - sessionId: string - classId: string - className: string - subject: string - topic?: string - startTime: string // ISO timestamp - endTime?: string // ISO timestamp - phases: LessonPhase[] - totalPlannedDuration: number // minutes - currentPhaseIndex: number - elapsedTime: number // seconds - isPaused: boolean - pausedAt?: string - pauseDuration: number // total pause time in seconds - overtimeMinutes: number - status: LessonStatus - homeworkList: Homework[] - materials: Material[] - reflection?: LessonReflection -} - -// ============================================================================ -// Lesson Templates -// ============================================================================ - -export interface PhaseDurations { - einstieg: number - erarbeitung: number - sicherung: number - transfer: number - reflexion: number -} - -export interface LessonTemplate { - templateId: string - name: string - description?: string - subject?: string - durations: PhaseDurations - isSystemTemplate: boolean - createdBy?: string - createdAt?: string -} - -// ============================================================================ -// Settings -// ============================================================================ - -export interface TeacherSettings { - defaultPhaseDurations: PhaseDurations - preferredLessonLength: number // minutes (default 45) - autoAdvancePhases: boolean - soundNotifications: boolean - showKeyboardShortcuts: boolean - highContrastMode: boolean - onboardingCompleted: boolean - selectedTemplateId?: string -} - -// ============================================================================ -// Timer State -// ============================================================================ - -export type TimerColorStatus = 'plenty' | 'warning' | 'critical' | 'overtime' - -export interface TimerState { - isRunning: boolean - isPaused: boolean - elapsedSeconds: number - remainingSeconds: number - totalSeconds: number - progress: number // 0-1 - colorStatus: TimerColorStatus - currentPhase: LessonPhase | null -} - -// ============================================================================ -// Forms -// ============================================================================ - -export interface LessonStartFormData { - classId: string - subject: string - topic?: string - templateId?: string - customDurations?: PhaseDurations -} - -export interface Class { - id: string - name: string - grade: string - studentCount: number -} - -// ============================================================================ -// Feedback -// ============================================================================ - -export type FeedbackType = 'bug' | 'feature' | 'feedback' - -export interface FeedbackSubmission { - type: FeedbackType - title: string - description: string - screenshot?: string // base64 - sessionId?: string - metadata?: Record -} - -// ============================================================================ -// Onboarding -// ============================================================================ - -export interface OnboardingStep { - step: number - title: string - description: string - completed: boolean -} - -export interface OnboardingState { - currentStep: number - totalSteps: number - steps: OnboardingStep[] - selectedState?: string // Bundesland - selectedSchoolType?: string - completed: boolean -} - -// ============================================================================ -// WebSocket Messages -// ============================================================================ - -export type WSMessageType = - | 'phase_update' - | 'timer_tick' - | 'overtime_warning' - | 'pause_toggle' - | 'session_end' - | 'sync_request' - -export interface WSMessage { - type: WSMessageType - payload: { - sessionId: string - phase?: number - elapsed?: number - isPaused?: boolean - overtimeMinutes?: number - [key: string]: unknown - } - timestamp: string -} - -// ============================================================================ -// API Responses -// ============================================================================ - -export interface APIResponse { - success: boolean - data?: T - error?: string - message?: string -} - -export interface DashboardResponse extends APIResponse {} - -export interface LessonResponse extends APIResponse {} - -export interface TemplatesResponse extends APIResponse<{ templates: LessonTemplate[] }> {} - -export interface SettingsResponse extends APIResponse {} - -// ============================================================================ -// Component Props -// ============================================================================ - -export type CompanionMode = 'companion' | 'lesson' | 'classic' - -export interface ModeToggleProps { - currentMode: CompanionMode - onModeChange: (mode: CompanionMode) => void -} - -export interface PhaseTimelineProps { - phases: Phase[] - currentPhaseIndex: number - onPhaseClick?: (index: number) => void -} - -export interface VisualPieTimerProps { - progress: number // 0-1 - remainingSeconds: number - totalSeconds: number - colorStatus: TimerColorStatus - isPaused: boolean - currentPhaseName: string - phaseColor: string -} - -export interface QuickActionsBarProps { - onExtend: (minutes: number) => void - onPause: () => void - onResume: () => void - onSkip: () => void - isPaused: boolean - isLastPhase: boolean - disabled?: boolean -} +export * from '../../../shared/types/companion' diff --git a/studio-v2/tsconfig.json b/studio-v2/tsconfig.json index 9e9bbf7..3d016ab 100644 --- a/studio-v2/tsconfig.json +++ b/studio-v2/tsconfig.json @@ -24,6 +24,9 @@ "paths": { "@/*": [ "./*" + ], + "@shared/*": [ + "../shared/*" ] }, "target": "ES2017" diff --git a/website/Dockerfile b/website/Dockerfile index a4a1735..9d3ba6a 100644 --- a/website/Dockerfile +++ b/website/Dockerfile @@ -4,13 +4,14 @@ FROM node:20-alpine AS builder WORKDIR /app # Copy package files -COPY package.json package-lock.json* ./ +COPY website/package.json website/package-lock.json* ./ # Install dependencies RUN npm install -# Copy source code -COPY . . +# Copy source code + shared types +COPY website/ . +COPY shared/ /shared/ # Build arguments for environment variables ARG NEXT_PUBLIC_BILLING_API_URL diff --git a/website/app/admin/klausur-korrektur/types.ts b/website/app/admin/klausur-korrektur/types.ts index adbe877..ba897cb 100644 --- a/website/app/admin/klausur-korrektur/types.ts +++ b/website/app/admin/klausur-korrektur/types.ts @@ -1,195 +1 @@ -// TypeScript Interfaces für Klausur-Korrektur - -export interface Klausur { - id: string - title: string - subject: string - year: number - semester: string - modus: 'abitur' | 'vorabitur' - eh_id?: string - created_at: string - student_count?: number - completed_count?: number - status?: 'draft' | 'in_progress' | 'completed' -} - -export interface StudentWork { - id: string - klausur_id: string - anonym_id: string - file_path: string - file_type: 'pdf' | 'image' - ocr_text: string - criteria_scores: CriteriaScores - gutachten: string - status: StudentStatus - raw_points: number - grade_points: number - grade_label?: string - created_at: string - examiner_id?: string - second_examiner_id?: string - second_examiner_grade?: number -} - -export type StudentStatus = - | 'UPLOADED' - | 'OCR_PROCESSING' - | 'OCR_COMPLETE' - | 'ANALYZING' - | 'FIRST_EXAMINER' - | 'SECOND_EXAMINER' - | 'COMPLETED' - | 'ERROR' - -export interface CriteriaScores { - rechtschreibung?: number - grammatik?: number - inhalt?: number - struktur?: number - stil?: number - [key: string]: number | undefined -} - -export interface Criterion { - id: string - name: string - weight: number - description?: string -} - -export interface GradeInfo { - thresholds: Record - labels: Record - criteria: Record -} - -export interface Annotation { - id: string - student_work_id: string - page: number - position: AnnotationPosition - type: AnnotationType - text: string - severity: 'minor' | 'major' | 'critical' - suggestion?: string - created_by: string - created_at: string - role: 'first_examiner' | 'second_examiner' - linked_criterion?: string -} - -export interface AnnotationPosition { - x: number // Prozent (0-100) - y: number // Prozent (0-100) - width: number // Prozent (0-100) - height: number // Prozent (0-100) -} - -export type AnnotationType = - | 'rechtschreibung' - | 'grammatik' - | 'inhalt' - | 'struktur' - | 'stil' - | 'comment' - | 'highlight' - -export interface FairnessAnalysis { - klausur_id: string - student_count: number - average_grade: number - std_deviation: number - spread: number - outliers: OutlierInfo[] - criteria_analysis: Record - fairness_score: number - warnings: string[] -} - -export interface OutlierInfo { - student_id: string - anonym_id: string - grade_points: number - deviation: number - reason: string -} - -export interface CriteriaStats { - min: number - max: number - average: number - std_deviation: number -} - -export interface EHSuggestion { - criterion: string - excerpt: string - relevance_score: number - source_chunk_id: string -} - -export interface GutachtenSection { - title: string - content: string - evidence_links?: string[] -} - -export interface Gutachten { - einleitung: string - hauptteil: string - fazit: string - staerken: string[] - schwaechen: string[] - generated_at?: string -} - -// API Response Types -export interface KlausurenResponse { - klausuren: Klausur[] - total: number -} - -export interface StudentsResponse { - students: StudentWork[] - total: number -} - -export interface AnnotationsResponse { - annotations: Annotation[] -} - -// Color mapping for annotation types -export const ANNOTATION_COLORS: Record = { - rechtschreibung: '#dc2626', // Red - grammatik: '#2563eb', // Blue - inhalt: '#16a34a', // Green - struktur: '#9333ea', // Purple - stil: '#ea580c', // Orange - comment: '#6b7280', // Gray - highlight: '#eab308', // Yellow -} - -// Status colors -export const STATUS_COLORS: Record = { - UPLOADED: '#6b7280', - OCR_PROCESSING: '#eab308', - OCR_COMPLETE: '#3b82f6', - ANALYZING: '#8b5cf6', - FIRST_EXAMINER: '#f97316', - SECOND_EXAMINER: '#06b6d4', - COMPLETED: '#22c55e', - ERROR: '#ef4444', -} - -export const STATUS_LABELS: Record = { - UPLOADED: 'Hochgeladen', - OCR_PROCESSING: 'OCR läuft', - OCR_COMPLETE: 'OCR fertig', - ANALYZING: 'Analyse läuft', - FIRST_EXAMINER: 'Erstkorrektur', - SECOND_EXAMINER: 'Zweitkorrektur', - COMPLETED: 'Abgeschlossen', - ERROR: 'Fehler', -} +export * from '../../../../shared/types/klausur' diff --git a/website/app/admin/ocr-labeling/types.ts b/website/app/admin/ocr-labeling/types.ts index f73a28a..9af47a7 100644 --- a/website/app/admin/ocr-labeling/types.ts +++ b/website/app/admin/ocr-labeling/types.ts @@ -1,123 +1 @@ -/** - * TypeScript types for OCR Labeling UI - */ - -/** - * Available OCR Models - * - * - llama3.2-vision:11b: Vision LLM, beste Qualitaet bei Handschrift (Standard) - * - trocr: Microsoft TrOCR, schnell bei gedrucktem Text - * - paddleocr: PaddleOCR + LLM, 4x schneller durch Hybrid-Ansatz - * - donut: Document Understanding Transformer, strukturierte Dokumente - */ -export type OCRModel = 'llama3.2-vision:11b' | 'trocr' | 'paddleocr' | 'donut' - -export const OCR_MODEL_INFO: Record = { - 'llama3.2-vision:11b': { - label: 'Vision LLM', - description: 'Beste Qualitaet bei Handschrift', - speed: 'langsam', - }, - trocr: { - label: 'Microsoft TrOCR', - description: 'Schnell bei gedrucktem Text', - speed: 'schnell', - }, - paddleocr: { - label: 'PaddleOCR + LLM', - description: 'Hybrid-Ansatz: OCR + Strukturierung', - speed: 'sehr schnell', - }, - donut: { - label: 'Donut', - description: 'Document Understanding fuer Tabellen/Formulare', - speed: 'mittel', - }, -} - -export interface OCRSession { - id: string - name: string - source_type: 'klausur' | 'handwriting_sample' | 'scan' - description?: string - ocr_model?: OCRModel - total_items: number - labeled_items: number - confirmed_items: number - corrected_items: number - skipped_items: number - created_at: string -} - -export interface OCRItem { - id: string - session_id: string - session_name: string - image_path: string - image_url?: string - ocr_text?: string - ocr_confidence?: number - ground_truth?: string - status: 'pending' | 'confirmed' | 'corrected' | 'skipped' - metadata?: Record - created_at: string -} - -export interface OCRStats { - total_sessions?: number - session_id?: string - name?: string - total_items: number - labeled_items: number - confirmed_items: number - corrected_items: number - skipped_items?: number - pending_items: number - exportable_items?: number - accuracy_rate: number - avg_label_time_seconds?: number - progress_percent?: number -} - -export interface TrainingSample { - id: string - image_path: string - ground_truth: string - export_format: 'generic' | 'trocr' | 'llama_vision' - training_batch: string - exported_at?: string -} - -export interface CreateSessionRequest { - name: string - source_type: 'klausur' | 'handwriting_sample' | 'scan' - description?: string - ocr_model?: OCRModel -} - -export interface ConfirmRequest { - item_id: string - label_time_seconds?: number -} - -export interface CorrectRequest { - item_id: string - ground_truth: string - label_time_seconds?: number -} - -export interface ExportRequest { - export_format: 'generic' | 'trocr' | 'llama_vision' - session_id?: string - batch_id?: string -} - -export interface UploadResult { - id: string - filename: string - image_path: string - image_hash: string - ocr_text?: string - ocr_confidence?: number - status: string -} +export * from '../../../../shared/types/ocr-labeling' diff --git a/website/app/lehrer/klausur-korrektur/types.ts b/website/app/lehrer/klausur-korrektur/types.ts index adbe877..ba897cb 100644 --- a/website/app/lehrer/klausur-korrektur/types.ts +++ b/website/app/lehrer/klausur-korrektur/types.ts @@ -1,195 +1 @@ -// TypeScript Interfaces für Klausur-Korrektur - -export interface Klausur { - id: string - title: string - subject: string - year: number - semester: string - modus: 'abitur' | 'vorabitur' - eh_id?: string - created_at: string - student_count?: number - completed_count?: number - status?: 'draft' | 'in_progress' | 'completed' -} - -export interface StudentWork { - id: string - klausur_id: string - anonym_id: string - file_path: string - file_type: 'pdf' | 'image' - ocr_text: string - criteria_scores: CriteriaScores - gutachten: string - status: StudentStatus - raw_points: number - grade_points: number - grade_label?: string - created_at: string - examiner_id?: string - second_examiner_id?: string - second_examiner_grade?: number -} - -export type StudentStatus = - | 'UPLOADED' - | 'OCR_PROCESSING' - | 'OCR_COMPLETE' - | 'ANALYZING' - | 'FIRST_EXAMINER' - | 'SECOND_EXAMINER' - | 'COMPLETED' - | 'ERROR' - -export interface CriteriaScores { - rechtschreibung?: number - grammatik?: number - inhalt?: number - struktur?: number - stil?: number - [key: string]: number | undefined -} - -export interface Criterion { - id: string - name: string - weight: number - description?: string -} - -export interface GradeInfo { - thresholds: Record - labels: Record - criteria: Record -} - -export interface Annotation { - id: string - student_work_id: string - page: number - position: AnnotationPosition - type: AnnotationType - text: string - severity: 'minor' | 'major' | 'critical' - suggestion?: string - created_by: string - created_at: string - role: 'first_examiner' | 'second_examiner' - linked_criterion?: string -} - -export interface AnnotationPosition { - x: number // Prozent (0-100) - y: number // Prozent (0-100) - width: number // Prozent (0-100) - height: number // Prozent (0-100) -} - -export type AnnotationType = - | 'rechtschreibung' - | 'grammatik' - | 'inhalt' - | 'struktur' - | 'stil' - | 'comment' - | 'highlight' - -export interface FairnessAnalysis { - klausur_id: string - student_count: number - average_grade: number - std_deviation: number - spread: number - outliers: OutlierInfo[] - criteria_analysis: Record - fairness_score: number - warnings: string[] -} - -export interface OutlierInfo { - student_id: string - anonym_id: string - grade_points: number - deviation: number - reason: string -} - -export interface CriteriaStats { - min: number - max: number - average: number - std_deviation: number -} - -export interface EHSuggestion { - criterion: string - excerpt: string - relevance_score: number - source_chunk_id: string -} - -export interface GutachtenSection { - title: string - content: string - evidence_links?: string[] -} - -export interface Gutachten { - einleitung: string - hauptteil: string - fazit: string - staerken: string[] - schwaechen: string[] - generated_at?: string -} - -// API Response Types -export interface KlausurenResponse { - klausuren: Klausur[] - total: number -} - -export interface StudentsResponse { - students: StudentWork[] - total: number -} - -export interface AnnotationsResponse { - annotations: Annotation[] -} - -// Color mapping for annotation types -export const ANNOTATION_COLORS: Record = { - rechtschreibung: '#dc2626', // Red - grammatik: '#2563eb', // Blue - inhalt: '#16a34a', // Green - struktur: '#9333ea', // Purple - stil: '#ea580c', // Orange - comment: '#6b7280', // Gray - highlight: '#eab308', // Yellow -} - -// Status colors -export const STATUS_COLORS: Record = { - UPLOADED: '#6b7280', - OCR_PROCESSING: '#eab308', - OCR_COMPLETE: '#3b82f6', - ANALYZING: '#8b5cf6', - FIRST_EXAMINER: '#f97316', - SECOND_EXAMINER: '#06b6d4', - COMPLETED: '#22c55e', - ERROR: '#ef4444', -} - -export const STATUS_LABELS: Record = { - UPLOADED: 'Hochgeladen', - OCR_PROCESSING: 'OCR läuft', - OCR_COMPLETE: 'OCR fertig', - ANALYZING: 'Analyse läuft', - FIRST_EXAMINER: 'Erstkorrektur', - SECOND_EXAMINER: 'Zweitkorrektur', - COMPLETED: 'Abgeschlossen', - ERROR: 'Fehler', -} +export * from '../../../../shared/types/klausur' diff --git a/website/components/klausur-korrektur/list-types.ts b/website/components/klausur-korrektur/list-types.ts index a0d0a94..9a03f04 100644 --- a/website/components/klausur-korrektur/list-types.ts +++ b/website/components/klausur-korrektur/list-types.ts @@ -1,39 +1,17 @@ /** * Types and constants for the Klausur-Korrektur list page. * Shared between admin and lehrer routes. + * + * Domain types are re-exported from the shared module. + * Only the API_BASE constant remains local (env-dependent). */ +export type { + TabId, + CreateKlausurForm, + VorabiturEHForm, + EHTemplate, + DirektuploadForm, +} from '../../../shared/types/klausur' + export const API_BASE = process.env.NEXT_PUBLIC_KLAUSUR_SERVICE_URL || 'http://localhost:8086' - -export type TabId = 'willkommen' | 'klausuren' | 'erstellen' | 'direktupload' | 'statistiken' - -export interface CreateKlausurForm { - title: string - subject: string - year: number - semester: string - modus: 'abitur' | 'vorabitur' -} - -export interface VorabiturEHForm { - aufgabentyp: string - titel: string - text_titel: string - text_autor: string - aufgabenstellung: string -} - -export interface EHTemplate { - aufgabentyp: string - name: string - description: string - category: string -} - -export interface DirektuploadForm { - files: File[] - ehFile: File | null - ehText: string - aufgabentyp: string - klausurTitle: string -} diff --git a/website/components/klausur-korrektur/workspace-types.ts b/website/components/klausur-korrektur/workspace-types.ts index e5f1a59..64b7ee9 100644 --- a/website/components/klausur-korrektur/workspace-types.ts +++ b/website/components/klausur-korrektur/workspace-types.ts @@ -1,81 +1,23 @@ /** * Types and constants for the Korrektur-Workspace. * Shared between admin and lehrer routes. + * + * Domain types are re-exported from the shared module. + * Only the API_BASE constant remains local (env-dependent). */ -import type { CriteriaScores } from '../../app/admin/klausur-korrektur/types' +export type { + ExaminerInfo, + ExaminerResult, + ExaminerWorkflow, + ActiveTab, + CriteriaScores, +} from '../../../shared/types/klausur' -// Examiner workflow types -export interface ExaminerInfo { - id: string - assigned_at: string - notes?: string -} - -export interface ExaminerResult { - grade_points: number - criteria_scores?: CriteriaScores - notes?: string - submitted_at: string -} - -export interface ExaminerWorkflow { - student_id: string - workflow_status: string - visibility_mode: string - user_role: 'ek' | 'zk' | 'dk' | 'viewer' - first_examiner?: ExaminerInfo - second_examiner?: ExaminerInfo - third_examiner?: ExaminerInfo - first_result?: ExaminerResult - first_result_visible?: boolean - second_result?: ExaminerResult - third_result?: ExaminerResult - grade_difference?: number - final_grade?: number - consensus_reached?: boolean - consensus_type?: string - einigung?: { - final_grade: number - notes: string - type: string - submitted_by: string - submitted_at: string - ek_grade: number - zk_grade: number - } - drittkorrektur_reason?: string -} - -export type ActiveTab = 'kriterien' | 'gutachten' | 'annotationen' | 'eh-vorschlaege' - -// Workflow status labels -export const WORKFLOW_STATUS_LABELS: Record = { - not_started: { label: 'Nicht gestartet', color: 'bg-slate-100 text-slate-700' }, - ek_in_progress: { label: 'EK in Arbeit', color: 'bg-blue-100 text-blue-700' }, - ek_completed: { label: 'EK abgeschlossen', color: 'bg-blue-200 text-blue-800' }, - zk_assigned: { label: 'ZK zugewiesen', color: 'bg-amber-100 text-amber-700' }, - zk_in_progress: { label: 'ZK in Arbeit', color: 'bg-amber-200 text-amber-800' }, - zk_completed: { label: 'ZK abgeschlossen', color: 'bg-amber-300 text-amber-900' }, - einigung_required: { label: 'Einigung erforderlich', color: 'bg-orange-100 text-orange-700' }, - einigung_completed: { label: 'Einigung abgeschlossen', color: 'bg-green-100 text-green-700' }, - drittkorrektur_required: { label: 'DK erforderlich', color: 'bg-red-100 text-red-700' }, - drittkorrektur_assigned: { label: 'DK zugewiesen', color: 'bg-red-200 text-red-800' }, - drittkorrektur_in_progress: { label: 'DK in Arbeit', color: 'bg-red-300 text-red-900' }, - completed: { label: 'Abgeschlossen', color: 'bg-green-200 text-green-800' }, -} - -export const ROLE_LABELS: Record = { - ek: { label: 'Erstkorrektor', color: 'bg-blue-500' }, - zk: { label: 'Zweitkorrektor', color: 'bg-amber-500' }, - dk: { label: 'Drittkorrektor', color: 'bg-purple-500' }, - viewer: { label: 'Betrachter', color: 'bg-slate-500' }, -} +export { + WORKFLOW_STATUS_LABELS, + ROLE_LABELS, + GRADE_LABELS, +} from '../../../shared/types/klausur' export const API_BASE = process.env.NEXT_PUBLIC_KLAUSUR_SERVICE_URL || 'http://localhost:8086' - -export const GRADE_LABELS: Record = { - 15: '1+', 14: '1', 13: '1-', 12: '2+', 11: '2', 10: '2-', - 9: '3+', 8: '3', 7: '3-', 6: '4+', 5: '4', 4: '4-', - 3: '5+', 2: '5', 1: '5-', 0: '6', -} diff --git a/website/tsconfig.json b/website/tsconfig.json index d81d4ee..fcdc330 100644 --- a/website/tsconfig.json +++ b/website/tsconfig.json @@ -24,6 +24,9 @@ "paths": { "@/*": [ "./*" + ], + "@shared/*": [ + "../shared/*" ] }, "target": "ES2017"