191 lines
8.6 KiB
HTML
191 lines
8.6 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Inleveren{% endblock %}
|
|
{% block page_title %}Toestel inleveren{% endblock %}
|
|
{% block main_class %}medium{% endblock %}
|
|
{% block loader_text %}Inlevering verwerken...{% endblock %}
|
|
|
|
{% block head %}
|
|
<script src="https://unpkg.com/html5-qrcode" type="text/javascript"></script>
|
|
{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
#reader { width: 100%; margin-top: 14px; border: 3px solid var(--primary-color); border-radius: var(--radius); overflow: hidden; display: none; background: #000; }
|
|
#reader video { width: 100% !important; height: auto !important; object-fit: cover !important; }
|
|
#scan-result-container { display: none; margin-top: 14px; padding: 14px; border: 2px dashed var(--primary-color); border-radius: var(--radius); background: var(--primary-light); }
|
|
#scannedText { font-size: 1.2em; color: var(--primary-color); word-break: break-all; }
|
|
.scan-actions { display: flex; gap: 8px; margin-top: 10px; }
|
|
.scan-actions button { flex: 1; }
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="split">
|
|
<div class="card">
|
|
<h2 class="card-title">Toestel inleveren</h2>
|
|
|
|
{% if message %}
|
|
{% set ok = 'succesvol' in message or 'ingegeven' in message %}
|
|
<div class="alert {{ 'alert-success' if ok else 'alert-danger' }}">{{ message }}</div>
|
|
{% endif %}
|
|
|
|
<form method="POST" action="{{ url_for('devices.inleveren_submit') }}" onsubmit="showLoader()">
|
|
<input type="hidden" id="action_type_hidden" name="action" value="Geen_schade">
|
|
|
|
<div class="field zoek-wrap">
|
|
<label for="internnummer">Internnummer</label>
|
|
<input type="text" id="internnummer" name="internnummer" required autocomplete="off"
|
|
placeholder="Zoek op naam of scan/typ internnummer..." role="combobox"
|
|
value="{{ request.args.get('internnummer', '') }}"
|
|
aria-autocomplete="list" aria-controls="leerlingSuggesties" aria-expanded="false">
|
|
<ul class="suggesties" id="leerlingSuggesties" role="listbox"></ul>
|
|
{% if request.args.get('naam') %}<p class="field-hint">👤 {{ request.args.get('naam') }}</p>{% endif %}
|
|
</div>
|
|
<div class="field zoek-wrap">
|
|
<label for="serienummer">Toestelnummer</label>
|
|
<input type="text" id="serienummer" name="serienummer" required autocomplete="off"
|
|
placeholder="Zoek of scan toestelnummer (of serienummer)..." role="combobox"
|
|
value="{{ request.args.get('toestel', '') }}"
|
|
aria-autocomplete="list" aria-controls="toestelSuggesties" aria-expanded="false">
|
|
<ul class="suggesties" id="toestelSuggesties" role="listbox"></ul>
|
|
</div>
|
|
|
|
<label class="check-box">
|
|
<input type="checkbox" id="schade_aanwezig" onchange="updateActionType(this)">
|
|
<span>Chromebook heeft schade of opmerkingen</span>
|
|
</label>
|
|
|
|
<div class="field">
|
|
<label for="schade_type">Schadedetail</label>
|
|
<input type="text" id="schade_type" name="schade_type" placeholder="Beschrijf de schade..." disabled>
|
|
</div>
|
|
|
|
<button class="btn-block btn-lg" type="submit">📥 Toestel inleveren</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="card order-first-mobile text-center">
|
|
<h2 class="card-title">📷 Scanner</h2>
|
|
<button type="button" class="btn-block btn-secondary" id="btn-toggle-scan">📷 Open camera-scanner</button>
|
|
{# Zonder https geeft de browser geen live camera: dan een foto nemen met de camera-app en de code daaruit lezen #}
|
|
<label class="btn btn-block btn-secondary" id="btn-foto" style="display:none">
|
|
📷 Foto van de code nemen
|
|
<input type="file" id="foto-input" accept="image/*" capture="environment" hidden>
|
|
</label>
|
|
<p class="muted small" id="scan-hint" style="display:none;margin:8px 0 0"></p>
|
|
|
|
<div id="reader"></div>
|
|
|
|
<div id="scan-result-container">
|
|
<div class="muted small">Gescande waarde</div>
|
|
<strong id="scannedText">-</strong>
|
|
<div class="scan-actions">
|
|
<button type="button" class="btn-sm" onclick="copyToField('internnummer')">→ Internnr.</button>
|
|
<button type="button" class="btn-sm" onclick="copyToField('serienummer')">→ Toestelnr.</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
{% set toestel_voorkeur = 'uitgeleend' %}{% set toestel_vul_intern = true %}{% include '_toestel_zoekveld.html' %}
|
|
<script>
|
|
let html5QrCode;
|
|
const scanBtn = document.getElementById('btn-toggle-scan');
|
|
const readerDiv = document.getElementById('reader');
|
|
const resultContainer = document.getElementById('scan-result-container');
|
|
const scannedTextElement = document.getElementById('scannedText');
|
|
|
|
scanBtn.addEventListener('click', () => {
|
|
if (!html5QrCode || !html5QrCode.isScanning) startScanner(); else stopScanner();
|
|
});
|
|
|
|
// Live camera kan enkel via https (of localhost). Anders: foto nemen en de code uit de foto lezen.
|
|
const fotoBtn = document.getElementById('btn-foto');
|
|
const fotoInput = document.getElementById('foto-input');
|
|
const scanHint = document.getElementById('scan-hint');
|
|
function fotoModus(reden) {
|
|
scanBtn.style.display = 'none';
|
|
fotoBtn.style.display = '';
|
|
scanHint.textContent = reden;
|
|
scanHint.style.display = '';
|
|
}
|
|
if (!window.isSecureContext || !(navigator.mediaDevices && navigator.mediaDevices.getUserMedia)) {
|
|
fotoModus('Neem een scherpe foto van de streepjes- of QR-code, van dichtbij en recht ervoor.');
|
|
}
|
|
fotoInput.addEventListener('change', () => {
|
|
const bestand = fotoInput.files && fotoInput.files[0];
|
|
if (!bestand) return;
|
|
scanHint.textContent = 'Code zoeken in de foto...';
|
|
scanHint.style.display = '';
|
|
const lezer = new Html5Qrcode("reader");
|
|
lezer.scanFile(bestand, false)
|
|
.then(tekst => {
|
|
scannedTextElement.textContent = tekst;
|
|
resultContainer.style.display = 'block';
|
|
scanHint.textContent = 'Code gevonden. Kies hieronder in welk veld ze moet komen.';
|
|
})
|
|
.catch(() => { scanHint.textContent = 'Geen code gevonden in de foto. Probeer opnieuw, van dichterbij en zonder schittering.'; })
|
|
.finally(() => { fotoInput.value = ''; lezer.clear(); });
|
|
});
|
|
|
|
function startScanner() {
|
|
readerDiv.style.display = 'block';
|
|
scanBtn.textContent = '❌ Stop scanner';
|
|
scanBtn.classList.replace('btn-secondary', 'btn-danger');
|
|
|
|
html5QrCode = new Html5Qrcode("reader");
|
|
const config = { fps: 20, qrbox: { width: 300, height: 150 }, aspectRatio: 1.777778 };
|
|
|
|
html5QrCode.start(
|
|
{ facingMode: "environment" },
|
|
config,
|
|
(decodedText) => {
|
|
scannedTextElement.textContent = decodedText;
|
|
resultContainer.style.display = 'block';
|
|
},
|
|
() => {}
|
|
).catch((err) => {
|
|
console.error(err);
|
|
readerDiv.style.display = 'none';
|
|
scanBtn.textContent = '📷 Open camera-scanner';
|
|
scanBtn.classList.replace('btn-danger', 'btn-secondary');
|
|
fotoModus('De live camera kon niet starten. Neem in de plaats een foto van de code.');
|
|
});
|
|
}
|
|
|
|
function stopScanner() {
|
|
if (html5QrCode) {
|
|
html5QrCode.stop().then(() => {
|
|
readerDiv.style.display = 'none';
|
|
scanBtn.textContent = '📷 Open camera-scanner';
|
|
scanBtn.classList.replace('btn-danger', 'btn-secondary');
|
|
}).catch(err => console.error(err));
|
|
}
|
|
}
|
|
|
|
function copyToField(fieldId) {
|
|
const input = document.getElementById(fieldId);
|
|
input.value = scannedTextElement.textContent;
|
|
input.style.backgroundColor = '#d1fae5';
|
|
setTimeout(() => input.style.backgroundColor = '', 500);
|
|
}
|
|
|
|
function updateActionType(checkbox) {
|
|
const schadeInput = document.getElementById('schade_type');
|
|
const hiddenActionField = document.getElementById('action_type_hidden');
|
|
if (checkbox.checked) {
|
|
schadeInput.disabled = false;
|
|
schadeInput.focus();
|
|
hiddenActionField.value = "Schade";
|
|
} else {
|
|
schadeInput.disabled = true;
|
|
schadeInput.value = '';
|
|
hiddenActionField.value = "Geen_schade";
|
|
}
|
|
}
|
|
</script>
|
|
{% endblock %}
|