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/meetings/pages/dashboard.py
Benjamin Admin bfdaf63ba9 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

299 lines
11 KiB
Python

"""
Meetings Module - Dashboard Page
Main meetings dashboard with statistics and quick actions
"""
from ..templates import ICONS, render_base_page
def meetings_dashboard() -> str:
"""Main meetings dashboard"""
content = f'''
<div class="page-header">
<div>
<h1 class="page-title">Meeting Dashboard</h1>
<p class="page-subtitle">Videokonferenzen, Schulungen und Elterngespräche verwalten</p>
</div>
<div class="btn-group">
<button class="btn btn-primary" onclick="openModal('newMeeting')">
{ICONS['plus']} Neues Meeting
</button>
</div>
</div>
<!-- Quick Actions -->
<div class="quick-actions">
<a href="/meetings/quick" class="quick-action">
<div class="quick-action-icon card-icon primary">{ICONS['video']}</div>
<span class="quick-action-label">Sofort-Meeting starten</span>
</a>
<a href="/meetings/schedule/new" class="quick-action">
<div class="quick-action-icon card-icon info">{ICONS['calendar']}</div>
<span class="quick-action-label">Meeting planen</span>
</a>
<a href="/meetings/trainings/new" class="quick-action">
<div class="quick-action-icon card-icon accent">{ICONS['graduation']}</div>
<span class="quick-action-label">Schulung erstellen</span>
</a>
<a href="/meetings/parent-teacher" class="quick-action">
<div class="quick-action-icon card-icon warning">{ICONS['users']}</div>
<span class="quick-action-label">Elterngespräch</span>
</a>
</div>
<!-- Statistics Cards -->
<div class="dashboard-grid">
<div class="card">
<div class="card-header">
<span class="card-title">Aktive Meetings</span>
<div class="card-icon primary">{ICONS['video']}</div>
</div>
<div class="stat-value" id="activeMeetings">0</div>
<div class="stat-label">Jetzt live</div>
</div>
<div class="card">
<div class="card-header">
<span class="card-title">Geplante Termine</span>
<div class="card-icon info">{ICONS['calendar']}</div>
</div>
<div class="stat-value" id="scheduledMeetings">0</div>
<div class="stat-label">Diese Woche</div>
</div>
<div class="card">
<div class="card-header">
<span class="card-title">Aufzeichnungen</span>
<div class="card-icon accent">{ICONS['record']}</div>
</div>
<div class="stat-value" id="recordings">0</div>
<div class="stat-label">Verfügbar</div>
</div>
<div class="card">
<div class="card-header">
<span class="card-title">Teilnehmer</span>
<div class="card-icon warning">{ICONS['users']}</div>
</div>
<div class="stat-value" id="totalParticipants">0</div>
<div class="stat-label">Diese Woche</div>
</div>
</div>
<!-- Upcoming Meetings -->
<div class="card">
<div class="card-header">
<span class="card-title">Nächste Meetings</span>
<a href="/meetings/schedule" class="btn btn-secondary">Alle anzeigen</a>
</div>
<div class="meeting-list" id="upcomingMeetings">
<div class="meeting-item">
<div class="meeting-time">
<div class="meeting-time-value">14:00</div>
<div class="meeting-time-date">Heute</div>
</div>
<div class="meeting-info">
<div class="meeting-title">Elterngespräch - Max Müller</div>
<div class="meeting-meta">
<span>{ICONS['clock']} 30 Min</span>
<span>{ICONS['users']} 2 Teilnehmer</span>
</div>
</div>
<span class="meeting-badge badge-scheduled">Geplant</span>
<div class="meeting-actions">
<button class="btn btn-primary" onclick="joinMeeting('parent-123')">Beitreten</button>
<button class="btn-icon" onclick="copyMeetingLink('parent-123')">{ICONS['copy']}</button>
</div>
</div>
<div class="meeting-item">
<div class="meeting-time">
<div class="meeting-time-value">15:30</div>
<div class="meeting-time-date">Heute</div>
</div>
<div class="meeting-info">
<div class="meeting-title">Go Grundlagen Schulung</div>
<div class="meeting-meta">
<span>{ICONS['clock']} 120 Min</span>
<span>{ICONS['users']} 12 Teilnehmer</span>
<span>{ICONS['record']} Aufzeichnung</span>
</div>
</div>
<span class="meeting-badge badge-scheduled">Geplant</span>
<div class="meeting-actions">
<button class="btn btn-primary" onclick="joinMeeting('training-456')">Beitreten</button>
<button class="btn-icon" onclick="copyMeetingLink('training-456')">{ICONS['copy']}</button>
</div>
</div>
</div>
</div>
<!-- New Meeting Modal -->
<div class="modal-overlay" id="newMeetingModal">
<div class="modal">
<div class="modal-header">
<h2 class="modal-title">Neues Meeting erstellen</h2>
<button class="modal-close" onclick="closeModal('newMeeting')">&times;</button>
</div>
<div class="modal-body">
<div class="form-group">
<label class="form-label">Meeting-Typ</label>
<select class="form-select" id="meetingType" onchange="updateMeetingForm()">
<option value="quick">Sofort-Meeting</option>
<option value="scheduled">Geplantes Meeting</option>
<option value="training">Schulung</option>
<option value="parent">Elterngespräch</option>
<option value="class">Klassenkonferenz</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Titel</label>
<input type="text" class="form-input" id="meetingTitle" placeholder="Meeting-Titel eingeben">
</div>
<div class="form-group" id="dateTimeGroup" style="display: none;">
<label class="form-label">Datum & Uhrzeit</label>
<input type="datetime-local" class="form-input" id="meetingDateTime">
</div>
<div class="form-group" id="durationGroup">
<label class="form-label">Dauer (Minuten)</label>
<select class="form-select" id="meetingDuration">
<option value="30">30 Minuten</option>
<option value="60" selected>60 Minuten</option>
<option value="90">90 Minuten</option>
<option value="120">120 Minuten</option>
</select>
</div>
<div class="form-group">
<label class="form-label">
<input type="checkbox" id="enableLobby" checked> Warteraum aktivieren
</label>
</div>
<div class="form-group">
<label class="form-label">
<input type="checkbox" id="enableRecording"> Aufzeichnung erlauben
</label>
</div>
<div class="form-group">
<label class="form-label">
<input type="checkbox" id="muteOnStart" checked> Teilnehmer stummschalten bei Beitritt
</label>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeModal('newMeeting')">Abbrechen</button>
<button class="btn btn-primary" onclick="createMeeting()">Meeting erstellen</button>
</div>
</div>
</div>
<script>
// Modal Functions
function openModal(type) {{
document.getElementById(type + 'Modal').classList.add('active');
}}
function closeModal(type) {{
document.getElementById(type + 'Modal').classList.remove('active');
}}
function updateMeetingForm() {{
const type = document.getElementById('meetingType').value;
const dateTimeGroup = document.getElementById('dateTimeGroup');
if (type === 'quick') {{
dateTimeGroup.style.display = 'none';
}} else {{
dateTimeGroup.style.display = 'block';
}}
}}
// Meeting Functions
async function createMeeting() {{
const type = document.getElementById('meetingType').value;
const title = document.getElementById('meetingTitle').value || 'Neues Meeting';
const duration = document.getElementById('meetingDuration').value;
const enableLobby = document.getElementById('enableLobby').checked;
const enableRecording = document.getElementById('enableRecording').checked;
const muteOnStart = document.getElementById('muteOnStart').checked;
const payload = {{
type: type,
title: title,
duration: parseInt(duration),
config: {{
enable_lobby: enableLobby,
enable_recording: enableRecording,
start_with_audio_muted: muteOnStart
}}
}};
try {{
const response = await fetch('/api/meetings/create', {{
method: 'POST',
headers: {{ 'Content-Type': 'application/json' }},
body: JSON.stringify(payload)
}});
if (response.ok) {{
const data = await response.json();
closeModal('newMeeting');
if (type === 'quick') {{
window.location.href = '/meetings/room/' + data.room_name;
}} else {{
alert('Meeting erfolgreich erstellt!\\nLink: ' + data.join_url);
loadUpcomingMeetings();
}}
}} else {{
alert('Fehler beim Erstellen des Meetings');
}}
}} catch (error) {{
console.error('Error:', error);
alert('Fehler beim Erstellen des Meetings');
}}
}}
function joinMeeting(roomId) {{
window.location.href = '/meetings/room/' + roomId;
}}
function copyMeetingLink(roomId) {{
const link = window.location.origin + '/meetings/room/' + roomId;
navigator.clipboard.writeText(link).then(() => {{
alert('Link kopiert!');
}});
}}
// Load Data
async function loadDashboardData() {{
try {{
const response = await fetch('/api/meetings/stats');
if (response.ok) {{
const data = await response.json();
document.getElementById('activeMeetings').textContent = data.active || 0;
document.getElementById('scheduledMeetings').textContent = data.scheduled || 0;
document.getElementById('recordings').textContent = data.recordings || 0;
document.getElementById('totalParticipants').textContent = data.participants || 0;
}}
}} catch (error) {{
console.error('Error loading stats:', error);
}}
}}
async function loadUpcomingMeetings() {{
// In production, load from API
console.log('Loading upcoming meetings...');
}}
// Initialize
loadDashboardData();
</script>
'''
return render_base_page("Dashboard", content, "dashboard")