This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/backend/frontend/modules/worksheets.py
Benjamin Admin 21a844cb8a fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.

This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).

Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 09:51:32 +01:00

1919 lines
55 KiB
Python

"""
BreakPilot Studio - Lerneinheiten/Arbeitsblaetter Modul
Refactored: 2024-12-18
- Kachel-basierte Startansicht
- Sub-Module fuer verschiedene Funktionen
Funktionen (als Kacheln):
- Lerneinheiten erstellen und verwalten
- Dateien hochladen
- Dateien bereinigen (OCR, Neuaufbau)
- Lernflows (Interaktive Uebungen)
- Multiple Choice Tests
- Lueckentexte
- Mindmap Generator
- Uebersetzungen
"""
class WorksheetsModule:
"""Modul fuer Lerneinheiten und Arbeitsblaetter."""
@staticmethod
def get_css() -> str:
"""CSS fuer das Worksheets-Modul."""
return """
/* =============================================
WORKSHEETS MODULE - Lerneinheiten & Arbeitsblaetter
============================================= */
/* Panel Layout */
.panel-worksheets {
display: none;
flex-direction: column;
height: 100%;
background: var(--bp-bg);
overflow: hidden;
}
.panel-worksheets.active {
display: flex;
}
/* Worksheets Header */
.worksheets-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 32px;
border-bottom: 1px solid var(--bp-border);
background: var(--bp-surface);
}
.worksheets-title-section h1 {
font-size: 24px;
font-weight: 700;
color: var(--bp-text);
margin-bottom: 4px;
}
.worksheets-subtitle {
font-size: 14px;
color: var(--bp-text-muted);
}
.worksheets-nav {
display: flex;
gap: 8px;
}
.worksheets-nav-btn {
padding: 8px 16px;
border-radius: 8px;
border: 1px solid var(--bp-border);
background: var(--bp-surface);
color: var(--bp-text-muted);
font-size: 13px;
cursor: pointer;
transition: all 0.2s;
}
.worksheets-nav-btn:hover {
background: var(--bp-surface-elevated);
color: var(--bp-text);
}
.worksheets-nav-btn.active {
background: var(--bp-primary);
border-color: var(--bp-primary);
color: white;
}
/* Worksheets Content - Kacheln */
.worksheets-content {
flex: 1;
overflow-y: auto;
padding: 32px;
}
/* Tiles Grid */
.worksheets-tiles {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
max-width: 1400px;
margin: 0 auto;
}
/* Section Header */
.tiles-section {
margin-bottom: 32px;
}
.tiles-section-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px;
}
.tiles-section-icon {
width: 40px;
height: 40px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
}
.tiles-section-icon.create { background: rgba(59, 130, 246, 0.15); }
.tiles-section-icon.process { background: rgba(16, 185, 129, 0.15); }
.tiles-section-icon.generate { background: rgba(245, 158, 11, 0.15); }
.tiles-section-icon.translate { background: rgba(139, 92, 246, 0.15); }
.tiles-section-title {
font-size: 16px;
font-weight: 600;
color: var(--bp-text);
}
.tiles-section-desc {
font-size: 13px;
color: var(--bp-text-muted);
}
/* Worksheet Tile */
.worksheet-tile {
background: var(--bp-surface);
border: 1px solid var(--bp-border);
border-radius: 16px;
padding: 24px;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.worksheet-tile:hover {
transform: translateY(-4px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1);
border-color: var(--bp-primary);
}
.worksheet-tile::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: var(--bp-primary);
opacity: 0;
transition: opacity 0.3s;
}
.worksheet-tile:hover::before {
opacity: 1;
}
.tile-icon-container {
width: 56px;
height: 56px;
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
margin-bottom: 16px;
}
/* Icon Colors */
.tile-icon-container.blue { background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%); }
.tile-icon-container.green { background: linear-gradient(135deg, #10b981 0%, #059669 100%); }
.tile-icon-container.orange { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); }
.tile-icon-container.purple { background: linear-gradient(135deg, #8b5cf6 0%, #6d28d9 100%); }
.tile-icon-container.red { background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); }
.tile-icon-container.cyan { background: linear-gradient(135deg, #06b6d4 0%, #0891b2 100%); }
.tile-icon-container.pink { background: linear-gradient(135deg, #ec4899 0%, #be185d 100%); }
.tile-icon-container.teal { background: linear-gradient(135deg, #14b8a6 0%, #0d9488 100%); }
.tile-title {
font-size: 16px;
font-weight: 600;
color: var(--bp-text);
margin-bottom: 8px;
}
.tile-description {
font-size: 13px;
color: var(--bp-text-muted);
line-height: 1.5;
margin-bottom: 16px;
}
.tile-features {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.tile-feature-tag {
padding: 4px 10px;
background: var(--bp-bg);
border-radius: 12px;
font-size: 11px;
color: var(--bp-text-muted);
}
.tile-arrow {
position: absolute;
bottom: 20px;
right: 20px;
width: 32px;
height: 32px;
border-radius: 50%;
background: var(--bp-bg);
display: flex;
align-items: center;
justify-content: center;
color: var(--bp-text-muted);
transition: all 0.3s;
}
.worksheet-tile:hover .tile-arrow {
background: var(--bp-primary);
color: white;
transform: translateX(4px);
}
/* Sub-Panel (hidden by default, shown when tile clicked) */
.worksheets-subpanel {
display: none;
flex-direction: column;
height: 100%;
}
.worksheets-subpanel.active {
display: flex;
}
.subpanel-header {
display: flex;
align-items: center;
gap: 16px;
padding: 16px 24px;
border-bottom: 1px solid var(--bp-border);
background: var(--bp-surface);
}
.subpanel-back {
width: 36px;
height: 36px;
border-radius: 8px;
border: 1px solid var(--bp-border);
background: var(--bp-bg);
color: var(--bp-text);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
transition: all 0.2s;
}
.subpanel-back:hover {
background: var(--bp-surface-elevated);
}
.subpanel-title {
font-size: 18px;
font-weight: 600;
}
.subpanel-content {
flex: 1;
overflow-y: auto;
padding: 24px;
}
/* Status Bar */
.worksheets-status {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 24px;
border-top: 1px solid var(--bp-border);
background: var(--bp-surface);
font-size: 12px;
}
.status-indicator {
width: 8px;
height: 8px;
border-radius: 50%;
background: #10b981;
}
.status-indicator.busy {
background: #f59e0b;
animation: statusPulse 1.5s infinite;
}
.status-indicator.error {
background: #ef4444;
}
@keyframes statusPulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.status-text {
color: var(--bp-text);
}
.status-detail {
color: var(--bp-text-muted);
}
/* ============================================
SUB-MODULE STYLES: Lerneinheiten Manager
============================================ */
.units-manager {
display: grid;
grid-template-columns: 320px 1fr;
gap: 24px;
height: 100%;
}
.units-sidebar {
background: var(--bp-surface);
border: 1px solid var(--bp-border);
border-radius: 12px;
display: flex;
flex-direction: column;
overflow: hidden;
}
.units-form {
padding: 16px;
border-bottom: 1px solid var(--bp-border);
}
.units-form h3 {
font-size: 14px;
font-weight: 600;
margin-bottom: 12px;
color: var(--bp-text-muted);
}
.units-form input {
width: 100%;
padding: 10px 12px;
margin-bottom: 8px;
border: 1px solid var(--bp-border);
border-radius: 8px;
background: var(--bp-bg);
color: var(--bp-text);
font-size: 13px;
}
.units-form input:focus {
outline: none;
border-color: var(--bp-primary);
}
.units-form .form-row {
display: flex;
gap: 8px;
}
.units-form .form-row input {
flex: 1;
}
.units-list {
flex: 1;
overflow-y: auto;
padding: 8px;
}
.unit-list-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s;
margin-bottom: 4px;
}
.unit-list-item:hover {
background: var(--bp-bg);
}
.unit-list-item.selected {
background: var(--bp-primary);
color: white;
}
.unit-info {
flex: 1;
min-width: 0;
}
.unit-name {
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.unit-meta {
font-size: 11px;
opacity: 0.7;
margin-top: 2px;
}
.unit-delete {
opacity: 0;
cursor: pointer;
padding: 4px;
font-size: 12px;
}
.unit-list-item:hover .unit-delete {
opacity: 0.6;
}
.unit-list-item:hover .unit-delete:hover {
opacity: 1;
color: #ef4444;
}
.units-main {
background: var(--bp-surface);
border: 1px solid var(--bp-border);
border-radius: 12px;
padding: 24px;
overflow-y: auto;
}
.units-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 300px;
text-align: center;
color: var(--bp-text-muted);
}
.units-empty-icon {
font-size: 48px;
margin-bottom: 16px;
opacity: 0.5;
}
/* ============================================
SUB-MODULE STYLES: File Upload
============================================ */
.upload-container {
max-width: 800px;
margin: 0 auto;
}
.upload-drop-zone {
border: 2px dashed var(--bp-border);
border-radius: 16px;
padding: 48px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
background: var(--bp-surface);
}
.upload-drop-zone:hover,
.upload-drop-zone.dragover {
border-color: var(--bp-primary);
background: rgba(59, 130, 246, 0.05);
}
.upload-icon {
font-size: 48px;
margin-bottom: 16px;
}
.upload-title {
font-size: 18px;
font-weight: 600;
margin-bottom: 8px;
}
.upload-hint {
font-size: 14px;
color: var(--bp-text-muted);
margin-bottom: 16px;
}
.upload-formats {
font-size: 12px;
color: var(--bp-text-muted);
padding: 8px 16px;
background: var(--bp-bg);
border-radius: 20px;
display: inline-block;
}
.upload-progress {
margin-top: 24px;
padding: 16px;
background: var(--bp-bg);
border-radius: 12px;
}
.upload-file-item {
display: flex;
align-items: center;
gap: 12px;
padding: 8px 0;
}
.upload-file-name {
flex: 1;
font-size: 13px;
}
.upload-file-status {
font-size: 12px;
color: var(--bp-text-muted);
}
/* ============================================
SUB-MODULE STYLES: Generator Tiles
============================================ */
.generator-options {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
margin-top: 24px;
}
.generator-option {
padding: 20px;
background: var(--bp-surface);
border: 1px solid var(--bp-border);
border-radius: 12px;
cursor: pointer;
transition: all 0.2s;
text-align: center;
}
.generator-option:hover {
border-color: var(--bp-primary);
transform: translateY(-2px);
}
.generator-option.selected {
border-color: var(--bp-primary);
background: rgba(59, 130, 246, 0.05);
}
.generator-option-icon {
font-size: 32px;
margin-bottom: 8px;
}
.generator-option-title {
font-weight: 600;
margin-bottom: 4px;
}
.generator-option-desc {
font-size: 12px;
color: var(--bp-text-muted);
}
/* ============================================
Legacy Support: Original 3-Column Layout
============================================ */
/* Linke Spalte - Lerneinheiten */
.worksheets-units-panel {
width: 280px;
border-right: 1px solid var(--bp-border);
display: flex;
flex-direction: column;
background: var(--bp-surface);
}
.units-header {
padding: 16px;
border-bottom: 1px solid var(--bp-border);
}
.units-header h3 {
font-size: 14px;
font-weight: 600;
color: var(--bp-text-muted);
margin-bottom: 12px;
}
/* Mittlere Spalte - Dateiliste */
.worksheets-files-panel {
width: 260px;
border-right: 1px solid var(--bp-border);
display: flex;
flex-direction: column;
background: var(--bp-bg);
}
.files-header {
padding: 12px 16px;
border-bottom: 1px solid var(--bp-border);
display: flex;
justify-content: space-between;
align-items: center;
}
.files-list {
flex: 1;
overflow-y: auto;
padding: 8px;
}
.file-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 12px;
margin-bottom: 2px;
border-radius: 6px;
cursor: pointer;
transition: background 0.2s;
}
.file-item:hover {
background: var(--bp-surface);
}
.file-item.active {
background: var(--bp-primary);
color: white;
}
/* Rechte Spalte - Preview */
.worksheets-preview-panel {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* Compare Wrapper */
.compare-wrapper {
flex: 1;
display: flex;
gap: 16px;
padding: 16px;
overflow: hidden;
}
.compare-section {
flex: 1;
display: flex;
flex-direction: column;
border-radius: 12px;
overflow: hidden;
background: var(--bp-surface);
border: 1px solid var(--bp-border);
}
.compare-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 16px;
background: var(--bp-bg);
border-bottom: 1px solid var(--bp-border);
font-size: 13px;
font-weight: 500;
}
.compare-body {
flex: 1;
overflow: auto;
padding: 16px;
display: flex;
justify-content: center;
align-items: flex-start;
}
.preview-img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
border-radius: 4px;
cursor: zoom-in;
}
/* Lightbox */
.lightbox {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.9);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 10000;
padding: 20px;
}
.lightbox.hidden {
display: none;
}
.lightbox-close {
position: absolute;
top: 20px;
right: 20px;
font-size: 32px;
color: white;
cursor: pointer;
opacity: 0.7;
transition: opacity 0.2s;
}
.lightbox-close:hover {
opacity: 1;
}
.lightbox-img {
max-width: 95%;
max-height: 85%;
object-fit: contain;
}
.lightbox-caption {
margin-top: 16px;
color: rgba(255, 255, 255, 0.7);
font-size: 14px;
}
"""
@staticmethod
def get_html() -> str:
"""HTML fuer das Worksheets-Modul mit Kachel-basierter Startansicht."""
return """
<!-- Worksheets Panel -->
<div id="panel-worksheets" class="panel-worksheets">
<!-- Main Tiles View -->
<div id="worksheets-tiles-view">
<!-- Header -->
<div class="worksheets-header">
<div class="worksheets-title-section">
<h1>Arbeitsblaetter Studio</h1>
<p class="worksheets-subtitle">Erstelle und verwalte Lernmaterialien fuer deine Schueler</p>
</div>
<div class="worksheets-nav">
<button class="worksheets-nav-btn active" onclick="showWorksheetsTiles()">Uebersicht</button>
<button class="worksheets-nav-btn" onclick="showWorksheetsManager()">Lerneinheiten</button>
</div>
</div>
<!-- Tiles Content -->
<div class="worksheets-content">
<!-- Erstellen & Verwalten -->
<div class="tiles-section">
<div class="tiles-section-header">
<div class="tiles-section-icon create">&#128221;</div>
<div>
<div class="tiles-section-title">Erstellen &amp; Verwalten</div>
<div class="tiles-section-desc">Lerneinheiten anlegen und Materialien hochladen</div>
</div>
</div>
<div class="worksheets-tiles">
<!-- Lerneinheiten -->
<div class="worksheet-tile" onclick="openWorksheetsSubpanel('units')">
<div class="tile-icon-container blue">&#128218;</div>
<div class="tile-title">Lerneinheiten</div>
<div class="tile-description">Erstelle und verwalte Lerneinheiten fuer deine Schueler. Ordne Materialien zu und verfolge den Fortschritt.</div>
<div class="tile-features">
<span class="tile-feature-tag">CRUD</span>
<span class="tile-feature-tag">Organisation</span>
</div>
<div class="tile-arrow">&#8594;</div>
</div>
<!-- Dateien hochladen -->
<div class="worksheet-tile" onclick="openWorksheetsSubpanel('upload')">
<div class="tile-icon-container green">&#128228;</div>
<div class="tile-title">Dateien hochladen</div>
<div class="tile-description">Lade Arbeitsblaetter, Bilder und PDFs hoch. Unterstuetzt Drag &amp; Drop und Batch-Upload.</div>
<div class="tile-features">
<span class="tile-feature-tag">PDF</span>
<span class="tile-feature-tag">Bilder</span>
<span class="tile-feature-tag">Batch</span>
</div>
<div class="tile-arrow">&#8594;</div>
</div>
</div>
</div>
<!-- Verarbeiten -->
<div class="tiles-section">
<div class="tiles-section-header">
<div class="tiles-section-icon process">&#9881;</div>
<div>
<div class="tiles-section-title">Verarbeiten</div>
<div class="tiles-section-desc">Automatische Aufbereitung und Bereinigung</div>
</div>
</div>
<div class="worksheets-tiles">
<!-- Dateien bereinigen -->
<div class="worksheet-tile" onclick="openWorksheetsSubpanel('clean')">
<div class="tile-icon-container cyan">&#128295;</div>
<div class="tile-title">Dateien bereinigen</div>
<div class="tile-description">OCR-Verarbeitung und automatischer Neuaufbau von gescannten Arbeitsblaettern.</div>
<div class="tile-features">
<span class="tile-feature-tag">OCR</span>
<span class="tile-feature-tag">Neuaufbau</span>
<span class="tile-feature-tag">AI</span>
</div>
<div class="tile-arrow">&#8594;</div>
</div>
<!-- Lernflows -->
<div class="worksheet-tile" onclick="openWorksheetsSubpanel('lernflows')">
<div class="tile-icon-container orange">&#127919;</div>
<div class="tile-title">Lernflows</div>
<div class="tile-description">Interaktive Lernpfade erstellen. Kombiniere verschiedene Aufgabentypen zu einem Flow.</div>
<div class="tile-features">
<span class="tile-feature-tag">Interaktiv</span>
<span class="tile-feature-tag">Sequenzen</span>
</div>
<div class="tile-arrow">&#8594;</div>
</div>
</div>
</div>
<!-- Generieren -->
<div class="tiles-section">
<div class="tiles-section-header">
<div class="tiles-section-icon generate">&#10024;</div>
<div>
<div class="tiles-section-title">Druckdateien generieren</div>
<div class="tiles-section-desc">AI-gestuetzte Erstellung von Uebungsmaterialien</div>
</div>
</div>
<div class="worksheets-tiles">
<!-- Multiple Choice -->
<div class="worksheet-tile" onclick="openWorksheetsSubpanel('mc')">
<div class="tile-icon-container purple">&#9989;</div>
<div class="tile-title">Multiple Choice</div>
<div class="tile-description">Generiere Multiple-Choice-Tests aus deinen Lernmaterialien. Inkl. Antwortschluessel.</div>
<div class="tile-features">
<span class="tile-feature-tag">Tests</span>
<span class="tile-feature-tag">AI</span>
<span class="tile-feature-tag">Druckbar</span>
</div>
<div class="tile-arrow">&#8594;</div>
</div>
<!-- Lueckentexte -->
<div class="worksheet-tile" onclick="openWorksheetsSubpanel('cloze')">
<div class="tile-icon-container red">&#128221;</div>
<div class="tile-title">Lueckentexte</div>
<div class="tile-description">Erstelle Lueckentexte fuer effektives Vokabel- und Faktenlernen.</div>
<div class="tile-features">
<span class="tile-feature-tag">Uebung</span>
<span class="tile-feature-tag">AI</span>
<span class="tile-feature-tag">Druckbar</span>
</div>
<div class="tile-arrow">&#8594;</div>
</div>
<!-- Mindmap -->
<div class="worksheet-tile" onclick="openWorksheetsSubpanel('mindmap')">
<div class="tile-icon-container teal">&#128506;</div>
<div class="tile-title">Mindmap</div>
<div class="tile-description">Visualisiere Zusammenhaenge in einer uebersichtlichen Mindmap.</div>
<div class="tile-features">
<span class="tile-feature-tag">Visualisierung</span>
<span class="tile-feature-tag">AI</span>
</div>
<div class="tile-arrow">&#8594;</div>
</div>
<!-- Fragen & Antworten -->
<div class="worksheet-tile" onclick="openWorksheetsSubpanel('qa')">
<div class="tile-icon-container pink">&#10067;</div>
<div class="tile-title">Fragen &amp; Antworten</div>
<div class="tile-description">Generiere Fragenkataloge und Lernkarten aus deinen Materialien.</div>
<div class="tile-features">
<span class="tile-feature-tag">Lernkarten</span>
<span class="tile-feature-tag">AI</span>
</div>
<div class="tile-arrow">&#8594;</div>
</div>
</div>
</div>
<!-- Uebersetzen -->
<div class="tiles-section">
<div class="tiles-section-header">
<div class="tiles-section-icon translate">&#127760;</div>
<div>
<div class="tiles-section-title">Uebersetzen</div>
<div class="tiles-section-desc">Mehrsprachige Unterstuetzung fuer alle Materialien</div>
</div>
</div>
<div class="worksheets-tiles">
<!-- Uebersetzungen -->
<div class="worksheet-tile" onclick="openWorksheetsSubpanel('translate')">
<div class="tile-icon-container purple">&#128483;</div>
<div class="tile-title">Uebersetzungen</div>
<div class="tile-description">Uebersetze Arbeitsblaetter und generierte Inhalte in verschiedene Sprachen.</div>
<div class="tile-features">
<span class="tile-feature-tag">DE</span>
<span class="tile-feature-tag">EN</span>
<span class="tile-feature-tag">TR</span>
<span class="tile-feature-tag">AR</span>
<span class="tile-feature-tag">+10</span>
</div>
<div class="tile-arrow">&#8594;</div>
</div>
</div>
</div>
</div>
</div>
<!-- Sub-Panel: Lerneinheiten Manager -->
<div id="worksheets-subpanel-units" class="worksheets-subpanel">
<div class="subpanel-header">
<button class="subpanel-back" onclick="closeWorksheetsSubpanel()">&#8592;</button>
<div class="subpanel-title">Lerneinheiten verwalten</div>
</div>
<div class="subpanel-content">
<div class="units-manager">
<div class="units-sidebar">
<div class="units-form">
<h3>Neue Lerneinheit</h3>
<input type="text" id="new-unit-student" placeholder="Schueler/in">
<div class="form-row">
<input type="text" id="new-unit-subject" placeholder="Fach">
<input type="text" id="new-unit-grade" placeholder="Klasse">
</div>
<input type="text" id="new-unit-title" placeholder="Thema / Titel">
<button class="btn btn-primary" onclick="createNewUnit()" style="width: 100%; margin-top: 8px;">Anlegen</button>
</div>
<div class="units-list" id="units-list-container">
<!-- Dynamisch gefuellt -->
</div>
</div>
<div class="units-main" id="units-detail-container">
<div class="units-empty">
<div class="units-empty-icon">&#128218;</div>
<h3>Lerneinheit auswaehlen</h3>
<p>Waehle eine Lerneinheit aus der Liste oder erstelle eine neue.</p>
</div>
</div>
</div>
</div>
</div>
<!-- Sub-Panel: Dateien hochladen -->
<div id="worksheets-subpanel-upload" class="worksheets-subpanel">
<div class="subpanel-header">
<button class="subpanel-back" onclick="closeWorksheetsSubpanel()">&#8592;</button>
<div class="subpanel-title">Dateien hochladen</div>
</div>
<div class="subpanel-content">
<div class="upload-container">
<div class="upload-drop-zone" id="worksheets-upload-zone">
<div class="upload-icon">&#128228;</div>
<div class="upload-title">Dateien hochladen</div>
<div class="upload-hint">Dateien hierher ziehen oder klicken zum Auswaehlen</div>
<div class="upload-formats">PDF, JPG, PNG - max. 50MB pro Datei</div>
<input type="file" id="worksheets-file-input" multiple accept=".pdf,.jpg,.jpeg,.png" hidden>
</div>
<div class="upload-progress" id="upload-progress" style="display: none;">
<!-- Dynamisch gefuellt -->
</div>
</div>
</div>
</div>
<!-- Sub-Panel: Dateien bereinigen -->
<div id="worksheets-subpanel-clean" class="worksheets-subpanel">
<div class="subpanel-header">
<button class="subpanel-back" onclick="closeWorksheetsSubpanel()">&#8592;</button>
<div class="subpanel-title">Dateien bereinigen</div>
</div>
<div class="subpanel-content">
<p style="color: var(--bp-text-muted); margin-bottom: 24px;">Waehle Dateien aus einer Lerneinheit und starte den OCR-Neuaufbau.</p>
<div id="clean-files-list">
<!-- Wird dynamisch geladen -->
</div>
</div>
</div>
<!-- Sub-Panel: MC Generator -->
<div id="worksheets-subpanel-mc" class="worksheets-subpanel">
<div class="subpanel-header">
<button class="subpanel-back" onclick="closeWorksheetsSubpanel()">&#8592;</button>
<div class="subpanel-title">Multiple Choice Generator</div>
</div>
<div class="subpanel-content">
<p style="color: var(--bp-text-muted); margin-bottom: 24px;">Generiere Multiple-Choice-Tests aus deinen Lernmaterialien.</p>
<div class="generator-options">
<div class="generator-option" onclick="selectGeneratorOption(this, 'easy')">
<div class="generator-option-icon">&#127775;</div>
<div class="generator-option-title">Einfach</div>
<div class="generator-option-desc">5 Fragen, grundlegend</div>
</div>
<div class="generator-option selected" onclick="selectGeneratorOption(this, 'medium')">
<div class="generator-option-icon">&#128170;</div>
<div class="generator-option-title">Mittel</div>
<div class="generator-option-desc">10 Fragen, ausgewogen</div>
</div>
<div class="generator-option" onclick="selectGeneratorOption(this, 'hard')">
<div class="generator-option-icon">&#128293;</div>
<div class="generator-option-title">Schwer</div>
<div class="generator-option-desc">15 Fragen, anspruchsvoll</div>
</div>
</div>
<button class="btn btn-primary" style="margin-top: 24px;" onclick="generateMC()">&#10024; MC-Test generieren</button>
</div>
</div>
<!-- Sub-Panel: Lueckentexte -->
<div id="worksheets-subpanel-cloze" class="worksheets-subpanel">
<div class="subpanel-header">
<button class="subpanel-back" onclick="closeWorksheetsSubpanel()">&#8592;</button>
<div class="subpanel-title">Lueckentext Generator</div>
</div>
<div class="subpanel-content">
<p style="color: var(--bp-text-muted); margin-bottom: 24px;">Erstelle Lueckentexte aus deinen Lernmaterialien.</p>
<button class="btn btn-primary" onclick="generateCloze()">&#10024; Lueckentext generieren</button>
</div>
</div>
<!-- Sub-Panel: Mindmap -->
<div id="worksheets-subpanel-mindmap" class="worksheets-subpanel">
<div class="subpanel-header">
<button class="subpanel-back" onclick="closeWorksheetsSubpanel()">&#8592;</button>
<div class="subpanel-title">Mindmap Generator</div>
</div>
<div class="subpanel-content">
<p style="color: var(--bp-text-muted); margin-bottom: 24px;">Erstelle eine visuelle Mindmap aus deinen Lernmaterialien.</p>
<button class="btn btn-primary" onclick="generateMindmap()">&#10024; Mindmap generieren</button>
</div>
</div>
<!-- Sub-Panel: Q&A -->
<div id="worksheets-subpanel-qa" class="worksheets-subpanel">
<div class="subpanel-header">
<button class="subpanel-back" onclick="closeWorksheetsSubpanel()">&#8592;</button>
<div class="subpanel-title">Fragen &amp; Antworten Generator</div>
</div>
<div class="subpanel-content">
<p style="color: var(--bp-text-muted); margin-bottom: 24px;">Generiere Fragen und Antworten fuer Lernkarten.</p>
<button class="btn btn-primary" onclick="generateQA()">&#10024; Fragen generieren</button>
</div>
</div>
<!-- Sub-Panel: Lernflows -->
<div id="worksheets-subpanel-lernflows" class="worksheets-subpanel">
<div class="subpanel-header">
<button class="subpanel-back" onclick="closeWorksheetsSubpanel()">&#8592;</button>
<div class="subpanel-title">Lernflows erstellen</div>
</div>
<div class="subpanel-content">
<p style="color: var(--bp-text-muted); margin-bottom: 24px;">Erstelle interaktive Lernpfade aus verschiedenen Aufgabentypen.</p>
<button class="btn btn-primary" onclick="createLernflow()">&#127919; Neuen Lernflow erstellen</button>
</div>
</div>
<!-- Sub-Panel: Uebersetzen -->
<div id="worksheets-subpanel-translate" class="worksheets-subpanel">
<div class="subpanel-header">
<button class="subpanel-back" onclick="closeWorksheetsSubpanel()">&#8592;</button>
<div class="subpanel-title">Uebersetzungen</div>
</div>
<div class="subpanel-content">
<p style="color: var(--bp-text-muted); margin-bottom: 24px;">Uebersetze deine Materialien in verschiedene Sprachen.</p>
<div class="generator-options">
<div class="generator-option" onclick="selectLanguage('en')">
<div class="generator-option-icon">&#127468;&#127463;</div>
<div class="generator-option-title">Englisch</div>
</div>
<div class="generator-option" onclick="selectLanguage('tr')">
<div class="generator-option-icon">&#127481;&#127479;</div>
<div class="generator-option-title">Tuerkisch</div>
</div>
<div class="generator-option" onclick="selectLanguage('ar')">
<div class="generator-option-icon">&#127462;&#127466;</div>
<div class="generator-option-title">Arabisch</div>
</div>
<div class="generator-option" onclick="selectLanguage('uk')">
<div class="generator-option-icon">&#127482;&#127462;</div>
<div class="generator-option-title">Ukrainisch</div>
</div>
</div>
</div>
</div>
<!-- Status Bar -->
<div class="worksheets-status">
<span class="status-indicator" id="ws-status-indicator"></span>
<span class="status-text" id="ws-status-text">Bereit</span>
<span class="status-detail" id="ws-status-detail"></span>
</div>
</div>
<!-- Lightbox -->
<div id="lightbox" class="lightbox hidden">
<span class="lightbox-close" id="lightbox-close">&times;</span>
<img id="lightbox-img" class="lightbox-img" src="" alt="">
<div id="lightbox-caption" class="lightbox-caption"></div>
</div>
"""
@staticmethod
def get_js() -> str:
"""JavaScript fuer das Worksheets-Modul."""
return """
// =============================================
// WORKSHEETS MODULE - Refactored with Tiles
// =============================================
let worksheetsInitialized = false;
let worksheetsUnits = [];
let worksheetsCurrentUnit = null;
function loadWorksheetsModule() {
if (worksheetsInitialized) {
console.log('Worksheets module already initialized');
return;
}
console.log('Loading Worksheets Module (Tiles)...');
// Initialize upload zone
initWorksheetsUpload();
// Load units
loadWorksheetsUnits();
// Init lightbox
initWorksheetsLightbox();
worksheetsInitialized = true;
console.log('Worksheets Module loaded successfully');
}
// =============================================
// VIEW SWITCHING
// =============================================
function showWorksheetsTiles() {
document.getElementById('worksheets-tiles-view').style.display = 'block';
closeWorksheetsSubpanel();
document.querySelectorAll('.worksheets-nav-btn').forEach(btn => btn.classList.remove('active'));
document.querySelector('.worksheets-nav-btn').classList.add('active');
}
function showWorksheetsManager() {
openWorksheetsSubpanel('units');
document.querySelectorAll('.worksheets-nav-btn').forEach(btn => btn.classList.remove('active'));
document.querySelectorAll('.worksheets-nav-btn')[1].classList.add('active');
}
// =============================================
// SUBPANEL NAVIGATION
// =============================================
function openWorksheetsSubpanel(panelId) {
// Hide tiles view
document.getElementById('worksheets-tiles-view').style.display = 'none';
// Hide all subpanels
document.querySelectorAll('.worksheets-subpanel').forEach(p => {
p.classList.remove('active');
});
// Show selected subpanel
const panel = document.getElementById('worksheets-subpanel-' + panelId);
if (panel) {
panel.classList.add('active');
}
}
function closeWorksheetsSubpanel() {
document.querySelectorAll('.worksheets-subpanel').forEach(p => {
p.classList.remove('active');
});
document.getElementById('worksheets-tiles-view').style.display = 'block';
// Reset nav buttons
document.querySelectorAll('.worksheets-nav-btn').forEach(btn => btn.classList.remove('active'));
document.querySelector('.worksheets-nav-btn').classList.add('active');
}
// =============================================
// STATUS
// =============================================
function setWorksheetsStatus(text, detail = '', state = 'idle') {
const indicator = document.getElementById('ws-status-indicator');
const textEl = document.getElementById('ws-status-text');
const detailEl = document.getElementById('ws-status-detail');
if (textEl) textEl.textContent = text;
if (detailEl) detailEl.textContent = detail;
if (indicator) {
indicator.classList.remove('busy', 'error');
if (state === 'busy') indicator.classList.add('busy');
else if (state === 'error') indicator.classList.add('error');
}
}
// =============================================
// LERNEINHEITEN
// =============================================
async function loadWorksheetsUnits() {
try {
const resp = await fetch('/api/learning-units/');
if (!resp.ok) {
console.error('Failed to load units');
return;
}
worksheetsUnits = await resp.json();
renderWorksheetsUnits();
} catch (e) {
console.error('Error loading units:', e);
}
}
function renderWorksheetsUnits() {
const container = document.getElementById('units-list-container');
if (!container) return;
if (!worksheetsUnits.length) {
container.innerHTML = '<div style="padding: 16px; text-align: center; color: var(--bp-text-muted);">Keine Lerneinheiten vorhanden</div>';
return;
}
container.innerHTML = worksheetsUnits.map(unit => `
<div class="unit-list-item ${worksheetsCurrentUnit?.id === unit.id ? 'selected' : ''}"
onclick="selectWorksheetsUnit('${unit.id}')">
<div class="unit-info">
<div class="unit-name">${unit.label || unit.title || 'Lerneinheit'}</div>
<div class="unit-meta">${unit.subject || ''} ${unit.grade || ''} - ${(unit.worksheet_files || []).length} Dateien</div>
</div>
<span class="unit-delete" onclick="event.stopPropagation(); deleteWorksheetsUnit('${unit.id}')">&#128465;</span>
</div>
`).join('');
}
function selectWorksheetsUnit(unitId) {
worksheetsCurrentUnit = worksheetsUnits.find(u => u.id === unitId);
renderWorksheetsUnits();
showUnitDetail();
}
function showUnitDetail() {
const container = document.getElementById('units-detail-container');
if (!container || !worksheetsCurrentUnit) return;
const unit = worksheetsCurrentUnit;
const files = unit.worksheet_files || [];
container.innerHTML = `
<h2 style="margin-bottom: 8px;">${unit.label || unit.title}</h2>
<p style="color: var(--bp-text-muted); margin-bottom: 24px;">
${unit.student_name ? 'Schueler: ' + unit.student_name + ' | ' : ''}
${unit.subject || ''} ${unit.grade || ''}
</p>
<h3 style="font-size: 14px; color: var(--bp-text-muted); margin-bottom: 12px;">
Zugeordnete Dateien (${files.length})
</h3>
${files.length ? `
<div style="display: flex; flex-direction: column; gap: 8px;">
${files.map(f => `
<div style="display: flex; align-items: center; justify-content: space-between; padding: 12px; background: var(--bp-bg); border-radius: 8px;">
<span>${f}</span>
<span style="cursor: pointer; color: var(--bp-text-muted);" onclick="removeFileFromUnit('${f}')">&#10005;</span>
</div>
`).join('')}
</div>
` : '<p style="color: var(--bp-text-muted);">Keine Dateien zugeordnet</p>'}
<div style="margin-top: 24px; display: flex; gap: 12px;">
<button class="btn btn-ghost" onclick="openWorksheetsSubpanel('upload')">Dateien hinzufuegen</button>
<button class="btn btn-ghost" onclick="openWorksheetsSubpanel('mc')">MC-Test erstellen</button>
</div>
`;
}
async function createNewUnit() {
const student = document.getElementById('new-unit-student')?.value.trim() || '';
const subject = document.getElementById('new-unit-subject')?.value.trim() || '';
const grade = document.getElementById('new-unit-grade')?.value.trim() || '';
const title = document.getElementById('new-unit-title')?.value.trim() || '';
if (!title) {
alert('Bitte einen Titel eingeben');
return;
}
try {
setWorksheetsStatus('Erstelle Lerneinheit...', '', 'busy');
const resp = await fetch('/api/learning-units/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
label: title,
student_name: student,
subject: subject,
grade: grade,
title: title
})
});
if (!resp.ok) {
throw new Error('Failed to create unit');
}
const newUnit = await resp.json();
worksheetsUnits.push(newUnit);
worksheetsCurrentUnit = newUnit;
// Clear form
['new-unit-student', 'new-unit-subject', 'new-unit-grade', 'new-unit-title'].forEach(id => {
const el = document.getElementById(id);
if (el) el.value = '';
});
renderWorksheetsUnits();
showUnitDetail();
setWorksheetsStatus('Lerneinheit erstellt', '');
} catch (e) {
console.error(e);
setWorksheetsStatus('Fehler', String(e), 'error');
}
}
async function deleteWorksheetsUnit(unitId) {
if (!confirm('Lerneinheit wirklich loeschen?')) return;
try {
setWorksheetsStatus('Loesche Lerneinheit...', '', 'busy');
const resp = await fetch(`/api/learning-units/${unitId}`, { method: 'DELETE' });
if (!resp.ok) {
throw new Error('Failed to delete unit');
}
worksheetsUnits = worksheetsUnits.filter(u => u.id !== unitId);
if (worksheetsCurrentUnit?.id === unitId) {
worksheetsCurrentUnit = null;
}
renderWorksheetsUnits();
setWorksheetsStatus('Lerneinheit geloescht', '');
} catch (e) {
console.error(e);
setWorksheetsStatus('Fehler', String(e), 'error');
}
}
// =============================================
// FILE UPLOAD
// =============================================
function initWorksheetsUpload() {
const dropZone = document.getElementById('worksheets-upload-zone');
const fileInput = document.getElementById('worksheets-file-input');
if (!dropZone || !fileInput) return;
dropZone.addEventListener('click', () => fileInput.click());
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('dragover');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('dragover');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('dragover');
if (e.dataTransfer.files.length) {
uploadWorksheetsFiles(e.dataTransfer.files);
}
});
fileInput.addEventListener('change', () => {
if (fileInput.files.length) {
uploadWorksheetsFiles(fileInput.files);
}
});
}
async function uploadWorksheetsFiles(files) {
const formData = new FormData();
for (const file of files) {
formData.append('files', file);
}
try {
setWorksheetsStatus('Lade hoch...', `${files.length} Datei(en)`, 'busy');
const resp = await fetch('/api/upload', {
method: 'POST',
body: formData
});
if (!resp.ok) {
throw new Error('Upload failed');
}
setWorksheetsStatus('Upload erfolgreich', `${files.length} Datei(en)`);
// If unit selected, offer to add files
if (worksheetsCurrentUnit) {
// Could auto-add here
}
} catch (e) {
console.error(e);
setWorksheetsStatus('Upload fehlgeschlagen', String(e), 'error');
}
}
// =============================================
// GENERATORS - Connected to /api/worksheets/
// =============================================
let generatorDifficulty = 'medium';
let generatorNumQuestions = 10;
let generatedContent = null;
function selectGeneratorOption(el, difficulty) {
document.querySelectorAll('.generator-option').forEach(o => o.classList.remove('selected'));
el.classList.add('selected');
generatorDifficulty = difficulty;
generatorNumQuestions = difficulty === 'easy' ? 5 : difficulty === 'medium' ? 10 : 15;
}
// Get source text from current unit or prompt user
async function getSourceText() {
if (worksheetsCurrentUnit && worksheetsCurrentUnit.worksheet_files?.length > 0) {
// In production: extract text from files via OCR/parser
// For now, prompt user for text input
const text = prompt('Gib den Quelltext ein (min. 50 Zeichen) oder fuege Inhalt aus deiner Lerneinheit ein:');
return text;
}
return prompt('Gib den Quelltext ein (min. 50 Zeichen):');
}
async function generateMC() {
const sourceText = await getSourceText();
if (!sourceText || sourceText.length < 50) {
alert('Bitte einen laengeren Text eingeben (mind. 50 Zeichen).');
return;
}
setWorksheetsStatus('Generiere MC-Test...', '', 'busy');
try {
const resp = await fetch('/api/worksheets/generate/multiple-choice', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
source_text: sourceText,
num_questions: generatorNumQuestions,
difficulty: generatorDifficulty,
topic: worksheetsCurrentUnit?.title || 'Lerneinheit',
subject: worksheetsCurrentUnit?.subject || null
})
});
const result = await resp.json();
if (!result.success) {
throw new Error(result.error || 'Generation failed');
}
generatedContent = result.content;
setWorksheetsStatus('MC-Test generiert', `${result.content.data.questions.length} Fragen`);
showGeneratedMC(result.content);
} catch (e) {
console.error(e);
setWorksheetsStatus('Generation fehlgeschlagen', String(e), 'error');
alert('Fehler bei der MC-Generierung: ' + e.message);
}
}
function showGeneratedMC(content) {
const questions = content.data.questions;
const container = document.querySelector('#worksheets-subpanel-mc .subpanel-content');
let html = `
<div style="background: var(--bp-surface); border: 1px solid var(--bp-border); border-radius: 12px; padding: 20px; margin-bottom: 20px;">
<h3 style="margin-bottom: 16px;">Generierte Fragen (${questions.length})</h3>
<div style="max-height: 400px; overflow-y: auto;">
`;
questions.forEach((q, i) => {
html += `
<div style="padding: 12px; margin-bottom: 12px; background: var(--bp-bg); border-radius: 8px;">
<strong>Frage ${i + 1}:</strong> ${q.question}
<ul style="margin: 8px 0 0 20px;">
${q.options.map(opt => `
<li style="color: ${opt.is_correct ? '#10b981' : 'var(--bp-text)'};">
${opt.text} ${opt.is_correct ? '' : ''}
</li>
`).join('')}
</ul>
</div>
`;
});
html += `
</div>
<div style="display: flex; gap: 12px; margin-top: 16px;">
<button class="btn btn-primary" onclick="exportGeneratedContent('h5p')">Als H5P exportieren</button>
<button class="btn btn-ghost" onclick="exportGeneratedContent('json')">Als JSON exportieren</button>
<button class="btn btn-ghost" onclick="printGeneratedContent()">Drucken</button>
</div>
</div>
`;
container.innerHTML = html;
}
async function generateCloze() {
const sourceText = await getSourceText();
if (!sourceText || sourceText.length < 50) {
alert('Bitte einen laengeren Text eingeben (mind. 50 Zeichen).');
return;
}
setWorksheetsStatus('Generiere Lueckentext...', '', 'busy');
try {
const resp = await fetch('/api/worksheets/generate/cloze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
source_text: sourceText,
num_gaps: generatorDifficulty === 'easy' ? 3 : generatorDifficulty === 'medium' ? 5 : 8,
difficulty: generatorDifficulty,
cloze_type: 'fill_in',
topic: worksheetsCurrentUnit?.title || null
})
});
const result = await resp.json();
if (!result.success) {
throw new Error(result.error || 'Generation failed');
}
generatedContent = result.content;
setWorksheetsStatus('Lueckentext generiert', `${result.content.data.gaps.length} Luecken`);
showGeneratedCloze(result.content);
} catch (e) {
console.error(e);
setWorksheetsStatus('Generation fehlgeschlagen', String(e), 'error');
alert('Fehler bei der Lueckentext-Generierung: ' + e.message);
}
}
function showGeneratedCloze(content) {
const container = document.querySelector('#worksheets-subpanel-cloze .subpanel-content');
const clozeData = content.data;
let html = `
<div style="background: var(--bp-surface); border: 1px solid var(--bp-border); border-radius: 12px; padding: 20px; margin-bottom: 20px;">
<h3 style="margin-bottom: 16px;">Generierter Lueckentext</h3>
<div style="padding: 16px; background: var(--bp-bg); border-radius: 8px; line-height: 1.8; margin-bottom: 16px;">
${clozeData.text_with_gaps}
</div>
<h4 style="margin: 16px 0 8px;">Loesungen:</h4>
<div style="display: flex; flex-wrap: wrap; gap: 8px;">
${clozeData.gaps.map((g, i) => `
<span style="padding: 4px 12px; background: #10b981; color: white; border-radius: 16px; font-size: 13px;">
${i + 1}. ${g.answer}
</span>
`).join('')}
</div>
<div style="display: flex; gap: 12px; margin-top: 16px;">
<button class="btn btn-primary" onclick="exportGeneratedContent('h5p')">Als H5P exportieren</button>
<button class="btn btn-ghost" onclick="printGeneratedContent()">Drucken</button>
</div>
</div>
`;
container.innerHTML = html;
}
async function generateMindmap() {
const sourceText = await getSourceText();
if (!sourceText || sourceText.length < 50) {
alert('Bitte einen laengeren Text eingeben (mind. 50 Zeichen).');
return;
}
setWorksheetsStatus('Generiere Mindmap...', '', 'busy');
try {
const resp = await fetch('/api/worksheets/generate/mindmap', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
source_text: sourceText,
topic: worksheetsCurrentUnit?.title || 'Thema',
max_depth: 3
})
});
const result = await resp.json();
if (!result.success) {
throw new Error(result.error || 'Generation failed');
}
generatedContent = result.content;
setWorksheetsStatus('Mindmap generiert', `${result.content.data.mindmap.total_nodes} Knoten`);
showGeneratedMindmap(result.content);
} catch (e) {
console.error(e);
setWorksheetsStatus('Generation fehlgeschlagen', String(e), 'error');
alert('Fehler bei der Mindmap-Generierung: ' + e.message);
}
}
function showGeneratedMindmap(content) {
const container = document.querySelector('#worksheets-subpanel-mindmap .subpanel-content');
const mindmap = content.data.mindmap;
const mermaid = content.data.mermaid;
let html = `
<div style="background: var(--bp-surface); border: 1px solid var(--bp-border); border-radius: 12px; padding: 20px; margin-bottom: 20px;">
<h3 style="margin-bottom: 16px;">Generierte Mindmap: ${mindmap.title}</h3>
<div style="padding: 16px; background: var(--bp-bg); border-radius: 8px; margin-bottom: 16px;">
<pre style="font-family: monospace; font-size: 12px; white-space: pre-wrap;">${mermaid}</pre>
</div>
<h4 style="margin: 16px 0 8px;">Struktur:</h4>
<div style="padding: 16px; background: var(--bp-bg); border-radius: 8px;">
${renderMindmapNode(mindmap.root)}
</div>
<div style="display: flex; gap: 12px; margin-top: 16px;">
<button class="btn btn-primary" onclick="copyMermaidCode()">Mermaid-Code kopieren</button>
<button class="btn btn-ghost" onclick="exportGeneratedContent('json')">Als JSON exportieren</button>
</div>
</div>
`;
container.innerHTML = html;
}
function renderMindmapNode(node, indent = 0) {
const padding = indent * 20;
let html = `<div style="padding-left: ${padding}px; margin: 4px 0;">
<span style="color: ${node.color || 'var(--bp-text)'}; font-weight: ${indent === 0 ? 'bold' : 'normal'};">
${indent > 0 ? '├─ ' : ''}${node.label}
</span>
</div>`;
if (node.children) {
node.children.forEach(child => {
html += renderMindmapNode(child, indent + 1);
});
}
return html;
}
function copyMermaidCode() {
if (generatedContent?.data?.mermaid) {
navigator.clipboard.writeText(generatedContent.data.mermaid);
alert('Mermaid-Code in Zwischenablage kopiert!');
}
}
async function generateQA() {
const sourceText = await getSourceText();
if (!sourceText || sourceText.length < 50) {
alert('Bitte einen laengeren Text eingeben (mind. 50 Zeichen).');
return;
}
setWorksheetsStatus('Generiere Quiz...', '', 'busy');
try {
const resp = await fetch('/api/worksheets/generate/quiz', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
source_text: sourceText,
quiz_types: ['true_false', 'matching'],
num_items: generatorDifficulty === 'easy' ? 3 : 5,
difficulty: generatorDifficulty,
topic: worksheetsCurrentUnit?.title || null
})
});
const result = await resp.json();
if (!result.success) {
throw new Error(result.error || 'Generation failed');
}
generatedContent = result.content;
const totalItems = (result.content.data.true_false_questions?.length || 0) +
(result.content.data.matching_pairs?.length || 0);
setWorksheetsStatus('Quiz generiert', `${totalItems} Items`);
showGeneratedQuiz(result.content);
} catch (e) {
console.error(e);
setWorksheetsStatus('Generation fehlgeschlagen', String(e), 'error');
alert('Fehler bei der Quiz-Generierung: ' + e.message);
}
}
function showGeneratedQuiz(content) {
const container = document.querySelector('#worksheets-subpanel-qa .subpanel-content');
const quiz = content.data;
let html = `
<div style="background: var(--bp-surface); border: 1px solid var(--bp-border); border-radius: 12px; padding: 20px; margin-bottom: 20px;">
<h3 style="margin-bottom: 16px;">Generiertes Quiz</h3>
`;
if (quiz.true_false_questions?.length > 0) {
html += `<h4 style="margin: 16px 0 8px;">Richtig/Falsch Fragen:</h4>`;
quiz.true_false_questions.forEach((q, i) => {
html += `
<div style="padding: 12px; margin-bottom: 8px; background: var(--bp-bg); border-radius: 8px;">
<strong>${i + 1}.</strong> ${q.statement}
<span style="color: ${q.is_true ? '#10b981' : '#ef4444'}; margin-left: 12px;">
${q.is_true ? '✓ Richtig' : '✗ Falsch'}
</span>
</div>
`;
});
}
if (quiz.matching_pairs?.length > 0) {
html += `<h4 style="margin: 16px 0 8px;">Zuordnungsaufgaben:</h4>`;
quiz.matching_pairs.forEach((p, i) => {
html += `
<div style="padding: 12px; margin-bottom: 8px; background: var(--bp-bg); border-radius: 8px; display: flex; align-items: center; gap: 12px;">
<span style="flex: 1;">${p.left}</span>
<span style="color: var(--bp-text-muted);">↔</span>
<span style="flex: 1;">${p.right}</span>
</div>
`;
});
}
html += `
<div style="display: flex; gap: 12px; margin-top: 16px;">
<button class="btn btn-primary" onclick="exportGeneratedContent('h5p')">Als H5P exportieren</button>
<button class="btn btn-ghost" onclick="printGeneratedContent()">Drucken</button>
</div>
</div>
`;
container.innerHTML = html;
}
// Export functions
async function exportGeneratedContent(format) {
if (!generatedContent) {
alert('Kein generierter Inhalt vorhanden.');
return;
}
if (format === 'h5p' && generatedContent.h5p_format) {
// Download H5P format as JSON (real H5P package would need backend support)
const blob = new Blob([JSON.stringify(generatedContent.h5p_format, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `content_h5p_${generatedContent.id}.json`;
a.click();
URL.revokeObjectURL(url);
} else if (format === 'json') {
const blob = new Blob([JSON.stringify(generatedContent.data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `content_${generatedContent.content_type}_${generatedContent.id}.json`;
a.click();
URL.revokeObjectURL(url);
}
}
function printGeneratedContent() {
window.print();
}
function createLernflow() {
alert('Lernflow-Editor: Kombiniere mehrere Generatoren zu einem interaktiven Lernpfad. Kommt in einer spaeteren Version!');
}
function selectLanguage(lang) {
alert('Uebersetzung nach ' + lang.toUpperCase() + ': Diese Funktion wird mit DeepL/LLM-Integration umgesetzt. Kommt bald!');
}
// =============================================
// LIGHTBOX
// =============================================
function initWorksheetsLightbox() {
const lightbox = document.getElementById('lightbox');
const closeBtn = document.getElementById('lightbox-close');
if (closeBtn) {
closeBtn.addEventListener('click', closeLightbox);
}
if (lightbox) {
lightbox.addEventListener('click', (e) => {
if (e.target === lightbox) closeLightbox();
});
}
}
function openLightbox(src, caption) {
const lightbox = document.getElementById('lightbox');
const img = document.getElementById('lightbox-img');
const captionEl = document.getElementById('lightbox-caption');
if (!lightbox || !src) return;
img.src = src;
captionEl.textContent = caption || '';
lightbox.classList.remove('hidden');
}
function closeLightbox() {
const lightbox = document.getElementById('lightbox');
if (lightbox) lightbox.classList.add('hidden');
}
// =============================================
// SHOW PANEL
// =============================================
function showWorksheetsPanel() {
console.log('showWorksheetsPanel called');
hideAllPanels();
if (typeof hideStudioSubMenu === 'function') hideStudioSubMenu();
const panel = document.getElementById('panel-worksheets');
if (panel) {
panel.style.display = 'flex';
loadWorksheetsModule();
console.log('Worksheets panel shown');
} else {
console.error('panel-worksheets not found');
}
}
// Escape key to close subpanels
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
closeLightbox();
if (document.querySelector('.worksheets-subpanel.active')) {
closeWorksheetsSubpanel();
}
}
});
"""