Initial commit

This commit is contained in:
Kristof committed 2026-10-14 12:29:29 +02:00
commit 536ff43ce4
158 files changed
+30355

No files matched your search

+147
View File
@@ -0,0 +1,147 @@
<!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()">&times;</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>