Klik op je naam rechtsboven om een menu te openen met een schakelaar voor het donkere thema. De keuze wordt per browser onthouden. - global.css: donkere kleurvariabelen, alleen op scherm (afdrukken blijft licht) - base.html: gebruikersmenu met themaschakelaar - vaste witte achtergronden en pastelkleuren op enkele pagina's volgen nu het thema Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
355 lines
22 KiB
HTML
355 lines
22 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}2FA-hulp{% endblock %}
|
||
{% block page_title %}2FA-hulp{% endblock %}
|
||
{% block main_class %}{{ '' if tab == 'lijst' else 'medium' }}{% endblock %}
|
||
|
||
{% block extra_css %}
|
||
<style>
|
||
.user-head { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; margin-bottom: 4px; }
|
||
.avatar { width: 46px; height: 46px; border-radius: 50%; display: grid; place-items: center; flex-shrink: 0;
|
||
background: var(--primary-light); color: var(--primary-color); font-weight: 800; font-size: 18px; }
|
||
.user-head .who { flex: 1; min-width: 220px; }
|
||
.user-head .who strong { display: block; font-size: 1.1rem; }
|
||
.badges { display: flex; flex-wrap: wrap; gap: 6px; }
|
||
.actions-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; margin-top: 18px; }
|
||
@media (max-width: 760px) { .actions-grid { grid-template-columns: 1fr; } }
|
||
.action-box { border: 1px solid var(--border); border-radius: var(--radius); padding: 18px; background: var(--surface-muted); }
|
||
.action-box h3 { margin: 0 0 4px; font-size: 1rem; }
|
||
.action-box p { font-size: 14px; color: var(--text-muted); margin: 0 0 14px; }
|
||
.action-box .btn-row { display: flex; flex-wrap: wrap; gap: 8px; }
|
||
.code-panel { margin-top: 18px; border: 2px solid var(--success); border-radius: var(--radius-lg); background: var(--success-bg); padding: 20px; text-align: center; }
|
||
.big-code { font-family: ui-monospace, Consolas, monospace; font-size: 40px; font-weight: 800; letter-spacing: 6px; color: var(--success-text); margin: 6px 0 4px; }
|
||
.code-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 6px; margin-top: 12px; }
|
||
.code-grid span { font-family: ui-monospace, Consolas, monospace; background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 6px; font-size: 15px; }
|
||
details summary { cursor: pointer; color: var(--text-muted); font-size: 14px; margin-top: 12px; }
|
||
.exception-active { margin-top: 14px; }
|
||
.pick-list { list-style: none; margin: 10px 0 0; padding: 0; }
|
||
.pick-list a { display: flex; justify-content: space-between; gap: 12px; padding: 10px 12px; border-radius: var(--radius-sm); text-decoration: none; color: var(--text); }
|
||
.pick-list a:hover { background: var(--primary-light); }
|
||
.pick-list .ou { color: var(--text-muted); font-size: 13px; }
|
||
.setup-hint { font-size: 13px; }
|
||
/* Afdrukken: enkel het codebriefje */
|
||
@media print {
|
||
body.print-slip * { visibility: hidden; }
|
||
body.print-slip #codeSlip, body.print-slip #codeSlip * { visibility: visible; }
|
||
body.print-slip #codeSlip { position: absolute; left: 0; top: 0; width: 100%; border: 1px dashed #999; background: #fff; }
|
||
body.print-slip #codeSlip .no-print { display: none; }
|
||
}
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="tabs">
|
||
<a class="tab-link {{ 'active' if tab != 'lijst' else '' }}" href="{{ url_for('tweestaps.overzicht') }}" style="text-decoration:none">🆘 Hulp bij inloggen</a>
|
||
<a class="tab-link {{ 'active' if tab == 'lijst' else '' }}" href="{{ url_for('tweestaps.overzicht', tab='lijst') }}" style="text-decoration:none" onclick="showLoader('Gebruikers ophalen bij Google...')">⚠️ Nog geen 2FA ingesteld</a>
|
||
</div>
|
||
|
||
{% if tab != 'lijst' %}
|
||
<section class="tab-content card active">
|
||
{# ---------- Zoeken ---------- #}
|
||
<form class="search-bar" method="GET" action="{{ url_for('tweestaps.overzicht') }}" onsubmit="showLoader()" style="margin:0">
|
||
<input type="text" name="zoek" placeholder="Naam of e-mailadres van de leerkracht" value="{{ zoek or (gebruiker.email if gebruiker else '') }}" autofocus>
|
||
<button type="submit">🔍 Zoeken</button>
|
||
</form>
|
||
|
||
{% if zoekresultaten %}
|
||
<ul class="pick-list">
|
||
{% for u in zoekresultaten %}
|
||
<li><a href="{{ url_for('tweestaps.overzicht', email=u.email) }}" onclick="showLoader()">
|
||
<span><strong>{{ u.naam }}</strong> · {{ u.email }}</span><span class="ou">{{ u.ou }}</span></a></li>
|
||
{% endfor %}
|
||
</ul>
|
||
{% endif %}
|
||
|
||
{% if fout %}<div class="alert alert-danger" style="margin-top:14px">{{ fout }}</div>{% endif %}
|
||
|
||
{# ---------- Gekozen gebruiker ---------- #}
|
||
{% if gebruiker %}
|
||
<div style="margin-top: 22px; padding-top: 20px; border-top: 1px solid var(--border);">
|
||
<div class="user-head">
|
||
<span class="avatar">{{ (gebruiker.naam or gebruiker.email)[:1]|upper }}</span>
|
||
<div class="who">
|
||
<strong>{{ gebruiker.naam }}</strong>
|
||
<span class="muted">{{ gebruiker.email }} · {{ gebruiker.ou }}</span>
|
||
</div>
|
||
<div class="badges">
|
||
{% if gebruiker.ingesteld %}<span class="badge badge-success">✓ 2FA ingesteld</span>
|
||
{% else %}<span class="badge badge-warning">2FA niet ingesteld</span>{% endif %}
|
||
{% if uitzondering %}<span class="badge badge-info">⏳ Uitzondering tot {{ uitzondering.Tot.strftime('%H:%M') }}</span>
|
||
{% elif gebruiker.afgedwongen %}<span class="badge">Verplicht</span>{% endif %}
|
||
{% if gebruiker.geschorst %}<span class="badge badge-danger">Geschorst</span>{% endif %}
|
||
</div>
|
||
</div>
|
||
|
||
{# ---------- Codes tonen ---------- #}
|
||
{% if codes %}
|
||
<div class="code-panel" id="codeSlip">
|
||
<div class="muted small">Back-upcode voor <strong>{{ gebruiker.email }}</strong></div>
|
||
<div class="big-code">{{ codes[0][:4] }} {{ codes[0][4:] }}</div>
|
||
<div class="small">Typ deze code bij "Probeer een andere manier" › "Voer een van je back-upcodes van 8 cijfers in". Elke code werkt maar één keer.</div>
|
||
<details class="no-print">
|
||
<summary>Alle {{ codes|length }} geldige codes tonen</summary>
|
||
<div class="code-grid">{% for c in codes %}<span>{{ c[:4] }} {{ c[4:] }}</span>{% endfor %}</div>
|
||
</details>
|
||
<div class="btn-row no-print" style="justify-content:center; margin-top:14px">
|
||
<button type="button" class="btn-light btn-sm" onclick="printSlip()">🖨️ Afdrukken</button>
|
||
</div>
|
||
{% if code_actie == 'nieuw' %}<div class="small muted no-print" style="margin-top:8px">Er is een nieuwe set aangemaakt; oudere codes van deze gebruiker werken niet meer.</div>{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="actions-grid">
|
||
{% if mag('tweestaps_codes') %}
|
||
<div class="action-box">
|
||
<h3>📱 Telefoon vergeten</h3>
|
||
<p>Geef een back-upcode. 2FA blijft gewoon aan; de code werkt één keer.</p>
|
||
<form method="POST" action="{{ url_for('tweestaps.codes') }}" class="btn-row" onsubmit="showLoader()">
|
||
<input type="hidden" name="email" value="{{ gebruiker.email }}">
|
||
<button type="submit" name="actie" value="tonen">🔑 Back-upcode geven</button>
|
||
<button type="submit" name="actie" value="nieuw" class="btn-light"
|
||
onclick="return confirm('Een nieuwe set maken? Alle oudere back-upcodes van deze gebruiker werken dan niet meer.')">Nieuwe set</button>
|
||
<button type="submit" name="actie" value="ongeldig" class="btn-ghost"
|
||
onclick="return confirm('Alle back-upcodes van deze gebruiker ongeldig maken?')">Codes intrekken</button>
|
||
</form>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if mag('tweestaps_uitzetten') and reset_wacht %}
|
||
<div class="action-box">
|
||
<h3>⏳ 2FA-reset wacht op Google</h3>
|
||
<p>Gevraagd om {{ reset_wacht.Gevraagd_op.strftime('%H:%M') }} door {{ reset_wacht.Door or '?' }}.
|
||
{{ gebruiker.email }} staat al in de uitzonderingsgroep, maar Google past dat pas na enkele minuten toe.
|
||
PIVOT probeert elke minuut opnieuw (al {{ reset_wacht.Pogingen }} poging(en)), tot {{ reset_wacht.Tot.strftime('%H:%M') }}.
|
||
Je hoeft niets te doen.</p>
|
||
<p>Google meldt nu: 2FA afgedwongen voor deze gebruiker: <strong>{{ 'ja (uitzondering nog niet toegepast)' if gebruiker.afgedwongen else 'nee, de reset volgt bij de volgende poging' }}</strong>.
|
||
{% if reset_wacht.Laatste_fout %}<br><span class="muted">Laatste antwoord van Google: {{ reset_wacht.Laatste_fout }}</span>{% endif %}</p>
|
||
<p class="field-hint">Moet de gebruiker nu meteen binnen? Toon hierboven een <strong>back-upcode</strong>: daarmee logt hij in en stelt hij zijn nieuwe telefoon in via myaccount.google.com › Beveiliging › Authenticatie in 2 stappen. Dat werkt meteen, ook als 2FA afgedwongen wordt.</p>
|
||
<a class="btn-light" href="{{ url_for('tweestaps.overzicht', email=gebruiker.email) }}">🔄 Status vernieuwen</a>
|
||
</div>
|
||
{% elif mag('tweestaps_uitzetten') and gebruiker.ingesteld %}
|
||
<div class="action-box">
|
||
<h3>📵 Nieuwe telefoon: 2FA resetten</h3>
|
||
<p class="field-hint">💡 Snelste weg bij een nieuwe telefoon: geef de gebruiker een <strong>back-upcode</strong> (hierboven). Hij logt daarmee in en koppelt zelf zijn nieuwe telefoon. Resetten is pas nodig als dat niet lukt.</p>
|
||
<p>Zet 2FA uit zodat de gebruiker het opnieuw kan instellen met een nieuw toestel. Oude back-upcodes en het oude toestel werken daarna niet meer.
|
||
{% if gebruiker.afgedwongen %}Omdat 2FA verplicht is, krijgt hij ook een tijdelijke uitzondering tot vandaag {{ einde.strftime('%H:%M') }} om opnieuw in te stellen.{% endif %}</p>
|
||
<form method="POST" action="{{ url_for('tweestaps.reset') }}" onsubmit="return confirm('2FA resetten voor {{ gebruiker.email }}? De gebruiker moet 2FA daarna opnieuw instellen.') && (showLoader(), true)">
|
||
<input type="hidden" name="email" value="{{ gebruiker.email }}">
|
||
<div class="field" style="margin-bottom:10px">
|
||
<input type="text" name="reden" maxlength="255" placeholder="Reden (optioneel), bv. nieuwe telefoon">
|
||
</div>
|
||
<button type="submit" class="btn-danger">📵 2FA resetten</button>
|
||
</form>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if mag('tweestaps_uitzetten') %}
|
||
<div class="action-box">
|
||
<h3>⏳ Tijdelijk zonder 2FA</h3>
|
||
{% if uitzondering %}
|
||
<p>Deze gebruiker kan tot <strong>{{ uitzondering.Tot.strftime('%d-%m %H:%M') }}</strong> zonder 2FA inloggen. Daarna wordt 2FA automatisch terug verplicht.</p>
|
||
<div class="btn-row">
|
||
{% if uitzondering.Tot < einde %}
|
||
<form method="POST" action="{{ url_for('tweestaps.uitzondering') }}" style="margin:0" onsubmit="showLoader()">
|
||
<input type="hidden" name="email" value="{{ gebruiker.email }}">
|
||
<button type="submit" class="btn-light">Verlengen tot {{ einde.strftime('%H:%M') }}</button>
|
||
</form>
|
||
{% endif %}
|
||
<form method="POST" action="{{ url_for('tweestaps.stop_uitzondering', uitz_id=uitzondering.ID) }}" style="margin:0" onsubmit="showLoader()">
|
||
<input type="hidden" name="terug" value="{{ gebruiker.email }}">
|
||
<button type="submit" class="btn-danger">Nu beëindigen</button>
|
||
</form>
|
||
</div>
|
||
{% elif not groep %}
|
||
<p>Nog niet ingesteld: vul de <strong>Uitzonderingsgroep</strong> in bij
|
||
{% if mag('koppelingen') %}<a href="{{ url_for('scholen.mijn_school') }}">🔌 Koppelingen van deze school</a>{% else %}🔌 Koppelingen van deze school{% endif %}
|
||
› 🛡️ 2FA-hulp (een Google-groep waarvoor 2FA niet afgedwongen wordt).</p>
|
||
{% else %}
|
||
<p>Voor wie 2FA nog moet instellen, of als een back-upcode niet lukt. De uitzondering loopt tot
|
||
<strong>vandaag {{ einde.strftime('%H:%M') }}</strong>; daarna zet PIVOT 2FA automatisch terug aan.</p>
|
||
<form method="POST" action="{{ url_for('tweestaps.uitzondering') }}" onsubmit="showLoader()">
|
||
<input type="hidden" name="email" value="{{ gebruiker.email }}">
|
||
<div class="field" style="margin-bottom:10px">
|
||
<input type="text" name="reden" maxlength="255" placeholder="Reden (optioneel), bv. nieuwe telefoon">
|
||
</div>
|
||
<button type="submit" class="btn-warning">⏳ Uitzondering geven tot {{ einde.strftime('%H:%M') }}</button>
|
||
</form>
|
||
{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
</section>
|
||
|
||
{# ---------- Lopende uitzonderingen ---------- #}
|
||
<div class="card">
|
||
<h3 class="card-title">⏳ Lopende uitzonderingen</h3>
|
||
{% if actief %}
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead><tr><th>Gebruiker</th><th>Tot</th><th>Gegeven door</th><th>Reden</th><th class="actions">Actie</th></tr></thead>
|
||
<tbody>
|
||
{% for u in actief %}
|
||
<tr>
|
||
<td><a href="{{ url_for('tweestaps.overzicht', email=u.Email) }}"><strong>{{ u.Naam or u.Email }}</strong></a><div class="muted small">{{ u.Email }}</div></td>
|
||
<td class="nowrap">
|
||
{{ u.Tot.strftime('%H:%M') if u.Tot.date() == nu.date() else u.Tot.strftime('%d-%m %H:%M') }}
|
||
{% if u.Tot <= nu %}<span class="badge badge-warning">wordt beëindigd</span>{% endif %}
|
||
</td>
|
||
<td>{{ u.Door or '?' }}</td>
|
||
<td class="muted">{{ u.Reden or '' }}</td>
|
||
<td class="actions">
|
||
{% if mag('tweestaps_uitzetten') %}
|
||
<form method="POST" action="{{ url_for('tweestaps.stop_uitzondering', uitz_id=u.ID) }}" onsubmit="showLoader()">
|
||
<button type="submit" class="btn-sm btn-danger">Nu beëindigen</button>
|
||
</form>
|
||
{% endif %}
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{% else %}
|
||
<p class="muted" style="margin:0">Geen lopende uitzonderingen. Iedereen moet 2FA gebruiken.</p>
|
||
{% endif %}
|
||
</div>
|
||
|
||
{# ---------- Logboek ---------- #}
|
||
{% if logboek %}
|
||
<div class="card" id="logboek">
|
||
<div class="page-head" style="margin-bottom:10px">
|
||
<h3 class="card-title" style="margin:0">🕑 Recente acties</h3>
|
||
<span class="muted small">{{ log_totaal }} actie(s)</span>
|
||
</div>
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead><tr><th>Tijdstip</th><th>Door</th><th>Actie</th><th>Gebruiker</th><th>Details</th></tr></thead>
|
||
<tbody>
|
||
{% for l in logboek %}
|
||
<tr>
|
||
<td class="nowrap">{{ l.Tijdstip.strftime('%d-%m %H:%M') }}</td>
|
||
<td>{{ l.Door or '?' }}</td>
|
||
<td>{{ l.Actie }}</td>
|
||
<td>{{ l.Email }}</td>
|
||
<td class="muted">{{ l.Details or '' }}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{% set extra = {'email': gebruiker.email} if gebruiker else {} %}
|
||
<div style="display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px;margin-top:12px">
|
||
<form method="GET" action="{{ url_for('tweestaps.overzicht') }}#logboek" style="margin:0">
|
||
{% if gebruiker %}<input type="hidden" name="email" value="{{ gebruiker.email }}">{% endif %}
|
||
<label class="per-pagina">Toon
|
||
<select name="logper" onchange="this.form.submit()" aria-label="Aantal per pagina">
|
||
{% for n in log_keuzes %}<option value="{{ n }}" {% if n == log_per %}selected{% endif %}>{{ n or 'alles' }}</option>{% endfor %}
|
||
</select> per pagina</label>
|
||
</form>
|
||
{% if log_paginas > 1 %}
|
||
<nav class="paginering" aria-label="Pagina's logboek" style="margin-top:0">
|
||
{% if log_pagina > 1 %}
|
||
<a class="btn btn-sm btn-light" href="{{ url_for('tweestaps.overzicht', logpagina=1, **extra) }}#logboek" title="Eerste pagina">«</a>
|
||
<a class="btn btn-sm btn-light" href="{{ url_for('tweestaps.overzicht', logpagina=log_pagina - 1, **extra) }}#logboek">‹ Vorige</a>
|
||
{% endif %}
|
||
{% for p in range([1, log_pagina - 2]|max, [log_paginas, log_pagina + 2]|min + 1) %}
|
||
<a class="btn btn-sm {{ 'btn-secondary' if p == log_pagina else 'btn-light' }}"
|
||
href="{{ url_for('tweestaps.overzicht', logpagina=p, **extra) }}#logboek" {% if p == log_pagina %}aria-current="page"{% endif %}>{{ p }}</a>
|
||
{% endfor %}
|
||
{% if log_pagina < log_paginas %}
|
||
<a class="btn btn-sm btn-light" href="{{ url_for('tweestaps.overzicht', logpagina=log_pagina + 1, **extra) }}#logboek">Volgende ›</a>
|
||
<a class="btn btn-sm btn-light" href="{{ url_for('tweestaps.overzicht', logpagina=log_paginas, **extra) }}#logboek" title="Laatste pagina">»</a>
|
||
{% endif %}
|
||
<span class="muted small" style="margin-left:6px">pagina {{ log_pagina }} van {{ log_paginas }}</span>
|
||
</nav>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% else %}
|
||
{# ---------- Tab: nog geen 2FA ingesteld ---------- #}
|
||
<section class="tab-content card active">
|
||
<div class="page-head">
|
||
<div>
|
||
<h2 style="margin:0">Nog geen 2FA ingesteld</h2>
|
||
<div class="muted small">Actieve accounts in {{ ous }} zonder 2FA</div>
|
||
</div>
|
||
<div class="actions">
|
||
<a class="btn btn-light" href="{{ url_for('tweestaps.overzicht', tab='lijst', vernieuw=1) }}" onclick="showLoader('Gebruikers ophalen bij Google...')">🔄 Vernieuwen</a>
|
||
</div>
|
||
</div>
|
||
{% if lijst_fout %}<div class="alert alert-danger">{{ lijst_fout }}</div>{% endif %}
|
||
{% if lijst is not none %}
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead><tr><th class="sorteer" data-kol="0" data-type="tekst" title="Sorteren op naam">Naam <span class="pijl"></span></th>
|
||
<th class="sorteer" data-kol="1" data-type="tekst" title="Sorteren op e-mail">E-mail <span class="pijl"></span></th><th>OU</th>
|
||
<th class="sorteer" data-kol="3" data-type="datum" title="Sorteren op laatste login">Laatste login <span class="pijl"></span></th>
|
||
<th class="sorteer" data-kol="4" data-type="datum" title="Sorteren op status">Status <span class="pijl"></span></th><th class="actions"></th></tr></thead>
|
||
<tbody id="lijstBody">
|
||
{% set uitz = actief|map(attribute='Email')|list %}
|
||
{% for u in lijst %}
|
||
<tr>
|
||
<td data-sort="{{ u.naam|lower }}"><strong>{{ u.naam }}</strong></td>
|
||
<td data-sort="{{ u.email|lower }}">{{ u.email }}</td>
|
||
<td class="muted">{{ u.ou }}</td>
|
||
<td class="nowrap" data-sort="{{ u.laatste_login or '' }}">{{ u.laatste_login or 'Nooit' }}</td>
|
||
{# volgorde bij sorteren: kan niet inloggen → uitzondering → nog niet verplicht; daarbinnen op naam #}
|
||
<td data-sort="{{ '2' if u.email in uitz else ('1' if u.afgedwongen else '3') }} {{ u.naam|lower }}">
|
||
{% if u.email in uitz %}<span class="badge badge-info">Uitzondering</span>
|
||
{% elif u.afgedwongen %}<span class="badge badge-danger">Kan niet inloggen</span>
|
||
{% else %}<span class="badge badge-warning">Nog niet verplicht</span>{% endif %}
|
||
</td>
|
||
<td class="actions"><a class="btn btn-sm btn-light" href="{{ url_for('tweestaps.overzicht', email=u.email) }}">Helpen</a></td>
|
||
</tr>
|
||
{% else %}
|
||
<tr class="empty-row"><td colspan="6">🎉 Iedereen heeft 2FA ingesteld.</td></tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{% if lijst %}<p class="field-hint">{{ lijst|length }} account(s). "Kan niet inloggen": voor deze mensen wordt 2FA afgedwongen maar ze hebben het nog niet ingesteld. Geef ze een tijdelijke uitzondering zodat ze het kunnen instellen.</p>{% endif %}
|
||
{% endif %}
|
||
</section>
|
||
{% endif %}
|
||
{% endblock %}
|
||
|
||
{% block scripts %}
|
||
<style>
|
||
.paginering { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 12px; }
|
||
th.sorteer { cursor: pointer; user-select: none; white-space: nowrap; }
|
||
th.sorteer:hover { color: var(--primary-color); }
|
||
th.sorteer .pijl::after { content: "↕"; opacity: .35; margin-left: 2px; }
|
||
th.sorteer.op .pijl::after { content: "▲"; opacity: 1; }
|
||
th.sorteer.af .pijl::after { content: "▼"; opacity: 1; }
|
||
</style>
|
||
<script>
|
||
// Sorteren via de kolomkop (Naam, Laatste login). "Nooit" telt als oudste.
|
||
document.querySelectorAll('th.sorteer').forEach(function (th) {
|
||
th.addEventListener('click', function () {
|
||
var body = document.getElementById('lijstBody');
|
||
if (!body) return;
|
||
var kol = +th.dataset.kol, op = !th.classList.contains('op');
|
||
document.querySelectorAll('th.sorteer').forEach(function (x) { x.classList.remove('op', 'af'); });
|
||
th.classList.add(op ? 'op' : 'af');
|
||
var rijen = Array.prototype.filter.call(body.rows, function (r) { return !r.classList.contains('empty-row'); });
|
||
rijen.sort(function (a, b) {
|
||
var x = a.cells[kol].dataset.sort || '', y = b.cells[kol].dataset.sort || '';
|
||
var c = th.dataset.type === 'tekst' ? x.localeCompare(y, 'nl') : (x < y ? -1 : x > y ? 1 : 0);
|
||
return op ? c : -c;
|
||
});
|
||
rijen.forEach(function (r) { body.appendChild(r); });
|
||
});
|
||
});
|
||
|
||
function printSlip() {
|
||
document.body.classList.add('print-slip');
|
||
window.print();
|
||
setTimeout(() => document.body.classList.remove('print-slip'), 500);
|
||
}
|
||
</script>
|
||
{% endblock %}
|