mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-28 23:43:22 +00:00
feat: Mise en place des paiements en plusieurs fois (partie BACK) [#25]
This commit is contained in:
@ -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}
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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 && (
|
||||
|
||||
@ -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}
|
||||
|
||||
66
Front-End/src/components/DateTab.js
Normal file
66
Front-End/src/components/DateTab.js
Normal 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;
|
||||
@ -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>}
|
||||
|
||||
44
Front-End/src/components/PaymentModeSelector.js
Normal file
44
Front-End/src/components/PaymentModeSelector.js
Normal 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;
|
||||
248
Front-End/src/components/PaymentPlanSelector.js
Normal file
248
Front-End/src/components/PaymentPlanSelector.js
Normal 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;
|
||||
@ -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" />
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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" />
|
||||
|
||||
@ -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)}
|
||||
>
|
||||
|
||||
@ -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`
|
||||
|
||||
Reference in New Issue
Block a user