148 lines
6.9 KiB
HTML
148 lines
6.9 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="nl">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>{% block title %}PIVOT{% endblock %} – {{ school_name_short|upper or 'PIVOT' }}</title>
|
||
<link rel="icon" type="image/png" href="{{ url_for('static', filename='pivot_mark.png') }}">
|
||
<link rel="stylesheet" href="{{ url_for('static', filename='global.css') }}">
|
||
{% block head %}{% endblock %}
|
||
<style>.header-school { display: inline-block; max-width: 200px; overflow: hidden; text-overflow: ellipsis; vertical-align: middle; }</style>
|
||
{% block extra_css %}{% endblock %}
|
||
</head>
|
||
<body class="{% block body_class %}{% endblock %}">
|
||
|
||
{# ---------- Laadscherm (gedeeld) ---------- #}
|
||
<div id="loader-overlay" aria-live="polite">
|
||
<div class="spinner"></div>
|
||
<div class="loader-text" id="loader-text">{% block loader_text %}Verwerken...{% endblock %}</div>
|
||
{% block loader_sub %}{% endblock %}
|
||
</div>
|
||
|
||
{# ---------- Header ---------- #}
|
||
{% block header %}
|
||
<header class="app-header {% block header_class %}{% endblock %}">
|
||
<div class="header-left">
|
||
<a href="{{ url_for('main.dashboard') }}" class="brand" title="Naar het dashboard">
|
||
<span class="brand-mark"><img src="{{ url_for('static', filename='pivot_mark.png') }}" alt=""></span>
|
||
<span class="brand-name">PIVOT</span>
|
||
</a>
|
||
{% block header_nav %}
|
||
<a href="{{ url_for('main.dashboard') }}" class="home-btn">🏠 Home</a>
|
||
{% endblock %}
|
||
</div>
|
||
|
||
<h1 class="page-title">{% block page_title %}{% endblock %}</h1>
|
||
|
||
<div class="header-right">
|
||
{% if school %}
|
||
{% if is_superbeheerder %}
|
||
<a href="{{ url_for('scholen.overzicht') }}" class="header-user header-school" style="color:inherit;text-decoration:none" title="{{ school.naam }} – andere school kiezen">🏫 {{ school.naam }} ⇄</a>
|
||
{% elif andere_scholen %}
|
||
<select class="header-user header-school" title="Andere school kiezen" aria-label="School kiezen"
|
||
style="width:auto;color:inherit;background:transparent;border:0;cursor:pointer"
|
||
onchange="if (this.value) location.href = this.value">
|
||
<option value="" selected>🏫 {{ school.naam }} ⇄</option>
|
||
{% for s in andere_scholen %}
|
||
<option value="{{ url_for('scholen.wissel', code=s.code) }}" style="color:#1e293b">{{ s.naam }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
{% else %}
|
||
<span class="header-user header-school" title="{{ school.naam }}">🏫 {{ school.naam }}</span>
|
||
{% endif %}
|
||
{% endif %}
|
||
{% if session.get('user') %}
|
||
<span class="header-user">👤 {{ session.get('user') }}</span>
|
||
{% endif %}
|
||
<form action="{{ url_for('auth.logout') }}" method="get">
|
||
<button type="submit" class="logout-btn">Uitloggen</button>
|
||
</form>
|
||
</div>
|
||
</header>
|
||
{% endblock %}
|
||
|
||
<main class="app-main {% block main_class %}{% endblock %}">
|
||
{# ---------- Flash-berichten (gedeeld) ---------- #}
|
||
{% block flashes %}
|
||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||
{% if messages %}
|
||
<div class="flash-container">
|
||
{% for category, message in messages %}
|
||
<div class="flash-message {{ category if category else 'info' }}" role="alert">
|
||
<span>{{ message }}</span>
|
||
<button type="button" class="flash-close" aria-label="Sluiten" onclick="this.parentElement.remove()">×</button>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
{% endwith %}
|
||
{% endblock %}
|
||
|
||
{% block content %}{% endblock %}
|
||
</main>
|
||
|
||
{% block modals %}{% endblock %}
|
||
|
||
{% block footer %}
|
||
<footer class="app-footer">
|
||
{% if school %}{{ school.naam }} – {% endif %}PIVOT – Versie {{ version }}
|
||
</footer>
|
||
{% endblock %}
|
||
|
||
<script>
|
||
/* Gedeelde helpers – beschikbaar op elke pagina */
|
||
|
||
/* Aantal rijen per pagina in tabellen: 10 / 25 / 50 / 100 / alles (0). Standaard 25 (of de opgegeven standaard).
|
||
De keuze wordt per tabel onthouden in deze browser. */
|
||
const PER_PAGINA_KEUZES = [10, 25, 50, 100, 0];
|
||
function perPagina(sleutel, standaard = 25) {
|
||
try {
|
||
const v = localStorage.getItem('pivot.perpagina.' + sleutel);
|
||
if (v !== null && PER_PAGINA_KEUZES.includes(+v)) return +v;
|
||
} catch (e) { /* geen opslag beschikbaar */ }
|
||
return standaard;
|
||
}
|
||
function perPaginaKiezer(container, sleutel, opWijzig, standaard = 25) {
|
||
if (!container) return;
|
||
const huidig = perPagina(sleutel, standaard);
|
||
container.innerHTML = '<label class="per-pagina">Toon <select aria-label="Aantal per pagina">'
|
||
+ PER_PAGINA_KEUZES.map(n => `<option value="${n}" ${n === huidig ? 'selected' : ''}>${n || 'alles'}</option>`).join('')
|
||
+ '</select> per pagina</label>';
|
||
container.querySelector('select').addEventListener('change', e => {
|
||
const n = +e.target.value;
|
||
try { localStorage.setItem('pivot.perpagina.' + sleutel, n); } catch (err) { /* negeren */ }
|
||
opWijzig(n);
|
||
});
|
||
}
|
||
|
||
function showLoader(text) {
|
||
if (text) document.getElementById('loader-text').innerText = text;
|
||
document.getElementById('loader-overlay').style.display = 'flex';
|
||
}
|
||
function hideLoader() {
|
||
document.getElementById('loader-overlay').style.display = 'none';
|
||
}
|
||
// Loader verbergen wanneer de pagina via 'terug' uit de cache komt
|
||
window.addEventListener('pageshow', function (e) { if (e.persisted) hideLoader(); });
|
||
|
||
// Succes- en infoberichten verdwijnen na 6 seconden; foutmeldingen blijven staan
|
||
setTimeout(function () {
|
||
document.querySelectorAll('.flash-message:not(.error):not(.danger)').forEach(function (f) {
|
||
f.style.transition = 'opacity .5s ease';
|
||
f.style.opacity = '0';
|
||
setTimeout(function () { f.remove(); }, 500);
|
||
});
|
||
}, 6000);
|
||
|
||
// Escape sluit open modals
|
||
document.addEventListener('keydown', function (e) {
|
||
if (e.key !== 'Escape') return;
|
||
document.querySelectorAll('.modal, .modal-overlay').forEach(function (m) {
|
||
if (m.style.display && m.style.display !== 'none') m.style.display = 'none';
|
||
});
|
||
});
|
||
</script>
|
||
{% block scripts %}{% endblock %}
|
||
</body>
|
||
</html>
|