@kasuvia/sdk 0.0.2 → 0.0.4

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