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

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:
Benjamin Admin
2026-03-04 11:43:00 +01:00
parent 7e5047290c
commit 215b95adfa
136 changed files with 8 additions and 8162 deletions

View File

@@ -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`
}

View File

@@ -1,2 +0,0 @@
export * from './types'
export * from './constants'

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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]
}

View File

@@ -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 : []
}