arky-sdk 0.1.2 → 0.3.0

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/dist/stores.cjs DELETED
@@ -1,2148 +0,0 @@
1
- 'use strict';
2
-
3
- var nanostores = require('nanostores');
4
- var persistent = require('@nanostores/persistent');
5
-
6
- // src/config.ts
7
- function setGlobalConfig(config) {
8
- API_URL = config.apiUrl;
9
- BUSINESS_ID = config.businessId;
10
- STORAGE_URL = config.storageUrl || "";
11
- }
12
- var API_URL = "";
13
- var BUSINESS_ID = "";
14
- var STORAGE_URL = "";
15
-
16
- // src/utils/queryParams.ts
17
- function buildQueryString(params) {
18
- const queryParts = [];
19
- Object.entries(params).forEach(([key, value]) => {
20
- if (value === null || value === void 0) {
21
- return;
22
- }
23
- if (Array.isArray(value)) {
24
- const jsonString = JSON.stringify(value);
25
- queryParts.push(`${key}=${encodeURIComponent(jsonString)}`);
26
- } else if (typeof value === "string") {
27
- queryParts.push(`${key}=${encodeURIComponent(value)}`);
28
- } else if (typeof value === "number" || typeof value === "boolean") {
29
- queryParts.push(`${key}=${value}`);
30
- } else if (typeof value === "object") {
31
- const jsonString = JSON.stringify(value);
32
- queryParts.push(`${key}=${encodeURIComponent(jsonString)}`);
33
- }
34
- });
35
- return queryParts.length > 0 ? `?${queryParts.join("&")}` : "";
36
- }
37
- function appendQueryString(url, params) {
38
- const queryString = buildQueryString(params);
39
- return queryString ? `${url}${queryString}` : url;
40
- }
41
-
42
- // src/services/http.ts
43
- async function get(url, options) {
44
- try {
45
- const finalUrl = options?.params ? appendQueryString(url, options.params) : url;
46
- const response = await fetch(finalUrl);
47
- if (!response.ok) {
48
- throw new Error(`HTTP error! status: ${response.status}`);
49
- }
50
- const data = await response.json();
51
- return {
52
- value: data,
53
- success: true
54
- };
55
- } catch (error) {
56
- const errorMsg = error instanceof Error ? error.message : "Unknown error occurred";
57
- return {
58
- value: null,
59
- success: false,
60
- error: errorMsg
61
- };
62
- }
63
- }
64
- async function post(url, data, options) {
65
- try {
66
- const response = await fetch(url, {
67
- method: "POST",
68
- headers: {
69
- "Content-Type": "application/json"
70
- },
71
- body: JSON.stringify(data)
72
- });
73
- if (!response.ok) {
74
- throw new Error(`HTTP error! status: ${response.status}`);
75
- }
76
- const responseData = await response.json();
77
- return {
78
- value: responseData,
79
- success: true
80
- };
81
- } catch (error) {
82
- const errorMsg = error instanceof Error ? error.message : "Unknown error occurred";
83
- return {
84
- value: null,
85
- success: false,
86
- error: errorMsg
87
- };
88
- }
89
- }
90
- var httpClient = {
91
- get,
92
- post
93
- };
94
- var http_default = httpClient;
95
-
96
- // src/api/reservation.ts
97
- var reservationApi = {
98
- // Get quote for reservation parts
99
- async getQuote({
100
- token,
101
- businessId,
102
- market,
103
- currency: currency2,
104
- userId,
105
- parts,
106
- paymentMethod = "CASH",
107
- promoCode
108
- }) {
109
- try {
110
- const lines = parts.map((part) => ({
111
- type: "SERVICE",
112
- serviceId: part.serviceId,
113
- quantity: 1
114
- }));
115
- const payload = {
116
- businessId,
117
- market,
118
- currency: currency2,
119
- userId,
120
- paymentMethod,
121
- lines,
122
- promoCode: promoCode || void 0,
123
- shippingMethodId: null
124
- };
125
- const res = await fetch(`${API_URL}/v1/payments/quote`, {
126
- method: "POST",
127
- headers: {
128
- "Content-Type": "application/json",
129
- "Authorization": `Bearer ${token}`
130
- },
131
- body: JSON.stringify(payload)
132
- });
133
- const text = await res.text();
134
- if (!res.ok) {
135
- try {
136
- const json = JSON.parse(text);
137
- return { success: false, error: json.reason || json.error || "Failed to fetch quote", code: json.code };
138
- } catch {
139
- return { success: false, error: text || "Failed to fetch quote" };
140
- }
141
- }
142
- const quote = text ? JSON.parse(text) : null;
143
- return { success: true, data: quote };
144
- } catch (e) {
145
- return {
146
- success: false,
147
- error: e.message || "Failed to fetch quote"
148
- };
149
- }
150
- },
151
- // Get available slots for a service
152
- async getAvailableSlots({
153
- businessId,
154
- serviceId,
155
- from,
156
- to,
157
- limit = 1e3,
158
- providerId = null
159
- }) {
160
- const url = `${API_URL}/v1/businesses/${businessId}/services/${serviceId}/available-slots`;
161
- const response = await http_default.get(url, {
162
- params: {
163
- from,
164
- to,
165
- limit,
166
- providerId
167
- }
168
- });
169
- if (response.success) {
170
- const json = response.value;
171
- return {
172
- success: true,
173
- data: json.data?.items || json.items || []
174
- };
175
- } else {
176
- console.error("Error fetching available slots:", response.error);
177
- return {
178
- success: false,
179
- error: response.error,
180
- data: []
181
- };
182
- }
183
- },
184
- // Get all providers for a service
185
- async getProviders({ businessId, serviceId, limit = 50 }) {
186
- const url = `${API_URL}/v1/businesses/${businessId}/providers`;
187
- const response = await http_default.get(url, {
188
- params: {
189
- serviceId,
190
- limit
191
- }
192
- });
193
- if (response.success) {
194
- const json = response.value;
195
- return {
196
- success: true,
197
- data: json.items || []
198
- };
199
- } else {
200
- console.error("Error loading providers:", response.error);
201
- return {
202
- success: false,
203
- error: response.error,
204
- data: []
205
- };
206
- }
207
- },
208
- // Get guest token or create a new one
209
- async getGuestToken() {
210
- try {
211
- const res = await fetch(`${API_URL}/v1/users/login`, {
212
- method: "POST",
213
- headers: { "Content-Type": "application/json" },
214
- body: JSON.stringify({ provider: "GUEST" })
215
- });
216
- if (!res.ok) throw new Error("Guest login failed");
217
- const json = await res.json();
218
- return {
219
- success: true,
220
- data: { token: json.accessToken }
221
- };
222
- } catch (e) {
223
- return {
224
- success: false,
225
- error: e.message
226
- };
227
- }
228
- },
229
- // Update user's phone number
230
- async updateProfilePhone({ token, phoneNumber }) {
231
- try {
232
- const res = await fetch(`${API_URL}/v1/users/update`, {
233
- method: "PUT",
234
- headers: {
235
- "Content-Type": "application/json",
236
- Authorization: `Bearer ${token}`
237
- },
238
- body: JSON.stringify({
239
- phoneNumber,
240
- phoneNumbers: [],
241
- addresses: []
242
- })
243
- });
244
- if (!res.ok) {
245
- const error = await res.text() || res.statusText;
246
- return {
247
- success: false,
248
- error
249
- };
250
- }
251
- return {
252
- success: true
253
- };
254
- } catch (e) {
255
- return {
256
- success: false,
257
- error: e.message
258
- };
259
- }
260
- },
261
- // Verify phone number with code
262
- async verifyPhoneCode({ token, phoneNumber, code }) {
263
- try {
264
- const res = await fetch(`${API_URL}/v1/users/confirm/phone-number`, {
265
- method: "PUT",
266
- headers: {
267
- "Content-Type": "application/json",
268
- Authorization: `Bearer ${token}`
269
- },
270
- body: JSON.stringify({
271
- phoneNumber,
272
- code
273
- })
274
- });
275
- if (!res.ok) {
276
- const error = await res.text() || res.statusText;
277
- return {
278
- success: false,
279
- error
280
- };
281
- }
282
- return {
283
- success: true
284
- };
285
- } catch (e) {
286
- return {
287
- success: false,
288
- error: e.message
289
- };
290
- }
291
- },
292
- // Complete reservation checkout - Backend calculates currency from market
293
- async checkout({
294
- token,
295
- businessId,
296
- parts,
297
- paymentMethod = "CASH",
298
- blocks = [],
299
- market = "US",
300
- promoCode
301
- }) {
302
- try {
303
- const payload = {
304
- businessId,
305
- blocks,
306
- market,
307
- parts: parts.map((p) => ({
308
- serviceId: p.serviceId,
309
- from: p.from,
310
- to: p.to,
311
- blocks: p.blocks,
312
- reservationMethod: p.reservationMethod,
313
- providerId: p.providerId
314
- }))
315
- };
316
- if (paymentMethod !== void 0) {
317
- payload.paymentMethod = paymentMethod;
318
- }
319
- if (promoCode) {
320
- payload.promoCode = promoCode;
321
- }
322
- const res = await fetch(`${API_URL}/v1/reservations/checkout`, {
323
- method: "POST",
324
- headers: {
325
- "Content-Type": "application/json",
326
- Authorization: `Bearer ${token}`
327
- },
328
- body: JSON.stringify(payload)
329
- });
330
- if (!res.ok) {
331
- const error = await res.text() || res.statusText;
332
- throw new Error(error);
333
- }
334
- const json = await res.json();
335
- return {
336
- success: true,
337
- data: json
338
- // Should include reservationId and clientSecret for payments
339
- };
340
- } catch (e) {
341
- return {
342
- success: false,
343
- error: e.message
344
- };
345
- }
346
- }
347
- };
348
-
349
- // src/services/auth.ts
350
- async function getGuestToken(currentToken = null) {
351
- if (currentToken) return currentToken;
352
- const response = await reservationApi.getGuestToken();
353
- if (response.success && response.data) {
354
- return response.data.token;
355
- }
356
- throw new Error("Failed to get guest token");
357
- }
358
- async function updateProfilePhone(token, phoneNumber) {
359
- if (!phoneNumber) {
360
- throw new Error("Phone number is required");
361
- }
362
- const response = await reservationApi.updateProfilePhone({ token, phoneNumber });
363
- if (response.success) {
364
- return { success: true };
365
- } else {
366
- throw new Error(response.error || "Failed to send verification code");
367
- }
368
- }
369
- async function verifyPhoneCode(token, phoneNumber, code) {
370
- if (!code) {
371
- throw new Error("Verification code is required");
372
- }
373
- const response = await reservationApi.verifyPhoneCode({ token, phoneNumber, code });
374
- if (response.success) {
375
- return { success: true };
376
- } else {
377
- throw new Error(response.error || "Invalid verification code");
378
- }
379
- }
380
- async function getBusinessConfig(businessId) {
381
- try {
382
- const response = await fetch(`${API_URL}/v1/businesses/${businessId}`, {
383
- method: "GET",
384
- headers: {
385
- "Content-Type": "application/json"
386
- }
387
- });
388
- if (!response.ok) {
389
- throw new Error(`Failed to fetch business config: ${response.status}`);
390
- }
391
- const business = await response.json();
392
- return {
393
- success: true,
394
- data: business
395
- };
396
- } catch (error) {
397
- return {
398
- success: false,
399
- error: error.message
400
- };
401
- }
402
- }
403
-
404
- // src/utils/currency.ts
405
- function getCurrencySymbol(currency2) {
406
- const currencySymbols = {
407
- USD: "$",
408
- EUR: "\u20AC",
409
- GBP: "\xA3",
410
- CAD: "C$",
411
- AUD: "A$",
412
- JPY: "\xA5",
413
- CHF: "CHF",
414
- SEK: "kr",
415
- NOK: "kr",
416
- DKK: "kr",
417
- PLN: "z\u0142",
418
- CZK: "K\u010D",
419
- HUF: "Ft",
420
- RON: "lei",
421
- BGN: "\u043B\u0432",
422
- HRK: "kn",
423
- RSD: "\u0434\u0438\u043D",
424
- BAM: "KM",
425
- MKD: "\u0434\u0435\u043D",
426
- ALL: "L",
427
- TRY: "\u20BA",
428
- RUB: "\u20BD",
429
- UAH: "\u20B4",
430
- BYN: "Br",
431
- CNY: "\xA5",
432
- INR: "\u20B9",
433
- KRW: "\u20A9",
434
- THB: "\u0E3F",
435
- VND: "\u20AB",
436
- SGD: "S$",
437
- MYR: "RM",
438
- IDR: "Rp",
439
- PHP: "\u20B1",
440
- BRL: "R$",
441
- ARS: "$",
442
- CLP: "$",
443
- COP: "$",
444
- PEN: "S/",
445
- MXN: "$",
446
- ZAR: "R",
447
- EGP: "E\xA3",
448
- NGN: "\u20A6",
449
- KES: "KSh",
450
- GHS: "\u20B5",
451
- MAD: "DH",
452
- TND: "\u062F.\u062A",
453
- DZD: "\u062F.\u062C",
454
- LYD: "\u0644.\u062F",
455
- AED: "\u062F.\u0625",
456
- SAR: "\u0631.\u0633",
457
- QAR: "\u0631.\u0642",
458
- KWD: "\u062F.\u0643",
459
- BHD: "\u0628.\u062F",
460
- OMR: "\u0631.\u0639",
461
- JOD: "\u062F.\u0623",
462
- LBP: "\u0644.\u0644",
463
- SYP: "\u0644.\u0633",
464
- IQD: "\u0639.\u062F",
465
- IRR: "\uFDFC",
466
- AFN: "\u060B",
467
- PKR: "\u20A8",
468
- LKR: "\u20A8",
469
- NPR: "\u20A8",
470
- BDT: "\u09F3",
471
- MMK: "K",
472
- LAK: "\u20AD",
473
- KHR: "\u17DB",
474
- MNT: "\u20AE",
475
- KZT: "\u20B8",
476
- UZS: "\u043B\u0432",
477
- KGS: "\u043B\u0432",
478
- TJS: "SM",
479
- TMT: "T",
480
- AZN: "\u20BC",
481
- GEL: "\u20BE",
482
- AMD: "\u058F",
483
- BYR: "p.",
484
- MDL: "L"
485
- };
486
- return currencySymbols[currency2.toUpperCase()] || currency2;
487
- }
488
-
489
- // src/stores/business.ts
490
- var businessStore = nanostores.deepMap({
491
- data: null,
492
- loading: false,
493
- error: null,
494
- initialized: false
495
- });
496
- var selectedMarket = nanostores.computed(businessStore, (state) => {
497
- if (!state.data?.configs?.markets) return null;
498
- const markets2 = state.data.configs.markets;
499
- return markets2.find((m) => m.id === "us") || markets2[0] || null;
500
- });
501
- var currency = nanostores.computed(selectedMarket, (market) => {
502
- return market?.currency || "USD";
503
- });
504
- var currencySymbol = nanostores.computed(selectedMarket, (market) => {
505
- return getCurrencySymbol(market?.currency || "USD");
506
- });
507
- var markets = nanostores.computed(businessStore, (state) => {
508
- if (!state.data?.configs?.markets) return [];
509
- return state.data.configs.markets;
510
- });
511
- var zones = nanostores.computed(businessStore, (state) => {
512
- if (!state.data?.configs?.zones) return [];
513
- return state.data.configs.zones;
514
- });
515
- var getZoneByCountry = (countryCode) => {
516
- const allZones = zones.get();
517
- return allZones.find(
518
- (zone) => zone.countries.length === 0 || // Empty = all countries
519
- zone.countries.includes(countryCode.toUpperCase())
520
- ) || null;
521
- };
522
- var getShippingMethodsForCountry = (countryCode) => {
523
- const zone = getZoneByCountry(countryCode);
524
- return zone?.shippingMethods || [];
525
- };
526
- var paymentMethods = nanostores.computed(selectedMarket, (market) => {
527
- if (!market) return ["CASH"];
528
- const methods = market.paymentMethods || [];
529
- return methods.map((pm) => pm.method || pm);
530
- });
531
- var paymentConfig = nanostores.computed(businessStore, (state) => {
532
- if (!state.data?.configs) return { provider: null, enabled: false };
533
- const provider = state.data.configs.paymentProvider || null;
534
- const hasCreditCard = paymentMethods.get().includes("CREDIT_CARD");
535
- return {
536
- provider,
537
- enabled: hasCreditCard && !!provider
538
- };
539
- });
540
- var orderBlocks = nanostores.computed(businessStore, (state) => {
541
- return state.data?.configs?.orderBlocks || [];
542
- });
543
- var reservationBlocks = nanostores.computed(businessStore, (state) => {
544
- return state.data?.configs?.reservationBlocks || [];
545
- });
546
- var businessActions = {
547
- // Initialize business data - SINGLE API CALL for entire app
548
- async init() {
549
- const state = businessStore.get();
550
- if (state.initialized && state.data) {
551
- return;
552
- }
553
- try {
554
- businessStore.setKey("loading", true);
555
- businessStore.setKey("error", null);
556
- const result = await getBusinessConfig(BUSINESS_ID);
557
- if (result.success) {
558
- businessStore.setKey("data", result.data);
559
- businessStore.setKey("initialized", true);
560
- } else {
561
- throw new Error(result.error || "Failed to load business configuration");
562
- }
563
- } catch (error) {
564
- businessStore.setKey("error", error.message);
565
- console.error("Business store initialization failed:", error);
566
- } finally {
567
- businessStore.setKey("loading", false);
568
- }
569
- },
570
- // Reset store (useful for testing)
571
- reset() {
572
- businessStore.setKey("data", null);
573
- businessStore.setKey("loading", false);
574
- businessStore.setKey("error", null);
575
- businessStore.setKey("initialized", false);
576
- },
577
- // Get business data (with auto-init)
578
- async getBusiness() {
579
- const state = businessStore.get();
580
- if (!state.initialized || !state.data) {
581
- await this.init();
582
- }
583
- return businessStore.get().data;
584
- }
585
- };
586
- if (typeof window !== "undefined" && BUSINESS_ID) {
587
- businessActions.init().catch(console.error);
588
- }
589
-
590
- // src/api/eshop.ts
591
- var eshopApi = {
592
- // Get products
593
- async getProducts({
594
- businessId,
595
- categoryIds = null,
596
- status = "ACTIVE",
597
- limit = 20,
598
- cursor = null
599
- }) {
600
- const url = `${API_URL}/v1/businesses/${encodeURIComponent(businessId)}/products`;
601
- const response = await http_default.get(url, {
602
- params: {
603
- categoryIds: categoryIds && categoryIds.length > 0 ? categoryIds : void 0,
604
- status,
605
- limit,
606
- cursor
607
- }
608
- });
609
- if (response.success) {
610
- const json = response.value;
611
- return {
612
- success: true,
613
- data: json.items || [],
614
- cursor: json.cursor,
615
- total: json.total || 0
616
- };
617
- } else {
618
- console.error("Error fetching products:", response.error);
619
- return {
620
- success: false,
621
- error: response.error,
622
- data: []
623
- };
624
- }
625
- },
626
- // Get product by slug
627
- async getProductBySlug({ businessId, slug }) {
628
- try {
629
- const url = `${API_URL}/v1/businesses/${encodeURIComponent(businessId)}/products/slug/${encodeURIComponent(businessId)}/${encodeURIComponent(slug)}`;
630
- const res = await fetch(url);
631
- if (!res.ok) throw new Error("Product not found");
632
- const json = await res.json();
633
- return {
634
- success: true,
635
- data: json
636
- };
637
- } catch (e) {
638
- console.error("Error fetching product:", e);
639
- return {
640
- success: false,
641
- error: e.message,
642
- data: null
643
- };
644
- }
645
- },
646
- // Get quote for cart (pricing with promo codes, shipping, taxes)
647
- async getQuote({
648
- token,
649
- businessId,
650
- items,
651
- market,
652
- currency: currency2,
653
- userId,
654
- paymentMethod,
655
- shippingMethodId,
656
- promoCode
657
- }) {
658
- try {
659
- const lines = items.map((item) => ({
660
- type: "PRODUCT_VARIANT",
661
- productId: item.productId,
662
- variantId: item.variantId,
663
- quantity: item.quantity
664
- }));
665
- const payload = {
666
- businessId,
667
- market,
668
- currency: currency2,
669
- userId,
670
- paymentMethod,
671
- lines,
672
- ...shippingMethodId && { shippingMethodId },
673
- ...promoCode && { promoCode }
674
- };
675
- const res = await fetch(`${API_URL}/v1/payments/quote`, {
676
- method: "POST",
677
- headers: {
678
- "Content-Type": "application/json",
679
- Authorization: `Bearer ${token}`
680
- },
681
- body: JSON.stringify(payload)
682
- });
683
- const text = await res.text();
684
- if (!res.ok) {
685
- try {
686
- const json = JSON.parse(text);
687
- return { success: false, error: json.reason || json.error || res.statusText, code: json.code };
688
- } catch {
689
- return { success: false, error: text || res.statusText };
690
- }
691
- }
692
- const quote = text ? JSON.parse(text) : null;
693
- return { success: true, data: quote };
694
- } catch (e) {
695
- console.error("Quote fetch failed:", e);
696
- return {
697
- success: false,
698
- error: e.message
699
- };
700
- }
701
- },
702
- // Checkout - Backend calculates currency from market
703
- async checkout({
704
- token,
705
- businessId,
706
- items,
707
- paymentMethod,
708
- blocks,
709
- market = "US",
710
- shippingMethodId,
711
- promoCode,
712
- paymentIntentId = null
713
- }) {
714
- try {
715
- const payload = {
716
- businessId,
717
- items,
718
- paymentMethod,
719
- blocks,
720
- market,
721
- ...shippingMethodId && { shippingMethodId },
722
- ...promoCode && { promoCode },
723
- ...paymentIntentId && { paymentIntentId }
724
- };
725
- const res = await fetch(`${API_URL}/v1/businesses/${encodeURIComponent(businessId)}/orders/checkout`, {
726
- method: "POST",
727
- headers: {
728
- "Content-Type": "application/json",
729
- Authorization: `Bearer ${token}`
730
- },
731
- body: JSON.stringify(payload)
732
- });
733
- const text = await res.text();
734
- if (!res.ok) {
735
- try {
736
- const json2 = JSON.parse(text);
737
- return { success: false, error: json2.reason || json2.error || res.statusText, code: json2.code };
738
- } catch {
739
- return { success: false, error: text || res.statusText };
740
- }
741
- }
742
- const json = text ? JSON.parse(text) : null;
743
- return { success: true, data: json };
744
- } catch (e) {
745
- return {
746
- success: false,
747
- error: e.message
748
- };
749
- }
750
- },
751
- // Create payment intent for Stripe
752
- async createPaymentIntent({ amount, currency: currency2, businessId }) {
753
- try {
754
- const tokenResponse = await reservationApi.getGuestToken();
755
- if (!tokenResponse.success || !tokenResponse.data) {
756
- throw new Error("Failed to get guest token");
757
- }
758
- const token = tokenResponse.data.token;
759
- const res = await fetch(`${API_URL}/v1/businesses/${encodeURIComponent(businessId)}/payment/create-intent`, {
760
- method: "POST",
761
- headers: {
762
- "Content-Type": "application/json",
763
- Authorization: `Bearer ${token}`
764
- },
765
- body: JSON.stringify({
766
- amount,
767
- currency: currency2,
768
- businessId
769
- })
770
- });
771
- if (!res.ok) {
772
- const error = await res.text() || res.statusText;
773
- throw new Error(error);
774
- }
775
- const json = await res.json();
776
- return {
777
- success: true,
778
- data: json
779
- };
780
- } catch (e) {
781
- console.error("Payment intent creation failed:", e);
782
- return {
783
- success: false,
784
- error: e.message
785
- };
786
- }
787
- }
788
- };
789
-
790
- // src/utils/price.ts
791
- var CURRENCY_SYMBOLS = {
792
- "USD": "$",
793
- "EUR": "\u20AC",
794
- "GBP": "\xA3",
795
- "CAD": "C$",
796
- "AUD": "A$"
797
- };
798
- var MARKET_CURRENCIES = {
799
- "US": "USD",
800
- "EU": "EUR",
801
- "UK": "GBP",
802
- "CA": "CAD",
803
- "AU": "AUD"
804
- };
805
- function convertToMajor(minorAmount) {
806
- return (minorAmount ?? 0) / 100;
807
- }
808
- function getSymbol(currency2) {
809
- return CURRENCY_SYMBOLS[currency2] || "$";
810
- }
811
- function getCurrencyFromMarket(marketId) {
812
- return MARKET_CURRENCIES[marketId] || "USD";
813
- }
814
- function formatCurrencyAmount(amount, currency2, options = {}) {
815
- const { showSymbols = true, decimalPlaces = 2, customSymbol } = options;
816
- const roundedAmount = amount.toFixed(decimalPlaces);
817
- if (!showSymbols) {
818
- return `${roundedAmount} ${currency2}`;
819
- }
820
- const symbol = customSymbol || getSymbol(currency2);
821
- return `${symbol}${roundedAmount}`;
822
- }
823
- function formatMinor(amountMinor, currency2, options = {}) {
824
- const major = convertToMajor(amountMinor);
825
- return formatCurrencyAmount(major, currency2, options);
826
- }
827
- function formatPayment(payment, options = {}) {
828
- if (!payment) return "";
829
- const { showSymbols = true, decimalPlaces = 2, showBreakdown = false } = options;
830
- if (showBreakdown) {
831
- const subtotal = formatMinor(payment.subtotal, payment.currency, { showSymbols, decimalPlaces });
832
- const discount = (payment.discount ?? 0) > 0 ? formatMinor(payment.discount, payment.currency, { showSymbols, decimalPlaces }) : null;
833
- const tax = (payment.tax ?? 0) > 0 ? formatMinor(payment.tax, payment.currency, { showSymbols, decimalPlaces }) : null;
834
- const total = formatMinor(payment.total, payment.currency, { showSymbols, decimalPlaces });
835
- let result = `Subtotal: ${subtotal}`;
836
- if (discount) result += `, Discount: -${discount}`;
837
- if (tax) result += `, Tax: ${tax}`;
838
- result += `, Total: ${total}`;
839
- return result;
840
- }
841
- return formatMinor(payment.total, payment.currency, { showSymbols, decimalPlaces });
842
- }
843
- function getMarketPrice(prices, marketId, businessMarkets, options = {}) {
844
- if (!prices || prices.length === 0) return "";
845
- const {
846
- showSymbols = true,
847
- decimalPlaces = 2,
848
- showCompareAt = true,
849
- fallbackMarket = "US"
850
- } = options;
851
- let price = prices.find((p) => p.market === marketId);
852
- if (!price) {
853
- price = prices.find((p) => p.market === fallbackMarket) || prices[0];
854
- }
855
- if (!price) return "";
856
- let currency2;
857
- let symbol;
858
- {
859
- currency2 = getCurrencyFromMarket(price.market);
860
- symbol = getSymbol(currency2);
861
- }
862
- const formattedPrice = formatMinor(price.amount ?? 0, currency2, {
863
- showSymbols,
864
- decimalPlaces,
865
- customSymbol: symbol
866
- });
867
- if (showCompareAt && price.compareAt && price.compareAt > (price.amount ?? 0)) {
868
- const formattedCompareAt = formatMinor(price.compareAt, currency2, {
869
- showSymbols,
870
- decimalPlaces,
871
- customSymbol: symbol
872
- });
873
- return `${formattedPrice} was ${formattedCompareAt}`;
874
- }
875
- return formattedPrice;
876
- }
877
- function getPriceAmount(prices, marketId, fallbackMarket = "US") {
878
- if (!prices || prices.length === 0) return 0;
879
- const price = prices.find((p) => p.market === marketId) || prices.find((p) => p.market === fallbackMarket) || prices[0];
880
- return price?.amount || 0;
881
- }
882
- function createPaymentForCheckout(subtotalMinor, marketId, currency2, paymentMethod, options = {}) {
883
- const { discount = 0, tax = 0, promoCodeId } = options;
884
- const total = subtotalMinor - discount + tax;
885
- return {
886
- currency: currency2,
887
- market: marketId,
888
- subtotal: subtotalMinor,
889
- shipping: 0,
890
- discount,
891
- tax,
892
- total,
893
- promoCodeId,
894
- method: paymentMethod
895
- };
896
- }
897
-
898
- // src/stores/eshop.ts
899
- var cartItems = persistent.persistentAtom("eshopCart", [], {
900
- encode: JSON.stringify,
901
- decode: JSON.parse
902
- });
903
- var promoCodeAtom = nanostores.atom(null);
904
- var quoteAtom = nanostores.atom(null);
905
- var store = nanostores.deepMap({
906
- businessId: BUSINESS_ID,
907
- selectedShippingMethodId: null,
908
- // Selected shipping method ID
909
- shippingLocation: null,
910
- // Deprecated; kept for backward compat
911
- userToken: null,
912
- processingCheckout: false,
913
- loading: false,
914
- error: null,
915
- // Phone verification
916
- phoneNumber: "",
917
- phoneError: null,
918
- verificationCode: "",
919
- verifyError: null,
920
- // Quote fetching
921
- fetchingQuote: false,
922
- quoteError: null
923
- });
924
- var cartTotal = nanostores.computed([cartItems, selectedMarket, currency], (items, market, curr) => {
925
- const subtotalMinor = (items || []).reduce((sum, item) => {
926
- let amountMinor = 0;
927
- if ("amount" in item.price) {
928
- amountMinor = item.price.amount || 0;
929
- }
930
- return sum + amountMinor * item.quantity;
931
- }, 0);
932
- const marketId = market?.id || "us";
933
- const currencyCode = curr || "USD";
934
- return createPaymentForCheckout(subtotalMinor, marketId, currencyCode, "CASH" /* Cash */);
935
- });
936
- var cartItemCount = nanostores.computed(cartItems, (items) => {
937
- return items.reduce((sum, item) => sum + item.quantity, 0);
938
- });
939
- var allowedPaymentMethods = paymentMethods;
940
- var actions = {
941
- // Add item to cart
942
- addItem(product, variant, quantity = 1) {
943
- const items = cartItems.get();
944
- const market = selectedMarket.get();
945
- const existingItemIndex = items.findIndex(
946
- (item) => item.productId === product.id && item.variantId === variant.id
947
- );
948
- if (existingItemIndex !== -1) {
949
- const updatedItems = [...items];
950
- updatedItems[existingItemIndex].quantity += quantity;
951
- cartItems.set(updatedItems);
952
- } else {
953
- let cartPrice;
954
- if (variant.prices && Array.isArray(variant.prices)) {
955
- const marketCode = market?.id || "us";
956
- const marketAmount = getPriceAmount(variant.prices, marketCode);
957
- cartPrice = {
958
- amount: marketAmount ?? 0,
959
- market: marketCode
960
- };
961
- } else {
962
- cartPrice = { amount: 0, market: market?.id || "us" };
963
- }
964
- const newItem = {
965
- id: crypto.randomUUID(),
966
- productId: product.id,
967
- variantId: variant.id,
968
- productName: product.name,
969
- productSlug: product.slug,
970
- variantAttributes: variant.attributes || {},
971
- price: cartPrice,
972
- quantity,
973
- addedAt: Date.now()
974
- };
975
- cartItems.set([...items, newItem]);
976
- }
977
- },
978
- // Update item quantity
979
- updateQuantity(itemId, newQuantity) {
980
- const items = cartItems.get();
981
- const updatedItems = items.map(
982
- (item) => item.id === itemId ? { ...item, quantity: Math.max(1, newQuantity) } : item
983
- );
984
- cartItems.set(updatedItems);
985
- },
986
- // Remove item from cart
987
- removeItem(itemId) {
988
- const items = cartItems.get();
989
- const updatedItems = items.filter((item) => item.id !== itemId);
990
- cartItems.set(updatedItems);
991
- },
992
- // Clear entire cart
993
- clearCart() {
994
- cartItems.set([]);
995
- },
996
- // Get guest token
997
- async getGuestToken() {
998
- const state = store.get();
999
- const token = await getGuestToken(state.userToken);
1000
- if (token !== state.userToken) {
1001
- store.setKey("userToken", token);
1002
- }
1003
- return token;
1004
- },
1005
- // Prepare order items for checkout API
1006
- prepareOrderItems() {
1007
- const items = cartItems.get();
1008
- return items.map((item) => ({
1009
- productId: item.productId,
1010
- variantId: item.variantId,
1011
- quantity: item.quantity
1012
- }));
1013
- },
1014
- // Get order info blocks (they already have values from DynamicForm)
1015
- getOrderInfoBlocks() {
1016
- return orderBlocks.get() || [];
1017
- },
1018
- // Process checkout - Updated to use Payment structure
1019
- async checkout(paymentMethod = "CASH" /* Cash */, orderInfoBlocks, promoCode) {
1020
- const items = cartItems.get();
1021
- if (!items.length) {
1022
- return { success: false, error: "Cart is empty" };
1023
- }
1024
- try {
1025
- store.setKey("processingCheckout", true);
1026
- store.setKey("error", null);
1027
- const token = await this.getGuestToken();
1028
- const orderItems = this.prepareOrderItems();
1029
- const blocks = orderInfoBlocks || this.getOrderInfoBlocks();
1030
- const state = store.get();
1031
- const market = selectedMarket.get();
1032
- if (!market) {
1033
- throw new Error("No market selected");
1034
- }
1035
- const locationBlock = blocks.find((b) => b.key === "location" && b.type === "GEO_LOCATION");
1036
- const countryCode = locationBlock?.value?.[0]?.countryCode;
1037
- if (!countryCode) {
1038
- throw new Error("Country is required for checkout");
1039
- }
1040
- const availableShippingMethods = getShippingMethodsForCountry(countryCode) || [];
1041
- if (!availableShippingMethods || availableShippingMethods.length === 0) {
1042
- throw new Error(`No shipping methods available for country: ${countryCode}`);
1043
- }
1044
- const shippingMethodId = state.selectedShippingMethodId;
1045
- const shippingMethod = availableShippingMethods.find((sm) => sm.id === shippingMethodId) || availableShippingMethods[0];
1046
- if (!shippingMethod) {
1047
- throw new Error("No shipping method available");
1048
- }
1049
- const promo = promoCode !== void 0 ? promoCode : promoCodeAtom.get();
1050
- const response = await eshopApi.checkout({
1051
- token,
1052
- businessId: BUSINESS_ID,
1053
- items: orderItems,
1054
- paymentMethod,
1055
- blocks,
1056
- market: market.id,
1057
- shippingMethodId: shippingMethod.id,
1058
- promoCode: promo || void 0
1059
- });
1060
- if (response.success) {
1061
- return {
1062
- success: true,
1063
- data: {
1064
- orderId: response.data.orderId,
1065
- orderNumber: response.data.orderNumber,
1066
- clientSecret: response.data.clientSecret
1067
- }
1068
- };
1069
- } else {
1070
- throw new Error(response.error || "Failed to place order");
1071
- }
1072
- } catch (err) {
1073
- const errorMessage = `Checkout failed: ${err.message}`;
1074
- store.setKey("error", errorMessage);
1075
- console.error("Checkout error:", err);
1076
- return { success: false, error: errorMessage };
1077
- } finally {
1078
- store.setKey("processingCheckout", false);
1079
- }
1080
- },
1081
- // Phone verification for eshop
1082
- async updateProfilePhone() {
1083
- try {
1084
- const token = await this.getGuestToken();
1085
- const phoneNumber = store.get().phoneNumber;
1086
- await updateProfilePhone(token, phoneNumber);
1087
- store.setKey("phoneError", null);
1088
- return true;
1089
- } catch (error) {
1090
- console.error("Phone update error:", error);
1091
- store.setKey("phoneError", error.message);
1092
- return false;
1093
- }
1094
- },
1095
- async verifyPhoneCode() {
1096
- try {
1097
- const token = await this.getGuestToken();
1098
- const phoneNumber = store.get().phoneNumber;
1099
- const verificationCode = store.get().verificationCode;
1100
- await verifyPhoneCode(token, phoneNumber, verificationCode);
1101
- store.setKey("verifyError", null);
1102
- return true;
1103
- } catch (error) {
1104
- console.error("Phone verification error:", error);
1105
- store.setKey("verifyError", error.message);
1106
- return false;
1107
- }
1108
- },
1109
- formatPrice(priceOrPayment) {
1110
- const currencyCode = currency.get();
1111
- if ("total" in priceOrPayment) {
1112
- return formatPayment(priceOrPayment, { showSymbols: true, decimalPlaces: 2 });
1113
- }
1114
- return formatMinor(priceOrPayment.amount || 0, currencyCode);
1115
- },
1116
- getCartPayment() {
1117
- const items = cartItems.get();
1118
- const market = selectedMarket.get();
1119
- const currencyCode = currency.get();
1120
- const marketId = market?.id || "us";
1121
- if (!items || items.length === 0) {
1122
- return createPaymentForCheckout(0, marketId, currencyCode, "CASH" /* Cash */);
1123
- }
1124
- const subtotalMinor = items.reduce((sum, item) => {
1125
- let amountMinor = 0;
1126
- if ("amount" in item.price) {
1127
- amountMinor = item.price.amount || 0;
1128
- }
1129
- return sum + amountMinor * item.quantity;
1130
- }, 0);
1131
- return createPaymentForCheckout(subtotalMinor, marketId, currencyCode, "CASH" /* Cash */);
1132
- },
1133
- // Get available payment methods for selected market
1134
- getAvailablePaymentMethods() {
1135
- return paymentMethods.get() || ["CASH" /* Cash */];
1136
- },
1137
- // Get shipping methods for a country code
1138
- getShippingMethodsForCountry(countryCode) {
1139
- return getShippingMethodsForCountry(countryCode);
1140
- },
1141
- // Fetch quote from backend
1142
- async fetchQuote(promoCode) {
1143
- const items = cartItems.get();
1144
- const market = selectedMarket.get();
1145
- const currencyCode = currency.get();
1146
- const state = store.get();
1147
- const promo = promoCode !== void 0 ? promoCode : promoCodeAtom.get();
1148
- if (!items || items.length === 0) {
1149
- quoteAtom.set(null);
1150
- return;
1151
- }
1152
- if (!market) {
1153
- console.error("No market selected for quote");
1154
- return;
1155
- }
1156
- try {
1157
- store.setKey("fetchingQuote", true);
1158
- store.setKey("quoteError", null);
1159
- const token = await this.getGuestToken();
1160
- const shippingMethodId = state.selectedShippingMethodId || void 0;
1161
- const response = await eshopApi.getQuote({
1162
- token,
1163
- businessId: BUSINESS_ID,
1164
- items: items.map((item) => ({
1165
- productId: item.productId,
1166
- variantId: item.variantId,
1167
- quantity: item.quantity
1168
- })),
1169
- market: market.id,
1170
- currency: currencyCode,
1171
- userId: token,
1172
- paymentMethod: "CASH" /* Cash */,
1173
- shippingMethodId,
1174
- promoCode: promo || void 0
1175
- });
1176
- if (response.success && response.data) {
1177
- quoteAtom.set(response.data);
1178
- } else {
1179
- const friendly = mapQuoteError(response.code, response.error);
1180
- store.setKey("quoteError", friendly);
1181
- quoteAtom.set(null);
1182
- }
1183
- } catch (error) {
1184
- console.error("Quote fetch error:", error);
1185
- store.setKey("quoteError", error.message);
1186
- quoteAtom.set(null);
1187
- } finally {
1188
- store.setKey("fetchingQuote", false);
1189
- }
1190
- },
1191
- // Apply promo code
1192
- async applyPromoCode(code) {
1193
- promoCodeAtom.set(code);
1194
- await this.fetchQuote();
1195
- },
1196
- // Remove promo code
1197
- async removePromoCode() {
1198
- promoCodeAtom.set(null);
1199
- await this.fetchQuote();
1200
- }
1201
- };
1202
- function mapQuoteError(code, fallback) {
1203
- switch (code) {
1204
- case "PROMO.MIN_ORDER":
1205
- return fallback || "Promo requires a higher minimum order.";
1206
- case "PROMO.NOT_ACTIVE":
1207
- return "Promo code is not active.";
1208
- case "PROMO.NOT_YET_VALID":
1209
- return "Promo code is not yet valid.";
1210
- case "PROMO.EXPIRED":
1211
- return "Promo code has expired.";
1212
- case "PROMO.MAX_USES":
1213
- return "Promo code usage limit exceeded.";
1214
- case "PROMO.MAX_USES_PER_USER":
1215
- return "You have already used this promo code.";
1216
- case "PROMO.NOT_FOUND":
1217
- return "Promo code not found.";
1218
- default:
1219
- return fallback || "Failed to fetch quote.";
1220
- }
1221
- }
1222
- function initEshopStore() {
1223
- businessActions.init();
1224
- }
1225
-
1226
- // src/utils/i18n.ts
1227
- var defaultLocale = "en";
1228
- function getLocale() {
1229
- if (typeof window !== "undefined" && window.navigator) {
1230
- return window.navigator.language.split("-")[0] || defaultLocale;
1231
- }
1232
- return defaultLocale;
1233
- }
1234
- function getLocalizedString(value, locale) {
1235
- if (!value) return "";
1236
- if (typeof value === "string") return value;
1237
- if (typeof value === "object") {
1238
- const targetLocale = locale || getLocale();
1239
- return value[targetLocale] || value["en"] || value[Object.keys(value)[0]] || "";
1240
- }
1241
- return String(value);
1242
- }
1243
-
1244
- // src/utils/validation.ts
1245
- function validatePhoneNumber(phone) {
1246
- if (!phone) {
1247
- return { isValid: false, error: "Phone number is required" };
1248
- }
1249
- const cleaned = phone.replace(/\D/g, "");
1250
- if (cleaned.length < 8) {
1251
- return { isValid: false, error: "Phone number is too short" };
1252
- }
1253
- if (cleaned.length > 15) {
1254
- return { isValid: false, error: "Phone number is too long" };
1255
- }
1256
- return { isValid: true };
1257
- }
1258
-
1259
- // src/utils/timezone.ts
1260
- var tzGroups = [
1261
- {
1262
- label: "US",
1263
- zones: [
1264
- { label: "Eastern Time", value: "America/New_York" },
1265
- { label: "Central Time", value: "America/Chicago" },
1266
- { label: "Mountain Time", value: "America/Denver" },
1267
- { label: "Pacific Time", value: "America/Los_Angeles" }
1268
- ]
1269
- },
1270
- {
1271
- label: "Europe",
1272
- zones: [
1273
- { label: "London", value: "Europe/London" },
1274
- { label: "Paris", value: "Europe/Paris" },
1275
- { label: "Berlin", value: "Europe/Berlin" },
1276
- { label: "Rome", value: "Europe/Rome" }
1277
- ]
1278
- },
1279
- {
1280
- label: "Asia",
1281
- zones: [
1282
- { label: "Tokyo", value: "Asia/Tokyo" },
1283
- { label: "Shanghai", value: "Asia/Shanghai" },
1284
- { label: "Mumbai", value: "Asia/Kolkata" },
1285
- { label: "Dubai", value: "Asia/Dubai" }
1286
- ]
1287
- }
1288
- ];
1289
- function findTimeZone(groups) {
1290
- try {
1291
- const detected = Intl.DateTimeFormat().resolvedOptions().timeZone;
1292
- for (const group of groups) {
1293
- for (const zone of group.zones) {
1294
- if (zone.value === detected) {
1295
- return detected;
1296
- }
1297
- }
1298
- }
1299
- return "UTC";
1300
- } catch (e) {
1301
- return "UTC";
1302
- }
1303
- }
1304
-
1305
- // src/stores/reservation.ts
1306
- var cartParts = persistent.persistentAtom("reservationCart", [], {
1307
- encode: JSON.stringify,
1308
- decode: JSON.parse
1309
- });
1310
- var store2 = nanostores.deepMap({
1311
- currentStep: 1,
1312
- totalSteps: 4,
1313
- steps: {
1314
- 1: { name: "method", labelKey: "method" },
1315
- 2: { name: "provider", labelKey: "provider" },
1316
- 3: { name: "datetime", labelKey: "datetime" },
1317
- 4: { name: "review", labelKey: "review" }
1318
- },
1319
- // Calendar data
1320
- weekdays: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
1321
- monthYear: "",
1322
- days: [],
1323
- current: /* @__PURE__ */ new Date(),
1324
- // Selection state
1325
- selectedDate: null,
1326
- slots: [],
1327
- selectedSlot: null,
1328
- selectedMethod: null,
1329
- selectedProvider: null,
1330
- providers: [],
1331
- // Status flags
1332
- loading: false,
1333
- startDate: null,
1334
- endDate: null,
1335
- isMultiDay: false,
1336
- // Phone verification
1337
- phoneNumber: "",
1338
- phoneError: null,
1339
- phoneSuccess: null,
1340
- verificationCode: "",
1341
- verifyError: null,
1342
- isPhoneVerified: false,
1343
- isSendingCode: false,
1344
- isVerifying: false,
1345
- codeSentAt: null,
1346
- canResendAt: null,
1347
- // Quote state
1348
- fetchingQuote: false,
1349
- quote: null,
1350
- quoteError: null,
1351
- // Service & config
1352
- guestToken: null,
1353
- service: null,
1354
- apiUrl: API_URL,
1355
- businessId: BUSINESS_ID,
1356
- storageUrl: STORAGE_URL,
1357
- timezone: findTimeZone(tzGroups),
1358
- tzGroups,
1359
- parts: []
1360
- });
1361
- var currentStepName = nanostores.computed(store2, (state) => {
1362
- return state?.steps?.[state?.currentStep]?.name || "";
1363
- });
1364
- var canProceed = nanostores.computed(store2, (state) => {
1365
- const stepName = state?.steps?.[state?.currentStep]?.name;
1366
- switch (stepName) {
1367
- case "method":
1368
- return !!state.selectedMethod;
1369
- case "provider":
1370
- return !!state.selectedProvider;
1371
- case "datetime":
1372
- return state.isMultiDay ? !!(state.startDate && state.endDate && state.selectedSlot) : !!(state.selectedDate && state.selectedSlot);
1373
- case "review":
1374
- return true;
1375
- default:
1376
- return false;
1377
- }
1378
- });
1379
- var createCalendarGrid = (date) => {
1380
- const first = new Date(date.getFullYear(), date.getMonth(), 1);
1381
- const last = new Date(date.getFullYear(), date.getMonth() + 1, 0);
1382
- const cells = [];
1383
- const pad = (first.getDay() + 6) % 7;
1384
- for (let i = 0; i < pad; i++) cells.push({ key: `b-${i}`, blank: true });
1385
- for (let d = 1; d <= last.getDate(); d++) {
1386
- cells.push({
1387
- key: `d-${d}`,
1388
- blank: false,
1389
- date: new Date(date.getFullYear(), date.getMonth(), d),
1390
- available: false
1391
- });
1392
- }
1393
- const suffix = (7 - cells.length % 7) % 7;
1394
- for (let i = 0; i < suffix; i++) cells.push({ key: `b2-${i}`, blank: true });
1395
- return cells;
1396
- };
1397
- var formatTimeSlot = (from, to, timezone) => {
1398
- const opts = { hour: "2-digit", minute: "2-digit", timeZone: timezone };
1399
- return `${new Date(from * 1e3).toLocaleTimeString([], opts)} \u2013 ${new Date(to * 1e3).toLocaleTimeString([], opts)}`;
1400
- };
1401
- var actions2 = {
1402
- // Calendar management
1403
- updateCalendarGrid() {
1404
- const state = store2.get();
1405
- const cur = state.current || new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), 1);
1406
- const days = createCalendarGrid(cur);
1407
- store2.setKey("current", cur);
1408
- store2.setKey("monthYear", cur.toLocaleString(void 0, { month: "long", year: "numeric" }));
1409
- store2.setKey("days", days);
1410
- },
1411
- updateCalendar() {
1412
- this.updateCalendarGrid();
1413
- const state = store2.get();
1414
- if (state.service) this.fetchAvailability("month");
1415
- },
1416
- prevMonth() {
1417
- const { current } = store2.get();
1418
- store2.setKey("current", new Date(current.getFullYear(), current.getMonth() - 1, 1));
1419
- this.updateCalendar();
1420
- },
1421
- nextMonth() {
1422
- const { current } = store2.get();
1423
- store2.setKey("current", new Date(current.getFullYear(), current.getMonth() + 1, 1));
1424
- this.updateCalendar();
1425
- },
1426
- // Service initialization
1427
- setService(service) {
1428
- store2.setKey("service", service);
1429
- store2.setKey("selectedMethod", null);
1430
- store2.setKey("selectedProvider", null);
1431
- store2.setKey("providers", []);
1432
- store2.setKey("selectedDate", null);
1433
- store2.setKey("startDate", null);
1434
- store2.setKey("endDate", null);
1435
- store2.setKey("slots", []);
1436
- store2.setKey("selectedSlot", null);
1437
- store2.setKey("currentStep", 1);
1438
- store2.setKey("isMultiDay", !!service?.reservationConfigs?.isMultiDay);
1439
- const now = /* @__PURE__ */ new Date();
1440
- store2.setKey("current", new Date(now.getFullYear(), now.getMonth(), 1));
1441
- this.updateCalendarGrid();
1442
- if (service.reservationMethods?.length === 1) {
1443
- const method = service.reservationMethods[0];
1444
- store2.setKey("selectedMethod", method);
1445
- this.determineTotalSteps();
1446
- this.handleMethodSelection(method, false);
1447
- } else {
1448
- this.determineTotalSteps();
1449
- }
1450
- this.fetchAvailability("month");
1451
- },
1452
- // Step management
1453
- determineTotalSteps() {
1454
- const state = store2.get();
1455
- if (!state.service) {
1456
- store2.setKey("totalSteps", 1);
1457
- return 1;
1458
- }
1459
- const active = [];
1460
- if (state.service.reservationMethods?.length > 1) {
1461
- active.push({ name: "method", label: "Choose Reservation Type" });
1462
- }
1463
- if (state.selectedMethod?.includes("SPECIFIC")) {
1464
- active.push({ name: "provider", label: "Choose Provider" });
1465
- }
1466
- if (state.selectedMethod && state.selectedMethod !== "ORDER") {
1467
- active.push({
1468
- name: "datetime",
1469
- label: state.isMultiDay ? "Choose Date Range" : "Choose Date & Time"
1470
- });
1471
- }
1472
- active.push({ name: "review", label: "Review & Confirm" });
1473
- const stepObj = {};
1474
- active.forEach((st, idx) => {
1475
- stepObj[idx + 1] = st;
1476
- });
1477
- store2.setKey("steps", stepObj);
1478
- store2.setKey("totalSteps", active.length);
1479
- if (state.currentStep > active.length) {
1480
- store2.setKey("currentStep", active.length);
1481
- }
1482
- return active.length;
1483
- },
1484
- async getGuestToken() {
1485
- const state = store2.get();
1486
- const token = await getGuestToken(state.guestToken);
1487
- if (token !== state.guestToken) {
1488
- store2.setKey("guestToken", token);
1489
- }
1490
- return token;
1491
- },
1492
- getStepNumberByName(name) {
1493
- const { steps } = store2.get();
1494
- for (const [k, v] of Object.entries(steps)) {
1495
- if (v.name === name) return Number(k);
1496
- }
1497
- return null;
1498
- },
1499
- nextStep() {
1500
- const state = store2.get();
1501
- if (state.currentStep >= state.totalSteps || !canProceed.get()) return;
1502
- const next = state.currentStep + 1;
1503
- const name = state.steps[next]?.name;
1504
- store2.setKey("currentStep", next);
1505
- if (name === "datetime") {
1506
- this.fetchAvailability("month");
1507
- if (!state.selectedDate && !state.startDate) {
1508
- this.findFirstAvailable();
1509
- }
1510
- }
1511
- },
1512
- prevStep() {
1513
- const state = store2.get();
1514
- if (state.currentStep <= 1) return;
1515
- this.clearCurrentStepState();
1516
- store2.setKey("currentStep", state.currentStep - 1);
1517
- },
1518
- clearCurrentStepState() {
1519
- const name = currentStepName.get();
1520
- if (name === "method") {
1521
- store2.setKey("selectedMethod", null);
1522
- } else if (name === "provider") {
1523
- store2.setKey("selectedProvider", null);
1524
- store2.setKey("providers", []);
1525
- } else if (name === "datetime") {
1526
- store2.setKey("selectedDate", null);
1527
- store2.setKey("startDate", null);
1528
- store2.setKey("endDate", null);
1529
- store2.setKey("slots", []);
1530
- store2.setKey("selectedSlot", null);
1531
- }
1532
- },
1533
- goToStep(step) {
1534
- const state = store2.get();
1535
- if (step < 1 || step > state.totalSteps) return;
1536
- if (step < state.currentStep) {
1537
- for (let i = state.currentStep; i > step; i--) {
1538
- const n = state.steps[i]?.name;
1539
- if (n === "datetime") {
1540
- store2.setKey("selectedDate", null);
1541
- store2.setKey("startDate", null);
1542
- store2.setKey("endDate", null);
1543
- store2.setKey("slots", []);
1544
- store2.setKey("selectedSlot", null);
1545
- } else if (n === "provider") {
1546
- store2.setKey("selectedProvider", null);
1547
- store2.setKey("providers", []);
1548
- } else if (n === "method") {
1549
- store2.setKey("selectedMethod", null);
1550
- }
1551
- }
1552
- }
1553
- store2.setKey("currentStep", step);
1554
- if (state.steps[step]?.name === "datetime") {
1555
- this.fetchAvailability("month");
1556
- if (!state.selectedDate && !state.startDate) {
1557
- this.findFirstAvailable();
1558
- }
1559
- }
1560
- },
1561
- // Method selection
1562
- async handleMethodSelection(method, advance = true) {
1563
- store2.setKey("selectedDate", null);
1564
- store2.setKey("startDate", null);
1565
- store2.setKey("endDate", null);
1566
- store2.setKey("slots", []);
1567
- store2.setKey("selectedSlot", null);
1568
- store2.setKey("selectedMethod", method);
1569
- this.determineTotalSteps();
1570
- if (method === "ORDER") {
1571
- this.handleOrderMethod();
1572
- if (advance) {
1573
- const reviewStep = this.getStepNumberByName("review");
1574
- if (reviewStep) this.goToStep(reviewStep);
1575
- return;
1576
- }
1577
- } else if (method.includes("SPECIFIC")) {
1578
- await this.loadProviders();
1579
- const state = store2.get();
1580
- if (advance && state.providers.length === 1) {
1581
- this.selectProvider(state.providers[0]);
1582
- const datetimeStep = this.getStepNumberByName("datetime");
1583
- if (datetimeStep) this.goToStep(datetimeStep);
1584
- return;
1585
- }
1586
- } else if (method === "STANDARD" && advance) {
1587
- const datetimeStep = this.getStepNumberByName("datetime");
1588
- if (datetimeStep) this.goToStep(datetimeStep);
1589
- return;
1590
- }
1591
- if (advance && store2.get().currentStep < store2.get().totalSteps) {
1592
- this.nextStep();
1593
- }
1594
- },
1595
- handleOrderMethod() {
1596
- const state = store2.get();
1597
- const now = /* @__PURE__ */ new Date();
1598
- const dur = state.service.durations?.reduce((a, c) => a + c.duration, 0) || 3600;
1599
- const from = Math.floor(now.getTime() / 1e3);
1600
- const to = from + dur;
1601
- store2.setKey("selectedSlot", {
1602
- from,
1603
- to,
1604
- timeText: formatTimeSlot(from, to, state.timezone)
1605
- });
1606
- },
1607
- // Provider management
1608
- async loadProviders() {
1609
- store2.setKey("loading", true);
1610
- store2.setKey("providers", []);
1611
- try {
1612
- const { businessId, service } = store2.get();
1613
- const res = await reservationApi.getProviders({ businessId, serviceId: service.id });
1614
- store2.setKey("providers", res.success ? res.data : []);
1615
- } catch (e) {
1616
- console.error("Error loading providers:", e);
1617
- } finally {
1618
- store2.setKey("loading", false);
1619
- }
1620
- },
1621
- selectProvider(provider) {
1622
- store2.setKey("selectedProvider", provider);
1623
- store2.setKey("selectedDate", null);
1624
- store2.setKey("startDate", null);
1625
- store2.setKey("endDate", null);
1626
- store2.setKey("slots", []);
1627
- store2.setKey("selectedSlot", null);
1628
- if (currentStepName.get() === "datetime") {
1629
- this.fetchAvailability("month");
1630
- this.findFirstAvailable();
1631
- }
1632
- },
1633
- // Availability and date management
1634
- async fetchAvailability(type, date = null) {
1635
- const state = store2.get();
1636
- if (!state.service || currentStepName.get() !== "datetime") return;
1637
- store2.setKey("loading", true);
1638
- try {
1639
- let from, to, limit;
1640
- if (type === "month") {
1641
- from = Math.floor(
1642
- new Date(state.current.getFullYear(), state.current.getMonth(), 1).getTime() / 1e3
1643
- );
1644
- to = Math.floor(
1645
- new Date(state.current.getFullYear(), state.current.getMonth() + 1, 0).getTime() / 1e3
1646
- );
1647
- limit = 100;
1648
- } else if (type === "day" && date) {
1649
- const dObj = typeof date === "string" ? new Date(date) : date;
1650
- from = Math.floor(dObj.getTime() / 1e3);
1651
- to = from + 24 * 3600;
1652
- limit = 100;
1653
- } else if (type === "first") {
1654
- const now = /* @__PURE__ */ new Date();
1655
- from = Math.floor(now.setHours(0, 0, 0, 0) / 1e3);
1656
- to = Math.floor(new Date(now.getFullYear(), now.getMonth() + 3, 0).getTime() / 1e3);
1657
- limit = 1;
1658
- } else {
1659
- store2.setKey("loading", false);
1660
- return;
1661
- }
1662
- const params = { businessId: state.businessId, serviceId: state.service.id, from, to, limit };
1663
- if (state.selectedProvider) params.providerId = state.selectedProvider.id;
1664
- const result = await reservationApi.getAvailableSlots(params);
1665
- if (!result.success) {
1666
- console.error(`Error fetching availability (${type}):`, result.error);
1667
- return;
1668
- }
1669
- if (type === "month") {
1670
- const avail = new Set(
1671
- result.data.map((i) => {
1672
- const date2 = new Date(i.from * 1e3);
1673
- return date2.toISOString().slice(0, 10);
1674
- })
1675
- );
1676
- store2.setKey(
1677
- "days",
1678
- state.days.map((c) => {
1679
- if (!c.blank && c.date) {
1680
- const iso = c.date.toISOString().slice(0, 10);
1681
- return { ...c, available: avail.has(iso) };
1682
- }
1683
- return c;
1684
- })
1685
- );
1686
- } else if (type === "day") {
1687
- const slots = result.data.map((i, idx) => ({
1688
- ...i,
1689
- id: `slot-${i.from}-${idx}`,
1690
- day: new Date(i.from * 1e3).toISOString().slice(0, 10),
1691
- timeText: formatTimeSlot(i.from, i.to, state.timezone)
1692
- }));
1693
- store2.setKey("slots", slots);
1694
- if (slots.length && !state.selectedSlot) {
1695
- store2.setKey("selectedSlot", slots[0]);
1696
- }
1697
- } else if (type === "first" && result.data.length) {
1698
- const first = new Date(result.data[0].from * 1e3);
1699
- const iso = first.toISOString().slice(0, 10);
1700
- store2.setKey("current", new Date(first.getFullYear(), first.getMonth(), 1));
1701
- this.updateCalendarGrid();
1702
- await this.fetchAvailability("month");
1703
- if (state.isMultiDay) {
1704
- store2.setKey("startDate", iso);
1705
- store2.setKey("selectedDate", iso);
1706
- } else {
1707
- store2.setKey("selectedDate", iso);
1708
- await this.fetchAvailability("day", iso);
1709
- }
1710
- }
1711
- } catch (err) {
1712
- console.error(`Error in fetchAvailability (${type}):`, err);
1713
- } finally {
1714
- store2.setKey("loading", false);
1715
- }
1716
- },
1717
- findFirstAvailable() {
1718
- if (currentStepName.get() === "datetime") this.fetchAvailability("first");
1719
- },
1720
- // Date selection
1721
- selectDate(cell) {
1722
- if (!cell.date || !cell.available) return;
1723
- const dateInfo = {
1724
- year: cell.date.getFullYear(),
1725
- month: cell.date.getMonth() + 1,
1726
- day: cell.date.getDate(),
1727
- iso: `${cell.date.getFullYear()}-${String(cell.date.getMonth() + 1).padStart(2, "0")}-${String(cell.date.getDate()).padStart(2, "0")}`
1728
- };
1729
- const state = store2.get();
1730
- if (state.isMultiDay) {
1731
- if (!state.startDate) {
1732
- store2.setKey("startDate", dateInfo.iso);
1733
- store2.setKey("selectedSlot", null);
1734
- store2.setKey("selectedDate", dateInfo.iso);
1735
- store2.setKey("endDate", null);
1736
- } else if (!state.endDate) {
1737
- const start = new Date(state.startDate).getTime();
1738
- const cellT = cell.date.getTime();
1739
- if (cellT < start) {
1740
- store2.setKey("endDate", state.startDate);
1741
- store2.setKey("startDate", dateInfo.iso);
1742
- } else {
1743
- store2.setKey("endDate", dateInfo.iso);
1744
- }
1745
- } else {
1746
- store2.setKey("startDate", dateInfo.iso);
1747
- store2.setKey("selectedDate", dateInfo.iso);
1748
- store2.setKey("endDate", null);
1749
- store2.setKey("selectedSlot", null);
1750
- }
1751
- } else {
1752
- store2.setKey("selectedSlot", null);
1753
- store2.setKey("selectedDate", dateInfo.iso);
1754
- this.fetchAvailability("day", dateInfo.iso);
1755
- }
1756
- },
1757
- createMultiDaySlot() {
1758
- const state = store2.get();
1759
- if (!state.startDate || !state.endDate) return;
1760
- const startDT = new Date(state.startDate);
1761
- startDT.setHours(9, 0, 0, 0);
1762
- const endDT = new Date(state.endDate);
1763
- endDT.setHours(17, 0, 0, 0);
1764
- const from = Math.floor(startDT.getTime() / 1e3);
1765
- const to = Math.floor(endDT.getTime() / 1e3);
1766
- const rangeSlot = {
1767
- id: `multi-day-slot-${from}-${to}`,
1768
- from,
1769
- to,
1770
- isMultiDay: true,
1771
- timeText: `9:00 AM - 5:00 PM daily`,
1772
- dateRange: `${this.formatDateDisplay(state.startDate)} to ${this.formatDateDisplay(state.endDate)}`,
1773
- day: state.startDate
1774
- };
1775
- store2.setKey("slots", [rangeSlot]);
1776
- store2.setKey("selectedSlot", rangeSlot);
1777
- },
1778
- resetDateSelection() {
1779
- store2.setKey("startDate", null);
1780
- store2.setKey("endDate", null);
1781
- store2.setKey("selectedDate", null);
1782
- store2.setKey("slots", []);
1783
- store2.setKey("selectedSlot", null);
1784
- },
1785
- selectTimeSlot(slot) {
1786
- store2.setKey("selectedSlot", slot);
1787
- },
1788
- setSelectedTimeZone(zone) {
1789
- const state = store2.get();
1790
- if (zone === state.timezone) return;
1791
- store2.setKey("timezone", zone);
1792
- if (currentStepName.get() === "datetime") {
1793
- if (state.selectedDate) {
1794
- this.fetchAvailability("day", state.selectedDate);
1795
- } else if (!state.selectedDate && !state.startDate) {
1796
- this.findFirstAvailable();
1797
- }
1798
- }
1799
- },
1800
- // Calendar helpers
1801
- isAvailable(cell) {
1802
- return cell.date && cell.available;
1803
- },
1804
- isSelectedDay(cell) {
1805
- if (cell.blank || !cell.date) return false;
1806
- const iso = `${cell.date.getFullYear()}-${String(cell.date.getMonth() + 1).padStart(2, "0")}-${String(cell.date.getDate()).padStart(2, "0")}`;
1807
- const state = store2.get();
1808
- return iso === state.startDate || iso === state.endDate || iso === state.selectedDate;
1809
- },
1810
- isInSelectedRange(cell) {
1811
- const state = store2.get();
1812
- if (cell.blank || !cell.date || !state.startDate || !state.endDate) return false;
1813
- const t = cell.date.getTime();
1814
- const a = new Date(state.startDate).getTime();
1815
- const b = new Date(state.endDate).getTime();
1816
- return t >= a && t <= b;
1817
- },
1818
- formatDateDisplay(ds) {
1819
- if (!ds) return "";
1820
- const d = new Date(ds);
1821
- return d.toLocaleDateString(getLocale(), { month: "short", day: "numeric" });
1822
- },
1823
- // Cart operations
1824
- addToCart(slot) {
1825
- const state = store2.get();
1826
- const id = crypto.randomUUID();
1827
- let dateDisplay, timeText;
1828
- if (state.isMultiDay && slot.isMultiDay) {
1829
- const a = new Date(slot.from * 1e3), b = new Date(slot.to * 1e3);
1830
- dateDisplay = `${a.toLocaleDateString(getLocale(), { month: "short", day: "numeric" })} - ${b.toLocaleDateString(getLocale(), { month: "short", day: "numeric", year: "numeric" })}`;
1831
- timeText = slot.timeText;
1832
- } else {
1833
- const date = state.selectedDate ? new Date(state.selectedDate) : new Date(slot.from * 1e3);
1834
- dateDisplay = date.toLocaleDateString(getLocale(), {
1835
- weekday: "short",
1836
- year: "numeric",
1837
- month: "short",
1838
- day: "numeric"
1839
- });
1840
- timeText = slot.timeText;
1841
- }
1842
- const blocks = (state.service?.reservationBlocks || []).map((f) => ({
1843
- ...f,
1844
- value: Array.isArray(f.value) ? f.value : [f.value]
1845
- }));
1846
- const newPart = {
1847
- id,
1848
- serviceId: state.service.id,
1849
- serviceName: getLocalizedString(state.service.name, getLocale()),
1850
- date: dateDisplay,
1851
- from: slot.from,
1852
- to: slot.to,
1853
- timeText,
1854
- isMultiDay: state.isMultiDay && (!!state.endDate || slot.isMultiDay),
1855
- reservationMethod: state.selectedMethod || "",
1856
- providerId: state.selectedProvider?.id,
1857
- blocks
1858
- };
1859
- const newParts = [...state.parts, newPart];
1860
- store2.setKey("parts", newParts);
1861
- cartParts.set(newParts);
1862
- this.resetDateSelection();
1863
- store2.setKey("currentStep", 1);
1864
- if (state.service.reservationMethods?.length > 1) {
1865
- store2.setKey("selectedMethod", null);
1866
- }
1867
- },
1868
- removePart(id) {
1869
- const filteredParts = store2.get().parts.filter((p) => p.id !== id);
1870
- store2.setKey("parts", filteredParts);
1871
- cartParts.set(filteredParts);
1872
- },
1873
- // Phone validation helper (using shared utility)
1874
- validatePhoneNumber(phone) {
1875
- const result = validatePhoneNumber(phone);
1876
- return result.isValid;
1877
- },
1878
- // Phone verification
1879
- async updateProfilePhone() {
1880
- store2.setKey("phoneError", null);
1881
- store2.setKey("phoneSuccess", null);
1882
- store2.setKey("isSendingCode", true);
1883
- try {
1884
- const phoneNumber = store2.get().phoneNumber;
1885
- if (!this.validatePhoneNumber(phoneNumber)) {
1886
- store2.setKey("phoneError", "Please enter a valid phone number");
1887
- return false;
1888
- }
1889
- const token = await this.getGuestToken();
1890
- await updateProfilePhone(token, phoneNumber);
1891
- store2.setKey("phoneSuccess", "Verification code sent successfully!");
1892
- store2.setKey("codeSentAt", Date.now());
1893
- return true;
1894
- } catch (e) {
1895
- store2.setKey("phoneError", e.message);
1896
- return false;
1897
- } finally {
1898
- store2.setKey("isSendingCode", false);
1899
- }
1900
- },
1901
- async verifyPhoneCode() {
1902
- store2.setKey("verifyError", null);
1903
- store2.setKey("isVerifying", true);
1904
- try {
1905
- const { phoneNumber, verificationCode } = store2.get();
1906
- if (!verificationCode || verificationCode.length !== 4) {
1907
- store2.setKey("verifyError", "Please enter a 4-digit verification code");
1908
- return false;
1909
- }
1910
- const token = await this.getGuestToken();
1911
- await verifyPhoneCode(token, phoneNumber, verificationCode);
1912
- store2.setKey("isPhoneVerified", true);
1913
- store2.setKey("phoneSuccess", null);
1914
- store2.setKey("verificationCode", "");
1915
- return true;
1916
- } catch (e) {
1917
- let errorMessage = "Invalid verification code";
1918
- if (e.message?.includes("expired")) {
1919
- errorMessage = "Verification code has expired. Please request a new one.";
1920
- } else if (e.message?.includes("incorrect") || e.message?.includes("invalid")) {
1921
- errorMessage = "Incorrect verification code. Please try again.";
1922
- }
1923
- store2.setKey("verifyError", errorMessage);
1924
- return false;
1925
- } finally {
1926
- store2.setKey("isVerifying", false);
1927
- }
1928
- },
1929
- async checkout(paymentMethod = "CASH" /* Cash */, reservationBlocks3, promoCode) {
1930
- const state = store2.get();
1931
- if (state.loading || !state.parts.length) return { success: false, error: "No parts in cart" };
1932
- store2.setKey("loading", true);
1933
- try {
1934
- const token = await this.getGuestToken();
1935
- const result = await reservationApi.checkout({
1936
- token,
1937
- businessId: state.businessId,
1938
- blocks: reservationBlocks3 || [],
1939
- parts: state.parts,
1940
- paymentMethod,
1941
- market: "us",
1942
- promoCode
1943
- });
1944
- if (result.success) {
1945
- return {
1946
- success: true,
1947
- data: {
1948
- reservationId: result.data?.reservationId,
1949
- clientSecret: result.data?.clientSecret
1950
- }
1951
- };
1952
- } else {
1953
- throw new Error(result.error);
1954
- }
1955
- } catch (e) {
1956
- console.error("Reservation checkout error:", e);
1957
- return { success: false, error: e.message };
1958
- } finally {
1959
- store2.setKey("loading", false);
1960
- }
1961
- },
1962
- async fetchQuote(paymentMethod = "CASH" /* Cash */, promoCode) {
1963
- const state = store2.get();
1964
- console.log("fetchQuote called with promoCode:", promoCode);
1965
- if (!state.parts.length) {
1966
- store2.setKey("quote", null);
1967
- store2.setKey("quoteError", null);
1968
- return;
1969
- }
1970
- store2.setKey("fetchingQuote", true);
1971
- store2.setKey("quoteError", null);
1972
- try {
1973
- const token = await this.getGuestToken();
1974
- const marketObj = selectedMarket.get();
1975
- const market = marketObj?.id || "us";
1976
- const curr = currency.get() || "USD";
1977
- console.log("Calling reservationApi.getQuote with:", { market, currency: curr, promoCode });
1978
- const result = await reservationApi.getQuote({
1979
- token,
1980
- businessId: state.businessId,
1981
- market,
1982
- currency: curr,
1983
- userId: token,
1984
- // Use token as userId for guests
1985
- parts: state.parts,
1986
- paymentMethod,
1987
- promoCode
1988
- });
1989
- if (result.success && result.data) {
1990
- console.log("Quote received:", result.data);
1991
- store2.setKey("quote", result.data);
1992
- store2.setKey("quoteError", null);
1993
- } else {
1994
- console.error("Quote error:", result.error);
1995
- store2.setKey("quote", null);
1996
- store2.setKey("quoteError", mapQuoteError2(result.code, result.error));
1997
- }
1998
- } catch (e) {
1999
- console.error("Fetch quote error:", e);
2000
- store2.setKey("quote", null);
2001
- store2.setKey("quoteError", e.message || "Failed to get quote");
2002
- } finally {
2003
- store2.setKey("fetchingQuote", false);
2004
- }
2005
- },
2006
- // Helpers
2007
- getLabel(block, locale = getLocale()) {
2008
- if (!block) return "";
2009
- if (block.properties?.label) {
2010
- if (typeof block.properties.label === "object") {
2011
- return block.properties.label[locale] || block.properties.label.en || Object.values(block.properties.label)[0] || "";
2012
- }
2013
- if (typeof block.properties.label === "string") {
2014
- return block.properties.label;
2015
- }
2016
- }
2017
- return block.key || "";
2018
- },
2019
- getServicePrice() {
2020
- const state = store2.get();
2021
- if (state.service?.prices && Array.isArray(state.service.prices)) {
2022
- return getMarketPrice(state.service.prices, "us");
2023
- }
2024
- return "";
2025
- },
2026
- // NEW: Get reservation total as Payment structure
2027
- getReservationPayment() {
2028
- const state = store2.get();
2029
- const subtotalMinor = state.parts.reduce((sum, part) => {
2030
- const servicePrices = state.service?.prices || [];
2031
- const amountMinor = servicePrices.length > 0 ? getPriceAmount(servicePrices, "US") : 0;
2032
- return sum + amountMinor;
2033
- }, 0);
2034
- const currencyCode = currency.get();
2035
- return createPaymentForCheckout(
2036
- subtotalMinor,
2037
- "US",
2038
- currencyCode,
2039
- "CASH" /* Cash */
2040
- );
2041
- }
2042
- };
2043
- function initReservationStore() {
2044
- actions2.updateCalendarGrid();
2045
- businessActions.init();
2046
- const savedParts = cartParts.get();
2047
- if (savedParts && savedParts.length > 0) {
2048
- store2.setKey("parts", savedParts);
2049
- }
2050
- store2.listen((state) => {
2051
- if (state.isMultiDay && state.startDate && state.endDate && currentStepName.get() === "datetime" && (!state.slots.length || !state.slots[0].isMultiDay)) {
2052
- actions2.createMultiDaySlot();
2053
- }
2054
- if (JSON.stringify(state.parts) !== JSON.stringify(cartParts.get())) {
2055
- cartParts.set(state.parts);
2056
- }
2057
- });
2058
- cartParts.listen((parts) => {
2059
- const currentParts = store2.get().parts;
2060
- if (JSON.stringify(parts) !== JSON.stringify(currentParts)) {
2061
- store2.setKey("parts", [...parts]);
2062
- }
2063
- });
2064
- }
2065
- function mapQuoteError2(code, fallback) {
2066
- switch (code) {
2067
- case "PROMO.MIN_ORDER":
2068
- return fallback || "Promo requires a higher minimum order.";
2069
- case "PROMO.NOT_ACTIVE":
2070
- return "Promo code is not active.";
2071
- case "PROMO.NOT_YET_VALID":
2072
- return "Promo code is not yet valid.";
2073
- case "PROMO.EXPIRED":
2074
- return "Promo code has expired.";
2075
- case "PROMO.MAX_USES":
2076
- return "Promo code usage limit exceeded.";
2077
- case "PROMO.MAX_USES_PER_USER":
2078
- return "You have already used this promo code.";
2079
- case "PROMO.NOT_FOUND":
2080
- return "Promo code not found.";
2081
- default:
2082
- return fallback || "Failed to get quote.";
2083
- }
2084
- }
2085
- var totalCartItems = nanostores.computed([cartItems, cartParts], (eshop, reservation) => {
2086
- const eshopCount = eshop?.reduce((sum, item) => sum + item.quantity, 0) || 0;
2087
- const reservationCount = reservation?.length || 0;
2088
- return eshopCount + reservationCount;
2089
- });
2090
- var hasEshopItems = nanostores.computed(cartItems, (items) => items?.length > 0);
2091
- var hasReservationItems = nanostores.computed(cartParts, (items) => items?.length > 0);
2092
- var isCartEmpty = nanostores.computed([cartItems, cartParts], (eshop, reservation) => {
2093
- return (!eshop || eshop.length === 0) && (!reservation || reservation.length === 0);
2094
- });
2095
- var showEshopSection = nanostores.computed([hasEshopItems, isCartEmpty], (hasEshop, isEmpty) => hasEshop || isEmpty);
2096
- var showReservationSection = nanostores.computed([hasReservationItems, isCartEmpty], (hasReservation, isEmpty) => hasReservation || isEmpty);
2097
- var showBothSections = nanostores.computed([hasEshopItems, hasReservationItems], (hasEshop, hasReservation) => hasEshop && hasReservation);
2098
-
2099
- // src/stores/index.ts
2100
- function initArky(config) {
2101
- if (!config.apiUrl) {
2102
- throw new Error("apiUrl is required");
2103
- }
2104
- if (!config.businessId) {
2105
- throw new Error("businessId is required");
2106
- }
2107
- setGlobalConfig(config);
2108
- return config;
2109
- }
2110
-
2111
- exports.allowedPaymentMethods = allowedPaymentMethods;
2112
- exports.businessActions = businessActions;
2113
- exports.businessStore = businessStore;
2114
- exports.canProceed = canProceed;
2115
- exports.cartItemCount = cartItemCount;
2116
- exports.cartItems = cartItems;
2117
- exports.cartParts = cartParts;
2118
- exports.cartTotal = cartTotal;
2119
- exports.currency = currency;
2120
- exports.currencySymbol = currencySymbol;
2121
- exports.currentStepName = currentStepName;
2122
- exports.eshopActions = actions;
2123
- exports.eshopStore = store;
2124
- exports.getShippingMethodsForCountry = getShippingMethodsForCountry;
2125
- exports.getZoneByCountry = getZoneByCountry;
2126
- exports.hasEshopItems = hasEshopItems;
2127
- exports.hasReservationItems = hasReservationItems;
2128
- exports.initArky = initArky;
2129
- exports.initEshopStore = initEshopStore;
2130
- exports.initReservationStore = initReservationStore;
2131
- exports.isCartEmpty = isCartEmpty;
2132
- exports.markets = markets;
2133
- exports.orderBlocks = orderBlocks;
2134
- exports.paymentConfig = paymentConfig;
2135
- exports.paymentMethods = paymentMethods;
2136
- exports.promoCodeAtom = promoCodeAtom;
2137
- exports.quoteAtom = quoteAtom;
2138
- exports.reservationActions = actions2;
2139
- exports.reservationBlocks = reservationBlocks;
2140
- exports.reservationStore = store2;
2141
- exports.selectedMarket = selectedMarket;
2142
- exports.showBothSections = showBothSections;
2143
- exports.showEshopSection = showEshopSection;
2144
- exports.showReservationSection = showReservationSection;
2145
- exports.totalCartItems = totalCartItems;
2146
- exports.zones = zones;
2147
- //# sourceMappingURL=stores.cjs.map
2148
- //# sourceMappingURL=stores.cjs.map