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