mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
refactor: Revue de la modale permettant de créer un dossier
d'inscription
This commit is contained in:
@ -1,93 +1,231 @@
|
||||
import { Trash2, MoreVertical, Edit3, Plus } from 'lucide-react';
|
||||
import { Plus, Trash2, Edit3, Check, X, BookOpen } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import Table from '@/components/Table';
|
||||
import DropdownMenu from '@/components/DropdownMenu';
|
||||
import Modal from '@/components/Modal';
|
||||
import SpecialityForm from '@/components/Structure/Configuration/SpecialityForm';
|
||||
import { SpecialityFormProvider } from '@/context/SpecialityFormContext';
|
||||
import Popup from '@/components/Popup';
|
||||
import InputTextWithColorIcon from '@/components/InputTextWithColorIcon';
|
||||
import { DndProvider, useDrag } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
|
||||
const SpecialitiesSection = ({ specialities, handleCreate, handleEdit, handleDelete }) => {
|
||||
const ItemTypes = {
|
||||
SPECIALITY: 'speciality',
|
||||
};
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [editingSpeciality, setEditingSpeciality] = useState(null);
|
||||
|
||||
const openEditModal = (speciality) => {
|
||||
setIsOpen(true);
|
||||
setEditingSpeciality(speciality);
|
||||
}
|
||||
|
||||
const closeEditModal = () => {
|
||||
setIsOpen(false);
|
||||
setEditingSpeciality(null);
|
||||
};
|
||||
|
||||
const handleModalSubmit = (updatedData) => {
|
||||
if (editingSpeciality) {
|
||||
handleEdit(editingSpeciality.id, updatedData);
|
||||
} else {
|
||||
handleCreate(updatedData);
|
||||
}
|
||||
closeEditModal();
|
||||
};
|
||||
const SpecialityItem = ({ speciality }) => {
|
||||
const [{ isDragging }, drag] = useDrag(() => ({
|
||||
type: ItemTypes.SPECIALITY,
|
||||
item: { id: speciality.id, name: speciality.name },
|
||||
collect: (monitor) => ({
|
||||
isDragging: !!monitor.isDragging(),
|
||||
}),
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className="mb-8">
|
||||
<div className="flex justify-between items-center mb-4 max-w-4xl ml-0">
|
||||
<h2 className="text-xl font-bold mb-4">Gestion des spécialités</h2>
|
||||
<button
|
||||
onClick={() => openEditModal(null)} // ouvrir le modal pour créer une nouvelle spécialité
|
||||
className="flex items-center bg-emerald-600 text-white p-2 rounded-full shadow hover:bg-emerald-900 transition duration-200"
|
||||
>
|
||||
<Plus className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="bg-white rounded-lg border border-gray-200 max-w-4xl ml-0">
|
||||
<Table
|
||||
columns={[
|
||||
{
|
||||
name: 'INTITULÉ',
|
||||
transform: (row) => (
|
||||
<div
|
||||
className="inline-block px-3 py-1 rounded-full font-bold text-white"
|
||||
style={{ backgroundColor: row.color_code }}
|
||||
title={row.color_code}
|
||||
>
|
||||
<span className="font-bold text-white">{row.name.toUpperCase()}</span>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{ name: 'DATE DE CREATION', transform: (row) => row.updated_date_formatted },
|
||||
{ name: 'ACTIONS', transform: (row) => (
|
||||
<DropdownMenu
|
||||
buttonContent={<MoreVertical size={20} className="text-gray-400 hover:text-gray-600" />}
|
||||
items={[
|
||||
{ label: 'Modifier', icon:Edit3, onClick: () => openEditModal(row) },
|
||||
{ label: 'Supprimer', icon: Trash2, onClick: () => handleDelete(row.id) }
|
||||
]
|
||||
}
|
||||
buttonClassName="text-gray-400 hover:text-gray-600"
|
||||
menuClassName="absolute right-0 mt-2 w-48 bg-white border border-gray-200 rounded-md shadow-lg z-10 flex flex-col items-center"
|
||||
/>
|
||||
)}
|
||||
]}
|
||||
data={specialities}
|
||||
/>
|
||||
</div>
|
||||
{isOpen && (
|
||||
<SpecialityFormProvider initialSpeciality={editingSpeciality || {}}>
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
setIsOpen={setIsOpen}
|
||||
title={editingSpeciality ? "Modification de la spécialité" : "Création d'une nouvelle spécialité"}
|
||||
size='sm:w-1/6'
|
||||
ContentComponent={() => (
|
||||
<SpecialityForm onSubmit={handleModalSubmit} isNew={!editingSpeciality} />
|
||||
)}
|
||||
/>
|
||||
</SpecialityFormProvider>
|
||||
)}
|
||||
<div
|
||||
ref={drag}
|
||||
className="inline-block px-3 py-1 rounded-full font-bold text-white text-center"
|
||||
style={{ backgroundColor: speciality.color_code, opacity: isDragging ? 0.5 : 1 }}
|
||||
title={speciality.name}
|
||||
>
|
||||
{speciality.name}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const SpecialitiesSection = ({ specialities, setSpecialities, handleCreate, handleEdit, handleDelete }) => {
|
||||
|
||||
const [newSpeciality, setNewSpeciality] = useState(null);
|
||||
const [editingSpeciality, setEditingSpeciality] = useState(null);
|
||||
const [formData, setFormData] = useState({});
|
||||
const [localErrors, setLocalErrors] = useState({});
|
||||
const [popupVisible, setPopupVisible] = useState(false);
|
||||
const [popupMessage, setPopupMessage] = useState("");
|
||||
|
||||
const handleAddSpeciality = () => {
|
||||
setNewSpeciality({ id: Date.now(), name: '', color_code: '' });
|
||||
};
|
||||
|
||||
const handleRemoveSpeciality = (id) => {
|
||||
handleDelete(id)
|
||||
.then(() => {
|
||||
setSpecialities(prevSpecialities => prevSpecialities.filter(speciality => speciality.id !== id));
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
};
|
||||
|
||||
const handleSaveNewSpeciality = () => {
|
||||
if (
|
||||
newSpeciality.name) {
|
||||
handleCreate(newSpeciality)
|
||||
.then((createdSpeciality) => {
|
||||
setSpecialities([createdSpeciality, ...specialities]);
|
||||
setNewSpeciality(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 handleUpdateSpeciality = (id, updatedSpeciality) => {
|
||||
if (
|
||||
updatedSpeciality.name) {
|
||||
handleEdit(id, updatedSpeciality)
|
||||
.then((updatedSpeciality) => {
|
||||
setSpecialities(specialities.map(speciality => speciality.id === id ? updatedSpeciality : speciality));
|
||||
setEditingSpeciality(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 handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
let parsedValue = value;
|
||||
if (name.includes('_color')) {
|
||||
parsedValue = value;
|
||||
}
|
||||
|
||||
const fieldName = name.includes('_color') ? 'color_code' : name;
|
||||
if (editingSpeciality) {
|
||||
setFormData((prevData) => ({
|
||||
...prevData,
|
||||
[fieldName]: parsedValue,
|
||||
}));
|
||||
} else if (newSpeciality) {
|
||||
setNewSpeciality((prevData) => ({
|
||||
...prevData,
|
||||
[fieldName]: parsedValue,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const renderSpecialityCell = (speciality, column) => {
|
||||
const isEditing = editingSpeciality === speciality.id;
|
||||
const isCreating = newSpeciality && newSpeciality.id === speciality.id;
|
||||
const currentData = isEditing ? formData : newSpeciality;
|
||||
|
||||
if (isEditing || isCreating) {
|
||||
switch (column) {
|
||||
case 'LIBELLE':
|
||||
return (
|
||||
<InputTextWithColorIcon
|
||||
name="name"
|
||||
textValue={currentData.name}
|
||||
colorValue={currentData.color_code}
|
||||
onTextChange={handleChange}
|
||||
onColorChange={handleChange}
|
||||
placeholder="Nom de la spécialité"
|
||||
errorMsg={localErrors && localErrors.name && Array.isArray(localErrors.name) ? localErrors.name[0] : ''}
|
||||
/>
|
||||
);
|
||||
case 'ACTIONS':
|
||||
return (
|
||||
<div className="flex justify-center space-x-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => (isEditing ? handleUpdateSpeciality(editingSpeciality, formData) : handleSaveNewSpeciality())}
|
||||
className="text-green-500 hover:text-green-700"
|
||||
>
|
||||
<Check className="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => (isEditing ? setEditingSpeciality(null) : setNewSpeciality(null))}
|
||||
className="text-red-500 hover:text-red-700"
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
switch (column) {
|
||||
case 'LIBELLE':
|
||||
return (
|
||||
<SpecialityItem key={speciality.id} speciality={speciality} />
|
||||
);
|
||||
case 'MISE A JOUR':
|
||||
return speciality.updated_date_formatted;
|
||||
case 'ACTIONS':
|
||||
return (
|
||||
<div className="flex justify-center space-x-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setEditingSpeciality(speciality.id) || setFormData(speciality)}
|
||||
className="text-blue-500 hover:text-blue-700"
|
||||
>
|
||||
<Edit3 className="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveSpeciality(speciality.id)}
|
||||
className="text-red-500 hover:text-red-700"
|
||||
>
|
||||
<Trash2 className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{ name: 'LIBELLE', label: 'Libellé' },
|
||||
{ name: 'MISE A JOUR', label: 'Date mise à jour' },
|
||||
{ name: 'ACTIONS', label: 'Actions' }
|
||||
];
|
||||
|
||||
return (
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex items-center mb-4">
|
||||
<BookOpen className="w-6 h-6 text-emerald-500 mr-2" />
|
||||
<h2 className="text-xl font-semibold">Spécialités</h2>
|
||||
</div>
|
||||
<button type="button" onClick={handleAddSpeciality} className="text-emerald-500 hover:text-emerald-700">
|
||||
<Plus className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
<Table
|
||||
data={newSpeciality ? [newSpeciality, ...specialities] : specialities}
|
||||
columns={columns}
|
||||
renderCell={renderSpecialityCell}
|
||||
/>
|
||||
<Popup
|
||||
visible={popupVisible}
|
||||
message={popupMessage}
|
||||
onConfirm={() => setPopupVisible(false)}
|
||||
onCancel={() => setPopupVisible(false)}
|
||||
uniqueConfirmButton={true}
|
||||
/>
|
||||
</div>
|
||||
</DndProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default SpecialitiesSection;
|
||||
|
||||
Reference in New Issue
Block a user