mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
feat: Gestion des profils des enseignants / Visualisation d'une classe [#4]
This commit is contained in:
@ -1,4 +1,5 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
|
from GestionLogin.models import Profil
|
||||||
|
|
||||||
class Specialite(models.Model):
|
class Specialite(models.Model):
|
||||||
nom = models.CharField(max_length=100)
|
nom = models.CharField(max_length=100)
|
||||||
@ -13,6 +14,7 @@ class Enseignant(models.Model):
|
|||||||
prenom = models.CharField(max_length=100)
|
prenom = models.CharField(max_length=100)
|
||||||
mail = models.EmailField(unique=True)
|
mail = models.EmailField(unique=True)
|
||||||
specialite = models.ForeignKey(Specialite, on_delete=models.SET_NULL, null=True, blank=True, related_name='enseignants')
|
specialite = models.ForeignKey(Specialite, on_delete=models.SET_NULL, null=True, blank=True, related_name='enseignants')
|
||||||
|
profilAssocie = models.ForeignKey(Profil, on_delete=models.CASCADE, null=True, blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.nom} {self.prenom}"
|
return f"{self.nom} {self.prenom}"
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from .models import Enseignant, Specialite, Classe
|
from .models import Enseignant, Specialite, Classe
|
||||||
from N3wtSchool import settings
|
from GestionInscriptions.models import FicheInscription
|
||||||
|
from GestionInscriptions.serializers import EleveSerializer
|
||||||
|
from GestionLogin.serializers import ProfilSerializer
|
||||||
|
from GestionLogin.models import Profil
|
||||||
|
from N3wtSchool import settings, bdd
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
import pytz
|
import pytz
|
||||||
|
|
||||||
@ -23,10 +27,11 @@ class ClasseSerializer(serializers.ModelSerializer):
|
|||||||
dateCreation_formattee = serializers.SerializerMethodField()
|
dateCreation_formattee = serializers.SerializerMethodField()
|
||||||
enseignant_principal = serializers.SerializerMethodField()
|
enseignant_principal = serializers.SerializerMethodField()
|
||||||
enseignant_principal_id = serializers.PrimaryKeyRelatedField(queryset=Enseignant.objects.all(), source='enseignant_principal', write_only=False, read_only=False)
|
enseignant_principal_id = serializers.PrimaryKeyRelatedField(queryset=Enseignant.objects.all(), source='enseignant_principal', write_only=False, read_only=False)
|
||||||
|
eleves = serializers.SerializerMethodField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Classe
|
model = Classe
|
||||||
fields = ['id', 'nom_ambiance', 'tranche_age', 'nombre_eleves', 'langue_enseignement', 'specialites', 'specialites_ids', 'enseignant_principal', 'enseignant_principal_id', 'annee_scolaire', 'dateCreation', 'dateCreation_formattee']
|
fields = ['id', 'nom_ambiance', 'tranche_age', 'nombre_eleves', 'langue_enseignement', 'specialites', 'specialites_ids', 'enseignant_principal', 'enseignant_principal_id', 'annee_scolaire', 'dateCreation', 'dateCreation_formattee', 'eleves']
|
||||||
|
|
||||||
def get_enseignant_principal(self, obj):
|
def get_enseignant_principal(self, obj):
|
||||||
from .serializers import EnseignantDetailSerializer
|
from .serializers import EnseignantDetailSerializer
|
||||||
@ -59,22 +64,45 @@ class ClasseSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
return local_time.strftime("%d-%m-%Y %H:%M")
|
return local_time.strftime("%d-%m-%Y %H:%M")
|
||||||
|
|
||||||
|
def get_eleves(self, obj):
|
||||||
|
elevesList = obj.eleves.all()
|
||||||
|
filtered_eleves = []
|
||||||
|
for eleve in elevesList:
|
||||||
|
ficheInscription=bdd.getObject(FicheInscription, "eleve__id", eleve.id)
|
||||||
|
if ficheInscription.etat == ficheInscription.EtatDossierInscription.DI_VALIDE:
|
||||||
|
filtered_eleves.append(eleve)
|
||||||
|
return EleveSerializer(filtered_eleves, many=True, read_only=True).data
|
||||||
|
|
||||||
class EnseignantSerializer(serializers.ModelSerializer):
|
class EnseignantSerializer(serializers.ModelSerializer):
|
||||||
specialite = SpecialiteSerializer(read_only=True)
|
specialite = SpecialiteSerializer(read_only=True)
|
||||||
specialite_id = serializers.PrimaryKeyRelatedField(queryset=Specialite.objects.all(), source='specialite', write_only=False, read_only=False)
|
specialite_id = serializers.PrimaryKeyRelatedField(queryset=Specialite.objects.all(), source='specialite', write_only=False, read_only=False)
|
||||||
|
profilAssocie_id = serializers.PrimaryKeyRelatedField(queryset=Profil.objects.all(), source='profilAssocie', write_only=False, read_only=False)
|
||||||
classes_principal = ClasseSerializer(many=True, read_only=True)
|
classes_principal = ClasseSerializer(many=True, read_only=True)
|
||||||
|
profilAssocie = ProfilSerializer(read_only=True)
|
||||||
|
DroitLabel = serializers.SerializerMethodField()
|
||||||
|
DroitValue = serializers.SerializerMethodField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Enseignant
|
model = Enseignant
|
||||||
fields = ['id', 'nom', 'prenom', 'mail', 'specialite', 'specialite_id', 'classes_principal']
|
fields = ['id', 'nom', 'prenom', 'mail', 'specialite', 'specialite_id', 'classes_principal', 'profilAssocie', 'profilAssocie_id', 'DroitLabel', 'DroitValue']
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
specialite = validated_data.pop('specialite', None)
|
specialite = validated_data.pop('specialite', None)
|
||||||
|
profilAssocie = validated_data.pop('profilAssocie', None)
|
||||||
enseignant = Enseignant.objects.create(**validated_data)
|
enseignant = Enseignant.objects.create(**validated_data)
|
||||||
|
if specialite:
|
||||||
enseignant.specialite = specialite
|
enseignant.specialite = specialite
|
||||||
|
if profilAssocie:
|
||||||
|
enseignant.profilAssocie = profilAssocie
|
||||||
enseignant.save()
|
enseignant.save()
|
||||||
return enseignant
|
return enseignant
|
||||||
|
|
||||||
|
def get_DroitLabel(self, obj):
|
||||||
|
return obj.profilAssocie.get_droit_display() if obj.profilAssocie else None
|
||||||
|
|
||||||
|
def get_DroitValue(self, obj):
|
||||||
|
return obj.profilAssocie.droit if obj.profilAssocie else None
|
||||||
|
|
||||||
class EnseignantDetailSerializer(serializers.ModelSerializer):
|
class EnseignantDetailSerializer(serializers.ModelSerializer):
|
||||||
specialite = SpecialiteSerializer(read_only=True)
|
specialite = SpecialiteSerializer(read_only=True)
|
||||||
|
|
||||||
|
|||||||
@ -47,10 +47,15 @@ class EnseignantView(APIView):
|
|||||||
|
|
||||||
def delete(self, request, _id):
|
def delete(self, request, _id):
|
||||||
enseignant = bdd.getObject(_objectName=Enseignant, _columnName='id', _value=_id)
|
enseignant = bdd.getObject(_objectName=Enseignant, _columnName='id', _value=_id)
|
||||||
if enseignant != None:
|
if enseignant is not None:
|
||||||
|
if enseignant.profilAssocie:
|
||||||
|
print('Suppression du profil associé')
|
||||||
|
enseignant.profilAssocie.delete()
|
||||||
enseignant.delete()
|
enseignant.delete()
|
||||||
|
return JsonResponse({'message': 'La suppression de l\'enseignant a été effectuée avec succès'}, safe=False)
|
||||||
|
else:
|
||||||
|
return JsonResponse({'erreur': 'L\'enseignant n\'a pas été trouvé'}, safe=False)
|
||||||
|
|
||||||
return JsonResponse("La suppression de la spécialité a été effectuée avec succès", safe=False)
|
|
||||||
|
|
||||||
@method_decorator(csrf_protect, name='dispatch')
|
@method_decorator(csrf_protect, name='dispatch')
|
||||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||||
@ -175,6 +180,10 @@ class ClasseView(APIView):
|
|||||||
def delete(self, request, _id):
|
def delete(self, request, _id):
|
||||||
classe = bdd.getObject(_objectName=Classe, _columnName='id', _value=_id)
|
classe = bdd.getObject(_objectName=Classe, _columnName='id', _value=_id)
|
||||||
if classe != None:
|
if classe != None:
|
||||||
|
for eleve in classe.eleves.all():
|
||||||
|
print(f'eleve a retirer la classe : {eleve}')
|
||||||
|
eleve.classeAssociee = None
|
||||||
|
eleve.save()
|
||||||
classe.delete()
|
classe.delete()
|
||||||
|
|
||||||
return JsonResponse("La suppression de la classe a été effectuée avec succès", safe=False)
|
return JsonResponse("La suppression de la classe a été effectuée avec succès", safe=False)
|
||||||
@ -4,6 +4,27 @@ from django.conf import settings
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from GestionLogin.models import Profil
|
from GestionLogin.models import Profil
|
||||||
|
from GestionEnseignants.models import Classe
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
class FraisInscription(models.Model):
|
||||||
|
class OptionsPaiements(models.IntegerChoices):
|
||||||
|
PAIEMENT_1_FOIS = 0, _('Paiement en une seule fois')
|
||||||
|
PAIEMENT_MENSUEL = 1, _('Paiement mensuel')
|
||||||
|
PAIEMENT_TRIMESTRIEL = 2, _('Paiement trimestriel')
|
||||||
|
|
||||||
|
nom = models.CharField(max_length=255, unique=True)
|
||||||
|
description = models.TextField(blank=True)
|
||||||
|
montant_de_base = models.DecimalField(max_digits=10, decimal_places=2)
|
||||||
|
reductions = models.JSONField(blank=True, null=True)
|
||||||
|
supplements = models.JSONField(blank=True, null=True)
|
||||||
|
date_debut_validite = models.DateField()
|
||||||
|
date_fin_validite = models.DateField()
|
||||||
|
options_paiement = models.IntegerField(choices=OptionsPaiements, default=OptionsPaiements.PAIEMENT_1_FOIS)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.nom
|
||||||
|
|
||||||
class Langue(models.Model):
|
class Langue(models.Model):
|
||||||
id = models.AutoField(primary_key=True)
|
id = models.AutoField(primary_key=True)
|
||||||
@ -59,7 +80,7 @@ class Eleve(models.Model):
|
|||||||
niveau = models.IntegerField(choices=NiveauEleve, default=NiveauEleve.NONE, blank=True)
|
niveau = models.IntegerField(choices=NiveauEleve, default=NiveauEleve.NONE, blank=True)
|
||||||
nationalite = models.CharField(max_length=200, default="", blank=True)
|
nationalite = models.CharField(max_length=200, default="", blank=True)
|
||||||
adresse = models.CharField(max_length=200, default="", blank=True)
|
adresse = models.CharField(max_length=200, default="", blank=True)
|
||||||
dateNaissance = models.CharField(max_length=200, default="", blank=True)
|
dateNaissance = models.DateField(null=True, blank=True)
|
||||||
lieuNaissance = models.CharField(max_length=200, default="", blank=True)
|
lieuNaissance = models.CharField(max_length=200, default="", blank=True)
|
||||||
codePostalNaissance = models.IntegerField(default=0, blank=True)
|
codePostalNaissance = models.IntegerField(default=0, blank=True)
|
||||||
medecinTraitant = models.CharField(max_length=200, default="", blank=True)
|
medecinTraitant = models.CharField(max_length=200, default="", blank=True)
|
||||||
@ -77,6 +98,9 @@ class Eleve(models.Model):
|
|||||||
# Relation N-N
|
# Relation N-N
|
||||||
languesParlees = models.ManyToManyField(Langue, blank=True)
|
languesParlees = models.ManyToManyField(Langue, blank=True)
|
||||||
|
|
||||||
|
# Relation 1-N
|
||||||
|
classeAssociee = models.ForeignKey(Classe, on_delete=models.SET_NULL, null=True, blank=True, related_name='eleves')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.nom + "_" + self.prenom
|
return self.nom + "_" + self.prenom
|
||||||
|
|
||||||
@ -98,6 +122,31 @@ class Eleve(models.Model):
|
|||||||
def getNbFreres(self):
|
def getNbFreres(self):
|
||||||
return self.freres.count()
|
return self.freres.count()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def age(self):
|
||||||
|
if self.dateNaissance:
|
||||||
|
today = datetime.today()
|
||||||
|
years = today.year - self.dateNaissance.year
|
||||||
|
months = today.month - self.dateNaissance.month
|
||||||
|
if today.day < self.dateNaissance.day:
|
||||||
|
months -= 1
|
||||||
|
if months < 0:
|
||||||
|
years -= 1
|
||||||
|
months += 12
|
||||||
|
|
||||||
|
# Déterminer le format de l'âge
|
||||||
|
if months >= 6 and months <= 12:
|
||||||
|
return f"{years} ans 1/2"
|
||||||
|
else:
|
||||||
|
return f"{years} ans"
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dateNaissance_formattee(self):
|
||||||
|
if self.dateNaissance:
|
||||||
|
return self.dateNaissance.strftime('%d-%m-%Y')
|
||||||
|
return None
|
||||||
|
|
||||||
class FicheInscription(models.Model):
|
class FicheInscription(models.Model):
|
||||||
|
|
||||||
class EtatDossierInscription(models.IntegerChoices):
|
class EtatDossierInscription(models.IntegerChoices):
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from GestionInscriptions.models import FicheInscription, Eleve, Responsable, Frere, Langue
|
from GestionInscriptions.models import FicheInscription, Eleve, Responsable, Frere, Langue, FraisInscription
|
||||||
|
from GestionEnseignants.models import Classe
|
||||||
from GestionLogin.models import Profil
|
from GestionLogin.models import Profil
|
||||||
from GestionLogin.serializers import ProfilSerializer
|
from GestionLogin.serializers import ProfilSerializer
|
||||||
from GestionMessagerie.models import Messagerie
|
from GestionMessagerie.models import Messagerie
|
||||||
@ -7,6 +8,13 @@ from GestionNotification.models import Notification
|
|||||||
from N3wtSchool import settings
|
from N3wtSchool import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
import pytz
|
import pytz
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
class FraisInscriptionSerializer(serializers.ModelSerializer):
|
||||||
|
id = serializers.IntegerField(required=False)
|
||||||
|
class Meta:
|
||||||
|
model = FraisInscription
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
class LanguesSerializer(serializers.ModelSerializer):
|
class LanguesSerializer(serializers.ModelSerializer):
|
||||||
id = serializers.IntegerField(required=False)
|
id = serializers.IntegerField(required=False)
|
||||||
@ -35,6 +43,12 @@ class EleveSerializer(serializers.ModelSerializer):
|
|||||||
responsables = ResponsableSerializer(many=True, required=False)
|
responsables = ResponsableSerializer(many=True, required=False)
|
||||||
freres = FrereSerializer(many=True, required=False)
|
freres = FrereSerializer(many=True, required=False)
|
||||||
langues = LanguesSerializer(many=True, required=False)
|
langues = LanguesSerializer(many=True, required=False)
|
||||||
|
classeAssocie_id = serializers.PrimaryKeyRelatedField(queryset=Classe.objects.all(), source='classeAssociee', required=False, write_only=False, read_only=False)
|
||||||
|
age = serializers.SerializerMethodField()
|
||||||
|
dateNaissance_formattee = serializers.SerializerMethodField()
|
||||||
|
dateNaissance = serializers.DateField(input_formats=['%d-%m-%Y', '%Y-%m-%d'], required=False, allow_null=True)
|
||||||
|
classeAssocieeName = serializers.SerializerMethodField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Eleve
|
model = Eleve
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
@ -89,6 +103,15 @@ class EleveSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
def get_age(self, obj):
|
||||||
|
return obj.age
|
||||||
|
|
||||||
|
def get_dateNaissance_formattee(self, obj):
|
||||||
|
return obj.dateNaissance_formattee
|
||||||
|
|
||||||
|
def get_classeAssocieeName(self, obj):
|
||||||
|
return obj.classeAssociee.nom_ambiance if obj.classeAssociee else None
|
||||||
|
|
||||||
class FicheInscriptionSerializer(serializers.ModelSerializer):
|
class FicheInscriptionSerializer(serializers.ModelSerializer):
|
||||||
eleve = EleveSerializer(many=False, required=True)
|
eleve = EleveSerializer(many=False, required=True)
|
||||||
fichierInscription = serializers.FileField(required=False)
|
fichierInscription = serializers.FileField(required=False)
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
from django.urls import path, re_path
|
from django.urls import path, re_path
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
from GestionInscriptions.views import ListFichesInscriptionView, FicheInscriptionView, EleveView, ResponsableView, ListeEnfantsView, ListeElevesView
|
from GestionInscriptions.views import ListFichesInscriptionView, FicheInscriptionView, EleveView, ResponsableView, ListeEnfantsView, ListeElevesView, FraisInscriptionView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
re_path(r'^fichesInscription/([a-zA-z]+)$', ListFichesInscriptionView.as_view(), name="listefichesInscriptions"),
|
re_path(r'^fichesInscription/([a-zA-z]+)$', ListFichesInscriptionView.as_view(), name="listefichesInscriptions"),
|
||||||
@ -28,4 +28,7 @@ urlpatterns = [
|
|||||||
|
|
||||||
# Page INSCRIPTION - Liste des élèves
|
# Page INSCRIPTION - Liste des élèves
|
||||||
re_path(r'^eleves$', ListeElevesView.as_view(), name="enfants"),
|
re_path(r'^eleves$', ListeElevesView.as_view(), name="enfants"),
|
||||||
|
|
||||||
|
# Frais d'inscription
|
||||||
|
re_path(r'^tarifsInscription$', FraisInscriptionView.as_view(), name="fraisInscription"),
|
||||||
]
|
]
|
||||||
@ -179,3 +179,23 @@ def getArgFromRequest(_argument, _request):
|
|||||||
data=JSONParser().parse(_request)
|
data=JSONParser().parse(_request)
|
||||||
resultat = data[_argument]
|
resultat = data[_argument]
|
||||||
return resultat
|
return resultat
|
||||||
|
|
||||||
|
def diToPDF(ficheEleve):
|
||||||
|
# Ajout du fichier d'inscriptions
|
||||||
|
data = {
|
||||||
|
'pdf_title': "Dossier d'inscription de %s"%ficheEleve.eleve.prenom,
|
||||||
|
'dateSignature': convertToStr(_now(), '%d-%m-%Y'),
|
||||||
|
'heureSignature': convertToStr(_now(), '%H:%M'),
|
||||||
|
'eleve':ficheEleve.eleve,
|
||||||
|
}
|
||||||
|
|
||||||
|
pdf = renderers.render_to_pdf('pdfs/dossier_inscription.html', data)
|
||||||
|
|
||||||
|
nomFichierPDF = "Dossier_Inscription_%s_%s.pdf"%(ficheEleve.eleve.nom, ficheEleve.eleve.prenom)
|
||||||
|
pathFichier = Path(settings.DOCUMENT_DIR + "/" + nomFichierPDF)
|
||||||
|
if os.path.exists(str(pathFichier)):
|
||||||
|
print(f'File exists : {str(pathFichier)}')
|
||||||
|
os.remove(str(pathFichier))
|
||||||
|
|
||||||
|
receipt_file = BytesIO(pdf.content)
|
||||||
|
ficheEleve.fichierInscription = File(receipt_file, nomFichierPDF)
|
||||||
@ -17,10 +17,10 @@ from io import BytesIO
|
|||||||
|
|
||||||
import GestionInscriptions.mailManager as mailer
|
import GestionInscriptions.mailManager as mailer
|
||||||
import GestionInscriptions.util as util
|
import GestionInscriptions.util as util
|
||||||
from GestionInscriptions.serializers import FicheInscriptionSerializer, EleveSerializer, FicheInscriptionByParentSerializer, EleveByDICreationSerializer
|
from GestionInscriptions.serializers import FicheInscriptionSerializer, EleveSerializer, FicheInscriptionByParentSerializer, EleveByDICreationSerializer, FraisInscriptionSerializer
|
||||||
from GestionInscriptions.pagination import CustomPagination
|
from GestionInscriptions.pagination import CustomPagination
|
||||||
from GestionInscriptions.signals import clear_cache
|
from GestionInscriptions.signals import clear_cache
|
||||||
from .models import Eleve, Responsable, FicheInscription
|
from .models import Eleve, Responsable, FicheInscription, FraisInscription
|
||||||
from GestionInscriptions.automate import Automate_DI_Inscription, load_config, getStateMachineObjectState, updateStateMachine
|
from GestionInscriptions.automate import Automate_DI_Inscription, load_config, getStateMachineObjectState, updateStateMachine
|
||||||
|
|
||||||
from GestionLogin.models import Profil
|
from GestionLogin.models import Profil
|
||||||
@ -30,8 +30,23 @@ from N3wtSchool import settings, renderers, bdd
|
|||||||
class ListFichesInscriptionView(APIView):
|
class ListFichesInscriptionView(APIView):
|
||||||
pagination_class = CustomPagination
|
pagination_class = CustomPagination
|
||||||
|
|
||||||
|
def get_fiche_inscriptions(self, _filter, search=None):
|
||||||
|
"""
|
||||||
|
Récupère les fiches d'inscriptions en fonction du filtre passé.
|
||||||
|
_filter: Filtre pour déterminer l'état des fiches ('pending', 'archived', 'subscribed')
|
||||||
|
search: Terme de recherche (optionnel)
|
||||||
|
"""
|
||||||
|
if _filter == 'pending':
|
||||||
|
exclude_states = [FicheInscription.EtatDossierInscription.DI_VALIDE, FicheInscription.EtatDossierInscription.DI_ARCHIVE]
|
||||||
|
return bdd.searchObjects(FicheInscription, search, _excludeStates=exclude_states)
|
||||||
|
elif _filter == 'archived':
|
||||||
|
return bdd.getObjects(FicheInscription, 'etat', FicheInscription.EtatDossierInscription.DI_ARCHIVE)
|
||||||
|
elif _filter == 'subscribed':
|
||||||
|
return bdd.getObjects(FicheInscription, 'etat', FicheInscription.EtatDossierInscription.DI_VALIDE)
|
||||||
|
return None
|
||||||
|
|
||||||
def get(self, request, _filter):
|
def get(self, request, _filter):
|
||||||
if _filter == 'all':
|
|
||||||
# Récupération des paramètres
|
# Récupération des paramètres
|
||||||
search = request.GET.get('search', '').strip()
|
search = request.GET.get('search', '').strip()
|
||||||
page_size = request.GET.get('page_size', None)
|
page_size = request.GET.get('page_size', None)
|
||||||
@ -43,29 +58,18 @@ class ListFichesInscriptionView(APIView):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
page_size = settings.NB_RESULT_PER_PAGE
|
page_size = settings.NB_RESULT_PER_PAGE
|
||||||
|
|
||||||
cached_page_size = cache.get('N3WT_page_size')
|
# Définir le cache_key en fonction du filtre
|
||||||
if cached_page_size != page_size:
|
|
||||||
clear_cache()
|
|
||||||
cache.set('N3WT_page_size', page_size)
|
|
||||||
|
|
||||||
# Gestion du cache
|
|
||||||
page_number = request.GET.get('page', 1)
|
page_number = request.GET.get('page', 1)
|
||||||
cache_key = f'N3WT_ficheInscriptions_page_{page_number}_search_{search}'
|
cache_key = f'N3WT_ficheInscriptions_{_filter}_page_{page_number}_search_{search if _filter == "pending" else ""}'
|
||||||
cached_page = cache.get(cache_key)
|
cached_page = cache.get(cache_key)
|
||||||
if cached_page:
|
if cached_page:
|
||||||
return JsonResponse(cached_page, safe=False)
|
return JsonResponse(cached_page, safe=False)
|
||||||
|
|
||||||
# Filtrage des résultats
|
# Récupérer les fiches d'inscriptions en fonction du filtre
|
||||||
if search:
|
ficheInscriptions_List = self.get_fiche_inscriptions(_filter, search)
|
||||||
# Utiliser la nouvelle fonction de recherche
|
|
||||||
ficheInscriptions_List = bdd.searchObjects(
|
if not ficheInscriptions_List:
|
||||||
FicheInscription,
|
return JsonResponse({'error' : 'aucune donnée trouvée', 'count' :0}, safe=False)
|
||||||
search,
|
|
||||||
_excludeState=6 # Exclure les fiches archivées
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# Récupère toutes les fiches non archivées
|
|
||||||
ficheInscriptions_List = bdd.getObjects(FicheInscription, 'etat', 6, _reverseCondition=True)
|
|
||||||
|
|
||||||
# Pagination
|
# Pagination
|
||||||
paginator = self.pagination_class()
|
paginator = self.pagination_class()
|
||||||
@ -76,39 +80,7 @@ class ListFichesInscriptionView(APIView):
|
|||||||
cache.set(cache_key, response_data, timeout=60*15)
|
cache.set(cache_key, response_data, timeout=60*15)
|
||||||
return JsonResponse(response_data, safe=False)
|
return JsonResponse(response_data, safe=False)
|
||||||
|
|
||||||
elif _filter == 'archived' :
|
return JsonResponse({'error' : 'aucune donnée trouvée', 'count' :0}, safe=False)
|
||||||
page_size = request.GET.get('page_size', None)
|
|
||||||
if page_size is not None:
|
|
||||||
try:
|
|
||||||
page_size = int(page_size)
|
|
||||||
except ValueError:
|
|
||||||
page_size = settings.NB_RESULT_PER_PAGE
|
|
||||||
|
|
||||||
cached_page_size = cache.get('N3WT_archived_page_size')
|
|
||||||
|
|
||||||
# Comparer avec le nouveau page_size
|
|
||||||
if cached_page_size != page_size:
|
|
||||||
# Appeler cached_page() et mettre à jour le cache
|
|
||||||
clear_cache()
|
|
||||||
cache.set('N3WT_archived_page_size',page_size)
|
|
||||||
|
|
||||||
page_number = request.GET.get('page', 1)
|
|
||||||
cache_key_page = f'N3WT_ficheInscriptions_archives_page_{page_number}'
|
|
||||||
cached_page = cache.get(cache_key_page)
|
|
||||||
if cached_page:
|
|
||||||
return JsonResponse(cached_page, safe=False)
|
|
||||||
|
|
||||||
ficheInscriptions_List=bdd.getObjects(FicheInscription, 'etat', 6)
|
|
||||||
paginator = self.pagination_class()
|
|
||||||
page = paginator.paginate_queryset(ficheInscriptions_List, request)
|
|
||||||
if page is not None:
|
|
||||||
ficheInscriptions_serializer = FicheInscriptionSerializer(page, many=True)
|
|
||||||
response_data = paginator.get_paginated_response(ficheInscriptions_serializer.data)
|
|
||||||
cache.set(cache_key_page, response_data, timeout=60*15)
|
|
||||||
|
|
||||||
return JsonResponse(response_data, safe=False)
|
|
||||||
|
|
||||||
return JsonResponse(status=status.HTTP_404_NOT_FOUND)
|
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
fichesEleve_data=JSONParser().parse(request)
|
fichesEleve_data=JSONParser().parse(request)
|
||||||
@ -168,39 +140,28 @@ class FicheInscriptionView(APIView):
|
|||||||
|
|
||||||
def put(self, request, id):
|
def put(self, request, id):
|
||||||
ficheEleve_data=JSONParser().parse(request)
|
ficheEleve_data=JSONParser().parse(request)
|
||||||
admin = ficheEleve_data.pop('admin', 1)
|
etat = ficheEleve_data.pop('etat', 0)
|
||||||
ficheEleve_data["dateMAJ"] = str(util.convertToStr(util._now(), '%d-%m-%Y %H:%M'))
|
ficheEleve_data["dateMAJ"] = str(util.convertToStr(util._now(), '%d-%m-%Y %H:%M'))
|
||||||
ficheEleve = bdd.getObject(_objectName=FicheInscription, _columnName='eleve__id', _value=id)
|
ficheEleve = bdd.getObject(_objectName=FicheInscription, _columnName='eleve__id', _value=id)
|
||||||
currentState = getStateMachineObjectState(ficheEleve.etat)
|
|
||||||
if admin == 0 and currentState == FicheInscription.EtatDossierInscription.DI_ENVOYE:
|
if etat == FicheInscription.EtatDossierInscription.DI_EN_VALIDATION:
|
||||||
|
# Le parent a complété le dossier d'inscription, il est soumis à validation par l'école
|
||||||
|
print('EN VALIDATION')
|
||||||
json.dumps(ficheEleve_data)
|
json.dumps(ficheEleve_data)
|
||||||
|
util.diToPDF(ficheEleve)
|
||||||
# Ajout du fichier d'inscriptions
|
|
||||||
data = {
|
|
||||||
'pdf_title': "Dossier d'inscription de %s"%ficheEleve.eleve.prenom,
|
|
||||||
'dateSignature': util.convertToStr(util._now(), '%d-%m-%Y'),
|
|
||||||
'heureSignature': util.convertToStr(util._now(), '%H:%M'),
|
|
||||||
'eleve':ficheEleve.eleve,
|
|
||||||
}
|
|
||||||
|
|
||||||
pdf = renderers.render_to_pdf('pdfs/dossier_inscription.html', data)
|
|
||||||
|
|
||||||
nomFichierPDF = "Dossier_Inscription_%s_%s.pdf"%(ficheEleve.eleve.nom, ficheEleve.eleve.prenom)
|
|
||||||
pathFichier = Path(settings.DOCUMENT_DIR + "/" + nomFichierPDF)
|
|
||||||
if os.path.exists(str(pathFichier)):
|
|
||||||
print(f'File exists : {str(pathFichier)}')
|
|
||||||
os.remove(str(pathFichier))
|
|
||||||
|
|
||||||
receipt_file = BytesIO(pdf.content)
|
|
||||||
ficheEleve.fichierInscription = File(receipt_file, nomFichierPDF)
|
|
||||||
|
|
||||||
# Mise à jour de l'automate
|
# Mise à jour de l'automate
|
||||||
updateStateMachine(di, 'saisiDI')
|
updateStateMachine(ficheEleve, 'saisiDI')
|
||||||
|
elif etat == FicheInscription.EtatDossierInscription.DI_VALIDE:
|
||||||
|
# L'école a validé le dossier d'inscription
|
||||||
|
# Mise à jour de l'automate
|
||||||
|
print('VALIDATION')
|
||||||
|
updateStateMachine(ficheEleve, 'valideDI')
|
||||||
|
|
||||||
|
|
||||||
ficheEleve_serializer = FicheInscriptionSerializer(ficheEleve, data=ficheEleve_data)
|
ficheEleve_serializer = FicheInscriptionSerializer(ficheEleve, data=ficheEleve_data)
|
||||||
if ficheEleve_serializer.is_valid():
|
if ficheEleve_serializer.is_valid():
|
||||||
di = ficheEleve_serializer.save()
|
ficheEleve_serializer.save()
|
||||||
return JsonResponse("Updated Successfully", safe=False)
|
return JsonResponse(ficheEleve_serializer.data, safe=False)
|
||||||
|
|
||||||
return JsonResponse(ficheEleve_serializer.errors, safe=False)
|
return JsonResponse(ficheEleve_serializer.errors, safe=False)
|
||||||
|
|
||||||
@ -287,3 +248,10 @@ class ListeElevesView(APIView):
|
|||||||
students = bdd.getAllObjects(_objectName=Eleve)
|
students = bdd.getAllObjects(_objectName=Eleve)
|
||||||
students_serializer = EleveByDICreationSerializer(students, many=True)
|
students_serializer = EleveByDICreationSerializer(students, many=True)
|
||||||
return JsonResponse(students_serializer.data, safe=False)
|
return JsonResponse(students_serializer.data, safe=False)
|
||||||
|
|
||||||
|
# API utilisée pour la vue de personnalisation des frais d'inscription pour la structure
|
||||||
|
class FraisInscriptionView(APIView):
|
||||||
|
def get(self, request):
|
||||||
|
tarifs = bdd.getAllObjects(FraisInscription)
|
||||||
|
tarifs_serializer = FraisInscriptionSerializer(tarifs, many=True)
|
||||||
|
return JsonResponse(tarifs_serializer.data, safe=False)
|
||||||
|
|||||||
@ -5,10 +5,10 @@ from django.core.validators import EmailValidator
|
|||||||
|
|
||||||
class Profil(AbstractUser):
|
class Profil(AbstractUser):
|
||||||
class Droits(models.IntegerChoices):
|
class Droits(models.IntegerChoices):
|
||||||
PROFIL_UNDEFINED = -1, _('Profil non défini')
|
PROFIL_UNDEFINED = -1, _('NON DEFINI')
|
||||||
PROFIL_ECOLE = 0, _('Profil école')
|
PROFIL_ECOLE = 0, _('ECOLE')
|
||||||
PROFIL_PARENT = 1, _('Profil parent')
|
PROFIL_PARENT = 1, _('PARENT')
|
||||||
PROFIL_ADMIN = 2, _('Profil administrateur')
|
PROFIL_ADMIN = 2, _('ADMIN')
|
||||||
|
|
||||||
email = models.EmailField(max_length=255, unique=True, default="", validators=[EmailValidator()])
|
email = models.EmailField(max_length=255, unique=True, default="", validators=[EmailValidator()])
|
||||||
|
|
||||||
|
|||||||
@ -26,3 +26,26 @@ class ProfilSerializer(serializers.ModelSerializer):
|
|||||||
ret = super().to_representation(instance)
|
ret = super().to_representation(instance)
|
||||||
ret['password'] = '********'
|
ret['password'] = '********'
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
class ProfilUpdateSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Profil
|
||||||
|
fields = ['id', 'password', 'email', 'code', 'datePeremption', 'estConnecte', 'droit', 'username', 'is_active']
|
||||||
|
extra_kwargs = {
|
||||||
|
'password': {'write_only': True, 'required': False}
|
||||||
|
}
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
password = validated_data.pop('password', None)
|
||||||
|
instance = super().update(instance, validated_data)
|
||||||
|
|
||||||
|
if password:
|
||||||
|
instance.set_password(password)
|
||||||
|
instance.save()
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
|
def to_representation(self, instance):
|
||||||
|
ret = super().to_representation(instance)
|
||||||
|
ret['password'] = '********'
|
||||||
|
return ret
|
||||||
|
|||||||
@ -17,8 +17,8 @@ import json
|
|||||||
from . import validator
|
from . import validator
|
||||||
from .models import Profil
|
from .models import Profil
|
||||||
|
|
||||||
|
from GestionLogin.serializers import ProfilSerializer, ProfilUpdateSerializer
|
||||||
from GestionInscriptions.models import FicheInscription
|
from GestionInscriptions.models import FicheInscription
|
||||||
from GestionInscriptions.serializers import ProfilSerializer
|
|
||||||
from GestionInscriptions.signals import clear_cache
|
from GestionInscriptions.signals import clear_cache
|
||||||
import GestionInscriptions.mailManager as mailer
|
import GestionInscriptions.mailManager as mailer
|
||||||
import GestionInscriptions.util as util
|
import GestionInscriptions.util as util
|
||||||
@ -83,7 +83,7 @@ class ProfilView(APIView):
|
|||||||
def put(self, request, _id):
|
def put(self, request, _id):
|
||||||
data=JSONParser().parse(request)
|
data=JSONParser().parse(request)
|
||||||
profil = Profil.objects.get(id=_id)
|
profil = Profil.objects.get(id=_id)
|
||||||
profil_serializer = ProfilSerializer(profil, data=data)
|
profil_serializer = ProfilUpdateSerializer(profil, data=data)
|
||||||
if profil_serializer.is_valid():
|
if profil_serializer.is_valid():
|
||||||
profil_serializer.save()
|
profil_serializer.save()
|
||||||
return JsonResponse("Updated Successfully", safe=False)
|
return JsonResponse("Updated Successfully", safe=False)
|
||||||
@ -143,6 +143,7 @@ class LoginView(APIView):
|
|||||||
'errorFields':errorFields,
|
'errorFields':errorFields,
|
||||||
'errorMessage':retour,
|
'errorMessage':retour,
|
||||||
'profil':user.id if user else -1,
|
'profil':user.id if user else -1,
|
||||||
|
'droit':user.droit if user else -1,
|
||||||
#'jwtToken':jwt_token if profil != -1 else ''
|
#'jwtToken':jwt_token if profil != -1 else ''
|
||||||
}, safe=False)
|
}, safe=False)
|
||||||
|
|
||||||
|
|||||||
@ -56,19 +56,19 @@ def getLastId(_object):
|
|||||||
logging.warning("Aucun résultat n'a été trouvé - ")
|
logging.warning("Aucun résultat n'a été trouvé - ")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def searchObjects(_objectName, _searchTerm, _excludeState=None):
|
def searchObjects(_objectName, _searchTerm=None, _excludeStates=None):
|
||||||
"""
|
"""
|
||||||
Recherche générique sur les objets avec possibilité d'exclure certains états
|
Recherche générique sur les objets avec possibilité d'exclure certains états
|
||||||
_objectName: Classe du modèle
|
_objectName: Classe du modèle
|
||||||
_searchTerm: Terme de recherche
|
_searchTerm: Terme de recherche
|
||||||
_excludeState: État à exclure de la recherche (optionnel)
|
_excludeStates: Liste d'état à exclure de la recherche (optionnel)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
query = _objectName.objects.all()
|
query = _objectName.objects.all()
|
||||||
|
|
||||||
# Si on a un état à exclure
|
# Si on a un état à exclure
|
||||||
if _excludeState is not None:
|
if _excludeStates is not None:
|
||||||
query = query.filter(etat__lt=_excludeState)
|
query = query.exclude(etat__in=_excludeStates)
|
||||||
|
|
||||||
# Si on a un terme de recherche
|
# Si on a un terme de recherche
|
||||||
if _searchTerm and _searchTerm.strip():
|
if _searchTerm and _searchTerm.strip():
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
"addStudent": "New",
|
"addStudent": "New",
|
||||||
"allStudents": "All Students",
|
"allStudents": "All Students",
|
||||||
"pending": "Pending Registrations",
|
"pending": "Pending Registrations",
|
||||||
|
"subscribed": "Subscribed",
|
||||||
"archived": "Archived",
|
"archived": "Archived",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"class": "Class",
|
"class": "Class",
|
||||||
@ -25,6 +26,7 @@
|
|||||||
"mainContactMail":"Main contact email",
|
"mainContactMail":"Main contact email",
|
||||||
"phone":"Phone",
|
"phone":"Phone",
|
||||||
"lastUpdateDate":"Last update",
|
"lastUpdateDate":"Last update",
|
||||||
|
"classe":"Class",
|
||||||
"registrationFileStatus":"Registration file status",
|
"registrationFileStatus":"Registration file status",
|
||||||
"files":"Files"
|
"files":"Files"
|
||||||
}
|
}
|
||||||
@ -3,6 +3,7 @@
|
|||||||
"addStudent": "Nouveau",
|
"addStudent": "Nouveau",
|
||||||
"allStudents": "Tous les élèves",
|
"allStudents": "Tous les élèves",
|
||||||
"pending": "Inscriptions en attente",
|
"pending": "Inscriptions en attente",
|
||||||
|
"subscribed": "Inscrits",
|
||||||
"archived": "Archivés",
|
"archived": "Archivés",
|
||||||
"name": "Nom",
|
"name": "Nom",
|
||||||
"class": "Classe",
|
"class": "Classe",
|
||||||
@ -25,6 +26,7 @@
|
|||||||
"mainContactMail":"Email de contact principal",
|
"mainContactMail":"Email de contact principal",
|
||||||
"phone":"Téléphone",
|
"phone":"Téléphone",
|
||||||
"lastUpdateDate":"Dernière mise à jour",
|
"lastUpdateDate":"Dernière mise à jour",
|
||||||
|
"classe":"Classe",
|
||||||
"registrationFileStatus":"État du dossier d'inscription",
|
"registrationFileStatus":"État du dossier d'inscription",
|
||||||
"files":"Fichiers"
|
"files":"Fichiers"
|
||||||
}
|
}
|
||||||
@ -4,6 +4,8 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { Users, Clock, CalendarCheck, School, TrendingUp, UserCheck } from 'lucide-react';
|
import { Users, Clock, CalendarCheck, School, TrendingUp, UserCheck } from 'lucide-react';
|
||||||
import Loader from '@/components/Loader';
|
import Loader from '@/components/Loader';
|
||||||
|
import { BK_GESTIONINSCRIPTION_CLASSES_URL } from '@/utils/Url';
|
||||||
|
import ClasseDetails from '@/components/ClasseDetails';
|
||||||
|
|
||||||
// Composant StatCard pour afficher une statistique
|
// Composant StatCard pour afficher une statistique
|
||||||
const StatCard = ({ title, value, icon, change, color = "blue" }) => (
|
const StatCard = ({ title, value, icon, change, color = "blue" }) => (
|
||||||
@ -54,7 +56,23 @@ export default function DashboardPage() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [classes, setClasses] = useState([]);
|
||||||
|
|
||||||
|
const fetchClasses = () => {
|
||||||
|
fetch(`${BK_GESTIONINSCRIPTION_CLASSES_URL}`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
setClasses(data);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error fetching classes:', error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// Fetch data for classes
|
||||||
|
fetchClasses();
|
||||||
|
|
||||||
// Simulation de chargement des données
|
// Simulation de chargement des données
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setStats({
|
setStats({
|
||||||
@ -120,7 +138,7 @@ export default function DashboardPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Événements et KPIs */}
|
{/* Événements et KPIs */}
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6">
|
||||||
{/* Graphique des inscriptions */}
|
{/* Graphique des inscriptions */}
|
||||||
<div className="lg:col-span-2 bg-white p-6 rounded-lg shadow-sm border border-gray-100">
|
<div className="lg:col-span-2 bg-white p-6 rounded-lg shadow-sm border border-gray-100">
|
||||||
<h2 className="text-lg font-semibold mb-4">{t('inscriptionTrends')}</h2>
|
<h2 className="text-lg font-semibold mb-4">{t('inscriptionTrends')}</h2>
|
||||||
@ -138,6 +156,14 @@ export default function DashboardPage() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-wrap">
|
||||||
|
{classes.map((classe) => (
|
||||||
|
<div className="lg:col-span-2 bg-white p-6 rounded-lg shadow-sm border border-gray-100 mr-4">
|
||||||
|
<ClasseDetails key={classe.id} classe={classe} />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1,10 +1,8 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import Table from '@/components/Table';
|
|
||||||
import SpecialitiesSection from '@/components/SpecialitiesSection'
|
import SpecialitiesSection from '@/components/SpecialitiesSection'
|
||||||
import ClassesSection from '@/components/ClassesSection'
|
import ClassesSection from '@/components/ClassesSection'
|
||||||
import TeachersSection from '@/components/TeachersSection';
|
import TeachersSection from '@/components/TeachersSection';
|
||||||
import { User, School } from 'lucide-react'
|
|
||||||
import { BK_GESTIONINSCRIPTION_SPECIALITES_URL,
|
import { BK_GESTIONINSCRIPTION_SPECIALITES_URL,
|
||||||
BK_GESTIONINSCRIPTION_CLASSES_URL,
|
BK_GESTIONINSCRIPTION_CLASSES_URL,
|
||||||
BK_GESTIONINSCRIPTION_SPECIALITE_URL,
|
BK_GESTIONINSCRIPTION_SPECIALITE_URL,
|
||||||
@ -66,7 +64,6 @@ export default function Page() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleCreate = (url, newData, setDatas) => {
|
const handleCreate = (url, newData, setDatas) => {
|
||||||
console.log('SEND POST :', JSON.stringify(newData));
|
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@ -13,19 +13,29 @@ import Button from '@/components/Button';
|
|||||||
import DropdownMenu from "@/components/DropdownMenu";
|
import DropdownMenu from "@/components/DropdownMenu";
|
||||||
import { swapFormatDate } from '@/utils/Date';
|
import { swapFormatDate } from '@/utils/Date';
|
||||||
import { formatPhoneNumber } from '@/utils/Telephone';
|
import { formatPhoneNumber } from '@/utils/Telephone';
|
||||||
import { MoreVertical, Send, Edit, Trash2, FileText, ChevronUp, UserPlus } from 'lucide-react';
|
import { MoreVertical, Send, Edit, Trash2, FileText, ChevronUp, UserPlus, CheckCircle } from 'lucide-react';
|
||||||
import Modal from '@/components/Modal';
|
import Modal from '@/components/Modal';
|
||||||
import InscriptionForm from '@/components/Inscription/InscriptionForm'
|
import InscriptionForm from '@/components/Inscription/InscriptionForm'
|
||||||
|
import AffectationClasseForm from '@/components/AffectationClasseForm'
|
||||||
|
|
||||||
import { BK_GESTIONINSCRIPTION_FICHESINSCRIPTION_URL, BK_GESTIONINSCRIPTION_SEND_URL, FR_ADMIN_STUDENT_EDIT_SUBSCRIBE, BK_GESTIONINSCRIPTION_ARCHIVE_URL } from '@/utils/Url';
|
import { BK_GESTIONINSCRIPTION_FICHESINSCRIPTION_URL,
|
||||||
|
BK_GESTIONINSCRIPTION_SEND_URL,
|
||||||
|
FR_ADMIN_STUDENT_EDIT_SUBSCRIBE,
|
||||||
|
BK_GESTIONINSCRIPTION_ARCHIVE_URL,
|
||||||
|
BK_GESTIONINSCRIPTION_CLASSES_URL,
|
||||||
|
BK_GESTIONINSCRIPTION_FICHEINSCRIPTION_URL } from '@/utils/Url';
|
||||||
|
|
||||||
|
import DjangoCSRFToken from '@/components/DjangoCSRFToken'
|
||||||
|
import useCsrfToken from '@/hooks/useCsrfToken';
|
||||||
|
|
||||||
const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true';
|
const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true';
|
||||||
|
|
||||||
export default function Page({ params: { locale } }) {
|
export default function Page({ params: { locale } }) {
|
||||||
const t = useTranslations('students');
|
const t = useTranslations('students');
|
||||||
const [ficheInscriptions, setFicheInscriptions] = useState([]);
|
const [ficheInscriptions, setFicheInscriptions] = useState([]);
|
||||||
const [ficheInscriptionsData, setFicheInscriptionsData] = useState([]);
|
const [fichesInscriptionsDataEnCours, setFichesInscriptionsDataEnCours] = useState([]);
|
||||||
const [fichesInscriptionsDataArchivees, setFicheInscriptionsDataArchivees] = useState([]);
|
const [fichesInscriptionsDataInscrits, setichesInscriptionsDataInscrits] = useState([]);
|
||||||
|
const [fichesInscriptionsDataArchivees, setFichesInscriptionsDataArchivees] = useState([]);
|
||||||
// const [filter, setFilter] = useState('*');
|
// const [filter, setFilter] = useState('*');
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [alertPage, setAlertPage] = useState(false);
|
const [alertPage, setAlertPage] = useState(false);
|
||||||
@ -33,22 +43,32 @@ export default function Page({ params: { locale } }) {
|
|||||||
const [ficheArchivee, setFicheArchivee] = useState(false);
|
const [ficheArchivee, setFicheArchivee] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [popup, setPopup] = useState({ visible: false, message: '', onConfirm: null });
|
const [popup, setPopup] = useState({ visible: false, message: '', onConfirm: null });
|
||||||
const [activeTab, setActiveTab] = useState('all');
|
const [activeTab, setActiveTab] = useState('pending');
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [totalPages, setTotalPages] = useState(1);
|
const [totalPages, setTotalPages] = useState(1);
|
||||||
const [totalStudents, setTotalStudents] = useState(0);
|
const [totalPending, setTotalPending] = useState(0);
|
||||||
|
const [totalSubscribed, setTotalSubscribed] = useState(0);
|
||||||
const [totalArchives, setTotalArchives] = useState(0);
|
const [totalArchives, setTotalArchives] = useState(0);
|
||||||
const [itemsPerPage, setItemsPerPage] = useState(5); // Définir le nombre d'éléments par page
|
const [itemsPerPage, setItemsPerPage] = useState(5); // Définir le nombre d'éléments par page
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const [isOpenAffectationClasse, setIsOpenAffectationClasse] = useState(false);
|
||||||
|
const [eleve, setEleve] = useState('');
|
||||||
|
const [classes, setClasses] = useState([]);
|
||||||
|
|
||||||
|
const csrfToken = useCsrfToken();
|
||||||
|
|
||||||
const openModal = () => {
|
const openModal = () => {
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const openModalAssociationEleve = (eleveSelected) => {
|
||||||
|
setIsOpenAffectationClasse(true);
|
||||||
|
setEleve(eleveSelected);
|
||||||
|
}
|
||||||
// Modifier la fonction fetchData pour inclure le terme de recherche
|
// Modifier la fonction fetchData pour inclure le terme de recherche
|
||||||
const fetchData = (page, pageSize, search = '') => {
|
const fetchData = (page, pageSize, search = '') => {
|
||||||
const url = `${BK_GESTIONINSCRIPTION_FICHESINSCRIPTION_URL}/all?page=${page}&page_size=${pageSize}&search=${search}`;
|
const url = `${BK_GESTIONINSCRIPTION_FICHESINSCRIPTION_URL}/pending?page=${page}&page_size=${pageSize}&search=${search}`;
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@ -58,11 +78,33 @@ export default function Page({ params: { locale } }) {
|
|||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
if (data) {
|
if (data) {
|
||||||
const { fichesInscriptions, count } = data;
|
const { fichesInscriptions, count } = data;
|
||||||
setFicheInscriptionsData(fichesInscriptions);
|
setFichesInscriptionsDataEnCours(fichesInscriptions);
|
||||||
const calculatedTotalPages = Math.ceil(count / pageSize);
|
const calculatedTotalPages = Math.ceil(count / pageSize);
|
||||||
setTotalStudents(count);
|
setTotalPending(count);
|
||||||
setTotalPages(calculatedTotalPages);
|
setTotalPages(calculatedTotalPages);
|
||||||
}
|
}
|
||||||
|
console.log('Success PENDING:', data);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error fetching data:', error);
|
||||||
|
setIsLoading(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchDataSubscribed = () => {
|
||||||
|
fetch(`${BK_GESTIONINSCRIPTION_FICHESINSCRIPTION_URL}/subscribed`, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
}).then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
setIsLoading(false);
|
||||||
|
if (data) {
|
||||||
|
const { fichesInscriptions, count } = data;
|
||||||
|
setTotalSubscribed(count);
|
||||||
|
setichesInscriptionsDataInscrits(fichesInscriptions);
|
||||||
|
}
|
||||||
|
console.log('Success SUBSCRIBED:', data);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Error fetching data:', error);
|
console.error('Error fetching data:', error);
|
||||||
@ -81,7 +123,7 @@ export default function Page({ params: { locale } }) {
|
|||||||
if (data) {
|
if (data) {
|
||||||
const { fichesInscriptions, count } = data;
|
const { fichesInscriptions, count } = data;
|
||||||
setTotalArchives(count);
|
setTotalArchives(count);
|
||||||
setFicheInscriptionsDataArchivees(fichesInscriptions);
|
setFichesInscriptionsDataArchivees(fichesInscriptions);
|
||||||
}
|
}
|
||||||
console.log('Success ARCHIVED:', data);
|
console.log('Success ARCHIVED:', data);
|
||||||
})
|
})
|
||||||
@ -91,14 +133,31 @@ export default function Page({ params: { locale } }) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchClasses = () => {
|
||||||
|
fetch(`${BK_GESTIONINSCRIPTION_CLASSES_URL}`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
setClasses(data);
|
||||||
|
console.log("classes : ", data)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error fetching classes:', error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchClasses();
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchDataAndSetState = () => {
|
const fetchDataAndSetState = () => {
|
||||||
if (!useFakeData) {
|
if (!useFakeData) {
|
||||||
fetchData(currentPage, itemsPerPage, searchTerm);
|
fetchData(currentPage, itemsPerPage, searchTerm);
|
||||||
|
fetchDataSubscribed();
|
||||||
fetchDataArchived();
|
fetchDataArchived();
|
||||||
} else {
|
} else {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setFicheInscriptionsData(mockFicheInscription);
|
setFichesInscriptionsDataEnCours(mockFicheInscription);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
@ -183,13 +242,37 @@ export default function Page({ params: { locale } }) {
|
|||||||
fetchData(newPage, itemsPerPage); // Appeler fetchData directement ici
|
fetchData(newPage, itemsPerPage); // Appeler fetchData directement ici
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const validateAndAssociate = (updatedData) => {
|
||||||
|
fetch(`${BK_GESTIONINSCRIPTION_FICHEINSCRIPTION_URL}/${eleve.id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRFToken': csrfToken
|
||||||
|
},
|
||||||
|
body: JSON.stringify(updatedData),
|
||||||
|
credentials: 'include'
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
console.log('Succès :', data);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Erreur :', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ name: t('studentName'), transform: (row) => row.eleve.nom },
|
{ name: t('studentName'), transform: (row) => row.eleve.nom },
|
||||||
{ name: t('studentFistName'), transform: (row) => row.eleve.prenom },
|
{ name: t('studentFistName'), transform: (row) => row.eleve.prenom },
|
||||||
{ name: t('mainContactMail'), transform: (row) => row.eleve.responsables[0].mail },
|
{ name: t('mainContactMail'), transform: (row) => row.eleve.responsables[0].mail },
|
||||||
{ name: t('phone'), transform: (row) => formatPhoneNumber(row.eleve.responsables[0].telephone) },
|
{ name: t('phone'), transform: (row) => formatPhoneNumber(row.eleve.responsables[0].telephone) },
|
||||||
{ name: t('lastUpdateDate'), transform: (row) => swapFormatDate(row.dateMAJ, "DD-MM-YYYY hh:mm:ss", "DD/MM/YYYY hh:mm") },
|
{ name: t('lastUpdateDate'), transform: (row) => row.dateMAJ_formattee},
|
||||||
{ name: t('registrationFileStatus'), transform: (row) => <StatusLabel etat={row.etat} onChange={(newStatus) => updateStatusAction(row.eleve.id, newStatus)} /> },
|
{ name: t('registrationFileStatus'), transform: (row) => (
|
||||||
|
<div className="flex justify-center items-center h-full">
|
||||||
|
<StatusLabel etat={row.etat} onChange={(newStatus) => updateStatusAction(row.eleve.id, newStatus)} showDropdown={false} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
{ name: t('files'), transform: (row) => (
|
{ name: t('files'), transform: (row) => (
|
||||||
<ul>
|
<ul>
|
||||||
{row.fichiers?.map((fichier, fileIndex) => (
|
{row.fichiers?.map((fichier, fileIndex) => (
|
||||||
@ -228,6 +311,22 @@ const columns = [
|
|||||||
),
|
),
|
||||||
onClick: () => window.location.href = `${FR_ADMIN_STUDENT_EDIT_SUBSCRIBE}?idEleve=${row.eleve.id}&id=1`,
|
onClick: () => window.location.href = `${FR_ADMIN_STUDENT_EDIT_SUBSCRIBE}?idEleve=${row.eleve.id}&id=1`,
|
||||||
}] : []),
|
}] : []),
|
||||||
|
...(row.etat === 3 ? [{
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<CheckCircle size={16} className="mr-2" /> Valider
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
onClick: () => openModalAssociationEleve(row.eleve),
|
||||||
|
}] : []),
|
||||||
|
...(row.etat === 5 ? [{
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<CheckCircle size={16} className="mr-2" /> Rattacher
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
onClick: () => openModalAssociationEleve(row.eleve),
|
||||||
|
}] : []),
|
||||||
...(row.etat !== 6 ? [{
|
...(row.etat !== 6 ? [{
|
||||||
label: (
|
label: (
|
||||||
<>
|
<>
|
||||||
@ -242,6 +341,53 @@ const columns = [
|
|||||||
/>
|
/>
|
||||||
) },
|
) },
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
const columnsSubscribed = [
|
||||||
|
{ name: t('studentName'), transform: (row) => row.eleve.nom },
|
||||||
|
{ name: t('studentFistName'), transform: (row) => row.eleve.prenom },
|
||||||
|
{ name: t('lastUpdateDate'), transform: (row) => row.dateMAJ_formattee},
|
||||||
|
{ name: t('class'), transform: (row) => row.eleve.classeAssocieeName},
|
||||||
|
{ name: t('registrationFileStatus'), transform: (row) => (
|
||||||
|
<div className="flex justify-center items-center h-full">
|
||||||
|
<StatusLabel etat={row.etat} onChange={(newStatus) => updateStatusAction(row.eleve.id, newStatus)} showDropdown={false} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{ name: t('files'), transform: (row) => (
|
||||||
|
<ul>
|
||||||
|
{row.fichiers?.map((fichier, fileIndex) => (
|
||||||
|
<li key={fileIndex} className="flex items-center gap-2">
|
||||||
|
<FileText size={16} />
|
||||||
|
<a href={fichier.url}>{fichier.nom}</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
) },
|
||||||
|
{ name: 'Actions', transform: (row) => (
|
||||||
|
<DropdownMenu
|
||||||
|
buttonContent={<MoreVertical size={20} className="text-gray-400 hover:text-gray-600" />}
|
||||||
|
items={[
|
||||||
|
{ label: (
|
||||||
|
<>
|
||||||
|
<CheckCircle size={16} className="mr-2" /> Rattacher
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
onClick: () => openModalAssociationEleve(row.eleve)
|
||||||
|
},
|
||||||
|
{ label: (
|
||||||
|
<>
|
||||||
|
<Trash2 size={16} className="mr-2 text-red-700" /> Archiver
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
onClick: () => archiveFicheInscription(row.eleve.id, row.eleve.nom, row.eleve.prenom),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
buttonClassName="text-gray-400 hover:text-gray-600"
|
||||||
|
menuClassName="absolute right-0 mt-2 w-48 bg-white border border-gray-200 rounded-md shadow-lg z-10 flex flex-col items-center"
|
||||||
|
/>
|
||||||
|
) },
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
@ -262,22 +408,22 @@ const columns = [
|
|||||||
<div className='p-8'>
|
<div className='p-8'>
|
||||||
<div className="border-b border-gray-200 mb-6">
|
<div className="border-b border-gray-200 mb-6">
|
||||||
<div className="flex gap-8">
|
<div className="flex gap-8">
|
||||||
<Tab
|
|
||||||
text={<>
|
|
||||||
{t('allStudents')}
|
|
||||||
<span className="ml-2 text-sm text-gray-400">({totalStudents})</span>
|
|
||||||
</>}
|
|
||||||
active={activeTab === 'all'}
|
|
||||||
onClick={() => setActiveTab('all')}
|
|
||||||
/>
|
|
||||||
<Tab
|
<Tab
|
||||||
text={<>
|
text={<>
|
||||||
{t('pending')}
|
{t('pending')}
|
||||||
<span className="ml-2 text-sm text-gray-400">({12})</span>
|
<span className="ml-2 text-sm text-gray-400">({totalPending})</span>
|
||||||
</>}
|
</>}
|
||||||
active={activeTab === 'pending'}
|
active={activeTab === 'pending'}
|
||||||
onClick={() => setActiveTab('pending')}
|
onClick={() => setActiveTab('pending')}
|
||||||
/>
|
/>
|
||||||
|
<Tab
|
||||||
|
text={<>
|
||||||
|
{t('subscribed')}
|
||||||
|
<span className="ml-2 text-sm text-gray-400">({totalSubscribed})</span>
|
||||||
|
</>}
|
||||||
|
active={activeTab === 'subscribed'}
|
||||||
|
onClick={() => setActiveTab('subscribed')}
|
||||||
|
/>
|
||||||
<Tab
|
<Tab
|
||||||
text={<>
|
text={<>
|
||||||
{t('archived')}
|
{t('archived')}
|
||||||
@ -309,10 +455,21 @@ const columns = [
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<DjangoCSRFToken csrfToken={csrfToken} />
|
||||||
<Table
|
<Table
|
||||||
key={`${currentPage}-${searchTerm}`}
|
key={`${currentPage}-${searchTerm}`}
|
||||||
data={(activeTab === 'all' || activeTab === 'pending') ? ficheInscriptionsData : fichesInscriptionsDataArchivees}
|
data={
|
||||||
columns={columns}
|
activeTab === 'pending'
|
||||||
|
? fichesInscriptionsDataEnCours
|
||||||
|
: activeTab === 'subscribed'
|
||||||
|
? fichesInscriptionsDataInscrits
|
||||||
|
: fichesInscriptionsDataArchivees
|
||||||
|
}
|
||||||
|
columns={
|
||||||
|
activeTab === 'subscribed'
|
||||||
|
? columnsSubscribed
|
||||||
|
: columns
|
||||||
|
}
|
||||||
itemsPerPage={itemsPerPage}
|
itemsPerPage={itemsPerPage}
|
||||||
currentPage={currentPage}
|
currentPage={currentPage}
|
||||||
totalPages={totalPages}
|
totalPages={totalPages}
|
||||||
@ -327,6 +484,20 @@ const columns = [
|
|||||||
}}
|
}}
|
||||||
onCancel={() => setPopup({ ...popup, visible: false })}
|
onCancel={() => setPopup({ ...popup, visible: false })}
|
||||||
/>
|
/>
|
||||||
|
{isOpenAffectationClasse && (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpenAffectationClasse}
|
||||||
|
setIsOpen={setIsOpenAffectationClasse}
|
||||||
|
title="Affectation à une classe"
|
||||||
|
ContentComponent={() => (
|
||||||
|
<AffectationClasseForm
|
||||||
|
eleve={eleve}
|
||||||
|
onSubmit={validateAndAssociate}
|
||||||
|
classes={classes}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import InputTextIcon from '@/components/InputTextIcon';
|
|||||||
import Loader from '@/components/Loader'; // Importez le composant Loader
|
import Loader from '@/components/Loader'; // Importez le composant Loader
|
||||||
import Button from '@/components/Button'; // Importez le composant Button
|
import Button from '@/components/Button'; // Importez le composant Button
|
||||||
import { User, KeySquare } from 'lucide-react'; // Importez directement les icônes nécessaires
|
import { User, KeySquare } from 'lucide-react'; // Importez directement les icônes nécessaires
|
||||||
import { BK_LOGIN_URL, FR_ADMIN_STUDENT_EDIT_SUBSCRIBE, FR_PARENTS_HOME_URL, FR_USERS_NEW_PASSWORD_URL, FR_USERS_SUBSCRIBE_URL } from '@/utils/Url';
|
import { BK_LOGIN_URL, FR_ADMIN_STUDENT_EDIT_SUBSCRIBE, FR_ADMIN_STUDENT_URL, FR_PARENTS_HOME_URL, FR_USERS_NEW_PASSWORD_URL, FR_USERS_SUBSCRIBE_URL } from '@/utils/Url';
|
||||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||||
import useCsrfToken from '@/hooks/useCsrfToken';
|
import useCsrfToken from '@/hooks/useCsrfToken';
|
||||||
const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true';
|
const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true';
|
||||||
@ -78,7 +78,19 @@ export default function Page() {
|
|||||||
setErrorMessage("")
|
setErrorMessage("")
|
||||||
if(isOK(data)){
|
if(isOK(data)){
|
||||||
localStorage.setItem('userId', data.profil); // Stocker l'identifiant de l'utilisateur
|
localStorage.setItem('userId', data.profil); // Stocker l'identifiant de l'utilisateur
|
||||||
|
if (data.droit == 0) {
|
||||||
|
// Vue ECOLE
|
||||||
|
|
||||||
|
} else if (data.droit == 1) {
|
||||||
|
// Vue PARENT
|
||||||
router.push(`${FR_PARENTS_HOME_URL}`);
|
router.push(`${FR_PARENTS_HOME_URL}`);
|
||||||
|
} else if (data.droit == 2) {
|
||||||
|
// Vue ADMIN
|
||||||
|
router.push(`${FR_ADMIN_STUDENT_URL}`);
|
||||||
|
} else {
|
||||||
|
// Cas anormal
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if(data.errorFields){
|
if(data.errorFields){
|
||||||
setUserFieldError(data.errorFields.email)
|
setUserFieldError(data.errorFields.email)
|
||||||
|
|||||||
68
Front-End/src/components/AffectationClasseForm.js
Normal file
68
Front-End/src/components/AffectationClasseForm.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
const AffectationClasseForm = ({ eleve, onSubmit, classes }) => {
|
||||||
|
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
classeAssocie_id: eleve.classeAssocie_id || null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
const { name, value, type } = e.target;
|
||||||
|
setFormData((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
[name]: parseInt(value, 10),
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
onSubmit({
|
||||||
|
eleve: {
|
||||||
|
...formData
|
||||||
|
},
|
||||||
|
etat:5
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700">
|
||||||
|
Classes
|
||||||
|
</label>
|
||||||
|
<div className="mt-2 grid grid-cols-1 gap-4">
|
||||||
|
{classes.map(classe => (
|
||||||
|
<div key={classe.id} className="flex items-center">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
id={`classe-${classe.id}`}
|
||||||
|
name="classeAssocie_id"
|
||||||
|
value={classe.id}
|
||||||
|
checked={formData.classeAssocie_id === classe.id}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="form-radio h-3 w-3 text-emerald-600 focus:ring-emerald-500 hover:ring-emerald-400 checked:bg-emerald-600 checked:h-3 checked:w-3"
|
||||||
|
/>
|
||||||
|
<label htmlFor={`classe-${classe.id}`} className="ml-2 block text-sm text-gray-900 flex items-center">
|
||||||
|
{classe.nom_ambiance}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-end mt-4 space-x-4">
|
||||||
|
<button
|
||||||
|
onClick={handleSubmit}
|
||||||
|
className={`px-4 py-2 rounded-md shadow-sm focus:outline-none ${
|
||||||
|
(!formData.classeAssocie_id )
|
||||||
|
? "bg-gray-300 text-gray-700 cursor-not-allowed"
|
||||||
|
: "bg-emerald-500 text-white hover:bg-emerald-600"
|
||||||
|
}`}
|
||||||
|
disabled={(!formData.classeAssocie_id)}
|
||||||
|
>
|
||||||
|
Associer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AffectationClasseForm;
|
||||||
72
Front-End/src/components/ClasseDetails.js
Normal file
72
Front-End/src/components/ClasseDetails.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Table from '@/components/Table';
|
||||||
|
import { GraduationCap } from 'lucide-react';
|
||||||
|
|
||||||
|
const ClasseDetails = ({ classe }) => {
|
||||||
|
if (!classe) return null;
|
||||||
|
|
||||||
|
const nombreElevesInscrits = classe.eleves.length;
|
||||||
|
const capaciteTotale = classe.nombre_eleves;
|
||||||
|
const pourcentage = Math.round((nombreElevesInscrits / capaciteTotale) * 100);
|
||||||
|
|
||||||
|
const getColor = (pourcentage) => {
|
||||||
|
if (pourcentage < 50) return 'bg-emerald-500';
|
||||||
|
if (pourcentage < 75) return 'bg-orange-500';
|
||||||
|
return 'bg-red-500';
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-4">
|
||||||
|
<div className="mb-4 flex justify-between items-center">
|
||||||
|
{/* Section Enseignant Principal */}
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<div className="bg-gray-100 p-3 rounded-lg shadow-md flex items-center space-x-4">
|
||||||
|
<GraduationCap className="w-10 h-10 text-gray-600" />
|
||||||
|
<div>
|
||||||
|
<p className="italic text-gray-600">Enseignant Principal :</p>
|
||||||
|
<p className="font-bold text-gray-800">
|
||||||
|
{classe.enseignant_principal ? (
|
||||||
|
`${classe.enseignant_principal.nom} ${classe.enseignant_principal.prenom}`
|
||||||
|
) : (
|
||||||
|
<i>Non assigné</i>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Section Capacité de la Classe */}
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span className="font-bold text-gray-700 mr-4">
|
||||||
|
{nombreElevesInscrits}/{capaciteTotale}
|
||||||
|
</span>
|
||||||
|
<div className="w-32 bg-gray-200 rounded-full h-6 shadow-inner">
|
||||||
|
<div
|
||||||
|
className={`h-full rounded-full ${getColor(pourcentage)}`}
|
||||||
|
style={{ width: `${pourcentage}%` }}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<span className="ml-4 font-bold text-gray-700">
|
||||||
|
{pourcentage}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 className="text-xl font-semibold mb-4">Liste des élèves</h3>
|
||||||
|
<div className="bg-white rounded-lg border border-gray-200 shadow-md">
|
||||||
|
<Table
|
||||||
|
columns={[
|
||||||
|
{ name: 'NOM', transform: (row) => row.nom },
|
||||||
|
{ name: 'PRENOM', transform: (row) => row.prenom },
|
||||||
|
{ name: 'AGE', transform: (row) => `${row.age}` }
|
||||||
|
]}
|
||||||
|
data={classe.eleves}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ClasseDetails;
|
||||||
@ -1,13 +1,15 @@
|
|||||||
import { School, Trash2, MoreVertical, Edit3, Plus, ZoomIn } from 'lucide-react';
|
import { Users, Trash2, MoreVertical, Edit3, Plus, ZoomIn } from 'lucide-react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import Table from '@/components/Table';
|
import Table from '@/components/Table';
|
||||||
import DropdownMenu from '@/components/DropdownMenu';
|
import DropdownMenu from '@/components/DropdownMenu';
|
||||||
import Modal from '@/components/Modal';
|
import Modal from '@/components/Modal';
|
||||||
import ClassForm from '@/components/ClassForm';
|
import ClassForm from '@/components/ClassForm';
|
||||||
|
import ClasseDetails from '@/components/ClasseDetails';
|
||||||
|
|
||||||
const ClassesSection = ({ classes, specialities, teachers, handleCreate, handleEdit, handleDelete }) => {
|
const ClassesSection = ({ classes, specialities, teachers, handleCreate, handleEdit, handleDelete }) => {
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const [isOpenDetails, setIsOpenDetails] = useState(false);
|
||||||
const [editingClass, setEditingClass] = useState(null);
|
const [editingClass, setEditingClass] = useState(null);
|
||||||
|
|
||||||
const openEditModal = (classe) => {
|
const openEditModal = (classe) => {
|
||||||
@ -15,11 +17,21 @@ const ClassesSection = ({ classes, specialities, teachers, handleCreate, handleE
|
|||||||
setEditingClass(classe);
|
setEditingClass(classe);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const openEditModalDetails = (classe) => {
|
||||||
|
setIsOpenDetails(true);
|
||||||
|
setEditingClass(classe);
|
||||||
|
}
|
||||||
|
|
||||||
const closeEditModal = () => {
|
const closeEditModal = () => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
setEditingClass(null);
|
setEditingClass(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const closeEditModalDetails = () => {
|
||||||
|
setIsOpenDetails(false);
|
||||||
|
setEditingClass(null);
|
||||||
|
};
|
||||||
|
|
||||||
const handleModalSubmit = (updatedData) => {
|
const handleModalSubmit = (updatedData) => {
|
||||||
if (editingClass) {
|
if (editingClass) {
|
||||||
handleEdit(editingClass.id, updatedData);
|
handleEdit(editingClass.id, updatedData);
|
||||||
@ -29,15 +41,11 @@ const ClassesSection = ({ classes, specialities, teachers, handleCreate, handleE
|
|||||||
closeEditModal();
|
closeEditModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleInspect = (data) => {
|
|
||||||
console.log('inspect classe : ', data)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<div className="flex justify-between items-center mb-4 max-w-8xl ml-0">
|
<div className="flex justify-between items-center mb-4 max-w-8xl ml-0">
|
||||||
<h2 className="text-3xl text-gray-800 flex items-center">
|
<h2 className="text-3xl text-gray-800 flex items-center">
|
||||||
<School className="w-8 h-8 mr-2" />
|
<Users className="w-8 h-8 mr-2" />
|
||||||
Classes
|
Classes
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button
|
||||||
@ -82,7 +90,7 @@ const ClassesSection = ({ classes, specialities, teachers, handleCreate, handleE
|
|||||||
<DropdownMenu
|
<DropdownMenu
|
||||||
buttonContent={<MoreVertical size={20} className="text-gray-400 hover:text-gray-600" />}
|
buttonContent={<MoreVertical size={20} className="text-gray-400 hover:text-gray-600" />}
|
||||||
items={[
|
items={[
|
||||||
{ label: 'Inspecter', icon: ZoomIn, onClick: () => handleInspect(row) },
|
{ label: 'Inspecter', icon: ZoomIn, onClick: () => openEditModalDetails(row) },
|
||||||
{ label: 'Modifier', icon:Edit3, onClick: () => openEditModal(row) },
|
{ label: 'Modifier', icon:Edit3, onClick: () => openEditModal(row) },
|
||||||
{ label: 'Supprimer', icon: Trash2, onClick: () => handleDelete(row.id) }
|
{ label: 'Supprimer', icon: Trash2, onClick: () => handleDelete(row.id) }
|
||||||
]
|
]
|
||||||
@ -104,6 +112,25 @@ const ClassesSection = ({ classes, specialities, teachers, handleCreate, handleE
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{isOpenDetails && (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpenDetails}
|
||||||
|
setIsOpen={setIsOpenDetails}
|
||||||
|
title={(
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Users className="w-8 h-8 mr-2" />
|
||||||
|
{editingClass ? (
|
||||||
|
<>
|
||||||
|
{editingClass.nom_ambiance} - {editingClass.tranche_age[0]} à {editingClass.tranche_age[1]} ans
|
||||||
|
</>
|
||||||
|
) : ''}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
ContentComponent={() => (
|
||||||
|
<ClasseDetails classe={editingClass} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -16,7 +16,7 @@ const Modal = ({ isOpen, setIsOpen, title, ContentComponent }) => {
|
|||||||
<div className="mt-4 flex justify-end">
|
<div className="mt-4 flex justify-end">
|
||||||
<Dialog.Close asChild>
|
<Dialog.Close asChild>
|
||||||
<button
|
<button
|
||||||
className="inline-flex justify-center px-4 py-2 bg-emerald-500 text-white rounded-md shadow-sm hover:bg-emerald-600 focus:outline-none"
|
className="px-4 py-2 rounded-md shadow-sm focus:outline-none bg-gray-300 text-gray-700 hover:bg-gray-400"
|
||||||
onClick={() => setIsOpen(false)}
|
onClick={() => setIsOpen(false)}
|
||||||
>
|
>
|
||||||
Fermer
|
Fermer
|
||||||
|
|||||||
@ -31,7 +31,7 @@ function Sidebar({ currentPage, items }) {
|
|||||||
{/* Sidebar */}
|
{/* Sidebar */}
|
||||||
<div className="w-64 bg-white border-r border-gray-200 py-6 px-4">
|
<div className="w-64 bg-white border-r border-gray-200 py-6 px-4">
|
||||||
<div className="flex items-center mb-8 px-2">
|
<div className="flex items-center mb-8 px-2">
|
||||||
<div className="text-xl font-semibold">Collège Saint-Joseph</div>
|
<div className="text-xl font-semibold">Ecole NEWT</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav className="space-y-1">
|
<nav className="space-y-1">
|
||||||
|
|||||||
@ -20,7 +20,7 @@ const Table = ({ data, columns, renderCell, itemsPerPage = 0, currentPage, total
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{data.map((row, rowIndex) => (
|
{data?.map((row, rowIndex) => (
|
||||||
<tr key={rowIndex} className={` ${rowIndex % 2 === 0 ? 'bg-emerald-50' : ''}`}>
|
<tr key={rowIndex} className={` ${rowIndex % 2 === 0 ? 'bg-emerald-50' : ''}`}>
|
||||||
{columns.map((column, colIndex) => (
|
{columns.map((column, colIndex) => (
|
||||||
<td key={colIndex} className="py-2 px-4 border-b border-gray-200 text-center text-sm text-gray-700">
|
<td key={colIndex} className="py-2 px-4 border-b border-gray-200 text-center text-sm text-gray-700">
|
||||||
|
|||||||
@ -1,17 +1,25 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
const TeacherForm = ({ teacher, onSubmit, isNew, specialities }) => {
|
const TeacherForm = ({ teacher, onSubmit, isNew, specialities }) => {
|
||||||
|
const profils = [
|
||||||
|
{ value: 0, label: "École" },
|
||||||
|
{ value: 2, label: "Administrateur" },
|
||||||
|
];
|
||||||
|
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
nom: teacher.nom || '',
|
nom: teacher.nom || '',
|
||||||
prenom: teacher.prenom || '',
|
prenom: teacher.prenom || '',
|
||||||
mail: teacher.mail || '',
|
mail: teacher.mail || '',
|
||||||
specialite_id: teacher.specialite_id || 1,
|
specialite_id: teacher.specialite_id || '',
|
||||||
classes: teacher.classes || []
|
classes: teacher.classes || [],
|
||||||
|
profilAssocie_id: teacher.profilAssocie_id || [],
|
||||||
|
DroitValue: teacher.DroitValue || 0
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleChange = (e) => {
|
const handleChange = (e) => {
|
||||||
const { name, value, type } = e.target;
|
const { name, value, type } = e.target;
|
||||||
const newValue = type === 'radio' ? parseInt(value) : value;
|
const newValue = type === 'radio' ? parseInt(value, 10) : value;
|
||||||
|
console.log(`Name: ${name}, Value: ${newValue}`);
|
||||||
setFormData((prevState) => ({
|
setFormData((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
[name]: newValue,
|
[name]: newValue,
|
||||||
@ -91,15 +99,41 @@ const TeacherForm = ({ teacher, onSubmit, isNew, specialities }) => {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700">
|
||||||
|
Types de profil
|
||||||
|
</label>
|
||||||
|
<div className="mt-2 grid grid-cols-1 gap-4">
|
||||||
|
{profils.map((profil) => (
|
||||||
|
<div key={profil.value} className="flex items-center">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
id={`profil-${profil.value}`}
|
||||||
|
name="DroitValue"
|
||||||
|
value={profil.value}
|
||||||
|
checked={formData.DroitValue === profil.value}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="form-radio h-4 w-4 text-emerald-600 focus:ring-emerald-500"
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor={`profil-${profil.value}`}
|
||||||
|
className="ml-2 block text-sm text-gray-900"
|
||||||
|
>
|
||||||
|
{profil.label}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="flex justify-end mt-4 space-x-4">
|
<div className="flex justify-end mt-4 space-x-4">
|
||||||
<button
|
<button
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
className={`px-4 py-2 rounded-md shadow-sm focus:outline-none ${
|
className={`px-4 py-2 rounded-md shadow-sm focus:outline-none ${
|
||||||
(!formData.nom || !formData.prenom || !formData.mail)
|
(!formData.nom || !formData.prenom || !formData.mail || !formData.specialite_id)
|
||||||
? "bg-gray-300 text-gray-700 cursor-not-allowed"
|
? "bg-gray-300 text-gray-700 cursor-not-allowed"
|
||||||
: "bg-emerald-500 text-white hover:bg-emerald-600"
|
: "bg-emerald-500 text-white hover:bg-emerald-600"
|
||||||
}`}
|
}`}
|
||||||
disabled={(!formData.nom || !formData.prenom || !formData.mail)}
|
disabled={(!formData.nom || !formData.prenom || !formData.mail || !formData.specialite_id)}
|
||||||
>
|
>
|
||||||
Soumettre
|
Soumettre
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -1,14 +1,19 @@
|
|||||||
import { School, Trash2, MoreVertical, Edit3, Plus } from 'lucide-react';
|
import { GraduationCap, Trash2, MoreVertical, Edit3, Plus } from 'lucide-react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import Table from '@/components/Table';
|
import Table from '@/components/Table';
|
||||||
import DropdownMenu from '@/components/DropdownMenu';
|
import DropdownMenu from '@/components/DropdownMenu';
|
||||||
import Modal from '@/components/Modal';
|
import Modal from '@/components/Modal';
|
||||||
import TeacherForm from '@/components/TeacherForm';
|
import TeacherForm from '@/components/TeacherForm';
|
||||||
|
import {BK_PROFILE_URL} from '@/utils/Url';
|
||||||
|
import useCsrfToken from '@/hooks/useCsrfToken';
|
||||||
|
|
||||||
const TeachersSection = ({ teachers, handleCreate, handleEdit, handleDelete, specialities }) => {
|
const TeachersSection = ({ teachers, handleCreate, handleEdit, handleDelete, specialities }) => {
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [editingTeacher, setEditingTeacher] = useState(null);
|
const [editingTeacher, setEditingTeacher] = useState(null);
|
||||||
|
|
||||||
|
const csrfToken = useCsrfToken();
|
||||||
|
|
||||||
const openEditModal = (teacher) => {
|
const openEditModal = (teacher) => {
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
setEditingTeacher(teacher);
|
setEditingTeacher(teacher);
|
||||||
@ -21,10 +26,71 @@ const TeachersSection = ({ teachers, handleCreate, handleEdit, handleDelete, spe
|
|||||||
|
|
||||||
const handleModalSubmit = (updatedData) => {
|
const handleModalSubmit = (updatedData) => {
|
||||||
if (editingTeacher) {
|
if (editingTeacher) {
|
||||||
|
// Modification du profil
|
||||||
|
const request = new Request(
|
||||||
|
`${BK_PROFILE_URL}/${updatedData.profilAssocie_id}`,
|
||||||
|
{
|
||||||
|
method:'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type':'application/json',
|
||||||
|
'X-CSRFToken': csrfToken
|
||||||
|
},
|
||||||
|
credentials: 'include',
|
||||||
|
body: JSON.stringify( {
|
||||||
|
email: updatedData.mail,
|
||||||
|
username: updatedData.mail,
|
||||||
|
droit:updatedData.DroitValue
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
fetch(request).then(response => response.json())
|
||||||
|
.then(response => {
|
||||||
|
console.log('Success:', response);
|
||||||
|
console.log('UpdateData:', updatedData);
|
||||||
handleEdit(editingTeacher.id, updatedData);
|
handleEdit(editingTeacher.id, updatedData);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error fetching data:', error);
|
||||||
|
error = error.errorMessage;
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
|
// Création d'un profil associé à l'adresse mail du responsable saisie
|
||||||
|
// Le profil est inactif
|
||||||
|
const request = new Request(
|
||||||
|
`${BK_PROFILE_URL}`,
|
||||||
|
{
|
||||||
|
method:'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type':'application/json',
|
||||||
|
'X-CSRFToken': csrfToken
|
||||||
|
},
|
||||||
|
credentials: 'include',
|
||||||
|
body: JSON.stringify( {
|
||||||
|
email: updatedData.mail,
|
||||||
|
password: 'Provisoire01!',
|
||||||
|
username: updatedData.mail,
|
||||||
|
is_active: 1, // On rend le profil actif : on considère qu'au moment de la configuration de l'école un abonnement a été souscrit
|
||||||
|
droit:updatedData.DroitValue
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
fetch(request).then(response => response.json())
|
||||||
|
.then(response => {
|
||||||
|
console.log('Success:', response);
|
||||||
|
console.log('UpdateData:', updatedData);
|
||||||
|
if (response.id) {
|
||||||
|
let idProfil = response.id;
|
||||||
|
updatedData.profilAssocie_id = idProfil;
|
||||||
handleCreate(updatedData);
|
handleCreate(updatedData);
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error fetching data:', error);
|
||||||
|
error = error.errorMessage;
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
closeEditModal();
|
closeEditModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -32,7 +98,7 @@ const TeachersSection = ({ teachers, handleCreate, handleEdit, handleDelete, spe
|
|||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<div className="flex justify-between items-center mb-4 max-w-7xl ml-0">
|
<div className="flex justify-between items-center mb-4 max-w-7xl ml-0">
|
||||||
<h2 className="text-3xl text-gray-800 flex items-center">
|
<h2 className="text-3xl text-gray-800 flex items-center">
|
||||||
<School className="w-8 h-8 mr-2" />
|
<GraduationCap className="w-8 h-8 mr-2" />
|
||||||
Enseignants
|
Enseignants
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button
|
||||||
@ -49,7 +115,9 @@ const TeachersSection = ({ teachers, handleCreate, handleEdit, handleDelete, spe
|
|||||||
{ name: 'PRENOM', transform: (row) => row.prenom },
|
{ name: 'PRENOM', transform: (row) => row.prenom },
|
||||||
{ name: 'MAIL', transform: (row) => row.mail },
|
{ name: 'MAIL', transform: (row) => row.mail },
|
||||||
{ name: 'SPECIALITE',
|
{ name: 'SPECIALITE',
|
||||||
transform: (row) => (
|
transform: (row) => {
|
||||||
|
return row.specialite
|
||||||
|
?
|
||||||
<div key={row.id} className="flex justify-center items-center space-x-2">
|
<div key={row.id} className="flex justify-center items-center space-x-2">
|
||||||
<span
|
<span
|
||||||
key={row.specialite.id}
|
key={row.specialite.id}
|
||||||
@ -58,7 +126,18 @@ const TeachersSection = ({ teachers, handleCreate, handleEdit, handleDelete, spe
|
|||||||
title={row.specialite.nom}
|
title={row.specialite.nom}
|
||||||
></span>
|
></span>
|
||||||
</div>
|
</div>
|
||||||
)
|
: <i>Non définie</i>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ name: 'TYPE PROFIL',
|
||||||
|
transform: (row) => {
|
||||||
|
return row.profilAssocie
|
||||||
|
?
|
||||||
|
<div key={row.id} className="flex justify-center items-center space-x-2">
|
||||||
|
{row.DroitLabel}
|
||||||
|
</div>
|
||||||
|
: <i>Non définie</i>;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ name: 'ACTIONS', transform: (row) => (
|
{ name: 'ACTIONS', transform: (row) => (
|
||||||
<DropdownMenu
|
<DropdownMenu
|
||||||
@ -72,8 +151,6 @@ const TeachersSection = ({ teachers, handleCreate, handleEdit, handleDelete, spe
|
|||||||
menuClassName="absolute right-0 mt-2 w-48 bg-white border border-gray-200 rounded-md shadow-lg z-10 flex flex-col items-center"
|
menuClassName="absolute right-0 mt-2 w-48 bg-white border border-gray-200 rounded-md shadow-lg z-10 flex flex-col items-center"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
// { name: 'SPECIALITE', transform: (row) => row.specialite_id },
|
|
||||||
// { name: 'CLASSES', transform: (row) => row.classe },
|
|
||||||
]}
|
]}
|
||||||
data={teachers}
|
data={teachers}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user