Initial commit
This commit is contained in:
commit
536ff43ce4
158 files changed
+30355
No files matched your search
@@ -0,0 +1,190 @@
|
||||
{% 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 %}
|
||||
Reference in new issue
Block a user