feat: add global action in evaluation
All checks were successful
Build and Publish Docker Images / build-and-push (push) Successful in 1m42s
All checks were successful
Build and Publish Docker Images / build-and-push (push) Successful in 1m42s
This commit is contained in:
@@ -220,9 +220,17 @@
|
||||
{% for student in students %}
|
||||
<tr class="student-row hover:bg-gray-25 text-sm transition-colors duration-150" data-student-name="{{ (student.first_name + ' ' + student.last_name)|lower }}">
|
||||
<td class="px-3 py-1.5 whitespace-nowrap text-sm font-medium text-gray-900 sticky left-0 bg-white border-r border-gray-200">
|
||||
<div class="flex items-center">
|
||||
<div class="text-xs text-gray-500 w-6 student-index">{{ loop.index }}</div>
|
||||
<div>{{ student.first_name }} {{ student.last_name }}</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<div class="text-xs text-gray-500 w-6 student-index">{{ loop.index }}</div>
|
||||
<div>{{ student.first_name }} {{ student.last_name }}</div>
|
||||
</div>
|
||||
<button type="button"
|
||||
onclick="openCompleteModal({{ student.id }})"
|
||||
class="ml-2 text-xs bg-blue-100 hover:bg-blue-200 text-blue-700 px-2 py-1 rounded transition-colors"
|
||||
title="Compléter les notes">
|
||||
⚡
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
{% set current_exercise = '' %}
|
||||
@@ -437,6 +445,52 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal de complétion des notes -->
|
||||
<div id="complete-modal" class="hidden fixed inset-0 bg-black bg-opacity-50 z-[10000]">
|
||||
<div class="flex items-center justify-center min-h-screen p-4">
|
||||
<div class="bg-white rounded-lg p-6 w-96 max-w-full">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-semibold text-gray-900">Compléter les notes</h3>
|
||||
<button onclick="closeCompleteModal()" class="text-gray-400 hover:text-gray-600">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="complete-value" class="block text-sm font-medium text-gray-700 mb-2">
|
||||
Valeur à attribuer :
|
||||
</label>
|
||||
<select id="complete-value" class="w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-blue-500 focus:border-blue-500">
|
||||
<!-- Options générées dynamiquement par JavaScript -->
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-6">
|
||||
<label class="flex items-center">
|
||||
<input type="checkbox" id="overwrite-existing" class="mr-3 h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
|
||||
<span class="text-sm text-gray-700">Écraser les valeurs existantes</span>
|
||||
</label>
|
||||
<p class="text-xs text-gray-500 mt-2 ml-7">
|
||||
Si décoché, seuls les champs vides seront modifiés
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end space-x-3">
|
||||
<button onclick="closeCompleteModal()"
|
||||
class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
|
||||
Annuler
|
||||
</button>
|
||||
<button onclick="executeComplete()"
|
||||
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
|
||||
Appliquer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// ====== CONFIGURATION GLOBALE ======
|
||||
const GRADING_CONFIG = {
|
||||
@@ -1377,5 +1431,97 @@ window.addEventListener('beforeunload', function(e) {
|
||||
e.returnValue = 'Vous avez des modifications non sauvegardées. Êtes-vous sûr de vouloir quitter ?';
|
||||
}
|
||||
});
|
||||
|
||||
// ====== GESTIONNAIRE DE COMPLÉTION DES NOTES ======
|
||||
let currentStudentId = null;
|
||||
|
||||
function populateSpecialValues() {
|
||||
const select = document.getElementById('complete-value');
|
||||
select.innerHTML = '';
|
||||
|
||||
// Récupérer les valeurs spéciales depuis la config existante
|
||||
for (const [value, config] of Object.entries(GRADING_CONFIG.scale_values)) {
|
||||
if (GRADING_CONFIG.special_keys.includes(value)) {
|
||||
const option = document.createElement('option');
|
||||
option.value = value;
|
||||
option.textContent = `${value} - ${config.label}`;
|
||||
option.style.color = config.color;
|
||||
select.appendChild(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function openCompleteModal(studentId) {
|
||||
currentStudentId = studentId;
|
||||
populateSpecialValues();
|
||||
document.getElementById('complete-modal').classList.remove('hidden');
|
||||
|
||||
// Focus sur le select pour une meilleure UX
|
||||
setTimeout(() => {
|
||||
document.getElementById('complete-value').focus();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function closeCompleteModal() {
|
||||
document.getElementById('complete-modal').classList.add('hidden');
|
||||
currentStudentId = null;
|
||||
|
||||
// Réinitialiser les valeurs
|
||||
document.getElementById('overwrite-existing').checked = false;
|
||||
}
|
||||
|
||||
function executeComplete() {
|
||||
if (!currentStudentId) {
|
||||
showToast('Erreur: aucun élève sélectionné', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const value = document.getElementById('complete-value').value;
|
||||
const overwrite = document.getElementById('overwrite-existing').checked;
|
||||
|
||||
if (!value) {
|
||||
showToast('Veuillez sélectionner une valeur', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const studentInputs = document.querySelectorAll(`[data-student-id="${currentStudentId}"].grading-input`);
|
||||
let modifiedCount = 0;
|
||||
|
||||
studentInputs.forEach(input => {
|
||||
const isEmpty = !input.value || input.value.trim() === '';
|
||||
|
||||
if (isEmpty || overwrite) {
|
||||
if (input.tagName === 'SELECT') {
|
||||
// Pour les selects (scores)
|
||||
input.value = value;
|
||||
} else {
|
||||
// Pour les inputs (notes)
|
||||
input.value = value;
|
||||
}
|
||||
|
||||
// Déclencher la validation, coloration et sauvegarde automatique
|
||||
handleGradeChange(input);
|
||||
modifiedCount++;
|
||||
}
|
||||
});
|
||||
|
||||
closeCompleteModal();
|
||||
|
||||
// Message de confirmation avec le label de la valeur
|
||||
const config = GRADING_CONFIG.scale_values[value];
|
||||
const label = config ? config.label : value;
|
||||
const message = overwrite
|
||||
? `${modifiedCount} champs modifiés avec "${value}" (${label})`
|
||||
: `${modifiedCount} champs vides complétés avec "${value}" (${label})`;
|
||||
|
||||
showToast(message, 'success');
|
||||
}
|
||||
|
||||
// Gestion de la fermeture de la modal avec Escape
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape' && !document.getElementById('complete-modal').classList.contains('hidden')) {
|
||||
closeCompleteModal();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
Reference in New Issue
Block a user