532 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			532 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| {% extends "base.html" %}
 | ||
| 
 | ||
| {% block title %}{{ title }} - Gestion Scolaire{% endblock %}
 | ||
| 
 | ||
| {% block content %}
 | ||
| <div class="max-w-6xl mx-auto">
 | ||
|     <div class="mb-6">
 | ||
|         {% if is_edit %}
 | ||
|             <a href="{{ url_for('assessments.detail', id=assessment.id) }}" class="text-blue-600 hover:text-blue-800 text-sm font-medium">
 | ||
|                 ← Retour à l'évaluation
 | ||
|             </a>
 | ||
|         {% else %}
 | ||
|             <a href="{{ url_for('assessments.list') }}" class="text-blue-600 hover:text-blue-800 text-sm font-medium">
 | ||
|                 ← Retour aux évaluations
 | ||
|             </a>
 | ||
|         {% endif %}
 | ||
|     </div>
 | ||
|     
 | ||
|     <div class="bg-white shadow rounded-lg">
 | ||
|         <div class="px-6 py-4 border-b border-gray-200">
 | ||
|             <h1 class="text-xl font-semibold text-gray-900">{{ title }}</h1>
 | ||
|             {% if is_edit %}
 | ||
|                 <p class="text-sm text-gray-600 mt-1">Modifiez votre évaluation complète avec exercices et éléments de notation</p>
 | ||
|             {% else %}
 | ||
|                 <p class="text-sm text-gray-600 mt-1">Créez votre évaluation complète avec exercices et éléments de notation</p>
 | ||
|             {% endif %}
 | ||
|         </div>
 | ||
|         
 | ||
|         <form id="unified-form" method="POST" class="px-6 py-6 space-y-8">
 | ||
|             {{ form.hidden_tag() }}
 | ||
|             
 | ||
|             <!-- Section Évaluation -->
 | ||
|             <div class="bg-blue-50 border border-blue-200 rounded-lg p-6">
 | ||
|                 <h2 class="text-lg font-medium text-blue-900 mb-4">📝 Informations de l'évaluation</h2>
 | ||
|                 
 | ||
|                 <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
 | ||
|                     <div>
 | ||
|                         <label for="{{ form.title.id }}" class="block text-sm font-medium text-gray-700 mb-1">
 | ||
|                             {{ form.title.label.text }}
 | ||
|                         </label>
 | ||
|                         {{ form.title(class="block w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-blue-500 focus:border-blue-500") }}
 | ||
|                         {% if form.title.errors %}
 | ||
|                             <div class="mt-1 text-sm text-red-600">
 | ||
|                                 {% for error in form.title.errors %}
 | ||
|                                     <p>{{ error }}</p>
 | ||
|                                 {% endfor %}
 | ||
|                             </div>
 | ||
|                         {% endif %}
 | ||
|                     </div>
 | ||
|                     
 | ||
|                     <div>
 | ||
|                         <label for="{{ form.class_group_id.id }}" class="block text-sm font-medium text-gray-700 mb-1">
 | ||
|                             {{ form.class_group_id.label.text }}
 | ||
|                         </label>
 | ||
|                         {{ form.class_group_id(class="block w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-blue-500 focus:border-blue-500") }}
 | ||
|                         {% if form.class_group_id.errors %}
 | ||
|                             <div class="mt-1 text-sm text-red-600">
 | ||
|                                 {% for error in form.class_group_id.errors %}
 | ||
|                                     <p>{{ error }}</p>
 | ||
|                                 {% endfor %}
 | ||
|                             </div>
 | ||
|                         {% endif %}
 | ||
|                     </div>
 | ||
|                     
 | ||
|                     <div>
 | ||
|                         <label for="{{ form.date.id }}" class="block text-sm font-medium text-gray-700 mb-1">
 | ||
|                             {{ form.date.label.text }}
 | ||
|                         </label>
 | ||
|                         {{ form.date(class="block w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-blue-500 focus:border-blue-500") }}
 | ||
|                         {% if form.date.errors %}
 | ||
|                             <div class="mt-1 text-sm text-red-600">
 | ||
|                                 {% for error in form.date.errors %}
 | ||
|                                     <p>{{ error }}</p>
 | ||
|                                 {% endfor %}
 | ||
|                             </div>
 | ||
|                         {% endif %}
 | ||
|                     </div>
 | ||
|                     
 | ||
|                     <div>
 | ||
|                         <label for="{{ form.coefficient.id }}" class="block text-sm font-medium text-gray-700 mb-1">
 | ||
|                             {{ form.coefficient.label.text }}
 | ||
|                         </label>
 | ||
|                         {{ form.coefficient(class="block w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-blue-500 focus:border-blue-500", step="0.5") }}
 | ||
|                         {% if form.coefficient.errors %}
 | ||
|                             <div class="mt-1 text-sm text-red-600">
 | ||
|                                 {% for error in form.coefficient.errors %}
 | ||
|                                     <p>{{ error }}</p>
 | ||
|                                 {% endfor %}
 | ||
|                             </div>
 | ||
|                         {% endif %}
 | ||
|                     </div>
 | ||
|                 </div>
 | ||
|                 
 | ||
|                 <div class="mt-4">
 | ||
|                     <label for="{{ form.description.id }}" class="block text-sm font-medium text-gray-700 mb-1">
 | ||
|                         {{ form.description.label.text }}
 | ||
|                     </label>
 | ||
|                     {{ form.description(class="block w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-blue-500 focus:border-blue-500", rows="3") }}
 | ||
|                     {% if form.description.errors %}
 | ||
|                         <div class="mt-1 text-sm text-red-600">
 | ||
|                             {% for error in form.description.errors %}
 | ||
|                                 <p>{{ error }}</p>
 | ||
|                             {% endfor %}
 | ||
|                         </div>
 | ||
|                     {% endif %}
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|             
 | ||
|             <!-- Section Exercices -->
 | ||
|             <div class="bg-green-50 border border-green-200 rounded-lg p-6">
 | ||
|                 <div class="mb-4">
 | ||
|                     <h2 class="text-lg font-medium text-green-900">🏃 Exercices</h2>
 | ||
|                 </div>
 | ||
|                 
 | ||
|                 <div id="exercises-container" class="space-y-4">
 | ||
|                     <!-- Les exercices seront ajoutés ici dynamiquement -->
 | ||
|                 </div>
 | ||
|                 
 | ||
|                 <div id="no-exercises" class="text-center py-8 text-green-700">
 | ||
|                     <p class="text-sm">Aucun exercice ajouté. Utilisez le bouton ci-dessous pour commencer.</p>
 | ||
|                 </div>
 | ||
|                 
 | ||
|                 <!-- Bouton d'ajout placé après la liste pour une meilleure navigation clavier -->
 | ||
|                 <div class="mt-4 pt-4 border-t border-green-200">
 | ||
|                     <button type="button" id="add-exercise" class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2">
 | ||
|                         ➕ Ajouter un exercice
 | ||
|                     </button>
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|             
 | ||
|             <!-- Guide d'aide -->
 | ||
|             <div class="bg-gray-50 border border-gray-200 rounded-lg p-4">
 | ||
|                 <h3 class="text-sm font-medium text-gray-900 mb-2">💡 Guide rapide</h3>
 | ||
|                 <div class="text-xs text-gray-700 space-y-1">
 | ||
|                     <p><strong>Points :</strong> Notation classique (ex: 2.5/4 points)</p>
 | ||
|                     <p><strong>Score :</strong> Évaluation par niveaux (0=non acquis, 1=en cours, 2=acquis, 3=expert, .=non évalué)</p>
 | ||
|                     <p><strong>Ordre :</strong> Les exercices et éléments seront affichés dans l'ordre numérique</p>
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|             
 | ||
|             <!-- Actions -->
 | ||
|             <div class="flex justify-end space-x-3 pt-4 border-t border-gray-200">
 | ||
|                 {% if is_edit %}
 | ||
|                     <a href="{{ url_for('assessments.detail', id=assessment.id) }}" class="px-6 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2">
 | ||
|                         Annuler
 | ||
|                     </a>
 | ||
|                     <button type="submit" class="px-6 py-2 bg-blue-600 text-white rounded-md text-sm font-medium hover:bg-blue-700 transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">
 | ||
|                         💾 Modifier l'évaluation complète
 | ||
|                     </button>
 | ||
|                 {% else %}
 | ||
|                     <a href="{{ url_for('assessments.list') }}" class="px-6 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2">
 | ||
|                         Annuler
 | ||
|                     </a>
 | ||
|                     <button type="submit" class="px-6 py-2 bg-blue-600 text-white rounded-md text-sm font-medium hover:bg-blue-700 transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">
 | ||
|                         💾 Créer l'évaluation complète
 | ||
|                     </button>
 | ||
|                 {% endif %}
 | ||
|             </div>
 | ||
|         </form>
 | ||
|     </div>
 | ||
| </div>
 | ||
| 
 | ||
| <!-- Template pour un exercice -->
 | ||
| <template id="exercise-template">
 | ||
|     <div class="exercise-item border border-green-300 rounded-lg p-4 bg-white">
 | ||
|         <div class="flex justify-between items-start mb-4">
 | ||
|             <h3 class="text-md font-medium text-green-800">Exercice <span class="exercise-number"></span></h3>
 | ||
|             <button type="button" class="remove-exercise text-red-600 hover:text-red-800 text-sm font-medium">
 | ||
|                 Supprimer
 | ||
|             </button>
 | ||
|         </div>
 | ||
|         
 | ||
|         <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
 | ||
|             <div>
 | ||
|                 <label class="block text-sm font-medium text-gray-700 mb-1">Titre de l'exercice</label>
 | ||
|                 <input type="text" class="exercise-title block w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-green-500 focus:border-green-500" required>
 | ||
|             </div>
 | ||
|             <div>
 | ||
|                 <label class="block text-sm font-medium text-gray-700 mb-1">Ordre</label>
 | ||
|                 <input type="number" class="exercise-order block w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-green-500 focus:border-green-500" min="1" required>
 | ||
|             </div>
 | ||
|         </div>
 | ||
|         
 | ||
|         <div class="mb-4">
 | ||
|             <label class="block text-sm font-medium text-gray-700 mb-1">Description (optionnel)</label>
 | ||
|             <textarea class="exercise-description block w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-green-500 focus:border-green-500" rows="2"></textarea>
 | ||
|         </div>
 | ||
|         
 | ||
|         <!-- Éléments de notation -->
 | ||
|         <div class="bg-purple-50 border border-purple-200 rounded p-4">
 | ||
|             <div class="mb-3">
 | ||
|                 <h4 class="text-sm font-medium text-purple-900">Éléments de notation</h4>
 | ||
|             </div>
 | ||
|             
 | ||
|             <div class="grading-elements-container space-y-3">
 | ||
|                 <!-- Les éléments de notation seront ajoutés ici -->
 | ||
|             </div>
 | ||
|             
 | ||
|             <div class="no-grading-elements text-center py-4 text-purple-700 text-xs">
 | ||
|                 Aucun élément de notation. Utilisez le bouton ci-dessous pour commencer.
 | ||
|             </div>
 | ||
|             
 | ||
|             <!-- Bouton d'ajout placé après la liste pour une meilleure navigation clavier -->
 | ||
|             <div class="mt-3 pt-3 border-t border-purple-200">
 | ||
|                 <button type="button" class="add-grading-element bg-purple-600 hover:bg-purple-700 text-white px-3 py-1 rounded text-xs font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-1">
 | ||
|                     ➕ Ajouter élément
 | ||
|                 </button>
 | ||
|             </div>
 | ||
|         </div>
 | ||
|     </div>
 | ||
| </template>
 | ||
| 
 | ||
| <!-- Template pour un élément de notation -->
 | ||
| <template id="grading-element-template">
 | ||
|     <div class="grading-element-item border border-purple-300 rounded p-3 bg-white">
 | ||
|         <div class="flex justify-between items-start mb-3">
 | ||
|             <h5 class="text-sm font-medium text-purple-800">Élément de notation</h5>
 | ||
|             <button type="button" class="remove-grading-element text-red-600 hover:text-red-800 text-xs font-medium">
 | ||
|                 Supprimer
 | ||
|             </button>
 | ||
|         </div>
 | ||
|         
 | ||
|         <div class="grid grid-cols-1 md:grid-cols-2 gap-3">
 | ||
|             <div>
 | ||
|                 <label class="block text-xs font-medium text-gray-700 mb-1">Label</label>
 | ||
|                 <input type="text" class="element-label block w-full border border-gray-300 rounded-md px-2 py-1 text-sm focus:ring-purple-500 focus:border-purple-500" required>
 | ||
|             </div>
 | ||
|             <div>
 | ||
|                 <label class="block text-xs font-medium text-gray-700 mb-1">Compétence</label>
 | ||
|                 <input type="text" class="element-skill block w-full border border-gray-300 rounded-md px-2 py-1 text-sm focus:ring-purple-500 focus:border-purple-500">
 | ||
|             </div>
 | ||
|             <div>
 | ||
|                 <label class="block text-xs font-medium text-gray-700 mb-1">Points max</label>
 | ||
|                 <input type="number" step="0.1" min="0" class="element-max-points block w-full border border-gray-300 rounded-md px-2 py-1 text-sm focus:ring-purple-500 focus:border-purple-500" required>
 | ||
|             </div>
 | ||
|             <div>
 | ||
|                 <label class="block text-xs font-medium text-gray-700 mb-1">Type de notation</label>
 | ||
|                 <select class="element-grading-type block w-full border border-gray-300 rounded-md px-2 py-1 text-sm focus:ring-purple-500 focus:border-purple-500" required>
 | ||
|                     <option value="">Choisir...</option>
 | ||
|                     <option value="points">Points</option>
 | ||
|                     <option value="score">Score</option>
 | ||
|                 </select>
 | ||
|             </div>
 | ||
|         </div>
 | ||
|         
 | ||
|         <div class="mt-3">
 | ||
|             <label class="block text-xs font-medium text-gray-700 mb-1">Description (optionnel)</label>
 | ||
|             <textarea class="element-description block w-full border border-gray-300 rounded-md px-2 py-1 text-sm focus:ring-purple-500 focus:border-purple-500" rows="2"></textarea>
 | ||
|         </div>
 | ||
|     </div>
 | ||
| </template>
 | ||
| 
 | ||
| <script>
 | ||
| let exerciseCounter = 0;
 | ||
| 
 | ||
| document.addEventListener('DOMContentLoaded', function() {
 | ||
|     const addExerciseBtn = document.getElementById('add-exercise');
 | ||
|     const exercisesContainer = document.getElementById('exercises-container');
 | ||
|     const noExercisesMsg = document.getElementById('no-exercises');
 | ||
|     const form = document.getElementById('unified-form');
 | ||
|     
 | ||
|     // Pré-remplir les données en mode édition
 | ||
|     {% if is_edit %}
 | ||
|         loadExistingData();
 | ||
|     {% endif %}
 | ||
|     
 | ||
|     // Ajouter un exercice
 | ||
|     addExerciseBtn.addEventListener('click', function() {
 | ||
|         addExercise();
 | ||
|     });
 | ||
|     
 | ||
|     // Soumission du formulaire
 | ||
|     form.addEventListener('submit', function(e) {
 | ||
|         e.preventDefault();
 | ||
|         submitForm();
 | ||
|     });
 | ||
|     
 | ||
|     function addExercise() {
 | ||
|         exerciseCounter++;
 | ||
|         const template = document.getElementById('exercise-template');
 | ||
|         const exerciseDiv = template.content.cloneNode(true);
 | ||
|         
 | ||
|         // Mettre à jour le numéro d'exercice
 | ||
|         exerciseDiv.querySelector('.exercise-number').textContent = exerciseCounter;
 | ||
|         exerciseDiv.querySelector('.exercise-order').value = exerciseCounter;
 | ||
|         
 | ||
|         // Ajouter les event listeners
 | ||
|         const removeBtn = exerciseDiv.querySelector('.remove-exercise');
 | ||
|         removeBtn.addEventListener('click', function() {
 | ||
|             removeExercise(this);
 | ||
|         });
 | ||
|         
 | ||
|         const addElementBtn = exerciseDiv.querySelector('.add-grading-element');
 | ||
|         addElementBtn.addEventListener('click', function() {
 | ||
|             addGradingElement(this);
 | ||
|         });
 | ||
|         
 | ||
|         exercisesContainer.appendChild(exerciseDiv);
 | ||
|         updateExercisesVisibility();
 | ||
|         
 | ||
|         // Focus automatique sur le champ titre du nouvel exercice pour faciliter la navigation clavier
 | ||
|         setTimeout(() => {
 | ||
|             const titleInput = exercisesContainer.lastElementChild.querySelector('.exercise-title');
 | ||
|             if (titleInput) {
 | ||
|                 titleInput.focus();
 | ||
|             }
 | ||
|         }, 100);
 | ||
|     }
 | ||
|     
 | ||
|     function removeExercise(btn) {
 | ||
|         btn.closest('.exercise-item').remove();
 | ||
|         updateExercisesVisibility();
 | ||
|         renumberExercises();
 | ||
|     }
 | ||
|     
 | ||
|     function addGradingElement(btn) {
 | ||
|         const exerciseDiv = btn.closest('.exercise-item');
 | ||
|         const elementsContainer = exerciseDiv.querySelector('.grading-elements-container');
 | ||
|         const noElementsMsg = exerciseDiv.querySelector('.no-grading-elements');
 | ||
|         
 | ||
|         const template = document.getElementById('grading-element-template');
 | ||
|         const elementDiv = template.content.cloneNode(true);
 | ||
|         
 | ||
|         // Ajouter l'event listener pour supprimer
 | ||
|         const removeBtn = elementDiv.querySelector('.remove-grading-element');
 | ||
|         removeBtn.addEventListener('click', function() {
 | ||
|             removeGradingElement(this);
 | ||
|         });
 | ||
|         
 | ||
|         elementsContainer.appendChild(elementDiv);
 | ||
|         noElementsMsg.style.display = 'none';
 | ||
|         
 | ||
|         // Focus automatique sur le champ label du nouvel élément pour faciliter la navigation clavier
 | ||
|         setTimeout(() => {
 | ||
|             const labelInput = elementsContainer.lastElementChild.querySelector('.element-label');
 | ||
|             if (labelInput) {
 | ||
|                 labelInput.focus();
 | ||
|             }
 | ||
|         }, 100);
 | ||
|     }
 | ||
|     
 | ||
|     function removeGradingElement(btn) {
 | ||
|         const exerciseDiv = btn.closest('.exercise-item');
 | ||
|         const elementsContainer = exerciseDiv.querySelector('.grading-elements-container');
 | ||
|         const noElementsMsg = exerciseDiv.querySelector('.no-grading-elements');
 | ||
|         
 | ||
|         btn.closest('.grading-element-item').remove();
 | ||
|         
 | ||
|         if (elementsContainer.children.length === 0) {
 | ||
|             noElementsMsg.style.display = 'block';
 | ||
|         }
 | ||
|     }
 | ||
|     
 | ||
|     function updateExercisesVisibility() {
 | ||
|         const hasExercises = exercisesContainer.children.length > 0;
 | ||
|         noExercisesMsg.style.display = hasExercises ? 'none' : 'block';
 | ||
|     }
 | ||
|     
 | ||
|     function renumberExercises() {
 | ||
|         const exercises = exercisesContainer.querySelectorAll('.exercise-item');
 | ||
|         exercises.forEach((exercise, index) => {
 | ||
|             const number = index + 1;
 | ||
|             exercise.querySelector('.exercise-number').textContent = number;
 | ||
|             exercise.querySelector('.exercise-order').value = number;
 | ||
|         });
 | ||
|         exerciseCounter = exercises.length;
 | ||
|     }
 | ||
|     
 | ||
|     function collectFormData() {
 | ||
|         const exercises = [];
 | ||
|         const exerciseItems = exercisesContainer.querySelectorAll('.exercise-item');
 | ||
|         
 | ||
|         exerciseItems.forEach(exerciseItem => {
 | ||
|             const title = exerciseItem.querySelector('.exercise-title').value;
 | ||
|             const description = exerciseItem.querySelector('.exercise-description').value;
 | ||
|             const order = parseInt(exerciseItem.querySelector('.exercise-order').value);
 | ||
|             
 | ||
|             if (!title.trim()) return;
 | ||
|             
 | ||
|             const gradingElements = [];
 | ||
|             const elementItems = exerciseItem.querySelectorAll('.grading-element-item');
 | ||
|             
 | ||
|             elementItems.forEach(elementItem => {
 | ||
|                 const label = elementItem.querySelector('.element-label').value;
 | ||
|                 const skill = elementItem.querySelector('.element-skill').value;
 | ||
|                 const maxPoints = parseFloat(elementItem.querySelector('.element-max-points').value);
 | ||
|                 const gradingType = elementItem.querySelector('.element-grading-type').value;
 | ||
|                 const description = elementItem.querySelector('.element-description').value;
 | ||
|                 
 | ||
|                 if (!label.trim() || !maxPoints || !gradingType) return;
 | ||
|                 
 | ||
|                 gradingElements.push({
 | ||
|                     label: label.trim(),
 | ||
|                     skill: skill.trim(),
 | ||
|                     max_points: maxPoints,
 | ||
|                     grading_type: gradingType,
 | ||
|                     description: description.trim()
 | ||
|                 });
 | ||
|             });
 | ||
|             
 | ||
|             exercises.push({
 | ||
|                 title: title.trim(),
 | ||
|                 description: description.trim(),
 | ||
|                 order: order,
 | ||
|                 grading_elements: gradingElements
 | ||
|             });
 | ||
|         });
 | ||
|         
 | ||
|         return exercises;
 | ||
|     }
 | ||
|     
 | ||
|     function submitForm() {
 | ||
|         const formData = new FormData(form);
 | ||
|         const exercises = collectFormData();
 | ||
|         
 | ||
|         // Validation côté client
 | ||
|         if (exercises.length === 0) {
 | ||
|             alert('Vous devez ajouter au moins un exercice.');
 | ||
|             return;
 | ||
|         }
 | ||
|         
 | ||
|         let hasElementsWithoutGrading = false;
 | ||
|         exercises.forEach(ex => {
 | ||
|             if (ex.grading_elements.length === 0) {
 | ||
|                 hasElementsWithoutGrading = true;
 | ||
|             }
 | ||
|         });
 | ||
|         
 | ||
|         if (hasElementsWithoutGrading) {
 | ||
|             if (!confirm('Certains exercices n\'ont pas d\'éléments de notation. Voulez-vous continuer ?')) {
 | ||
|                 return;
 | ||
|             }
 | ||
|         }
 | ||
|         
 | ||
|         // Envoyer via AJAX
 | ||
|         const data = {
 | ||
|             title: formData.get('title'),
 | ||
|             description: formData.get('description'),
 | ||
|             date: formData.get('date'),
 | ||
|             class_group_id: formData.get('class_group_id'),
 | ||
|             coefficient: formData.get('coefficient'),
 | ||
|             exercises: exercises
 | ||
|         };
 | ||
|         
 | ||
|         // Ajouter le CSRF token aux données
 | ||
|         data.csrf_token = formData.get('csrf_token');
 | ||
|         
 | ||
|         fetch(form.action, {
 | ||
|             method: 'POST',
 | ||
|             headers: {
 | ||
|                 'Content-Type': 'application/json'
 | ||
|             },
 | ||
|             body: JSON.stringify(data)
 | ||
|         })
 | ||
|         .then(response => response.json())
 | ||
|         .then(data => {
 | ||
|             if (data.success) {
 | ||
|                 window.location.href = `/assessments/${data.assessment_id}`;
 | ||
|             } else {
 | ||
|                 alert('Erreur lors de la création : ' + JSON.stringify(data.errors));
 | ||
|             }
 | ||
|         })
 | ||
|         .catch(error => {
 | ||
|             console.error('Erreur:', error);
 | ||
|             alert('Une erreur est survenue lors de la création de l\'évaluation.');
 | ||
|         });
 | ||
|     }
 | ||
|     
 | ||
|     {% if is_edit %}
 | ||
|     function loadExistingData() {
 | ||
|         // Charger les exercices existants
 | ||
|         const existingExercises = {{ exercises_json|tojson if exercises_json else "[]" }};
 | ||
|         
 | ||
|         existingExercises.forEach(exercise => {
 | ||
|             exerciseCounter++;
 | ||
|             const template = document.getElementById('exercise-template');
 | ||
|             const exerciseDiv = template.content.cloneNode(true);
 | ||
|             
 | ||
|             // Remplir les données de l'exercice
 | ||
|             exerciseDiv.querySelector('.exercise-number').textContent = exerciseCounter;
 | ||
|             exerciseDiv.querySelector('.exercise-title').value = exercise.title;
 | ||
|             exerciseDiv.querySelector('.exercise-description').value = exercise.description || '';
 | ||
|             exerciseDiv.querySelector('.exercise-order').value = exercise.order;
 | ||
|             
 | ||
|             // Ajouter les event listeners
 | ||
|             const removeBtn = exerciseDiv.querySelector('.remove-exercise');
 | ||
|             removeBtn.addEventListener('click', function() {
 | ||
|                 removeExercise(this);
 | ||
|             });
 | ||
|             
 | ||
|             const addElementBtn = exerciseDiv.querySelector('.add-grading-element');
 | ||
|             addElementBtn.addEventListener('click', function() {
 | ||
|                 addGradingElement(this);
 | ||
|             });
 | ||
|             
 | ||
|             const elementsContainer = exerciseDiv.querySelector('.grading-elements-container');
 | ||
|             const noElementsMsg = exerciseDiv.querySelector('.no-grading-elements');
 | ||
|             
 | ||
|             // Charger les éléments de notation existants
 | ||
|             exercise.grading_elements.forEach(element => {
 | ||
|                 const elementTemplate = document.getElementById('grading-element-template');
 | ||
|                 const elementDiv = elementTemplate.content.cloneNode(true);
 | ||
|                 
 | ||
|                 // Remplir les données de l'élément
 | ||
|                 elementDiv.querySelector('.element-label').value = element.label;
 | ||
|                 elementDiv.querySelector('.element-skill').value = element.skill || '';
 | ||
|                 elementDiv.querySelector('.element-max-points').value = element.max_points;
 | ||
|                 elementDiv.querySelector('.element-grading-type').value = element.grading_type;
 | ||
|                 elementDiv.querySelector('.element-description').value = element.description || '';
 | ||
|                 
 | ||
|                 // Ajouter l'event listener pour supprimer
 | ||
|                 const removeElementBtn = elementDiv.querySelector('.remove-grading-element');
 | ||
|                 removeElementBtn.addEventListener('click', function() {
 | ||
|                     removeGradingElement(this);
 | ||
|                 });
 | ||
|                 
 | ||
|                 elementsContainer.appendChild(elementDiv);
 | ||
|             });
 | ||
|             
 | ||
|             if (exercise.grading_elements.length > 0) {
 | ||
|                 noElementsMsg.style.display = 'none';
 | ||
|             }
 | ||
|             
 | ||
|             exercisesContainer.appendChild(exerciseDiv);
 | ||
|         });
 | ||
|         
 | ||
|         updateExercisesVisibility();
 | ||
|     }
 | ||
|     {% endif %}
 | ||
| });
 | ||
| </script>
 | ||
| {% endblock %} |