mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 16:03:21 +00:00
chore: application prettier
This commit is contained in:
@ -2,17 +2,19 @@ import React from 'react';
|
||||
import Table from '@/components/Table';
|
||||
|
||||
export default function FilesToSign({ fileTemplates, 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 à remplir</h2>
|
||||
<Table
|
||||
data={fileTemplates}
|
||||
columns={columns}
|
||||
itemsPerPage={5}
|
||||
currentPage={1}
|
||||
totalPages={1}
|
||||
onPageChange={() => {}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
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 à remplir
|
||||
</h2>
|
||||
<Table
|
||||
data={fileTemplates}
|
||||
columns={columns}
|
||||
itemsPerPage={5}
|
||||
currentPage={1}
|
||||
totalPages={1}
|
||||
onPageChange={() => {}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -2,17 +2,19 @@ import React from 'react';
|
||||
import Table from '@/components/Table';
|
||||
|
||||
export default function FilesToUpload({ fileTemplates, 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>
|
||||
<Table
|
||||
data={fileTemplates}
|
||||
columns={columns}
|
||||
itemsPerPage={5}
|
||||
currentPage={1}
|
||||
totalPages={1}
|
||||
onPageChange={() => {}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
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>
|
||||
<Table
|
||||
data={fileTemplates}
|
||||
columns={columns}
|
||||
itemsPerPage={5}
|
||||
currentPage={1}
|
||||
totalPages={1}
|
||||
onPageChange={() => {}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -3,22 +3,29 @@ import React, { useState, useEffect } from 'react';
|
||||
import Loader from '@/components/Loader';
|
||||
import Button from '@/components/Button';
|
||||
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
|
||||
import { fetchRegisterForm, fetchTemplatesFromRegistrationFiles } from '@/app/actions/subscriptionAction';
|
||||
import { downloadTemplate,
|
||||
createRegistrationTemplates,
|
||||
editRegistrationTemplates,
|
||||
deleteRegistrationTemplates
|
||||
import {
|
||||
fetchRegisterForm,
|
||||
fetchTemplatesFromRegistrationFiles,
|
||||
} from '@/app/actions/subscriptionAction';
|
||||
import {
|
||||
downloadTemplate,
|
||||
createRegistrationTemplates,
|
||||
editRegistrationTemplates,
|
||||
deleteRegistrationTemplates,
|
||||
} from '@/app/actions/registerFileGroupAction';
|
||||
import {
|
||||
fetchRegistrationPaymentModes,
|
||||
fetchTuitionPaymentModes } from '@/app/actions/schoolAction';
|
||||
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 StudentInfoForm, {
|
||||
validateStudentInfo,
|
||||
} from '@/components/Inscription/StudentInfoForm';
|
||||
import FilesToUpload from '@/components/Inscription/FilesToUpload';
|
||||
import { DocusealForm } from '@docuseal/react';
|
||||
|
||||
@ -31,440 +38,498 @@ import { DocusealForm } from '@docuseal/react';
|
||||
* @param {object} errors - Erreurs de validation du formulaire
|
||||
*/
|
||||
export default function InscriptionFormShared({
|
||||
studentId,
|
||||
csrfToken,
|
||||
selectedEstablishmentId,
|
||||
onSubmit,
|
||||
cancelUrl,
|
||||
errors = {} // Nouvelle prop pour les erreurs
|
||||
studentId,
|
||||
csrfToken,
|
||||
selectedEstablishmentId,
|
||||
onSubmit,
|
||||
cancelUrl,
|
||||
errors = {}, // Nouvelle prop pour les erreurs
|
||||
}) {
|
||||
// États pour gérer les données du formulaire
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [formData, setFormData] = useState({
|
||||
id: '',
|
||||
last_name: '',
|
||||
first_name: '',
|
||||
address: '',
|
||||
birth_date: '',
|
||||
birth_place: '',
|
||||
birth_postal_code: '',
|
||||
nationality: '',
|
||||
attending_physician: '',
|
||||
level: '',
|
||||
registration_payment: '',
|
||||
tuition_payment: ''
|
||||
// États pour gérer les données du formulaire
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [formData, setFormData] = useState({
|
||||
id: '',
|
||||
last_name: '',
|
||||
first_name: '',
|
||||
address: '',
|
||||
birth_date: '',
|
||||
birth_place: '',
|
||||
birth_postal_code: '',
|
||||
nationality: '',
|
||||
attending_physician: '',
|
||||
level: '',
|
||||
registration_payment: '',
|
||||
tuition_payment: '',
|
||||
});
|
||||
|
||||
const [guardians, setGuardians] = useState([]);
|
||||
|
||||
const [registrationPaymentModes, setRegistrationPaymentModes] = useState([]);
|
||||
const [tuitionPaymentModes, setTuitionPaymentModes] = useState([]);
|
||||
|
||||
// États pour la gestion des fichiers
|
||||
const [uploadedFiles, setUploadedFiles] = useState([]);
|
||||
const [fileTemplates, setFileTemplates] = 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 = () => {
|
||||
if (currentPage === 1) {
|
||||
const isValid = validateStudentInfo(formData);
|
||||
return isValid;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Chargement initial des données
|
||||
// Mettre à jour les données quand initialData change
|
||||
useEffect(() => {
|
||||
if (studentId) {
|
||||
fetchRegisterForm(studentId).then((data) => {
|
||||
logger.debug(data);
|
||||
|
||||
setFormData({
|
||||
id: data?.student?.id || '',
|
||||
last_name: data?.student?.last_name || '',
|
||||
first_name: data?.student?.first_name || '',
|
||||
address: data?.student?.address || '',
|
||||
birth_date: data?.student?.birth_date || '',
|
||||
birth_place: data?.student?.birth_place || '',
|
||||
birth_postal_code: data?.student?.birth_postal_code || '',
|
||||
nationality: data?.student?.nationality || '',
|
||||
attending_physician: data?.student?.attending_physician || '',
|
||||
level: data?.student?.level || '',
|
||||
registration_payment: data?.registration_payment || '',
|
||||
tuition_payment: data?.tuition_payment || '',
|
||||
totalRegistrationFees: data?.totalRegistrationFees,
|
||||
totalTuitionFees: data?.totalTuitionFees,
|
||||
});
|
||||
setGuardians(data?.student?.guardians || []);
|
||||
setUploadedFiles(data.registration_files || []);
|
||||
});
|
||||
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [studentId]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchTemplatesFromRegistrationFiles(studentId).then((data) => {
|
||||
setFileTemplates(data);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const [guardians, setGuardians] = useState([]);
|
||||
useEffect(() => {
|
||||
if (selectedEstablishmentId) {
|
||||
// Fetch data for registration payment modes
|
||||
handleRegistrationPaymentModes();
|
||||
|
||||
const [registrationPaymentModes, setRegistrationPaymentModes] = useState([]);
|
||||
const [tuitionPaymentModes, setTuitionPaymentModes] = useState([]);
|
||||
// Fetch data for tuition payment modes
|
||||
handleTuitionPaymentModes();
|
||||
}
|
||||
}, [selectedEstablishmentId]);
|
||||
|
||||
// États pour la gestion des fichiers
|
||||
const [uploadedFiles, setUploadedFiles] = useState([]);
|
||||
const [fileTemplates, setFileTemplates] = 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 = () => {
|
||||
if (currentPage === 1) {
|
||||
const isValid = validateStudentInfo(formData);
|
||||
return isValid;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Chargement initial des données
|
||||
// Mettre à jour les données quand initialData change
|
||||
useEffect(() => {
|
||||
if (studentId) {
|
||||
fetchRegisterForm(studentId).then((data) => {
|
||||
logger.debug(data);
|
||||
|
||||
setFormData({
|
||||
id: data?.student?.id || '',
|
||||
last_name: data?.student?.last_name || '',
|
||||
first_name: data?.student?.first_name || '',
|
||||
address: data?.student?.address || '',
|
||||
birth_date: data?.student?.birth_date || '',
|
||||
birth_place: data?.student?.birth_place || '',
|
||||
birth_postal_code: data?.student?.birth_postal_code || '',
|
||||
nationality: data?.student?.nationality || '',
|
||||
attending_physician: data?.student?.attending_physician || '',
|
||||
level: data?.student?.level || '',
|
||||
registration_payment: data?.registration_payment || '',
|
||||
tuition_payment: data?.tuition_payment || '',
|
||||
totalRegistrationFees: data?.totalRegistrationFees,
|
||||
totalTuitionFees: data?.totalTuitionFees,
|
||||
});
|
||||
setGuardians(data?.student?.guardians || []);
|
||||
setUploadedFiles(data.registration_files || []);
|
||||
});
|
||||
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [studentId]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchTemplatesFromRegistrationFiles(studentId).then((data) => {
|
||||
setFileTemplates(data);
|
||||
})
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedEstablishmentId) {
|
||||
// Fetch data for registration payment modes
|
||||
handleRegistrationPaymentModes();
|
||||
|
||||
// Fetch data for tuition payment modes
|
||||
handleTuitionPaymentModes();
|
||||
}
|
||||
}, [selectedEstablishmentId]);
|
||||
|
||||
const handleRegistrationPaymentModes = () => {
|
||||
fetchRegistrationPaymentModes(selectedEstablishmentId)
|
||||
.then(data => {
|
||||
const activePaymentModes = data.filter(mode => mode.is_active === true);
|
||||
setRegistrationPaymentModes(activePaymentModes);
|
||||
})
|
||||
.catch(error => logger.error('Error fetching registration payment modes:', error));
|
||||
};
|
||||
|
||||
const handleTuitionPaymentModes = () => {
|
||||
fetchTuitionPaymentModes(selectedEstablishmentId)
|
||||
.then(data => {
|
||||
const activePaymentModes = data.filter(mode => mode.is_active === true);
|
||||
setTuitionPaymentModes(activePaymentModes);
|
||||
})
|
||||
.catch(error => logger.error('Error fetching tuition payment modes:', error));
|
||||
};
|
||||
|
||||
// Fonctions de gestion du formulaire et des fichiers
|
||||
const updateFormField = (field, value) => {
|
||||
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');
|
||||
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 createRegistrationTemplates(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
|
||||
const handleRegistrationPaymentModes = () => {
|
||||
fetchRegistrationPaymentModes(selectedEstablishmentId)
|
||||
.then((data) => {
|
||||
const activePaymentModes = data.filter(
|
||||
(mode) => mode.is_active === true
|
||||
);
|
||||
};
|
||||
setRegistrationPaymentModes(activePaymentModes);
|
||||
})
|
||||
.catch((error) =>
|
||||
logger.error('Error fetching registration payment modes:', error)
|
||||
);
|
||||
};
|
||||
|
||||
// Récupération d'un fichier uploadé
|
||||
const getUploadedFile = (templateId) => {
|
||||
return uploadedFiles.find(file => parseInt(file.template) === templateId);
|
||||
};
|
||||
const handleTuitionPaymentModes = () => {
|
||||
fetchTuitionPaymentModes(selectedEstablishmentId)
|
||||
.then((data) => {
|
||||
const activePaymentModes = data.filter(
|
||||
(mode) => mode.is_active === true
|
||||
);
|
||||
setTuitionPaymentModes(activePaymentModes);
|
||||
})
|
||||
.catch((error) =>
|
||||
logger.error('Error fetching tuition payment modes:', error)
|
||||
);
|
||||
};
|
||||
|
||||
// Suppression d'un fichier
|
||||
const handleDeleteFile = async (templateId) => {
|
||||
const fileToDelete = getUploadedFile(templateId);
|
||||
if (!fileToDelete) return;
|
||||
// Fonctions de gestion du formulaire et des fichiers
|
||||
const updateFormField = (field, value) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
try {
|
||||
await deleteRegistrationTemplates(fileToDelete.id, csrfToken);
|
||||
setUploadedFiles(prev => prev.filter(f => parseInt(f.template) !== templateId));
|
||||
} catch (error) {
|
||||
logger.error('Error deleting file:', error);
|
||||
}
|
||||
};
|
||||
// Gestion du téléversement de fichiers
|
||||
const handleFileUpload = async (file, fileName) => {
|
||||
if (!file || !currentTemplateId || !formData.id) {
|
||||
logger.error('Missing required data for upload');
|
||||
return;
|
||||
}
|
||||
|
||||
// Soumission du formulaire
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
const data ={
|
||||
student: {
|
||||
...formData,
|
||||
guardians
|
||||
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 createRegistrationTemplates(data, csrfToken);
|
||||
if (response) {
|
||||
setUploadedFiles((prev) => {
|
||||
const newFiles = prev.filter(
|
||||
(f) => parseInt(f.template) !== currentTemplateId
|
||||
);
|
||||
return [
|
||||
...newFiles,
|
||||
{
|
||||
name: fileName,
|
||||
template: currentTemplateId,
|
||||
file: response.file,
|
||||
},
|
||||
establishment: selectedEstablishmentId,
|
||||
status:3,
|
||||
tuition_payment:formData.tuition_payment,
|
||||
registration_payment:formData.registration_payment
|
||||
];
|
||||
});
|
||||
|
||||
// Rafraîchir les données du formulaire pour avoir les fichiers à jour
|
||||
if (studentId) {
|
||||
fetchRegisterForm(studentId).then((data) => {
|
||||
setUploadedFiles(data.registration_files || []);
|
||||
});
|
||||
}
|
||||
onSubmit(data);
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error uploading file:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Soumission du formulaire
|
||||
const handleSave = (e) => {
|
||||
e.preventDefault();
|
||||
const data ={
|
||||
student: {
|
||||
...formData,
|
||||
guardians
|
||||
},
|
||||
establishment: selectedEstablishmentId
|
||||
// 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 deleteRegistrationTemplates(fileToDelete.id, csrfToken);
|
||||
setUploadedFiles((prev) =>
|
||||
prev.filter((f) => parseInt(f.template) !== templateId)
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error('Error deleting file:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Soumission du formulaire
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
const data = {
|
||||
student: {
|
||||
...formData,
|
||||
guardians,
|
||||
},
|
||||
establishment: selectedEstablishmentId,
|
||||
status: 3,
|
||||
tuition_payment: formData.tuition_payment,
|
||||
registration_payment: formData.registration_payment,
|
||||
};
|
||||
onSubmit(data);
|
||||
};
|
||||
|
||||
// Soumission du formulaire
|
||||
const handleSave = (e) => {
|
||||
e.preventDefault();
|
||||
const data = {
|
||||
student: {
|
||||
...formData,
|
||||
guardians,
|
||||
},
|
||||
establishment: selectedEstablishmentId,
|
||||
};
|
||||
onSubmit(data);
|
||||
};
|
||||
|
||||
const handleNextPage = () => {
|
||||
setCurrentPage(currentPage + 1);
|
||||
};
|
||||
|
||||
const handlePreviousPage = () => {
|
||||
setCurrentPage(currentPage - 1);
|
||||
};
|
||||
|
||||
const requiredFileTemplates = fileTemplates;
|
||||
|
||||
// Configuration des colonnes pour le tableau des fichiers
|
||||
const columns = [
|
||||
{ name: 'Nom du fichier', transform: (row) => row.name },
|
||||
{
|
||||
name: 'Fichier à Remplir',
|
||||
transform: (row) => (row.is_required ? 'Oui' : 'Non'),
|
||||
},
|
||||
{
|
||||
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>
|
||||
);
|
||||
}
|
||||
onSubmit(data);
|
||||
};
|
||||
|
||||
const handleNextPage = () => {
|
||||
setCurrentPage(currentPage + 1);
|
||||
};
|
||||
|
||||
const handlePreviousPage = () => {
|
||||
setCurrentPage(currentPage - 1);
|
||||
};
|
||||
return (
|
||||
<button
|
||||
className="text-emerald-500 hover:text-emerald-700"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setCurrentTemplateId(row.id);
|
||||
setShowUploadModal(true);
|
||||
}}
|
||||
>
|
||||
<Upload size={16} />
|
||||
</button>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const requiredFileTemplates = fileTemplates;
|
||||
// Affichage du loader pendant le chargement
|
||||
if (isLoading) return <Loader />;
|
||||
|
||||
// Configuration des colonnes pour le tableau des fichiers
|
||||
const columns = [
|
||||
{ name: 'Nom du fichier', transform: (row) => row.name },
|
||||
{ name: 'Fichier à Remplir', transform: (row) => row.is_required ? 'Oui' : 'Non' },
|
||||
{ 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;
|
||||
// Rendu du composant
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto p-6">
|
||||
<form onSubmit={handleSubmit} className="space-y-8">
|
||||
<DjangoCSRFToken csrfToken={csrfToken} />
|
||||
{/* Page 1 : Informations de l'élève et Responsables */}
|
||||
{currentPage === 1 && (
|
||||
<StudentInfoForm
|
||||
formData={formData}
|
||||
updateFormField={updateFormField}
|
||||
guardians={guardians}
|
||||
setGuardians={setGuardians}
|
||||
registrationPaymentModes={registrationPaymentModes}
|
||||
tuitionPaymentModes={tuitionPaymentModes}
|
||||
errors={errors}
|
||||
/>
|
||||
)}
|
||||
|
||||
const uploadedFile = getUploadedFile(row.id);
|
||||
{/* Pages suivantes : Section Fichiers d'inscription */}
|
||||
{currentPage > 1 && currentPage <= requiredFileTemplates.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">
|
||||
{requiredFileTemplates[currentPage - 2].name ||
|
||||
'Document sans nom'}
|
||||
</h2>
|
||||
<p className="text-sm text-gray-500">
|
||||
{requiredFileTemplates[currentPage - 2].description ||
|
||||
'Aucune description disponible pour ce document.'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
{/* Affichage du formulaire ou du document */}
|
||||
{requiredFileTemplates[currentPage - 2].file === '' ? (
|
||||
<DocusealForm
|
||||
id="docusealForm"
|
||||
src={
|
||||
'https://docuseal.com/s/' +
|
||||
requiredFileTemplates[currentPage - 2].slug
|
||||
}
|
||||
withDownloadButton={false}
|
||||
onComplete={() => {
|
||||
downloadTemplate(requiredFileTemplates[currentPage - 2].slug)
|
||||
.then((data) => fetch(data))
|
||||
.then((response) => response.blob())
|
||||
.then((blob) => {
|
||||
const file = new File(
|
||||
[blob],
|
||||
`${requiredFileTemplates[currentPage - 2].name}.pdf`,
|
||||
{ type: blob.type }
|
||||
);
|
||||
const updateData = new FormData();
|
||||
updateData.append('file', file);
|
||||
|
||||
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">
|
||||
<form onSubmit={handleSubmit} className="space-y-8">
|
||||
<DjangoCSRFToken csrfToken={csrfToken}/>
|
||||
{/* Page 1 : Informations de l'élève et Responsables */}
|
||||
{currentPage === 1 && (
|
||||
<StudentInfoForm
|
||||
formData={formData}
|
||||
updateFormField={updateFormField}
|
||||
guardians={guardians}
|
||||
setGuardians={setGuardians}
|
||||
registrationPaymentModes={registrationPaymentModes}
|
||||
tuitionPaymentModes={tuitionPaymentModes}
|
||||
errors={errors}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Pages suivantes : Section Fichiers d'inscription */}
|
||||
{currentPage > 1 && currentPage <= requiredFileTemplates.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">
|
||||
{requiredFileTemplates[currentPage - 2].name || "Document sans nom"}
|
||||
</h2>
|
||||
<p className="text-sm text-gray-500">
|
||||
{requiredFileTemplates[currentPage - 2].description || "Aucune description disponible pour ce document."}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Affichage du formulaire ou du document */}
|
||||
{requiredFileTemplates[currentPage - 2].file === "" ? (
|
||||
<DocusealForm
|
||||
id="docusealForm"
|
||||
src={"https://docuseal.com/s/" + requiredFileTemplates[currentPage - 2].slug}
|
||||
withDownloadButton={false}
|
||||
onComplete={() => {
|
||||
downloadTemplate(requiredFileTemplates[currentPage - 2].slug)
|
||||
.then((data) => fetch(data))
|
||||
.then((response) => response.blob())
|
||||
.then((blob) => {
|
||||
const file = new File([blob], `${requiredFileTemplates[currentPage - 2].name}.pdf`, { type: blob.type });
|
||||
const updateData = new FormData();
|
||||
updateData.append('file', file);
|
||||
|
||||
return editRegistrationTemplates(requiredFileTemplates[currentPage - 2].id, updateData, csrfToken);
|
||||
})
|
||||
.then((data) => {
|
||||
logger.debug("EDIT TEMPLATE : ", data);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error("error editing template : ", error);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${BASE_URL}/${requiredFileTemplates[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 === requiredFileTemplates.length + 2 && (
|
||||
<>
|
||||
<FilesToUpload
|
||||
fileTemplates={fileTemplates.filter(template => !template.is_required)}
|
||||
columns={columns}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Boutons de contrôle */}
|
||||
<div className="flex justify-end space-x-4">
|
||||
<Button
|
||||
text="Sauvegarder"
|
||||
onClick={handleSave}
|
||||
className="px-4 py-2 rounded-md shadow-sm focus:outline-none bg-orange-500 text-white hover:bg-orange-600"
|
||||
primary
|
||||
name="Save"
|
||||
/>
|
||||
{currentPage > 1 && (
|
||||
<Button text="Précédent" onClick={(e) => { e.preventDefault(); handlePreviousPage(); }} />
|
||||
)}
|
||||
{currentPage < requiredFileTemplates.length + 2 && (
|
||||
<Button
|
||||
text="Suivant"
|
||||
onClick={(e) => { e.preventDefault(); handleNextPage(); }}
|
||||
className={`px-4 py-2 rounded-md shadow-sm focus:outline-none ${
|
||||
!isCurrentPageValid()
|
||||
? "bg-gray-300 text-gray-700 cursor-not-allowed"
|
||||
: "bg-emerald-500 text-white hover:bg-emerald-600"
|
||||
}`}
|
||||
disabled={!isCurrentPageValid()}
|
||||
primary
|
||||
name="Next"
|
||||
/>
|
||||
)}
|
||||
{currentPage === requiredFileTemplates.length + 2 && (
|
||||
<Button type="submit" text="Valider" primary />
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
{fileTemplates.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>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
return editRegistrationTemplates(
|
||||
requiredFileTemplates[currentPage - 2].id,
|
||||
updateData,
|
||||
csrfToken
|
||||
);
|
||||
})
|
||||
.then((data) => {
|
||||
logger.debug('EDIT TEMPLATE : ', data);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error('error editing template : ', error);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<iframe
|
||||
src={`${BASE_URL}/${requiredFileTemplates[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 === requiredFileTemplates.length + 2 && (
|
||||
<>
|
||||
<FilesToUpload
|
||||
fileTemplates={fileTemplates.filter(
|
||||
(template) => !template.is_required
|
||||
)}
|
||||
columns={columns}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Boutons de contrôle */}
|
||||
<div className="flex justify-end space-x-4">
|
||||
<Button
|
||||
text="Sauvegarder"
|
||||
onClick={handleSave}
|
||||
className="px-4 py-2 rounded-md shadow-sm focus:outline-none bg-orange-500 text-white hover:bg-orange-600"
|
||||
primary
|
||||
name="Save"
|
||||
/>
|
||||
{currentPage > 1 && (
|
||||
<Button
|
||||
text="Précédent"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
handlePreviousPage();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{currentPage < requiredFileTemplates.length + 2 && (
|
||||
<Button
|
||||
text="Suivant"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
handleNextPage();
|
||||
}}
|
||||
className={`px-4 py-2 rounded-md shadow-sm focus:outline-none ${
|
||||
!isCurrentPageValid()
|
||||
? 'bg-gray-300 text-gray-700 cursor-not-allowed'
|
||||
: 'bg-emerald-500 text-white hover:bg-emerald-600'
|
||||
}`}
|
||||
disabled={!isCurrentPageValid()}
|
||||
primary
|
||||
name="Next"
|
||||
/>
|
||||
)}
|
||||
{currentPage === requiredFileTemplates.length + 2 && (
|
||||
<Button type="submit" text="Valider" primary />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
</form>
|
||||
{fileTemplates.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>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,34 +1,48 @@
|
||||
import React from 'react';
|
||||
import SelectChoice from '@/components/SelectChoice';
|
||||
|
||||
export default function PaymentMethodSelector({ formData, title, name, updateFormField, selected, paymentModes, paymentModesOptions, amount, getError }) {
|
||||
//console.log(paymentModes)
|
||||
//console.log(selected)
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
||||
{/* Titre */}
|
||||
<h2 className="text-2xl font-semibold mb-6 text-gray-800 border-b pb-2">{title}</h2>
|
||||
export default function PaymentMethodSelector({
|
||||
formData,
|
||||
title,
|
||||
name,
|
||||
updateFormField,
|
||||
selected,
|
||||
paymentModes,
|
||||
paymentModesOptions,
|
||||
amount,
|
||||
getError,
|
||||
}) {
|
||||
//console.log(paymentModes)
|
||||
//console.log(selected)
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
||||
{/* Titre */}
|
||||
<h2 className="text-2xl font-semibold mb-6 text-gray-800 border-b pb-2">
|
||||
{title}
|
||||
</h2>
|
||||
|
||||
{/* Section d'information */}
|
||||
<div className="mb-6 bg-gray-50 p-4 rounded-lg border border-gray-100">
|
||||
<p className="text-gray-700 text-sm mb-2">
|
||||
<strong className="text-gray-900">Montant :</strong> {amount} €
|
||||
</p>
|
||||
</div>
|
||||
{/* Section d'information */}
|
||||
<div className="mb-6 bg-gray-50 p-4 rounded-lg border border-gray-100">
|
||||
<p className="text-gray-700 text-sm mb-2">
|
||||
<strong className="text-gray-900">Montant :</strong> {amount} €
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<SelectChoice
|
||||
name={name}
|
||||
label="Mode de Paiement"
|
||||
placeHolder="Sélectionner un mode de paiement"
|
||||
selected={selected || ''}
|
||||
callback={(e) => updateFormField(name, e.target.value)}
|
||||
choices={paymentModes.map((mode) => ({
|
||||
value: mode.mode,
|
||||
label: paymentModesOptions.find(option => option.id === mode.mode)?.name || 'Mode inconnu'
|
||||
}))}
|
||||
required
|
||||
errorMsg={getError('payment_method')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
<SelectChoice
|
||||
name={name}
|
||||
label="Mode de Paiement"
|
||||
placeHolder="Sélectionner un mode de paiement"
|
||||
selected={selected || ''}
|
||||
callback={(e) => updateFormField(name, e.target.value)}
|
||||
choices={paymentModes.map((mode) => ({
|
||||
value: mode.mode,
|
||||
label:
|
||||
paymentModesOptions.find((option) => option.id === mode.mode)
|
||||
?.name || 'Mode inconnu',
|
||||
}))}
|
||||
required
|
||||
errorMsg={getError('payment_method')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,112 +5,137 @@ import React from 'react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Trash2, Plus } from 'lucide-react';
|
||||
|
||||
export default function ResponsableInputFields({guardians, onGuardiansChange, addGuardian, deleteGuardian, errors = []}) {
|
||||
const t = useTranslations('ResponsableInputFields');
|
||||
export default function ResponsableInputFields({
|
||||
guardians,
|
||||
onGuardiansChange,
|
||||
addGuardian,
|
||||
deleteGuardian,
|
||||
errors = [],
|
||||
}) {
|
||||
const t = useTranslations('ResponsableInputFields');
|
||||
|
||||
const getError = (index, field) => {
|
||||
return errors[index]?.[field]?.[0];
|
||||
};
|
||||
const getError = (index, field) => {
|
||||
return errors[index]?.[field]?.[0];
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
{guardians.map((item, index) => (
|
||||
<div className="p-6 bg-gray-50 rounded-lg shadow-sm" key={index}>
|
||||
<div className='flex justify-between items-center mb-4'>
|
||||
<h3 className='text-xl font-bold'>{t('responsable')} {index+1}</h3>
|
||||
{guardians.length > 1 && (
|
||||
<Trash2
|
||||
className="w-5 h-5 text-red-500 cursor-pointer hover:text-red-700 transition-colors"
|
||||
onClick={() => deleteGuardian(index)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
{guardians.map((item, index) => (
|
||||
<div className="p-6 bg-gray-50 rounded-lg shadow-sm" key={index}>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h3 className="text-xl font-bold">
|
||||
{t('responsable')} {index + 1}
|
||||
</h3>
|
||||
{guardians.length > 1 && (
|
||||
<Trash2
|
||||
className="w-5 h-5 text-red-500 cursor-pointer hover:text-red-700 transition-colors"
|
||||
onClick={() => deleteGuardian(index)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="idResponsable" value={item.id} />
|
||||
<input type="hidden" name="idResponsable" value={item.id} />
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
||||
<InputText
|
||||
name="nomResponsable"
|
||||
type="text"
|
||||
label={t('lastname')}
|
||||
value={item.last_name}
|
||||
onChange={(event) => {
|
||||
onGuardiansChange(item.id, 'last_name', event.target.value);
|
||||
}}
|
||||
errorMsg={getError(index, 'last_name')}
|
||||
required
|
||||
/>
|
||||
<InputText
|
||||
name="prenomResponsable"
|
||||
type="text"
|
||||
label={t('firstname')}
|
||||
value={item.first_name}
|
||||
onChange={(event) => {
|
||||
onGuardiansChange(item.id, 'first_name', event.target.value);
|
||||
}}
|
||||
errorMsg={getError(index, 'first_name')}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='grid grid-cols-1 md:grid-cols-2 gap-4 mb-4'>
|
||||
<InputText
|
||||
name="nomResponsable"
|
||||
type="text"
|
||||
label={t('lastname')}
|
||||
value={item.last_name}
|
||||
onChange={(event) => {onGuardiansChange(item.id, "last_name", event.target.value)}}
|
||||
errorMsg={getError(index, 'last_name')}
|
||||
required
|
||||
/>
|
||||
<InputText
|
||||
name="prenomResponsable"
|
||||
type="text"
|
||||
label={t('firstname')}
|
||||
value={item.first_name}
|
||||
onChange={(event) => {onGuardiansChange(item.id, "first_name", event.target.value)}}
|
||||
errorMsg={getError(index, 'first_name')}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
||||
<InputText
|
||||
name="mailResponsable"
|
||||
type="email"
|
||||
label={t('email')}
|
||||
value={item.associated_profile_email}
|
||||
onChange={(event) => {
|
||||
onGuardiansChange(
|
||||
item.id,
|
||||
'associated_profile_email',
|
||||
event.target.value
|
||||
);
|
||||
}}
|
||||
required
|
||||
errorMsg={getError(index, 'email')}
|
||||
/>
|
||||
<InputPhone
|
||||
name="telephoneResponsable"
|
||||
label={t('phone')}
|
||||
value={item.phone}
|
||||
onChange={(event) => {
|
||||
onGuardiansChange(item.id, 'phone', event);
|
||||
}}
|
||||
required
|
||||
errorMsg={getError(index, 'phone')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='grid grid-cols-1 md:grid-cols-2 gap-4 mb-4'>
|
||||
<InputText
|
||||
name="mailResponsable"
|
||||
type="email"
|
||||
label={t('email')}
|
||||
value={item.associated_profile_email}
|
||||
onChange={(event) => {onGuardiansChange(item.id, "associated_profile_email", event.target.value)}}
|
||||
required
|
||||
errorMsg={getError(index, 'email')}
|
||||
/>
|
||||
<InputPhone
|
||||
name="telephoneResponsable"
|
||||
label={t('phone')}
|
||||
value={item.phone}
|
||||
onChange={(event) => {onGuardiansChange(item.id, "phone", event)}}
|
||||
required
|
||||
errorMsg={getError(index, 'phone')}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
||||
<InputText
|
||||
name="dateNaissanceResponsable"
|
||||
type="date"
|
||||
label={t('birthdate')}
|
||||
value={item.birth_date}
|
||||
onChange={(event) => {
|
||||
onGuardiansChange(item.id, 'birth_date', event.target.value);
|
||||
}}
|
||||
required
|
||||
errorMsg={getError(index, 'birth_date')}
|
||||
/>
|
||||
<InputText
|
||||
name="professionResponsable"
|
||||
type="text"
|
||||
label={t('profession')}
|
||||
value={item.profession}
|
||||
onChange={(event) => {
|
||||
onGuardiansChange(item.id, 'profession', event.target.value);
|
||||
}}
|
||||
required
|
||||
errorMsg={getError(index, 'profession')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='grid grid-cols-1 md:grid-cols-2 gap-4 mb-4'>
|
||||
<InputText
|
||||
name="dateNaissanceResponsable"
|
||||
type="date"
|
||||
label={t('birthdate')}
|
||||
value={item.birth_date}
|
||||
onChange={(event) => {onGuardiansChange(item.id, "birth_date", event.target.value)}}
|
||||
required
|
||||
errorMsg={getError(index, 'birth_date')}
|
||||
/>
|
||||
<InputText
|
||||
name="professionResponsable"
|
||||
type="text"
|
||||
label={t('profession')}
|
||||
value={item.profession}
|
||||
onChange={(event) => {onGuardiansChange(item.id, "profession", event.target.value)}}
|
||||
required
|
||||
errorMsg={getError(index, 'profession')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='grid grid-cols-1 gap-4'>
|
||||
<InputText
|
||||
name="adresseResponsable"
|
||||
type="text"
|
||||
label={t('address')}
|
||||
value={item.address}
|
||||
onChange={(event) => {onGuardiansChange(item.id, "address", event.target.value)}}
|
||||
required
|
||||
errorMsg={getError(index, 'address')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="flex justify-center">
|
||||
<Plus
|
||||
className="w-8 h-8 text-green-500 cursor-pointer hover:text-green-700 transition-colors border-2 border-green-500 hover:border-green-700 rounded-full p-1"
|
||||
onClick={(e) => addGuardian(e)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
<InputText
|
||||
name="adresseResponsable"
|
||||
type="text"
|
||||
label={t('address')}
|
||||
value={item.address}
|
||||
onChange={(event) => {
|
||||
onGuardiansChange(item.id, 'address', event.target.value);
|
||||
}}
|
||||
required
|
||||
errorMsg={getError(index, 'address')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
))}
|
||||
|
||||
<div className="flex justify-center">
|
||||
<Plus
|
||||
className="w-8 h-8 text-green-500 cursor-pointer hover:text-green-700 transition-colors border-2 border-green-500 hover:border-green-700 rounded-full p-1"
|
||||
onClick={(e) => addGuardian(e)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,176 +5,190 @@ import ResponsableInputFields from '@/components/Inscription/ResponsableInputFie
|
||||
import PaymentMethodSelector from '@/components/Inscription/PaymentMethodSelector';
|
||||
|
||||
const levels = [
|
||||
{ value:'1', label: 'TPS - Très Petite Section'},
|
||||
{ value:'2', label: 'PS - Petite Section'},
|
||||
{ value:'3', label: 'MS - Moyenne Section'},
|
||||
{ value:'4', label: 'GS - Grande Section'},
|
||||
{ value: '1', label: 'TPS - Très Petite Section' },
|
||||
{ value: '2', label: 'PS - Petite Section' },
|
||||
{ value: '3', label: 'MS - Moyenne Section' },
|
||||
{ value: '4', label: 'GS - Grande Section' },
|
||||
];
|
||||
|
||||
const paymentModesOptions = [
|
||||
{ id: 1, name: 'Prélèvement SEPA' },
|
||||
{ id: 2, name: 'Virement' },
|
||||
{ id: 3, name: 'Chèque' },
|
||||
{ id: 4, name: 'Espèce' },
|
||||
];
|
||||
{ id: 1, name: 'Prélèvement SEPA' },
|
||||
{ id: 2, name: 'Virement' },
|
||||
{ id: 3, name: 'Chèque' },
|
||||
{ id: 4, name: 'Espèce' },
|
||||
];
|
||||
|
||||
// Fonction de validation pour vérifier les champs requis
|
||||
export function validateStudentInfo(formData) {
|
||||
const requiredFields = [
|
||||
'last_name',
|
||||
'first_name',
|
||||
'nationality',
|
||||
'birth_date',
|
||||
'birth_place',
|
||||
'birth_postal_code',
|
||||
'address',
|
||||
'attending_physician',
|
||||
'level',
|
||||
];
|
||||
const requiredFields = [
|
||||
'last_name',
|
||||
'first_name',
|
||||
'nationality',
|
||||
'birth_date',
|
||||
'birth_place',
|
||||
'birth_postal_code',
|
||||
'address',
|
||||
'attending_physician',
|
||||
'level',
|
||||
];
|
||||
|
||||
const isValid = requiredFields.every((field) => {
|
||||
const value = formData[field];
|
||||
return typeof value === 'string' ? value.trim() !== '' : Boolean(value);
|
||||
});
|
||||
const isValid = requiredFields.every((field) => {
|
||||
const value = formData[field];
|
||||
return typeof value === 'string' ? value.trim() !== '' : Boolean(value);
|
||||
});
|
||||
|
||||
return isValid;
|
||||
return isValid;
|
||||
}
|
||||
|
||||
export default function StudentInfoForm({ formData, updateFormField, guardians, setGuardians, registrationPaymentModes, tuitionPaymentModes, errors }) {
|
||||
const getError = (field) => {
|
||||
return errors?.student?.[field]?.[0];
|
||||
};
|
||||
export default function StudentInfoForm({
|
||||
formData,
|
||||
updateFormField,
|
||||
guardians,
|
||||
setGuardians,
|
||||
registrationPaymentModes,
|
||||
tuitionPaymentModes,
|
||||
errors,
|
||||
}) {
|
||||
const getError = (field) => {
|
||||
return errors?.student?.[field]?.[0];
|
||||
};
|
||||
|
||||
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">Informations de l'élève</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<InputText
|
||||
name="last_name"
|
||||
label="Nom"
|
||||
value={formData.last_name}
|
||||
onChange={(e) => updateFormField('last_name', e.target.value)}
|
||||
required
|
||||
errorMsg={getError('last_name')}
|
||||
/>
|
||||
<InputText
|
||||
name="first_name"
|
||||
label="Prénom"
|
||||
value={formData.first_name}
|
||||
onChange={(e) => updateFormField('first_name', e.target.value)}
|
||||
errorMsg={getError('first_name')}
|
||||
required
|
||||
/>
|
||||
<InputText
|
||||
name="nationality"
|
||||
label="Nationalité"
|
||||
value={formData.nationality}
|
||||
required
|
||||
onChange={(e) => updateFormField('nationality', e.target.value)}
|
||||
/>
|
||||
<InputText
|
||||
name="birth_date"
|
||||
type="date"
|
||||
label="Date de Naissance"
|
||||
value={formData.birth_date}
|
||||
onChange={(e) => updateFormField('birth_date', e.target.value)}
|
||||
required
|
||||
errorMsg={getError('birth_date')}
|
||||
/>
|
||||
<InputText
|
||||
name="birth_place"
|
||||
label="Lieu de Naissance"
|
||||
value={formData.birth_place}
|
||||
onChange={(e) => updateFormField('birth_place', e.target.value)}
|
||||
required
|
||||
errorMsg={getError('birth_place')}
|
||||
/>
|
||||
<InputText
|
||||
name="birth_postal_code"
|
||||
label="Code Postal de Naissance"
|
||||
value={formData.birth_postal_code}
|
||||
onChange={(e) => updateFormField('birth_postal_code', e.target.value)}
|
||||
required
|
||||
errorMsg={getError('birth_postal_code')}
|
||||
/>
|
||||
<div className="md:col-span-2">
|
||||
<InputText
|
||||
name="address"
|
||||
label="Adresse"
|
||||
value={formData.address}
|
||||
onChange={(e) => updateFormField('address', e.target.value)}
|
||||
required
|
||||
errorMsg={getError('address')}
|
||||
/>
|
||||
</div>
|
||||
<InputText
|
||||
name="attending_physician"
|
||||
label="Médecin Traitant"
|
||||
value={formData.attending_physician}
|
||||
onChange={(e) => updateFormField('attending_physician', e.target.value)}
|
||||
required
|
||||
errorMsg={getError('attending_physician')}
|
||||
/>
|
||||
<SelectChoice
|
||||
name="level"
|
||||
label="Niveau"
|
||||
placeHolder="Sélectionner un niveau"
|
||||
selected={formData.level}
|
||||
callback={(e) => updateFormField('level', e.target.value)}
|
||||
choices={levels}
|
||||
required
|
||||
errorMsg={getError('level')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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">Responsables</h2>
|
||||
<ResponsableInputFields
|
||||
guardians={guardians}
|
||||
onGuardiansChange={(id, field, value) => {
|
||||
const updatedGuardians = guardians.map(resp =>
|
||||
resp.id === id ? { ...resp, [field]: value } : resp
|
||||
);
|
||||
setGuardians(updatedGuardians);
|
||||
}}
|
||||
addGuardian={(e) => {
|
||||
e.preventDefault();
|
||||
setGuardians([...guardians, { id: Date.now() }]);
|
||||
}}
|
||||
deleteGuardian={(index) => {
|
||||
const newArray = [...guardians];
|
||||
newArray.splice(index, 1);
|
||||
setGuardians(newArray);
|
||||
}}
|
||||
errors={errors?.student?.guardians || []}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<PaymentMethodSelector
|
||||
formData={formData}
|
||||
title="Frais d'inscription"
|
||||
name="registration_payment"
|
||||
updateFormField={updateFormField}
|
||||
selected={formData.registration_payment}
|
||||
paymentModes={registrationPaymentModes}
|
||||
paymentModesOptions={paymentModesOptions}
|
||||
amount={formData.totalRegistrationFees}
|
||||
getError={getError}
|
||||
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">
|
||||
Informations de l'élève
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<InputText
|
||||
name="last_name"
|
||||
label="Nom"
|
||||
value={formData.last_name}
|
||||
onChange={(e) => updateFormField('last_name', e.target.value)}
|
||||
required
|
||||
errorMsg={getError('last_name')}
|
||||
/>
|
||||
<InputText
|
||||
name="first_name"
|
||||
label="Prénom"
|
||||
value={formData.first_name}
|
||||
onChange={(e) => updateFormField('first_name', e.target.value)}
|
||||
errorMsg={getError('first_name')}
|
||||
required
|
||||
/>
|
||||
<InputText
|
||||
name="nationality"
|
||||
label="Nationalité"
|
||||
value={formData.nationality}
|
||||
required
|
||||
onChange={(e) => updateFormField('nationality', e.target.value)}
|
||||
/>
|
||||
<InputText
|
||||
name="birth_date"
|
||||
type="date"
|
||||
label="Date de Naissance"
|
||||
value={formData.birth_date}
|
||||
onChange={(e) => updateFormField('birth_date', e.target.value)}
|
||||
required
|
||||
errorMsg={getError('birth_date')}
|
||||
/>
|
||||
<InputText
|
||||
name="birth_place"
|
||||
label="Lieu de Naissance"
|
||||
value={formData.birth_place}
|
||||
onChange={(e) => updateFormField('birth_place', e.target.value)}
|
||||
required
|
||||
errorMsg={getError('birth_place')}
|
||||
/>
|
||||
<InputText
|
||||
name="birth_postal_code"
|
||||
label="Code Postal de Naissance"
|
||||
value={formData.birth_postal_code}
|
||||
onChange={(e) =>
|
||||
updateFormField('birth_postal_code', e.target.value)
|
||||
}
|
||||
required
|
||||
errorMsg={getError('birth_postal_code')}
|
||||
/>
|
||||
<div className="md:col-span-2">
|
||||
<InputText
|
||||
name="address"
|
||||
label="Adresse"
|
||||
value={formData.address}
|
||||
onChange={(e) => updateFormField('address', e.target.value)}
|
||||
required
|
||||
errorMsg={getError('address')}
|
||||
/>
|
||||
</div>
|
||||
<InputText
|
||||
name="attending_physician"
|
||||
label="Médecin Traitant"
|
||||
value={formData.attending_physician}
|
||||
onChange={(e) =>
|
||||
updateFormField('attending_physician', e.target.value)
|
||||
}
|
||||
required
|
||||
errorMsg={getError('attending_physician')}
|
||||
/>
|
||||
<SelectChoice
|
||||
name="level"
|
||||
label="Niveau"
|
||||
placeHolder="Sélectionner un niveau"
|
||||
selected={formData.level}
|
||||
callback={(e) => updateFormField('level', e.target.value)}
|
||||
choices={levels}
|
||||
required
|
||||
errorMsg={getError('level')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PaymentMethodSelector
|
||||
formData={formData}
|
||||
title="Frais de scolarité"
|
||||
name="tuition_payment"
|
||||
updateFormField={updateFormField}
|
||||
selected={formData.tuition_payment}
|
||||
paymentModes={tuitionPaymentModes}
|
||||
paymentModesOptions={paymentModesOptions}
|
||||
amount={formData.totalTuitionFees}
|
||||
getError={getError}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
<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">Responsables</h2>
|
||||
<ResponsableInputFields
|
||||
guardians={guardians}
|
||||
onGuardiansChange={(id, field, value) => {
|
||||
const updatedGuardians = guardians.map((resp) =>
|
||||
resp.id === id ? { ...resp, [field]: value } : resp
|
||||
);
|
||||
setGuardians(updatedGuardians);
|
||||
}}
|
||||
addGuardian={(e) => {
|
||||
e.preventDefault();
|
||||
setGuardians([...guardians, { id: Date.now() }]);
|
||||
}}
|
||||
deleteGuardian={(index) => {
|
||||
const newArray = [...guardians];
|
||||
newArray.splice(index, 1);
|
||||
setGuardians(newArray);
|
||||
}}
|
||||
errors={errors?.student?.guardians || []}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<PaymentMethodSelector
|
||||
formData={formData}
|
||||
title="Frais d'inscription"
|
||||
name="registration_payment"
|
||||
updateFormField={updateFormField}
|
||||
selected={formData.registration_payment}
|
||||
paymentModes={registrationPaymentModes}
|
||||
paymentModesOptions={paymentModesOptions}
|
||||
amount={formData.totalRegistrationFees}
|
||||
getError={getError}
|
||||
/>
|
||||
|
||||
<PaymentMethodSelector
|
||||
formData={formData}
|
||||
title="Frais de scolarité"
|
||||
name="tuition_payment"
|
||||
updateFormField={updateFormField}
|
||||
selected={formData.tuition_payment}
|
||||
paymentModes={tuitionPaymentModes}
|
||||
paymentModesOptions={paymentModesOptions}
|
||||
amount={formData.totalTuitionFees}
|
||||
getError={getError}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
'use client'
|
||||
'use client';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { DocusealBuilder } from '@docuseal/react';
|
||||
import Button from '@/components/Button';
|
||||
@ -7,7 +7,14 @@ import { generateToken } from '@/app/actions/registerFileGroupAction';
|
||||
import logger from '@/utils/logger';
|
||||
import { GraduationCap, CloudUpload } from 'lucide-react';
|
||||
|
||||
export default function ValidateSubscription({ studentId, firstName, lastName, paymentMode, file, onAccept }) {
|
||||
export default function ValidateSubscription({
|
||||
studentId,
|
||||
firstName,
|
||||
lastName,
|
||||
paymentMode,
|
||||
file,
|
||||
onAccept,
|
||||
}) {
|
||||
const [token, setToken] = useState(null);
|
||||
const [uploadedFileName, setUploadedFileName] = useState('');
|
||||
const [pdfUrl, setPdfUrl] = useState(`${BASE_URL}/${file}`);
|
||||
@ -20,7 +27,9 @@ export default function ValidateSubscription({ studentId, firstName, lastName, p
|
||||
.then((data) => {
|
||||
setToken(data.token);
|
||||
})
|
||||
.catch((error) => logger.error('Erreur lors de la génération du token:', error));
|
||||
.catch((error) =>
|
||||
logger.error('Erreur lors de la génération du token:', error)
|
||||
);
|
||||
}
|
||||
}, [isSepa]);
|
||||
|
||||
@ -32,23 +41,23 @@ export default function ValidateSubscription({ studentId, firstName, lastName, p
|
||||
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) {
|
||||
logger.error('Aucun fichier sélectionné pour le champ SEPA.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const data = {
|
||||
status: 7,
|
||||
status: 7,
|
||||
sepa_file: file,
|
||||
};
|
||||
|
||||
|
||||
// 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);
|
||||
logger.debug("Dossier refusé pour l'étudiant:", studentId);
|
||||
// Logique pour refuser l'inscription
|
||||
};
|
||||
|
||||
@ -75,10 +84,14 @@ export default function ValidateSubscription({ studentId, firstName, lastName, p
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-800">
|
||||
Dossier scolaire de <span className="text-emerald-600">{firstName} {lastName}</span>
|
||||
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}
|
||||
Année scolaire {new Date().getFullYear()}-
|
||||
{new Date().getFullYear() + 1}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -101,45 +114,54 @@ export default function ValidateSubscription({ studentId, firstName, lastName, p
|
||||
|
||||
{currentPage === 2 && isSepa && (
|
||||
<div className="border p-4 rounded-md shadow-md">
|
||||
<h3 className="text-lg font-semibold mb-4">Sélection du mandat de pélèvement SEPA</h3>
|
||||
<h3 className="text-lg font-semibold mb-4">
|
||||
Sélection du mandat de pélèvement SEPA
|
||||
</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={(e) => {
|
||||
e.preventDefault();
|
||||
const file = e.dataTransfer.files[0];
|
||||
if (file) {
|
||||
setUploadedFileName(file.name); // Stocke uniquement le nom du fichier
|
||||
logger.debug('Fichier déposé:', file.name);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<CloudUpload className="w-12 h-12 text-emerald-500 mb-4" /> {/* Icône de cloud */}
|
||||
<input
|
||||
type="file"
|
||||
accept=".pdf"
|
||||
onChange={(e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
setUploadedFileName(file.name); // Stocke uniquement le nom du fichier
|
||||
logger.debug('Fichier sélectionné:', file.name);
|
||||
}
|
||||
}}
|
||||
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>
|
||||
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={(e) => {
|
||||
e.preventDefault();
|
||||
const file = e.dataTransfer.files[0];
|
||||
if (file) {
|
||||
setUploadedFileName(file.name); // Stocke uniquement le nom du fichier
|
||||
logger.debug('Fichier déposé:', file.name);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<CloudUpload className="w-12 h-12 text-emerald-500 mb-4" />{' '}
|
||||
{/* Icône de cloud */}
|
||||
<input
|
||||
type="file"
|
||||
accept=".pdf"
|
||||
onChange={(e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
setUploadedFileName(file.name); // Stocke uniquement le nom du fichier
|
||||
logger.debug('Fichier sélectionné:', file.name);
|
||||
}
|
||||
}}
|
||||
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>
|
||||
{uploadedFileName && (
|
||||
<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">{uploadedFileName}</span></p>
|
||||
</div>
|
||||
)}
|
||||
<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">{uploadedFileName}</span>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -172,4 +194,4 @@ export default function ValidateSubscription({ studentId, firstName, lastName, p
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user