417 lines
26 KiB
HTML
417 lines
26 KiB
HTML
{% 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'">×</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'">×</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'">×</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'">×</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 => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[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 %}
|