@traky-app/ui 1.6.1 → 1.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/package.json +15 -1
  2. package/registry/traky/json/city-select.json +19 -0
  3. package/registry/traky/json/country-select.json +24 -0
  4. package/registry/traky/json/geo-search-select.json +24 -0
  5. package/registry/traky/json/geo-selectors.json +21 -0
  6. package/registry/traky/json/google-maps-address-picker.json +22 -0
  7. package/registry/traky/json/internal-chat-mention.json +1 -1
  8. package/registry/traky/json/internal-chat-panel.json +1 -1
  9. package/registry/traky/json/lib-geo-city-codes.json +14 -0
  10. package/registry/traky/json/lib-geo-countries.json +14 -0
  11. package/registry/traky/json/lib-geo-csc.json +17 -0
  12. package/registry/traky/json/lib-geo-locations.json +14 -0
  13. package/registry/traky/json/lib-geo-overrides.json +14 -0
  14. package/registry/traky/json/lib-geo-types.json +14 -0
  15. package/registry/traky/json/lib-google-maps-address.json +14 -0
  16. package/registry/traky/json/lib-phone-countries.json +14 -0
  17. package/registry/traky/json/lib-phone-e164.json +14 -0
  18. package/registry/traky/json/lib-utils.json +1 -1
  19. package/registry/traky/json/office-address-form-dialog.json +17 -0
  20. package/registry/traky/json/office-address-form.json +31 -0
  21. package/registry/traky/json/phone-number-input.json +25 -0
  22. package/registry/traky/json/state-select.json +19 -0
  23. package/registry/traky/lib/geo-city-codes.ts +234 -0
  24. package/registry/traky/lib/geo-countries.ts +75 -0
  25. package/registry/traky/lib/geo-csc.ts +82 -0
  26. package/registry/traky/lib/geo-locations.ts +171 -0
  27. package/registry/traky/lib/geo-overrides.ts +375 -0
  28. package/registry/traky/lib/geo-types.ts +34 -0
  29. package/registry/traky/lib/google-maps-address.ts +100 -0
  30. package/registry/traky/lib/phone-countries.ts +380 -0
  31. package/registry/traky/lib/phone-e164.ts +66 -0
  32. package/registry/traky/lib/utils.ts +24 -0
  33. package/registry/traky/registry.json +180 -0
  34. package/registry/traky/ui/city-select.tsx +113 -0
  35. package/registry/traky/ui/country-select.tsx +233 -0
  36. package/registry/traky/ui/geo-search-select.tsx +136 -0
  37. package/registry/traky/ui/geo-selectors.tsx +162 -0
  38. package/registry/traky/ui/google-maps-address-picker.tsx +422 -0
  39. package/registry/traky/ui/internal-chat-mention.ts +34 -3
  40. package/registry/traky/ui/internal-chat-panel.tsx +147 -12
  41. package/registry/traky/ui/office-address-form-dialog.tsx +16 -0
  42. package/registry/traky/ui/office-address-form.tsx +868 -0
  43. package/registry/traky/ui/phone-number-input.tsx +253 -0
  44. package/registry/traky/ui/state-select.tsx +107 -0
  45. package/src/lib/geo-city-codes.ts +234 -0
  46. package/src/lib/geo-countries.ts +75 -0
  47. package/src/lib/geo-csc.ts +82 -0
  48. package/src/lib/geo-locations.ts +171 -0
  49. package/src/lib/geo-overrides.ts +375 -0
  50. package/src/lib/geo-types.ts +34 -0
  51. package/src/lib/google-maps-address.ts +100 -0
  52. package/src/lib/phone-countries.ts +380 -0
  53. package/src/lib/phone-e164.ts +66 -0
  54. package/src/test/setup.ts +31 -0
  55. package/src/ui/__tests__/geo-selects.test.tsx +504 -0
  56. package/src/ui/city-select.tsx +113 -0
  57. package/src/ui/country-select.tsx +233 -0
  58. package/src/ui/geo-search-select.tsx +136 -0
  59. package/src/ui/geo-selectors.tsx +162 -0
  60. package/src/ui/google-maps-address-picker.tsx +422 -0
  61. package/src/ui/internal-chat-mention.ts +34 -3
  62. package/src/ui/internal-chat-panel.tsx +147 -12
  63. package/src/ui/office-address-form-dialog.tsx +16 -0
  64. package/src/ui/office-address-form.tsx +877 -0
  65. package/src/ui/phone-number-input.tsx +253 -0
  66. package/src/ui/state-select.tsx +107 -0
@@ -0,0 +1,100 @@
1
+ export const OFFICE_ADDRESS_GEOCODE_SOURCE_GOOGLE_MAPS = "google_maps" as const
2
+
3
+ export type GoogleMapsAddressSelection = {
4
+ street: string
5
+ streetNumber?: string
6
+ city: string
7
+ state?: string
8
+ postalCode?: string
9
+ countryCode?: string
10
+ formattedAddress?: string
11
+ latitude: number
12
+ longitude: number
13
+ }
14
+
15
+ type AddressComponentLike = {
16
+ types: string[]
17
+ long_name?: string
18
+ short_name?: string
19
+ longText?: string
20
+ shortText?: string
21
+ }
22
+
23
+ export function parseGoogleAddressComponents(
24
+ components: AddressComponentLike[] | undefined,
25
+ fallbackStreet: string,
26
+ latitude: number,
27
+ longitude: number,
28
+ ): GoogleMapsAddressSelection | null {
29
+ let streetNumber = ""
30
+ let route = ""
31
+ let city = ""
32
+ let state = ""
33
+ let postalCode = ""
34
+ let countryCode = ""
35
+
36
+ for (const component of components ?? []) {
37
+ const types = component.types
38
+ const longName = component.long_name ?? component.longText ?? ""
39
+ const shortName = component.short_name ?? component.shortText ?? ""
40
+
41
+ if (types.includes("street_number")) streetNumber = longName
42
+ if (types.includes("route")) route = longName
43
+ if (types.includes("locality")) city = longName
44
+ else if (
45
+ !city &&
46
+ (types.includes("sublocality") || types.includes("postal_town"))
47
+ ) {
48
+ city = longName
49
+ }
50
+ if (types.includes("administrative_area_level_1")) {
51
+ state = longName || shortName
52
+ }
53
+ if (types.includes("postal_code")) postalCode = longName
54
+ if (types.includes("country")) countryCode = shortName.toUpperCase()
55
+ }
56
+
57
+ const street = route || fallbackStreet
58
+ if (!street.trim() && !fallbackStreet.trim()) return null
59
+
60
+ return {
61
+ street: street.trim() || fallbackStreet.trim(),
62
+ streetNumber: streetNumber.trim() || undefined,
63
+ city: city.trim(),
64
+ state: state.trim() || undefined,
65
+ postalCode: postalCode.trim() || undefined,
66
+ countryCode: countryCode.trim() || undefined,
67
+ latitude,
68
+ longitude,
69
+ }
70
+ }
71
+
72
+ export function googleMapsSelectionToFormPatch(
73
+ selection: GoogleMapsAddressSelection,
74
+ ): Pick<
75
+ GoogleMapsAddressSelection,
76
+ "latitude" | "longitude"
77
+ > & {
78
+ address: string
79
+ city: string
80
+ stateOrProvince: string
81
+ postalCode: string
82
+ countryCode: string
83
+ addressGeocodeSource: string
84
+ } {
85
+ const addressLine =
86
+ [selection.streetNumber, selection.street].filter(Boolean).join(" ").trim() ||
87
+ selection.formattedAddress?.split(",")[0]?.trim() ||
88
+ selection.street
89
+
90
+ return {
91
+ address: addressLine,
92
+ city: selection.city,
93
+ stateOrProvince: selection.state ?? "",
94
+ postalCode: selection.postalCode ?? "",
95
+ countryCode: selection.countryCode?.trim().toUpperCase() ?? "",
96
+ latitude: selection.latitude,
97
+ longitude: selection.longitude,
98
+ addressGeocodeSource: OFFICE_ADDRESS_GEOCODE_SOURCE_GOOGLE_MAPS,
99
+ }
100
+ }
@@ -0,0 +1,380 @@
1
+ export type PhoneCountry = {
2
+ iso: string;
3
+ name: string;
4
+ dialCode: string;
5
+ /** Solo dígitos nacionales (sin código de país). */
6
+ nationalMinLength: number;
7
+ nationalMaxLength: number;
8
+ };
9
+
10
+ /**
11
+ * Catálogo mundial de códigos telefónicos (flags en /public/assets/flags).
12
+ * Prioriza LATAM / uso frecuente arriba; el resto va A–Z en español.
13
+ */
14
+ export const PHONE_COUNTRIES: PhoneCountry[] = [
15
+ { iso: "EC", name: "Ecuador", dialCode: "+593", nationalMinLength: 9, nationalMaxLength: 9 },
16
+ { iso: "CO", name: "Colombia", dialCode: "+57", nationalMinLength: 10, nationalMaxLength: 10 },
17
+ { iso: "MX", name: "México", dialCode: "+52", nationalMinLength: 10, nationalMaxLength: 11 },
18
+ { iso: "PE", name: "Perú", dialCode: "+51", nationalMinLength: 9, nationalMaxLength: 9 },
19
+ { iso: "CL", name: "Chile", dialCode: "+56", nationalMinLength: 9, nationalMaxLength: 9 },
20
+ { iso: "AR", name: "Argentina", dialCode: "+54", nationalMinLength: 10, nationalMaxLength: 11 },
21
+ { iso: "US", name: "Estados Unidos", dialCode: "+1", nationalMinLength: 10, nationalMaxLength: 10 },
22
+ { iso: "CA", name: "Canadá", dialCode: "+1", nationalMinLength: 10, nationalMaxLength: 10 },
23
+ { iso: "ES", name: "España", dialCode: "+34", nationalMinLength: 9, nationalMaxLength: 9 },
24
+ { iso: "BR", name: "Brasil", dialCode: "+55", nationalMinLength: 10, nationalMaxLength: 11 },
25
+ { iso: "VE", name: "Venezuela", dialCode: "+58", nationalMinLength: 10, nationalMaxLength: 10 },
26
+ { iso: "PA", name: "Panamá", dialCode: "+507", nationalMinLength: 8, nationalMaxLength: 8 },
27
+ { iso: "CR", name: "Costa Rica", dialCode: "+506", nationalMinLength: 8, nationalMaxLength: 8 },
28
+ { iso: "GT", name: "Guatemala", dialCode: "+502", nationalMinLength: 8, nationalMaxLength: 8 },
29
+ { iso: "HN", name: "Honduras", dialCode: "+504", nationalMinLength: 8, nationalMaxLength: 8 },
30
+ { iso: "SV", name: "El Salvador", dialCode: "+503", nationalMinLength: 8, nationalMaxLength: 8 },
31
+ { iso: "NI", name: "Nicaragua", dialCode: "+505", nationalMinLength: 8, nationalMaxLength: 8 },
32
+ { iso: "BO", name: "Bolivia", dialCode: "+591", nationalMinLength: 8, nationalMaxLength: 8 },
33
+ { iso: "PY", name: "Paraguay", dialCode: "+595", nationalMinLength: 9, nationalMaxLength: 9 },
34
+ { iso: "UY", name: "Uruguay", dialCode: "+598", nationalMinLength: 8, nationalMaxLength: 8 },
35
+ { iso: "DO", name: "Rep. Dominicana", dialCode: "+1", nationalMinLength: 10, nationalMaxLength: 10 },
36
+ { iso: "PR", name: "Puerto Rico", dialCode: "+1", nationalMinLength: 10, nationalMaxLength: 10 },
37
+ { iso: "AF", name: "Afganistán", dialCode: "+93", nationalMinLength: 7, nationalMaxLength: 15 },
38
+ { iso: "AX", name: "Aland Islands", dialCode: "+358", nationalMinLength: 7, nationalMaxLength: 15 },
39
+ { iso: "AL", name: "Albania", dialCode: "+355", nationalMinLength: 7, nationalMaxLength: 15 },
40
+ { iso: "DE", name: "Alemania", dialCode: "+49", nationalMinLength: 10, nationalMaxLength: 11 },
41
+ { iso: "AD", name: "Andorra", dialCode: "+376", nationalMinLength: 7, nationalMaxLength: 15 },
42
+ { iso: "AO", name: "Angola", dialCode: "+244", nationalMinLength: 7, nationalMaxLength: 15 },
43
+ { iso: "AI", name: "Anguila", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
44
+ { iso: "AG", name: "Antigua y Barbuda", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
45
+ { iso: "SA", name: "Arabia Saudita", dialCode: "+966", nationalMinLength: 9, nationalMaxLength: 9 },
46
+ { iso: "DZ", name: "Argelia", dialCode: "+213", nationalMinLength: 7, nationalMaxLength: 15 },
47
+ { iso: "AM", name: "Armenia", dialCode: "+374", nationalMinLength: 7, nationalMaxLength: 15 },
48
+ { iso: "AW", name: "Aruba", dialCode: "+297", nationalMinLength: 7, nationalMaxLength: 15 },
49
+ { iso: "AU", name: "Australia", dialCode: "+61", nationalMinLength: 9, nationalMaxLength: 9 },
50
+ { iso: "AT", name: "Austria", dialCode: "+43", nationalMinLength: 10, nationalMaxLength: 11 },
51
+ { iso: "AZ", name: "Azerbaiyán", dialCode: "+994", nationalMinLength: 7, nationalMaxLength: 15 },
52
+ { iso: "BS", name: "Bahamas", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
53
+ { iso: "BD", name: "Bangladés", dialCode: "+880", nationalMinLength: 7, nationalMaxLength: 15 },
54
+ { iso: "BB", name: "Barbados", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
55
+ { iso: "BH", name: "Baréin", dialCode: "+973", nationalMinLength: 7, nationalMaxLength: 15 },
56
+ { iso: "BE", name: "Bélgica", dialCode: "+32", nationalMinLength: 9, nationalMaxLength: 9 },
57
+ { iso: "BZ", name: "Belice", dialCode: "+501", nationalMinLength: 7, nationalMaxLength: 15 },
58
+ { iso: "BJ", name: "Benín", dialCode: "+229", nationalMinLength: 7, nationalMaxLength: 15 },
59
+ { iso: "BM", name: "Bermudas", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
60
+ { iso: "BY", name: "Bielorrusia", dialCode: "+375", nationalMinLength: 7, nationalMaxLength: 15 },
61
+ { iso: "BQ", name: "Bonaire, Sint Eustatius and Saba", dialCode: "+599", nationalMinLength: 7, nationalMaxLength: 15 },
62
+ { iso: "BA", name: "Bosnia y Herzegovina", dialCode: "+387", nationalMinLength: 7, nationalMaxLength: 15 },
63
+ { iso: "BW", name: "Botsuana", dialCode: "+267", nationalMinLength: 7, nationalMaxLength: 15 },
64
+ { iso: "BV", name: "Bouvet Island", dialCode: "+0055", nationalMinLength: 7, nationalMaxLength: 15 },
65
+ { iso: "IO", name: "British Indian Ocean Territory", dialCode: "+246", nationalMinLength: 7, nationalMaxLength: 15 },
66
+ { iso: "BN", name: "Brunéi", dialCode: "+673", nationalMinLength: 7, nationalMaxLength: 15 },
67
+ { iso: "BG", name: "Bulgaria", dialCode: "+359", nationalMinLength: 7, nationalMaxLength: 15 },
68
+ { iso: "BF", name: "Burkina Faso", dialCode: "+226", nationalMinLength: 7, nationalMaxLength: 15 },
69
+ { iso: "BI", name: "Burundi", dialCode: "+257", nationalMinLength: 7, nationalMaxLength: 15 },
70
+ { iso: "BT", name: "Bután", dialCode: "+975", nationalMinLength: 7, nationalMaxLength: 15 },
71
+ { iso: "CV", name: "Cabo Verde", dialCode: "+238", nationalMinLength: 7, nationalMaxLength: 15 },
72
+ { iso: "KH", name: "Camboya", dialCode: "+855", nationalMinLength: 7, nationalMaxLength: 15 },
73
+ { iso: "CM", name: "Camerún", dialCode: "+237", nationalMinLength: 7, nationalMaxLength: 15 },
74
+ { iso: "QA", name: "Catar", dialCode: "+974", nationalMinLength: 7, nationalMaxLength: 15 },
75
+ { iso: "KY", name: "Cayman Islands", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
76
+ { iso: "TD", name: "Chad", dialCode: "+235", nationalMinLength: 7, nationalMaxLength: 15 },
77
+ { iso: "CN", name: "China", dialCode: "+86", nationalMinLength: 11, nationalMaxLength: 11 },
78
+ { iso: "CY", name: "Chipre", dialCode: "+357", nationalMinLength: 7, nationalMaxLength: 15 },
79
+ { iso: "CX", name: "Christmas Island", dialCode: "+61", nationalMinLength: 7, nationalMaxLength: 15 },
80
+ { iso: "VA", name: "Ciudad del Vaticano", dialCode: "+379", nationalMinLength: 7, nationalMaxLength: 15 },
81
+ { iso: "CC", name: "Cocos (Keeling) Islands", dialCode: "+61", nationalMinLength: 7, nationalMaxLength: 15 },
82
+ { iso: "KM", name: "Comoras", dialCode: "+269", nationalMinLength: 7, nationalMaxLength: 15 },
83
+ { iso: "CG", name: "Congo", dialCode: "+242", nationalMinLength: 7, nationalMaxLength: 15 },
84
+ { iso: "CD", name: "Congo (RDC)", dialCode: "+243", nationalMinLength: 7, nationalMaxLength: 15 },
85
+ { iso: "CK", name: "Cook Islands", dialCode: "+682", nationalMinLength: 7, nationalMaxLength: 15 },
86
+ { iso: "KP", name: "Corea del Norte", dialCode: "+850", nationalMinLength: 7, nationalMaxLength: 15 },
87
+ { iso: "KR", name: "Corea del Sur", dialCode: "+82", nationalMinLength: 9, nationalMaxLength: 10 },
88
+ { iso: "CI", name: "Costa de Marfil", dialCode: "+225", nationalMinLength: 7, nationalMaxLength: 15 },
89
+ { iso: "HR", name: "Croacia", dialCode: "+385", nationalMinLength: 7, nationalMaxLength: 15 },
90
+ { iso: "CU", name: "Cuba", dialCode: "+53", nationalMinLength: 7, nationalMaxLength: 15 },
91
+ { iso: "CW", name: "Curazao", dialCode: "+599", nationalMinLength: 7, nationalMaxLength: 15 },
92
+ { iso: "DK", name: "Dinamarca", dialCode: "+45", nationalMinLength: 8, nationalMaxLength: 8 },
93
+ { iso: "DM", name: "Dominica", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
94
+ { iso: "EG", name: "Egipto", dialCode: "+20", nationalMinLength: 10, nationalMaxLength: 10 },
95
+ { iso: "AE", name: "Emiratos Árabes Unidos", dialCode: "+971", nationalMinLength: 9, nationalMaxLength: 9 },
96
+ { iso: "ER", name: "Eritrea", dialCode: "+291", nationalMinLength: 7, nationalMaxLength: 15 },
97
+ { iso: "SK", name: "Eslovaquia", dialCode: "+421", nationalMinLength: 7, nationalMaxLength: 15 },
98
+ { iso: "SI", name: "Eslovenia", dialCode: "+386", nationalMinLength: 7, nationalMaxLength: 15 },
99
+ { iso: "EE", name: "Estonia", dialCode: "+372", nationalMinLength: 7, nationalMaxLength: 15 },
100
+ { iso: "ET", name: "Etiopía", dialCode: "+251", nationalMinLength: 7, nationalMaxLength: 15 },
101
+ { iso: "FK", name: "Falkland Islands", dialCode: "+500", nationalMinLength: 7, nationalMaxLength: 15 },
102
+ { iso: "FO", name: "Faroe Islands", dialCode: "+298", nationalMinLength: 7, nationalMaxLength: 15 },
103
+ { iso: "PH", name: "Filipinas", dialCode: "+63", nationalMinLength: 10, nationalMaxLength: 10 },
104
+ { iso: "FI", name: "Finlandia", dialCode: "+358", nationalMinLength: 9, nationalMaxLength: 10 },
105
+ { iso: "FJ", name: "Fiyi", dialCode: "+679", nationalMinLength: 7, nationalMaxLength: 15 },
106
+ { iso: "FR", name: "Francia", dialCode: "+33", nationalMinLength: 9, nationalMaxLength: 9 },
107
+ { iso: "TF", name: "French Southern Territories", dialCode: "+262", nationalMinLength: 7, nationalMaxLength: 15 },
108
+ { iso: "GA", name: "Gabón", dialCode: "+241", nationalMinLength: 7, nationalMaxLength: 15 },
109
+ { iso: "GM", name: "Gambia", dialCode: "+220", nationalMinLength: 7, nationalMaxLength: 15 },
110
+ { iso: "GE", name: "Georgia", dialCode: "+995", nationalMinLength: 7, nationalMaxLength: 15 },
111
+ { iso: "GH", name: "Ghana", dialCode: "+233", nationalMinLength: 7, nationalMaxLength: 15 },
112
+ { iso: "GI", name: "Gibraltar", dialCode: "+350", nationalMinLength: 7, nationalMaxLength: 15 },
113
+ { iso: "GD", name: "Granada", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
114
+ { iso: "GR", name: "Grecia", dialCode: "+30", nationalMinLength: 10, nationalMaxLength: 10 },
115
+ { iso: "GL", name: "Groenlandia", dialCode: "+299", nationalMinLength: 7, nationalMaxLength: 15 },
116
+ { iso: "GP", name: "Guadalupe", dialCode: "+590", nationalMinLength: 7, nationalMaxLength: 15 },
117
+ { iso: "GU", name: "Guam", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
118
+ { iso: "GF", name: "Guayana Francesa", dialCode: "+594", nationalMinLength: 7, nationalMaxLength: 15 },
119
+ { iso: "GG", name: "Guernsey", dialCode: "+44", nationalMinLength: 7, nationalMaxLength: 15 },
120
+ { iso: "GN", name: "Guinea", dialCode: "+224", nationalMinLength: 7, nationalMaxLength: 15 },
121
+ { iso: "GQ", name: "Guinea Ecuatorial", dialCode: "+240", nationalMinLength: 7, nationalMaxLength: 15 },
122
+ { iso: "GW", name: "Guinea-Bisáu", dialCode: "+245", nationalMinLength: 7, nationalMaxLength: 15 },
123
+ { iso: "GY", name: "Guyana", dialCode: "+592", nationalMinLength: 7, nationalMaxLength: 15 },
124
+ { iso: "HT", name: "Haití", dialCode: "+509", nationalMinLength: 7, nationalMaxLength: 15 },
125
+ { iso: "HM", name: "Heard Island and McDonald Islands", dialCode: "+672", nationalMinLength: 7, nationalMaxLength: 15 },
126
+ { iso: "HK", name: "Hong Kong", dialCode: "+852", nationalMinLength: 8, nationalMaxLength: 8 },
127
+ { iso: "HU", name: "Hungría", dialCode: "+36", nationalMinLength: 9, nationalMaxLength: 9 },
128
+ { iso: "IN", name: "India", dialCode: "+91", nationalMinLength: 10, nationalMaxLength: 10 },
129
+ { iso: "ID", name: "Indonesia", dialCode: "+62", nationalMinLength: 9, nationalMaxLength: 12 },
130
+ { iso: "IQ", name: "Irak", dialCode: "+964", nationalMinLength: 7, nationalMaxLength: 15 },
131
+ { iso: "IR", name: "Irán", dialCode: "+98", nationalMinLength: 7, nationalMaxLength: 15 },
132
+ { iso: "IE", name: "Irlanda", dialCode: "+353", nationalMinLength: 9, nationalMaxLength: 9 },
133
+ { iso: "IS", name: "Islandia", dialCode: "+354", nationalMinLength: 7, nationalMaxLength: 15 },
134
+ { iso: "IL", name: "Israel", dialCode: "+972", nationalMinLength: 9, nationalMaxLength: 9 },
135
+ { iso: "IT", name: "Italia", dialCode: "+39", nationalMinLength: 9, nationalMaxLength: 10 },
136
+ { iso: "JM", name: "Jamaica", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
137
+ { iso: "JP", name: "Japón", dialCode: "+81", nationalMinLength: 10, nationalMaxLength: 10 },
138
+ { iso: "JE", name: "Jersey", dialCode: "+44", nationalMinLength: 7, nationalMaxLength: 15 },
139
+ { iso: "JO", name: "Jordania", dialCode: "+962", nationalMinLength: 7, nationalMaxLength: 15 },
140
+ { iso: "KZ", name: "Kazajistán", dialCode: "+7", nationalMinLength: 7, nationalMaxLength: 15 },
141
+ { iso: "KE", name: "Kenia", dialCode: "+254", nationalMinLength: 7, nationalMaxLength: 15 },
142
+ { iso: "KG", name: "Kirguistán", dialCode: "+996", nationalMinLength: 7, nationalMaxLength: 15 },
143
+ { iso: "KI", name: "Kiribati", dialCode: "+686", nationalMinLength: 7, nationalMaxLength: 15 },
144
+ { iso: "XK", name: "Kosovo", dialCode: "+383", nationalMinLength: 7, nationalMaxLength: 15 },
145
+ { iso: "KW", name: "Kuwait", dialCode: "+965", nationalMinLength: 7, nationalMaxLength: 15 },
146
+ { iso: "LA", name: "Laos", dialCode: "+856", nationalMinLength: 7, nationalMaxLength: 15 },
147
+ { iso: "LS", name: "Lesoto", dialCode: "+266", nationalMinLength: 7, nationalMaxLength: 15 },
148
+ { iso: "LV", name: "Letonia", dialCode: "+371", nationalMinLength: 7, nationalMaxLength: 15 },
149
+ { iso: "LB", name: "Líbano", dialCode: "+961", nationalMinLength: 7, nationalMaxLength: 15 },
150
+ { iso: "LR", name: "Liberia", dialCode: "+231", nationalMinLength: 7, nationalMaxLength: 15 },
151
+ { iso: "LY", name: "Libia", dialCode: "+218", nationalMinLength: 7, nationalMaxLength: 15 },
152
+ { iso: "LI", name: "Liechtenstein", dialCode: "+423", nationalMinLength: 7, nationalMaxLength: 15 },
153
+ { iso: "LT", name: "Lituania", dialCode: "+370", nationalMinLength: 7, nationalMaxLength: 15 },
154
+ { iso: "LU", name: "Luxemburgo", dialCode: "+352", nationalMinLength: 7, nationalMaxLength: 15 },
155
+ { iso: "MO", name: "Macao", dialCode: "+853", nationalMinLength: 7, nationalMaxLength: 15 },
156
+ { iso: "MK", name: "Macedonia del Norte", dialCode: "+389", nationalMinLength: 7, nationalMaxLength: 15 },
157
+ { iso: "MG", name: "Madagascar", dialCode: "+261", nationalMinLength: 7, nationalMaxLength: 15 },
158
+ { iso: "MY", name: "Malasia", dialCode: "+60", nationalMinLength: 9, nationalMaxLength: 10 },
159
+ { iso: "MW", name: "Malaui", dialCode: "+265", nationalMinLength: 7, nationalMaxLength: 15 },
160
+ { iso: "MV", name: "Maldivas", dialCode: "+960", nationalMinLength: 7, nationalMaxLength: 15 },
161
+ { iso: "ML", name: "Malí", dialCode: "+223", nationalMinLength: 7, nationalMaxLength: 15 },
162
+ { iso: "MT", name: "Malta", dialCode: "+356", nationalMinLength: 7, nationalMaxLength: 15 },
163
+ { iso: "IM", name: "Man (Isle of)", dialCode: "+44", nationalMinLength: 7, nationalMaxLength: 15 },
164
+ { iso: "MA", name: "Marruecos", dialCode: "+212", nationalMinLength: 9, nationalMaxLength: 9 },
165
+ { iso: "MH", name: "Marshall Islands", dialCode: "+692", nationalMinLength: 7, nationalMaxLength: 15 },
166
+ { iso: "MQ", name: "Martinica", dialCode: "+596", nationalMinLength: 7, nationalMaxLength: 15 },
167
+ { iso: "MU", name: "Mauricio", dialCode: "+230", nationalMinLength: 7, nationalMaxLength: 15 },
168
+ { iso: "MR", name: "Mauritania", dialCode: "+222", nationalMinLength: 7, nationalMaxLength: 15 },
169
+ { iso: "YT", name: "Mayotte", dialCode: "+262", nationalMinLength: 7, nationalMaxLength: 15 },
170
+ { iso: "FM", name: "Micronesia", dialCode: "+691", nationalMinLength: 7, nationalMaxLength: 15 },
171
+ { iso: "MD", name: "Moldavia", dialCode: "+373", nationalMinLength: 7, nationalMaxLength: 15 },
172
+ { iso: "MC", name: "Mónaco", dialCode: "+377", nationalMinLength: 7, nationalMaxLength: 15 },
173
+ { iso: "MN", name: "Mongolia", dialCode: "+976", nationalMinLength: 7, nationalMaxLength: 15 },
174
+ { iso: "ME", name: "Montenegro", dialCode: "+382", nationalMinLength: 7, nationalMaxLength: 15 },
175
+ { iso: "MS", name: "Montserrat", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
176
+ { iso: "MZ", name: "Mozambique", dialCode: "+258", nationalMinLength: 7, nationalMaxLength: 15 },
177
+ { iso: "MM", name: "Myanmar", dialCode: "+95", nationalMinLength: 7, nationalMaxLength: 15 },
178
+ { iso: "NA", name: "Namibia", dialCode: "+264", nationalMinLength: 7, nationalMaxLength: 15 },
179
+ { iso: "NR", name: "Nauru", dialCode: "+674", nationalMinLength: 7, nationalMaxLength: 15 },
180
+ { iso: "NP", name: "Nepal", dialCode: "+977", nationalMinLength: 7, nationalMaxLength: 15 },
181
+ { iso: "NE", name: "Níger", dialCode: "+227", nationalMinLength: 7, nationalMaxLength: 15 },
182
+ { iso: "NG", name: "Nigeria", dialCode: "+234", nationalMinLength: 10, nationalMaxLength: 10 },
183
+ { iso: "NU", name: "Niue", dialCode: "+683", nationalMinLength: 7, nationalMaxLength: 15 },
184
+ { iso: "NF", name: "Norfolk Island", dialCode: "+672", nationalMinLength: 7, nationalMaxLength: 15 },
185
+ { iso: "MP", name: "Northern Mariana Islands", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
186
+ { iso: "NO", name: "Noruega", dialCode: "+47", nationalMinLength: 8, nationalMaxLength: 8 },
187
+ { iso: "NC", name: "Nueva Caledonia", dialCode: "+687", nationalMinLength: 7, nationalMaxLength: 15 },
188
+ { iso: "NZ", name: "Nueva Zelanda", dialCode: "+64", nationalMinLength: 8, nationalMaxLength: 10 },
189
+ { iso: "OM", name: "Omán", dialCode: "+968", nationalMinLength: 7, nationalMaxLength: 15 },
190
+ { iso: "NL", name: "Países Bajos", dialCode: "+31", nationalMinLength: 9, nationalMaxLength: 9 },
191
+ { iso: "PK", name: "Pakistán", dialCode: "+92", nationalMinLength: 7, nationalMaxLength: 15 },
192
+ { iso: "PW", name: "Palaos", dialCode: "+680", nationalMinLength: 7, nationalMaxLength: 15 },
193
+ { iso: "PS", name: "Palestina", dialCode: "+970", nationalMinLength: 7, nationalMaxLength: 15 },
194
+ { iso: "PG", name: "Papúa Nueva Guinea", dialCode: "+675", nationalMinLength: 7, nationalMaxLength: 15 },
195
+ { iso: "PN", name: "Pitcairn Island", dialCode: "+870", nationalMinLength: 7, nationalMaxLength: 15 },
196
+ { iso: "PF", name: "Polinesia Francesa", dialCode: "+689", nationalMinLength: 7, nationalMaxLength: 15 },
197
+ { iso: "PL", name: "Polonia", dialCode: "+48", nationalMinLength: 9, nationalMaxLength: 9 },
198
+ { iso: "PT", name: "Portugal", dialCode: "+351", nationalMinLength: 9, nationalMaxLength: 9 },
199
+ { iso: "GB", name: "Reino Unido", dialCode: "+44", nationalMinLength: 10, nationalMaxLength: 10 },
200
+ { iso: "CZ", name: "Rep. Checa", dialCode: "+420", nationalMinLength: 9, nationalMaxLength: 9 },
201
+ { iso: "CF", name: "República Centroafricana", dialCode: "+236", nationalMinLength: 7, nationalMaxLength: 15 },
202
+ { iso: "RE", name: "Reunión", dialCode: "+262", nationalMinLength: 7, nationalMaxLength: 15 },
203
+ { iso: "RW", name: "Ruanda", dialCode: "+250", nationalMinLength: 7, nationalMaxLength: 15 },
204
+ { iso: "RO", name: "Rumanía", dialCode: "+40", nationalMinLength: 9, nationalMaxLength: 9 },
205
+ { iso: "RU", name: "Rusia", dialCode: "+7", nationalMinLength: 10, nationalMaxLength: 10 },
206
+ { iso: "EH", name: "Sáhara Occidental", dialCode: "+212", nationalMinLength: 7, nationalMaxLength: 15 },
207
+ { iso: "WS", name: "Samoa", dialCode: "+685", nationalMinLength: 7, nationalMaxLength: 15 },
208
+ { iso: "AS", name: "Samoa Americana", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
209
+ { iso: "BL", name: "San Bartolomé", dialCode: "+590", nationalMinLength: 7, nationalMaxLength: 15 },
210
+ { iso: "KN", name: "San Cristóbal y Nieves", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
211
+ { iso: "SM", name: "San Marino", dialCode: "+378", nationalMinLength: 7, nationalMaxLength: 15 },
212
+ { iso: "MF", name: "San Martín", dialCode: "+590", nationalMinLength: 7, nationalMaxLength: 15 },
213
+ { iso: "PM", name: "San Pedro y Miquelón", dialCode: "+508", nationalMinLength: 7, nationalMaxLength: 15 },
214
+ { iso: "VC", name: "San Vicente y las Granadinas", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
215
+ { iso: "SH", name: "Santa Elena", dialCode: "+290", nationalMinLength: 7, nationalMaxLength: 15 },
216
+ { iso: "LC", name: "Santa Lucía", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
217
+ { iso: "ST", name: "Santo Tomé y Príncipe", dialCode: "+239", nationalMinLength: 7, nationalMaxLength: 15 },
218
+ { iso: "SN", name: "Senegal", dialCode: "+221", nationalMinLength: 7, nationalMaxLength: 15 },
219
+ { iso: "RS", name: "Serbia", dialCode: "+381", nationalMinLength: 7, nationalMaxLength: 15 },
220
+ { iso: "SC", name: "Seychelles", dialCode: "+248", nationalMinLength: 7, nationalMaxLength: 15 },
221
+ { iso: "SL", name: "Sierra Leona", dialCode: "+232", nationalMinLength: 7, nationalMaxLength: 15 },
222
+ { iso: "SG", name: "Singapur", dialCode: "+65", nationalMinLength: 8, nationalMaxLength: 8 },
223
+ { iso: "SX", name: "Sint Maarten", dialCode: "+1721", nationalMinLength: 7, nationalMaxLength: 15 },
224
+ { iso: "SY", name: "Siria", dialCode: "+963", nationalMinLength: 7, nationalMaxLength: 15 },
225
+ { iso: "SB", name: "Solomon Islands", dialCode: "+677", nationalMinLength: 7, nationalMaxLength: 15 },
226
+ { iso: "SO", name: "Somalia", dialCode: "+252", nationalMinLength: 7, nationalMaxLength: 15 },
227
+ { iso: "GS", name: "South Georgia", dialCode: "+500", nationalMinLength: 7, nationalMaxLength: 15 },
228
+ { iso: "LK", name: "Sri Lanka", dialCode: "+94", nationalMinLength: 7, nationalMaxLength: 15 },
229
+ { iso: "SZ", name: "Suazilandia", dialCode: "+268", nationalMinLength: 7, nationalMaxLength: 15 },
230
+ { iso: "ZA", name: "Sudáfrica", dialCode: "+27", nationalMinLength: 9, nationalMaxLength: 9 },
231
+ { iso: "SD", name: "Sudán", dialCode: "+249", nationalMinLength: 7, nationalMaxLength: 15 },
232
+ { iso: "SS", name: "Sudán del Sur", dialCode: "+211", nationalMinLength: 7, nationalMaxLength: 15 },
233
+ { iso: "SE", name: "Suecia", dialCode: "+46", nationalMinLength: 9, nationalMaxLength: 9 },
234
+ { iso: "CH", name: "Suiza", dialCode: "+41", nationalMinLength: 9, nationalMaxLength: 9 },
235
+ { iso: "SR", name: "Surinam", dialCode: "+597", nationalMinLength: 7, nationalMaxLength: 15 },
236
+ { iso: "SJ", name: "Svalbard and Jan Mayen Islands", dialCode: "+47", nationalMinLength: 7, nationalMaxLength: 15 },
237
+ { iso: "TH", name: "Tailandia", dialCode: "+66", nationalMinLength: 9, nationalMaxLength: 9 },
238
+ { iso: "TW", name: "Taiwán", dialCode: "+886", nationalMinLength: 9, nationalMaxLength: 9 },
239
+ { iso: "TZ", name: "Tanzania", dialCode: "+255", nationalMinLength: 7, nationalMaxLength: 15 },
240
+ { iso: "TJ", name: "Tayikistán", dialCode: "+992", nationalMinLength: 7, nationalMaxLength: 15 },
241
+ { iso: "TL", name: "Timor Oriental", dialCode: "+670", nationalMinLength: 7, nationalMaxLength: 15 },
242
+ { iso: "TG", name: "Togo", dialCode: "+228", nationalMinLength: 7, nationalMaxLength: 15 },
243
+ { iso: "TK", name: "Tokelau", dialCode: "+690", nationalMinLength: 7, nationalMaxLength: 15 },
244
+ { iso: "TO", name: "Tonga", dialCode: "+676", nationalMinLength: 7, nationalMaxLength: 15 },
245
+ { iso: "TT", name: "Trinidad y Tobago", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
246
+ { iso: "TN", name: "Túnez", dialCode: "+216", nationalMinLength: 7, nationalMaxLength: 15 },
247
+ { iso: "TM", name: "Turkmenistán", dialCode: "+993", nationalMinLength: 7, nationalMaxLength: 15 },
248
+ { iso: "TC", name: "Turks and Caicos Islands", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
249
+ { iso: "TR", name: "Turquía", dialCode: "+90", nationalMinLength: 10, nationalMaxLength: 10 },
250
+ { iso: "TV", name: "Tuvalu", dialCode: "+688", nationalMinLength: 7, nationalMaxLength: 15 },
251
+ { iso: "UA", name: "Ucrania", dialCode: "+380", nationalMinLength: 7, nationalMaxLength: 15 },
252
+ { iso: "UG", name: "Uganda", dialCode: "+256", nationalMinLength: 7, nationalMaxLength: 15 },
253
+ { iso: "UM", name: "United States Minor Outlying Islands", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
254
+ { iso: "UZ", name: "Uzbekistán", dialCode: "+998", nationalMinLength: 7, nationalMaxLength: 15 },
255
+ { iso: "VU", name: "Vanuatu", dialCode: "+678", nationalMinLength: 7, nationalMaxLength: 15 },
256
+ { iso: "VN", name: "Vietnam", dialCode: "+84", nationalMinLength: 9, nationalMaxLength: 10 },
257
+ { iso: "VG", name: "Virgin Islands (British)", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
258
+ { iso: "VI", name: "Virgin Islands (US)", dialCode: "+1", nationalMinLength: 7, nationalMaxLength: 15 },
259
+ { iso: "WF", name: "Wallis and Futuna Islands", dialCode: "+681", nationalMinLength: 7, nationalMaxLength: 15 },
260
+ { iso: "YE", name: "Yemen", dialCode: "+967", nationalMinLength: 7, nationalMaxLength: 15 },
261
+ { iso: "DJ", name: "Yibuti", dialCode: "+253", nationalMinLength: 7, nationalMaxLength: 15 },
262
+ { iso: "ZM", name: "Zambia", dialCode: "+260", nationalMinLength: 7, nationalMaxLength: 15 },
263
+ { iso: "ZW", name: "Zimbabue", dialCode: "+263", nationalMinLength: 7, nationalMaxLength: 15 },
264
+ ];
265
+
266
+ export const DEFAULT_PHONE_COUNTRY_ISO = "EC";
267
+
268
+ export function getPhoneCountryByIso(
269
+ iso?: string | null,
270
+ ): PhoneCountry | undefined {
271
+ if (!iso) return undefined;
272
+ return PHONE_COUNTRIES.find((c) => c.iso === iso);
273
+ }
274
+
275
+ export function getPhoneCountryByDialCode(dialCode: string): PhoneCountry | undefined {
276
+ const normalized = dialCode.startsWith("+") ? dialCode : `+${dialCode}`;
277
+ return [...PHONE_COUNTRIES]
278
+ .sort((a, b) => b.dialCode.length - a.dialCode.length)
279
+ .find((c) => normalized.startsWith(c.dialCode));
280
+ }
281
+
282
+ function digitsOnly(value: string): string {
283
+ return value.replace(/\D/g, "");
284
+ }
285
+
286
+ function findCountryByDialDigits(digits: string): PhoneCountry | undefined {
287
+ return [...PHONE_COUNTRIES]
288
+ .sort((a, b) => b.dialCode.length - a.dialCode.length)
289
+ .find((country) => digits.startsWith(digitsOnly(country.dialCode)));
290
+ }
291
+
292
+ /** Quita el 0 de red después del código de país (+59309… → +5939…). */
293
+ function stripTrunkZeroAfterDialCode(digits: string): string {
294
+ const matched = matchInternationalE164(digits);
295
+ return matched ? digitsOnly(matched) : digits;
296
+ }
297
+
298
+ /**
299
+ * Si los dígitos ya traen código de país y el tramo nacional cierra,
300
+ * devuelve E.164. Evita tratar 3001234567 (Colombia) como +30 Grecia.
301
+ */
302
+ function matchInternationalE164(digits: string): string | null {
303
+ const country = findCountryByDialDigits(digits);
304
+ if (!country) return null;
305
+ const dial = digitsOnly(country.dialCode);
306
+ if (digits.length <= dial.length) return null;
307
+ let national = digits.slice(dial.length);
308
+ if (national.startsWith("0")) national = national.slice(1);
309
+ if (
310
+ national.length >= country.nationalMinLength &&
311
+ national.length <= country.nationalMaxLength
312
+ ) {
313
+ return `+${dial}${national}`;
314
+ }
315
+ return null;
316
+ }
317
+
318
+ export function looksLikePhoneNumber(value: string): boolean {
319
+ const trimmed = value.trim();
320
+ if (!trimmed) return false;
321
+ if (/^https?:\/\//i.test(trimmed)) return false;
322
+ if (/www\./i.test(trimmed)) return false;
323
+ if (/\.[a-z]{2,}(\/|$)/i.test(trimmed) && !trimmed.startsWith("+")) {
324
+ return false;
325
+ }
326
+
327
+ const digits = digitsOnly(trimmed);
328
+ if (digits.length < 7 || digits.length > 15) return false;
329
+
330
+ return /^\+?[\d\s().-]+$/.test(trimmed);
331
+ }
332
+
333
+ /** Formato E.164 (+593…) al guardar prospectos de marketing. */
334
+ export function normalizePhoneForStorage(
335
+ raw: string,
336
+ countryCode?: string | null,
337
+ ): string {
338
+ const trimmed = raw.trim();
339
+ if (!trimmed) return "";
340
+
341
+ const international =
342
+ trimmed.startsWith("+") || trimmed.startsWith("00");
343
+ let digits = digitsOnly(
344
+ international && trimmed.startsWith("00") ? trimmed.slice(2) : trimmed,
345
+ );
346
+ if (!digits) return trimmed;
347
+
348
+ const asInternational = matchInternationalE164(digits);
349
+ if (asInternational) return asInternational;
350
+
351
+ if (international) {
352
+ return `+${stripTrunkZeroAfterDialCode(digits)}`;
353
+ }
354
+
355
+ const phoneCountry =
356
+ getPhoneCountryByIso(countryCode) ??
357
+ getPhoneCountryByIso(DEFAULT_PHONE_COUNTRY_ISO)!;
358
+ const dialDigits = digitsOnly(phoneCountry.dialCode);
359
+
360
+ if (digits.startsWith(dialDigits)) {
361
+ return `+${stripTrunkZeroAfterDialCode(digits)}`;
362
+ }
363
+
364
+ if (digits.startsWith("0")) {
365
+ digits = digits.slice(1);
366
+ }
367
+
368
+ if (
369
+ digits.length >= phoneCountry.nationalMinLength &&
370
+ digits.length <= phoneCountry.nationalMaxLength
371
+ ) {
372
+ return `${phoneCountry.dialCode}${digits}`;
373
+ }
374
+
375
+ if (digits.length >= 10) {
376
+ return `+${stripTrunkZeroAfterDialCode(digits)}`;
377
+ }
378
+
379
+ return `${phoneCountry.dialCode}${digits}`;
380
+ }
@@ -0,0 +1,66 @@
1
+ import {
2
+ DEFAULT_PHONE_COUNTRY_ISO,
3
+ getPhoneCountryByDialCode,
4
+ getPhoneCountryByIso,
5
+ type PhoneCountry,
6
+ } from "./phone-countries";
7
+
8
+ /** Normaliza a E.164 básico para auth (+593..., +1..., etc.). */
9
+ export function normalizePhoneToE164(raw: string): string {
10
+ const trimmed = raw.trim().replace(/\s/g, "");
11
+ if (!trimmed) return "";
12
+ if (trimmed.startsWith("+")) return trimmed;
13
+ if (trimmed.startsWith("00")) return `+${trimmed.slice(2)}`;
14
+ return `+${trimmed}`;
15
+ }
16
+
17
+ export function digitsOnly(value: string): string {
18
+ return value.replace(/\D/g, "");
19
+ }
20
+
21
+ export function buildPhoneE164(country: PhoneCountry, nationalNumber: string): string {
22
+ const national = digitsOnly(nationalNumber);
23
+ if (!national) return "";
24
+ return `${country.dialCode}${national}`;
25
+ }
26
+
27
+ export type ParsedPhone = {
28
+ country: PhoneCountry;
29
+ nationalNumber: string;
30
+ e164: string;
31
+ };
32
+
33
+ export function parsePhoneE164(
34
+ raw: string,
35
+ fallbackIso = DEFAULT_PHONE_COUNTRY_ISO,
36
+ ): ParsedPhone | null {
37
+ const e164 = normalizePhoneToE164(raw);
38
+ const fallback = getPhoneCountryByIso(fallbackIso) ?? getPhoneCountryByIso("EC");
39
+ if (!fallback) return null;
40
+
41
+ if (!e164) {
42
+ return { country: fallback, nationalNumber: "", e164: "" };
43
+ }
44
+
45
+ const country = getPhoneCountryByDialCode(e164) ?? fallback;
46
+ const nationalNumber = digitsOnly(e164.slice(country.dialCode.length));
47
+ return {
48
+ country,
49
+ nationalNumber,
50
+ e164: nationalNumber ? buildPhoneE164(country, nationalNumber) : "",
51
+ };
52
+ }
53
+
54
+ export function isNationalNumberValidForCountry(
55
+ country: PhoneCountry,
56
+ nationalNumber: string,
57
+ ): boolean {
58
+ const len = digitsOnly(nationalNumber).length;
59
+ return len >= country.nationalMinLength && len <= country.nationalMaxLength;
60
+ }
61
+
62
+ export function isPhoneE164ReadyForSms(raw: string): boolean {
63
+ const parsed = parsePhoneE164(raw);
64
+ if (!parsed?.e164 || !parsed.nationalNumber) return false;
65
+ return isNationalNumberValidForCountry(parsed.country, parsed.nationalNumber);
66
+ }
@@ -0,0 +1,31 @@
1
+ import "@testing-library/jest-dom/vitest";
2
+ import { cleanup } from "@testing-library/react";
3
+ import { afterEach } from "vitest";
4
+
5
+ afterEach(() => {
6
+ cleanup();
7
+ });
8
+
9
+ class ResizeObserverMock {
10
+ observe() {}
11
+ unobserve() {}
12
+ disconnect() {}
13
+ }
14
+
15
+ globalThis.ResizeObserver = ResizeObserverMock;
16
+
17
+ if (!Element.prototype.scrollIntoView) {
18
+ Element.prototype.scrollIntoView = () => {};
19
+ }
20
+
21
+ if (!HTMLElement.prototype.hasPointerCapture) {
22
+ HTMLElement.prototype.hasPointerCapture = () => false;
23
+ }
24
+
25
+ if (!HTMLElement.prototype.setPointerCapture) {
26
+ HTMLElement.prototype.setPointerCapture = () => {};
27
+ }
28
+
29
+ if (!HTMLElement.prototype.releasePointerCapture) {
30
+ HTMLElement.prototype.releasePointerCapture = () => {};
31
+ }