9a61f55cb1
- Design-System (assets/css/design-system.css) mit 21 Komponenten, iPad-Responsive-Breakpoints, Touch-Ziele 36px, Music-Player, Glossar-Tooltips - Templates (sims/template.html + student/teacher-Dashboard) - Docs: module-interface.md (inkl. 4a Sprachregel, 4b Leichte Sprache, 4c iPad), content-architecture.md, crash-recovery.md, music-registry.md - Admin-Infrastruktur: admin-modules.html + api/admin-modules.php (Titel, Emoji, Bild, Status, Dauer, Alter pro Modul) - Inbox-System: _inbox/README.md + _status.md fuer Atlas + Briefings an Klima, Glossar, Lehrplan, Fluss - Zentrale SFX-Pipeline (scripts/generate-sounds.py) - DALL-E-Bilder: 8 Badges + 5 Glossar-Repraesentationsbilder (Querformat) - Logo + Inter-Font lokal - PHP-APIs: admin, glossar, levels, licenses, progress, waypoints, assignments, profile, tickets - Spielsprache entfernt (admin-modules, admin-levels, schueler) - Landing-Page-Bearbeitungen (Boote sichtbarer, Button-Hintergrund) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
95 lines
4.1 KiB
PHP
95 lines
4.1 KiB
PHP
<?php
|
|
/**
|
|
* API: Einladungstickets (druckbare HTML-Seite)
|
|
* GET /api/tickets?class_id=X → Druckbare Zugangskarten fuer alle Schueler der Klasse
|
|
*/
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
|
Response::error('Nur GET erlaubt', 405);
|
|
}
|
|
|
|
$teacherId = Session::requireTeacher();
|
|
$db = Database::get();
|
|
$classId = (int)($_GET['class_id'] ?? 0);
|
|
if (!$classId) Response::error('class_id erforderlich');
|
|
|
|
$class = $db->fetchOne(
|
|
'SELECT id, name, school_year, join_code FROM classes WHERE id = ? AND teacher_id = ?',
|
|
[$classId, $teacherId]
|
|
);
|
|
if (!$class) Response::error('Klasse nicht gefunden', 404);
|
|
|
|
$students = $db->fetchAll(
|
|
'SELECT username, display_name, emoji_avatar FROM students WHERE class_id = ? ORDER BY username',
|
|
[$classId]
|
|
);
|
|
|
|
// Druckbare HTML-Seite ausgeben (kein JSON)
|
|
header('Content-Type: text/html; charset=utf-8');
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Zugangskarten — <?= htmlspecialchars($class['name']) ?></title>
|
|
<style>
|
|
@page { size: A4; margin: 10mm; }
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { font-family: 'Inter', 'Segoe UI', system-ui, sans-serif; background: #fff; color: #1a1a1a; }
|
|
.print-header { text-align: center; padding: 1rem 0 .5rem; border-bottom: 2px solid #4a7c8a; margin-bottom: 1rem; }
|
|
.print-header h1 { font-size: 1.1rem; color: #4a7c8a; }
|
|
.print-header p { font-size: .75rem; color: #8a8a8a; }
|
|
.tickets { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8mm; }
|
|
.ticket {
|
|
border: 2px solid #4a7c8a; border-radius: 10px; padding: 10px;
|
|
page-break-inside: avoid; position: relative; min-height: 140px;
|
|
}
|
|
.ticket-top { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; border-bottom: 1px dashed #dae8ec; padding-bottom: 6px; }
|
|
.ticket-emoji { font-size: 1.8rem; }
|
|
.ticket-brand { font-size: .6rem; font-weight: 800; color: #4a7c8a; letter-spacing: .02em; }
|
|
.ticket-class { font-size: .55rem; color: #8a8a8a; }
|
|
.ticket-row { display: flex; gap: 4px; margin-bottom: 4px; font-size: .7rem; }
|
|
.ticket-label { color: #8a8a8a; font-weight: 500; min-width: 80px; }
|
|
.ticket-val { font-weight: 700; font-family: 'Courier New', monospace; letter-spacing: .04em; }
|
|
.ticket-hint { font-size: .55rem; color: #aaa; margin-top: 6px; text-align: center; }
|
|
.ticket-url { font-size: .55rem; color: #4a7c8a; text-align: center; margin-top: 4px; font-weight: 600; }
|
|
.no-print { text-align: center; padding: 1rem; }
|
|
.no-print button { padding: .5rem 1.5rem; background: #4a7c8a; color: #fff; border: none; border-radius: 8px; font-size: .85rem; font-weight: 700; cursor: pointer; margin: 0 .3rem; }
|
|
.no-print button:hover { background: #3a6470; }
|
|
@media print {
|
|
.no-print { display: none; }
|
|
body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="no-print">
|
|
<button onclick="window.print()">Drucken / als PDF speichern</button>
|
|
<button onclick="window.close()">Schliessen</button>
|
|
</div>
|
|
<div class="print-header">
|
|
<h1>GeoGraSim — Zugangskarten</h1>
|
|
<p>Klasse <?= htmlspecialchars($class['name']) ?> · <?= htmlspecialchars($class['school_year']) ?> · Code: <?= htmlspecialchars($class['join_code']) ?></p>
|
|
</div>
|
|
<div class="tickets">
|
|
<?php foreach ($students as $s): ?>
|
|
<div class="ticket">
|
|
<div class="ticket-top">
|
|
<span class="ticket-emoji"><?= $s['emoji_avatar'] ?: '🧑🎓' ?></span>
|
|
<div>
|
|
<div class="ticket-brand">GeoGraSim</div>
|
|
<div class="ticket-class">Klasse <?= htmlspecialchars($class['name']) ?></div>
|
|
</div>
|
|
</div>
|
|
<div class="ticket-row"><span class="ticket-label">Klassencode:</span><span class="ticket-val"><?= htmlspecialchars($class['join_code']) ?></span></div>
|
|
<div class="ticket-row"><span class="ticket-label">Benutzer:</span><span class="ticket-val"><?= htmlspecialchars($s['username']) ?></span></div>
|
|
<div class="ticket-row"><span class="ticket-label">Passwort:</span><span class="ticket-val">****</span></div>
|
|
<div class="ticket-hint">Passwort wurde bei der Erstellung angezeigt.</div>
|
|
<div class="ticket-url">geograsim.at</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
<?php exit;
|