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 Dev 19855efacc
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
feat: BreakPilot PWA - Full codebase (clean push without large binaries)
All services: admin-v2, studio-v2, website, ai-compliance-sdk,
consent-service, klausur-service, voice-service, and infrastructure.
Large PDFs and compiled binaries excluded via .gitignore.
2026-02-11 13:25:58 +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")