mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
feat: Ajout de la configuration des tarifs de l'école [#18]
This commit is contained in:
committed by
Luc SORIGNET
parent
147a70135d
commit
5a0e65bb75
@ -1,28 +1,22 @@
|
||||
'use client'
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { School, Calendar } from 'lucide-react';
|
||||
import TabsStructure from '@/components/Structure/Configuration/TabsStructure';
|
||||
import ScheduleManagement from '@/components/Structure/Planning/ScheduleManagement'
|
||||
import StructureManagement from '@/components/Structure/Configuration/StructureManagement'
|
||||
import { BE_SCHOOL_SPECIALITIES_URL,
|
||||
BE_SCHOOL_SCHOOLCLASSES_URL,
|
||||
BE_SCHOOL_TEACHERS_URL,
|
||||
BE_SCHOOL_PLANNINGS_URL } from '@/utils/Url';
|
||||
import DjangoCSRFToken from '@/components/DjangoCSRFToken'
|
||||
import { School, Calendar, DollarSign } from 'lucide-react'; // Import de l'icône DollarSign
|
||||
import StructureManagement from '@/components/Structure/Configuration/StructureManagement';
|
||||
import ScheduleManagement from '@/components/Structure/Planning/ScheduleManagement';
|
||||
import FeesManagement from '@/components/Structure/Configuration/FeesManagement';
|
||||
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
|
||||
import useCsrfToken from '@/hooks/useCsrfToken';
|
||||
import { ClassesProvider } from '@/context/ClassesContext';
|
||||
import { fetchSpecialities, fetchTeachers, fetchClasses, fetchSchedules } from '@/app/lib/schoolAction';
|
||||
import { fetchSpecialities, fetchTeachers, fetchClasses, fetchSchedules, fetchDiscounts, fetchFees, fetchTuitionFees } from '@/app/lib/schoolAction';
|
||||
import SidebarTabs from '@/components/SidebarTabs';
|
||||
|
||||
export default function Page() {
|
||||
const [specialities, setSpecialities] = useState([]);
|
||||
const [classes, setClasses] = useState([]);
|
||||
const [teachers, setTeachers] = useState([]);
|
||||
const [schedules, setSchedules] = useState([]);
|
||||
const [activeTab, setActiveTab] = useState('Configuration');
|
||||
const tabs = [
|
||||
{ id: 'Configuration', title: "Configuration de l'école", icon: School },
|
||||
{ id: 'Schedule', title: "Gestion de l'emploi du temps", icon: Calendar },
|
||||
];
|
||||
const [fees, setFees] = useState([]);
|
||||
const [discounts, setDiscounts] = useState([]);
|
||||
const [tuitionFees, setTuitionFees] = useState([]);
|
||||
|
||||
const csrfToken = useCsrfToken();
|
||||
|
||||
@ -38,6 +32,15 @@ export default function Page() {
|
||||
|
||||
// Fetch data for schedules
|
||||
handleSchedules();
|
||||
|
||||
// Fetch data for fees
|
||||
handleFees();
|
||||
|
||||
// Fetch data for discounts
|
||||
handleDiscounts();
|
||||
|
||||
// Fetch data for TuitionFee
|
||||
handleTuitionFees();
|
||||
}, []);
|
||||
|
||||
const handleSpecialities = () => {
|
||||
@ -45,9 +48,7 @@ export default function Page() {
|
||||
.then(data => {
|
||||
setSpecialities(data);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching specialities:', error);
|
||||
});
|
||||
.catch(error => console.error('Error fetching specialities:', error));
|
||||
};
|
||||
|
||||
const handleTeachers = () => {
|
||||
@ -55,9 +56,7 @@ export default function Page() {
|
||||
.then(data => {
|
||||
setTeachers(data);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching teachers:', error);
|
||||
});
|
||||
.catch(error => console.error('Error fetching teachers:', error));
|
||||
};
|
||||
|
||||
const handleClasses = () => {
|
||||
@ -65,9 +64,7 @@ export default function Page() {
|
||||
.then(data => {
|
||||
setClasses(data);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching classes:', error);
|
||||
});
|
||||
.catch(error => console.error('Error fetching classes:', error));
|
||||
};
|
||||
|
||||
const handleSchedules = () => {
|
||||
@ -75,13 +72,35 @@ export default function Page() {
|
||||
.then(data => {
|
||||
setSchedules(data);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching classes:', error);
|
||||
});
|
||||
.catch(error => console.error('Error fetching schedules:', error));
|
||||
};
|
||||
|
||||
const handleCreate = (url, newData, setDatas) => {
|
||||
fetch(url, {
|
||||
const handleFees = () => {
|
||||
fetchFees()
|
||||
.then(data => {
|
||||
setFees(data);
|
||||
})
|
||||
.catch(error => console.error('Error fetching fees:', error));
|
||||
};
|
||||
|
||||
const handleDiscounts = () => {
|
||||
fetchDiscounts()
|
||||
.then(data => {
|
||||
setDiscounts(data);
|
||||
})
|
||||
.catch(error => console.error('Error fetching discounts:', error));
|
||||
};
|
||||
|
||||
const handleTuitionFees = () => {
|
||||
fetchTuitionFees()
|
||||
.then(data => {
|
||||
setTuitionFees(data);
|
||||
})
|
||||
.catch(error => console.error('Error fetching tuition fees', error));
|
||||
};
|
||||
|
||||
const handleCreate = (url, newData, setDatas, setErrors) => {
|
||||
return fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@ -90,18 +109,28 @@ export default function Page() {
|
||||
body: JSON.stringify(newData),
|
||||
credentials: 'include'
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
return response.json().then(errorData => {
|
||||
throw errorData;
|
||||
});
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log('Succes :', data);
|
||||
setDatas(prevState => [...prevState, data]);
|
||||
setErrors({});
|
||||
return data;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Erreur :', error);
|
||||
setErrors(error);
|
||||
console.error('Error creating data:', error);
|
||||
throw error;
|
||||
});
|
||||
};
|
||||
|
||||
const handleEdit = (url, id, updatedData, setDatas) => {
|
||||
fetch(`${url}/${id}`, {
|
||||
const handleEdit = (url, id, updatedData, setDatas, setErrors) => {
|
||||
return fetch(`${url}/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@ -110,15 +139,41 @@ export default function Page() {
|
||||
body: JSON.stringify(updatedData),
|
||||
credentials: 'include'
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
return response.json().then(errorData => {
|
||||
throw errorData;
|
||||
});
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
setDatas(prevState => prevState.map(item => item.id === id ? data : item));
|
||||
setErrors({});
|
||||
return data;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Erreur :', error);
|
||||
setErrors(error);
|
||||
console.error('Error editing data:', error);
|
||||
throw error;
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (url, id, setDatas) => {
|
||||
fetch(`${url}/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': csrfToken
|
||||
},
|
||||
credentials: 'include'
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
setDatas(prevState => prevState.filter(item => item.id !== id));
|
||||
})
|
||||
.catch(error => console.error('Error deleting data:', error));
|
||||
};
|
||||
const handleUpdatePlanning = (url, planningId, updatedData) => {
|
||||
fetch(`${url}/${planningId}`, {
|
||||
method: 'PUT',
|
||||
@ -139,35 +194,11 @@ export default function Page() {
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (url, id, setDatas) => {
|
||||
fetch(`${url}/${id}`, {
|
||||
method:'DELETE',
|
||||
headers: {
|
||||
'Content-Type':'application/json',
|
||||
'X-CSRFToken': csrfToken
|
||||
},
|
||||
credentials: 'include'
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log('Success:', data);
|
||||
setDatas(prevState => prevState.filter(item => item.id !== id));
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching data:', error);
|
||||
error = error.errorMessage;
|
||||
console.log(error);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='p-8'>
|
||||
<DjangoCSRFToken csrfToken={csrfToken} />
|
||||
|
||||
<TabsStructure activeTab={activeTab} setActiveTab={setActiveTab} tabs={tabs} />
|
||||
|
||||
{activeTab === 'Configuration' && (
|
||||
<>
|
||||
const tabs = [
|
||||
{
|
||||
id: 'Configuration',
|
||||
label: "Configuration de l'école",
|
||||
content: (
|
||||
<StructureManagement
|
||||
specialities={specialities}
|
||||
setSpecialities={setSpecialities}
|
||||
@ -177,18 +208,49 @@ export default function Page() {
|
||||
setClasses={setClasses}
|
||||
handleCreate={handleCreate}
|
||||
handleEdit={handleEdit}
|
||||
handleDelete={handleDelete} />
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeTab === 'Schedule' && (
|
||||
handleDelete={handleDelete}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
id: 'Schedule',
|
||||
label: "Gestion de l'emploi du temps",
|
||||
content: (
|
||||
<ClassesProvider>
|
||||
<ScheduleManagement
|
||||
handleUpdatePlanning={handleUpdatePlanning}
|
||||
classes={classes}
|
||||
/>
|
||||
</ClassesProvider>
|
||||
)}
|
||||
)
|
||||
},
|
||||
{
|
||||
id: 'Fees',
|
||||
label: 'Tarifications',
|
||||
content: (
|
||||
<FeesManagement
|
||||
fees={fees}
|
||||
setFees={setFees}
|
||||
discounts={discounts}
|
||||
setDiscounts={setDiscounts}
|
||||
tuitionFees={tuitionFees}
|
||||
setTuitionFees={setTuitionFees}
|
||||
handleCreate={handleCreate}
|
||||
handleEdit={handleEdit}
|
||||
handleDelete={handleDelete}
|
||||
/>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className='p-8'>
|
||||
<DjangoCSRFToken csrfToken={csrfToken} />
|
||||
|
||||
<div className="w-full p-4">
|
||||
<SidebarTabs tabs={tabs} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
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,61 +1,47 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Upload } from 'lucide-react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import ToggleSwitch from '@/components/ToggleSwitch'; // Import du composant ToggleSwitch
|
||||
import DraggableFileUpload from './DraggableFileUpload';
|
||||
|
||||
export default function FileUpload({ onFileUpload }) {
|
||||
const [dragActive, setDragActive] = useState(false);
|
||||
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);
|
||||
|
||||
const handleDragOver = (event) => {
|
||||
event.preventDefault();
|
||||
setDragActive(true);
|
||||
};
|
||||
|
||||
const handleDragLeave = () => {
|
||||
setDragActive(false);
|
||||
};
|
||||
|
||||
const handleDrop = (event) => {
|
||||
event.preventDefault();
|
||||
setDragActive(false);
|
||||
const droppedFile = event.dataTransfer.files[0];
|
||||
setFile(droppedFile);
|
||||
setFileName(droppedFile.name.replace(/\.[^/.]+$/, ""));
|
||||
};
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
const selectedFile = event.target.files[0];
|
||||
setFile(selectedFile);
|
||||
setFileName(selectedFile.name.replace(/\.[^/.]+$/, ""));
|
||||
};
|
||||
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, fileName);
|
||||
setFile(null);
|
||||
setFileName('');
|
||||
|
||||
onFileUpload({
|
||||
file,
|
||||
name: fileName,
|
||||
is_required: isRequired,
|
||||
order: parseInt(order, 10),
|
||||
});
|
||||
setFile(null);
|
||||
setFileName('');
|
||||
setIsRequired(false);
|
||||
setOrder(0);
|
||||
};
|
||||
|
||||
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>
|
||||
<DraggableFileUpload
|
||||
fileName={fileName}
|
||||
onFileSelect={(selectedFile) => {
|
||||
setFile(selectedFile);
|
||||
setFileName(selectedFile.name.replace(/\.[^/.]+$/, ""));
|
||||
}}
|
||||
/>
|
||||
<div className="flex mt-2">
|
||||
<input
|
||||
type="text"
|
||||
@ -64,14 +50,28 @@ export default function FileUpload({ onFileUpload }) {
|
||||
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==""}
|
||||
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>
|
||||
);
|
||||
}
|
||||
@ -17,6 +17,7 @@ export default function Page() {
|
||||
|
||||
const [initialData, setInitialData] = useState(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [formErrors, setFormErrors] = useState({});
|
||||
const csrfToken = useCsrfToken();
|
||||
|
||||
useEffect(() => {
|
||||
@ -55,9 +56,8 @@ export default function Page() {
|
||||
console.error('Error:', error.message);
|
||||
if (error.details) {
|
||||
console.error('Form errors:', error.details);
|
||||
// Handle form errors (e.g., display them to the user)
|
||||
setFormErrors(error.details);
|
||||
}
|
||||
alert('Une erreur est survenue lors de la mise à jour des données');
|
||||
});
|
||||
|
||||
};
|
||||
@ -69,6 +69,7 @@ export default function Page() {
|
||||
onSubmit={handleSubmit}
|
||||
cancelUrl={FE_ADMIN_SUBSCRIPTIONS_URL}
|
||||
isLoading={isLoading}
|
||||
errors={formErrors}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -28,6 +28,7 @@ import {
|
||||
fetchRegisterFormFileTemplate,
|
||||
deleteRegisterFormFileTemplate,
|
||||
createRegistrationFormFileTemplate,
|
||||
editRegistrationFormFileTemplate,
|
||||
fetchStudents,
|
||||
editRegisterForm } from "@/app/lib/subscriptionAction"
|
||||
|
||||
@ -40,6 +41,7 @@ import {
|
||||
|
||||
import DjangoCSRFToken from '@/components/DjangoCSRFToken'
|
||||
import useCsrfToken from '@/hooks/useCsrfToken';
|
||||
import { formatDate } from '@/utils/Date';
|
||||
|
||||
const useFakeData = process.env.NEXT_PUBLIC_USE_FAKE_DATA === 'true';
|
||||
|
||||
@ -69,6 +71,9 @@ 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 csrfToken = useCsrfToken();
|
||||
|
||||
@ -185,7 +190,11 @@ const registerFormArchivedDataHandler = (data) => {
|
||||
.then(registerFormArchivedDataHandler)
|
||||
.catch(requestErrorHandler)
|
||||
fetchRegisterFormFileTemplate()
|
||||
.then((data)=> {setFichiers(data)})
|
||||
.then((data)=> {
|
||||
console.log(data);
|
||||
|
||||
setFichiers(data)
|
||||
})
|
||||
.catch((err)=>{ err = err.message; console.log(err);});
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
@ -548,9 +557,17 @@ const handleFileDelete = (fileId) => {
|
||||
});
|
||||
};
|
||||
|
||||
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) => row.last_update },
|
||||
{ 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">
|
||||
{
|
||||
@ -559,6 +576,9 @@ const columnsFiles = [
|
||||
<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>
|
||||
@ -566,27 +586,43 @@ const columnsFiles = [
|
||||
) },
|
||||
];
|
||||
|
||||
const handleFileUpload = (file, fileName) => {
|
||||
if ( !fileName) {
|
||||
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', fileName);
|
||||
createRegistrationFormFileTemplate(formData,csrfToken)
|
||||
.then(data => {
|
||||
console.log('Success:', data);
|
||||
setFichiers([...fichiers, data]);
|
||||
closeUploadModal();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error uploading file:', error);
|
||||
});
|
||||
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);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
@ -699,7 +735,23 @@ const handleFileUpload = (file, fileName) => {
|
||||
{/*SI STATE == subscribeFiles */}
|
||||
{activeTab === 'subscribeFiles' && (
|
||||
<div>
|
||||
<FileUpload onFileUpload={handleFileUpload} className="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 ml-4"
|
||||
>
|
||||
<Plus className="w-5 h-5" />
|
||||
</button>
|
||||
<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}
|
||||
|
||||
@ -3,10 +3,12 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import DropdownMenu from '@/components/DropdownMenu';
|
||||
import { useRouter } from 'next/navigation'; // Ajout de l'importation
|
||||
import { Bell, User, MessageSquare, LogOut, Settings, Home } from 'lucide-react'; // Ajout de l'importation de l'icône Home
|
||||
import { User, MessageSquare, LogOut, Settings, Home } from 'lucide-react'; // Ajout de l'importation de l'icône Home
|
||||
import Logo from '@/components/Logo'; // Ajout de l'importation du composant Logo
|
||||
import { FE_PARENTS_HOME_URL,FE_PARENTS_MESSAGERIE_URL,FE_PARENTS_SETTINGS_URL, BE_GESTIONMESSAGERIE_MESSAGES_URL } from '@/utils/Url'; // Ajout de l'importation de l'URL de la page d'accueil parent
|
||||
import { FE_PARENTS_HOME_URL,FE_PARENTS_MESSAGERIE_URL,FE_PARENTS_SETTINGS_URL } from '@/utils/Url'; // Ajout de l'importation de l'URL de la page d'accueil parent
|
||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||
import { fetchMessages } from '@/app/lib/messagerieAction';
|
||||
import ProtectedRoute from '@/components/ProtectedRoute';
|
||||
|
||||
export default function Layout({
|
||||
children,
|
||||
@ -18,12 +20,8 @@ export default function Layout({
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setUserId(userId);
|
||||
fetch(`${BE_GESTIONMESSAGERIE_MESSAGES_URL}/${userId}`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}).then(response => response.json())
|
||||
setUserId(userId)
|
||||
fetchMessages(userId)
|
||||
.then(data => {
|
||||
if (data) {
|
||||
setMessages(data);
|
||||
@ -33,11 +31,10 @@ export default function Layout({
|
||||
.catch(error => {
|
||||
console.error('Error fetching data:', error);
|
||||
});
|
||||
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProtectedRoute>
|
||||
<div className="flex flex-col min-h-screen bg-gray-50">
|
||||
{/* Entête */}
|
||||
<header className="bg-white border-b border-gray-200 px-8 py-4 flex items-center justify-between fixed top-0 left-0 right-0 z-10">
|
||||
@ -85,7 +82,7 @@ export default function Layout({
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</ProtectedRoute>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
24
Front-End/src/app/lib/messagerieAction.js
Normal file
24
Front-End/src/app/lib/messagerieAction.js
Normal file
@ -0,0 +1,24 @@
|
||||
import {
|
||||
BE_GESTIONMESSAGERIE_MESSAGES_URL
|
||||
} from '@/utils/Url';
|
||||
|
||||
const requestResponseHandler = async (response) => {
|
||||
|
||||
const body = await response.json();
|
||||
if (response.ok) {
|
||||
return body;
|
||||
}
|
||||
// Throw an error with the JSON body containing the form errors
|
||||
const error = new Error('Form submission error');
|
||||
error.details = body;
|
||||
throw error;
|
||||
}
|
||||
|
||||
|
||||
export const fetchMessages = (id) =>{
|
||||
return fetch(`${BE_GESTIONMESSAGERIE_MESSAGES_URL}/${id}`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}).then(requestResponseHandler)
|
||||
}
|
||||
@ -2,7 +2,10 @@ import {
|
||||
BE_SCHOOL_SPECIALITIES_URL,
|
||||
BE_SCHOOL_TEACHERS_URL,
|
||||
BE_SCHOOL_SCHOOLCLASSES_URL,
|
||||
BE_SCHOOL_PLANNINGS_URL
|
||||
BE_SCHOOL_PLANNINGS_URL,
|
||||
BE_SCHOOL_FEES_URL,
|
||||
BE_SCHOOL_DISCOUNTS_URL,
|
||||
BE_SCHOOL_TUITION_FEES_URL
|
||||
} from '@/utils/Url';
|
||||
|
||||
const requestResponseHandler = async (response) => {
|
||||
@ -36,4 +39,19 @@ export const fetchClasses = () => {
|
||||
export const fetchSchedules = () => {
|
||||
return fetch(`${BE_SCHOOL_PLANNINGS_URL}`)
|
||||
.then(requestResponseHandler)
|
||||
};
|
||||
};
|
||||
|
||||
export const fetchDiscounts = () => {
|
||||
return fetch(`${BE_SCHOOL_DISCOUNTS_URL}`)
|
||||
.then(requestResponseHandler)
|
||||
};
|
||||
|
||||
export const fetchFees = () => {
|
||||
return fetch(`${BE_SCHOOL_FEES_URL}`)
|
||||
.then(requestResponseHandler)
|
||||
};
|
||||
|
||||
export const fetchTuitionFees = () => {
|
||||
return fetch(`${BE_SCHOOL_TUITION_FEES_URL}`)
|
||||
.then(requestResponseHandler)
|
||||
};
|
||||
|
||||
@ -7,7 +7,8 @@ import {
|
||||
BE_SUBSCRIPTION_REGISTERFORM_URL,
|
||||
BE_SUBSCRIPTION_REGISTERFORMS_URL,
|
||||
BE_SUBSCRIPTION_REGISTRATIONFORMFILE_TEMPLATE_URL,
|
||||
BE_SUBSCRIPTION_LAST_GUARDIAN_URL
|
||||
BE_SUBSCRIPTION_LAST_GUARDIAN_URL,
|
||||
BE_SUBSCRIPTION_REGISTRATIONFORMFILE_URL
|
||||
} from '@/utils/Url';
|
||||
|
||||
export const PENDING = 'pending';
|
||||
@ -110,6 +111,32 @@ export const fetchRegisterFormFileTemplate = () => {
|
||||
return fetch(request).then(requestResponseHandler)
|
||||
};
|
||||
|
||||
export const fetchRegisterFormFile = (id) => {
|
||||
const request = new Request(
|
||||
`${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_URL}/${id}`,
|
||||
{
|
||||
method:'GET',
|
||||
headers: {
|
||||
'Content-Type':'application/json'
|
||||
},
|
||||
}
|
||||
);
|
||||
return fetch(request).then(requestResponseHandler)
|
||||
};
|
||||
|
||||
export const createRegistrationFormFile = (data,csrfToken) => {
|
||||
|
||||
return fetch(`${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_URL}`, {
|
||||
method: 'POST',
|
||||
body: data,
|
||||
headers: {
|
||||
'X-CSRFToken': csrfToken,
|
||||
},
|
||||
credentials: 'include',
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
}
|
||||
|
||||
export const createRegistrationFormFileTemplate = (data,csrfToken) => {
|
||||
|
||||
return fetch(`${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_TEMPLATE_URL}`, {
|
||||
@ -132,6 +159,19 @@ export const deleteRegisterFormFileTemplate = (fileId,csrfToken) => {
|
||||
credentials: 'include',
|
||||
})
|
||||
}
|
||||
|
||||
export const editRegistrationFormFileTemplate = (fileId, data, csrfToken) => {
|
||||
return fetch(`${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_TEMPLATE_URL}/${fileId}`, {
|
||||
method: 'PUT',
|
||||
body: data,
|
||||
headers: {
|
||||
'X-CSRFToken': csrfToken,
|
||||
},
|
||||
credentials: 'include',
|
||||
})
|
||||
.then(requestResponseHandler)
|
||||
}
|
||||
|
||||
export const fetchStudents = () => {
|
||||
const request = new Request(
|
||||
`${BE_SUBSCRIPTION_STUDENTS_URL}`,
|
||||
|
||||
Reference in New Issue
Block a user