feat: add temporal student gestion

This commit is contained in:
2025-08-16 06:42:47 +02:00
parent f438082c4c
commit 6549591f63
15 changed files with 2212 additions and 148 deletions

View File

@@ -92,7 +92,7 @@
</a>
{# Action BLEUE - Gérer les élèves #}
<a href="{{ url_for('students') }}?class_id={{ class_group.id }}"
<a href="{{ url_for('classes.students', id=class_group.id) }}"
class="group bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-xl p-6 hover:from-blue-600 hover:to-blue-700 transition-all duration-300 transform hover:scale-[1.02] shadow-lg hover:shadow-xl">
<div class="flex items-center">
<div class="w-12 h-12 bg-white/20 rounded-xl flex items-center justify-center mr-4 group-hover:bg-white/30 transition-colors">
@@ -102,7 +102,7 @@
</div>
<div>
<h3 class="text-lg font-bold mb-1">Gérer élèves</h3>
<p class="text-sm opacity-90">{{ class_group.students|length }} élèves inscrits</p>
<p class="text-sm opacity-90">{% if class_group._current_students %}{{ class_group._current_students|length }}{% else %}{{ class_group.students|length }}{% endif %} élèves inscrits</p>
</div>
</div>
</a>
@@ -410,7 +410,7 @@
Effectif complet : {{ class_group.students|length }} élèves
</span>
</div>
<a href="{{ url_for('students') }}?class_id={{ class_group.id }}"
<a href="{{ url_for('classes.students', id=class_group.id) }}"
class="inline-flex items-center text-sm bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white px-4 py-2 rounded-lg transition-all duration-300 font-semibold shadow-lg hover:shadow-xl transform hover:scale-[1.02]">
<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"/>
@@ -427,7 +427,7 @@
</div>
<h3 class="text-sm font-medium text-gray-900 mb-2">Aucun élève inscrit</h3>
<p class="text-sm text-gray-500 mb-4">Ajoutez des élèves à cette classe pour commencer les évaluations</p>
<a href="{{ url_for('students') }}?class_id={{ class_group.id }}"
<a href="{{ url_for('classes.students', id=class_group.id) }}"
class="inline-flex items-center text-sm bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 text-white px-4 py-2 rounded-lg transition-all duration-300 font-semibold shadow-lg hover:shadow-xl transform hover:scale-[1.02]">
<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clip-rule="evenodd"/>

View File

@@ -0,0 +1,608 @@
{% extends "base.html" %}
{% from 'components/common/macros.html' import hero_section %}
{% block title %}Élèves de {{ class_group.name }} - Gestion Scolaire{% endblock %}
{% block content %}
<div class="space-y-6">
{# Hero Section avec Statistiques des Élèves #}
{% set meta_info = [
{
'icon': '<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20"><path d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"/></svg>',
'text': stats.total_current ~ ' élèves actuels'
},
{
'icon': '<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clip-rule="evenodd"/></svg>',
'text': stats.recent_arrivals ~ ' arrivées (30j)'
},
{
'icon': '<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd"/></svg>',
'text': stats.recent_departures ~ ' départs (30j)'
}
] %}
{% set primary_action = {
'url': 'javascript:openEnrollModal()',
'text': 'Inscrire un élève',
'icon': '<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clip-rule="evenodd"/></svg>'
} %}
{{ hero_section(
title=class_group.name ~ " 👥",
subtitle="Gestion des élèves",
meta_info=meta_info,
primary_action=primary_action,
gradient_class="from-blue-500 to-blue-600"
) }}
{# Breadcrumb Navigation #}
<div class="flex items-center text-sm text-gray-600">
<a href="{{ url_for('classes.dashboard', id=class_group.id) }}"
class="hover:text-blue-600 transition-colors flex items-center">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
Retour au dashboard
</a>
</div>
{# Statistiques d'effectifs #}
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-xl p-6">
<div class="flex items-center">
<div class="w-12 h-12 bg-white/20 rounded-xl flex items-center justify-center mr-4">
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</div>
<div>
<h3 class="text-2xl font-bold">{{ stats.total_current }}</h3>
<p class="text-sm opacity-90">Élèves actuels</p>
</div>
</div>
</div>
<div class="bg-gradient-to-r from-green-500 to-green-600 text-white rounded-xl p-6">
<div class="flex items-center">
<div class="w-12 h-12 bg-white/20 rounded-xl flex items-center justify-center mr-4">
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clip-rule="evenodd"/>
</svg>
</div>
<div>
<h3 class="text-2xl font-bold">{{ stats.recent_arrivals }}</h3>
<p class="text-sm opacity-90">Arrivées (30j)</p>
</div>
</div>
</div>
<div class="bg-gradient-to-r from-orange-500 to-orange-600 text-white rounded-xl p-6">
<div class="flex items-center">
<div class="w-12 h-12 bg-white/20 rounded-xl flex items-center justify-center mr-4">
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd"/>
</svg>
</div>
<div>
<h3 class="text-2xl font-bold">{{ stats.recent_departures }}</h3>
<p class="text-sm opacity-90">Départs (30j)</p>
</div>
</div>
</div>
</div>
{# Liste des élèves actuels #}
<div class="bg-white shadow rounded-lg">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-xl font-semibold text-gray-900 flex items-center">
<svg class="w-6 h-6 mr-2 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
Élèves actuellement inscrits ({{ current_students|length }})
</h2>
</div>
{% if current_students %}
<div class="divide-y divide-gray-200">
{% for student in current_students %}
{% set enrollment = student.get_current_enrollment() %}
<div class="px-6 py-4 flex items-center justify-between hover:bg-gray-50">
<div class="flex items-center">
<div class="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center mr-4">
<span class="text-sm font-medium text-blue-600">{{ student.first_name[0] }}{{ student.last_name[0] }}</span>
</div>
<div>
<div class="text-lg font-medium text-gray-900">{{ student.first_name }} {{ student.last_name }}</div>
<div class="text-sm text-gray-500">
{% if enrollment %}
Inscrit depuis le {{ enrollment.enrollment_date.strftime('%d/%m/%Y') }}
{% if enrollment.enrollment_reason %}
({{ enrollment.enrollment_reason }})
{% endif %}
{% endif %}
</div>
</div>
</div>
<div class="flex space-x-2">
<button onclick="transferStudent({{ student.id }}, '{{ student.first_name }} {{ student.last_name }}')"
class="bg-blue-100 hover:bg-blue-200 text-blue-700 px-3 py-1 rounded text-sm transition-colors">
Transférer
</button>
<button onclick="departureStudent({{ student.id }}, '{{ student.first_name }} {{ student.last_name }}')"
class="bg-orange-100 hover:bg-orange-200 text-orange-700 px-3 py-1 rounded text-sm transition-colors">
Départ
</button>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="px-6 py-8 text-center text-gray-500">
<svg class="w-12 h-12 mx-auto mb-4 text-gray-300" fill="currentColor" viewBox="0 0 20 20">
<path d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"/>
</svg>
<p>Aucun élève inscrit dans cette classe</p>
</div>
{% endif %}
</div>
{# Historique des mouvements #}
{% if class_movements %}
<div class="bg-white shadow rounded-lg">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-xl font-semibold text-gray-900 flex items-center">
<svg class="w-6 h-6 mr-2 text-gray-600" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/>
</svg>
Historique des mouvements (6 derniers mois)
</h2>
</div>
<div class="divide-y divide-gray-200">
{% for student, enrollments in class_movements %}
<div class="px-6 py-4">
<div class="flex items-center justify-between mb-2">
<span class="font-medium text-gray-900">{{ student.first_name }} {{ student.last_name }}</span>
</div>
<div class="space-y-1">
{% for enrollment in enrollments %}
<div class="flex items-center justify-between text-sm">
<div class="flex items-center">
{% if enrollment.departure_date %}
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-orange-100 text-orange-800 mr-2">
Départ
</span>
<span class="text-gray-600">
Du {{ enrollment.enrollment_date.strftime('%d/%m/%Y') }} au {{ enrollment.departure_date.strftime('%d/%m/%Y') }}
{% if enrollment.departure_reason %}
- {{ enrollment.departure_reason }}
{% endif %}
</span>
{% else %}
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800 mr-2">
Arrivée
</span>
<span class="text-gray-600">
Depuis le {{ enrollment.enrollment_date.strftime('%d/%m/%Y') }}
{% if enrollment.enrollment_reason %}
- {{ enrollment.enrollment_reason }}
{% endif %}
</span>
{% endif %}
</div>
{% if enrollment.departure_date %}
<button onclick="cancelDeparture({{ enrollment.id }}, '{{ student.first_name }} {{ student.last_name }}')"
class="bg-green-100 hover:bg-green-200 text-green-700 px-2 py-1 rounded text-xs transition-colors">
Annuler le départ
</button>
{% endif %}
</div>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
{# Modal d'inscription d'élève #}
<div id="enrollModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden items-center justify-center z-50">
<div class="bg-white rounded-lg p-6 max-w-md w-full mx-4">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold">Inscrire un élève dans {{ class_group.name }}</h3>
<button type="button" onclick="closeEnrollModal()"
class="text-gray-400 hover:text-gray-600 transition-colors">
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
</svg>
</button>
</div>
{# Tabs pour choisir le mode #}
<div class="flex mb-4 border-b border-gray-200">
<button type="button" id="newStudentTab"
class="px-4 py-2 text-sm font-medium text-blue-600 border-b-2 border-blue-600"
onclick="switchEnrollMode('new')">
Nouvel élève
</button>
<button type="button" id="existingStudentTab"
class="px-4 py-2 text-sm font-medium text-gray-500 border-b-2 border-transparent"
onclick="switchEnrollMode('existing')">
Élève existant
</button>
</div>
<form id="enrollForm" method="post" action="{{ url_for('classes.enroll_student') }}">
<input type="hidden" name="class_id" value="{{ class_group.id }}">
<input type="hidden" name="mode" id="enrollMode" value="new">
{# Mode élève existant #}
<div id="existingStudentFields" class="hidden">
<div class="mb-4">
<label for="student_id" class="block text-sm font-medium text-gray-700 mb-2">Élève</label>
<select name="student_id" id="student_id"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="">Sélectionner un élève</option>
{% for student in available_students %}
<option value="{{ student.id }}">
{{ student.first_name }} {{ student.last_name }}
{% set current_class = student.get_current_class() %}
{% if current_class %}
(actuellement en {{ current_class.name }})
{% else %}
(non inscrit)
{% endif %}
</option>
{% endfor %}
</select>
</div>
</div>
{# Mode nouvel élève #}
<div id="newStudentFields">
<div class="grid grid-cols-2 gap-4 mb-4">
<div>
<label for="new_first_name" class="block text-sm font-medium text-gray-700 mb-2">Prénom</label>
<input type="text" name="new_first_name" id="new_first_name"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div>
<label for="new_last_name" class="block text-sm font-medium text-gray-700 mb-2">Nom</label>
<input type="text" name="new_last_name" id="new_last_name"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
</div>
<div class="mb-4">
<label for="new_email" class="block text-sm font-medium text-gray-700 mb-2">Email (optionnel)</label>
<input type="email" name="new_email" id="new_email"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
</div>
<div class="mb-4">
<label for="enrollment_date" class="block text-sm font-medium text-gray-700 mb-2">Date d'inscription</label>
<input type="date" name="enrollment_date" id="enrollment_date" required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div class="mb-6">
<label for="enrollment_reason" class="block text-sm font-medium text-gray-700 mb-2">Motif (optionnel)</label>
<input type="text" name="enrollment_reason" id="enrollment_reason"
placeholder="Ex: Nouvelle inscription, Transfert d'établissement..."
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div class="flex justify-end space-x-3">
<button type="button" onclick="closeEnrollModal()"
class="px-4 py-2 text-gray-600 hover:text-gray-800 transition-colors">
Annuler
</button>
<button type="submit"
class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-md transition-colors">
Inscrire
</button>
</div>
</form>
</div>
</div>
{# Modal de transfert #}
<div id="transferModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden items-center justify-center z-50">
<div class="bg-white rounded-lg p-6 max-w-md w-full mx-4">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold">Transférer <span id="transferStudentName"></span></h3>
<button type="button" onclick="closeTransferModal()"
class="text-gray-400 hover:text-gray-600 transition-colors">
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
</svg>
</button>
</div>
<form id="transferForm" method="post" action="{{ url_for('classes.transfer_student') }}">
<input type="hidden" name="student_id" id="transferStudentId">
<div class="mb-4">
<label for="new_class_id" class="block text-sm font-medium text-gray-700 mb-2">Nouvelle classe</label>
<select name="new_class_id" id="new_class_id" required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="">Sélectionner une classe</option>
{% for class in other_classes %}
<option value="{{ class.id }}">{{ class.name }}</option>
{% endfor %}
</select>
</div>
<div class="mb-4">
<label for="transfer_date" class="block text-sm font-medium text-gray-700 mb-2">Date de transfert</label>
<input type="date" name="transfer_date" id="transfer_date" required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div class="mb-6">
<label for="transfer_reason" class="block text-sm font-medium text-gray-700 mb-2">Motif (optionnel)</label>
<input type="text" name="transfer_reason" id="transfer_reason"
placeholder="Ex: Changement de niveau, Réorientation..."
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div class="flex justify-end space-x-3">
<button type="button" onclick="closeTransferModal()"
class="px-4 py-2 text-gray-600 hover:text-gray-800 transition-colors">
Annuler
</button>
<button type="submit"
class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md transition-colors">
Transférer
</button>
</div>
</form>
</div>
</div>
{# Modal de départ #}
<div id="departureModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden items-center justify-center z-50">
<div class="bg-white rounded-lg p-6 max-w-md w-full mx-4">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold">Marquer le départ de <span id="departureStudentName"></span></h3>
<button type="button" onclick="closeDepartureModal()"
class="text-gray-400 hover:text-gray-600 transition-colors">
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
</svg>
</button>
</div>
<form id="departureForm" method="post" action="{{ url_for('classes.student_departure') }}">
<input type="hidden" name="student_id" id="departureStudentId">
<div class="mb-4">
<label for="departure_date" class="block text-sm font-medium text-gray-700 mb-2">Date de départ</label>
<input type="date" name="departure_date" id="departure_date" required
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div class="mb-6">
<label for="departure_reason" class="block text-sm font-medium text-gray-700 mb-2">Motif (optionnel)</label>
<input type="text" name="departure_reason" id="departure_reason"
placeholder="Ex: Fin de scolarité, Déménagement, Changement d'établissement..."
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div class="flex justify-end space-x-3">
<button type="button" onclick="closeDepartureModal()"
class="px-4 py-2 text-gray-600 hover:text-gray-800 transition-colors">
Annuler
</button>
<button type="submit"
class="bg-orange-600 hover:bg-orange-700 text-white px-4 py-2 rounded-md transition-colors">
Confirmer le départ
</button>
</div>
</form>
</div>
</div>
{# Modal de confirmation d'annulation de départ #}
<div id="cancelDepartureModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden items-center justify-center z-50">
<div class="bg-white rounded-lg p-6 max-w-md w-full mx-4">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold">Annuler le départ de <span id="cancelDepartureStudentName"></span></h3>
<button type="button" onclick="closeCancelDepartureModal()"
class="text-gray-400 hover:text-gray-600 transition-colors">
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
</svg>
</button>
</div>
<div class="mb-6">
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-yellow-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
</svg>
</div>
<div class="ml-3">
<h4 class="text-sm font-medium text-yellow-800">Attention</h4>
<p class="text-sm text-yellow-700 mt-1">
Cette action va réintégrer l'élève dans la classe et supprimer la date de départ.
L'élève redeviendra éligible pour les futures évaluations de cette classe.
</p>
</div>
</div>
</div>
</div>
<form id="cancelDepartureForm" method="post" action="{{ url_for('classes.cancel_departure') }}">
<input type="hidden" name="enrollment_id" id="cancelDepartureEnrollmentId">
<div class="flex justify-end space-x-3">
<button type="button" onclick="closeCancelDepartureModal()"
class="px-4 py-2 text-gray-600 hover:text-gray-800 transition-colors">
Annuler
</button>
<button type="submit"
class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-md transition-colors">
Confirmer l'annulation
</button>
</div>
</form>
</div>
</div>
<script>
// Initialiser la date d'aujourd'hui dans les champs de date
document.addEventListener('DOMContentLoaded', function() {
const today = new Date().toISOString().split('T')[0];
document.getElementById('enrollment_date').value = today;
document.getElementById('transfer_date').value = today;
document.getElementById('departure_date').value = today;
// Vérifier s'il y a eu un rechargement après inscription
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('reload')) {
// Supprimer le paramètre de l'URL pour éviter le rechargement en boucle
const newUrl = window.location.pathname;
window.history.replaceState({}, document.title, newUrl);
// Faire défiler vers le haut pour voir le message de succès
window.scrollTo(0, 0);
}
});
// Gestion des modals d'inscription
function openEnrollModal() {
document.getElementById('enrollModal').classList.remove('hidden');
document.getElementById('enrollModal').classList.add('flex');
// Réinitialiser au mode "nouvel élève" par défaut
switchEnrollMode('new');
}
function closeEnrollModal() {
document.getElementById('enrollModal').classList.add('hidden');
document.getElementById('enrollModal').classList.remove('flex');
// Réinitialiser le formulaire
document.getElementById('enrollForm').reset();
const today = new Date().toISOString().split('T')[0];
document.getElementById('enrollment_date').value = today;
}
// Gestion du changement de mode d'inscription
function switchEnrollMode(mode) {
const existingTab = document.getElementById('existingStudentTab');
const newTab = document.getElementById('newStudentTab');
const existingFields = document.getElementById('existingStudentFields');
const newFields = document.getElementById('newStudentFields');
const modeInput = document.getElementById('enrollMode');
if (mode === 'existing') {
// Activer l'onglet "Élève existant"
existingTab.classList.add('text-blue-600', 'border-blue-600');
existingTab.classList.remove('text-gray-500', 'border-transparent');
newTab.classList.add('text-gray-500', 'border-transparent');
newTab.classList.remove('text-blue-600', 'border-blue-600');
// Afficher les champs correspondants
existingFields.classList.remove('hidden');
newFields.classList.add('hidden');
// Mettre à jour le mode
modeInput.value = 'existing';
// Rendre student_id requis
document.getElementById('student_id').required = true;
document.getElementById('new_first_name').required = false;
document.getElementById('new_last_name').required = false;
} else {
// Activer l'onglet "Nouvel élève"
newTab.classList.add('text-blue-600', 'border-blue-600');
newTab.classList.remove('text-gray-500', 'border-transparent');
existingTab.classList.add('text-gray-500', 'border-transparent');
existingTab.classList.remove('text-blue-600', 'border-blue-600');
// Afficher les champs correspondants
newFields.classList.remove('hidden');
existingFields.classList.add('hidden');
// Mettre à jour le mode
modeInput.value = 'new';
// Rendre les champs du nouvel élève requis
document.getElementById('student_id').required = false;
document.getElementById('new_first_name').required = true;
document.getElementById('new_last_name').required = true;
}
}
// Gestion des modals de transfert
function transferStudent(studentId, studentName) {
document.getElementById('transferStudentId').value = studentId;
document.getElementById('transferStudentName').textContent = studentName;
document.getElementById('transferModal').classList.remove('hidden');
document.getElementById('transferModal').classList.add('flex');
}
function closeTransferModal() {
document.getElementById('transferModal').classList.add('hidden');
document.getElementById('transferModal').classList.remove('flex');
}
// Gestion des modals de départ
function departureStudent(studentId, studentName) {
document.getElementById('departureStudentId').value = studentId;
document.getElementById('departureStudentName').textContent = studentName;
document.getElementById('departureModal').classList.remove('hidden');
document.getElementById('departureModal').classList.add('flex');
}
function closeDepartureModal() {
document.getElementById('departureModal').classList.add('hidden');
document.getElementById('departureModal').classList.remove('flex');
}
// Gestion des modals d'annulation de départ
function cancelDeparture(enrollmentId, studentName) {
document.getElementById('cancelDepartureEnrollmentId').value = enrollmentId;
document.getElementById('cancelDepartureStudentName').textContent = studentName;
document.getElementById('cancelDepartureModal').classList.remove('hidden');
document.getElementById('cancelDepartureModal').classList.add('flex');
}
function closeCancelDepartureModal() {
document.getElementById('cancelDepartureModal').classList.add('hidden');
document.getElementById('cancelDepartureModal').classList.remove('flex');
}
// Fermer les modals en cliquant à l'extérieur
document.addEventListener('click', function(event) {
const modals = ['enrollModal', 'transferModal', 'departureModal', 'cancelDepartureModal'];
modals.forEach(modalId => {
const modal = document.getElementById(modalId);
if (event.target === modal) {
modal.classList.add('hidden');
modal.classList.remove('flex');
}
});
});
// Fermer les modals avec la touche Échap
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape') {
const modals = ['enrollModal', 'transferModal', 'departureModal', 'cancelDepartureModal'];
modals.forEach(modalId => {
const modal = document.getElementById(modalId);
if (modal && !modal.classList.contains('hidden')) {
modal.classList.add('hidden');
modal.classList.remove('flex');
}
});
}
});
</script>
{% endblock %}

View File

@@ -39,7 +39,7 @@
<div class="flex flex-col{% if not class.description %} mt-2{% endif %}">
<!-- Actions principales -->
<div class="grid grid-cols-2 gap-2 mb-3">
<a href="{{ url_for('students') }}?class_id={{ class.id }}"
<a href="{{ url_for('classes.students', id=class.id) }}"
class="bg-{{ year_config.accent }}-50 hover:bg-{{ year_config.accent }}-100 text-{{ year_config.accent }}-700 hover:text-{{ year_config.accent }}-900 px-3 py-2.5 rounded-lg text-xs font-medium transition-colors flex items-center justify-center space-x-2">
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<path d="M9 6a3 3 0 11-6 0 3 3 0 616 0zM17 6a3 3 0 11-6 0 3 3 0 616 0zM12.93 17c.046-.327.07-.66.07-1a6.97 6.97 0 00-1.5-4.33A5 5 0 0119 16v1h-6.07zM6 11a5 5 0 515 5v1H1v-1a5 5 0 515-5z"/>