@kasuvia/sdk 0.0.1

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.
Files changed (73) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +112 -0
  3. package/dist/index.d.mts +1891 -0
  4. package/dist/index.d.ts +1891 -0
  5. package/dist/index.js +3540 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/index.mjs +3314 -0
  8. package/dist/index.mjs.map +1 -0
  9. package/package.json +59 -0
  10. package/src/client/authClient.ts +135 -0
  11. package/src/client/graphqlClient.ts +53 -0
  12. package/src/client/httpClient.ts +298 -0
  13. package/src/config/index.ts +102 -0
  14. package/src/graphql/collections.graphql.ts +56 -0
  15. package/src/graphql/index.ts +7 -0
  16. package/src/graphql/products.graphql.ts +710 -0
  17. package/src/graphql/wishlist.graphql.ts +33 -0
  18. package/src/hooks/index.ts +26 -0
  19. package/src/hooks/use-auth.ts +182 -0
  20. package/src/hooks/use-bespoke-availability.ts +20 -0
  21. package/src/hooks/use-cart.ts +207 -0
  22. package/src/hooks/use-cms.ts +165 -0
  23. package/src/hooks/use-collections.ts +40 -0
  24. package/src/hooks/use-consultation-stream.ts +141 -0
  25. package/src/hooks/use-consultations.ts +76 -0
  26. package/src/hooks/use-countries.ts +24 -0
  27. package/src/hooks/use-currencies.ts +27 -0
  28. package/src/hooks/use-google-translate.ts +164 -0
  29. package/src/hooks/use-is-mounted.ts +9 -0
  30. package/src/hooks/use-languages.ts +33 -0
  31. package/src/hooks/use-order-stream.ts +195 -0
  32. package/src/hooks/use-orders.ts +64 -0
  33. package/src/hooks/use-payment.ts +68 -0
  34. package/src/hooks/use-products.ts +113 -0
  35. package/src/hooks/use-service-booking.ts +118 -0
  36. package/src/hooks/use-states.ts +28 -0
  37. package/src/hooks/use-wishlist.ts +121 -0
  38. package/src/index.ts +65 -0
  39. package/src/services/app.service.ts +17 -0
  40. package/src/services/auth.service.ts +332 -0
  41. package/src/services/bespoke.service.ts +17 -0
  42. package/src/services/cart.service.ts +245 -0
  43. package/src/services/cms.service.ts +112 -0
  44. package/src/services/collection.service.ts +77 -0
  45. package/src/services/config.service.ts +42 -0
  46. package/src/services/consultation.service.ts +51 -0
  47. package/src/services/index.ts +21 -0
  48. package/src/services/logistics.service.ts +95 -0
  49. package/src/services/notifications.service.ts +50 -0
  50. package/src/services/order.service.ts +169 -0
  51. package/src/services/payment.service.ts +127 -0
  52. package/src/services/product.service.ts +678 -0
  53. package/src/services/service-booking.service.ts +86 -0
  54. package/src/services/wishlist.service.ts +84 -0
  55. package/src/types/auth/index.ts +126 -0
  56. package/src/types/bespoke/index.ts +39 -0
  57. package/src/types/business/index.ts +6 -0
  58. package/src/types/cms/index.ts +77 -0
  59. package/src/types/consultations/index.ts +79 -0
  60. package/src/types/index.ts +27 -0
  61. package/src/types/media/index.ts +41 -0
  62. package/src/types/payments/index.ts +120 -0
  63. package/src/types/services/index.ts +80 -0
  64. package/src/types/store/index.ts +282 -0
  65. package/src/types/store/shipping.ts +38 -0
  66. package/src/types/ui/index.ts +32 -0
  67. package/src/utils/cache.ts +33 -0
  68. package/src/utils/checkout.ts +39 -0
  69. package/src/utils/customer.ts +20 -0
  70. package/src/utils/error-handler.ts +14 -0
  71. package/src/utils/index.ts +10 -0
  72. package/src/utils/logger.ts +79 -0
  73. package/src/utils/sanitize-url.ts +55 -0
package/dist/index.js ADDED
@@ -0,0 +1,3540 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ AuthCtx: () => AuthCtx,
34
+ AuthProvider: () => AuthProvider,
35
+ CartCtx: () => CartCtx,
36
+ CartProvider: () => CartProvider,
37
+ InvalidBaseURLError: () => InvalidBaseURLError,
38
+ acceptQuote: () => acceptQuote,
39
+ addToCart: () => addToCart,
40
+ addToGuestCart: () => addToGuestCart,
41
+ addToWishlist: () => addToWishlist,
42
+ approveDeliverable: () => approveDeliverable,
43
+ authAxios: () => authAxios,
44
+ bespokeService: () => bespokeService,
45
+ bookAppointment: () => bookAppointment,
46
+ buildDeliveryAddress: () => buildDeliveryAddress,
47
+ buildFullName: () => buildFullName,
48
+ cancelAppointment: () => cancelAppointment,
49
+ cancelOrder: () => cancelOrder,
50
+ checkAvailability: () => checkAvailability,
51
+ checkInWishlist: () => checkInWishlist,
52
+ clearCart: () => clearCart,
53
+ clearGuestCart: () => clearGuestCart,
54
+ clearGuestSessionId: () => clearGuestSessionId,
55
+ cmsAxios: () => cmsAxios,
56
+ collectionKeys: () => collectionKeys,
57
+ configService: () => configService,
58
+ configureHttpClients: () => configureHttpClients,
59
+ consultationService: () => consultationService,
60
+ createCmsEntry: () => createCmsEntry,
61
+ createConsultation: () => createConsultation,
62
+ createDedicatedAccount: () => createDedicatedAccount,
63
+ createOrder: () => createOrder,
64
+ deleteCmsEntry: () => deleteCmsEntry,
65
+ enrichCompletedOrder: () => enrichCompletedOrder,
66
+ findOrder: () => findOrder,
67
+ findOrderByToken: () => findOrderByToken,
68
+ getAppointment: () => getAppointment,
69
+ getAppointments: () => getAppointments,
70
+ getAuthUser: () => getAuthUser,
71
+ getAvailableSlots: () => getAvailableSlots,
72
+ getBackendUrl: () => getBackendUrl,
73
+ getBusinessConfig: () => getBusinessConfig,
74
+ getCacheTier: () => getCacheTier,
75
+ getCart: () => getCart,
76
+ getCheckoutCustomerName: () => getCheckoutCustomerName,
77
+ getCmsEntry: () => getCmsEntry,
78
+ getConsultation: () => getConsultation,
79
+ getConsultations: () => getConsultations,
80
+ getCountries: () => getCountries,
81
+ getCustomerDisplayName: () => getCustomerDisplayName,
82
+ getDeliverables: () => getDeliverables,
83
+ getErrorMessage: () => getErrorMessage,
84
+ getFeaturedProducts: () => getFeaturedProducts,
85
+ getGraphQLClient: () => getGraphQLClient,
86
+ getGuestCart: () => getGuestCart,
87
+ getGuestCartCount: () => getGuestCartCount,
88
+ getGuestCartProductType: () => getGuestCartProductType,
89
+ getGuestCartSubtotal: () => getGuestCartSubtotal,
90
+ getGuestSessionId: () => getGuestSessionId,
91
+ getLatestProducts: () => getLatestProducts,
92
+ getMe: () => getMe,
93
+ getNewProducts: () => getNewProducts,
94
+ getOrCreateGuestSessionId: () => getOrCreateGuestSessionId,
95
+ getOrders: () => getOrders,
96
+ getPopularProducts: () => getPopularProducts,
97
+ getRecommendedProducts: () => getRecommendedProducts,
98
+ getRuntimeConfig: () => getRuntimeConfig,
99
+ getSellerShippingAddress: () => getSellerShippingAddress,
100
+ getShippingRates: () => getShippingRates,
101
+ getStates: () => getStates,
102
+ getStorefrontCollectionBySlug: () => getStorefrontCollectionBySlug,
103
+ getStorefrontCollections: () => getStorefrontCollections,
104
+ getStorefrontProductBySlug: () => getStorefrontProductBySlug,
105
+ getStorefrontProducts: () => getStorefrontProducts,
106
+ getSupportedCurrencies: () => getSupportedCurrencies,
107
+ getSupportedCurrenciesList: () => getSupportedCurrenciesList,
108
+ getSupportedLanguages: () => getSupportedLanguages,
109
+ getSupportedLanguagesList: () => getSupportedLanguagesList,
110
+ getTransactionStatus: () => getTransactionStatus,
111
+ getWishlist: () => getWishlist,
112
+ googleTranslate: () => googleTranslate,
113
+ initSdk: () => initSdk,
114
+ initiatePayment: () => initiatePayment,
115
+ isInitialized: () => isInitialized,
116
+ listCmsEntries: () => listCmsEntries,
117
+ logger: () => logger,
118
+ loginCustomer: () => loginCustomer,
119
+ logisticsService: () => logisticsService,
120
+ logout: () => logout,
121
+ mapAuthUserToCustomer: () => mapAuthUserToCustomer,
122
+ mergeGuestCart: () => mergeGuestCart,
123
+ mergeGuestCartIfPresent: () => mergeGuestCartIfPresent,
124
+ notificationsService: () => notificationsService,
125
+ paymentAxios: () => paymentAxios,
126
+ paymentService: () => paymentService,
127
+ productKeys: () => productKeys,
128
+ publicStoreAxios: () => publicStoreAxios,
129
+ refreshToken: () => refreshToken,
130
+ registerCustomer: () => registerCustomer,
131
+ rejectQuote: () => rejectQuote,
132
+ removeFromCart: () => removeFromCart,
133
+ removeFromGuestCart: () => removeFromGuestCart,
134
+ removeFromWishlist: () => removeFromWishlist,
135
+ removeProductFromWishlist: () => removeProductFromWishlist,
136
+ requestQuote: () => requestQuote,
137
+ requestRevision: () => requestRevision,
138
+ resetConfig: () => resetConfig,
139
+ resetGraphQLClient: () => resetGraphQLClient,
140
+ sanitizeBaseUrl: () => sanitizeBaseUrl,
141
+ sendContactConfirmation: () => sendContactConfirmation,
142
+ sendContactEmail: () => sendContactEmail,
143
+ sendMessage: () => sendMessage,
144
+ serviceBookingService: () => serviceBookingService,
145
+ setEnvironmentMode: () => setEnvironmentMode,
146
+ setLoggerEnvironmentMode: () => setLoggerEnvironmentMode,
147
+ silentRefresh: () => silentRefresh,
148
+ splitFullName: () => splitFullName,
149
+ storeManagementAxios: () => storeManagementAxios,
150
+ storeTokens: () => storeTokens,
151
+ tokenStore: () => tokenStore,
152
+ trackOrder: () => trackOrder,
153
+ updateCartItem: () => updateCartItem,
154
+ updateCmsEntry: () => updateCmsEntry,
155
+ updateGuestCartItemQuantity: () => updateGuestCartItemQuantity,
156
+ updateProfile: () => updateProfile,
157
+ useAcceptQuote: () => useAcceptQuote,
158
+ useAddToWishlist: () => useAddToWishlist,
159
+ useAppointment: () => useAppointment,
160
+ useAppointments: () => useAppointments,
161
+ useApproveDeliverable: () => useApproveDeliverable,
162
+ useAuth: () => useAuth,
163
+ useAuthUser: () => useAuthUser,
164
+ useAvailableSlots: () => useAvailableSlots,
165
+ useBespokeAvailability: () => useBespokeAvailability,
166
+ useBookAppointment: () => useBookAppointment,
167
+ useCancelAppointment: () => useCancelAppointment,
168
+ useCancelOrder: () => useCancelOrder,
169
+ useCart: () => useCart,
170
+ useCheckInWishlist: () => useCheckInWishlist,
171
+ useCmsEntries: () => useCmsEntries,
172
+ useCmsEntry: () => useCmsEntry,
173
+ useConsultation: () => useConsultation,
174
+ useConsultationStream: () => useConsultationStream,
175
+ useConsultations: () => useConsultations,
176
+ useCountries: () => useCountries,
177
+ useCreateCmsEntry: () => useCreateCmsEntry,
178
+ useCreateConsultation: () => useCreateConsultation,
179
+ useCreateDVA: () => useCreateDVA,
180
+ useCreateOrder: () => useCreateOrder,
181
+ useCurrencies: () => useCurrencies,
182
+ useDeleteCmsEntry: () => useDeleteCmsEntry,
183
+ useDeliverables: () => useDeliverables,
184
+ useFeaturedProducts: () => useFeaturedProducts,
185
+ useFindOrder: () => useFindOrder,
186
+ useGoogleTranslate: () => useGoogleTranslate,
187
+ useInitiatePayment: () => useInitiatePayment,
188
+ useIsMounted: () => useIsMounted,
189
+ useLatestProducts: () => useLatestProducts,
190
+ useLogin: () => useLogin,
191
+ useLogout: () => useLogout,
192
+ useNewProducts: () => useNewProducts,
193
+ useOrder: () => useOrder,
194
+ useOrderStream: () => useOrderStream,
195
+ useOrderTracking: () => useOrderTracking,
196
+ useOrders: () => useOrders,
197
+ useOrdersList: () => useOrdersList,
198
+ usePollTransactionStatus: () => usePollTransactionStatus,
199
+ usePopularProducts: () => usePopularProducts,
200
+ useProduct: () => useProduct,
201
+ useRecommendedProducts: () => useRecommendedProducts,
202
+ useRegister: () => useRegister,
203
+ useRejectQuote: () => useRejectQuote,
204
+ useRemoveFromWishlist: () => useRemoveFromWishlist,
205
+ useRemoveProductFromWishlist: () => useRemoveProductFromWishlist,
206
+ useRequestRevision: () => useRequestRevision,
207
+ useSendMessage: () => useSendMessage,
208
+ useStates: () => useStates,
209
+ useStorefrontCollection: () => useStorefrontCollection,
210
+ useStorefrontCollections: () => useStorefrontCollections,
211
+ useStorefrontProducts: () => useStorefrontProducts,
212
+ useSupportedLanguages: () => useSupportedLanguages,
213
+ useTransactionStatus: () => useTransactionStatus,
214
+ useUpdateCmsEntry: () => useUpdateCmsEntry,
215
+ useUpdateProfile: () => useUpdateProfile,
216
+ useVerifyTransaction: () => useVerifyTransaction,
217
+ useWishlist: () => useWishlist,
218
+ useWishlistCount: () => useWishlistCount,
219
+ validateShippingAddress: () => validateShippingAddress,
220
+ verifySocialToken: () => verifySocialToken,
221
+ verifyTransaction: () => verifyTransaction,
222
+ wishlistKeys: () => wishlistKeys
223
+ });
224
+ module.exports = __toCommonJS(index_exports);
225
+
226
+ // src/utils/cache.ts
227
+ var CACHE_TIERS = {
228
+ none: { staleTime: 0, gcTime: 0 },
229
+ infinity: { staleTime: Infinity, gcTime: 0 },
230
+ low: { staleTime: 1e3 * 30, gcTime: 1e3 * 60 },
231
+ // 30s / 1m
232
+ medium: { staleTime: 1e3 * 60 * 5, gcTime: 1e3 * 60 * 10 },
233
+ // 5m / 10m
234
+ high: { staleTime: 1e3 * 60 * 30, gcTime: 1e3 * 60 * 60 }
235
+ // 30m / 1h
236
+ };
237
+ var isDevelopment = true;
238
+ function setEnvironmentMode(development) {
239
+ isDevelopment = development;
240
+ }
241
+ var getCacheTier = (tier) => {
242
+ if (isDevelopment) return CACHE_TIERS.none;
243
+ return CACHE_TIERS[tier];
244
+ };
245
+
246
+ // src/utils/logger.ts
247
+ var isDevelopment2 = true;
248
+ function setLoggerEnvironmentMode(development) {
249
+ isDevelopment2 = development;
250
+ }
251
+ var logger = {
252
+ log: (...args) => {
253
+ if (isDevelopment2) {
254
+ console.log(...args);
255
+ }
256
+ },
257
+ warn: (...args) => {
258
+ if (isDevelopment2) {
259
+ console.warn(...args);
260
+ }
261
+ },
262
+ /**
263
+ * Logs a warning to the console in development only.
264
+ * Alias for warn() for Python logging compatibility.
265
+ * In production, this is a no-op.
266
+ */
267
+ warning: (...args) => {
268
+ if (isDevelopment2) {
269
+ console.warn(...args);
270
+ }
271
+ },
272
+ error: (...args) => {
273
+ if (isDevelopment2) {
274
+ console.error(...args);
275
+ }
276
+ },
277
+ info: (...args) => {
278
+ if (isDevelopment2) {
279
+ console.info(...args);
280
+ }
281
+ },
282
+ createScope: (scope) => ({
283
+ log: (...args) => {
284
+ if (isDevelopment2) {
285
+ console.log(`[${scope}]`, ...args);
286
+ }
287
+ },
288
+ warn: (...args) => {
289
+ if (isDevelopment2) {
290
+ console.warn(`[${scope}]`, ...args);
291
+ }
292
+ },
293
+ warning: (...args) => {
294
+ if (isDevelopment2) {
295
+ console.warn(`[${scope}]`, ...args);
296
+ }
297
+ },
298
+ error: (...args) => {
299
+ if (isDevelopment2) {
300
+ console.error(`[${scope}]`, ...args);
301
+ }
302
+ },
303
+ info: (...args) => {
304
+ if (isDevelopment2) {
305
+ console.info(`[${scope}]`, ...args);
306
+ }
307
+ }
308
+ })
309
+ };
310
+
311
+ // src/utils/sanitize-url.ts
312
+ var InvalidBaseURLError = class extends Error {
313
+ constructor(message) {
314
+ super(message);
315
+ this.name = "InvalidBaseURLError";
316
+ }
317
+ };
318
+ var ALLOWED_SCHEMES = /* @__PURE__ */ new Set(["http:", "https:", "ws:", "wss:"]);
319
+ function sanitizeBaseUrl(url, name = "URL") {
320
+ if (url == null) {
321
+ throw new InvalidBaseURLError(`${name} is not set`);
322
+ }
323
+ const cleaned0 = url.trim();
324
+ if (!cleaned0) {
325
+ throw new InvalidBaseURLError(`${name} is empty`);
326
+ }
327
+ const cleaned = cleaned0.replace(/\/+$/, "");
328
+ let parsed;
329
+ try {
330
+ parsed = new URL(cleaned);
331
+ } catch {
332
+ throw new InvalidBaseURLError(
333
+ `${name} must start with http://, https://, ws://, or wss://, got: ${url}`
334
+ );
335
+ }
336
+ if (!ALLOWED_SCHEMES.has(parsed.protocol)) {
337
+ throw new InvalidBaseURLError(
338
+ `${name} must start with http://, https://, ws://, or wss://, got: ${url}`
339
+ );
340
+ }
341
+ if (!parsed.hostname) {
342
+ throw new InvalidBaseURLError(`${name} is missing a host: ${url}`);
343
+ }
344
+ if (parsed.search || parsed.hash) {
345
+ throw new InvalidBaseURLError(
346
+ `${name} should not contain a query or fragment: ${url}`
347
+ );
348
+ }
349
+ return cleaned;
350
+ }
351
+
352
+ // src/utils/customer.ts
353
+ function buildFullName(firstname, lastname) {
354
+ const name = [firstname, lastname].filter(Boolean).join(" ").trim();
355
+ return name || void 0;
356
+ }
357
+ function splitFullName(fullName) {
358
+ if (!fullName?.trim()) return { firstname: "", lastname: "" };
359
+ const parts = fullName.trim().split(/\s+/);
360
+ return {
361
+ firstname: parts[0] ?? "",
362
+ lastname: parts.slice(1).join(" ")
363
+ };
364
+ }
365
+ function getCustomerDisplayName(user) {
366
+ const fromParts = buildFullName(user.firstname, user.lastname);
367
+ return fromParts || user.full_name || user.email.split("@")[0];
368
+ }
369
+
370
+ // src/utils/checkout.ts
371
+ function buildDeliveryAddress(customer, location) {
372
+ return {
373
+ first_name: customer.first_name,
374
+ last_name: customer.last_name,
375
+ phone: customer.phone,
376
+ ...location
377
+ };
378
+ }
379
+ function getCheckoutCustomerName(customer) {
380
+ return buildFullName(customer.first_name, customer.last_name) ?? customer.email.split("@")[0];
381
+ }
382
+ function enrichCompletedOrder(order, options) {
383
+ const customerName = getCheckoutCustomerName(options.customer);
384
+ return {
385
+ ...order,
386
+ customerName: order.customerName || customerName,
387
+ customerEmail: order.customerEmail || options.customer.email,
388
+ shippingAddress: order.shippingAddress ?? (options.productType === "physical" && options.location ? buildDeliveryAddress(options.customer, options.location) : void 0),
389
+ bespokeBrief: order.bespokeBrief ?? options.bespokeBrief
390
+ };
391
+ }
392
+
393
+ // src/utils/error-handler.ts
394
+ var import_axios = __toESM(require("axios"));
395
+ function getErrorMessage(error, fallback = "Something went wrong") {
396
+ if (import_axios.default.isAxiosError(error)) {
397
+ return error.response?.data?.detail || error.response?.data?.message || error.message || fallback;
398
+ }
399
+ if (error instanceof Error) {
400
+ return error.message;
401
+ }
402
+ return fallback;
403
+ }
404
+
405
+ // src/config/index.ts
406
+ var runtimeConfig = null;
407
+ var BACKEND_URLS = {
408
+ auth: "https://127.0.0.1:8000/api/v1",
409
+ store: "https://127.0.0.1:8001/api/v1",
410
+ payment: "https://127.0.0.1:8002/api/v1",
411
+ notifications: "https://127.0.0.1:8007/api/v1",
412
+ cms: "https://127.0.0.1:8003/api/v1"
413
+ };
414
+ function initSdk(config) {
415
+ runtimeConfig = {
416
+ businessId: config.businessId,
417
+ storefrontApiKey: config.storefrontApiKey
418
+ };
419
+ const isDevelopment3 = typeof window === "undefined" && globalThis.process?.env?.NODE_ENV === "development";
420
+ setEnvironmentMode(isDevelopment3);
421
+ setLoggerEnvironmentMode(isDevelopment3);
422
+ logger.log("SDK initialized with businessId:", config.businessId);
423
+ }
424
+ function getRuntimeConfig() {
425
+ if (!runtimeConfig) {
426
+ throw new Error("SDK not initialized. Call initSdk() before using SDK functions.");
427
+ }
428
+ return runtimeConfig;
429
+ }
430
+ function getBackendUrl(service) {
431
+ return BACKEND_URLS[service];
432
+ }
433
+ function isInitialized() {
434
+ return runtimeConfig !== null;
435
+ }
436
+ function resetConfig() {
437
+ runtimeConfig = null;
438
+ }
439
+
440
+ // src/client/httpClient.ts
441
+ var import_axios3 = __toESM(require("axios"));
442
+
443
+ // src/client/authClient.ts
444
+ var import_axios2 = __toESM(require("axios"));
445
+ var _accessToken = null;
446
+ var _isRefreshing = false;
447
+ var _pendingQueue = [];
448
+ function getStorageKey() {
449
+ if (!isInitialized()) return "kasuvia-sf-anon-refresh";
450
+ const config = getRuntimeConfig();
451
+ return `kasuvia-sf-${config.businessId}-refresh`;
452
+ }
453
+ function drainQueue(error, newToken = null) {
454
+ _pendingQueue.forEach(({ resolve, reject }) => {
455
+ if (newToken) resolve(newToken);
456
+ else reject(error);
457
+ });
458
+ _pendingQueue = [];
459
+ }
460
+ var tokenStore = {
461
+ getAccessToken: () => _accessToken,
462
+ setAccessToken: (token) => {
463
+ _accessToken = token;
464
+ },
465
+ getRefreshToken: () => {
466
+ if (typeof window === "undefined") return null;
467
+ const key = getStorageKey();
468
+ return localStorage.getItem(key) || sessionStorage.getItem(key);
469
+ },
470
+ setRefreshToken: (token, remember = true) => {
471
+ if (typeof window === "undefined") return;
472
+ const key = getStorageKey();
473
+ localStorage.removeItem(key);
474
+ sessionStorage.removeItem(key);
475
+ if (token) {
476
+ const store = remember ? localStorage : sessionStorage;
477
+ store.setItem(key, token);
478
+ }
479
+ },
480
+ clearAll: () => {
481
+ _accessToken = null;
482
+ if (typeof window !== "undefined") {
483
+ const key = getStorageKey();
484
+ localStorage.removeItem(key);
485
+ sessionStorage.removeItem(key);
486
+ }
487
+ },
488
+ hasSession: () => {
489
+ if (typeof window === "undefined") return false;
490
+ const key = getStorageKey();
491
+ return !!(localStorage.getItem(key) || sessionStorage.getItem(key));
492
+ },
493
+ isPersistent: () => {
494
+ if (typeof window === "undefined") return true;
495
+ const key = getStorageKey();
496
+ return !!localStorage.getItem(key);
497
+ }
498
+ };
499
+ var refreshAxios = import_axios2.default.create({
500
+ baseURL: getBackendUrl("auth"),
501
+ headers: {
502
+ "Content-Type": "application/json",
503
+ Accept: "application/json"
504
+ },
505
+ withCredentials: true,
506
+ timeout: 3e4
507
+ });
508
+ async function refreshToken() {
509
+ if (_isRefreshing) {
510
+ return new Promise((resolve, reject) => {
511
+ _pendingQueue.push({ resolve, reject });
512
+ });
513
+ }
514
+ _isRefreshing = true;
515
+ const refresh = tokenStore.getRefreshToken();
516
+ if (!refresh) {
517
+ _isRefreshing = false;
518
+ tokenStore.clearAll();
519
+ throw new Error("[kasuvia SDK] No refresh token available");
520
+ }
521
+ try {
522
+ const { data } = await refreshAxios.post("/auth/token/refresh/", { refresh });
523
+ const newAccess = data.access;
524
+ tokenStore.setAccessToken(newAccess);
525
+ if (data.refresh) {
526
+ tokenStore.setRefreshToken(data.refresh, tokenStore.isPersistent());
527
+ }
528
+ drainQueue(null, newAccess);
529
+ return newAccess;
530
+ } catch (err) {
531
+ drainQueue(err);
532
+ tokenStore.clearAll();
533
+ throw err;
534
+ } finally {
535
+ _isRefreshing = false;
536
+ }
537
+ }
538
+
539
+ // src/client/httpClient.ts
540
+ var _guestSessionId = null;
541
+ function getOrCreateGuestSessionId() {
542
+ if (_guestSessionId) return _guestSessionId;
543
+ const storageKey = isInitialized() ? `kasuvia-guest-${getRuntimeConfig().businessId}` : "kasuvia-guest-anon";
544
+ if (typeof window !== "undefined") {
545
+ _guestSessionId = sessionStorage.getItem(storageKey) || crypto.randomUUID();
546
+ sessionStorage.setItem(storageKey, _guestSessionId);
547
+ } else {
548
+ _guestSessionId = crypto.randomUUID();
549
+ }
550
+ return _guestSessionId;
551
+ }
552
+ function getGuestSessionId() {
553
+ if (!_guestSessionId && typeof window !== "undefined") {
554
+ const storageKey = isInitialized() ? `kasuvia-guest-${getRuntimeConfig().businessId}` : "kasuvia-guest-anon";
555
+ _guestSessionId = sessionStorage.getItem(storageKey);
556
+ }
557
+ return _guestSessionId;
558
+ }
559
+ function clearGuestSessionId() {
560
+ _guestSessionId = null;
561
+ if (typeof window !== "undefined") {
562
+ const storageKey = isInitialized() ? `kasuvia-guest-${getRuntimeConfig().businessId}` : "kasuvia-guest-anon";
563
+ sessionStorage.removeItem(storageKey);
564
+ }
565
+ }
566
+ var storeManagementAxios = import_axios3.default.create({
567
+ baseURL: getBackendUrl("store"),
568
+ headers: {
569
+ "Content-Type": "application/json",
570
+ Accept: "application/json"
571
+ },
572
+ timeout: 3e4
573
+ });
574
+ storeManagementAxios.interceptors.request.use((cfg) => {
575
+ const config = getRuntimeConfig();
576
+ const token = tokenStore.getAccessToken();
577
+ if (token) {
578
+ cfg.headers = cfg.headers ?? {};
579
+ cfg.headers["Authorization"] = `Bearer ${token}`;
580
+ }
581
+ cfg.headers = cfg.headers ?? {};
582
+ cfg.headers["X-Storefront-Key"] = config.storefrontApiKey;
583
+ cfg.headers["X-Business-ID"] = config.businessId;
584
+ if (!token) {
585
+ const sid = getOrCreateGuestSessionId();
586
+ if (sid) cfg.headers["X-Guest-Session"] = sid;
587
+ }
588
+ return cfg;
589
+ });
590
+ storeManagementAxios.interceptors.response.use(
591
+ (res) => res,
592
+ async (err) => {
593
+ const originalRequest = err.config;
594
+ if (!originalRequest) return Promise.reject(err);
595
+ const status = err.response?.status;
596
+ const isUnauthorized = status === 401;
597
+ const isRefreshEndpoint = originalRequest.url?.includes("/auth/token/refresh/");
598
+ const alreadyRetried = originalRequest._retry === true;
599
+ if (isUnauthorized && isRefreshEndpoint) {
600
+ tokenStore.clearAll();
601
+ return Promise.reject(err);
602
+ }
603
+ if (!isUnauthorized || alreadyRetried) return Promise.reject(err);
604
+ try {
605
+ const newToken = await refreshToken();
606
+ originalRequest._retry = true;
607
+ if (originalRequest.headers) {
608
+ originalRequest.headers["Authorization"] = `Bearer ${newToken}`;
609
+ }
610
+ return storeManagementAxios(originalRequest);
611
+ } catch (refreshErr) {
612
+ tokenStore.clearAll();
613
+ return Promise.reject(refreshErr);
614
+ }
615
+ }
616
+ );
617
+ var publicStoreAxios = import_axios3.default.create({
618
+ baseURL: getBackendUrl("store"),
619
+ headers: {
620
+ "Content-Type": "application/json",
621
+ Accept: "application/json"
622
+ },
623
+ timeout: 3e4
624
+ });
625
+ publicStoreAxios.interceptors.request.use((cfg) => {
626
+ const config = getRuntimeConfig();
627
+ cfg.headers = cfg.headers ?? {};
628
+ cfg.headers["X-Storefront-Key"] = config.storefrontApiKey;
629
+ cfg.headers["X-Business-ID"] = config.businessId;
630
+ return cfg;
631
+ });
632
+ publicStoreAxios.interceptors.response.use(
633
+ (res) => res,
634
+ (err) => Promise.reject(err)
635
+ );
636
+ var authAxios = import_axios3.default.create({
637
+ baseURL: getBackendUrl("auth"),
638
+ headers: {
639
+ "Content-Type": "application/json",
640
+ Accept: "application/json"
641
+ },
642
+ withCredentials: true,
643
+ timeout: 3e4
644
+ });
645
+ authAxios.interceptors.request.use((cfg) => {
646
+ const config = getRuntimeConfig();
647
+ const token = tokenStore.getAccessToken();
648
+ if (token) {
649
+ cfg.headers = cfg.headers ?? {};
650
+ cfg.headers["Authorization"] = `Bearer ${token}`;
651
+ }
652
+ cfg.headers = cfg.headers ?? {};
653
+ cfg.headers["X-Storefront-Key"] = config.storefrontApiKey;
654
+ cfg.headers["X-Business-ID"] = config.businessId;
655
+ return cfg;
656
+ });
657
+ authAxios.interceptors.response.use(
658
+ (res) => res,
659
+ async (err) => {
660
+ const originalRequest = err.config;
661
+ if (!originalRequest) return Promise.reject(err);
662
+ const status = err.response?.status;
663
+ const isUnauthorized = status === 401;
664
+ const isRefreshEndpoint = originalRequest.url?.includes("/auth/token/refresh/");
665
+ const alreadyRetried = originalRequest._retry === true;
666
+ if (isUnauthorized && isRefreshEndpoint) {
667
+ tokenStore.clearAll();
668
+ return Promise.reject(err);
669
+ }
670
+ if (!isUnauthorized || alreadyRetried) return Promise.reject(err);
671
+ try {
672
+ const newToken = await refreshToken();
673
+ originalRequest._retry = true;
674
+ if (originalRequest.headers) {
675
+ originalRequest.headers["Authorization"] = `Bearer ${newToken}`;
676
+ }
677
+ return authAxios(originalRequest);
678
+ } catch (refreshErr) {
679
+ tokenStore.clearAll();
680
+ return Promise.reject(refreshErr);
681
+ }
682
+ }
683
+ );
684
+ var paymentAxios = import_axios3.default.create({
685
+ baseURL: getBackendUrl("payment"),
686
+ headers: {
687
+ "Content-Type": "application/json",
688
+ Accept: "application/json"
689
+ },
690
+ timeout: 3e4
691
+ });
692
+ paymentAxios.interceptors.request.use((cfg) => {
693
+ const config = getRuntimeConfig();
694
+ cfg.headers = cfg.headers ?? {};
695
+ cfg.headers["X-Storefront-Key"] = config.storefrontApiKey;
696
+ cfg.headers["X-Business-ID"] = config.businessId;
697
+ return cfg;
698
+ });
699
+ paymentAxios.interceptors.response.use(
700
+ (res) => res,
701
+ (err) => Promise.reject(err)
702
+ );
703
+ var notificationsAxios = import_axios3.default.create({
704
+ baseURL: getBackendUrl("notifications"),
705
+ headers: {
706
+ "Content-Type": "application/json",
707
+ Accept: "application/json"
708
+ },
709
+ timeout: 3e4
710
+ });
711
+ notificationsAxios.interceptors.response.use(
712
+ (res) => res,
713
+ (err) => Promise.reject(err)
714
+ );
715
+ var cmsAxios = import_axios3.default.create({
716
+ baseURL: getBackendUrl("cms"),
717
+ headers: {
718
+ "Content-Type": "application/json",
719
+ Accept: "application/json"
720
+ },
721
+ timeout: 3e4
722
+ });
723
+ cmsAxios.interceptors.request.use((cfg) => {
724
+ const config = getRuntimeConfig();
725
+ cfg.headers = cfg.headers ?? {};
726
+ cfg.headers["X-Storefront-Key"] = config.storefrontApiKey;
727
+ cfg.headers["X-Business-ID"] = config.businessId;
728
+ return cfg;
729
+ });
730
+ cmsAxios.interceptors.response.use(
731
+ (res) => res,
732
+ (err) => Promise.reject(err)
733
+ );
734
+ var refreshAxios2 = import_axios3.default.create({
735
+ baseURL: getBackendUrl("auth"),
736
+ headers: {
737
+ "Content-Type": "application/json",
738
+ Accept: "application/json"
739
+ },
740
+ withCredentials: true,
741
+ timeout: 3e4
742
+ });
743
+ function configureHttpClients() {
744
+ if (!isInitialized()) return;
745
+ const storeUrl = getBackendUrl("store");
746
+ const authUrl = getBackendUrl("auth");
747
+ const paymentUrl = getBackendUrl("payment");
748
+ const notificationsUrl = getBackendUrl("notifications");
749
+ const cmsUrl = getBackendUrl("cms");
750
+ storeManagementAxios.defaults.baseURL = storeUrl;
751
+ publicStoreAxios.defaults.baseURL = storeUrl;
752
+ authAxios.defaults.baseURL = authUrl;
753
+ paymentAxios.defaults.baseURL = paymentUrl;
754
+ notificationsAxios.defaults.baseURL = notificationsUrl;
755
+ cmsAxios.defaults.baseURL = cmsUrl;
756
+ refreshAxios2.defaults.baseURL = authUrl;
757
+ }
758
+
759
+ // src/client/graphqlClient.ts
760
+ var import_graphql_request = require("graphql-request");
761
+ var GRAPHQL_URL = `${getBackendUrl("store")}/graphql`;
762
+ function buildHeaders() {
763
+ const headers = {
764
+ "Content-Type": "application/json",
765
+ Accept: "application/json"
766
+ };
767
+ if (isInitialized()) {
768
+ const config = getRuntimeConfig();
769
+ headers["X-Storefront-Key"] = config.storefrontApiKey;
770
+ headers["X-Business-ID"] = config.businessId;
771
+ }
772
+ const token = tokenStore.getAccessToken();
773
+ if (token) headers["Authorization"] = `Bearer ${token}`;
774
+ return headers;
775
+ }
776
+ function getGraphQLClient() {
777
+ return new import_graphql_request.GraphQLClient(GRAPHQL_URL, { headers: buildHeaders() });
778
+ }
779
+ function resetGraphQLClient() {
780
+ }
781
+
782
+ // src/services/cart.service.ts
783
+ function parseItems(items) {
784
+ return items.map((item) => ({
785
+ ...item,
786
+ variant_attributes: item.variant_attributes && typeof item.variant_attributes === "string" ? JSON.parse(item.variant_attributes) : item.variant_attributes
787
+ }));
788
+ }
789
+ async function getGuestCart() {
790
+ const sid = getGuestSessionId();
791
+ if (!sid) return [];
792
+ try {
793
+ const { data } = await storeManagementAxios.get("cart/", {
794
+ headers: { "X-Guest-Session": sid }
795
+ });
796
+ return parseItems(data.items || []);
797
+ } catch {
798
+ return [];
799
+ }
800
+ }
801
+ async function addToGuestCart(item) {
802
+ const sid = getOrCreateGuestSessionId();
803
+ if (!sid) return [];
804
+ const payload = {
805
+ product_id: item.product_id,
806
+ product_slug: item.product_slug,
807
+ product_name: item.product_name,
808
+ image: item.image,
809
+ variant_id: item.variant_id,
810
+ variant_attributes: typeof item.variant_attributes === "string" ? item.variant_attributes : JSON.stringify(item.variant_attributes),
811
+ unit_price: item.unit_price,
812
+ quantity: item.quantity,
813
+ custom_measurements: item.custom_measurements,
814
+ is_bespoke: item.is_bespoke
815
+ };
816
+ const { data } = await storeManagementAxios.post("cart/", payload, {
817
+ headers: { "X-Guest-Session": sid }
818
+ });
819
+ return parseItems(data.items || []);
820
+ }
821
+ async function updateGuestCartItemQuantity(itemId, quantity) {
822
+ const sid = getGuestSessionId();
823
+ if (!sid) return [];
824
+ const { data } = await storeManagementAxios.patch(
825
+ `cart/${itemId}`,
826
+ { quantity },
827
+ { headers: { "X-Guest-Session": sid } }
828
+ );
829
+ return parseItems(data.items || []);
830
+ }
831
+ async function removeFromGuestCart(itemId) {
832
+ const sid = getGuestSessionId();
833
+ if (!sid) return [];
834
+ await storeManagementAxios.delete(`cart/${itemId}`, {
835
+ headers: { "X-Guest-Session": sid }
836
+ });
837
+ return getGuestCart();
838
+ }
839
+ async function clearGuestCart() {
840
+ const sid = getGuestSessionId();
841
+ if (!sid) return;
842
+ await storeManagementAxios.delete("cart/clear", {
843
+ headers: { "X-Guest-Session": sid }
844
+ });
845
+ }
846
+ async function getCart() {
847
+ const { data } = await storeManagementAxios.get("cart/");
848
+ const parsedItems = parseItems(data.items || []);
849
+ return { ...data, items: parsedItems };
850
+ }
851
+ async function addToCart(item) {
852
+ const { data } = await storeManagementAxios.post("cart/", item);
853
+ const parsedItems = parseItems(data.items || []);
854
+ return { ...data, items: parsedItems };
855
+ }
856
+ async function updateCartItem(itemId, quantity) {
857
+ const { data } = await storeManagementAxios.patch(
858
+ `cart/${itemId}`,
859
+ { quantity }
860
+ );
861
+ const parsedItems = parseItems(data.items || []);
862
+ return { ...data, items: parsedItems };
863
+ }
864
+ async function removeFromCart(itemId) {
865
+ await storeManagementAxios.delete(`cart/${itemId}`);
866
+ return getCart();
867
+ }
868
+ async function clearCart() {
869
+ await storeManagementAxios.delete("cart/clear");
870
+ }
871
+ async function mergeGuestCart() {
872
+ const sid = getGuestSessionId();
873
+ const guestItems = sid ? await getGuestCart() : [];
874
+ if (guestItems.length === 0) {
875
+ return getCart();
876
+ }
877
+ const payloadItems = guestItems.map((item) => ({
878
+ product_id: item.product_id,
879
+ product_slug: item.product_slug,
880
+ product_name: item.product_name,
881
+ image: item.image,
882
+ variant_id: item.variant_id,
883
+ variant_attributes: typeof item.variant_attributes === "string" ? item.variant_attributes : JSON.stringify(item.variant_attributes ?? {}),
884
+ unit_price: item.unit_price,
885
+ quantity: item.quantity,
886
+ custom_measurements: typeof item.custom_measurements === "string" ? item.custom_measurements : JSON.stringify(item.custom_measurements ?? {}),
887
+ is_bespoke: item.is_bespoke
888
+ }));
889
+ const { data } = await storeManagementAxios.post(
890
+ "cart/merge",
891
+ { items: payloadItems },
892
+ sid ? { headers: { "X-Guest-Session": sid } } : {}
893
+ );
894
+ clearGuestSessionId();
895
+ return { ...data, items: parseItems(data.items || []) };
896
+ }
897
+ async function mergeGuestCartIfPresent() {
898
+ try {
899
+ const guestItems = await getGuestCart();
900
+ if (guestItems.length === 0) return;
901
+ await mergeGuestCart();
902
+ } catch (error) {
903
+ console.warn("[Cart] Failed to merge guest cart:", error);
904
+ }
905
+ }
906
+ function getGuestCartCount() {
907
+ return 0;
908
+ }
909
+ function getGuestCartSubtotal() {
910
+ return 0;
911
+ }
912
+ function getGuestCartProductType() {
913
+ return "physical";
914
+ }
915
+
916
+ // src/services/order.service.ts
917
+ function deriveCustomerName(raw) {
918
+ if (raw.customer_name) return raw.customer_name;
919
+ if (raw.guest_name) return raw.guest_name;
920
+ const addr = typeof raw.shipping_address === "object" && raw.shipping_address !== null ? raw.shipping_address : null;
921
+ if (addr) {
922
+ const full = `${addr.first_name ?? ""} ${addr.last_name ?? ""}`.trim();
923
+ if (full) return full;
924
+ }
925
+ const email = raw.guest_email ?? raw.customer_email ?? "";
926
+ if (email) return email.split("@")[0];
927
+ return "";
928
+ }
929
+ function normaliseStorefrontOrder(raw) {
930
+ const items = (raw.items ?? []).map((item) => ({
931
+ id: item.id,
932
+ productName: item.product_name ?? item.productName ?? "",
933
+ image: item.image ?? "",
934
+ quantity: item.quantity ?? 1,
935
+ price: item.unit_price ?? item.price ?? 0
936
+ }));
937
+ const addr = typeof raw.shipping_address === "object" && raw.shipping_address !== null ? raw.shipping_address : null;
938
+ return {
939
+ id: raw.id,
940
+ display_id: raw.display_id,
941
+ items,
942
+ total: raw.total_amount ?? raw.total ?? 0,
943
+ status: raw.status,
944
+ customerEmail: raw.guest_email ?? raw.customer_email ?? "",
945
+ customerName: deriveCustomerName(raw),
946
+ createdAt: raw.created_at ? new Date(raw.created_at).getTime() : Date.now(),
947
+ updatedAt: raw.updated_at ? new Date(raw.updated_at).getTime() : Date.now(),
948
+ deliveryMethod: raw.delivery_method,
949
+ trackingNumber: raw.tracking_number,
950
+ deliveryAddress: typeof raw.shipping_address === "string" ? raw.shipping_address : addr ? `${addr.street}, ${addr.city}, ${addr.state}, ${addr.country}` : void 0,
951
+ shippingAddress: addr ? {
952
+ first_name: addr.first_name ?? "",
953
+ last_name: addr.last_name ?? "",
954
+ phone: addr.phone ?? "",
955
+ street: addr.street ?? "",
956
+ city: addr.city ?? "",
957
+ state: addr.state ?? "",
958
+ lga: addr.lga ?? "",
959
+ country: addr.country ?? "",
960
+ postal: addr.postal ?? ""
961
+ } : void 0,
962
+ cancellationReason: raw.cancellation_reason,
963
+ hasBespoke: raw.product_type === "bespoke",
964
+ bespokeStage: raw.bespoke_stage,
965
+ bespokeBrief: raw.bespoke_brief,
966
+ deliveryStatus: raw.delivery_status,
967
+ carrierName: raw.carrier_name,
968
+ terminalShipmentId: raw.terminal_shipment_id,
969
+ guestToken: raw.guest_token,
970
+ guestEmail: raw.guest_email,
971
+ guestPhone: raw.guest_phone,
972
+ guestName: raw.guest_name
973
+ };
974
+ }
975
+ async function createOrder(payload) {
976
+ const { data } = await storeManagementAxios.post("orders/storefront", payload);
977
+ return normaliseStorefrontOrder(data);
978
+ }
979
+ async function findOrder(params) {
980
+ const { data } = await publicStoreAxios.get("orders/find/", { params });
981
+ return normaliseStorefrontOrder(data);
982
+ }
983
+ async function findOrderByToken(token) {
984
+ const { data } = await publicStoreAxios.get("orders/find/", {
985
+ params: { guest_token: token }
986
+ });
987
+ return normaliseStorefrontOrder(data);
988
+ }
989
+ async function getOrders(params) {
990
+ const { data } = await storeManagementAxios.get("orders/", { params });
991
+ const items = Array.isArray(data) ? data : data.items ?? [];
992
+ return items.map(normaliseStorefrontOrder);
993
+ }
994
+ async function cancelOrder(orderId, reason) {
995
+ const { data } = await storeManagementAxios.post(`orders/${orderId}/cancel/`, { reason });
996
+ return normaliseStorefrontOrder(data);
997
+ }
998
+ async function trackOrder(orderId, token) {
999
+ const { data } = await publicStoreAxios.get(`orders/track/${orderId}`, {
1000
+ params: token ? { token } : void 0
1001
+ });
1002
+ return normaliseStorefrontOrder(data);
1003
+ }
1004
+
1005
+ // src/services/auth.service.ts
1006
+ async function loginCustomer(payload) {
1007
+ const config = getRuntimeConfig();
1008
+ const { data } = await authAxios.post("/auth/login/customer/", {
1009
+ ...payload,
1010
+ tenant_id: config.businessId
1011
+ });
1012
+ tokenStore.setAccessToken(data.access);
1013
+ tokenStore.setRefreshToken(data.refresh);
1014
+ resetGraphQLClient();
1015
+ await mergeGuestCartIfPresent();
1016
+ return data;
1017
+ }
1018
+ async function registerCustomer(payload) {
1019
+ const config = getRuntimeConfig();
1020
+ const { data } = await authAxios.post("/auth/register/customer/", {
1021
+ ...payload,
1022
+ tenant_id: config.businessId
1023
+ });
1024
+ tokenStore.setAccessToken(data.access);
1025
+ tokenStore.setRefreshToken(data.refresh);
1026
+ resetGraphQLClient();
1027
+ await mergeGuestCartIfPresent();
1028
+ return data;
1029
+ }
1030
+ async function refreshToken2(refresh) {
1031
+ const { data } = await refreshAxios2.post("/auth/token/refresh/", { refresh });
1032
+ return data;
1033
+ }
1034
+ async function logout() {
1035
+ const refresh = tokenStore.getRefreshToken();
1036
+ const payload = refresh ? { refresh } : {};
1037
+ try {
1038
+ await authAxios.post("/auth/logout/", payload);
1039
+ } catch {
1040
+ } finally {
1041
+ tokenStore.clearAll();
1042
+ resetGraphQLClient();
1043
+ }
1044
+ }
1045
+ async function verifySocialToken(provider, token) {
1046
+ const config = getRuntimeConfig();
1047
+ const { data } = await authAxios.post(
1048
+ `/auth/social/verify/${provider}/`,
1049
+ {
1050
+ id_token: token,
1051
+ storefront_key: config.storefrontApiKey
1052
+ }
1053
+ );
1054
+ tokenStore.setAccessToken(data.access);
1055
+ tokenStore.setRefreshToken(data.refresh);
1056
+ resetGraphQLClient();
1057
+ return data;
1058
+ }
1059
+ function parseJwtPayload(token) {
1060
+ try {
1061
+ return JSON.parse(atob(token.split(".")[1].replace(/-/g, "+").replace(/_/g, "/")));
1062
+ } catch {
1063
+ return {};
1064
+ }
1065
+ }
1066
+ function resolveProvider(data) {
1067
+ if (data.is_google_auth_enabled) return "google";
1068
+ if (data.is_facebook_auth_enabled) return "facebook";
1069
+ return "email";
1070
+ }
1071
+ function resolveAvatar(profilePicture) {
1072
+ if (!profilePicture) return null;
1073
+ if (typeof profilePicture === "string") return profilePicture;
1074
+ if (typeof profilePicture === "object" && profilePicture !== null && "url" in profilePicture) {
1075
+ const url = profilePicture.url;
1076
+ return typeof url === "string" ? url : null;
1077
+ }
1078
+ return null;
1079
+ }
1080
+ function mapAuthUserToCustomer(data, provider) {
1081
+ const firstname = data.profile?.firstname?.trim() || void 0;
1082
+ const lastname = data.profile?.lastname?.trim() || void 0;
1083
+ const phone = data.profile?.phone?.trim() || void 0;
1084
+ return {
1085
+ id: String(data.id),
1086
+ email: data.email,
1087
+ firstname,
1088
+ lastname,
1089
+ phone,
1090
+ full_name: buildFullName2(firstname, lastname),
1091
+ avatar: resolveAvatar(data.profile?.profile_picture),
1092
+ provider: provider ?? resolveProvider(data)
1093
+ };
1094
+ }
1095
+ function buildCustomerFromClaims(claims) {
1096
+ const provider = claims.is_google_auth_enabled ? "google" : claims.is_facebook_auth_enabled ? "facebook" : "email";
1097
+ const jwtFullName = String(claims.full_name ?? claims.name ?? "").trim() || void 0;
1098
+ const { firstname: splitFirst, lastname: splitLast } = splitFullName2(jwtFullName);
1099
+ return {
1100
+ id: String(claims.user_id ?? claims.sub ?? ""),
1101
+ email: String(claims.email ?? ""),
1102
+ firstname: splitFirst || void 0,
1103
+ lastname: splitLast || void 0,
1104
+ phone: String(claims.phone ?? "").trim() || void 0,
1105
+ full_name: jwtFullName,
1106
+ avatar: claims.avatar,
1107
+ provider
1108
+ };
1109
+ }
1110
+ async function getMe() {
1111
+ const { data } = await authAxios.get("/auth/profile/");
1112
+ return mapAuthUserToCustomer(data);
1113
+ }
1114
+ async function getAuthUser() {
1115
+ const { data } = await authAxios.get("/auth/profile/");
1116
+ return data;
1117
+ }
1118
+ async function updateProfile(payload) {
1119
+ const { data } = await authAxios.put("/auth/profile/", payload);
1120
+ return mapAuthUserToCustomer(data);
1121
+ }
1122
+ var refreshPromise = null;
1123
+ function classifyError(error) {
1124
+ const err = error;
1125
+ const isNetworkError = !!(!err.response && (err.code === "ECONNABORTED" || err.code === "ERR_NETWORK" || err.message?.includes("Network Error")));
1126
+ const isAuthError = !!(err.response?.status === 401 || err.response?.status === 403);
1127
+ const isBlacklistedError = !!(err.response?.data?.detail === "Token is blacklisted" || err.message === "Token is blacklisted");
1128
+ return { isNetworkError, isAuthError, isBlacklistedError };
1129
+ }
1130
+ async function retryWithBackoff(fn, maxRetries = 2, baseDelay = 1e3) {
1131
+ let lastError;
1132
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
1133
+ try {
1134
+ return await fn();
1135
+ } catch (error) {
1136
+ lastError = error;
1137
+ const { isNetworkError, isAuthError } = classifyError(error);
1138
+ if (!isNetworkError || isAuthError) {
1139
+ throw error;
1140
+ }
1141
+ if (attempt === maxRetries) {
1142
+ throw error;
1143
+ }
1144
+ const delay = baseDelay * Math.pow(2, attempt);
1145
+ await new Promise((resolve) => setTimeout(resolve, delay));
1146
+ }
1147
+ }
1148
+ throw lastError;
1149
+ }
1150
+ async function silentRefresh() {
1151
+ const refresh = tokenStore.getRefreshToken();
1152
+ if (!refresh) return null;
1153
+ if (refreshPromise) return refreshPromise;
1154
+ refreshPromise = (async () => {
1155
+ try {
1156
+ const tokens = await retryWithBackoff(() => refreshToken2(refresh), 2, 1e3);
1157
+ tokenStore.setAccessToken(tokens.access);
1158
+ if (tokens.refresh) tokenStore.setRefreshToken(tokens.refresh, tokenStore.isPersistent());
1159
+ const claims = parseJwtPayload(tokens.access);
1160
+ const user = buildCustomerFromClaims(claims);
1161
+ return { user, provider: user.provider };
1162
+ } catch (error) {
1163
+ const { isNetworkError, isAuthError, isBlacklistedError } = classifyError(error);
1164
+ if (isNetworkError) {
1165
+ logger.warn("[silentRefresh] Network error during token refresh (offline/CORS), clearing session:", error);
1166
+ } else if (isBlacklistedError) {
1167
+ logger.log("[silentRefresh] Token was blacklisted (likely from logout), clearing session");
1168
+ } else if (isAuthError) {
1169
+ logger.log("[silentRefresh] Token refresh failed with auth error (expired/invalid), clearing session");
1170
+ } else {
1171
+ logger.error("[silentRefresh] Unexpected error during token refresh, clearing session:", error);
1172
+ }
1173
+ tokenStore.clearAll();
1174
+ return null;
1175
+ } finally {
1176
+ refreshPromise = null;
1177
+ }
1178
+ })();
1179
+ return refreshPromise;
1180
+ }
1181
+ function storeTokens(access, refresh, remember = true) {
1182
+ tokenStore.setAccessToken(access);
1183
+ tokenStore.setRefreshToken(refresh, remember);
1184
+ }
1185
+ function buildFullName2(firstname, lastname) {
1186
+ if (firstname && lastname) return `${firstname} ${lastname}`;
1187
+ return firstname || lastname || "";
1188
+ }
1189
+ function splitFullName2(fullName) {
1190
+ if (!fullName) return { firstname: "", lastname: "" };
1191
+ const parts = fullName.trim().split(/\s+/);
1192
+ if (parts.length === 1) {
1193
+ return { firstname: parts[0], lastname: "" };
1194
+ }
1195
+ return {
1196
+ firstname: parts[0],
1197
+ lastname: parts.slice(1).join(" ")
1198
+ };
1199
+ }
1200
+
1201
+ // src/services/product.service.ts
1202
+ var GET_STOREFRONT_PRODUCTS = `
1203
+ query GetStorefrontProducts($page: Int!, $pageSize: Int!, $collectionSlug: String, $search: String) {
1204
+ storefront_products(page: $page, pageSize: $pageSize, collectionSlug: $collectionSlug, search: $search) {
1205
+ items {
1206
+ id
1207
+ business_id
1208
+ name
1209
+ slug
1210
+ description
1211
+ brand
1212
+ product_type
1213
+ requires_shipping
1214
+ is_consumable
1215
+ is_bespoke
1216
+ base_price
1217
+ currency
1218
+ images { url alt id }
1219
+ tags
1220
+ custom_fields { label unit required }
1221
+ is_published
1222
+ is_featured
1223
+ is_popular
1224
+ is_new
1225
+ is_temporary
1226
+ digital_file_key
1227
+ download_limit
1228
+ download_expires_at
1229
+ weight_kg
1230
+ length_cm
1231
+ width_cm
1232
+ height_cm
1233
+ created_at
1234
+ updated_at
1235
+ variants {
1236
+ id
1237
+ product_id
1238
+ sku
1239
+ attributes
1240
+ price
1241
+ images { url alt id }
1242
+ weight_kg
1243
+ is_active
1244
+ inventory {
1245
+ id
1246
+ product_id
1247
+ variant_id
1248
+ quantity
1249
+ reserved
1250
+ reorder_point
1251
+ reorder_quantity
1252
+ track_quantity
1253
+ allow_backorder
1254
+ available
1255
+ is_in_stock
1256
+ is_low_stock
1257
+ updated_at
1258
+ }
1259
+ created_at
1260
+ updated_at
1261
+ }
1262
+ collections {
1263
+ id
1264
+ business_id
1265
+ name
1266
+ slug
1267
+ description
1268
+ image { url alt id }
1269
+ collection_type
1270
+ rules
1271
+ status
1272
+ sort_order
1273
+ created_at
1274
+ updated_at
1275
+ }
1276
+ }
1277
+ total
1278
+ page
1279
+ page_size
1280
+ total_pages
1281
+ }
1282
+ }
1283
+ `;
1284
+ var GET_STOREFRONT_PRODUCT_BY_SLUG = `
1285
+ query GetStorefrontProductBySlug($slug: String!) {
1286
+ storefront_product_by_slug(slug: $slug) {
1287
+ id
1288
+ business_id
1289
+ name
1290
+ slug
1291
+ description
1292
+ brand
1293
+ product_type
1294
+ requires_shipping
1295
+ is_consumable
1296
+ is_bespoke
1297
+ base_price
1298
+ currency
1299
+ images { url alt id }
1300
+ tags
1301
+ custom_fields { label unit required }
1302
+ is_published
1303
+ is_featured
1304
+ is_popular
1305
+ is_new
1306
+ is_temporary
1307
+ digital_file_key
1308
+ download_limit
1309
+ download_expires_at
1310
+ weight_kg
1311
+ length_cm
1312
+ width_cm
1313
+ height_cm
1314
+ created_at
1315
+ updated_at
1316
+ variants {
1317
+ id
1318
+ product_id
1319
+ sku
1320
+ attributes
1321
+ price
1322
+ images { url alt id }
1323
+ weight_kg
1324
+ is_active
1325
+ inventory {
1326
+ id
1327
+ product_id
1328
+ variant_id
1329
+ quantity
1330
+ reserved
1331
+ reorder_point
1332
+ reorder_quantity
1333
+ track_quantity
1334
+ allow_backorder
1335
+ available
1336
+ is_in_stock
1337
+ is_low_stock
1338
+ updated_at
1339
+ }
1340
+ created_at
1341
+ updated_at
1342
+ }
1343
+ collections {
1344
+ id
1345
+ business_id
1346
+ name
1347
+ slug
1348
+ description
1349
+ image { url alt id }
1350
+ collection_type
1351
+ rules
1352
+ status
1353
+ sort_order
1354
+ created_at
1355
+ updated_at
1356
+ }
1357
+ }
1358
+ }
1359
+ `;
1360
+ var GET_STOREFRONT_LATEST = `
1361
+ query GetStorefrontLatest($page: Int!, $pageSize: Int!) {
1362
+ storefront_latest(page: $page, pageSize: $pageSize) {
1363
+ items {
1364
+ id
1365
+ business_id
1366
+ name
1367
+ slug
1368
+ description
1369
+ brand
1370
+ product_type
1371
+ requires_shipping
1372
+ is_consumable
1373
+ is_bespoke
1374
+ base_price
1375
+ currency
1376
+ images { url alt id }
1377
+ tags
1378
+ custom_fields { label unit required }
1379
+ is_published
1380
+ is_featured
1381
+ is_popular
1382
+ is_new
1383
+ is_temporary
1384
+ digital_file_key
1385
+ download_limit
1386
+ download_expires_at
1387
+ weight_kg
1388
+ length_cm
1389
+ width_cm
1390
+ height_cm
1391
+ created_at
1392
+ updated_at
1393
+ variants {
1394
+ id
1395
+ product_id
1396
+ sku
1397
+ attributes
1398
+ price
1399
+ images { url alt id }
1400
+ weight_kg
1401
+ is_active
1402
+ inventory {
1403
+ id
1404
+ product_id
1405
+ variant_id
1406
+ quantity
1407
+ reserved
1408
+ reorder_point
1409
+ reorder_quantity
1410
+ track_quantity
1411
+ allow_backorder
1412
+ available
1413
+ is_in_stock
1414
+ is_low_stock
1415
+ updated_at
1416
+ }
1417
+ created_at
1418
+ updated_at
1419
+ }
1420
+ collections {
1421
+ id
1422
+ business_id
1423
+ name
1424
+ slug
1425
+ description
1426
+ image { url alt id }
1427
+ collection_type
1428
+ rules
1429
+ status
1430
+ sort_order
1431
+ created_at
1432
+ updated_at
1433
+ }
1434
+ }
1435
+ total
1436
+ page
1437
+ page_size
1438
+ total_pages
1439
+ }
1440
+ }
1441
+ `;
1442
+ var GET_STOREFRONT_POPULAR = `
1443
+ query GetStorefrontPopular($page: Int!, $pageSize: Int!) {
1444
+ storefront_popular(page: $page, pageSize: $pageSize) {
1445
+ items {
1446
+ id
1447
+ business_id
1448
+ name
1449
+ slug
1450
+ description
1451
+ brand
1452
+ product_type
1453
+ requires_shipping
1454
+ is_consumable
1455
+ is_bespoke
1456
+ base_price
1457
+ currency
1458
+ images { url alt id }
1459
+ tags
1460
+ custom_fields { label unit required }
1461
+ is_published
1462
+ is_featured
1463
+ is_popular
1464
+ is_new
1465
+ is_temporary
1466
+ digital_file_key
1467
+ download_limit
1468
+ download_expires_at
1469
+ weight_kg
1470
+ length_cm
1471
+ width_cm
1472
+ height_cm
1473
+ created_at
1474
+ updated_at
1475
+ variants {
1476
+ id
1477
+ product_id
1478
+ sku
1479
+ attributes
1480
+ price
1481
+ images { url alt id }
1482
+ weight_kg
1483
+ is_active
1484
+ inventory {
1485
+ id
1486
+ product_id
1487
+ variant_id
1488
+ quantity
1489
+ reserved
1490
+ reorder_point
1491
+ reorder_quantity
1492
+ track_quantity
1493
+ allow_backorder
1494
+ available
1495
+ is_in_stock
1496
+ is_low_stock
1497
+ updated_at
1498
+ }
1499
+ created_at
1500
+ updated_at
1501
+ }
1502
+ collections {
1503
+ id
1504
+ business_id
1505
+ name
1506
+ slug
1507
+ description
1508
+ image { url alt id }
1509
+ collection_type
1510
+ rules
1511
+ status
1512
+ sort_order
1513
+ created_at
1514
+ updated_at
1515
+ }
1516
+ }
1517
+ total
1518
+ page
1519
+ page_size
1520
+ total_pages
1521
+ }
1522
+ }
1523
+ `;
1524
+ var GET_STOREFRONT_FEATURED = `
1525
+ query GetStorefrontFeatured($page: Int!, $pageSize: Int!) {
1526
+ storefront_featured(page: $page, pageSize: $pageSize) {
1527
+ items {
1528
+ id
1529
+ business_id
1530
+ name
1531
+ slug
1532
+ description
1533
+ brand
1534
+ product_type
1535
+ requires_shipping
1536
+ is_consumable
1537
+ is_bespoke
1538
+ base_price
1539
+ currency
1540
+ images { url alt id }
1541
+ tags
1542
+ custom_fields { label unit required }
1543
+ is_published
1544
+ is_featured
1545
+ is_popular
1546
+ is_new
1547
+ is_temporary
1548
+ digital_file_key
1549
+ download_limit
1550
+ download_expires_at
1551
+ weight_kg
1552
+ length_cm
1553
+ width_cm
1554
+ height_cm
1555
+ created_at
1556
+ updated_at
1557
+ variants {
1558
+ id
1559
+ product_id
1560
+ sku
1561
+ attributes
1562
+ price
1563
+ images { url alt id }
1564
+ weight_kg
1565
+ is_active
1566
+ inventory {
1567
+ id
1568
+ product_id
1569
+ variant_id
1570
+ quantity
1571
+ reserved
1572
+ reorder_point
1573
+ reorder_quantity
1574
+ track_quantity
1575
+ allow_backorder
1576
+ available
1577
+ is_in_stock
1578
+ is_low_stock
1579
+ updated_at
1580
+ }
1581
+ created_at
1582
+ updated_at
1583
+ }
1584
+ collections {
1585
+ id
1586
+ business_id
1587
+ name
1588
+ slug
1589
+ description
1590
+ image { url alt id }
1591
+ collection_type
1592
+ rules
1593
+ status
1594
+ sort_order
1595
+ created_at
1596
+ updated_at
1597
+ }
1598
+ }
1599
+ total
1600
+ page
1601
+ page_size
1602
+ total_pages
1603
+ }
1604
+ }
1605
+ `;
1606
+ var GET_STOREFRONT_NEW = `
1607
+ query GetStorefrontNew($page: Int!, $pageSize: Int!) {
1608
+ storefront_new(page: $page, pageSize: $pageSize) {
1609
+ items {
1610
+ id
1611
+ business_id
1612
+ name
1613
+ slug
1614
+ description
1615
+ brand
1616
+ product_type
1617
+ requires_shipping
1618
+ is_consumable
1619
+ is_bespoke
1620
+ base_price
1621
+ currency
1622
+ images { url alt id }
1623
+ tags
1624
+ custom_fields { label unit required }
1625
+ is_published
1626
+ is_featured
1627
+ is_popular
1628
+ is_new
1629
+ is_temporary
1630
+ digital_file_key
1631
+ download_limit
1632
+ download_expires_at
1633
+ weight_kg
1634
+ length_cm
1635
+ width_cm
1636
+ height_cm
1637
+ created_at
1638
+ updated_at
1639
+ variants {
1640
+ id
1641
+ product_id
1642
+ sku
1643
+ attributes
1644
+ price
1645
+ images { url alt id }
1646
+ weight_kg
1647
+ is_active
1648
+ inventory {
1649
+ id
1650
+ product_id
1651
+ variant_id
1652
+ quantity
1653
+ reserved
1654
+ reorder_point
1655
+ reorder_quantity
1656
+ track_quantity
1657
+ allow_backorder
1658
+ available
1659
+ is_in_stock
1660
+ is_low_stock
1661
+ updated_at
1662
+ }
1663
+ created_at
1664
+ updated_at
1665
+ }
1666
+ collections {
1667
+ id
1668
+ business_id
1669
+ name
1670
+ slug
1671
+ description
1672
+ image { url alt id }
1673
+ collection_type
1674
+ rules
1675
+ status
1676
+ sort_order
1677
+ created_at
1678
+ updated_at
1679
+ }
1680
+ }
1681
+ total
1682
+ page
1683
+ page_size
1684
+ total_pages
1685
+ }
1686
+ }
1687
+ `;
1688
+ var GET_STOREFRONT_RECOMMENDED = `
1689
+ query GetStorefrontRecommended($page: Int!, $pageSize: Int!, $excludeProductId: String) {
1690
+ storefront_recommended(page: $page, pageSize: $pageSize, excludeProductId: $excludeProductId) {
1691
+ items {
1692
+ id
1693
+ business_id
1694
+ name
1695
+ slug
1696
+ description
1697
+ brand
1698
+ product_type
1699
+ requires_shipping
1700
+ is_consumable
1701
+ is_bespoke
1702
+ base_price
1703
+ currency
1704
+ images { url alt id }
1705
+ tags
1706
+ custom_fields { label unit required }
1707
+ is_published
1708
+ is_featured
1709
+ is_popular
1710
+ is_new
1711
+ is_temporary
1712
+ digital_file_key
1713
+ download_limit
1714
+ download_expires_at
1715
+ weight_kg
1716
+ length_cm
1717
+ width_cm
1718
+ height_cm
1719
+ created_at
1720
+ updated_at
1721
+ variants {
1722
+ id
1723
+ product_id
1724
+ sku
1725
+ attributes
1726
+ price
1727
+ images { url alt id }
1728
+ weight_kg
1729
+ is_active
1730
+ inventory {
1731
+ id
1732
+ product_id
1733
+ variant_id
1734
+ quantity
1735
+ reserved
1736
+ reorder_point
1737
+ reorder_quantity
1738
+ track_quantity
1739
+ allow_backorder
1740
+ available
1741
+ is_in_stock
1742
+ is_low_stock
1743
+ updated_at
1744
+ }
1745
+ created_at
1746
+ updated_at
1747
+ }
1748
+ collections {
1749
+ id
1750
+ business_id
1751
+ name
1752
+ slug
1753
+ description
1754
+ image { url alt id }
1755
+ collection_type
1756
+ rules
1757
+ status
1758
+ sort_order
1759
+ created_at
1760
+ updated_at
1761
+ }
1762
+ }
1763
+ total
1764
+ page
1765
+ page_size
1766
+ total_pages
1767
+ }
1768
+ }
1769
+ `;
1770
+ async function getStorefrontProducts(params) {
1771
+ const client = getGraphQLClient();
1772
+ const variables = {
1773
+ page: params?.page || 1,
1774
+ pageSize: params?.pageSize || 20,
1775
+ collectionSlug: params?.collectionSlug,
1776
+ search: params?.search
1777
+ };
1778
+ const data = await client.request(GET_STOREFRONT_PRODUCTS, variables);
1779
+ return data.storefront_products;
1780
+ }
1781
+ async function getStorefrontProductBySlug(slug) {
1782
+ const client = getGraphQLClient();
1783
+ const data = await client.request(GET_STOREFRONT_PRODUCT_BY_SLUG, { slug });
1784
+ return data.storefront_product_by_slug;
1785
+ }
1786
+ async function getLatestProducts(params) {
1787
+ const client = getGraphQLClient();
1788
+ const variables = {
1789
+ page: params?.page || 1,
1790
+ pageSize: params?.pageSize || 4
1791
+ };
1792
+ const data = await client.request(GET_STOREFRONT_LATEST, variables);
1793
+ return data.storefront_latest;
1794
+ }
1795
+ async function getPopularProducts(params) {
1796
+ const client = getGraphQLClient();
1797
+ const variables = {
1798
+ page: params?.page || 1,
1799
+ pageSize: params?.pageSize || 4
1800
+ };
1801
+ const data = await client.request(GET_STOREFRONT_POPULAR, variables);
1802
+ return data.storefront_popular;
1803
+ }
1804
+ async function getFeaturedProducts(params) {
1805
+ const client = getGraphQLClient();
1806
+ const variables = {
1807
+ page: params?.page || 1,
1808
+ pageSize: params?.pageSize || 4
1809
+ };
1810
+ const data = await client.request(GET_STOREFRONT_FEATURED, variables);
1811
+ return data.storefront_featured;
1812
+ }
1813
+ async function getNewProducts(params) {
1814
+ const client = getGraphQLClient();
1815
+ const variables = {
1816
+ page: params?.page || 1,
1817
+ pageSize: params?.pageSize || 4
1818
+ };
1819
+ const data = await client.request(GET_STOREFRONT_NEW, variables);
1820
+ return data.storefront_new;
1821
+ }
1822
+ async function getRecommendedProducts(params) {
1823
+ const client = getGraphQLClient();
1824
+ const variables = {
1825
+ page: params?.page || 1,
1826
+ pageSize: params?.pageSize || 4,
1827
+ excludeProductId: params?.excludeProductId
1828
+ };
1829
+ const data = await client.request(GET_STOREFRONT_RECOMMENDED, variables);
1830
+ return data.storefront_recommended;
1831
+ }
1832
+
1833
+ // src/services/collection.service.ts
1834
+ var GET_STOREFRONT_COLLECTIONS = `
1835
+ query GetStorefrontCollections($page: Int!, $pageSize: Int!) {
1836
+ storefront_collections(page: $page, pageSize: $pageSize) {
1837
+ items {
1838
+ id
1839
+ business_id
1840
+ name
1841
+ slug
1842
+ description
1843
+ image { url alt id }
1844
+ collection_type
1845
+ rules
1846
+ status
1847
+ sort_order
1848
+ created_at
1849
+ updated_at
1850
+ }
1851
+ total
1852
+ page
1853
+ page_size
1854
+ total_pages
1855
+ }
1856
+ }
1857
+ `;
1858
+ var GET_STOREFRONT_COLLECTION_BY_SLUG = `
1859
+ query GetStorefrontCollectionBySlug($slug: String!) {
1860
+ storefront_collection_by_slug(slug: $slug) {
1861
+ id
1862
+ business_id
1863
+ name
1864
+ slug
1865
+ description
1866
+ image { url alt id }
1867
+ collection_type
1868
+ rules
1869
+ status
1870
+ sort_order
1871
+ created_at
1872
+ updated_at
1873
+ }
1874
+ }
1875
+ `;
1876
+ async function getStorefrontCollections(params) {
1877
+ const client = getGraphQLClient();
1878
+ const variables = {
1879
+ page: params?.page || 1,
1880
+ pageSize: params?.pageSize || 20
1881
+ };
1882
+ const data = await client.request(GET_STOREFRONT_COLLECTIONS, variables);
1883
+ return data.storefront_collections;
1884
+ }
1885
+ async function getStorefrontCollectionBySlug(slug) {
1886
+ const client = getGraphQLClient();
1887
+ const data = await client.request(GET_STOREFRONT_COLLECTION_BY_SLUG, { slug });
1888
+ return data.storefront_collection_by_slug;
1889
+ }
1890
+
1891
+ // src/services/wishlist.service.ts
1892
+ var GET_WISHLIST = `
1893
+ query GetWishlist($page: Int!, $pageSize: Int!) {
1894
+ wishlist(page: $page, pageSize: $pageSize) {
1895
+ items {
1896
+ id
1897
+ product_id
1898
+ notes
1899
+ created_at
1900
+ product_name
1901
+ product_slug
1902
+ product_price
1903
+ product_image
1904
+ }
1905
+ total
1906
+ page
1907
+ page_size
1908
+ total_pages
1909
+ }
1910
+ }
1911
+ `;
1912
+ var CHECK_IN_WISHLIST = `
1913
+ query CheckInWishlist($productId: String!) {
1914
+ check_in_wishlist(productId: $productId) {
1915
+ in_wishlist
1916
+ }
1917
+ }
1918
+ `;
1919
+ async function getWishlist(params) {
1920
+ const client = getGraphQLClient();
1921
+ const variables = {
1922
+ page: params?.page || 1,
1923
+ pageSize: params?.pageSize || 20
1924
+ };
1925
+ const data = await client.request(GET_WISHLIST, variables);
1926
+ return data.wishlist;
1927
+ }
1928
+ async function checkInWishlist(productId) {
1929
+ const client = getGraphQLClient();
1930
+ const data = await client.request(CHECK_IN_WISHLIST, { productId });
1931
+ return data.check_in_wishlist;
1932
+ }
1933
+ async function addToWishlist(productId, notes) {
1934
+ const { data } = await storeManagementAxios.post("wishlist/", {
1935
+ product_id: productId,
1936
+ notes: notes || void 0
1937
+ });
1938
+ return data;
1939
+ }
1940
+ async function removeFromWishlist(itemId) {
1941
+ await storeManagementAxios.delete(`wishlist/${itemId}`);
1942
+ }
1943
+ async function removeProductFromWishlist(productId) {
1944
+ await storeManagementAxios.delete(`wishlist/product/${productId}`);
1945
+ }
1946
+
1947
+ // src/services/payment.service.ts
1948
+ async function createDedicatedAccount(request) {
1949
+ const isValidUUID = (str) => {
1950
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1951
+ return uuidRegex.test(str);
1952
+ };
1953
+ const { data } = await paymentAxios.post(
1954
+ "payments/dedicated-account",
1955
+ {
1956
+ order_id: request.order_id && isValidUUID(request.order_id) ? request.order_id : void 0,
1957
+ amount: request.amount,
1958
+ customer_email: request.customer_email,
1959
+ customer_name: request.customer_name,
1960
+ currency: request.currency,
1961
+ idempotency_key: request.idempotency_key
1962
+ }
1963
+ );
1964
+ return data;
1965
+ }
1966
+ async function initiatePayment(request) {
1967
+ const isValidUUID = (str) => {
1968
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1969
+ return uuidRegex.test(str);
1970
+ };
1971
+ const validOrderId = request.order_id && isValidUUID(request.order_id) ? request.order_id : void 0;
1972
+ const { data } = await paymentAxios.post("payments/storefront/initiate", {
1973
+ amount: request.amount,
1974
+ currency: request.currency,
1975
+ order_id: validOrderId,
1976
+ quote_id: request.quote_id,
1977
+ metadata_json: {
1978
+ customer_email: request.customer_email,
1979
+ customer_name: request.customer_name ?? "",
1980
+ order_id: validOrderId,
1981
+ ...request.metadata ?? {}
1982
+ },
1983
+ idempotency_key: request.idempotency_key
1984
+ });
1985
+ return {
1986
+ id: data.id,
1987
+ gateway_ref: data.gateway_ref,
1988
+ gateway: data.gateway,
1989
+ status: data.status,
1990
+ checkout_url: data.metadata_json?.checkout_url ?? ""
1991
+ };
1992
+ }
1993
+ async function requestQuote(amount) {
1994
+ const { data } = await paymentAxios.post("payments/quote", { amount });
1995
+ return data;
1996
+ }
1997
+ async function verifyTransaction(reference) {
1998
+ const { data } = await paymentAxios.post(
1999
+ `payments/verify/${reference}`
2000
+ );
2001
+ return data;
2002
+ }
2003
+ async function getTransactionStatus(transactionId) {
2004
+ const { data } = await paymentAxios.get(
2005
+ `payments/transactions/${transactionId}`
2006
+ );
2007
+ return data;
2008
+ }
2009
+ var paymentService = {
2010
+ createDedicatedAccount,
2011
+ initiatePayment,
2012
+ requestQuote,
2013
+ verifyTransaction,
2014
+ getTransactionStatus
2015
+ };
2016
+
2017
+ // src/services/consultation.service.ts
2018
+ async function createConsultation(payload) {
2019
+ const { data } = await storeManagementAxios.post("social/consultations/", payload);
2020
+ return data;
2021
+ }
2022
+ async function getConsultations() {
2023
+ const { data } = await storeManagementAxios.get("social/consultations/");
2024
+ return data;
2025
+ }
2026
+ async function getConsultation(id) {
2027
+ const { data } = await storeManagementAxios.get(`social/consultations/${id}`);
2028
+ return data;
2029
+ }
2030
+ async function sendMessage(id, payload) {
2031
+ await storeManagementAxios.post(`social/consultations/${id}/messages`, payload);
2032
+ }
2033
+ async function acceptQuote(id, payload) {
2034
+ const { data } = await storeManagementAxios.post(`social/consultations/${id}/accept-quote`, payload);
2035
+ return data;
2036
+ }
2037
+ async function rejectQuote(id, reason) {
2038
+ const { data } = await storeManagementAxios.post(`social/consultations/${id}/reject-quote`, { reason });
2039
+ return data;
2040
+ }
2041
+ var consultationService = {
2042
+ createConsultation,
2043
+ getConsultations,
2044
+ getConsultation,
2045
+ sendMessage,
2046
+ acceptQuote,
2047
+ rejectQuote
2048
+ };
2049
+
2050
+ // src/services/logistics.service.ts
2051
+ async function getShippingRates(payload) {
2052
+ const { data } = await storeManagementAxios.post(
2053
+ "logistics/shipping/rates",
2054
+ payload
2055
+ );
2056
+ return data;
2057
+ }
2058
+ async function getSellerShippingAddress() {
2059
+ const { data } = await storeManagementAxios.get("logistics/shipping-address");
2060
+ return data;
2061
+ }
2062
+ async function validateShippingAddress(payload) {
2063
+ const { data } = await storeManagementAxios.post("logistics/shipping-address/validate", payload);
2064
+ return data;
2065
+ }
2066
+ var logisticsService = {
2067
+ getShippingRates,
2068
+ getSellerShippingAddress,
2069
+ validateShippingAddress
2070
+ };
2071
+
2072
+ // src/services/bespoke.service.ts
2073
+ async function checkAvailability(product_id) {
2074
+ const { data } = await storeManagementAxios.get(`bespoke/availability/${product_id}`);
2075
+ return data;
2076
+ }
2077
+ var bespokeService = {
2078
+ checkAvailability
2079
+ };
2080
+
2081
+ // src/services/service-booking.service.ts
2082
+ async function getAvailableSlots(product_id, date_from, date_to) {
2083
+ const { data } = await storeManagementAxios.get(
2084
+ `service/available-slots/${product_id}`,
2085
+ { params: { date_from, date_to } }
2086
+ );
2087
+ return data;
2088
+ }
2089
+ async function bookAppointment(payload) {
2090
+ const { data } = await storeManagementAxios.post("service/appointments/", payload);
2091
+ return data;
2092
+ }
2093
+ async function getAppointments() {
2094
+ const { data } = await storeManagementAxios.get("service/bookings/my");
2095
+ return data;
2096
+ }
2097
+ async function getAppointment(id) {
2098
+ const { data } = await storeManagementAxios.get(`service/bookings/${id}`);
2099
+ return data;
2100
+ }
2101
+ async function cancelAppointment(id) {
2102
+ await storeManagementAxios.delete(`service/bookings/${id}`);
2103
+ }
2104
+ async function getDeliverables(order_id) {
2105
+ const { data } = await storeManagementAxios.get(`service/projects/${order_id}/deliverables/`);
2106
+ return data;
2107
+ }
2108
+ async function approveDeliverable(order_id, deliverable_id) {
2109
+ const { data } = await storeManagementAxios.post(
2110
+ `service/projects/${order_id}/deliverables/${deliverable_id}/approve`,
2111
+ { approved: true }
2112
+ );
2113
+ return data;
2114
+ }
2115
+ async function requestRevision(order_id, deliverable_id, feedback) {
2116
+ const { data } = await storeManagementAxios.post(
2117
+ `service/projects/${order_id}/deliverables/${deliverable_id}/revision`,
2118
+ { approved: false, feedback }
2119
+ );
2120
+ return data;
2121
+ }
2122
+ var serviceBookingService = {
2123
+ getAvailableSlots,
2124
+ bookAppointment,
2125
+ getAppointments,
2126
+ getAppointment,
2127
+ cancelAppointment,
2128
+ getDeliverables,
2129
+ approveDeliverable,
2130
+ requestRevision
2131
+ };
2132
+
2133
+ // src/services/notifications.service.ts
2134
+ async function sendContactEmail(payload) {
2135
+ const { data } = await notificationsAxios.post(
2136
+ "contact/send-contact-email",
2137
+ payload
2138
+ );
2139
+ return data;
2140
+ }
2141
+ async function sendContactConfirmation(payload) {
2142
+ const { data } = await notificationsAxios.post(
2143
+ "contact/send-contact-confirmation",
2144
+ payload
2145
+ );
2146
+ return data;
2147
+ }
2148
+ var notificationsService = {
2149
+ sendContactEmail,
2150
+ sendContactConfirmation
2151
+ };
2152
+
2153
+ // src/services/app.service.ts
2154
+ async function googleTranslate(text, target, source = "en") {
2155
+ if (!text || target === source) return text;
2156
+ try {
2157
+ const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${source}&tl=${target}&dt=t&q=${encodeURIComponent(text)}`;
2158
+ const res = await fetch(url);
2159
+ const data = await res.json();
2160
+ return (data?.[0] || []).map((s) => s[0]).join("");
2161
+ } catch {
2162
+ return text;
2163
+ }
2164
+ }
2165
+
2166
+ // src/services/config.service.ts
2167
+ async function getSupportedLanguages() {
2168
+ const { data } = await paymentAxios.get("/config/supported-languages");
2169
+ return data;
2170
+ }
2171
+ async function getSupportedCurrencies() {
2172
+ const { data } = await paymentAxios.get("/config/supported-currencies");
2173
+ return data;
2174
+ }
2175
+ async function getBusinessConfig() {
2176
+ const { data } = await publicStoreAxios.get("business/profile");
2177
+ return data;
2178
+ }
2179
+ var configService = {
2180
+ getSupportedLanguages,
2181
+ getSupportedCurrencies,
2182
+ getBusinessConfig
2183
+ };
2184
+
2185
+ // src/services/cms.service.ts
2186
+ async function listCmsEntries(modelSlug, params) {
2187
+ const config = getRuntimeConfig();
2188
+ const { businessId } = config;
2189
+ const response = await cmsAxios.get(
2190
+ `/sdk/${businessId}/${modelSlug}/entries`,
2191
+ {
2192
+ params: {
2193
+ page: params?.page || 1,
2194
+ page_size: params?.page_size || 50
2195
+ }
2196
+ }
2197
+ );
2198
+ return response.data;
2199
+ }
2200
+ async function getCmsEntry(modelSlug, entryId) {
2201
+ const config = getRuntimeConfig();
2202
+ const { businessId } = config;
2203
+ const response = await cmsAxios.get(
2204
+ `/sdk/${businessId}/${modelSlug}/entries/${entryId}`
2205
+ );
2206
+ return response.data;
2207
+ }
2208
+ async function createCmsEntry(modelSlug, params) {
2209
+ const config = getRuntimeConfig();
2210
+ const { businessId } = config;
2211
+ const response = await cmsAxios.post(
2212
+ `/sdk/${businessId}/${modelSlug}/entries`,
2213
+ params
2214
+ );
2215
+ return response.data;
2216
+ }
2217
+ async function updateCmsEntry(modelSlug, entryId, params) {
2218
+ const config = getRuntimeConfig();
2219
+ const { businessId } = config;
2220
+ const response = await cmsAxios.put(
2221
+ `/sdk/${businessId}/${modelSlug}/entries/${entryId}`,
2222
+ params
2223
+ );
2224
+ return response.data;
2225
+ }
2226
+ async function deleteCmsEntry(modelSlug, entryId) {
2227
+ const config = getRuntimeConfig();
2228
+ const { businessId } = config;
2229
+ await cmsAxios.delete(
2230
+ `/sdk/${businessId}/${modelSlug}/entries/${entryId}`
2231
+ );
2232
+ }
2233
+
2234
+ // src/hooks/use-cart.ts
2235
+ var import_react = __toESM(require("react"));
2236
+ var import_react_query = require("@tanstack/react-query");
2237
+ var import_axios4 = __toESM(require("axios"));
2238
+ var CartCtx = import_react.default.createContext ? import_react.default.createContext(null) : null;
2239
+ function CartProvider({ children }) {
2240
+ const [isOpen, setIsOpen] = (0, import_react.useState)(false);
2241
+ if (!CartCtx) return children;
2242
+ return import_react.default.createElement(CartCtx.Provider, { value: { isOpen, setOpen: setIsOpen } }, children);
2243
+ }
2244
+ function useCart() {
2245
+ const queryClient = (0, import_react_query.useQueryClient)();
2246
+ const context = CartCtx ? (0, import_react.useContext)(CartCtx) : null;
2247
+ const isOpen = context ? context.isOpen : false;
2248
+ const setOpen = context ? context.setOpen : () => {
2249
+ };
2250
+ const [productTypeMismatch, setProductTypeMismatch] = (0, import_react.useState)(null);
2251
+ const cartQuery = (0, import_react_query.useQuery)({
2252
+ queryKey: ["cart"],
2253
+ queryFn: getCart,
2254
+ enabled: isInitialized()
2255
+ });
2256
+ const addToCartMutation = (0, import_react_query.useMutation)({
2257
+ mutationFn: addToCart,
2258
+ onSuccess: () => {
2259
+ setProductTypeMismatch(null);
2260
+ queryClient.invalidateQueries({ queryKey: ["cart"] });
2261
+ },
2262
+ onError: (error) => {
2263
+ if (import_axios4.default.isAxiosError(error) && error.response?.status === 409) {
2264
+ const responseData = error.response?.data;
2265
+ const errorDetail = responseData?.detail;
2266
+ if (errorDetail?.code === "CART_PRODUCT_TYPE_MISMATCH") {
2267
+ setProductTypeMismatch({
2268
+ message: errorDetail.message ?? "Your cart contains incompatible items.",
2269
+ existing_type: errorDetail.existing_type ?? "physical",
2270
+ new_type: errorDetail.new_type ?? "physical"
2271
+ });
2272
+ }
2273
+ }
2274
+ }
2275
+ });
2276
+ const updateCartItemMutation = (0, import_react_query.useMutation)({
2277
+ mutationFn: ({ itemId, quantity }) => updateCartItem(itemId, quantity),
2278
+ onSuccess: () => {
2279
+ queryClient.invalidateQueries({ queryKey: ["cart"] });
2280
+ }
2281
+ });
2282
+ const removeFromCartMutation = (0, import_react_query.useMutation)({
2283
+ mutationFn: removeFromCart,
2284
+ onSuccess: () => {
2285
+ queryClient.invalidateQueries({ queryKey: ["cart"] });
2286
+ }
2287
+ });
2288
+ const clearCartMutation = (0, import_react_query.useMutation)({
2289
+ mutationFn: clearCart,
2290
+ onSuccess: () => {
2291
+ queryClient.invalidateQueries({ queryKey: ["cart"] });
2292
+ }
2293
+ });
2294
+ const mergeGuestCartMutation = (0, import_react_query.useMutation)({
2295
+ mutationFn: mergeGuestCartIfPresent,
2296
+ onSuccess: () => {
2297
+ queryClient.invalidateQueries({ queryKey: ["cart"] });
2298
+ }
2299
+ });
2300
+ const addItem = (product, variantId, quantity = 1, customMeasurements) => {
2301
+ const variant = variantId ? product.variants.find((v) => v.id === variantId) : void 0;
2302
+ return addToCartMutation.mutate({
2303
+ product_id: product.id,
2304
+ product_slug: product.slug,
2305
+ product_name: product.name,
2306
+ image: product.images[0]?.url,
2307
+ variant_id: variantId,
2308
+ variant_attributes: variant?.attributes ? JSON.stringify(variant.attributes) : void 0,
2309
+ unit_price: variant?.price ?? product.base_price,
2310
+ quantity,
2311
+ custom_measurements: customMeasurements ? JSON.stringify(customMeasurements) : void 0,
2312
+ is_bespoke: product.is_bespoke
2313
+ });
2314
+ };
2315
+ const items = cartQuery.data?.items || [];
2316
+ const subtotal = cartQuery.data?.subtotal || 0;
2317
+ const count = items.reduce((total, item) => total + item.quantity, 0);
2318
+ const updateQty = (itemId, quantity) => {
2319
+ updateCartItemMutation.mutate({ itemId, quantity });
2320
+ };
2321
+ const removeItem = (itemId) => {
2322
+ removeFromCartMutation.mutate(itemId);
2323
+ };
2324
+ const handleClearCartAndAdd = () => {
2325
+ clearCartMutation.mutate();
2326
+ setProductTypeMismatch(null);
2327
+ };
2328
+ const handleKeepCurrentCart = () => {
2329
+ setProductTypeMismatch(null);
2330
+ };
2331
+ return {
2332
+ cart: cartQuery.data,
2333
+ items,
2334
+ count,
2335
+ isOpen,
2336
+ setOpen,
2337
+ isLoading: cartQuery.isLoading,
2338
+ error: cartQuery.error,
2339
+ addToCart: addToCartMutation.mutate,
2340
+ updateCartItem: updateCartItemMutation.mutate,
2341
+ removeFromCart: removeFromCartMutation.mutate,
2342
+ clearCart: clearCartMutation.mutate,
2343
+ clear: clearCartMutation.mutate,
2344
+ // Alias for storefront compatibility
2345
+ mergeGuestCart: mergeGuestCartMutation.mutate,
2346
+ addItem,
2347
+ updateQty,
2348
+ removeItem,
2349
+ subtotal,
2350
+ productTypeMismatch,
2351
+ handleClearCartAndAdd,
2352
+ handleKeepCurrentCart
2353
+ };
2354
+ }
2355
+
2356
+ // src/hooks/use-orders.ts
2357
+ var import_react_query2 = require("@tanstack/react-query");
2358
+ function useOrders(params) {
2359
+ return (0, import_react_query2.useQuery)({
2360
+ queryKey: ["orders", params],
2361
+ queryFn: () => getOrders(params)
2362
+ });
2363
+ }
2364
+ function useOrdersList(params) {
2365
+ return useOrders(params);
2366
+ }
2367
+ function useFindOrder(params) {
2368
+ return (0, import_react_query2.useQuery)({
2369
+ queryKey: ["order", params],
2370
+ queryFn: () => findOrder(params),
2371
+ enabled: !!params.email && !!params.order_id
2372
+ });
2373
+ }
2374
+ function useOrder(params) {
2375
+ return useFindOrder(params);
2376
+ }
2377
+ function useOrderTracking(orderId, token) {
2378
+ return (0, import_react_query2.useQuery)({
2379
+ queryKey: ["order-tracking", orderId, token],
2380
+ queryFn: () => trackOrder(orderId, token),
2381
+ enabled: !!orderId,
2382
+ refetchInterval: 3e4
2383
+ // Refetch every 30 seconds for tracking updates
2384
+ });
2385
+ }
2386
+ function useCreateOrder() {
2387
+ const queryClient = (0, import_react_query2.useQueryClient)();
2388
+ return (0, import_react_query2.useMutation)({
2389
+ mutationFn: createOrder,
2390
+ onSuccess: () => {
2391
+ queryClient.invalidateQueries({ queryKey: ["orders"] });
2392
+ }
2393
+ });
2394
+ }
2395
+ function useCancelOrder() {
2396
+ const queryClient = (0, import_react_query2.useQueryClient)();
2397
+ return (0, import_react_query2.useMutation)({
2398
+ mutationFn: ({ orderId, reason }) => cancelOrder(orderId, reason),
2399
+ onSuccess: () => {
2400
+ queryClient.invalidateQueries({ queryKey: ["orders"] });
2401
+ }
2402
+ });
2403
+ }
2404
+
2405
+ // src/hooks/use-auth.ts
2406
+ var import_react2 = __toESM(require("react"));
2407
+ var import_react_query3 = require("@tanstack/react-query");
2408
+ var AuthCtx = import_react2.default.createContext ? import_react2.default.createContext(null) : null;
2409
+ function AuthProvider({ children }) {
2410
+ const [customer, setCustomer] = (0, import_react2.useState)(null);
2411
+ const [isLoading, setIsLoading] = (0, import_react2.useState)(true);
2412
+ const [isModalOpen, setIsModalOpen] = (0, import_react2.useState)(false);
2413
+ (0, import_react2.useEffect)(() => {
2414
+ let voided = false;
2415
+ (async () => {
2416
+ setIsLoading(true);
2417
+ try {
2418
+ const result = await silentRefresh();
2419
+ if (!voided && result) {
2420
+ setCustomer(result.user);
2421
+ resetGraphQLClient();
2422
+ }
2423
+ } catch (error) {
2424
+ logger.error("[AuthProvider] Session restore failed:", error);
2425
+ if (!voided) setCustomer(null);
2426
+ } finally {
2427
+ if (!voided) setIsLoading(false);
2428
+ }
2429
+ })();
2430
+ return () => {
2431
+ voided = true;
2432
+ };
2433
+ }, []);
2434
+ const openModal = (0, import_react2.useCallback)(() => setIsModalOpen(true), []);
2435
+ const closeModal = (0, import_react2.useCallback)(() => {
2436
+ setIsModalOpen(false);
2437
+ if (typeof window !== "undefined" && (window.location.hash === "#login" || window.location.hash === "#signup")) {
2438
+ window.history.replaceState(null, "", window.location.pathname + window.location.search);
2439
+ }
2440
+ }, []);
2441
+ (0, import_react2.useEffect)(() => {
2442
+ const handleHash = () => {
2443
+ if (typeof window === "undefined") return;
2444
+ if (window.location.hash === "#login" || window.location.hash === "#signup") {
2445
+ if (!isLoading && customer) {
2446
+ window.history.replaceState(null, "", window.location.pathname + window.location.search);
2447
+ } else if (!isLoading && !customer) {
2448
+ setIsModalOpen(true);
2449
+ }
2450
+ }
2451
+ };
2452
+ handleHash();
2453
+ window.addEventListener("hashchange", handleHash);
2454
+ return () => window.removeEventListener("hashchange", handleHash);
2455
+ }, [customer, isLoading]);
2456
+ (0, import_react2.useEffect)(() => {
2457
+ resetGraphQLClient();
2458
+ }, [customer]);
2459
+ const handleLogout = (0, import_react2.useCallback)(async () => {
2460
+ await logout();
2461
+ setCustomer(null);
2462
+ setIsModalOpen(false);
2463
+ resetGraphQLClient();
2464
+ }, []);
2465
+ const value = {
2466
+ customer,
2467
+ isLoading,
2468
+ isModalOpen,
2469
+ openModal,
2470
+ closeModal,
2471
+ setCustomer,
2472
+ logout: handleLogout
2473
+ };
2474
+ if (!AuthCtx) return children;
2475
+ return import_react2.default.createElement(AuthCtx.Provider, { value }, children);
2476
+ }
2477
+ function useAuthUser() {
2478
+ return (0, import_react_query3.useQuery)({
2479
+ queryKey: ["auth-user"],
2480
+ queryFn: getAuthUser,
2481
+ enabled: !!tokenStore.getAccessToken()
2482
+ });
2483
+ }
2484
+ function useLogin() {
2485
+ const queryClient = (0, import_react_query3.useQueryClient)();
2486
+ return (0, import_react_query3.useMutation)({
2487
+ mutationFn: loginCustomer,
2488
+ onSuccess: () => {
2489
+ queryClient.invalidateQueries({ queryKey: ["auth-user"] });
2490
+ queryClient.invalidateQueries({ queryKey: ["cart"] });
2491
+ }
2492
+ });
2493
+ }
2494
+ function useRegister() {
2495
+ const queryClient = (0, import_react_query3.useQueryClient)();
2496
+ return (0, import_react_query3.useMutation)({
2497
+ mutationFn: registerCustomer,
2498
+ onSuccess: () => {
2499
+ queryClient.invalidateQueries({ queryKey: ["auth-user"] });
2500
+ queryClient.invalidateQueries({ queryKey: ["cart"] });
2501
+ }
2502
+ });
2503
+ }
2504
+ function useUpdateProfile() {
2505
+ const queryClient = (0, import_react_query3.useQueryClient)();
2506
+ return (0, import_react_query3.useMutation)({
2507
+ mutationFn: updateProfile,
2508
+ onSuccess: () => {
2509
+ queryClient.invalidateQueries({ queryKey: ["auth-user"] });
2510
+ }
2511
+ });
2512
+ }
2513
+ function useLogout() {
2514
+ const queryClient = (0, import_react_query3.useQueryClient)();
2515
+ return (0, import_react_query3.useMutation)({
2516
+ mutationFn: logout,
2517
+ onSuccess: () => {
2518
+ queryClient.clear();
2519
+ }
2520
+ });
2521
+ }
2522
+ function useAuth() {
2523
+ const context = AuthCtx ? (0, import_react2.useContext)(AuthCtx) : null;
2524
+ if (context) return context;
2525
+ const authUserQuery = useAuthUser();
2526
+ const logoutMutation = useLogout();
2527
+ return {
2528
+ customer: authUserQuery.data,
2529
+ isLoading: authUserQuery.isLoading,
2530
+ isModalOpen: false,
2531
+ openModal: () => {
2532
+ },
2533
+ closeModal: () => {
2534
+ },
2535
+ setCustomer: () => {
2536
+ },
2537
+ logout: async () => {
2538
+ await logoutMutation.mutateAsync();
2539
+ }
2540
+ };
2541
+ }
2542
+
2543
+ // src/hooks/use-countries.ts
2544
+ var import_react_query4 = require("@tanstack/react-query");
2545
+ async function getCountries() {
2546
+ const { data } = await authAxios.get("auth/countries/");
2547
+ return data;
2548
+ }
2549
+ function useCountries() {
2550
+ return (0, import_react_query4.useQuery)({
2551
+ queryKey: ["countries"],
2552
+ queryFn: () => getCountries(),
2553
+ retry: 1,
2554
+ refetchOnWindowFocus: false,
2555
+ refetchOnReconnect: false,
2556
+ staleTime: 24 * 60 * 60 * 1e3
2557
+ // 24 hours
2558
+ });
2559
+ }
2560
+
2561
+ // src/hooks/use-states.ts
2562
+ var import_react_query5 = require("@tanstack/react-query");
2563
+ async function getStates(countryIso) {
2564
+ if (!countryIso) return [];
2565
+ const { data } = await authAxios.get("auth/countries/", {
2566
+ params: { iso: countryIso }
2567
+ });
2568
+ return data;
2569
+ }
2570
+ function useStates(countryIso) {
2571
+ return (0, import_react_query5.useQuery)({
2572
+ queryKey: ["states", countryIso],
2573
+ queryFn: () => getStates(countryIso),
2574
+ enabled: !!countryIso,
2575
+ retry: 1,
2576
+ refetchOnWindowFocus: false,
2577
+ refetchOnReconnect: false,
2578
+ staleTime: 24 * 60 * 60 * 1e3
2579
+ // 24 hours
2580
+ });
2581
+ }
2582
+
2583
+ // src/hooks/use-languages.ts
2584
+ var import_react_query6 = require("@tanstack/react-query");
2585
+ async function getSupportedLanguagesList() {
2586
+ const data = await getSupportedLanguages();
2587
+ return data.languages.map((language) => ({
2588
+ code: language.code,
2589
+ appCode: language.code,
2590
+ label: language.label,
2591
+ native: language.label,
2592
+ flag: language.flag
2593
+ }));
2594
+ }
2595
+ function useSupportedLanguages() {
2596
+ return (0, import_react_query6.useQuery)({
2597
+ queryKey: ["supported-languages"],
2598
+ queryFn: () => getSupportedLanguagesList(),
2599
+ enabled: isInitialized(),
2600
+ retry: 1,
2601
+ ...getCacheTier("high")
2602
+ });
2603
+ }
2604
+
2605
+ // src/hooks/use-currencies.ts
2606
+ var import_react_query7 = require("@tanstack/react-query");
2607
+ async function getSupportedCurrenciesList() {
2608
+ const data = await getSupportedCurrencies();
2609
+ return data.currencies || [];
2610
+ }
2611
+ function useCurrencies() {
2612
+ return (0, import_react_query7.useQuery)({
2613
+ queryKey: ["supported-currencies"],
2614
+ queryFn: () => getSupportedCurrenciesList(),
2615
+ enabled: isInitialized(),
2616
+ retry: 1,
2617
+ ...getCacheTier("high")
2618
+ });
2619
+ }
2620
+
2621
+ // src/hooks/use-bespoke-availability.ts
2622
+ var import_react_query8 = require("@tanstack/react-query");
2623
+ function useBespokeAvailability(product_id) {
2624
+ return (0, import_react_query8.useQuery)({
2625
+ queryKey: ["bespoke-availability", product_id],
2626
+ queryFn: () => bespokeService.checkAvailability(product_id),
2627
+ enabled: !!product_id,
2628
+ retry: false
2629
+ // Don't retry if no config exists (returns 404)
2630
+ });
2631
+ }
2632
+
2633
+ // src/hooks/use-collections.ts
2634
+ var import_react_query9 = require("@tanstack/react-query");
2635
+ var collectionKeys = {
2636
+ all: ["core", "collections"],
2637
+ list: (params) => ["core", "collections", "list", params ?? {}],
2638
+ slug: (slug) => ["core", "collections", "slug", slug]
2639
+ };
2640
+ function useStorefrontCollections(params) {
2641
+ return (0, import_react_query9.useQuery)({
2642
+ queryKey: collectionKeys.list(params),
2643
+ queryFn: () => getStorefrontCollections(params),
2644
+ // medium — collections change infrequently
2645
+ ...getCacheTier("medium")
2646
+ });
2647
+ }
2648
+ function useStorefrontCollection(slug) {
2649
+ return (0, import_react_query9.useQuery)({
2650
+ queryKey: collectionKeys.slug(slug),
2651
+ queryFn: () => getStorefrontCollectionBySlug(slug),
2652
+ // medium — collection details change infrequently
2653
+ ...getCacheTier("medium"),
2654
+ enabled: !!slug
2655
+ });
2656
+ }
2657
+
2658
+ // src/hooks/use-products.ts
2659
+ var import_react_query10 = require("@tanstack/react-query");
2660
+ var productKeys = {
2661
+ all: ["products"],
2662
+ list: (params) => [...productKeys.all, "list", params],
2663
+ detail: (slug) => [...productKeys.all, "detail", slug],
2664
+ recommended: (params) => [...productKeys.all, "recommended", params],
2665
+ latest: (params) => [...productKeys.all, "latest", params],
2666
+ popular: (params) => [...productKeys.all, "popular", params],
2667
+ featured: (params) => [...productKeys.all, "featured", params],
2668
+ new: (params) => [...productKeys.all, "new", params]
2669
+ };
2670
+ function useLatestProducts(params) {
2671
+ return (0, import_react_query10.useQuery)({
2672
+ queryKey: productKeys.latest(params),
2673
+ queryFn: () => getLatestProducts(params),
2674
+ // medium — latest products change infrequently
2675
+ ...getCacheTier("medium")
2676
+ });
2677
+ }
2678
+ function usePopularProducts(params) {
2679
+ return (0, import_react_query10.useQuery)({
2680
+ queryKey: productKeys.popular(params),
2681
+ queryFn: () => getPopularProducts(params),
2682
+ // medium — popular products change infrequently
2683
+ ...getCacheTier("medium")
2684
+ });
2685
+ }
2686
+ function useFeaturedProducts(params) {
2687
+ return (0, import_react_query10.useQuery)({
2688
+ queryKey: productKeys.featured(params),
2689
+ queryFn: () => getFeaturedProducts(params),
2690
+ // medium — featured products change infrequently
2691
+ ...getCacheTier("medium")
2692
+ });
2693
+ }
2694
+ function useNewProducts(params) {
2695
+ return (0, import_react_query10.useQuery)({
2696
+ queryKey: productKeys.new(params),
2697
+ queryFn: () => getNewProducts(params),
2698
+ // medium — new products change infrequently
2699
+ ...getCacheTier("medium")
2700
+ });
2701
+ }
2702
+ function useStorefrontProducts(params) {
2703
+ return (0, import_react_query10.useQuery)({
2704
+ queryKey: productKeys.list(params),
2705
+ queryFn: () => getStorefrontProducts(params),
2706
+ // medium — product catalog changes infrequently
2707
+ ...getCacheTier("medium")
2708
+ });
2709
+ }
2710
+ function useProduct(slug) {
2711
+ return (0, import_react_query10.useQuery)({
2712
+ queryKey: productKeys.detail(slug),
2713
+ queryFn: () => getStorefrontProductBySlug(slug),
2714
+ // medium — product details change infrequently
2715
+ ...getCacheTier("medium"),
2716
+ enabled: !!slug
2717
+ });
2718
+ }
2719
+ function useRecommendedProducts(params) {
2720
+ return (0, import_react_query10.useQuery)({
2721
+ queryKey: productKeys.recommended(params),
2722
+ queryFn: () => getRecommendedProducts(params),
2723
+ // medium — recommendations change infrequently
2724
+ ...getCacheTier("medium")
2725
+ });
2726
+ }
2727
+
2728
+ // src/hooks/use-wishlist.ts
2729
+ var import_react_query11 = require("@tanstack/react-query");
2730
+ var wishlistKeys = {
2731
+ all: ["wishlist"],
2732
+ list: () => [...wishlistKeys.all, "list"],
2733
+ check: (productId) => [...wishlistKeys.all, "check", productId]
2734
+ };
2735
+ function useWishlist(params) {
2736
+ return (0, import_react_query11.useQuery)({
2737
+ queryKey: [...wishlistKeys.list(), params],
2738
+ queryFn: () => getWishlist(params),
2739
+ // medium — wishlist changes infrequently
2740
+ ...getCacheTier("medium")
2741
+ });
2742
+ }
2743
+ function useCheckInWishlist(productId) {
2744
+ return (0, import_react_query11.useQuery)({
2745
+ queryKey: wishlistKeys.check(productId),
2746
+ queryFn: () => checkInWishlist(productId),
2747
+ // low — wishlist check changes frequently
2748
+ ...getCacheTier("low"),
2749
+ enabled: !!productId
2750
+ });
2751
+ }
2752
+ function useAddToWishlist() {
2753
+ const queryClient = (0, import_react_query11.useQueryClient)();
2754
+ return (0, import_react_query11.useMutation)({
2755
+ mutationFn: ({ productId, notes }) => {
2756
+ return addToWishlist(productId, notes);
2757
+ },
2758
+ onSuccess: () => {
2759
+ queryClient.invalidateQueries({ queryKey: wishlistKeys.all });
2760
+ queryClient.invalidateQueries({ queryKey: ["wishlist", "check"] });
2761
+ }
2762
+ });
2763
+ }
2764
+ function useRemoveFromWishlist() {
2765
+ const queryClient = (0, import_react_query11.useQueryClient)();
2766
+ return (0, import_react_query11.useMutation)({
2767
+ mutationFn: (itemId) => {
2768
+ return removeFromWishlist(itemId);
2769
+ },
2770
+ onSuccess: () => {
2771
+ queryClient.invalidateQueries({ queryKey: wishlistKeys.all });
2772
+ queryClient.invalidateQueries({ queryKey: ["wishlist", "check"] });
2773
+ }
2774
+ });
2775
+ }
2776
+ function useRemoveProductFromWishlist() {
2777
+ const queryClient = (0, import_react_query11.useQueryClient)();
2778
+ return (0, import_react_query11.useMutation)({
2779
+ mutationFn: (productId) => {
2780
+ return removeProductFromWishlist(productId);
2781
+ },
2782
+ onSuccess: () => {
2783
+ queryClient.invalidateQueries({ queryKey: wishlistKeys.all });
2784
+ queryClient.invalidateQueries({ queryKey: ["wishlist", "check"] });
2785
+ }
2786
+ });
2787
+ }
2788
+ function useWishlistCount() {
2789
+ const backendQuery = (0, import_react_query11.useQuery)({
2790
+ queryKey: wishlistKeys.list(),
2791
+ queryFn: () => getWishlist(),
2792
+ // low — wishlist count changes frequently
2793
+ ...getCacheTier("low"),
2794
+ select: (data) => data.total
2795
+ });
2796
+ return backendQuery;
2797
+ }
2798
+
2799
+ // src/hooks/use-service-booking.ts
2800
+ var import_react_query12 = require("@tanstack/react-query");
2801
+ function useAvailableSlots(product_id, date_from, date_to) {
2802
+ return (0, import_react_query12.useQuery)({
2803
+ queryKey: ["available-slots", product_id, date_from, date_to],
2804
+ queryFn: () => serviceBookingService.getAvailableSlots(product_id, date_from, date_to),
2805
+ enabled: !!product_id && !!date_from && !!date_to
2806
+ });
2807
+ }
2808
+ function useBookAppointment() {
2809
+ const queryClient = (0, import_react_query12.useQueryClient)();
2810
+ return (0, import_react_query12.useMutation)({
2811
+ mutationFn: serviceBookingService.bookAppointment,
2812
+ onSuccess: () => {
2813
+ queryClient.invalidateQueries({ queryKey: ["appointments"] });
2814
+ }
2815
+ });
2816
+ }
2817
+ function useAppointments() {
2818
+ return (0, import_react_query12.useQuery)({
2819
+ queryKey: ["appointments"],
2820
+ queryFn: serviceBookingService.getAppointments
2821
+ });
2822
+ }
2823
+ function useAppointment(id) {
2824
+ return (0, import_react_query12.useQuery)({
2825
+ queryKey: ["appointment", id],
2826
+ queryFn: () => serviceBookingService.getAppointment(id),
2827
+ enabled: !!id
2828
+ });
2829
+ }
2830
+ function useCancelAppointment() {
2831
+ const queryClient = (0, import_react_query12.useQueryClient)();
2832
+ return (0, import_react_query12.useMutation)({
2833
+ mutationFn: (id) => serviceBookingService.cancelAppointment(id),
2834
+ onSuccess: () => {
2835
+ queryClient.invalidateQueries({ queryKey: ["appointments"] });
2836
+ }
2837
+ });
2838
+ }
2839
+ function useDeliverables(order_id) {
2840
+ return (0, import_react_query12.useQuery)({
2841
+ queryKey: ["deliverables", order_id],
2842
+ queryFn: () => serviceBookingService.getDeliverables(order_id),
2843
+ enabled: !!order_id
2844
+ });
2845
+ }
2846
+ function useApproveDeliverable(order_id, deliverable_id) {
2847
+ const queryClient = (0, import_react_query12.useQueryClient)();
2848
+ return (0, import_react_query12.useMutation)({
2849
+ mutationFn: () => serviceBookingService.approveDeliverable(order_id, deliverable_id),
2850
+ onSuccess: () => {
2851
+ queryClient.invalidateQueries({ queryKey: ["deliverables", order_id] });
2852
+ }
2853
+ });
2854
+ }
2855
+ function useRequestRevision(order_id, deliverable_id) {
2856
+ const queryClient = (0, import_react_query12.useQueryClient)();
2857
+ return (0, import_react_query12.useMutation)({
2858
+ mutationFn: (feedback) => serviceBookingService.requestRevision(order_id, deliverable_id, feedback),
2859
+ onSuccess: () => {
2860
+ queryClient.invalidateQueries({ queryKey: ["deliverables", order_id] });
2861
+ }
2862
+ });
2863
+ }
2864
+
2865
+ // src/hooks/use-consultations.ts
2866
+ var import_react_query13 = require("@tanstack/react-query");
2867
+ function useConsultations() {
2868
+ return (0, import_react_query13.useQuery)({
2869
+ queryKey: ["consultations"],
2870
+ queryFn: () => consultationService.getConsultations()
2871
+ });
2872
+ }
2873
+ function useConsultation(id) {
2874
+ return (0, import_react_query13.useQuery)({
2875
+ queryKey: ["consultation", id],
2876
+ queryFn: () => consultationService.getConsultation(id),
2877
+ enabled: !!id
2878
+ });
2879
+ }
2880
+ function useCreateConsultation() {
2881
+ const queryClient = (0, import_react_query13.useQueryClient)();
2882
+ return (0, import_react_query13.useMutation)({
2883
+ mutationFn: (payload) => consultationService.createConsultation(payload),
2884
+ onSuccess: () => {
2885
+ queryClient.invalidateQueries({ queryKey: ["consultations"] });
2886
+ }
2887
+ });
2888
+ }
2889
+ function useSendMessage(consultationId) {
2890
+ const queryClient = (0, import_react_query13.useQueryClient)();
2891
+ return (0, import_react_query13.useMutation)({
2892
+ mutationFn: (payload) => consultationService.sendMessage(consultationId, payload),
2893
+ onSuccess: () => {
2894
+ queryClient.invalidateQueries({ queryKey: ["consultation", consultationId] });
2895
+ }
2896
+ });
2897
+ }
2898
+ function useAcceptQuote(consultationId) {
2899
+ const queryClient = (0, import_react_query13.useQueryClient)();
2900
+ return (0, import_react_query13.useMutation)({
2901
+ mutationFn: (payload) => consultationService.acceptQuote(consultationId, payload),
2902
+ onSuccess: () => {
2903
+ queryClient.invalidateQueries({ queryKey: ["consultation", consultationId] });
2904
+ }
2905
+ });
2906
+ }
2907
+ function useRejectQuote(consultationId) {
2908
+ const queryClient = (0, import_react_query13.useQueryClient)();
2909
+ return (0, import_react_query13.useMutation)({
2910
+ mutationFn: (reason) => consultationService.rejectQuote(consultationId, reason),
2911
+ onSuccess: () => {
2912
+ queryClient.invalidateQueries({ queryKey: ["consultation", consultationId] });
2913
+ }
2914
+ });
2915
+ }
2916
+
2917
+ // src/hooks/use-consultation-stream.ts
2918
+ var import_react3 = require("react");
2919
+ var import_fetch_event_source = require("@microsoft/fetch-event-source");
2920
+ function useConsultationStream({
2921
+ consultationId,
2922
+ onMessage,
2923
+ onQuote,
2924
+ onStatusChange,
2925
+ onError
2926
+ }) {
2927
+ const abortControllerRef = (0, import_react3.useRef)(null);
2928
+ const [isConnected, setIsConnected] = (0, import_react3.useState)(false);
2929
+ const [lastEvent, setLastEvent] = (0, import_react3.useState)(null);
2930
+ (0, import_react3.useEffect)(() => {
2931
+ if (!consultationId) {
2932
+ return;
2933
+ }
2934
+ const abortController = new AbortController();
2935
+ abortControllerRef.current = abortController;
2936
+ const paymentUrl = getBackendUrl("payment");
2937
+ const streamUrl = `${paymentUrl}/events/stream/consultation/${consultationId}`;
2938
+ (0, import_fetch_event_source.fetchEventSource)(streamUrl, {
2939
+ method: "GET",
2940
+ signal: abortController.signal,
2941
+ onmessage: (event) => {
2942
+ try {
2943
+ if (!event.data || event.data.trim() === "") {
2944
+ return;
2945
+ }
2946
+ const data = JSON.parse(event.data);
2947
+ logger.log("[ConsultationStream] Received event:", data);
2948
+ setLastEvent(data);
2949
+ if (data.event === "consultation.message") {
2950
+ logger.log("[ConsultationStream] Message detected, calling onMessage");
2951
+ if (onMessage) {
2952
+ onMessage(data);
2953
+ }
2954
+ }
2955
+ if (data.event === "consultation.quote") {
2956
+ logger.log("[ConsultationStream] Quote detected, calling onQuote");
2957
+ if (onQuote) {
2958
+ onQuote(data);
2959
+ }
2960
+ }
2961
+ if (data.event === "consultation.status") {
2962
+ logger.log("[ConsultationStream] Status change detected, calling onStatusChange");
2963
+ if (onStatusChange) {
2964
+ onStatusChange(data);
2965
+ }
2966
+ }
2967
+ } catch (error) {
2968
+ logger.error("[ConsultationStream] Error parsing event:", error);
2969
+ }
2970
+ },
2971
+ onerror: (error) => {
2972
+ if (abortController.signal.aborted) {
2973
+ logger.log("[ConsultationStream] Connection closed via AbortController");
2974
+ return;
2975
+ }
2976
+ logger.error("[ConsultationStream] Connection error:", error);
2977
+ setIsConnected(false);
2978
+ if (onError) {
2979
+ onError(error);
2980
+ }
2981
+ },
2982
+ onopen: async (response) => {
2983
+ if (response.ok && response.headers.get("content-type")?.includes("text/event-stream")) {
2984
+ logger.log("[ConsultationStream] Connection established for consultation:", consultationId);
2985
+ setIsConnected(true);
2986
+ return;
2987
+ }
2988
+ throw new Error(`Unexpected response: ${response.status}`);
2989
+ }
2990
+ }).catch((error) => {
2991
+ if (abortController.signal.aborted || error.name === "AbortError") {
2992
+ return;
2993
+ }
2994
+ logger.error("[ConsultationStream] fetchEventSource failed:", error);
2995
+ });
2996
+ return () => {
2997
+ abortController.abort();
2998
+ setIsConnected(false);
2999
+ };
3000
+ }, [consultationId, onMessage, onQuote, onStatusChange, onError]);
3001
+ return { isConnected, lastEvent };
3002
+ }
3003
+
3004
+ // src/hooks/use-order-stream.ts
3005
+ var import_react4 = require("react");
3006
+ var import_fetch_event_source2 = require("@microsoft/fetch-event-source");
3007
+ function useOrderStream({
3008
+ orderId,
3009
+ onPaymentComplete,
3010
+ onDigitalReady,
3011
+ onBespokeUpdate,
3012
+ onConsultationUpdate,
3013
+ onServiceBookingUpdate,
3014
+ onError
3015
+ }) {
3016
+ const abortControllerRef = (0, import_react4.useRef)(null);
3017
+ (0, import_react4.useEffect)(() => {
3018
+ if (!orderId) {
3019
+ return;
3020
+ }
3021
+ const abortController = new AbortController();
3022
+ abortControllerRef.current = abortController;
3023
+ const paymentUrl = getBackendUrl("payment");
3024
+ const streamUrl = `${paymentUrl}/events/stream/order/${encodeURIComponent(orderId)}`;
3025
+ (0, import_fetch_event_source2.fetchEventSource)(streamUrl, {
3026
+ method: "GET",
3027
+ signal: abortController.signal,
3028
+ onmessage: (event) => {
3029
+ try {
3030
+ if (!event.data || event.data.trim() === "") {
3031
+ return;
3032
+ }
3033
+ const data = JSON.parse(event.data);
3034
+ logger.log("[OrderStream] Received event:", data);
3035
+ if (data.event === "payment.success") {
3036
+ logger.log("[OrderStream] Payment success detected, calling onPaymentComplete");
3037
+ if (onPaymentComplete) {
3038
+ onPaymentComplete(data);
3039
+ }
3040
+ abortController.abort();
3041
+ }
3042
+ if (data.event === "digital.ready") {
3043
+ logger.log("[OrderStream] Digital delivery ready detected, calling onDigitalReady");
3044
+ if (onDigitalReady) {
3045
+ onDigitalReady(data);
3046
+ }
3047
+ }
3048
+ if (data.event === "bespoke.update") {
3049
+ logger.log("[OrderStream] Bespoke update detected, calling onBespokeUpdate");
3050
+ if (onBespokeUpdate) {
3051
+ onBespokeUpdate(data);
3052
+ }
3053
+ }
3054
+ if (data.event === "consultation.update") {
3055
+ logger.log("[OrderStream] Consultation update detected, calling onConsultationUpdate");
3056
+ if (onConsultationUpdate) {
3057
+ onConsultationUpdate(data);
3058
+ }
3059
+ }
3060
+ if (data.event === "service_booking.update") {
3061
+ logger.log("[OrderStream] Service booking update detected, calling onServiceBookingUpdate");
3062
+ if (onServiceBookingUpdate) {
3063
+ onServiceBookingUpdate(data);
3064
+ }
3065
+ }
3066
+ } catch (error) {
3067
+ logger.error("[OrderStream] Error parsing event:", error);
3068
+ }
3069
+ },
3070
+ onerror: (error) => {
3071
+ if (abortController.signal.aborted) {
3072
+ logger.log("[OrderStream] Connection closed via AbortController");
3073
+ return;
3074
+ }
3075
+ if (error.message?.includes("404") || error.message?.includes("Not Found")) {
3076
+ logger.warn("[OrderStream] Stream endpoint unavailable for order:", orderId);
3077
+ return;
3078
+ }
3079
+ logger.error("[OrderStream] Connection error:", error);
3080
+ if (onError) {
3081
+ onError(error);
3082
+ }
3083
+ },
3084
+ onopen: async (response) => {
3085
+ if (response.ok && response.headers.get("content-type")?.includes("text/event-stream")) {
3086
+ logger.log("[OrderStream] Connection established for order:", orderId);
3087
+ return;
3088
+ }
3089
+ if (response.status === 404) {
3090
+ logger.warn("[OrderStream] Stream endpoint unavailable for order:", orderId);
3091
+ return;
3092
+ }
3093
+ throw new Error(`Unexpected response: ${response.status}`);
3094
+ }
3095
+ }).catch((error) => {
3096
+ if (abortController.signal.aborted || error.name === "AbortError") {
3097
+ return;
3098
+ }
3099
+ logger.error("[OrderStream] fetchEventSource failed:", error);
3100
+ });
3101
+ return () => {
3102
+ abortController.abort();
3103
+ };
3104
+ }, [orderId, onPaymentComplete, onDigitalReady, onBespokeUpdate, onConsultationUpdate, onServiceBookingUpdate, onError]);
3105
+ return null;
3106
+ }
3107
+
3108
+ // src/hooks/use-is-mounted.ts
3109
+ var import_react5 = require("react");
3110
+ function useIsMounted() {
3111
+ const [mounted, setMounted] = (0, import_react5.useState)(false);
3112
+ (0, import_react5.useEffect)(() => {
3113
+ setMounted(true);
3114
+ }, []);
3115
+ return mounted;
3116
+ }
3117
+
3118
+ // src/hooks/use-google-translate.ts
3119
+ var import_react6 = require("react");
3120
+ var SCRIPT_ID = "google-translate-script";
3121
+ var ELEMENT_ID = "google_translate_element";
3122
+ function fireChange(googleCode) {
3123
+ const combo = document.querySelector(".goog-te-combo");
3124
+ if (combo) {
3125
+ combo.value = googleCode;
3126
+ combo.dispatchEvent(new Event("change"));
3127
+ return true;
3128
+ }
3129
+ return false;
3130
+ }
3131
+ function useGoogleTranslate(appLang) {
3132
+ const [isLoaded, setIsLoaded] = (0, import_react6.useState)(false);
3133
+ const initRef = (0, import_react6.useRef)(false);
3134
+ const { data: supportedLangs = [] } = useSupportedLanguages();
3135
+ const clearCookies = (0, import_react6.useCallback)(() => {
3136
+ if (typeof window === "undefined") return;
3137
+ const domains = [
3138
+ window.location.hostname,
3139
+ "." + window.location.hostname,
3140
+ "." + window.location.hostname.split(".").slice(-2).join(".")
3141
+ ];
3142
+ const path = "/";
3143
+ document.cookie = `googtrans=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=${path};`;
3144
+ domains.forEach((domain) => {
3145
+ document.cookie = `googtrans=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=${path}; domain=${domain};`;
3146
+ });
3147
+ }, []);
3148
+ const resetToEnglish = (0, import_react6.useCallback)(() => {
3149
+ if (typeof window === "undefined") return;
3150
+ const googtrans = document.cookie.split("; ").find((row) => row.startsWith("googtrans="))?.split("=")[1];
3151
+ const domains = [
3152
+ window.location.hostname,
3153
+ "." + window.location.hostname,
3154
+ window.location.hostname.split(".").slice(-2).join("."),
3155
+ "." + window.location.hostname.split(".").slice(-2).join(".")
3156
+ ];
3157
+ const clear = (name) => {
3158
+ document.cookie = `${name}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
3159
+ domains.forEach((d) => {
3160
+ document.cookie = `${name}=; Path=/; Domain=${d}; Expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
3161
+ });
3162
+ };
3163
+ clear("googtrans");
3164
+ if (googtrans || document.documentElement.classList.contains("translated-ltr") || document.querySelector(".goog-te-banner-frame")) {
3165
+ window.location.reload();
3166
+ return;
3167
+ }
3168
+ document.body.style.top = "0";
3169
+ document.documentElement.classList.remove("translated-ltr", "translated-rtl");
3170
+ const container = document.getElementById(":1.container");
3171
+ if (container) container.style.display = "none";
3172
+ }, []);
3173
+ const changeLanguage = (0, import_react6.useCallback)((nextAppCode) => {
3174
+ if (nextAppCode === "en") {
3175
+ resetToEnglish();
3176
+ return;
3177
+ }
3178
+ const googleCode = nextAppCode;
3179
+ const container = document.getElementById(":1.container");
3180
+ if (container) container.style.display = "block";
3181
+ let tries = 0;
3182
+ const tick = () => {
3183
+ if (fireChange(googleCode)) return;
3184
+ if (tries++ < 30) setTimeout(tick, 200);
3185
+ };
3186
+ tick();
3187
+ }, [resetToEnglish]);
3188
+ (0, import_react6.useEffect)(() => {
3189
+ if (typeof window === "undefined") return;
3190
+ if (appLang === "en" && !initRef.current) return;
3191
+ if (initRef.current) return;
3192
+ if (supportedLangs.length === 0) return;
3193
+ initRef.current = true;
3194
+ if (!document.getElementById(ELEMENT_ID)) {
3195
+ const host = document.createElement("div");
3196
+ host.id = ELEMENT_ID;
3197
+ host.style.display = "none";
3198
+ document.body.appendChild(host);
3199
+ }
3200
+ const includedLanguages = supportedLangs.map((l) => l.code).join(",");
3201
+ window.googleTranslateElementInit = () => {
3202
+ if (window.google?.translate?.TranslateElement) {
3203
+ new window.google.translate.TranslateElement(
3204
+ { pageLanguage: "en", includedLanguages, autoDisplay: false },
3205
+ ELEMENT_ID
3206
+ );
3207
+ setIsLoaded(true);
3208
+ }
3209
+ };
3210
+ if (!document.getElementById(SCRIPT_ID)) {
3211
+ const script = document.createElement("script");
3212
+ script.id = SCRIPT_ID;
3213
+ script.src = "//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit";
3214
+ script.async = true;
3215
+ document.body.appendChild(script);
3216
+ } else {
3217
+ if (window.google?.translate) window.googleTranslateElementInit?.();
3218
+ }
3219
+ }, [appLang, supportedLangs]);
3220
+ (0, import_react6.useEffect)(() => {
3221
+ if (isLoaded || appLang === "en" && initRef.current) {
3222
+ changeLanguage(appLang);
3223
+ }
3224
+ }, [appLang, isLoaded, changeLanguage]);
3225
+ return { isLoaded };
3226
+ }
3227
+
3228
+ // src/hooks/use-payment.ts
3229
+ var import_react_query14 = require("@tanstack/react-query");
3230
+ function useCreateDVA() {
3231
+ const queryClient = (0, import_react_query14.useQueryClient)();
3232
+ return (0, import_react_query14.useMutation)({
3233
+ mutationFn: (request) => paymentService.createDedicatedAccount(request),
3234
+ onSuccess: () => {
3235
+ queryClient.invalidateQueries({ queryKey: ["payment"] });
3236
+ }
3237
+ });
3238
+ }
3239
+ function useInitiatePayment() {
3240
+ return (0, import_react_query14.useMutation)({
3241
+ mutationFn: (request) => paymentService.initiatePayment(request)
3242
+ });
3243
+ }
3244
+ function useVerifyTransaction() {
3245
+ return (0, import_react_query14.useMutation)({
3246
+ mutationFn: (reference) => paymentService.verifyTransaction(reference)
3247
+ });
3248
+ }
3249
+ function useTransactionStatus(transactionId, enabled = false) {
3250
+ return (0, import_react_query14.useQuery)({
3251
+ queryKey: ["transaction", "status", transactionId],
3252
+ queryFn: () => paymentService.getTransactionStatus(transactionId),
3253
+ enabled: isInitialized() && enabled,
3254
+ ...getCacheTier("none"),
3255
+ refetchInterval: 3e3
3256
+ // Poll every 3 seconds
3257
+ });
3258
+ }
3259
+ function usePollTransactionStatus(reference, enabled = false) {
3260
+ return (0, import_react_query14.useQuery)({
3261
+ queryKey: ["transaction", "poll", reference],
3262
+ queryFn: () => paymentService.verifyTransaction(reference),
3263
+ enabled: isInitialized() && enabled,
3264
+ ...getCacheTier("none"),
3265
+ refetchInterval: 2e3,
3266
+ // Poll every 2 seconds
3267
+ retry: 10
3268
+ // Retry up to 10 times
3269
+ });
3270
+ }
3271
+
3272
+ // src/hooks/use-cms.ts
3273
+ var import_react_query15 = require("@tanstack/react-query");
3274
+ function useCmsEntries(modelSlug, params) {
3275
+ const query = (0, import_react_query15.useQuery)({
3276
+ queryKey: ["cms-entries", modelSlug, params],
3277
+ queryFn: () => listCmsEntries(modelSlug, params),
3278
+ enabled: isInitialized() && !!modelSlug
3279
+ });
3280
+ return {
3281
+ entries: query.data?.entries || [],
3282
+ total: query.data?.total || 0,
3283
+ page: query.data?.page || 1,
3284
+ page_size: query.data?.page_size || 50,
3285
+ isLoading: query.isLoading,
3286
+ error: query.error,
3287
+ refetch: query.refetch
3288
+ };
3289
+ }
3290
+ function useCmsEntry(modelSlug, entryId) {
3291
+ const query = (0, import_react_query15.useQuery)({
3292
+ queryKey: ["cms-entry", modelSlug, entryId],
3293
+ queryFn: () => getCmsEntry(modelSlug, entryId),
3294
+ enabled: isInitialized() && !!modelSlug && !!entryId
3295
+ });
3296
+ return {
3297
+ entry: query.data,
3298
+ isLoading: query.isLoading,
3299
+ error: query.error,
3300
+ refetch: query.refetch
3301
+ };
3302
+ }
3303
+ function useCreateCmsEntry(modelSlug) {
3304
+ const queryClient = (0, import_react_query15.useQueryClient)();
3305
+ const mutation = (0, import_react_query15.useMutation)({
3306
+ mutationFn: (params) => createCmsEntry(modelSlug, params),
3307
+ onSuccess: () => {
3308
+ queryClient.invalidateQueries({ queryKey: ["cms-entries", modelSlug] });
3309
+ }
3310
+ });
3311
+ return {
3312
+ createEntry: mutation.mutate,
3313
+ isLoading: mutation.isPending,
3314
+ error: mutation.error
3315
+ };
3316
+ }
3317
+ function useUpdateCmsEntry(modelSlug) {
3318
+ const queryClient = (0, import_react_query15.useQueryClient)();
3319
+ const mutation = (0, import_react_query15.useMutation)({
3320
+ mutationFn: ({ entryId, params }) => updateCmsEntry(modelSlug, entryId, params),
3321
+ onSuccess: () => {
3322
+ queryClient.invalidateQueries({ queryKey: ["cms-entries", modelSlug] });
3323
+ queryClient.invalidateQueries({ queryKey: ["cms-entry", modelSlug] });
3324
+ }
3325
+ });
3326
+ return {
3327
+ updateEntry: (entryId, params) => mutation.mutate({ entryId, params }),
3328
+ isLoading: mutation.isPending,
3329
+ error: mutation.error
3330
+ };
3331
+ }
3332
+ function useDeleteCmsEntry(modelSlug) {
3333
+ const queryClient = (0, import_react_query15.useQueryClient)();
3334
+ const mutation = (0, import_react_query15.useMutation)({
3335
+ mutationFn: (entryId) => deleteCmsEntry(modelSlug, entryId),
3336
+ onSuccess: () => {
3337
+ queryClient.invalidateQueries({ queryKey: ["cms-entries", modelSlug] });
3338
+ queryClient.invalidateQueries({ queryKey: ["cms-entry", modelSlug] });
3339
+ }
3340
+ });
3341
+ return {
3342
+ deleteEntry: mutation.mutate,
3343
+ isLoading: mutation.isPending,
3344
+ error: mutation.error
3345
+ };
3346
+ }
3347
+ // Annotate the CommonJS export names for ESM import in node:
3348
+ 0 && (module.exports = {
3349
+ AuthCtx,
3350
+ AuthProvider,
3351
+ CartCtx,
3352
+ CartProvider,
3353
+ InvalidBaseURLError,
3354
+ acceptQuote,
3355
+ addToCart,
3356
+ addToGuestCart,
3357
+ addToWishlist,
3358
+ approveDeliverable,
3359
+ authAxios,
3360
+ bespokeService,
3361
+ bookAppointment,
3362
+ buildDeliveryAddress,
3363
+ buildFullName,
3364
+ cancelAppointment,
3365
+ cancelOrder,
3366
+ checkAvailability,
3367
+ checkInWishlist,
3368
+ clearCart,
3369
+ clearGuestCart,
3370
+ clearGuestSessionId,
3371
+ cmsAxios,
3372
+ collectionKeys,
3373
+ configService,
3374
+ configureHttpClients,
3375
+ consultationService,
3376
+ createCmsEntry,
3377
+ createConsultation,
3378
+ createDedicatedAccount,
3379
+ createOrder,
3380
+ deleteCmsEntry,
3381
+ enrichCompletedOrder,
3382
+ findOrder,
3383
+ findOrderByToken,
3384
+ getAppointment,
3385
+ getAppointments,
3386
+ getAuthUser,
3387
+ getAvailableSlots,
3388
+ getBackendUrl,
3389
+ getBusinessConfig,
3390
+ getCacheTier,
3391
+ getCart,
3392
+ getCheckoutCustomerName,
3393
+ getCmsEntry,
3394
+ getConsultation,
3395
+ getConsultations,
3396
+ getCountries,
3397
+ getCustomerDisplayName,
3398
+ getDeliverables,
3399
+ getErrorMessage,
3400
+ getFeaturedProducts,
3401
+ getGraphQLClient,
3402
+ getGuestCart,
3403
+ getGuestCartCount,
3404
+ getGuestCartProductType,
3405
+ getGuestCartSubtotal,
3406
+ getGuestSessionId,
3407
+ getLatestProducts,
3408
+ getMe,
3409
+ getNewProducts,
3410
+ getOrCreateGuestSessionId,
3411
+ getOrders,
3412
+ getPopularProducts,
3413
+ getRecommendedProducts,
3414
+ getRuntimeConfig,
3415
+ getSellerShippingAddress,
3416
+ getShippingRates,
3417
+ getStates,
3418
+ getStorefrontCollectionBySlug,
3419
+ getStorefrontCollections,
3420
+ getStorefrontProductBySlug,
3421
+ getStorefrontProducts,
3422
+ getSupportedCurrencies,
3423
+ getSupportedCurrenciesList,
3424
+ getSupportedLanguages,
3425
+ getSupportedLanguagesList,
3426
+ getTransactionStatus,
3427
+ getWishlist,
3428
+ googleTranslate,
3429
+ initSdk,
3430
+ initiatePayment,
3431
+ isInitialized,
3432
+ listCmsEntries,
3433
+ logger,
3434
+ loginCustomer,
3435
+ logisticsService,
3436
+ logout,
3437
+ mapAuthUserToCustomer,
3438
+ mergeGuestCart,
3439
+ mergeGuestCartIfPresent,
3440
+ notificationsService,
3441
+ paymentAxios,
3442
+ paymentService,
3443
+ productKeys,
3444
+ publicStoreAxios,
3445
+ refreshToken,
3446
+ registerCustomer,
3447
+ rejectQuote,
3448
+ removeFromCart,
3449
+ removeFromGuestCart,
3450
+ removeFromWishlist,
3451
+ removeProductFromWishlist,
3452
+ requestQuote,
3453
+ requestRevision,
3454
+ resetConfig,
3455
+ resetGraphQLClient,
3456
+ sanitizeBaseUrl,
3457
+ sendContactConfirmation,
3458
+ sendContactEmail,
3459
+ sendMessage,
3460
+ serviceBookingService,
3461
+ setEnvironmentMode,
3462
+ setLoggerEnvironmentMode,
3463
+ silentRefresh,
3464
+ splitFullName,
3465
+ storeManagementAxios,
3466
+ storeTokens,
3467
+ tokenStore,
3468
+ trackOrder,
3469
+ updateCartItem,
3470
+ updateCmsEntry,
3471
+ updateGuestCartItemQuantity,
3472
+ updateProfile,
3473
+ useAcceptQuote,
3474
+ useAddToWishlist,
3475
+ useAppointment,
3476
+ useAppointments,
3477
+ useApproveDeliverable,
3478
+ useAuth,
3479
+ useAuthUser,
3480
+ useAvailableSlots,
3481
+ useBespokeAvailability,
3482
+ useBookAppointment,
3483
+ useCancelAppointment,
3484
+ useCancelOrder,
3485
+ useCart,
3486
+ useCheckInWishlist,
3487
+ useCmsEntries,
3488
+ useCmsEntry,
3489
+ useConsultation,
3490
+ useConsultationStream,
3491
+ useConsultations,
3492
+ useCountries,
3493
+ useCreateCmsEntry,
3494
+ useCreateConsultation,
3495
+ useCreateDVA,
3496
+ useCreateOrder,
3497
+ useCurrencies,
3498
+ useDeleteCmsEntry,
3499
+ useDeliverables,
3500
+ useFeaturedProducts,
3501
+ useFindOrder,
3502
+ useGoogleTranslate,
3503
+ useInitiatePayment,
3504
+ useIsMounted,
3505
+ useLatestProducts,
3506
+ useLogin,
3507
+ useLogout,
3508
+ useNewProducts,
3509
+ useOrder,
3510
+ useOrderStream,
3511
+ useOrderTracking,
3512
+ useOrders,
3513
+ useOrdersList,
3514
+ usePollTransactionStatus,
3515
+ usePopularProducts,
3516
+ useProduct,
3517
+ useRecommendedProducts,
3518
+ useRegister,
3519
+ useRejectQuote,
3520
+ useRemoveFromWishlist,
3521
+ useRemoveProductFromWishlist,
3522
+ useRequestRevision,
3523
+ useSendMessage,
3524
+ useStates,
3525
+ useStorefrontCollection,
3526
+ useStorefrontCollections,
3527
+ useStorefrontProducts,
3528
+ useSupportedLanguages,
3529
+ useTransactionStatus,
3530
+ useUpdateCmsEntry,
3531
+ useUpdateProfile,
3532
+ useVerifyTransaction,
3533
+ useWishlist,
3534
+ useWishlistCount,
3535
+ validateShippingAddress,
3536
+ verifySocialToken,
3537
+ verifyTransaction,
3538
+ wishlistKeys
3539
+ });
3540
+ //# sourceMappingURL=index.js.map