Files
Pivot/templates/database_toestellen.html
2026-10-14 13:34:37 +02:00

162 lines
9.7 KiB
HTML
Raw Permalink 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 %}Toestellen importeren{% endblock %}
{% block page_title %}Database › Toestellen{% endblock %}
{% block loader_text %}Bestand controleren...{% endblock %}
{% block extra_css %}
<style>
.stappen { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: start; }
.stappen > .card + .card { margin-top: 0; }
@media (max-width: 860px) { .stappen { grid-template-columns: 1fr; } }
.stap-nr { display: inline-grid; place-items: center; width: 26px; height: 26px; border-radius: 50%;
background: var(--primary-color); color: #fff; font-size: 14px; margin-right: 6px; }
.kolom-lijst { margin: 10px 0 0; padding: 0; list-style: none; font-size: 14px; }
.kolom-lijst li { padding: 6px 0; border-bottom: 1px solid var(--border); }
.kolom-lijst li:last-child { border-bottom: 0; }
.kolom-lijst strong { display: inline-block; min-width: 120px; }
.modus-keuze { display: grid; gap: 8px; margin: 4px 0 16px; }
.modus-keuze label { display: flex; gap: 10px; align-items: flex-start; padding: 10px 12px; border: 1px solid var(--border);
border-radius: var(--radius-sm); font-weight: 500; cursor: pointer; background: var(--surface-muted); }
.modus-keuze label:has(input:checked) { border-color: var(--primary-color); background: var(--primary-light); }
.modus-keuze input { margin-top: 2px; flex-shrink: 0; }
.modus-keuze small { display: block; color: var(--text-muted); font-weight: 400; }
.tellers { display: flex; flex-wrap: wrap; gap: 10px; margin: 6px 0 14px; }
.teller-knop { all: unset; cursor: pointer; display: inline-flex; gap: 8px; align-items: center; padding: 8px 14px;
border-radius: var(--radius); border: 1px solid var(--border); background: #fff; font-weight: 600; font-size: 14px; }
.teller-knop.actief { border-color: var(--primary-color); box-shadow: 0 0 0 2px rgba(30,111,165,.18); }
.teller-knop .n { font-size: 18px; }
tr.rij-fout td { background: #fff7f7; }
td.melding { font-size: 13px; color: var(--text-muted); min-width: 220px; }
td.melding .fout { color: var(--danger-text); }
td.melding .wijz { color: var(--text); }
.bevestig { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; justify-content: space-between; margin-top: 16px; }
</style>
{% endblock %}
{% block content %}
{% if not plan %}
<div class="stappen">
<div class="card">
<h2 class="card-title"><span class="stap-nr">1</span>Sjabloon downloaden</h2>
<p class="muted">Vul het sjabloon in met één toestel per rij. De kolom <em>Lokaal</em> heeft een keuzelijst met
de lokalen van {{ school.naam if school else 'deze school' }}.</p>
<a class="btn" href="{{ url_for('database_beheer.toestellen_sjabloon') }}">⬇️ Sjabloon downloaden (.xlsx)</a>
<ul class="kolom-lijst">
{% for k in kolommen %}
<li><strong>{{ k[1] }}</strong> <span class="muted">{{ k[3] }}</span></li>
{% endfor %}
</ul>
</div>
<div class="card">
<h2 class="card-title"><span class="stap-nr">2</span>Excel uploaden</h2>
<p class="muted">Je krijgt eerst een voorbeeld te zien. Er wordt pas iets bewaard als je bevestigt.
{% if aantal is not none %}Er staan nu <strong>{{ aantal }}</strong> toestellen in de database.{% endif %}</p>
<form method="POST" action="{{ url_for('database_beheer.toestellen_controleren') }}" enctype="multipart/form-data"
onsubmit="showLoader('Bestand controleren...')">
<div class="field">
<label for="bestand">Excel-bestand (.xlsx)</label>
<input type="file" id="bestand" name="bestand" accept=".xlsx,.xlsm,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" required>
<div class="field-hint">Max. {{ max_rijen }} rijen. Het tabblad <em>Toestellen</em> wordt gelezen (of anders het eerste tabblad).</div>
</div>
<label>Toestellen die al bestaan</label>
<div class="modus-keuze">
<label><input type="radio" name="modus" value="aanvullen" checked>
<span>Enkel lege velden aanvullen <small>Wat al ingevuld is in PIVOT blijft staan. (Aanbevolen)</small></span></label>
<label><input type="radio" name="modus" value="overschrijven">
<span>Ingevulde cellen overschrijven <small>Wat in Excel staat, vervangt de waarde in PIVOT (ook de status). Lege cellen wissen niets.</small></span></label>
</div>
<button type="submit">🔎 Controleren</button>
</form>
</div>
</div>
{% else %}
{% set t = plan.tellers %}
<div class="card">
<div class="page-head">
<h2>🔎 Voorbeeld: {{ bestandsnaam }}</h2>
<a class="btn btn-light" href="{{ url_for('database_beheer.toestellen') }}">↩️ Ander bestand</a>
</div>
<p class="muted" style="margin-top:-8px">Nog niets bewaard. Bestaande toestellen:
<strong>{{ 'ingevulde cellen overschrijven' if plan.modus == 'overschrijven' else 'enkel lege velden aanvullen' }}</strong>.</p>
{% if onbekend %}
<div class="alert alert-warning">Deze kolommen worden genegeerd: {{ onbekend|join(', ') }}.</div>
{% endif %}
<div class="tellers" role="group" aria-label="Filter">
<button type="button" class="teller-knop actief" data-filter="">Alle <span class="n">{{ plan.rijen|length }}</span></button>
<button type="button" class="teller-knop" data-filter="nieuw"><span class="badge badge-success">➕ Nieuw</span> <span class="n">{{ t.nieuw }}</span></button>
<button type="button" class="teller-knop" data-filter="bijwerken"><span class="badge badge-info">✏️ Bijwerken</span> <span class="n">{{ t.bijwerken }}</span></button>
<button type="button" class="teller-knop" data-filter="ongewijzigd"><span class="badge">= Ongewijzigd</span> <span class="n">{{ t.ongewijzigd }}</span></button>
<button type="button" class="teller-knop" data-filter="fout"><span class="badge badge-danger">⚠️ Fout</span> <span class="n">{{ t.fout }}</span></button>
</div>
<div class="table-wrap">
<table id="voorbeeld">
<thead>
<tr>
<th>Rij</th><th>Actie</th>
{% for k in kolommen %}<th>{{ k[1] }}</th>{% endfor %}
<th>Wat gebeurt er</th>
</tr>
</thead>
<tbody>
{% for r in plan.rijen %}
<tr data-actie="{{ r.actie }}" class="{{ 'rij-fout' if r.actie == 'fout' }}">
<td class="muted">{{ r.rij }}</td>
<td>
{% if r.actie == 'nieuw' %}<span class="badge badge-success">➕ Nieuw</span>
{% elif r.actie == 'bijwerken' %}<span class="badge badge-info">✏️ Bijwerken</span>
{% elif r.actie == 'ongewijzigd' %}<span class="badge">= Ongewijzigd</span>
{% else %}<span class="badge badge-danger">⚠️ Fout</span>{% endif %}
</td>
{% for k in kolommen %}<td class="{{ 'mono' if k[0] in ('schooleigen_id', 'serienummer') }}">{{ r.invoer[k[0]] }}</td>{% endfor %}
<td class="melding">
{% if r.actie == 'fout' %}{% for m in r.meldingen %}<div class="fout">{{ m }}</div>{% endfor %}
{% else %}
{% if r.actie == 'nieuw' %}<div class="wijz">Wordt toegevoegd{% if not r.invoer.status %} (status Beschikbaar){% endif %}.</div>{% endif %}
{% if r.label %}<div>Toestel {{ r.label }}</div>{% endif %}
{% for w in r.wijzigingen %}<div class="wijz">{{ w }}</div>{% endfor %}
{% for m in r.meldingen %}<div>{{ m }}</div>{% endfor %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% set te_doen = t.nieuw + t.bijwerken %}
<form method="POST" action="{{ url_for('database_beheer.toestellen_importeren') }}" class="bevestig"
onsubmit="return confirm({{ ('Nu ' ~ t.nieuw ~ ' toestel(len) toevoegen en ' ~ t.bijwerken ~ ' bijwerken?' ~ (' De ' ~ t.fout ~ ' rij(en) met een fout worden overgeslagen.' if t.fout else ''))|tojson|forceescape }}) && (showLoader('Importeren...'), true)">
<input type="hidden" name="modus" value="{{ plan.modus }}">
<input type="hidden" name="rijen" value="{{ rijen_json }}">
<span class="muted">
{% if te_doen %}{{ t.nieuw }} nieuw en {{ t.bijwerken }} bij te werken toestel(len).
{% if t.fout %}Rijen met een fout worden overgeslagen; verbeter ze in Excel en upload opnieuw.{% endif %}
{% else %}Er is niets te importeren.{% endif %}
</span>
<span style="display:flex;gap:8px">
<a class="btn btn-light" href="{{ url_for('database_beheer.toestellen') }}">Annuleren</a>
<button type="submit" class="btn-success" {{ 'disabled' if not te_doen }}>✅ Importeren</button>
</span>
</form>
</div>
{% endif %}
{% endblock %}
{% block scripts %}
<script>
// Voorbeeld filteren op actie (nieuw / bijwerken / ongewijzigd / fout)
document.querySelectorAll('.teller-knop').forEach(knop => knop.addEventListener('click', () => {
document.querySelectorAll('.teller-knop').forEach(k => k.classList.toggle('actief', k === knop));
const f = knop.dataset.filter;
document.querySelectorAll('#voorbeeld tbody tr').forEach(tr => {
tr.style.display = !f || tr.dataset.actie === f ? '' : 'none';
});
}));
</script>
{% endblock %}