mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-28 23:43:22 +00:00
feat: ajout des documents d'inscription [#20]
This commit is contained in:
@ -182,6 +182,10 @@ class Student(models.Model):
|
|||||||
return self.birth_date.strftime('%d-%m-%Y')
|
return self.birth_date.strftime('%d-%m-%Y')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def registration_file_path(instance, filename):
|
||||||
|
# Génère le chemin : registration_files/dossier_rf_{student_id}/filename
|
||||||
|
return f'registration_files/dossier_rf_{instance.student_id}/{filename}'
|
||||||
|
|
||||||
class RegistrationForm(models.Model):
|
class RegistrationForm(models.Model):
|
||||||
"""
|
"""
|
||||||
Gère le dossier d’inscription lié à un élève donné.
|
Gère le dossier d’inscription lié à un élève donné.
|
||||||
@ -201,7 +205,11 @@ class RegistrationForm(models.Model):
|
|||||||
last_update = models.DateTimeField(auto_now=True)
|
last_update = models.DateTimeField(auto_now=True)
|
||||||
notes = models.CharField(max_length=200, blank=True)
|
notes = models.CharField(max_length=200, blank=True)
|
||||||
registration_link_code = models.CharField(max_length=200, default="", blank=True)
|
registration_link_code = models.CharField(max_length=200, default="", blank=True)
|
||||||
registration_file = models.FileField(upload_to=settings.DOCUMENT_DIR, default="", blank=True)
|
registration_file = models.FileField(
|
||||||
|
upload_to=registration_file_path,
|
||||||
|
null=True,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
associated_rf = models.CharField(max_length=200, default="", blank=True)
|
associated_rf = models.CharField(max_length=200, default="", blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|||||||
@ -93,31 +93,57 @@ def getArgFromRequest(_argument, _request):
|
|||||||
|
|
||||||
def merge_files_pdf(filenames, output_filename):
|
def merge_files_pdf(filenames, output_filename):
|
||||||
"""
|
"""
|
||||||
Insère plusieurs fichiers PDF dans un seul document de sortie.
|
Fusionne plusieurs fichiers PDF en un seul document.
|
||||||
|
Vérifie l'existence des fichiers sources avant la fusion.
|
||||||
"""
|
"""
|
||||||
merger = pymupdf.open()
|
merger = pymupdf.open()
|
||||||
|
valid_files = []
|
||||||
|
|
||||||
|
# Vérifier l'existence des fichiers et ne garder que ceux qui existent
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
|
if os.path.exists(filename):
|
||||||
|
valid_files.append(filename)
|
||||||
|
|
||||||
|
# Fusionner les fichiers valides
|
||||||
|
for filename in valid_files:
|
||||||
merger.insert_file(filename)
|
merger.insert_file(filename)
|
||||||
|
|
||||||
|
# S'assurer que le dossier de destination existe
|
||||||
|
os.makedirs(os.path.dirname(output_filename), exist_ok=True)
|
||||||
|
|
||||||
|
# Sauvegarder le fichier fusionné
|
||||||
merger.save(output_filename)
|
merger.save(output_filename)
|
||||||
merger.close()
|
merger.close()
|
||||||
|
|
||||||
def rfToPDF(registerForm,filename):
|
return output_filename
|
||||||
|
|
||||||
|
def rfToPDF(registerForm, filename):
|
||||||
"""
|
"""
|
||||||
Génère le PDF d’un dossier d’inscription et l’associe au RegistrationForm.
|
Génère le PDF d'un dossier d'inscription et l'associe au RegistrationForm.
|
||||||
"""
|
"""
|
||||||
# Ajout du fichier d'inscriptions
|
|
||||||
data = {
|
data = {
|
||||||
'pdf_title': "Dossier d'inscription de %s"%registerForm.student.first_name,
|
'pdf_title': f"Dossier d'inscription de {registerForm.student.first_name}",
|
||||||
'signatureDate': convertToStr(_now(), '%d-%m-%Y'),
|
'signatureDate': convertToStr(_now(), '%d-%m-%Y'),
|
||||||
'signatureTime': convertToStr(_now(), '%H:%M'),
|
'signatureTime': convertToStr(_now(), '%H:%M'),
|
||||||
'student':registerForm.student,
|
'student': registerForm.student,
|
||||||
}
|
}
|
||||||
PDFFileName = filename
|
|
||||||
|
# S'assurer que le dossier parent existe
|
||||||
|
os.makedirs(os.path.dirname(filename), exist_ok=True)
|
||||||
|
|
||||||
|
# Générer le PDF
|
||||||
pdf = renderers.render_to_pdf('pdfs/dossier_inscription.html', data)
|
pdf = renderers.render_to_pdf('pdfs/dossier_inscription.html', data)
|
||||||
pathFichier = Path(filename)
|
|
||||||
if os.path.exists(str(pathFichier)):
|
# Écrire le fichier directement
|
||||||
print(f'File exists : {str(pathFichier)}')
|
with open(filename, 'wb') as f:
|
||||||
os.remove(str(pathFichier))
|
f.write(pdf.content)
|
||||||
receipt_file = BytesIO(pdf.content)
|
|
||||||
registerForm.fichierInscription = File(receipt_file, PDFFileName)
|
# Mettre à jour le champ registration_file du registerForm
|
||||||
registerForm.fichierInscription.save()
|
with open(filename, 'rb') as f:
|
||||||
|
registerForm.registration_file.save(
|
||||||
|
os.path.basename(filename),
|
||||||
|
File(f),
|
||||||
|
save=True
|
||||||
|
)
|
||||||
|
|
||||||
|
return registerForm.registration_file
|
||||||
@ -179,20 +179,37 @@ class RegisterFormView(APIView):
|
|||||||
registerForm = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=_id)
|
registerForm = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=_id)
|
||||||
|
|
||||||
if _status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW:
|
if _status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW:
|
||||||
# Le parent a complété le dossier d'inscription, il est soumis à validation par l'école
|
try:
|
||||||
json.dumps(studentForm_data)
|
# Génération de la fiche d'inscription au format PDF
|
||||||
#Génération de la fiche d'inscription au format PDF
|
base_dir = f"registration_files/dossier_rf_{registerForm.pk}"
|
||||||
PDFFileName = "rf_%s_%s.pdf"%(registerForm.student.last_name, registerForm.student.first_name)
|
os.makedirs(base_dir, exist_ok=True)
|
||||||
path = Path(f"registration_files/dossier_rf_{registerForm.pk}/{PDFFileName}")
|
|
||||||
registerForm.fichierInscription = util.rfToPDF(registerForm, path)
|
# Fichier PDF initial
|
||||||
# Récupération des fichiers d'inscription
|
initial_pdf = f"{base_dir}/rf_{registerForm.student.last_name}_{registerForm.student.first_name}.pdf"
|
||||||
fileNames = RegistrationFile.get_files_from_rf(registerForm.pk)
|
registerForm.registration_file = util.rfToPDF(registerForm, initial_pdf)
|
||||||
fileNames.insert(0,path)
|
registerForm.save()
|
||||||
# Création du fichier PDF Fusionné avec le dossier complet
|
|
||||||
output_path = f"registration_files/dossier_rf_{registerForm.pk}/dossier_{registerForm.pk}.pdf"
|
# Récupération des fichiers d'inscription
|
||||||
util.merge_files_pdf(fileNames, output_path)
|
fileNames = RegistrationFile.get_files_from_rf(registerForm.pk)
|
||||||
# Mise à jour de l'automate
|
if registerForm.registration_file:
|
||||||
updateStateMachine(registerForm, 'saisiDI')
|
fileNames.insert(0, registerForm.registration_file.path)
|
||||||
|
|
||||||
|
# Création du fichier PDF Fusionné
|
||||||
|
merged_pdf = f"{base_dir}/dossier_complet_{registerForm.pk}.pdf"
|
||||||
|
util.merge_files_pdf(fileNames, merged_pdf)
|
||||||
|
|
||||||
|
# Mise à jour du champ registration_file avec le fichier fusionné
|
||||||
|
with open(merged_pdf, 'rb') as f:
|
||||||
|
registerForm.registration_file.save(
|
||||||
|
os.path.basename(merged_pdf),
|
||||||
|
File(f),
|
||||||
|
save=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Mise à jour de l'automate
|
||||||
|
updateStateMachine(registerForm, 'saisiDI')
|
||||||
|
except Exception as e:
|
||||||
|
return JsonResponse({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
elif _status == RegistrationForm.RegistrationFormStatus.RF_VALIDATED:
|
elif _status == RegistrationForm.RegistrationFormStatus.RF_VALIDATED:
|
||||||
# L'école a validé le dossier d'inscription
|
# L'école a validé le dossier d'inscription
|
||||||
# Mise à jour de l'automate
|
# Mise à jour de l'automate
|
||||||
|
|||||||
@ -16,29 +16,10 @@ export default function Page() {
|
|||||||
const studentId = searchParams.get('studentId'); // Changé de codeDI à studentId
|
const studentId = searchParams.get('studentId'); // Changé de codeDI à studentId
|
||||||
|
|
||||||
const [initialData, setInitialData] = useState(null);
|
const [initialData, setInitialData] = useState(null);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
|
||||||
const [formErrors, setFormErrors] = useState({});
|
const [formErrors, setFormErrors] = useState({});
|
||||||
const csrfToken = useCsrfToken();
|
const csrfToken = useCsrfToken();
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (useFakeData) {
|
|
||||||
setInitialData(mockStudent);
|
|
||||||
} else {
|
|
||||||
fetchRegisterForm(studentId)
|
|
||||||
.then(data => {
|
|
||||||
console.log('Fetched data:', data); // Pour le débogage
|
|
||||||
const formattedData = {
|
|
||||||
...data,
|
|
||||||
guardians: data.guardians || []
|
|
||||||
};
|
|
||||||
setInitialData(formattedData);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error fetching student data:', error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setIsLoading(false);
|
|
||||||
}, [studentId]); // Dépendance changée à studentId
|
|
||||||
|
|
||||||
const handleSubmit = (data) => {
|
const handleSubmit = (data) => {
|
||||||
if (useFakeData) {
|
if (useFakeData) {
|
||||||
@ -64,11 +45,10 @@ export default function Page() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<InscriptionFormShared
|
<InscriptionFormShared
|
||||||
initialData={initialData}
|
studentId={studentId}
|
||||||
csrfToken={csrfToken}
|
csrfToken={csrfToken}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
cancelUrl={FE_ADMIN_SUBSCRIPTIONS_URL}
|
cancelUrl={FE_ADMIN_SUBSCRIPTIONS_URL}
|
||||||
isLoading={isLoading}
|
|
||||||
errors={formErrors}
|
errors={formErrors}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -320,99 +320,79 @@ useEffect(()=>{
|
|||||||
};
|
};
|
||||||
|
|
||||||
const createRF = (updatedData) => {
|
const createRF = (updatedData) => {
|
||||||
console.log('createRF updatedData:', updatedData);
|
|
||||||
|
|
||||||
if (updatedData.selectedGuardians.length !== 0) {
|
if (updatedData.selectedGuardians.length !== 0) {
|
||||||
const selectedGuardiansIds = updatedData.selectedGuardians.map(guardianId => guardianId)
|
const selectedGuardiansIds = updatedData.selectedGuardians.map(guardianId => guardianId)
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
student: {
|
student: {
|
||||||
last_name: updatedData.studentLastName,
|
last_name: updatedData.studentLastName,
|
||||||
first_name: updatedData.studentFirstName,
|
first_name: updatedData.studentFirstName,
|
||||||
},
|
},
|
||||||
idGuardians: selectedGuardiansIds
|
idGuardians: selectedGuardiansIds
|
||||||
};
|
};
|
||||||
|
|
||||||
createRegisterForm(data,csrfToken)
|
createRegisterForm(data, csrfToken)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
console.log('Success:', data);
|
// Mise à jour immédiate des données
|
||||||
setRegistrationFormsDataPending(prevState => {
|
setRegistrationFormsDataPending(prevState => [...(prevState || []), data]);
|
||||||
if (prevState) {
|
setTotalPending(prev => prev + 1);
|
||||||
return [...prevState, data];
|
if (updatedData.autoMail) {
|
||||||
|
sendConfirmRegisterForm(data.student.id, updatedData.studentLastName, updatedData.studentFirstName);
|
||||||
}
|
}
|
||||||
return [data];
|
closeModal();
|
||||||
});
|
// Forcer le rechargement complet des données
|
||||||
setTotalPending(totalPending+1);
|
setReloadFetch(true);
|
||||||
if (updatedData.autoMail) {
|
})
|
||||||
sendConfirmRegisterForm(data.student.id, updatedData.studentLastName, updatedData.studentFirstName);
|
.catch((error) => {
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
});
|
});
|
||||||
}
|
} else {
|
||||||
else {
|
const data = {
|
||||||
// Création d'un profil associé à l'adresse mail du responsable saisie
|
email: updatedData.guardianEmail,
|
||||||
// Le profil est inactif
|
password: 'Provisoire01!',
|
||||||
const data = {
|
username: updatedData.guardianEmail,
|
||||||
email: updatedData.guardianEmail,
|
is_active: 0,
|
||||||
password: 'Provisoire01!',
|
droit: 2
|
||||||
username: updatedData.guardianEmail,
|
}
|
||||||
is_active: 0, // On rend le profil inactif : impossible de s'y connecter dans la fenêtre du login tant qu'il ne s'est pas inscrit
|
|
||||||
droit:2 // Profil PARENT
|
createProfile(data, csrfToken)
|
||||||
}
|
|
||||||
createProfile(data,csrfToken)
|
|
||||||
.then(response => {
|
.then(response => {
|
||||||
console.log('Success:', response);
|
if (response.id) {
|
||||||
if (response.id) {
|
const data = {
|
||||||
let idProfile = response.id;
|
student: {
|
||||||
|
last_name: updatedData.studentLastName,
|
||||||
|
first_name: updatedData.studentFirstName,
|
||||||
|
guardians: [{
|
||||||
|
email: updatedData.guardianEmail,
|
||||||
|
phone: updatedData.guardianPhone,
|
||||||
|
associated_profile: response.id
|
||||||
|
}],
|
||||||
|
sibling: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const data = {
|
createRegisterForm(data, csrfToken)
|
||||||
student: {
|
.then(data => {
|
||||||
last_name: updatedData.studentLastName,
|
// Mise à jour immédiate des données
|
||||||
first_name: updatedData.studentFirstName,
|
setRegistrationFormsDataPending(prevState => [...(prevState || []), data]);
|
||||||
guardians: [
|
setTotalPending(prev => prev + 1);
|
||||||
{
|
if (updatedData.autoMail) {
|
||||||
email: updatedData.guardianEmail,
|
sendConfirmRegisterForm(data.student.id, updatedData.studentLastName, updatedData.studentFirstName);
|
||||||
phone: updatedData.guardianPhone,
|
}
|
||||||
associated_profile: idProfile // Association entre le responsable de l'élève et le profil créé par défaut précédemment
|
closeModal();
|
||||||
}
|
// Forcer le rechargement complet des données
|
||||||
],
|
setReloadFetch(true);
|
||||||
sibling: []
|
})
|
||||||
}
|
.catch((error) => {
|
||||||
};
|
console.error('Error:', error);
|
||||||
|
});
|
||||||
createRegisterForm(data,csrfToken)
|
}
|
||||||
.then(data => {
|
|
||||||
console.log('Success:', data);
|
|
||||||
setRegistrationFormsDataPending(prevState => {
|
|
||||||
if (prevState && prevState.length > 0) {
|
|
||||||
return [...prevState, data];
|
|
||||||
}
|
|
||||||
return prevState;
|
|
||||||
});
|
|
||||||
setTotalPending(totalPending+1);
|
|
||||||
if (updatedData.autoMail) {
|
|
||||||
sendConfirmRegisterForm(data.student.id, updatedData.studentLastName, updatedData.studentFirstName);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error('Error:', error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Error fetching data:', error);
|
console.error('Error:', error);
|
||||||
error = error.errorMessage;
|
|
||||||
console.log(error);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
closeModal();
|
|
||||||
setReloadFetch(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ name: t('studentName'), transform: (row) => row.student.last_name },
|
{ name: t('studentName'), transform: (row) => row.student.last_name },
|
||||||
{ name: t('studentFistName'), transform: (row) => row.student.first_name },
|
{ name: t('studentFistName'), transform: (row) => row.student.first_name },
|
||||||
@ -426,11 +406,11 @@ const columns = [
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
{ name: t('files'), transform: (row) =>
|
{ name: t('files'), transform: (row) =>
|
||||||
(row.registerForms != null) &&(
|
(row.registration_file != null) &&(
|
||||||
<ul>
|
<ul>
|
||||||
<li className="flex items-center gap-2">
|
<li className="flex items-center gap-2">
|
||||||
<FileText size={16} />
|
<FileText size={16} />
|
||||||
<a href={ `${BASE_URL}${row.registerForms}`} target='_blank'>{row.registerForms?.split('/').pop()}</a>
|
<a href={ `${BASE_URL}${row.registration_file}`} target='_blank'>{row.registration_file?.split('/').pop()}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
) },
|
) },
|
||||||
@ -507,11 +487,11 @@ const columnsSubscribed = [
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
{ name: t('files'), transform: (row) =>
|
{ name: t('files'), transform: (row) =>
|
||||||
(row.registerForm != null) &&(
|
(row.registration_file != null) &&(
|
||||||
<ul>
|
<ul>
|
||||||
<li className="flex items-center gap-2">
|
<li className="flex items-center gap-2">
|
||||||
<FileText size={16} />
|
<FileText size={16} />
|
||||||
<a href={ `${BASE_URL}${row.registerForm}`} target='_blank'>{row.registerForm?.split('/').pop()}</a>
|
<a href={ `${BASE_URL}${row.registration_file}`} target='_blank'>{row.registration_file?.split('/').pop()}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
) },
|
) },
|
||||||
@ -677,7 +657,7 @@ const handleFileUpload = ({file, name, is_required, order}) => {
|
|||||||
text={(
|
text={(
|
||||||
<>
|
<>
|
||||||
{t('subscribeFiles')}
|
{t('subscribeFiles')}
|
||||||
<span className="ml-2 text-sm text-gray-400">({totalSubscribed})</span>
|
<span className="ml-2 text-sm text-gray-400">({fichiers.length})</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
active={activeTab === 'subscribeFiles'}
|
active={activeTab === 'subscribeFiles'}
|
||||||
@ -735,12 +715,14 @@ const handleFileUpload = ({file, name, is_required, order}) => {
|
|||||||
{/*SI STATE == subscribeFiles */}
|
{/*SI STATE == subscribeFiles */}
|
||||||
{activeTab === 'subscribeFiles' && (
|
{activeTab === 'subscribeFiles' && (
|
||||||
<div>
|
<div>
|
||||||
<button
|
<div className="flex justify-end mb-4">
|
||||||
onClick={() => { setIsModalOpen(true); setIsEditing(false); }}
|
<button
|
||||||
className="flex items-center bg-emerald-600 text-white p-2 rounded-full shadow hover:bg-emerald-900 transition duration-200 ml-4"
|
onClick={() => { setIsModalOpen(true); setIsEditing(false); }}
|
||||||
>
|
className="flex items-center bg-emerald-600 text-white p-2 rounded-full shadow hover:bg-emerald-900 transition duration-200"
|
||||||
<Plus className="w-5 h-5" />
|
>
|
||||||
</button>
|
<Plus className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isModalOpen}
|
isOpen={isModalOpen}
|
||||||
setIsOpen={setIsModalOpen}
|
setIsOpen={setIsModalOpen}
|
||||||
|
|||||||
@ -16,60 +16,17 @@ export default function Page() {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const [initialData, setInitialData] = useState(null);
|
const [initialData, setInitialData] = useState(null);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
|
||||||
const csrfToken = useCsrfToken();
|
const csrfToken = useCsrfToken();
|
||||||
const [currentProfil, setCurrentProfil] = useState("");
|
const [currentProfil, setCurrentProfil] = useState("");
|
||||||
const [lastGuardianId, setLastGuardianId] = useState(1);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!studentId || !idProfil) {
|
|
||||||
console.error('Missing studentId or idProfil');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (useFakeData) {
|
|
||||||
setInitialData(mockStudent);
|
|
||||||
setLastGuardianId(999);
|
|
||||||
setIsLoading(false);
|
|
||||||
} else {
|
|
||||||
Promise.all([
|
|
||||||
// Fetch eleve data
|
|
||||||
fetchRegisterForm(studentId),
|
|
||||||
// Fetch last guardian ID
|
|
||||||
fetchLastGuardian()
|
|
||||||
])
|
|
||||||
.then(async ([studentData, guardianData]) => {
|
|
||||||
const formattedData = {
|
|
||||||
...studentData,
|
|
||||||
guardians: studentData.guardians || []
|
|
||||||
};
|
|
||||||
|
|
||||||
setInitialData(formattedData);
|
|
||||||
setLastGuardianId(guardianData.lastid);
|
|
||||||
|
|
||||||
let profils = studentData.profils;
|
|
||||||
const currentProf = profils.find(profil => profil.id === idProfil);
|
|
||||||
if (currentProf) {
|
|
||||||
setCurrentProfil(currentProf);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error fetching data:', error);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setIsLoading(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [studentId, idProfil]);
|
|
||||||
|
|
||||||
const handleSubmit = async (data) => {
|
const handleSubmit = async (data) => {
|
||||||
if (useFakeData) {
|
if (useFakeData) {
|
||||||
console.log('Fake submit:', data);
|
console.log('Fake submit:', data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const result = await editRegisterForm(studentId, data, csrfToken);
|
const result = await editRegisterForm(studentId, data, csrfToken);
|
||||||
console.log('Success:', result);
|
console.log('Success:', result);
|
||||||
router.push(FE_PARENTS_HOME_URL);
|
router.push(FE_PARENTS_HOME_URL);
|
||||||
@ -80,7 +37,7 @@ export default function Page() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<InscriptionFormShared
|
<InscriptionFormShared
|
||||||
initialData={initialData}
|
studentId={studentId}
|
||||||
csrfToken={csrfToken}
|
csrfToken={csrfToken}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
cancelUrl={FE_PARENTS_HOME_URL}
|
cancelUrl={FE_PARENTS_HOME_URL}
|
||||||
|
|||||||
@ -41,7 +41,7 @@ export const fetchRegisterForms = (type=PENDING, page='', pageSize='', search =
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const fetchRegisterForm = (id) =>{
|
export const fetchRegisterForm = (id) =>{
|
||||||
return fetch(`${BE_SUBSCRIPTION_STUDENT_URL}/${id}`) // Utilisation de studentId au lieu de codeDI
|
return fetch(`${BE_SUBSCRIPTION_REGISTERFORM_URL}/${id}`) // Utilisation de studentId au lieu de codeDI
|
||||||
.then(requestResponseHandler)
|
.then(requestResponseHandler)
|
||||||
}
|
}
|
||||||
export const fetchLastGuardian = () =>{
|
export const fetchLastGuardian = () =>{
|
||||||
@ -98,31 +98,36 @@ export const sendRegisterForm = (id) => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchRegisterFormFileTemplate = () => {
|
|
||||||
const request = new Request(
|
|
||||||
`${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_TEMPLATE_URL}`,
|
export const fetchRegisterFormFile = (id = null) => {
|
||||||
{
|
let url = `${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_URL}`
|
||||||
method:'GET',
|
if (id) {
|
||||||
headers: {
|
url = `${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_URL}/${id}`;
|
||||||
'Content-Type':'application/json'
|
}
|
||||||
},
|
const request = new Request(
|
||||||
}
|
`${url}`,
|
||||||
);
|
{
|
||||||
return fetch(request).then(requestResponseHandler)
|
method:'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type':'application/json'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return fetch(request).then(requestResponseHandler)
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchRegisterFormFile = (id) => {
|
export const editRegistrationFormFile= (fileId, data, csrfToken) => {
|
||||||
const request = new Request(
|
return fetch(`${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_URL}/${fileId}`, {
|
||||||
`${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_URL}/${id}`,
|
method: 'PUT',
|
||||||
{
|
body: data,
|
||||||
method:'GET',
|
headers: {
|
||||||
headers: {
|
'X-CSRFToken': csrfToken,
|
||||||
'Content-Type':'application/json'
|
},
|
||||||
},
|
credentials: 'include',
|
||||||
}
|
})
|
||||||
);
|
.then(requestResponseHandler)
|
||||||
return fetch(request).then(requestResponseHandler)
|
}
|
||||||
};
|
|
||||||
|
|
||||||
export const createRegistrationFormFile = (data,csrfToken) => {
|
export const createRegistrationFormFile = (data,csrfToken) => {
|
||||||
|
|
||||||
@ -137,6 +142,33 @@ export const createRegistrationFormFile = (data,csrfToken) => {
|
|||||||
.then(requestResponseHandler)
|
.then(requestResponseHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const deleteRegisterFormFile= (fileId,csrfToken) => {
|
||||||
|
return fetch(`${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_URL}/${fileId}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': csrfToken,
|
||||||
|
},
|
||||||
|
credentials: 'include',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fetchRegisterFormFileTemplate = (id = null) => {
|
||||||
|
let url = `${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_TEMPLATE_URL}`;
|
||||||
|
if(id){
|
||||||
|
url = `${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_TEMPLATE_URL}/${id}`;
|
||||||
|
}
|
||||||
|
const request = new Request(
|
||||||
|
`${url}`,
|
||||||
|
{
|
||||||
|
method:'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type':'application/json'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return fetch(request).then(requestResponseHandler)
|
||||||
|
};
|
||||||
|
|
||||||
export const createRegistrationFormFileTemplate = (data,csrfToken) => {
|
export const createRegistrationFormFileTemplate = (data,csrfToken) => {
|
||||||
|
|
||||||
return fetch(`${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_TEMPLATE_URL}`, {
|
return fetch(`${BE_SUBSCRIPTION_REGISTRATIONFORMFILE_TEMPLATE_URL}`, {
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
const AffectationClasseForm = ({ eleve, onSubmit, classes }) => {
|
const AffectationClasseForm = ({ eleve = {}, onSubmit, classes }) => {
|
||||||
|
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
classeAssocie_id: eleve.classeAssocie_id || null,
|
classeAssocie_id: eleve?.classeAssocie_id || null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleChange = (e) => {
|
const handleChange = (e) => {
|
||||||
|
|||||||
33
Front-End/src/components/FileStatusLabel.js
Normal file
33
Front-End/src/components/FileStatusLabel.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Check, Clock } from 'lucide-react';
|
||||||
|
|
||||||
|
const FileStatusLabel = ({ status }) => {
|
||||||
|
const getStatusConfig = () => {
|
||||||
|
switch (status) {
|
||||||
|
case 'sent':
|
||||||
|
return {
|
||||||
|
label: 'Envoyé',
|
||||||
|
className: 'bg-green-50 text-green-600',
|
||||||
|
icon: <Check size={16} className="text-green-600" />
|
||||||
|
};
|
||||||
|
case 'pending':
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
label: 'En attente',
|
||||||
|
className: 'bg-orange-50 text-orange-600',
|
||||||
|
icon: <Clock size={16} className="text-orange-600" />
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const { label, className, icon } = getStatusConfig();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`flex items-center justify-center gap-2 px-3 py-1 rounded-md text-sm font-medium ${className}`}>
|
||||||
|
{icon}
|
||||||
|
<span>{label}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FileStatusLabel;
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
// Import des dépendances nécessaires
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import InputText from '@/components/InputText';
|
import InputText from '@/components/InputText';
|
||||||
import SelectChoice from '@/components/SelectChoice';
|
import SelectChoice from '@/components/SelectChoice';
|
||||||
@ -6,12 +7,14 @@ import Loader from '@/components/Loader';
|
|||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
|
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
|
||||||
import Table from '@/components/Table';
|
import Table from '@/components/Table';
|
||||||
import { fetchRegisterFormFileTemplate, createRegistrationFormFile } from '@/app/lib/subscriptionAction';
|
import { fetchRegisterFormFileTemplate, createRegistrationFormFile, fetchRegisterForm, deleteRegisterFormFile } from '@/app/lib/subscriptionAction';
|
||||||
import { Download, Upload } from 'lucide-react';
|
import { Download, Upload, Trash2, Eye } from 'lucide-react';
|
||||||
import { BASE_URL } from '@/utils/Url';
|
import { BASE_URL } from '@/utils/Url';
|
||||||
import DraggableFileUpload from '@/app/[locale]/admin/subscriptions/components/DraggableFileUpload';
|
import DraggableFileUpload from '@/app/[locale]/admin/subscriptions/components/DraggableFileUpload';
|
||||||
import Modal from '@/components/Modal';
|
import Modal from '@/components/Modal';
|
||||||
|
import FileStatusLabel from '@/components/FileStatusLabel';
|
||||||
|
|
||||||
|
// Définition des niveaux scolaires disponibles
|
||||||
const levels = [
|
const levels = [
|
||||||
{ value:'1', label: 'TPS - Très Petite Section'},
|
{ value:'1', label: 'TPS - Très Petite Section'},
|
||||||
{ value:'2', label: 'PS - Petite Section'},
|
{ value:'2', label: 'PS - Petite Section'},
|
||||||
@ -19,32 +22,28 @@ const levels = [
|
|||||||
{ value:'4', label: 'GS - Grande Section'},
|
{ value:'4', label: 'GS - Grande Section'},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composant de formulaire d'inscription partagé
|
||||||
|
* @param {string} studentId - ID de l'étudiant
|
||||||
|
* @param {string} csrfToken - Token CSRF pour la sécurité
|
||||||
|
* @param {function} onSubmit - Fonction de soumission du formulaire
|
||||||
|
* @param {string} cancelUrl - URL de redirection en cas d'annulation
|
||||||
|
* @param {object} errors - Erreurs de validation du formulaire
|
||||||
|
*/
|
||||||
export default function InscriptionFormShared({
|
export default function InscriptionFormShared({
|
||||||
initialData,
|
studentId,
|
||||||
csrfToken,
|
csrfToken,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
cancelUrl,
|
cancelUrl,
|
||||||
isLoading = false,
|
|
||||||
errors = {} // Nouvelle prop pour les erreurs
|
errors = {} // Nouvelle prop pour les erreurs
|
||||||
}) {
|
}) {
|
||||||
|
// États pour gérer les données du formulaire
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const [formData, setFormData] = useState({});
|
||||||
|
|
||||||
const [formData, setFormData] = useState(() => ({
|
const [guardians, setGuardians] = useState([]);
|
||||||
id: initialData?.id || '',
|
|
||||||
last_name: initialData?.last_name || '',
|
|
||||||
first_name: initialData?.first_name || '',
|
|
||||||
address: initialData?.address || '',
|
|
||||||
birth_date: initialData?.birth_date || '',
|
|
||||||
birth_place: initialData?.birth_place || '',
|
|
||||||
birth_postal_code: initialData?.birth_postal_code || '',
|
|
||||||
nationality: initialData?.nationality || '',
|
|
||||||
attending_physician: initialData?.attending_physician || '',
|
|
||||||
level: initialData?.level || ''
|
|
||||||
}));
|
|
||||||
|
|
||||||
const [guardians, setGuardians] = useState(() =>
|
|
||||||
initialData?.guardians || []
|
|
||||||
);
|
|
||||||
|
|
||||||
|
// États pour la gestion des fichiers
|
||||||
const [uploadedFiles, setUploadedFiles] = useState([]);
|
const [uploadedFiles, setUploadedFiles] = useState([]);
|
||||||
const [fileTemplates, setFileTemplates] = useState([]);
|
const [fileTemplates, setFileTemplates] = useState([]);
|
||||||
const [fileName, setFileName] = useState("");
|
const [fileName, setFileName] = useState("");
|
||||||
@ -52,50 +51,104 @@ export default function InscriptionFormShared({
|
|||||||
const [showUploadModal, setShowUploadModal] = useState(false);
|
const [showUploadModal, setShowUploadModal] = useState(false);
|
||||||
const [currentTemplateId, setCurrentTemplateId] = useState(null);
|
const [currentTemplateId, setCurrentTemplateId] = useState(null);
|
||||||
|
|
||||||
|
// Chargement initial des données
|
||||||
// Mettre à jour les données quand initialData change
|
// Mettre à jour les données quand initialData change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialData) {
|
if (studentId) {
|
||||||
setFormData({
|
fetchRegisterForm(studentId).then((data) => {
|
||||||
id: initialData.id || '',
|
console.log(data);
|
||||||
last_name: initialData.last_name || '',
|
|
||||||
first_name: initialData.first_name || '',
|
setFormData({
|
||||||
address: initialData.address || '',
|
id: data?.student?.id || '',
|
||||||
birth_date: initialData.birth_date || '',
|
last_name: data?.student?.last_name || '',
|
||||||
birth_place: initialData.birth_place || '',
|
first_name: data?.student?.first_name || '',
|
||||||
birth_postal_code: initialData.birth_postal_code || '',
|
address: data?.student?.address || '',
|
||||||
nationality: initialData.nationality || '',
|
birth_date: data?.student?.birth_date || '',
|
||||||
attending_physician: initialData.attending_physician || '',
|
birth_place: data?.student?.birth_place || '',
|
||||||
level: initialData.level || ''
|
birth_postal_code: data?.student?.birth_postal_code || '',
|
||||||
|
nationality: data?.student?.nationality || '',
|
||||||
|
attending_physician: data?.student?.attending_physician || '',
|
||||||
|
level: data?.student?.level || ''
|
||||||
|
});
|
||||||
|
setGuardians(data?.student?.guardians || []);
|
||||||
|
setUploadedFiles(data.registration_files || []);
|
||||||
});
|
});
|
||||||
setGuardians(initialData.guardians || []);
|
|
||||||
fetchRegisterFormFileTemplate().then((data) => {
|
fetchRegisterFormFileTemplate().then((data) => {
|
||||||
setFileTemplates(data);
|
setFileTemplates(data);
|
||||||
});
|
});
|
||||||
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
}, [initialData]);
|
}, [studentId]);
|
||||||
|
|
||||||
|
// Fonctions de gestion du formulaire et des fichiers
|
||||||
const updateFormField = (field, value) => {
|
const updateFormField = (field, value) => {
|
||||||
setFormData(prev => ({...prev, [field]: value}));
|
setFormData(prev => ({...prev, [field]: value}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Gestion du téléversement de fichiers
|
||||||
const handleFileUpload = async (file, fileName) => {
|
const handleFileUpload = async (file, fileName) => {
|
||||||
|
if (!file || !currentTemplateId || !formData.id) {
|
||||||
|
console.error('Missing required data for upload');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const data = new FormData();
|
const data = new FormData();
|
||||||
data.append('file', file);
|
data.append('file', file);
|
||||||
data.append('name',fileName);
|
data.append('name', fileName);
|
||||||
data.append('template', currentTemplateId);
|
data.append('template', currentTemplateId);
|
||||||
data.append('register_form', formData.id);
|
data.append('register_form', formData.id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await createRegistrationFormFile(data, csrfToken);
|
const response = await createRegistrationFormFile(data, csrfToken);
|
||||||
// Optionnellement, rafraîchir la liste des fichiers
|
if (response) {
|
||||||
fetchRegisterFormFileTemplate().then((data) => {
|
setUploadedFiles(prev => {
|
||||||
setFileTemplates(data);
|
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) {
|
} catch (error) {
|
||||||
console.error('Error uploading file:', error);
|
console.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 deleteRegisterFormFile(fileToDelete.id, csrfToken);
|
||||||
|
setUploadedFiles(prev => prev.filter(f => parseInt(f.template) !== templateId));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting file:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Soumission du formulaire
|
||||||
const handleSubmit = (e) => {
|
const handleSubmit = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const data ={
|
const data ={
|
||||||
@ -107,36 +160,70 @@ export default function InscriptionFormShared({
|
|||||||
onSubmit(data);
|
onSubmit(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Récupération des messages d'erreur
|
||||||
const getError = (field) => {
|
const getError = (field) => {
|
||||||
return errors?.student?.[field]?.[0];
|
return errors?.student?.[field]?.[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
const getGuardianError = (index, field) => {
|
// Configuration des colonnes pour le tableau des fichiers
|
||||||
return errors?.student?.guardians?.[index]?.[field]?.[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ name: 'Nom du fichier', transform: (row) => row.name },
|
{ name: 'Nom du fichier', transform: (row) => row.name },
|
||||||
{ name: 'Fichier à Remplir', transform: (row) => row.is_required ? 'Oui' : 'Non' },
|
{ 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">
|
{ 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} />
|
<Download size={16} />
|
||||||
</a> </div>},
|
</a> </div>},
|
||||||
{ name: 'Actions', transform: (row) => (
|
{ name: 'Statut', transform: (row) =>
|
||||||
<div className="flex items-center justify-center gap-2">
|
row.is_required && (
|
||||||
{row.is_required &&
|
<FileStatusLabel
|
||||||
<button className="text-emerald-500 hover:text-emerald-700" type="button" onClick={() => {
|
status={isFileUploaded(row.id) ? 'sent' : 'pending'}
|
||||||
setCurrentTemplateId(row.id);
|
/>
|
||||||
setShowUploadModal(true);
|
)
|
||||||
}}>
|
},
|
||||||
|
{ 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} />
|
<Upload size={16} />
|
||||||
</button>
|
</button>
|
||||||
}
|
);
|
||||||
</div>
|
}},
|
||||||
) },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Affichage du loader pendant le chargement
|
||||||
if (isLoading) return <Loader />;
|
if (isLoading) return <Loader />;
|
||||||
|
|
||||||
|
// Rendu du composant
|
||||||
return (
|
return (
|
||||||
<div className="max-w-4xl mx-auto p-6">
|
<div className="max-w-4xl mx-auto p-6">
|
||||||
<form onSubmit={handleSubmit} className="space-y-8">
|
<form onSubmit={handleSubmit} className="space-y-8">
|
||||||
@ -245,17 +332,19 @@ export default function InscriptionFormShared({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Section Fichiers d'inscription */}
|
{/* Section Fichiers d'inscription */}
|
||||||
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
{fileTemplates.length > 0 && (
|
||||||
<h2 className="text-xl font-bold mb-4 text-gray-800">Fichiers à remplir</h2>
|
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
||||||
<Table
|
<h2 className="text-xl font-bold mb-4 text-gray-800">Fichiers à remplir</h2>
|
||||||
data={fileTemplates}
|
<Table
|
||||||
columns={columns}
|
data={fileTemplates}
|
||||||
itemsPerPage={5}
|
columns={columns}
|
||||||
currentPage={1}
|
itemsPerPage={5}
|
||||||
totalPages={1}
|
currentPage={1}
|
||||||
onPageChange={() => {}}
|
totalPages={1}
|
||||||
/>
|
onPageChange={() => {}}
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Boutons de contrôle */}
|
{/* Boutons de contrôle */}
|
||||||
<div className="flex justify-end space-x-4">
|
<div className="flex justify-end space-x-4">
|
||||||
@ -263,44 +352,52 @@ export default function InscriptionFormShared({
|
|||||||
<Button type="submit" text="Valider" primary />
|
<Button type="submit" text="Valider" primary />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<Modal
|
{fileTemplates.length > 0 && (
|
||||||
isOpen={showUploadModal}
|
<Modal
|
||||||
setIsOpen={setShowUploadModal}
|
isOpen={showUploadModal}
|
||||||
title="Téléverser un fichier"
|
setIsOpen={setShowUploadModal}
|
||||||
ContentComponent={() => (
|
title="Téléverser un fichier"
|
||||||
<>
|
ContentComponent={() => (
|
||||||
<DraggableFileUpload
|
<>
|
||||||
className="w-full"
|
<DraggableFileUpload
|
||||||
fileName={fileName}
|
className="w-full"
|
||||||
onFileSelect={(selectedFile) => {
|
fileName={fileName}
|
||||||
setFile(selectedFile);
|
onFileSelect={(selectedFile) => {
|
||||||
setFileName(selectedFile.name);
|
if (selectedFile) {
|
||||||
}}
|
setFile(selectedFile);
|
||||||
>
|
setFileName(selectedFile.name);
|
||||||
<input type="hidden" name="template" value={currentTemplateId} />
|
}
|
||||||
<input type="hidden" name="register_form" value={formData.id} />
|
|
||||||
</DraggableFileUpload>
|
|
||||||
<div className="mt-4 flex justify-center space-x-4">
|
|
||||||
<Button
|
|
||||||
text="Annuler"
|
|
||||||
onClick={() => {
|
|
||||||
setShowUploadModal(false);
|
|
||||||
setCurrentTemplateId(null);
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button
|
<div className="mt-4 flex justify-center space-x-4">
|
||||||
text="Valider"
|
<Button
|
||||||
onClick={() => {
|
text="Annuler"
|
||||||
setShowUploadModal(false);
|
onClick={() => {
|
||||||
handleFileUpload(file, fileName);
|
setShowUploadModal(false);
|
||||||
setCurrentTemplateId(null);
|
setCurrentTemplateId(null);
|
||||||
}}
|
setFile(null);
|
||||||
primary={true}
|
setFileName("");
|
||||||
/>
|
}}
|
||||||
</div>
|
/>
|
||||||
</>
|
<Button
|
||||||
)}
|
text="Valider"
|
||||||
/>
|
onClick={() => {
|
||||||
|
if (file && fileName) {
|
||||||
|
handleFileUpload(file, fileName);
|
||||||
|
setShowUploadModal(false);
|
||||||
|
setCurrentTemplateId(null);
|
||||||
|
setFile(null);
|
||||||
|
setFileName("");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
primary={true}
|
||||||
|
disabled={!file || !fileName}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -4,6 +4,7 @@ import Button from '@/components/Button';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import 'react-phone-number-input/style.css'
|
import 'react-phone-number-input/style.css'
|
||||||
|
import { Trash2, Plus } from 'lucide-react';
|
||||||
|
|
||||||
export default function ResponsableInputFields({guardians, onGuardiansChange, addGuardian, deleteGuardian, errors = []}) {
|
export default function ResponsableInputFields({guardians, onGuardiansChange, addGuardian, deleteGuardian, errors = []}) {
|
||||||
const t = useTranslations('ResponsableInputFields');
|
const t = useTranslations('ResponsableInputFields');
|
||||||
@ -19,10 +20,9 @@ export default function ResponsableInputFields({guardians, onGuardiansChange, ad
|
|||||||
<div className='flex justify-between items-center mb-4'>
|
<div className='flex justify-between items-center mb-4'>
|
||||||
<h3 className='text-xl font-bold'>{t('responsable')} {index+1}</h3>
|
<h3 className='text-xl font-bold'>{t('responsable')} {index+1}</h3>
|
||||||
{guardians.length > 1 && (
|
{guardians.length > 1 && (
|
||||||
<Button
|
<Trash2
|
||||||
text={t('delete')}
|
className="w-5 h-5 text-red-500 cursor-pointer hover:text-red-700 transition-colors"
|
||||||
onClick={() => deleteGuardian(index)}
|
onClick={() => deleteGuardian(index)}
|
||||||
className="w-32"
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -102,13 +102,9 @@ export default function ResponsableInputFields({guardians, onGuardiansChange, ad
|
|||||||
))}
|
))}
|
||||||
|
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<Button
|
<Plus
|
||||||
text={t('add_responsible')}
|
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)}
|
onClick={(e) => addGuardian(e)}
|
||||||
primary
|
|
||||||
icon={<i className="icon profile-add" />}
|
|
||||||
type="button"
|
|
||||||
className="w-64"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -14,7 +14,7 @@ const useCsrfToken = () => {
|
|||||||
if (data) {
|
if (data) {
|
||||||
if(data.csrfToken != token) {
|
if(data.csrfToken != token) {
|
||||||
setToken(data.csrfToken);
|
setToken(data.csrfToken);
|
||||||
console.log('------------> CSRF Token reçu:', data.csrfToken);
|
//console.log('------------> CSRF Token reçu:', data.csrfToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user