PIVOT 1.1.0

This commit is contained in:
marijn committed 2026-10-14 13:34:37 +02:00
commit 8a53234eb6
161 files changed
+31109

No files matched your search

+134
View File
@@ -0,0 +1,134 @@
{% extends "base.html" %}
{% from "_personeel.html" import subnav, status_badge, soort_label, periode with context %}
{% block title %}{{ SOORTEN[item.Soort].label }} – {{ item.Naam }}{% endblock %}
{% block page_title %}{{ SOORTEN[item.Soort].icoon }} {{ SOORTEN[item.Soort].label }}{% endblock %}
{% block main_class %}medium{% endblock %}
{% block extra_css %}
<style>
.detail-head { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 12px; align-items: flex-start; margin-bottom: 14px; }
.detail-head h2 { margin: 0 0 4px; }
dl.facts { display: grid; grid-template-columns: 160px 1fr; gap: 8px 16px; margin: 0; }
dl.facts dt { color: var(--text-muted); font-size: 14px; }
dl.facts dd { margin: 0; font-weight: 500; }
@media (max-width: 560px) { dl.facts { grid-template-columns: 1fr; gap: 2px; } dl.facts dd { margin-bottom: 8px; } }
.chips { display: flex; flex-wrap: wrap; gap: 5px; }
.chips span { padding: 2px 10px; border-radius: 999px; background: var(--primary-light); color: var(--primary-color); font-size: 13px; font-weight: 600; }
.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li { position: relative; padding: 0 0 18px 28px; }
.timeline li::before { content: ""; position: absolute; left: 7px; top: 18px; bottom: 0; width: 2px; background: var(--border); }
.timeline li:last-child::before { display: none; }
.timeline .dot { position: absolute; left: 0; top: 3px; width: 16px; height: 16px; border-radius: 50%; background: var(--border-strong); border: 3px solid #fff; box-shadow: 0 0 0 1px var(--border); }
.timeline .dot.goedgekeurd { background: var(--success); } .timeline .dot.geweigerd { background: var(--danger); }
.timeline .dot.vraag { background: #f59e0b; } .timeline .dot.antwoord, .timeline .dot.commentaar { background: var(--primary-color); }
.timeline .who { font-weight: 600; } .timeline .when { color: var(--text-muted); font-size: 13px; }
.timeline .txt { margin-top: 4px; padding: 8px 12px; background: var(--surface-muted); border-radius: var(--radius-sm); white-space: pre-line; }
.decision-buttons { display: flex; flex-wrap: wrap; gap: 8px; }
.panel { border: 2px solid var(--border); border-radius: var(--radius-lg); padding: 18px; }
.panel.directie { border-color: #fde68a; background: #fffdf3; }
.panel.vraag { border-color: #fde68a; background: #fffbeb; }
</style>
{% endblock %}
{% block content %}
{{ subnav('') }}
<div class="card">
<div class="detail-head">
<div>
<h2>{{ item.Titel }}</h2>
<div class="muted">{{ item.Naam or item.Email }}{% if item.Ingediend_door %} · gemeld door {{ item.Ingediend_door }}{% endif %}</div>
</div>
<div>{{ status_badge(item.Status) }}</div>
</div>
<dl class="facts">
<dt>Soort</dt><dd>{{ soort_label(item.Soort) }}</dd>
<dt>Wanneer</dt><dd>{{ periode(item) }}</dd>
{% if item.Locatie %}<dt>Locatie</dt><dd>{{ item.Locatie }}</dd>{% endif %}
{% if item.Klassen %}<dt>{{ 'Klassen mee' if item.Soort == 'uitstap' else 'Klassen' }}</dt>
<dd><div class="chips">{% for k in item.Klassen.split(',') %}<span>{{ k.strip() }}</span>{% endfor %}</div></dd>{% endif %}
{% if item.Begeleiders %}<dt>Begeleiders</dt><dd>{{ item.Begeleiders }}</dd>{% endif %}
{% if item.Kost %}<dt>Kost</dt><dd>€ {{ '%.2f'|format(item.Kost)|replace('.', ',') }}</dd>{% endif %}
{% if item.Omschrijving %}<dt>{{ 'Opmerking' if item.Soort == 'afwezigheid' else 'Toelichting' }}</dt><dd style="white-space:pre-line">{{ item.Omschrijving }}</dd>{% endif %}
{% if 'secretariaat' in p_rollen and item.Klassen and item.Soort != 'uitstap' %}
<dt>Vervanging</dt>
<dd>{% if item.Vervanging_ok %}<span class="badge badge-success">✓ Geregeld</span>{% else %}<span class="badge badge-warning">Nog te regelen</span>{% endif %}
{% if item.Vervanging_info %}<span class="muted"> · {{ item.Vervanging_info }}</span>{% endif %}</dd>
{% endif %}
</dl>
</div>
{# ---------- Directie: beslissen ---------- #}
{% if 'directie' in p_rollen and SOORTEN[item.Soort].goedkeuring and item.Status in ('ingediend', 'vraag') %}
<div class="card panel directie">
<h3 class="card-title">Beslissing directie</h3>
{% if item.Status == 'vraag' %}<p class="muted small">Je hebt een vraag gesteld; de leerkracht heeft nog niet geantwoord. Je kan toch al beslissen.</p>{% endif %}
<form method="POST" action="{{ url_for('personeel.beslissing', aid=item.ID) }}" onsubmit="showLoader()">
<div class="field">
<label for="tekst">Commentaar <span class="muted">(verplicht bij weigeren of vraag)</span></label>
<textarea id="tekst" name="tekst" placeholder="bv. Akkoord, graag verslag op de personeelsvergadering."></textarea>
</div>
<div class="decision-buttons">
<button type="submit" name="actie" value="goedgekeurd" class="btn-success">✓ Goedkeuren</button>
<button type="submit" name="actie" value="vraag" class="btn-warning">❓ Vraag stellen</button>
<button type="submit" name="actie" value="geweigerd" class="btn-danger">✕ Weigeren</button>
</div>
</form>
</div>
{% endif %}
{# ---------- Leerkracht: antwoorden op vraag ---------- #}
{% if is_eigenaar and item.Status == 'vraag' %}
<div class="card panel vraag">
<h3 class="card-title">❓ De directie heeft een vraag</h3>
<form method="POST" action="{{ url_for('personeel.antwoord', aid=item.ID) }}" onsubmit="showLoader()">
<div class="field"><textarea name="tekst" required placeholder="Je antwoord..."></textarea></div>
<button type="submit">Antwoord versturen</button>
</form>
</div>
{% endif %}
{# ---------- Secretariaat: vervanging ---------- #}
{% if 'secretariaat' in p_rollen and item.Status in ('gemeld', 'goedgekeurd') and item.Soort != 'uitstap' %}
<div class="card">
<h3 class="card-title">🔁 Vervanging</h3>
<form method="POST" action="{{ url_for('personeel.vervanging', aid=item.ID) }}" class="search-bar" style="margin:0">
<input type="text" name="info" value="{{ item.Vervanging_info or '' }}" placeholder="bv. 3A studie, 4B les door Jan">
<button type="submit" name="ok" value="1" class="btn-success">✓ Geregeld</button>
{% if item.Vervanging_ok %}<button type="submit" name="ok" value="0" class="btn-light">Nog niet geregeld</button>{% endif %}
</form>
</div>
{% endif %}
{# ---------- Verloop + commentaar ---------- #}
<div class="card">
<h3 class="card-title">Verloop</h3>
<ul class="timeline">
{% set labels = {'ingediend': 'diende de aanvraag in', 'gemeld': 'meldde de afwezigheid', 'goedgekeurd': 'keurde goed',
'geweigerd': 'weigerde', 'vraag': 'stelde een vraag', 'antwoord': 'antwoordde',
'commentaar': 'schreef', 'geannuleerd': 'annuleerde', 'vervanging': 'vervanging'} %}
{% for r in reacties %}
<li>
<span class="dot {{ r.Actie }}"></span>
<span class="who">{{ r.Naam or r.Email or '?' }}</span> {{ labels.get(r.Actie, r.Actie) }}
<span class="when"> · {{ r.Tijdstip.strftime('%d/%m %H:%M') }}</span>
{% if r.Tekst %}<div class="txt">{{ r.Tekst }}</div>{% endif %}
</li>
{% endfor %}
</ul>
{% if item.Status not in ('geannuleerd',) %}
<form method="POST" action="{{ url_for('personeel.commentaar', aid=item.ID) }}" class="search-bar" style="margin: 6px 0 0">
<input type="text" name="tekst" placeholder="Opmerking toevoegen..." required>
<button type="submit" class="btn-light">Toevoegen</button>
</form>
{% endif %}
</div>
{% if (is_eigenaar or 'secretariaat' in p_rollen) and item.Status not in ('geannuleerd', 'geweigerd') and item.Tot_datum >= vandaag %}
<form method="POST" action="{{ url_for('personeel.annuleren', aid=item.ID) }}" style="text-align:right"
onsubmit="return confirm('Deze {{ SOORTEN[item.Soort].label|lower }} annuleren?')">
<button type="submit" class="btn-link">{{ 'Afwezigheid intrekken' if item.Soort == 'afwezigheid' else 'Aanvraag annuleren' }}</button>
</form>
{% endif %}
{% endblock %}