feat: Mise en place des paiements en plusieurs fois (partie BACK) [#25]

This commit is contained in:
N3WT DE COMPET
2025-02-11 20:48:11 +01:00
parent ffc6ce8de8
commit 23203c0397
17 changed files with 599 additions and 86 deletions

View File

@ -9,15 +9,16 @@ import { ClassesProvider } from '@/context/ClassesContext';
import { createDatas,
updateDatas,
removeDatas,
fetchSpecialities,
fetchTeachers,
fetchClasses,
fetchSchedules,
fetchRegistrationDiscounts,
fetchTuitionDiscounts,
fetchRegistrationFees,
fetchSpecialities,
fetchTeachers,
fetchClasses,
fetchSchedules,
fetchRegistrationDiscounts,
fetchTuitionDiscounts,
fetchRegistrationFees,
fetchTuitionFees,
} from '@/app/lib/schoolAction';
fetchRregistrationPaymentPlans,
fetchTuitionPaymentPlans } from '@/app/lib/schoolAction';
import SidebarTabs from '@/components/SidebarTabs';
import FilesManagement from '@/components/Structure/Files/FilesManagement';
@ -35,6 +36,8 @@ export default function Page() {
const [registrationFees, setRegistrationFees] = useState([]);
const [tuitionFees, setTuitionFees] = useState([]);
const [fichiers, setFichiers] = useState([]);
const [registrationPaymentPlans, setRegistrationPaymentPlans] = useState([]);
const [tuitionPaymentPlans, setTuitionPaymentPlans] = useState([]);
const csrfToken = useCsrfToken();
@ -70,7 +73,11 @@ export default function Page() {
})
.catch(error => console.error('Error fetching files:', error));
// Fetch data for registration payment plans
handleRegistrationPaymentPlans();
// Fetch data for tuition payment plans
handleTuitionPaymentPlans();
}, []);
const handleSpecialities = () => {
@ -137,6 +144,22 @@ export default function Page() {
.catch(error => console.error('Error fetching tuition fees', error));
};
const handleRegistrationPaymentPlans = () => {
fetchRregistrationPaymentPlans()
.then(data => {
setRegistrationPaymentPlans(data);
})
.catch(error => console.error('Error fetching registration payment plans:', error));
};
const handleTuitionPaymentPlans = () => {
fetchTuitionPaymentPlans()
.then(data => {
setTuitionPaymentPlans(data);
})
.catch(error => console.error('Error fetching tuition payment plans:', error));
};
const handleCreate = (url, newData, setDatas) => {
return createDatas(url, newData, csrfToken)
.then(data => {
@ -236,6 +259,11 @@ export default function Page() {
setRegistrationFees={setRegistrationFees}
tuitionFees={tuitionFees}
setTuitionFees={setTuitionFees}
registrationPaymentPlans={registrationPaymentPlans}
setRegistrationPaymentPlans={setRegistrationPaymentPlans}
tuitionPaymentPlans={tuitionPaymentPlans}
setTuitionPaymentPlans={setTuitionPaymentPlans}
setRegist
handleCreate={handleCreate}
handleEdit={handleEdit}
handleDelete={handleDelete}

View File

@ -4,7 +4,8 @@ import {
BE_SCHOOL_SCHOOLCLASSES_URL,
BE_SCHOOL_PLANNINGS_URL,
BE_SCHOOL_FEES_URL,
BE_SCHOOL_DISCOUNTS_URL
BE_SCHOOL_DISCOUNTS_URL,
BE_SCHOOL_PAYMENT_PLANS_URL,
} from '@/utils/Url';
const requestResponseHandler = async (response) => {
@ -60,6 +61,16 @@ export const fetchTuitionFees = () => {
.then(requestResponseHandler)
};
export const fetchRregistrationPaymentPlans = () => {
return fetch(`${BE_SCHOOL_PAYMENT_PLANS_URL}/registration`)
.then(requestResponseHandler)
}
export const fetchTuitionPaymentPlans = () => {
return fetch(`${BE_SCHOOL_PAYMENT_PLANS_URL}/tuition`)
.then(requestResponseHandler)
}
export const createDatas = (url, newData, csrfToken) => {
return fetch(url, {
method: 'POST',

View File

@ -3,7 +3,6 @@ import React from 'react';
const CheckBox = ({ item, formData, handleChange, fieldName, itemLabelFunc = () => null, labelAttenuated = () => false, horizontal }) => {
const isChecked = formData[fieldName].includes(parseInt(item.id));
const isAttenuated = labelAttenuated(item) && !isChecked;
return (
<div key={item.id} className={`flex ${horizontal ? 'flex-col items-center' : 'flex-row items-center'}`}>
{horizontal && (

View File

@ -14,7 +14,7 @@ const CheckBoxList = ({
horizontal = false // Ajouter l'option horizontal
}) => {
return (
<div className={`mb-4 ${className}`}>
<div className={`mb-4 w-full ${className}`}>
<label className="block text-sm font-medium text-gray-700 flex items-center">
{Icon && <Icon className="w-5 h-5 mr-2" />}
{label}

View File

@ -0,0 +1,66 @@
import React, { useState } from 'react';
import { Check } from 'lucide-react';
import Popup from '@/components/Popup';
const DateTab = ({ dates, activeTab, handleDateChange, handleEdit, type, paymentPlanId }) => {
const [popupVisible, setPopupVisible] = useState(false);
const [popupMessage, setPopupMessage] = useState("");
const [modifiedDates, setModifiedDates] = useState({});
const submit = (updatedData) => {
const dataWithType = {
...updatedData,
type: type
};
handleEdit(paymentPlanId, dataWithType)
.then(() => {
setPopupMessage(`Mise à jour de la date d'échéance effectuée avec succès`);
setPopupVisible(true);
setModifiedDates({});
})
.catch(error => {
console.error(error);
});
};
const handleDateChangeWithModification = (tab, index, value) => {
handleDateChange(tab, index, value);
setModifiedDates(prev => ({ ...prev, [`${tab}-${index}`]: true }));
};
return (
<div className="bg-white p-4 rounded-lg h-auto max-h-96 overflow-y-auto">
<div className="flex flex-col space-y-3">
{dates[activeTab]?.map((date, index) => (
<div key={index} className="flex items-center space-x-3">
<span className="text-emerald-700 font-semibold">Échéance {index + 1}</span>
<input
type="date"
value={date}
onChange={(e) => handleDateChangeWithModification(activeTab, index, e.target.value)}
className="p-2 border border-emerald-300 rounded focus:outline-none focus:ring-2 focus:ring-emerald-500 cursor-pointer"
/>
{modifiedDates[`${activeTab}-${index}`] && (
<button
type="button"
onClick={() => submit({ frequency: dates[activeTab].length, due_dates: dates[activeTab] })}
className="text-emerald-500 hover:text-emerald-800"
>
<Check className="w-5 h-5" />
</button>
)}
</div>
))}
</div>
<Popup
visible={popupVisible}
message={popupMessage}
onConfirm={() => setPopupVisible(false)}
onCancel={() => setPopupVisible(false)}
uniqueConfirmButton={true}
/>
</div>
);
};
export default DateTab;

View File

@ -1,4 +1,4 @@
export default function InputTextIcon({name, type, IconItem, label, value, onChange, errorMsg, placeholder, className}) {
export default function InputTextIcon({name, type, IconItem, label, value, onChange, errorMsg, placeholder, className, min, max}) {
return (
<>
<div className={`${className}`}>
@ -15,6 +15,8 @@ export default function InputTextIcon({name, type, IconItem, label, value, onCha
value={value}
onChange={onChange}
className="flex-1 px-3 py-2 block w-full rounded-r-md sm:text-sm border-none focus:ring-0 outline-none"
min={min}
max={max}
/>
</div>
{errorMsg && <p className="mt-2 text-sm text-red-600">{errorMsg}</p>}

View File

@ -0,0 +1,44 @@
import React from 'react';
import CheckBoxList from '@/components/CheckBoxList';
import { CreditCard } from 'lucide-react';
const paymentModes = [
{ id: 1, name: 'Prélèvement SEPA' },
{ id: 2, name: 'Virement' },
{ id: 3, name: 'Chèque' },
{ id: 4, name: 'Espèce' },
];
const PaymentModeSelector = ({ formData, setFormData, fieldName }) => {
const handleCheckboxChange = (event) => {
const value = parseInt(event.target.value, 10);
setFormData((prevFormData) => {
const selectedModes = prevFormData[fieldName] || [];
const newSelectedModes = selectedModes.includes(value)
? selectedModes.filter((mode) => mode !== value)
: [...selectedModes, value];
return { ...prevFormData, [fieldName]: newSelectedModes };
});
};
return (
<div className="mb-4 w-full">
<div className="flex justify-center bg-gray p-4 rounded-lg shadow-md w-full">
<CreditCard className="w-6 h-6 text-emerald-500 mr-2" />
<h2 className="text-xl font-semibold">Mode de paiement</h2>
<CheckBoxList
items={paymentModes}
formData={formData}
handleChange={handleCheckboxChange}
fieldName={fieldName}
itemLabelFunc={(item) => (
<span className="text-sm font-medium">{item.name}</span>
)}
horizontal={true}
/>
</div>
</div>
);
};
export default PaymentModeSelector;

View File

@ -0,0 +1,248 @@
import React, { useState, useEffect } from 'react';
import { Calendar, Eye, EyeOff, Clock, Check } from 'lucide-react';
import Table from '@/components/Table';
import DateTab from '@/components/DateTab';
import InputTextIcon from '@/components/InputTextIcon';
import Popup from '@/components/Popup';
const paymentPlansOptions = [
{ id: 0, name: '1 fois', frequency: 1 },
{ id: 1, name: '3 fois', frequency: 3 },
{ id: 2, name: '10 fois', frequency: 10 },
{ id: 3, name: '12 fois', frequency: 12 },
];
const PaymentPlanSelector = ({ paymentPlans, setPaymentPlans, handleEdit, type }) => {
const [dates, setDates] = useState({});
const [selectedFrequency, setSelectedFrequency] = useState(null);
const [activeFrequencies, setActiveFrequencies] = useState([]);
const [defaultDay, setDefaultDay] = useState(new Date().getDate());
const [isDefaultDayModified, setIsDefaultDayModified] = useState(false);
const [popupVisible, setPopupVisible] = useState(false);
const [popupMessage, setPopupMessage] = useState("");
useEffect(() => {
if (paymentPlans && paymentPlans.length > 0) {
const activePlans = paymentPlans.filter(plan => plan.is_active);
const frequencies = activePlans.map(plan => {
const paymentPlanOption = paymentPlansOptions.find(p => p.frequency === plan.frequency);
return paymentPlanOption ? paymentPlanOption.id : null;
}).filter(id => id !== null);
setActiveFrequencies(frequencies);
if (activePlans.length > 0) {
const firstDueDate = new Date(activePlans[0].due_dates[0]);
setDefaultDay(firstDueDate.getDate());
}
const initialDates = {};
paymentPlans.forEach(plan => {
const paymentPlanOption = paymentPlansOptions.find(p => p.frequency === plan.frequency);
if (paymentPlanOption) {
initialDates[paymentPlanOption.id] = plan.due_dates;
}
});
setDates(initialDates);
}
}, [paymentPlans]);
const handleActivationChange = (value) => {
const selectedPlan = paymentPlans.find(plan => plan.frequency === paymentPlansOptions.find(p => p.id === value)?.frequency);
if (!selectedPlan) return;
const updatedData = {
...selectedPlan,
is_active: !selectedPlan.is_active
};
handleEdit(selectedPlan.id, updatedData)
.then(() => {
setPaymentPlans(prevPlans => prevPlans.map(plan =>
plan.id === selectedPlan.id ? { ...plan, is_active: updatedData.is_active } : plan
));
setActiveFrequencies(prevFrequencies => {
if (updatedData.is_active) {
setPopupMessage(`L'option de paiement en ${paymentPlansOptions.find(p => p.id === value).name} a été activée.`);
setPopupVisible(true);
return [...prevFrequencies, value];
} else {
setPopupMessage(`L'option de paiement en ${paymentPlansOptions.find(p => p.id === value).name} a été désactivée.`);
setPopupVisible(true);
return prevFrequencies.filter(item => item !== value);
}
});
})
.catch(error => {
console.error(error);
});
};
const handleRowClick = (row) => {
const value = row.id;
if (selectedFrequency === value) {
setSelectedFrequency(null); // Désélectionner l'onglet si la ligne est déjà sélectionnée
} else {
setSelectedFrequency(value);
if (!dates[value]) {
const frequencyValue = paymentPlansOptions.find(plan => plan.id === value)?.frequency || 1;
const newDates = Array(frequencyValue).fill('').map((_, index) => {
const newDate = new Date();
newDate.setDate(defaultDay);
if (value === 1) {
newDate.setMonth(newDate.getMonth() + index * 4); // Espacer de 4 mois pour le paiement en 3 fois
} else {
newDate.setMonth(newDate.getMonth() + index);
}
return newDate.toISOString().split('T')[0];
});
setDates(prevDates => ({ ...prevDates, [value]: newDates }));
}
}
};
const handleDateChange = (planId, index, date) => {
setDates((prevDates) => {
const newDates = { ...prevDates };
newDates[planId][index] = date;
return newDates;
});
};
const handleDefaultDayChange = (e) => {
const newDefaultDay = parseInt(e.target.value, 10);
if (newDefaultDay >= 1 && newDefaultDay <= 31) {
setDefaultDay(newDefaultDay);
setIsDefaultDayModified(true);
if (selectedFrequency !== null) {
const frequencyValue = paymentPlansOptions.find(plan => plan.id === selectedFrequency)?.frequency || 1;
const newDates = Array(frequencyValue).fill('').map((_, index) => {
const newDate = new Date();
newDate.setDate(newDefaultDay);
if (selectedFrequency === 1) {
newDate.setMonth(newDate.getMonth() + index * 4); // Espacer de 4 mois pour le paiement en 3 fois
} else {
newDate.setMonth(newDate.getMonth() + index);
}
return newDate.toISOString().split('T')[0];
});
setDates(prevDates => ({ ...prevDates, [selectedFrequency]: newDates }));
}
}
};
const handleSubmitDefaultDay = () => {
const selectedPlan = paymentPlans.find(plan => plan.frequency === paymentPlansOptions.find(p => p.id === selectedFrequency)?.frequency);
if (!selectedPlan) return;
const updatedData = {
...selectedPlan,
due_dates: dates[selectedFrequency]
};
handleEdit(selectedPlan.id, updatedData)
.then(() => {
setPopupMessage(`Mise à jour des dates d'échéances effectuée avec succès`);
setPopupVisible(true);
setIsDefaultDayModified(false); // Réinitialiser l'état de modification après la soumission
})
.catch(error => {
console.error(error);
});
};
const columns = [
{ name: 'OPTIONS', label: 'Option' },
{ name: 'ACTIONS', label: 'Action' },
];
const renderCell = (row, column) => {
switch (column) {
case 'OPTIONS':
return <span className="text-sm font-medium text-gray-900">{row.name}</span>;
case 'ACTIONS':
return (
<button
type="button"
onClick={(e) => {
e.stopPropagation();
handleActivationChange(row.id);
}}
className={activeFrequencies.includes(row.id) ? 'text-emerald-500 hover:text-emerald-700' : 'text-orange-500 hover:text-orange-700'}
>
{activeFrequencies.includes(row.id) ? <Eye className="w-5 h-5" /> : <EyeOff className="w-5 h-5" />}
</button>
);
default:
return null;
}
};
const selectedPaymentPlan = paymentPlans.find(plan => plan.frequency === paymentPlansOptions.find(p => p.id === selectedFrequency)?.frequency);
return (
<div className="space-y-4">
<div className="flex items-center mb-4">
<Calendar className="w-6 h-6 text-emerald-500 mr-2" />
<h2 className="text-xl font-semibold">Paiement en plusieurs fois</h2>
</div>
<div className="grid grid-cols-2 gap-4">
<Table
data={paymentPlansOptions}
columns={columns}
renderCell={renderCell}
isSelectable={true}
onRowClick={handleRowClick}
selectedRows={selectedFrequency !== null ? [selectedFrequency] : []}
/>
{selectedFrequency !== null && selectedPaymentPlan && (
<div>
<div className="flex items-center space-x-3">
<div className="flex-grow">
<InputTextIcon
name="defaultDay"
type="number"
IconItem={Clock}
label="Jour d'échéance"
value={defaultDay}
onChange={handleDefaultDayChange}
placeholder="Jour d'échéance"
errorMsg=""
min={1}
max={31}
/>
</div>
{isDefaultDayModified && (
<button
type="button"
onClick={handleSubmitDefaultDay}
className="text-emerald-500 hover:text-emerald-700 ml-2 cursor-pointer"
style={{ marginTop: '1.75rem' }} // Adjust this value to align with the input
>
<Check className="w-5 h-5" />
</button>
)}
</div>
<DateTab
dates={dates}
activeTab={selectedFrequency}
handleDateChange={handleDateChange}
handleEdit={handleEdit}
type={type}
paymentPlanId={selectedPaymentPlan.id}
/>
</div>
)}
</div>
<Popup
visible={popupVisible}
message={popupMessage}
onConfirm={() => setPopupVisible(false)}
onCancel={() => setPopupVisible(false)}
uniqueConfirmButton={true}
/>
</div>
);
};
export default PaymentPlanSelector;

View File

@ -119,15 +119,17 @@ const DiscountsSection = ({ discounts, setDiscounts, handleCreate, handleEdit, h
};
const renderInputField = (field, value, onChange, placeholder) => (
<div>
<InputText
name={field}
type={field === 'amount' ? 'number' : 'text'}
value={value}
onChange={onChange}
placeholder={placeholder}
errorMsg={localErrors && localErrors[field] && Array.isArray(localErrors[field]) ? localErrors[field][0] : ''}
/>
<div className="flex justify-center space-x-2">
<div className="w-full max-w-xs">
<InputText
name={field}
type={field === 'amount' ? 'number' : 'text'}
value={value}
onChange={onChange}
placeholder={placeholder}
errorMsg={localErrors && localErrors[field] && Array.isArray(localErrors[field]) ? localErrors[field][0] : ''}
/>
</div>
</div>
);
@ -142,7 +144,7 @@ const DiscountsSection = ({ discounts, setDiscounts, handleCreate, handleEdit, h
return renderInputField('name', currentData.name, handleChange, 'Libellé de la réduction');
case 'REMISE':
return (
<div className="flex items-center space-x-2">
<div className="flex justify-center space-x-2">
{renderInputField('amount', currentData.amount, handleChange,'Montant')}
<button
@ -270,7 +272,7 @@ const DiscountsSection = ({ discounts, setDiscounts, handleCreate, handleEdit, h
<div className="flex justify-between items-center">
<div className="flex items-center mb-4">
<Tag className="w-6 h-6 text-emerald-500 mr-2" />
<h2 className="text-xl font-semibold">Réductions {type === 0 ? 'd\'inscription' : 'de scolarité'}</h2>
<h2 className="text-xl font-semibold">Liste des réductions</h2>
</div>
<button type="button" onClick={handleAddDiscount} className="text-emerald-500 hover:text-emerald-700">
<Plus className="w-5 h-5" />

View File

@ -1,9 +1,26 @@
import React from 'react';
import React, { useState } from 'react';
import FeesSection from '@/components/Structure/Tarification/FeesSection';
import DiscountsSection from '@/components/Structure/Tarification/DiscountsSection';
import { BE_SCHOOL_FEE_URL, BE_SCHOOL_DISCOUNT_URL } from '@/utils/Url';
import PaymentPlanSelector from '@/components/PaymentPlanSelector';
import PaymentModeSelector from '@/components/PaymentModeSelector';
import { BE_SCHOOL_FEE_URL, BE_SCHOOL_DISCOUNT_URL, BE_SCHOOL_PAYMENT_PLAN_URL } from '@/utils/Url';
import { set } from 'lodash';
const FeesManagement = ({ registrationDiscounts, setRegistrationDiscounts, tuitionDiscounts, setTuitionDiscounts, registrationFees, setRegistrationFees, tuitionFees, setTuitionFees, handleCreate, handleEdit, handleDelete }) => {
const FeesManagement = ({ registrationDiscounts,
setRegistrationDiscounts,
tuitionDiscounts,
setTuitionDiscounts,
registrationFees,
setRegistrationFees,
tuitionFees,
setTuitionFees,
registrationPaymentPlans,
setRegistrationPaymentPlans,
tuitionPaymentPlans,
setTuitionPaymentPlans,
handleCreate,
handleEdit,
handleDelete }) => {
const handleDiscountDelete = (id, type) => {
if (type === 0) {
@ -24,53 +41,82 @@ const FeesManagement = ({ registrationDiscounts, setRegistrationDiscounts, tuiti
};
return (
<div className="max-w-8xl mx-auto p-4 mt-6 space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="bg-white p-6 rounded-lg shadow-md">
<FeesSection
fees={registrationFees}
setFees={setRegistrationFees}
discounts={registrationDiscounts}
handleCreate={(newData) => handleCreate(`${BE_SCHOOL_FEE_URL}`, newData, setRegistrationFees)}
handleEdit={(id, updatedData) => handleEdit(`${BE_SCHOOL_FEE_URL}`, id, updatedData, setRegistrationFees)}
handleDelete={(id) => handleDelete(`${BE_SCHOOL_FEE_URL}`, id, setRegistrationFees)}
type={0}
/>
</div>
<div className="bg-white p-6 rounded-lg shadow-md">
<DiscountsSection
discounts={registrationDiscounts}
setDiscounts={setRegistrationDiscounts}
handleCreate={(newData) => handleCreate(`${BE_SCHOOL_DISCOUNT_URL}`, newData, setRegistrationDiscounts)}
handleEdit={(id, updatedData) => handleEdit(`${BE_SCHOOL_DISCOUNT_URL}`, id, updatedData, setRegistrationDiscounts)}
handleDelete={(id) => handleDelete(`${BE_SCHOOL_DISCOUNT_URL}`, id, setRegistrationDiscounts)}
onDiscountDelete={(id) => handleDiscountDelete(id, 0)}
type={0}
/>
<div className="w-full mx-auto p-2 mt-6 space-y-6">
<div className="bg-white p-2 rounded-lg shadow-md">
<h2 className="text-2xl font-semibold mb-4">Frais d'inscription</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="col-span-1 p-6 rounded-lg shadow-inner mt-4">
<FeesSection
fees={registrationFees}
setFees={setRegistrationFees}
discounts={registrationDiscounts}
handleCreate={(newData) => handleCreate(`${BE_SCHOOL_FEE_URL}`, newData, setRegistrationFees)}
handleEdit={(id, updatedData) => handleEdit(`${BE_SCHOOL_FEE_URL}`, id, updatedData, setRegistrationFees)}
handleDelete={(id) => handleDelete(`${BE_SCHOOL_FEE_URL}`, id, setRegistrationFees)}
type={0}
/>
</div>
<div className="col-span-1 p-6 rounded-lg shadow-inner mt-4">
<DiscountsSection
discounts={registrationDiscounts}
setDiscounts={setRegistrationDiscounts}
handleCreate={(newData) => handleCreate(`${BE_SCHOOL_DISCOUNT_URL}`, newData, setRegistrationDiscounts)}
handleEdit={(id, updatedData) => handleEdit(`${BE_SCHOOL_DISCOUNT_URL}`, id, updatedData, setRegistrationDiscounts)}
handleDelete={(id) => handleDelete(`${BE_SCHOOL_DISCOUNT_URL}`, id, setRegistrationDiscounts)}
onDiscountDelete={(id) => handleDiscountDelete(id, 0)}
type={0}
/>
</div>
<div className="col-span-1 p-6 rounded-lg shadow-inner mt-4">
<PaymentPlanSelector
paymentPlans={registrationPaymentPlans}
setPaymentPlans={setRegistrationPaymentPlans}
handleEdit={(id, updatedData) => handleEdit(`${BE_SCHOOL_PAYMENT_PLAN_URL}`, id, updatedData, setRegistrationPaymentPlans)}
type={0}
/>
</div>
{/* <div className="col-span-1">
<PaymentModeSelector
formData={formRegistrationData}
setFormData={setFormRegistrationData}
fieldName="paymentRegistrationMode"
/>
</div> */}
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="bg-white p-6 rounded-lg shadow-md">
<FeesSection
fees={tuitionFees}
setFees={setTuitionFees}
discounts={tuitionDiscounts}
handleCreate={(newData) => handleCreate(`${BE_SCHOOL_FEE_URL}`, newData, setTuitionFees)}
handleEdit={(id, updatedData) => handleEdit(`${BE_SCHOOL_FEE_URL}`, id, updatedData, setTuitionFees)}
handleDelete={(id) => handleDelete(`${BE_SCHOOL_FEE_URL}`, id, setTuitionFees)}
type={1}
/>
</div>
<div className="bg-white p-6 rounded-lg shadow-md">
<DiscountsSection
discounts={tuitionDiscounts}
setDiscounts={setTuitionDiscounts}
handleCreate={(newData) => handleCreate(`${BE_SCHOOL_DISCOUNT_URL}`, newData, setTuitionDiscounts)}
handleEdit={(id, updatedData) => handleEdit(`${BE_SCHOOL_DISCOUNT_URL}`, id, updatedData, setTuitionDiscounts)}
handleDelete={(id) => handleDelete(`${BE_SCHOOL_DISCOUNT_URL}`, id, setTuitionDiscounts)}
onDiscountDelete={(id) => handleDiscountDelete(id, 1)}
type={1}
/>
<div className="bg-white p-2 rounded-lg shadow-md">
<h2 className="text-2xl font-semibold mb-4">Frais de scolarité</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="col-span-1 p-6 rounded-lg shadow-inner mt-4">
<FeesSection
fees={tuitionFees}
setFees={setTuitionFees}
discounts={tuitionDiscounts}
handleCreate={(newData) => handleCreate(`${BE_SCHOOL_FEE_URL}`, newData, setTuitionFees)}
handleEdit={(id, updatedData) => handleEdit(`${BE_SCHOOL_FEE_URL}`, id, updatedData, setTuitionFees)}
handleDelete={(id) => handleDelete(`${BE_SCHOOL_FEE_URL}`, id, setTuitionFees)}
type={1}
/>
</div>
<div className="col-span-1 p-6 rounded-lg shadow-inner mt-4">
<DiscountsSection
discounts={tuitionDiscounts}
setDiscounts={setTuitionDiscounts}
handleCreate={(newData) => handleCreate(`${BE_SCHOOL_DISCOUNT_URL}`, newData, setTuitionDiscounts)}
handleEdit={(id, updatedData) => handleEdit(`${BE_SCHOOL_DISCOUNT_URL}`, id, updatedData, setTuitionDiscounts)}
handleDelete={(id) => handleDelete(`${BE_SCHOOL_DISCOUNT_URL}`, id, setTuitionDiscounts)}
onDiscountDelete={(id) => handleDiscountDelete(id, 1)}
type={1}
/>
</div>
<div className="col-span-1 p-6 rounded-lg shadow-inner mt-4">
<PaymentPlanSelector
paymentPlans={tuitionPaymentPlans}
setPaymentPlans={setTuitionPaymentPlans}
handleEdit={(id, updatedData) => handleEdit(`${BE_SCHOOL_PAYMENT_PLAN_URL}`, id, updatedData, setRegistrationPaymentPlans)}
type={1}
/>
</div>
</div>
</div>
</div>

View File

@ -120,15 +120,17 @@ const FeesSection = ({ fees, setFees, discounts, handleCreate, handleEdit, handl
};
const renderInputField = (field, value, onChange, placeholder) => (
<div className="flex justify-center items-center h-full">
<InputText
name={field}
type={field === 'base_amount' ? 'number' : 'text'}
value={value}
onChange={onChange}
placeholder={placeholder}
errorMsg={getError(field)}
/>
<div className="flex justify-center space-x-2">
<div className="w-full max-w-xs">
<InputText
name={field}
type={field === 'base_amount' ? 'number' : 'text'}
value={value}
onChange={onChange}
placeholder={placeholder}
errorMsg={getError(field)}
/>
</div>
</div>
);
@ -259,7 +261,7 @@ const FeesSection = ({ fees, setFees, discounts, handleCreate, handleEdit, handl
<div className="flex justify-between items-center">
<div className="flex items-center mb-4">
<CreditCard className="w-6 h-6 text-emerald-500 mr-2" />
<h2 className="text-xl font-semibold">{labelTypeFrais}</h2>
<h2 className="text-xl font-semibold">Liste des frais</h2>
</div>
<button type="button" onClick={handleAddFee} className="text-emerald-500 hover:text-emerald-700">
<Plus className="w-5 h-5" />

View File

@ -25,8 +25,8 @@ const Table = ({ data, columns, renderCell, itemsPerPage = 0, currentPage, total
key={rowIndex}
className={`
${isSelectable ? 'cursor-pointer' : ''}
${selectedRows?.includes(row.id) ? 'bg-emerald-500 text-white' : rowIndex % 2 === 0 ? `${defaultTheme}` : ''}
${isSelectable ? 'hover:bg-emerald-600' : ''}
${selectedRows?.includes(row.id) ? 'bg-emerald-300 text-white' : rowIndex % 2 === 0 ? `${defaultTheme}` : ''}
${isSelectable ? 'hover:bg-emerald-200' : ''}
`}
onClick={() => isSelectable && onRowClick && onRowClick(row)}
>

View File

@ -24,7 +24,7 @@ export const BE_SUBSCRIPTION_REGISTRATIONFILE_GROUPS_URL = `${BASE_URL}/Subscrip
export const BE_SUBSCRIPTION_LAST_GUARDIAN_ID_URL = `${BASE_URL}/Subscriptions/lastGuardianId`
//GESTION ENSEIGNANT
//GESTION ECOLE
export const BE_SCHOOL_SPECIALITY_URL = `${BASE_URL}/School/speciality`
export const BE_SCHOOL_SPECIALITIES_URL = `${BASE_URL}/School/specialities`
export const BE_SCHOOL_SCHOOLCLASS_URL = `${BASE_URL}/School/schoolClass`
@ -37,6 +37,8 @@ export const BE_SCHOOL_FEE_URL = `${BASE_URL}/School/fee`;
export const BE_SCHOOL_FEES_URL = `${BASE_URL}/School/fees`;
export const BE_SCHOOL_DISCOUNT_URL = `${BASE_URL}/School/discount`;
export const BE_SCHOOL_DISCOUNTS_URL = `${BASE_URL}/School/discounts`;
export const BE_SCHOOL_PAYMENT_PLAN_URL = `${BASE_URL}/School/paymentPlan`;
export const BE_SCHOOL_PAYMENT_PLANS_URL = `${BASE_URL}/School/paymentPlans`;
// GESTION MESSAGERIE
export const BE_GESTIONMESSAGERIE_MESSAGES_URL = `${BASE_URL}/GestionMessagerie/messagerie`