From 4f774c18e47fc57e081022a03ea352638e7211d2 Mon Sep 17 00:00:00 2001 From: Luc SORIGNET Date: Fri, 11 Apr 2025 16:59:15 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20Correction=20de=20l'affichage=20des=20nu?= =?UTF-8?q?m=C3=A9ros=20de=20t=C3=A9l=C3=A9phone=20[#41]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../management/commands/init_mock_datas.py | 16 ++-- Back-End/Subscriptions/models.py | 2 +- Front-End/package-lock.json | 94 ++++--------------- Front-End/package.json | 2 +- .../app/[locale]/admin/subscriptions/page.js | 16 ++-- Front-End/src/components/InputPhone.js | 66 ++++++------- .../components/Inscription/InscriptionForm.js | 30 +++--- .../Inscription/PaymentMethodSelector.js | 4 +- .../Inscription/ResponsableInputFields.js | 1 - Front-End/src/components/PhoneLabel.js | 7 ++ Front-End/src/utils/Telephone.js | 61 +++++++----- 11 files changed, 125 insertions(+), 174 deletions(-) create mode 100644 Front-End/src/components/PhoneLabel.js diff --git a/Back-End/School/management/commands/init_mock_datas.py b/Back-End/School/management/commands/init_mock_datas.py index b570cc2..0ca75d1 100644 --- a/Back-End/School/management/commands/init_mock_datas.py +++ b/Back-End/School/management/commands/init_mock_datas.py @@ -11,13 +11,13 @@ from Subscriptions.models import ( ) from Auth.models import Profile, ProfileRole from School.models import ( - FeeType, - Speciality, - Teacher, - SchoolClass, - PaymentMode, - PaymentModeType, - PaymentPlan, + FeeType, + Speciality, + Teacher, + SchoolClass, + PaymentMode, + PaymentModeType, + PaymentPlan, PaymentPlanType, DiscountType ) @@ -371,7 +371,7 @@ class Command(BaseCommand): "first_name": fake.first_name(), "birth_date": fake.date_of_birth().strftime('%Y-%m-%d'), "address": fake.address(), - "phone": fake.phone_number(), + "phone":"+33122334455", "profession": fake.job() } diff --git a/Back-End/Subscriptions/models.py b/Back-End/Subscriptions/models.py index 3b3703a..42de8e4 100644 --- a/Back-End/Subscriptions/models.py +++ b/Back-End/Subscriptions/models.py @@ -231,7 +231,7 @@ class RegistrationTemplate(models.Model): def __str__(self): return self.name - + @staticmethod def get_files_from_rf(register_form_id): """ diff --git a/Front-End/package-lock.json b/Front-End/package-lock.json index d94d8f6..a5bb5e4 100644 --- a/Front-End/package-lock.json +++ b/Front-End/package-lock.json @@ -27,7 +27,7 @@ "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18", - "react-phone-number-input": "^3.4.8", + "react-international-phone": "^4.5.0", "react-tooltip": "^5.28.0" }, "devDependencies": { @@ -2147,11 +2147,6 @@ "node": ">= 0.6" } }, - "node_modules/country-flag-icons": { - "version": "1.5.18", - "resolved": "https://registry.npmjs.org/country-flag-icons/-/country-flag-icons-1.5.18.tgz", - "integrity": "sha512-z+Uzesi8u8IdkViqqbzzbkf3+a7WJpcET5B7sPwTg7GXqPYpVEgNlZ/FC3l8KO4mEf+mNkmzKLppKTN4PlCJEQ==" - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -3628,26 +3623,6 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "node_modules/input-format": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/input-format/-/input-format-0.3.14.tgz", - "integrity": "sha512-gHMrgrbCgmT4uK5Um5eVDUohuV9lcs95ZUUN9Px2Y0VIfjTzT2wF8Q3Z4fwLFm7c5Z2OXCm53FHoovj6SlOKdg==", - "dependencies": { - "prop-types": "^15.8.1" - }, - "peerDependencies": { - "react": ">=18.1.0", - "react-dom": ">=18.1.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -4275,11 +4250,6 @@ "node": ">= 0.8.0" } }, - "node_modules/libphonenumber-js": { - "version": "1.12.6", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.6.tgz", - "integrity": "sha512-PJiS4ETaUfCOFLpmtKzAbqZQjCCKVu2OhTV4SVNNE7c2nu/dACvtCqj4L0i/KWNnIgRv7yrILvBj5Lonv5Ncxw==" - }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -5249,6 +5219,7 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -5366,27 +5337,19 @@ "react": "^18.3.1" } }, + "node_modules/react-international-phone": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/react-international-phone/-/react-international-phone-4.5.0.tgz", + "integrity": "sha512-wjwHv+VfiwM49B5/6El4Z5vZKmf3ILpUeiOCI9X+b0Dq4g5nL8gROcwCdVcTXywxznbDSoxSassBX3i9tPZX6g==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, - "node_modules/react-phone-number-input": { - "version": "3.4.12", - "resolved": "https://registry.npmjs.org/react-phone-number-input/-/react-phone-number-input-3.4.12.tgz", - "integrity": "sha512-Raob77KdtLGm49iC6nuOX9qy6Mg16idkgC7Y1mHmvG2WBYoauHpzxYNlfmFskQKeiztrJIwPhPzBhjFwjenNCA==", - "dependencies": { - "classnames": "^2.5.1", - "country-flag-icons": "^1.5.17", - "input-format": "^0.3.10", - "libphonenumber-js": "^1.11.20", - "prop-types": "^15.8.1" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, "node_modules/react-remove-scroll": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", @@ -8266,11 +8229,6 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==" }, - "country-flag-icons": { - "version": "1.5.18", - "resolved": "https://registry.npmjs.org/country-flag-icons/-/country-flag-icons-1.5.18.tgz", - "integrity": "sha512-z+Uzesi8u8IdkViqqbzzbkf3+a7WJpcET5B7sPwTg7GXqPYpVEgNlZ/FC3l8KO4mEf+mNkmzKLppKTN4PlCJEQ==" - }, "cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -9349,14 +9307,6 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "input-format": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/input-format/-/input-format-0.3.14.tgz", - "integrity": "sha512-gHMrgrbCgmT4uK5Um5eVDUohuV9lcs95ZUUN9Px2Y0VIfjTzT2wF8Q3Z4fwLFm7c5Z2OXCm53FHoovj6SlOKdg==", - "requires": { - "prop-types": "^15.8.1" - } - }, "internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -9795,11 +9745,6 @@ "type-check": "~0.4.0" } }, - "libphonenumber-js": { - "version": "1.12.6", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.6.tgz", - "integrity": "sha512-PJiS4ETaUfCOFLpmtKzAbqZQjCCKVu2OhTV4SVNNE7c2nu/dACvtCqj4L0i/KWNnIgRv7yrILvBj5Lonv5Ncxw==" - }, "lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -10425,6 +10370,7 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -10499,23 +10445,17 @@ "scheduler": "^0.23.2" } }, + "react-international-phone": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/react-international-phone/-/react-international-phone-4.5.0.tgz", + "integrity": "sha512-wjwHv+VfiwM49B5/6El4Z5vZKmf3ILpUeiOCI9X+b0Dq4g5nL8gROcwCdVcTXywxznbDSoxSassBX3i9tPZX6g==", + "requires": {} + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, - "react-phone-number-input": { - "version": "3.4.12", - "resolved": "https://registry.npmjs.org/react-phone-number-input/-/react-phone-number-input-3.4.12.tgz", - "integrity": "sha512-Raob77KdtLGm49iC6nuOX9qy6Mg16idkgC7Y1mHmvG2WBYoauHpzxYNlfmFskQKeiztrJIwPhPzBhjFwjenNCA==", - "requires": { - "classnames": "^2.5.1", - "country-flag-icons": "^1.5.17", - "input-format": "^0.3.10", - "libphonenumber-js": "^1.11.20", - "prop-types": "^15.8.1" - } - }, "react-remove-scroll": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", diff --git a/Front-End/package.json b/Front-End/package.json index e05b435..7e2fe59 100644 --- a/Front-End/package.json +++ b/Front-End/package.json @@ -29,7 +29,7 @@ "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18", - "react-phone-number-input": "^3.4.8", + "react-international-phone": "^4.5.0", "react-tooltip": "^5.28.0" }, "devDependencies": { diff --git a/Front-End/src/app/[locale]/admin/subscriptions/page.js b/Front-End/src/app/[locale]/admin/subscriptions/page.js index 60f8c85..c02ee1a 100644 --- a/Front-End/src/app/[locale]/admin/subscriptions/page.js +++ b/Front-End/src/app/[locale]/admin/subscriptions/page.js @@ -9,7 +9,6 @@ import Popup from '@/components/Popup'; import Loader from '@/components/Loader'; import AlertWithModal from '@/components/AlertWithModal'; import DropdownMenu from "@/components/DropdownMenu"; -import { formatPhoneNumber } from '@/utils/Telephone'; import { MoreVertical, Send, Edit, Archive, FileText, CircleCheck, Plus, XCircle } from 'lucide-react'; import Modal from '@/components/Modal'; import InscriptionForm from '@/components/Inscription/InscriptionForm' @@ -50,6 +49,7 @@ import { import DjangoCSRFToken from '@/components/DjangoCSRFToken' import { useCsrfToken } from '@/context/CsrfContext'; import logger from '@/utils/logger'; +import { PhoneLabel } from '@/components/PhoneLabel'; export default function Page({ params: { locale } }) { const t = useTranslations('subscriptions'); @@ -103,7 +103,7 @@ export default function Page({ params: { locale } }) { setIsOpenAddGuardian(true); setStudent(eleveSelected); }; - + const handleCloseAddGuardian = () => { setIsOpenAddGuardian(false); }; @@ -285,7 +285,7 @@ useEffect(() => { fetchRegistrationTemplateMaster() .then((data)=> {setTemplateMasters(data)}) .catch((err)=>{ err = err.message; logger.debug(err);}); - + setIsLoading(false); setReloadFetch(false); } @@ -383,7 +383,7 @@ useEffect(()=>{ }; const updateStatusAction = (id, newStatus) => { - logger.debug(`Mise à jour du statut du dossier d'inscription avec l'ID : ${id} vers le statut : ${newStatus}`); + logger.debug(`Mise à jour du statut du dossier d'inscription avec l'ID : ${id} vers le statut : ${newStatus}`); }; const handleSearchChange = (event) => { @@ -427,7 +427,7 @@ useEffect(()=>{ profession: updatedData.guardianProfession }]; } - + // Si aucun profil existant n'est trouvé, créer un nouveau profil return [{ profile_role_data: { @@ -531,7 +531,7 @@ useEffect(()=>{ profession: updatedData.guardianProfession }]; } - + // Si aucun profil existant n'est trouvé, créer un nouveau profil return [{ profile_role_data: { @@ -614,7 +614,7 @@ useEffect(()=>{ }, ], }; - + // Combine actions for the specific status and default actions return [...(actions[row.status] || []), ...(row.status !== 6 ? actions.default : [])]; }; @@ -641,7 +641,7 @@ const columns = [ ) ) }, - { name: t('phone'), transform: (row) => formatPhoneNumber(row.student.guardians[0]?.phone) }, + { name: t('phone'), transform: (row) => }, { name: t('lastUpdateDate'), transform: (row) => row.formatted_last_update}, { name: t('registrationFileStatus'), transform: (row) => (
diff --git a/Front-End/src/components/InputPhone.js b/Front-End/src/components/InputPhone.js index f083008..aa70493 100644 --- a/Front-End/src/components/InputPhone.js +++ b/Front-End/src/components/InputPhone.js @@ -1,40 +1,32 @@ -import { useEffect, useRef } from 'react'; - - -export default function InputPhone({ name, label, value, onChange, errorMsg, placeholder, className, required }) { - const inputRef = useRef(null); - - useEffect(() => { - if (inputRef.current) { - inputRef.current.focus(); - } - }, []); - - const handleChange = (e) => { - const newValue = e.target.value; - onChange(newValue); - }; +import React from 'react'; +import { PhoneInput } from 'react-international-phone'; +import 'react-international-phone/style.css'; +export default function InputPhone({ name, label, value, onChange, errorMsg, className, required }) { return ( - <> -
- -
- -
- {errorMsg &&

{errorMsg}

} -
- - ) +
+ + + onChange(phone)} + inputProps={{ + name: name, + required: required, + }} + className="!w-full mt-1 !h-[38px]" + containerClassName="!w-full !h-[36px] !flex !items-center !rounded-md" + inputClassName={`flex-1 px-3 py-2 block w-full sm:text-sm border-none focus:ring-0 outline-none !rounded-r-md !outline-none items-center !border !border-gray-200 rounded-md ${errorMsg ? 'border-red-500' : ''} hover:border-gray-400 focus-within:border-gray-500` } + buttonClassName="!h-[38px] !flex !items-center !justify-center !rounded-l-md !border border-gray-200 !border-r-0" + /> + + {errorMsg && ( +

{errorMsg}

+ )} +
+ ); } diff --git a/Front-End/src/components/Inscription/InscriptionForm.js b/Front-End/src/components/Inscription/InscriptionForm.js index 6dc841b..d41f5d7 100644 --- a/Front-End/src/components/Inscription/InscriptionForm.js +++ b/Front-End/src/components/Inscription/InscriptionForm.js @@ -10,6 +10,8 @@ import SectionTitle from '@/components/SectionTitle'; import ProgressStep from '@/components/ProgressStep'; import logger from '@/utils/logger'; import Popup from '@/components/Popup'; +import InputPhone from '../InputPhone'; +import { PhoneLabel } from '../PhoneLabel'; const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, registrationFees, tuitionFees, profiles, onSubmit, currentStep, groups, showOnlyStep2 = false }) => { const [formData, setFormData] = useState(() => { @@ -66,7 +68,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r const isStep1Valid = formData.studentLastName && formData.studentFirstName; const isStep2Valid = ( - formData.selectedGuardians.length > 0 || + formData.selectedGuardians.length > 0 || (!formData.emailError && formData.guardianEmail.length > 0 && filteredStudents.length === 0) ); const isStep3Valid = formData.selectedRegistrationFees?.length > 0; @@ -127,7 +129,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r const validateAndSubmit = async () => { const existingProfile = profiles.find(profile => profile.email === formData.guardianEmail); - + if (existingProfile) { console.log('existingProfile : ', existingProfile); await setFormData((prevData) => ({ @@ -137,7 +139,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r existingProfileId: existingProfile.id, })); } - + // Utiliser la dernière version de formData via formDataRef logger.debug('Submitting form data:', formDataRef.current); onSubmit(formDataRef.current); @@ -146,7 +148,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r const nextStep = async () => { if (step === 2) { const existingProfile = profiles.find(profile => profile.email === formData.guardianEmail); - + if (existingProfile) { console.log('existingProfile : ', existingProfile); await setFormData((prevData) => ({ @@ -157,7 +159,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r })); } } - + if (!showOnlyStep2 && step < steps.length) { setStep(step + 1); } @@ -184,16 +186,16 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r const selectedGuardians = isSelected ? prevData.selectedGuardians.filter(id => id !== guardianId) // Retirer le guardian si déjà sélectionné : [...prevData.selectedGuardians, guardianId]; // Ajouter le guardian s'il n'est pas sélectionné - + // Mettre à jour l'email uniquement si un guardian est sélectionné const updatedGuardianEmail = isSelected ? '' : guardianEmail; - + // Si aucun guardian n'est sélectionné, réinitialiser le tableau des élèves if (selectedGuardians.length === 0) { setFilteredStudents(students); // Réafficher tous les élèves setSelectedEleve(null); } - + return { ...prevData, selectedGuardians, @@ -361,11 +363,9 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r onChange={handleChange} className="w-full mt-4" /> - ({ ...prevData, guardianEmail: email, - emailError: + emailError: email.length > 0 && // Le champ de mail est non null (!emailRegex.test(email) && filteredStudents.length === 0) // Format invalide ou aucun résultat ? "Format d'email invalide ou aucun élève trouvé" @@ -661,7 +661,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r {formData.guardianEmail} - {formData.guardianPhone} + @@ -781,7 +781,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r )}
- + {/* Titre */} diff --git a/Front-End/src/components/Inscription/ResponsableInputFields.js b/Front-End/src/components/Inscription/ResponsableInputFields.js index c1346e4..ce46fc2 100644 --- a/Front-End/src/components/Inscription/ResponsableInputFields.js +++ b/Front-End/src/components/Inscription/ResponsableInputFields.js @@ -3,7 +3,6 @@ import InputPhone from '@/components/InputPhone'; import Button from '@/components/Button'; import React from 'react'; import { useTranslations } from 'next-intl'; -import 'react-phone-number-input/style.css' import { Trash2, Plus } from 'lucide-react'; export default function ResponsableInputFields({guardians, onGuardiansChange, addGuardian, deleteGuardian, errors = []}) { diff --git a/Front-End/src/components/PhoneLabel.js b/Front-End/src/components/PhoneLabel.js new file mode 100644 index 0000000..3b12b5b --- /dev/null +++ b/Front-End/src/components/PhoneLabel.js @@ -0,0 +1,7 @@ +import { formatPhoneNumber } from "@/utils/Telephone"; + +export function PhoneLabel({phoneNumber}){ + return ( + {formatPhoneNumber(phoneNumber)} + ); +} \ No newline at end of file diff --git a/Front-End/src/utils/Telephone.js b/Front-End/src/utils/Telephone.js index aafa2e0..a55eb05 100644 --- a/Front-End/src/utils/Telephone.js +++ b/Front-End/src/utils/Telephone.js @@ -1,36 +1,49 @@ -const localePrefixes = { - "fr-FR": "+33", - // Ajoutez d'autres locales et leurs préfixes ici -}; - - -export function formatPhoneNumber(phoneString, fromFormat = 'XX-XX-XX-XX-XX', toFormat = 'LX-XX-XX-XX-XX', locale = "fr-FR") { + /** + * + * @param {*} phoneString au format E.164 + * @param {*} toFormat L=Country code, X=Number Format="LX XX XX XX XX" + * @returns + */ +export function formatPhoneNumber(phoneString, toFormat = 'LX XX XX XX XX') { if (!phoneString) return; - // Extraire les chiffres du numéro de téléphone - const digits = phoneString.replace(/\D/g, ''); - - // Déterminer le préfixe international en fonction de la locale - - - let prefix = localePrefixes[locale] || ''; - - // Si le format d'entrée commence par 'L', détecter la locale - if (fromFormat.startsWith('L')) { - const detectedPrefix = phoneString.match(/^\+\d+/); - if (detectedPrefix) { - prefix = detectedPrefix[0]; - phoneString = phoneString.replace(prefix, ''); - } + // Vérifier si le numéro est au format international + if(!validateE164PhoneNumber(phoneString)){ + return phoneString; } + const matches = phoneString.match(/^\+(\d{1,3})(\d{9})$/); + if (!matches) return phoneString; - // Remplacer 'L' par le préfixe et 'X' par les chiffres du numéro de téléphone + const [_, countryCode, number] = matches; + const prefix = `+${countryCode}`; + const digits = number; + + // Initialiser le numéro formaté avec le préfixe let formattedNumber = toFormat.replace('L', prefix); + let digitIndex = 0; - formattedNumber = formattedNumber.replace(/X/g, () => { + + // Remplacer les X par les chiffres + formattedNumber = formattedNumber.replace(/X/g, (match, offset) => { + // Si c'est le dernier groupe de X, mettre tous les chiffres restants + if (offset === formattedNumber.lastIndexOf('X') - match.length + 1) { + const remainingDigits = digits.substring(digitIndex); + digitIndex += remainingDigits.length; + return remainingDigits; + } + // Sinon, mettre un seul chiffre return digits[digitIndex++] || ''; }); return formattedNumber; } + +// Fonction pour valider un numéro de téléphone au format E.164 +// Le format E.164 est un format international pour les numéros de téléphone +// qui commence par un signe '+' suivi du code pays et du numéro de téléphone +// Par exemple : +33123456789 pour un numéro français +export function validateE164PhoneNumber(phoneNumber) { + const regEx = /^\+[1-9]\d{9,14}$/; + return regEx.test(phoneNumber); +};