Files
Adminator 1e51ef7def Nachtrag: alle bisher untracked Ordner + hängende Änderungen mit-committen
- Konzept/, didaktik_geografie/, didaktik_simulation/, v2-modules/, v2-platform/
- 12 code-workspace-Files
- STATUS-*.md
- viele M/D/R-Änderungen an bereits getrackten Files
- .gitignore verstärkt: **/.humaninput/, **/secret_keys.txt

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-08 02:27:02 +02:00

118 lines
4.7 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>GeoGraSim V2 — Plattform-Konfiguration</title>
<link rel="stylesheet" href="./assets/css/design-system-v2.css">
<style>
body { background: var(--ggs-bg); min-height: 100dvh; padding: var(--ggs-gap-lg); }
.wrap { max-width: 900px; margin: 0 auto; }
h1 { color: var(--ggs-fjord-dark); margin-bottom: var(--ggs-gap-md); }
.group { background: var(--ggs-white); border: 1px solid var(--ggs-border); border-radius: var(--ggs-radius-md); padding: var(--ggs-gap-lg); margin-bottom: var(--ggs-gap-md); }
.group h2 { font-size: 0.85rem; text-transform: uppercase; color: var(--ggs-text-muted); margin-bottom: var(--ggs-gap-md); }
.row { display: grid; grid-template-columns: 1fr 180px 90px; gap: var(--ggs-gap-md); align-items: center; padding: var(--ggs-gap-sm) 0; border-bottom: 1px solid var(--ggs-border); }
.row:last-child { border-bottom: none; }
.row label { font-weight: 600; font-size: 0.9rem; color: var(--ggs-text); }
.row .help { font-size: 0.72rem; color: var(--ggs-text-muted); margin-top: 2px; }
.row .range { font-size: 0.7rem; color: var(--ggs-text-light); margin-top: 2px; }
.row input { width: 100%; }
.status { font-size: 0.75rem; color: var(--ggs-text-muted); padding: 4px 8px; border-radius: var(--ggs-radius-sm); }
.status.saved { background: var(--ggs-moss-light); color: var(--ggs-moss-dark); }
.status.error { background: var(--ggs-coral-light); color: var(--ggs-coral); }
.topbar { display: flex; align-items: center; gap: var(--ggs-gap-md); margin-bottom: var(--ggs-gap-lg); }
.topbar .spacer { flex: 1; }
</style>
</head>
<body>
<div class="wrap">
<div class="topbar">
<h1>⚙ Plattform-Konfiguration</h1>
<div class="spacer"></div>
<a href="./teacher.html" class="ggs-btn ggs-btn-secondary">Zurück zum Cockpit</a>
<button class="ggs-btn ggs-btn-secondary" id="logoutBtn">Abmelden</button>
</div>
<div id="content">
<p style="color:var(--ggs-text-muted)">Wird geladen …</p>
</div>
</div>
<script type="module">
import { auth } from './assets/js/auth-client.js';
if (!auth.token || auth.role !== 'admin') {
alert('Nur Admins dürfen die Plattform-Konfiguration ändern.');
location.replace('./login.html');
}
document.getElementById('logoutBtn').addEventListener('click', async () => {
await auth.logout(); location.replace('./login.html');
});
const groupLabels = {
telemetry: '📡 Telemetrie',
auth: '🔐 Authentifizierung',
general: '⚙ Allgemein',
};
async function load() {
const r = await auth.api('GET', 'admin/platform-config.php');
const html = Object.entries(r.groups).map(([gname, entries]) => `
<div class="group">
<h2>${escapeHtml(groupLabels[gname] || gname)}</h2>
${entries.map(e => `
<div class="row" data-key="${escapeAttr(e.key)}">
<div>
<label for="i-${escapeAttr(e.key)}">${escapeHtml(e.label)}</label>
<div class="help">${escapeHtml(e.help || '')}</div>
${e.min !== null && e.max !== null ? `<div class="range">Bereich: ${e.min} ${e.max}</div>` : ''}
</div>
<input class="ggs-input" id="i-${escapeAttr(e.key)}" type="${e.type === 'int' ? 'number' : 'text'}"
value="${escapeAttr(e.value)}"
${e.min !== null ? `min="${e.min}"` : ''}
${e.max !== null ? `max="${e.max}"` : ''}>
<span class="status"></span>
</div>
`).join('')}
</div>
`).join('');
document.getElementById('content').innerHTML = html;
document.querySelectorAll('.row input').forEach(input => {
let timer = null;
input.addEventListener('input', () => {
clearTimeout(timer);
const status = input.closest('.row').querySelector('.status');
status.textContent = '…';
status.className = 'status';
timer = setTimeout(() => save(input, status), 800);
});
});
}
async function save(input, status) {
const key = input.closest('.row').dataset.key;
try {
await auth.api('PATCH', 'admin/platform-config.php', { key, value: input.value });
status.textContent = '✓ gespeichert';
status.className = 'status saved';
setTimeout(() => { status.textContent = ''; status.className = 'status'; }, 2000);
} catch (e) {
status.textContent = '✗ ' + e.message;
status.className = 'status error';
}
}
function escapeHtml(s) { return String(s ?? '').replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c])); }
function escapeAttr(s) { return escapeHtml(s); }
load().catch(err => {
document.getElementById('content').innerHTML = `<p style="color:var(--ggs-coral)">Fehler: ${err.message}</p>`;
});
</script>
</body>
</html>