Files
2026-10-14 13:34:37 +02:00

417 lines
26 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 %}Chromebooks{% endblock %}
{% block page_title %}Chromebooks{% endblock %}
{% block main_class %}wide{% endblock %}
{% block extra_css %}
<style>
/* Knoppen naast elkaar; enkel op een smal scherm lopen ze door naar een tweede regel */
.knoppen { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; min-width: 200px; }
.knoppen > button { white-space: nowrap; margin: 0; }
/* Garantie en Opmerking zijn standaard verborgen */
.col-garantie, .col-opmerking { display: none; }
.toggle-garantie-label, .toggle-opmerking-label { display: none !important; }
.toggle-garantie-label.shown, .toggle-opmerking-label.shown { display: inline-flex !important; }
th.sorteer { cursor: pointer; user-select: none; white-space: nowrap; }
th.sorteer:hover { color: var(--primary-color); }
th.sorteer .pijl::after { content: "↕"; opacity: .35; margin-left: 2px; }
th.sorteer.op .pijl::after { content: "▲"; opacity: 1; }
th.sorteer.af .pijl::after { content: "▼"; opacity: 1; }
.tabel-voet { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 10px; margin-top: 12px; }
.paginering { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
[hidden] { display: none !important; }
.blok-badge { margin-left: 4px; }
</style>
{% endblock %}
{% block content %}
<div class="card">
<div class="page-head">
<h2>💻 Alle toestellen</h2>
<form method="POST" action="{{ url_for('devices.google_import') }}" style="margin:0"
onsubmit="return confirm('Alle Chromebooks uit Google Admin ophalen? Nieuwe serienummers worden toegevoegd, ontbrekende modellen aangevuld. Er wordt niets verwijderd of overschreven.') && (showLoader('Chromebooks ophalen bij Google...'), true)">
<button type="submit" class="btn-sm btn-secondary">🔄 Chromebooks ophalen uit Google</button>
</form>
</div>
<form class="search-bar" method="POST" action="{{ url_for('devices.inventaris_toestellen') }}">
<input type="text" name="query" placeholder="Zoek op toestelnummer, serienummer, ..." value="{{ query or '' }}">
<button type="submit">🔍 Zoeken</button>
</form>
{% if message %}
<div class="alert">{{ message }}</div>
{% endif %}
{% if results %}
<div class="column-toggles" id="column-toggles">
<label><input type="checkbox" id="toggle-kamxm-nummer" checked onchange="toggleColumn('col-kamxm-nummer')"> Toestelnummer</label>
<label><input type="checkbox" id="toggle-signpostnummer" checked onchange="toggleColumn('col-signpostnummer')"> Signpostnummer</label>
<label><input type="checkbox" id="toggle-serienummer" checked onchange="toggleColumn('col-serienummer')"> Serienummer</label>
<label><input type="checkbox" id="toggle-type" checked onchange="toggleColumn('col-type')"> Type</label>
<label class="toggle-garantie-label"><input type="checkbox" id="toggle-garantie" onchange="toggleColumn('col-garantie')"> Garantie</label>
<label class="toggle-opmerking-label"><input type="checkbox" id="toggle-opmerking" onchange="toggleColumn('col-opmerking')"> Opmerking</label>
<label><input type="checkbox" id="toggle-status" checked onchange="toggleColumn('col-status')"> Status</label>
<label><input type="checkbox" id="toggle-bewerken" checked onchange="toggleColumn('col-bewerken')"> Bewerken</label>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th class="col-kamxm-nummer sorteer" data-kol="0">Toestelnummer <span class="pijl"></span></th>
<th class="col-signpostnummer sorteer" data-kol="1">Signpostnummer <span class="pijl"></span></th>
<th class="col-serienummer sorteer" data-kol="2">Serienummer <span class="pijl"></span></th>
<th class="col-type sorteer" data-kol="3">Type <span class="pijl"></span></th>
<th class="col-garantie sorteer" data-kol="4">Garantie <span class="pijl"></span></th>
<th class="col-opmerking sorteer" data-kol="5">Opmerking <span class="pijl"></span></th>
<th class="col-status sorteer" data-kol="6">Status <span class="pijl"></span></th>
<th class="actions" aria-label="Acties"></th>
<th class="col-bewerken actions" aria-label="Bewerken"></th>
</tr>
</thead>
<tbody id="toestelBody">
{% for row in results %}
{% set g = (row['Garantie'] or '').split('-') %}
<tr>
<td class="col-kamxm-nummer" data-sort="{{ row['KAMXM_nummer'] or '' }}"><strong>{{ row['KAMXM_nummer'] or '' }}</strong></td>
<td class="col-signpostnummer" data-sort="{{ row['Signpostnummer'] or '' }}">{{ row['Signpostnummer'] or '' }}</td>
<td class="col-serienummer" data-sort="{{ row['Serienummer'] or '' }}">{{ row['Serienummer'] or '' }}</td>
<td class="col-type" data-sort="{{ row['Type'] or '' }}">{{ row['Type'] or '' }}</td>
<td class="col-garantie" data-sort="{{ (g[2] ~ '-' ~ g[1] ~ '-' ~ g[0]) if g|length == 3 else '' }}">{{ row['Garantie'] or '' }}</td>
<td class="col-opmerking" data-sort="{{ row['Opmerking'] or '' }}">{{ row['Opmerking'] or '' }}</td>
<td class="col-status" data-sort="{{ row['Status'] or 'Beschikbaar' }}">
{% if row['Status'] == 'Defect' %}<span class="badge badge-danger">🛠️ Defect</span>
{% elif row['Status'] == 'Uitgeleend' %}<span class="badge badge-info">🧑‍💻 Uitgeleend</span>
{% else %}<span class="badge badge-success">✅ Beschikbaar</span>
{% endif %}
<span class="badge badge-danger blok-badge" {{ '' if row['Geblokkeerd']|int == 1 else 'hidden' }}>🔒 Geblokkeerd</span>
</td>
<td class="actions">
<span class="knoppen">
<button type="button" class="btn-sm" onclick="openStatusModal('{{ row['Toestel_ID'] }}', '{{ query }}', '{{ row['KAMXM_nummer'] or row['Serienummer'] or '' }}')">Wijzig status</button>
{% if mag('toestel_blokkeren') and row['Serienummer'] %}
{% set blok = row['Geblokkeerd']|int == 1 %}
<button type="button" class="btn-sm blok-knop {{ 'btn-success' if blok else 'btn-danger' }}"
data-id="{{ row['Toestel_ID'] }}" data-label="{{ row['KAMXM_nummer'] or row['Serienummer'] }}"
data-geblokkeerd="{{ 1 if blok else 0 }}" onclick="wisselBlokkering(this)">{{ '🔓 Deblokkeren' if blok else '🔒 Blokkeren' }}</button>
{% endif %}
{% if row['Serienummer'] %}
<button type="button" class="btn-sm btn-light" title="Wie gebruikte dit toestel? (Google Admin)"
data-serie="{{ row['Serienummer'] }}" data-label="{{ row['KAMXM_nummer'] or row['Serienummer'] }}"
onclick="openGoogleInfo(this)">👤 Gebruik</button>
{% endif %}
<button type="button" class="btn-sm btn-light" title="Welke leerlingen hadden dit toestel?"
data-id="{{ row['Toestel_ID'] }}" data-label="{{ row['KAMXM_nummer'] or row['Serienummer'] or '' }}"
onclick="openToestelHistorie(this)">🕑 Historie</button>
</span>
</td>
<td class="col-bewerken actions">
<button type="button" class="btn-bewerk btn-sm"
data-kamxm-key="{{ row['Toestel_ID'] }}"
data-serienummer="{{ row['Serienummer'] or '' }}"
data-kamxm="{{ row['KAMXM_nummer'] or '' }}"
data-signpost="{{ row['Signpostnummer'] or '' }}"
data-type="{{ row['Type'] or '' }}"
data-garantie="{{ row['Garantie'] or '' }}"
data-opmerking="{{ row['Opmerking'] or '' }}"
onclick="openEditModalFromButton(this)">✏️ Bewerken</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="tabel-voet">
<div style="display:flex;align-items:center;gap:14px;flex-wrap:wrap">
<div class="muted small" id="toestelTeller"></div>
<span id="toestellenPerPagina"></span>
</div>
<nav class="paginering" id="toestelPaginering" aria-label="Pagina's"></nav>
</div>
{% elif query %}
<p class="muted">Geen resultaten gevonden voor "{{ query }}".</p>
{% endif %}
</div>
{% endblock %}
{% block modals %}
<div id="statusModal" class="modal">
<div class="modal-content">
<span class="close" onclick="document.getElementById('statusModal').style.display='none'">&times;</span>
<h3 id="modalTitle">Status wijzigen: <span id="modalkamxm_nummer"></span></h3>
<form id="statusForm" method="POST" action="{{ url_for('devices.wijzig_status') }}">
<input type="hidden" name="toestel_id" id="modalHiddenkamxm_nummer">
<input type="hidden" name="query" id="modalHiddenQuery" value="{{ query or '' }}">
<input type="hidden" name="new_status" id="modalHiddenStatus">
<button type="button" class="status-button status-success" id="btn-beschikbaar" onclick="submitStatus('Beschikbaar')">✅ Beschikbaar</button>
<button type="button" class="status-button status-info" id="btn-uitgeleend" onclick="submitStatus('Uitgeleend')">🧑‍💻 Uitgeleend</button>
<button type="button" class="status-button status-danger" id="btn-defect" onclick="submitStatus('Defect')">🛠️ Defect</button>
</form>
</div>
</div>
<div id="googleInfoModal" class="modal">
<div class="modal-content wide">
<span class="close" onclick="document.getElementById('googleInfoModal').style.display='none'">&times;</span>
<h3>👤 Gebruik van toestel <span id="giTitel"></span></h3>
<div id="giInhoud"><p class="muted">Gegevens ophalen bij Google…</p></div>
</div>
</div>
<div id="historieModal" class="modal">
<div class="modal-content wide">
<span class="close" onclick="document.getElementById('historieModal').style.display='none'">&times;</span>
<h3>🕑 Eigenaars van toestel <span id="thTitel"></span></h3>
<div id="thInhoud"><p class="muted">Laden...</p></div>
</div>
</div>
<div id="editModal" class="modal">
<div class="modal-content">
<span class="close" onclick="document.getElementById('editModal').style.display='none'">&times;</span>
<h3>Bewerk toestel: <span id="editModalkamxm_nummerTitle"></span></h3>
<form id="editForm" method="POST" action="{{ url_for('devices.bewerk_toestel') }}">
<input type="hidden" name="toestel_id" id="editHiddenkamxm_nummerKey">
<input type="hidden" name="query" id="editHiddenQuery" value="{{ query or '' }}">
<div class="field"><label>Toestelnummer</label><input type="text" name="schooleigen_id" id="edit_kamxm_nummer_display"></div>
<div class="field"><label>Serienummer</label><input type="text" name="serienummer" id="edit_serienummer"></div>
<div class="field"><label>Signpostnummer</label><input type="text" name="signpostnummer" id="edit_signpostnummer"></div>
<div class="field"><label>Type</label><input type="text" name="type" id="edit_type"></div>
<div class="field"><label>Garantie tot</label><input type="text" name="garantie" id="edit_garantie" placeholder="dd-mm-jjjj"></div>
<div class="field"><label>Opmerking</label><textarea name="opmerking" id="edit_opmerking"></textarea></div>
<button type="submit" class="btn-block">💾 Gegevens opslaan</button>
</form>
</div>
</div>
{% endblock %}
{% block scripts %}
<style>
#giInhoud h4 { margin: 16px 0 6px; font-size: .95rem; }
#giInhoud table { width: 100%; border-collapse: collapse; font-size: .92rem; }
#giInhoud td, #giInhoud th { padding: 6px 8px; border-bottom: 1px solid var(--border); text-align: left; }
.gi-meta { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 8px 16px; font-size: .9rem; }
.gi-meta div span { display: block; color: var(--text-muted); font-size: .78rem; text-transform: uppercase; letter-spacing: .03em; }
.gi-balk { display: inline-block; height: 8px; border-radius: 4px; background: var(--primary-color); vertical-align: middle; margin-right: 6px; }
</style>
<script>
function giEsc(t) { return String(t ?? '').replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c])); }
function giDatum(iso) {
if (!iso) return 'onbekend';
const d = new Date(iso);
return isNaN(d) ? iso : d.toLocaleString('nl-BE', { dateStyle: 'short', timeStyle: 'short' });
}
function giDuur(min) { return min >= 60 ? `${Math.floor(min / 60)} u ${String(min % 60).padStart(2, '0')}` : `${min} min`; }
const TOESTEL_HISTORIE_URL = {{ url_for('devices.toestel_historie', toestel_id=0)|tojson }}.replace(/0$/, '');
function thDuur(dagen) {
if (dagen == null) return '';
if (dagen < 31) return dagen + ' dag' + (dagen === 1 ? '' : 'en');
const m = Math.round(dagen / 30.44);
return m < 12 ? m + ' maand' + (m === 1 ? '' : 'en') : (dagen / 365.25).toFixed(1).replace('.', ',') + ' jaar';
}
function openToestelHistorie(knop) {
const venster = document.getElementById('historieModal'), inhoud = document.getElementById('thInhoud');
document.getElementById('thTitel').textContent = knop.dataset.label;
inhoud.innerHTML = '<p class="muted">Laden...</p>';
venster.style.display = 'block';
fetch(TOESTEL_HISTORIE_URL + encodeURIComponent(knop.dataset.id), { credentials: 'same-origin' })
.then(r => r.json()).then(res => {
const e = res.eigenaars || [];
if (!e.length) { inhoud.innerHTML = '<p class="muted">Dit toestel werd nog nooit aan een leerling uitgedeeld.</p>'; return; }
inhoud.innerHTML = `<p class="muted small">${e.length} eigenaar(s) in totaal</p>
<div class="table-wrap"><table><thead><tr><th>Internnummer</th><th>Leerling</th><th>Gebruikersnaam</th>
<th>Gekregen</th><th>Ingeleverd</th><th>Duur</th></tr></thead><tbody>`
+ e.map(x => `<tr${x.huidig ? ' style="background:var(--primary-light)"' : ''}>
<td><strong>${giEsc(x.internnummer)}</strong></td><td>${giEsc(x.naam)}</td><td>${giEsc(x.gebruikersnaam)}</td>
<td style="white-space:nowrap">${giEsc(x.van)}</td>
<td style="white-space:nowrap">${x.huidig ? '<span class="badge badge-success">nu in gebruik</span>' : giEsc(x.tot)}${x.geblokkeerd && x.huidig ? ' <span class="badge badge-danger">🔒</span>' : ''}</td>
<td style="white-space:nowrap">${thDuur(x.dagen)}</td></tr>`).join('')
+ '</tbody></table></div>';
}).catch(() => { inhoud.innerHTML = '<div class="alert alert-danger">Kon de historie niet ophalen.</div>'; });
}
window.addEventListener('click', e => { if (e.target.id === 'historieModal') e.target.style.display = 'none'; });
// Blokkeren / deblokkeren in Google zonder de pagina te herladen (zoekopdracht, sortering en pagina blijven)
const BLOKKEER_URL = {{ url_for('devices.toestel_blokkeren')|tojson }};
function wisselBlokkering(knop) {
const blokkeren = knop.dataset.geblokkeerd !== '1';
const vraag = blokkeren
? `Chromebook ${knop.dataset.label} blokkeren in Google Admin?\nDe leerling kan het toestel dan niet meer gebruiken.`
: `Chromebook ${knop.dataset.label} deblokkeren in Google Admin?`;
if (!confirm(vraag)) return;
const tekst = knop.textContent;
knop.disabled = true; knop.textContent = '⏳';
const data = new FormData();
data.append('toestel_id', knop.dataset.id);
data.append('actie', blokkeren ? 'blokkeren' : 'deblokkeren');
fetch(BLOKKEER_URL, { method: 'POST', body: data, credentials: 'same-origin', headers: { 'X-Requested-With': 'fetch' } })
.then(r => r.json()).then(res => {
knop.disabled = false;
if (!res.ok) { knop.textContent = tekst; alert(res.melding || 'Mislukt.'); return; }
const nu = !!res.geblokkeerd;
knop.dataset.geblokkeerd = nu ? '1' : '0';
knop.textContent = nu ? '🔓 Deblokkeren' : '🔒 Blokkeren';
knop.classList.toggle('btn-success', nu); knop.classList.toggle('btn-danger', !nu);
const badge = knop.closest('tr').querySelector('.blok-badge');
if (badge) badge.hidden = !nu;
}).catch(() => { knop.disabled = false; knop.textContent = tekst; alert('Er ging iets mis; probeer opnieuw.'); });
}
function openGoogleInfo(knop) {
document.getElementById('giTitel').textContent = knop.dataset.label;
const doel = document.getElementById('giInhoud');
doel.innerHTML = '<p class="muted">Gegevens ophalen bij Google…</p>';
document.getElementById('googleInfoModal').style.display = 'block';
fetch("{{ url_for('devices.google_info') }}?serie=" + encodeURIComponent(knop.dataset.serie), { credentials: 'same-origin' })
.then(r => r.json())
.then(res => {
if (!res.ok) { doel.innerHTML = `<div class="alert alert-danger">${giEsc(res.fout)}</div>`; return; }
const i = res.info;
let h = `<div class="gi-meta">
<div><span>Laatste synchronisatie</span>${giEsc(giDatum(i.laatste_sync))}</div>
<div><span>Model</span>${giEsc(i.model || '–')}</div>
<div><span>Organisatie-eenheid</span>${giEsc(i.ou || '–')}</div>
<div><span>Status in Google</span>${giEsc({ACTIVE: 'Actief', DISABLED: 'Geblokkeerd', DEPROVISIONED: 'Uitgeschreven'}[i.status] || i.status || '–')}</div>
</div>`;
h += '<h4>Recente gebruikers <span class="muted small">(meest recent bovenaan)</span></h4>';
if (i.gebruikers.length) {
h += '<table><tbody>' + i.gebruikers.map((g, n) => `<tr>
<td style="width:28px" class="muted">${n + 1}</td>
<td>${g.beheerd ? giEsc(g.email) : '<span class="muted">niet-beheerd account (gast of extern)</span>'}</td>
<td>${giEsc(g.naam || '')}</td></tr>`).join('') + '</tbody></table>';
} else {
h += '<p class="muted">Google geeft geen gebruikers terug. Staat de gebruikersrapportage aan voor deze OU '
+ '(Google Admin › Apparaten › Chrome › Instellingen › Apparaat)?</p>';
}
h += '<h4>Actieve tijd per dag</h4>';
if (i.actief.length) {
const max = Math.max(...i.actief.map(a => a.minuten), 1);
h += '<table><tbody>' + i.actief.map(a => `<tr>
<td style="width:110px">${giEsc(new Date(a.datum).toLocaleDateString('nl-BE', { weekday: 'short', day: '2-digit', month: '2-digit' }))}</td>
<td><span class="gi-balk" style="width:${Math.round(a.minuten / max * 220)}px"></span>${giDuur(a.minuten)}</td></tr>`).join('') + '</tbody></table>';
} else {
h += '<p class="muted">Geen gegevens over actieve tijd.</p>';
}
doel.innerHTML = h;
})
.catch(e => { doel.innerHTML = `<div class="alert alert-danger">Ophalen mislukt: ${giEsc(e)}</div>`; });
}
// Sorteren (klik op kolomkop) en 10 toestellen per pagina — alles in de browser
(function () {
const body = document.getElementById('toestelBody');
if (!body) return;
let PER_PAGINA = perPagina('toestellen');
const alle = Array.from(body.rows);
let pagina = 1, kol = null, op = true;
function toon() {
const grootte = PER_PAGINA || Infinity;
const paginas = Math.max(1, Math.ceil(alle.length / grootte));
pagina = Math.min(Math.max(1, pagina), paginas);
const van = PER_PAGINA ? (pagina - 1) * PER_PAGINA : 0, tot = van + grootte; // 0 = alles
Array.from(body.rows).forEach((tr, i) => tr.style.display = (i >= van && i < tot) ? '' : 'none');
document.getElementById('toestelTeller').textContent =
`${van + 1}–${Math.min(tot, alle.length)} van ${alle.length} toestel(len)`;
const nav = document.getElementById('toestelPaginering');
nav.innerHTML = '';
if (paginas <= 1) return;
const knop = (label, p, actief, titel) => {
const b = document.createElement('button');
b.type = 'button'; b.textContent = label; b.className = 'btn-sm ' + (actief ? 'btn-secondary' : 'btn-light');
if (titel) b.title = titel;
b.onclick = () => { pagina = p; toon(); body.closest('.table-wrap').scrollIntoView({ block: 'start' }); };
nav.appendChild(b);
};
if (pagina > 1) { knop('«', 1, false, 'Eerste pagina'); knop('‹ Vorige', pagina - 1); }
for (let p = Math.max(1, pagina - 2); p <= Math.min(paginas, pagina + 2); p++) knop(String(p), p, p === pagina);
if (pagina < paginas) { knop('Volgende ›', pagina + 1); knop('»', paginas, false, 'Laatste pagina'); }
const info = document.createElement('span');
info.className = 'muted small'; info.textContent = `pagina ${pagina} van ${paginas}`;
nav.appendChild(info);
}
document.querySelectorAll('th.sorteer').forEach(th => th.addEventListener('click', () => {
const k = +th.dataset.kol;
op = kol === k ? !op : true; kol = k;
document.querySelectorAll('th.sorteer').forEach(x => x.classList.remove('op', 'af'));
th.classList.add(op ? 'op' : 'af');
const rijen = Array.from(body.rows);
rijen.sort((a, b) => {
const x = a.cells[k].dataset.sort || '', y = b.cells[k].dataset.sort || '';
if (!x !== !y) return x ? -1 : 1; // lege waarden altijd onderaan
const c = x.localeCompare(y, 'nl', { numeric: true, sensitivity: 'base' });
return op ? c : -c;
});
rijen.forEach(tr => body.appendChild(tr));
pagina = 1; toon();
}));
perPaginaKiezer(document.getElementById('toestellenPerPagina'), 'toestellen', n => { PER_PAGINA = n; pagina = 1; toon(); });
toon();
})();
function openStatusModal(toestelId, currentQuery, label) {
document.getElementById('modalkamxm_nummer').textContent = label || toestelId;
document.getElementById('modalHiddenkamxm_nummer').value = toestelId;
document.getElementById('modalHiddenQuery').value = currentQuery;
document.getElementById('statusModal').style.display = 'block';
}
function submitStatus(status) {
document.getElementById('modalHiddenStatus').value = status;
document.getElementById('statusForm').submit();
}
function openEditModalFromButton(buttonElement) {
openEditModal({
kamxm_nummer_key: buttonElement.getAttribute('data-kamxm-key'),
kamxm: buttonElement.getAttribute('data-kamxm'),
serienummer: buttonElement.getAttribute('data-serienummer'),
signpostnummer: buttonElement.getAttribute('data-signpost'),
type: buttonElement.getAttribute('data-type'),
garantie: buttonElement.getAttribute('data-garantie'),
opmerking: buttonElement.getAttribute('data-opmerking')
});
}
function openEditModal(rowData) {
document.getElementById('editModalkamxm_nummerTitle').textContent = rowData.kamxm || rowData.serienummer || '';
document.getElementById('editHiddenkamxm_nummerKey').value = rowData.kamxm_nummer_key;
document.getElementById('edit_kamxm_nummer_display').value = rowData.kamxm || '';
document.getElementById('edit_serienummer').value = rowData.serienummer || '';
document.getElementById('edit_signpostnummer').value = rowData.signpostnummer || '';
document.getElementById('edit_type').value = rowData.type || '';
document.getElementById('edit_garantie').value = rowData.garantie || '';
document.getElementById('edit_opmerking').value = rowData.opmerking || '';
document.getElementById('editModal').style.display = 'block';
makeHiddenTogglesVisible();
}
function toggleColumn(columnClass) {
const checkbox = document.getElementById('toggle-' + columnClass.replace('col-', ''));
if (!checkbox) return;
const displayStyle = checkbox.checked ? 'table-cell' : 'none';
document.querySelectorAll('.' + columnClass).forEach(cell => { cell.style.display = displayStyle; });
}
function makeHiddenTogglesVisible() {
['.toggle-garantie-label', '.toggle-opmerking-label'].forEach(selector => {
const el = document.querySelector(selector);
if (el) el.classList.add('shown');
});
}
document.addEventListener('DOMContentLoaded', () => {
['col-kamxm-nummer', 'col-signpostnummer', 'col-serienummer', 'col-type', 'col-status', 'col-bewerken']
.forEach(toggleColumn);
});
window.onclick = function (event) {
if (event.target.classList.contains('modal')) event.target.style.display = 'none';
};
</script>
{% endblock %}