refactor: Admin-Layout komplett entfernt — SDK als einziges Layout
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 32s
CI / test-python-backend-compliance (push) Successful in 31s
CI / test-python-document-crawler (push) Successful in 21s
CI / test-python-dsms-gateway (push) Successful in 19s
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 32s
CI / test-python-backend-compliance (push) Successful in 31s
CI / test-python-document-crawler (push) Successful in 21s
CI / test-python-dsms-gateway (push) Successful in 19s
Kaputtes (admin) Layout geloescht (Role-Selection, 404-Sidebar, localhost-Dashboard). SDK-Flow nach /sdk/sdk-flow verschoben. Route-Gruppe (sdk) aufgeloest. Root-Seite redirected auf /sdk. ~25 ungenutzte Dateien/Verzeichnisse entfernt. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,364 +0,0 @@
|
||||
/**
|
||||
* Constants for Companion Module
|
||||
* Phase colors, defaults, and configuration
|
||||
*/
|
||||
|
||||
import { PhaseId, PhaseDurations, Phase, TeacherSettings } from './types'
|
||||
|
||||
// ============================================================================
|
||||
// Phase Colors (Didactic Color Psychology)
|
||||
// ============================================================================
|
||||
|
||||
export const PHASE_COLORS: Record<PhaseId, { hex: string; tailwind: string; gradient: string }> = {
|
||||
einstieg: {
|
||||
hex: '#4A90E2',
|
||||
tailwind: 'bg-blue-500',
|
||||
gradient: 'from-blue-500 to-blue-600',
|
||||
},
|
||||
erarbeitung: {
|
||||
hex: '#F5A623',
|
||||
tailwind: 'bg-orange-500',
|
||||
gradient: 'from-orange-500 to-orange-600',
|
||||
},
|
||||
sicherung: {
|
||||
hex: '#7ED321',
|
||||
tailwind: 'bg-green-500',
|
||||
gradient: 'from-green-500 to-green-600',
|
||||
},
|
||||
transfer: {
|
||||
hex: '#9013FE',
|
||||
tailwind: 'bg-purple-600',
|
||||
gradient: 'from-purple-600 to-purple-700',
|
||||
},
|
||||
reflexion: {
|
||||
hex: '#6B7280',
|
||||
tailwind: 'bg-gray-500',
|
||||
gradient: 'from-gray-500 to-gray-600',
|
||||
},
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Phase Definitions
|
||||
// ============================================================================
|
||||
|
||||
export const PHASE_SHORT_NAMES: Record<PhaseId, string> = {
|
||||
einstieg: 'E',
|
||||
erarbeitung: 'A',
|
||||
sicherung: 'S',
|
||||
transfer: 'T',
|
||||
reflexion: 'R',
|
||||
}
|
||||
|
||||
export const PHASE_DISPLAY_NAMES: Record<PhaseId, string> = {
|
||||
einstieg: 'Einstieg',
|
||||
erarbeitung: 'Erarbeitung',
|
||||
sicherung: 'Sicherung',
|
||||
transfer: 'Transfer',
|
||||
reflexion: 'Reflexion',
|
||||
}
|
||||
|
||||
export const PHASE_DESCRIPTIONS: Record<PhaseId, string> = {
|
||||
einstieg: 'Motivation, Kontext setzen, Vorwissen aktivieren',
|
||||
erarbeitung: 'Hauptinhalt, aktives Lernen, neue Konzepte',
|
||||
sicherung: 'Konsolidierung, Zusammenfassung, Uebungen',
|
||||
transfer: 'Anwendung, neue Kontexte, kreative Aufgaben',
|
||||
reflexion: 'Rueckblick, Selbsteinschaetzung, Ausblick',
|
||||
}
|
||||
|
||||
export const PHASE_ORDER: PhaseId[] = [
|
||||
'einstieg',
|
||||
'erarbeitung',
|
||||
'sicherung',
|
||||
'transfer',
|
||||
'reflexion',
|
||||
]
|
||||
|
||||
// ============================================================================
|
||||
// Default Durations (in minutes)
|
||||
// ============================================================================
|
||||
|
||||
export const DEFAULT_PHASE_DURATIONS: PhaseDurations = {
|
||||
einstieg: 8,
|
||||
erarbeitung: 20,
|
||||
sicherung: 10,
|
||||
transfer: 7,
|
||||
reflexion: 5,
|
||||
}
|
||||
|
||||
export const DEFAULT_LESSON_LENGTH = 45 // minutes (German standard)
|
||||
export const EXTENDED_LESSON_LENGTH = 50 // minutes (with buffer)
|
||||
|
||||
// ============================================================================
|
||||
// Timer Thresholds (in seconds)
|
||||
// ============================================================================
|
||||
|
||||
export const TIMER_WARNING_THRESHOLD = 5 * 60 // 5 minutes = warning (yellow)
|
||||
export const TIMER_CRITICAL_THRESHOLD = 2 * 60 // 2 minutes = critical (red)
|
||||
|
||||
// ============================================================================
|
||||
// SVG Pie Timer Constants
|
||||
// ============================================================================
|
||||
|
||||
export const PIE_TIMER_RADIUS = 42
|
||||
export const PIE_TIMER_CIRCUMFERENCE = 2 * Math.PI * PIE_TIMER_RADIUS // ~263.89
|
||||
export const PIE_TIMER_STROKE_WIDTH = 8
|
||||
export const PIE_TIMER_SIZE = 120 // viewBox size
|
||||
|
||||
// ============================================================================
|
||||
// Timer Color Classes
|
||||
// ============================================================================
|
||||
|
||||
export const TIMER_COLOR_CLASSES = {
|
||||
plenty: 'text-green-500 stroke-green-500',
|
||||
warning: 'text-amber-500 stroke-amber-500',
|
||||
critical: 'text-red-500 stroke-red-500',
|
||||
overtime: 'text-red-600 stroke-red-600 animate-pulse',
|
||||
}
|
||||
|
||||
export const TIMER_BG_COLORS = {
|
||||
plenty: 'bg-green-500/10',
|
||||
warning: 'bg-amber-500/10',
|
||||
critical: 'bg-red-500/10',
|
||||
overtime: 'bg-red-600/20',
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Keyboard Shortcuts
|
||||
// ============================================================================
|
||||
|
||||
export const KEYBOARD_SHORTCUTS = {
|
||||
PAUSE_RESUME: ' ', // Spacebar
|
||||
EXTEND_5MIN: 'e',
|
||||
NEXT_PHASE: 'n',
|
||||
CLOSE_MODAL: 'Escape',
|
||||
SHOW_HELP: '?',
|
||||
} as const
|
||||
|
||||
export const KEYBOARD_SHORTCUT_DESCRIPTIONS: Record<string, string> = {
|
||||
' ': 'Pause/Fortsetzen',
|
||||
'e': '+5 Minuten',
|
||||
'n': 'Naechste Phase',
|
||||
'Escape': 'Modal schliessen',
|
||||
'?': 'Hilfe anzeigen',
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Default Settings
|
||||
// ============================================================================
|
||||
|
||||
export const DEFAULT_TEACHER_SETTINGS: TeacherSettings = {
|
||||
defaultPhaseDurations: DEFAULT_PHASE_DURATIONS,
|
||||
preferredLessonLength: DEFAULT_LESSON_LENGTH,
|
||||
autoAdvancePhases: true,
|
||||
soundNotifications: true,
|
||||
showKeyboardShortcuts: true,
|
||||
highContrastMode: false,
|
||||
onboardingCompleted: false,
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// System Templates
|
||||
// ============================================================================
|
||||
|
||||
export const SYSTEM_TEMPLATES = [
|
||||
{
|
||||
templateId: 'standard-45',
|
||||
name: 'Standard (45 Min)',
|
||||
description: 'Klassische Unterrichtsstunde',
|
||||
durations: DEFAULT_PHASE_DURATIONS,
|
||||
isSystemTemplate: true,
|
||||
},
|
||||
{
|
||||
templateId: 'double-90',
|
||||
name: 'Doppelstunde (90 Min)',
|
||||
description: 'Fuer laengere Arbeitsphasen',
|
||||
durations: {
|
||||
einstieg: 10,
|
||||
erarbeitung: 45,
|
||||
sicherung: 15,
|
||||
transfer: 12,
|
||||
reflexion: 8,
|
||||
},
|
||||
isSystemTemplate: true,
|
||||
},
|
||||
{
|
||||
templateId: 'math-focused',
|
||||
name: 'Mathematik-fokussiert',
|
||||
description: 'Lange Erarbeitung und Sicherung',
|
||||
durations: {
|
||||
einstieg: 5,
|
||||
erarbeitung: 25,
|
||||
sicherung: 10,
|
||||
transfer: 5,
|
||||
reflexion: 5,
|
||||
},
|
||||
isSystemTemplate: true,
|
||||
},
|
||||
{
|
||||
templateId: 'language-practice',
|
||||
name: 'Sprachpraxis',
|
||||
description: 'Betont kommunikative Phasen',
|
||||
durations: {
|
||||
einstieg: 10,
|
||||
erarbeitung: 15,
|
||||
sicherung: 8,
|
||||
transfer: 10,
|
||||
reflexion: 7,
|
||||
},
|
||||
isSystemTemplate: true,
|
||||
},
|
||||
]
|
||||
|
||||
// ============================================================================
|
||||
// Suggestion Icons (Lucide icon names)
|
||||
// ============================================================================
|
||||
|
||||
export const SUGGESTION_ICONS = {
|
||||
grading: 'ClipboardCheck',
|
||||
homework: 'BookOpen',
|
||||
planning: 'Calendar',
|
||||
meeting: 'Users',
|
||||
deadline: 'Clock',
|
||||
material: 'FileText',
|
||||
communication: 'MessageSquare',
|
||||
default: 'Lightbulb',
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Priority Colors
|
||||
// ============================================================================
|
||||
|
||||
export const PRIORITY_COLORS = {
|
||||
urgent: {
|
||||
bg: 'bg-red-100',
|
||||
text: 'text-red-700',
|
||||
border: 'border-red-200',
|
||||
dot: 'bg-red-500',
|
||||
},
|
||||
high: {
|
||||
bg: 'bg-orange-100',
|
||||
text: 'text-orange-700',
|
||||
border: 'border-orange-200',
|
||||
dot: 'bg-orange-500',
|
||||
},
|
||||
medium: {
|
||||
bg: 'bg-yellow-100',
|
||||
text: 'text-yellow-700',
|
||||
border: 'border-yellow-200',
|
||||
dot: 'bg-yellow-500',
|
||||
},
|
||||
low: {
|
||||
bg: 'bg-slate-100',
|
||||
text: 'text-slate-700',
|
||||
border: 'border-slate-200',
|
||||
dot: 'bg-slate-400',
|
||||
},
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Event Type Icons & Colors
|
||||
// ============================================================================
|
||||
|
||||
export const EVENT_TYPE_CONFIG = {
|
||||
exam: {
|
||||
icon: 'FileQuestion',
|
||||
color: 'text-red-600',
|
||||
bg: 'bg-red-50',
|
||||
},
|
||||
parent_meeting: {
|
||||
icon: 'Users',
|
||||
color: 'text-blue-600',
|
||||
bg: 'bg-blue-50',
|
||||
},
|
||||
deadline: {
|
||||
icon: 'Clock',
|
||||
color: 'text-amber-600',
|
||||
bg: 'bg-amber-50',
|
||||
},
|
||||
other: {
|
||||
icon: 'Calendar',
|
||||
color: 'text-slate-600',
|
||||
bg: 'bg-slate-50',
|
||||
},
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Storage Keys
|
||||
// ============================================================================
|
||||
|
||||
export const STORAGE_KEYS = {
|
||||
SETTINGS: 'companion_settings',
|
||||
CURRENT_SESSION: 'companion_current_session',
|
||||
ONBOARDING_STATE: 'companion_onboarding',
|
||||
CUSTOM_TEMPLATES: 'companion_custom_templates',
|
||||
LAST_MODE: 'companion_last_mode',
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// API Endpoints (relative to backend)
|
||||
// ============================================================================
|
||||
|
||||
export const API_ENDPOINTS = {
|
||||
DASHBOARD: '/api/state/dashboard',
|
||||
LESSON_START: '/api/classroom/sessions',
|
||||
LESSON_UPDATE: '/api/classroom/sessions', // + /{id}
|
||||
TEMPLATES: '/api/classroom/templates',
|
||||
SETTINGS: '/api/teacher/settings',
|
||||
FEEDBACK: '/api/feedback',
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Helper Functions
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Create default phases array from durations
|
||||
*/
|
||||
export function createDefaultPhases(durations: PhaseDurations = DEFAULT_PHASE_DURATIONS): Phase[] {
|
||||
return PHASE_ORDER.map((phaseId, index) => ({
|
||||
id: phaseId,
|
||||
shortName: PHASE_SHORT_NAMES[phaseId],
|
||||
displayName: PHASE_DISPLAY_NAMES[phaseId],
|
||||
duration: durations[phaseId],
|
||||
status: index === 0 ? 'active' : 'planned',
|
||||
color: PHASE_COLORS[phaseId].hex,
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate total duration from phase durations
|
||||
*/
|
||||
export function calculateTotalDuration(durations: PhaseDurations): number {
|
||||
return Object.values(durations).reduce((sum, d) => sum + d, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get timer color status based on remaining time
|
||||
*/
|
||||
export function getTimerColorStatus(
|
||||
remainingSeconds: number,
|
||||
isOvertime: boolean
|
||||
): 'plenty' | 'warning' | 'critical' | 'overtime' {
|
||||
if (isOvertime) return 'overtime'
|
||||
if (remainingSeconds <= TIMER_CRITICAL_THRESHOLD) return 'critical'
|
||||
if (remainingSeconds <= TIMER_WARNING_THRESHOLD) return 'warning'
|
||||
return 'plenty'
|
||||
}
|
||||
|
||||
/**
|
||||
* Format seconds as MM:SS
|
||||
*/
|
||||
export function formatTime(seconds: number): string {
|
||||
const absSeconds = Math.abs(seconds)
|
||||
const mins = Math.floor(absSeconds / 60)
|
||||
const secs = absSeconds % 60
|
||||
const sign = seconds < 0 ? '-' : ''
|
||||
return `${sign}${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Format minutes as "X Min"
|
||||
*/
|
||||
export function formatMinutes(minutes: number): string {
|
||||
return `${minutes} Min`
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './types'
|
||||
export * from './constants'
|
||||
@@ -1,329 +0,0 @@
|
||||
/**
|
||||
* 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<string, unknown>
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 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<T> {
|
||||
success: boolean
|
||||
data?: T
|
||||
error?: string
|
||||
message?: string
|
||||
}
|
||||
|
||||
export interface DashboardResponse extends APIResponse<CompanionData> {}
|
||||
|
||||
export interface LessonResponse extends APIResponse<LessonSession> {}
|
||||
|
||||
export interface TemplatesResponse extends APIResponse<{ templates: LessonTemplate[] }> {}
|
||||
|
||||
export interface SettingsResponse extends APIResponse<TeacherSettings> {}
|
||||
|
||||
// ============================================================================
|
||||
// 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
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/**
|
||||
* Website Content Type Definitions
|
||||
*
|
||||
* Types for website content (no server-side imports)
|
||||
*/
|
||||
|
||||
export interface HeroContent {
|
||||
badge: string
|
||||
title: string
|
||||
titleHighlight1: string
|
||||
titleHighlight2: string
|
||||
subtitle: string
|
||||
ctaPrimary: string
|
||||
ctaSecondary: string
|
||||
ctaHint: string
|
||||
}
|
||||
|
||||
export interface FeatureContent {
|
||||
id: string
|
||||
icon: string
|
||||
title: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface FAQItem {
|
||||
question: string
|
||||
answer: string[]
|
||||
}
|
||||
|
||||
export interface PricingPlan {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
price: number
|
||||
currency: string
|
||||
interval: string
|
||||
popular?: boolean
|
||||
features: {
|
||||
tasks: string
|
||||
taskDescription: string
|
||||
included: string[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface WebsiteContent {
|
||||
hero: HeroContent
|
||||
features: FeatureContent[]
|
||||
faq: FAQItem[]
|
||||
pricing: PricingPlan[]
|
||||
trust: {
|
||||
item1: { value: string; label: string }
|
||||
item2: { value: string; label: string }
|
||||
item3: { value: string; label: string }
|
||||
}
|
||||
testimonial: {
|
||||
quote: string
|
||||
author: string
|
||||
role: string
|
||||
}
|
||||
}
|
||||
@@ -1,284 +0,0 @@
|
||||
/**
|
||||
* Website Content Management (Server-only)
|
||||
*
|
||||
* Loads website texts from JSON files.
|
||||
* Admin can edit texts via /development/content
|
||||
*
|
||||
* IMPORTANT: This file may only be used in Server Components!
|
||||
* For Client Components use @/lib/content-types
|
||||
*/
|
||||
|
||||
import { readFileSync, writeFileSync, existsSync, mkdirSync, accessSync, constants } from 'fs'
|
||||
import { join, dirname } from 'path'
|
||||
|
||||
// Re-export types from content-types for backward compatibility
|
||||
export type {
|
||||
HeroContent,
|
||||
FeatureContent,
|
||||
FAQItem,
|
||||
PricingPlan,
|
||||
WebsiteContent,
|
||||
} from './content-types'
|
||||
|
||||
import type { WebsiteContent } from './content-types'
|
||||
|
||||
// Content directory - use environment variable or relative path
|
||||
function getContentDir(): string {
|
||||
// Check environment variable first
|
||||
if (process.env.CONTENT_DIR) {
|
||||
return process.env.CONTENT_DIR
|
||||
}
|
||||
|
||||
// Try various possible paths
|
||||
const possiblePaths = [
|
||||
join(process.cwd(), 'content'), // Standard: CWD/content
|
||||
join(process.cwd(), 'admin-v2', 'content'), // If CWD is project root
|
||||
'/app/content', // Docker container
|
||||
join(dirname(__filename), '..', 'content'), // Relative to this file
|
||||
]
|
||||
|
||||
// Check if any path exists and is writable
|
||||
for (const path of possiblePaths) {
|
||||
try {
|
||||
if (existsSync(path)) {
|
||||
accessSync(path, constants.W_OK)
|
||||
return path
|
||||
}
|
||||
} catch {
|
||||
// Path not writable, try next
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: Create in CWD
|
||||
const fallbackPath = join(process.cwd(), 'content')
|
||||
try {
|
||||
mkdirSync(fallbackPath, { recursive: true, mode: 0o755 })
|
||||
console.log(`[Content] Created content directory at: ${fallbackPath}`)
|
||||
} catch (err) {
|
||||
console.error(`[Content] Failed to create content directory: ${err}`)
|
||||
}
|
||||
return fallbackPath
|
||||
}
|
||||
|
||||
const CONTENT_DIR = getContentDir()
|
||||
|
||||
// Default Content
|
||||
const defaultContent: WebsiteContent = {
|
||||
hero: {
|
||||
badge: 'Entwickelt fuer deutsche Lehrkraefte',
|
||||
title: 'Korrigieren Sie',
|
||||
titleHighlight1: 'schneller',
|
||||
titleHighlight2: 'besser',
|
||||
subtitle: 'BreakPilot unterstuetzt Lehrkraefte mit intelligenter KI bei der Bewertung von Aufgaben. Sparen Sie bis zu 50% Ihrer Korrekturzeit und geben Sie besseres Feedback.',
|
||||
ctaPrimary: '7 Tage kostenlos testen',
|
||||
ctaSecondary: 'Mehr erfahren',
|
||||
ctaHint: 'Keine Kreditkarte fuer den Start erforderlich',
|
||||
},
|
||||
features: [
|
||||
{
|
||||
id: 'ai-correction',
|
||||
icon: '✍️',
|
||||
title: 'KI-gestuetzte Korrektur',
|
||||
description: 'Intelligente Analyse von Schuelerantworten mit Verbesserungsvorschlaegen und automatischer Bewertung nach Ihren Kriterien.',
|
||||
},
|
||||
{
|
||||
id: 'templates',
|
||||
icon: '📋',
|
||||
title: 'Dokumentvorlagen',
|
||||
description: 'Erstellen und verwalten Sie Ihre eigenen Arbeitsblatt-Vorlagen. Wiederverwendbar fuer verschiedene Klassen und Jahrgaenge.',
|
||||
},
|
||||
{
|
||||
id: 'analytics',
|
||||
icon: '📊',
|
||||
title: 'Fortschrittsanalyse',
|
||||
description: 'Verfolgen Sie die Entwicklung Ihrer Schueler ueber Zeit. Erkennen Sie Staerken und Schwaechen fruehzeitig.',
|
||||
},
|
||||
{
|
||||
id: 'gdpr',
|
||||
icon: '🔒',
|
||||
title: 'DSGVO-konform',
|
||||
description: 'Hosting in Deutschland, volle Datenschutzkonformitaet. Ihre Daten und die Ihrer Schueler sind sicher.',
|
||||
},
|
||||
{
|
||||
id: 'team',
|
||||
icon: '👥',
|
||||
title: 'Team-Funktionen',
|
||||
description: 'Arbeiten Sie im Fachbereich zusammen. Teilen Sie Vorlagen, Bewertungskriterien und Best Practices.',
|
||||
},
|
||||
{
|
||||
id: 'mobile',
|
||||
icon: '📱',
|
||||
title: 'Ueberall verfuegbar',
|
||||
description: 'Browserbasiert und responsive. Funktioniert auf Desktop, Tablet und Smartphone - ohne Installation.',
|
||||
},
|
||||
],
|
||||
faq: [
|
||||
{
|
||||
question: 'Was ist bei Breakpilot eine „Aufgabe"?',
|
||||
answer: [
|
||||
'Eine Aufgabe ist ein abgeschlossener Arbeitsauftrag, den du mit Breakpilot erledigst.',
|
||||
'Typische Beispiele:',
|
||||
'• eine Klassenarbeit korrigieren (egal wie viele Seiten)',
|
||||
'• mehrere Klassenarbeiten in einer Serie korrigieren',
|
||||
'• einen Elternbrief erstellen',
|
||||
'Wichtig: Die Anzahl der Seiten, Dateien oder Uploads spielt dabei keine Rolle.',
|
||||
],
|
||||
},
|
||||
{
|
||||
question: 'Kann ich Breakpilot kostenlos testen?',
|
||||
answer: [
|
||||
'Ja.',
|
||||
'• Du kannst Breakpilot 7 Tage kostenlos testen',
|
||||
'• Dafuer ist eine Kreditkarte erforderlich',
|
||||
'• Wenn du innerhalb der Testphase kuendigst, entstehen keine Kosten',
|
||||
],
|
||||
},
|
||||
{
|
||||
question: 'Werden meine Daten fuer KI-Training verwendet?',
|
||||
answer: [
|
||||
'Nein.',
|
||||
'• Deine Inhalte werden nicht fuer das Training oeffentlicher KI-Modelle genutzt',
|
||||
'• Die Verarbeitung erfolgt DSGVO-konform',
|
||||
'• Deine Daten bleiben unter deiner Kontrolle',
|
||||
],
|
||||
},
|
||||
{
|
||||
question: 'Kann ich meinen Tarif jederzeit aendern oder kuendigen?',
|
||||
answer: [
|
||||
'Ja.',
|
||||
'• Upgrades sind jederzeit moeglich',
|
||||
'• Downgrades greifen zum naechsten Abrechnungszeitraum',
|
||||
'• Kuendigungen sind jederzeit moeglich',
|
||||
],
|
||||
},
|
||||
],
|
||||
pricing: [
|
||||
{
|
||||
id: 'basic',
|
||||
name: 'Basic',
|
||||
description: 'Perfekt fuer den Einstieg',
|
||||
price: 9.90,
|
||||
currency: 'EUR',
|
||||
interval: 'Monat',
|
||||
features: {
|
||||
tasks: '30 Aufgaben',
|
||||
taskDescription: 'pro Monat',
|
||||
included: [
|
||||
'KI-gestuetzte Korrektur',
|
||||
'Basis-Dokumentvorlagen',
|
||||
'E-Mail Support',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'standard',
|
||||
name: 'Standard',
|
||||
description: 'Fuer regelmaessige Nutzer',
|
||||
price: 19.90,
|
||||
currency: 'EUR',
|
||||
interval: 'Monat',
|
||||
popular: true,
|
||||
features: {
|
||||
tasks: '100 Aufgaben',
|
||||
taskDescription: 'pro Monat',
|
||||
included: [
|
||||
'Alles aus Basic',
|
||||
'Eigene Vorlagen erstellen',
|
||||
'Batch-Verarbeitung',
|
||||
'Bis zu 3 Teammitglieder',
|
||||
'Prioritaets-Support',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'premium',
|
||||
name: 'Premium',
|
||||
description: 'Sorglos-Tarif fuer Vielnutzer',
|
||||
price: 39.90,
|
||||
currency: 'EUR',
|
||||
interval: 'Monat',
|
||||
features: {
|
||||
tasks: 'Unbegrenzt',
|
||||
taskDescription: 'Fair Use',
|
||||
included: [
|
||||
'Alles aus Standard',
|
||||
'Unbegrenzte Aufgaben (Fair Use)',
|
||||
'Bis zu 10 Teammitglieder',
|
||||
'Admin-Panel & Audit-Log',
|
||||
'API-Zugang',
|
||||
'Eigenes Branding',
|
||||
'Dedizierter Support',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
trust: {
|
||||
item1: { value: 'DSGVO', label: 'Konform & sicher' },
|
||||
item2: { value: '7 Tage', label: 'Kostenlos testen' },
|
||||
item3: { value: '100%', label: 'Made in Germany' },
|
||||
},
|
||||
testimonial: {
|
||||
quote: 'BreakPilot hat meine Korrekturzeit halbiert. Ich habe endlich wieder Zeit fuer das Wesentliche: meine Schueler.',
|
||||
author: 'Maria S.',
|
||||
role: 'Deutschlehrerin, Gymnasium',
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads content from JSON file or returns default
|
||||
*/
|
||||
export function getContent(): WebsiteContent {
|
||||
const contentPath = join(CONTENT_DIR, 'website.json')
|
||||
|
||||
try {
|
||||
if (existsSync(contentPath)) {
|
||||
const fileContent = readFileSync(contentPath, 'utf-8')
|
||||
return JSON.parse(fileContent) as WebsiteContent
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading content:', error)
|
||||
}
|
||||
|
||||
return defaultContent
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves content to JSON file
|
||||
* @returns Object with success status and optional error message
|
||||
*/
|
||||
export function saveContent(content: WebsiteContent): { success: boolean; error?: string } {
|
||||
const contentPath = join(CONTENT_DIR, 'website.json')
|
||||
|
||||
try {
|
||||
// Ensure directory exists
|
||||
if (!existsSync(CONTENT_DIR)) {
|
||||
console.log(`[Content] Creating directory: ${CONTENT_DIR}`)
|
||||
mkdirSync(CONTENT_DIR, { recursive: true, mode: 0o755 })
|
||||
}
|
||||
|
||||
// Check write permissions
|
||||
try {
|
||||
accessSync(CONTENT_DIR, constants.W_OK)
|
||||
} catch {
|
||||
const error = `Directory not writable: ${CONTENT_DIR}`
|
||||
console.error(`[Content] ${error}`)
|
||||
return { success: false, error }
|
||||
}
|
||||
|
||||
// Write file
|
||||
writeFileSync(contentPath, JSON.stringify(content, null, 2), 'utf-8')
|
||||
console.log(`[Content] Saved successfully to: ${contentPath}`)
|
||||
return { success: true }
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
|
||||
console.error(`[Content] Error saving: ${errorMessage}`)
|
||||
return { success: false, error: errorMessage }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns default content (for reset)
|
||||
*/
|
||||
export function getDefaultContent(): WebsiteContent {
|
||||
return defaultContent
|
||||
}
|
||||
@@ -1,494 +0,0 @@
|
||||
/**
|
||||
* Module Registry - Track all backend modules and their frontend connections
|
||||
*
|
||||
* This registry ensures no backend modules get lost during migration.
|
||||
* Each module entry defines:
|
||||
* - Backend service and endpoints
|
||||
* - Frontend pages that use it
|
||||
* - Connection status (connected, partial, not connected)
|
||||
*/
|
||||
|
||||
export interface BackendModule {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
category: 'compliance' | 'ai' | 'infrastructure' | 'communication' | 'development'
|
||||
backend: {
|
||||
service: string // e.g. 'consent-service', 'python-backend', 'klausur-service'
|
||||
port: number
|
||||
basePath: string
|
||||
endpoints: {
|
||||
path: string
|
||||
method: string
|
||||
description: string
|
||||
}[]
|
||||
}
|
||||
frontend: {
|
||||
adminV2Page?: string // New admin-v2 page path
|
||||
oldAdminPage?: string // Old admin page path (for reference)
|
||||
status: 'connected' | 'partial' | 'not-connected' | 'deprecated'
|
||||
}
|
||||
dependencies?: string[] // IDs of other modules this depends on
|
||||
priority: 'critical' | 'high' | 'medium' | 'low'
|
||||
notes?: string
|
||||
}
|
||||
|
||||
export const MODULE_REGISTRY: BackendModule[] = [
|
||||
// ===========================================
|
||||
// COMPLIANCE MODULES
|
||||
// ===========================================
|
||||
{
|
||||
id: 'consent-documents',
|
||||
name: 'Consent Dokumente',
|
||||
description: 'Verwaltung rechtlicher Dokumente (AGB, Datenschutz, etc.)',
|
||||
category: 'compliance',
|
||||
backend: {
|
||||
service: 'consent-service',
|
||||
port: 8081,
|
||||
basePath: '/api/consent/admin',
|
||||
endpoints: [
|
||||
{ path: '/documents', method: 'GET', description: 'Liste aller Dokumente' },
|
||||
{ path: '/documents', method: 'POST', description: 'Dokument erstellen' },
|
||||
{ path: '/documents/{id}', method: 'GET', description: 'Dokument Details' },
|
||||
{ path: '/documents/{id}', method: 'PUT', description: 'Dokument aktualisieren' },
|
||||
{ path: '/documents/{id}', method: 'DELETE', description: 'Dokument loeschen' },
|
||||
]
|
||||
},
|
||||
frontend: {
|
||||
adminV2Page: '/sdk/consent-management',
|
||||
oldAdminPage: '/admin/consent',
|
||||
status: 'connected'
|
||||
},
|
||||
priority: 'critical'
|
||||
},
|
||||
{
|
||||
id: 'consent-versions',
|
||||
name: 'Dokument-Versionierung',
|
||||
description: 'Versionsverwaltung und Freigabe-Workflow fuer rechtliche Dokumente',
|
||||
category: 'compliance',
|
||||
backend: {
|
||||
service: 'consent-service',
|
||||
port: 8081,
|
||||
basePath: '/api/consent/admin',
|
||||
endpoints: [
|
||||
{ path: '/documents/{id}/versions', method: 'GET', description: 'Versionen eines Dokuments' },
|
||||
{ path: '/versions', method: 'POST', description: 'Neue Version erstellen' },
|
||||
{ path: '/versions/{id}', method: 'PUT', description: 'Version aktualisieren' },
|
||||
{ path: '/versions/{id}', method: 'DELETE', description: 'Version loeschen' },
|
||||
{ path: '/versions/{id}/submit-review', method: 'POST', description: 'Zur Pruefung einreichen' },
|
||||
{ path: '/versions/{id}/approve', method: 'POST', description: 'Version genehmigen' },
|
||||
{ path: '/versions/{id}/reject', method: 'POST', description: 'Version ablehnen' },
|
||||
{ path: '/versions/{id}/publish', method: 'POST', description: 'Version veroeffentlichen' },
|
||||
{ path: '/versions/{id}/approval-history', method: 'GET', description: 'Genehmigungsverlauf' },
|
||||
{ path: '/versions/upload-word', method: 'POST', description: 'Word-Dokument importieren' },
|
||||
]
|
||||
},
|
||||
frontend: {
|
||||
adminV2Page: '/sdk/workflow',
|
||||
oldAdminPage: '/admin/consent (Versions Tab)',
|
||||
status: 'connected'
|
||||
},
|
||||
dependencies: ['consent-documents'],
|
||||
priority: 'critical'
|
||||
},
|
||||
{
|
||||
id: 'consent-user',
|
||||
name: 'Nutzer-Einwilligungen',
|
||||
description: 'Tracking von Nutzer-Einwilligungen fuer DSGVO-Compliance',
|
||||
category: 'compliance',
|
||||
backend: {
|
||||
service: 'consent-service',
|
||||
port: 8081,
|
||||
basePath: '/api/consent',
|
||||
endpoints: [
|
||||
{ path: '/status', method: 'GET', description: 'Einwilligungsstatus pruefen' },
|
||||
{ path: '/give', method: 'POST', description: 'Einwilligung erteilen' },
|
||||
{ path: '/withdraw', method: 'POST', description: 'Einwilligung widerrufen' },
|
||||
{ path: '/history', method: 'GET', description: 'Einwilligungshistorie' },
|
||||
]
|
||||
},
|
||||
frontend: {
|
||||
adminV2Page: '/sdk/einwilligungen',
|
||||
oldAdminPage: '/admin/consent (Users Tab)',
|
||||
status: 'connected',
|
||||
},
|
||||
priority: 'critical',
|
||||
},
|
||||
{
|
||||
id: 'dsr-requests',
|
||||
name: 'Datenschutzanfragen (DSR)',
|
||||
description: 'DSGVO Art. 15-21 Anfragen verwalten',
|
||||
category: 'compliance',
|
||||
backend: {
|
||||
service: 'python-backend',
|
||||
port: 8000,
|
||||
basePath: '/api/dsr',
|
||||
endpoints: [
|
||||
{ path: '/requests', method: 'GET', description: 'Alle DSR-Anfragen' },
|
||||
{ path: '/requests', method: 'POST', description: 'Neue Anfrage erstellen' },
|
||||
{ path: '/requests/{id}', method: 'GET', description: 'Anfrage-Details' },
|
||||
{ path: '/requests/{id}/process', method: 'POST', description: 'Anfrage bearbeiten' },
|
||||
{ path: '/requests/{id}/export', method: 'GET', description: 'Daten exportieren' },
|
||||
]
|
||||
},
|
||||
frontend: {
|
||||
adminV2Page: '/sdk/dsr',
|
||||
oldAdminPage: '/admin/dsr',
|
||||
status: 'connected'
|
||||
},
|
||||
priority: 'high',
|
||||
},
|
||||
{
|
||||
id: 'dsms',
|
||||
name: 'Datenschutz-Management-System',
|
||||
description: 'Zentrales DSMS fuer Dokumentation und Compliance',
|
||||
category: 'compliance',
|
||||
backend: {
|
||||
service: 'python-backend',
|
||||
port: 8000,
|
||||
basePath: '/api/dsms',
|
||||
endpoints: [
|
||||
{ path: '/documents', method: 'GET', description: 'DSMS-Dokumente' },
|
||||
{ path: '/processes', method: 'GET', description: 'Verarbeitungsverzeichnis' },
|
||||
{ path: '/toms', method: 'GET', description: 'TOM-Katalog' },
|
||||
{ path: '/audits', method: 'GET', description: 'Audit-Historie' },
|
||||
]
|
||||
},
|
||||
frontend: {
|
||||
adminV2Page: '/sdk/dsms',
|
||||
oldAdminPage: '/admin/dsms',
|
||||
status: 'connected'
|
||||
},
|
||||
priority: 'medium'
|
||||
},
|
||||
{
|
||||
id: 'cookie-categories',
|
||||
name: 'Cookie-Kategorien',
|
||||
description: 'Verwaltung von Cookie-Kategorien fuer Consent Banner',
|
||||
category: 'compliance',
|
||||
backend: {
|
||||
service: 'consent-service',
|
||||
port: 8081,
|
||||
basePath: '/api/consent/admin',
|
||||
endpoints: [
|
||||
{ path: '/cookies/categories', method: 'GET', description: 'Alle Cookie-Kategorien' },
|
||||
{ path: '/cookies/categories', method: 'POST', description: 'Kategorie erstellen' },
|
||||
{ path: '/cookies/categories/{id}', method: 'PUT', description: 'Kategorie aktualisieren' },
|
||||
{ path: '/cookies/categories/{id}', method: 'DELETE', description: 'Kategorie loeschen' },
|
||||
]
|
||||
},
|
||||
frontend: {
|
||||
adminV2Page: undefined,
|
||||
oldAdminPage: '/admin/consent (Cookies Tab)',
|
||||
status: 'not-connected'
|
||||
},
|
||||
priority: 'medium',
|
||||
notes: 'Cookie-Kategorien Tab im alten Admin vorhanden'
|
||||
},
|
||||
|
||||
// ===========================================
|
||||
// AI MODULES
|
||||
// ===========================================
|
||||
{
|
||||
id: 'ai-agents',
|
||||
name: 'AI Agents',
|
||||
description: 'Multi-Agent System Verwaltung und Monitoring',
|
||||
category: 'ai',
|
||||
backend: {
|
||||
service: 'voice-service',
|
||||
port: 8088,
|
||||
basePath: '/api/v1/agents',
|
||||
endpoints: [
|
||||
{ path: '/sessions', method: 'GET', description: 'Agent-Sessions' },
|
||||
{ path: '/statistics', method: 'GET', description: 'Agent-Statistiken' },
|
||||
{ path: '/{agentId}', method: 'GET', description: 'Agent-Details' },
|
||||
{ path: '/{agentId}/soul', method: 'GET', description: 'SOUL-Konfiguration' },
|
||||
]
|
||||
},
|
||||
frontend: {
|
||||
adminV2Page: '/ai/agents',
|
||||
oldAdminPage: undefined,
|
||||
status: 'connected'
|
||||
},
|
||||
priority: 'high',
|
||||
notes: 'Neues Multi-Agent System'
|
||||
},
|
||||
{
|
||||
id: 'ai-quality',
|
||||
name: 'AI Quality (BQAS)',
|
||||
description: 'KI-Qualitaetssicherung und Evaluierung',
|
||||
category: 'ai',
|
||||
backend: {
|
||||
service: 'voice-service',
|
||||
port: 8088,
|
||||
basePath: '/api/bqas',
|
||||
endpoints: [
|
||||
{ path: '/evaluate', method: 'POST', description: 'Antwort evaluieren' },
|
||||
{ path: '/metrics', method: 'GET', description: 'Qualitaetsmetriken' },
|
||||
]
|
||||
},
|
||||
frontend: {
|
||||
adminV2Page: '/ai/quality',
|
||||
oldAdminPage: '/admin/quality',
|
||||
status: 'connected'
|
||||
},
|
||||
priority: 'high'
|
||||
},
|
||||
{
|
||||
id: 'llm-compare',
|
||||
name: 'LLM Vergleich',
|
||||
description: 'Vergleich verschiedener KI-Modelle und Provider',
|
||||
category: 'ai',
|
||||
backend: {
|
||||
service: 'python-backend',
|
||||
port: 8000,
|
||||
basePath: '/api/llm',
|
||||
endpoints: [
|
||||
{ path: '/providers', method: 'GET', description: 'Verfuegbare Provider' },
|
||||
{ path: '/compare', method: 'POST', description: 'Modelle vergleichen' },
|
||||
{ path: '/benchmark', method: 'POST', description: 'Benchmark ausfuehren' },
|
||||
]
|
||||
},
|
||||
frontend: {
|
||||
adminV2Page: '/ai/llm-compare',
|
||||
oldAdminPage: '/admin/llm-compare',
|
||||
status: 'connected'
|
||||
},
|
||||
priority: 'medium'
|
||||
},
|
||||
{
|
||||
id: 'rag-management',
|
||||
name: 'RAG & Daten',
|
||||
description: 'Retrieval Augmented Generation und Training Data',
|
||||
category: 'ai',
|
||||
backend: {
|
||||
service: 'python-backend',
|
||||
port: 8000,
|
||||
basePath: '/api/rag',
|
||||
endpoints: [
|
||||
{ path: '/documents', method: 'GET', description: 'RAG-Dokumente' },
|
||||
{ path: '/collections', method: 'GET', description: 'Vector-Collections' },
|
||||
{ path: '/query', method: 'POST', description: 'RAG-Abfrage' },
|
||||
]
|
||||
},
|
||||
frontend: {
|
||||
adminV2Page: '/ai/rag',
|
||||
oldAdminPage: '/admin/rag',
|
||||
status: 'connected'
|
||||
},
|
||||
priority: 'medium'
|
||||
},
|
||||
|
||||
// ===========================================
|
||||
// INFRASTRUCTURE MODULES
|
||||
// ===========================================
|
||||
{
|
||||
id: 'security-dashboard',
|
||||
name: 'Security Dashboard',
|
||||
description: 'DevSecOps Dashboard und Vulnerability Scans',
|
||||
category: 'infrastructure',
|
||||
backend: {
|
||||
service: 'python-backend',
|
||||
port: 8000,
|
||||
basePath: '/api/security',
|
||||
endpoints: [
|
||||
{ path: '/scans', method: 'GET', description: 'Security-Scans' },
|
||||
{ path: '/vulnerabilities', method: 'GET', description: 'Schwachstellen' },
|
||||
{ path: '/compliance', method: 'GET', description: 'Compliance-Status' },
|
||||
]
|
||||
},
|
||||
frontend: {
|
||||
adminV2Page: '/infrastructure/security',
|
||||
oldAdminPage: '/admin/security',
|
||||
status: 'connected'
|
||||
},
|
||||
priority: 'high'
|
||||
},
|
||||
{
|
||||
id: 'sbom',
|
||||
name: 'SBOM',
|
||||
description: 'Software Bill of Materials',
|
||||
category: 'infrastructure',
|
||||
backend: {
|
||||
service: 'python-backend',
|
||||
port: 8000,
|
||||
basePath: '/api/sbom',
|
||||
endpoints: [
|
||||
{ path: '/components', method: 'GET', description: 'Komponenten-Liste' },
|
||||
{ path: '/licenses', method: 'GET', description: 'Lizenz-Uebersicht' },
|
||||
{ path: '/export', method: 'GET', description: 'SBOM exportieren' },
|
||||
]
|
||||
},
|
||||
frontend: {
|
||||
adminV2Page: '/infrastructure/sbom',
|
||||
oldAdminPage: '/admin/sbom',
|
||||
status: 'connected'
|
||||
},
|
||||
priority: 'medium'
|
||||
},
|
||||
|
||||
{
|
||||
id: 'middleware',
|
||||
name: 'Middleware Manager',
|
||||
description: 'Verwaltung und Monitoring der Backend-Middleware',
|
||||
category: 'infrastructure',
|
||||
backend: {
|
||||
service: 'python-backend',
|
||||
port: 8000,
|
||||
basePath: '/api/middleware',
|
||||
endpoints: [
|
||||
{ path: '/status', method: 'GET', description: 'Middleware-Status' },
|
||||
{ path: '/config', method: 'GET', description: 'Konfiguration' },
|
||||
]
|
||||
},
|
||||
frontend: {
|
||||
adminV2Page: '/infrastructure/middleware',
|
||||
oldAdminPage: '/admin/middleware',
|
||||
status: 'connected'
|
||||
},
|
||||
priority: 'medium'
|
||||
},
|
||||
{
|
||||
id: 'ci-cd',
|
||||
name: 'CI/CD Pipeline',
|
||||
description: 'Build-Pipeline und Deployment-Management',
|
||||
category: 'infrastructure',
|
||||
backend: {
|
||||
service: 'python-backend',
|
||||
port: 8000,
|
||||
basePath: '/api/builds',
|
||||
endpoints: [
|
||||
{ path: '/pipelines', method: 'GET', description: 'Pipeline-Status' },
|
||||
{ path: '/builds', method: 'GET', description: 'Build-Historie' },
|
||||
]
|
||||
},
|
||||
frontend: {
|
||||
adminV2Page: '/infrastructure/ci-cd',
|
||||
oldAdminPage: '/admin/builds',
|
||||
status: 'connected'
|
||||
},
|
||||
priority: 'medium'
|
||||
},
|
||||
|
||||
// ===========================================
|
||||
// COMMUNICATION MODULES
|
||||
// ===========================================
|
||||
{
|
||||
id: 'alerts',
|
||||
name: 'Alerts & Benachrichtigungen',
|
||||
description: 'System-Benachrichtigungen und Alerts',
|
||||
category: 'communication',
|
||||
backend: {
|
||||
service: 'python-backend',
|
||||
port: 8000,
|
||||
basePath: '/api/alerts',
|
||||
endpoints: [
|
||||
{ path: '/notifications', method: 'GET', description: 'Benachrichtigungen' },
|
||||
{ path: '/alerts', method: 'GET', description: 'Aktive Alerts' },
|
||||
]
|
||||
},
|
||||
frontend: {
|
||||
adminV2Page: '/communication/alerts',
|
||||
oldAdminPage: '/admin/alerts',
|
||||
status: 'connected'
|
||||
},
|
||||
priority: 'medium'
|
||||
},
|
||||
{
|
||||
id: 'unified-inbox',
|
||||
name: 'Unified Inbox',
|
||||
description: 'E-Mail-Konten und KI-Analyse',
|
||||
category: 'communication',
|
||||
backend: {
|
||||
service: 'python-backend',
|
||||
port: 8000,
|
||||
basePath: '/api/mail',
|
||||
endpoints: [
|
||||
{ path: '/accounts', method: 'GET', description: 'E-Mail-Konten' },
|
||||
{ path: '/messages', method: 'GET', description: 'Nachrichten' },
|
||||
{ path: '/analyze', method: 'POST', description: 'KI-Analyse' },
|
||||
]
|
||||
},
|
||||
frontend: {
|
||||
adminV2Page: '/communication/mail',
|
||||
oldAdminPage: '/admin/mail',
|
||||
status: 'connected'
|
||||
},
|
||||
priority: 'low'
|
||||
},
|
||||
|
||||
// ===========================================
|
||||
// DEVELOPMENT MODULES
|
||||
// ===========================================
|
||||
{
|
||||
id: 'voice-service',
|
||||
name: 'Voice Service',
|
||||
description: 'Voice-First Interface',
|
||||
category: 'development',
|
||||
backend: {
|
||||
service: 'voice-service',
|
||||
port: 8088,
|
||||
basePath: '/api/voice',
|
||||
endpoints: [
|
||||
{ path: '/transcribe', method: 'POST', description: 'Sprache transkribieren' },
|
||||
{ path: '/synthesize', method: 'POST', description: 'Text zu Sprache' },
|
||||
]
|
||||
},
|
||||
frontend: {
|
||||
adminV2Page: '/development/voice',
|
||||
oldAdminPage: '/admin/voice',
|
||||
status: 'not-connected'
|
||||
},
|
||||
priority: 'low'
|
||||
},
|
||||
]
|
||||
|
||||
// Helper functions
|
||||
export function getModulesByCategory(category: BackendModule['category']): BackendModule[] {
|
||||
return MODULE_REGISTRY.filter(m => m.category === category)
|
||||
}
|
||||
|
||||
export function getConnectedModules(): BackendModule[] {
|
||||
return MODULE_REGISTRY.filter(m => m.frontend.status === 'connected')
|
||||
}
|
||||
|
||||
export function getNotConnectedModules(): BackendModule[] {
|
||||
return MODULE_REGISTRY.filter(m => m.frontend.status === 'not-connected')
|
||||
}
|
||||
|
||||
export function getPartialModules(): BackendModule[] {
|
||||
return MODULE_REGISTRY.filter(m => m.frontend.status === 'partial')
|
||||
}
|
||||
|
||||
export function getModuleStats() {
|
||||
const total = MODULE_REGISTRY.length
|
||||
const connected = MODULE_REGISTRY.filter(m => m.frontend.status === 'connected').length
|
||||
const partial = MODULE_REGISTRY.filter(m => m.frontend.status === 'partial').length
|
||||
const notConnected = MODULE_REGISTRY.filter(m => m.frontend.status === 'not-connected').length
|
||||
const deprecated = MODULE_REGISTRY.filter(m => m.frontend.status === 'deprecated').length
|
||||
|
||||
return {
|
||||
total,
|
||||
connected,
|
||||
partial,
|
||||
notConnected,
|
||||
deprecated,
|
||||
percentComplete: Math.round((connected / total) * 100)
|
||||
}
|
||||
}
|
||||
|
||||
export function getCategoryStats(category: BackendModule['category']) {
|
||||
const modules = getModulesByCategory(category)
|
||||
const total = modules.length
|
||||
const connected = modules.filter(m => m.frontend.status === 'connected').length
|
||||
const partial = modules.filter(m => m.frontend.status === 'partial').length
|
||||
const notConnected = modules.filter(m => m.frontend.status === 'not-connected').length
|
||||
|
||||
return {
|
||||
total,
|
||||
connected,
|
||||
partial,
|
||||
notConnected,
|
||||
percentComplete: total > 0 ? Math.round((connected / total) * 100) : 0
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
/**
|
||||
* Navigation Structure for Admin v2
|
||||
*
|
||||
* Main categories with color-coded modules.
|
||||
* All DSGVO and Compliance modules are now consolidated under the SDK.
|
||||
*/
|
||||
|
||||
export type CategoryId = 'ai'
|
||||
|
||||
export interface NavModule {
|
||||
id: string
|
||||
name: string
|
||||
href: string
|
||||
description: string
|
||||
purpose: string
|
||||
audience: string[]
|
||||
gdprArticles?: string[]
|
||||
oldAdminPath?: string // Reference to old admin for migration
|
||||
subgroup?: string // Optional subgroup for visual grouping in sidebar
|
||||
}
|
||||
|
||||
export interface NavCategory {
|
||||
id: CategoryId
|
||||
name: string
|
||||
icon: string
|
||||
color: string
|
||||
colorClass: string
|
||||
description: string
|
||||
modules: NavModule[]
|
||||
}
|
||||
|
||||
export const navigation: NavCategory[] = [
|
||||
// =========================================================================
|
||||
// KI & Automatisierung
|
||||
// =========================================================================
|
||||
{
|
||||
id: 'ai',
|
||||
name: 'KI & Automatisierung',
|
||||
icon: 'brain',
|
||||
color: '#14b8a6', // Teal
|
||||
colorClass: 'ai',
|
||||
description: 'LLM, OCR, RAG & Machine Learning',
|
||||
modules: [
|
||||
{
|
||||
id: 'rag',
|
||||
name: 'Daten & RAG',
|
||||
href: '/sdk/rag',
|
||||
description: 'Vektor-Suche & Collections',
|
||||
purpose: 'Verwalten und durchsuchen Sie indexierte Dokumente. Zeigt Status aller Qdrant Collections und ermoeglicht semantische Suche.',
|
||||
audience: ['Entwickler', 'Data Scientists', 'Compliance Officer'],
|
||||
subgroup: 'KI-Daten-Pipeline',
|
||||
},
|
||||
{
|
||||
id: 'training',
|
||||
name: 'Schulungen',
|
||||
href: '/sdk/training',
|
||||
description: 'KI-gestuetzte Mitarbeiter-Schulungen',
|
||||
purpose: 'Erstellen und verwalten Sie Compliance-Schulungen fuer Mitarbeiter mit KI-Unterstuetzung.',
|
||||
audience: ['Compliance Officer', 'HR', 'Admins'],
|
||||
subgroup: 'KI-Anwendungen',
|
||||
},
|
||||
{
|
||||
id: 'screening',
|
||||
name: 'KI-Screening',
|
||||
href: '/sdk/screening',
|
||||
description: 'Automatische Compliance-Pruefung',
|
||||
purpose: 'Automatisierte Pruefung von Dokumenten und Prozessen auf Compliance-Konformitaet.',
|
||||
audience: ['Compliance Officer', 'DSB'],
|
||||
subgroup: 'KI-Anwendungen',
|
||||
},
|
||||
{
|
||||
id: 'quality',
|
||||
name: 'Qualitaetssicherung',
|
||||
href: '/sdk/quality',
|
||||
description: 'KI-Qualitaets-Dashboard',
|
||||
purpose: 'Ueberwachung und Sicherstellung der KI-Ausgabequalitaet.',
|
||||
audience: ['Entwickler', 'QA'],
|
||||
subgroup: 'KI-Werkzeuge',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
// Meta modules (always visible)
|
||||
export const metaModules: NavModule[] = [
|
||||
{
|
||||
id: 'dashboard',
|
||||
name: 'Dashboard',
|
||||
href: '/dashboard',
|
||||
description: 'Uebersicht & Statistiken',
|
||||
purpose: 'Zentrale Uebersicht ueber alle Systeme mit wichtigen Kennzahlen.',
|
||||
audience: ['Alle'],
|
||||
oldAdminPath: '/admin',
|
||||
},
|
||||
{
|
||||
id: 'sdk-overview',
|
||||
name: 'SDK Module',
|
||||
href: '/sdk',
|
||||
description: 'Alle DSGVO & Compliance Module',
|
||||
purpose: 'Uebersicht aller SDK-Module fuer Datenschutz und Compliance.',
|
||||
audience: ['Alle'],
|
||||
},
|
||||
]
|
||||
|
||||
// Helper function to get category by ID
|
||||
export function getCategoryById(id: CategoryId): NavCategory | undefined {
|
||||
return navigation.find(cat => cat.id === id)
|
||||
}
|
||||
|
||||
// Helper function to get module by href
|
||||
export function getModuleByHref(href: string): { category: NavCategory; module: NavModule } | undefined {
|
||||
for (const category of navigation) {
|
||||
const module = category.modules.find(m => m.href === href)
|
||||
if (module) {
|
||||
return { category, module }
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
// Helper function to get all modules flat
|
||||
export function getAllModules(): NavModule[] {
|
||||
return [...navigation.flatMap(cat => cat.modules), ...metaModules]
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/**
|
||||
* Role-based Access System for Admin v2
|
||||
*
|
||||
* Roles determine which categories and modules are visible
|
||||
*/
|
||||
|
||||
import { CategoryId } from './navigation'
|
||||
|
||||
export type RoleId = 'developer' | 'manager' | 'auditor' | 'dsb'
|
||||
|
||||
export interface Role {
|
||||
id: RoleId
|
||||
name: string
|
||||
description: string
|
||||
icon: string
|
||||
visibleCategories: CategoryId[]
|
||||
color: string
|
||||
}
|
||||
|
||||
export const roles: Role[] = [
|
||||
{
|
||||
id: 'developer',
|
||||
name: 'Entwickler',
|
||||
description: 'Voller Zugriff auf alle Bereiche',
|
||||
icon: 'code',
|
||||
visibleCategories: ['ai'],
|
||||
color: 'bg-primary-100 border-primary-300 text-primary-700',
|
||||
},
|
||||
{
|
||||
id: 'manager',
|
||||
name: 'Manager',
|
||||
description: 'Executive Uebersicht',
|
||||
icon: 'chart',
|
||||
visibleCategories: [],
|
||||
color: 'bg-blue-100 border-blue-300 text-blue-700',
|
||||
},
|
||||
{
|
||||
id: 'auditor',
|
||||
name: 'Auditor',
|
||||
description: 'Compliance Pruefung',
|
||||
icon: 'clipboard',
|
||||
visibleCategories: [],
|
||||
color: 'bg-amber-100 border-amber-300 text-amber-700',
|
||||
},
|
||||
{
|
||||
id: 'dsb',
|
||||
name: 'DSB',
|
||||
description: 'Datenschutzbeauftragter',
|
||||
icon: 'shield',
|
||||
visibleCategories: [],
|
||||
color: 'bg-purple-100 border-purple-300 text-purple-700',
|
||||
},
|
||||
]
|
||||
|
||||
// Storage key for localStorage
|
||||
const ROLE_STORAGE_KEY = 'admin-v2-selected-role'
|
||||
|
||||
// Get role by ID
|
||||
export function getRoleById(id: RoleId): Role | undefined {
|
||||
return roles.find(role => role.id === id)
|
||||
}
|
||||
|
||||
// Check if category is visible for a role
|
||||
export function isCategoryVisibleForRole(categoryId: CategoryId, roleId: RoleId): boolean {
|
||||
const role = getRoleById(roleId)
|
||||
return role ? role.visibleCategories.includes(categoryId) : false
|
||||
}
|
||||
|
||||
// Get stored role from localStorage (client-side only)
|
||||
export function getStoredRole(): RoleId | null {
|
||||
if (typeof window === 'undefined') return null
|
||||
const stored = localStorage.getItem(ROLE_STORAGE_KEY)
|
||||
if (stored && roles.some(r => r.id === stored)) {
|
||||
return stored as RoleId
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// Store role in localStorage
|
||||
export function storeRole(roleId: RoleId): void {
|
||||
if (typeof window === 'undefined') return
|
||||
localStorage.setItem(ROLE_STORAGE_KEY, roleId)
|
||||
}
|
||||
|
||||
// Clear stored role
|
||||
export function clearStoredRole(): void {
|
||||
if (typeof window === 'undefined') return
|
||||
localStorage.removeItem(ROLE_STORAGE_KEY)
|
||||
}
|
||||
|
||||
// Check if this is a first-time visitor (no role stored)
|
||||
export function isFirstTimeVisitor(): boolean {
|
||||
return getStoredRole() === null
|
||||
}
|
||||
|
||||
// Get visible categories for a role
|
||||
export function getVisibleCategoriesForRole(roleId: RoleId): CategoryId[] {
|
||||
const role = getRoleById(roleId)
|
||||
return role ? role.visibleCategories : []
|
||||
}
|
||||
Reference in New Issue
Block a user