""" Lehrer-Dashboard Modul fuer das BreakPilot Studio. Ein frei konfigurierbares Dashboard mit Drag & Drop Widget-System. Lehrer koennen ihre persoenliche Startseite aus verschiedenen Widgets zusammenstellen. """ from .widgets import ( TodosWidget, SchnellzugriffWidget, NotizenWidget, StundenplanWidget, KlassenWidget, FehlzeitenWidget, ArbeitenWidget, NachrichtenWidget, MatrixWidget, AlertsWidget, StatistikWidget, KalenderWidget, ) class LehrerDashboardModule: """ Haupt-Modul fuer das konfigurierbare Lehrer-Dashboard. """ @staticmethod def get_css() -> str: # Sammle CSS von allen Widgets widget_css = "\n".join([ TodosWidget.get_css(), SchnellzugriffWidget.get_css(), NotizenWidget.get_css(), StundenplanWidget.get_css(), KlassenWidget.get_css(), FehlzeitenWidget.get_css(), ArbeitenWidget.get_css(), NachrichtenWidget.get_css(), MatrixWidget.get_css(), AlertsWidget.get_css(), StatistikWidget.get_css(), KalenderWidget.get_css(), ]) return f""" /* ===== Lehrer-Dashboard Styles ===== */ .lehrer-dashboard-container {{ padding: 24px; max-width: 1400px; margin: 0 auto; }} /* Dashboard Header */ .dashboard-header {{ display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 24px; }} .dashboard-greeting {{ font-size: 24px; font-weight: 700; color: var(--bp-text, #e5e7eb); margin-bottom: 4px; }} .dashboard-date {{ font-size: 14px; color: var(--bp-text-muted, #9ca3af); }} .dashboard-actions {{ display: flex; gap: 8px; }} .dashboard-edit-btn {{ display: flex; align-items: center; gap: 6px; padding: 8px 16px; background: var(--bp-surface, #1e293b); border: 1px solid var(--bp-border, #475569); border-radius: 8px; color: var(--bp-text, #e5e7eb); font-size: 13px; cursor: pointer; transition: all 0.2s; }} .dashboard-edit-btn:hover {{ background: var(--bp-surface-elevated, #334155); border-color: var(--bp-primary, #6C1B1B); }} .dashboard-edit-btn.active {{ background: var(--bp-primary, #6C1B1B); border-color: var(--bp-primary, #6C1B1B); color: white; }} /* Widget Grid */ .dashboard-grid {{ display: flex; flex-direction: column; gap: 16px; }} .dashboard-row {{ display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }} .dashboard-row.single {{ grid-template-columns: 1fr; }} /* Widget Container */ .dashboard-widget {{ position: relative; min-height: 200px; }} .dashboard-widget.full {{ grid-column: 1 / -1; }} /* Widget Settings Button */ .widget-settings-btn {{ width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; background: transparent; border: none; color: var(--bp-text-muted, #9ca3af); cursor: pointer; border-radius: 6px; transition: all 0.2s; font-size: 14px; }} .widget-settings-btn:hover {{ background: var(--bp-surface-elevated, #334155); color: var(--bp-text, #e5e7eb); }} /* ===== Edit Mode Styles ===== */ .dashboard-edit-mode .widget-catalog {{ display: block !important; }} .dashboard-edit-mode .dashboard-widget {{ position: relative; }} .dashboard-edit-mode .dashboard-widget::after {{ content: ''; position: absolute; inset: 0; border: 2px dashed var(--bp-border, #475569); border-radius: 12px; pointer-events: none; opacity: 0; transition: opacity 0.2s; }} .dashboard-edit-mode .dashboard-widget:hover::after {{ opacity: 1; }} .dashboard-edit-mode .widget-remove-btn {{ display: flex !important; }} /* Widget Remove Button */ .widget-remove-btn {{ display: none; position: absolute; top: 8px; right: 8px; width: 24px; height: 24px; align-items: center; justify-content: center; background: rgba(239, 68, 68, 0.9); color: white; border: none; border-radius: 50%; cursor: pointer; font-size: 14px; z-index: 10; transition: all 0.2s; }} .widget-remove-btn:hover {{ background: #ef4444; transform: scale(1.1); }} /* Widget Catalog */ .widget-catalog {{ display: none; background: var(--bp-surface, #1e293b); border: 1px solid var(--bp-border, #475569); border-radius: 12px; padding: 16px; margin-bottom: 24px; }} .widget-catalog-title {{ font-size: 14px; font-weight: 600; color: var(--bp-text, #e5e7eb); margin-bottom: 12px; }} .widget-catalog-grid {{ display: flex; flex-wrap: wrap; gap: 8px; }} .widget-catalog-item {{ display: flex; align-items: center; gap: 8px; padding: 10px 14px; background: var(--bp-bg, #0f172a); border: 1px solid var(--bp-border-subtle, rgba(255,255,255,0.1)); border-radius: 8px; cursor: grab; transition: all 0.2s; user-select: none; }} .widget-catalog-item:hover {{ border-color: var(--bp-primary, #6C1B1B); transform: translateY(-2px); }} .widget-catalog-item:active {{ cursor: grabbing; }} .widget-catalog-item.dragging {{ opacity: 0.5; }} .widget-catalog-item.disabled {{ opacity: 0.4; cursor: not-allowed; }} .widget-catalog-item-icon {{ font-size: 16px; }} .widget-catalog-item-name {{ font-size: 12px; font-weight: 500; color: var(--bp-text, #e5e7eb); }} /* Drop Zone */ .drop-zone {{ display: none; min-height: 100px; border: 2px dashed var(--bp-border, #475569); border-radius: 12px; background: var(--bp-bg, #0f172a); transition: all 0.2s; }} .dashboard-edit-mode .drop-zone {{ display: flex; align-items: center; justify-content: center; color: var(--bp-text-muted, #9ca3af); font-size: 13px; }} .drop-zone.drag-over {{ border-color: var(--bp-accent, #5ABF60); background: rgba(90, 191, 96, 0.1); color: var(--bp-accent, #5ABF60); }} /* Add Row Button */ .add-row-btn {{ display: none; width: 100%; padding: 16px; background: transparent; border: 2px dashed var(--bp-border, #475569); border-radius: 12px; color: var(--bp-text-muted, #9ca3af); font-size: 13px; cursor: pointer; transition: all 0.2s; }} .dashboard-edit-mode .add-row-btn {{ display: block; }} .add-row-btn:hover {{ border-color: var(--bp-accent, #5ABF60); color: var(--bp-accent, #5ABF60); }} /* Widget Settings Modal */ .widget-settings-modal {{ display: none; position: fixed; inset: 0; background: rgba(0, 0, 0, 0.6); z-index: 1000; align-items: center; justify-content: center; }} .widget-settings-modal.active {{ display: flex; }} .widget-settings-content {{ background: var(--bp-surface, #1e293b); border: 1px solid var(--bp-border, #475569); border-radius: 16px; padding: 24px; max-width: 400px; width: 90%; }} .widget-settings-title {{ font-size: 18px; font-weight: 600; color: var(--bp-text, #e5e7eb); margin-bottom: 16px; }} .widget-settings-close {{ position: absolute; top: 16px; right: 16px; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; background: transparent; border: none; color: var(--bp-text-muted, #9ca3af); cursor: pointer; border-radius: 8px; font-size: 18px; }} .widget-settings-close:hover {{ background: var(--bp-surface-elevated, #334155); }} /* Responsive */ @media (max-width: 768px) {{ .lehrer-dashboard-container {{ padding: 16px; }} .dashboard-header {{ flex-direction: column; gap: 16px; }} .dashboard-row {{ grid-template-columns: 1fr; }} .dashboard-greeting {{ font-size: 20px; }} .widget-catalog-grid {{ flex-direction: column; }} .widget-catalog-item {{ width: 100%; }} }} /* Widget CSS */ {widget_css} """ @staticmethod def get_html() -> str: return """
""" @staticmethod def get_js() -> str: # Sammle JS von allen Widgets widget_js = "\n".join([ TodosWidget.get_js(), SchnellzugriffWidget.get_js(), NotizenWidget.get_js(), StundenplanWidget.get_js(), KlassenWidget.get_js(), FehlzeitenWidget.get_js(), ArbeitenWidget.get_js(), NachrichtenWidget.get_js(), MatrixWidget.get_js(), AlertsWidget.get_js(), StatistikWidget.get_js(), KalenderWidget.get_js(), ]) return f""" // ===== Lehrer-Dashboard JavaScript ===== const DASHBOARD_LAYOUT_KEY = 'bp-dashboard-layout'; const LEHRER_PROFIL_KEY = 'bp-lehrer-profil'; let dashboardEditMode = false; let lehrerDashboardInitialized = false; // Widget Registry const WidgetRegistry = {{ stundenplan: {{ id: 'stundenplan', name: 'Stundenplan', icon: '📅', color: '#3b82f6', defaultWidth: 'half', init: initStundenplanWidget, getHtml: () => `{StundenplanWidget.get_html().replace('`', '\\`').replace('${', '\\${')}` }}, klassen: {{ id: 'klassen', name: 'Meine Klassen', icon: '📊', color: '#8b5cf6', defaultWidth: 'half', init: initKlassenWidget, getHtml: () => `{KlassenWidget.get_html().replace('`', '\\`').replace('${', '\\${')}` }}, fehlzeiten: {{ id: 'fehlzeiten', name: 'Fehlzeiten', icon: '⚠', color: '#ef4444', defaultWidth: 'half', init: initFehlzeitenWidget, getHtml: () => `{FehlzeitenWidget.get_html().replace('`', '\\`').replace('${', '\\${')}` }}, arbeiten: {{ id: 'arbeiten', name: 'Arbeiten', icon: '📝', color: '#f59e0b', defaultWidth: 'half', init: initArbeitenWidget, getHtml: () => `{ArbeitenWidget.get_html().replace('`', '\\`').replace('${', '\\${')}` }}, todos: {{ id: 'todos', name: 'To-Dos', icon: '✓', color: '#10b981', defaultWidth: 'half', init: initTodosWidget, getHtml: () => `{TodosWidget.get_html().replace('`', '\\`').replace('${', '\\${')}` }}, nachrichten: {{ id: 'nachrichten', name: 'E-Mails', icon: '📧', color: '#06b6d4', defaultWidth: 'half', init: initNachrichtenWidget, getHtml: () => `{NachrichtenWidget.get_html().replace('`', '\\`').replace('${', '\\${')}` }}, matrix: {{ id: 'matrix', name: 'Matrix-Chat', icon: '💬', color: '#8b5cf6', defaultWidth: 'half', init: initMatrixWidget, getHtml: () => `{MatrixWidget.get_html().replace('`', '\\`').replace('${', '\\${')}` }}, alerts: {{ id: 'alerts', name: 'Google Alerts', icon: '🔔', color: '#f59e0b', defaultWidth: 'half', init: initAlertsWidget, getHtml: () => `{AlertsWidget.get_html().replace('`', '\\`').replace('${', '\\${')}` }}, statistik: {{ id: 'statistik', name: 'Statistik', icon: '📈', color: '#3b82f6', defaultWidth: 'full', init: initStatistikWidget, getHtml: () => `{StatistikWidget.get_html().replace('`', '\\`').replace('${', '\\${')}` }}, schnellzugriff: {{ id: 'schnellzugriff', name: 'Schnellzugriff', icon: '⚡', color: '#6b7280', defaultWidth: 'full', init: initSchnellzugriffWidget, getHtml: () => `{SchnellzugriffWidget.get_html().replace('`', '\\`').replace('${', '\\${')}` }}, notizen: {{ id: 'notizen', name: 'Notizen', icon: '📋', color: '#fbbf24', defaultWidth: 'half', init: initNotizenWidget, getHtml: () => `{NotizenWidget.get_html().replace('`', '\\`').replace('${', '\\${')}` }}, kalender: {{ id: 'kalender', name: 'Termine', icon: '📆', color: '#ec4899', defaultWidth: 'half', init: initKalenderWidget, getHtml: () => `{KalenderWidget.get_html().replace('`', '\\`').replace('${', '\\${')}` }} }}; // Default Layout function getDefaultLayout() {{ return {{ version: 1, rows: [ {{ id: 'row-1', widgets: [ {{ widgetId: 'stundenplan', width: 'half' }}, {{ widgetId: 'klassen', width: 'half' }} ] }}, {{ id: 'row-2', widgets: [ {{ widgetId: 'fehlzeiten', width: 'half' }}, {{ widgetId: 'arbeiten', width: 'half' }} ] }}, {{ id: 'row-3', widgets: [ {{ widgetId: 'todos', width: 'half' }}, {{ widgetId: 'nachrichten', width: 'half' }} ] }}, {{ id: 'row-4', widgets: [ {{ widgetId: 'schnellzugriff', width: 'full' }} ] }} ] }}; }} function loadDashboardLayout() {{ const stored = localStorage.getItem(DASHBOARD_LAYOUT_KEY); return stored ? JSON.parse(stored) : getDefaultLayout(); }} function saveDashboardLayout(layout) {{ localStorage.setItem(DASHBOARD_LAYOUT_KEY, JSON.stringify(layout)); }} function getGreeting() {{ const hour = new Date().getHours(); if (hour < 12) return 'Guten Morgen'; if (hour < 18) return 'Guten Tag'; return 'Guten Abend'; }} function getLehrerName() {{ const profil = localStorage.getItem(LEHRER_PROFIL_KEY); if (profil) {{ try {{ return JSON.parse(profil).name || 'Lehrer'; }} catch (e) {{}} }} return ''; }} function formatDashboardDate() {{ return new Date().toLocaleDateString('de-DE', {{ weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }}); }} function renderDashboardHeader() {{ const greetingEl = document.getElementById('dashboard-greeting'); const dateEl = document.getElementById('dashboard-date'); if (greetingEl) {{ const name = getLehrerName(); greetingEl.textContent = `${{getGreeting()}}${{name ? ', ' + name : ''}}!`; }} if (dateEl) {{ dateEl.textContent = formatDashboardDate(); }} }} function renderWidgetCatalog() {{ const grid = document.getElementById('widget-catalog-grid'); if (!grid) return; const layout = loadDashboardLayout(); const usedWidgets = new Set(); layout.rows.forEach(row => {{ row.widgets.forEach(w => usedWidgets.add(w.widgetId)); }}); grid.innerHTML = Object.values(WidgetRegistry).map(widget => {{ const isUsed = usedWidgets.has(widget.id); return ` `; }}).join(''); }} function renderDashboardGrid() {{ const grid = document.getElementById('dashboard-grid'); if (!grid) return; const layout = loadDashboardLayout(); grid.innerHTML = layout.rows.map((row, rowIndex) => {{ const isSingleFull = row.widgets.length === 1 && row.widgets[0].width === 'full'; return `Widget-Einstellungen werden in einer zukuenftigen Version verfuegbar sein.
`; modal.classList.add('active'); }} function closeWidgetSettings() {{ const modal = document.getElementById('widget-settings-modal'); if (modal) {{ modal.classList.remove('active'); }} }} function loadLehrerDashboardModule() {{ if (lehrerDashboardInitialized) {{ console.log('Lehrer-Dashboard already initialized'); return; }} console.log('Loading Lehrer-Dashboard Module...'); renderDashboardHeader(); renderWidgetCatalog(); renderDashboardGrid(); lehrerDashboardInitialized = true; console.log('Lehrer-Dashboard Module loaded successfully'); }} // Widget JavaScript {widget_js} """