Browse Source

Ajout saisie manuelle coureur avec autocomplétion

master
scayac 3 months ago
parent
commit
955a936d2a
  1. 53
      main/templates/scan.html
  2. 1
      main/urls.py
  3. 22
      main/views.py

53
main/templates/scan.html

@ -10,10 +10,18 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<div id="reader" style="width:100%; max-width:400px; margin:auto;"></div> <div id="reader" style="width:100%; max-width:400px; margin:auto;"></div>
<div class="mt-4 position-relative" style="max-width:400px; margin:auto;">
<label id="manualCoureurLabel">Saisie manuelle :</label>
<div class="input-group align-items-center">
<input type="text" id="manualCoureurInput" class="form-control" placeholder="Nom ou dossard..." autocomplete="off" aria-label="Saisir un coureur" aria-describedby="manualCoureurLabel">
<!-- plus de bouton OK, déclenchement direct sur sélection -->
</div>
<div id="manualCoureurSuggestions" class="list-group position-absolute w-100" style="z-index:1000;"></div>
</div>
<div id="scanResult" class="mt-3"></div> <div id="scanResult" class="mt-3"></div>
{% if error %} {% if error %}
<div class="alert alert-danger mt-3">{{ error }}</div> <div class="alert alert-danger mt-3">{{ error }}</div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
<div class="card shadow mb-4"> <div class="card shadow mb-4">
@ -56,6 +64,47 @@
{% block extra_js %} {% block extra_js %}
<script src="{% static 'html5-qrcode/html5-qrcode.min.js' %}"></script> <script src="{% static 'html5-qrcode/html5-qrcode.min.js' %}"></script>
<script> <script>
document.addEventListener('DOMContentLoaded', function() {
let selectedCoureurId = null;
const input = document.getElementById('manualCoureurInput');
const suggestions = document.getElementById('manualCoureurSuggestions');
if (input) {
input.addEventListener('input', function() {
const val = this.value.trim();
selectedCoureurId = null;
suggestions.innerHTML = '';
if (val.length < 2) return;
fetch('/ajax/coureur_autocomplete/?q=' + encodeURIComponent(val))
.then(r => r.json())
.then(data => {
suggestions.innerHTML = '';
data.forEach(c => {
const item = document.createElement('a');
item.className = 'list-group-item list-group-item-action';
item.textContent = c.label;
item.style.cursor = 'pointer';
item.onclick = function() {
input.value = c.label;
selectedCoureurId = c.id;
suggestions.innerHTML = '';
onScanSuccess(selectedCoureurId, "");
setTimeout(() => { input.value = ''; }, 100);
};
suggestions.appendChild(item);
});
});
});
// Cacher suggestions si clic ailleurs
document.addEventListener('click', function(e) {
if (!input.contains(e.target) && !suggestions.contains(e.target)) {
suggestions.innerHTML = '';
}
});
}
// plus de bouton OK, plus de gestion de clic
});
function beep() { function beep() {
const ctx = new(window.AudioContext || window.webkitAudioContext)(); const ctx = new(window.AudioContext || window.webkitAudioContext)();
const oscillator = ctx.createOscillator(); const oscillator = ctx.createOscillator();

1
main/urls.py

@ -8,4 +8,5 @@ urlpatterns = [
path('course/<int:course_id>/export_pdf/', views.export_pdf, name='export_pdf'), path('course/<int:course_id>/export_pdf/', views.export_pdf, name='export_pdf'),
path('scan/', views.scan_view, name='scan'), path('scan/', views.scan_view, name='scan'),
path('dossards/', views.dossards_view, name='dossards'), path('dossards/', views.dossards_view, name='dossards'),
path('ajax/coureur_autocomplete/', views.coureur_autocomplete, name='coureur_autocomplete'),
] ]

22
main/views.py

@ -1,3 +1,5 @@
from django.http import JsonResponse
from django.views.decorators.http import require_GET
import csv import csv
import io import io
from datetime import timedelta from datetime import timedelta
@ -15,12 +17,12 @@ from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync from asgiref.sync import async_to_sync
from .models import Course, Arrivee, Coureur from .models import Course, Arrivee, Coureur
from django.db import models
from .forms import CourseForm, ScanForm, DossardForm from .forms import CourseForm, ScanForm, DossardForm
import qrcode import qrcode
from PIL import Image from PIL import Image
def seconds_to_hms(delta: timedelta) -> str: def seconds_to_hms(delta: timedelta) -> str:
if delta is None: if delta is None:
return '' return ''
@ -30,6 +32,24 @@ def seconds_to_hms(delta: timedelta) -> str:
seconds = total % 60 seconds = total % 60
return f"{hours}h{minutes:02d}m{seconds:02d}s" return f"{hours}h{minutes:02d}m{seconds:02d}s"
@login_required
@require_GET
def coureur_autocomplete(request):
q = request.GET.get('q', '').strip()
results = []
if len(q) >= 2:
# Recherche sur nom, prénom, classe, dossard (id)
qs = Coureur.objects.filter(
models.Q(nom__icontains=q) |
models.Q(prenom__icontains=q) |
models.Q(classe__icontains=q) |
models.Q(id__icontains=q)
).order_by('nom', 'prenom')[:10]
for c in qs:
label = f"{c.nom} {c.prenom} ({c.classe}) [dossard: {c.id}]"
results.append({'id': c.id, 'label': label})
return JsonResponse(results, safe=False)
@login_required @login_required
def main_view(request): def main_view(request):
courses = Course.objects.filter(owner=request.user) courses = Course.objects.filter(owner=request.user)

Loading…
Cancel
Save