mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-04-05 20:51:26 +00:00
feat: Securisation du téléchargement de fichier
This commit is contained in:
@ -3,11 +3,11 @@ import React, { useState, useEffect } from 'react';
|
||||
import Popup from '@/components/Popup';
|
||||
import ToggleSwitch from '@/components/Form/ToggleSwitch';
|
||||
import SelectChoice from '@/components/Form/SelectChoice';
|
||||
import { BASE_URL } from '@/utils/Url';
|
||||
import {
|
||||
fetchSchoolFileTemplatesFromRegistrationFiles,
|
||||
fetchParentFileTemplatesFromRegistrationFiles,
|
||||
} from '@/app/actions/subscriptionAction';
|
||||
import { getSecureFileUrl } from '@/utils/fileUrl';
|
||||
import logger from '@/utils/logger';
|
||||
import { School, FileText } from 'lucide-react';
|
||||
import SectionHeader from '@/components/SectionHeader';
|
||||
@ -49,15 +49,18 @@ export default function ValidateSubscription({
|
||||
// Parent templates
|
||||
parentFileTemplates.forEach((tpl, i) => {
|
||||
if (typeof tpl.isValidated === 'boolean') {
|
||||
newStatuses[1 + schoolFileTemplates.length + i] = tpl.isValidated ? 'accepted' : 'refused';
|
||||
newStatuses[1 + schoolFileTemplates.length + i] = tpl.isValidated
|
||||
? 'accepted'
|
||||
: 'refused';
|
||||
}
|
||||
});
|
||||
setDocStatuses(s => ({ ...s, ...newStatuses }));
|
||||
setDocStatuses((s) => ({ ...s, ...newStatuses }));
|
||||
}, [schoolFileTemplates, parentFileTemplates]);
|
||||
const [showRefusedPopup, setShowRefusedPopup] = useState(false);
|
||||
|
||||
// Affiche la popup de confirmation finale (tous docs validés et classe sélectionnée)
|
||||
const [showFinalValidationPopup, setShowFinalValidationPopup] = useState(false);
|
||||
const [showFinalValidationPopup, setShowFinalValidationPopup] =
|
||||
useState(false);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
associated_class: null,
|
||||
@ -131,7 +134,7 @@ export default function ValidateSubscription({
|
||||
const handleRefuseDossier = () => {
|
||||
// Message clair avec la liste des documents refusés
|
||||
let notes = 'Dossier non validé pour les raisons suivantes :\n';
|
||||
notes += refusedDocs.map(doc => `- ${doc.name}`).join('\n');
|
||||
notes += refusedDocs.map((doc) => `- ${doc.name}`).join('\n');
|
||||
const data = {
|
||||
status: 2,
|
||||
notes,
|
||||
@ -177,10 +180,18 @@ export default function ValidateSubscription({
|
||||
.filter((doc, idx) => docStatuses[idx] === 'refused');
|
||||
|
||||
// Récupère la liste des documents à cocher (hors fiche élève)
|
||||
const docIndexes = allTemplates.map((_, idx) => idx).filter(idx => idx !== 0);
|
||||
const allChecked = docIndexes.length > 0 && docIndexes.every(idx => docStatuses[idx] === 'accepted' || docStatuses[idx] === 'refused');
|
||||
const allValidated = docIndexes.length > 0 && docIndexes.every(idx => docStatuses[idx] === 'accepted');
|
||||
const hasRefused = docIndexes.some(idx => docStatuses[idx] === 'refused');
|
||||
const docIndexes = allTemplates
|
||||
.map((_, idx) => idx)
|
||||
.filter((idx) => idx !== 0);
|
||||
const allChecked =
|
||||
docIndexes.length > 0 &&
|
||||
docIndexes.every(
|
||||
(idx) => docStatuses[idx] === 'accepted' || docStatuses[idx] === 'refused'
|
||||
);
|
||||
const allValidated =
|
||||
docIndexes.length > 0 &&
|
||||
docIndexes.every((idx) => docStatuses[idx] === 'accepted');
|
||||
const hasRefused = docIndexes.some((idx) => docStatuses[idx] === 'refused');
|
||||
logger.debug(allTemplates);
|
||||
|
||||
return (
|
||||
@ -202,7 +213,7 @@ export default function ValidateSubscription({
|
||||
{allTemplates[currentTemplateIndex].name || 'Document sans nom'}
|
||||
</h3>
|
||||
<iframe
|
||||
src={`${BASE_URL}${allTemplates[currentTemplateIndex].file}`}
|
||||
src={getSecureFileUrl(allTemplates[currentTemplateIndex].file)}
|
||||
title={
|
||||
allTemplates[currentTemplateIndex].type === 'main'
|
||||
? 'Document Principal'
|
||||
@ -252,18 +263,32 @@ export default function ValidateSubscription({
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium border transition-colors focus:outline-none focus:ring-2 focus:ring-blue-400 flex items-center gap-1
|
||||
${docStatuses[index] === 'accepted' ? 'bg-emerald-500 text-white border-emerald-500' : 'bg-white text-emerald-600 border-emerald-300'}`}
|
||||
aria-pressed={docStatuses[index] === 'accepted'}
|
||||
onClick={e => {
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setDocStatuses(s => ({ ...s, [index]: 'accepted' }));
|
||||
setDocStatuses((s) => ({
|
||||
...s,
|
||||
[index]: 'accepted',
|
||||
}));
|
||||
// Appel API pour valider le document
|
||||
if (handleValidateOrRefuseDoc) {
|
||||
let template = null;
|
||||
let type = null;
|
||||
if (index > 0 && index <= schoolFileTemplates.length) {
|
||||
if (
|
||||
index > 0 &&
|
||||
index <= schoolFileTemplates.length
|
||||
) {
|
||||
template = schoolFileTemplates[index - 1];
|
||||
type = 'school';
|
||||
} else if (index > schoolFileTemplates.length && index <= schoolFileTemplates.length + parentFileTemplates.length) {
|
||||
template = parentFileTemplates[index - 1 - schoolFileTemplates.length];
|
||||
} else if (
|
||||
index > schoolFileTemplates.length &&
|
||||
index <=
|
||||
schoolFileTemplates.length +
|
||||
parentFileTemplates.length
|
||||
) {
|
||||
template =
|
||||
parentFileTemplates[
|
||||
index - 1 - schoolFileTemplates.length
|
||||
];
|
||||
type = 'parent';
|
||||
}
|
||||
if (template && template.id) {
|
||||
@ -284,18 +309,29 @@ export default function ValidateSubscription({
|
||||
className={`px-2 py-1 rounded-full text-xs font-medium border transition-colors focus:outline-none focus:ring-2 focus:ring-blue-400 flex items-center gap-1
|
||||
${docStatuses[index] === 'refused' ? 'bg-red-500 text-white border-red-500' : 'bg-white text-red-600 border-red-300'}`}
|
||||
aria-pressed={docStatuses[index] === 'refused'}
|
||||
onClick={e => {
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setDocStatuses(s => ({ ...s, [index]: 'refused' }));
|
||||
setDocStatuses((s) => ({ ...s, [index]: 'refused' }));
|
||||
// Appel API pour refuser le document
|
||||
if (handleValidateOrRefuseDoc) {
|
||||
let template = null;
|
||||
let type = null;
|
||||
if (index > 0 && index <= schoolFileTemplates.length) {
|
||||
if (
|
||||
index > 0 &&
|
||||
index <= schoolFileTemplates.length
|
||||
) {
|
||||
template = schoolFileTemplates[index - 1];
|
||||
type = 'school';
|
||||
} else if (index > schoolFileTemplates.length && index <= schoolFileTemplates.length + parentFileTemplates.length) {
|
||||
template = parentFileTemplates[index - 1 - schoolFileTemplates.length];
|
||||
} else if (
|
||||
index > schoolFileTemplates.length &&
|
||||
index <=
|
||||
schoolFileTemplates.length +
|
||||
parentFileTemplates.length
|
||||
) {
|
||||
template =
|
||||
parentFileTemplates[
|
||||
index - 1 - schoolFileTemplates.length
|
||||
];
|
||||
type = 'parent';
|
||||
}
|
||||
if (template && template.id) {
|
||||
@ -351,7 +387,7 @@ export default function ValidateSubscription({
|
||||
<div className="mt-auto py-4">
|
||||
<Button
|
||||
text="Soumettre"
|
||||
onClick={e => {
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
// 1. Si tous les documents ne sont pas cochés, rien ne se passe (bouton désactivé)
|
||||
// 2. Si tous cochés et au moins un refusé : popup refus
|
||||
@ -367,12 +403,14 @@ export default function ValidateSubscription({
|
||||
}}
|
||||
primary
|
||||
className={`w-full h-12 rounded-md shadow-sm focus:outline-none ${
|
||||
!allChecked || (allChecked && allValidated && !formData.associated_class)
|
||||
!allChecked ||
|
||||
(allChecked && allValidated && !formData.associated_class)
|
||||
? 'bg-gray-300 text-gray-700 cursor-not-allowed'
|
||||
: 'bg-emerald-500 text-white hover:bg-emerald-600'
|
||||
}`}
|
||||
disabled={
|
||||
!allChecked || (allChecked && allValidated && !formData.associated_class)
|
||||
!allChecked ||
|
||||
(allChecked && allValidated && !formData.associated_class)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
@ -391,7 +429,7 @@ export default function ValidateSubscription({
|
||||
<span className="font-semibold text-blue-700">{email}</span>
|
||||
{' avec la liste des documents non validés :'}
|
||||
<ul className="list-disc ml-6 mt-2">
|
||||
{refusedDocs.map(doc => (
|
||||
{refusedDocs.map((doc) => (
|
||||
<li key={doc.idx}>{doc.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
Reference in New Issue
Block a user