/** * Utilities to normalize and format phone numbers without relying on external libraries. * The helpers keep the behaviour permissive to avoid breaking existing flows while * still providing a single place where formatting rules live. */ /** Matches characters that should be kept when normalising a phone number. */ const PHONE_CHAR_PATTERN = /[^+\d]/g /** * Normalises a phone number by trimming whitespace, removing spacing/separators and * converting international prefixes written with `00` to their `+` variant. */ export const normalizePhone = (rawValue: string): string => { const trimmed = (rawValue || '').trim() if (!trimmed) { return '' } const cleaned = trimmed.replace(PHONE_CHAR_PATTERN, '') if (cleaned.startsWith('00')) { return `+${cleaned.slice(2)}` } return cleaned } /** * Formats a phone number by grouping digits by two while keeping any international * prefix. The function remains tolerant to partially entered numbers. */ export const formatPhone = (rawValue: string): string => { const normalized = normalizePhone(rawValue) if (!normalized) { return '' } const hasInternationalPrefix = normalized.startsWith('+') const prefix = hasInternationalPrefix ? normalized.slice(0, 1) : '' const digits = hasInternationalPrefix ? normalized.slice(1) : normalized const groups = digits.match(/.{1,2}/g) ?? [] const grouped = groups.join(' ') return prefix ? `${prefix}${grouped ? ' ' : ''}${grouped}` : grouped } /** * Masks a phone number for display purposes by replacing the middle digits with ·. * Useful for UI fragments where the full number should not be exposed. */ export const maskPhone = (rawValue: string): string => { const normalized = normalizePhone(rawValue) if (!normalized) { return '' } if (normalized.length <= 4) { return normalized } const start = normalized.slice(0, 2) const end = normalized.slice(-2) const maskedMiddle = '·'.repeat(Math.max(0, normalized.length - 4)) return `${start}${maskedMiddle}${end}` }