144 lines
7.6 KiB
HTML
144 lines
7.6 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}Toegestane IP-adressen{% endblock %}
|
||
{% block page_title %}Toegestane IP-adressen{% endblock %}
|
||
|
||
{% block extra_css %}
|
||
<style>
|
||
.ip-form { display: flex; flex-wrap: wrap; gap: 10px; align-items: end; }
|
||
.ip-form .field { margin: 0; flex: 0 1 340px; }
|
||
.mijn-ip { display: inline-flex; gap: 8px; align-items: center; padding: 6px 12px; border-radius: 999px;
|
||
background: var(--surface-muted); border: 1px solid var(--border); font-size: .92em; }
|
||
.mijn-ip button { all: unset; cursor: pointer; color: var(--primary-color); font-size: .9em; }
|
||
.mono, td.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
|
||
.tabel-zoek { margin-bottom: 10px; max-width: 360px; }
|
||
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; }
|
||
.tabel-voet { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 10px; margin-top: 12px; }
|
||
.paginering { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="card">
|
||
<div class="page-head">
|
||
<h2>✅ Toegestane IP-adressen · {{ school_naam }}</h2>
|
||
<span class="mijn-ip">Jouw IP-adres: <strong class="mono">{{ mijn_ip }}</strong>
|
||
{% if not lokaal %}<button type="button" id="gebruikMijnIp" title="Invullen in het veld hieronder">invullen</button>{% endif %}</span>
|
||
</div>
|
||
<p class="field-hint" style="margin-top:-6px">Een adres in deze lijst mag PIVOT altijd openen. Je voegt het toe voor
|
||
{{ school_naam }}, met jouw naam ({{ naam }}) als opmerking. Je kan enkel de adressen verwijderen die je zelf toegevoegd hebt.</p>
|
||
|
||
<form method="POST" action="{{ url_for('ipblokkade.toevoegen') }}" class="ip-form">
|
||
<div class="field">
|
||
<label for="reeks">IP-adres</label>
|
||
<input type="text" id="reeks" name="reeks" required class="mono" placeholder="bv. 85.12.3.4" autocomplete="off">
|
||
</div>
|
||
<button type="submit">➕ Toevoegen</button>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="card ip-tabel" data-sleutel="school">
|
||
<h2 class="card-title">Lijst <span class="muted small">({{ rijen|length }})</span></h2>
|
||
{% if rijen|length > 10 %}<input type="search" class="tabel-zoek" placeholder="Zoek een adres of naam..." aria-label="Zoeken">{% endif %}
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th class="sorteer" data-tekst>IP-adres <span class="pijl"></span></th>
|
||
<th class="sorteer" data-tekst>Opmerking <span class="pijl"></span></th>
|
||
<th class="sorteer">Toegevoegd <span class="pijl"></span></th>
|
||
<th class="actions"></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for r in rijen %}
|
||
<tr>
|
||
<td class="mono" data-sort="{{ r.Sorteer }}"><strong>{{ r.Reeks }}</strong></td>
|
||
<td data-sort="{{ r.Opmerking or '' }}">{{ r.Opmerking or '' }}</td>
|
||
<td class="muted small" data-sort="{{ r.Aangemaakt.isoformat() }}">{{ r.Aangemaakt.strftime('%d-%m-%Y %H:%M') }}</td>
|
||
<td class="actions">
|
||
{% if r.Eigen %}
|
||
<form method="POST" action="{{ url_for('ipblokkade.verwijderen', regel_id=r.ID) }}" style="margin:0"
|
||
onsubmit="return confirm({{ (r.Reeks ~ ' uit de lijst verwijderen?')|tojson|forceescape }})">
|
||
<button type="submit" class="btn-sm btn-ghost">🗑️ Verwijderen</button>
|
||
</form>
|
||
{% endif %}
|
||
</td>
|
||
</tr>
|
||
{% else %}
|
||
<tr class="empty-row"><td colspan="4">Er zijn nog geen toegestane adressen voor {{ school_naam }}.</td></tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{% if rijen %}
|
||
<div class="tabel-voet">
|
||
<div style="display:flex;align-items:center;gap:14px;flex-wrap:wrap">
|
||
<div class="muted small teller"></div>
|
||
<span class="per-pagina-plek"></span>
|
||
</div>
|
||
<nav class="paginering" aria-label="Pagina's"></nav>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block scripts %}
|
||
<script>
|
||
document.getElementById('gebruikMijnIp')?.addEventListener('click', () => {
|
||
const v = document.getElementById('reeks'); v.value = {{ mijn_ip|tojson }}; v.focus();
|
||
});
|
||
document.querySelectorAll('.ip-tabel').forEach(kaart => {
|
||
const body = kaart.querySelector('tbody');
|
||
if (!body || body.querySelector('.empty-row')) return;
|
||
const sleutel = 'ip-' + kaart.dataset.sleutel;
|
||
let PER_PAGINA = perPagina(sleutel), pagina = 1, zoek = '';
|
||
const zoekveld = kaart.querySelector('.tabel-zoek');
|
||
function toon() {
|
||
const rijen = Array.from(body.rows).filter(tr => !zoek || tr.textContent.toLowerCase().includes(zoek));
|
||
const grootte = PER_PAGINA || Infinity, paginas = Math.max(1, Math.ceil(rijen.length / grootte));
|
||
pagina = Math.min(Math.max(1, pagina), paginas);
|
||
const van = PER_PAGINA ? (pagina - 1) * PER_PAGINA : 0, tot = van + grootte;
|
||
Array.from(body.rows).forEach(tr => tr.style.display = 'none');
|
||
rijen.forEach((tr, i) => tr.style.display = (i >= van && i < tot) ? '' : 'none');
|
||
kaart.querySelector('.teller').textContent = rijen.length
|
||
? `${van + 1}–${Math.min(tot, rijen.length)} van ${rijen.length}` : 'Niets gevonden';
|
||
const nav = kaart.querySelector('.paginering');
|
||
nav.innerHTML = '';
|
||
if (paginas <= 1) return;
|
||
const knop = (label, p, actief) => {
|
||
const b = document.createElement('button');
|
||
b.type = 'button'; b.textContent = label; b.className = 'btn-sm ' + (actief ? 'btn-secondary' : 'btn-light');
|
||
b.onclick = () => { pagina = p; toon(); };
|
||
nav.appendChild(b);
|
||
};
|
||
if (pagina > 1) { knop('«', 1); knop('‹ Vorige', pagina - 1); }
|
||
for (let p = Math.max(1, pagina - 2); p <= Math.min(paginas, pagina + 2); p++) knop(String(p), p, p === pagina);
|
||
if (pagina < paginas) { knop('Volgende ›', pagina + 1); knop('»', paginas); }
|
||
}
|
||
if (zoekveld) zoekveld.addEventListener('input', () => { zoek = zoekveld.value.trim().toLowerCase(); pagina = 1; toon(); });
|
||
let kol = null, op = true;
|
||
kaart.querySelectorAll('th.sorteer').forEach(th => th.addEventListener('click', () => {
|
||
const k = th.cellIndex;
|
||
op = kol === k ? !op : 'tekst' in th.dataset;
|
||
kol = k;
|
||
kaart.querySelectorAll('th.sorteer').forEach(x => x.classList.remove('op', 'af'));
|
||
th.classList.add(op ? 'op' : 'af');
|
||
Array.from(body.rows).sort((a, b) => {
|
||
const x = a.cells[k].dataset.sort || '', y = b.cells[k].dataset.sort || '';
|
||
if (!x !== !y) return x ? -1 : 1;
|
||
const c = x.localeCompare(y, 'nl', { numeric: true, sensitivity: 'base' });
|
||
return op ? c : -c;
|
||
}).forEach(tr => body.appendChild(tr));
|
||
pagina = 1; toon();
|
||
}));
|
||
perPaginaKiezer(kaart.querySelector('.per-pagina-plek'), sleutel, n => { PER_PAGINA = n; pagina = 1; toon(); });
|
||
toon();
|
||
});
|
||
</script>
|
||
{% endblock %}
|