'use client';
import React, { useState, useRef, useEffect } from 'react';
import { User, Mail } from 'lucide-react';
import InputTextIcon from '@/components/InputTextIcon';
import ToggleSwitch from '@/components/ToggleSwitch';
import Button from '@/components/Button';
import Table from '@/components/Table';
import FeesSection from '@/components/Structure/Tarification/FeesSection';
import DiscountsSection from '@/components/Structure/Tarification/DiscountsSection';
import SectionTitle from '@/components/SectionTitle';
import InputPhone from '@/components/InputPhone';
import CheckBox from '@/components/CheckBox';
import RadioList from '@/components/RadioList';
import SelectChoice from '@/components/SelectChoice';
import Loader from '@/components/Loader';
import { getCurrentSchoolYear, getNextSchoolYear } from '@/utils/Date';
import logger from '@/utils/logger';
import { levels, genders } from '@/utils/constants';
import { useEstablishment } from '@/context/EstablishmentContext';
import { useSearchParams, useRouter } from 'next/navigation';
import {
fetchRegisterForm,
fetchStudents,
createRegisterForm,
editRegisterForm,
} from '@/app/actions/subscriptionAction';
import {
fetchRegistrationDiscounts,
fetchTuitionDiscounts,
fetchRegistrationFees,
fetchTuitionFees,
} from '@/app/actions/schoolAction';
import {
fetchRegistrationFileGroups,
fetchRegistrationSchoolFileMasters,
fetchRegistrationParentFileMasters,
cloneTemplate,
createRegistrationSchoolFileTemplate,
createRegistrationParentFileTemplate,
} from '@/app/actions/registerFileGroupAction';
import { fetchProfiles } from '@/app/actions/authAction';
import { useClasses } from '@/context/ClassesContext';
import { useCsrfToken } from '@/context/CsrfContext';
import { FE_ADMIN_SUBSCRIPTIONS_URL, BASE_URL } from '@/utils/Url';
import { useNotification } from '@/context/NotificationContext';
export default function CreateSubscriptionPage() {
const [formData, setFormData] = useState({
studentLastName: '',
studentFirstName: '',
studentLevel: '',
studentGender: '',
guardianLastName: '',
guardianFirstName: '',
guardianEmail: '',
guardianPhone: '',
guardianProfileRole: '',
selectedGuardians: [],
associatedGuardians: [],
autoMail: false,
selectedRegistrationDiscounts: [],
selectedRegistrationFees: [],
selectedTuitionDiscounts: [],
selectedTuitionFees: [],
selectedFileGroup: null,
schoolYear: getCurrentSchoolYear(),
});
const searchParams = useSearchParams();
const { showNotification } = useNotification();
// Si l'ID est valorisé, alors on est en mode édition
const registerFormID = searchParams.get('id');
const registerFormMoment = searchParams.get('school_year');
const [students, setStudents] = useState([]);
const [registrationDiscounts, setRegistrationDiscounts] = useState([]);
const [tuitionDiscounts, setTuitionDiscounts] = useState([]);
const [registrationFees, setRegistrationFees] = useState([]);
const [tuitionFees, setTuitionFees] = useState([]);
const [groups, setGroups] = useState([]);
const [schoolFileMasters, setSchoolFileMasters] = useState([]);
const [parentFileMasters, setParentFileMasters] = useState([]);
const [profiles, setProfiles] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [existingGuardians, setExistingGuardians] = useState([]);
const [totalRegistrationAmount, setTotalRegistrationAmount] = useState(0);
const [totalTuitionAmount, setTotalTuitionAmount] = useState(0);
const [selectedStudent, setSelectedEleve] = useState(null);
const [isNewResponsable, setIsNewResponsable] = useState(true);
const [initialGuardianEmail, setInitialGuardianEmail] = useState('');
const { getNiveauLabel } = useClasses();
const formDataRef = useRef(formData);
const { selectedEstablishmentId, apiDocuseal } = useEstablishment();
const csrfToken = useCsrfToken();
const router = useRouter();
const isSubmitDisabled = () => {
// Vérifie si les champs requis sont remplis
const requiredFields = [
'schoolYear',
'studentLastName',
'studentFirstName',
];
const hasErrors = requiredFields.some(
(field) => getLocalError(field) !== ''
);
// Vérifie si un groupe de fichiers est sélectionné
const isFileGroupSelected = formData.selectedFileGroup !== null;
// Vérifie si au moins un frais de scolarité est sélectionné
const hasSelectedTuitionFees = formData.selectedTuitionFees.length > 0;
// Vérifie les conditions spécifiques pour le responsable
const isGuardianValid = isNewResponsable
? getLocalError('guardianEmail') === ''
: formData.selectedGuardians.length > 0;
// Retourne true si une des conditions n'est pas remplie
return (
hasErrors ||
!isFileGroupSelected ||
!hasSelectedTuitionFees ||
!isGuardianValid
);
};
const getLocalError = (field) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (field === 'guardianEmail') {
if (!formData.guardianEmail || formData.guardianEmail.trim() === '') {
return 'Champs requis';
}
if (!emailRegex.test(formData.guardianEmail)) {
return 'Email invalide';
}
// Vérifiez si l'email existe déjà, mais ne mettez pas à jour `formData` ici
const existingGuardian = students
.flatMap((student) => student.guardians)
.find(
(guardian) =>
guardian.associated_profile_email === formData.guardianEmail
);
if (existingGuardian) {
return ''; // Pas d'erreur, mais l'email existe déjà
}
}
if (
// Student Form
(field === 'studentLastName' &&
(!formData.studentLastName ||
formData.studentLastName.trim() === '')) ||
(field === 'studentFirstName' &&
(!formData.studentFirstName ||
formData.studentFirstName.trim() === '')) ||
(field === 'schoolYear' &&
(!formData.schoolYear || formData.schoolYear.trim() === ''))
) {
return 'Champs requis';
}
return '';
};
const requestErrorHandler = (err) => {
logger.error('Error fetching data:', err);
//setErrors(err);
};
useEffect(() => {
formDataRef.current = formData;
}, [formData]);
useEffect(() => {
if (!formData.guardianEmail) {
// Si l'email est vide, réinitialiser existingProfileId et existingProfileInSchool
setFormData((prevData) => ({
...prevData,
isExistingParentProfile: false,
existingProfileId: null,
existingProfileInSchool: false,
associatedGuardians: [],
}));
return;
}
// Vérifiez si le profil existe dans la liste des profils
const existingProfile = profiles.find(
(profile) => profile.email === formData.guardianEmail
);
if (existingProfile) {
// Vérifiez si le profil parent est associé à l'établissement sélectionné
const isInSchool = existingProfile.roles.some(
(role) =>
role.role_type === 2 && role.establishment === selectedEstablishmentId
);
// Récupérer l'ID de l'id_associated_person si applicable
const associatedPersonId = existingProfile.roles.find(
(role) =>
role.role_type === 2 && role.establishment === selectedEstablishmentId
)?.id_associated_person;
// Mettre à jour les variables en fonction des résultats
setFormData((prevData) => ({
...prevData,
isExistingParentProfile: true,
existingProfileId: existingProfile.id, // Récupérer l'ID du profil associé
existingProfileInSchool: isInSchool, // Vérifie si le profil est dans l'établissement
guardianEmail: existingProfile.email || '',
associatedGuardians: associatedPersonId
? [associatedPersonId] // Ajouter l'ID de l'id_associated_person si trouvé
: [],
}));
} else {
// Si aucun profil avec cet email n'existe, réinitialiser les champs
setFormData((prevData) => ({
...prevData,
isExistingParentProfile: false,
existingProfileId: null,
existingProfileInSchool: false,
associatedGuardians: [],
}));
}
}, [formData.guardianEmail, profiles, selectedEstablishmentId]);
useEffect(() => {
fetchProfiles()
.then((data) => {
setProfiles(data);
})
.catch(requestErrorHandler);
if (selectedEstablishmentId) {
if (registerFormID) {
fetchRegisterForm(registerFormID)
.then((data) => {
setFormData((prevData) => ({
...prevData,
studentLastName: data?.student?.last_name || '',
studentFirstName: data?.student?.first_name || '',
studentLevel: data?.student?.level || '',
studentGender: data?.student?.gender || '',
guardianLastName: data?.student?.guardians[0]?.last_name || '',
guardianFirstName: data?.student?.guardians[0]?.first_name || '',
guardianEmail:
data?.student?.guardians[0]?.associated_profile_email || '',
guardianPhone: data?.student?.guardians[0]?.phone || '',
selectedFileGroup: data?.fileGroup || '',
schoolYear: data?.school_year || '',
guardianProfileRole:
data?.student?.guardians[0]?.profile_role || '',
}));
setIsNewResponsable(
!Array.isArray(data?.student?.guardians) ||
data.student.guardians.length <= 1
);
// Définir l'email initial
setInitialGuardianEmail(
data?.student?.guardians[0]?.associated_profile_email || ''
);
})
.catch(requestErrorHandler);
}
fetchStudents(selectedEstablishmentId)
.then((studentsData) => {
setStudents(studentsData);
})
.catch(requestErrorHandler);
fetchRegistrationDiscounts(selectedEstablishmentId)
.then((data) => {
setRegistrationDiscounts(data);
})
.catch(requestErrorHandler);
fetchTuitionDiscounts(selectedEstablishmentId)
.then((data) => {
setTuitionDiscounts(data);
})
.catch(requestErrorHandler);
fetchRegistrationFees(selectedEstablishmentId)
.then((data) => {
setRegistrationFees(data);
// Sélectionner par défaut les frais d'inscription
setFormData((prevData) => ({
...prevData,
selectedRegistrationFees: data.map((fee) => fee.id),
}));
})
.catch(requestErrorHandler);
fetchTuitionFees(selectedEstablishmentId)
.then((data) => {
setTuitionFees(data);
})
.catch(requestErrorHandler);
fetchRegistrationFileGroups(selectedEstablishmentId)
.then((data) => {
setGroups(data);
})
.catch(requestErrorHandler);
fetchRegistrationSchoolFileMasters()
.then((data) => {
setSchoolFileMasters(data);
})
.catch(requestErrorHandler);
fetchRegistrationParentFileMasters()
.then((data) => {
setParentFileMasters(data);
})
.catch(requestErrorHandler);
}
}, [selectedEstablishmentId]);
useEffect(() => {
if (registrationFees.length > 0) {
const defaultSelectedFees = registrationFees.map((fee) => fee.id);
const totalAmount = calculateFinalRegistrationAmount(
defaultSelectedFees,
formData.selectedRegistrationDiscounts
);
setTotalRegistrationAmount(totalAmount);
}
}, [registrationFees]);
const handleChange = (e) => {
const { name, value } = e.target;
setFormData((prevState) => ({
...prevState,
[name]: value,
}));
};
const resetGuardianFields = () => {
setFormData((prevData) => ({
...prevData,
guardianLastName: '',
guardianFirstName: '',
guardianEmail: '',
guardianPhone: '',
selectedGuardians: [],
}));
};
// Gestion du changement du toggle
const handleToggleChange = (e) => {
const isChecked = e.target.checked;
setIsNewResponsable(isChecked);
if (isChecked) {
// Réinitialiser les champs si on passe à "Nouveau responsable"
resetGuardianFields();
}
};
const submit = () => {
logger.debug('formData:', formDataRef.current);
const selectedRegistrationFeesIds =
formDataRef.current.selectedRegistrationFees.map((feeId) => feeId);
const selectedRegistrationDiscountsIds =
formDataRef.current.selectedRegistrationDiscounts.map(
(discountId) => discountId
);
const selectedTuitionFeesIds = formDataRef.current.selectedTuitionFees.map(
(feeId) => feeId
);
const selectedTuitionDiscountsIds =
formDataRef.current.selectedTuitionDiscounts.map(
(discountId) => discountId
);
const selectedFileGroup = formDataRef.current.selectedFileGroup;
const allFeesIds = [
...selectedRegistrationFeesIds,
...selectedTuitionFeesIds,
];
const allDiscountsIds = [
...selectedRegistrationDiscountsIds,
...selectedTuitionDiscountsIds,
];
// Vérifiez si le profil existe dans la liste des profils
const existingProfile = profiles.find(
(profile) => profile.id === formDataRef.current.existingProfileId
);
const guardians = (() => {
if (formDataRef.current.selectedGuardians.length > 0) {
// Cas 3 : Des guardians sont sélectionnés
logger.debug('Cas 3 : Des guardians sont sélectionnés');
return formDataRef.current.selectedGuardians.map((guardianId) => ({
id: guardianId,
}));
} else if (formDataRef.current.isExistingParentProfile) {
if (initialGuardianEmail !== existingProfile?.email) {
// Cas 2 : Profil existant différent de l'ancien
logger.debug(
"Cas 2 : Profil existant différent de l'ancien, mise à jour du profil",
{
existingProfile,
guardianEmail: formDataRef.current.guardianEmail,
}
);
return [
{
profile_role_data: {
establishment: selectedEstablishmentId,
role_type: 2,
is_active: true,
profile: formDataRef.current.existingProfileId,
},
last_name: formDataRef.current.guardianLastName,
first_name: formDataRef.current.guardianFirstName,
phone: formDataRef.current.guardianPhone,
},
];
} else {
// Cas 4 : Profil existant avec le même email
logger.debug('Cas 4 : Profil existant avec le même email', {
existingProfile,
});
return [
{
profile_role_data: {
establishment: selectedEstablishmentId,
role_type: 2,
is_active: true,
profile: formDataRef.current.existingProfileId,
},
last_name: formDataRef.current.guardianLastName,
first_name: formDataRef.current.guardianFirstName,
phone: formDataRef.current.guardianPhone,
},
];
}
} else {
// Cas 1 : Profil inexistant
logger.debug("Cas 1 : Profil inexistant, création d'un nouveau profil");
return [
{
profile_role_data: {
establishment: selectedEstablishmentId,
role_type: 2,
is_active: false,
profile_data: {
email: formDataRef.current.guardianEmail,
password: 'Provisoire01!',
username: formDataRef.current.guardianEmail,
},
},
last_name: formDataRef.current.guardianLastName,
first_name: formDataRef.current.guardianFirstName,
birth_date: formDataRef.current.guardianBirthDate,
phone: formDataRef.current.guardianPhone,
},
];
}
})();
const data = {
student: {
last_name: formDataRef.current.studentLastName,
first_name: formDataRef.current.studentFirstName,
...(formDataRef.current.studentLevel && {
level: formDataRef.current.studentLevel,
}),
...(formDataRef.current.studentGender && {
gender: formDataRef.current.studentGender,
}),
guardians,
sibling: [],
},
fees: allFeesIds,
discounts: allDiscountsIds,
fileGroup: selectedFileGroup,
establishment: selectedEstablishmentId,
school_year: formDataRef.current.schoolYear,
};
setIsLoading(true);
if (registerFormID) {
const formData = new FormData();
// Ajouter les données JSON sous forme de chaîne
formData.append('data', JSON.stringify(data));
// Mode édition
editRegisterForm(registerFormID, formData, csrfToken)
.then((response) => {
logger.debug('Dossier mis à jour avec succès:', response);
router.push(FE_ADMIN_SUBSCRIPTIONS_URL);
})
.catch((error) => {
setIsLoading(false);
logger.error('Erreur lors de la mise à jour du dossier:', error);
showNotification(
"Erreur lors de la mise à jour du dossier d'inscription",
'error',
'Erreur',
'ERR_ADM_SUB_06'
);
});
} else {
// Création du dossier d'inscription
createRegisterForm(data, csrfToken)
.then((data) => {
// Clonage des schoolFileTemplates
const masters = schoolFileMasters.filter((file) =>
file.groups.includes(selectedFileGroup)
);
const parentMasters = parentFileMasters.filter((file) =>
file.groups.includes(selectedFileGroup)
);
const clonePromises = masters.map((templateMaster) =>
cloneTemplate(
templateMaster.id,
formDataRef.current.guardianEmail,
templateMaster.is_required,
selectedEstablishmentId,
apiDocuseal
)
.then((clonedDocument) => {
const cloneData = {
name: `${templateMaster.name}_${formDataRef.current.studentFirstName}_${formDataRef.current.studentLastName}`,
slug: clonedDocument.slug,
id: clonedDocument.id,
master: templateMaster.id,
registration_form: data.student.id,
};
return createRegistrationSchoolFileTemplate(
cloneData,
csrfToken
)
.then((response) =>
logger.debug('Template enregistré avec succès:', response)
)
.catch((error) => {
setIsLoading(false);
logger.error(
"Erreur lors de l'enregistrement du template:",
error
);
showNotification(
"Erreur lors de la création du dossier d'inscription",
'error',
'Erreur',
'ERR_ADM_SUB_03'
);
});
})
.catch((error) => {
setIsLoading(false);
logger.error('Error during cloning or sending:', error);
showNotification(
"Erreur lors de la création du dossier d'inscription",
'error',
'Erreur',
'ERR_ADM_SUB_05'
);
})
);
// Clonage des parentFileTemplates
const parentClonePromises = parentMasters.map((parentMaster) => {
const parentTemplateData = {
master: parentMaster.id,
registration_form: data.student.id,
};
return createRegistrationParentFileTemplate(
parentTemplateData,
csrfToken
)
.then((response) =>
logger.debug(
'Parent template enregistré avec succès:',
response
)
)
.catch((error) => {
setIsLoading(false);
logger.error(
"Erreur lors de l'enregistrement du parent template:",
error
);
showNotification(
"Erreur lors de la création du dossier d'inscription",
'error',
'Erreur',
'ERR_ADM_SUB_02'
);
});
});
// Attendre que tous les clones soient créés
Promise.all([...clonePromises, ...parentClonePromises])
.then(() => {
// Redirection après succès
router.push(FE_ADMIN_SUBSCRIPTIONS_URL);
})
.catch((error) => {
setIsLoading(false);
showNotification(
"Erreur lors de la création du dossier d'inscription",
'error',
'Erreur',
'ERR_ADM_SUB_04'
);
logger.error('Error during cloning or sending:', error);
});
})
.catch((error) => {
setIsLoading(false);
showNotification(
"Erreur lors de la création du dossier d'inscription",
'error',
'Erreur',
'ERR_ADM_SUB_01'
);
logger.error('Error during register form creation:', error);
});
}
};
const handleEleveSelection = (student) => {
setSelectedEleve(student);
setExistingGuardians(student.guardians);
};
// Gestion des changements dans la checkbox
const handleGuardianCheckboxChange = (guardian) => {
setFormData((prevData) => {
const isSelected = prevData.selectedGuardians.includes(guardian.id);
const updatedSelectedGuardians = isSelected
? prevData.selectedGuardians.filter((id) => id !== guardian.id)
: [...prevData.selectedGuardians, guardian.id];
return {
...prevData,
selectedGuardians: updatedSelectedGuardians,
guardianEmail: guardian.associated_profile_email,
};
});
};
// Gestion de la désélection d'un élève
const handleEleveDeselection = () => {
setSelectedEleve(null);
setExistingGuardians([]);
resetGuardianFields();
};
const handleRegistrationFeeSelection = (feeId) => {
setFormData((prevData) => {
const selectedRegistrationFees =
prevData.selectedRegistrationFees.includes(feeId)
? prevData.selectedRegistrationFees.filter((id) => id !== feeId)
: [...prevData.selectedRegistrationFees, feeId];
const finalAmount = calculateFinalRegistrationAmount(
selectedRegistrationFees,
prevData.selectedRegistrationDiscounts
);
setTotalRegistrationAmount(finalAmount);
return { ...prevData, selectedRegistrationFees };
});
};
const handleTuitionFeeSelection = (feeId) => {
setFormData((prevData) => {
const selectedTuitionFees = prevData.selectedTuitionFees.includes(feeId)
? prevData.selectedTuitionFees.filter((id) => id !== feeId)
: [...prevData.selectedTuitionFees, feeId];
const finalAmount = calculateFinalTuitionAmount(
selectedTuitionFees,
prevData.selectedTuitionDiscounts
);
setTotalTuitionAmount(finalAmount);
return { ...prevData, selectedTuitionFees };
});
};
const handleRegistrationDiscountSelection = (discountId) => {
setFormData((prevData) => {
const selectedRegistrationDiscounts =
prevData.selectedRegistrationDiscounts.includes(discountId)
? prevData.selectedRegistrationDiscounts.filter(
(id) => id !== discountId
)
: [...prevData.selectedRegistrationDiscounts, discountId];
const finalAmount = calculateFinalRegistrationAmount(
prevData.selectedRegistrationFees,
selectedRegistrationDiscounts
);
setTotalRegistrationAmount(finalAmount);
return { ...prevData, selectedRegistrationDiscounts };
});
};
const handleTuitionDiscountSelection = (discountId) => {
setFormData((prevData) => {
const selectedTuitionDiscounts =
prevData.selectedTuitionDiscounts.includes(discountId)
? prevData.selectedTuitionDiscounts.filter((id) => id !== discountId)
: [...prevData.selectedTuitionDiscounts, discountId];
const finalAmount = calculateFinalTuitionAmount(
prevData.selectedTuitionFees,
selectedTuitionDiscounts
);
setTotalTuitionAmount(finalAmount);
return { ...prevData, selectedTuitionDiscounts };
});
};
const calculateFinalRegistrationAmount = (
selectedRegistrationFees,
selectedRegistrationDiscounts
) => {
const totalFees = selectedRegistrationFees.reduce((sum, feeId) => {
const fee = registrationFees.find((f) => f.id === feeId);
if (fee && !isNaN(parseFloat(fee.base_amount))) {
return sum + parseFloat(fee.base_amount);
}
return sum;
}, 0);
const totalDiscounts = selectedRegistrationDiscounts.reduce(
(sum, discountId) => {
const discount = registrationDiscounts.find((d) => d.id === discountId);
if (discount) {
if (
discount.discount_type === 0 &&
!isNaN(parseFloat(discount.amount))
) {
// Currency
return sum + parseFloat(discount.amount);
} else if (
discount.discount_type === 1 &&
!isNaN(parseFloat(discount.amount))
) {
// Percent
return sum + (totalFees * parseFloat(discount.amount)) / 100;
}
}
return sum;
},
0
);
const finalAmount = totalFees - totalDiscounts;
return finalAmount.toFixed(2);
};
const calculateFinalTuitionAmount = (
selectedTuitionFees,
selectedTuitionDiscounts
) => {
const totalFees = selectedTuitionFees.reduce((sum, feeId) => {
const fee = tuitionFees.find((f) => f.id === feeId);
if (fee && !isNaN(parseFloat(fee.base_amount))) {
return sum + parseFloat(fee.base_amount);
}
return sum;
}, 0);
const totalDiscounts = selectedTuitionDiscounts.reduce(
(sum, discountId) => {
const discount = tuitionDiscounts.find((d) => d.id === discountId);
if (discount) {
if (
discount.discount_type === 0 &&
!isNaN(parseFloat(discount.amount))
) {
// Currency
return sum + parseFloat(discount.amount);
} else if (
discount.discount_type === 1 &&
!isNaN(parseFloat(discount.amount))
) {
// Percent
return sum + (totalFees * parseFloat(discount.amount)) / 100;
}
}
return sum;
},
0
);
const finalAmount = totalFees - totalDiscounts;
return finalAmount.toFixed(2);
};
if (isLoading === true) {
return