Files
Pivot/templates/verzendlijsten.html
T
2026-10-14 12:29:29 +02:00

167 lines
9.0 KiB
HTML
Raw 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.
{% extends "base.html" %}
{% block title %}Verzendlijsten{% endblock %}
{% block page_title %}Verzendlijsten{% endblock %}
{% block loader_text %}Verzendlijsten ophalen bij Google...{% endblock %}
{% block extra_css %}
<style>
.nieuw-grid { display: grid; grid-template-columns: 1.4fr 1.2fr 1.6fr; gap: 12px; align-items: end; }
.nieuw-grid .breed { grid-column: 1 / -1; }
.nieuw-grid .field { margin: 0; }
.adres-veld { display: flex; align-items: stretch; }
.adres-veld input { border-top-right-radius: 0; border-bottom-right-radius: 0; flex: 1; min-width: 0; }
.adres-veld select, .adres-veld .at { border-top-left-radius: 0; border-bottom-left-radius: 0; }
.adres-veld .at { display: grid; place-items: center; padding: 0 8px; background: var(--surface-muted);
border: 1px solid var(--border); border-left: 0; border-right: 0; color: var(--text-muted); }
.adres-veld select { width: auto; max-width: 55%; }
@media (max-width: 760px) { .nieuw-grid { grid-template-columns: 1fr; } }
details.nieuw > summary { cursor: pointer; list-style: none; display: inline-flex; }
details.nieuw > summary::-webkit-details-marker { display: none; }
details.nieuw[open] > summary { margin-bottom: 14px; }
.zoekbalk { display: flex; gap: 10px; margin-bottom: 12px; }
.zoekbalk input { flex: 1; }
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; }
td.mail { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .88em; }
</style>
{% endblock %}
{% block content %}
<div class="card">
<details class="nieuw" {% if not lijsten and not fout %}open{% endif %}>
<summary class="btn btn-success">➕ Nieuwe verzendlijst</summary>
<form method="POST" action="{{ url_for('verzendlijsten.nieuw') }}" class="nieuw-grid" onsubmit="showLoader('Verzendlijst aanmaken...')">
<div class="field">
<label for="adres">E-mailadres</label>
<div class="adres-veld">
<input type="text" id="adres" name="adres" required placeholder="bv. leerkrachten-1ste-graad" autocomplete="off">
{% if domeinen %}
<span class="at">@</span>
<select name="domein" aria-label="Domein">
{% for d in domeinen %}<option>{{ d }}</option>{% endfor %}
</select>
{% endif %}
</div>
</div>
<div class="field">
<label for="naam">Naam</label>
<input type="text" id="naam" name="naam" required placeholder="bv. Leerkrachten 1ste graad">
</div>
<div class="field">
<label for="wie_mailt_n">Wie mag mailen naar de lijst?</label>
<select id="wie_mailt_n" name="wie_mailt">
<option value="">Standaard van Google</option>
{% for w, l in wie_mailt %}<option value="{{ w }}">{{ l }}</option>{% endfor %}
</select>
</div>
<div class="field breed">
<label for="beschrijving">Beschrijving</label>
<input type="text" id="beschrijving" name="beschrijving" placeholder="Waarvoor dient deze lijst? (optioneel)">
</div>
<div class="breed"><button type="submit">💾 Verzendlijst aanmaken</button></div>
</form>
</details>
</div>
<div class="card">
<div class="page-head">
<h2>📮 Verzendlijsten</h2>
<a class="btn btn-light btn-sm" href="{{ url_for('verzendlijsten.overzicht') }}" onclick="showLoader()">🔄 Vernieuwen</a>
</div>
{% if fout %}
<div class="alert alert-danger">{{ fout }}</div>
{% else %}
<div class="zoekbalk">
<input type="search" id="zoek" placeholder="Zoek op naam, adres of beschrijving..." aria-label="Zoeken">
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th class="sorteer" data-kol="0">Naam <span class="pijl"></span></th>
<th class="sorteer" data-kol="1">E-mailadres <span class="pijl"></span></th>
<th class="sorteer text-right" data-kol="2">Leden <span class="pijl"></span></th>
<th class="sorteer" data-kol="3">Beschrijving <span class="pijl"></span></th>
<th class="actions"></th>
</tr>
</thead>
<tbody id="lijstBody">
{% for g in lijsten %}
<tr data-zoek="{{ [g.naam, g.email, g.beschrijving]|join(' ')|lower }}">
<td data-sort="{{ g.naam }}"><strong>{{ g.naam }}</strong></td>
<td class="mail" data-sort="{{ g.email }}">{{ g.email }}{% if g.aliassen %}<br><span class="muted small">ook: {{ g.aliassen|join(', ') }}</span>{% endif %}</td>
<td class="text-right" data-sort="{{ '%06d'|format(g.leden) }}">{{ g.leden }}</td>
<td class="muted small" data-sort="{{ g.beschrijving }}">{{ g.beschrijving }}</td>
<td class="actions"><a class="btn btn-sm btn-secondary" href="{{ url_for('verzendlijsten.detail', email=g.email) }}" onclick="showLoader('Leden ophalen...')">✏️ Openen</a></td>
</tr>
{% else %}
<tr class="empty-row"><td colspan="5">Er zijn nog geen verzendlijsten in Google.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="tabel-voet">
<div style="display:flex;align-items:center;gap:14px;flex-wrap:wrap">
<div class="muted small" id="teller"></div>
<span id="perPagina"></span>
</div>
<nav class="paginering" id="paginering" aria-label="Pagina's"></nav>
</div>
{% endif %}
</div>
{% endblock %}
{% block scripts %}
<script>
(function () {
const body = document.getElementById('lijstBody');
if (!body || body.querySelector('.empty-row')) return;
let PER_PAGINA = perPagina('verzendlijsten'), pagina = 1, kol = null, op = true, zoek = '';
const zichtbaar = () => Array.from(body.rows).filter(tr => !zoek || tr.dataset.zoek.includes(zoek));
function toon() {
const rijen = zichtbaar(), grootte = PER_PAGINA || Infinity;
const 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');
document.getElementById('teller').textContent = rijen.length
? `${van + 1}–${Math.min(tot, rijen.length)} van ${rijen.length} verzendlijst(en)` : 'Geen verzendlijsten gevonden';
const nav = document.getElementById('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); }
}
document.getElementById('zoek').addEventListener('input', e => { zoek = e.target.value.trim().toLowerCase(); pagina = 1; toon(); });
document.querySelectorAll('th.sorteer').forEach(th => th.addEventListener('click', () => {
const k = +th.dataset.kol;
op = kol === k ? !op : (k !== 2); kol = k;
document.querySelectorAll('th.sorteer').forEach(x => x.classList.remove('op', 'af'));
th.classList.add(op ? 'op' : 'af');
Array.from(body.rows).sort((a, b) => {
const c = (a.cells[k].dataset.sort || '').localeCompare(b.cells[k].dataset.sort || '', 'nl', { numeric: true, sensitivity: 'base' });
return op ? c : -c;
}).forEach(tr => body.appendChild(tr));
pagina = 1; toon();
}));
perPaginaKiezer(document.getElementById('perPagina'), 'verzendlijsten', n => { PER_PAGINA = n; pagina = 1; toon(); });
toon();
})();
</script>
{% endblock %}