@traky-app/ui 1.6.3 → 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.
- package/package.json +15 -1
- package/registry/traky/json/city-select.json +19 -0
- package/registry/traky/json/country-select.json +24 -0
- package/registry/traky/json/geo-search-select.json +24 -0
- package/registry/traky/json/geo-selectors.json +21 -0
- package/registry/traky/json/google-maps-address-picker.json +22 -0
- package/registry/traky/json/lib-geo-city-codes.json +14 -0
- package/registry/traky/json/lib-geo-countries.json +14 -0
- package/registry/traky/json/lib-geo-csc.json +17 -0
- package/registry/traky/json/lib-geo-locations.json +14 -0
- package/registry/traky/json/lib-geo-overrides.json +14 -0
- package/registry/traky/json/lib-geo-types.json +14 -0
- package/registry/traky/json/lib-google-maps-address.json +14 -0
- package/registry/traky/json/lib-phone-countries.json +14 -0
- package/registry/traky/json/lib-phone-e164.json +14 -0
- package/registry/traky/json/lib-utils.json +1 -1
- package/registry/traky/json/office-address-form-dialog.json +17 -0
- package/registry/traky/json/office-address-form.json +31 -0
- package/registry/traky/json/phone-number-input.json +25 -0
- package/registry/traky/json/state-select.json +19 -0
- package/registry/traky/lib/geo-city-codes.ts +234 -0
- package/registry/traky/lib/geo-countries.ts +75 -0
- package/registry/traky/lib/geo-csc.ts +82 -0
- package/registry/traky/lib/geo-locations.ts +171 -0
- package/registry/traky/lib/geo-overrides.ts +375 -0
- package/registry/traky/lib/geo-types.ts +34 -0
- package/registry/traky/lib/google-maps-address.ts +100 -0
- package/registry/traky/lib/phone-countries.ts +380 -0
- package/registry/traky/lib/phone-e164.ts +66 -0
- package/registry/traky/lib/utils.ts +24 -0
- package/registry/traky/registry.json +180 -0
- package/registry/traky/ui/city-select.tsx +113 -0
- package/registry/traky/ui/country-select.tsx +233 -0
- package/registry/traky/ui/geo-search-select.tsx +136 -0
- package/registry/traky/ui/geo-selectors.tsx +162 -0
- package/registry/traky/ui/google-maps-address-picker.tsx +422 -0
- package/registry/traky/ui/office-address-form-dialog.tsx +16 -0
- package/registry/traky/ui/office-address-form.tsx +868 -0
- package/registry/traky/ui/phone-number-input.tsx +253 -0
- package/registry/traky/ui/state-select.tsx +107 -0
- package/src/lib/geo-city-codes.ts +234 -0
- package/src/lib/geo-countries.ts +75 -0
- package/src/lib/geo-csc.ts +82 -0
- package/src/lib/geo-locations.ts +171 -0
- package/src/lib/geo-overrides.ts +375 -0
- package/src/lib/geo-types.ts +34 -0
- package/src/lib/google-maps-address.ts +100 -0
- package/src/lib/phone-countries.ts +380 -0
- package/src/lib/phone-e164.ts +66 -0
- package/src/test/setup.ts +31 -0
- package/src/ui/__tests__/geo-selects.test.tsx +504 -0
- package/src/ui/city-select.tsx +113 -0
- package/src/ui/country-select.tsx +233 -0
- package/src/ui/geo-search-select.tsx +136 -0
- package/src/ui/geo-selectors.tsx +162 -0
- package/src/ui/google-maps-address-picker.tsx +422 -0
- package/src/ui/office-address-form-dialog.tsx +16 -0
- package/src/ui/office-address-form.tsx +877 -0
- package/src/ui/phone-number-input.tsx +253 -0
- package/src/ui/state-select.tsx +107 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Catálogo geo vía `@countrystatecity/countries-browser` (CDN / fetch).
|
|
3
|
+
* Países, provincias y ciudades; sin JSON embebido en el repo.
|
|
4
|
+
*/
|
|
5
|
+
import {
|
|
6
|
+
getCitiesOfState,
|
|
7
|
+
getCountries,
|
|
8
|
+
getStatesOfCountry,
|
|
9
|
+
} from "@countrystatecity/countries-browser";
|
|
10
|
+
|
|
11
|
+
export type GeoApiOption = {
|
|
12
|
+
code: string;
|
|
13
|
+
name: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type GeoApiCityOption = GeoApiOption & {
|
|
17
|
+
stateCode?: string;
|
|
18
|
+
stateName?: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export async function getGeoCountries(): Promise<GeoApiOption[]> {
|
|
22
|
+
try {
|
|
23
|
+
const countries = await getCountries();
|
|
24
|
+
return (countries ?? []).map((c) => ({
|
|
25
|
+
code: c.iso2,
|
|
26
|
+
name: c.name,
|
|
27
|
+
}));
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error("getGeoCountries:", error);
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function getGeoStates(
|
|
35
|
+
countryCode: string,
|
|
36
|
+
): Promise<GeoApiOption[]> {
|
|
37
|
+
const cc = countryCode.trim().toUpperCase();
|
|
38
|
+
if (!cc) return [];
|
|
39
|
+
try {
|
|
40
|
+
const states = await getStatesOfCountry(cc);
|
|
41
|
+
return (states ?? [])
|
|
42
|
+
.filter((s) => Boolean(s.iso2?.trim() && s.name?.trim()))
|
|
43
|
+
.map((s) => ({
|
|
44
|
+
code: s.iso2.trim(),
|
|
45
|
+
name: s.name.trim(),
|
|
46
|
+
}));
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error("getGeoStates:", cc, error);
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function getGeoCities(params: {
|
|
54
|
+
countryCode: string;
|
|
55
|
+
stateCode?: string | null;
|
|
56
|
+
q?: string | null;
|
|
57
|
+
limit?: number;
|
|
58
|
+
}): Promise<GeoApiCityOption[]> {
|
|
59
|
+
const cc = params.countryCode.trim().toUpperCase();
|
|
60
|
+
const stateCode = params.stateCode?.trim();
|
|
61
|
+
if (!cc || !stateCode) return [];
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const cities = await getCitiesOfState(cc, stateCode);
|
|
65
|
+
const q = params.q?.trim().toLowerCase();
|
|
66
|
+
let list = cities ?? [];
|
|
67
|
+
if (q && q.length >= 2) {
|
|
68
|
+
list = list.filter((c) => c.name.toLowerCase().includes(q));
|
|
69
|
+
}
|
|
70
|
+
if (params.limit != null && params.limit > 0) {
|
|
71
|
+
list = list.slice(0, params.limit);
|
|
72
|
+
}
|
|
73
|
+
return list.map((c) => ({
|
|
74
|
+
code: c.name,
|
|
75
|
+
name: c.name,
|
|
76
|
+
stateCode,
|
|
77
|
+
}));
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error("getGeoCities:", error);
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { getGeoCities, getGeoStates } from "./geo-csc";
|
|
2
|
+
import { resolveCityCode } from "./geo-city-codes";
|
|
3
|
+
import {
|
|
4
|
+
filterStatesForCountry,
|
|
5
|
+
mergeExtraCitiesForState,
|
|
6
|
+
resolveCscStateCode,
|
|
7
|
+
resolveStateDisplayName,
|
|
8
|
+
} from "./geo-overrides";
|
|
9
|
+
import {
|
|
10
|
+
DEPARTMENT_NAME_AS_CITY_COUNTRY_CODES,
|
|
11
|
+
type GeoCityOption,
|
|
12
|
+
type GeoOption,
|
|
13
|
+
} from "./geo-types";
|
|
14
|
+
import { normalizeSearchText } from "./utils";
|
|
15
|
+
|
|
16
|
+
function mapApiCity(
|
|
17
|
+
countryCode: string,
|
|
18
|
+
city: {
|
|
19
|
+
name: string;
|
|
20
|
+
stateCode?: string;
|
|
21
|
+
stateName?: string;
|
|
22
|
+
},
|
|
23
|
+
stateNameByCode: Map<string, string>,
|
|
24
|
+
): GeoCityOption {
|
|
25
|
+
const stateCode = city.stateCode ?? "";
|
|
26
|
+
return {
|
|
27
|
+
code: resolveCityCode(countryCode, city.name),
|
|
28
|
+
name: city.name,
|
|
29
|
+
stateCode: stateCode || undefined,
|
|
30
|
+
stateName:
|
|
31
|
+
city.stateName ??
|
|
32
|
+
(stateCode ? stateNameByCode.get(stateCode) : undefined),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function fetchStates(countryCode: string): Promise<GeoOption[]> {
|
|
37
|
+
const cc = countryCode.trim().toUpperCase();
|
|
38
|
+
if (!cc) return [];
|
|
39
|
+
const states = await getGeoStates(cc);
|
|
40
|
+
return filterStatesForCountry(
|
|
41
|
+
cc,
|
|
42
|
+
states.map((s) => ({ code: s.code, name: s.name })),
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function fetchCitiesForStateCode(
|
|
47
|
+
cc: string,
|
|
48
|
+
stateCode: string,
|
|
49
|
+
): Promise<
|
|
50
|
+
Array<{ name: string; stateCode?: string; stateName?: string }>
|
|
51
|
+
> {
|
|
52
|
+
const primary = await getGeoCities({
|
|
53
|
+
countryCode: cc,
|
|
54
|
+
stateCode,
|
|
55
|
+
});
|
|
56
|
+
if (primary.length > 0) return primary;
|
|
57
|
+
|
|
58
|
+
const mapped = resolveCscStateCode(cc, stateCode);
|
|
59
|
+
if (mapped !== stateCode) {
|
|
60
|
+
return getGeoCities({
|
|
61
|
+
countryCode: cc,
|
|
62
|
+
stateCode: mapped,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function fetchCities(
|
|
69
|
+
countryCode: string,
|
|
70
|
+
stateCode?: string | null,
|
|
71
|
+
): Promise<GeoCityOption[]> {
|
|
72
|
+
const cc = countryCode.trim().toUpperCase();
|
|
73
|
+
if (!cc) return [];
|
|
74
|
+
|
|
75
|
+
// Sin estado no listamos el país entero (catálogo grande).
|
|
76
|
+
if (!stateCode?.trim()) return [];
|
|
77
|
+
|
|
78
|
+
const code = stateCode.trim();
|
|
79
|
+
const states = await fetchStates(cc);
|
|
80
|
+
const stateNameByCode = new Map(
|
|
81
|
+
states.map((s) => [
|
|
82
|
+
s.code,
|
|
83
|
+
resolveStateDisplayName(cc, s.code, s.name),
|
|
84
|
+
]),
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const apiStateCode = resolveCscStateCode(cc, code);
|
|
88
|
+
const state =
|
|
89
|
+
states.find((s) => s.code === code) ||
|
|
90
|
+
states.find((s) => s.code === apiStateCode);
|
|
91
|
+
|
|
92
|
+
const fromCatalogRaw = await fetchCitiesForStateCode(cc, code);
|
|
93
|
+
const fromCatalog = fromCatalogRaw.map((c) =>
|
|
94
|
+
mapApiCity(cc, c, stateNameByCode),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const cities = mergeExtraCitiesForState(
|
|
98
|
+
cc,
|
|
99
|
+
code,
|
|
100
|
+
mergeExtraCitiesForState(cc, apiStateCode, fromCatalog),
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const withDepartment = (() => {
|
|
104
|
+
if (
|
|
105
|
+
!DEPARTMENT_NAME_AS_CITY_COUNTRY_CODES.has(cc) ||
|
|
106
|
+
!state?.name?.trim()
|
|
107
|
+
) {
|
|
108
|
+
return cities;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const departmentName = resolveStateDisplayName(
|
|
112
|
+
cc,
|
|
113
|
+
state.code,
|
|
114
|
+
state.name,
|
|
115
|
+
).trim();
|
|
116
|
+
const alreadyListed = cities.some(
|
|
117
|
+
(city) => city.name.trim().toLowerCase() === departmentName.toLowerCase(),
|
|
118
|
+
);
|
|
119
|
+
if (alreadyListed) return cities;
|
|
120
|
+
|
|
121
|
+
return [
|
|
122
|
+
...cities,
|
|
123
|
+
{
|
|
124
|
+
code: resolveCityCode(cc, departmentName),
|
|
125
|
+
name: departmentName,
|
|
126
|
+
stateCode: state.code,
|
|
127
|
+
stateName: state.name,
|
|
128
|
+
},
|
|
129
|
+
].sort((a, b) => a.name.localeCompare(b.name, "es"));
|
|
130
|
+
})();
|
|
131
|
+
|
|
132
|
+
if (cc === "ES" && code !== apiStateCode) {
|
|
133
|
+
const postalName = resolveStateDisplayName(
|
|
134
|
+
cc,
|
|
135
|
+
code,
|
|
136
|
+
state?.name ?? code,
|
|
137
|
+
);
|
|
138
|
+
return withDepartment.map((city) => ({
|
|
139
|
+
...city,
|
|
140
|
+
stateCode: code,
|
|
141
|
+
stateName: postalName,
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return withDepartment;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function geoNameKey(value: string): string {
|
|
149
|
+
return normalizeSearchText(value).replace(/[^a-z0-9]/g, "");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function findStateByName(
|
|
153
|
+
states: GeoOption[],
|
|
154
|
+
name?: string | null,
|
|
155
|
+
): GeoOption | undefined {
|
|
156
|
+
if (!name?.trim()) return undefined;
|
|
157
|
+
const q = geoNameKey(name);
|
|
158
|
+
const code = name.trim().toUpperCase();
|
|
159
|
+
return states.find(
|
|
160
|
+
(s) => geoNameKey(s.name) === q || s.code.toUpperCase() === code,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function findCityByName(
|
|
165
|
+
cities: GeoCityOption[],
|
|
166
|
+
name?: string | null,
|
|
167
|
+
): GeoCityOption | undefined {
|
|
168
|
+
if (!name?.trim()) return undefined;
|
|
169
|
+
const q = geoNameKey(name);
|
|
170
|
+
return cities.find((c) => geoNameKey(c.name) === q);
|
|
171
|
+
}
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import type { GeoCityOption, GeoOption } from "./geo-types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Provincias de España (ISO 3166-2) para direcciones postales.
|
|
5
|
+
* `cscStateCode` es el código de comunidad en el catálogo geo (API / dataset).
|
|
6
|
+
* Castilla y León no trae ciudades en `CL`; el catálogo las agrupa en `LE`.
|
|
7
|
+
*/
|
|
8
|
+
export const ES_PROVINCES: Array<{
|
|
9
|
+
code: string;
|
|
10
|
+
name: string;
|
|
11
|
+
cscStateCode: string;
|
|
12
|
+
}> = [
|
|
13
|
+
{ code: "C", name: "A Coruña", cscStateCode: "GA" },
|
|
14
|
+
{ code: "VI", name: "Álava", cscStateCode: "PV" },
|
|
15
|
+
{ code: "AB", name: "Albacete", cscStateCode: "CM" },
|
|
16
|
+
{ code: "A", name: "Alicante", cscStateCode: "VC" },
|
|
17
|
+
{ code: "AL", name: "Almería", cscStateCode: "AN" },
|
|
18
|
+
{ code: "O", name: "Asturias", cscStateCode: "AS" },
|
|
19
|
+
{ code: "AV", name: "Ávila", cscStateCode: "LE" },
|
|
20
|
+
{ code: "BA", name: "Badajoz", cscStateCode: "EX" },
|
|
21
|
+
{ code: "PM", name: "Baleares", cscStateCode: "PM" },
|
|
22
|
+
{ code: "B", name: "Barcelona", cscStateCode: "CT" },
|
|
23
|
+
{ code: "BU", name: "Burgos", cscStateCode: "LE" },
|
|
24
|
+
{ code: "CC", name: "Cáceres", cscStateCode: "EX" },
|
|
25
|
+
{ code: "CA", name: "Cádiz", cscStateCode: "AN" },
|
|
26
|
+
{ code: "S", name: "Cantabria", cscStateCode: "CB" },
|
|
27
|
+
{ code: "CS", name: "Castellón", cscStateCode: "VC" },
|
|
28
|
+
{ code: "CE", name: "Ceuta", cscStateCode: "CE" },
|
|
29
|
+
{ code: "CR", name: "Ciudad Real", cscStateCode: "CM" },
|
|
30
|
+
{ code: "CO", name: "Córdoba", cscStateCode: "AN" },
|
|
31
|
+
{ code: "CU", name: "Cuenca", cscStateCode: "CM" },
|
|
32
|
+
{ code: "SS", name: "Gipuzkoa", cscStateCode: "PV" },
|
|
33
|
+
{ code: "GI", name: "Girona", cscStateCode: "CT" },
|
|
34
|
+
{ code: "GR", name: "Granada", cscStateCode: "AN" },
|
|
35
|
+
{ code: "GU", name: "Guadalajara", cscStateCode: "CM" },
|
|
36
|
+
{ code: "H", name: "Huelva", cscStateCode: "AN" },
|
|
37
|
+
{ code: "HU", name: "Huesca", cscStateCode: "AR" },
|
|
38
|
+
{ code: "J", name: "Jaén", cscStateCode: "AN" },
|
|
39
|
+
{ code: "LO", name: "La Rioja", cscStateCode: "RI" },
|
|
40
|
+
{ code: "GC", name: "Las Palmas", cscStateCode: "CN" },
|
|
41
|
+
{ code: "LE", name: "León", cscStateCode: "LE" },
|
|
42
|
+
{ code: "L", name: "Lleida", cscStateCode: "CT" },
|
|
43
|
+
{ code: "LU", name: "Lugo", cscStateCode: "GA" },
|
|
44
|
+
{ code: "M", name: "Madrid", cscStateCode: "MD" },
|
|
45
|
+
{ code: "MA", name: "Málaga", cscStateCode: "AN" },
|
|
46
|
+
{ code: "ML", name: "Melilla", cscStateCode: "ML" },
|
|
47
|
+
{ code: "MU", name: "Murcia", cscStateCode: "MC" },
|
|
48
|
+
{ code: "NA", name: "Navarra", cscStateCode: "NC" },
|
|
49
|
+
{ code: "OR", name: "Ourense", cscStateCode: "GA" },
|
|
50
|
+
{ code: "P", name: "Palencia", cscStateCode: "LE" },
|
|
51
|
+
{ code: "PO", name: "Pontevedra", cscStateCode: "GA" },
|
|
52
|
+
{ code: "SA", name: "Salamanca", cscStateCode: "LE" },
|
|
53
|
+
{ code: "TF", name: "Santa Cruz de Tenerife", cscStateCode: "CN" },
|
|
54
|
+
{ code: "SG", name: "Segovia", cscStateCode: "LE" },
|
|
55
|
+
{ code: "SE", name: "Sevilla", cscStateCode: "AN" },
|
|
56
|
+
{ code: "SO", name: "Soria", cscStateCode: "LE" },
|
|
57
|
+
{ code: "T", name: "Tarragona", cscStateCode: "CT" },
|
|
58
|
+
{ code: "TE", name: "Teruel", cscStateCode: "AR" },
|
|
59
|
+
{ code: "TO", name: "Toledo", cscStateCode: "CM" },
|
|
60
|
+
{ code: "V", name: "Valencia", cscStateCode: "VC" },
|
|
61
|
+
{ code: "VA", name: "Valladolid", cscStateCode: "LE" },
|
|
62
|
+
{ code: "BI", name: "Bizkaia", cscStateCode: "PV" },
|
|
63
|
+
{ code: "ZA", name: "Zamora", cscStateCode: "LE" },
|
|
64
|
+
{ code: "Z", name: "Zaragoza", cscStateCode: "AR" },
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
/** Limita el selector de región a códigos concretos (p. ej. naciones del Reino Unido). */
|
|
68
|
+
export const STATE_LIST_FILTER_BY_COUNTRY: Partial<Record<string, string[]>> = {
|
|
69
|
+
GB: ["ENG", "SCT", "WLS", "NIR"],
|
|
70
|
+
VE: [
|
|
71
|
+
"Z",
|
|
72
|
+
"B",
|
|
73
|
+
"C",
|
|
74
|
+
"D",
|
|
75
|
+
"E",
|
|
76
|
+
"F",
|
|
77
|
+
"G",
|
|
78
|
+
"H",
|
|
79
|
+
"Y",
|
|
80
|
+
"A",
|
|
81
|
+
"I",
|
|
82
|
+
"J",
|
|
83
|
+
"X",
|
|
84
|
+
"K",
|
|
85
|
+
"M",
|
|
86
|
+
"N",
|
|
87
|
+
"L",
|
|
88
|
+
"O",
|
|
89
|
+
"P",
|
|
90
|
+
"R",
|
|
91
|
+
"T",
|
|
92
|
+
"S",
|
|
93
|
+
"U",
|
|
94
|
+
"V",
|
|
95
|
+
],
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/** Nombres de región en español para el selector. */
|
|
99
|
+
export const STATE_DISPLAY_NAME_OVERRIDES: Partial<
|
|
100
|
+
Record<string, Record<string, string>>
|
|
101
|
+
> = {
|
|
102
|
+
ES: {
|
|
103
|
+
AN: "Andalucía",
|
|
104
|
+
AR: "Aragón",
|
|
105
|
+
AS: "Asturias",
|
|
106
|
+
PM: "Islas Baleares",
|
|
107
|
+
PV: "País Vasco",
|
|
108
|
+
CN: "Canarias",
|
|
109
|
+
CB: "Cantabria",
|
|
110
|
+
CL: "Castilla y León",
|
|
111
|
+
CM: "Castilla-La Mancha",
|
|
112
|
+
CT: "Cataluña",
|
|
113
|
+
CE: "Ceuta",
|
|
114
|
+
EX: "Extremadura",
|
|
115
|
+
GA: "Galicia",
|
|
116
|
+
RI: "La Rioja",
|
|
117
|
+
MD: "Comunidad de Madrid",
|
|
118
|
+
ML: "Melilla",
|
|
119
|
+
MC: "Región de Murcia",
|
|
120
|
+
NC: "Navarra",
|
|
121
|
+
VC: "Comunidad Valenciana",
|
|
122
|
+
},
|
|
123
|
+
VE: {
|
|
124
|
+
X: "La Guaira",
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/** Ciudades que faltan en el catálogo para una región concreta. */
|
|
129
|
+
export const EXTRA_CITIES_BY_STATE: Partial<
|
|
130
|
+
Record<string, Partial<Record<string, GeoCityOption[]>>>
|
|
131
|
+
> = {
|
|
132
|
+
ES: {
|
|
133
|
+
B: [
|
|
134
|
+
{
|
|
135
|
+
code: "LHO",
|
|
136
|
+
name: "L'Hospitalet de Llobregat",
|
|
137
|
+
stateCode: "B",
|
|
138
|
+
stateName: "Barcelona",
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
code: "BDL",
|
|
142
|
+
name: "Badalona",
|
|
143
|
+
stateCode: "B",
|
|
144
|
+
stateName: "Barcelona",
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
code: "BCN",
|
|
148
|
+
name: "Barcelona",
|
|
149
|
+
stateCode: "B",
|
|
150
|
+
stateName: "Barcelona",
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
AS: [
|
|
154
|
+
{
|
|
155
|
+
code: "OVD",
|
|
156
|
+
name: "Oviedo",
|
|
157
|
+
stateCode: "AS",
|
|
158
|
+
stateName: "Asturias",
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
code: "GIJ",
|
|
162
|
+
name: "Gijón",
|
|
163
|
+
stateCode: "AS",
|
|
164
|
+
stateName: "Asturias",
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
code: "AVS",
|
|
168
|
+
name: "Avilés",
|
|
169
|
+
stateCode: "AS",
|
|
170
|
+
stateName: "Asturias",
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
CL: [
|
|
174
|
+
{
|
|
175
|
+
code: "LEN",
|
|
176
|
+
name: "León",
|
|
177
|
+
stateCode: "CL",
|
|
178
|
+
stateName: "Castilla y León",
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
code: "BUR",
|
|
182
|
+
name: "Burgos",
|
|
183
|
+
stateCode: "CL",
|
|
184
|
+
stateName: "Castilla y León",
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
code: "SLM",
|
|
188
|
+
name: "Salamanca",
|
|
189
|
+
stateCode: "CL",
|
|
190
|
+
stateName: "Castilla y León",
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
code: "VLL",
|
|
194
|
+
name: "Valladolid",
|
|
195
|
+
stateCode: "CL",
|
|
196
|
+
stateName: "Castilla y León",
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
code: "SEG",
|
|
200
|
+
name: "Segovia",
|
|
201
|
+
stateCode: "CL",
|
|
202
|
+
stateName: "Castilla y León",
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
code: "SOR",
|
|
206
|
+
name: "Soria",
|
|
207
|
+
stateCode: "CL",
|
|
208
|
+
stateName: "Castilla y León",
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
code: "ZMR",
|
|
212
|
+
name: "Zamora",
|
|
213
|
+
stateCode: "CL",
|
|
214
|
+
stateName: "Castilla y León",
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
code: "AVL",
|
|
218
|
+
name: "Ávila",
|
|
219
|
+
stateCode: "CL",
|
|
220
|
+
stateName: "Castilla y León",
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
code: "PAL",
|
|
224
|
+
name: "Palencia",
|
|
225
|
+
stateCode: "CL",
|
|
226
|
+
stateName: "Castilla y León",
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
},
|
|
230
|
+
VE: {
|
|
231
|
+
C: [
|
|
232
|
+
{
|
|
233
|
+
code: "SFA",
|
|
234
|
+
name: "San Fernando de Apure",
|
|
235
|
+
stateCode: "C",
|
|
236
|
+
stateName: "Apure",
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
code: "GUA",
|
|
240
|
+
name: "Guasdualito",
|
|
241
|
+
stateCode: "C",
|
|
242
|
+
stateName: "Apure",
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
G: [
|
|
246
|
+
{
|
|
247
|
+
code: "NGN",
|
|
248
|
+
name: "Naguanagua",
|
|
249
|
+
stateCode: "G",
|
|
250
|
+
stateName: "Carabobo",
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
B: [
|
|
254
|
+
{
|
|
255
|
+
code: "LEC",
|
|
256
|
+
name: "Lechería",
|
|
257
|
+
stateCode: "B",
|
|
258
|
+
stateName: "Anzoátegui",
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
F: [
|
|
262
|
+
{
|
|
263
|
+
code: "PZO",
|
|
264
|
+
name: "Puerto Ordaz",
|
|
265
|
+
stateCode: "F",
|
|
266
|
+
stateName: "Bolívar",
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
},
|
|
270
|
+
GB: {
|
|
271
|
+
NIR: [
|
|
272
|
+
{
|
|
273
|
+
code: "BEL",
|
|
274
|
+
name: "Belfast",
|
|
275
|
+
stateCode: "NIR",
|
|
276
|
+
stateName: "Northern Ireland",
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
code: "DRY",
|
|
280
|
+
name: "Derry",
|
|
281
|
+
stateCode: "NIR",
|
|
282
|
+
stateName: "Northern Ireland",
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
code: "LSB",
|
|
286
|
+
name: "Lisburn",
|
|
287
|
+
stateCode: "NIR",
|
|
288
|
+
stateName: "Northern Ireland",
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
code: "NEW",
|
|
292
|
+
name: "Newry",
|
|
293
|
+
stateCode: "NIR",
|
|
294
|
+
stateName: "Northern Ireland",
|
|
295
|
+
},
|
|
296
|
+
],
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
export function resolveCscStateCode(
|
|
301
|
+
countryCode: string,
|
|
302
|
+
stateCode: string,
|
|
303
|
+
): string {
|
|
304
|
+
const cc = countryCode.trim().toUpperCase();
|
|
305
|
+
const code = stateCode.trim();
|
|
306
|
+
if (!code) return code;
|
|
307
|
+
if (cc === "ES") {
|
|
308
|
+
return (
|
|
309
|
+
ES_PROVINCES.find((province) => province.code === code)?.cscStateCode ??
|
|
310
|
+
code
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
return code;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export function resolveStateDisplayName(
|
|
317
|
+
countryCode: string,
|
|
318
|
+
stateCode: string,
|
|
319
|
+
defaultName: string,
|
|
320
|
+
): string {
|
|
321
|
+
const cc = countryCode.trim().toUpperCase();
|
|
322
|
+
const code = stateCode.trim();
|
|
323
|
+
const provinceName = ES_PROVINCES.find(
|
|
324
|
+
(province) => cc === "ES" && province.code === code,
|
|
325
|
+
)?.name;
|
|
326
|
+
return (
|
|
327
|
+
provinceName ||
|
|
328
|
+
STATE_DISPLAY_NAME_OVERRIDES[cc]?.[code]?.trim() ||
|
|
329
|
+
defaultName
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export function filterStatesForCountry(
|
|
334
|
+
countryCode: string,
|
|
335
|
+
states: GeoOption[],
|
|
336
|
+
): GeoOption[] {
|
|
337
|
+
const cc = countryCode.trim().toUpperCase();
|
|
338
|
+
if (cc === "ES") {
|
|
339
|
+
return ES_PROVINCES.map((province) => ({
|
|
340
|
+
code: province.code,
|
|
341
|
+
name: province.name,
|
|
342
|
+
}));
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const allowed = STATE_LIST_FILTER_BY_COUNTRY[cc];
|
|
346
|
+
const filtered = allowed?.length
|
|
347
|
+
? states.filter((state) => new Set(allowed).has(state.code))
|
|
348
|
+
: states;
|
|
349
|
+
|
|
350
|
+
return filtered.map((state) => ({
|
|
351
|
+
...state,
|
|
352
|
+
name: resolveStateDisplayName(cc, state.code, state.name),
|
|
353
|
+
}));
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export function mergeExtraCitiesForState(
|
|
357
|
+
countryCode: string,
|
|
358
|
+
stateCode: string,
|
|
359
|
+
cities: GeoCityOption[],
|
|
360
|
+
): GeoCityOption[] {
|
|
361
|
+
const extras = EXTRA_CITIES_BY_STATE[countryCode]?.[stateCode];
|
|
362
|
+
if (!extras?.length) return cities;
|
|
363
|
+
|
|
364
|
+
const seen = new Set(cities.map((city) => city.name.trim().toLowerCase()));
|
|
365
|
+
const merged = [...cities];
|
|
366
|
+
|
|
367
|
+
for (const city of extras) {
|
|
368
|
+
const key = city.name.trim().toLowerCase();
|
|
369
|
+
if (seen.has(key)) continue;
|
|
370
|
+
seen.add(key);
|
|
371
|
+
merged.push(city);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return merged.sort((a, b) => a.name.localeCompare(b.name, "es"));
|
|
375
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type GeoOption = {
|
|
2
|
+
code: string;
|
|
3
|
+
name: string;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export type GeoCityOption = GeoOption & {
|
|
7
|
+
stateCode?: string;
|
|
8
|
+
stateName?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Países donde el formulario usa catálogo CSC (CDN) con provincia/estado primero.
|
|
13
|
+
*/
|
|
14
|
+
export function usesGeoSearch(countryCode?: string | null): boolean {
|
|
15
|
+
return Boolean(countryCode?.trim() && countryCode.trim().length === 2);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Provincia/estado antes que ciudad (evita listar ciudades de país entero). */
|
|
19
|
+
export function isStateFirstCountry(countryCode?: string | null): boolean {
|
|
20
|
+
return usesGeoSearch(countryCode);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Países donde el nombre del departamento se ofrece también como ciudad
|
|
25
|
+
* (p. ej. Chontales en Nicaragua no existe como municipio en el catálogo).
|
|
26
|
+
*/
|
|
27
|
+
export const DEPARTMENT_NAME_AS_CITY_COUNTRY_CODES = new Set([
|
|
28
|
+
"CO",
|
|
29
|
+
"ES",
|
|
30
|
+
"GB",
|
|
31
|
+
"NI",
|
|
32
|
+
"PE",
|
|
33
|
+
"VE",
|
|
34
|
+
]);
|