|
@@ -0,0 +1,160 @@
|
|
|
|
|
+/**
|
|
|
|
|
+ * Phone Number Formatter Utility
|
|
|
|
|
+ *
|
|
|
|
|
+ * Formats phone numbers to international format (E.164)
|
|
|
|
|
+ * Example: +36309284614
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Format a phone number to international format
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param phoneNumber - The raw phone number string
|
|
|
|
|
+ * @param defaultCountryCode - Default country code to use if none is present (e.g., '36' for Hungary)
|
|
|
|
|
+ * @returns Formatted phone number in international format (e.g., +36309284614) or null if invalid
|
|
|
|
|
+ */
|
|
|
|
|
+export function formatPhoneNumber(
|
|
|
|
|
+ phoneNumber: string | null | undefined,
|
|
|
|
|
+ defaultCountryCode: string = '36'
|
|
|
|
|
+): string | null {
|
|
|
|
|
+ // Return null for empty/null/undefined values
|
|
|
|
|
+ if (!phoneNumber || typeof phoneNumber !== 'string') {
|
|
|
|
|
+ return null
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Remove all non-digit characters except leading +
|
|
|
|
|
+ let cleaned = phoneNumber.trim()
|
|
|
|
|
+ const hasPlus = cleaned.startsWith('+')
|
|
|
|
|
+ cleaned = cleaned.replace(/[^\d]/g, '')
|
|
|
|
|
+
|
|
|
|
|
+ // Return null if no digits found
|
|
|
|
|
+ if (!cleaned || cleaned.length === 0) {
|
|
|
|
|
+ return null
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // If the number already has a + and digits, return formatted
|
|
|
|
|
+ if (hasPlus && cleaned.length >= 8) {
|
|
|
|
|
+ return `+${cleaned}`
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // If number starts with 00 (international format), replace with +
|
|
|
|
|
+ if (cleaned.startsWith('00') && cleaned.length >= 10) {
|
|
|
|
|
+ return `+${cleaned.substring(2)}`
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // If number starts with country code (without +), add +
|
|
|
|
|
+ // Check for common country codes (1-3 digits)
|
|
|
|
|
+ if (cleaned.length >= 10) {
|
|
|
|
|
+ // Try to detect if it already starts with a country code
|
|
|
|
|
+ // Common patterns:
|
|
|
|
|
+ // - 36... (Hungary - 2 digits)
|
|
|
|
|
+ // - 1... (US/Canada - 1 digit)
|
|
|
|
|
+ // - 44... (UK - 2 digits)
|
|
|
|
|
+ // - etc.
|
|
|
|
|
+
|
|
|
|
|
+ // If starts with common country codes, add +
|
|
|
|
|
+ const twoDigitPrefix = cleaned.substring(0, 2)
|
|
|
|
|
+ const threeDigitPrefix = cleaned.substring(0, 3)
|
|
|
|
|
+
|
|
|
|
|
+ // List of common 2-digit country codes
|
|
|
|
|
+ const twoDigitCodes = ['36', '44', '49', '33', '34', '39', '48', '31', '32', '43', '41', '45', '46', '47', '90']
|
|
|
|
|
+ // List of common 3-digit country codes
|
|
|
|
|
+ const threeDigitCodes = ['420', '421']
|
|
|
|
|
+
|
|
|
|
|
+ if (threeDigitCodes.includes(threeDigitPrefix) && cleaned.length >= 12) {
|
|
|
|
|
+ return `+${cleaned}`
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (twoDigitCodes.includes(twoDigitPrefix)) {
|
|
|
|
|
+ return `+${cleaned}`
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // If starts with 1 and is 11 digits (US/Canada)
|
|
|
|
|
+ if (cleaned.startsWith('1') && cleaned.length === 11) {
|
|
|
|
|
+ return `+${cleaned}`
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // If number doesn't have country code, add default
|
|
|
|
|
+ // Remove leading 0 if present (common in local formats)
|
|
|
|
|
+ if (cleaned.startsWith('0')) {
|
|
|
|
|
+ cleaned = cleaned.substring(1)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Add default country code
|
|
|
|
|
+ if (cleaned.length >= 8) {
|
|
|
|
|
+ return `+${defaultCountryCode}${cleaned}`
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Number too short to be valid, return null
|
|
|
|
|
+ return null
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Format multiple phone numbers (useful for arrays or fallback chains)
|
|
|
|
|
+ * Returns the first valid formatted phone number
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param phoneNumbers - Array of phone number strings to try
|
|
|
|
|
+ * @param defaultCountryCode - Default country code to use
|
|
|
|
|
+ * @returns First valid formatted phone number or null
|
|
|
|
|
+ */
|
|
|
|
|
+export function formatFirstValidPhone(
|
|
|
|
|
+ phoneNumbers: (string | null | undefined)[],
|
|
|
|
|
+ defaultCountryCode: string = '36'
|
|
|
|
|
+): string | null {
|
|
|
|
|
+ for (const phone of phoneNumbers) {
|
|
|
|
|
+ const formatted = formatPhoneNumber(phone, defaultCountryCode)
|
|
|
|
|
+ if (formatted) {
|
|
|
|
|
+ return formatted
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return null
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Detect country code from store domain or settings
|
|
|
|
|
+ * Used to set appropriate default country code per store
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param storeDomain - Store domain or URL
|
|
|
|
|
+ * @returns Detected country code or '36' (Hungary) as default
|
|
|
|
|
+ */
|
|
|
|
|
+export function detectCountryCode(storeDomain: string | null | undefined): string {
|
|
|
|
|
+ if (!storeDomain) {
|
|
|
|
|
+ return '36' // Default to Hungary
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const domain = storeDomain.toLowerCase()
|
|
|
|
|
+
|
|
|
|
|
+ // Country code mappings based on TLD or domain patterns
|
|
|
|
|
+ const countryMappings: Record<string, string> = {
|
|
|
|
|
+ '.hu': '36', // Hungary
|
|
|
|
|
+ '.sk': '421', // Slovakia
|
|
|
|
|
+ '.cz': '420', // Czech Republic
|
|
|
|
|
+ '.ro': '40', // Romania
|
|
|
|
|
+ '.at': '43', // Austria
|
|
|
|
|
+ '.de': '49', // Germany
|
|
|
|
|
+ '.uk': '44', // United Kingdom
|
|
|
|
|
+ '.us': '1', // United States
|
|
|
|
|
+ '.ca': '1', // Canada
|
|
|
|
|
+ '.fr': '33', // France
|
|
|
|
|
+ '.es': '34', // Spain
|
|
|
|
|
+ '.it': '39', // Italy
|
|
|
|
|
+ '.pl': '48', // Poland
|
|
|
|
|
+ '.nl': '31', // Netherlands
|
|
|
|
|
+ '.be': '32', // Belgium
|
|
|
|
|
+ '.ch': '41', // Switzerland
|
|
|
|
|
+ '.se': '46', // Sweden
|
|
|
|
|
+ '.no': '47', // Norway
|
|
|
|
|
+ '.dk': '45', // Denmark
|
|
|
|
|
+ '.tr': '90', // Turkey
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Check for TLD match
|
|
|
|
|
+ for (const [tld, code] of Object.entries(countryMappings)) {
|
|
|
|
|
+ if (domain.includes(tld)) {
|
|
|
|
|
+ return code
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Default to Hungary if no match
|
|
|
|
|
+ return '36'
|
|
|
|
|
+}
|