mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
feat: Upload du SEPA par les parents / Création d'un composant header
pour les titres de tableau
This commit is contained in:
@ -1,59 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { CloudUpload } from 'lucide-react';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
export default function FileUpload({ selectionMessage, onFileSelect, uploadedFileName }) {
|
||||
const [localFileName, setLocalFileName] = useState(uploadedFileName || '');
|
||||
|
||||
const handleFileChange = (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
setLocalFileName(file.name);
|
||||
logger.debug('Fichier sélectionné:', file.name);
|
||||
onFileSelect(file); // Appelle la fonction passée en prop
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileDrop = (e) => {
|
||||
e.preventDefault();
|
||||
const file = e.dataTransfer.files[0];
|
||||
if (file) {
|
||||
setLocalFileName(file.name);
|
||||
logger.debug('Fichier déposé:', file.name);
|
||||
onFileSelect(file); // Appelle la fonction passée en prop
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="border p-4 rounded-md shadow-md">
|
||||
<h3 className="text-lg font-semibold mb-4">{`${selectionMessage}`}</h3>
|
||||
<div
|
||||
className="border-2 border-dashed border-gray-500 p-6 rounded-lg flex flex-col items-center justify-center cursor-pointer hover:border-emerald-500"
|
||||
onClick={() => document.getElementById('fileInput').click()} // Ouvre l'explorateur de fichiers au clic
|
||||
onDragOver={(e) => e.preventDefault()}
|
||||
onDrop={handleFileDrop}
|
||||
>
|
||||
<CloudUpload className="w-12 h-12 text-emerald-500 mb-4" /> {/* Icône de cloud */}
|
||||
<input
|
||||
type="file"
|
||||
accept=".pdf"
|
||||
onChange={handleFileChange}
|
||||
className="hidden"
|
||||
id="fileInput"
|
||||
/>
|
||||
<label htmlFor="fileInput" className="text-center text-gray-500">
|
||||
<p className="text-lg font-semibold text-gray-800">Déposez votre fichier ici</p>
|
||||
<p className="text-sm text-gray-500 mt-2">ou cliquez pour sélectionner un fichier PDF</p>
|
||||
</label>
|
||||
</div>
|
||||
{localFileName && (
|
||||
<div className="mt-4 flex items-center space-x-4 bg-gray-100 p-3 rounded-md shadow-sm">
|
||||
<CloudUpload className="w-6 h-6 text-emerald-500" />
|
||||
<p className="text-sm font-medium text-gray-800">
|
||||
<span className="font-semibold">{localFileName}</span>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,17 +1,214 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import Table from '@/components/Table';
|
||||
import FileUpload from '@/components/FileUpload';
|
||||
import { Upload, Eye, Trash2, FileText } from 'lucide-react';
|
||||
import { BASE_URL } from '@/utils/Url';
|
||||
import Popup from '@/components/Popup';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
export default function FilesToUpload({ parentFileTemplates, uploadedFiles, onFileUpload, onFileDelete }) {
|
||||
const [selectedFile, setSelectedFile] = useState(null); // État pour le fichier sélectionné
|
||||
const [actionType, setActionType] = useState(null);
|
||||
|
||||
const [popupVisible, setPopupVisible] = useState(false);
|
||||
const [popupMessage, setPopupMessage] = useState("");
|
||||
const [removePopupVisible, setRemovePopupVisible] = useState(false);
|
||||
const [removePopupMessage, setRemovePopupMessage] = useState("");
|
||||
const [removePopupOnConfirm, setRemovePopupOnConfirm] = useState(() => {});
|
||||
|
||||
// Vérification si un fichier est déjà uploadé
|
||||
const isFileUploaded = (file) => {
|
||||
return file && file.fileName; // Si `fileName` est défini, le fichier est considéré comme téléversé
|
||||
};
|
||||
|
||||
// Récupération d'un fichier uploadé
|
||||
const getUploadedFile = (templateId) => {
|
||||
return uploadedFiles.find(file => parseInt(file.id) === templateId && file.fileName);
|
||||
};
|
||||
|
||||
const handleUpload = (file, selectedFile) => {
|
||||
if (!file || !selectedFile) {
|
||||
logger.error('Données manquantes pour le téléversement.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Appeler la fonction de téléversement passée en prop
|
||||
onFileUpload(file, selectedFile)
|
||||
.then((response) => {
|
||||
// Mettre à jour uploadedFiles avec les nouvelles données
|
||||
const updatedFiles = uploadedFiles.map(f =>
|
||||
f.id === selectedFile.id
|
||||
? { ...f, fileName: response.data.fileName, file: response.data.file_url }
|
||||
: f
|
||||
);
|
||||
|
||||
// Si le fichier n'existe pas encore, l'ajouter
|
||||
if (!updatedFiles.find(f => f.id === selectedFile.id)) {
|
||||
updatedFiles.push({
|
||||
id: selectedFile.id,
|
||||
fileName: response.data.fileName,
|
||||
file: response.data.file_url,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error('Erreur lors du téléversement du fichier :', error);
|
||||
});
|
||||
|
||||
// Mettre à jour l'état local
|
||||
setSelectedFile(null);
|
||||
setActionType(null); // Réinitialiser l'action après l'upload
|
||||
};
|
||||
|
||||
// Définition des colonnes
|
||||
const columns = [
|
||||
{ name: 'Nom du fichier', transform: (row) => row.master_name },
|
||||
{ name: 'Description du fichier', transform: (row) => row.master_description },
|
||||
{ name: 'Statut', transform: (row) => {
|
||||
const uploadedFile = getUploadedFile(row.id);
|
||||
return (
|
||||
<span className={`px-2 py-1 rounded-md text-sm font-medium ${
|
||||
isFileUploaded(uploadedFile) ? 'bg-green-50 text-green-600' : 'bg-orange-50 text-orange-600'
|
||||
}`}>
|
||||
{isFileUploaded(uploadedFile) ? 'Chargé' : 'A ajouter'}
|
||||
</span>
|
||||
);
|
||||
}},
|
||||
{ name: 'Actions', transform: (row) => {
|
||||
const uploadedFile = getUploadedFile(row.id);
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-center gap-4">
|
||||
{uploadedFile && (
|
||||
<>
|
||||
<button
|
||||
className={`flex items-center justify-center w-8 h-8 rounded-full ${
|
||||
actionType === 'view' && selectedFile?.id === row.id
|
||||
? 'bg-blue-100 text-blue-600 ring-3 ring-blue-500'
|
||||
: 'text-blue-500 hover:text-blue-700'
|
||||
}`}
|
||||
onClick={() => {
|
||||
if (actionType === 'view' && selectedFile?.id === row.id) {
|
||||
setSelectedFile(null);
|
||||
setActionType(null);
|
||||
} else {
|
||||
const uploadedFile = getUploadedFile(row.id);
|
||||
setSelectedFile(uploadedFile || row); // Utiliser les données mises à jour
|
||||
setActionType('view');
|
||||
}
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<Eye className="w-5 h-5" />
|
||||
</button>
|
||||
<button
|
||||
className="flex items-center justify-center w-8 h-8 rounded-full text-red-500 hover:text-red-700"
|
||||
onClick={() => {
|
||||
setRemovePopupVisible(true);
|
||||
setRemovePopupMessage(
|
||||
`Êtes-vous sûr(e) de vouloir supprimer le fichier "${row.master_name}" ?`
|
||||
);
|
||||
setRemovePopupOnConfirm(() => () => {
|
||||
onFileDelete(row.id)
|
||||
.then(() => {
|
||||
setPopupMessage(`Le fichier "${row.master_name}" a été supprimé avec succès.`);
|
||||
setPopupVisible(true);
|
||||
setRemovePopupVisible(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error('Erreur lors de la suppression du fichier :', error);
|
||||
setPopupMessage(`Erreur lors de la suppression du fichier "${row.master_name}".`);
|
||||
setPopupVisible(true);
|
||||
setRemovePopupVisible(false);
|
||||
});
|
||||
setActionType(null);
|
||||
setSelectedFile(null);
|
||||
});
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<Trash2 className="w-5 h-5" />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
{!uploadedFile && (
|
||||
<button
|
||||
className={`flex items-center justify-center w-8 h-8 rounded-full ${
|
||||
actionType === 'upload' && selectedFile?.id === row.id
|
||||
? 'bg-emerald-100 text-emerald-600 ring-3 ring-emerald-500'
|
||||
: 'text-emerald-500 hover:text-emerald-700'
|
||||
}`}
|
||||
onClick={() => {
|
||||
if (actionType === 'upload' && selectedFile?.id === row.id) {
|
||||
setSelectedFile(null);
|
||||
setActionType(null);
|
||||
} else {
|
||||
setSelectedFile(row);
|
||||
setActionType('upload');
|
||||
}
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<Upload className="w-5 h-5" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}},
|
||||
];
|
||||
|
||||
export default function FilesToUpload({ parentFileTemplates, columns }) {
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
||||
<h2 className="text-xl font-bold mb-4 text-gray-800">Fichiers à uploader</h2>
|
||||
<div className="mt-8 mb-4 w-3/5">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="bg-emerald-100 p-3 rounded-full shadow-md">
|
||||
<FileText className="w-8 h-8 text-emerald-600" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-800">Pièces à fournir</h2>
|
||||
<p className="text-sm text-gray-500 italic">
|
||||
Ajoutez les documents pour compléter votre inscription
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Table
|
||||
data={parentFileTemplates}
|
||||
columns={columns}
|
||||
itemsPerPage={5}
|
||||
currentPage={1}
|
||||
totalPages={1}
|
||||
onPageChange={() => {}}
|
||||
/>
|
||||
{selectedFile && (
|
||||
<div className="mt-4">
|
||||
{actionType === 'view' && selectedFile.fileName ? (
|
||||
<iframe
|
||||
src={`${BASE_URL}/${selectedFile.fileName}`}
|
||||
title="Document Viewer"
|
||||
className="w-full"
|
||||
style={{
|
||||
height: '75vh',
|
||||
border: 'none',
|
||||
}}
|
||||
/>
|
||||
) : actionType === 'upload' ? (
|
||||
<FileUpload
|
||||
selectionMessage={`Téléversez le fichier ${selectedFile.master_name}`}
|
||||
onFileSelect={(file) => handleUpload(file, selectedFile)}
|
||||
uploadedFileName={selectedFile.fileName || ''}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
<Popup
|
||||
visible={popupVisible}
|
||||
message={popupMessage}
|
||||
onConfirm={() => setPopupVisible(false)}
|
||||
onCancel={() => setPopupVisible(false)}
|
||||
uniqueConfirmButton={true}
|
||||
/>
|
||||
<Popup
|
||||
visible={removePopupVisible}
|
||||
message={removePopupMessage}
|
||||
onConfirm={removePopupOnConfirm}
|
||||
onCancel={() => setRemovePopupVisible(false)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -4,24 +4,18 @@ import Loader from '@/components/Loader';
|
||||
import Button from '@/components/Button';
|
||||
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
|
||||
import { fetchRegisterForm, fetchSchoolFileTemplatesFromRegistrationFiles, fetchParentFileTemplatesFromRegistrationFiles } from '@/app/actions/subscriptionAction';
|
||||
import { downloadTemplate,
|
||||
createRegistrationSchoolFileTemplate,
|
||||
editRegistrationSchoolFileTemplates,
|
||||
deleteRegistrationSchoolFileTemplates
|
||||
import { downloadTemplate,
|
||||
editRegistrationSchoolFileTemplates,
|
||||
editRegistrationParentFileTemplates
|
||||
} from '@/app/actions/registerFileGroupAction';
|
||||
import {
|
||||
fetchRegistrationPaymentModes,
|
||||
fetchTuitionPaymentModes } from '@/app/actions/schoolAction';
|
||||
import { Download, Upload, Trash2, Eye } from 'lucide-react';
|
||||
import { BASE_URL } from '@/utils/Url';
|
||||
import DraggableFileUpload from '@/components/DraggableFileUpload';
|
||||
import Modal from '@/components/Modal';
|
||||
import FileStatusLabel from '@/components/FileStatusLabel';
|
||||
import logger from '@/utils/logger';
|
||||
import StudentInfoForm, { validateStudentInfo } from '@/components/Inscription/StudentInfoForm';
|
||||
import FilesToUpload from '@/components/Inscription/FilesToUpload';
|
||||
import { DocusealForm } from '@docuseal/react';
|
||||
import FileUpload from '@/components/Inscription/FileUpload';
|
||||
|
||||
/**
|
||||
* Composant de formulaire d'inscription partagé
|
||||
@ -36,7 +30,6 @@ export default function InscriptionFormShared({
|
||||
csrfToken,
|
||||
selectedEstablishmentId,
|
||||
onSubmit,
|
||||
cancelUrl,
|
||||
errors = {} // Nouvelle prop pour les erreurs
|
||||
}) {
|
||||
// États pour gérer les données du formulaire
|
||||
@ -65,12 +58,6 @@ export default function InscriptionFormShared({
|
||||
const [uploadedFiles, setUploadedFiles] = useState([]);
|
||||
const [schoolFileTemplates, setSchoolFileTemplates] = useState([]);
|
||||
const [parentFileTemplates, setParentFileTemplates] = useState([]);
|
||||
const [fileGroup, setFileGroup] = useState(null);
|
||||
const [fileName, setFileName] = useState("");
|
||||
const [file, setFile] = useState("");
|
||||
const [showUploadModal, setShowUploadModal] = useState(false);
|
||||
const [currentTemplateId, setCurrentTemplateId] = useState(null);
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
|
||||
const isCurrentPageValid = () => {
|
||||
@ -105,7 +92,6 @@ export default function InscriptionFormShared({
|
||||
totalTuitionFees: data?.totalTuitionFees,
|
||||
});
|
||||
setGuardians(data?.student?.guardians || []);
|
||||
setUploadedFiles(data.registration_files || []);
|
||||
});
|
||||
|
||||
setIsLoading(false);
|
||||
@ -119,6 +105,13 @@ export default function InscriptionFormShared({
|
||||
|
||||
fetchParentFileTemplatesFromRegistrationFiles(studentId).then((data) => {
|
||||
setParentFileTemplates(data);
|
||||
|
||||
// Initialiser uploadedFiles avec uniquement les fichiers dont `file` n'est pas null
|
||||
const filteredFiles = data.filter(item => item.file !== null).map(item => ({
|
||||
id: item.id,
|
||||
fileName: item.file
|
||||
}));
|
||||
setUploadedFiles(filteredFiles);
|
||||
})
|
||||
|
||||
if (selectedEstablishmentId) {
|
||||
@ -153,66 +146,74 @@ export default function InscriptionFormShared({
|
||||
setFormData(prev => ({...prev, [field]: value}));
|
||||
};
|
||||
|
||||
// Gestion du téléversement de fichiers
|
||||
const handleFileUpload = async (file, fileName) => {
|
||||
if (!file || !currentTemplateId || !formData.id) {
|
||||
logger.error('Missing required data for upload');
|
||||
const handleFileUpload = (file, selectedFile) => {
|
||||
if (!file || !selectedFile) {
|
||||
logger.error('Données manquantes pour le téléversement.');
|
||||
return Promise.reject(new Error('Données manquantes pour le téléversement.'));
|
||||
}
|
||||
|
||||
const updateData = new FormData();
|
||||
updateData.append('file', file);
|
||||
|
||||
return editRegistrationParentFileTemplates(selectedFile.id, updateData, csrfToken)
|
||||
.then((response) => {
|
||||
logger.debug('Template mis à jour avec succès :', response);
|
||||
|
||||
setUploadedFiles((prev) => {
|
||||
const updatedFiles = prev.map((uploadedFile) =>
|
||||
uploadedFile.id === selectedFile.id
|
||||
? { ...uploadedFile, fileName: response.data.file } // Met à jour le fichier téléversé
|
||||
: uploadedFile
|
||||
);
|
||||
|
||||
// Si le fichier n'existe pas encore, l'ajouter
|
||||
if (!updatedFiles.find(file => file.id === selectedFile.id)) {
|
||||
updatedFiles.push({
|
||||
id: selectedFile.id,
|
||||
fileName: response.data.file
|
||||
});
|
||||
}
|
||||
|
||||
return updatedFiles;
|
||||
});
|
||||
|
||||
return response; // Retourner la réponse pour signaler le succès
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error('Erreur lors de la mise à jour du fichier :', error);
|
||||
throw error; // Relancer l'erreur pour que l'appelant puisse la capturer
|
||||
});
|
||||
};
|
||||
|
||||
const handleDeleteFile = (templateId) => {
|
||||
const fileToDelete = uploadedFiles.find(file => parseInt(file.id) === templateId && file.fileName);
|
||||
if (!fileToDelete) {
|
||||
logger.error('Aucun fichier trouvé pour suppression.');
|
||||
return;
|
||||
}
|
||||
|
||||
const data = new FormData();
|
||||
data.append('file', file);
|
||||
data.append('name', fileName);
|
||||
data.append('template', currentTemplateId);
|
||||
data.append('register_form', formData.id);
|
||||
|
||||
try {
|
||||
const response = await createRegistrationSchoolFileTemplate(data, csrfToken);
|
||||
if (response) {
|
||||
setUploadedFiles(prev => {
|
||||
const newFiles = prev.filter(f => parseInt(f.template) !== currentTemplateId);
|
||||
return [...newFiles, {
|
||||
name: fileName,
|
||||
template: currentTemplateId,
|
||||
file: response.file
|
||||
}];
|
||||
});
|
||||
|
||||
// Rafraîchir les données du formulaire pour avoir les fichiers à jour
|
||||
if (studentId) {
|
||||
fetchRegisterForm(studentId).then((data) => {
|
||||
setUploadedFiles(data.registration_files || []);
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error uploading file:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Vérification si un fichier est déjà uploadé
|
||||
const isFileUploaded = (templateId) => {
|
||||
return uploadedFiles.find(template =>
|
||||
template.template === templateId
|
||||
);
|
||||
};
|
||||
|
||||
// Récupération d'un fichier uploadé
|
||||
const getUploadedFile = (templateId) => {
|
||||
return uploadedFiles.find(file => parseInt(file.template) === templateId);
|
||||
};
|
||||
|
||||
// Suppression d'un fichier
|
||||
const handleDeleteFile = async (templateId) => {
|
||||
const fileToDelete = getUploadedFile(templateId);
|
||||
if (!fileToDelete) return;
|
||||
|
||||
try {
|
||||
await deleteRegistrationSchoolFileTemplates(fileToDelete.id, csrfToken);
|
||||
setUploadedFiles(prev => prev.filter(f => parseInt(f.template) !== templateId));
|
||||
} catch (error) {
|
||||
logger.error('Error deleting file:', error);
|
||||
}
|
||||
|
||||
// Créer un FormData avec un champ vide pour "file"
|
||||
const updateData = new FormData();
|
||||
updateData.append('file', ''); // Envoyer chaine vide pour indiquer qu'aucun fichier n'est uploadé
|
||||
|
||||
return editRegistrationParentFileTemplates(templateId, updateData, csrfToken)
|
||||
.then((response) => {
|
||||
logger.debug('Fichier supprimé avec succès dans la base :', response);
|
||||
|
||||
// Mettre à jour l'état local pour refléter la suppression
|
||||
setUploadedFiles((prev) =>
|
||||
prev.map((uploadedFile) =>
|
||||
uploadedFile.id === templateId
|
||||
? { ...uploadedFile, fileName: null, fileUrl: null } // Réinitialiser les champs liés au fichier
|
||||
: uploadedFile
|
||||
)
|
||||
);
|
||||
return response;
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error('Erreur lors de la suppression du fichier dans la base :', error);
|
||||
throw error;
|
||||
});
|
||||
};
|
||||
|
||||
// Soumission du formulaire
|
||||
@ -252,67 +253,12 @@ export default function InscriptionFormShared({
|
||||
setCurrentPage(currentPage - 1);
|
||||
};
|
||||
|
||||
// Configuration des colonnes pour le tableau des fichiers
|
||||
const columns = [
|
||||
{ name: 'Nom du fichier', transform: (row) => row.master_name },
|
||||
{ name: 'Description du fichier', transform: (row) => row.master_description },
|
||||
{ name: 'Fichier de référence', transform: (row) => row.file && <div className="flex items-center justify-center gap-2"> <a href={`${BASE_URL}${row.file}`} target='_blank' className="text-blue-500 hover:text-blue-700">
|
||||
<Download size={16} />
|
||||
</a> </div>},
|
||||
{ name: 'Statut', transform: (row) =>
|
||||
row.is_required && (
|
||||
<FileStatusLabel
|
||||
status={isFileUploaded(row.id) ? 'sent' : 'pending'}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{ name: 'Actions', transform: (row) => {
|
||||
if (!row.is_required) return null;
|
||||
|
||||
const uploadedFile = getUploadedFile(row.id);
|
||||
|
||||
if (uploadedFile) {
|
||||
return (
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<a
|
||||
href={`${BASE_URL}${uploadedFile.file}`}
|
||||
target="_blank"
|
||||
className="text-blue-500 hover:text-blue-700"
|
||||
>
|
||||
<Eye size={16} />
|
||||
</a>
|
||||
<button
|
||||
className="text-red-500 hover:text-red-700"
|
||||
onClick={() => handleDeleteFile(row.id)}
|
||||
type="button"
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className="text-emerald-500 hover:text-emerald-700"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setCurrentTemplateId(row.id);
|
||||
setShowUploadModal(true);
|
||||
}}
|
||||
>
|
||||
<Upload size={16} />
|
||||
</button>
|
||||
);
|
||||
}},
|
||||
];
|
||||
|
||||
// Affichage du loader pendant le chargement
|
||||
if (isLoading) return <Loader />;
|
||||
|
||||
// Rendu du composant
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto p-6">
|
||||
<div className="mx-auto p-6">
|
||||
<form onSubmit={handleSubmit} className="space-y-8">
|
||||
<DjangoCSRFToken csrfToken={csrfToken}/>
|
||||
{/* Page 1 : Informations de l'élève et Responsables */}
|
||||
@ -330,76 +276,70 @@ export default function InscriptionFormShared({
|
||||
|
||||
{/* Pages suivantes : Section Fichiers d'inscription */}
|
||||
{currentPage > 1 && currentPage <= schoolFileTemplates.length + 1 && (
|
||||
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
||||
{/* Titre du document */}
|
||||
<div className="mb-4">
|
||||
<h2 className="text-lg font-semibold text-gray-800">
|
||||
{schoolFileTemplates[currentPage - 2].name || "Document sans nom"}
|
||||
</h2>
|
||||
<p className="text-sm text-gray-500">
|
||||
{schoolFileTemplates[currentPage - 2].description || "Aucune description disponible pour ce document."}
|
||||
</p>
|
||||
<div className="mt-8 mb-4 w-3/5">
|
||||
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
||||
{/* Titre du document */}
|
||||
<div className="mb-4">
|
||||
<h2 className="text-lg font-semibold text-gray-800">
|
||||
{schoolFileTemplates[currentPage - 2].name || "Document sans nom"}
|
||||
</h2>
|
||||
<p className="text-sm text-gray-500">
|
||||
{schoolFileTemplates[currentPage - 2].description || "Aucune description disponible pour ce document."}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Affichage du formulaire ou du document */}
|
||||
{schoolFileTemplates[currentPage - 2].file === null ? (
|
||||
<DocusealForm
|
||||
id="docusealForm"
|
||||
src={"https://docuseal.com/s/" + schoolFileTemplates[currentPage - 2].slug}
|
||||
withDownloadButton={false}
|
||||
onComplete={() => {
|
||||
downloadTemplate(schoolFileTemplates[currentPage - 2].slug)
|
||||
.then((data) => fetch(data))
|
||||
.then((response) => response.blob())
|
||||
.then((blob) => {
|
||||
const file = new File([blob], `${schoolFileTemplates[currentPage - 2].name}.pdf`, { type: blob.type });
|
||||
const updateData = new FormData();
|
||||
updateData.append('file', file);
|
||||
|
||||
return editRegistrationSchoolFileTemplates(schoolFileTemplates[currentPage - 2].id, updateData, csrfToken);
|
||||
})
|
||||
.then((data) => {
|
||||
logger.debug("EDIT TEMPLATE : ", data);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error("error editing template : ", error);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${BASE_URL}/${schoolFileTemplates[currentPage - 2].file}`}
|
||||
title="Document Viewer"
|
||||
className="w-full"
|
||||
style={{
|
||||
height: '75vh', // Ajuster la hauteur à 75% de la fenêtre
|
||||
border: 'none',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Affichage du formulaire ou du document */}
|
||||
{schoolFileTemplates[currentPage - 2].file === null ? (
|
||||
<DocusealForm
|
||||
id="docusealForm"
|
||||
src={"https://docuseal.com/s/" + schoolFileTemplates[currentPage - 2].slug}
|
||||
withDownloadButton={false}
|
||||
onComplete={() => {
|
||||
downloadTemplate(schoolFileTemplates[currentPage - 2].slug)
|
||||
.then((data) => fetch(data))
|
||||
.then((response) => response.blob())
|
||||
.then((blob) => {
|
||||
const file = new File([blob], `${schoolFileTemplates[currentPage - 2].name}.pdf`, { type: blob.type });
|
||||
const updateData = new FormData();
|
||||
updateData.append('file', file);
|
||||
|
||||
return editRegistrationSchoolFileTemplates(schoolFileTemplates[currentPage - 2].id, updateData, csrfToken);
|
||||
})
|
||||
.then((data) => {
|
||||
logger.debug("EDIT TEMPLATE : ", data);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error("error editing template : ", error);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${BASE_URL}/${schoolFileTemplates[currentPage - 2].file}`}
|
||||
title="Document Viewer"
|
||||
className="w-full"
|
||||
style={{
|
||||
height: '75vh', // Ajuster la hauteur à 75% de la fenêtre
|
||||
border: 'none',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Dernière page : Section Fichiers parents */}
|
||||
{currentPage === schoolFileTemplates.length + 2 && (
|
||||
<>
|
||||
<FilesToUpload
|
||||
parentFileTemplates={parentFileTemplates}
|
||||
columns={columns}
|
||||
/>
|
||||
<FileUpload
|
||||
selectionMessage='Sélectionnez un fichier'
|
||||
onFileSelect={(file) => {
|
||||
setUploadedFiles(file.name); // Stocke uniquement le nom du fichier
|
||||
logger.debug('Fichier sélectionné:', file.name);
|
||||
}}
|
||||
uploadedFileName={uploadedFiles}
|
||||
/>
|
||||
</>
|
||||
<FilesToUpload
|
||||
parentFileTemplates={parentFileTemplates}
|
||||
uploadedFiles={uploadedFiles}
|
||||
onFileUpload={handleFileUpload}
|
||||
onFileDelete={handleDeleteFile}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Boutons de contrôle */}
|
||||
<div className="flex justify-end space-x-4">
|
||||
<div className="flex justify-center space-x-4">
|
||||
<Button
|
||||
text="Sauvegarder"
|
||||
onClick={handleSave}
|
||||
@ -429,52 +369,6 @@ export default function InscriptionFormShared({
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
{schoolFileTemplates.length > 0 && (
|
||||
<Modal
|
||||
isOpen={showUploadModal}
|
||||
setIsOpen={setShowUploadModal}
|
||||
title="Téléverser un fichier"
|
||||
ContentComponent={() => (
|
||||
<>
|
||||
<DraggableFileUpload
|
||||
className="w-full"
|
||||
fileName={fileName}
|
||||
onFileSelect={(selectedFile) => {
|
||||
if (selectedFile) {
|
||||
setFile(selectedFile);
|
||||
setFileName(selectedFile.name);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div className="mt-4 flex justify-center space-x-4">
|
||||
<Button
|
||||
text="Annuler"
|
||||
onClick={() => {
|
||||
setShowUploadModal(false);
|
||||
setCurrentTemplateId(null);
|
||||
setFile(null);
|
||||
setFileName("");
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
text="Valider"
|
||||
onClick={() => {
|
||||
if (file && fileName) {
|
||||
handleFileUpload(file, fileName);
|
||||
setShowUploadModal(false);
|
||||
setCurrentTemplateId(null);
|
||||
setFile(null);
|
||||
setFileName("");
|
||||
}
|
||||
}}
|
||||
primary={true}
|
||||
disabled={!file || !fileName}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -6,14 +6,16 @@ import { BASE_URL } from '@/utils/Url';
|
||||
import { generateToken } from '@/app/actions/registerFileGroupAction';
|
||||
import logger from '@/utils/logger';
|
||||
import { GraduationCap, CloudUpload } from 'lucide-react';
|
||||
import FileUpload from '@/components/Inscription/FileUpload';
|
||||
import FileUpload from '@/components/FileUpload';
|
||||
import SectionHeader from '@/components/SectionHeader';
|
||||
|
||||
export default function ValidateSubscription({ studentId, firstName, lastName, paymentMode, file, onAccept }) {
|
||||
export default function ValidateSubscription({ studentId, firstName, lastName, paymentSepa, file, onAccept }) {
|
||||
const [token, setToken] = useState(null);
|
||||
const [uploadedFileName, setUploadedFileName] = useState('');
|
||||
const [selectedFile, setSelectedFile] = useState(null); // Nouvel état pour le fichier sélectionné
|
||||
const [pdfUrl, setPdfUrl] = useState(`${BASE_URL}/${file}`);
|
||||
const [isSepa, setIsSepa] = useState(paymentMode === '1'); // Vérifie si le mode de paiement est SEPA
|
||||
const [currentPage, setCurrentPage] = useState(1); // Gestion des pages
|
||||
const [isSepa, setIsSepa] = useState(paymentSepa); // Vérifie si le mode de paiement est SEPA
|
||||
const [currentPage, setCurrentPage] = useState(1); // Gestion des étapes
|
||||
|
||||
useEffect(() => {
|
||||
if (isSepa) {
|
||||
@ -25,34 +27,21 @@ export default function ValidateSubscription({ studentId, firstName, lastName, p
|
||||
}
|
||||
}, [isSepa]);
|
||||
|
||||
const handleUpload = (detail) => {
|
||||
logger.debug('Uploaded file detail:', detail);
|
||||
setUploadedFileName(detail.name);
|
||||
};
|
||||
|
||||
const handleAccept = () => {
|
||||
const fileInput = document.getElementById('fileInput'); // Récupère l'élément input
|
||||
const file = fileInput?.files[0]; // Récupère le fichier sélectionné
|
||||
|
||||
if (!file) {
|
||||
if (!selectedFile && isSepa) {
|
||||
logger.error('Aucun fichier sélectionné pour le champ SEPA.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const data = {
|
||||
status: 7,
|
||||
sepa_file: file,
|
||||
status: 7,
|
||||
sepa_file: selectedFile, // Utilise le fichier sélectionné depuis l'état
|
||||
};
|
||||
|
||||
|
||||
// Appeler la fonction passée par le parent pour mettre à jour le RF
|
||||
onAccept(data);
|
||||
};
|
||||
|
||||
const handleRefuse = () => {
|
||||
logger.debug('Dossier refusé pour l\'étudiant:', studentId);
|
||||
// Logique pour refuser l'inscription
|
||||
};
|
||||
|
||||
const isValidateButtonDisabled = isSepa && !uploadedFileName;
|
||||
|
||||
const goToNextPage = () => {
|
||||
@ -68,25 +57,16 @@ export default function ValidateSubscription({ studentId, firstName, lastName, p
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-8 space-y-6 bg-gray-50 rounded-lg shadow-lg">
|
||||
{/* Titre */}
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="bg-emerald-100 p-3 rounded-full shadow-md">
|
||||
<GraduationCap className="w-8 h-8 text-emerald-600" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-800">
|
||||
Dossier scolaire de <span className="text-emerald-600">{firstName} {lastName}</span>
|
||||
</h1>
|
||||
<p className="text-sm text-gray-500 italic">
|
||||
Année scolaire {new Date().getFullYear()}-{new Date().getFullYear() + 1}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-6 p-6">
|
||||
<SectionHeader
|
||||
icon={GraduationCap}
|
||||
title={`Dossier scolaire de ${firstName} ${lastName}`}
|
||||
description={`Année scolaire ${new Date().getFullYear()}-${new Date().getFullYear() + 1}`}
|
||||
/>
|
||||
|
||||
{/* Contenu principal */}
|
||||
{currentPage === 1 && (
|
||||
<div className="border p-6 rounded-lg shadow-md bg-white flex justify-center items-center">
|
||||
<div className="p-6 items-center">
|
||||
<iframe
|
||||
src={pdfUrl}
|
||||
title="Aperçu du PDF"
|
||||
@ -102,9 +82,10 @@ export default function ValidateSubscription({ studentId, firstName, lastName, p
|
||||
|
||||
{currentPage === 2 && isSepa && (
|
||||
<FileUpload
|
||||
selectionMessage='Sélectionnez un mandat de prélèvement SEPA'
|
||||
selectionMessage="Sélectionnez un mandat de prélèvement SEPA"
|
||||
onFileSelect={(file) => {
|
||||
setUploadedFileName(file.name); // Stocke uniquement le nom du fichier
|
||||
setSelectedFile(file); // Stocke le fichier dans l'état
|
||||
logger.debug('Fichier sélectionné:', file.name);
|
||||
}}
|
||||
uploadedFileName={uploadedFileName}
|
||||
@ -120,7 +101,7 @@ export default function ValidateSubscription({ studentId, firstName, lastName, p
|
||||
className="bg-gray-300 text-gray-700 hover:bg-gray-400 px-6 py-2"
|
||||
/>
|
||||
)}
|
||||
{currentPage < (isSepa ? 2 : 1) && (
|
||||
{isSepa && currentPage === 1 && (
|
||||
<Button
|
||||
text="Suivant"
|
||||
onClick={goToNextPage}
|
||||
|
||||
Reference in New Issue
Block a user