mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
feat: Ajout d'un nouvel état dans l'automatique lorsqu'un mandat SEPA
doit être envoyé aux parent
This commit is contained in:
@ -30,6 +30,16 @@
|
|||||||
"from": "SENT",
|
"from": "SENT",
|
||||||
"to": "UNDER_REVIEW"
|
"to": "UNDER_REVIEW"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "EVENT_WAITING_FOR_SEPA",
|
||||||
|
"from": "SENT",
|
||||||
|
"to": "SEPA_TO_SEND"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "EVENT_SEND_SEPA",
|
||||||
|
"from": "SEPA_TO_SEND",
|
||||||
|
"to": "SEPA_SENT"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "EVENT_FOLLOW_UP",
|
"name": "EVENT_FOLLOW_UP",
|
||||||
"from": "SENT",
|
"from": "SENT",
|
||||||
@ -55,11 +65,6 @@
|
|||||||
"from": "UNDER_REVIEW",
|
"from": "UNDER_REVIEW",
|
||||||
"to": "VALIDATED"
|
"to": "VALIDATED"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "EVENT_SEND_SEPA",
|
|
||||||
"from": "UNDER_REVIEW",
|
|
||||||
"to": "SEPA_SENT"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "EVENT_ARCHIVE",
|
"name": "EVENT_ARCHIVE",
|
||||||
"from": "UNDER_REVIEW",
|
"from": "UNDER_REVIEW",
|
||||||
|
|||||||
@ -10,7 +10,8 @@ state_mapping = {
|
|||||||
"TO_BE_FOLLOWED_UP": RegistrationForm.RegistrationFormStatus.RF_TO_BE_FOLLOWED_UP,
|
"TO_BE_FOLLOWED_UP": RegistrationForm.RegistrationFormStatus.RF_TO_BE_FOLLOWED_UP,
|
||||||
"VALIDATED": RegistrationForm.RegistrationFormStatus.RF_VALIDATED,
|
"VALIDATED": RegistrationForm.RegistrationFormStatus.RF_VALIDATED,
|
||||||
"ARCHIVED": RegistrationForm.RegistrationFormStatus.RF_ARCHIVED,
|
"ARCHIVED": RegistrationForm.RegistrationFormStatus.RF_ARCHIVED,
|
||||||
"SEPA_SENT": RegistrationForm.RegistrationFormStatus.RF_SEPA_SENT
|
"SEPA_SENT": RegistrationForm.RegistrationFormStatus.RF_SEPA_SENT,
|
||||||
|
"SEPA_TO_SEND": RegistrationForm.RegistrationFormStatus.RF_SEPA_TO_SEND
|
||||||
}
|
}
|
||||||
|
|
||||||
def load_config(config_file):
|
def load_config(config_file):
|
||||||
|
|||||||
@ -182,6 +182,7 @@ class RegistrationForm(models.Model):
|
|||||||
RF_VALIDATED = 5, _('Dossier d\'inscription validé')
|
RF_VALIDATED = 5, _('Dossier d\'inscription validé')
|
||||||
RF_ARCHIVED = 6, _('Dossier d\'inscription archivé')
|
RF_ARCHIVED = 6, _('Dossier d\'inscription archivé')
|
||||||
RF_SEPA_SENT = 7, _('Mandat SEPA envoyé')
|
RF_SEPA_SENT = 7, _('Mandat SEPA envoyé')
|
||||||
|
RF_SEPA_TO_SEND = 8, _('Mandat SEPA à envoyer')
|
||||||
|
|
||||||
# One-to-One Relationship
|
# One-to-One Relationship
|
||||||
student = models.OneToOneField(Student, on_delete=models.CASCADE, primary_key=True)
|
student = models.OneToOneField(Student, on_delete=models.CASCADE, primary_key=True)
|
||||||
|
|||||||
@ -252,6 +252,8 @@ class RegisterFormWithIdView(APIView):
|
|||||||
return JsonResponse(studentForm_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
return JsonResponse(studentForm_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
if _status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW:
|
if _status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW:
|
||||||
|
# Le parent a rempli le dossier d'inscription sans sélectionner "Prélèvement par Mandat SEPA"
|
||||||
|
# L'école doit désormais valider le dossier d'inscription
|
||||||
try:
|
try:
|
||||||
# Génération de la fiche d'inscription au format PDF
|
# Génération de la fiche d'inscription au format PDF
|
||||||
base_dir = os.path.join(settings.MEDIA_ROOT, f"registration_files/dossier_rf_{registerForm.pk}")
|
base_dir = os.path.join(settings.MEDIA_ROOT, f"registration_files/dossier_rf_{registerForm.pk}")
|
||||||
@ -304,11 +306,14 @@ class RegisterFormWithIdView(APIView):
|
|||||||
guardian = student.getMainGuardian()
|
guardian = student.getMainGuardian()
|
||||||
email = guardian.profile_role.profile.email
|
email = guardian.profile_role.profile.email
|
||||||
errorMessage = mailer.sendMandatSEPA(email, registerForm.establishment.pk)
|
errorMessage = mailer.sendMandatSEPA(email, registerForm.establishment.pk)
|
||||||
if errorMessage == '':
|
if errorMessage != '':
|
||||||
registerForm.last_update = util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
|
return JsonResponse({"errorMessage": errorMessage}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||||
updateStateMachine(registerForm, 'EVENT_SEND_SEPA')
|
registerForm.last_update = util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
|
||||||
return JsonResponse({"message": f"Le mandat SEPA a bien été envoyé à l'adresse {email}"}, safe=False)
|
updateStateMachine(registerForm, 'EVENT_SEND_SEPA')
|
||||||
return JsonResponse({"errorMessage": errorMessage}, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
elif _status == RegistrationForm.RegistrationFormStatus.RF_SEPA_TO_SEND:
|
||||||
|
# Le parent a rempli le dossier d'inscription en sélectionnant "Prélèvement par Mandat SEPA"
|
||||||
|
# L'école doit désormais envoyer le mandat SEPA pour poursuivre l'inscription
|
||||||
|
updateStateMachine(registerForm, 'EVENT_WAITING_FOR_SEPA')
|
||||||
|
|
||||||
# Retourner les données mises à jour
|
# Retourner les données mises à jour
|
||||||
return JsonResponse(studentForm_serializer.data, safe=False)
|
return JsonResponse(studentForm_serializer.data, safe=False)
|
||||||
|
|||||||
@ -101,7 +101,8 @@ class ChildrenListView(APIView):
|
|||||||
status__in=[
|
status__in=[
|
||||||
RegistrationForm.RegistrationFormStatus.RF_SENT,
|
RegistrationForm.RegistrationFormStatus.RF_SENT,
|
||||||
RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW,
|
RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW,
|
||||||
RegistrationForm.RegistrationFormStatus.RF_SEPA_SENT
|
RegistrationForm.RegistrationFormStatus.RF_SEPA_SENT,
|
||||||
|
RegistrationForm.RegistrationFormStatus.RF_SEPA_TO_SEND
|
||||||
]
|
]
|
||||||
).distinct()
|
).distinct()
|
||||||
students_serializer = RegistrationFormByParentSerializer(students, many=True)
|
students_serializer = RegistrationFormByParentSerializer(students, many=True)
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { useCsrfToken } from '@/context/CsrfContext';
|
|||||||
import { useEstablishment } from '@/context/EstablishmentContext';
|
import { useEstablishment } from '@/context/EstablishmentContext';
|
||||||
import { editRegisterForm } from '@/app/actions/subscriptionAction';
|
import { editRegisterForm } from '@/app/actions/subscriptionAction';
|
||||||
import logger from '@/utils/logger';
|
import logger from '@/utils/logger';
|
||||||
|
import Loader from '@/components/Loader';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -16,14 +17,18 @@ export default function Page() {
|
|||||||
const [formErrors, setFormErrors] = useState({});
|
const [formErrors, setFormErrors] = useState({});
|
||||||
const csrfToken = useCsrfToken();
|
const csrfToken = useCsrfToken();
|
||||||
const { selectedEstablishmentId } = useEstablishment();
|
const { selectedEstablishmentId } = useEstablishment();
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const handleSubmit = (data) => {
|
const handleSubmit = (data) => {
|
||||||
|
setIsLoading(true);
|
||||||
editRegisterForm(studentId, data, csrfToken)
|
editRegisterForm(studentId, data, csrfToken)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
|
setIsLoading(false);
|
||||||
logger.debug('Success:', result);
|
logger.debug('Success:', result);
|
||||||
router.push(FE_ADMIN_SUBSCRIPTIONS_URL);
|
router.push(FE_ADMIN_SUBSCRIPTIONS_URL);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
setIsLoading(false);
|
||||||
logger.error('Error:', error.message);
|
logger.error('Error:', error.message);
|
||||||
if (error.details) {
|
if (error.details) {
|
||||||
logger.error('Form errors:', error.details);
|
logger.error('Form errors:', error.details);
|
||||||
@ -32,6 +37,10 @@ export default function Page() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <Loader />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InscriptionFormShared
|
<InscriptionFormShared
|
||||||
studentId={studentId}
|
studentId={studentId}
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import {
|
|||||||
FileText,
|
FileText,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
Plus,
|
Plus,
|
||||||
XCircle,
|
Upload,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import Modal from '@/components/Modal';
|
import Modal from '@/components/Modal';
|
||||||
import InscriptionForm from '@/components/Inscription/InscriptionForm';
|
import InscriptionForm from '@/components/Inscription/InscriptionForm';
|
||||||
@ -35,6 +35,7 @@ import {
|
|||||||
archiveRegisterForm,
|
archiveRegisterForm,
|
||||||
fetchStudents,
|
fetchStudents,
|
||||||
editRegisterForm,
|
editRegisterForm,
|
||||||
|
sendSEPARegisterForm,
|
||||||
} from '@/app/actions/subscriptionAction';
|
} from '@/app/actions/subscriptionAction';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -66,6 +67,7 @@ import DjangoCSRFToken from '@/components/DjangoCSRFToken';
|
|||||||
import { useCsrfToken } from '@/context/CsrfContext';
|
import { useCsrfToken } from '@/context/CsrfContext';
|
||||||
import logger from '@/utils/logger';
|
import logger from '@/utils/logger';
|
||||||
import { PhoneLabel } from '@/components/PhoneLabel';
|
import { PhoneLabel } from '@/components/PhoneLabel';
|
||||||
|
import FileUpload from '@/components/FileUpload';
|
||||||
|
|
||||||
export default function Page({ params: { locale } }) {
|
export default function Page({ params: { locale } }) {
|
||||||
const t = useTranslations('subscriptions');
|
const t = useTranslations('subscriptions');
|
||||||
@ -80,11 +82,6 @@ export default function Page({ params: { locale } }) {
|
|||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [alertPage, setAlertPage] = useState(false);
|
const [alertPage, setAlertPage] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [popup, setPopup] = useState({
|
|
||||||
visible: false,
|
|
||||||
message: '',
|
|
||||||
onConfirm: null,
|
|
||||||
});
|
|
||||||
const [activeTab, setActiveTab] = useState('pending');
|
const [activeTab, setActiveTab] = useState('pending');
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [totalPages, setTotalPages] = useState(1);
|
const [totalPages, setTotalPages] = useState(1);
|
||||||
@ -113,10 +110,29 @@ export default function Page({ params: { locale } }) {
|
|||||||
const [isFilesModalOpen, setIsFilesModalOpen] = useState(false);
|
const [isFilesModalOpen, setIsFilesModalOpen] = useState(false);
|
||||||
const [selectedRowFiles, setSelectedRowFiles] = useState([]);
|
const [selectedRowFiles, setSelectedRowFiles] = useState([]);
|
||||||
|
|
||||||
|
const [popupVisible, setPopupVisible] = useState(false);
|
||||||
|
const [popupMessage, setPopupMessage] = useState('');
|
||||||
|
const [confirmPopupVisible, setConfirmPopupVisible] = useState(false);
|
||||||
|
const [confirmPopupMessage, setConfirmPopupMessage] = useState('');
|
||||||
|
const [confirmPopupOnConfirm, setConfirmPopupOnConfirm] = useState(() => {});
|
||||||
|
|
||||||
|
const [isSepaUploadModalOpen, setIsSepaUploadModalOpen] = useState(false);
|
||||||
|
const [selectedRowForUpload, setSelectedRowForUpload] = useState(null);
|
||||||
|
|
||||||
const csrfToken = useCsrfToken();
|
const csrfToken = useCsrfToken();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { selectedEstablishmentId } = useEstablishment();
|
const { selectedEstablishmentId } = useEstablishment();
|
||||||
|
|
||||||
|
const openSepaUploadModal = (row) => {
|
||||||
|
setSelectedRowForUpload(row);
|
||||||
|
setIsSepaUploadModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeSepaUploadModal = () => {
|
||||||
|
setSelectedRowForUpload(null);
|
||||||
|
setIsSepaUploadModalOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
const openModal = () => {
|
const openModal = () => {
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
};
|
};
|
||||||
@ -221,28 +237,6 @@ export default function Page({ params: { locale } }) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedEstablishmentId) {
|
|
||||||
const fetchInitialData = () => {
|
|
||||||
Promise.all([
|
|
||||||
fetchClasses(selectedEstablishmentId),
|
|
||||||
fetchStudents(selectedEstablishmentId),
|
|
||||||
])
|
|
||||||
.then(([classesData, studentsData]) => {
|
|
||||||
setClasses(classesData);
|
|
||||||
setEleves(studentsData);
|
|
||||||
logger.debug('Success - Classes:', classesData);
|
|
||||||
logger.debug('Success - Students:', studentsData);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
logger.error('Error fetching initial data:', error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchInitialData();
|
|
||||||
}
|
|
||||||
}, [selectedEstablishmentId]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedEstablishmentId) {
|
if (selectedEstablishmentId) {
|
||||||
const fetchDataAndSetState = () => {
|
const fetchDataAndSetState = () => {
|
||||||
@ -257,6 +251,19 @@ export default function Page({ params: { locale } }) {
|
|||||||
)
|
)
|
||||||
.then(registerFormPendingDataHandler)
|
.then(registerFormPendingDataHandler)
|
||||||
.catch(requestErrorHandler),
|
.catch(requestErrorHandler),
|
||||||
|
|
||||||
|
fetchClasses(selectedEstablishmentId)
|
||||||
|
.then((classesData) => {
|
||||||
|
setClasses(classesData);
|
||||||
|
})
|
||||||
|
.catch(requestErrorHandler),
|
||||||
|
|
||||||
|
fetchStudents(selectedEstablishmentId)
|
||||||
|
.then((studentsData) => {
|
||||||
|
setEleves(studentsData);
|
||||||
|
})
|
||||||
|
.catch(requestErrorHandler),
|
||||||
|
|
||||||
fetchRegisterForms(selectedEstablishmentId, SUBSCRIBED)
|
fetchRegisterForms(selectedEstablishmentId, SUBSCRIBED)
|
||||||
.then(registerFormSubscribedDataHandler)
|
.then(registerFormSubscribedDataHandler)
|
||||||
.catch(requestErrorHandler),
|
.catch(requestErrorHandler),
|
||||||
@ -378,6 +385,33 @@ export default function Page({ params: { locale } }) {
|
|||||||
setTotalPages(Math.ceil(totalArchives / itemsPerPage));
|
setTotalPages(Math.ceil(totalArchives / itemsPerPage));
|
||||||
}
|
}
|
||||||
}, [currentPage]);
|
}, [currentPage]);
|
||||||
|
|
||||||
|
const handleSepaFileUpload = (file, row) => {
|
||||||
|
if (!file || !row) {
|
||||||
|
logger.error("Aucun fichier ou ligne sélectionnée pour l'upload.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('status', 7);
|
||||||
|
formData.append('sepa_file', file);
|
||||||
|
|
||||||
|
// Appeler l'API pour uploader le fichier SEPA
|
||||||
|
sendSEPARegisterForm(row.student.id, formData, csrfToken)
|
||||||
|
.then((response) => {
|
||||||
|
logger.debug('Mandat SEPA uploadé avec succès :', response);
|
||||||
|
setPopupMessage('Le mandat SEPA a été uploadé avec succès.');
|
||||||
|
setPopupVisible(true);
|
||||||
|
setReloadFetch(true);
|
||||||
|
closeSepaUploadModal();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
logger.error("Erreur lors de l'upload du mandat SEPA :", error);
|
||||||
|
setPopupMessage("Erreur lors de l'upload du mandat SEPA.");
|
||||||
|
setPopupVisible(true);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Archives a registration form after user confirmation.
|
* Archives a registration form after user confirmation.
|
||||||
*
|
*
|
||||||
@ -386,44 +420,56 @@ export default function Page({ params: { locale } }) {
|
|||||||
* @param {string} prenom - The first name of the person whose registration form is being archived.
|
* @param {string} prenom - The first name of the person whose registration form is being archived.
|
||||||
*/
|
*/
|
||||||
const archiveFicheInscription = (id, nom, prenom) => {
|
const archiveFicheInscription = (id, nom, prenom) => {
|
||||||
setPopup({
|
setConfirmPopupMessage(
|
||||||
visible: true,
|
`Attentions ! \nVous êtes sur le point d'archiver le dossier d'inscription de ${nom} ${prenom}\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?`
|
||||||
message: `Attentions ! \nVous êtes sur le point d'archiver le dossier d'inscription de ${nom} ${prenom}\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?`,
|
);
|
||||||
onConfirm: () => {
|
setConfirmPopupOnConfirm(() => () => {
|
||||||
archiveRegisterForm(id)
|
archiveRegisterForm(id)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
logger.debug('Success:', data);
|
logger.debug('Success:', data);
|
||||||
setRegistrationForms(
|
setPopupMessage(
|
||||||
registrationForms.filter((fiche) => fiche.id !== id)
|
`Le dossier d'inscription a été correctement archivé`
|
||||||
);
|
);
|
||||||
setReloadFetch(true);
|
setPopupVisible(true);
|
||||||
alert("Le dossier d'inscription a été correctement archivé");
|
setRegistrationForms(
|
||||||
})
|
registrationForms.filter((fiche) => fiche.id !== id)
|
||||||
.catch((error) => {
|
);
|
||||||
logger.error('Error archiving data:', error);
|
setReloadFetch(true);
|
||||||
alert(
|
})
|
||||||
"Erreur lors de l'archivage du dossier d'inscription.\nContactez l'administrateur."
|
.catch((error) => {
|
||||||
);
|
logger.error('Error archiving data:', error);
|
||||||
});
|
setPopupMessage(
|
||||||
},
|
`Erreur lors de l'archivage du dossier d'inscription.\nContactez l'administrateur.`
|
||||||
|
);
|
||||||
|
setPopupVisible(true);
|
||||||
|
});
|
||||||
|
setConfirmPopupVisible(false);
|
||||||
});
|
});
|
||||||
|
setConfirmPopupVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const sendConfirmRegisterForm = (id, nom, prenom) => {
|
const sendConfirmRegisterForm = (id, nom, prenom) => {
|
||||||
setPopup({
|
setConfirmPopupMessage(
|
||||||
visible: true,
|
`Avertissement ! \nVous êtes sur le point d'envoyer un dossier d'inscription à ${nom} ${prenom}\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?`
|
||||||
message: `Avertissement ! \nVous êtes sur le point d'envoyer un dossier d'inscription à ${nom} ${prenom}\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?`,
|
);
|
||||||
onConfirm: () => {
|
setConfirmPopupOnConfirm(() => () => {
|
||||||
sendRegisterForm(id)
|
sendRegisterForm(id)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
logger.debug('Success:', data);
|
logger.debug('Success:', data);
|
||||||
setReloadFetch(true);
|
setPopupMessage(`Le dossier d'inscription a été envoyé avec succès`);
|
||||||
})
|
setPopupVisible(true);
|
||||||
.catch((error) => {
|
setReloadFetch(true);
|
||||||
logger.error('Error fetching data:', error);
|
})
|
||||||
});
|
.catch((error) => {
|
||||||
},
|
logger.error('Error archiving data:', error);
|
||||||
|
setPopupMessage(
|
||||||
|
`Erreur lors de l'envoi du dossier d'inscription.\nContactez l'administrateur.`
|
||||||
|
);
|
||||||
|
setPopupVisible(true);
|
||||||
|
});
|
||||||
|
setConfirmPopupVisible(false);
|
||||||
});
|
});
|
||||||
|
setConfirmPopupVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const affectationClassFormSubmitHandler = (formdata) => {
|
const affectationClassFormSubmitHandler = (formdata) => {
|
||||||
@ -437,25 +483,6 @@ export default function Page({ params: { locale } }) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const refuseRegistrationForm = (id, lastname, firstname, guardianEmail) => {
|
|
||||||
const data = { status: 2, establishment: selectedEstablishmentId };
|
|
||||||
|
|
||||||
setPopup({
|
|
||||||
visible: true,
|
|
||||||
message: `Avertissement ! \nVous êtes sur le point de refuser le dossier d'inscription de ${lastname} ${firstname}\nUne notification va être envoyée à l'adresse ${guardianEmail}\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?`,
|
|
||||||
onConfirm: () => {
|
|
||||||
editRegisterForm(id, data, csrfToken)
|
|
||||||
.then((data) => {
|
|
||||||
logger.debug('Success:', data);
|
|
||||||
setReloadFetch(true);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
logger.error('Error refusing RF:', error);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateStatusAction = (id, newStatus) => {
|
const updateStatusAction = (id, newStatus) => {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`Mise à jour du statut du dossier d'inscription avec l'ID : ${id} vers le statut : ${newStatus}`
|
`Mise à jour du statut du dossier d'inscription avec l'ID : ${id} vers le statut : ${newStatus}`
|
||||||
@ -551,6 +578,7 @@ export default function Page({ params: { locale } }) {
|
|||||||
establishment: selectedEstablishmentId,
|
establishment: selectedEstablishmentId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
createRegisterForm(data, csrfToken)
|
createRegisterForm(data, csrfToken)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
// Cloner les schoolFileTemplates pour chaque templateMaster du fileGroup
|
// Cloner les schoolFileTemplates pour chaque templateMaster du fileGroup
|
||||||
@ -582,6 +610,7 @@ export default function Page({ params: { locale } }) {
|
|||||||
logger.debug('Template enregistré avec succès:', response);
|
logger.debug('Template enregistré avec succès:', response);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
setIsLoading(false);
|
||||||
logger.error(
|
logger.error(
|
||||||
"Erreur lors de l'enregistrement du template:",
|
"Erreur lors de l'enregistrement du template:",
|
||||||
error
|
error
|
||||||
@ -589,6 +618,7 @@ export default function Page({ params: { locale } }) {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
setIsLoading(false);
|
||||||
logger.error('Error during cloning or sending:', error);
|
logger.error('Error during cloning or sending:', error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -608,6 +638,7 @@ export default function Page({ params: { locale } }) {
|
|||||||
logger.debug('Parent template enregistré avec succès:', response);
|
logger.debug('Parent template enregistré avec succès:', response);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
setIsLoading(false);
|
||||||
logger.error(
|
logger.error(
|
||||||
"Erreur lors de l'enregistrement du parent template:",
|
"Erreur lors de l'enregistrement du parent template:",
|
||||||
error
|
error
|
||||||
@ -634,12 +665,15 @@ export default function Page({ params: { locale } }) {
|
|||||||
closeModal(); // Appeler closeModal ici après que tout soit terminé
|
closeModal(); // Appeler closeModal ici après que tout soit terminé
|
||||||
// Forcer le rechargement complet des données
|
// Forcer le rechargement complet des données
|
||||||
setReloadFetch(true);
|
setReloadFetch(true);
|
||||||
|
setIsLoading(false);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
setIsLoading(false);
|
||||||
logger.error('Error during cloning or sending:', error);
|
logger.error('Error during cloning or sending:', error);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
setIsLoading(false);
|
||||||
logger.error('Error:', error);
|
logger.error('Error:', error);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -810,6 +844,24 @@ export default function Page({ params: { locale } }) {
|
|||||||
onClick: () => openFilesModal(row),
|
onClick: () => openFilesModal(row),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
8: [
|
||||||
|
{
|
||||||
|
icon: (
|
||||||
|
<span title="Voir les fichiers">
|
||||||
|
<FileText className="w-5 h-5 text-cyan-500 hover:text-cyan-700" />
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
onClick: () => openFilesModal(row),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: (
|
||||||
|
<span title="Uploader un mandat SEPA">
|
||||||
|
<Upload className="w-5 h-5 text-emerald-500 hover:text-emerald-700" />
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
onClick: () => openSepaUploadModal(row),
|
||||||
|
},
|
||||||
|
],
|
||||||
default: [
|
default: [
|
||||||
{
|
{
|
||||||
icon: (
|
icon: (
|
||||||
@ -879,37 +931,6 @@ export default function Page({ params: { locale } }) {
|
|||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: t('files'),
|
|
||||||
transform: (row) => (
|
|
||||||
<ul>
|
|
||||||
{row.registration_file && (
|
|
||||||
<li className="flex justify-center items-center gap-2">
|
|
||||||
<FileText size={16} />
|
|
||||||
<a
|
|
||||||
href={`${BASE_URL}${row.registration_file}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
{row.registration_file?.split('/').pop()}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
)}
|
|
||||||
{row.sepa_file && (
|
|
||||||
<li className="flex justify-center items-center gap-2">
|
|
||||||
<FileText size={16} />
|
|
||||||
<a
|
|
||||||
href={`${BASE_URL}${row.sepa_file}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
{row.sepa_file?.split('/').pop()}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'Actions',
|
name: 'Actions',
|
||||||
transform: (row) => (
|
transform: (row) => (
|
||||||
@ -1116,13 +1137,16 @@ export default function Page({ params: { locale } }) {
|
|||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<Popup
|
<Popup
|
||||||
visible={popup.visible}
|
visible={popupVisible}
|
||||||
message={popup.message}
|
message={popupMessage}
|
||||||
onConfirm={() => {
|
onConfirm={() => setPopupVisible(false)}
|
||||||
popup.onConfirm();
|
uniqueConfirmButton={true}
|
||||||
setPopup({ ...popup, visible: false });
|
/>
|
||||||
}}
|
<Popup
|
||||||
onCancel={() => setPopup({ ...popup, visible: false })}
|
visible={confirmPopupVisible}
|
||||||
|
message={confirmPopupMessage}
|
||||||
|
onConfirm={confirmPopupOnConfirm}
|
||||||
|
onCancel={() => setConfirmPopupVisible(false)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
@ -1176,6 +1200,21 @@ export default function Page({ params: { locale } }) {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{isSepaUploadModalOpen && (
|
||||||
|
<Modal
|
||||||
|
isOpen={isSepaUploadModalOpen}
|
||||||
|
setIsOpen={setIsSepaUploadModalOpen}
|
||||||
|
title="Uploader un mandat SEPA"
|
||||||
|
ContentComponent={() => (
|
||||||
|
<FileUpload
|
||||||
|
selectionMessage="Sélectionnez un mandat SEPA à uploader"
|
||||||
|
onFileSelect={(file) =>
|
||||||
|
handleSepaFileUpload(file, selectedRowForUpload)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{isFilesModalOpen && (
|
{isFilesModalOpen && (
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isFilesModalOpen}
|
isOpen={isFilesModalOpen}
|
||||||
|
|||||||
@ -24,15 +24,16 @@ export default function ParentHomePage() {
|
|||||||
const [uploadState, setUploadState] = useState('off'); // État "on" ou "off" pour l'affichage du composant
|
const [uploadState, setUploadState] = useState('off'); // État "on" ou "off" pour l'affichage du composant
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const csrfToken = useCsrfToken();
|
const csrfToken = useCsrfToken();
|
||||||
|
const [reloadFetch, setReloadFetch] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const userIdFromSession = user.user_id;
|
const userIdFromSession = user.user_id;
|
||||||
setUserId(userIdFromSession);
|
setUserId(userIdFromSession);
|
||||||
console.log(selectedEstablishmentId);
|
|
||||||
fetchChildren(userIdFromSession, selectedEstablishmentId).then((data) => {
|
fetchChildren(userIdFromSession, selectedEstablishmentId).then((data) => {
|
||||||
setChildren(data);
|
setChildren(data);
|
||||||
});
|
});
|
||||||
}, [selectedEstablishmentId]);
|
setReloadFetch(false);
|
||||||
|
}, [selectedEstablishmentId, reloadFetch]);
|
||||||
|
|
||||||
function handleView(eleveId) {
|
function handleView(eleveId) {
|
||||||
logger.debug(`View dossier for student id: ${eleveId}`);
|
logger.debug(`View dossier for student id: ${eleveId}`);
|
||||||
@ -70,7 +71,8 @@ export default function ParentHomePage() {
|
|||||||
sendSEPARegisterForm(uploadingStudentId, formData, csrfToken)
|
sendSEPARegisterForm(uploadingStudentId, formData, csrfToken)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
logger.debug('RF mis à jour avec succès:', response);
|
logger.debug('RF mis à jour avec succès:', response);
|
||||||
// Logique supplémentaire après la mise à jour (par exemple, redirection ou notification)
|
setReloadFetch(true);
|
||||||
|
setUploadState('off');
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
logger.error('Erreur lors de la mise à jour du RF:', error);
|
logger.error('Erreur lors de la mise à jour du RF:', error);
|
||||||
@ -118,7 +120,7 @@ export default function ParentHomePage() {
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{row.status === 3 && (
|
{(row.status === 3 || row.status === 8) && (
|
||||||
<button
|
<button
|
||||||
className="text-purple-500 hover:text-purple-700"
|
className="text-purple-500 hover:text-purple-700"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import StudentInfoForm from '@/components/Inscription/StudentInfoForm';
|
|||||||
import ResponsableInputFields from '@/components/Inscription/ResponsableInputFields';
|
import ResponsableInputFields from '@/components/Inscription/ResponsableInputFields';
|
||||||
import PaymentMethodSelector from '@/components/Inscription/PaymentMethodSelector';
|
import PaymentMethodSelector from '@/components/Inscription/PaymentMethodSelector';
|
||||||
import ProgressStep from '@/components/ProgressStep';
|
import ProgressStep from '@/components/ProgressStep';
|
||||||
import { CheckCircle, Loader2 } from 'lucide-react';
|
import { CheckCircle, Hourglass } from 'lucide-react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Composant de formulaire d'inscription partagé
|
* Composant de formulaire d'inscription partagé
|
||||||
@ -315,16 +315,21 @@ export default function InscriptionFormShared({
|
|||||||
// Soumission du formulaire
|
// Soumission du formulaire
|
||||||
const handleSubmit = (e) => {
|
const handleSubmit = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
// Vérifier si le mode de paiement sélectionné est un prélèvement SEPA
|
||||||
|
const isSepaPayment =
|
||||||
|
formData.registration_payment === '1' || formData.tuition_payment === '1';
|
||||||
const data = {
|
const data = {
|
||||||
student: {
|
student: {
|
||||||
...formData,
|
...formData,
|
||||||
guardians,
|
guardians,
|
||||||
},
|
},
|
||||||
establishment: selectedEstablishmentId,
|
establishment: selectedEstablishmentId,
|
||||||
status: 3,
|
status: isSepaPayment ? 8 : 3,
|
||||||
tuition_payment: formData.tuition_payment,
|
tuition_payment: formData.tuition_payment,
|
||||||
registration_payment: formData.registration_payment,
|
registration_payment: formData.registration_payment,
|
||||||
};
|
};
|
||||||
|
|
||||||
onSubmit(data);
|
onSubmit(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -448,7 +453,7 @@ export default function InscriptionFormShared({
|
|||||||
{template.file !== null ? (
|
{template.file !== null ? (
|
||||||
<CheckCircle className="w-5 h-5 text-green-600" />
|
<CheckCircle className="w-5 h-5 text-green-600" />
|
||||||
) : (
|
) : (
|
||||||
<Loader2 className="w-5 h-5 text-gray-600" />
|
<Hourglass className="w-5 h-5 text-gray-600" />
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
{template.name || 'Document sans nom'}
|
{template.name || 'Document sans nom'}
|
||||||
|
|||||||
@ -13,7 +13,6 @@ export default function PaymentMethodSelector({
|
|||||||
const isValid = !Object.keys(formData).some(
|
const isValid = !Object.keys(formData).some(
|
||||||
(field) => getLocalError(field) !== ''
|
(field) => getLocalError(field) !== ''
|
||||||
);
|
);
|
||||||
console.log(isValid);
|
|
||||||
setIsPageValid(isValid);
|
setIsPageValid(isValid);
|
||||||
}, [formData, setIsPageValid]);
|
}, [formData, setIsPageValid]);
|
||||||
|
|
||||||
|
|||||||
@ -1,18 +1,9 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import {
|
import { Trash2, ToggleLeft, ToggleRight, Info, XCircle } from 'lucide-react';
|
||||||
Trash2,
|
|
||||||
Eye,
|
|
||||||
EyeOff,
|
|
||||||
ToggleLeft,
|
|
||||||
ToggleRight,
|
|
||||||
Info,
|
|
||||||
XCircle,
|
|
||||||
} from 'lucide-react';
|
|
||||||
import Table from '@/components/Table';
|
import Table from '@/components/Table';
|
||||||
import Popup from '@/components/Popup';
|
import Popup from '@/components/Popup';
|
||||||
import StatusLabel from '@/components/StatusLabel';
|
import StatusLabel from '@/components/StatusLabel';
|
||||||
import SpecialityItem from '@/components/Structure/Configuration/SpecialityItem';
|
import SpecialityItem from '@/components/Structure/Configuration/SpecialityItem';
|
||||||
import Tooltip from '@/components/Tooltip';
|
|
||||||
|
|
||||||
const roleTypeToLabel = (roleType) => {
|
const roleTypeToLabel = (roleType) => {
|
||||||
switch (roleType) {
|
switch (roleType) {
|
||||||
|
|||||||
@ -11,6 +11,7 @@ const StatusLabel = ({ status, onChange, showDropdown = true, parent }) => {
|
|||||||
{ value: 2, label: 'Nouveau' },
|
{ value: 2, label: 'Nouveau' },
|
||||||
{ value: 3, label: 'En validation' },
|
{ value: 3, label: 'En validation' },
|
||||||
{ value: 7, label: 'SEPA reçu' },
|
{ value: 7, label: 'SEPA reçu' },
|
||||||
|
{ value: 8, label: 'En validation' },
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
{ value: 1, label: 'A envoyer' },
|
{ value: 1, label: 'A envoyer' },
|
||||||
@ -20,6 +21,7 @@ const StatusLabel = ({ status, onChange, showDropdown = true, parent }) => {
|
|||||||
{ value: 5, label: 'Validé' },
|
{ value: 5, label: 'Validé' },
|
||||||
{ value: 6, label: 'Archivé' },
|
{ value: 6, label: 'Archivé' },
|
||||||
{ value: 7, label: 'En attente SEPA' },
|
{ value: 7, label: 'En attente SEPA' },
|
||||||
|
{ value: 8, label: 'SEPA à envoyer' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const currentStatus = statusOptions.find((option) => option.value === status);
|
const currentStatus = statusOptions.find((option) => option.value === status);
|
||||||
@ -29,7 +31,7 @@ const StatusLabel = ({ status, onChange, showDropdown = true, parent }) => {
|
|||||||
if (parent) {
|
if (parent) {
|
||||||
return (
|
return (
|
||||||
(status === 2 && 'bg-orange-50 text-orange-600') ||
|
(status === 2 && 'bg-orange-50 text-orange-600') ||
|
||||||
(status === 3 && 'bg-purple-50 text-purple-600') ||
|
((status === 3 || status === 8) && 'bg-purple-50 text-purple-600') ||
|
||||||
(status === 7 && 'bg-yellow-50 text-yellow-600')
|
(status === 7 && 'bg-yellow-50 text-yellow-600')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -40,7 +42,8 @@ const StatusLabel = ({ status, onChange, showDropdown = true, parent }) => {
|
|||||||
(status === 4 && 'bg-red-50 text-red-600') ||
|
(status === 4 && 'bg-red-50 text-red-600') ||
|
||||||
(status === 5 && 'bg-green-50 text-green-600') ||
|
(status === 5 && 'bg-green-50 text-green-600') ||
|
||||||
(status === 6 && 'bg-red-50 text-red-600') ||
|
(status === 6 && 'bg-red-50 text-red-600') ||
|
||||||
(status === 7 && 'bg-yellow-50 text-yellow-600')
|
(status === 7 && 'bg-yellow-50 text-yellow-600') ||
|
||||||
|
(status === 8 && 'bg-cyan-50 text-cyan-600')
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -163,7 +163,7 @@ export default function ParentFilesSection({
|
|||||||
return (
|
return (
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
name="groups"
|
name="groups"
|
||||||
label="Sélection de groupes de fichiers"
|
label="Sélection du(des) dossier(s) d'inscription"
|
||||||
options={groups}
|
options={groups}
|
||||||
selectedOptions={selectedGroups}
|
selectedOptions={selectedGroups}
|
||||||
onChange={handleGroupChange}
|
onChange={handleGroupChange}
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {*} phoneString au format E.164
|
* @param {*} phoneString au format E.164
|
||||||
* @param {*} toFormat L=Country code, X=Number Format="LX XX XX XX XX"
|
* @param {*} toFormat L=Country code, X=Number Format="L X XX XX XX XX"
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function formatPhoneNumber(phoneString, toFormat = 'LX XX XX XX XX') {
|
export function formatPhoneNumber(phoneString, toFormat = 'L X XX XX XX XX') {
|
||||||
if (!phoneString) return;
|
if (!phoneString) return;
|
||||||
// Vérifier si le numéro est au format international
|
// Vérifier si le numéro est au format international
|
||||||
if (!validateE164PhoneNumber(phoneString)) {
|
if (!validateE164PhoneNumber(phoneString)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user