mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
feat: Ajout des Bundles de fichiers [#24]
This commit is contained in:
@ -6,27 +6,35 @@ import FeesManagement from '@/components/Structure/Tarification/FeesManagement';
|
||||
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
|
||||
import useCsrfToken from '@/hooks/useCsrfToken';
|
||||
import { ClassesProvider } from '@/context/ClassesContext';
|
||||
import { createDatas,
|
||||
import { createDatas,
|
||||
updateDatas,
|
||||
removeDatas,
|
||||
fetchSpecialities,
|
||||
fetchTeachers,
|
||||
fetchClasses,
|
||||
fetchSchedules,
|
||||
fetchRegistrationDiscounts,
|
||||
fetchTuitionDiscounts,
|
||||
fetchRegistrationFees,
|
||||
fetchTuitionFees } from '@/app/lib/schoolAction';
|
||||
fetchSpecialities,
|
||||
fetchTeachers,
|
||||
fetchClasses,
|
||||
fetchSchedules,
|
||||
fetchRegistrationDiscounts,
|
||||
fetchTuitionDiscounts,
|
||||
fetchRegistrationFees,
|
||||
fetchTuitionFees,
|
||||
} from '@/app/lib/schoolAction';
|
||||
import SidebarTabs from '@/components/SidebarTabs';
|
||||
import FilesManagement from '@/components/Structure/Files/FilesManagement';
|
||||
|
||||
import { fetchRegisterFormFileTemplate } from '@/app/lib/subscriptionAction';
|
||||
|
||||
|
||||
|
||||
export default function Page() {
|
||||
const [specialities, setSpecialities] = useState([]);
|
||||
const [classes, setClasses] = useState([]);
|
||||
const [teachers, setTeachers] = useState([]);
|
||||
const [schedules, setSchedules] = useState([]); // Add this line
|
||||
const [registrationDiscounts, setRegistrationDiscounts] = useState([]);
|
||||
const [tuitionDiscounts, setTuitionDiscounts] = useState([]);
|
||||
const [registrationFees, setRegistrationFees] = useState([]);
|
||||
const [tuitionFees, setTuitionFees] = useState([]);
|
||||
const [fichiers, setFichiers] = useState([]);
|
||||
|
||||
const csrfToken = useCsrfToken();
|
||||
|
||||
@ -42,18 +50,27 @@ export default function Page() {
|
||||
|
||||
// Fetch data for schedules
|
||||
handleSchedules();
|
||||
|
||||
|
||||
// Fetch data for registration discounts
|
||||
handleRegistrationDiscounts();
|
||||
|
||||
|
||||
// Fetch data for tuition discounts
|
||||
handleTuitionDiscounts();
|
||||
|
||||
|
||||
// Fetch data for registration fees
|
||||
handleRegistrationFees();
|
||||
|
||||
|
||||
// Fetch data for tuition fees
|
||||
handleTuitionFees();
|
||||
|
||||
// Fetch data for registration file templates
|
||||
fetchRegisterFormFileTemplate()
|
||||
.then((data)=> {
|
||||
setFichiers(data)
|
||||
})
|
||||
.catch(error => console.error('Error fetching files:', error));
|
||||
|
||||
|
||||
}, []);
|
||||
|
||||
const handleSpecialities = () => {
|
||||
@ -96,7 +113,7 @@ export default function Page() {
|
||||
.catch(error => console.error('Error fetching registration discounts:', error));
|
||||
};
|
||||
|
||||
const handleTuitionDiscounts = () => {
|
||||
const handleTuitionDiscounts = () => {
|
||||
fetchTuitionDiscounts()
|
||||
.then(data => {
|
||||
setTuitionDiscounts(data);
|
||||
@ -224,6 +241,11 @@ export default function Page() {
|
||||
handleDelete={handleDelete}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
id: 'Files',
|
||||
label: 'Documents d\'inscription',
|
||||
content: <FilesManagement csrfToken={csrfToken} />
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Upload } from 'lucide-react';
|
||||
|
||||
export default function DraggableFileUpload({ fileName, onFileSelect }) {
|
||||
const [dragActive, setDragActive] = useState(false);
|
||||
|
||||
|
||||
const handleDragOver = (event) => {
|
||||
event.preventDefault();
|
||||
setDragActive(true);
|
||||
};
|
||||
|
||||
const handleDragLeave = () => {
|
||||
setDragActive(false);
|
||||
};
|
||||
|
||||
const handleFileChosen = (selectedFile) => {
|
||||
onFileSelect && onFileSelect(selectedFile);
|
||||
};
|
||||
|
||||
const handleDrop = (event) => {
|
||||
event.preventDefault();
|
||||
setDragActive(false);
|
||||
const droppedFile = event.dataTransfer.files[0];
|
||||
handleFileChosen(droppedFile);
|
||||
};
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
const selectedFile = event.target.files[0];
|
||||
handleFileChosen(selectedFile);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
onDragOver={handleDragOver}
|
||||
onDragLeave={handleDragLeave}
|
||||
onDrop={handleDrop}
|
||||
className={`border-2 border-dashed p-8 rounded-md ${dragActive ? 'border-blue-500' : 'border-gray-300'} flex flex-col items-center justify-center`}
|
||||
style={{ height: '200px' }}
|
||||
>
|
||||
<input type="file" onChange={handleFileChange} className="hidden" id="fileInput" />
|
||||
<label htmlFor="fileInput" className="cursor-pointer flex flex-col items-center">
|
||||
<Upload size={48} className="text-gray-400 mb-2" />
|
||||
<p className="text-center">{fileName || 'Glissez et déposez un fichier ici ou cliquez ici pour sélectionner un fichier'}</p>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import ToggleSwitch from '@/components/ToggleSwitch'; // Import du composant ToggleSwitch
|
||||
import DraggableFileUpload from './DraggableFileUpload';
|
||||
|
||||
export default function FileUpload({ onFileUpload, fileToEdit = null }) {
|
||||
const [fileName, setFileName] = useState('');
|
||||
const [file, setFile] = useState(null);
|
||||
const [isRequired, setIsRequired] = useState(false); // État pour le toggle isRequired
|
||||
const [order, setOrder] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (fileToEdit) {
|
||||
setFileName(fileToEdit.name || '');
|
||||
setIsRequired(fileToEdit.is_required || false);
|
||||
setOrder(fileToEdit.fusion_order || 0);
|
||||
}
|
||||
}, [fileToEdit]);
|
||||
|
||||
const handleFileNameChange = (event) => {
|
||||
setFileName(event.target.value);
|
||||
};
|
||||
|
||||
const handleUpload = () => {
|
||||
onFileUpload({
|
||||
file,
|
||||
name: fileName,
|
||||
is_required: isRequired,
|
||||
order: parseInt(order, 10),
|
||||
});
|
||||
setFile(null);
|
||||
setFileName('');
|
||||
setIsRequired(false);
|
||||
setOrder(0);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<DraggableFileUpload
|
||||
fileName={fileName}
|
||||
onFileSelect={(selectedFile) => {
|
||||
setFile(selectedFile);
|
||||
setFileName(selectedFile.name.replace(/\.[^/.]+$/, ""));
|
||||
}}
|
||||
/>
|
||||
<div className="flex mt-2">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Nom du fichier"
|
||||
value={fileName}
|
||||
onChange={handleFileNameChange}
|
||||
className="flex-grow p-2 border border-gray-200 rounded-md"
|
||||
/>
|
||||
<input
|
||||
type="number"
|
||||
value={order}
|
||||
onChange={(e) => setOrder(e.target.value)}
|
||||
placeholder="Ordre de fusion"
|
||||
className="p-2 border border-gray-200 rounded-md ml-2 w-20"
|
||||
/>
|
||||
<button
|
||||
onClick={handleUpload}
|
||||
className={`p-2 rounded-md shadow transition duration-200 ml-2 ${fileName !== "" ? 'bg-emerald-600 text-white hover:bg-emerald-900' : 'bg-gray-300 text-gray-500 cursor-not-allowed'}`}
|
||||
disabled={fileName === ""}
|
||||
>
|
||||
Ajouter
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center mt-4">
|
||||
<ToggleSwitch
|
||||
label="Fichier à remplir obligatoirement"
|
||||
checked={isRequired}
|
||||
onChange={() => setIsRequired(!isRequired)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -11,11 +11,10 @@ import Loader from '@/components/Loader';
|
||||
import AlertWithModal from '@/components/AlertWithModal';
|
||||
import DropdownMenu from "@/components/DropdownMenu";
|
||||
import { formatPhoneNumber } from '@/utils/Telephone';
|
||||
import { MoreVertical, Send, Edit, Trash2, FileText, CheckCircle, Plus, Download } from 'lucide-react';
|
||||
import { MoreVertical, Send, Edit, Trash2, FileText, CheckCircle, Plus } from 'lucide-react';
|
||||
import Modal from '@/components/Modal';
|
||||
import InscriptionForm from '@/components/Inscription/InscriptionForm'
|
||||
import AffectationClasseForm from '@/components/AffectationClasseForm'
|
||||
import FileUpload from './components/FileUpload';
|
||||
|
||||
import {
|
||||
PENDING,
|
||||
@ -26,17 +25,14 @@ import {
|
||||
sendRegisterForm,
|
||||
archiveRegisterForm,
|
||||
fetchRegisterFormFileTemplate,
|
||||
deleteRegisterFormFileTemplate,
|
||||
createRegistrationFormFileTemplate,
|
||||
editRegistrationFormFileTemplate,
|
||||
fetchStudents,
|
||||
editRegisterForm } from "@/app/lib/subscriptionAction"
|
||||
|
||||
import {
|
||||
import {
|
||||
fetchClasses,
|
||||
fetchRegistrationDiscounts,
|
||||
fetchTuitionDiscounts,
|
||||
fetchRegistrationFees,
|
||||
fetchRegistrationDiscounts,
|
||||
fetchTuitionDiscounts,
|
||||
fetchRegistrationFees,
|
||||
fetchTuitionFees } from '@/app/lib/schoolAction';
|
||||
|
||||
import { createProfile } from '@/app/lib/authAction';
|
||||
@ -47,7 +43,7 @@ import {
|
||||
|
||||
import DjangoCSRFToken from '@/components/DjangoCSRFToken'
|
||||
import useCsrfToken from '@/hooks/useCsrfToken';
|
||||
import { formatDate } from '@/utils/Date';
|
||||
import { fetchRegistrationFileGroups } from '@/app/lib/registerFileGroupAction';
|
||||
|
||||
const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true';
|
||||
|
||||
@ -77,14 +73,13 @@ export default function Page({ params: { locale } }) {
|
||||
const [classes, setClasses] = useState([]);
|
||||
const [students, setEleves] = useState([]);
|
||||
const [reloadFetch, setReloadFetch] = useState(false);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [fileToEdit, setFileToEdit] = useState(null);
|
||||
|
||||
|
||||
const [registrationDiscounts, setRegistrationDiscounts] = useState([]);
|
||||
const [tuitionDiscounts, setTuitionDiscounts] = useState([]);
|
||||
const [registrationFees, setRegistrationFees] = useState([]);
|
||||
const [tuitionFees, setTuitionFees] = useState([]);
|
||||
const [groups, setGroups] = useState([]);
|
||||
|
||||
const csrfToken = useCsrfToken();
|
||||
|
||||
@ -228,6 +223,11 @@ const registerFormArchivedDataHandler = (data) => {
|
||||
setTuitionFees(data);
|
||||
})
|
||||
.catch(requestErrorHandler);
|
||||
fetchRegistrationFileGroups()
|
||||
.then(data => {
|
||||
setGroups(data);
|
||||
})
|
||||
.catch(error => console.error('Error fetching file groups:', error));
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
setRegistrationFormsDataPending(mockFicheInscription);
|
||||
@ -357,7 +357,7 @@ useEffect(()=>{
|
||||
const selectedRegistrationDiscountsIds = updatedData.selectedRegistrationDiscounts.map(discountId => discountId)
|
||||
const selectedTuitionFeesIds = updatedData.selectedTuitionFees.map(feeId => feeId)
|
||||
const selectedTuitionDiscountsIds = updatedData.selectedTuitionDiscounts.map(discountId => discountId)
|
||||
|
||||
const selectedFileGroup = updatedData.selectedFileGroup
|
||||
const allFeesIds = [...selectedRegistrationFeesIds, ...selectedTuitionFeesIds];
|
||||
const allDiscountsds = [...selectedRegistrationDiscountsIds, ...selectedTuitionDiscountsIds];
|
||||
|
||||
@ -370,7 +370,8 @@ useEffect(()=>{
|
||||
},
|
||||
idGuardians: selectedGuardiansIds,
|
||||
fees: allFeesIds,
|
||||
discounts: allDiscountsds
|
||||
discounts: allDiscountsds,
|
||||
fileGroup: selectedFileGroup
|
||||
};
|
||||
|
||||
createRegisterForm(data, csrfToken)
|
||||
@ -567,89 +568,23 @@ const columnsSubscribed = [
|
||||
|
||||
];
|
||||
|
||||
const handleFileDelete = (fileId) => {
|
||||
deleteRegisterFormFileTemplate(fileId,csrfToken)
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
setFichiers(fichiers.filter(fichier => fichier.id !== fileId));
|
||||
alert('Fichier supprimé avec succès.');
|
||||
} else {
|
||||
alert('Erreur lors de la suppression du fichier.');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error deleting file:', error);
|
||||
alert('Erreur lors de la suppression du fichier.');
|
||||
});
|
||||
};
|
||||
|
||||
const handleFileEdit = (file) => {
|
||||
setIsEditing(true);
|
||||
setFileToEdit(file);
|
||||
setIsModalOpen(true);
|
||||
};
|
||||
|
||||
const columnsFiles = [
|
||||
{ name: 'Nom du fichier', transform: (row) => row.name },
|
||||
{ name: 'Date de création', transform: (row) => formatDate(new Date (row.date_added),"DD/MM/YYYY hh:mm:ss") },
|
||||
{ name: 'Fichier Obligatoire', transform: (row) => row.is_required ? 'Oui' : 'Non' },
|
||||
{ name: 'Ordre de fusion', transform: (row) => row.order },
|
||||
{ name: 'Actions', transform: (row) => (
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
{
|
||||
row.file && (
|
||||
<a href={`${BASE_URL}${row.file}`} target='_blank' className="text-blue-500 hover:text-blue-700">
|
||||
<Download size={16} />
|
||||
</a>)
|
||||
}
|
||||
<button onClick={() => handleFileEdit(row)} className="text-blue-500 hover:text-blue-700">
|
||||
<Edit size={16} />
|
||||
</button>
|
||||
<button onClick={() => handleFileDelete(row.id)} className="text-red-500 hover:text-red-700">
|
||||
<Trash2 size={16} />
|
||||
</button>
|
||||
</div>
|
||||
) },
|
||||
];
|
||||
|
||||
const handleFileUpload = ({file, name, is_required, order}) => {
|
||||
if (!name) {
|
||||
alert('Veuillez entrer un nom de fichier.');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
if(file){
|
||||
formData.append('file', file);
|
||||
}
|
||||
formData.append('name', name);
|
||||
formData.append('is_required', is_required);
|
||||
formData.append('order', order);
|
||||
|
||||
if (isEditing && fileToEdit) {
|
||||
editRegistrationFormFileTemplate(fileToEdit.id, formData, csrfToken)
|
||||
.then(data => {
|
||||
setFichiers(prevFichiers =>
|
||||
prevFichiers.map(f => f.id === fileToEdit.id ? data : f)
|
||||
);
|
||||
setIsModalOpen(false);
|
||||
setFileToEdit(null);
|
||||
setIsEditing(false);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error editing file:', error);
|
||||
});
|
||||
} else {
|
||||
createRegistrationFormFileTemplate(formData, csrfToken)
|
||||
.then(data => {
|
||||
setFichiers([...fichiers, data]);
|
||||
setIsModalOpen(false);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error uploading file:', error);
|
||||
});
|
||||
}
|
||||
};
|
||||
const tabs = [
|
||||
{
|
||||
id: 'pending',
|
||||
label: t('pending'),
|
||||
count: totalPending
|
||||
},
|
||||
{
|
||||
id: 'subscribed',
|
||||
label: t('subscribed'),
|
||||
count: totalSubscribed
|
||||
},
|
||||
{
|
||||
id: 'archived',
|
||||
label: t('archived'),
|
||||
count: totalArchives
|
||||
}
|
||||
];
|
||||
|
||||
if (isLoading) {
|
||||
return <Loader />;
|
||||
@ -699,16 +634,6 @@ const handleFileUpload = ({file, name, is_required, order}) => {
|
||||
active={activeTab === 'archived'}
|
||||
onClick={() => setActiveTab('archived')}
|
||||
/>
|
||||
<Tab
|
||||
text={(
|
||||
<>
|
||||
{t('subscribeFiles')}
|
||||
<span className="ml-2 text-sm text-gray-400">({fichiers.length})</span>
|
||||
</>
|
||||
)}
|
||||
active={activeTab === 'subscribeFiles'}
|
||||
onClick={() => setActiveTab('subscribeFiles')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-b border-gray-200 mb-6 w-full">
|
||||
@ -758,41 +683,6 @@ const handleFileUpload = ({file, name, is_required, order}) => {
|
||||
</div>
|
||||
</React.Fragment>
|
||||
) : null}
|
||||
{/*SI STATE == subscribeFiles */}
|
||||
{activeTab === 'subscribeFiles' && (
|
||||
<div>
|
||||
<div className="flex justify-end mb-4">
|
||||
<button
|
||||
onClick={() => { setIsModalOpen(true); setIsEditing(false); }}
|
||||
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>
|
||||
<Modal
|
||||
isOpen={isModalOpen}
|
||||
setIsOpen={setIsModalOpen}
|
||||
title={isEditing ? 'Modifier un fichier' : 'Ajouter un fichier'}
|
||||
ContentComponent={() => (
|
||||
<FileUpload
|
||||
onFileUpload={handleFileUpload}
|
||||
fileToEdit={fileToEdit}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<div className="mt-8">
|
||||
<Table
|
||||
data={fichiers}
|
||||
columns={columnsFiles}
|
||||
itemsPerPage={itemsPerPage}
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
onPageChange={handlePageChange}
|
||||
className="mt-8"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Popup
|
||||
visible={popup.visible}
|
||||
@ -815,6 +705,7 @@ const handleFileUpload = ({file, name, is_required, order}) => {
|
||||
tuitionDiscounts={tuitionDiscounts}
|
||||
registrationFees={registrationFees.filter(fee => fee.is_active)}
|
||||
tuitionFees={tuitionFees.filter(fee => fee.is_active)}
|
||||
groups={groups}
|
||||
onSubmit={createRF}
|
||||
/>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user