Initial commit: breakpilot-lehrer - Lehrer KI Platform

Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website,
Klausur-Service, School-Service, Voice-Service, Geo-Service,
BreakPilot Drive, Agent-Core

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Boenisch
2026-02-11 23:47:26 +01:00
commit 5a31f52310
1224 changed files with 425430 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
'use client'
import { createContext, useContext, useState, useEffect, ReactNode } from 'react'
import { Language, DEFAULT_LANGUAGE, isRTL, t as translate } from './i18n'
interface LanguageContextType {
language: Language
setLanguage: (lang: Language) => void
t: (key: string) => string
isRTL: boolean
}
const LanguageContext = createContext<LanguageContextType | undefined>(undefined)
const STORAGE_KEY = 'breakpilot-language'
export function LanguageProvider({ children }: { children: ReactNode }) {
const [language, setLanguageState] = useState<Language>(DEFAULT_LANGUAGE)
const [mounted, setMounted] = useState(false)
// Load language from localStorage on mount
useEffect(() => {
const stored = localStorage.getItem(STORAGE_KEY)
if (stored && ['de', 'en', 'tr', 'ar', 'ru', 'uk', 'pl'].includes(stored)) {
setLanguageState(stored as Language)
}
setMounted(true)
}, [])
// Update document direction for RTL languages
useEffect(() => {
if (mounted) {
document.documentElement.dir = isRTL(language) ? 'rtl' : 'ltr'
document.documentElement.lang = language
}
}, [language, mounted])
const setLanguage = (lang: Language) => {
setLanguageState(lang)
localStorage.setItem(STORAGE_KEY, lang)
}
const t = (key: string) => translate(key, language)
const value: LanguageContextType = {
language,
setLanguage,
t,
isRTL: isRTL(language),
}
// Prevent hydration mismatch
if (!mounted) {
return (
<LanguageContext.Provider value={{ ...value, language: DEFAULT_LANGUAGE, isRTL: false }}>
{children}
</LanguageContext.Provider>
)
}
return <LanguageContext.Provider value={value}>{children}</LanguageContext.Provider>
}
export function useLanguage() {
const context = useContext(LanguageContext)
if (context === undefined) {
throw new Error('useLanguage must be used within a LanguageProvider')
}
return context
}

View File

@@ -0,0 +1,267 @@
// ==============================================
// Architecture Data for Wizard Framework
// ==============================================
// Zentrale Konfiguration der Systemarchitektur fuer
// Visualisierung in den Admin-Wizards
export interface ServiceNode {
name: string
port: number
type: 'frontend' | 'api' | 'service' | 'database' | 'cache' | 'external'
description?: string
dependencies?: string[]
}
export interface ModuleArchitecture {
displayName: string
description: string
primaryServices: string[]
databases: string[]
tables?: string[]
externalTools?: string[]
dataFlow: string[]
dependencies: string[]
}
// ==============================================
// Service-Definitionen
// ==============================================
export const ARCHITECTURE_SERVICES: Record<string, ServiceNode> = {
frontend: {
name: 'Next.js Admin',
port: 3000,
type: 'frontend',
description: 'React-basiertes Admin Panel',
},
backend: {
name: 'Python Backend',
port: 8000,
type: 'api',
description: 'FastAPI REST API',
dependencies: ['postgres', 'valkey'],
},
'consent-service': {
name: 'Go Consent Service',
port: 8081,
type: 'service',
description: 'DSGVO-konforme Einwilligungsverwaltung',
dependencies: ['postgres'],
},
postgres: {
name: 'PostgreSQL',
port: 5432,
type: 'database',
description: 'Relationale Datenbank',
},
valkey: {
name: 'Valkey',
port: 6379,
type: 'cache',
description: 'In-Memory Cache & Session Store (Redis-kompatibler Open Source Fork)',
},
matrix: {
name: 'Matrix Synapse',
port: 8008,
type: 'service',
description: 'Dezentraler Messenger Server',
},
jitsi: {
name: 'Jitsi Meet',
port: 8443,
type: 'service',
description: 'Video-Konferenz Server',
},
mailserver: {
name: 'Mailserver',
port: 993,
type: 'external',
description: 'IMAP/SMTP Mailserver',
},
'unity-bridge': {
name: 'Unity AI Bridge',
port: 8090,
type: 'external',
description: 'REST API im Unity Editor fuer externe Steuerung',
},
}
// ==============================================
// Modul-spezifische Architektur
// ==============================================
export const MODULE_ARCHITECTURE: Record<string, ModuleArchitecture> = {
middleware: {
displayName: 'Middleware Stack',
description: 'Security & Request Processing Layer',
primaryServices: ['backend'],
databases: ['postgres', 'valkey'],
tables: ['middleware_config', 'middleware_events', 'rate_limit_ips'],
dataFlow: ['Browser', 'Next.js', 'FastAPI Middleware', 'PostgreSQL'],
dependencies: ['JWT Auth', 'Valkey Session'],
},
consent: {
displayName: 'Consent Verwaltung',
description: 'DSGVO-konforme Einwilligungsverwaltung',
primaryServices: ['consent-service'],
databases: ['postgres'],
tables: ['consent_records', 'document_versions', 'consent_templates'],
dataFlow: ['Browser', 'Next.js', 'FastAPI', 'Go Consent Service', 'PostgreSQL'],
dependencies: ['JWT Auth', 'RBAC (data_protection_officer)'],
},
dsr: {
displayName: 'Datenschutzanfragen (DSR)',
description: 'DSGVO Art. 15-21 Anfragenbearbeitung',
primaryServices: ['backend', 'consent-service'],
databases: ['postgres'],
tables: ['dsr_requests', 'dsr_exports', 'audit_log'],
dataFlow: ['Browser', 'Next.js', 'FastAPI', 'Go Consent Service', 'PostgreSQL'],
dependencies: ['JWT Auth', 'RBAC', 'Export Service'],
},
security: {
displayName: 'Security & DevSecOps',
description: 'SAST, SCA, Secret Detection, SBOM',
primaryServices: ['backend'],
databases: ['postgres'],
tables: ['security_scans', 'security_findings', 'sbom_components'],
externalTools: ['gitleaks', 'semgrep', 'trivy', 'grype'],
dataFlow: ['Git Repository', 'Scanner Tools', 'FastAPI', 'PostgreSQL'],
dependencies: ['Git Integration', 'CI/CD Pipeline'],
},
rbac: {
displayName: 'RBAC & Authentifizierung',
description: 'Rollen- und Berechtigungsverwaltung',
primaryServices: ['backend', 'consent-service'],
databases: ['postgres', 'valkey'],
tables: ['users', 'roles', 'permissions', 'role_permissions'],
dataFlow: ['Browser', 'Next.js', 'FastAPI', 'JWT/Session', 'PostgreSQL'],
dependencies: ['Valkey Session', 'bcrypt'],
},
communication: {
displayName: 'Kommunikation',
description: 'Matrix Messenger & Jitsi Video',
primaryServices: ['matrix', 'jitsi'],
databases: ['postgres'],
tables: ['matrix_rooms', 'jitsi_meetings'],
dataFlow: ['Browser', 'Matrix Synapse', 'Jitsi Meet', 'PostgreSQL'],
dependencies: ['Matrix Federation', 'STUN/TURN'],
},
mail: {
displayName: 'E-Mail Management',
description: 'IMAP/SMTP mit KI-Analyse',
primaryServices: ['backend', 'mailserver'],
databases: ['postgres'],
tables: ['mail_accounts', 'mail_messages', 'mail_analysis'],
dataFlow: ['Mailserver (IMAP)', 'FastAPI', 'LLM Analysis', 'PostgreSQL'],
dependencies: ['IMAP Auth', 'SMTP Auth', 'LLM Service'],
},
gpu: {
displayName: 'GPU Infrastruktur',
description: 'CUDA/ROCm GPU Management',
primaryServices: ['backend'],
databases: ['postgres'],
tables: ['gpu_nodes', 'gpu_jobs', 'gpu_metrics'],
dataFlow: ['Browser', 'FastAPI', 'NVIDIA/AMD Driver', 'GPU Hardware'],
dependencies: ['CUDA', 'ROCm', 'nvidia-smi'],
},
llm: {
displayName: 'LLM Vergleich',
description: 'Vergleich verschiedener LLM Provider',
primaryServices: ['backend'],
databases: ['postgres'],
tables: ['llm_providers', 'llm_benchmarks', 'llm_costs'],
externalTools: ['OpenAI API', 'Anthropic API', 'Local LLMs'],
dataFlow: ['Browser', 'FastAPI', 'LLM Provider API', 'PostgreSQL'],
dependencies: ['API Keys', 'Token Counting'],
},
rag: {
displayName: 'RAG & BYOEH',
description: 'Retrieval Augmented Generation mit Client-Side Encryption',
primaryServices: ['backend', 'klausur-frontend'],
databases: ['postgres', 'qdrant'],
tables: ['erwartungshorizonte', 'eh_chunks', 'documents', 'embeddings'],
externalTools: ['Qdrant Vector DB', 'BGE-M3 Embeddings', 'BGE Reranker'],
dataFlow: ['EH Upload', 'Client-Side Encryption', 'Chunking', 'Embedding', 'Vector Store', 'RAG Search', 'Client-Side Decryption'],
dependencies: ['AES-256-GCM', 'PBKDF2', 'Web Crypto API', 'Qdrant'],
},
sbom: {
displayName: 'SBOM Management',
description: 'Software Bill of Materials',
primaryServices: ['backend'],
databases: ['postgres'],
tables: ['sbom_components', 'vulnerabilities', 'licenses'],
externalTools: ['syft', 'cyclonedx'],
dataFlow: ['Container Image', 'SBOM Generator', 'FastAPI', 'PostgreSQL'],
dependencies: ['Container Runtime', 'CVE Database'],
},
docs: {
displayName: 'Entwickler-Dokumentation',
description: 'Projektdokumentation & Guides',
primaryServices: ['frontend'],
databases: [],
dataFlow: ['Markdown Files', 'Next.js', 'Browser'],
dependencies: ['File System'],
},
'unity-bridge': {
displayName: 'Unity AI Bridge',
description: 'REST API zur externen Steuerung des Unity Editors',
primaryServices: ['unity-bridge'],
databases: [],
dataFlow: ['Admin Panel', 'API Proxy', 'Unity Bridge', 'Unity Editor'],
dependencies: ['Unity Editor muss laufen', 'Bridge Server gestartet'],
},
}
// ==============================================
// Hilfsfunktionen
// ==============================================
export function getServiceInfo(serviceId: string): ServiceNode | undefined {
return ARCHITECTURE_SERVICES[serviceId]
}
export function getModuleArchitecture(moduleId: string): ModuleArchitecture | undefined {
return MODULE_ARCHITECTURE[moduleId]
}
export function getAllServices(moduleId: string): ServiceNode[] {
const module = MODULE_ARCHITECTURE[moduleId]
if (!module) return []
const serviceIds = [...module.primaryServices, ...module.databases]
return serviceIds
.map((id) => ARCHITECTURE_SERVICES[id])
.filter((s): s is ServiceNode => s !== undefined)
}
export function getDependencyChain(moduleId: string): string[] {
const module = MODULE_ARCHITECTURE[moduleId]
if (!module) return []
return module.dataFlow
}
// ==============================================
// Wizard-spezifische Konfiguration
// ==============================================
export interface WizardModuleConfig {
module: string
available: boolean
steps: number
priority: 'high' | 'medium' | 'low'
}
export const AVAILABLE_WIZARDS: WizardModuleConfig[] = [
{ module: 'middleware', available: true, steps: 8, priority: 'high' },
{ module: 'consent', available: true, steps: 6, priority: 'high' },
{ module: 'dsr', available: true, steps: 7, priority: 'high' },
{ module: 'security', available: true, steps: 8, priority: 'high' },
{ module: 'rbac', available: true, steps: 6, priority: 'medium' },
{ module: 'communication', available: true, steps: 5, priority: 'medium' },
{ module: 'mail', available: true, steps: 6, priority: 'medium' },
{ module: 'unity-bridge', available: true, steps: 7, priority: 'medium' },
{ module: 'gpu', available: true, steps: 4, priority: 'low' },
{ module: 'llm', available: true, steps: 4, priority: 'low' },
{ module: 'rag', available: true, steps: 6, priority: 'high' },
]

View File

@@ -0,0 +1,361 @@
/**
* Compliance Framework - Internationalization (i18n)
*
* Provides bilingual terminology (DE/EN) for the compliance module.
* This helps non-technical stakeholders understand GRC concepts.
*/
export type Language = 'de' | 'en'
// Core compliance terms with translations and explanations
export const COMPLIANCE_TERMS = {
de: {
// Core concepts
control: 'Massnahme',
control_description: 'Eine technische oder organisatorische Massnahme zur Erfuellung einer Compliance-Anforderung.',
evidence: 'Nachweis',
evidence_description: 'Dokumentierter Beleg, dass eine Massnahme umgesetzt und wirksam ist.',
requirement: 'Anforderung',
requirement_description: 'Eine gesetzliche oder normative Vorgabe, die erfuellt werden muss.',
regulation: 'Verordnung',
regulation_description: 'Ein Gesetz oder Standard, der verbindliche Anforderungen definiert.',
risk: 'Risiko',
risk_description: 'Eine potenzielle Bedrohung fuer die Organisation mit Eintrittswahrscheinlichkeit und Schadenshoehe.',
// Status values
pass: 'Erfuellt',
pass_description: 'Die Massnahme ist vollstaendig umgesetzt und nachgewiesen.',
partial: 'Teilweise erfuellt',
partial_description: 'Die Massnahme ist teilweise umgesetzt, es bestehen noch Luecken.',
fail: 'Nicht erfuellt',
fail_description: 'Die Massnahme ist nicht oder unzureichend umgesetzt.',
planned: 'Geplant',
planned_description: 'Die Umsetzung der Massnahme ist geplant aber noch nicht begonnen.',
not_applicable: 'Nicht anwendbar',
not_applicable_description: 'Die Anforderung trifft auf unsere Organisation nicht zu.',
// Metrics
compliance_score: 'Erfuellungsgrad',
compliance_score_description: 'Prozentsatz der erfuellten Compliance-Anforderungen.',
coverage_level: 'Abdeckungsgrad',
coverage_level_description: 'Wie gut eine Massnahme die Anforderung erfuellt (vollstaendig/teilweise).',
risk_level: 'Risikostufe',
risk_level_description: 'Kombinierte Bewertung aus Eintrittswahrscheinlichkeit und Schadenshoehe.',
// Risk levels
low: 'Niedrig',
low_description: 'Geringes Risiko, keine sofortigen Massnahmen erforderlich.',
medium: 'Mittel',
medium_description: 'Moderates Risiko, Massnahmen sollten geplant werden.',
high: 'Hoch',
high_description: 'Hohes Risiko, zeitnahe Massnahmen erforderlich.',
critical: 'Kritisch',
critical_description: 'Kritisches Risiko, sofortige Massnahmen erforderlich.',
// Domains
governance: 'Governance',
governance_description: 'Organisatorische Steuerung und Fuehrung.',
privacy: 'Datenschutz',
privacy_description: 'Schutz personenbezogener Daten.',
iam: 'Identitaets- & Zugriffsmanagement',
iam_description: 'Verwaltung von Benutzerkonten und Zugriffsrechten.',
crypto: 'Kryptografie',
crypto_description: 'Verschluesselung und kryptografische Verfahren.',
sdlc: 'Sichere Entwicklung',
sdlc_description: 'Sicherheit im Softwareentwicklungsprozess.',
ops: 'Betrieb',
ops_description: 'IT-Betrieb, Monitoring und Incident Response.',
ai: 'KI-spezifisch',
ai_description: 'Anforderungen fuer Kuenstliche Intelligenz.',
cra: 'Supply Chain',
cra_description: 'Lieferketten-Sicherheit und Produkthaftung.',
audit: 'Audit',
audit_description: 'Pruefung und Nachvollziehbarkeit.',
// UI Elements
dashboard: 'Uebersicht',
export: 'Export',
filter: 'Filter',
search: 'Suche',
details: 'Details',
actions: 'Aktionen',
status: 'Status',
owner: 'Verantwortlich',
deadline: 'Frist',
last_review: 'Letzte Pruefung',
next_review: 'Naechste Pruefung',
// Executive Dashboard
traffic_light_status: 'Ampelstatus',
traffic_light_description: 'Schnelle Einschaetzung: Gruen = gut, Gelb = Aufmerksamkeit, Rot = kritisch.',
top_risks: 'Top Risiken',
upcoming_deadlines: 'Naechste Fristen',
trend: 'Trend',
trend_description: 'Entwicklung des Erfuellungsgrades ueber Zeit.',
workload: 'Arbeitsbelastung',
workload_description: 'Offene Aufgaben pro Team oder Person.',
// Audit & Sign-off (Sprint 3)
audit_session: 'Pruefung',
audit_session_description: 'Eine strukturierte Compliance-Pruefung mit definierten Anforderungen.',
sign_off: 'Freigabe',
sign_off_description: 'Bestaetigung eines Pruefergebnisses durch den Auditor.',
digital_signature: 'Digitale Signatur',
digital_signature_description: 'SHA-256 Hash zur unveraenderlichen Dokumentation des Pruefergebnisses.',
audit_checklist: 'Pruefungscheckliste',
audit_checklist_description: 'Liste aller zu pruefenden Anforderungen einer Audit-Session.',
completion_rate: 'Abschlussrate',
completion_rate_description: 'Prozentsatz der bereits abgeschlossenen Pruefpunkte.',
// Audit Results
compliant: 'Konform',
compliant_description: 'Die Anforderung ist vollstaendig erfuellt.',
compliant_with_notes: 'Konform mit Anmerkungen',
compliant_with_notes_description: 'Die Anforderung ist erfuellt, aber es gibt Hinweise zur Verbesserung.',
non_compliant: 'Nicht konform',
non_compliant_description: 'Die Anforderung ist nicht oder unzureichend erfuellt.',
pending_audit: 'Ausstehend',
pending_audit_description: 'Die Pruefung dieses Punktes steht noch aus.',
// Session Status
session_draft: 'Entwurf',
session_in_progress: 'In Bearbeitung',
session_completed: 'Abgeschlossen',
session_archived: 'Archiviert',
// Actions
create_session: 'Neue Session erstellen',
start_audit: 'Pruefung starten',
complete_audit: 'Pruefung abschliessen',
export_report: 'Report exportieren',
sign_item: 'Punkt signieren',
},
en: {
// Core concepts
control: 'Control',
control_description: 'A technical or organizational measure to fulfill a compliance requirement.',
evidence: 'Evidence',
evidence_description: 'Documented proof that a control is implemented and effective.',
requirement: 'Requirement',
requirement_description: 'A legal or normative obligation that must be fulfilled.',
regulation: 'Regulation',
regulation_description: 'A law or standard that defines binding requirements.',
risk: 'Risk',
risk_description: 'A potential threat to the organization with likelihood and impact.',
// Status values
pass: 'Pass',
pass_description: 'The control is fully implemented and evidenced.',
partial: 'Partial',
partial_description: 'The control is partially implemented, gaps remain.',
fail: 'Fail',
fail_description: 'The control is not or insufficiently implemented.',
planned: 'Planned',
planned_description: 'Implementation of the control is planned but not started.',
not_applicable: 'N/A',
not_applicable_description: 'The requirement does not apply to our organization.',
// Metrics
compliance_score: 'Compliance Score',
compliance_score_description: 'Percentage of fulfilled compliance requirements.',
coverage_level: 'Coverage Level',
coverage_level_description: 'How well a control fulfills the requirement (full/partial).',
risk_level: 'Risk Level',
risk_level_description: 'Combined assessment of likelihood and impact.',
// Risk levels
low: 'Low',
low_description: 'Low risk, no immediate action required.',
medium: 'Medium',
medium_description: 'Moderate risk, actions should be planned.',
high: 'High',
high_description: 'High risk, timely action required.',
critical: 'Critical',
critical_description: 'Critical risk, immediate action required.',
// Domains
governance: 'Governance',
governance_description: 'Organizational steering and leadership.',
privacy: 'Privacy',
privacy_description: 'Protection of personal data.',
iam: 'Identity & Access Management',
iam_description: 'Management of user accounts and access rights.',
crypto: 'Cryptography',
crypto_description: 'Encryption and cryptographic procedures.',
sdlc: 'Secure Development',
sdlc_description: 'Security in the software development process.',
ops: 'Operations',
ops_description: 'IT operations, monitoring, and incident response.',
ai: 'AI-specific',
ai_description: 'Requirements for Artificial Intelligence.',
cra: 'Supply Chain',
cra_description: 'Supply chain security and product liability.',
audit: 'Audit',
audit_description: 'Auditing and traceability.',
// UI Elements
dashboard: 'Dashboard',
export: 'Export',
filter: 'Filter',
search: 'Search',
details: 'Details',
actions: 'Actions',
status: 'Status',
owner: 'Owner',
deadline: 'Deadline',
last_review: 'Last Review',
next_review: 'Next Review',
// Executive Dashboard
traffic_light_status: 'Traffic Light Status',
traffic_light_description: 'Quick assessment: Green = good, Yellow = attention, Red = critical.',
top_risks: 'Top Risks',
upcoming_deadlines: 'Upcoming Deadlines',
trend: 'Trend',
trend_description: 'Development of compliance score over time.',
workload: 'Workload',
workload_description: 'Open tasks per team or person.',
// Audit & Sign-off (Sprint 3)
audit_session: 'Audit Session',
audit_session_description: 'A structured compliance audit with defined requirements.',
sign_off: 'Sign-off',
sign_off_description: 'Confirmation of an audit result by the auditor.',
digital_signature: 'Digital Signature',
digital_signature_description: 'SHA-256 hash for immutable documentation of audit results.',
audit_checklist: 'Audit Checklist',
audit_checklist_description: 'List of all requirements to be audited in a session.',
completion_rate: 'Completion Rate',
completion_rate_description: 'Percentage of completed audit items.',
// Audit Results
compliant: 'Compliant',
compliant_description: 'The requirement is fully met.',
compliant_with_notes: 'Compliant with Notes',
compliant_with_notes_description: 'The requirement is met, but there are notes for improvement.',
non_compliant: 'Non-Compliant',
non_compliant_description: 'The requirement is not or insufficiently met.',
pending_audit: 'Pending',
pending_audit_description: 'The audit of this item is still pending.',
// Session Status
session_draft: 'Draft',
session_in_progress: 'In Progress',
session_completed: 'Completed',
session_archived: 'Archived',
// Actions
create_session: 'Create Session',
start_audit: 'Start Audit',
complete_audit: 'Complete Audit',
export_report: 'Export Report',
sign_item: 'Sign Item',
},
}
// Domain mapping for display
export const DOMAIN_LABELS: Record<string, { de: string; en: string }> = {
gov: { de: 'Governance', en: 'Governance' },
priv: { de: 'Datenschutz', en: 'Privacy' },
iam: { de: 'Identitaet & Zugriff', en: 'Identity & Access' },
crypto: { de: 'Kryptografie', en: 'Cryptography' },
sdlc: { de: 'Sichere Entwicklung', en: 'Secure Dev' },
ops: { de: 'Betrieb', en: 'Operations' },
ai: { de: 'KI-spezifisch', en: 'AI-specific' },
cra: { de: 'Supply Chain', en: 'Supply Chain' },
aud: { de: 'Audit', en: 'Audit' },
}
// Status colors and labels
export const STATUS_CONFIG: Record<string, { color: string; bgColor: string; de: string; en: string }> = {
pass: { color: 'text-green-700', bgColor: 'bg-green-100', de: 'Erfuellt', en: 'Pass' },
partial: { color: 'text-yellow-700', bgColor: 'bg-yellow-100', de: 'Teilweise', en: 'Partial' },
fail: { color: 'text-red-700', bgColor: 'bg-red-100', de: 'Nicht erfuellt', en: 'Fail' },
planned: { color: 'text-slate-700', bgColor: 'bg-slate-100', de: 'Geplant', en: 'Planned' },
'n/a': { color: 'text-slate-500', bgColor: 'bg-slate-50', de: 'N/A', en: 'N/A' },
}
// Risk level colors
export const RISK_CONFIG: Record<string, { color: string; bgColor: string; de: string; en: string }> = {
low: { color: 'text-green-700', bgColor: 'bg-green-100', de: 'Niedrig', en: 'Low' },
medium: { color: 'text-yellow-700', bgColor: 'bg-yellow-100', de: 'Mittel', en: 'Medium' },
high: { color: 'text-orange-700', bgColor: 'bg-orange-100', de: 'Hoch', en: 'High' },
critical: { color: 'text-red-700', bgColor: 'bg-red-100', de: 'Kritisch', en: 'Critical' },
}
// Traffic light colors for executive dashboard
export const TRAFFIC_LIGHT_CONFIG: Record<string, { color: string; bgColor: string; borderColor: string; de: string; en: string }> = {
green: {
color: 'text-green-700',
bgColor: 'bg-green-500',
borderColor: 'border-green-500',
de: 'Gut',
en: 'Good'
},
yellow: {
color: 'text-yellow-700',
bgColor: 'bg-yellow-500',
borderColor: 'border-yellow-500',
de: 'Aufmerksamkeit',
en: 'Attention'
},
red: {
color: 'text-red-700',
bgColor: 'bg-red-500',
borderColor: 'border-red-500',
de: 'Kritisch',
en: 'Critical'
},
}
// Helper function to get term with fallback
export function getTerm(lang: Language, key: string): string {
const terms = COMPLIANCE_TERMS[lang]
return (terms as Record<string, string>)[key] || key
}
// Helper function to get description
export function getDescription(lang: Language, key: string): string {
const terms = COMPLIANCE_TERMS[lang]
return (terms as Record<string, string>)[`${key}_description`] || ''
}
// Helper to get domain label
export function getDomainLabel(domain: string, lang: Language): string {
return DOMAIN_LABELS[domain]?.[lang] || domain.toUpperCase()
}
// Helper to get status display
export function getStatusDisplay(status: string, lang: Language): { label: string; color: string; bgColor: string } {
const config = STATUS_CONFIG[status] || STATUS_CONFIG['planned']
return {
label: config[lang],
color: config.color,
bgColor: config.bgColor,
}
}
// Helper to get risk display
export function getRiskDisplay(level: string, lang: Language): { label: string; color: string; bgColor: string } {
const config = RISK_CONFIG[level] || RISK_CONFIG['medium']
return {
label: config[lang],
color: config.color,
bgColor: config.bgColor,
}
}
// Helper to get traffic light display
export function getTrafficLightDisplay(status: string, lang: Language): { label: string; color: string; bgColor: string; borderColor: string } {
const config = TRAFFIC_LIGHT_CONFIG[status] || TRAFFIC_LIGHT_CONFIG['yellow']
return {
label: config[lang],
color: config.color,
bgColor: config.bgColor,
borderColor: config.borderColor,
}
}
// Language context default
export const DEFAULT_LANGUAGE: Language = 'de'

View File

@@ -0,0 +1,60 @@
/**
* Website Content Type Definitions
*
* Typen fuer Website-Inhalte (ohne 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
}
}

284
website/lib/content.ts Normal file
View File

@@ -0,0 +1,284 @@
/**
* Website Content Management (Server-only)
*
* Laedt Website-Texte aus JSON-Dateien.
* Admin kann Texte ueber /admin bearbeiten.
*
* WICHTIG: Diese Datei darf nur in Server Components verwendet werden!
* Fuer Client Components verwende @/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-Verzeichnis - nutze Umgebungsvariable oder relativen Pfad
function getContentDir(): string {
// Prüfe Umgebungsvariable zuerst
if (process.env.CONTENT_DIR) {
return process.env.CONTENT_DIR
}
// Versuche verschiedene mögliche Pfade
const possiblePaths = [
join(process.cwd(), 'content'), // Standard: CWD/content
join(process.cwd(), 'website', 'content'), // Falls CWD das Projekt-Root ist
'/app/content', // Docker-Container
join(dirname(__filename), '..', 'content'), // Relativ zu dieser Datei
]
// Prüfe ob einer der Pfade existiert und beschreibbar ist
for (const path of possiblePaths) {
try {
if (existsSync(path)) {
accessSync(path, constants.W_OK)
return path
}
} catch {
// Pfad nicht beschreibbar, versuche nächsten
}
}
// Fallback: Erstelle im 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',
},
}
/**
* Laedt Content aus JSON-Datei oder gibt Default zurueck
*/
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
}
/**
* Speichert Content in JSON-Datei
* @returns Objekt mit success-Status und optionaler Fehlermeldung
*/
export function saveContent(content: WebsiteContent): { success: boolean; error?: string } {
const contentPath = join(CONTENT_DIR, 'website.json')
try {
// Stelle sicher, dass Verzeichnis existiert
if (!existsSync(CONTENT_DIR)) {
console.log(`[Content] Creating directory: ${CONTENT_DIR}`)
mkdirSync(CONTENT_DIR, { recursive: true, mode: 0o755 })
}
// Prüfe Schreibrechte
try {
accessSync(CONTENT_DIR, constants.W_OK)
} catch {
const error = `Verzeichnis nicht beschreibbar: ${CONTENT_DIR}`
console.error(`[Content] ${error}`)
return { success: false, error }
}
// Schreibe Datei
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 : 'Unbekannter Fehler'
console.error(`[Content] Error saving: ${errorMessage}`)
return { success: false, error: errorMessage }
}
}
/**
* Gibt Default Content zurueck (fuer Reset)
*/
export function getDefaultContent(): WebsiteContent {
return defaultContent
}

1173
website/lib/i18n.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,211 @@
'use client'
/**
* LLM Mode Context
*
* Globaler Kontext fuer den LLM-Modus (Hybrid, Local-Only, Cloud-Only, Auto).
* Steuert welche Provider fuer KI-Anfragen verwendet werden.
*
* Modi:
* - hybrid: Lokal zuerst (Ollama), Cloud als Fallback (Claude/OpenAI)
* - local-only: Nur lokale Modelle (Ollama) - maximaler Datenschutz
* - cloud-only: Nur Cloud-Provider (Claude, OpenAI) - beste Qualitaet
* - auto: Automatische Auswahl basierend auf Komplexitaet
*/
import { createContext, useContext, useState, useEffect, ReactNode, useCallback } from 'react'
// LLM Mode Types
export type LLMMode = 'hybrid' | 'local-only' | 'cloud-only' | 'auto'
export interface LLMModeConfig {
mode: LLMMode
label: string
description: string
providers: {
enableOllama: boolean
enableClaude: boolean
enableOpenAI: boolean
}
icon: string
}
// Mode configurations
export const LLM_MODE_CONFIGS: Record<LLMMode, LLMModeConfig> = {
hybrid: {
mode: 'hybrid',
label: 'Hybrid',
description: 'Lokal zuerst, Cloud als Fallback',
providers: {
enableOllama: true,
enableClaude: true,
enableOpenAI: false,
},
icon: '🔄',
},
'local-only': {
mode: 'local-only',
label: 'Nur Lokal',
description: 'Maximaler Datenschutz (nur Ollama)',
providers: {
enableOllama: true,
enableClaude: false,
enableOpenAI: false,
},
icon: '🔒',
},
'cloud-only': {
mode: 'cloud-only',
label: 'Nur Cloud',
description: 'Beste Qualitaet (Claude/OpenAI)',
providers: {
enableOllama: false,
enableClaude: true,
enableOpenAI: true,
},
icon: '☁️',
},
auto: {
mode: 'auto',
label: 'Auto',
description: 'Automatisch nach Komplexitaet',
providers: {
enableOllama: true,
enableClaude: true,
enableOpenAI: true,
},
icon: '⚡',
},
}
// Context type
interface LLMModeContextType {
mode: LLMMode
config: LLMModeConfig
setMode: (mode: LLMMode) => void
// Convenience getters for provider states
enableOllama: boolean
enableClaude: boolean
enableOpenAI: boolean
// Override individual providers (for fine-tuning within a mode)
setProviderOverrides: (overrides: Partial<LLMModeConfig['providers']>) => void
providerOverrides: Partial<LLMModeConfig['providers']>
clearOverrides: () => void
}
const LLMModeContext = createContext<LLMModeContextType | undefined>(undefined)
const STORAGE_KEY = 'breakpilot-llm-mode'
const OVERRIDES_KEY = 'breakpilot-llm-overrides'
const DEFAULT_MODE: LLMMode = 'hybrid'
export function LLMModeProvider({ children }: { children: ReactNode }) {
const [mode, setModeState] = useState<LLMMode>(DEFAULT_MODE)
const [providerOverrides, setProviderOverridesState] = useState<Partial<LLMModeConfig['providers']>>({})
const [mounted, setMounted] = useState(false)
// Load from localStorage on mount
useEffect(() => {
try {
const storedMode = localStorage.getItem(STORAGE_KEY)
if (storedMode && storedMode in LLM_MODE_CONFIGS) {
setModeState(storedMode as LLMMode)
}
const storedOverrides = localStorage.getItem(OVERRIDES_KEY)
if (storedOverrides) {
setProviderOverridesState(JSON.parse(storedOverrides))
}
} catch (e) {
console.warn('Failed to load LLM mode from localStorage:', e)
}
setMounted(true)
}, [])
const setMode = useCallback((newMode: LLMMode) => {
setModeState(newMode)
// Clear overrides when switching modes
setProviderOverridesState({})
try {
localStorage.setItem(STORAGE_KEY, newMode)
localStorage.removeItem(OVERRIDES_KEY)
} catch (e) {
console.warn('Failed to save LLM mode to localStorage:', e)
}
}, [])
const setProviderOverrides = useCallback((overrides: Partial<LLMModeConfig['providers']>) => {
setProviderOverridesState(prev => {
const newOverrides = { ...prev, ...overrides }
try {
localStorage.setItem(OVERRIDES_KEY, JSON.stringify(newOverrides))
} catch (e) {
console.warn('Failed to save provider overrides:', e)
}
return newOverrides
})
}, [])
const clearOverrides = useCallback(() => {
setProviderOverridesState({})
try {
localStorage.removeItem(OVERRIDES_KEY)
} catch (e) {
console.warn('Failed to clear provider overrides:', e)
}
}, [])
const config = LLM_MODE_CONFIGS[mode]
// Compute effective provider states (mode defaults + overrides)
const enableOllama = providerOverrides.enableOllama ?? config.providers.enableOllama
const enableClaude = providerOverrides.enableClaude ?? config.providers.enableClaude
const enableOpenAI = providerOverrides.enableOpenAI ?? config.providers.enableOpenAI
const value: LLMModeContextType = {
mode,
config,
setMode,
enableOllama,
enableClaude,
enableOpenAI,
setProviderOverrides,
providerOverrides,
clearOverrides,
}
// Prevent hydration mismatch
if (!mounted) {
return (
<LLMModeContext.Provider
value={{
...value,
mode: DEFAULT_MODE,
config: LLM_MODE_CONFIGS[DEFAULT_MODE],
enableOllama: LLM_MODE_CONFIGS[DEFAULT_MODE].providers.enableOllama,
enableClaude: LLM_MODE_CONFIGS[DEFAULT_MODE].providers.enableClaude,
enableOpenAI: LLM_MODE_CONFIGS[DEFAULT_MODE].providers.enableOpenAI,
providerOverrides: {},
}}
>
{children}
</LLMModeContext.Provider>
)
}
return <LLMModeContext.Provider value={value}>{children}</LLMModeContext.Provider>
}
export function useLLMMode() {
const context = useContext(LLMModeContext)
if (context === undefined) {
throw new Error('useLLMMode must be used within a LLMModeProvider')
}
return context
}
// Utility hook for getting provider settings without full context
export function useLLMProviders() {
const { enableOllama, enableClaude, enableOpenAI, mode } = useLLMMode()
return { enableOllama, enableClaude, enableOpenAI, mode }
}