Application de gestion de courses avec génération de dossards avec QRcode et scan des coureurs à l'arrivée.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

140 lines
4.9 KiB

{% extends 'base.html' %}
{% block content %}
<div class="container-fluid mt-4">
<h1 class="h3 mb-4 text-gray-800">Mode Scan : {% if course %}{{ course.nom }} ({{ course.date }}){% endif %}</h1>
<div class="row">
<div class="col-lg-8 mx-auto">
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Scanner un coureur</h6>
</div>
<div class="card-body">
<div id="reader" style="width:100%; max-width:400px; margin:auto;"></div>
<div id="scanResult" class="mt-3"></div>
{% if error %}
<div class="alert alert-danger mt-3">{{ error }}</div>
{% endif %}
</div>
</div>
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Actions</h6>
</div>
<div class="card-body d-flex justify-content-between">
<a href="/" class="btn btn-secondary">Menu principal</a>
<button id="toggleBeep" type="button" class="btn btn-info">Désactiver bip scan</button>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script src="{% static 'html5-qrcode/html5-qrcode.min.js' %}"></script>
<script>
function beep() {
const ctx = new(window.AudioContext || window.webkitAudioContext)();
const oscillator = ctx.createOscillator();
oscillator.type = 'sine';
oscillator.frequency.setValueAtTime(1000, ctx.currentTime);
oscillator.connect(ctx.destination);
oscillator.start();
setTimeout(() => { oscillator.stop(); ctx.close(); }, 150);
}
let lastScanned = '';
let html5QrCode;
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
document.addEventListener('DOMContentLoaded', function() {
html5QrCode = new Html5Qrcode("reader");
Html5Qrcode.getCameras().then(cameras => {
if (cameras && cameras.length) {
html5QrCode.start(
cameras[0].id,
{ fps: 10, qrbox: 250 },
onScanSuccess
);
}
});
// Plus aucune référence à l’ancien form ou input qrcode
});
function getCourseIdFromUrl() {
const params = new URLSearchParams(window.location.search);
return params.get('course_id');
}
function onScanSuccess(decodedText, decodedResult) {
console.log('Scan détecté:', decodedText, 'Course:', getCourseIdFromUrl());
if (decodedText === lastScanned || window.scanDebounce) return;
window.scanDebounce = true;
lastScanned = decodedText;
beep();
const courseId = getCourseIdFromUrl();
if (!courseId) {
window.scanDebounce = false;
return;
}
fetch("{% url 'scan' %}" + window.location.search, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-CSRFToken': getCookie('csrftoken'),
'X-Requested-With': 'XMLHttpRequest'
},
body: `course_id=${courseId}&qrcode=${encodeURIComponent(decodedText)}`
})
.then(response => response.text())
.then(html => {
document.getElementById('scanResult').innerHTML = html;
window.scanDebounce = false;
})
.catch(() => {
window.scanDebounce = false;
});
}
document.addEventListener('DOMContentLoaded', function() {
const beepBtn = document.getElementById('toggleBeep');
let beepEnabled = true;
beepBtn.onclick = function() {
beepEnabled = !beepEnabled;
beepBtn.textContent = beepEnabled ? 'Désactiver bip scan' : 'Activer bip scan';
};
window.beep = function() {
if (!beepEnabled) return;
const ctx = new(window.AudioContext || window.webkitAudioContext)();
const oscillator = ctx.createOscillator();
oscillator.type = 'sine';
oscillator.frequency.setValueAtTime(1000, ctx.currentTime);
oscillator.connect(ctx.destination);
oscillator.start();
setTimeout(() => { oscillator.stop(); ctx.close(); }, 150);
};
});
Html5Qrcode.getCameras().then(cameras => {
if (cameras && cameras.length) {
html5QrCode.start(
cameras[0].id,
{ fps: 10, qrbox: 250 },
onScanSuccess
);
}
});
</script>
{% endblock %}