fix: Calcul du montant total des tarif par RF + affichage des tarifs

actifs lors de la création d'un RF [#26]
This commit is contained in:
N3WT DE COMPET
2025-02-07 20:36:02 +01:00
parent f2628bb45a
commit c269b89d3d
12 changed files with 192 additions and 70 deletions

View File

@ -8,27 +8,6 @@ from School.models import SchoolClass, Fee, Discount
from datetime import datetime from datetime import datetime
class RegistrationFee(models.Model):
"""
Représente un tarif ou frais dinscription avec différentes options de paiement.
"""
class PaymentOptions(models.IntegerChoices):
SINGLE_PAYMENT = 0, _('Paiement en une seule fois')
MONTHLY_PAYMENT = 1, _('Paiement mensuel')
QUARTERLY_PAYMENT = 2, _('Paiement trimestriel')
name = models.CharField(max_length=255, unique=True)
description = models.TextField(blank=True)
base_amount = models.DecimalField(max_digits=10, decimal_places=2)
discounts = models.JSONField(blank=True, null=True)
supplements = models.JSONField(blank=True, null=True)
validity_start_date = models.DateField()
validity_end_date = models.DateField()
payment_option = models.IntegerField(choices=PaymentOptions, default=PaymentOptions.SINGLE_PAYMENT)
def __str__(self):
return self.name
class Language(models.Model): class Language(models.Model):
""" """
Représente une langue parlée par lélève. Représente une langue parlée par lélève.

View File

@ -1,6 +1,6 @@
from rest_framework import serializers from rest_framework import serializers
from .models import RegistrationFileTemplate, RegistrationFile, RegistrationForm, Student, Guardian, Sibling, Language, RegistrationFee from .models import RegistrationFileTemplate, RegistrationFile, RegistrationForm, Student, Guardian, Sibling, Language
from School.models import SchoolClass, Fee, Discount from School.models import SchoolClass, Fee, Discount, FeeType
from School.serializers import FeeSerializer, DiscountSerializer from School.serializers import FeeSerializer, DiscountSerializer
from Auth.models import Profile from Auth.models import Profile
from Auth.serializers import ProfileSerializer from Auth.serializers import ProfileSerializer
@ -11,12 +11,6 @@ from django.utils import timezone
import pytz import pytz
from datetime import datetime from datetime import datetime
class RegistrationFeeSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False)
class Meta:
model = RegistrationFee
fields = '__all__'
class RegistrationFileSerializer(serializers.ModelSerializer): class RegistrationFileSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = RegistrationFile model = RegistrationFile
@ -136,6 +130,8 @@ class RegistrationFormSerializer(serializers.ModelSerializer):
registration_files = RegistrationFileSerializer(many=True, required=False) registration_files = RegistrationFileSerializer(many=True, required=False)
fees = serializers.PrimaryKeyRelatedField(queryset=Fee.objects.all(), many=True, required=False) fees = serializers.PrimaryKeyRelatedField(queryset=Fee.objects.all(), many=True, required=False)
discounts = serializers.PrimaryKeyRelatedField(queryset=Discount.objects.all(), many=True, required=False) discounts = serializers.PrimaryKeyRelatedField(queryset=Discount.objects.all(), many=True, required=False)
totalRegistrationFees = serializers.SerializerMethodField()
totalTuitionFees = serializers.SerializerMethodField()
class Meta: class Meta:
model = RegistrationForm model = RegistrationForm
@ -184,6 +180,14 @@ class RegistrationFormSerializer(serializers.ModelSerializer):
return local_time.strftime("%d-%m-%Y %H:%M") return local_time.strftime("%d-%m-%Y %H:%M")
def get_totalRegistrationFees(self, obj):
for fee in obj.fees.filter(type=FeeType.REGISTRATION_FEE):
print(fee.base_amount)
return sum(fee.base_amount for fee in obj.fees.filter(type=FeeType.REGISTRATION_FEE))
def get_totalTuitionFees(self, obj):
return sum(fee.base_amount for fee in obj.fees.filter(type=FeeType.TUITION_FEE))
class StudentByParentSerializer(serializers.ModelSerializer): class StudentByParentSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False) id = serializers.IntegerField(required=False)

View File

@ -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 .views import RegistrationFileTemplateView, RegisterFormListView, RegisterFormView, StudentView, GuardianView, ChildrenListView, StudentListView, RegistrationFeeView, RegistrationFileView from .views import RegistrationFileTemplateView, RegisterFormListView, RegisterFormView, StudentView, GuardianView, ChildrenListView, StudentListView, RegistrationFileView
urlpatterns = [ urlpatterns = [
re_path(r'^registerForms/(?P<_filter>[a-zA-z]+)$', RegisterFormListView.as_view(), name="registerForms"), re_path(r'^registerForms/(?P<_filter>[a-zA-z]+)$', RegisterFormListView.as_view(), name="registerForms"),
@ -30,9 +30,6 @@ urlpatterns = [
# Page INSCRIPTION - Liste des élèves # Page INSCRIPTION - Liste des élèves
re_path(r'^students$', StudentListView.as_view(), name="students"), re_path(r'^students$', StudentListView.as_view(), name="students"),
# Frais d'inscription
re_path(r'^registrationFees$', RegistrationFeeView.as_view(), name="registrationFees"),
# modèles de fichiers d'inscription # modèles de fichiers d'inscription
re_path(r'^registrationFileTemplates$', RegistrationFileTemplateView.as_view(), name='registrationFileTemplates'), re_path(r'^registrationFileTemplates$', RegistrationFileTemplateView.as_view(), name='registrationFileTemplates'),
re_path(r'^registrationFileTemplates/(?P<_id>[0-9]+)$', RegistrationFileTemplateView.as_view(), name="registrationFileTemplate"), re_path(r'^registrationFileTemplates/(?P<_id>[0-9]+)$', RegistrationFileTemplateView.as_view(), name="registrationFileTemplate"),

View File

@ -22,10 +22,10 @@ import Subscriptions.mailManager as mailer
import Subscriptions.util as util import Subscriptions.util as util
from Subscriptions.automate import Automate_RF_Register, load_config, getStateMachineObjectState, updateStateMachine from Subscriptions.automate import Automate_RF_Register, load_config, getStateMachineObjectState, updateStateMachine
from .serializers import RegistrationFormSerializer, StudentSerializer, RegistrationFormByParentSerializer, StudentByRFCreationSerializer, RegistrationFileSerializer, RegistrationFileTemplateSerializer, RegistrationFormByParentSerializer, StudentByRFCreationSerializer, RegistrationFeeSerializer from .serializers import RegistrationFormSerializer, StudentSerializer, RegistrationFormByParentSerializer, StudentByRFCreationSerializer, RegistrationFileSerializer, RegistrationFileTemplateSerializer, RegistrationFormByParentSerializer, StudentByRFCreationSerializer
from .pagination import CustomPagination from .pagination import CustomPagination
from .signals import clear_cache from .signals import clear_cache
from .models import Student, Guardian, RegistrationForm, RegistrationFee, RegistrationFileTemplate, RegistrationFile from .models import Student, Guardian, RegistrationForm, RegistrationFileTemplate, RegistrationFile
from .automate import Automate_RF_Register, load_config, getStateMachineObjectState, updateStateMachine from .automate import Automate_RF_Register, load_config, getStateMachineObjectState, updateStateMachine
from Auth.models import Profile from Auth.models import Profile
@ -335,16 +335,6 @@ class StudentListView(APIView):
students_serializer = StudentByRFCreationSerializer(students, many=True) students_serializer = StudentByRFCreationSerializer(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 RegistrationFeeView(APIView):
"""
Liste les frais dinscription.
"""
def get(self, request):
tarifs = bdd.getAllObjects(RegistrationFee)
tarifs_serializer = RegistrationFeeSerializer(tarifs, many=True)
return JsonResponse(tarifs_serializer.data, safe=False)
class RegistrationFileTemplateView(APIView): class RegistrationFileTemplateView(APIView):
""" """
Gère les fichiers templates pour les dossiers dinscription. Gère les fichiers templates pour les dossiers dinscription.

View File

@ -410,7 +410,9 @@ useEffect(()=>{
associated_profile: response.id associated_profile: response.id
}], }],
sibling: [] sibling: []
} },
fees: allFeesIds,
discounts: allDiscountsds
}; };
createRegisterForm(data, csrfToken) createRegisterForm(data, csrfToken)
@ -422,6 +424,7 @@ useEffect(()=>{
sendConfirmRegisterForm(data.student.id, updatedData.studentLastName, updatedData.studentFirstName); sendConfirmRegisterForm(data.student.id, updatedData.studentLastName, updatedData.studentFirstName);
} }
closeModal(); closeModal();
console.log('Success:', data);
// Forcer le rechargement complet des données // Forcer le rechargement complet des données
setReloadFetch(true); setReloadFetch(true);
}) })
@ -810,8 +813,8 @@ const handleFileUpload = ({file, name, is_required, order}) => {
<InscriptionForm students={students} <InscriptionForm students={students}
registrationDiscounts={registrationDiscounts} registrationDiscounts={registrationDiscounts}
tuitionDiscounts={tuitionDiscounts} tuitionDiscounts={tuitionDiscounts}
registrationFees={registrationFees} registrationFees={registrationFees.filter(fee => fee.is_active)}
tuitionFees={tuitionFees} tuitionFees={tuitionFees.filter(fee => fee.is_active)}
onSubmit={createRF} onSubmit={createRF}
/> />
)} )}

View File

@ -123,6 +123,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
}; };
const submit = () => { const submit = () => {
console.log('Submitting form data:', formData);
onSubmit(formData); onSubmit(formData);
} }

View File

@ -4,10 +4,15 @@ import ReactDOM from 'react-dom';
const Popup = ({ visible, message, onConfirm, onCancel, uniqueConfirmButton = false }) => { const Popup = ({ visible, message, onConfirm, onCancel, uniqueConfirmButton = false }) => {
if (!visible) return null; if (!visible) return null;
// Diviser le message en lignes
const messageLines = message.split('\n');
return ReactDOM.createPortal( return ReactDOM.createPortal(
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50"> <div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
<div className="bg-white p-6 rounded-md shadow-md"> <div className="bg-white p-6 rounded-md shadow-md">
<p className="mb-4">{message}</p> {messageLines.map((line, index) => (
<p key={index} className="mb-4">{line}</p>
))}
<div className={`flex ${uniqueConfirmButton ? 'justify-center' : 'justify-end'} gap-4`}> <div className={`flex ${uniqueConfirmButton ? 'justify-center' : 'justify-end'} gap-4`}>
{!uniqueConfirmButton && ( {!uniqueConfirmButton && (
<button className="px-4 py-2 bg-gray-200 rounded-md" onClick={onCancel}>Annuler</button> <button className="px-4 py-2 bg-gray-200 rounded-md" onClick={onCancel}>Annuler</button>

View File

@ -94,6 +94,9 @@ const ClassesSection = ({ classes, setClasses, teachers, handleCreate, handleEdi
const [localErrors, setLocalErrors] = useState({}); const [localErrors, setLocalErrors] = useState({});
const [popupVisible, setPopupVisible] = useState(false); const [popupVisible, setPopupVisible] = useState(false);
const [popupMessage, setPopupMessage] = useState(""); const [popupMessage, setPopupMessage] = useState("");
const [removePopupVisible, setRemovePopupVisible] = useState(false);
const [removePopupMessage, setRemovePopupMessage] = useState("");
const [removePopupOnConfirm, setRemovePopupOnConfirm] = useState(() => {});
const niveauxPremierCycle = [ const niveauxPremierCycle = [
{ id: 1, name: 'TPS', age: 2 }, { id: 1, name: 'TPS', age: 2 },
@ -377,7 +380,25 @@ const ClassesSection = ({ classes, setClasses, teachers, handleCreate, handleEdi
</button> </button>
<button <button
type="button" type="button"
onClick={() => handleDelete(classe.id)} onClick={() => {
setRemovePopupVisible(true);
setRemovePopupMessage("Attentions ! \nVous êtes sur le point de supprimer la classe " + classe.atmosphere_name + ".\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?");
setRemovePopupOnConfirm(() => () => {
handleDelete(classe.id)
.then(data => {
console.log('Success:', data);
setPopupMessage("La classe " + classe.atmosphere_name + " a été correctement supprimée");
setPopupVisible(true);
setRemovePopupVisible(false);
})
.catch(error => {
console.error('Error archiving data:', error);
setPopupMessage("Erreur lors de la suppression de la classe " + classe.atmosphere_name);
setPopupVisible(true);
setRemovePopupVisible(false);
});
});
}}
className="text-red-500 hover:text-red-700" className="text-red-500 hover:text-red-700"
> >
<Trash2 className="w-5 h-5" /> <Trash2 className="w-5 h-5" />
@ -432,6 +453,12 @@ const ClassesSection = ({ classes, setClasses, teachers, handleCreate, handleEdi
onCancel={() => setPopupVisible(false)} onCancel={() => setPopupVisible(false)}
uniqueConfirmButton={true} uniqueConfirmButton={true}
/> />
<Popup
visible={removePopupVisible}
message={removePopupMessage}
onConfirm={removePopupOnConfirm}
onCancel={() => setRemovePopupVisible(false)}
/>
</div> </div>
</DndProvider> </DndProvider>
); );

View File

@ -16,6 +16,10 @@ const SpecialitiesSection = ({ specialities, setSpecialities, handleCreate, hand
const [popupVisible, setPopupVisible] = useState(false); const [popupVisible, setPopupVisible] = useState(false);
const [popupMessage, setPopupMessage] = useState(""); const [popupMessage, setPopupMessage] = useState("");
const [removePopupVisible, setRemovePopupVisible] = useState(false);
const [removePopupMessage, setRemovePopupMessage] = useState("");
const [removePopupOnConfirm, setRemovePopupOnConfirm] = useState(() => {});
// Récupération des messages d'erreur // Récupération des messages d'erreur
const getError = (field) => { const getError = (field) => {
return localErrors?.[field]?.[0]; return localErrors?.[field]?.[0];
@ -26,7 +30,7 @@ const SpecialitiesSection = ({ specialities, setSpecialities, handleCreate, hand
}; };
const handleRemoveSpeciality = (id) => { const handleRemoveSpeciality = (id) => {
handleDelete(id) return handleDelete(id)
.then(() => { .then(() => {
setSpecialities(prevSpecialities => prevSpecialities.filter(speciality => speciality.id !== id)); setSpecialities(prevSpecialities => prevSpecialities.filter(speciality => speciality.id !== id));
}) })
@ -161,7 +165,25 @@ const SpecialitiesSection = ({ specialities, setSpecialities, handleCreate, hand
</button> </button>
<button <button
type="button" type="button"
onClick={() => handleRemoveSpeciality(speciality.id)} onClick={() => {
setRemovePopupVisible(true);
setRemovePopupMessage("Attentions ! \nVous êtes sur le point de supprimer la spécialité " + speciality.name + ".\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?");
setRemovePopupOnConfirm(() => () => {
handleRemoveSpeciality(speciality.id)
.then(data => {
console.log('Success:', data);
setPopupMessage("La spécialité " + speciality.name + " a été correctement supprimée");
setPopupVisible(true);
setRemovePopupVisible(false);
})
.catch(error => {
console.error('Error archiving data:', error);
setPopupMessage("Erreur lors de la suppression de la spécialité " + speciality.name);
setPopupVisible(true);
setRemovePopupVisible(false);
});
});
}}
className="text-red-500 hover:text-red-700" className="text-red-500 hover:text-red-700"
> >
<Trash2 className="w-5 h-5" /> <Trash2 className="w-5 h-5" />
@ -204,6 +226,12 @@ const SpecialitiesSection = ({ specialities, setSpecialities, handleCreate, hand
onCancel={() => setPopupVisible(false)} onCancel={() => setPopupVisible(false)}
uniqueConfirmButton={true} uniqueConfirmButton={true}
/> />
<Popup
visible={removePopupVisible}
message={removePopupMessage}
onConfirm={removePopupOnConfirm}
onCancel={() => setRemovePopupVisible(false)}
/>
</div> </div>
</DndProvider> </DndProvider>
); );

View File

@ -98,6 +98,10 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
const [popupVisible, setPopupVisible] = useState(false); const [popupVisible, setPopupVisible] = useState(false);
const [popupMessage, setPopupMessage] = useState(""); const [popupMessage, setPopupMessage] = useState("");
const [removePopupVisible, setRemovePopupVisible] = useState(false);
const [removePopupMessage, setRemovePopupMessage] = useState("");
const [removePopupOnConfirm, setRemovePopupOnConfirm] = useState(() => {});
// Récupération des messages d'erreur // Récupération des messages d'erreur
const getError = (field) => { const getError = (field) => {
return localErrors?.[field]?.[0]; return localErrors?.[field]?.[0];
@ -109,7 +113,7 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
}; };
const handleRemoveTeacher = (id) => { const handleRemoveTeacher = (id) => {
handleDelete(id) return handleDelete(id)
.then(() => { .then(() => {
setTeachers(prevTeachers => prevTeachers.filter(teacher => teacher.id !== id)); setTeachers(prevTeachers => prevTeachers.filter(teacher => teacher.id !== id));
}) })
@ -361,7 +365,25 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
</button> </button>
<button <button
type="button" type="button"
onClick={() => handleRemoveTeacher(teacher.id)} onClick={() => {
setRemovePopupVisible(true);
setRemovePopupMessage("Attentions ! \nVous êtes sur le point de supprimer l'enseignant " + teacher.last_name + " " + teacher.first_name + ".\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?");
setRemovePopupOnConfirm(() => () => {
handleRemoveTeacher(teacher.id)
.then(data => {
console.log('Success:', data);
setPopupMessage("L'enseignant " + teacher.last_name + " " + teacher.first_name + " a été correctement supprimé");
setPopupVisible(true);
setRemovePopupVisible(false);
})
.catch(error => {
console.error('Error archiving data:', error);
setPopupMessage("Erreur lors de la suppression de l'enseignant " + teacher.last_name + " " + teacher.first_name);
setPopupVisible(true);
setRemovePopupVisible(false);
});
});
}}
className="text-red-500 hover:text-red-700" className="text-red-500 hover:text-red-700"
> >
<Trash2 className="w-5 h-5" /> <Trash2 className="w-5 h-5" />
@ -407,6 +429,12 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
onCancel={() => setPopupVisible(false)} onCancel={() => setPopupVisible(false)}
uniqueConfirmButton={true} uniqueConfirmButton={true}
/> />
<Popup
visible={removePopupVisible}
message={removePopupMessage}
onConfirm={removePopupOnConfirm}
onCancel={() => setRemovePopupVisible(false)}
/>
</div> </div>
</DndProvider> </DndProvider>
); );

View File

@ -12,13 +12,16 @@ const DiscountsSection = ({ discounts, setDiscounts, handleCreate, handleEdit, h
const [localErrors, setLocalErrors] = useState({}); const [localErrors, setLocalErrors] = useState({});
const [popupVisible, setPopupVisible] = useState(false); const [popupVisible, setPopupVisible] = useState(false);
const [popupMessage, setPopupMessage] = useState(""); const [popupMessage, setPopupMessage] = useState("");
const [removePopupVisible, setRemovePopupVisible] = useState(false);
const [removePopupMessage, setRemovePopupMessage] = useState("");
const [removePopupOnConfirm, setRemovePopupOnConfirm] = useState(() => {});
const handleAddDiscount = () => { const handleAddDiscount = () => {
setNewDiscount({ id: Date.now(), name: '', amount: '', description: '', discount_type: 0, type: type }); setNewDiscount({ id: Date.now(), name: '', amount: '', description: '', discount_type: 0, type: type });
}; };
const handleRemoveDiscount = (id) => { const handleRemoveDiscount = (id) => {
handleDelete(id) return handleDelete(id)
.then(() => { .then(() => {
setDiscounts(prevDiscounts => prevDiscounts.filter(discount => discount.id !== id)); setDiscounts(prevDiscounts => prevDiscounts.filter(discount => discount.id !== id));
}) })
@ -204,7 +207,25 @@ const DiscountsSection = ({ discounts, setDiscounts, handleCreate, handleEdit, h
</button> </button>
<button <button
type="button" type="button"
onClick={() => handleRemoveDiscount(discount.id)} onClick={() => {
setRemovePopupVisible(true);
setRemovePopupMessage("Attentions ! \nVous êtes sur le point de supprimer un tarif personnalisé.\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?");
setRemovePopupOnConfirm(() => () => {
handleRemoveDiscount(discount.id)
.then(data => {
console.log('Success:', data);
setPopupMessage("Réduction correctement supprimé");
setPopupVisible(true);
setRemovePopupVisible(false);
})
.catch(error => {
console.error('Error archiving data:', error);
setPopupMessage("Erreur lors de la suppression de la réduction");
setPopupVisible(true);
setRemovePopupVisible(false);
});
});
}}
className="text-red-500 hover:text-red-700" className="text-red-500 hover:text-red-700"
> >
<Trash2 className="w-5 h-5" /> <Trash2 className="w-5 h-5" />
@ -269,6 +290,12 @@ const DiscountsSection = ({ discounts, setDiscounts, handleCreate, handleEdit, h
onCancel={() => setPopupVisible(false)} onCancel={() => setPopupVisible(false)}
uniqueConfirmButton={true} uniqueConfirmButton={true}
/> />
<Popup
visible={removePopupVisible}
message={removePopupMessage}
onConfirm={removePopupOnConfirm}
onCancel={() => setRemovePopupVisible(false)}
/>
</div> </div>
); );
}; };

View File

@ -12,13 +12,22 @@ const FeesSection = ({ fees, setFees, discounts, handleCreate, handleEdit, handl
const [localErrors, setLocalErrors] = useState({}); const [localErrors, setLocalErrors] = useState({});
const [popupVisible, setPopupVisible] = useState(false); const [popupVisible, setPopupVisible] = useState(false);
const [popupMessage, setPopupMessage] = useState(""); const [popupMessage, setPopupMessage] = useState("");
const [removePopupVisible, setRemovePopupVisible] = useState(false);
const [removePopupMessage, setRemovePopupMessage] = useState("");
const [removePopupOnConfirm, setRemovePopupOnConfirm] = useState(() => {});
const labelTypeFrais = (type === 0 ? 'Frais d\'inscription' : 'Frais de scolarité');
// Récupération des messages d'erreur
const getError = (field) => {
return localErrors?.[field]?.[0];
};
const handleAddFee = () => { const handleAddFee = () => {
setNewFee({ id: Date.now(), name: '', base_amount: '', description: '', validity_start_date: '', validity_end_date: '', discounts: [], type: type }); setNewFee({ id: Date.now(), name: '', base_amount: '', description: '', validity_start_date: '', validity_end_date: '', discounts: [], type: type });
}; };
const handleRemoveFee = (id) => { const handleRemoveFee = (id) => {
handleDelete(id) return handleDelete(id)
.then(() => { .then(() => {
setFees(prevFees => prevFees.filter(fee => fee.id !== id)); setFees(prevFees => prevFees.filter(fee => fee.id !== id));
}) })
@ -37,11 +46,11 @@ const FeesSection = ({ fees, setFees, discounts, handleCreate, handleEdit, handl
setNewFee(null); setNewFee(null);
setLocalErrors({}); setLocalErrors({});
}) })
.catch(error => { .catch((error) => {
if (error && typeof error === 'object') { console.error('Error:', error.message);
setLocalErrors(error); if (error.details) {
} else { console.error('Form errors:', error.details);
console.error(error); setLocalErrors(error.details);
} }
}); });
} else { } else {
@ -60,11 +69,11 @@ const FeesSection = ({ fees, setFees, discounts, handleCreate, handleEdit, handl
setEditingFee(null); setEditingFee(null);
setLocalErrors({}); setLocalErrors({});
}) })
.catch(error => { .catch((error) => {
if (error && typeof error === 'object') { console.error('Error:', error.message);
setLocalErrors(error); if (error.details) {
} else { console.error('Form errors:', error.details);
console.error(error); setLocalErrors(error.details);
} }
}); });
} else { } else {
@ -118,7 +127,7 @@ const FeesSection = ({ fees, setFees, discounts, handleCreate, handleEdit, handl
value={value} value={value}
onChange={onChange} onChange={onChange}
placeholder={placeholder} placeholder={placeholder}
errorMsg={localErrors && localErrors[field] && Array.isArray(localErrors[field]) ? localErrors[field][0] : ''} errorMsg={getError(field)}
/> />
</div> </div>
); );
@ -187,7 +196,25 @@ const FeesSection = ({ fees, setFees, discounts, handleCreate, handleEdit, handl
</button> </button>
<button <button
type="button" type="button"
onClick={() => handleRemoveFee(fee.id)} onClick={() => {
setRemovePopupVisible(true);
setRemovePopupMessage("Attentions ! \nVous êtes sur le point de supprimer un " + labelTypeFrais + ".\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?");
setRemovePopupOnConfirm(() => () => {
handleRemoveFee(fee.id)
.then(data => {
console.log('Success:', data);
setPopupMessage(labelTypeFrais + " correctement supprimé");
setPopupVisible(true);
setRemovePopupVisible(false);
})
.catch(error => {
console.error('Error archiving data:', error);
setPopupMessage("Erreur lors de la suppression du " + labelTypeFrais);
setPopupVisible(true);
setRemovePopupVisible(false);
});
});
}}
className="text-red-500 hover:text-red-700" className="text-red-500 hover:text-red-700"
> >
<Trash2 className="w-5 h-5" /> <Trash2 className="w-5 h-5" />
@ -232,7 +259,7 @@ const FeesSection = ({ fees, setFees, discounts, handleCreate, handleEdit, handl
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<div className="flex items-center mb-4"> <div className="flex items-center mb-4">
<CreditCard className="w-6 h-6 text-emerald-500 mr-2" /> <CreditCard className="w-6 h-6 text-emerald-500 mr-2" />
<h2 className="text-xl font-semibold">{type === 0 ? 'Frais d\'inscription' : 'Frais de scolarité'}</h2> <h2 className="text-xl font-semibold">{labelTypeFrais}</h2>
</div> </div>
<button type="button" onClick={handleAddFee} className="text-emerald-500 hover:text-emerald-700"> <button type="button" onClick={handleAddFee} className="text-emerald-500 hover:text-emerald-700">
<Plus className="w-5 h-5" /> <Plus className="w-5 h-5" />
@ -251,6 +278,12 @@ const FeesSection = ({ fees, setFees, discounts, handleCreate, handleEdit, handl
onCancel={() => setPopupVisible(false)} onCancel={() => setPopupVisible(false)}
uniqueConfirmButton={true} uniqueConfirmButton={true}
/> />
<Popup
visible={removePopupVisible}
message={removePopupMessage}
onConfirm={removePopupOnConfirm}
onCancel={() => setRemovePopupVisible(false)}
/>
</div> </div>
); );
}; };