"use client"; import { useEffect, useRef, useState } from "react"; import { PhoneNumberFormat, PhoneNumberUtil } from "google-libphonenumber"; import type { QuestionField } from "@/data/question-data"; import type { MarriagePhoneFieldValue } from "@/hooks/marriage/types"; import { useQuestionAnswer } from "./question-answer-storage"; import QuestionTitle from "./question-title"; type QuestionPhoneProps = { question: QuestionField; questionIndex: number; countryCode?: string; }; type PhoneValueParts = { codeValue: string; phoneValue: string; }; const phoneUtil = PhoneNumberUtil.getInstance(); function isMarriagePhoneFieldValue( value: unknown, ): value is MarriagePhoneFieldValue { if (!value || typeof value !== "object") { return false; } const phoneValue = value as Partial; return ( typeof phoneValue.countryCode === "string" && typeof phoneValue.phoneNumber === "string" ); } function readPhoneValue(value: unknown, fallbackCode: string): PhoneValueParts { if (value === null) { return { codeValue: "", phoneValue: "", }; } if (isMarriagePhoneFieldValue(value)) { return { codeValue: value.countryCode ? `+${value.countryCode.replace(/^\+/, "")}` : fallbackCode, phoneValue: value.phoneNumber, }; } if (typeof value !== "string") { return { codeValue: fallbackCode, phoneValue: "", }; } if (value.length === 0) { return { codeValue: "", phoneValue: "", }; } const separatorIndex = value.indexOf(" "); if (separatorIndex >= 0) { return { codeValue: value.slice(0, separatorIndex), phoneValue: value.slice(separatorIndex + 1), }; } if (value.startsWith("+")) { try { const parsedNumber = phoneUtil.parse(value); const countryCode = parsedNumber.getCountryCode(); const nationalNumber = String(parsedNumber.getNationalNumber()); return { codeValue: countryCode ? `+${countryCode}` : fallbackCode, phoneValue: nationalNumber, }; } catch { return { codeValue: fallbackCode, phoneValue: value, }; } } return { codeValue: fallbackCode, phoneValue: value, }; } function writePhoneValue(codeValue: string, phoneValue: string) { if (!codeValue && !phoneValue) { return null; } if (codeValue && phoneValue) { return `${codeValue} ${phoneValue}`; } if (codeValue) { return codeValue.startsWith("+") ? codeValue : `${codeValue} `; } return phoneValue; } function toStoredPhoneValue( codeValue: string, phoneValue: string, ): MarriagePhoneFieldValue | null { const normalizedCountryCode = sanitizeCountryCode(codeValue).replace(/^\+/, ""); const normalizedPhoneNumber = phoneValue.trim().replace(/\s+/g, ""); if (!normalizedCountryCode && !normalizedPhoneNumber) { return null; } return { countryCode: normalizedCountryCode, phoneNumber: normalizedPhoneNumber, }; } function sanitizeCountryCode(value: string) { const sanitized = value.replace(/[^\d+]/g, ""); if (sanitized.length === 0) { return ""; } return sanitized.startsWith("+") ? `+${sanitized.slice(1).replace(/\+/g, "")}` : `+${sanitized.replace(/\+/g, "")}`; } function sanitizePhoneNumber(value: string) { return value.replace(/[^\d\s\-().]/g, ""); } function getNormalizedPhoneValue(codeValue: string, phoneValue: string) { const nextCodeValue = sanitizeCountryCode(codeValue); const nextPhoneValue = phoneValue.trim(); if (nextCodeValue.length === 0 && nextPhoneValue.length === 0) { return { isValid: !nextPhoneValue.length, normalizedValue: null, }; } if (nextCodeValue.length === 0 || nextPhoneValue.length === 0) { return { isValid: false, normalizedValue: null, }; } try { const parsedNumber = phoneUtil.parse(`${nextCodeValue} ${nextPhoneValue}`); if (!phoneUtil.isValidNumber(parsedNumber)) { return { isValid: false, normalizedValue: null, }; } return { isValid: true, normalizedValue: phoneUtil.format(parsedNumber, PhoneNumberFormat.E164), }; } catch { return { isValid: false, normalizedValue: null, }; } } export function QuestionPhone({ question, questionIndex, countryCode = "+98", }: QuestionPhoneProps) { const { setValue, value } = useQuestionAnswer(question, questionIndex); const defaultCodeValue = countryCode.trim() || "+98"; const initialValue = readPhoneValue(value, defaultCodeValue); const [codeValue, setCodeValue] = useState(initialValue.codeValue); const [phoneValue, setPhoneValue] = useState(initialValue.phoneValue); const lastCommittedValueRef = useRef(value); const normalizedPhoneState = getNormalizedPhoneValue(codeValue, phoneValue); const showInvalidState = codeValue.trim().length > 0 && phoneValue.trim().length > 0 && !normalizedPhoneState.isValid; const isAnswered = question.required === false ? normalizedPhoneState.isValid || phoneValue.trim().length === 0 : normalizedPhoneState.isValid; useEffect(() => { if (value === lastCommittedValueRef.current) { return; } const nextValue = readPhoneValue(value, defaultCodeValue); setCodeValue(nextValue.codeValue); setPhoneValue(nextValue.phoneValue); lastCommittedValueRef.current = value; }, [defaultCodeValue, value]); const updateStoredValue = (nextCodeValue: string, nextPhoneValue: string) => { const draftValue = writePhoneValue(nextCodeValue, nextPhoneValue); const nextPhoneState = getNormalizedPhoneValue( nextCodeValue, nextPhoneValue, ); const nextValue = draftValue === null ? null : nextPhoneState.isValid ? toStoredPhoneValue(nextCodeValue, nextPhoneValue) : null; lastCommittedValueRef.current = nextValue; setValue(nextValue); }; return ( ); } export default QuestionPhone;