feat: no hardcoded scale_values
This commit is contained in:
@@ -65,12 +65,26 @@
|
|||||||
<div class="grading-guide text-xs text-blue-800">
|
<div class="grading-guide text-xs text-blue-800">
|
||||||
<span><strong>Notes :</strong> Valeurs décimales (ex: 15.5)</span>
|
<span><strong>Notes :</strong> Valeurs décimales (ex: 15.5)</span>
|
||||||
<span class="mx-3">•</span>
|
<span class="mx-3">•</span>
|
||||||
<span><strong>Scores :</strong> 0=Non acquis, 1=En cours, 2=Acquis, 3=Expert</span>
|
<span><strong>Scores :</strong>
|
||||||
|
{% for value in ['0', '1', '2', '3'] %}
|
||||||
|
{% if value in scale_values %}
|
||||||
|
{% set config = scale_values[value] %}
|
||||||
|
<span style="color: {{ config.color }}; font-weight: bold;">{{ value }}={{ config.label }}</span>
|
||||||
|
{% if not loop.last %}, {% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</span>
|
||||||
<span class="mx-3">•</span>
|
<span class="mx-3">•</span>
|
||||||
<span><strong>Spéciaux :</strong>
|
<span><strong>Spéciaux :</strong>
|
||||||
<kbd class="bg-gray-200 px-1 rounded text-xs">.</kbd>=Pas de réponse,
|
{% set special_items = [] %}
|
||||||
<kbd class="bg-gray-200 px-1 rounded text-xs">d</kbd>=Dispensé,
|
{% for value in scale_values.keys() if not value.isdigit() %}
|
||||||
<kbd class="bg-gray-200 px-1 rounded text-xs">a</kbd>=Absent
|
{% if value in scale_values %}
|
||||||
|
{% set config = scale_values[value] %}
|
||||||
|
{% set item = '<kbd class="bg-gray-200 px-1 rounded text-xs" style="color: ' + config.color + '; font-weight: bold;">' + value + '</kbd><span style="color: ' + config.color + ';">=' + config.label + '</span>' %}
|
||||||
|
{% set _ = special_items.append(item) %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{{ special_items | join(', ') | safe }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -143,16 +157,16 @@
|
|||||||
onchange="handleGradeChange(this)"
|
onchange="handleGradeChange(this)"
|
||||||
onfocus="handleGradeFocus(this)"
|
onfocus="handleGradeFocus(this)"
|
||||||
onkeydown="handleGradeKeydown(event, this)">
|
onkeydown="handleGradeKeydown(event, this)">
|
||||||
<option value="">-</option>
|
<option value="" style="color: #6b7280;">-</option>
|
||||||
{% for special_value in ['.', 'd', 'a'] %}
|
|
||||||
{% if special_value in scale_values %}
|
|
||||||
{% set display_info = config_manager.get_display_info(special_value, 'score') %}
|
|
||||||
<option value="{{ special_value }}" style="color: {{ display_info.color }}" {% if existing_grade and existing_grade.value == special_value %}selected{% endif %}>{{ special_value }} ({{ display_info.label }})</option>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% for value in ['0', '1', '2', '3'] %}
|
{% for value in ['0', '1', '2', '3'] %}
|
||||||
{% set display_info = config_manager.get_display_info(value, 'score') %}
|
{% set display_info = config_manager.get_display_info(value, 'score') %}
|
||||||
<option value="{{ value }}" style="color: {{ display_info.color }}" {% if existing_grade and existing_grade.value == value %}selected{% endif %}>{{ value }} ({{ display_info.label }})</option>
|
<option value="{{ value }}" style="color: {{ display_info.color }}; font-weight: bold;" {% if existing_grade and existing_grade.value == value %}selected{% endif %}>{{ value }} - {{ display_info.label }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
{% for special_value in scale_values.keys() if not special_value.isdigit() %}
|
||||||
|
{% if special_value in scale_values %}
|
||||||
|
{% set display_info = config_manager.get_display_info(special_value, 'score') %}
|
||||||
|
<option value="{{ special_value }}" style="color: {{ display_info.color }}; font-style: italic;" {% if existing_grade and existing_grade.value == special_value %}selected{% endif %}>{{ special_value }} - {{ display_info.label }}</option>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
{% else %}
|
{% else %}
|
||||||
@@ -166,7 +180,7 @@
|
|||||||
data-element-id="{{ element.id }}"
|
data-element-id="{{ element.id }}"
|
||||||
data-row="{{ loop.index0 }}"
|
data-row="{{ loop.index0 }}"
|
||||||
data-col="{{ loop.index0 }}"
|
data-col="{{ loop.index0 }}"
|
||||||
placeholder="0-{{ element.max_points }} ou {% for v, c in scale_values.items() if v in ['.', 'd', 'a'] %}{{ v }}{% if not loop.last %} {% endif %}{% endfor %}"
|
placeholder="0-{{ element.max_points }} ou {% for v in scale_values.keys() if not v.isdigit() %}{{ v }}{% if not loop.last %} {% endif %}{% endfor %}"
|
||||||
oninput="handleGradeChange(this)"
|
oninput="handleGradeChange(this)"
|
||||||
onfocus="handleGradeFocus(this)"
|
onfocus="handleGradeFocus(this)"
|
||||||
onkeydown="handleGradeKeydown(event, this)">
|
onkeydown="handleGradeKeydown(event, this)">
|
||||||
@@ -231,7 +245,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Configuration simple pour 2 types
|
// Configuration avec couleurs de la base de données
|
||||||
const GRADING_CONFIG = {
|
const GRADING_CONFIG = {
|
||||||
types: {
|
types: {
|
||||||
notes: {
|
notes: {
|
||||||
@@ -241,13 +255,18 @@ const GRADING_CONFIG = {
|
|||||||
},
|
},
|
||||||
score: {
|
score: {
|
||||||
label: 'Échelle de compétences (0-3)',
|
label: 'Échelle de compétences (0-3)',
|
||||||
description: 'Échelle : {% for i in range(4) %}{{ i }}={{ scale_values[i|string]['label'] }}{% if not loop.last %}, {% endif %}{% endfor %}',
|
description: 'Échelle fixe de 0 à 3',
|
||||||
max_value: 3,
|
max_value: 3,
|
||||||
input_type: 'select'
|
input_type: 'select'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// Couleurs configurées en base
|
||||||
|
scale_values: {{ scale_values | tojson }},
|
||||||
|
// Valeurs spéciales pour compatibilité
|
||||||
special_values: {
|
special_values: {
|
||||||
{% if '.' in scale_values %}'.' : { label: '{{ scale_values['.']['label'] }}', color: '{{ scale_values['.']['color'] }}' }{% endif %}{% if 'd' in scale_values %}{% if '.' in scale_values %},{% endif %}'d' : { label: '{{ scale_values['d']['label'] }}', color: '{{ scale_values['d']['color'] }}' }{% endif %}{% if 'a' in scale_values %}{% if '.' in scale_values or 'd' in scale_values %},{% endif %}'a' : { label: '{{ scale_values['a']['label'] }}', color: '{{ scale_values['a']['color'] }}' }{% endif %}
|
{% for value in scale_values.keys() if not value.isdigit() %}
|
||||||
|
'{{ value }}' : {{ scale_values[value] | tojson }}{% if not loop.last %},{% endif %}
|
||||||
|
{% endfor %}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -260,8 +279,8 @@ let unsavedChanges = new Set();
|
|||||||
let undoStack = [];
|
let undoStack = [];
|
||||||
let isAutoSaving = false;
|
let isAutoSaving = false;
|
||||||
|
|
||||||
// Liste dynamique des valeurs spéciales
|
// Liste dynamique des valeurs spéciales (exclut les scores 0-3)
|
||||||
const SPECIAL_VALUES_KEYS = Object.keys(GRADING_CONFIG.special_values);
|
const SPECIAL_VALUES_KEYS = Object.keys(GRADING_CONFIG.scale_values).filter(key => !['0', '1', '2', '3'].includes(key));
|
||||||
|
|
||||||
// Initialisation au chargement de la page
|
// Initialisation au chargement de la page
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
@@ -269,8 +288,23 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
updateProgressIndicator();
|
updateProgressIndicator();
|
||||||
focusFirstInput();
|
focusFirstInput();
|
||||||
setupAutosave();
|
setupAutosave();
|
||||||
|
applyInitialColors();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Applique les couleurs aux champs déjà remplis au chargement
|
||||||
|
function applyInitialColors() {
|
||||||
|
document.querySelectorAll('.grading-input').forEach(input => {
|
||||||
|
const value = input.value;
|
||||||
|
const type = input.dataset.type;
|
||||||
|
const maxPoints = parseFloat(input.dataset.maxPoints) || 20;
|
||||||
|
|
||||||
|
if (value && value.trim() !== '') {
|
||||||
|
const isValid = validateGradeValue(value, type, maxPoints);
|
||||||
|
applyColorToInput(input, value, type, isValid, maxPoints);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Configuration de la navigation clavier
|
// Configuration de la navigation clavier
|
||||||
function setupKeyboardNavigation() {
|
function setupKeyboardNavigation() {
|
||||||
// Raccourcis globaux
|
// Raccourcis globaux
|
||||||
@@ -323,7 +357,7 @@ function handleGradeKeydown(event, input) {
|
|||||||
|
|
||||||
// Navigation clavier pour valeurs spéciales - SIMPLIFIÉ
|
// Navigation clavier pour valeurs spéciales - SIMPLIFIÉ
|
||||||
// On n'intercepte que 'd' et 'a' automatiquement, pas le point
|
// On n'intercepte que 'd' et 'a' automatiquement, pas le point
|
||||||
if (['d', 'a'].includes(e.key) && SPECIAL_VALUES_KEYS.includes(e.key)) {
|
if (SPECIAL_VALUES_KEYS.includes(e.key) && e.key !== '.') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (input.tagName === 'SELECT') {
|
if (input.tagName === 'SELECT') {
|
||||||
input.value = e.key;
|
input.value = e.key;
|
||||||
@@ -444,34 +478,8 @@ function handleGradeChange(input) {
|
|||||||
// Validation immédiate
|
// Validation immédiate
|
||||||
const isValid = validateGradeValue(value, type, maxPoints);
|
const isValid = validateGradeValue(value, type, maxPoints);
|
||||||
|
|
||||||
// Feedback visuel
|
// Appliquer la colorisation selon la valeur
|
||||||
if (GRADING_CONFIG.special_values[value]) {
|
applyColorToInput(input, value, type, isValid, maxPoints);
|
||||||
// Valeur spéciale : couleur spécifique
|
|
||||||
input.style.color = GRADING_CONFIG.special_values[value].color;
|
|
||||||
input.classList.remove('border-red-300', 'bg-red-50', 'border-green-300', 'bg-green-50');
|
|
||||||
} else if (!isValid && value.trim() !== '') {
|
|
||||||
// Valeur invalide : rouge
|
|
||||||
input.style.color = '#dc2626';
|
|
||||||
input.classList.add('border-red-300', 'bg-red-50');
|
|
||||||
input.classList.remove('border-green-300', 'bg-green-50');
|
|
||||||
|
|
||||||
// Message d'erreur temporaire
|
|
||||||
showValidationMessage(input,
|
|
||||||
type === 'score' ? 'Valeur autorisée : 0, 1, 2, 3 ou valeurs spéciales'
|
|
||||||
: `Valeur autorisée : 0 à ${maxPoints} ou valeurs spéciales`);
|
|
||||||
} else if (isValid && value.trim() !== '') {
|
|
||||||
// Valeur valide : vert léger
|
|
||||||
input.style.color = '#059669';
|
|
||||||
input.classList.remove('border-red-300', 'bg-red-50');
|
|
||||||
input.classList.add('border-green-300', 'bg-green-50');
|
|
||||||
setTimeout(() => {
|
|
||||||
input.classList.remove('border-green-300', 'bg-green-50');
|
|
||||||
}, 1000);
|
|
||||||
} else {
|
|
||||||
// Valeur vide : neutre
|
|
||||||
input.style.color = '';
|
|
||||||
input.classList.remove('border-red-300', 'bg-red-50', 'border-green-300', 'bg-green-50');
|
|
||||||
}
|
|
||||||
|
|
||||||
unsavedChanges.add(key);
|
unsavedChanges.add(key);
|
||||||
|
|
||||||
@@ -490,13 +498,90 @@ function handleGradeChange(input) {
|
|||||||
updateSaveStatus();
|
updateSaveStatus();
|
||||||
updateProgressIndicator();
|
updateProgressIndicator();
|
||||||
|
|
||||||
// Feedback visuel
|
// Feedback visuel temporaire de modification
|
||||||
input.classList.add('bg-yellow-50', 'border-yellow-300');
|
input.classList.add('bg-yellow-50', 'border-yellow-300');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
input.classList.remove('bg-yellow-50', 'border-yellow-300');
|
input.classList.remove('bg-yellow-50', 'border-yellow-300');
|
||||||
|
// Réappliquer la couleur après l'animation
|
||||||
|
applyColorToInput(input, value, type, isValid, maxPoints);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nouvelle fonction pour appliquer les couleurs aux champs de saisie
|
||||||
|
function applyColorToInput(input, value, type, isValid, maxPoints) {
|
||||||
|
// Réinitialiser les classes et styles
|
||||||
|
input.classList.remove('border-red-300', 'bg-red-50', 'border-green-300', 'bg-green-50');
|
||||||
|
input.style.backgroundColor = '';
|
||||||
|
input.style.borderColor = '';
|
||||||
|
input.style.color = '';
|
||||||
|
input.style.fontWeight = '';
|
||||||
|
input.style.fontStyle = '';
|
||||||
|
|
||||||
|
if (!value || value.trim() === '') {
|
||||||
|
// Valeur vide : style neutre
|
||||||
|
input.style.color = '#6b7280'; // gray-500
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier si c'est une valeur configurée dans l'échelle
|
||||||
|
if (GRADING_CONFIG.scale_values[value]) {
|
||||||
|
const config = GRADING_CONFIG.scale_values[value];
|
||||||
|
input.style.color = config.color;
|
||||||
|
input.style.backgroundColor = hexToRgba(config.color, 0.1);
|
||||||
|
input.style.borderColor = hexToRgba(config.color, 0.3);
|
||||||
|
input.style.fontWeight = 'bold';
|
||||||
|
|
||||||
|
// Pour les valeurs spéciales (non numériques), style italique
|
||||||
|
if (isNaN(value)) {
|
||||||
|
input.style.fontStyle = 'italic';
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validation et colorisation pour valeurs non configurées
|
||||||
|
if (!isValid) {
|
||||||
|
// Valeur invalide : rouge
|
||||||
|
input.style.color = '#dc2626';
|
||||||
|
input.style.backgroundColor = '#fef2f2';
|
||||||
|
input.style.borderColor = '#fca5a5';
|
||||||
|
|
||||||
|
// Message d'erreur temporaire
|
||||||
|
showValidationMessage(input,
|
||||||
|
type === 'score' ? 'Valeur autorisée : 0, 1, 2, 3 ou valeurs spéciales'
|
||||||
|
: `Valeur autorisée : 0 à ${maxPoints} ou valeurs spéciales`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pour les notes numériques non configurées, utiliser un gradient selon la valeur
|
||||||
|
if (type === 'notes') {
|
||||||
|
const percentage = Math.min(100, (parseFloat(value) / maxPoints) * 100);
|
||||||
|
let noteColor;
|
||||||
|
|
||||||
|
if (percentage >= 80) {
|
||||||
|
noteColor = '#059669'; // Vert foncé
|
||||||
|
} else if (percentage >= 60) {
|
||||||
|
noteColor = '#22c55e'; // Vert
|
||||||
|
} else if (percentage >= 40) {
|
||||||
|
noteColor = '#f6d32d'; // Jaune
|
||||||
|
} else {
|
||||||
|
noteColor = '#ef4444'; // Rouge
|
||||||
|
}
|
||||||
|
|
||||||
|
input.style.color = noteColor;
|
||||||
|
input.style.backgroundColor = hexToRgba(noteColor, 0.1);
|
||||||
|
input.style.borderColor = hexToRgba(noteColor, 0.3);
|
||||||
|
input.style.fontWeight = 'bold';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction utilitaire pour convertir hex en rgba
|
||||||
|
function hexToRgba(hex, alpha) {
|
||||||
|
const r = parseInt(hex.slice(1, 3), 16);
|
||||||
|
const g = parseInt(hex.slice(3, 5), 16);
|
||||||
|
const b = parseInt(hex.slice(5, 7), 16);
|
||||||
|
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
||||||
|
}
|
||||||
|
|
||||||
// Mise à jour de l'indicateur de progression
|
// Mise à jour de l'indicateur de progression
|
||||||
function updateProgressIndicator() {
|
function updateProgressIndicator() {
|
||||||
const filledInputs = document.querySelectorAll('.grading-input').length;
|
const filledInputs = document.querySelectorAll('.grading-input').length;
|
||||||
|
|||||||
Reference in New Issue
Block a user