feat: Evolution des modèles pour intégrer un planning et du m2m

This commit is contained in:
N3WT DE COMPET
2024-11-27 18:11:28 +01:00
parent e1202c6e6d
commit 85d4c007cb
9 changed files with 282 additions and 178 deletions

View File

@ -1,5 +1,8 @@
from django.db import models from django.db import models
from GestionLogin.models import Profil from GestionLogin.models import Profil
from django.db.models import JSONField
from django.db.models.signals import post_save
from django.dispatch import receiver
class Specialite(models.Model): class Specialite(models.Model):
nom = models.CharField(max_length=100) nom = models.CharField(max_length=100)
@ -13,7 +16,7 @@ class Enseignant(models.Model):
nom = models.CharField(max_length=100) nom = models.CharField(max_length=100)
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') specialites = models.ManyToManyField(Specialite, related_name='enseignants')
profilAssocie = models.ForeignKey(Profil, on_delete=models.CASCADE, null=True, blank=True) profilAssocie = models.ForeignKey(Profil, on_delete=models.CASCADE, null=True, blank=True)
def __str__(self): def __str__(self):
@ -26,9 +29,20 @@ class Classe(models.Model):
langue_enseignement = models.CharField(max_length=255) langue_enseignement = models.CharField(max_length=255)
annee_scolaire = models.CharField(max_length=9) annee_scolaire = models.CharField(max_length=9)
dateCreation = models.DateTimeField(auto_now_add=True) dateCreation = models.DateTimeField(auto_now_add=True)
specialites = models.ManyToManyField(Specialite, related_name='classes') enseignants = models.ManyToManyField(Enseignant, related_name='classes')
enseignant_principal = models.ForeignKey(Enseignant, on_delete=models.SET_NULL, null=True, blank=True, related_name='classes_principal')
def __str__(self): def __str__(self):
return self.nom_ambiance return self.nom_ambiance
class Planning(models.Model):
classe = models.OneToOneField(Classe, on_delete=models.SET_NULL, null=True, blank=True, related_name='planning')
emploiDuTemps = JSONField(default=list)
def __str__(self):
return f'Planning de {self.classe.nom_ambiance}'
@receiver(post_save, sender=Classe)
def create_planning(sender, instance, created, **kwargs):
if created and not hasattr(instance, 'planning'):
Planning.objects.create(classe=instance)

View File

@ -1,5 +1,5 @@
from rest_framework import serializers from rest_framework import serializers
from .models import Enseignant, Specialite, Classe from .models import Enseignant, Specialite, Classe, Planning
from GestionInscriptions.models import FicheInscription from GestionInscriptions.models import FicheInscription
from GestionInscriptions.serializers import EleveSerializer from GestionInscriptions.serializers import EleveSerializer
from GestionLogin.serializers import ProfilSerializer from GestionLogin.serializers import ProfilSerializer
@ -21,40 +21,93 @@ class SpecialiteSerializer(serializers.ModelSerializer):
return local_time.strftime("%d-%m-%Y %H:%M") return local_time.strftime("%d-%m-%Y %H:%M")
class ClasseSerializer(serializers.ModelSerializer): class EnseignantDetailSerializer(serializers.ModelSerializer):
specialites = SpecialiteSerializer(many=True, read_only=True) specialites = SpecialiteSerializer(many=True, read_only=True)
specialites_ids = serializers.PrimaryKeyRelatedField(queryset=Specialite.objects.all(), many=True, source='specialites')
dateCreation_formattee = serializers.SerializerMethodField()
enseignant_principal = serializers.SerializerMethodField()
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 = Enseignant
fields = ['id', 'nom_ambiance', 'tranche_age', 'nombre_eleves', 'langue_enseignement', 'specialites', 'specialites_ids', 'enseignant_principal', 'enseignant_principal_id', 'annee_scolaire', 'dateCreation', 'dateCreation_formattee', 'eleves'] fields = ['id', 'nom', 'prenom', 'mail', 'specialites']
def get_enseignant_principal(self, obj): class EnseignantSerializer(serializers.ModelSerializer):
from .serializers import EnseignantDetailSerializer specialites = SpecialiteSerializer(many=True, read_only=True)
if obj.enseignant_principal: specialites_ids = serializers.PrimaryKeyRelatedField(queryset=Specialite.objects.all(), many=True, source='specialites')
return EnseignantDetailSerializer(obj.enseignant_principal).data profilAssocie_id = serializers.PrimaryKeyRelatedField(queryset=Profil.objects.all(), source='profilAssocie', write_only=False, read_only=False)
return None classes_principal = serializers.PrimaryKeyRelatedField(many=True, read_only=True, source='classes')
profilAssocie = ProfilSerializer(read_only=True)
DroitLabel = serializers.SerializerMethodField()
DroitValue = serializers.SerializerMethodField()
class Meta:
model = Enseignant
fields = ['id', 'nom', 'prenom', 'mail', 'specialites', 'specialites_ids', 'classes_principal', 'profilAssocie', 'profilAssocie_id', 'DroitLabel', 'DroitValue']
def create(self, validated_data): def create(self, validated_data):
specialites_data = validated_data.pop('specialites', []) specialites_data = validated_data.pop('specialites', None)
classe = Classe.objects.create(**validated_data) profilAssocie = validated_data.pop('profilAssocie', None)
classe.specialites.set(specialites_data) enseignant = Enseignant.objects.create(**validated_data)
return classe enseignant.specialites.set(specialites_data)
if profilAssocie:
enseignant.profilAssocie = profilAssocie
enseignant.save()
return enseignant
def update(self, instance, validated_data): def update(self, instance, validated_data):
specialites_data = validated_data.pop('specialites', []) specialites_data = validated_data.pop('specialites', [])
instance.nom = validated_data.get('nom', instance.nom)
instance.prenom = validated_data.get('prenom', instance.prenom)
instance.mail = validated_data.get('mail', instance.mail)
instance.profilAssocie = validated_data.get('profilAssocie', instance.profilAssocie)
instance.save()
instance.specialites.set(specialites_data)
return instance
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 PlanningSerializer(serializers.ModelSerializer):
classe_id = serializers.PrimaryKeyRelatedField(queryset=Classe.objects.all(), source='classe')
class Meta:
model = Planning
fields = ['id', 'classe', 'classe_id', 'emploiDuTemps']
class ClasseSerializer(serializers.ModelSerializer):
dateCreation_formattee = serializers.SerializerMethodField()
enseignants = EnseignantSerializer(many=True, read_only=True)
enseignants_ids = serializers.PrimaryKeyRelatedField(queryset=Enseignant.objects.all(), many=True, source='enseignants')
eleves = serializers.SerializerMethodField()
planning = serializers.SerializerMethodField()
class Meta:
model = Classe
fields = ['id', 'nom_ambiance', 'tranche_age', 'nombre_eleves', 'langue_enseignement', 'enseignants', 'enseignants_ids', 'annee_scolaire', 'dateCreation', 'dateCreation_formattee', 'eleves', 'planning']
def create(self, validated_data):
planning_data = validated_data.pop('planning', None)
enseignants_data = validated_data.pop('enseignants', [])
classe = Classe.objects.create(**validated_data)
classe.enseignants.set(enseignants_data)
if planning_data and not hasattr(classe, 'planning'):
Planning.objects.create(classe=classe, **planning_data)
return classe
def update(self, instance, validated_data):
planning_data = validated_data.pop('planning', None)
enseignants_data = validated_data.pop('enseignants', [])
instance.nom_ambiance = validated_data.get('nom_ambiance', instance.nom_ambiance) instance.nom_ambiance = validated_data.get('nom_ambiance', instance.nom_ambiance)
instance.tranche_age = validated_data.get('tranche_age', instance.tranche_age) instance.tranche_age = validated_data.get('tranche_age', instance.tranche_age)
instance.nombre_eleves = validated_data.get('nombre_eleves', instance.nombre_eleves) instance.nombre_eleves = validated_data.get('nombre_eleves', instance.nombre_eleves)
instance.langue_enseignement = validated_data.get('langue_enseignement', instance.langue_enseignement) instance.langue_enseignement = validated_data.get('langue_enseignement', instance.langue_enseignement)
instance.annee_scolaire = validated_data.get('annee_scolaire', instance.annee_scolaire) instance.annee_scolaire = validated_data.get('annee_scolaire', instance.annee_scolaire)
instance.enseignant_principal = validated_data.get('enseignant_principal', instance.enseignant_principal)
instance.save() instance.save()
instance.specialites.set(specialites_data) instance.enseignants.set(enseignants_data)
if planning_data:
planning = instance.planning
planning.emploiDuTemps = planning_data.get('emploiDuTemps', planning.emploiDuTemps)
planning.save()
return instance return instance
def get_dateCreation_formattee(self, obj): def get_dateCreation_formattee(self, obj):
@ -73,39 +126,8 @@ class ClasseSerializer(serializers.ModelSerializer):
filtered_eleves.append(eleve) filtered_eleves.append(eleve)
return EleveSerializer(filtered_eleves, many=True, read_only=True).data return EleveSerializer(filtered_eleves, many=True, read_only=True).data
class EnseignantSerializer(serializers.ModelSerializer): def get_planning(self, obj):
specialite = SpecialiteSerializer(read_only=True) from .serializers import PlanningSerializer
specialite_id = serializers.PrimaryKeyRelatedField(queryset=Specialite.objects.all(), source='specialite', write_only=False, read_only=False) if obj.planning:
profilAssocie_id = serializers.PrimaryKeyRelatedField(queryset=Profil.objects.all(), source='profilAssocie', write_only=False, read_only=False) return PlanningSerializer(obj.planning).data
classes_principal = ClasseSerializer(many=True, read_only=True) return None
profilAssocie = ProfilSerializer(read_only=True)
DroitLabel = serializers.SerializerMethodField()
DroitValue = serializers.SerializerMethodField()
class Meta:
model = Enseignant
fields = ['id', 'nom', 'prenom', 'mail', 'specialite', 'specialite_id', 'classes_principal', 'profilAssocie', 'profilAssocie_id', 'DroitLabel', 'DroitValue']
def create(self, validated_data):
specialite = validated_data.pop('specialite', None)
profilAssocie = validated_data.pop('profilAssocie', None)
enseignant = Enseignant.objects.create(**validated_data)
if specialite:
enseignant.specialite = specialite
if profilAssocie:
enseignant.profilAssocie = profilAssocie
enseignant.save()
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):
specialite = SpecialiteSerializer(read_only=True)
class Meta:
model = Enseignant
fields = ['id', 'nom', 'prenom', 'mail', 'specialite']

View File

@ -81,6 +81,8 @@ class SpecialitesView(APIView):
specialitesList = bdd.getAllObjects(Specialite) specialitesList = bdd.getAllObjects(Specialite)
specialites_serializer = SpecialiteSerializer(specialitesList, many=True) specialites_serializer = SpecialiteSerializer(specialitesList, many=True)
return JsonResponse(specialites_serializer.data, safe=False)
return JsonResponse(specialite_serializer.errors, safe=False) return JsonResponse(specialite_serializer.errors, safe=False)

View File

@ -0,0 +1,43 @@
const CheckBoxList = ({ items, formData, handleChange, fieldName, label, icon: Icon, className, itemLabelFunc = (item) => item.name }) => {
const handleCheckboxChange = (e) => {
handleChange(e);
};
return (
<div className={`mb-4 ${className}`}>
<label className="block text-sm font-medium text-gray-700 flex items-center">
{Icon && <Icon className="w-5 h-5 mr-2" />}
{label}
</label>
<div className="mt-2 grid grid-cols-1 gap-4">
{items.map(item => (
<div key={item.id} className="flex items-center">
<input
type="checkbox"
id={`${fieldName}-${item.id}`}
name={fieldName}
value={item.id}
checked={formData[fieldName].includes(item.id)}
onChange={handleCheckboxChange}
className="form-checkbox h-4 w-4 rounded-md text-emerald-600 hover:ring-emerald-400 checked:bg-emerald-600 hover:border-emerald-500 hover:bg-emerald-500 cursor-pointer"
style={{ borderRadius: '6px', outline: 'none', boxShadow: 'none' }} // Remove focus styles
/>
<label htmlFor={`${fieldName}-${item.id}`} className="ml-2 block text-sm text-gray-900 flex items-center">
{itemLabelFunc(item)}
{item.codeCouleur && (
<div
className="w-4 h-4 rounded-full ml-2 hover:bg-opacity-70 transition duration-200 ease-in-out"
style={{ backgroundColor: item.codeCouleur }}
title={item.codeCouleur}
></div>
)}
</label>
</div>
))}
</div>
</div>
);
};
export default CheckBoxList;

View File

@ -3,7 +3,7 @@ import Slider from '@/components/Slider'
import InputTextIcon from '@/components/InputTextIcon'; import InputTextIcon from '@/components/InputTextIcon';
import Button from '@/components/Button'; import Button from '@/components/Button';
import SelectChoice from '@/components/SelectChoice'; import SelectChoice from '@/components/SelectChoice';
import RadioList from '@/components/RadioList'; import CheckBoxList from '@/components/CheckBoxList';
import { Users, Maximize2, Globe, Calendar, GraduationCap } from 'lucide-react'; import { Users, Maximize2, Globe, Calendar, GraduationCap } from 'lucide-react';
const ClassForm = ({ classe, onSubmit, isNew, specialities, teachers }) => { const ClassForm = ({ classe, onSubmit, isNew, specialities, teachers }) => {
@ -19,17 +19,32 @@ const ClassForm = ({ classe, onSubmit, isNew, specialities, teachers }) => {
nombre_eleves: classe.nombre_eleves || '', nombre_eleves: classe.nombre_eleves || '',
langue_enseignement: classe.langue_enseignement || 'Français', langue_enseignement: classe.langue_enseignement || 'Français',
annee_scolaire: classe.annee_scolaire || '', annee_scolaire: classe.annee_scolaire || '',
specialites_ids: classe.specialites_ids || [], enseignants_ids: classe.enseignants_ids || [],
enseignant_principal_id: classe.enseignant_principal_id || null, planning: classe.planning || []
}); });
const handleChange = (e) => { const handleChange = (e) => {
const { name, value, type } = e.target; const target = e.target || e.currentTarget;
const newValue = type === 'radio' ? parseInt(value) : value; const { name, value, type, checked } = target;
console.log('type : ', type);
if (type === 'checkbox') {
setFormData((prevState) => {
const newValues = checked
? [...(prevState[name] || []), parseInt(value, 10)]
: (prevState[name] || []).filter((v) => v !== parseInt(value, 10));
return {
...prevState,
[name]: newValues,
};
});
} else {
setFormData((prevState) => ({ setFormData((prevState) => ({
...prevState, ...prevState,
[name]: newValue, [name]: type === 'radio' ? parseInt(value, 10) : value,
})); }));
}
}; };
const handleSliderChange = (value) => { const handleSliderChange = (value) => {
@ -48,20 +63,32 @@ const ClassForm = ({ classe, onSubmit, isNew, specialities, teachers }) => {
}; };
const handleSubmit = () => { const handleSubmit = () => {
onSubmit(formData, isNew); const updatedFormData = {
...formData,
planning: formData.planning || []
}; };
onSubmit(updatedFormData, isNew);
const handleSpecialityChange = (id) => {
setFormData(prevFormData => {
const specialites_ids = prevFormData.specialites_ids.includes(id)
? prevFormData.specialites_ids.filter(specialityId => specialityId !== id)
: [...prevFormData.specialites_ids, id];
return { ...prevFormData, specialites_ids };
});
}; };
const getTeacherLabel = (teacher) => { const getTeacherLabel = (teacher) => {
return `${teacher.nom} ${teacher.prenom} (${teacher.specialite.nom})`; return (
<div style={{ display: 'flex', alignItems: 'center' }}>
<span>{teacher.nom} {teacher.prenom} -</span>
{teacher.specialites.map(specialite => (
<span
key={specialite.id}
style={{ display: 'flex', alignItems: 'center', marginLeft: '8px' }}
>
<span>{specialite.nom}</span>
<span
className="w-4 h-4 rounded-full ml-1"
style={{ backgroundColor: specialite.codeCouleur }}
title={specialite.nom}
></span>
</span>
))}
</div>
);
}; };
return ( return (
@ -121,56 +148,28 @@ const ClassForm = ({ classe, onSubmit, isNew, specialities, teachers }) => {
className="w-full" className="w-full"
/> />
</div> </div>
<div>
<label className="block text-sm font-medium text-gray-700">
Spécialités
</label>
<div className="mt-2 grid grid-cols-1 gap-4">
{specialities.map(speciality => (
<div key={speciality.id} className="flex items-center">
<input
type="checkbox"
id={`speciality-${speciality.id}`}
name="specialites"
value={speciality.id}
checked={formData.specialites_ids.includes(speciality.id)}
onChange={() => handleSpecialityChange(speciality.id)}
className="h-4 w-4 text-emerald-600 border-gray-300 rounded focus:ring-emerald-500"
/>
<label htmlFor={`speciality-${speciality.id}`} className="ml-2 block text-sm text-gray-900 flex items-center">
{speciality.nom}
<div
className="w-4 h-4 rounded-full ml-2"
style={{ backgroundColor: speciality.codeCouleur }}
title={speciality.codeCouleur}
></div>
</label>
</div>
))}
</div>
</div>
<div className="flex space-x-4"> <div className="flex space-x-4">
<RadioList <CheckBoxList
items={teachers} items={teachers}
formData={formData} formData={formData}
handleChange={handleChange} handleChange={handleChange}
fieldName="enseignant_principal_id" fieldName="enseignants_ids"
label="Enseignant principal" label="Enseignants"
itemLabelFunc={getTeacherLabel}
icon={GraduationCap} icon={GraduationCap}
className="w-full mt-4" className="w-full mt-4"
itemLabelFunc={getTeacherLabel}
/> />
</div> </div>
<div className="flex justify-end mt-4 space-x-4"> <div className="flex justify-end mt-4 space-x-4">
<Button text="Créer" <Button text="Créer"
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_ambiance || !formData.nombre_eleves || !formData.annee_scolaire || !formData.enseignant_principal_id) (!formData.nom_ambiance || !formData.nombre_eleves || !formData.annee_scolaire || formData.enseignants_ids.length === 0)
? "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"
}`} }`}
primary primary
disabled={(!formData.nom_ambiance || !formData.nombre_eleves || !formData.annee_scolaire || !formData.enseignant_principal_id)} disabled={(!formData.nom_ambiance || !formData.nombre_eleves || !formData.annee_scolaire || formData.enseignants_ids.length === 0)}
type="submit" type="submit"
name="Create" /> name="Create" />
</div> </div>

View File

@ -64,10 +64,13 @@ const ClassesSection = ({ classes, specialities, teachers, handleCreate, handleE
{ name: 'LANGUE D\'ENSEIGNEMENT', transform: (row) => row.langue_enseignement }, { name: 'LANGUE D\'ENSEIGNEMENT', transform: (row) => row.langue_enseignement },
{ name: 'ANNEE SCOLAIRE', transform: (row) => row.annee_scolaire }, { name: 'ANNEE SCOLAIRE', transform: (row) => row.annee_scolaire },
{ {
name: 'SPECIALITES', name: 'ENSEIGNANTS',
transform: (row) => ( transform: (row) => (
<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">
{row.specialites.map(specialite => ( {row.enseignants.map(teacher => (
<div key={teacher.id} className="flex items-center space-x-1">
<span>{teacher.nom} {teacher.prenom}</span>
{teacher.specialites.map(specialite => (
<span <span
key={specialite.id} key={specialite.id}
className="w-4 h-4 rounded-full" className="w-4 h-4 rounded-full"
@ -76,16 +79,10 @@ const ClassesSection = ({ classes, specialities, teachers, handleCreate, handleE
></span> ></span>
))} ))}
</div> </div>
))}
</div>
) )
}, },
{
name: 'ENSEIGNANT PRINCIPAL',
transform: (row) => {
return row.enseignant_principal
? `${row.enseignant_principal.nom || ''} ${row.enseignant_principal.prenom || ''}`
: <i>Non assigné</i>;
}
},
{ name: 'ACTIONS', transform: (row) => ( { name: 'ACTIONS', transform: (row) => (
<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" />}

View File

@ -1,8 +1,8 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { GraduationCap, Mail, BookOpen } from 'lucide-react'; import { GraduationCap, Mail, BookOpen, Check } from 'lucide-react';
import InputTextIcon from '@/components/InputTextIcon'; import InputTextIcon from '@/components/InputTextIcon';
import Button from '@/components/Button'; import Button from '@/components/Button';
import RadioList from '@/components/RadioList'; import CheckBoxList from '@/components/CheckBoxList';
import ToggleSwitch from '@/components/ToggleSwitch' import ToggleSwitch from '@/components/ToggleSwitch'
const TeacherForm = ({ teacher, onSubmit, isNew, specialities }) => { const TeacherForm = ({ teacher, onSubmit, isNew, specialities }) => {
@ -11,30 +11,47 @@ const TeacherForm = ({ teacher, onSubmit, isNew, specialities }) => {
nom: teacher.nom || '', nom: teacher.nom || '',
prenom: teacher.prenom || '', prenom: teacher.prenom || '',
mail: teacher.mail || '', mail: teacher.mail || '',
specialite_id: teacher.specialite_id || '', specialites_ids: teacher.specialites_ids || [],
classes: teacher.classes || [], profilAssocie_id:teacher.profilAssocie_id || '',
profilAssocie_id: teacher.profilAssocie_id || [],
droit: teacher.DroitValue || 0 droit: teacher.DroitValue || 0
}); });
const handleToggleChange = () => { const handleToggleChange = () => {
console.log('new value : ', 1-formData.droit)
setFormData({ ...formData, droit: 1-formData.droit }); setFormData({ ...formData, droit: 1-formData.droit });
}; };
const handleChange = (e) => { const handleChange = (e) => {
const { name, value, type } = e.target; const target = e.target || e.currentTarget;
const newValue = type === 'radio' ? parseInt(value, 10) : value; const { name, value, type, checked } = target;
console.log('type : ', type);
if (type === 'checkbox') {
setFormData((prevState) => {
const newValues = checked
? [...(prevState[name] || []), parseInt(value, 10)]
: (prevState[name] || []).filter((v) => v !== parseInt(value, 10));
return {
...prevState,
[name]: newValues,
};
});
} else {
setFormData((prevState) => ({ setFormData((prevState) => ({
...prevState, ...prevState,
[name]: newValue, [name]: type === 'radio' ? parseInt(value, 10) : value,
})); }));
}
}; };
const handleSubmit = () => { const handleSubmit = () => {
onSubmit(formData, isNew); onSubmit(formData, isNew);
}; };
const getSpecialityLabel = (speciality) => {
return `${speciality.nom}`;
};
return ( return (
<form onSubmit={handleSubmit} className="space-y-4 mt-8"> <form onSubmit={handleSubmit} className="space-y-4 mt-8">
<div> <div>
@ -71,14 +88,15 @@ const TeacherForm = ({ teacher, onSubmit, isNew, specialities }) => {
/> />
</div> </div>
<div className="flex space-x-4"> <div className="flex space-x-4">
<RadioList <CheckBoxList
items={specialities} items={specialities}
formData={formData} formData={formData}
handleChange={handleChange} handleChange={handleChange}
fieldName="specialite_id" fieldName="specialites_ids"
label="Spécialités" label="Spécialités"
icon={BookOpen} icon={BookOpen}
className="w-full mt-4" className="w-full mt-4"
itemLabelFunc={getSpecialityLabel}
/> />
</div> </div>
<div className='mt-4'> <div className='mt-4'>
@ -92,12 +110,12 @@ const TeacherForm = ({ teacher, onSubmit, isNew, specialities }) => {
<Button text="Créer" <Button text="Créer"
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.specialite_id) (!formData.nom || !formData.prenom || !formData.mail || formData.specialites_ids.length === 0)
? "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"
}`} }`}
primary primary
disabled={(!formData.nom || !formData.prenom || !formData.mail || !formData.specialite_id)} disabled={(!formData.nom || !formData.prenom || !formData.mail || formData.specialites_ids.length === 0)}
type="submit" type="submit"
name="Create" /> name="Create" />
</div> </div>

View File

@ -114,20 +114,19 @@ const TeachersSection = ({ teachers, handleCreate, handleEdit, handleDelete, spe
{ name: 'NOM', transform: (row) => row.nom }, { name: 'NOM', transform: (row) => row.nom },
{ 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: 'SPECIALITES',
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">
{row.specialites.map(specialite => (
<span <span
key={row.specialite.id} key={specialite.id}
className="w-4 h-4 rounded-full" className="w-4 h-4 rounded-full"
style={{ backgroundColor: row.specialite.codeCouleur }} style={{ backgroundColor: specialite.codeCouleur }}
title={row.specialite.nom} title={specialite.nom}
></span> ></span>
))}
</div> </div>
: <i>Non définie</i>; )
}
}, },
{ name: 'TYPE PROFIL', { name: 'TYPE PROFIL',
transform: (row) => { transform: (row) => {

View File

@ -1,6 +1,15 @@
import React from 'react'; import { useRef } from 'react';
const ToggleSwitch = ({ label, checked, onChange }) => { const ToggleSwitch = ({ label, checked, onChange }) => {
const inputRef = useRef(null);
const handleChange = (e) => {
onChange(e);
if (inputRef.current) {
inputRef.current.blur(); // Remove focus
}
};
return ( return (
<div className="flex items-center mt-4"> <div className="flex items-center mt-4">
<label className="mr-2 text-gray-600">{label}</label> <label className="mr-2 text-gray-600">{label}</label>
@ -10,8 +19,9 @@ const ToggleSwitch = ({ label, checked, onChange }) => {
name="toggle" name="toggle"
id="toggle" id="toggle"
checked={checked} checked={checked}
onChange={onChange} onChange={handleChange}
className="toggle-checkbox absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer border-emerald-500 checked:right-0 checked:border-emerald-500 checked:bg-emerald-500 focus:border-emerald-500 focus:bg-emerald-500 hover:border-emerald-500 hover:bg-emerald-500" className="toggle-checkbox absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer border-emerald-500 checked:right-0 checked:border-emerald-500 checked:bg-emerald-500 hover:border-emerald-500 hover:bg-emerald-500 focus:outline-none focus:ring-0"
ref={inputRef} // Reference to the input element
/> />
<label <label
htmlFor="toggle" htmlFor="toggle"