Files
Pivot/templates/base.html
T
marinostrusandClaude Opus 5.5 8b7fa3f716 Donker thema toevoegen via gebruikersmenu rechtsboven
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>
2026-10-14 18:08:30 +02:00

196 lines
9.5 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.
<!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>
<script>
/* Thema meteen toepassen (vóór het tekenen) zodat de pagina niet eerst licht oplicht */
try { if (localStorage.getItem('pivot.thema') === 'donker') document.documentElement.dataset.theme = 'dark'; } catch (e) { /* geen opslag */ }
</script>
<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:var(--text)">{{ s.naam }}</option>
{% endfor %}
</select>
{% else %}
<span class="header-user header-school" title="{{ school.naam }}">🏫 {{ school.naam }}</span>
{% endif %}
{% endif %}
{% if session.get('user') %}
<div class="user-menu" id="user-menu">
<button type="button" class="header-btn header-user user-menu-btn" id="user-menu-btn"
aria-haspopup="true" aria-expanded="false" aria-controls="user-menu-panel" title="Gebruikersmenu">
👤 <span class="user-naam">{{ session.get('user') }}</span> <span class="caret">▼</span>
</button>
<div class="user-menu-panel" id="user-menu-panel">
<div class="user-menu-head">Aangemeld als <strong>{{ session.get('user') }}</strong></div>
<label class="switch-row" for="thema-schakelaar">
<span>🌙 Donker thema</span>
<span class="switch">
<input type="checkbox" id="thema-schakelaar">
<span class="slider"></span>
</span>
</label>
</div>
</div>
{% 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);
// Gebruikersmenu rechtsboven + donker thema aan/uit (onthouden in deze browser)
(function () {
const menu = document.getElementById('user-menu');
if (!menu) return;
const knop = document.getElementById('user-menu-btn');
const schakelaar = document.getElementById('thema-schakelaar');
function zetOpen(open) {
menu.classList.toggle('open', open);
knop.setAttribute('aria-expanded', open);
}
knop.addEventListener('click', function (e) {
e.stopPropagation();
zetOpen(!menu.classList.contains('open'));
});
document.addEventListener('click', function (e) {
if (!menu.contains(e.target)) zetOpen(false);
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') zetOpen(false);
});
schakelaar.checked = document.documentElement.dataset.theme === 'dark';
schakelaar.addEventListener('change', function () {
if (schakelaar.checked) document.documentElement.dataset.theme = 'dark';
else delete document.documentElement.dataset.theme;
try { localStorage.setItem('pivot.thema', schakelaar.checked ? 'donker' : 'licht'); } catch (err) { /* negeren */ }
});
})();
// 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>