""" BreakPilot Studio - Abitur Docs Admin Module Admin-Modul fuer die Verwaltung von Abitur-Dokumenten (NiBiS Niedersachsen). Nur fuer Entwickler/Admins - nicht fuer normale Lehrer. Features: - ZIP-Import fuer Abitur-Dokumente - Automatische Dateierkennung (Jahr, Fach, Niveau, Typ) - Metadaten-Korrektur und Bestaetigung - Indexierung fuer RAG-System - Uebersicht aller Dokumente mit Filter """ class AbiturDocsAdminModule: """Admin-Modul fuer Abitur-Dokumentenverwaltung.""" @staticmethod def get_css() -> str: """CSS fuer das Abitur Docs Admin-Modul.""" return """ /* ============================================= ABITUR DOCS ADMIN MODULE ============================================= */ .panel-abitur-docs-admin { display: none; flex-direction: column; height: 100%; background: var(--bp-bg); } /* Header */ .abitur-docs-header { padding: 24px 32px; background: var(--bp-surface); border-bottom: 1px solid var(--bp-border); display: flex; justify-content: space-between; align-items: center; } .abitur-docs-header h1 { font-size: 24px; font-weight: 700; color: var(--bp-text); display: flex; align-items: center; gap: 12px; } .abitur-docs-header-badge { font-size: 11px; padding: 4px 10px; border-radius: 12px; background: rgba(139, 92, 246, 0.15); color: #8b5cf6; font-weight: 600; } .abitur-docs-actions { display: flex; gap: 12px; } /* Content Area - 2 Spalten */ .abitur-docs-content { display: grid; grid-template-columns: 1fr 400px; gap: 24px; padding: 24px 32px; flex: 1; overflow: hidden; } /* Dokumenten-Liste (links) */ .abitur-docs-list-section { display: flex; flex-direction: column; gap: 16px; overflow: hidden; } /* Filter-Bar */ .abitur-docs-filters { display: flex; gap: 12px; flex-wrap: wrap; padding: 16px; background: var(--bp-surface); border-radius: 12px; border: 1px solid var(--bp-border); } .abitur-docs-filter-group { display: flex; flex-direction: column; gap: 4px; } .abitur-docs-filter-label { font-size: 11px; color: var(--bp-text-muted); text-transform: uppercase; letter-spacing: 0.5px; } .abitur-docs-filter-select { background: var(--bp-surface-elevated); border: 1px solid var(--bp-border); border-radius: 6px; padding: 8px 12px; color: var(--bp-text); font-size: 13px; min-width: 140px; } .abitur-docs-search { flex: 1; min-width: 200px; } .abitur-docs-search input { width: 100%; background: var(--bp-surface-elevated); border: 1px solid var(--bp-border); border-radius: 6px; padding: 8px 12px; color: var(--bp-text); font-size: 13px; } .abitur-docs-search input::placeholder { color: var(--bp-text-muted); } /* Dokumenten-Tabelle */ .abitur-docs-table-container { flex: 1; overflow-y: auto; background: var(--bp-surface); border-radius: 12px; border: 1px solid var(--bp-border); } .abitur-docs-table { width: 100%; border-collapse: collapse; } .abitur-docs-table th, .abitur-docs-table td { padding: 12px 16px; text-align: left; border-bottom: 1px solid var(--bp-border); } .abitur-docs-table th { background: var(--bp-surface-elevated); font-size: 11px; font-weight: 600; color: var(--bp-text-muted); text-transform: uppercase; letter-spacing: 0.5px; position: sticky; top: 0; } .abitur-docs-table tr:hover { background: var(--bp-surface-elevated); } .abitur-docs-table tr.selected { background: rgba(139, 92, 246, 0.1); } .abitur-docs-filename { font-family: monospace; font-size: 12px; color: var(--bp-text); max-width: 300px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .abitur-docs-status { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; padding: 4px 10px; border-radius: 12px; font-weight: 500; } .abitur-docs-status.pending { background: rgba(245, 158, 11, 0.15); color: #f59e0b; } .abitur-docs-status.recognized { background: rgba(59, 130, 246, 0.15); color: #3b82f6; } .abitur-docs-status.confirmed { background: rgba(16, 185, 129, 0.15); color: #10b981; } .abitur-docs-status.indexed { background: rgba(139, 92, 246, 0.15); color: #8b5cf6; } .abitur-docs-status.error { background: rgba(239, 68, 68, 0.15); color: #ef4444; } /* Detail-Panel (rechts) */ .abitur-docs-detail-section { display: flex; flex-direction: column; gap: 16px; overflow-y: auto; } .abitur-docs-detail-card { background: var(--bp-surface); border-radius: 12px; border: 1px solid var(--bp-border); overflow: hidden; } .abitur-docs-detail-header { padding: 16px; background: var(--bp-surface-elevated); border-bottom: 1px solid var(--bp-border); display: flex; justify-content: space-between; align-items: center; } .abitur-docs-detail-title { font-size: 14px; font-weight: 600; color: var(--bp-text); } .abitur-docs-detail-body { padding: 16px; } /* Metadaten-Form */ .abitur-docs-form-group { margin-bottom: 16px; } .abitur-docs-form-label { display: block; font-size: 12px; font-weight: 500; color: var(--bp-text-muted); margin-bottom: 6px; } .abitur-docs-form-input, .abitur-docs-form-select { width: 100%; background: var(--bp-surface-elevated); border: 1px solid var(--bp-border); border-radius: 6px; padding: 10px 12px; color: var(--bp-text); font-size: 13px; } .abitur-docs-form-input:focus, .abitur-docs-form-select:focus { outline: none; border-color: var(--bp-primary); } .abitur-docs-form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; } /* Erkennungs-Ergebnis */ .abitur-docs-recognition { padding: 12px; background: rgba(59, 130, 246, 0.1); border-radius: 8px; margin-bottom: 16px; } .abitur-docs-recognition-title { font-size: 11px; font-weight: 600; color: #3b82f6; margin-bottom: 8px; display: flex; align-items: center; gap: 6px; } .abitur-docs-recognition-item { display: flex; justify-content: space-between; font-size: 12px; color: var(--bp-text); padding: 4px 0; } .abitur-docs-recognition-key { color: var(--bp-text-muted); } .abitur-docs-recognition-value { font-weight: 500; } .abitur-docs-recognition-confidence { font-size: 10px; color: var(--bp-text-muted); margin-left: 8px; } /* Upload-Bereich */ .abitur-docs-upload-card { background: var(--bp-surface); border-radius: 12px; border: 2px dashed var(--bp-border); padding: 32px; text-align: center; cursor: pointer; transition: all 0.2s; } .abitur-docs-upload-card:hover { border-color: var(--bp-primary); background: var(--bp-surface-elevated); } .abitur-docs-upload-card.dragover { border-color: var(--bp-primary); background: rgba(108, 27, 27, 0.1); } .abitur-docs-upload-icon { font-size: 48px; margin-bottom: 16px; opacity: 0.6; } .abitur-docs-upload-title { font-size: 16px; font-weight: 600; color: var(--bp-text); margin-bottom: 8px; } .abitur-docs-upload-hint { font-size: 13px; color: var(--bp-text-muted); } /* Statistiken */ .abitur-docs-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 16px; } .abitur-docs-stat { background: var(--bp-surface); border-radius: 8px; border: 1px solid var(--bp-border); padding: 16px; text-align: center; } .abitur-docs-stat-value { font-size: 24px; font-weight: 700; color: var(--bp-text); } .abitur-docs-stat-label { font-size: 11px; color: var(--bp-text-muted); margin-top: 4px; } /* Buttons im Detail-Panel */ .abitur-docs-btn-group { display: flex; gap: 8px; margin-top: 16px; } .abitur-docs-btn { flex: 1; padding: 10px 16px; border-radius: 6px; font-size: 13px; font-weight: 500; cursor: pointer; transition: all 0.2s; border: none; display: flex; align-items: center; justify-content: center; gap: 6px; } .abitur-docs-btn-primary { background: var(--bp-primary); color: white; } .abitur-docs-btn-primary:hover { background: var(--bp-primary-hover); } .abitur-docs-btn-secondary { background: var(--bp-surface-elevated); color: var(--bp-text); border: 1px solid var(--bp-border); } .abitur-docs-btn-secondary:hover { background: var(--bp-border); } .abitur-docs-btn-danger { background: rgba(239, 68, 68, 0.15); color: #ef4444; } .abitur-docs-btn-danger:hover { background: rgba(239, 68, 68, 0.25); } /* Empty State */ .abitur-docs-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 48px; color: var(--bp-text-muted); } .abitur-docs-empty-icon { font-size: 48px; margin-bottom: 16px; opacity: 0.5; } .abitur-docs-empty-text { font-size: 14px; } /* Progress */ .abitur-docs-progress { height: 4px; background: var(--bp-surface-elevated); border-radius: 2px; overflow: hidden; margin-top: 12px; } .abitur-docs-progress-bar { height: 100%; background: var(--bp-primary); transition: width 0.3s; } /* Toast Notifications */ .abitur-docs-toast { position: fixed; bottom: 24px; right: 24px; padding: 16px 24px; background: var(--bp-surface); border: 1px solid var(--bp-border); border-radius: 8px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3); z-index: 1000; display: flex; align-items: center; gap: 12px; animation: slideInRight 0.3s ease; } @keyframes slideInRight { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } .abitur-docs-toast.success { border-left: 4px solid var(--bp-success); } .abitur-docs-toast.error { border-left: 4px solid var(--bp-danger); } .abitur-docs-toast.info { border-left: 4px solid var(--bp-info); } """ @staticmethod def get_html() -> str: """HTML fuer das Abitur Docs Admin-Modul.""" return """

Abitur-Dokumente Admin

0
Gesamt
0
Ausstehend
0
Bestaetigt
0
Indexiert
Jahr
Fach
Niveau
Status
Dateiname Jahr Fach Niveau Typ Status
📦
ZIP-Datei hierher ziehen
oder klicken zum Auswaehlen
""" @staticmethod def get_js() -> str: """JavaScript fuer das Abitur Docs Admin-Modul.""" return """ // ============================================= // ABITUR DOCS ADMIN MODULE // ============================================= let abiturDocsInitialized = false; let abiturDocsList = []; let abiturDocsEnums = { faecher: [], bundeslaender: [], dokumenttypen: [], niveaus: [] }; let selectedAbiturDoc = null; async function loadAbiturDocsAdminModule() { if (abiturDocsInitialized) { console.log('Abitur Docs Admin already initialized'); return; } console.log('Loading Abitur Docs Admin Module...'); // Enums laden await abiturDocsLoadEnums(); // Dokumente laden await abiturDocsRefresh(); abiturDocsInitialized = true; console.log('Abitur Docs Admin Module loaded'); } async function abiturDocsLoadEnums() { try { const response = await fetch('/api/abitur-docs/enums'); if (response.ok) { abiturDocsEnums = await response.json(); abiturDocsPopulateFilterDropdowns(); } } catch (e) { console.error('Error loading enums:', e); } } function abiturDocsPopulateFilterDropdowns() { // Fach-Filter und Detail-Select fuellen const fachFilter = document.getElementById('filter-fach'); const fachDetail = document.getElementById('detail-fach'); if (fachFilter && abiturDocsEnums.faecher) { fachFilter.innerHTML = ''; abiturDocsEnums.faecher.forEach(f => { fachFilter.innerHTML += ``; }); } if (fachDetail && abiturDocsEnums.faecher) { fachDetail.innerHTML = ''; abiturDocsEnums.faecher.forEach(f => { fachDetail.innerHTML += ``; }); } } async function abiturDocsRefresh() { try { // Filters sammeln const params = new URLSearchParams(); const jahr = document.getElementById('filter-jahr')?.value; const fach = document.getElementById('filter-fach')?.value; const niveau = document.getElementById('filter-niveau')?.value; const status = document.getElementById('filter-status')?.value; const search = document.getElementById('filter-search')?.value; if (jahr) params.append('jahr', jahr); if (fach) params.append('fach', fach); if (niveau) params.append('niveau', niveau); if (status) params.append('status', status); if (search) params.append('search', search); const response = await fetch('/api/abitur-docs/documents?' + params.toString()); if (response.ok) { const data = await response.json(); abiturDocsList = data.documents || []; abiturDocsRenderTable(); abiturDocsUpdateStats(); } } catch (e) { console.error('Error loading documents:', e); abiturDocsShowToast('Fehler beim Laden der Dokumente', 'error'); } } function abiturDocsApplyFilters() { abiturDocsRefresh(); } function abiturDocsRenderTable() { const tbody = document.getElementById('abitur-docs-table-body'); if (!tbody) return; if (abiturDocsList.length === 0) { tbody.innerHTML = ` Keine Dokumente gefunden `; return; } tbody.innerHTML = abiturDocsList.map(doc => ` ${doc.filename} ${doc.metadata?.jahr || '-'} ${doc.metadata?.fach || '-'} ${doc.metadata?.niveau || '-'} ${doc.metadata?.dokument_typ || '-'} ${abiturDocsStatusLabel(doc.status)} `).join(''); } function abiturDocsStatusLabel(status) { const labels = { 'pending': 'Ausstehend', 'recognized': 'Erkannt', 'confirmed': 'Bestaetigt', 'indexed': 'Indexiert', 'error': 'Fehler' }; return labels[status] || status; } function abiturDocsUpdateStats() { // Zaehlen nach Status const stats = { total: abiturDocsList.length, pending: abiturDocsList.filter(d => d.status === 'pending').length, confirmed: abiturDocsList.filter(d => d.status === 'confirmed').length, indexed: abiturDocsList.filter(d => d.status === 'indexed').length }; document.getElementById('stat-total').textContent = stats.total; document.getElementById('stat-pending').textContent = stats.pending; document.getElementById('stat-confirmed').textContent = stats.confirmed; document.getElementById('stat-indexed').textContent = stats.indexed; } async function abiturDocsSelectDocument(docId) { const doc = abiturDocsList.find(d => d.id === docId); if (!doc) return; selectedAbiturDoc = doc; abiturDocsRenderTable(); // Re-render to show selection // Detail-Panel zeigen document.getElementById('abitur-docs-upload-area').style.display = 'none'; document.getElementById('abitur-docs-detail-card').style.display = 'block'; document.getElementById('abitur-docs-actions-card').style.display = 'block'; // Formular fuellen document.getElementById('detail-id').value = doc.id; document.getElementById('detail-filename').value = doc.filename; document.getElementById('detail-jahr').value = doc.metadata?.jahr || ''; document.getElementById('detail-bundesland').value = doc.metadata?.bundesland || 'niedersachsen'; document.getElementById('detail-fach').value = doc.metadata?.fach || ''; document.getElementById('detail-niveau').value = doc.metadata?.niveau || ''; document.getElementById('detail-doktyp').value = doc.metadata?.dokument_typ || ''; document.getElementById('detail-aufgabe-nr').value = doc.metadata?.aufgaben_nummer || ''; // Status-Badge const statusEl = document.getElementById('detail-status'); statusEl.textContent = abiturDocsStatusLabel(doc.status); statusEl.className = 'abitur-docs-status ' + doc.status; // Erkennungs-Ergebnis anzeigen const recItems = document.getElementById('detail-recognition-items'); if (doc.recognition_result) { const rec = doc.recognition_result; recItems.innerHTML = `
Confidence: ${(rec.confidence * 100).toFixed(0)}%
${rec.extracted.jahr ? `
Jahr: ${rec.extracted.jahr}
` : ''} ${rec.extracted.fach ? `
Fach: ${rec.extracted.fach}
` : ''} ${rec.extracted.niveau ? `
Niveau: ${rec.extracted.niveau}
` : ''} ${rec.extracted.typ ? `
Typ: ${rec.extracted.typ}
` : ''} `; document.getElementById('detail-recognition').style.display = 'block'; } else { document.getElementById('detail-recognition').style.display = 'none'; } } function abiturDocsClearSelection() { selectedAbiturDoc = null; abiturDocsRenderTable(); document.getElementById('abitur-docs-upload-area').style.display = 'block'; document.getElementById('abitur-docs-detail-card').style.display = 'none'; document.getElementById('abitur-docs-actions-card').style.display = 'none'; } async function abiturDocsConfirmMetadata() { if (!selectedAbiturDoc) return; const metadata = { jahr: parseInt(document.getElementById('detail-jahr').value) || null, bundesland: document.getElementById('detail-bundesland').value || null, fach: document.getElementById('detail-fach').value || null, niveau: document.getElementById('detail-niveau').value || null, dokument_typ: document.getElementById('detail-doktyp').value || null, aufgaben_nummer: document.getElementById('detail-aufgabe-nr').value || null }; try { const response = await fetch(`/api/abitur-docs/documents/${selectedAbiturDoc.id}/metadata`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(metadata) }); if (response.ok) { abiturDocsShowToast('Metadaten bestaetigt', 'success'); await abiturDocsRefresh(); // Re-select to update detail view if (selectedAbiturDoc) { abiturDocsSelectDocument(selectedAbiturDoc.id); } } else { const err = await response.json(); abiturDocsShowToast(err.detail || 'Fehler beim Speichern', 'error'); } } catch (e) { console.error('Error updating metadata:', e); abiturDocsShowToast('Fehler beim Speichern', 'error'); } } async function abiturDocsIndexDocument() { if (!selectedAbiturDoc) return; try { const response = await fetch(`/api/abitur-docs/documents/${selectedAbiturDoc.id}/index`, { method: 'POST' }); if (response.ok) { abiturDocsShowToast('Dokument indexiert', 'success'); await abiturDocsRefresh(); if (selectedAbiturDoc) { abiturDocsSelectDocument(selectedAbiturDoc.id); } } else { const err = await response.json(); abiturDocsShowToast(err.detail || 'Fehler beim Indexieren', 'error'); } } catch (e) { console.error('Error indexing:', e); abiturDocsShowToast('Fehler beim Indexieren', 'error'); } } function abiturDocsPreviewDocument() { if (!selectedAbiturDoc?.file_path) return; window.open('/api/abitur-docs/documents/' + selectedAbiturDoc.id + '/preview', '_blank'); } async function abiturDocsDeleteDocument() { if (!selectedAbiturDoc) return; if (!confirm('Dokument wirklich loeschen?')) return; try { const response = await fetch(`/api/abitur-docs/documents/${selectedAbiturDoc.id}`, { method: 'DELETE' }); if (response.ok) { abiturDocsShowToast('Dokument geloescht', 'success'); abiturDocsClearSelection(); await abiturDocsRefresh(); } else { abiturDocsShowToast('Fehler beim Loeschen', 'error'); } } catch (e) { console.error('Error deleting:', e); abiturDocsShowToast('Fehler beim Loeschen', 'error'); } } // Drag & Drop function abiturDocsDragOver(e) { e.preventDefault(); e.currentTarget.classList.add('dragover'); } function abiturDocsDragLeave(e) { e.currentTarget.classList.remove('dragover'); } function abiturDocsHandleDrop(e) { e.preventDefault(); e.currentTarget.classList.remove('dragover'); const files = e.dataTransfer.files; if (files.length > 0) { abiturDocsUploadFile(files[0]); } } function abiturDocsHandleFileSelect(e) { const files = e.target.files; if (files.length > 0) { abiturDocsUploadFile(files[0]); } } function abiturDocsShowUpload() { document.getElementById('abitur-docs-file-input').click(); } async function abiturDocsUploadFile(file) { if (!file.name.endsWith('.zip')) { abiturDocsShowToast('Bitte eine ZIP-Datei waehlen', 'error'); return; } abiturDocsShowToast('Import gestartet...', 'info'); const formData = new FormData(); formData.append('file', file); try { const response = await fetch('/api/abitur-docs/import-zip', { method: 'POST', body: formData }); if (response.ok) { const result = await response.json(); abiturDocsShowToast(`${result.imported_count} Dokumente importiert`, 'success'); await abiturDocsRefresh(); } else { const err = await response.json(); abiturDocsShowToast(err.detail || 'Fehler beim Import', 'error'); } } catch (e) { console.error('Error uploading:', e); abiturDocsShowToast('Fehler beim Upload', 'error'); } } // Toast Helper function abiturDocsShowToast(message, type = 'info') { const existing = document.querySelector('.abitur-docs-toast'); if (existing) existing.remove(); const toast = document.createElement('div'); toast.className = `abitur-docs-toast ${type}`; toast.innerHTML = ` ${type === 'success' ? '✓' : type === 'error' ? '✗' : 'ℹ'} ${message} `; document.body.appendChild(toast); setTimeout(() => toast.remove(), 3000); } // Panel Show Function function showAbiturDocsAdminPanel() { console.log('showAbiturDocsAdminPanel called'); hideAllPanels(); const panel = document.getElementById('panel-abitur-docs-admin'); if (panel) { panel.style.display = 'flex'; loadAbiturDocsAdminModule(); console.log('Abitur Docs Admin panel shown'); } } """