ti2-ventrata 1.0.37 → 1.0.38
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/index.js +99 -17
- package/index.test.js +1 -1
- package/package.json +1 -1
- package/resolvers/availability.js +2 -0
- package/resolvers/booking.js +3 -0
- package/resolvers/product.js +1 -0
- package/utils/countrymapping.js +206 -0
package/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const { translateProduct } = require('./resolvers/product');
|
|
|
8
8
|
const { translateAvailability } = require('./resolvers/availability');
|
|
9
9
|
const { translateBooking } = require('./resolvers/booking');
|
|
10
10
|
const { translatePickupPoint } = require('./resolvers/pickup-point');
|
|
11
|
+
const { validateCountry } = require('./utils/countrymapping');
|
|
11
12
|
|
|
12
13
|
const CONCURRENCY = 3; // is this ok ?
|
|
13
14
|
|
|
@@ -71,6 +72,24 @@ class Plugin {
|
|
|
71
72
|
this.errorPathsAxiosAny = () => ([]); // 200's that should be errors
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
static getSettlementMethod(reference, availableSettlementMethods = []) {
|
|
76
|
+
// Determine settlement method from product's available methods
|
|
77
|
+
let settlementMethod = 'DEFERRED'; // default fallback
|
|
78
|
+
|
|
79
|
+
if (reference && availableSettlementMethods.includes('VOUCHER')) {
|
|
80
|
+
settlementMethod = 'VOUCHER';
|
|
81
|
+
} else if (reference && availableSettlementMethods.includes('DIRECT')) {
|
|
82
|
+
settlementMethod = 'DIRECT';
|
|
83
|
+
} else if (availableSettlementMethods.includes('DEFERRED')) {
|
|
84
|
+
settlementMethod = 'DEFERRED';
|
|
85
|
+
} else if (availableSettlementMethods.length > 0) {
|
|
86
|
+
// Use the first available settlement method if none of the preferred ones are available
|
|
87
|
+
[settlementMethod] = availableSettlementMethods;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return settlementMethod;
|
|
91
|
+
}
|
|
92
|
+
|
|
74
93
|
async validateToken({
|
|
75
94
|
axios,
|
|
76
95
|
token: {
|
|
@@ -223,12 +242,22 @@ class Plugin {
|
|
|
223
242
|
const url = `${endpoint || this.endpoint}/availability`;
|
|
224
243
|
const availability = (
|
|
225
244
|
await Promise.map(productIds, async (productId, ix) => {
|
|
245
|
+
// Fetch product data to get settlement methods
|
|
246
|
+
const productUrl = `${endpoint || this.endpoint}/products/${productId}`;
|
|
247
|
+
const productResult = await axios({
|
|
248
|
+
method: 'get',
|
|
249
|
+
url: productUrl,
|
|
250
|
+
headers,
|
|
251
|
+
});
|
|
252
|
+
const settlementMethods = R.path(['data', 'settlementMethods'], productResult) || [];
|
|
253
|
+
// eslint-disable-next-line max-len
|
|
254
|
+
const unitsWithId = units[ix].filter(u => u.unitId).map(u => ({ id: u.unitId, quantity: u.quantity }));
|
|
226
255
|
const data = {
|
|
227
256
|
productId,
|
|
228
257
|
optionId: optionIds[ix],
|
|
229
258
|
localDateStart,
|
|
230
259
|
localDateEnd,
|
|
231
|
-
|
|
260
|
+
...(unitsWithId && unitsWithId.length > 0 && { units: unitsWithId }),
|
|
232
261
|
};
|
|
233
262
|
if (currency) data.currency = currency;
|
|
234
263
|
const result = R.path(['data'], await axios({
|
|
@@ -247,6 +276,7 @@ class Plugin {
|
|
|
247
276
|
currency,
|
|
248
277
|
unitsWithQuantity: units[ix],
|
|
249
278
|
jwtKey: this.jwtKey,
|
|
279
|
+
settlementMethods,
|
|
250
280
|
},
|
|
251
281
|
}));
|
|
252
282
|
}, { concurrency: CONCURRENCY })
|
|
@@ -297,18 +327,28 @@ class Plugin {
|
|
|
297
327
|
acceptLanguage,
|
|
298
328
|
resellerId,
|
|
299
329
|
});
|
|
300
|
-
const url = `${endpoint || this.endpoint}/availability/calendar`;
|
|
301
330
|
const availability = (
|
|
302
331
|
await Promise.map(productIds, async (productId, ix) => {
|
|
332
|
+
// Fetch product data to get settlement methods
|
|
333
|
+
const productUrl = `${endpoint || this.endpoint}/products/${productId}`;
|
|
334
|
+
const productResult = await axios({
|
|
335
|
+
method: 'get',
|
|
336
|
+
url: productUrl,
|
|
337
|
+
headers,
|
|
338
|
+
});
|
|
339
|
+
const settlementMethods = R.path(['data', 'settlementMethods'], productResult) || [];
|
|
340
|
+
// eslint-disable-next-line max-len
|
|
341
|
+
const unitsWithId = units[ix].filter(u => u.unitId).map(u => ({ id: u.unitId, quantity: u.quantity }));
|
|
303
342
|
const data = {
|
|
304
343
|
productId,
|
|
305
344
|
optionId: optionIds[ix],
|
|
306
345
|
localDateStart,
|
|
307
346
|
localDateEnd,
|
|
308
347
|
// units is required here to get the total pricing for the calendar
|
|
309
|
-
|
|
348
|
+
...(unitsWithId && unitsWithId.length > 0 && { units: unitsWithId }),
|
|
310
349
|
};
|
|
311
350
|
if (currency) data.currency = currency;
|
|
351
|
+
const url = `${endpoint || this.endpoint}/availability/calendar`;
|
|
312
352
|
const result = await axios({
|
|
313
353
|
method: 'post',
|
|
314
354
|
url,
|
|
@@ -319,6 +359,14 @@ class Plugin {
|
|
|
319
359
|
rootValue: avail,
|
|
320
360
|
typeDefs: availTypeDefs,
|
|
321
361
|
query: availQuery,
|
|
362
|
+
variableValues: {
|
|
363
|
+
productId,
|
|
364
|
+
optionId: optionIds[ix],
|
|
365
|
+
currency,
|
|
366
|
+
unitsWithQuantity: units[ix],
|
|
367
|
+
jwtKey: this.jwtKey,
|
|
368
|
+
settlementMethods,
|
|
369
|
+
},
|
|
322
370
|
}));
|
|
323
371
|
}, { concurrency: CONCURRENCY })
|
|
324
372
|
);
|
|
@@ -381,18 +429,23 @@ class Plugin {
|
|
|
381
429
|
})),
|
|
382
430
|
R.values,
|
|
383
431
|
R.groupBy(o => {
|
|
384
|
-
const [
|
|
432
|
+
const [, unitItemIndex] = o.field.id.split('|');
|
|
385
433
|
return unitItemIndex;
|
|
386
434
|
}),
|
|
387
435
|
), unitItemCFV);
|
|
388
436
|
}
|
|
389
437
|
}
|
|
438
|
+
|
|
439
|
+
const settlementMethod = Plugin.getSettlementMethod(
|
|
440
|
+
reference,
|
|
441
|
+
dataForCreateBooking.settlementMethods,
|
|
442
|
+
);
|
|
390
443
|
let booking = R.path(['data'], await axios({
|
|
391
444
|
method: rebookingId ? 'patch' : 'post',
|
|
392
445
|
url: `${endpoint || this.endpoint}/bookings${rebookingId ? `/${rebookingId}` : ''}`,
|
|
393
446
|
data: {
|
|
394
447
|
orderId,
|
|
395
|
-
settlementMethod
|
|
448
|
+
settlementMethod,
|
|
396
449
|
...dataForCreateBooking,
|
|
397
450
|
notes,
|
|
398
451
|
...(pickupPoint ? { pickupRequested: true, pickupPointId: pickupPoint } : {}),
|
|
@@ -401,24 +454,53 @@ class Plugin {
|
|
|
401
454
|
}));
|
|
402
455
|
// for booking update, we may not need to confirm again
|
|
403
456
|
if (!booking.utcConfirmedAt && !partialOrder) {
|
|
457
|
+
const validatedCountry = validateCountry(R.pathOr('', ['country'], holder));
|
|
458
|
+
|
|
459
|
+
// Ensure required contact fields are present
|
|
460
|
+
const firstName = R.pathOr('', ['name'], holder);
|
|
461
|
+
const lastName = R.pathOr('', ['surname'], holder);
|
|
462
|
+
// Validate required fields
|
|
463
|
+
if (!firstName.trim() || !lastName.trim()) {
|
|
464
|
+
console.error('Customer validation failed: First and last name are required');
|
|
465
|
+
throw new Error('Customer validation failed: First and last name are required');
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const fullName = `${firstName.trim()} ${lastName.trim()}`;
|
|
469
|
+
const emailAddress = R.pathOr(null, ['emailAddress'], holder);
|
|
470
|
+
const phoneNumber = R.pathOr('', ['phone'], holder);
|
|
471
|
+
|
|
472
|
+
// Basic email validation
|
|
473
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
474
|
+
if (emailAddress && !emailRegex.test(emailAddress)) {
|
|
475
|
+
console.error(`Customer validation failed: Invalid email format: ${emailAddress}`);
|
|
476
|
+
throw new Error('Customer validation failed: Invalid email address format');
|
|
477
|
+
}
|
|
404
478
|
const dataForConfirmBooking = {
|
|
405
479
|
contact: {
|
|
406
|
-
fullName
|
|
407
|
-
emailAddress
|
|
408
|
-
phoneNumber:
|
|
409
|
-
locales: R.path(['locales'], holder),
|
|
410
|
-
country:
|
|
480
|
+
fullName,
|
|
481
|
+
emailAddress,
|
|
482
|
+
phoneNumber: phoneNumber || '',
|
|
483
|
+
locales: R.path(['locales'], holder) || ['en'],
|
|
484
|
+
country: validatedCountry,
|
|
485
|
+
postalCode: R.path(['zipCode'], holder) || '',
|
|
411
486
|
},
|
|
412
487
|
notes,
|
|
413
488
|
resellerReference: reference,
|
|
414
|
-
settlementMethod
|
|
489
|
+
settlementMethod,
|
|
490
|
+
...(emailAddress ? { emailReceipt: true } : {}), // Send email receipt since we have a valid email address
|
|
415
491
|
};
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
492
|
+
|
|
493
|
+
try {
|
|
494
|
+
booking = R.path(['data'], await axios({
|
|
495
|
+
method: 'post',
|
|
496
|
+
url: `${endpoint || this.endpoint}/bookings/${booking.uuid}/confirm`,
|
|
497
|
+
data: dataForConfirmBooking,
|
|
498
|
+
headers,
|
|
499
|
+
}));
|
|
500
|
+
} catch (error) {
|
|
501
|
+
console.error('Booking confirmation failed:', (error.response && error.response.data) ? error.response.data : error.message);
|
|
502
|
+
throw error;
|
|
503
|
+
}
|
|
422
504
|
}
|
|
423
505
|
return ({
|
|
424
506
|
booking: await translateBooking({
|
package/index.test.js
CHANGED
|
@@ -33,7 +33,7 @@ const app = new Plugin({
|
|
|
33
33
|
describe('search tests', () => {
|
|
34
34
|
let products;
|
|
35
35
|
let testProduct = {
|
|
36
|
-
productName: 'Edinburgh
|
|
36
|
+
productName: '[ED3D] Edinburgh 3 Day Pass',
|
|
37
37
|
};
|
|
38
38
|
const token = {
|
|
39
39
|
apiKey: process.env.ti2_ventrata_apiKey,
|
package/package.json
CHANGED
|
@@ -12,6 +12,7 @@ const resolvers = {
|
|
|
12
12
|
currency,
|
|
13
13
|
unitsWithQuantity,
|
|
14
14
|
jwtKey,
|
|
15
|
+
settlementMethods,
|
|
15
16
|
} = args;
|
|
16
17
|
if (!jwtKey) return null;
|
|
17
18
|
return jwt.sign({
|
|
@@ -19,6 +20,7 @@ const resolvers = {
|
|
|
19
20
|
optionId,
|
|
20
21
|
availabilityId: root.id,
|
|
21
22
|
currency,
|
|
23
|
+
settlementMethods,
|
|
22
24
|
unitItems: R.chain(u => new Array(u.quantity).fill(1).map(() => ({
|
|
23
25
|
unitId: u.unitId,
|
|
24
26
|
})), unitsWithQuantity),
|
package/resolvers/booking.js
CHANGED
|
@@ -35,6 +35,9 @@ const resolvers = {
|
|
|
35
35
|
fullName: R.pathOr('', ['contact', 'fullName'], root),
|
|
36
36
|
phoneNumber: R.pathOr('', ['contact', 'phoneNumber'], root),
|
|
37
37
|
emailAddress: R.pathOr('', ['contact', 'emailAddress'], root),
|
|
38
|
+
country: R.pathOr('', ['contact', 'country'], root),
|
|
39
|
+
locales: R.pathOr('', ['locales'], root),
|
|
40
|
+
postalCode: R.pathOr('', ['contact', 'postalCode'], root),
|
|
38
41
|
}),
|
|
39
42
|
notes: root => root.notes || '',
|
|
40
43
|
price: root => root.pricing,
|
package/resolvers/product.js
CHANGED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// Country name to ISO 3166-1 alpha-2 code mapping
|
|
2
|
+
const countryNameToCode = {
|
|
3
|
+
// Major countries
|
|
4
|
+
'UNITED STATES': 'US',
|
|
5
|
+
'USA': 'US',
|
|
6
|
+
'AMERICA': 'US',
|
|
7
|
+
'UNITED STATES OF AMERICA': 'US',
|
|
8
|
+
'UNITED KINGDOM': 'GB',
|
|
9
|
+
'UK': 'GB',
|
|
10
|
+
'GREAT BRITAIN': 'GB',
|
|
11
|
+
'ENGLAND': 'GB',
|
|
12
|
+
'BRITAIN': 'GB',
|
|
13
|
+
'CANADA': 'CA',
|
|
14
|
+
'FRANCE': 'FR',
|
|
15
|
+
'GERMANY': 'DE',
|
|
16
|
+
'ITALY': 'IT',
|
|
17
|
+
'SPAIN': 'ES',
|
|
18
|
+
'AUSTRALIA': 'AU',
|
|
19
|
+
'NEW ZEALAND': 'NZ',
|
|
20
|
+
'JAPAN': 'JP',
|
|
21
|
+
'CHINA': 'CN',
|
|
22
|
+
'INDIA': 'IN',
|
|
23
|
+
'BRAZIL': 'BR',
|
|
24
|
+
'MEXICO': 'MX',
|
|
25
|
+
'RUSSIA': 'RU',
|
|
26
|
+
'SOUTH AFRICA': 'ZA',
|
|
27
|
+
'ARGENTINA': 'AR',
|
|
28
|
+
'CHILE': 'CL',
|
|
29
|
+
'COLOMBIA': 'CO',
|
|
30
|
+
'PERU': 'PE',
|
|
31
|
+
'VENEZUELA': 'VE',
|
|
32
|
+
'ECUADOR': 'EC',
|
|
33
|
+
'BOLIVIA': 'BO',
|
|
34
|
+
'PARAGUAY': 'PY',
|
|
35
|
+
'URUGUAY': 'UY',
|
|
36
|
+
'GUYANA': 'GY',
|
|
37
|
+
'SURINAME': 'SR',
|
|
38
|
+
'NETHERLANDS': 'NL',
|
|
39
|
+
'BELGIUM': 'BE',
|
|
40
|
+
'SWITZERLAND': 'CH',
|
|
41
|
+
'AUSTRIA': 'AT',
|
|
42
|
+
'PORTUGAL': 'PT',
|
|
43
|
+
'SWEDEN': 'SE',
|
|
44
|
+
'NORWAY': 'NO',
|
|
45
|
+
'DENMARK': 'DK',
|
|
46
|
+
'FINLAND': 'FI',
|
|
47
|
+
'ICELAND': 'IS',
|
|
48
|
+
'IRELAND': 'IE',
|
|
49
|
+
'POLAND': 'PL',
|
|
50
|
+
'CZECH REPUBLIC': 'CZ',
|
|
51
|
+
'SLOVAKIA': 'SK',
|
|
52
|
+
'HUNGARY': 'HU',
|
|
53
|
+
'ROMANIA': 'RO',
|
|
54
|
+
'BULGARIA': 'BG',
|
|
55
|
+
'CROATIA': 'HR',
|
|
56
|
+
'SLOVENIA': 'SI',
|
|
57
|
+
'SERBIA': 'RS',
|
|
58
|
+
'BOSNIA AND HERZEGOVINA': 'BA',
|
|
59
|
+
'MONTENEGRO': 'ME',
|
|
60
|
+
'ALBANIA': 'AL',
|
|
61
|
+
'MACEDONIA': 'MK',
|
|
62
|
+
'GREECE': 'GR',
|
|
63
|
+
'TURKEY': 'TR',
|
|
64
|
+
'CYPRUS': 'CY',
|
|
65
|
+
'MALTA': 'MT',
|
|
66
|
+
'LUXEMBOURG': 'LU',
|
|
67
|
+
'ESTONIA': 'EE',
|
|
68
|
+
'LATVIA': 'LV',
|
|
69
|
+
'LITHUANIA': 'LT',
|
|
70
|
+
'UKRAINE': 'UA',
|
|
71
|
+
'BELARUS': 'BY',
|
|
72
|
+
'MOLDOVA': 'MD',
|
|
73
|
+
'GEORGIA': 'GE',
|
|
74
|
+
'ARMENIA': 'AM',
|
|
75
|
+
'AZERBAIJAN': 'AZ',
|
|
76
|
+
'KAZAKHSTAN': 'KZ',
|
|
77
|
+
'UZBEKISTAN': 'UZ',
|
|
78
|
+
'TURKMENISTAN': 'TM',
|
|
79
|
+
'KYRGYZSTAN': 'KG',
|
|
80
|
+
'TAJIKISTAN': 'TJ',
|
|
81
|
+
'AFGHANISTAN': 'AF',
|
|
82
|
+
'PAKISTAN': 'PK',
|
|
83
|
+
'BANGLADESH': 'BD',
|
|
84
|
+
'SRI LANKA': 'LK',
|
|
85
|
+
'NEPAL': 'NP',
|
|
86
|
+
'BHUTAN': 'BT',
|
|
87
|
+
'MALDIVES': 'MV',
|
|
88
|
+
'MYANMAR': 'MM',
|
|
89
|
+
'THAILAND': 'TH',
|
|
90
|
+
'LAOS': 'LA',
|
|
91
|
+
'VIETNAM': 'VN',
|
|
92
|
+
'CAMBODIA': 'KH',
|
|
93
|
+
'MALAYSIA': 'MY',
|
|
94
|
+
'SINGAPORE': 'SG',
|
|
95
|
+
'INDONESIA': 'ID',
|
|
96
|
+
'PHILIPPINES': 'PH',
|
|
97
|
+
'BRUNEI': 'BN',
|
|
98
|
+
'TIMOR-LESTE': 'TL',
|
|
99
|
+
'SOUTH KOREA': 'KR',
|
|
100
|
+
'NORTH KOREA': 'KP',
|
|
101
|
+
'MONGOLIA': 'MN',
|
|
102
|
+
'TAIWAN': 'TW',
|
|
103
|
+
'HONG KONG': 'HK',
|
|
104
|
+
'MACAU': 'MO',
|
|
105
|
+
'ISRAEL': 'IL',
|
|
106
|
+
'PALESTINE': 'PS',
|
|
107
|
+
'JORDAN': 'JO',
|
|
108
|
+
'LEBANON': 'LB',
|
|
109
|
+
'SYRIA': 'SY',
|
|
110
|
+
'IRAQ': 'IQ',
|
|
111
|
+
'IRAN': 'IR',
|
|
112
|
+
'KUWAIT': 'KW',
|
|
113
|
+
'SAUDI ARABIA': 'SA',
|
|
114
|
+
'BAHRAIN': 'BH',
|
|
115
|
+
'QATAR': 'QA',
|
|
116
|
+
'UAE': 'AE',
|
|
117
|
+
'UNITED ARAB EMIRATES': 'AE',
|
|
118
|
+
'OMAN': 'OM',
|
|
119
|
+
'YEMEN': 'YE',
|
|
120
|
+
'EGYPT': 'EG',
|
|
121
|
+
'LIBYA': 'LY',
|
|
122
|
+
'TUNISIA': 'TN',
|
|
123
|
+
'ALGERIA': 'DZ',
|
|
124
|
+
'MOROCCO': 'MA',
|
|
125
|
+
'SUDAN': 'SD',
|
|
126
|
+
'SOUTH SUDAN': 'SS',
|
|
127
|
+
'ETHIOPIA': 'ET',
|
|
128
|
+
'ERITREA': 'ER',
|
|
129
|
+
'DJIBOUTI': 'DJ',
|
|
130
|
+
'SOMALIA': 'SO',
|
|
131
|
+
'KENYA': 'KE',
|
|
132
|
+
'UGANDA': 'UG',
|
|
133
|
+
'TANZANIA': 'TZ',
|
|
134
|
+
'RWANDA': 'RW',
|
|
135
|
+
'BURUNDI': 'BI',
|
|
136
|
+
'DEMOCRATIC REPUBLIC OF THE CONGO': 'CD',
|
|
137
|
+
'CONGO': 'CG',
|
|
138
|
+
'CENTRAL AFRICAN REPUBLIC': 'CF',
|
|
139
|
+
'CHAD': 'TD',
|
|
140
|
+
'CAMEROON': 'CM',
|
|
141
|
+
'EQUATORIAL GUINEA': 'GQ',
|
|
142
|
+
'GABON': 'GA',
|
|
143
|
+
'SAO TOME AND PRINCIPE': 'ST',
|
|
144
|
+
'NIGERIA': 'NG',
|
|
145
|
+
'NIGER': 'NE',
|
|
146
|
+
'MALI': 'ML',
|
|
147
|
+
'BURKINA FASO': 'BF',
|
|
148
|
+
'SENEGAL': 'SN',
|
|
149
|
+
'GAMBIA': 'GM',
|
|
150
|
+
'GUINEA-BISSAU': 'GW',
|
|
151
|
+
'GUINEA': 'GN',
|
|
152
|
+
'SIERRA LEONE': 'SL',
|
|
153
|
+
'LIBERIA': 'LR',
|
|
154
|
+
'IVORY COAST': 'CI',
|
|
155
|
+
'GHANA': 'GH',
|
|
156
|
+
'TOGO': 'TG',
|
|
157
|
+
'BENIN': 'BJ',
|
|
158
|
+
'MAURITANIA': 'MR',
|
|
159
|
+
'WESTERN SAHARA': 'EH',
|
|
160
|
+
'CAPE VERDE': 'CV',
|
|
161
|
+
'MADAGASCAR': 'MG',
|
|
162
|
+
'MAURITIUS': 'MU',
|
|
163
|
+
'SEYCHELLES': 'SC',
|
|
164
|
+
'COMOROS': 'KM',
|
|
165
|
+
'MAYOTTE': 'YT',
|
|
166
|
+
'REUNION': 'RE',
|
|
167
|
+
'BOTSWANA': 'BW',
|
|
168
|
+
'NAMIBIA': 'NA',
|
|
169
|
+
'ZAMBIA': 'ZM',
|
|
170
|
+
'ZIMBABWE': 'ZW',
|
|
171
|
+
'MALAWI': 'MW',
|
|
172
|
+
'MOZAMBIQUE': 'MZ',
|
|
173
|
+
'SWAZILAND': 'SZ',
|
|
174
|
+
'LESOTHO': 'LS',
|
|
175
|
+
'ANGOLA': 'AO',
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const validateCountry = countryInput => {
|
|
179
|
+
// Validate and format country code - ensure it's a valid ISO 3166-1 alpha-2 code
|
|
180
|
+
let validatedCountry = 'US'; // Default fallback
|
|
181
|
+
|
|
182
|
+
if (countryInput && typeof countryInput === 'string') {
|
|
183
|
+
const cleanInput = countryInput.trim().toUpperCase();
|
|
184
|
+
|
|
185
|
+
// First, check if it's already a valid 2-letter ISO code
|
|
186
|
+
if (cleanInput.length === 2 && /^[A-Z]{2}$/.test(cleanInput)) {
|
|
187
|
+
validatedCountry = cleanInput;
|
|
188
|
+
} else if (countryNameToCode[cleanInput]) {
|
|
189
|
+
// Then, try to convert country name to ISO code
|
|
190
|
+
validatedCountry = countryNameToCode[cleanInput];
|
|
191
|
+
} else {
|
|
192
|
+
// If it looks like a partial match, try to find it
|
|
193
|
+
const matchedEntry = Object.entries(countryNameToCode).find(([name]) =>
|
|
194
|
+
name.includes(cleanInput) || cleanInput.includes(name));
|
|
195
|
+
if (matchedEntry) {
|
|
196
|
+
const [, countryCode] = matchedEntry;
|
|
197
|
+
validatedCountry = countryCode;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return validatedCountry;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
module.exports = {
|
|
205
|
+
validateCountry,
|
|
206
|
+
};
|