arky-sdk 0.9.12 → 0.9.16
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/README.md +16 -12
- package/dist/admin-C-ZTxvz3.d.ts +1544 -0
- package/dist/admin-Dm2WRN6q.d.cts +1544 -0
- package/dist/admin.cjs +980 -433
- package/dist/admin.cjs.map +1 -1
- package/dist/admin.d.cts +3 -3
- package/dist/admin.d.ts +3 -3
- package/dist/admin.js +980 -433
- package/dist/admin.js.map +1 -1
- package/dist/{api-D4lMmvF0.d.cts → api-D37IpMSq.d.cts} +1415 -671
- package/dist/{api-D4lMmvF0.d.ts → api-D37IpMSq.d.ts} +1415 -671
- package/dist/index.cjs +1315 -547
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1315 -547
- package/dist/index.js.map +1 -1
- package/dist/{index-Be8suRwP.d.ts → inventory-DdN96PX3.d.cts} +6 -4
- package/dist/{index-BS2x278C.d.cts → inventory-Dh1RevEb.d.ts} +6 -4
- package/dist/storefront.cjs +1210 -658
- package/dist/storefront.cjs.map +1 -1
- package/dist/storefront.d.cts +88 -285
- package/dist/storefront.d.ts +88 -285
- package/dist/storefront.js +1207 -659
- package/dist/storefront.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils.cjs +198 -16
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +18 -2
- package/dist/utils.d.ts +18 -2
- package/dist/utils.js +191 -17
- package/dist/utils.js.map +1 -1
- package/package.json +4 -5
- package/dist/admin-D8HiRDCl.d.cts +0 -1536
- package/dist/admin-Dnnv18wN.d.ts +0 -1536
- package/scripts/contract-admin.mjs +0 -137
- package/scripts/contract-storefront.mjs +0 -296
package/dist/storefront.js
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
import { atom, computed, map } from 'nanostores';
|
|
2
2
|
|
|
3
3
|
// src/utils/orderItems.ts
|
|
4
|
-
function
|
|
4
|
+
function sanitizePublicCheckoutItems(items) {
|
|
5
5
|
return items.map((item) => {
|
|
6
|
-
if (
|
|
7
|
-
return
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
if (item.type === "product") {
|
|
7
|
+
return {
|
|
8
|
+
type: "product",
|
|
9
|
+
...item.id ? { id: item.id } : {},
|
|
10
|
+
product_id: item.product_id,
|
|
11
|
+
variant_id: item.variant_id,
|
|
12
|
+
quantity: item.quantity
|
|
13
|
+
};
|
|
11
14
|
}
|
|
12
|
-
return {
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
return {
|
|
16
|
+
type: "service",
|
|
17
|
+
...item.id ? { id: item.id } : {},
|
|
18
|
+
service_id: item.service_id,
|
|
19
|
+
provider_id: item.provider_id,
|
|
20
|
+
slots: item.slots,
|
|
21
|
+
...item.forms ? { forms: item.forms } : {}
|
|
22
|
+
};
|
|
19
23
|
});
|
|
20
24
|
}
|
|
21
25
|
|
|
@@ -40,10 +44,9 @@ var createActionApi = (apiConfig) => ({
|
|
|
40
44
|
COMMON_ACTION_KEYS,
|
|
41
45
|
async track(params) {
|
|
42
46
|
try {
|
|
43
|
-
const key = "key" in params && params.key ? params.key : params.type;
|
|
44
47
|
await apiConfig.httpClient.post(
|
|
45
48
|
`/v1/storefront/${apiConfig.storeId}/actions/track`,
|
|
46
|
-
{ key, payload: params.payload }
|
|
49
|
+
{ key: params.key, payload: params.payload }
|
|
47
50
|
);
|
|
48
51
|
} catch {
|
|
49
52
|
}
|
|
@@ -51,6 +54,47 @@ var createActionApi = (apiConfig) => ({
|
|
|
51
54
|
});
|
|
52
55
|
var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
53
56
|
const base = (storeId = apiConfig.storeId) => `/v1/storefront/${storeId}`;
|
|
57
|
+
const pendingVerifications = /* @__PURE__ */ new Map();
|
|
58
|
+
function persistIdentification(result) {
|
|
59
|
+
const issued = result.token;
|
|
60
|
+
if (issued?.token) {
|
|
61
|
+
updateContactSession(() => ({
|
|
62
|
+
access_token: issued.token,
|
|
63
|
+
contact: result.contact,
|
|
64
|
+
store: result.store,
|
|
65
|
+
market: result.market
|
|
66
|
+
}));
|
|
67
|
+
} else {
|
|
68
|
+
if (result.verification_challenge) {
|
|
69
|
+
pendingVerifications.set(result.verification_challenge.challenge_id, {
|
|
70
|
+
store: result.store,
|
|
71
|
+
market: result.market
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
updateContactSession(
|
|
75
|
+
(current) => current ? {
|
|
76
|
+
...current,
|
|
77
|
+
contact: result.contact,
|
|
78
|
+
store: result.store,
|
|
79
|
+
market: result.market
|
|
80
|
+
} : null
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
async function submitIdentification(path, params, options) {
|
|
86
|
+
const store_id = apiConfig.storeId;
|
|
87
|
+
const result = await apiConfig.httpClient.post(
|
|
88
|
+
`${base(store_id)}/account/${path}`,
|
|
89
|
+
{
|
|
90
|
+
store_id,
|
|
91
|
+
market: params?.market || apiConfig.market || null,
|
|
92
|
+
email: params?.email
|
|
93
|
+
},
|
|
94
|
+
options
|
|
95
|
+
);
|
|
96
|
+
return persistIdentification(result);
|
|
97
|
+
}
|
|
54
98
|
return {
|
|
55
99
|
store: {
|
|
56
100
|
getStore(options) {
|
|
@@ -58,7 +102,10 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
58
102
|
},
|
|
59
103
|
location: {
|
|
60
104
|
getCountries(options) {
|
|
61
|
-
return apiConfig.httpClient.get(
|
|
105
|
+
return apiConfig.httpClient.get(
|
|
106
|
+
`/v1/platform/countries`,
|
|
107
|
+
options
|
|
108
|
+
);
|
|
62
109
|
},
|
|
63
110
|
getCountry(countryCode, options) {
|
|
64
111
|
return apiConfig.httpClient.get(
|
|
@@ -67,18 +114,30 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
67
114
|
);
|
|
68
115
|
},
|
|
69
116
|
list(options) {
|
|
70
|
-
return apiConfig.httpClient.get(
|
|
117
|
+
return apiConfig.httpClient.get(
|
|
118
|
+
`${base()}/locations`,
|
|
119
|
+
options
|
|
120
|
+
);
|
|
71
121
|
},
|
|
72
122
|
get(id, options) {
|
|
73
|
-
return apiConfig.httpClient.get(
|
|
123
|
+
return apiConfig.httpClient.get(
|
|
124
|
+
`${base()}/locations/${id}`,
|
|
125
|
+
options
|
|
126
|
+
);
|
|
74
127
|
}
|
|
75
128
|
},
|
|
76
129
|
market: {
|
|
77
130
|
list(options) {
|
|
78
|
-
return apiConfig.httpClient.get(
|
|
131
|
+
return apiConfig.httpClient.get(
|
|
132
|
+
`${base()}/markets`,
|
|
133
|
+
options
|
|
134
|
+
);
|
|
79
135
|
},
|
|
80
136
|
get(id, options) {
|
|
81
|
-
return apiConfig.httpClient.get(
|
|
137
|
+
return apiConfig.httpClient.get(
|
|
138
|
+
`${base()}/markets/${id}`,
|
|
139
|
+
options
|
|
140
|
+
);
|
|
82
141
|
}
|
|
83
142
|
}
|
|
84
143
|
},
|
|
@@ -86,11 +145,9 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
86
145
|
collection: {
|
|
87
146
|
get(params, options) {
|
|
88
147
|
const store_id = params.store_id || apiConfig.storeId;
|
|
89
|
-
|
|
90
|
-
throw new Error("GetCollectionParams requires id");
|
|
91
|
-
}
|
|
148
|
+
const identifier = params.id !== void 0 ? params.id : `${store_id}:${params.key}`;
|
|
92
149
|
return apiConfig.httpClient.get(
|
|
93
|
-
`${base(store_id)}/collections/${
|
|
150
|
+
`${base(store_id)}/collections/${identifier}`,
|
|
94
151
|
options
|
|
95
152
|
);
|
|
96
153
|
}
|
|
@@ -108,10 +165,13 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
108
165
|
},
|
|
109
166
|
find(params, options) {
|
|
110
167
|
const { store_id, ...queryParams } = params;
|
|
111
|
-
return apiConfig.httpClient.get(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
168
|
+
return apiConfig.httpClient.get(
|
|
169
|
+
`${base(store_id)}/entries`,
|
|
170
|
+
{
|
|
171
|
+
...options,
|
|
172
|
+
params: queryParams
|
|
173
|
+
}
|
|
174
|
+
);
|
|
115
175
|
}
|
|
116
176
|
},
|
|
117
177
|
form: {
|
|
@@ -187,10 +247,13 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
187
247
|
},
|
|
188
248
|
find(params, options) {
|
|
189
249
|
const { store_id, ...queryParams } = params;
|
|
190
|
-
return apiConfig.httpClient.get(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
250
|
+
return apiConfig.httpClient.get(
|
|
251
|
+
`${base(store_id)}/products`,
|
|
252
|
+
{
|
|
253
|
+
...options,
|
|
254
|
+
params: queryParams
|
|
255
|
+
}
|
|
256
|
+
);
|
|
194
257
|
}
|
|
195
258
|
},
|
|
196
259
|
cart: {
|
|
@@ -199,16 +262,23 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
199
262
|
const { store_id: _store_id, ...payload } = params;
|
|
200
263
|
return apiConfig.httpClient.post(
|
|
201
264
|
`${base(store_id)}/carts/current`,
|
|
202
|
-
{
|
|
265
|
+
{
|
|
266
|
+
...payload,
|
|
267
|
+
store_id,
|
|
268
|
+
market: payload.market || apiConfig.market
|
|
269
|
+
},
|
|
203
270
|
options
|
|
204
271
|
);
|
|
205
272
|
},
|
|
206
273
|
get(params, options) {
|
|
207
274
|
const store_id = params.store_id || apiConfig.storeId;
|
|
208
|
-
return apiConfig.httpClient.get(
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
275
|
+
return apiConfig.httpClient.get(
|
|
276
|
+
`${base(store_id)}/carts/${params.id}`,
|
|
277
|
+
{
|
|
278
|
+
...options,
|
|
279
|
+
params: params.token ? { token: params.token } : options?.params
|
|
280
|
+
}
|
|
281
|
+
);
|
|
212
282
|
},
|
|
213
283
|
update(params, options) {
|
|
214
284
|
const { store_id, items, ...payload } = params;
|
|
@@ -218,7 +288,7 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
218
288
|
{
|
|
219
289
|
...payload,
|
|
220
290
|
store_id: target,
|
|
221
|
-
...items ? { items:
|
|
291
|
+
...items ? { items: sanitizePublicCheckoutItems(items) } : {}
|
|
222
292
|
},
|
|
223
293
|
options
|
|
224
294
|
);
|
|
@@ -231,7 +301,7 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
231
301
|
{
|
|
232
302
|
...payload,
|
|
233
303
|
store_id: target,
|
|
234
|
-
item:
|
|
304
|
+
item: sanitizePublicCheckoutItems([item])[0]
|
|
235
305
|
},
|
|
236
306
|
options
|
|
237
307
|
);
|
|
@@ -286,18 +356,35 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
286
356
|
},
|
|
287
357
|
find(params, options) {
|
|
288
358
|
const { store_id, ...queryParams } = params;
|
|
289
|
-
return apiConfig.httpClient.get(
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
359
|
+
return apiConfig.httpClient.get(
|
|
360
|
+
`${base(store_id)}/orders`,
|
|
361
|
+
{
|
|
362
|
+
...options,
|
|
363
|
+
params: queryParams
|
|
364
|
+
}
|
|
365
|
+
);
|
|
293
366
|
},
|
|
294
367
|
downloadDigitalAccess(params, options) {
|
|
295
368
|
const store_id = params.store_id || apiConfig.storeId;
|
|
296
369
|
return apiConfig.httpClient.post(
|
|
297
|
-
`${base(store_id)}/orders/${params.
|
|
370
|
+
`${base(store_id)}/orders/${params.order_id}/digital-access/${params.grant_id}/download`,
|
|
298
371
|
{},
|
|
299
372
|
options
|
|
300
373
|
);
|
|
374
|
+
},
|
|
375
|
+
findDigitalAccess(params, options) {
|
|
376
|
+
const { order_id, store_id, ...queryParams } = params;
|
|
377
|
+
return apiConfig.httpClient.get(
|
|
378
|
+
`${base(store_id)}/orders/${order_id}/digital-access`,
|
|
379
|
+
{ ...options, params: queryParams }
|
|
380
|
+
);
|
|
381
|
+
},
|
|
382
|
+
getDigitalAccess(params, options) {
|
|
383
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
384
|
+
return apiConfig.httpClient.get(
|
|
385
|
+
`${base(store_id)}/orders/${params.order_id}/digital-access/${params.grant_id}`,
|
|
386
|
+
options
|
|
387
|
+
);
|
|
301
388
|
}
|
|
302
389
|
},
|
|
303
390
|
service: {
|
|
@@ -318,17 +405,23 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
318
405
|
},
|
|
319
406
|
find(params, options) {
|
|
320
407
|
const { store_id, ...queryParams } = params;
|
|
321
|
-
return apiConfig.httpClient.get(
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
408
|
+
return apiConfig.httpClient.get(
|
|
409
|
+
`${base(store_id)}/services`,
|
|
410
|
+
{
|
|
411
|
+
...options,
|
|
412
|
+
params: queryParams
|
|
413
|
+
}
|
|
414
|
+
);
|
|
325
415
|
},
|
|
326
416
|
findProviders(params, options) {
|
|
327
417
|
const { store_id, ...queryParams } = params;
|
|
328
|
-
return apiConfig.httpClient.get(
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
418
|
+
return apiConfig.httpClient.get(
|
|
419
|
+
`${base(store_id)}/service-providers`,
|
|
420
|
+
{
|
|
421
|
+
...options,
|
|
422
|
+
params: queryParams
|
|
423
|
+
}
|
|
424
|
+
);
|
|
332
425
|
},
|
|
333
426
|
getAvailability(params, options) {
|
|
334
427
|
const { store_id, ...queryParams } = params;
|
|
@@ -357,48 +450,47 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
357
450
|
},
|
|
358
451
|
find(params, options) {
|
|
359
452
|
const { store_id, ...queryParams } = params;
|
|
360
|
-
return apiConfig.httpClient.get(
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
453
|
+
return apiConfig.httpClient.get(
|
|
454
|
+
`${base(store_id)}/providers`,
|
|
455
|
+
{
|
|
456
|
+
...options,
|
|
457
|
+
params: queryParams
|
|
458
|
+
}
|
|
459
|
+
);
|
|
364
460
|
}
|
|
365
461
|
}
|
|
366
462
|
},
|
|
367
463
|
crm: {
|
|
368
464
|
contact: {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
store_id,
|
|
375
|
-
market: params?.market || apiConfig.market || null,
|
|
376
|
-
email: params?.email,
|
|
377
|
-
verify: params?.verify ?? false
|
|
378
|
-
},
|
|
379
|
-
options
|
|
380
|
-
);
|
|
381
|
-
if (result?.token?.token) {
|
|
382
|
-
updateContactSession(() => ({
|
|
383
|
-
access_token: result.token.token,
|
|
384
|
-
contact: result.contact,
|
|
385
|
-
store: result.store,
|
|
386
|
-
market: result.market
|
|
387
|
-
}));
|
|
388
|
-
}
|
|
389
|
-
return result;
|
|
465
|
+
identify(params, options) {
|
|
466
|
+
return submitIdentification("identify", params, options);
|
|
467
|
+
},
|
|
468
|
+
requestCode(params, options) {
|
|
469
|
+
return submitIdentification("code", params, options);
|
|
390
470
|
},
|
|
391
471
|
async verify(params, options) {
|
|
392
472
|
const store_id = apiConfig.storeId;
|
|
393
473
|
const result = await apiConfig.httpClient.post(
|
|
394
474
|
`${base(store_id)}/account/verify`,
|
|
395
|
-
{ store_id, code: params.code },
|
|
475
|
+
{ store_id, challenge_id: params.challenge_id, code: params.code },
|
|
396
476
|
options
|
|
397
477
|
);
|
|
398
|
-
if (result?.token) {
|
|
478
|
+
if (result?.token?.token) {
|
|
479
|
+
const pending = pendingVerifications.get(params.challenge_id);
|
|
480
|
+
const identifiedStore = pending?.store || await apiConfig.httpClient.get(base(store_id), options);
|
|
399
481
|
updateContactSession(
|
|
400
|
-
(prev) => prev ? {
|
|
482
|
+
(prev) => prev ? {
|
|
483
|
+
...prev,
|
|
484
|
+
access_token: result.token.token,
|
|
485
|
+
contact: result.contact
|
|
486
|
+
} : {
|
|
487
|
+
access_token: result.token.token,
|
|
488
|
+
contact: result.contact,
|
|
489
|
+
store: identifiedStore,
|
|
490
|
+
market: pending?.market || null
|
|
491
|
+
}
|
|
401
492
|
);
|
|
493
|
+
pendingVerifications.delete(params.challenge_id);
|
|
402
494
|
}
|
|
403
495
|
return result;
|
|
404
496
|
},
|
|
@@ -415,7 +507,10 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
415
507
|
}
|
|
416
508
|
},
|
|
417
509
|
getMe(options) {
|
|
418
|
-
return apiConfig.httpClient.get(
|
|
510
|
+
return apiConfig.httpClient.get(
|
|
511
|
+
`${base()}/account/me`,
|
|
512
|
+
options
|
|
513
|
+
);
|
|
419
514
|
}
|
|
420
515
|
},
|
|
421
516
|
contactList: {
|
|
@@ -428,10 +523,31 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
428
523
|
},
|
|
429
524
|
find(params, options) {
|
|
430
525
|
const { store_id, ...queryParams } = params || {};
|
|
431
|
-
return apiConfig.httpClient.get(
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
526
|
+
return apiConfig.httpClient.get(
|
|
527
|
+
`${base(store_id)}/contact-lists`,
|
|
528
|
+
{
|
|
529
|
+
...options,
|
|
530
|
+
params: queryParams
|
|
531
|
+
}
|
|
532
|
+
);
|
|
533
|
+
},
|
|
534
|
+
plans: {
|
|
535
|
+
find(params, options) {
|
|
536
|
+
const { store_id, contact_list_id, ...queryParams } = params;
|
|
537
|
+
return apiConfig.httpClient.get(
|
|
538
|
+
`${base(store_id)}/contact-lists/${contact_list_id}/plans`,
|
|
539
|
+
{ ...options, params: queryParams }
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
memberships: {
|
|
544
|
+
find(params, options) {
|
|
545
|
+
const { store_id, ...queryParams } = params || {};
|
|
546
|
+
return apiConfig.httpClient.get(`${base(store_id)}/contact-lists/memberships`, {
|
|
547
|
+
...options,
|
|
548
|
+
params: queryParams
|
|
549
|
+
});
|
|
550
|
+
}
|
|
435
551
|
},
|
|
436
552
|
subscribe(params, options) {
|
|
437
553
|
const { store_id, id, ...payload } = params;
|
|
@@ -456,13 +572,31 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
456
572
|
options
|
|
457
573
|
);
|
|
458
574
|
},
|
|
459
|
-
|
|
575
|
+
manage(token, options) {
|
|
460
576
|
return apiConfig.httpClient.post(
|
|
461
|
-
`${base()}/contact-lists/
|
|
577
|
+
`${base()}/contact-lists/manage`,
|
|
462
578
|
{ token },
|
|
463
579
|
options
|
|
464
580
|
);
|
|
465
581
|
},
|
|
582
|
+
unsubscribe(token, options) {
|
|
583
|
+
const headers = { ...options?.headers };
|
|
584
|
+
for (const name of Object.keys(headers)) {
|
|
585
|
+
if (name.toLowerCase() === "content-type") delete headers[name];
|
|
586
|
+
}
|
|
587
|
+
return apiConfig.httpClient.post(
|
|
588
|
+
`${base()}/contact-lists/unsubscribe`,
|
|
589
|
+
new URLSearchParams({ "List-Unsubscribe": "One-Click" }),
|
|
590
|
+
{
|
|
591
|
+
...options,
|
|
592
|
+
params: { ...options?.params || {}, token },
|
|
593
|
+
headers: {
|
|
594
|
+
...headers,
|
|
595
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
);
|
|
599
|
+
},
|
|
466
600
|
confirm(token, options) {
|
|
467
601
|
return apiConfig.httpClient.post(
|
|
468
602
|
`${base()}/contact-lists/confirm`,
|
|
@@ -627,27 +761,47 @@ var convertServerErrorToRequestError = (serverError, renameRules) => {
|
|
|
627
761
|
|
|
628
762
|
// src/utils/queryParams.ts
|
|
629
763
|
function buildQueryString(params) {
|
|
630
|
-
const queryParts =
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
const jsonString = JSON.stringify(value);
|
|
644
|
-
queryParts.push(`${key}=${encodeURIComponent(jsonString)}`);
|
|
764
|
+
const queryParts = Object.entries(params).flatMap(
|
|
765
|
+
([key, value]) => {
|
|
766
|
+
if (value === null || value === void 0) return [];
|
|
767
|
+
if (typeof value === "string") {
|
|
768
|
+
return [`${key}=${encodeURIComponent(value)}`];
|
|
769
|
+
}
|
|
770
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
771
|
+
return [`${key}=${value}`];
|
|
772
|
+
}
|
|
773
|
+
if (Array.isArray(value) || typeof value === "object") {
|
|
774
|
+
return [`${key}=${encodeURIComponent(JSON.stringify(value))}`];
|
|
775
|
+
}
|
|
776
|
+
return [];
|
|
645
777
|
}
|
|
646
|
-
|
|
778
|
+
);
|
|
647
779
|
return queryParts.length > 0 ? `?${queryParts.join("&")}` : "";
|
|
648
780
|
}
|
|
649
781
|
|
|
650
782
|
// src/services/createHttpClient.ts
|
|
783
|
+
function requestError(name, message, details = {}) {
|
|
784
|
+
return Object.assign(new Error(message), { name }, details);
|
|
785
|
+
}
|
|
786
|
+
function isRecord(value) {
|
|
787
|
+
return typeof value === "object" && value !== null;
|
|
788
|
+
}
|
|
789
|
+
function isTokenSet(value) {
|
|
790
|
+
return isRecord(value) && typeof value.access_token === "string" && (value.refresh_token === void 0 || typeof value.refresh_token === "string") && (value.access_expires_at === void 0 || typeof value.access_expires_at === "number");
|
|
791
|
+
}
|
|
792
|
+
function isValidationError(value) {
|
|
793
|
+
return isRecord(value) && typeof value.field === "string" && typeof value.error === "string";
|
|
794
|
+
}
|
|
795
|
+
function toServerError(value, statusCode) {
|
|
796
|
+
const payload = isRecord(value) ? value : {};
|
|
797
|
+
const validationErrors = Array.isArray(payload.validationErrors) ? payload.validationErrors.filter(isValidationError) : [];
|
|
798
|
+
return {
|
|
799
|
+
message: typeof payload.message === "string" ? payload.message : "Request failed",
|
|
800
|
+
error: typeof payload.error === "string" ? payload.error : "REQUEST_FAILED",
|
|
801
|
+
statusCode: typeof payload.statusCode === "number" ? payload.statusCode : statusCode,
|
|
802
|
+
validationErrors
|
|
803
|
+
};
|
|
804
|
+
}
|
|
651
805
|
function createHttpClient(cfg) {
|
|
652
806
|
const { authStorage } = cfg;
|
|
653
807
|
let refreshPromise = null;
|
|
@@ -664,32 +818,41 @@ function createHttpClient(cfg) {
|
|
|
664
818
|
const refresh_token = tokens?.refresh_token;
|
|
665
819
|
if (!refresh_token) {
|
|
666
820
|
authStorage.onForcedLogout();
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
throw err;
|
|
821
|
+
throw requestError("ApiError", "No refresh token available", {
|
|
822
|
+
statusCode: 401
|
|
823
|
+
});
|
|
671
824
|
}
|
|
672
825
|
const refRes = await fetch(getRefreshEndpoint(), {
|
|
673
826
|
method: "POST",
|
|
674
|
-
headers: {
|
|
827
|
+
headers: {
|
|
828
|
+
Accept: "application/json",
|
|
829
|
+
"Content-Type": "application/json"
|
|
830
|
+
},
|
|
675
831
|
body: JSON.stringify({ refresh_token })
|
|
676
832
|
});
|
|
677
833
|
if (!refRes.ok) {
|
|
678
834
|
authStorage.onForcedLogout();
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
throw err;
|
|
835
|
+
throw requestError("ApiError", "Token refresh failed", {
|
|
836
|
+
statusCode: 401
|
|
837
|
+
});
|
|
683
838
|
}
|
|
684
839
|
const data = await refRes.json();
|
|
840
|
+
if (!isTokenSet(data)) {
|
|
841
|
+
authStorage.onForcedLogout();
|
|
842
|
+
throw requestError(
|
|
843
|
+
"ParseError",
|
|
844
|
+
"Token refresh returned an invalid response",
|
|
845
|
+
{ statusCode: refRes.status }
|
|
846
|
+
);
|
|
847
|
+
}
|
|
685
848
|
authStorage.onTokensRefreshed(data);
|
|
686
849
|
})().finally(() => {
|
|
687
850
|
refreshPromise = null;
|
|
688
851
|
});
|
|
689
852
|
return refreshPromise;
|
|
690
853
|
}
|
|
691
|
-
async function request(method, path, body, options) {
|
|
692
|
-
if (options?.transformRequest) {
|
|
854
|
+
async function request(method, path, body, options, retried = false) {
|
|
855
|
+
if (!retried && options?.transformRequest) {
|
|
693
856
|
body = options.transformRequest(body);
|
|
694
857
|
}
|
|
695
858
|
const headers = {
|
|
@@ -707,23 +870,27 @@ function createHttpClient(cfg) {
|
|
|
707
870
|
headers["Authorization"] = `Bearer ${tokens.access_token}`;
|
|
708
871
|
}
|
|
709
872
|
const finalPath = options?.params ? path + buildQueryString(options.params) : path;
|
|
710
|
-
const
|
|
873
|
+
const fetchOptions = {
|
|
874
|
+
method,
|
|
875
|
+
headers,
|
|
876
|
+
signal: options?.signal
|
|
877
|
+
};
|
|
711
878
|
if (!["GET", "DELETE"].includes(method) && body !== void 0) {
|
|
712
|
-
|
|
879
|
+
fetchOptions.body = body instanceof URLSearchParams ? body.toString() : JSON.stringify(body);
|
|
713
880
|
}
|
|
714
881
|
const fullUrl = `${cfg.baseUrl}${finalPath}`;
|
|
715
882
|
let res;
|
|
716
883
|
let data;
|
|
717
884
|
const startedAt = Date.now();
|
|
718
885
|
try {
|
|
719
|
-
res = await fetch(fullUrl,
|
|
886
|
+
res = await fetch(fullUrl, fetchOptions);
|
|
720
887
|
} catch (error) {
|
|
721
888
|
const aborted = options?.signal?.aborted || error instanceof Error && error.name === "AbortError";
|
|
722
|
-
const err =
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
889
|
+
const err = requestError(
|
|
890
|
+
aborted ? "AbortError" : "NetworkError",
|
|
891
|
+
error instanceof Error ? error.message : "Network request failed",
|
|
892
|
+
{ method, url: fullUrl, aborted }
|
|
893
|
+
);
|
|
727
894
|
if (options?.onError && method !== "GET") {
|
|
728
895
|
Promise.resolve(
|
|
729
896
|
options.onError({ error: err, method, url: fullUrl, aborted })
|
|
@@ -732,18 +899,9 @@ function createHttpClient(cfg) {
|
|
|
732
899
|
}
|
|
733
900
|
throw err;
|
|
734
901
|
}
|
|
735
|
-
if (res.status === 401 && !
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
const refreshed = authStorage.getTokens();
|
|
739
|
-
if (refreshed?.access_token) {
|
|
740
|
-
headers["Authorization"] = `Bearer ${refreshed.access_token}`;
|
|
741
|
-
fetchOpts.headers = headers;
|
|
742
|
-
}
|
|
743
|
-
return request(method, path, body, { ...options, _retried: true });
|
|
744
|
-
} catch (refreshError) {
|
|
745
|
-
throw refreshError;
|
|
746
|
-
}
|
|
902
|
+
if (res.status === 401 && !retried) {
|
|
903
|
+
await ensureFreshToken();
|
|
904
|
+
return request(method, path, body, options, true);
|
|
747
905
|
}
|
|
748
906
|
try {
|
|
749
907
|
const contentLength = res.headers.get("content-length");
|
|
@@ -754,30 +912,35 @@ function createHttpClient(cfg) {
|
|
|
754
912
|
data = await res.json();
|
|
755
913
|
}
|
|
756
914
|
} catch (error) {
|
|
757
|
-
const err =
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
915
|
+
const err = requestError("ParseError", "Failed to parse response", {
|
|
916
|
+
method,
|
|
917
|
+
url: fullUrl,
|
|
918
|
+
statusCode: res.status
|
|
919
|
+
});
|
|
762
920
|
if (options?.onError && method !== "GET") {
|
|
763
921
|
Promise.resolve(
|
|
764
|
-
options.onError({
|
|
922
|
+
options.onError({
|
|
923
|
+
error: err,
|
|
924
|
+
method,
|
|
925
|
+
url: fullUrl,
|
|
926
|
+
status: res.status
|
|
927
|
+
})
|
|
765
928
|
).catch(() => {
|
|
766
929
|
});
|
|
767
930
|
}
|
|
768
931
|
throw err;
|
|
769
932
|
}
|
|
770
933
|
if (!res.ok) {
|
|
771
|
-
const serverErr = data;
|
|
934
|
+
const serverErr = toServerError(data, res.status);
|
|
772
935
|
const reqErr = convertServerErrorToRequestError(serverErr);
|
|
773
|
-
const err = new Error(serverErr.message || "Request failed");
|
|
774
|
-
err.name = "ApiError";
|
|
775
|
-
err.statusCode = serverErr.statusCode || res.status;
|
|
776
|
-
err.validationErrors = reqErr.validationErrors;
|
|
777
|
-
err.method = method;
|
|
778
|
-
err.url = fullUrl;
|
|
779
936
|
const requestId = res.headers.get("x-request-id") || res.headers.get("request-id");
|
|
780
|
-
|
|
937
|
+
const err = requestError("ApiError", serverErr.message, {
|
|
938
|
+
statusCode: serverErr.statusCode,
|
|
939
|
+
validationErrors: reqErr.validationErrors,
|
|
940
|
+
method,
|
|
941
|
+
url: fullUrl,
|
|
942
|
+
requestId: requestId || void 0
|
|
943
|
+
});
|
|
781
944
|
if (options?.onError && method !== "GET") {
|
|
782
945
|
Promise.resolve(
|
|
783
946
|
options.onError({
|
|
@@ -827,183 +990,279 @@ function supportConversationQuery(params) {
|
|
|
827
990
|
if (params.after_id) qs.set("after_id", params.after_id);
|
|
828
991
|
return qs.toString();
|
|
829
992
|
}
|
|
993
|
+
function storefrontSupportOptions(supportToken, options) {
|
|
994
|
+
if (!/^[0-9a-f]{64}$/.test(supportToken)) {
|
|
995
|
+
throw new Error("support_token must be a 64-character lowercase hexadecimal token");
|
|
996
|
+
}
|
|
997
|
+
const headers = { ...options?.headers };
|
|
998
|
+
for (const name of Object.keys(headers)) {
|
|
999
|
+
if (name.toLowerCase() === "x-arky-support-token") delete headers[name];
|
|
1000
|
+
}
|
|
1001
|
+
return {
|
|
1002
|
+
...options,
|
|
1003
|
+
headers: {
|
|
1004
|
+
...headers,
|
|
1005
|
+
"X-Arky-Support-Token": supportToken
|
|
1006
|
+
}
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
830
1009
|
function createStorefrontSupportApi(config) {
|
|
831
|
-
const { httpClient
|
|
1010
|
+
const { httpClient } = config;
|
|
832
1011
|
return {
|
|
833
1012
|
async startConversation(params = {}, opts) {
|
|
1013
|
+
const storeId = config.storeId;
|
|
834
1014
|
return httpClient.post(
|
|
835
1015
|
`/v1/storefront/${storeId}/support/conversations`,
|
|
836
|
-
{ store_id: storeId
|
|
1016
|
+
{ ...params, store_id: storeId },
|
|
837
1017
|
opts
|
|
838
1018
|
);
|
|
839
1019
|
},
|
|
840
1020
|
async sendMessage(params, opts) {
|
|
1021
|
+
const { support_token, ...request } = params;
|
|
1022
|
+
const storeId = config.storeId;
|
|
841
1023
|
return httpClient.post(
|
|
842
|
-
`/v1/storefront/${storeId}/support/conversations/${
|
|
843
|
-
{ store_id: storeId
|
|
844
|
-
opts
|
|
1024
|
+
`/v1/storefront/${storeId}/support/conversations/${request.conversation_id}/messages`,
|
|
1025
|
+
{ ...request, store_id: storeId },
|
|
1026
|
+
storefrontSupportOptions(support_token, opts)
|
|
845
1027
|
);
|
|
846
1028
|
},
|
|
847
1029
|
async getConversation(params, opts) {
|
|
848
|
-
const
|
|
1030
|
+
const { support_token, ...request } = params;
|
|
1031
|
+
const storeId = config.storeId;
|
|
1032
|
+
const qs = supportConversationQuery({ ...request, store_id: storeId });
|
|
849
1033
|
return httpClient.get(
|
|
850
|
-
`/v1/storefront/${storeId}/support/conversations/${
|
|
851
|
-
opts
|
|
1034
|
+
`/v1/storefront/${storeId}/support/conversations/${request.conversation_id}?${qs}`,
|
|
1035
|
+
storefrontSupportOptions(support_token, opts)
|
|
852
1036
|
);
|
|
853
1037
|
}
|
|
854
1038
|
};
|
|
855
1039
|
}
|
|
856
1040
|
|
|
857
1041
|
// src/utils/blocks.ts
|
|
1042
|
+
function isRecord2(value) {
|
|
1043
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1044
|
+
}
|
|
1045
|
+
function isBlock(value) {
|
|
1046
|
+
return isRecord2(value) && typeof value.key === "string" && typeof value.type === "string";
|
|
1047
|
+
}
|
|
1048
|
+
function recordFromBlocks(values, locale) {
|
|
1049
|
+
const result = {};
|
|
1050
|
+
for (const value of values) {
|
|
1051
|
+
if (!isBlock(value)) continue;
|
|
1052
|
+
result[value.key] = unwrapBlock(value, locale);
|
|
1053
|
+
}
|
|
1054
|
+
return result;
|
|
1055
|
+
}
|
|
1056
|
+
function localizedValue(value, locale) {
|
|
1057
|
+
if (typeof value === "string") return value;
|
|
1058
|
+
if (!isRecord2(value)) return "";
|
|
1059
|
+
const selected = value[locale] ?? value.en;
|
|
1060
|
+
return typeof selected === "string" ? selected : "";
|
|
1061
|
+
}
|
|
1062
|
+
function unwrapBlock(value, locale) {
|
|
1063
|
+
if (!isBlock(value)) return value;
|
|
1064
|
+
if (value.type === "localized_text" || value.type === "markdown") {
|
|
1065
|
+
return localizedValue(value.value, locale);
|
|
1066
|
+
}
|
|
1067
|
+
if (value.type === "array") {
|
|
1068
|
+
if (!Array.isArray(value.value)) return [];
|
|
1069
|
+
return value.value.map((item) => {
|
|
1070
|
+
if (isBlock(item)) return unwrapBlock(item, locale);
|
|
1071
|
+
if (isRecord2(item) && Array.isArray(item.value)) {
|
|
1072
|
+
return recordFromBlocks(item.value, locale);
|
|
1073
|
+
}
|
|
1074
|
+
if (!isRecord2(item)) return item;
|
|
1075
|
+
return Object.fromEntries(
|
|
1076
|
+
Object.entries(item).map(([key, nested]) => [key, unwrapBlock(nested, locale)])
|
|
1077
|
+
);
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
if (value.type === "object") {
|
|
1081
|
+
if (Array.isArray(value.value)) return recordFromBlocks(value.value, locale);
|
|
1082
|
+
if (!isRecord2(value.value)) return {};
|
|
1083
|
+
return Object.fromEntries(
|
|
1084
|
+
Object.entries(value.value).map(([key, nested]) => [key, unwrapBlock(nested, locale)])
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
return value.value;
|
|
1088
|
+
}
|
|
1089
|
+
function blockContentArray(values, locale) {
|
|
1090
|
+
const blocks = values.filter(isBlock);
|
|
1091
|
+
if (blocks.length === 0) return [];
|
|
1092
|
+
if (blocks.every((block) => block.key === blocks[0].key)) {
|
|
1093
|
+
return blocks.map((block) => blockContentValue(block, locale));
|
|
1094
|
+
}
|
|
1095
|
+
return Object.fromEntries(
|
|
1096
|
+
blocks.map((block) => [block.key, blockContentValue(block, locale)])
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
function blockContentValue(block, locale) {
|
|
1100
|
+
if (block.type === "localized_text" || block.type === "markdown") {
|
|
1101
|
+
return localizedValue(block.value, locale);
|
|
1102
|
+
}
|
|
1103
|
+
if (block.type === "media") return block.value ?? null;
|
|
1104
|
+
if (block.type === "array") {
|
|
1105
|
+
return Array.isArray(block.value) ? blockContentArray(block.value, locale) : [];
|
|
1106
|
+
}
|
|
1107
|
+
if (block.type === "object") {
|
|
1108
|
+
if (Array.isArray(block.value)) return blockContentArray(block.value, locale);
|
|
1109
|
+
if (!isRecord2(block.value)) return {};
|
|
1110
|
+
return Object.fromEntries(
|
|
1111
|
+
Object.entries(block.value).map(([key, value]) => [
|
|
1112
|
+
key,
|
|
1113
|
+
isBlock(value) ? blockContentValue(value, locale) : value
|
|
1114
|
+
])
|
|
1115
|
+
);
|
|
1116
|
+
}
|
|
1117
|
+
return block.value;
|
|
1118
|
+
}
|
|
1119
|
+
function findBlock(entry, key) {
|
|
1120
|
+
return entry?.blocks?.find((block) => block.key === key);
|
|
1121
|
+
}
|
|
858
1122
|
function getBlockLabel(block) {
|
|
859
|
-
|
|
860
|
-
return block.key?.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()) ?? "";
|
|
1123
|
+
return block?.key.replace(/_/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase()) ?? "";
|
|
861
1124
|
}
|
|
862
1125
|
function formatBlockValue(block) {
|
|
863
|
-
if (
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
return new Date(value).toLocaleDateString();
|
|
871
|
-
}
|
|
872
|
-
return String(value);
|
|
873
|
-
case "media":
|
|
874
|
-
if (value && typeof value === "object") {
|
|
875
|
-
return value.mime_type ? value.name || value.id : value.title || value.name || value.id;
|
|
876
|
-
}
|
|
877
|
-
return String(value);
|
|
878
|
-
default:
|
|
879
|
-
return String(value);
|
|
1126
|
+
if (block?.value === null || block?.value === void 0) return "";
|
|
1127
|
+
if (block.type === "boolean") return block.value ? "Yes" : "No";
|
|
1128
|
+
if (block.type === "number") {
|
|
1129
|
+
const properties = isRecord2(block.properties) ? block.properties : {};
|
|
1130
|
+
if (properties.variant === "DATE" || properties.variant === "DATE_TIME") {
|
|
1131
|
+
return new Date(Number(block.value)).toLocaleDateString();
|
|
1132
|
+
}
|
|
880
1133
|
}
|
|
1134
|
+
if (block.type === "media" && isRecord2(block.value)) {
|
|
1135
|
+
const label = block.value.name ?? block.value.title ?? block.value.id;
|
|
1136
|
+
return typeof label === "string" ? label : "";
|
|
1137
|
+
}
|
|
1138
|
+
return String(block.value);
|
|
881
1139
|
}
|
|
882
|
-
function prepareBlocksForSubmission(formData, blockTypes) {
|
|
883
|
-
return Object.
|
|
1140
|
+
function prepareBlocksForSubmission(formData, blockTypes = {}) {
|
|
1141
|
+
return Object.entries(formData).filter(([, value]) => value !== null && value !== void 0).map(([key, value]) => ({
|
|
884
1142
|
key,
|
|
885
|
-
value:
|
|
1143
|
+
value: blockTypes[key] === "array" && !Array.isArray(value) ? [value] : value
|
|
886
1144
|
}));
|
|
887
1145
|
}
|
|
888
1146
|
function extractBlockValues(blocks) {
|
|
889
|
-
|
|
890
|
-
blocks.forEach((block) => {
|
|
891
|
-
values[block.key] = block.value ?? null;
|
|
892
|
-
});
|
|
893
|
-
return values;
|
|
1147
|
+
return Object.fromEntries(blocks.map((block) => [block.key, block.value ?? null]));
|
|
894
1148
|
}
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
var getBlockTextValue = (block, locale = "en") => {
|
|
1149
|
+
function getBlockValue(entry, key) {
|
|
1150
|
+
return findBlock(entry, key)?.value ?? null;
|
|
1151
|
+
}
|
|
1152
|
+
function getBlockTextValue(block, locale = "en") {
|
|
900
1153
|
if (!block || block.value === null || block.value === void 0) return "";
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
if (typeof block.value === "object" && block.value !== null) {
|
|
904
|
-
return block.value[locale] ?? block.value["en"] ?? "";
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
if (typeof block.value === "string") return block.value;
|
|
908
|
-
return String(block.value ?? "");
|
|
909
|
-
};
|
|
910
|
-
var getBlockValues = (entry, blockKey) => {
|
|
911
|
-
const block = entry?.blocks?.find((f) => f.key === blockKey);
|
|
912
|
-
if (!block) return [];
|
|
913
|
-
if (block.type === "array" && Array.isArray(block.value)) {
|
|
914
|
-
return block.value;
|
|
915
|
-
}
|
|
916
|
-
return [];
|
|
917
|
-
};
|
|
918
|
-
function unwrapBlock(block, locale) {
|
|
919
|
-
if (!block?.type || block.value === void 0) return block;
|
|
920
|
-
const blockType = block.type;
|
|
921
|
-
if (blockType === "array") {
|
|
922
|
-
return block.value.map((obj) => {
|
|
923
|
-
const parsed = {};
|
|
924
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
925
|
-
parsed[k] = unwrapBlock(v, locale);
|
|
926
|
-
}
|
|
927
|
-
return parsed;
|
|
928
|
-
});
|
|
929
|
-
}
|
|
930
|
-
if (blockType === "object") {
|
|
931
|
-
const parsed = {};
|
|
932
|
-
for (const [k, v] of Object.entries(block.value || {})) {
|
|
933
|
-
parsed[k] = unwrapBlock(v, locale);
|
|
934
|
-
}
|
|
935
|
-
return parsed;
|
|
1154
|
+
if (block.type === "localized_text" || block.type === "markdown") {
|
|
1155
|
+
return localizedValue(block.value, locale);
|
|
936
1156
|
}
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
1157
|
+
return typeof block.value === "string" ? block.value : String(block.value);
|
|
1158
|
+
}
|
|
1159
|
+
function getBlockContentValue(entry, key, locale = "en") {
|
|
1160
|
+
const block = findBlock(entry, key);
|
|
1161
|
+
return block ? blockContentValue(block, locale) : null;
|
|
941
1162
|
}
|
|
942
|
-
|
|
943
|
-
const block = entry
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
return
|
|
950
|
-
}
|
|
1163
|
+
function getBlockValues(entry, key) {
|
|
1164
|
+
const block = findBlock(entry, key);
|
|
1165
|
+
return block?.type === "array" && Array.isArray(block.value) ? block.value : [];
|
|
1166
|
+
}
|
|
1167
|
+
function getBlockObjectValues(entry, key, locale = "en") {
|
|
1168
|
+
return getBlockValues(entry, key).map((value) => {
|
|
1169
|
+
if (isRecord2(value) && Array.isArray(value.value)) {
|
|
1170
|
+
return recordFromBlocks(value.value, locale);
|
|
1171
|
+
}
|
|
1172
|
+
return isRecord2(value) ? Object.fromEntries(
|
|
1173
|
+
Object.entries(value).map(([field, nested]) => [field, unwrapBlock(nested, locale)])
|
|
1174
|
+
) : {};
|
|
951
1175
|
});
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
const block = entry
|
|
1176
|
+
}
|
|
1177
|
+
function getBlockFromArray(entry, key, locale = "en") {
|
|
1178
|
+
const block = findBlock(entry, key);
|
|
955
1179
|
if (!block) return {};
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
const mediaValue = imageBlock.value;
|
|
975
|
-
return mediaValue?.resolutions?.original?.url || mediaValue?.url || null;
|
|
976
|
-
}
|
|
977
|
-
if (isBlock) {
|
|
978
|
-
if (typeof imageBlock === "string") return imageBlock;
|
|
979
|
-
if (imageBlock.url) return imageBlock.url;
|
|
980
|
-
}
|
|
981
|
-
return imageBlock.resolutions?.original?.url || null;
|
|
982
|
-
};
|
|
1180
|
+
const value = unwrapBlock(block, locale);
|
|
1181
|
+
if (isRecord2(value)) return value;
|
|
1182
|
+
if (Array.isArray(block.value)) return recordFromBlocks(block.value, locale);
|
|
1183
|
+
return { [block.key]: value };
|
|
1184
|
+
}
|
|
1185
|
+
function nestedUrl(value) {
|
|
1186
|
+
const resolutions = isRecord2(value.resolutions) ? value.resolutions : null;
|
|
1187
|
+
const original = resolutions && isRecord2(resolutions.original) ? resolutions.original : null;
|
|
1188
|
+
if (typeof original?.url === "string") return original.url;
|
|
1189
|
+
return typeof value.url === "string" ? value.url : null;
|
|
1190
|
+
}
|
|
1191
|
+
function getImageUrl(value, isBlock2 = true) {
|
|
1192
|
+
if (typeof value === "string") return value;
|
|
1193
|
+
if (!isRecord2(value)) return null;
|
|
1194
|
+
if (value.type === "media" && isRecord2(value.value)) return nestedUrl(value.value);
|
|
1195
|
+
if (isBlock2 && typeof value.url === "string") return value.url;
|
|
1196
|
+
return nestedUrl(value);
|
|
1197
|
+
}
|
|
983
1198
|
|
|
984
1199
|
// src/utils/price.ts
|
|
1200
|
+
var SUPPORTED_STORE_CURRENCIES = Object.freeze([
|
|
1201
|
+
"USD",
|
|
1202
|
+
"EUR",
|
|
1203
|
+
"GBP",
|
|
1204
|
+
"JPY",
|
|
1205
|
+
"CNY",
|
|
1206
|
+
"CHF",
|
|
1207
|
+
"AUD",
|
|
1208
|
+
"CAD",
|
|
1209
|
+
"HKD",
|
|
1210
|
+
"SGD",
|
|
1211
|
+
"NZD",
|
|
1212
|
+
"KRW",
|
|
1213
|
+
"SEK",
|
|
1214
|
+
"NOK",
|
|
1215
|
+
"DKK",
|
|
1216
|
+
"INR",
|
|
1217
|
+
"MXN",
|
|
1218
|
+
"BRL",
|
|
1219
|
+
"ZAR",
|
|
1220
|
+
"RUB",
|
|
1221
|
+
"TRY",
|
|
1222
|
+
"PLN",
|
|
1223
|
+
"THB",
|
|
1224
|
+
"IDR",
|
|
1225
|
+
"MYR",
|
|
1226
|
+
"PHP",
|
|
1227
|
+
"CZK",
|
|
1228
|
+
"ILS",
|
|
1229
|
+
"AED",
|
|
1230
|
+
"SAR",
|
|
1231
|
+
"HUF",
|
|
1232
|
+
"RON",
|
|
1233
|
+
"BGN",
|
|
1234
|
+
"HRK",
|
|
1235
|
+
"BAM",
|
|
1236
|
+
"RSD",
|
|
1237
|
+
"MKD",
|
|
1238
|
+
"ALL"
|
|
1239
|
+
]);
|
|
1240
|
+
var SUPPORTED_STORE_CURRENCY_SET = Object.freeze(
|
|
1241
|
+
new Set(SUPPORTED_STORE_CURRENCIES)
|
|
1242
|
+
);
|
|
1243
|
+
var ZERO_MINOR_UNIT_STORE_CURRENCIES = Object.freeze(
|
|
1244
|
+
/* @__PURE__ */ new Set(["JPY", "KRW"])
|
|
1245
|
+
);
|
|
985
1246
|
function formatCurrency(amount, currencyCode, locale = "en") {
|
|
986
|
-
|
|
1247
|
+
const normalized = currencyCode.trim().toUpperCase();
|
|
1248
|
+
if (!normalized) return "";
|
|
1249
|
+
const minorUnits = getCurrencyMinorUnits(normalized);
|
|
987
1250
|
return new Intl.NumberFormat(locale, {
|
|
988
1251
|
style: "currency",
|
|
989
|
-
currency:
|
|
1252
|
+
currency: normalized,
|
|
1253
|
+
minimumFractionDigits: minorUnits,
|
|
1254
|
+
maximumFractionDigits: minorUnits
|
|
990
1255
|
}).format(amount);
|
|
991
1256
|
}
|
|
992
|
-
function
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
currency: currency.toUpperCase()
|
|
997
|
-
});
|
|
998
|
-
const parts = formatter.formatToParts(1.11);
|
|
999
|
-
const fractionPart = parts.find((p) => p.type === "fraction");
|
|
1000
|
-
return fractionPart?.value.length ?? 2;
|
|
1001
|
-
} catch {
|
|
1002
|
-
return 2;
|
|
1257
|
+
function getCurrencyMinorUnits(currency) {
|
|
1258
|
+
const normalized = currency.trim().toUpperCase();
|
|
1259
|
+
if (!SUPPORTED_STORE_CURRENCY_SET.has(normalized)) {
|
|
1260
|
+
throw new RangeError(`Unsupported currency '${currency}'`);
|
|
1003
1261
|
}
|
|
1262
|
+
return ZERO_MINOR_UNIT_STORE_CURRENCIES.has(normalized) ? 0 : 2;
|
|
1004
1263
|
}
|
|
1005
1264
|
function convertToMajor(minorAmount, currency) {
|
|
1006
|
-
const units =
|
|
1265
|
+
const units = getCurrencyMinorUnits(currency);
|
|
1007
1266
|
return minorAmount / Math.pow(10, units);
|
|
1008
1267
|
}
|
|
1009
1268
|
function getCurrencySymbol(currency) {
|
|
@@ -1025,22 +1284,24 @@ function getCurrencyName(currency) {
|
|
|
1025
1284
|
}
|
|
1026
1285
|
}
|
|
1027
1286
|
function formatMinor(amountMinor, currency) {
|
|
1028
|
-
if (!
|
|
1287
|
+
if (!Number.isSafeInteger(amountMinor)) {
|
|
1288
|
+
throw new RangeError("Minor-unit amount must be a safe integer");
|
|
1289
|
+
}
|
|
1029
1290
|
return formatCurrency(convertToMajor(amountMinor, currency), currency);
|
|
1030
1291
|
}
|
|
1031
1292
|
function formatPayment(payment) {
|
|
1032
1293
|
return formatMinor(payment.total, payment.currency);
|
|
1033
1294
|
}
|
|
1034
1295
|
function formatPrice(prices, marketId) {
|
|
1035
|
-
if (!prices || prices.length === 0) return "";
|
|
1036
|
-
const price =
|
|
1037
|
-
if (!price) return "";
|
|
1296
|
+
if (!prices || prices.length === 0 || !marketId) return "";
|
|
1297
|
+
const price = prices.find((p) => p.market === marketId);
|
|
1298
|
+
if (!price || !Number.isSafeInteger(price.amount) || price.amount < 0 || !price.currency) return "";
|
|
1038
1299
|
return formatMinor(price.amount, price.currency);
|
|
1039
1300
|
}
|
|
1040
1301
|
function getPriceAmount(prices, marketId) {
|
|
1041
|
-
if (!prices || prices.length === 0) return
|
|
1042
|
-
const price = prices.find((p) => p.market === marketId)
|
|
1043
|
-
return price
|
|
1302
|
+
if (!prices || prices.length === 0 || !marketId) return null;
|
|
1303
|
+
const price = prices.find((p) => p.market === marketId);
|
|
1304
|
+
return price && Number.isSafeInteger(price.amount) && price.amount >= 0 ? price.amount : null;
|
|
1044
1305
|
}
|
|
1045
1306
|
|
|
1046
1307
|
// src/utils/validation.ts
|
|
@@ -1141,6 +1402,7 @@ function formatDate(date, locale) {
|
|
|
1141
1402
|
async function fetchSvgContent(mediaObject) {
|
|
1142
1403
|
if (!mediaObject) return null;
|
|
1143
1404
|
const svgUrl = getImageUrl(mediaObject, false);
|
|
1405
|
+
if (!svgUrl) return null;
|
|
1144
1406
|
try {
|
|
1145
1407
|
const response = await fetch(svgUrl);
|
|
1146
1408
|
if (!response.ok) {
|
|
@@ -1233,9 +1495,10 @@ function getFirstAvailableFCId(variant, quantity = 1) {
|
|
|
1233
1495
|
// src/index.ts
|
|
1234
1496
|
function createUtilitySurface(apiConfig) {
|
|
1235
1497
|
return {
|
|
1236
|
-
getImageUrl: (imageBlock,
|
|
1498
|
+
getImageUrl: (imageBlock, isBlock2 = true) => getImageUrl(imageBlock, isBlock2),
|
|
1237
1499
|
getBlockValue,
|
|
1238
1500
|
getBlockTextValue,
|
|
1501
|
+
getBlockContentValue,
|
|
1239
1502
|
getBlockValues,
|
|
1240
1503
|
getBlockLabel,
|
|
1241
1504
|
getBlockObjectValues,
|
|
@@ -1270,29 +1533,50 @@ function createUtilitySurface(apiConfig) {
|
|
|
1270
1533
|
getFirstAvailableFCId
|
|
1271
1534
|
};
|
|
1272
1535
|
}
|
|
1273
|
-
|
|
1274
|
-
function readContactSession() {
|
|
1536
|
+
function defaultStorefrontSessionStorage() {
|
|
1275
1537
|
if (typeof window === "undefined") return null;
|
|
1276
1538
|
try {
|
|
1277
|
-
|
|
1278
|
-
return raw ? JSON.parse(raw) : null;
|
|
1539
|
+
return window.localStorage;
|
|
1279
1540
|
} catch {
|
|
1280
1541
|
return null;
|
|
1281
1542
|
}
|
|
1282
1543
|
}
|
|
1283
|
-
function
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
localStorage.setItem(CONTACT_STORAGE_KEY, JSON.stringify(s));
|
|
1287
|
-
} else {
|
|
1288
|
-
localStorage.removeItem(CONTACT_STORAGE_KEY);
|
|
1289
|
-
}
|
|
1544
|
+
function storefrontSessionStorageKey(baseUrl, storeId) {
|
|
1545
|
+
const normalizedBaseUrl = baseUrl.trim().replace(/\/+$/, "").toLowerCase();
|
|
1546
|
+
return `arky_contact_session:${encodeURIComponent(normalizedBaseUrl)}:${encodeURIComponent(storeId)}`;
|
|
1290
1547
|
}
|
|
1291
|
-
function
|
|
1548
|
+
function createStorefrontClient(config) {
|
|
1292
1549
|
const locale = config.locale || "en";
|
|
1293
1550
|
const initialMarket = config.market || "";
|
|
1294
1551
|
const listeners = /* @__PURE__ */ new Set();
|
|
1295
1552
|
let bareIdentifyPromise = null;
|
|
1553
|
+
let identityTail = Promise.resolve();
|
|
1554
|
+
const sessionStorage = config.sessionStorage || defaultStorefrontSessionStorage();
|
|
1555
|
+
let memorySession = null;
|
|
1556
|
+
function readContactSession() {
|
|
1557
|
+
if (!sessionStorage) return memorySession;
|
|
1558
|
+
try {
|
|
1559
|
+
const raw = sessionStorage.getItem(
|
|
1560
|
+
storefrontSessionStorageKey(config.baseUrl, config.storeId)
|
|
1561
|
+
);
|
|
1562
|
+
return raw ? JSON.parse(raw) : null;
|
|
1563
|
+
} catch {
|
|
1564
|
+
return memorySession;
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
function writeContactSession(session) {
|
|
1568
|
+
memorySession = session;
|
|
1569
|
+
if (!sessionStorage) return;
|
|
1570
|
+
const key = storefrontSessionStorageKey(config.baseUrl, config.storeId);
|
|
1571
|
+
try {
|
|
1572
|
+
if (session) {
|
|
1573
|
+
sessionStorage.setItem(key, JSON.stringify(session));
|
|
1574
|
+
} else {
|
|
1575
|
+
sessionStorage.removeItem(key);
|
|
1576
|
+
}
|
|
1577
|
+
} catch {
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1296
1580
|
function toPublic(s) {
|
|
1297
1581
|
return s ? { contact: s.contact, store: s.store, market: s.market } : null;
|
|
1298
1582
|
}
|
|
@@ -1348,19 +1632,23 @@ function createStorefront(config) {
|
|
|
1348
1632
|
const contactApi = storefrontApi.crm.contact;
|
|
1349
1633
|
function identify(params) {
|
|
1350
1634
|
if (params?.market !== void 0) apiConfig.market = params.market;
|
|
1635
|
+
const market = apiConfig.market;
|
|
1351
1636
|
const isBareCall = !params?.email && !params?.verify;
|
|
1352
1637
|
if (isBareCall && bareIdentifyPromise) return bareIdentifyPromise;
|
|
1353
|
-
const
|
|
1638
|
+
const run = async () => {
|
|
1354
1639
|
try {
|
|
1355
|
-
const result = await contactApi.
|
|
1356
|
-
market
|
|
1357
|
-
email: params
|
|
1358
|
-
|
|
1359
|
-
|
|
1640
|
+
const result = await (params?.verify ? contactApi.requestCode({
|
|
1641
|
+
market,
|
|
1642
|
+
email: params.email
|
|
1643
|
+
}) : contactApi.identify({
|
|
1644
|
+
market,
|
|
1645
|
+
email: params?.email
|
|
1646
|
+
}));
|
|
1360
1647
|
return {
|
|
1361
1648
|
contact: result.contact,
|
|
1362
1649
|
store: result.store,
|
|
1363
|
-
market: result.market
|
|
1650
|
+
market: result.market,
|
|
1651
|
+
verification_challenge: result.verification_challenge
|
|
1364
1652
|
};
|
|
1365
1653
|
} catch (err) {
|
|
1366
1654
|
const e = err;
|
|
@@ -1368,21 +1656,34 @@ function createStorefront(config) {
|
|
|
1368
1656
|
if (isBareCall && status === 401) {
|
|
1369
1657
|
updateSession(() => null);
|
|
1370
1658
|
const result = await contactApi.identify({
|
|
1371
|
-
market
|
|
1659
|
+
market
|
|
1372
1660
|
});
|
|
1373
1661
|
return {
|
|
1374
1662
|
contact: result.contact,
|
|
1375
1663
|
store: result.store,
|
|
1376
|
-
market: result.market
|
|
1664
|
+
market: result.market,
|
|
1665
|
+
verification_challenge: result.verification_challenge
|
|
1377
1666
|
};
|
|
1378
1667
|
}
|
|
1379
1668
|
throw err;
|
|
1380
1669
|
}
|
|
1381
|
-
}
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1670
|
+
};
|
|
1671
|
+
const promise = identityTail.then(run);
|
|
1672
|
+
identityTail = promise.then(
|
|
1673
|
+
() => void 0,
|
|
1674
|
+
() => void 0
|
|
1675
|
+
);
|
|
1676
|
+
if (isBareCall) {
|
|
1677
|
+
bareIdentifyPromise = promise;
|
|
1678
|
+
void promise.then(
|
|
1679
|
+
() => {
|
|
1680
|
+
if (bareIdentifyPromise === promise) bareIdentifyPromise = null;
|
|
1681
|
+
},
|
|
1682
|
+
() => {
|
|
1683
|
+
if (bareIdentifyPromise === promise) bareIdentifyPromise = null;
|
|
1684
|
+
}
|
|
1685
|
+
);
|
|
1686
|
+
}
|
|
1386
1687
|
return promise;
|
|
1387
1688
|
}
|
|
1388
1689
|
async function verify(params) {
|
|
@@ -1425,17 +1726,12 @@ function createStorefront(config) {
|
|
|
1425
1726
|
};
|
|
1426
1727
|
},
|
|
1427
1728
|
store: storefrontApi.store,
|
|
1428
|
-
cart: createCartController(storefrontApi.eshop.cart),
|
|
1429
1729
|
cms: storefrontApi.cms,
|
|
1430
1730
|
eshop: storefrontApi.eshop,
|
|
1431
1731
|
crm: storefrontApi.crm,
|
|
1432
1732
|
action: storefrontApi.action,
|
|
1433
1733
|
experiments: storefrontApi.experiments,
|
|
1434
1734
|
support: createStorefrontSupportApi(apiConfig),
|
|
1435
|
-
setStoreId: (storeId) => {
|
|
1436
|
-
apiConfig.storeId = storeId;
|
|
1437
|
-
bareIdentifyPromise = null;
|
|
1438
|
-
},
|
|
1439
1735
|
getStoreId: () => apiConfig.storeId,
|
|
1440
1736
|
setMarket: (key) => {
|
|
1441
1737
|
apiConfig.market = key;
|
|
@@ -1449,14 +1745,40 @@ function createStorefront(config) {
|
|
|
1449
1745
|
utils: createUtilitySurface(apiConfig)
|
|
1450
1746
|
};
|
|
1451
1747
|
}
|
|
1748
|
+
function createStorefront(config) {
|
|
1749
|
+
const sessionStorage = config.sessionStorage || defaultStorefrontSessionStorage() || void 0;
|
|
1750
|
+
const scopedConfig = {
|
|
1751
|
+
...config,
|
|
1752
|
+
sessionStorage
|
|
1753
|
+
};
|
|
1754
|
+
const client = createStorefrontClient(scopedConfig);
|
|
1755
|
+
Object.defineProperty(client, "forStore", {
|
|
1756
|
+
enumerable: true,
|
|
1757
|
+
configurable: false,
|
|
1758
|
+
writable: false,
|
|
1759
|
+
value: (storeId) => createStorefront({
|
|
1760
|
+
...scopedConfig,
|
|
1761
|
+
storeId,
|
|
1762
|
+
market: client.getMarket(),
|
|
1763
|
+
locale: client.getLocale()
|
|
1764
|
+
})
|
|
1765
|
+
});
|
|
1766
|
+
return client;
|
|
1767
|
+
}
|
|
1452
1768
|
|
|
1453
1769
|
// src/payments/stripe.ts
|
|
1770
|
+
async function defaultStripeLoader(publishableKey, options) {
|
|
1771
|
+
const { loadStripe } = await import('@stripe/stripe-js');
|
|
1772
|
+
return loadStripe(publishableKey, options);
|
|
1773
|
+
}
|
|
1454
1774
|
function normalizeCurrency(currency) {
|
|
1455
1775
|
return currency.trim().toLowerCase();
|
|
1456
1776
|
}
|
|
1457
|
-
async function createStripeConfirmationTokenController(config) {
|
|
1458
|
-
const
|
|
1459
|
-
|
|
1777
|
+
async function createStripeConfirmationTokenController(config, stripeLoader = defaultStripeLoader) {
|
|
1778
|
+
const stripe = await stripeLoader(
|
|
1779
|
+
config.publishableKey,
|
|
1780
|
+
config.connectedAccountId ? { stripeAccount: config.connectedAccountId } : void 0
|
|
1781
|
+
);
|
|
1460
1782
|
if (!stripe) throw new Error("Stripe failed to initialize");
|
|
1461
1783
|
let elements = createElements(stripe, config);
|
|
1462
1784
|
let paymentElement = elements.create("payment", {
|
|
@@ -1545,11 +1867,11 @@ function firstLocalized(value, locale) {
|
|
|
1545
1867
|
}
|
|
1546
1868
|
return "";
|
|
1547
1869
|
}
|
|
1548
|
-
function
|
|
1870
|
+
function findBlock2(blocks, keys) {
|
|
1549
1871
|
return (blocks || []).find((block) => keys.includes(block.key)) || null;
|
|
1550
1872
|
}
|
|
1551
1873
|
function blockText(blocks, keys, locale) {
|
|
1552
|
-
const block =
|
|
1874
|
+
const block = findBlock2(blocks, keys);
|
|
1553
1875
|
if (!block) return "";
|
|
1554
1876
|
return firstLocalized(block.value, locale);
|
|
1555
1877
|
}
|
|
@@ -1565,15 +1887,32 @@ function providerName(provider, locale) {
|
|
|
1565
1887
|
function entitySlug(entity, locale) {
|
|
1566
1888
|
return entity.slug?.[locale] || entity.slug?.en || Object.values(entity.slug || {})[0] || entity.id;
|
|
1567
1889
|
}
|
|
1568
|
-
function priceForMarket(prices, market,
|
|
1569
|
-
const
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
}
|
|
1890
|
+
function priceForMarket(prices, market, marketCurrency) {
|
|
1891
|
+
const marketKey = market.trim();
|
|
1892
|
+
if (!marketKey) throw new Error("A market is required to select a product price");
|
|
1893
|
+
const currency = marketCurrency?.trim().toUpperCase();
|
|
1894
|
+
if (!currency) throw new Error(`Market ${marketKey} does not have an authoritative currency`);
|
|
1895
|
+
const marketPrices = prices.filter((candidate) => candidate.market === marketKey);
|
|
1896
|
+
if (marketPrices.length === 0) {
|
|
1897
|
+
throw new Error(`Product is not priced for market ${marketKey}`);
|
|
1898
|
+
}
|
|
1899
|
+
if (marketPrices.some(
|
|
1900
|
+
(candidate) => !Number.isSafeInteger(candidate.amount) || candidate.amount < 0 || candidate.currency.trim().toUpperCase() !== currency
|
|
1901
|
+
)) {
|
|
1902
|
+
throw new Error(`Product has an invalid price for market ${marketKey}`);
|
|
1903
|
+
}
|
|
1904
|
+
const authorizedPrices = marketPrices.filter((candidate) => candidate.contact_list_id);
|
|
1905
|
+
if (authorizedPrices.length > 0) {
|
|
1906
|
+
return authorizedPrices.reduce(
|
|
1907
|
+
(lowest, candidate) => candidate.amount < lowest.amount ? candidate : lowest
|
|
1908
|
+
);
|
|
1909
|
+
}
|
|
1910
|
+
const basePrices = marketPrices.filter((candidate) => !candidate.contact_list_id);
|
|
1911
|
+
if (basePrices.length !== 1) {
|
|
1912
|
+
throw new Error(`Product does not have one base price for market ${marketKey}`);
|
|
1913
|
+
}
|
|
1914
|
+
const price = basePrices[0];
|
|
1915
|
+
return price;
|
|
1577
1916
|
}
|
|
1578
1917
|
function availableStock(client, variant) {
|
|
1579
1918
|
const fromUtility = client.utils.getAvailableStock(variant);
|
|
@@ -1592,8 +1931,9 @@ function locationToAddress(location) {
|
|
|
1592
1931
|
street2: null
|
|
1593
1932
|
};
|
|
1594
1933
|
}
|
|
1595
|
-
function
|
|
1596
|
-
|
|
1934
|
+
function createFormEntry(formId, fields) {
|
|
1935
|
+
if (!formId.trim()) throw new Error("formId is required");
|
|
1936
|
+
return { form_id: formId, fields };
|
|
1597
1937
|
}
|
|
1598
1938
|
function toProductCheckoutItems(items) {
|
|
1599
1939
|
return items.map((item) => ({
|
|
@@ -1605,36 +1945,97 @@ function toProductCheckoutItems(items) {
|
|
|
1605
1945
|
}));
|
|
1606
1946
|
}
|
|
1607
1947
|
function toServiceCheckoutItems(items) {
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1948
|
+
return items.map((item) => ({
|
|
1949
|
+
type: "service",
|
|
1950
|
+
id: item.id,
|
|
1951
|
+
service_id: item.service_id,
|
|
1952
|
+
provider_id: item.provider_id,
|
|
1953
|
+
slots: [...item.slots].sort((a, b) => a.from - b.from),
|
|
1954
|
+
forms: item.forms
|
|
1955
|
+
}));
|
|
1956
|
+
}
|
|
1957
|
+
function formValueError(field, message) {
|
|
1958
|
+
return new Error(`Invalid value for form field '${field.key}': ${message}`);
|
|
1959
|
+
}
|
|
1960
|
+
function isValidGeoLocation(value) {
|
|
1961
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1962
|
+
const location = value;
|
|
1963
|
+
if (location.label !== void 0 && location.label !== null && typeof location.label !== "string") {
|
|
1964
|
+
return false;
|
|
1965
|
+
}
|
|
1966
|
+
const coordinates = location.coordinates;
|
|
1967
|
+
if (!coordinates || typeof coordinates !== "object") return false;
|
|
1968
|
+
return Number.isFinite(coordinates.lat) && Number.isFinite(coordinates.lon) && coordinates.lat >= -90 && coordinates.lat <= 90 && coordinates.lon >= -180 && coordinates.lon <= 180;
|
|
1969
|
+
}
|
|
1970
|
+
function buildFormField(field, value) {
|
|
1971
|
+
const common = { id: field.id, key: field.key };
|
|
1972
|
+
switch (field.type) {
|
|
1973
|
+
case "text":
|
|
1974
|
+
if (typeof value !== "string") throw formValueError(field, "expected text");
|
|
1975
|
+
if (field.required && value.trim().length === 0) throw formValueError(field, "required text is blank");
|
|
1976
|
+
return { ...common, type: "text", value };
|
|
1977
|
+
case "number":
|
|
1978
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
1979
|
+
throw formValueError(field, "expected a finite number");
|
|
1980
|
+
}
|
|
1981
|
+
if (field.min !== null && field.min !== void 0 && value < field.min) {
|
|
1982
|
+
throw formValueError(field, `must be at least ${field.min}`);
|
|
1983
|
+
}
|
|
1984
|
+
if (field.max !== null && field.max !== void 0 && value > field.max) {
|
|
1985
|
+
throw formValueError(field, `must be at most ${field.max}`);
|
|
1986
|
+
}
|
|
1987
|
+
return { ...common, type: "number", value };
|
|
1988
|
+
case "boolean":
|
|
1989
|
+
if (typeof value !== "boolean") throw formValueError(field, "expected a boolean");
|
|
1990
|
+
return { ...common, type: "boolean", value };
|
|
1991
|
+
case "date":
|
|
1992
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value)) {
|
|
1993
|
+
throw formValueError(field, "expected an integer timestamp");
|
|
1994
|
+
}
|
|
1995
|
+
return { ...common, type: "date", value };
|
|
1996
|
+
case "geo_location":
|
|
1997
|
+
if (!isValidGeoLocation(value)) throw formValueError(field, "expected valid coordinates");
|
|
1998
|
+
return { ...common, type: "geo_location", value };
|
|
1999
|
+
case "select": {
|
|
2000
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
2001
|
+
throw formValueError(field, "expected a list of options");
|
|
2002
|
+
}
|
|
2003
|
+
const selected = value;
|
|
2004
|
+
if (field.required && selected.length === 0) throw formValueError(field, "at least one option is required");
|
|
2005
|
+
if (new Set(selected).size !== selected.length || selected.some((item) => !field.options.includes(item))) {
|
|
2006
|
+
throw formValueError(field, "contains an unknown or duplicate option");
|
|
2007
|
+
}
|
|
2008
|
+
return { ...common, type: "select", value: selected };
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
function isEmptyOptionalValue(field, value) {
|
|
2013
|
+
if (field.required) return false;
|
|
2014
|
+
if (field.type === "text") return value === "";
|
|
2015
|
+
if (field.type === "select") return Array.isArray(value) && value.length === 0;
|
|
2016
|
+
if (field.type === "geo_location") {
|
|
2017
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0);
|
|
2018
|
+
}
|
|
2019
|
+
return false;
|
|
2020
|
+
}
|
|
2021
|
+
function buildFormFields(schema, values) {
|
|
2022
|
+
const knownKeys = new Set(schema.map((field) => field.key));
|
|
2023
|
+
const unknownKey = Object.keys(values).find((key) => !knownKeys.has(key));
|
|
2024
|
+
if (unknownKey) throw new Error(`Form field '${unknownKey}' is not defined by the form schema`);
|
|
2025
|
+
const fields = [];
|
|
2026
|
+
for (const field of schema) {
|
|
2027
|
+
const value = values[field.key];
|
|
2028
|
+
if (value === void 0) {
|
|
2029
|
+
if (field.required) throw formValueError(field, "required value is missing");
|
|
1615
2030
|
continue;
|
|
1616
2031
|
}
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
id: item.id,
|
|
1620
|
-
service_id: item.service_id,
|
|
1621
|
-
provider_id: item.provider_id,
|
|
1622
|
-
slots: [slot],
|
|
1623
|
-
forms: item.forms || []
|
|
1624
|
-
});
|
|
2032
|
+
if (isEmptyOptionalValue(field, value)) continue;
|
|
2033
|
+
fields.push(buildFormField(field, value));
|
|
1625
2034
|
}
|
|
1626
|
-
return
|
|
1627
|
-
...item,
|
|
1628
|
-
slots: [...item.slots].sort((a, b) => a.from - b.from)
|
|
1629
|
-
}));
|
|
2035
|
+
return fields;
|
|
1630
2036
|
}
|
|
1631
|
-
function
|
|
1632
|
-
return
|
|
1633
|
-
id: block.id,
|
|
1634
|
-
key: block.key,
|
|
1635
|
-
type: block.type,
|
|
1636
|
-
value: block.value
|
|
1637
|
-
}));
|
|
2037
|
+
function createFormEntryFromValues(form, values) {
|
|
2038
|
+
return createFormEntry(form.id, buildFormFields(form.schema, values));
|
|
1638
2039
|
}
|
|
1639
2040
|
function getFormBlockType(field) {
|
|
1640
2041
|
if (field.key === "email") return "email";
|
|
@@ -1644,11 +2045,15 @@ function getFormBlockType(field) {
|
|
|
1644
2045
|
}
|
|
1645
2046
|
function getFormBlockValue(field) {
|
|
1646
2047
|
if (field.type === "boolean") return false;
|
|
1647
|
-
if (field.type === "
|
|
2048
|
+
if (field.type === "select") return [];
|
|
1648
2049
|
if (field.type === "geo_location") return {};
|
|
2050
|
+
if (field.type === "number" || field.type === "date") return void 0;
|
|
1649
2051
|
return "";
|
|
1650
2052
|
}
|
|
1651
2053
|
function formSchemaToBlock(field) {
|
|
2054
|
+
const min = field.type === "number" ? field.min : void 0;
|
|
2055
|
+
const max = field.type === "number" ? field.max : void 0;
|
|
2056
|
+
const options = field.type === "select" ? field.options : void 0;
|
|
1652
2057
|
return {
|
|
1653
2058
|
id: field.id,
|
|
1654
2059
|
key: field.key,
|
|
@@ -1656,9 +2061,9 @@ function formSchemaToBlock(field) {
|
|
|
1656
2061
|
properties: {
|
|
1657
2062
|
isRequired: field.required,
|
|
1658
2063
|
minValues: field.required ? 1 : 0,
|
|
1659
|
-
min
|
|
1660
|
-
max
|
|
1661
|
-
options
|
|
2064
|
+
min,
|
|
2065
|
+
max,
|
|
2066
|
+
options,
|
|
1662
2067
|
pattern: field.key === "email" ? "^.+@.+\\..+$" : field.key === "phone" ? "^.{6,20}$" : void 0
|
|
1663
2068
|
},
|
|
1664
2069
|
value: getFormBlockValue(field)
|
|
@@ -1701,15 +2106,13 @@ function createServiceInitialState() {
|
|
|
1701
2106
|
service: null,
|
|
1702
2107
|
availability: null,
|
|
1703
2108
|
providers: [],
|
|
2109
|
+
serviceProviders: [],
|
|
1704
2110
|
selectedProviderId: null,
|
|
1705
2111
|
currentMonth: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), 1),
|
|
1706
2112
|
calendar: [],
|
|
1707
2113
|
selectedDate: null,
|
|
1708
|
-
startDate: null,
|
|
1709
|
-
endDate: null,
|
|
1710
2114
|
slots: [],
|
|
1711
2115
|
selectedSlot: null,
|
|
1712
|
-
cart: [],
|
|
1713
2116
|
timezone: typeof window !== "undefined" ? Intl.DateTimeFormat().resolvedOptions().timeZone : "UTC",
|
|
1714
2117
|
tzGroups: {},
|
|
1715
2118
|
loading: false,
|
|
@@ -1719,7 +2122,6 @@ function createServiceInitialState() {
|
|
|
1719
2122
|
quoteError: null,
|
|
1720
2123
|
currency: null,
|
|
1721
2124
|
dateTimeConfirmed: false,
|
|
1722
|
-
isMultiDay: false,
|
|
1723
2125
|
availablePaymentMethods: [],
|
|
1724
2126
|
cartId: null,
|
|
1725
2127
|
promoCode: null
|
|
@@ -1740,8 +2142,9 @@ function normalizeTimezoneGroups(groups) {
|
|
|
1740
2142
|
function firstFiniteNumber(...values) {
|
|
1741
2143
|
return values.find((value) => typeof value === "number" && Number.isFinite(value));
|
|
1742
2144
|
}
|
|
1743
|
-
function
|
|
2145
|
+
function initializeStore(config) {
|
|
1744
2146
|
const client = createStorefront(config);
|
|
2147
|
+
const formClients = /* @__PURE__ */ new Map([[client.getStoreId(), client]]);
|
|
1745
2148
|
const session = atom(client.session);
|
|
1746
2149
|
const locale = atom(config.locale || client.getLocale());
|
|
1747
2150
|
const market_key = atom(config.market || client.getMarket());
|
|
@@ -1752,7 +2155,10 @@ function initialize(config) {
|
|
|
1752
2155
|
const store = value?.store;
|
|
1753
2156
|
const methods = value?.market?.payment_methods || [];
|
|
1754
2157
|
const hasCreditCard = methods.some((method) => method.type === "credit_card");
|
|
1755
|
-
return {
|
|
2158
|
+
return {
|
|
2159
|
+
provider: store?.payment || null,
|
|
2160
|
+
enabled: hasCreditCard && !!store?.payment
|
|
2161
|
+
};
|
|
1756
2162
|
});
|
|
1757
2163
|
const cart = atom(null);
|
|
1758
2164
|
const product_items = atom([]);
|
|
@@ -1793,7 +2199,10 @@ function initialize(config) {
|
|
|
1793
2199
|
);
|
|
1794
2200
|
const service_item_count = computed(
|
|
1795
2201
|
[cart, service_items],
|
|
1796
|
-
(cartValue, items) => Math.max(
|
|
2202
|
+
(cartValue, items) => Math.max(
|
|
2203
|
+
rawServiceItemCount(cartValue),
|
|
2204
|
+
items.reduce((total, item) => total + Math.max(1, item.slots.length), 0)
|
|
2205
|
+
)
|
|
1797
2206
|
);
|
|
1798
2207
|
const item_count = computed(
|
|
1799
2208
|
[cart, product_item_count, service_item_count],
|
|
@@ -1833,8 +2242,15 @@ function initialize(config) {
|
|
|
1833
2242
|
error: null
|
|
1834
2243
|
});
|
|
1835
2244
|
const service_state = map(createServiceInitialState());
|
|
1836
|
-
const
|
|
1837
|
-
const
|
|
2245
|
+
const service_form_definitions = /* @__PURE__ */ new Map();
|
|
2246
|
+
const service_form_state = map({
|
|
2247
|
+
provider_id: null,
|
|
2248
|
+
groups: [],
|
|
2249
|
+
loading: false,
|
|
2250
|
+
error: null
|
|
2251
|
+
});
|
|
2252
|
+
const service_form_groups = computed(service_form_state, (state) => state.groups);
|
|
2253
|
+
const service_form_blocks = computed(service_form_groups, (groups) => groups.flatMap((group) => group.blocks));
|
|
1838
2254
|
client.onAuthStateChanged((value) => session.set(value));
|
|
1839
2255
|
currency.subscribe((value) => service_state.setKey("currency", value));
|
|
1840
2256
|
session.subscribe((value) => {
|
|
@@ -1849,25 +2265,30 @@ function initialize(config) {
|
|
|
1849
2265
|
function currentLocale() {
|
|
1850
2266
|
return locale.get() || client.getLocale() || "en";
|
|
1851
2267
|
}
|
|
1852
|
-
function
|
|
1853
|
-
|
|
2268
|
+
function clientForStore(storeId) {
|
|
2269
|
+
const existing = formClients.get(storeId);
|
|
2270
|
+
if (existing) return existing;
|
|
2271
|
+
const scoped = client.forStore(storeId);
|
|
2272
|
+
formClients.set(storeId, scoped);
|
|
2273
|
+
return scoped;
|
|
1854
2274
|
}
|
|
1855
2275
|
function currentStripePublishableKey() {
|
|
1856
2276
|
const provider = payment_config.get()?.provider;
|
|
1857
|
-
return provider?.publishable_key ||
|
|
2277
|
+
return provider?.publishable_key || null;
|
|
2278
|
+
}
|
|
2279
|
+
function currentStripeConnectedAccountId() {
|
|
2280
|
+
const provider = payment_config.get()?.provider;
|
|
2281
|
+
return provider?.connected_account_id || null;
|
|
1858
2282
|
}
|
|
1859
2283
|
function currentPaymentAmount() {
|
|
1860
2284
|
return Math.max(
|
|
1861
2285
|
0,
|
|
1862
|
-
firstFiniteNumber(
|
|
1863
|
-
quote.get()?.charge_amount,
|
|
1864
|
-
quote.get()?.payment?.total,
|
|
1865
|
-
cart.get()?.quote_snapshot?.charge_amount,
|
|
1866
|
-
cart.get()?.quote_snapshot?.payment?.total,
|
|
1867
|
-
cart.get()?.quote_snapshot?.total
|
|
1868
|
-
) ?? 0
|
|
2286
|
+
firstFiniteNumber(quote.get()?.charge_amount, cart.get()?.quote_snapshot?.charge_amount, cart.get()?.quote_snapshot?.total) ?? 0
|
|
1869
2287
|
);
|
|
1870
2288
|
}
|
|
2289
|
+
function currentPaymentCurrency() {
|
|
2290
|
+
return quote.get()?.money?.currency?.trim() || cart.get()?.quote_snapshot?.money?.currency?.trim() || null;
|
|
2291
|
+
}
|
|
1871
2292
|
function setPaymentController(controller) {
|
|
1872
2293
|
const current = payment_controller.get();
|
|
1873
2294
|
if (current && current !== controller) {
|
|
@@ -1884,10 +2305,24 @@ function initialize(config) {
|
|
|
1884
2305
|
if (!publishableKey) {
|
|
1885
2306
|
throw new Error("Stripe publishable key is required to mount card payment");
|
|
1886
2307
|
}
|
|
2308
|
+
const hasExplicitAmount = options.amount !== void 0;
|
|
2309
|
+
const hasExplicitCurrency = Boolean(options.currency?.trim());
|
|
2310
|
+
if (hasExplicitAmount !== hasExplicitCurrency) {
|
|
2311
|
+
throw new Error("Stripe amount and currency must be supplied together");
|
|
2312
|
+
}
|
|
2313
|
+
const amount = hasExplicitAmount ? options.amount : currentPaymentAmount();
|
|
2314
|
+
if (!Number.isSafeInteger(amount) || amount <= 0) {
|
|
2315
|
+
throw new Error("A positive minor-unit payment amount is required to mount card payment");
|
|
2316
|
+
}
|
|
2317
|
+
const paymentCurrency = hasExplicitCurrency ? options.currency.trim() : currentPaymentCurrency();
|
|
2318
|
+
if (!paymentCurrency || !/^[a-z]{3}$/i.test(paymentCurrency)) {
|
|
2319
|
+
throw new Error("An explicit three-letter payment currency is required to mount card payment");
|
|
2320
|
+
}
|
|
1887
2321
|
const controller = await createStripeConfirmationTokenController({
|
|
1888
2322
|
publishableKey,
|
|
1889
|
-
|
|
1890
|
-
|
|
2323
|
+
connectedAccountId: options.connectedAccountId || currentStripeConnectedAccountId() || void 0,
|
|
2324
|
+
amount,
|
|
2325
|
+
currency: paymentCurrency,
|
|
1891
2326
|
...options.appearance ? { appearance: options.appearance } : {}
|
|
1892
2327
|
});
|
|
1893
2328
|
controller.mount(target);
|
|
@@ -1913,10 +2348,24 @@ function initialize(config) {
|
|
|
1913
2348
|
}
|
|
1914
2349
|
async function identify(params = {}) {
|
|
1915
2350
|
if (params.market) setMarket(params.market);
|
|
1916
|
-
const result = await client.identify({
|
|
1917
|
-
|
|
2351
|
+
const result = await client.identify({
|
|
2352
|
+
...params,
|
|
2353
|
+
market: params.market || currentMarketKey()
|
|
2354
|
+
});
|
|
2355
|
+
session.set({
|
|
2356
|
+
contact: result.contact,
|
|
2357
|
+
store: result.store,
|
|
2358
|
+
market: result.market
|
|
2359
|
+
});
|
|
1918
2360
|
return result;
|
|
1919
2361
|
}
|
|
2362
|
+
async function identifyContactEmailIfMissing(email) {
|
|
2363
|
+
const normalizedEmail = email.trim().toLowerCase();
|
|
2364
|
+
if (!normalizedEmail) throw new Error("Contact email is required");
|
|
2365
|
+
const current = session.get();
|
|
2366
|
+
if (current?.contact.email === normalizedEmail) return current;
|
|
2367
|
+
return identify({ email: normalizedEmail });
|
|
2368
|
+
}
|
|
1920
2369
|
function setMarket(key) {
|
|
1921
2370
|
market_key.set(key);
|
|
1922
2371
|
client.setMarket(key);
|
|
@@ -1941,7 +2390,9 @@ function initialize(config) {
|
|
|
1941
2390
|
const refreshRevision = cartWriteRevision;
|
|
1942
2391
|
cartRequest = (async () => {
|
|
1943
2392
|
await ensureSession();
|
|
1944
|
-
const response = await client.cart.
|
|
2393
|
+
const response = await client.eshop.cart.current({
|
|
2394
|
+
market: currentMarketKey()
|
|
2395
|
+
});
|
|
1945
2396
|
await applyCartResponse(response, { ifRevision: refreshRevision });
|
|
1946
2397
|
return response;
|
|
1947
2398
|
})();
|
|
@@ -1955,9 +2406,9 @@ function initialize(config) {
|
|
|
1955
2406
|
cart_status.setKey("loading", false);
|
|
1956
2407
|
}
|
|
1957
2408
|
}
|
|
1958
|
-
async function buildProductCartItem(item, source) {
|
|
2409
|
+
async function buildProductCartItem(item, source, productHint) {
|
|
1959
2410
|
try {
|
|
1960
|
-
const product = await client.eshop.product.get({ id: item.product_id });
|
|
2411
|
+
const product = productHint?.id === item.product_id ? productHint : await client.eshop.product.get({ id: item.product_id });
|
|
1961
2412
|
const variant = product.variants.find((candidate) => candidate.id === item.variant_id);
|
|
1962
2413
|
if (!variant) return null;
|
|
1963
2414
|
return {
|
|
@@ -1968,7 +2419,7 @@ function initialize(config) {
|
|
|
1968
2419
|
product_slug: entitySlug(product, currentLocale()),
|
|
1969
2420
|
variant_attributes: variant.attributes,
|
|
1970
2421
|
requires_shipping: variant.requires_shipping !== false,
|
|
1971
|
-
price: priceForMarket(variant.prices, currentMarketKey(),
|
|
2422
|
+
price: priceForMarket(variant.prices, currentMarketKey(), market.get()?.currency),
|
|
1972
2423
|
quantity: item.quantity,
|
|
1973
2424
|
added_at: source.created_at ? source.created_at * 1e3 : Date.now(),
|
|
1974
2425
|
max_stock: availableStock(client, variant)
|
|
@@ -1990,18 +2441,15 @@ function initialize(config) {
|
|
|
1990
2441
|
provider = await client.eshop.provider.get({ id: item.provider_id });
|
|
1991
2442
|
} catch {
|
|
1992
2443
|
}
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
provider_name: provider ? providerName(provider, currentLocale()) : item.provider_id
|
|
2003
|
-
});
|
|
2004
|
-
}
|
|
2444
|
+
rows.push({
|
|
2445
|
+
id: item.id || createId("service"),
|
|
2446
|
+
service_id: item.service_id,
|
|
2447
|
+
provider_id: item.provider_id,
|
|
2448
|
+
slots: item.slots,
|
|
2449
|
+
forms: item.forms || [],
|
|
2450
|
+
service_name: service ? serviceName(service, currentLocale()) : item.service_id,
|
|
2451
|
+
provider_name: provider ? providerName(provider, currentLocale()) : item.provider_id
|
|
2452
|
+
});
|
|
2005
2453
|
}
|
|
2006
2454
|
return rows;
|
|
2007
2455
|
}
|
|
@@ -2016,11 +2464,9 @@ function initialize(config) {
|
|
|
2016
2464
|
quote.set(response.quote_snapshot || null);
|
|
2017
2465
|
const items = response.items || [];
|
|
2018
2466
|
const products = await Promise.all(
|
|
2019
|
-
items.filter((item) => item.type === "product").map((item) => buildProductCartItem(item, response))
|
|
2020
|
-
);
|
|
2021
|
-
const services = await buildServiceCartItems(
|
|
2022
|
-
items.filter((item) => item.type === "service")
|
|
2467
|
+
items.filter((item) => item.type === "product").map((item) => buildProductCartItem(item, response, options.productHint))
|
|
2023
2468
|
);
|
|
2469
|
+
const services = await buildServiceCartItems(items.filter((item) => item.type === "service"));
|
|
2024
2470
|
product_items.set(products.filter((item) => item !== null));
|
|
2025
2471
|
service_items.set(services);
|
|
2026
2472
|
return response;
|
|
@@ -2036,21 +2482,17 @@ function initialize(config) {
|
|
|
2036
2482
|
cart_status.setKey("error", null);
|
|
2037
2483
|
try {
|
|
2038
2484
|
const current = cart.get() || await ensureCart();
|
|
2039
|
-
const response = await client.cart.update({
|
|
2485
|
+
const response = await client.eshop.cart.update({
|
|
2040
2486
|
id: current.id,
|
|
2041
2487
|
market: currentMarketKey(),
|
|
2042
2488
|
items: checkoutItems(input),
|
|
2043
|
-
shipping_address: input.shipping_address
|
|
2044
|
-
billing_address: input.billing_address
|
|
2045
|
-
forms:
|
|
2046
|
-
promo_code: input.promo_code === void 0 ? promo_code.get() || void 0 : input.promo_code
|
|
2047
|
-
payment_method_key: input.payment_method_key
|
|
2048
|
-
shipping_method_id: input.shipping_method_id
|
|
2489
|
+
shipping_address: input.shipping_address,
|
|
2490
|
+
billing_address: input.billing_address,
|
|
2491
|
+
forms: input.forms,
|
|
2492
|
+
promo_code: input.promo_code === null ? "" : input.promo_code === void 0 ? promo_code.get() || void 0 : input.promo_code,
|
|
2493
|
+
payment_method_key: input.payment_method_key === null ? "" : input.payment_method_key,
|
|
2494
|
+
shipping_method_id: input.shipping_method_id === null ? "" : input.shipping_method_id === void 0 ? cart_status.get().selected_shipping_method_id || void 0 : input.shipping_method_id
|
|
2049
2495
|
});
|
|
2050
|
-
if (input.promo_code !== void 0) promo_code.set(input.promo_code);
|
|
2051
|
-
if (input.shipping_method_id !== void 0) {
|
|
2052
|
-
cart_status.setKey("selected_shipping_method_id", input.shipping_method_id);
|
|
2053
|
-
}
|
|
2054
2496
|
await applyCartResponse(response, { ifRevision: writeRevision });
|
|
2055
2497
|
return response;
|
|
2056
2498
|
} catch (error) {
|
|
@@ -2065,7 +2507,7 @@ function initialize(config) {
|
|
|
2065
2507
|
const writeRevision = nextCartWriteRevision();
|
|
2066
2508
|
try {
|
|
2067
2509
|
const current = cart.get() || await ensureCart();
|
|
2068
|
-
const response = await client.cart.addItem({
|
|
2510
|
+
const response = await client.eshop.cart.addItem({
|
|
2069
2511
|
id: current.id,
|
|
2070
2512
|
item: {
|
|
2071
2513
|
type: "product",
|
|
@@ -2074,7 +2516,7 @@ function initialize(config) {
|
|
|
2074
2516
|
quantity
|
|
2075
2517
|
}
|
|
2076
2518
|
});
|
|
2077
|
-
await applyCartResponse(response, { ifRevision: writeRevision });
|
|
2519
|
+
await applyCartResponse(response, { ifRevision: writeRevision, productHint: product });
|
|
2078
2520
|
return response;
|
|
2079
2521
|
} catch (error) {
|
|
2080
2522
|
cart_status.setKey("error", readErrorMessage(error, "Failed to add product to cart."));
|
|
@@ -2097,11 +2539,9 @@ function initialize(config) {
|
|
|
2097
2539
|
product_items.set(product_items.get().filter((candidate) => candidate.id !== itemId));
|
|
2098
2540
|
const current = cart.get();
|
|
2099
2541
|
if (!current || !item) return null;
|
|
2100
|
-
const response = await client.cart.removeItem({
|
|
2542
|
+
const response = await client.eshop.cart.removeItem({
|
|
2101
2543
|
id: current.id,
|
|
2102
|
-
item_id: item.id
|
|
2103
|
-
product_id: item.product_id,
|
|
2104
|
-
variant_id: item.variant_id
|
|
2544
|
+
item_id: item.id
|
|
2105
2545
|
});
|
|
2106
2546
|
await applyCartResponse(response, { ifRevision: writeRevision });
|
|
2107
2547
|
return response;
|
|
@@ -2123,7 +2563,7 @@ function initialize(config) {
|
|
|
2123
2563
|
const current = cart.get();
|
|
2124
2564
|
clearLocalCart();
|
|
2125
2565
|
if (!current) return null;
|
|
2126
|
-
const response = await client.cart.clear({ id: current.id });
|
|
2566
|
+
const response = await client.eshop.cart.clear({ id: current.id });
|
|
2127
2567
|
await applyCartResponse(response, { ifRevision: writeRevision });
|
|
2128
2568
|
return response;
|
|
2129
2569
|
}
|
|
@@ -2144,7 +2584,7 @@ function initialize(config) {
|
|
|
2144
2584
|
cart_status.setKey("quote_error", null);
|
|
2145
2585
|
try {
|
|
2146
2586
|
const current = await syncCart(input);
|
|
2147
|
-
const response = await client.cart.quote({ id: current.id });
|
|
2587
|
+
const response = await client.eshop.cart.quote({ id: current.id });
|
|
2148
2588
|
quote.set(response);
|
|
2149
2589
|
return response;
|
|
2150
2590
|
} catch (error) {
|
|
@@ -2162,23 +2602,17 @@ function initialize(config) {
|
|
|
2162
2602
|
try {
|
|
2163
2603
|
const current = await syncCart(input);
|
|
2164
2604
|
const quoteValue = quote.get();
|
|
2165
|
-
const paymentMethodKey = input.payment_method_key || current.payment_method_key || quoteValue?.
|
|
2166
|
-
let chargeAmount = firstFiniteNumber(
|
|
2167
|
-
quoteValue?.charge_amount,
|
|
2168
|
-
quoteValue?.payment?.total,
|
|
2169
|
-
current.quote_snapshot?.charge_amount,
|
|
2170
|
-
current.quote_snapshot?.payment?.total
|
|
2171
|
-
);
|
|
2605
|
+
const paymentMethodKey = input.payment_method_key || current.payment_method_key || quoteValue?.money?.payment_method_key || void 0;
|
|
2606
|
+
let chargeAmount = firstFiniteNumber(quoteValue?.charge_amount, current.quote_snapshot?.charge_amount);
|
|
2172
2607
|
if (paymentMethodKey === "credit_card" && chargeAmount === void 0) {
|
|
2173
|
-
const latestQuote = await client.cart.quote({ id: current.id });
|
|
2608
|
+
const latestQuote = await client.eshop.cart.quote({ id: current.id });
|
|
2174
2609
|
quote.set(latestQuote);
|
|
2175
|
-
chargeAmount = firstFiniteNumber(
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
);
|
|
2610
|
+
chargeAmount = firstFiniteNumber(latestQuote.charge_amount, latestQuote.total);
|
|
2611
|
+
}
|
|
2612
|
+
if (paymentMethodKey === "credit_card" && (typeof chargeAmount !== "number" || !Number.isSafeInteger(chargeAmount) || chargeAmount < 0)) {
|
|
2613
|
+
throw new Error("Card checkout requires a non-negative integer charge amount in minor units");
|
|
2180
2614
|
}
|
|
2181
|
-
const needsConfirmationToken = paymentMethodKey === "credit_card" &&
|
|
2615
|
+
const needsConfirmationToken = paymentMethodKey === "credit_card" && typeof chargeAmount === "number" && chargeAmount > 0;
|
|
2182
2616
|
let confirmationTokenId;
|
|
2183
2617
|
let returnUrl = input.return_url;
|
|
2184
2618
|
const paymentController = input.payment ?? payment_controller.get();
|
|
@@ -2192,7 +2626,7 @@ function initialize(config) {
|
|
|
2192
2626
|
confirmationTokenId = token.confirmation_token_id;
|
|
2193
2627
|
returnUrl = token.return_url || returnUrl;
|
|
2194
2628
|
}
|
|
2195
|
-
const response = await client.cart.checkout({
|
|
2629
|
+
const response = await client.eshop.cart.checkout({
|
|
2196
2630
|
id: current.id,
|
|
2197
2631
|
payment_method_key: paymentMethodKey,
|
|
2198
2632
|
confirmation_token_id: confirmationTokenId,
|
|
@@ -2211,8 +2645,8 @@ function initialize(config) {
|
|
|
2211
2645
|
service_items: input.service_items || service_items.get(),
|
|
2212
2646
|
shipping_address: input.shipping_address || null,
|
|
2213
2647
|
billing_address: input.billing_address || null,
|
|
2214
|
-
total:
|
|
2215
|
-
currency:
|
|
2648
|
+
total: response.payment.amount,
|
|
2649
|
+
currency: response.payment.currency,
|
|
2216
2650
|
payment_method_key: paymentMethodKey || null,
|
|
2217
2651
|
created_at: Date.now()
|
|
2218
2652
|
};
|
|
@@ -2230,7 +2664,7 @@ function initialize(config) {
|
|
|
2230
2664
|
}
|
|
2231
2665
|
function serviceCalendar() {
|
|
2232
2666
|
const state = service_state.get();
|
|
2233
|
-
const { currentMonth, selectedDate,
|
|
2667
|
+
const { currentMonth, selectedDate, availability, selectedProviderId } = state;
|
|
2234
2668
|
const year = currentMonth.getFullYear();
|
|
2235
2669
|
const monthIndex = currentMonth.getMonth();
|
|
2236
2670
|
const first = new Date(year, monthIndex, 1);
|
|
@@ -2253,18 +2687,12 @@ function initialize(config) {
|
|
|
2253
2687
|
for (let day = 1; day <= last.getDate(); day++) {
|
|
2254
2688
|
const date = new Date(year, monthIndex, day);
|
|
2255
2689
|
const iso = `${year}-${String(monthIndex + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
2256
|
-
const isSelected = iso === selectedDate || iso === startDate || iso === endDate;
|
|
2257
|
-
let isInRange = false;
|
|
2258
|
-
if (startDate && endDate) {
|
|
2259
|
-
const time = date.getTime();
|
|
2260
|
-
isInRange = time > new Date(startDate).getTime() && time < new Date(endDate).getTime();
|
|
2261
|
-
}
|
|
2262
2690
|
cells.push({
|
|
2263
2691
|
date,
|
|
2264
2692
|
iso,
|
|
2265
2693
|
available: hasAvailableSlotsForDate(availability, iso, selectedProviderId),
|
|
2266
|
-
isSelected,
|
|
2267
|
-
isInRange,
|
|
2694
|
+
isSelected: iso === selectedDate,
|
|
2695
|
+
isInRange: false,
|
|
2268
2696
|
isToday: date.getTime() === today.getTime(),
|
|
2269
2697
|
blank: false
|
|
2270
2698
|
});
|
|
@@ -2301,46 +2729,38 @@ function initialize(config) {
|
|
|
2301
2729
|
})
|
|
2302
2730
|
}));
|
|
2303
2731
|
}
|
|
2304
|
-
function toServiceCartItem(
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2732
|
+
function toServiceCartItem(slots, forms = []) {
|
|
2733
|
+
const orderedSlots = [...slots].sort((left, right) => left.from - right.from || left.to - right.to);
|
|
2734
|
+
const [first] = orderedSlots;
|
|
2735
|
+
if (!first) throw new Error("At least one service slot is required");
|
|
2736
|
+
if (orderedSlots.some((slot) => slot.serviceId !== first.serviceId || slot.providerId !== first.providerId)) {
|
|
2737
|
+
throw new Error("A booking can only contain slots for one service and provider");
|
|
2738
|
+
}
|
|
2739
|
+
if (orderedSlots.some((slot) => !Number.isSafeInteger(slot.from) || !Number.isSafeInteger(slot.to) || slot.from >= slot.to)) {
|
|
2740
|
+
throw new Error("A booking contains an invalid service slot");
|
|
2741
|
+
}
|
|
2742
|
+
for (let index = 1; index < orderedSlots.length; index += 1) {
|
|
2743
|
+
if (orderedSlots[index - 1].to !== orderedSlots[index].from) {
|
|
2744
|
+
throw new Error("A multi-slot booking must contain adjacent slots");
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2319
2747
|
return {
|
|
2320
|
-
id:
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
isMultiDay: item.is_multi_day
|
|
2748
|
+
id: createId("booking"),
|
|
2749
|
+
service_id: first.serviceId,
|
|
2750
|
+
provider_id: first.providerId,
|
|
2751
|
+
slots: orderedSlots.map(({ from, to }) => ({ from, to })),
|
|
2752
|
+
forms,
|
|
2753
|
+
service_name: first.serviceName,
|
|
2754
|
+
date_text: first.dateText,
|
|
2755
|
+
time_text: first.timeText,
|
|
2756
|
+
is_multi_day: orderedSlots.length > 1
|
|
2330
2757
|
};
|
|
2331
2758
|
}
|
|
2332
|
-
function
|
|
2333
|
-
const next = items.map(fromServiceCartItem);
|
|
2334
|
-
const current = service_state.get().cart;
|
|
2335
|
-
if (JSON.stringify(current) !== JSON.stringify(next)) {
|
|
2336
|
-
service_state.setKey("cart", next);
|
|
2337
|
-
}
|
|
2338
|
-
}
|
|
2339
|
-
async function syncServiceCart(slots) {
|
|
2759
|
+
async function syncServiceCart(items) {
|
|
2340
2760
|
try {
|
|
2341
2761
|
return await syncCart({
|
|
2342
2762
|
product_items: product_items.get(),
|
|
2343
|
-
service_items:
|
|
2763
|
+
service_items: items
|
|
2344
2764
|
});
|
|
2345
2765
|
} catch (error) {
|
|
2346
2766
|
service_state.setKey("quoteError", readErrorMessage(error, "Failed to sync service cart."));
|
|
@@ -2357,18 +2777,22 @@ function initialize(config) {
|
|
|
2357
2777
|
const service_can_proceed = computed(service_state, (state) => {
|
|
2358
2778
|
const step = serviceCurrentStepName();
|
|
2359
2779
|
if (step === "datetime") {
|
|
2360
|
-
return
|
|
2780
|
+
return !!(state.selectedDate && state.selectedSlot);
|
|
2361
2781
|
}
|
|
2362
2782
|
if (step === "review") return true;
|
|
2363
2783
|
return false;
|
|
2364
2784
|
});
|
|
2365
2785
|
const service_month_year = computed(
|
|
2366
2786
|
service_state,
|
|
2367
|
-
(state) => state.currentMonth.toLocaleString(void 0, {
|
|
2787
|
+
(state) => state.currentMonth.toLocaleString(void 0, {
|
|
2788
|
+
month: "long",
|
|
2789
|
+
year: "numeric"
|
|
2790
|
+
})
|
|
2368
2791
|
);
|
|
2369
|
-
const service_chain_start = computed(
|
|
2370
|
-
|
|
2371
|
-
|
|
2792
|
+
const service_chain_start = computed(service_items, (items) => {
|
|
2793
|
+
const slots = items.flatMap((item) => item.slots);
|
|
2794
|
+
if (!slots.length) return null;
|
|
2795
|
+
return Math.max(...slots.map((slot) => slot.to));
|
|
2372
2796
|
});
|
|
2373
2797
|
const service_total_steps = computed(service_state, (state) => state.service ? 2 : 0);
|
|
2374
2798
|
const service_steps = computed(service_state, () => ({
|
|
@@ -2383,40 +2807,101 @@ function initialize(config) {
|
|
|
2383
2807
|
});
|
|
2384
2808
|
function formatServiceDateDisplay(value) {
|
|
2385
2809
|
if (!value) return "";
|
|
2386
|
-
return new Date(value).toLocaleDateString(void 0, {
|
|
2810
|
+
return new Date(value).toLocaleDateString(void 0, {
|
|
2811
|
+
month: "short",
|
|
2812
|
+
day: "numeric"
|
|
2813
|
+
});
|
|
2387
2814
|
}
|
|
2388
|
-
function
|
|
2389
|
-
|
|
2815
|
+
function configuredServiceFormIds(relationship) {
|
|
2816
|
+
const formIds = relationship.forms.map((entry) => entry.form_id.trim());
|
|
2817
|
+
if (formIds.some((formId) => !formId) || new Set(formIds).size !== formIds.length) {
|
|
2818
|
+
throw new Error(`Service provider ${relationship.provider_id} has blank or duplicate configured form IDs`);
|
|
2819
|
+
}
|
|
2820
|
+
return formIds;
|
|
2821
|
+
}
|
|
2822
|
+
function resolveServiceProvider(state, providerId) {
|
|
2823
|
+
const serviceId = state.service?.id;
|
|
2824
|
+
if (!serviceId) return null;
|
|
2825
|
+
const relationships = state.serviceProviders.filter((relationship) => relationship.service_id === serviceId);
|
|
2826
|
+
const targetProviderId = providerId ?? state.selectedSlot?.providerId ?? state.selectedProviderId;
|
|
2827
|
+
if (targetProviderId) {
|
|
2828
|
+
return relationships.find((relationship) => relationship.provider_id === targetProviderId) || null;
|
|
2829
|
+
}
|
|
2830
|
+
return relationships.length === 1 ? relationships[0] : null;
|
|
2831
|
+
}
|
|
2832
|
+
function clearServiceFormState(error = null, loading = false) {
|
|
2833
|
+
service_form_state.set({
|
|
2834
|
+
provider_id: null,
|
|
2835
|
+
groups: [],
|
|
2836
|
+
loading,
|
|
2837
|
+
error
|
|
2838
|
+
});
|
|
2390
2839
|
}
|
|
2391
|
-
function
|
|
2392
|
-
const
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2840
|
+
function activateServiceProviderForms(providerId, reset = false) {
|
|
2841
|
+
const relationship = resolveServiceProvider(service_state.get(), providerId);
|
|
2842
|
+
if (!relationship) {
|
|
2843
|
+
clearServiceFormState();
|
|
2844
|
+
return null;
|
|
2845
|
+
}
|
|
2846
|
+
const formIds = configuredServiceFormIds(relationship);
|
|
2847
|
+
const current = service_form_state.get();
|
|
2848
|
+
const currentFormIds = current.groups.map((group) => group.form.id);
|
|
2849
|
+
if (!reset && current.provider_id === relationship.provider_id && currentFormIds.length === formIds.length && currentFormIds.every((formId, index) => formId === formIds[index])) {
|
|
2850
|
+
if (current.error) service_form_state.setKey("error", null);
|
|
2851
|
+
return relationship;
|
|
2398
2852
|
}
|
|
2399
|
-
|
|
2853
|
+
const groups = formIds.map((formId) => {
|
|
2854
|
+
const form = service_form_definitions.get(formId);
|
|
2855
|
+
if (!form) throw new Error(`Configured booking form '${formId}' was not loaded`);
|
|
2856
|
+
return {
|
|
2857
|
+
form,
|
|
2858
|
+
blocks: form.schema.map(formSchemaToBlock)
|
|
2859
|
+
};
|
|
2860
|
+
});
|
|
2861
|
+
service_form_state.set({
|
|
2862
|
+
provider_id: relationship.provider_id,
|
|
2863
|
+
groups,
|
|
2864
|
+
loading: false,
|
|
2865
|
+
error: null
|
|
2866
|
+
});
|
|
2867
|
+
return relationship;
|
|
2400
2868
|
}
|
|
2401
|
-
async function
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2869
|
+
async function loadServiceFormDefinitions(relationships) {
|
|
2870
|
+
const formIds = [
|
|
2871
|
+
...new Set(relationships.flatMap((relationship) => configuredServiceFormIds(relationship)))
|
|
2872
|
+
];
|
|
2873
|
+
if (formIds.length === 0) return;
|
|
2874
|
+
const forms = await Promise.all(formIds.map((formId) => loadForm({ id: formId })));
|
|
2875
|
+
for (let index = 0; index < forms.length; index += 1) {
|
|
2876
|
+
const form = forms[index];
|
|
2877
|
+
const formId = formIds[index];
|
|
2878
|
+
if (form.id !== formId) {
|
|
2879
|
+
throw new Error(`Configured booking form '${formId}' resolved to '${form.id}'`);
|
|
2880
|
+
}
|
|
2410
2881
|
}
|
|
2882
|
+
for (const form of forms) service_form_definitions.set(form.id, form);
|
|
2883
|
+
}
|
|
2884
|
+
function configuredServiceFormEntries(relationship) {
|
|
2885
|
+
const formIds = configuredServiceFormIds(relationship);
|
|
2886
|
+
if (formIds.length === 0) return [];
|
|
2887
|
+
activateServiceProviderForms(relationship.provider_id);
|
|
2888
|
+
const state = service_form_state.get();
|
|
2889
|
+
if (state.provider_id !== relationship.provider_id || state.groups.length !== formIds.length || state.groups.some((group, index) => group.form.id !== formIds[index])) {
|
|
2890
|
+
throw new Error(`Booking forms are not ready for provider ${relationship.provider_id}`);
|
|
2891
|
+
}
|
|
2892
|
+
return state.groups.map(
|
|
2893
|
+
(group) => createFormEntryFromValues(
|
|
2894
|
+
group.form,
|
|
2895
|
+
Object.fromEntries(group.blocks.map((block) => [block.key, block.value]))
|
|
2896
|
+
)
|
|
2897
|
+
);
|
|
2411
2898
|
}
|
|
2412
2899
|
const service_controller = {
|
|
2413
2900
|
async initialize() {
|
|
2414
2901
|
service_state.setKey("tzGroups", normalizeTimezoneGroups(client.utils.tzGroups));
|
|
2415
2902
|
await ensureCart();
|
|
2416
|
-
setServiceCartFromServiceItems(service_items.get());
|
|
2417
2903
|
const methods = session.get()?.market?.payment_methods || [];
|
|
2418
2904
|
if (methods.length) service_state.setKey("availablePaymentMethods", methods);
|
|
2419
|
-
await loadServiceForm();
|
|
2420
2905
|
},
|
|
2421
2906
|
setTimezone(tz) {
|
|
2422
2907
|
service_state.setKey("timezone", tz);
|
|
@@ -2425,39 +2910,62 @@ function initialize(config) {
|
|
|
2425
2910
|
if (state.selectedDate) {
|
|
2426
2911
|
service_state.setKey("slots", computeServiceSlots(state.selectedDate));
|
|
2427
2912
|
service_state.setKey("selectedSlot", null);
|
|
2913
|
+
service_state.setKey("quote", null);
|
|
2914
|
+
service_state.setKey("quoteError", null);
|
|
2915
|
+
activateServiceProviderForms();
|
|
2428
2916
|
}
|
|
2429
2917
|
},
|
|
2430
2918
|
async select(service) {
|
|
2431
|
-
|
|
2919
|
+
service_form_definitions.clear();
|
|
2920
|
+
clearServiceFormState(null, true);
|
|
2921
|
+
service_state.set({
|
|
2922
|
+
...service_state.get(),
|
|
2923
|
+
service: null,
|
|
2924
|
+
serviceProviders: [],
|
|
2925
|
+
providers: [],
|
|
2926
|
+
selectedProviderId: null,
|
|
2927
|
+
availability: null,
|
|
2928
|
+
selectedDate: null,
|
|
2929
|
+
slots: [],
|
|
2930
|
+
selectedSlot: null,
|
|
2931
|
+
dateTimeConfirmed: false,
|
|
2932
|
+
quote: null,
|
|
2933
|
+
quoteError: null,
|
|
2934
|
+
loading: true
|
|
2935
|
+
});
|
|
2432
2936
|
try {
|
|
2433
|
-
const isMultiDayBlock = service.blocks?.find((block) => block.key === "isMultiDay");
|
|
2434
|
-
const blockValue = isMultiDayBlock?.value;
|
|
2435
|
-
const isMultiDay = Array.isArray(blockValue) ? blockValue[0] === true : blockValue === true;
|
|
2436
2937
|
const [fullService, serviceProviders] = await Promise.all([
|
|
2437
2938
|
client.eshop.service.get({ id: service.id }),
|
|
2438
|
-
client.eshop.service.findProviders({
|
|
2939
|
+
client.eshop.service.findProviders({
|
|
2940
|
+
service_id: service.id
|
|
2941
|
+
})
|
|
2942
|
+
]);
|
|
2943
|
+
const providerIds = [...new Set(serviceProviders.map((relationship) => relationship.provider_id))];
|
|
2944
|
+
const [providerResults] = await Promise.all([
|
|
2945
|
+
Promise.all(providerIds.map((id) => client.eshop.provider.get({ id }).catch(() => null))),
|
|
2946
|
+
loadServiceFormDefinitions(serviceProviders)
|
|
2439
2947
|
]);
|
|
2440
|
-
const providerIds = [...new Set(serviceProviders.map(serviceProviderId))];
|
|
2441
|
-
const providerResults = await Promise.all(
|
|
2442
|
-
providerIds.map((id) => client.eshop.provider.get({ id }).catch(() => null))
|
|
2443
|
-
);
|
|
2444
2948
|
service_state.set({
|
|
2445
2949
|
...service_state.get(),
|
|
2446
2950
|
service: fullService,
|
|
2951
|
+
serviceProviders,
|
|
2447
2952
|
providers: providerResults.filter((provider) => provider !== null),
|
|
2448
2953
|
selectedProviderId: null,
|
|
2449
2954
|
availability: null,
|
|
2450
2955
|
selectedDate: null,
|
|
2451
|
-
startDate: null,
|
|
2452
|
-
endDate: null,
|
|
2453
2956
|
slots: [],
|
|
2454
2957
|
selectedSlot: null,
|
|
2455
2958
|
currentMonth: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), 1),
|
|
2456
2959
|
loading: false,
|
|
2457
|
-
|
|
2960
|
+
dateTimeConfirmed: false,
|
|
2961
|
+
quote: null,
|
|
2962
|
+
quoteError: null
|
|
2458
2963
|
});
|
|
2964
|
+
activateServiceProviderForms();
|
|
2459
2965
|
await service_controller.loadMonth();
|
|
2460
2966
|
} catch (error) {
|
|
2967
|
+
service_form_definitions.clear();
|
|
2968
|
+
clearServiceFormState(readErrorMessage(error, "Failed to load booking forms."));
|
|
2461
2969
|
service_state.setKey("loading", false);
|
|
2462
2970
|
throw error;
|
|
2463
2971
|
}
|
|
@@ -2500,90 +3008,71 @@ function initialize(config) {
|
|
|
2500
3008
|
void service_controller.loadMonth();
|
|
2501
3009
|
},
|
|
2502
3010
|
selectProvider(providerId) {
|
|
3011
|
+
const state = service_state.get();
|
|
3012
|
+
if (providerId && !resolveServiceProvider(state, providerId)) {
|
|
3013
|
+
throw new Error(`Provider ${providerId} is not configured for the selected service`);
|
|
3014
|
+
}
|
|
2503
3015
|
service_state.set({
|
|
2504
|
-
...
|
|
3016
|
+
...state,
|
|
2505
3017
|
selectedProviderId: providerId,
|
|
2506
3018
|
selectedDate: null,
|
|
2507
|
-
startDate: null,
|
|
2508
|
-
endDate: null,
|
|
2509
3019
|
slots: [],
|
|
2510
|
-
selectedSlot: null
|
|
3020
|
+
selectedSlot: null,
|
|
3021
|
+
dateTimeConfirmed: false,
|
|
3022
|
+
quote: null,
|
|
3023
|
+
quoteError: null
|
|
2511
3024
|
});
|
|
3025
|
+
activateServiceProviderForms(providerId);
|
|
2512
3026
|
void service_controller.loadMonth();
|
|
2513
3027
|
},
|
|
2514
3028
|
selectDate(cell) {
|
|
2515
3029
|
if (cell.blank || !cell.available) return;
|
|
2516
|
-
service_state.setKey("dateTimeConfirmed", false);
|
|
2517
3030
|
const state = service_state.get();
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
service_state.setKey("endDate", cell.iso);
|
|
2530
|
-
}
|
|
2531
|
-
service_controller.createMultiDaySlots();
|
|
2532
|
-
} else {
|
|
2533
|
-
service_state.setKey("startDate", cell.iso);
|
|
2534
|
-
service_state.setKey("selectedDate", cell.iso);
|
|
2535
|
-
service_state.setKey("endDate", null);
|
|
2536
|
-
service_state.setKey("selectedSlot", null);
|
|
2537
|
-
}
|
|
2538
|
-
service_controller.updateCalendar();
|
|
2539
|
-
} else {
|
|
2540
|
-
service_state.set({
|
|
2541
|
-
...state,
|
|
2542
|
-
selectedDate: cell.iso,
|
|
2543
|
-
slots: computeServiceSlots(cell.iso),
|
|
2544
|
-
selectedSlot: null
|
|
2545
|
-
});
|
|
2546
|
-
service_state.setKey("calendar", serviceCalendar());
|
|
2547
|
-
}
|
|
3031
|
+
service_state.set({
|
|
3032
|
+
...state,
|
|
3033
|
+
selectedDate: cell.iso,
|
|
3034
|
+
slots: computeServiceSlots(cell.iso),
|
|
3035
|
+
selectedSlot: null,
|
|
3036
|
+
dateTimeConfirmed: false,
|
|
3037
|
+
quote: null,
|
|
3038
|
+
quoteError: null
|
|
3039
|
+
});
|
|
3040
|
+
activateServiceProviderForms();
|
|
3041
|
+
service_state.setKey("calendar", serviceCalendar());
|
|
2548
3042
|
},
|
|
2549
|
-
|
|
3043
|
+
selectTimeSlot(slot) {
|
|
2550
3044
|
const state = service_state.get();
|
|
2551
|
-
if (
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
from: slot.from,
|
|
2561
|
-
to: slot.to,
|
|
2562
|
-
timeText: formatServiceSlotTime(slot.from, slot.to, state.timezone),
|
|
2563
|
-
dateText: new Date(slot.from * 1e3).toLocaleDateString([], {
|
|
2564
|
-
weekday: "short",
|
|
2565
|
-
month: "short",
|
|
2566
|
-
day: "numeric",
|
|
2567
|
-
timeZone: state.timezone
|
|
2568
|
-
}),
|
|
2569
|
-
isMultiDay: true
|
|
2570
|
-
});
|
|
3045
|
+
if (slot) {
|
|
3046
|
+
if (!state.service || slot.serviceId !== state.service.id) {
|
|
3047
|
+
throw new Error("The selected slot does not belong to the selected service");
|
|
3048
|
+
}
|
|
3049
|
+
if (state.selectedProviderId && slot.providerId !== state.selectedProviderId) {
|
|
3050
|
+
throw new Error("The selected slot does not belong to the selected provider");
|
|
3051
|
+
}
|
|
3052
|
+
if (!resolveServiceProvider(state, slot.providerId)) {
|
|
3053
|
+
throw new Error(`Provider ${slot.providerId} is not configured for the selected service`);
|
|
2571
3054
|
}
|
|
2572
3055
|
}
|
|
2573
|
-
service_state.
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
3056
|
+
service_state.set({
|
|
3057
|
+
...state,
|
|
3058
|
+
selectedSlot: slot,
|
|
3059
|
+
dateTimeConfirmed: false,
|
|
3060
|
+
quote: null,
|
|
3061
|
+
quoteError: null
|
|
3062
|
+
});
|
|
3063
|
+
activateServiceProviderForms(slot?.providerId);
|
|
2579
3064
|
},
|
|
2580
3065
|
resetDateSelection() {
|
|
2581
|
-
service_state.
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
3066
|
+
service_state.set({
|
|
3067
|
+
...service_state.get(),
|
|
3068
|
+
selectedDate: null,
|
|
3069
|
+
slots: [],
|
|
3070
|
+
selectedSlot: null,
|
|
3071
|
+
dateTimeConfirmed: false,
|
|
3072
|
+
quote: null,
|
|
3073
|
+
quoteError: null
|
|
3074
|
+
});
|
|
3075
|
+
activateServiceProviderForms();
|
|
2587
3076
|
},
|
|
2588
3077
|
updateCalendar() {
|
|
2589
3078
|
service_state.setKey("calendar", serviceCalendar());
|
|
@@ -2596,70 +3085,79 @@ function initialize(config) {
|
|
|
2596
3085
|
}
|
|
2597
3086
|
}
|
|
2598
3087
|
},
|
|
2599
|
-
async addToCart() {
|
|
3088
|
+
async addToCart(explicitSlots) {
|
|
2600
3089
|
const state = service_state.get();
|
|
2601
|
-
const
|
|
2602
|
-
|
|
3090
|
+
const slots = explicitSlots || (state.selectedSlot ? [state.selectedSlot] : []);
|
|
3091
|
+
if (slots.length === 0) return;
|
|
3092
|
+
const first = slots[0];
|
|
3093
|
+
if (!state.service || first.serviceId !== state.service.id) {
|
|
3094
|
+
throw new Error("The booking slots do not belong to the selected service");
|
|
3095
|
+
}
|
|
3096
|
+
const relationship = resolveServiceProvider(state, first.providerId);
|
|
3097
|
+
if (!relationship) {
|
|
3098
|
+
throw new Error(`Provider ${first.providerId} is not configured for the selected service`);
|
|
3099
|
+
}
|
|
3100
|
+
let forms;
|
|
3101
|
+
try {
|
|
3102
|
+
forms = configuredServiceFormEntries(relationship);
|
|
3103
|
+
} catch (error) {
|
|
3104
|
+
service_form_state.setKey("error", readErrorMessage(error, "Booking forms are invalid."));
|
|
3105
|
+
throw error;
|
|
3106
|
+
}
|
|
3107
|
+
const displayName = serviceName(state.service, currentLocale());
|
|
3108
|
+
const enriched = slots.map((slot) => ({
|
|
2603
3109
|
...slot,
|
|
2604
|
-
serviceName:
|
|
2605
|
-
date: slot.dateText
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
if (!selected.length) return;
|
|
2610
|
-
const nextCart = [...state.cart, ...selected];
|
|
3110
|
+
serviceName: displayName,
|
|
3111
|
+
date: slot.dateText
|
|
3112
|
+
}));
|
|
3113
|
+
const nextItems = [...service_items.get(), toServiceCartItem(enriched, forms)];
|
|
3114
|
+
await syncServiceCart(nextItems);
|
|
2611
3115
|
service_state.set({
|
|
2612
|
-
...
|
|
2613
|
-
cart: nextCart,
|
|
3116
|
+
...service_state.get(),
|
|
2614
3117
|
selectedDate: null,
|
|
2615
|
-
startDate: null,
|
|
2616
|
-
endDate: null,
|
|
2617
3118
|
slots: [],
|
|
2618
|
-
selectedSlot: null
|
|
3119
|
+
selectedSlot: null,
|
|
3120
|
+
dateTimeConfirmed: false,
|
|
3121
|
+
quote: null,
|
|
3122
|
+
quoteError: null
|
|
2619
3123
|
});
|
|
2620
|
-
|
|
3124
|
+
activateServiceProviderForms(service_state.get().selectedProviderId, true);
|
|
2621
3125
|
service_state.setKey("calendar", serviceCalendar());
|
|
2622
3126
|
},
|
|
2623
|
-
async removeFromCart(
|
|
2624
|
-
|
|
2625
|
-
service_state.setKey("cart", nextCart);
|
|
2626
|
-
await syncServiceCart(nextCart);
|
|
3127
|
+
async removeFromCart(bookingId) {
|
|
3128
|
+
await syncServiceCart(service_items.get().filter((item) => item.id !== bookingId));
|
|
2627
3129
|
},
|
|
2628
3130
|
async clearCart() {
|
|
2629
|
-
service_state.setKey("cart", []);
|
|
2630
3131
|
await syncServiceCart([]);
|
|
2631
3132
|
},
|
|
2632
3133
|
async checkout(paymentMethodId, forms = []) {
|
|
2633
3134
|
const state = service_state.get();
|
|
2634
|
-
|
|
3135
|
+
const items = service_items.get();
|
|
3136
|
+
if (!items.length) throw new Error("Cart is empty");
|
|
2635
3137
|
service_state.setKey("loading", true);
|
|
2636
3138
|
try {
|
|
2637
3139
|
const result = await checkout({
|
|
2638
|
-
service_items:
|
|
2639
|
-
...toServiceCartItem(slot),
|
|
2640
|
-
forms: []
|
|
2641
|
-
})),
|
|
3140
|
+
service_items: items,
|
|
2642
3141
|
payment_method_key: paymentMethodId,
|
|
2643
3142
|
promo_code: state.promoCode || void 0,
|
|
2644
3143
|
forms
|
|
2645
3144
|
});
|
|
2646
3145
|
service_state.setKey("cartId", cart.get()?.id || null);
|
|
2647
|
-
return
|
|
2648
|
-
} catch (error) {
|
|
2649
|
-
return { success: false, error: readErrorMessage(error, "Checkout failed.") };
|
|
3146
|
+
return result;
|
|
2650
3147
|
} finally {
|
|
2651
3148
|
service_state.setKey("loading", false);
|
|
2652
3149
|
}
|
|
2653
3150
|
},
|
|
2654
3151
|
async fetchQuote(paymentMethodId, promoCode) {
|
|
2655
|
-
|
|
2656
|
-
|
|
3152
|
+
service_state.get();
|
|
3153
|
+
const items = service_items.get();
|
|
3154
|
+
if (!items.length) return null;
|
|
2657
3155
|
service_state.setKey("fetchingQuote", true);
|
|
2658
3156
|
service_state.setKey("quoteError", null);
|
|
2659
3157
|
try {
|
|
2660
3158
|
service_state.setKey("promoCode", promoCode || null);
|
|
2661
3159
|
const response = await fetchQuote({
|
|
2662
|
-
service_items:
|
|
3160
|
+
service_items: items,
|
|
2663
3161
|
payment_method_key: paymentMethodId,
|
|
2664
3162
|
promo_code: promoCode || void 0
|
|
2665
3163
|
});
|
|
@@ -2687,6 +3185,9 @@ function initialize(config) {
|
|
|
2687
3185
|
if (current === "datetime") {
|
|
2688
3186
|
service_state.setKey("selectedSlot", null);
|
|
2689
3187
|
service_state.setKey("dateTimeConfirmed", false);
|
|
3188
|
+
service_state.setKey("quote", null);
|
|
3189
|
+
service_state.setKey("quoteError", null);
|
|
3190
|
+
activateServiceProviderForms();
|
|
2690
3191
|
}
|
|
2691
3192
|
},
|
|
2692
3193
|
nextStep() {
|
|
@@ -2696,17 +3197,20 @@ function initialize(config) {
|
|
|
2696
3197
|
},
|
|
2697
3198
|
getServicePrice() {
|
|
2698
3199
|
const state = service_state.get();
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
3200
|
+
const relationship = resolveServiceProvider(state);
|
|
3201
|
+
if (!relationship) return "";
|
|
3202
|
+
try {
|
|
3203
|
+
const price = priceForMarket(relationship.prices, currentMarketKey(), market.get()?.currency);
|
|
3204
|
+
return client.utils.formatPrice([price]);
|
|
3205
|
+
} catch {
|
|
3206
|
+
return "";
|
|
3207
|
+
}
|
|
2703
3208
|
},
|
|
2704
3209
|
formatDateDisplay: formatServiceDateDisplay,
|
|
2705
|
-
serviceItemsFromSlots(slots) {
|
|
2706
|
-
return slots.
|
|
3210
|
+
serviceItemsFromSlots(slots, forms = []) {
|
|
3211
|
+
return slots.length ? [toServiceCartItem(slots, forms)] : [];
|
|
2707
3212
|
}
|
|
2708
3213
|
};
|
|
2709
|
-
service_items.subscribe((items) => setServiceCartFromServiceItems(items));
|
|
2710
3214
|
async function loadEntry(params, options) {
|
|
2711
3215
|
cms_state.setKey("loading", true);
|
|
2712
3216
|
cms_state.setKey("error", null);
|
|
@@ -2716,7 +3220,10 @@ function initialize(config) {
|
|
|
2716
3220
|
if (entryParams.id) {
|
|
2717
3221
|
const entry2 = await client.cms.entry.get(entryParams, options);
|
|
2718
3222
|
const cacheKey = entryParams.key || entryParams.id || entry2.id;
|
|
2719
|
-
cms_state.setKey("entries", {
|
|
3223
|
+
cms_state.setKey("entries", {
|
|
3224
|
+
...cms_state.get().entries,
|
|
3225
|
+
[cacheKey]: entry2
|
|
3226
|
+
});
|
|
2720
3227
|
return entry2;
|
|
2721
3228
|
}
|
|
2722
3229
|
if (!entryParams.collection_id || !entryParams.key) {
|
|
@@ -2735,7 +3242,10 @@ function initialize(config) {
|
|
|
2735
3242
|
if (!entry) {
|
|
2736
3243
|
throw new Error("CMS entry not found");
|
|
2737
3244
|
}
|
|
2738
|
-
cms_state.setKey("entries", {
|
|
3245
|
+
cms_state.setKey("entries", {
|
|
3246
|
+
...cms_state.get().entries,
|
|
3247
|
+
[entryParams.key]: entry
|
|
3248
|
+
});
|
|
2739
3249
|
return entry;
|
|
2740
3250
|
} catch (error) {
|
|
2741
3251
|
cms_state.setKey("error", readErrorMessage(error, "Failed to load CMS entry."));
|
|
@@ -2744,13 +3254,22 @@ function initialize(config) {
|
|
|
2744
3254
|
cms_state.setKey("loading", false);
|
|
2745
3255
|
}
|
|
2746
3256
|
}
|
|
3257
|
+
function formCacheKey(params) {
|
|
3258
|
+
const storeId = params.store_id || client.getStoreId();
|
|
3259
|
+
const identifier = params.id ? `id:${params.id}` : params.key ? `key:${params.key}` : "missing";
|
|
3260
|
+
return `${storeId}:${identifier}`;
|
|
3261
|
+
}
|
|
2747
3262
|
async function loadForm(params, options) {
|
|
2748
3263
|
cms_state.setKey("loading", true);
|
|
2749
3264
|
cms_state.setKey("error", null);
|
|
2750
3265
|
try {
|
|
2751
|
-
const
|
|
2752
|
-
const
|
|
2753
|
-
|
|
3266
|
+
const storeId = params.store_id || client.getStoreId();
|
|
3267
|
+
const formClient = clientForStore(storeId);
|
|
3268
|
+
const form = await formClient.cms.form.get({ ...params, store_id: storeId }, options);
|
|
3269
|
+
const forms = { ...cms_state.get().forms };
|
|
3270
|
+
forms[formCacheKey({ id: form.id, store_id: storeId })] = form;
|
|
3271
|
+
forms[formCacheKey({ key: form.key, store_id: storeId })] = form;
|
|
3272
|
+
cms_state.setKey("forms", forms);
|
|
2754
3273
|
return form;
|
|
2755
3274
|
} catch (error) {
|
|
2756
3275
|
cms_state.setKey("error", readErrorMessage(error, "Failed to load CMS form."));
|
|
@@ -2759,12 +3278,26 @@ function initialize(config) {
|
|
|
2759
3278
|
cms_state.setKey("loading", false);
|
|
2760
3279
|
}
|
|
2761
3280
|
}
|
|
2762
|
-
async function
|
|
2763
|
-
const
|
|
2764
|
-
const
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
3281
|
+
async function ensureFormClient(storeId) {
|
|
3282
|
+
const formClient = clientForStore(storeId);
|
|
3283
|
+
const marketKey = currentMarketKey();
|
|
3284
|
+
if (formClient === client) {
|
|
3285
|
+
await ensureSession();
|
|
3286
|
+
} else if (!formClient.session || marketKey && formClient.session.market?.key !== marketKey) {
|
|
3287
|
+
await formClient.identify({ market: marketKey });
|
|
3288
|
+
}
|
|
3289
|
+
return formClient;
|
|
3290
|
+
}
|
|
3291
|
+
async function submitForm(params, options) {
|
|
3292
|
+
const storeId = params.store_id || client.getStoreId();
|
|
3293
|
+
const formClient = await ensureFormClient(storeId);
|
|
3294
|
+
return formClient.cms.form.submit({ ...params, store_id: storeId }, options);
|
|
3295
|
+
}
|
|
3296
|
+
async function submitFormByKey(params, options) {
|
|
3297
|
+
const storeId = params.store_id || client.getStoreId();
|
|
3298
|
+
const form = await loadForm({ key: params.key, store_id: storeId }, options);
|
|
3299
|
+
const entry = createFormEntryFromValues(form, params.values);
|
|
3300
|
+
return submitForm({ store_id: storeId, form_id: form.id, fields: entry.fields }, options);
|
|
2768
3301
|
}
|
|
2769
3302
|
async function loadProducts(params = {}, options) {
|
|
2770
3303
|
eshop_state.setKey("loading_products", true);
|
|
@@ -2867,38 +3400,32 @@ function initialize(config) {
|
|
|
2867
3400
|
destroy: destroyPaymentController
|
|
2868
3401
|
},
|
|
2869
3402
|
applyPromoCode(code, input = {}) {
|
|
2870
|
-
promo_code.set(code);
|
|
2871
3403
|
return fetchQuote({ ...input, promo_code: code });
|
|
2872
3404
|
},
|
|
2873
3405
|
removePromoCode(input = {}) {
|
|
2874
|
-
promo_code.set(null);
|
|
2875
3406
|
return fetchQuote({ ...input, promo_code: null });
|
|
2876
3407
|
},
|
|
2877
3408
|
selectShippingMethod(id) {
|
|
2878
3409
|
cart_status.setKey("selected_shipping_method_id", id);
|
|
2879
3410
|
},
|
|
2880
3411
|
locationToAddress,
|
|
3412
|
+
createFormEntry,
|
|
2881
3413
|
buildItems: checkoutItems,
|
|
2882
3414
|
buildProductItems: toProductCheckoutItems,
|
|
2883
3415
|
buildServiceItems: toServiceCheckoutItems
|
|
2884
3416
|
};
|
|
2885
3417
|
const product_store = {
|
|
2886
3418
|
get: (params, options) => client.eshop.product.get(params, options),
|
|
2887
|
-
|
|
2888
|
-
list: loadProducts,
|
|
2889
|
-
loadListing: loadProducts,
|
|
2890
|
-
loadDetail: (params, options) => client.eshop.product.get(params, options)
|
|
3419
|
+
list: loadProducts
|
|
2891
3420
|
};
|
|
2892
3421
|
const service_store = {
|
|
2893
3422
|
get: (params, options) => client.eshop.service.get(params, options),
|
|
2894
|
-
find: loadServices,
|
|
2895
3423
|
list: loadServices,
|
|
2896
|
-
loadListing: loadServices,
|
|
2897
|
-
loadDetail: (params, options) => client.eshop.service.get(params, options),
|
|
2898
3424
|
listProviders: (params, options) => client.eshop.service.findProviders(params, options),
|
|
2899
|
-
findProviders: (params, options) => client.eshop.service.findProviders(params, options),
|
|
2900
3425
|
getAvailability: loadAvailability,
|
|
2901
3426
|
state: service_state,
|
|
3427
|
+
form_state: service_form_state,
|
|
3428
|
+
form_groups: service_form_groups,
|
|
2902
3429
|
form_blocks: service_form_blocks,
|
|
2903
3430
|
current_step_name: service_current_step_name,
|
|
2904
3431
|
can_proceed: service_can_proceed,
|
|
@@ -2915,7 +3442,6 @@ function initialize(config) {
|
|
|
2915
3442
|
nextMonth: service_controller.nextMonth,
|
|
2916
3443
|
selectProvider: service_controller.selectProvider,
|
|
2917
3444
|
selectDate: service_controller.selectDate,
|
|
2918
|
-
createMultiDaySlots: service_controller.createMultiDaySlots,
|
|
2919
3445
|
selectTimeSlot: service_controller.selectTimeSlot,
|
|
2920
3446
|
resetDateSelection: service_controller.resetDateSelection,
|
|
2921
3447
|
updateCalendar: service_controller.updateCalendar,
|
|
@@ -2939,8 +3465,8 @@ function initialize(config) {
|
|
|
2939
3465
|
currency,
|
|
2940
3466
|
allowed_payment_methods,
|
|
2941
3467
|
payment_config,
|
|
2942
|
-
cart: cart_store,
|
|
2943
3468
|
identify,
|
|
3469
|
+
identifyContactEmailIfMissing,
|
|
2944
3470
|
verify: client.verify,
|
|
2945
3471
|
me: client.me,
|
|
2946
3472
|
logout: client.logout,
|
|
@@ -2965,7 +3491,7 @@ function initialize(config) {
|
|
|
2965
3491
|
},
|
|
2966
3492
|
form: {
|
|
2967
3493
|
get: loadForm,
|
|
2968
|
-
submit:
|
|
3494
|
+
submit: submitForm,
|
|
2969
3495
|
submitByKey: submitFormByKey
|
|
2970
3496
|
},
|
|
2971
3497
|
taxonomy: client.cms.taxonomy
|
|
@@ -2976,7 +3502,7 @@ function initialize(config) {
|
|
|
2976
3502
|
service: service_store,
|
|
2977
3503
|
provider: {
|
|
2978
3504
|
get: (params, options) => client.eshop.provider.get(params, options),
|
|
2979
|
-
|
|
3505
|
+
list: loadProviders
|
|
2980
3506
|
},
|
|
2981
3507
|
order: client.eshop.order,
|
|
2982
3508
|
cart: cart_store
|
|
@@ -2999,7 +3525,29 @@ function initialize(config) {
|
|
|
2999
3525
|
utils: client.utils
|
|
3000
3526
|
};
|
|
3001
3527
|
}
|
|
3528
|
+
function initialize(config) {
|
|
3529
|
+
const stores = /* @__PURE__ */ new Map();
|
|
3530
|
+
const createScope = (scopeConfig) => {
|
|
3531
|
+
const existing = stores.get(scopeConfig.storeId);
|
|
3532
|
+
if (existing) return existing;
|
|
3533
|
+
const store = initializeStore(scopeConfig);
|
|
3534
|
+
stores.set(scopeConfig.storeId, store);
|
|
3535
|
+
Object.defineProperty(store, "forStore", {
|
|
3536
|
+
enumerable: true,
|
|
3537
|
+
configurable: false,
|
|
3538
|
+
writable: false,
|
|
3539
|
+
value: (storeId) => createScope({
|
|
3540
|
+
...config,
|
|
3541
|
+
storeId,
|
|
3542
|
+
market: store.getMarket(),
|
|
3543
|
+
locale: store.getLocale()
|
|
3544
|
+
})
|
|
3545
|
+
});
|
|
3546
|
+
return store;
|
|
3547
|
+
};
|
|
3548
|
+
return createScope(config);
|
|
3549
|
+
}
|
|
3002
3550
|
|
|
3003
|
-
export { COMMON_ACTION_KEYS, createCartController, createStorefront, createStripeConfirmationTokenController, initialize };
|
|
3551
|
+
export { COMMON_ACTION_KEYS, buildFormFields, createCartController, createFormEntry, createFormEntryFromValues, createStorefront, createStripeConfirmationTokenController, getBlockContentValue, initialize };
|
|
3004
3552
|
//# sourceMappingURL=storefront.js.map
|
|
3005
3553
|
//# sourceMappingURL=storefront.js.map
|