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.
 
 
 
 
 

176 lines
8.0 KiB

{% extends 'base.html' %}
{% block content %}
{% load static %}
{% load temps_format %}
<div class="container-fluid mt-4">
<div class="row">
<div class="col-12">
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Gestion de la course</h6>
</div>
<div class="card-body">
<form method="post">
{% csrf_token %}
{% if not is_started %}
<button type="submit" name="start" class="btn btn-success">Départ
<i class="fa-solid fa-play"></i>
</button>
{% elif not is_finished %}
<button type="button" id="btnFinish" class="btn btn-danger">Fin course
<i class="fa-solid fa-stop"></i>
</button>
<!-- Modal confirmation fin de course -->
<div class="modal fade" id="finishModal" tabindex="-1" role="dialog" aria-labelledby="finishModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="finishModalLabel">Confirmer la fin de la course</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
Êtes-vous sûr de vouloir terminer la course ? Cette action est irréversible.
</div>
<div class="modal-footer">
<form method="post" style="margin:0;">
{% csrf_token %}
<button type="submit" name="finish" class="btn btn-danger">Valider la fin</button>
</form>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annuler</button>
</div>
</div>
</div>
</div>
{% else %}
<span class="badge badge-secondary">Course terminée</span>
{% endif %}
</form>
</div>
</div>
<div class="card shadow mb-4">
<div class="card-header py-3 d-flex justify-content-between align-items-center">
<h6 class="m-0 font-weight-bold text-primary">Arrivées</h6>
<div>
<form id="exportCsvForm" method="post" action="{% url 'export_csv' course.id %}" style="display:inline;">
{% csrf_token %}
<input type="hidden" name="rows" id="csvRowsInput">
<button type="submit" class="btn btn-success mb-2" id="btnExportCsv">
<i class="fas fa-file-csv" title="Exporter en CSV"></i>
</button>
</form>
<form id="exportPdfForm" method="post" action="{% url 'export_pdf' course.id %}" style="display:inline;">
{% csrf_token %}
<input type="hidden" name="rows" id="pdfRowsInput">
<button type="submit" class="btn btn-danger mb-2" id="btnExportPdf">
<i class="fas fa-file-pdf" title="Exporter en PDF"></i>
</button>
</form>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped" id="arriveesTable">
<thead>
<tr>
<th style="width: 1%; white-space: nowrap;">Rang</th>
<th>Nom</th>
<th>Prénom</th>
<th>Classe</th>
<th>Temps</th>
</tr>
</thead>
<tbody>
{% for a in arrivees %}
<tr>
<td>{{ a.rang }}</td>
<td>{{ a.coureur.nom }}</td>
<td>{{ a.coureur.prenom }}</td>
<td>{{ a.coureur.classe }}</td>
<td>{% if a.temps %}{{ a.temps|seconds_to_hms }}{% endif %}</td>
</tr>
{% empty %}
<tr>
<td>Aucun coureur arrivé.</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<!-- DataTables JS & CSS -->
<link rel="stylesheet" href="{% static 'bootstrap/dataTables.bootstrap4.min.css' %}">
<script src="{% static 'jquery/jquery.dataTables.min.js' %}"></script>
<script src="{% static 'bootstrap/dataTables.bootstrap4.min.js' %}"></script>
<script src="{% static 'jquery/datatables.fr.js' %}"></script>
<script>
const courseId = "{{ course.id }}";
const wsScheme = window.location.protocol === "https:" ? "wss" : "ws";
const wsUrl = `${wsScheme}://${window.location.host}/ws/course/${courseId}/`;
const socket = new WebSocket(wsUrl);
socket.onmessage = function(e) {
// Ajoute dynamiquement la nouvelle ligne reçue via WebSocket
let data;
try {
data = JSON.parse(e.data);
} catch {
return;
}
// Vérifie le format des données reçues
let rowData;
if (typeof data === 'object' && data !== null) {
// Transforme l'objet en tableau dans l'ordre attendu
rowData = [
data.rang,
data.nom || (data.coureur && data.coureur.nom),
data.prenom || (data.coureur && data.coureur.prenom),
data.classe || (data.coureur && data.coureur.classe),
data.temps
];
} else {
// Format inconnu, ignore
return;
}
var dt = $('#arriveesTable').DataTable();
dt.row.add(rowData).draw(false);
};
// Modal confirmation fin de course
document.getElementById('btnFinish').onclick = function() {
$('#finishModal').modal('show');
};
// Initialisation DataTables au chargement
$(document).ready(function() {
$('#arriveesTable').DataTable();
});
// Export CSV/PDF des données filtrées
function getVisibleRows() {
var dt = $('#arriveesTable').DataTable();
var rows = dt.rows({search: 'applied'}).data().toArray();
return JSON.stringify(rows);
}
$('#exportCsvForm').on('submit', function(e) {
$('#csvRowsInput').val(getVisibleRows());
});
$('#exportPdfForm').on('submit', function(e) {
$('#pdfRowsInput').val(getVisibleRows());
});
</script>
{% endblock %}