mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
258 lines
8.2 KiB
JavaScript
258 lines
8.2 KiB
JavaScript
import React, { useState } from 'react';
|
|
import { Plus, Trash2, Edit3, Check, X, EyeOff, Eye, CreditCard, BookOpen } from 'lucide-react';
|
|
import Table from '@/components/Table';
|
|
import Popup from '@/components/Popup';
|
|
import CheckBox from '@/components/CheckBox';
|
|
import InputText from '@/components/InputText';
|
|
|
|
const FeesSection = ({ fees, setFees, discounts, handleCreate, handleEdit, handleDelete, type, subscriptionMode = false, selectedFees, handleFeeSelection }) => {
|
|
const [editingFee, setEditingFee] = useState(null);
|
|
const [newFee, setNewFee] = useState(null);
|
|
const [formData, setFormData] = useState({});
|
|
const [localErrors, setLocalErrors] = useState({});
|
|
const [popupVisible, setPopupVisible] = useState(false);
|
|
const [popupMessage, setPopupMessage] = useState("");
|
|
|
|
const handleAddFee = () => {
|
|
setNewFee({ id: Date.now(), name: '', base_amount: '', description: '', validity_start_date: '', validity_end_date: '', discounts: [], type: type });
|
|
};
|
|
|
|
const handleRemoveFee = (id) => {
|
|
handleDelete(id)
|
|
.then(() => {
|
|
setFees(prevFees => prevFees.filter(fee => fee.id !== id));
|
|
})
|
|
.catch(error => {
|
|
console.error(error);
|
|
});
|
|
};
|
|
|
|
const handleSaveNewFee = () => {
|
|
if (
|
|
newFee.name &&
|
|
newFee.base_amount) {
|
|
handleCreate(newFee)
|
|
.then((createdFee) => {
|
|
setFees([createdFee, ...fees]);
|
|
setNewFee(null);
|
|
setLocalErrors({});
|
|
})
|
|
.catch(error => {
|
|
if (error && typeof error === 'object') {
|
|
setLocalErrors(error);
|
|
} else {
|
|
console.error(error);
|
|
}
|
|
});
|
|
} else {
|
|
setPopupMessage("Tous les champs doivent être remplis et valides");
|
|
setPopupVisible(true);
|
|
}
|
|
};
|
|
|
|
const handleUpdateFee = (id, updatedFee) => {
|
|
if (
|
|
updatedFee.name &&
|
|
updatedFee.base_amount) {
|
|
handleEdit(id, updatedFee)
|
|
.then((updatedFee) => {
|
|
setFees(fees.map(fee => fee.id === id ? updatedFee : fee));
|
|
setEditingFee(null);
|
|
setLocalErrors({});
|
|
})
|
|
.catch(error => {
|
|
if (error && typeof error === 'object') {
|
|
setLocalErrors(error);
|
|
} else {
|
|
console.error(error);
|
|
}
|
|
});
|
|
} else {
|
|
setPopupMessage("Tous les champs doivent être remplis et valides");
|
|
setPopupVisible(true);
|
|
}
|
|
};
|
|
|
|
const handleToggleActive = (id, isActive) => {
|
|
const fee = fees.find(fee => fee.id === id);
|
|
if (!fee) return;
|
|
|
|
const updatedData = {
|
|
is_active: !isActive,
|
|
discounts: fee.discounts
|
|
};
|
|
|
|
handleEdit(id, updatedData)
|
|
.then(() => {
|
|
setFees(prevFees => prevFees.map(fee => fee.id === id ? { ...fee, is_active: !isActive } : fee));
|
|
})
|
|
.catch(error => {
|
|
console.error(error);
|
|
});
|
|
};
|
|
|
|
const handleChange = (e) => {
|
|
const { name, value } = e.target;
|
|
let parsedValue = value;
|
|
if (name === 'discounts') {
|
|
parsedValue = value.split(',').map(v => parseInt(v, 10));
|
|
}
|
|
if (editingFee) {
|
|
setFormData((prevData) => ({
|
|
...prevData,
|
|
[name]: parsedValue,
|
|
}));
|
|
} else if (newFee) {
|
|
setNewFee((prevData) => ({
|
|
...prevData,
|
|
[name]: parsedValue,
|
|
}));
|
|
}
|
|
};
|
|
|
|
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={localErrors && localErrors[field] && Array.isArray(localErrors[field]) ? localErrors[field][0] : ''}
|
|
/>
|
|
</div>
|
|
);
|
|
|
|
const renderFeeCell = (fee, column) => {
|
|
const isEditing = editingFee === fee.id;
|
|
const isCreating = newFee && newFee.id === fee.id;
|
|
const currentData = isEditing ? formData : newFee;
|
|
|
|
if (isEditing || isCreating) {
|
|
switch (column) {
|
|
case 'NOM':
|
|
return renderInputField('name', currentData.name, handleChange, 'Nom des frais');
|
|
case 'MONTANT':
|
|
return renderInputField('base_amount', currentData.base_amount, handleChange, 'Montant de base');
|
|
case 'DESCRIPTION':
|
|
return renderInputField('description', currentData.description, handleChange, 'Description');
|
|
case 'ACTIONS':
|
|
return (
|
|
<div className="flex justify-center space-x-2">
|
|
<button
|
|
type="button"
|
|
onClick={() => (isEditing ? handleUpdateFee(editingFee, formData) : handleSaveNewFee())}
|
|
className="text-green-500 hover:text-green-700"
|
|
>
|
|
<Check className="w-5 h-5" />
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => (isEditing ? setEditingFee(null) : setNewFee(null))}
|
|
className="text-red-500 hover:text-red-700"
|
|
>
|
|
<X className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
);
|
|
default:
|
|
return null;
|
|
}
|
|
} else {
|
|
switch (column) {
|
|
case 'NOM':
|
|
return fee.name;
|
|
case 'MONTANT':
|
|
return fee.base_amount + ' €';
|
|
case 'MISE A JOUR':
|
|
return fee.updated_at_formatted;
|
|
case 'DESCRIPTION':
|
|
return fee.description;
|
|
case 'ACTIONS':
|
|
return (
|
|
<div className="flex justify-center space-x-2">
|
|
<button
|
|
type="button"
|
|
onClick={() => handleToggleActive(fee.id, fee.is_active)}
|
|
className={`text-${fee.is_active ? 'green' : 'orange'}-500 hover:text-${fee.is_active ? 'green' : 'orange'}-700`}
|
|
>
|
|
{fee.is_active ? <Eye className="w-5 h-5" /> : <EyeOff className="w-5 h-5" />}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => setEditingFee(fee.id) || setFormData(fee)}
|
|
className="text-blue-500 hover:text-blue-700"
|
|
>
|
|
<Edit3 className="w-5 h-5" />
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => handleRemoveFee(fee.id)}
|
|
className="text-red-500 hover:text-red-700"
|
|
>
|
|
<Trash2 className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
);
|
|
case '':
|
|
return (
|
|
<div className="flex justify-center">
|
|
<CheckBox
|
|
item={fee}
|
|
formData={{ selectedFees }}
|
|
handleChange={() => handleFeeSelection(fee.id)}
|
|
fieldName="selectedFees"
|
|
/>
|
|
</div>
|
|
);
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
};
|
|
|
|
const columns = subscriptionMode
|
|
? [
|
|
{ name: 'NOM', label: 'Nom' },
|
|
{ name: 'DESCRIPTION', label: 'Description' },
|
|
{ name: 'MONTANT', label: 'Montant de base' },
|
|
{ name: '', label: 'Sélection' }
|
|
]
|
|
: [
|
|
{ name: 'NOM', label: 'Nom' },
|
|
{ name: 'MONTANT', label: 'Montant de base' },
|
|
{ name: 'DESCRIPTION', label: 'Description' },
|
|
{ name: 'MISE A JOUR', label: 'Date mise à jour' },
|
|
{ name: 'ACTIONS', label: 'Actions' }
|
|
];
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
{!subscriptionMode && (
|
|
<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">{type === 0 ? 'Frais d\'inscription' : 'Frais de scolarité'}</h2>
|
|
</div>
|
|
<button type="button" onClick={handleAddFee} className="text-emerald-500 hover:text-emerald-700">
|
|
<Plus className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
)}
|
|
<Table
|
|
data={newFee ? [newFee, ...fees] : fees}
|
|
columns={columns}
|
|
renderCell={renderFeeCell}
|
|
/>
|
|
<Popup
|
|
visible={popupVisible}
|
|
message={popupMessage}
|
|
onConfirm={() => setPopupVisible(false)}
|
|
onCancel={() => setPopupVisible(false)}
|
|
uniqueConfirmButton={true}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default FeesSection; |