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>
This commit is contained in:
206
backend/frontend/meetings/pages/schedule.py
Normal file
206
backend/frontend/meetings/pages/schedule.py
Normal file
@@ -0,0 +1,206 @@
|
||||
"""
|
||||
Meetings Module - Schedule Page
|
||||
Schedule and manage upcoming meetings
|
||||
"""
|
||||
|
||||
from ..templates import ICONS, render_base_page
|
||||
|
||||
|
||||
def schedule_meetings() -> str:
|
||||
"""Schedule and manage upcoming meetings"""
|
||||
content = f'''
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h1 class="page-title">Termine</h1>
|
||||
<p class="page-subtitle">Geplante Meetings und Termine verwalten</p>
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="openScheduleModal()">
|
||||
{ICONS['plus']} Meeting planen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Tabs -->
|
||||
<div class="tabs">
|
||||
<button class="tab active" onclick="filterMeetings('all')">Alle</button>
|
||||
<button class="tab" onclick="filterMeetings('today')">Heute</button>
|
||||
<button class="tab" onclick="filterMeetings('week')">Diese Woche</button>
|
||||
<button class="tab" onclick="filterMeetings('month')">Dieser Monat</button>
|
||||
</div>
|
||||
|
||||
<div class="meeting-list" id="scheduledMeetings">
|
||||
<div class="meeting-item">
|
||||
<div class="meeting-time">
|
||||
<div class="meeting-time-value">14:00</div>
|
||||
<div class="meeting-time-date">Mo, 16.12.</div>
|
||||
</div>
|
||||
<div class="meeting-info">
|
||||
<div class="meeting-title">Team-Besprechung</div>
|
||||
<div class="meeting-meta">
|
||||
<span>{ICONS['clock']} 60 Min</span>
|
||||
<span>{ICONS['users']} 5 Teilnehmer</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="meeting-badge badge-scheduled">Geplant</span>
|
||||
<div class="meeting-actions">
|
||||
<button class="btn btn-primary" onclick="joinMeeting('team-abc')">Beitreten</button>
|
||||
<button class="btn-icon" onclick="editMeeting('team-abc')">{ICONS['settings']}</button>
|
||||
<button class="btn-icon" onclick="copyLink('team-abc')">{ICONS['link']}</button>
|
||||
<button class="btn-icon" onclick="deleteMeeting('team-abc')">{ICONS['trash']}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="meeting-item">
|
||||
<div class="meeting-time">
|
||||
<div class="meeting-time-value">09:30</div>
|
||||
<div class="meeting-time-date">Di, 17.12.</div>
|
||||
</div>
|
||||
<div class="meeting-info">
|
||||
<div class="meeting-title">Elterngespräch - Anna Schmidt</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-xyz')">Beitreten</button>
|
||||
<button class="btn-icon" onclick="editMeeting('parent-xyz')">{ICONS['settings']}</button>
|
||||
<button class="btn-icon" onclick="copyLink('parent-xyz')">{ICONS['link']}</button>
|
||||
<button class="btn-icon" onclick="deleteMeeting('parent-xyz')">{ICONS['trash']}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Schedule Modal -->
|
||||
<div class="modal-overlay" id="scheduleModal">
|
||||
<div class="modal">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title">Meeting planen</h2>
|
||||
<button class="modal-close" onclick="closeScheduleModal()">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Titel</label>
|
||||
<input type="text" class="form-input" id="scheduleTitle" placeholder="Meeting-Titel">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Datum</label>
|
||||
<input type="date" class="form-input" id="scheduleDate">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Uhrzeit</label>
|
||||
<input type="time" class="form-input" id="scheduleTime">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Dauer</label>
|
||||
<select class="form-select" id="scheduleDuration">
|
||||
<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">Beschreibung (optional)</label>
|
||||
<textarea class="form-textarea" id="scheduleDescription" placeholder="Agenda oder Beschreibung..."></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Teilnehmer einladen</label>
|
||||
<input type="email" class="form-input" id="scheduleInvites" placeholder="E-Mail-Adressen (kommagetrennt)">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" onclick="closeScheduleModal()">Abbrechen</button>
|
||||
<button class="btn btn-primary" onclick="scheduleMeeting()">Meeting planen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openScheduleModal() {{
|
||||
document.getElementById('scheduleModal').classList.add('active');
|
||||
// Set default date to today
|
||||
document.getElementById('scheduleDate').valueAsDate = new Date();
|
||||
}}
|
||||
|
||||
function closeScheduleModal() {{
|
||||
document.getElementById('scheduleModal').classList.remove('active');
|
||||
}}
|
||||
|
||||
async function scheduleMeeting() {{
|
||||
const title = document.getElementById('scheduleTitle').value;
|
||||
const date = document.getElementById('scheduleDate').value;
|
||||
const time = document.getElementById('scheduleTime').value;
|
||||
const duration = document.getElementById('scheduleDuration').value;
|
||||
const description = document.getElementById('scheduleDescription').value;
|
||||
const invites = document.getElementById('scheduleInvites').value;
|
||||
|
||||
if (!title || !date || !time) {{
|
||||
alert('Bitte füllen Sie alle Pflichtfelder aus.');
|
||||
return;
|
||||
}}
|
||||
|
||||
const payload = {{
|
||||
title,
|
||||
scheduled_at: `${{date}}T${{time}}`,
|
||||
duration: parseInt(duration),
|
||||
description,
|
||||
invites: invites.split(',').map(e => e.trim()).filter(e => e)
|
||||
}};
|
||||
|
||||
try {{
|
||||
const response = await fetch('/api/meetings/schedule', {{
|
||||
method: 'POST',
|
||||
headers: {{ 'Content-Type': 'application/json' }},
|
||||
body: JSON.stringify(payload)
|
||||
}});
|
||||
|
||||
if (response.ok) {{
|
||||
alert('Meeting erfolgreich geplant!');
|
||||
closeScheduleModal();
|
||||
location.reload();
|
||||
}}
|
||||
}} catch (error) {{
|
||||
console.error('Error:', error);
|
||||
}}
|
||||
}}
|
||||
|
||||
function filterMeetings(filter) {{
|
||||
// Update active tab
|
||||
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
||||
event.target.classList.add('active');
|
||||
|
||||
// Filter logic would go here
|
||||
console.log('Filter:', filter);
|
||||
}}
|
||||
|
||||
function joinMeeting(roomId) {{
|
||||
window.location.href = '/meetings/room/' + roomId;
|
||||
}}
|
||||
|
||||
function editMeeting(roomId) {{
|
||||
// Open edit modal
|
||||
console.log('Edit:', roomId);
|
||||
}}
|
||||
|
||||
function copyLink(roomId) {{
|
||||
const link = window.location.origin + '/meetings/room/' + roomId;
|
||||
navigator.clipboard.writeText(link);
|
||||
alert('Link kopiert!');
|
||||
}}
|
||||
|
||||
function deleteMeeting(roomId) {{
|
||||
if (confirm('Meeting wirklich löschen?')) {{
|
||||
// Delete logic
|
||||
console.log('Delete:', roomId);
|
||||
}}
|
||||
}}
|
||||
</script>
|
||||
'''
|
||||
|
||||
return render_base_page("Termine", content, "schedule")
|
||||
Reference in New Issue
Block a user