119 lines
3.7 KiB
HTML
119 lines
3.7 KiB
HTML
|
|
{% extends "v2/base.html" %}
|
||
|
|
|
||
|
|
{% block title %}Freischaltungen — GWÖ-Antragsprüfer{% endblock %}
|
||
|
|
|
||
|
|
{% set v2_active_nav = "admin_freischaltungen" %}
|
||
|
|
|
||
|
|
{% block main %}
|
||
|
|
<div style="padding:0 0 1.5rem;">
|
||
|
|
<h1 style="font-family:var(--font-display);font-size:22px;color:var(--ecg-teal);margin:0 0 4px;">Freischaltungen</h1>
|
||
|
|
<p style="font-size:12px;font-family:var(--font-mono);color:var(--ecg-dark);opacity:0.6;">
|
||
|
|
Ausstehende Registrierungen · nur für Admins
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div id="loading" style="font-family:var(--font-mono);font-size:12px;opacity:0.5;padding:16px 0;">
|
||
|
|
Lade ausstehende Freischaltungen …
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div id="empty" class="v2-kasten outline-green" style="display:none;">
|
||
|
|
<h4>Keine ausstehenden Freischaltungen</h4>
|
||
|
|
<p>Alle Registrierungen wurden bereits bearbeitet.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div id="error" class="v2-kasten outline-blue" style="display:none;">
|
||
|
|
<h4>Fehler beim Laden</h4>
|
||
|
|
<p id="error-msg"></p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div id="table-wrap" style="display:none;">
|
||
|
|
<table class="v2-admin-table">
|
||
|
|
<thead>
|
||
|
|
<tr>
|
||
|
|
<th>User-ID</th>
|
||
|
|
<th>E-Mail</th>
|
||
|
|
<th>Name</th>
|
||
|
|
<th>Registriert</th>
|
||
|
|
<th></th>
|
||
|
|
</tr>
|
||
|
|
</thead>
|
||
|
|
<tbody id="user-rows"></tbody>
|
||
|
|
</table>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{% endblock %}
|
||
|
|
|
||
|
|
{% block body_scripts %}
|
||
|
|
<script>
|
||
|
|
(async function () {
|
||
|
|
const loadingEl = document.getElementById('loading');
|
||
|
|
const emptyEl = document.getElementById('empty');
|
||
|
|
const errorEl = document.getElementById('error');
|
||
|
|
const errorMsgEl = document.getElementById('error-msg');
|
||
|
|
const tableEl = document.getElementById('table-wrap');
|
||
|
|
const tbodyEl = document.getElementById('user-rows');
|
||
|
|
|
||
|
|
function showError(msg) {
|
||
|
|
loadingEl.style.display = 'none';
|
||
|
|
errorMsgEl.textContent = msg;
|
||
|
|
errorEl.style.display = '';
|
||
|
|
}
|
||
|
|
|
||
|
|
async function loadUsers() {
|
||
|
|
try {
|
||
|
|
const resp = await fetch('/api/auth/pending-users');
|
||
|
|
if (resp.status === 403) { showError('Zugriff verweigert (kein Admin).'); return; }
|
||
|
|
if (!resp.ok) { showError('HTTP ' + resp.status); return; }
|
||
|
|
const users = await resp.json();
|
||
|
|
loadingEl.style.display = 'none';
|
||
|
|
|
||
|
|
if (!users.length) { emptyEl.style.display = ''; return; }
|
||
|
|
|
||
|
|
tbodyEl.innerHTML = users.map(u => `
|
||
|
|
<tr id="row-${u.id}">
|
||
|
|
<td style="font-family:var(--font-mono);font-size:11px;opacity:0.7;">${u.id}</td>
|
||
|
|
<td>${u.email || '—'}</td>
|
||
|
|
<td>${[u.firstName, u.lastName].filter(Boolean).join(' ') || '—'}</td>
|
||
|
|
<td style="font-family:var(--font-mono);font-size:11px;">${u.createdTimestamp ? new Date(u.createdTimestamp).toLocaleDateString('de-DE') : '—'}</td>
|
||
|
|
<td>
|
||
|
|
<button class="v2-admin-btn" onclick="approveUser('${u.id}', this)">
|
||
|
|
Freischalten
|
||
|
|
</button>
|
||
|
|
</td>
|
||
|
|
</tr>`).join('');
|
||
|
|
|
||
|
|
tableEl.style.display = '';
|
||
|
|
} catch (e) {
|
||
|
|
showError(e.message);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
window.approveUser = async function(userId, btn) {
|
||
|
|
btn.disabled = true;
|
||
|
|
btn.textContent = '…';
|
||
|
|
try {
|
||
|
|
const fd = new FormData();
|
||
|
|
fd.append('user_id', userId);
|
||
|
|
const resp = await fetch('/api/auth/approve-user', { method: 'POST', body: fd });
|
||
|
|
if (!resp.ok) {
|
||
|
|
const data = await resp.json().catch(() => ({}));
|
||
|
|
btn.textContent = 'Fehler: ' + (data.detail || resp.status);
|
||
|
|
btn.style.color = 'var(--ecg-blue)';
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const row = document.getElementById('row-' + userId);
|
||
|
|
if (row) {
|
||
|
|
row.style.opacity = '0.4';
|
||
|
|
row.querySelector('td:last-child').textContent = 'Freigeschaltet ✓';
|
||
|
|
}
|
||
|
|
} catch (e) {
|
||
|
|
btn.textContent = 'Fehler';
|
||
|
|
btn.style.color = 'var(--ecg-blue)';
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
await loadUsers();
|
||
|
|
})();
|
||
|
|
</script>
|
||
|
|
{% endblock %}
|