@kasuvia/sdk 0.0.8 → 0.0.9

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 (176) hide show
  1. package/README.md +42 -15
  2. package/dist/AddressAutocomplete-CjcqPElD.d.mts +46 -0
  3. package/dist/AddressAutocomplete-CvJHuRkf.d.ts +46 -0
  4. package/dist/components/index.d.mts +497 -0
  5. package/dist/components/index.d.ts +497 -0
  6. package/dist/components/index.js +4217 -0
  7. package/dist/components/index.js.map +1 -0
  8. package/dist/components/index.mjs +4194 -0
  9. package/dist/components/index.mjs.map +1 -0
  10. package/dist/config/server.d.mts +26 -0
  11. package/dist/config/server.d.ts +26 -0
  12. package/dist/config/server.js +38 -0
  13. package/dist/config/server.js.map +1 -0
  14. package/dist/config/server.mjs +13 -0
  15. package/dist/config/server.mjs.map +1 -0
  16. package/dist/hooks/index.d.mts +393 -160
  17. package/dist/hooks/index.d.ts +393 -160
  18. package/dist/hooks/index.js +2792 -2137
  19. package/dist/hooks/index.js.map +1 -1
  20. package/dist/hooks/index.mjs +2734 -2121
  21. package/dist/hooks/index.mjs.map +1 -1
  22. package/dist/index-CNW_LINy.d.mts +578 -0
  23. package/dist/index-CNW_LINy.d.ts +578 -0
  24. package/dist/{index-C1chUA3P.d.mts → index-F7N6UItS.d.mts} +1 -1
  25. package/dist/{index-C1chUA3P.d.ts → index-F7N6UItS.d.ts} +1 -1
  26. package/dist/index-NkI10J_E.d.mts +40 -0
  27. package/dist/index-NkI10J_E.d.ts +40 -0
  28. package/dist/index.d.mts +20437 -103
  29. package/dist/index.d.ts +20437 -103
  30. package/dist/index.js +2111 -1802
  31. package/dist/index.js.map +1 -1
  32. package/dist/index.mjs +2006 -1786
  33. package/dist/index.mjs.map +1 -1
  34. package/dist/media.service-Cx7dQjzX.d.mts +1393 -0
  35. package/dist/media.service-mrBFJ2UQ.d.ts +1393 -0
  36. package/dist/services/index.d.mts +908 -2
  37. package/dist/services/index.d.ts +908 -2
  38. package/dist/services/index.js +1492 -1686
  39. package/dist/services/index.js.map +1 -1
  40. package/dist/services/index.mjs +1431 -1670
  41. package/dist/services/index.mjs.map +1 -1
  42. package/package.json +46 -6
  43. package/src/components/bespoke/BespokeAvailabilityBadge.tsx +32 -0
  44. package/src/components/bespoke/BespokeProgressTracker.tsx +58 -0
  45. package/src/components/bespoke/index.ts +3 -0
  46. package/src/components/booking/SlotPicker.tsx +97 -0
  47. package/src/components/booking/index.ts +1 -0
  48. package/src/components/cart/CartItem.tsx +81 -0
  49. package/src/components/cart/CartSidebar.tsx +115 -0
  50. package/src/components/cart/CartTypeMismatchBanner.tsx +55 -0
  51. package/src/components/cart/index.ts +3 -0
  52. package/src/components/index.ts +8 -0
  53. package/src/components/orders/DeliverableReviewer.tsx +139 -0
  54. package/src/components/orders/index.ts +1 -0
  55. package/src/components/product/ProductCard.tsx +350 -0
  56. package/src/components/product/ProductGrid.tsx +84 -0
  57. package/src/components/product/index.ts +2 -0
  58. package/src/components/shared/AddressAutocomplete.tsx +212 -0
  59. package/src/components/shared/AnalyticsScripts.tsx +56 -0
  60. package/src/components/shared/CookieConsentBanner.tsx +98 -0
  61. package/src/components/shared/ImageEditorModal.tsx +1066 -0
  62. package/src/components/shared/ImageViewerModal.tsx +598 -0
  63. package/src/components/shared/LucideReactIcon.tsx +52 -0
  64. package/src/components/shared/MapPinDrop.tsx +101 -0
  65. package/src/components/shared/MultiSelectPicker.tsx +193 -0
  66. package/src/components/shared/OfflineBanner.tsx +46 -0
  67. package/src/components/shared/SearchableInput.tsx +153 -0
  68. package/src/components/shared/SearchablePopover.tsx +166 -0
  69. package/src/components/shared/SmartImage.tsx +311 -0
  70. package/src/components/shared/TruncatedValue.tsx +27 -0
  71. package/src/components/shared/UnsavedChangesGuard.tsx +138 -0
  72. package/src/components/shared/index.ts +15 -0
  73. package/src/components/skeletons/CardSkeleton.tsx +26 -0
  74. package/src/components/skeletons/CartItemSkeleton.tsx +26 -0
  75. package/src/components/skeletons/OrderItemSkeleton.tsx +26 -0
  76. package/src/components/skeletons/ProductCardSkeleton.tsx +26 -0
  77. package/src/components/skeletons/TableSkeleton.tsx +42 -0
  78. package/src/components/skeletons/index.ts +5 -0
  79. package/src/config/index.ts +22 -4
  80. package/src/config/server.ts +45 -0
  81. package/src/context/sdk-context.tsx +5 -2
  82. package/src/generated/auth-openapi.ts +2935 -0
  83. package/src/generated/cart-openapi.ts +652 -0
  84. package/src/generated/cms-openapi.ts +1668 -0
  85. package/src/generated/crm-openapi.ts +986 -0
  86. package/src/generated/graphql.ts +126 -0
  87. package/src/generated/index.ts +11 -0
  88. package/src/generated/media-openapi.ts +613 -0
  89. package/src/generated/notification-openapi.ts +862 -0
  90. package/src/generated/payment-openapi.ts +2713 -0
  91. package/src/generated/store-openapi.ts +9510 -0
  92. package/src/graphql/operations/collections.graphql +32 -0
  93. package/src/graphql/operations/products.graphql +197 -0
  94. package/src/graphql/operations/wishlist.graphql +25 -0
  95. package/src/hooks/index.ts +9 -2
  96. package/src/hooks/use-auth.ts +163 -69
  97. package/src/hooks/use-bespoke-availability.ts +39 -22
  98. package/src/hooks/use-business-config.ts +51 -0
  99. package/src/hooks/use-cart.ts +310 -238
  100. package/src/hooks/use-cms.ts +107 -127
  101. package/src/hooks/use-collections.ts +35 -55
  102. package/src/hooks/use-consultations.ts +9 -136
  103. package/src/hooks/use-countries.ts +19 -28
  104. package/src/hooks/use-currencies.ts +8 -20
  105. package/src/hooks/use-customer-address.ts +64 -0
  106. package/src/hooks/use-feedback.ts +34 -0
  107. package/src/hooks/use-languages.ts +26 -39
  108. package/src/hooks/use-media-upload.ts +23 -0
  109. package/src/hooks/use-notification-stream.ts +88 -0
  110. package/src/hooks/use-notifications.ts +163 -0
  111. package/src/hooks/use-order-stream.ts +29 -29
  112. package/src/hooks/use-orders.ts +91 -73
  113. package/src/hooks/use-payment.ts +116 -84
  114. package/src/hooks/use-products.ts +65 -269
  115. package/src/hooks/use-push-notifications.ts +122 -0
  116. package/src/hooks/use-reviews.ts +175 -0
  117. package/src/hooks/use-service-booking.ts +109 -132
  118. package/src/hooks/use-states.ts +16 -30
  119. package/src/hooks/use-wishlist.ts +44 -69
  120. package/src/index.ts +15 -11
  121. package/src/services/auth.service.ts +422 -132
  122. package/src/services/bespoke.service.ts +69 -10
  123. package/src/services/cart.service.ts +155 -186
  124. package/src/services/cms.service.ts +41 -50
  125. package/src/services/collection.service.ts +25 -63
  126. package/src/services/config.service.ts +10 -21
  127. package/src/services/consultation.service.ts +36 -93
  128. package/src/services/feedback.service.ts +76 -0
  129. package/src/services/index.ts +7 -1
  130. package/src/services/legal.service.ts +62 -0
  131. package/src/services/logistics.service.ts +235 -53
  132. package/src/services/media.service.ts +115 -0
  133. package/src/services/notifications.service.ts +204 -20
  134. package/src/services/order.service.ts +498 -127
  135. package/src/services/payment.service.ts +149 -100
  136. package/src/services/product.service.ts +157 -814
  137. package/src/services/review.service.ts +211 -0
  138. package/src/services/service-booking.service.ts +198 -99
  139. package/src/services/wishlist.service.ts +27 -74
  140. package/src/types/auth/index.ts +161 -18
  141. package/src/types/business/index.ts +9 -1
  142. package/src/types/cms/index.ts +91 -6
  143. package/src/types/cms/legal.ts +18 -0
  144. package/src/types/consultations/index.ts +60 -68
  145. package/src/types/index.ts +5 -3
  146. package/src/types/media/index.ts +35 -3
  147. package/src/types/payments/index.ts +80 -16
  148. package/src/types/reviews/index.ts +116 -0
  149. package/src/types/services/index.ts +113 -38
  150. package/src/types/store/checkout.ts +53 -0
  151. package/src/types/store/index.ts +273 -52
  152. package/src/types/store/invoice.ts +31 -0
  153. package/src/types/store/shipping.ts +84 -3
  154. package/src/types/ui/index.ts +1 -1
  155. package/src/utils/checkout.ts +19 -3
  156. package/src/utils/cookieConsent.ts +34 -0
  157. package/src/utils/format.ts +408 -0
  158. package/src/utils/geolocation.ts +134 -0
  159. package/src/utils/graphql-url.ts +47 -0
  160. package/src/utils/guest-session.ts +59 -0
  161. package/src/utils/http-client.ts +147 -0
  162. package/src/utils/index.ts +11 -4
  163. package/src/utils/status-colors.ts +79 -0
  164. package/src/utils/token-refresh.ts +115 -0
  165. package/src/utils/validators.ts +13 -0
  166. package/src/utils/youtube.ts +18 -0
  167. package/dist/index-C5YO9gTl.d.mts +0 -507
  168. package/dist/index-C5YO9gTl.d.ts +0 -507
  169. package/dist/index-CefG7_xq.d.mts +0 -793
  170. package/dist/index-DeHCf3zI.d.ts +0 -793
  171. package/src/graphql/collections.graphql.ts +0 -48
  172. package/src/graphql/index.ts +0 -7
  173. package/src/graphql/products.graphql.ts +0 -860
  174. package/src/graphql/wishlist.graphql.ts +0 -34
  175. package/src/hooks/use-consultation-stream.ts +0 -142
  176. package/src/types/bespoke/index.ts +0 -39
package/dist/index.mjs CHANGED
@@ -12,6 +12,13 @@ var PAYMENT_URLS = {
12
12
  };
13
13
 
14
14
  // src/types/store/index.ts
15
+ var ProductTypeEnum = /* @__PURE__ */ ((ProductTypeEnum2) => {
16
+ ProductTypeEnum2["PHYSICAL"] = "physical";
17
+ ProductTypeEnum2["DIGITAL"] = "digital";
18
+ ProductTypeEnum2["SERVICE"] = "service";
19
+ ProductTypeEnum2["BESPOKE"] = "bespoke";
20
+ return ProductTypeEnum2;
21
+ })(ProductTypeEnum || {});
15
22
  var ServiceTypeEnum = /* @__PURE__ */ ((ServiceTypeEnum2) => {
16
23
  ServiceTypeEnum2["APPOINTMENT"] = "appointment";
17
24
  ServiceTypeEnum2["PROJECT"] = "project";
@@ -20,8 +27,8 @@ var ServiceTypeEnum = /* @__PURE__ */ ((ServiceTypeEnum2) => {
20
27
  })(ServiceTypeEnum || {});
21
28
  var ServiceLocationTypeEnum = /* @__PURE__ */ ((ServiceLocationTypeEnum2) => {
22
29
  ServiceLocationTypeEnum2["REMOTE"] = "remote";
23
- ServiceLocationTypeEnum2["PHYSICAL"] = "physical";
24
- ServiceLocationTypeEnum2["BOTH"] = "both";
30
+ ServiceLocationTypeEnum2["AT_BUSINESS"] = "at_business";
31
+ ServiceLocationTypeEnum2["CUSTOMER_LOCATION"] = "customer_location";
25
32
  return ServiceLocationTypeEnum2;
26
33
  })(ServiceLocationTypeEnum || {});
27
34
  var ServiceRecurringIntervalEnum = /* @__PURE__ */ ((ServiceRecurringIntervalEnum2) => {
@@ -31,6 +38,130 @@ var ServiceRecurringIntervalEnum = /* @__PURE__ */ ((ServiceRecurringIntervalEnu
31
38
  return ServiceRecurringIntervalEnum2;
32
39
  })(ServiceRecurringIntervalEnum || {});
33
40
 
41
+ // src/types/payments/index.ts
42
+ var PaymentGatewayEnum = /* @__PURE__ */ ((PaymentGatewayEnum2) => {
43
+ PaymentGatewayEnum2["MONNIFY"] = "monnify";
44
+ PaymentGatewayEnum2["FLUTTERWAVE"] = "flutterwave";
45
+ PaymentGatewayEnum2["PAYSTACK"] = "paystack";
46
+ return PaymentGatewayEnum2;
47
+ })(PaymentGatewayEnum || {});
48
+ var PaymentMethodTypeEnum = /* @__PURE__ */ ((PaymentMethodTypeEnum2) => {
49
+ PaymentMethodTypeEnum2["CARD"] = "card";
50
+ PaymentMethodTypeEnum2["TRANSFER"] = "transfer";
51
+ PaymentMethodTypeEnum2["BANK_TRANSFER"] = "bank_transfer";
52
+ PaymentMethodTypeEnum2["ACCOUNT"] = "account";
53
+ PaymentMethodTypeEnum2["USSD"] = "ussd";
54
+ return PaymentMethodTypeEnum2;
55
+ })(PaymentMethodTypeEnum || {});
56
+
57
+ // src/types/reviews/index.ts
58
+ var ModerationStatus = /* @__PURE__ */ ((ModerationStatus2) => {
59
+ ModerationStatus2["PENDING"] = "pending";
60
+ ModerationStatus2["APPROVED"] = "approved";
61
+ ModerationStatus2["REJECTED"] = "rejected";
62
+ return ModerationStatus2;
63
+ })(ModerationStatus || {});
64
+
65
+ // src/utils/token-refresh.ts
66
+ var GLOBAL_KEY = "__kasuviaSdkTokenRefreshRegistry__";
67
+ function getRegistry() {
68
+ const g = globalThis;
69
+ if (!g[GLOBAL_KEY]) {
70
+ g[GLOBAL_KEY] = { handler: null, inFlight: null };
71
+ }
72
+ return g[GLOBAL_KEY];
73
+ }
74
+ function registerRefreshHandler(handler) {
75
+ getRegistry().handler = handler;
76
+ }
77
+ async function refreshAccessToken() {
78
+ const registry = getRegistry();
79
+ if (!registry.handler) return null;
80
+ if (registry.inFlight) return registry.inFlight;
81
+ registry.inFlight = registry.handler().finally(() => {
82
+ registry.inFlight = null;
83
+ });
84
+ return registry.inFlight;
85
+ }
86
+ function hasRefreshHandler() {
87
+ return getRegistry().handler !== null;
88
+ }
89
+
90
+ // src/utils/http-client.ts
91
+ var ApiError = class extends Error {
92
+ constructor(message, status, body) {
93
+ super(message);
94
+ this.name = "ApiError";
95
+ this.status = status;
96
+ this.body = body;
97
+ }
98
+ };
99
+ function extractApiErrorMessage(body, fallback) {
100
+ if (typeof body !== "object" || body === null) return fallback;
101
+ const record = body;
102
+ if (typeof record.detail === "string") return record.detail;
103
+ if (Array.isArray(record.detail) && record.detail.length > 0) {
104
+ const first = record.detail[0];
105
+ if (first && typeof first.msg === "string") {
106
+ const loc = first.loc;
107
+ const field = Array.isArray(loc) ? loc[loc.length - 1] : void 0;
108
+ return typeof field === "string" || typeof field === "number" ? `${field}: ${first.msg}` : first.msg;
109
+ }
110
+ }
111
+ const firstStringOrArrayItem = (value) => {
112
+ if (typeof value === "string") return value;
113
+ if (Array.isArray(value) && typeof value[0] === "string") return value[0];
114
+ return void 0;
115
+ };
116
+ const nonFieldMessage = firstStringOrArrayItem(record.non_field_errors);
117
+ if (nonFieldMessage) return nonFieldMessage;
118
+ for (const [key, value] of Object.entries(record)) {
119
+ if (key === "detail" || key === "non_field_errors") continue;
120
+ const message = firstStringOrArrayItem(value);
121
+ if (message) return message;
122
+ }
123
+ return fallback;
124
+ }
125
+ function storefrontHeaders(config) {
126
+ return {
127
+ "X-Storefront-Key": config.storefrontApiKey,
128
+ "X-Business-ID": config.businessId
129
+ };
130
+ }
131
+ function authHeaders(config, accessToken) {
132
+ return {
133
+ ...storefrontHeaders(config),
134
+ Authorization: `Bearer ${accessToken}`
135
+ };
136
+ }
137
+ var JSON_HEADERS = { "Content-Type": "application/json" };
138
+ async function apiRequest(url, options = {}) {
139
+ const { fallbackErrorMessage = "Request failed", ...init } = options;
140
+ const headers = new Headers(init.headers);
141
+ const hadBearerToken = headers.get("Authorization")?.startsWith("Bearer ") ?? false;
142
+ const response = await fetch(url, { ...init, headers });
143
+ if (response.status === 401 && hadBearerToken && hasRefreshHandler()) {
144
+ const freshToken = await refreshAccessToken();
145
+ if (freshToken) {
146
+ const retryHeaders = new Headers(headers);
147
+ retryHeaders.set("Authorization", `Bearer ${freshToken}`);
148
+ const retryResponse = await fetch(url, { ...init, headers: retryHeaders });
149
+ if (retryResponse.ok) {
150
+ const retryText = await retryResponse.text();
151
+ return retryText ? JSON.parse(retryText) : void 0;
152
+ }
153
+ const retryBody = await retryResponse.json().catch(() => ({}));
154
+ throw new ApiError(extractApiErrorMessage(retryBody, fallbackErrorMessage), retryResponse.status, retryBody);
155
+ }
156
+ }
157
+ if (!response.ok) {
158
+ const body = await response.json().catch(() => ({}));
159
+ throw new ApiError(extractApiErrorMessage(body, fallbackErrorMessage), response.status, body);
160
+ }
161
+ const text = await response.text();
162
+ return text ? JSON.parse(text) : void 0;
163
+ }
164
+
34
165
  // src/services/cart.service.ts
35
166
  function parseItems(items) {
36
167
  return items.map((item) => ({
@@ -38,218 +169,148 @@ function parseItems(items) {
38
169
  variant_attributes: item.variant_attributes && typeof item.variant_attributes === "string" ? JSON.parse(item.variant_attributes) : item.variant_attributes
39
170
  }));
40
171
  }
172
+ function guestHeaders(config, guestSessionId, extra) {
173
+ return { ...extra, ...storefrontHeaders(config), "X-Guest-Session": guestSessionId };
174
+ }
41
175
  async function getGuestCart(config, guestSessionId) {
42
- if (!guestSessionId) return [];
43
- try {
44
- const response = await fetch(`${config.backendUrls.store}/cart/`, {
45
- headers: {
46
- "X-Storefront-Key": config.storefrontApiKey,
47
- "X-Business-ID": config.businessId,
48
- "X-Guest-Session": guestSessionId
49
- }
50
- });
51
- if (!response.ok) return [];
52
- const data = await response.json();
53
- return parseItems(data.items || []);
54
- } catch {
55
- return [];
56
- }
176
+ if (!guestSessionId) throw new Error("Guest session ID is required to fetch guest cart");
177
+ const data = await apiRequest(`${config.backendUrls.cart}/cart/`, {
178
+ headers: guestHeaders(config, guestSessionId),
179
+ fallbackErrorMessage: "Failed to fetch guest cart"
180
+ });
181
+ return { ...data, items: parseItems(data.items || []) };
57
182
  }
58
183
  async function addToGuestCart(config, guestSessionId, item) {
59
- if (!guestSessionId) return [];
184
+ if (!guestSessionId) throw new Error("Guest session ID is required to add to guest cart");
60
185
  const payload = {
61
186
  product_id: item.product_id,
62
- product_slug: item.product_slug,
63
- product_name: item.product_name,
64
- image: item.image,
65
- variant_id: item.variant_id,
66
- variant_attributes: typeof item.variant_attributes === "string" ? item.variant_attributes : JSON.stringify(item.variant_attributes),
67
- unit_price: item.unit_price,
187
+ variant_id: item.variant_id ?? void 0,
68
188
  quantity: item.quantity,
69
- custom_measurements: item.custom_measurements,
70
- is_bespoke: item.is_bespoke
189
+ custom_measurements: item.custom_measurements ?? void 0
71
190
  };
72
- const response = await fetch(`${config.backendUrls.store}/cart/`, {
191
+ const data = await apiRequest(`${config.backendUrls.cart}/cart/`, {
73
192
  method: "POST",
74
- headers: {
75
- "Content-Type": "application/json",
76
- "X-Storefront-Key": config.storefrontApiKey,
77
- "X-Business-ID": config.businessId,
78
- "X-Guest-Session": guestSessionId
79
- },
80
- body: JSON.stringify(payload)
193
+ headers: guestHeaders(config, guestSessionId, JSON_HEADERS),
194
+ body: JSON.stringify(payload),
195
+ fallbackErrorMessage: "Failed to add to guest cart"
81
196
  });
82
- if (!response.ok) return [];
83
- const data = await response.json();
84
- return parseItems(data.items || []);
197
+ return { ...data, items: parseItems(data.items || []) };
85
198
  }
86
199
  async function updateGuestCartItemQuantity(config, guestSessionId, itemId, quantity) {
87
- if (!guestSessionId) return [];
88
- const response = await fetch(`${config.backendUrls.store}/cart/${itemId}`, {
200
+ if (!guestSessionId) throw new Error("Guest session ID is required to update guest cart item");
201
+ const data = await apiRequest(`${config.backendUrls.cart}/cart/${itemId}`, {
89
202
  method: "PATCH",
90
- headers: {
91
- "Content-Type": "application/json",
92
- "X-Storefront-Key": config.storefrontApiKey,
93
- "X-Business-ID": config.businessId,
94
- "X-Guest-Session": guestSessionId
95
- },
96
- body: JSON.stringify({ quantity })
203
+ headers: guestHeaders(config, guestSessionId, JSON_HEADERS),
204
+ body: JSON.stringify({ quantity }),
205
+ fallbackErrorMessage: "Failed to update guest cart item"
97
206
  });
98
- if (!response.ok) return [];
99
- const data = await response.json();
100
- return parseItems(data.items || []);
207
+ return { ...data, items: parseItems(data.items || []) };
101
208
  }
102
209
  async function removeFromGuestCart(config, guestSessionId, itemId) {
103
- if (!guestSessionId) return [];
104
- await fetch(`${config.backendUrls.store}/cart/${itemId}`, {
210
+ if (!guestSessionId) throw new Error("Guest session ID is required to remove from guest cart");
211
+ await apiRequest(`${config.backendUrls.cart}/cart/${itemId}`, {
105
212
  method: "DELETE",
106
- headers: {
107
- "X-Storefront-Key": config.storefrontApiKey,
108
- "X-Business-ID": config.businessId,
109
- "X-Guest-Session": guestSessionId
110
- }
213
+ headers: guestHeaders(config, guestSessionId),
214
+ fallbackErrorMessage: "Failed to remove item from guest cart"
111
215
  });
112
- return getGuestCart(config, guestSessionId);
113
216
  }
114
217
  async function clearGuestCart(config, guestSessionId) {
115
- if (!guestSessionId) return;
116
- await fetch(`${config.backendUrls.store}/cart/clear`, {
218
+ if (!guestSessionId) throw new Error("Guest session ID is required to clear guest cart");
219
+ await apiRequest(`${config.backendUrls.cart}/cart/clear`, {
117
220
  method: "DELETE",
118
- headers: {
119
- "X-Storefront-Key": config.storefrontApiKey,
120
- "X-Business-ID": config.businessId,
121
- "X-Guest-Session": guestSessionId
122
- }
221
+ headers: guestHeaders(config, guestSessionId),
222
+ fallbackErrorMessage: "Failed to clear guest cart"
123
223
  });
124
224
  }
125
225
  async function getCart(config, accessToken) {
126
226
  if (!accessToken) {
127
- return { id: "", items: [], subtotal: 0, total: 0, product_type: "physical" };
227
+ throw new Error("Access token is required to fetch authenticated cart");
128
228
  }
129
- const response = await fetch(`${config.backendUrls.store}/cart/`, {
130
- headers: {
131
- "Authorization": `Bearer ${accessToken}`,
132
- "X-Storefront-Key": config.storefrontApiKey,
133
- "X-Business-ID": config.businessId
134
- }
229
+ const data = await apiRequest(`${config.backendUrls.cart}/cart/`, {
230
+ headers: authHeaders(config, accessToken),
231
+ fallbackErrorMessage: "Failed to fetch cart"
135
232
  });
136
- if (!response.ok) throw new Error("Failed to fetch cart");
137
- const data = await response.json();
138
- const parsedItems = parseItems(data.items || []);
139
- return { ...data, items: parsedItems };
233
+ return { ...data, items: parseItems(data.items || []) };
140
234
  }
141
235
  async function addToCart(config, accessToken, item) {
142
- const response = await fetch(`${config.backendUrls.store}/cart/`, {
236
+ const payload = {
237
+ product_id: item.product_id,
238
+ variant_id: item.variant_id ?? void 0,
239
+ quantity: item.quantity,
240
+ custom_measurements: item.custom_measurements ?? void 0
241
+ };
242
+ const data = await apiRequest(`${config.backendUrls.cart}/cart/`, {
143
243
  method: "POST",
144
- headers: {
145
- "Content-Type": "application/json",
146
- "Authorization": `Bearer ${accessToken}`,
147
- "X-Storefront-Key": config.storefrontApiKey,
148
- "X-Business-ID": config.businessId
149
- },
150
- body: JSON.stringify(item)
244
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
245
+ body: JSON.stringify(payload),
246
+ fallbackErrorMessage: "Failed to add to cart"
151
247
  });
152
- if (!response.ok) throw new Error("Failed to add to cart");
153
- const data = await response.json();
154
- const parsedItems = parseItems(data.items || []);
155
- return { ...data, items: parsedItems };
248
+ return { ...data, items: parseItems(data.items || []) };
156
249
  }
157
250
  async function updateCartItem(config, accessToken, itemId, quantity) {
158
- const response = await fetch(`${config.backendUrls.store}/cart/${itemId}`, {
251
+ const data = await apiRequest(`${config.backendUrls.cart}/cart/${itemId}`, {
159
252
  method: "PATCH",
160
- headers: {
161
- "Content-Type": "application/json",
162
- "Authorization": `Bearer ${accessToken}`,
163
- "X-Storefront-Key": config.storefrontApiKey,
164
- "X-Business-ID": config.businessId
165
- },
166
- body: JSON.stringify({ quantity })
253
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
254
+ body: JSON.stringify({ quantity }),
255
+ fallbackErrorMessage: "Failed to update cart item"
167
256
  });
168
- if (!response.ok) throw new Error("Failed to update cart item");
169
- const data = await response.json();
170
- const parsedItems = parseItems(data.items || []);
171
- return { ...data, items: parsedItems };
257
+ return { ...data, items: parseItems(data.items || []) };
172
258
  }
173
259
  async function removeFromCart(config, accessToken, itemId) {
174
- await fetch(`${config.backendUrls.store}/cart/${itemId}`, {
260
+ await apiRequest(`${config.backendUrls.cart}/cart/${itemId}`, {
175
261
  method: "DELETE",
176
- headers: {
177
- "Authorization": `Bearer ${accessToken}`,
178
- "X-Storefront-Key": config.storefrontApiKey,
179
- "X-Business-ID": config.businessId
180
- }
262
+ headers: authHeaders(config, accessToken),
263
+ fallbackErrorMessage: "Failed to remove cart item"
181
264
  });
182
- return getCart(config, accessToken);
183
265
  }
184
266
  async function clearCart(config, accessToken) {
185
- await fetch(`${config.backendUrls.store}/cart/clear`, {
267
+ await apiRequest(`${config.backendUrls.cart}/cart/clear`, {
186
268
  method: "DELETE",
187
- headers: {
188
- "Authorization": `Bearer ${accessToken}`,
189
- "X-Storefront-Key": config.storefrontApiKey,
190
- "X-Business-ID": config.businessId
191
- }
269
+ headers: authHeaders(config, accessToken),
270
+ fallbackErrorMessage: "Failed to clear cart"
192
271
  });
193
272
  }
194
- async function mergeGuestCart(config, accessToken, guestSessionId) {
195
- const guestItems = guestSessionId ? await getGuestCart(config, guestSessionId) : [];
196
- if (guestItems.length === 0) {
197
- return getCart(config, accessToken);
198
- }
273
+ async function mergeGuestCart(config, accessToken, guestSessionId, guestItems) {
199
274
  const payloadItems = guestItems.map((item) => ({
200
275
  product_id: item.product_id,
201
- product_slug: item.product_slug,
202
- product_name: item.product_name,
203
- image: item.image,
204
- variant_id: item.variant_id,
205
- variant_attributes: typeof item.variant_attributes === "string" ? item.variant_attributes : JSON.stringify(item.variant_attributes ?? {}),
206
- unit_price: item.unit_price,
276
+ variant_id: item.variant_id ?? void 0,
207
277
  quantity: item.quantity,
208
- custom_measurements: typeof item.custom_measurements === "string" ? item.custom_measurements : JSON.stringify(item.custom_measurements ?? {}),
209
- is_bespoke: item.is_bespoke
278
+ custom_measurements: item.custom_measurements ?? void 0
210
279
  }));
211
- const headers = {
212
- "Content-Type": "application/json",
213
- "Authorization": `Bearer ${accessToken}`,
214
- "X-Storefront-Key": config.storefrontApiKey,
215
- "X-Business-ID": config.businessId
216
- };
280
+ const headers = { ...JSON_HEADERS, ...authHeaders(config, accessToken) };
217
281
  if (guestSessionId) {
218
282
  headers["X-Guest-Session"] = guestSessionId;
219
283
  }
220
- const response = await fetch(`${config.backendUrls.store}/cart/merge`, {
284
+ const data = await apiRequest(`${config.backendUrls.cart}/cart/merge`, {
221
285
  method: "POST",
222
286
  headers,
223
- body: JSON.stringify({ items: payloadItems })
287
+ body: JSON.stringify({ items: payloadItems }),
288
+ fallbackErrorMessage: "Failed to merge guest cart"
224
289
  });
225
- if (!response.ok) throw new Error("Failed to merge guest cart");
226
- const data = await response.json();
227
290
  return { ...data, items: parseItems(data.items || []) };
228
291
  }
229
292
  async function mergeGuestCartIfPresent(config, accessToken, guestSessionId) {
230
- try {
231
- const guestItems = await getGuestCart(config, guestSessionId);
232
- if (guestItems.length === 0) return;
233
- await mergeGuestCart(config, accessToken, guestSessionId);
234
- } catch (error) {
235
- console.warn("[Cart] Failed to merge guest cart:", error);
236
- }
237
- }
238
- function getGuestCartCount() {
239
- return 0;
293
+ const guestCart = await getGuestCart(config, guestSessionId);
294
+ if (!guestCart.items.length) return;
295
+ await mergeGuestCart(config, accessToken, guestSessionId, guestCart.items);
240
296
  }
241
- function getGuestCartSubtotal() {
242
- return 0;
243
- }
244
- function getGuestCartProductType() {
245
- return "physical";
297
+ function getGuestCartProductType(items) {
298
+ if (!items || !items.length) return null;
299
+ const types = new Set(items.map((i) => i.product_type).filter(Boolean));
300
+ if (types.size === 0) return null;
301
+ if (types.size === 1) return [...types][0] ?? null;
302
+ return "physical" /* PHYSICAL */;
246
303
  }
247
304
 
248
305
  // src/services/order.service.ts
306
+ function extractShippingAddress(raw) {
307
+ const val = raw.physical_fulfillment?.shipping_address;
308
+ return typeof val === "object" && val !== null ? val : null;
309
+ }
249
310
  function deriveCustomerName(raw) {
250
311
  if (raw.customer_name) return raw.customer_name;
251
312
  if (raw.guest_name) return raw.guest_name;
252
- const addr = typeof raw.shipping_address === "object" && raw.shipping_address !== null ? raw.shipping_address : null;
313
+ const addr = extractShippingAddress(raw);
253
314
  if (addr) {
254
315
  const full = `${addr.first_name ?? ""} ${addr.last_name ?? ""}`.trim();
255
316
  if (full) return full;
@@ -261,25 +322,123 @@ function deriveCustomerName(raw) {
261
322
  function normaliseStorefrontOrder(raw) {
262
323
  const items = (raw.items ?? []).map((item) => ({
263
324
  id: item.id,
264
- productName: item.product_name ?? item.productName ?? "",
265
- image: item.image ?? "",
266
- quantity: item.quantity ?? 1,
267
- price: item.unit_price ?? item.price ?? 0
325
+ order_id: item.order_id,
326
+ product_id: item.product_id ?? void 0,
327
+ variant_id: item.variant_id ?? void 0,
328
+ product_name: item.product_name ?? "",
329
+ quantity: item.quantity,
330
+ unit_price: item.unit_price,
331
+ variant_details: item.variant_details,
332
+ custom_measurements: item.custom_measurements,
333
+ image_url: item.image_url ?? void 0
268
334
  }));
269
- const addr = typeof raw.shipping_address === "object" && raw.shipping_address !== null ? raw.shipping_address : null;
335
+ const addr = extractShippingAddress(raw);
336
+ const customerName = deriveCustomerName(raw);
337
+ const customerEmail = raw.guest_email ?? raw.customer_email ?? "";
338
+ const physical = raw.physical_fulfillment;
339
+ if (!raw.currency) {
340
+ throw new Error(`Order ${raw.id} response is missing a currency field.`);
341
+ }
342
+ if (!raw.created_at) {
343
+ throw new Error(`Order ${raw.id} response is missing a created_at field.`);
344
+ }
345
+ if (!raw.updated_at) {
346
+ throw new Error(`Order ${raw.id} response is missing an updated_at field.`);
347
+ }
348
+ if (!raw.business_id) {
349
+ throw new Error(`Order ${raw.id} response is missing a business_id field.`);
350
+ }
270
351
  return {
271
352
  id: raw.id,
272
- display_id: raw.display_id,
353
+ display_id: raw.display_id || raw.id,
354
+ business_id: raw.business_id,
355
+ customer_id: raw.customer_id ?? void 0,
273
356
  items,
274
- total: raw.total_amount ?? raw.total ?? 0,
275
357
  status: raw.status,
276
- customerEmail: raw.guest_email ?? raw.customer_email ?? "",
277
- customerName: deriveCustomerName(raw),
278
- createdAt: raw.created_at ? new Date(raw.created_at).getTime() : Date.now(),
279
- updatedAt: raw.updated_at ? new Date(raw.updated_at).getTime() : Date.now(),
280
- deliveryMethod: raw.delivery_method,
281
- trackingNumber: raw.tracking_number,
282
- deliveryAddress: typeof raw.shipping_address === "string" ? raw.shipping_address : addr ? `${addr.street}, ${addr.city}, ${addr.state}, ${addr.country}` : void 0,
358
+ payment_status: raw.payment_status || "pending",
359
+ product_type: raw.product_type ?? void 0,
360
+ // Canonical backend fields
361
+ total_amount: raw.total_amount,
362
+ currency: raw.currency,
363
+ subtotal_amount: raw.subtotal_amount ?? void 0,
364
+ shipping_amount: raw.shipping_amount ?? void 0,
365
+ discount_amount: raw.discount_amount ?? void 0,
366
+ tax_amount: raw.tax_amount ?? void 0,
367
+ customer_name: customerName,
368
+ customer_email: customerEmail,
369
+ // REAL BUG FIXED (2026-09-23) via the REST codegen initiative: every
370
+ // field below this point used to read a flat `raw.<field>` that does
371
+ // NOT exist anywhere on the real OrderResponse - confirmed by reading
372
+ // the actual Pydantic classes, not just the generated types. They
373
+ // genuinely live nested under `physical_fulfillment` (shipping/
374
+ // tracking/carrier/pickup/surcharge/weight) or `digital_fulfillment`
375
+ // (buyer_country) - "One nested block per product type, instead of
376
+ // ~35 flat fields on OrderBase" per that schema file's own Order
377
+ // Schema Refactor comment. Every one of these was silently `undefined`
378
+ // on every real order: a customer's tracking number, carrier, delivery
379
+ // status, and shipping address never actually rendered anywhere on the
380
+ // storefront's order tracking page.
381
+ tracking_number: physical?.tracking_number ?? void 0,
382
+ delivery_status: physical?.delivery_status ?? void 0,
383
+ carrier_name: physical?.carrier_name ?? void 0,
384
+ carrier_slug: physical?.carrier_slug ?? void 0,
385
+ terminal_rate_id: physical?.terminal_rate_id ?? void 0,
386
+ guest_token: raw.guest_token ?? void 0,
387
+ guest_email: raw.guest_email ?? void 0,
388
+ guest_phone: raw.guest_phone ?? void 0,
389
+ guest_name: raw.guest_name ?? void 0,
390
+ buyer_country: raw.digital_fulfillment?.buyer_country ?? void 0,
391
+ created_at: raw.created_at,
392
+ updated_at: raw.updated_at,
393
+ // Weight discrepancy & surcharge fields (Task 2) — physical_fulfillment
394
+ shipping_surcharge_amount: physical?.shipping_surcharge_amount ?? void 0,
395
+ shipping_surcharge_reason: physical?.shipping_surcharge_reason ?? void 0,
396
+ shipping_surcharge_description: physical?.shipping_surcharge_description ?? void 0,
397
+ shipping_surcharge_applied_at: physical?.shipping_surcharge_applied_at ?? void 0,
398
+ declared_weight_kg: physical?.declared_weight_kg ?? void 0,
399
+ actual_weight_kg: physical?.actual_weight_kg ?? void 0,
400
+ // Self-pickup verification fields (Task 3) — physical_fulfillment
401
+ pickup_verification_code: physical?.pickup_verification_code ?? void 0,
402
+ pickup_verification_code_generated_at: physical?.pickup_verification_code_generated_at ?? void 0,
403
+ pickup_verification_code_used_at: physical?.pickup_verification_code_used_at ?? void 0,
404
+ pickup_verification_failed_attempts: physical?.pickup_verification_failed_attempts,
405
+ pickup_verification_locked_until: physical?.pickup_verification_locked_until ?? void 0,
406
+ pickup_qr_payload: physical?.pickup_qr_payload ?? void 0,
407
+ // Bespoke fields — real backend nests these under `bespoke_fulfillment`.
408
+ bespoke_brief: raw.bespoke_fulfillment?.brief ?? void 0,
409
+ bespoke_reference_images: raw.bespoke_fulfillment?.reference_images ?? void 0,
410
+ bespoke_measurements: raw.bespoke_fulfillment?.measurements ?? void 0,
411
+ bespoke_preferred_timeline: raw.bespoke_fulfillment?.preferred_timeline ?? void 0,
412
+ bespoke_special_instructions: raw.bespoke_fulfillment?.special_instructions ?? void 0,
413
+ bespoke_deposit_ratio: raw.bespoke_fulfillment?.deposit_ratio ?? void 0,
414
+ bespoke_deposit_amount: raw.bespoke_fulfillment?.deposit_amount ?? void 0,
415
+ bespoke_balance_amount: raw.bespoke_fulfillment?.balance_amount ?? void 0,
416
+ bespoke_status: raw.bespoke_fulfillment?.status ?? void 0,
417
+ bespoke_deliverable_submitted_at: raw.bespoke_fulfillment?.deliverable_submitted_at ?? void 0,
418
+ bespoke_deliverable_url: raw.bespoke_fulfillment?.deliverable_url ?? void 0,
419
+ bespoke_deliverable_notes: raw.bespoke_fulfillment?.deliverable_notes ?? void 0,
420
+ bespoke_buyer_approved_at: raw.bespoke_fulfillment?.buyer_approved_at ?? void 0,
421
+ bespoke_revision_count: raw.bespoke_fulfillment?.revision_count ?? 0,
422
+ bespoke_revision_notes: raw.bespoke_fulfillment?.revision_notes ?? void 0,
423
+ // SDK camelCase convenience aliases
424
+ total: raw.total_amount ?? void 0,
425
+ shippingAmount: raw.shipping_amount ?? void 0,
426
+ discountAmount: raw.discount_amount ?? void 0,
427
+ customerEmail,
428
+ customerName,
429
+ createdAt: new Date(raw.created_at).getTime(),
430
+ updatedAt: new Date(raw.updated_at).getTime(),
431
+ // REAL BUG FIXED (2026-09-23), found alongside the nesting bug above:
432
+ // `deliveryMethod` reads physical_fulfillment.shipping_method now
433
+ // (was reading a flat `delivery_method` field that never existed at
434
+ // all, anywhere - not even nested). The real value is "carrier" or
435
+ // "self_pickup" (PhysicalFulfillmentCreate.shipping_method's own
436
+ // docstring) - see the matching fix in kasuvia-storefront's
437
+ // OrderDeliveryInfo.tsx, which used to compare against the wrong
438
+ // string "pickup".
439
+ deliveryMethod: physical?.shipping_method ?? void 0,
440
+ trackingNumber: physical?.tracking_number ?? void 0,
441
+ deliveryAddress: addr ? `${addr.street}, ${addr.city}, ${addr.state}, ${addr.country}` : void 0,
283
442
  shippingAddress: addr ? {
284
443
  first_name: addr.first_name ?? "",
285
444
  last_name: addr.last_name ?? "",
@@ -287,62 +446,85 @@ function normaliseStorefrontOrder(raw) {
287
446
  street: addr.street ?? "",
288
447
  city: addr.city ?? "",
289
448
  state: addr.state ?? "",
290
- lga: addr.lga ?? "",
449
+ lga: addr.lga,
291
450
  country: addr.country ?? "",
292
- postal: addr.postal ?? ""
451
+ postal: addr.postal
293
452
  } : void 0,
294
- cancellationReason: raw.cancellation_reason,
453
+ // REAL GAP (2026-09-23), NOT fixed here: confirmed cancellation_reason
454
+ // does not exist anywhere in OrderResponse or any of its fulfillment
455
+ // sub-schemas (kasuvia-store-management's app/schemas/order.py) - the
456
+ // cancel-order endpoint accepts a `reason` in its request body but the
457
+ // backend never persists/returns it back on the order. Permanently
458
+ // undefined until the backend adds real storage for it; kept as a
459
+ // field on the public Order type rather than silently removed, since
460
+ // deleting a public SDK field is a breaking change this fix's scope
461
+ // doesn't cover.
462
+ cancellationReason: void 0,
295
463
  hasBespoke: raw.product_type === "bespoke",
296
- bespokeStage: raw.bespoke_stage,
297
- bespokeBrief: raw.bespoke_brief,
298
- deliveryStatus: raw.delivery_status,
299
- carrierName: raw.carrier_name,
300
- terminalShipmentId: raw.terminal_shipment_id,
301
- guestToken: raw.guest_token,
302
- guestEmail: raw.guest_email,
303
- guestPhone: raw.guest_phone,
304
- guestName: raw.guest_name
464
+ // REAL GAP (2026-09-23), NOT fixed here: same as cancellationReason -
465
+ // confirmed no `bespoke_stage` field exists anywhere; the real,
466
+ // equivalent concept is bespoke_fulfillment.status (already exposed
467
+ // above as `bespoke_status`). Reads that here rather than staying
468
+ // permanently undefined, since it's genuinely the same real data the
469
+ // consuming components (BespokeProgress) want to key off of.
470
+ bespokeStage: raw.bespoke_fulfillment?.status ?? void 0,
471
+ bespokeBrief: raw.bespoke_fulfillment?.brief ?? void 0,
472
+ deliveryStatus: physical?.delivery_status ?? void 0,
473
+ carrierName: physical?.carrier_name ?? void 0,
474
+ terminalShipmentId: physical?.terminal_shipment_id ?? void 0,
475
+ guestToken: raw.guest_token ?? void 0,
476
+ guestEmail: raw.guest_email ?? void 0,
477
+ guestPhone: raw.guest_phone ?? void 0,
478
+ guestName: raw.guest_name ?? void 0
305
479
  };
306
480
  }
307
- async function createOrder(config, accessToken, payload) {
308
- const response = await fetch(`${config.backendUrls.store}/orders/storefront`, {
481
+ async function createOrder(config, accessToken, payload, guestSessionId) {
482
+ const headers = { ...JSON_HEADERS, ...storefrontHeaders(config) };
483
+ if (accessToken) {
484
+ headers["Authorization"] = `Bearer ${accessToken}`;
485
+ } else if (guestSessionId) {
486
+ headers["X-Guest-Session"] = guestSessionId;
487
+ }
488
+ const data = await apiRequest(`${config.backendUrls.store}/orders/storefront`, {
489
+ method: "POST",
490
+ headers,
491
+ body: JSON.stringify(payload),
492
+ fallbackErrorMessage: "Failed to create order"
493
+ });
494
+ return normaliseStorefrontOrder(data);
495
+ }
496
+ async function createBespokeOrder(config, accessToken, payload) {
497
+ const headers = { ...JSON_HEADERS, ...storefrontHeaders(config) };
498
+ if (accessToken) headers["Authorization"] = `Bearer ${accessToken}`;
499
+ const data = await apiRequest(`${config.backendUrls.store}/orders/storefront/bespoke`, {
309
500
  method: "POST",
310
- headers: {
311
- "Content-Type": "application/json",
312
- "Authorization": `Bearer ${accessToken}`,
313
- "X-Storefront-Key": config.storefrontApiKey,
314
- "X-Business-ID": config.businessId
315
- },
316
- body: JSON.stringify(payload)
501
+ headers,
502
+ body: JSON.stringify(payload),
503
+ fallbackErrorMessage: "Failed to create bespoke order"
317
504
  });
318
- if (!response.ok) throw new Error("Failed to create order");
319
- const data = await response.json();
320
505
  return normaliseStorefrontOrder(data);
321
506
  }
322
- async function findOrder(config, params) {
323
- const queryParams = new URLSearchParams();
324
- if (params.email) queryParams.set("email", params.email);
325
- if (params.order_id) queryParams.set("order_id", params.order_id);
326
- if (params.guest_token) queryParams.set("guest_token", params.guest_token);
327
- const response = await fetch(`${config.backendUrls.store}/orders/find/?${queryParams.toString()}`, {
328
- headers: {
329
- "X-Storefront-Key": config.storefrontApiKey,
330
- "X-Business-ID": config.businessId
331
- }
507
+ async function createServiceBookingOrder(config, accessToken, payload) {
508
+ const headers = { ...JSON_HEADERS, ...storefrontHeaders(config) };
509
+ if (accessToken) headers["Authorization"] = `Bearer ${accessToken}`;
510
+ const data = await apiRequest(`${config.backendUrls.store}/orders/storefront/service`, {
511
+ method: "POST",
512
+ headers,
513
+ body: JSON.stringify(payload),
514
+ fallbackErrorMessage: "Failed to create service booking order"
332
515
  });
333
- if (!response.ok) throw new Error("Failed to find order");
334
- const data = await response.json();
335
516
  return normaliseStorefrontOrder(data);
336
517
  }
337
- async function findOrderByToken(config, token) {
338
- const response = await fetch(`${config.backendUrls.store}/orders/find/?guest_token=${token}`, {
339
- headers: {
340
- "X-Storefront-Key": config.storefrontApiKey,
341
- "X-Business-ID": config.businessId
342
- }
518
+ async function findOrder(config, params) {
519
+ if (!params.email || !params.order_id) {
520
+ throw new Error("Both email and order_id are required to find an order.");
521
+ }
522
+ const data = await apiRequest(`${config.backendUrls.store}/orders/track`, {
523
+ method: "POST",
524
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
525
+ body: JSON.stringify({ order_id: params.order_id, email: params.email }),
526
+ fallbackErrorMessage: "Failed to find order"
343
527
  });
344
- if (!response.ok) throw new Error("Failed to find order by token");
345
- const data = await response.json();
346
528
  return normaliseStorefrontOrder(data);
347
529
  }
348
530
  async function getOrders(config, accessToken, params) {
@@ -350,128 +532,262 @@ async function getOrders(config, accessToken, params) {
350
532
  if (params?.email) queryParams.set("email", params.email);
351
533
  if (params?.page) queryParams.set("page", params.page.toString());
352
534
  if (params?.page_size) queryParams.set("page_size", params.page_size.toString());
353
- const response = await fetch(`${config.backendUrls.store}/orders/?${queryParams.toString()}`, {
354
- headers: {
355
- "Authorization": `Bearer ${accessToken}`,
356
- "X-Storefront-Key": config.storefrontApiKey,
357
- "X-Business-ID": config.businessId
358
- }
535
+ const data = await apiRequest(`${config.backendUrls.store}/orders/?${queryParams.toString()}`, {
536
+ headers: authHeaders(config, accessToken),
537
+ fallbackErrorMessage: "Failed to get orders"
359
538
  });
360
- if (!response.ok) throw new Error("Failed to get orders");
361
- const data = await response.json();
362
539
  const items = Array.isArray(data) ? data : data.items ?? [];
363
540
  return items.map(normaliseStorefrontOrder);
364
541
  }
365
542
  async function cancelOrder(config, accessToken, orderId, reason) {
366
- const response = await fetch(`${config.backendUrls.store}/orders/${orderId}/cancel/`, {
543
+ const data = await apiRequest(`${config.backendUrls.store}/orders/${orderId}/cancel/`, {
367
544
  method: "POST",
368
- headers: {
369
- "Content-Type": "application/json",
370
- "Authorization": `Bearer ${accessToken}`,
371
- "X-Storefront-Key": config.storefrontApiKey,
372
- "X-Business-ID": config.businessId
373
- },
374
- body: JSON.stringify({ reason })
545
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
546
+ body: JSON.stringify({ reason }),
547
+ fallbackErrorMessage: "Failed to cancel order"
375
548
  });
376
- if (!response.ok) throw new Error("Failed to cancel order");
377
- const data = await response.json();
378
549
  return normaliseStorefrontOrder(data);
379
550
  }
380
- async function trackOrder(config, orderId, token) {
381
- const queryParams = token ? `?token=${token}` : "";
382
- const response = await fetch(`${config.backendUrls.store}/orders/track/${orderId}${queryParams}`, {
383
- headers: {
384
- "X-Storefront-Key": config.storefrontApiKey,
385
- "X-Business-ID": config.businessId
386
- }
551
+ async function trackOrder(config, orderId, accessToken) {
552
+ const data = await apiRequest(`${config.backendUrls.store}/orders/${orderId}`, {
553
+ headers: authHeaders(config, accessToken),
554
+ fallbackErrorMessage: "Failed to track order"
387
555
  });
388
- if (!response.ok) throw new Error("Failed to track order");
389
- const data = await response.json();
390
556
  return normaliseStorefrontOrder(data);
391
557
  }
558
+ async function validatePromoCode(config, code, cartTotal) {
559
+ try {
560
+ return await apiRequest(`${config.backendUrls.store}/sales/discounts/validate`, {
561
+ method: "POST",
562
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
563
+ body: JSON.stringify({ code, cart_total: cartTotal }),
564
+ fallbackErrorMessage: "Invalid promo code"
565
+ });
566
+ } catch (err) {
567
+ const message = err instanceof ApiError ? err.message : "Invalid promo code";
568
+ return { valid: false, discount_amount: 0, discount_type: "fixed", message };
569
+ }
570
+ }
571
+ async function resolveRedirectOrJson(url, fallbackErrorMessage) {
572
+ const response = await fetch(url, { redirect: "manual" });
573
+ if (response.status >= 300 && response.status < 400) {
574
+ const location = response.headers.get("location");
575
+ if (!location) {
576
+ throw new Error(`${fallbackErrorMessage}: server redirected with no destination URL.`);
577
+ }
578
+ return { type: "redirect", url: location };
579
+ }
580
+ const body = await response.json().catch(() => ({}));
581
+ if (!response.ok) {
582
+ throw new ApiError(extractDigitalDownloadError(body, fallbackErrorMessage), response.status, body);
583
+ }
584
+ return { type: "json", data: body };
585
+ }
586
+ function extractDigitalDownloadError(body, fallback) {
587
+ if (typeof body === "object" && body !== null && "detail" in body && typeof body.detail === "string") {
588
+ return body.detail;
589
+ }
590
+ return fallback;
591
+ }
592
+ async function resolveDigitalDownload(config, orderId, token) {
593
+ const result = await resolveRedirectOrJson(
594
+ `${config.backendUrls.store}/orders/${orderId}/download/${token}`,
595
+ "Failed to resolve digital download link"
596
+ );
597
+ if (result.type === "redirect") return result;
598
+ return { type: "listing", ...result.data };
599
+ }
600
+ async function resolveDigitalDownloadFile(config, orderId, token, fileId) {
601
+ const result = await resolveRedirectOrJson(
602
+ `${config.backendUrls.store}/orders/${orderId}/download/${token}/${fileId}`,
603
+ "Failed to resolve this file's download link"
604
+ );
605
+ if (result.type !== "redirect") {
606
+ throw new Error("Failed to resolve this file's download link: server did not return a redirect.");
607
+ }
608
+ return result.url;
609
+ }
392
610
 
393
- // src/services/auth.service.ts
394
- async function loginCustomer(config, payload) {
395
- const response = await fetch(`${config.backendUrls.auth}/auth/login/customer/`, {
611
+ // src/services/consultation.service.ts
612
+ async function createConsultation(config, accessToken, params) {
613
+ return apiRequest(`${config.backendUrls.crm}/consultations/`, {
396
614
  method: "POST",
397
- headers: {
398
- "Content-Type": "application/json",
399
- "X-Storefront-Key": config.storefrontApiKey,
400
- "X-Business-ID": config.businessId
401
- },
402
- body: JSON.stringify({
403
- ...payload,
404
- tenant_id: config.businessId
405
- })
615
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
616
+ body: JSON.stringify(params),
617
+ fallbackErrorMessage: "Failed to submit your request"
406
618
  });
407
- if (!response.ok) {
408
- throw new Error("Login failed");
619
+ }
620
+
621
+ // src/services/feedback.service.ts
622
+ async function createFeedback(config, accessToken, params) {
623
+ return apiRequest(`${config.backendUrls.crm}/feedback/`, {
624
+ method: "POST",
625
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
626
+ body: JSON.stringify(params),
627
+ fallbackErrorMessage: "Failed to submit your message"
628
+ });
629
+ }
630
+ async function getMyFeedback(config, accessToken, params) {
631
+ const query = new URLSearchParams();
632
+ if (params?.page) query.set("page", String(params.page));
633
+ if (params?.page_size) query.set("page_size", String(params.page_size));
634
+ return apiRequest(`${config.backendUrls.crm}/feedback/my?${query.toString()}`, {
635
+ headers: authHeaders(config, accessToken),
636
+ fallbackErrorMessage: "Failed to load your requests"
637
+ });
638
+ }
639
+
640
+ // src/utils/customer.ts
641
+ function buildFullName(firstname, lastname) {
642
+ const name = [firstname, lastname].filter(Boolean).join(" ").trim();
643
+ return name || void 0;
644
+ }
645
+ function splitFullName(fullName) {
646
+ if (!fullName?.trim()) return { firstname: "", lastname: "" };
647
+ const parts = fullName.trim().split(/\s+/);
648
+ return {
649
+ firstname: parts[0] ?? "",
650
+ lastname: parts.slice(1).join(" ")
651
+ };
652
+ }
653
+ function getCustomerDisplayName(user) {
654
+ const fromParts = buildFullName(user.firstname, user.lastname);
655
+ return fromParts || user.full_name || user.email.split("@")[0];
656
+ }
657
+ function getStorefrontUrl(businessId, customDomain) {
658
+ if (customDomain && customDomain.trim()) {
659
+ const cleanDomain = customDomain.trim().replace(/^https?:\/\//, "");
660
+ return `https://${cleanDomain}`;
409
661
  }
410
- const data = await response.json();
411
- return data;
662
+ const cleanBid = (businessId || "store").toLowerCase().trim();
663
+ return `https://${cleanBid}.kasuvia.com`;
412
664
  }
413
- async function registerCustomer(config, payload) {
414
- const response = await fetch(`${config.backendUrls.auth}/auth/register/customer/`, {
665
+
666
+ // src/services/auth.service.ts
667
+ async function loginCustomer(config, payload, guestSessionId) {
668
+ const headers = { ...JSON_HEADERS, ...storefrontHeaders(config) };
669
+ if (guestSessionId) {
670
+ headers["X-Guest-Session"] = guestSessionId;
671
+ }
672
+ return apiRequest(`${config.backendUrls.auth}/auth/login/customer/`, {
415
673
  method: "POST",
416
- headers: {
417
- "Content-Type": "application/json",
418
- "X-Storefront-Key": config.storefrontApiKey,
419
- "X-Business-ID": config.businessId
420
- },
421
- body: JSON.stringify({
422
- ...payload,
423
- tenant_id: config.businessId
424
- })
674
+ headers,
675
+ body: JSON.stringify(payload),
676
+ fallbackErrorMessage: "Login failed"
425
677
  });
426
- if (!response.ok) {
427
- throw new Error("Registration failed");
678
+ }
679
+ async function registerCustomer(config, payload, guestSessionId) {
680
+ const headers = { ...JSON_HEADERS, ...storefrontHeaders(config) };
681
+ if (guestSessionId) {
682
+ headers["X-Guest-Session"] = guestSessionId;
428
683
  }
429
- const data = await response.json();
430
- return data;
684
+ return apiRequest(`${config.backendUrls.auth}/auth/register/customer/`, {
685
+ method: "POST",
686
+ headers,
687
+ body: JSON.stringify(payload),
688
+ fallbackErrorMessage: "Registration failed"
689
+ });
431
690
  }
432
691
  async function refreshToken(config, refresh) {
433
- const response = await fetch(`${config.backendUrls.auth}/auth/token/refresh/`, {
692
+ return apiRequest(`${config.backendUrls.auth}/auth/token/refresh/`, {
434
693
  method: "POST",
435
- headers: {
436
- "Content-Type": "application/json"
437
- },
438
- body: JSON.stringify({ refresh })
694
+ headers: JSON_HEADERS,
695
+ body: JSON.stringify({ refresh }),
696
+ fallbackErrorMessage: "Token refresh failed"
439
697
  });
440
- if (!response.ok) {
441
- throw new Error("Token refresh failed");
442
- }
443
- const data = await response.json();
444
- return data;
445
698
  }
446
699
  async function logout(config, refresh) {
447
700
  const payload = refresh ? { refresh } : {};
448
701
  try {
449
- await fetch(`${config.backendUrls.auth}/auth/logout/`, {
702
+ await apiRequest(`${config.backendUrls.auth}/auth/logout/`, {
450
703
  method: "POST",
451
- headers: {
452
- "Content-Type": "application/json"
453
- },
454
- body: JSON.stringify(payload)
704
+ headers: JSON_HEADERS,
705
+ body: JSON.stringify(payload),
706
+ fallbackErrorMessage: "Server-side logout failed"
455
707
  });
456
- } catch {
708
+ } catch (err) {
709
+ if (err instanceof ApiError) {
710
+ console.error(`[auth.service] Server-side logout failed with status ${err.status}; local session will still be cleared.`);
711
+ } else {
712
+ console.error("[auth.service] Server-side logout request failed; local session will still be cleared.", err);
713
+ }
457
714
  }
458
715
  }
459
716
  async function verifySocialToken(config, provider, token) {
460
- const response = await fetch(`${config.backendUrls.auth}/auth/social/verify/${provider}/`, {
717
+ return apiRequest(`${config.backendUrls.auth}/auth/social/verify/${provider}/`, {
461
718
  method: "POST",
462
- headers: {
463
- "Content-Type": "application/json"
464
- },
465
- body: JSON.stringify({
466
- id_token: token,
467
- storefront_key: config.storefrontApiKey
468
- })
719
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
720
+ body: JSON.stringify({ id_token: token }),
721
+ fallbackErrorMessage: "Social token verification failed"
469
722
  });
470
- if (!response.ok) {
471
- throw new Error("Social token verification failed");
723
+ }
724
+ async function requestCustomerOtp(config, payload) {
725
+ await apiRequest(`${config.backendUrls.auth}/auth/login/customer/passwordless/request/`, {
726
+ method: "POST",
727
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
728
+ body: JSON.stringify(payload),
729
+ fallbackErrorMessage: "Failed to send login code"
730
+ });
731
+ }
732
+ async function verifyCustomerOtp(config, payload, guestSessionId) {
733
+ const headers = { ...JSON_HEADERS, ...storefrontHeaders(config) };
734
+ if (guestSessionId) {
735
+ headers["X-Guest-Session"] = guestSessionId;
736
+ }
737
+ return apiRequest(`${config.backendUrls.auth}/auth/login/customer/passwordless/verify/`, {
738
+ method: "POST",
739
+ headers,
740
+ body: JSON.stringify(payload),
741
+ fallbackErrorMessage: "Invalid or expired code"
742
+ });
743
+ }
744
+ async function forgotPasswordCustomer(config, payload) {
745
+ return apiRequest(`${config.backendUrls.auth}/auth/forgot-password/customer/`, {
746
+ method: "POST",
747
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
748
+ body: JSON.stringify(payload),
749
+ fallbackErrorMessage: "Failed to request password reset"
750
+ });
751
+ }
752
+ async function resetPasswordCustomer(config, payload) {
753
+ return apiRequest(`${config.backendUrls.auth}/auth/reset-password/customer/`, {
754
+ method: "POST",
755
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
756
+ body: JSON.stringify(payload),
757
+ fallbackErrorMessage: "Invalid or expired reset code"
758
+ });
759
+ }
760
+ async function deactivateCustomerAccount(config, accessToken, payload) {
761
+ try {
762
+ const data = await apiRequest(`${config.backendUrls.auth}/auth/customer/deactivate/`, {
763
+ method: "POST",
764
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
765
+ body: JSON.stringify(payload || {}),
766
+ fallbackErrorMessage: "Failed to deactivate account"
767
+ });
768
+ return { status: "success", detail: data.detail };
769
+ } catch (err) {
770
+ if (err instanceof ApiError && err.status === 409) {
771
+ return { status: "confirmation_required", data: err.body };
772
+ }
773
+ throw err;
472
774
  }
473
- const data = await response.json();
474
- return data;
775
+ }
776
+ async function requestCustomerReactivation(config, payload) {
777
+ return apiRequest(`${config.backendUrls.auth}/auth/customer/reactivate/`, {
778
+ method: "POST",
779
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
780
+ body: JSON.stringify(payload),
781
+ fallbackErrorMessage: "Failed to request reactivation"
782
+ });
783
+ }
784
+ async function verifyCustomerReactivation(config, payload) {
785
+ return apiRequest(`${config.backendUrls.auth}/auth/customer/reactivate/verify/`, {
786
+ method: "POST",
787
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
788
+ body: JSON.stringify(payload),
789
+ fallbackErrorMessage: "Invalid or expired reactivation code"
790
+ });
475
791
  }
476
792
  function parseJwtPayload(token) {
477
793
  try {
@@ -526,70 +842,94 @@ function buildCustomerFromClaims(claims) {
526
842
  }
527
843
  async function getMe(config, accessToken) {
528
844
  if (!config || !accessToken) {
529
- const response2 = await fetch("/api/auth/me", { credentials: "include" });
530
- if (!response2.ok) throw new Error("Failed to fetch profile");
531
- const data2 = await response2.json();
532
- return data2.user;
533
- }
534
- const response = await fetch(`${config.backendUrls.auth}/auth/profile/`, {
535
- headers: {
536
- "Authorization": `Bearer ${accessToken}`
537
- }
538
- });
539
- if (!response.ok) {
540
- throw new Error("Failed to fetch profile");
845
+ const data2 = await apiRequest("/api/auth/me", {
846
+ credentials: "include",
847
+ fallbackErrorMessage: "Failed to fetch profile"
848
+ });
849
+ return mapAuthUserToCustomer(data2.user);
541
850
  }
542
- const data = await response.json();
851
+ const data = await apiRequest(`${config.backendUrls.auth}/auth/profile/`, {
852
+ headers: authHeaders(config, accessToken),
853
+ fallbackErrorMessage: "Failed to fetch profile"
854
+ });
543
855
  return mapAuthUserToCustomer(data);
544
856
  }
545
857
  async function getAuthUser(config, accessToken) {
546
858
  if (!config || !accessToken) {
547
- const response2 = await fetch("/api/auth/me", { credentials: "include" });
548
- if (!response2.ok) throw new Error("Failed to fetch auth user");
549
- const data2 = await response2.json();
550
- return data2.user;
551
- }
552
- const response = await fetch(`${config.backendUrls.auth}/auth/profile/`, {
553
- headers: {
554
- "Authorization": `Bearer ${accessToken}`
555
- }
556
- });
557
- if (!response.ok) {
558
- throw new Error("Failed to fetch auth user");
859
+ const data = await apiRequest("/api/auth/me", {
860
+ credentials: "include",
861
+ fallbackErrorMessage: "Failed to fetch auth user"
862
+ });
863
+ return data.user;
559
864
  }
560
- const data = await response.json();
561
- return data;
865
+ return apiRequest(`${config.backendUrls.auth}/auth/profile/`, {
866
+ headers: authHeaders(config, accessToken),
867
+ fallbackErrorMessage: "Failed to fetch auth user"
868
+ });
562
869
  }
563
870
  async function updateProfile(configOrPayload, accessTokenOrPayload, payloadParam) {
564
871
  const isArg1Config = typeof configOrPayload === "object" && configOrPayload !== null && "businessId" in configOrPayload;
565
872
  if (!isArg1Config) {
566
873
  const payload2 = configOrPayload;
567
- const response2 = await fetch("/api/auth/profile", {
874
+ return apiRequest("/api/auth/profile", {
568
875
  method: "PUT",
569
- headers: { "Content-Type": "application/json" },
876
+ headers: JSON_HEADERS,
570
877
  credentials: "include",
571
- body: JSON.stringify(payload2)
878
+ body: JSON.stringify(payload2),
879
+ fallbackErrorMessage: "Failed to update profile"
572
880
  });
573
- if (!response2.ok) throw new Error("Failed to update profile");
574
- return response2.json();
575
881
  }
576
882
  const config = configOrPayload;
577
883
  const accessToken = accessTokenOrPayload;
578
884
  const payload = payloadParam;
579
- const response = await fetch(`${config.backendUrls.auth}/auth/profile/`, {
885
+ const data = await apiRequest(`${config.backendUrls.auth}/auth/profile/`, {
580
886
  method: "PUT",
581
- headers: {
582
- "Content-Type": "application/json",
583
- "Authorization": `Bearer ${accessToken}`
584
- },
585
- body: JSON.stringify(payload)
887
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
888
+ body: JSON.stringify(payload),
889
+ fallbackErrorMessage: "Failed to update profile"
586
890
  });
587
- if (!response.ok) {
588
- throw new Error("Failed to update profile");
589
- }
590
- const data = await response.json();
591
891
  return mapAuthUserToCustomer(data);
592
892
  }
893
+ async function changePassword(config, accessToken, payload) {
894
+ return apiRequest(`${config.backendUrls.auth}/auth/change-password/`, {
895
+ method: "POST",
896
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
897
+ body: JSON.stringify(payload),
898
+ fallbackErrorMessage: "Failed to change password"
899
+ });
900
+ }
901
+ async function getStorefrontBusinessInfo(config) {
902
+ return apiRequest(`${config.backendUrls.auth}/auth/storefront/business-info/`, {
903
+ headers: storefrontHeaders(config),
904
+ fallbackErrorMessage: "Failed to fetch business info"
905
+ });
906
+ }
907
+ async function getPresignedUploadUrl(config, accessToken, params) {
908
+ const query = new URLSearchParams(
909
+ Object.entries(params).filter(([, v]) => v !== void 0)
910
+ );
911
+ return apiRequest(
912
+ `${config.backendUrls.auth}/auth/media/presigned-url/?${query.toString()}`,
913
+ {
914
+ method: "POST",
915
+ headers: { Authorization: `Bearer ${accessToken}` },
916
+ fallbackErrorMessage: "Failed to get presigned upload URL"
917
+ }
918
+ );
919
+ }
920
+ async function confirmMediaUpload(config, accessToken, params) {
921
+ const query = new URLSearchParams(
922
+ Object.entries(params).filter(([, v]) => v !== void 0)
923
+ );
924
+ return apiRequest(
925
+ `${config.backendUrls.auth}/auth/media/confirm-upload/?${query.toString()}`,
926
+ {
927
+ method: "POST",
928
+ headers: { Authorization: `Bearer ${accessToken}` },
929
+ fallbackErrorMessage: "Failed to confirm upload"
930
+ }
931
+ );
932
+ }
593
933
  async function silentRefresh(config, refresh) {
594
934
  try {
595
935
  const data = await refreshToken(config, refresh);
@@ -600,25 +940,11 @@ async function silentRefresh(config, refresh) {
600
940
  const user = buildCustomerFromClaims(claims);
601
941
  const provider = user.provider;
602
942
  return { user, provider, newAccessToken: data.access, newRefreshToken: data.refresh };
603
- } catch {
943
+ } catch (err) {
944
+ console.error("[auth.service] Silent token refresh failed:", err);
604
945
  return null;
605
946
  }
606
947
  }
607
- function buildFullName(firstname, lastname) {
608
- if (firstname && lastname) return `${firstname} ${lastname}`;
609
- return firstname || lastname || "";
610
- }
611
- function splitFullName(fullName) {
612
- if (!fullName) return { firstname: "", lastname: "" };
613
- const parts = fullName.trim().split(/\s+/);
614
- if (parts.length === 1) {
615
- return { firstname: parts[0], lastname: "" };
616
- }
617
- return {
618
- firstname: parts[0],
619
- lastname: parts.slice(1).join(" ")
620
- };
621
- }
622
948
  function storeTokens(accessToken, refreshToken2) {
623
949
  if (typeof window !== "undefined") {
624
950
  localStorage.setItem(STORAGE_KEYS.ACCESS_TOKEN, accessToken);
@@ -640,1061 +966,369 @@ function getStoredTokens() {
640
966
  refreshToken: localStorage.getItem(STORAGE_KEYS.REFRESH_TOKEN)
641
967
  };
642
968
  }
969
+ async function getCustomerAddresses(config, accessToken) {
970
+ const data = await apiRequest(`${config.backendUrls.auth}/auth/profile/addresses/`, {
971
+ headers: authHeaders(config, accessToken),
972
+ fallbackErrorMessage: "Failed to fetch saved addresses"
973
+ });
974
+ return data.results;
975
+ }
976
+ async function createCustomerAddress(config, accessToken, payload) {
977
+ return apiRequest(`${config.backendUrls.auth}/auth/profile/addresses/`, {
978
+ method: "POST",
979
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
980
+ body: JSON.stringify(payload),
981
+ fallbackErrorMessage: "Failed to save address"
982
+ });
983
+ }
984
+ async function updateCustomerAddress(config, accessToken, addressId, payload) {
985
+ return apiRequest(`${config.backendUrls.auth}/auth/profile/addresses/${addressId}/`, {
986
+ method: "PATCH",
987
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
988
+ body: JSON.stringify(payload),
989
+ fallbackErrorMessage: "Failed to update address"
990
+ });
991
+ }
992
+ async function deleteCustomerAddress(config, accessToken, addressId) {
993
+ await apiRequest(`${config.backendUrls.auth}/auth/profile/addresses/${addressId}/`, {
994
+ method: "DELETE",
995
+ headers: authHeaders(config, accessToken),
996
+ fallbackErrorMessage: "Failed to delete address"
997
+ });
998
+ }
643
999
 
644
- // src/services/product.service.ts
1000
+ // src/utils/graphql-url.ts
645
1001
  import { GraphQLClient } from "graphql-request";
646
- function createGraphQLClient(config) {
1002
+ function resolveGraphQLEndpoint(url) {
1003
+ if (typeof window === "undefined") return url;
1004
+ try {
1005
+ new URL(url);
1006
+ return url;
1007
+ } catch {
1008
+ return `${window.location.origin}${url}`;
1009
+ }
1010
+ }
1011
+ function createGraphQLClient(config, accessToken) {
647
1012
  const headers = {
648
1013
  "Content-Type": "application/json",
649
1014
  "Accept": "application/json",
650
1015
  "X-Storefront-Key": config.storefrontApiKey,
651
1016
  "X-Business-ID": config.businessId
652
1017
  };
653
- return new GraphQLClient(`${config.backendUrls.store}/graphql`, { headers });
654
- }
655
- var GET_STOREFRONT_PRODUCTS = `
656
- query GetStorefrontProducts($page: Int!, $pageSize: Int!, $collectionSlug: String, $search: String) {
657
- storefront_products(page: $page, pageSize: $pageSize, collectionSlug: $collectionSlug, search: $search) {
658
- items {
659
- id
660
- business_id
661
- name
662
- slug
663
- description
664
- brand
665
- product_type
666
- requires_shipping
667
- is_consumable
668
- is_bespoke
669
- base_price
670
- currency
671
- images
672
- tags
673
- custom_fields { label unit required }
674
- is_published
675
- is_featured
676
- is_popular
677
- is_new
678
- is_temporary
679
- digital_file_key
680
- download_limit
681
- download_expires_at
682
- weight_kg
683
- length_cm
684
- width_cm
685
- height_cm
686
- created_at
687
- updated_at
688
- variants {
689
- id
690
- product_id
691
- sku
692
- attributes
693
- price
694
- images
695
- weight_kg
696
- is_active
697
- inventory {
698
- id
699
- product_id
700
- variant_id
701
- quantity
702
- reserved
703
- reorder_point
704
- reorder_quantity
705
- track_quantity
706
- allow_backorder
707
- available
708
- is_in_stock
709
- is_low_stock
710
- updated_at
711
- }
712
- created_at
713
- updated_at
714
- }
715
- collections {
716
- id
717
- business_id
718
- name
719
- slug
720
- description
721
- image
722
- collection_type
723
- rules
724
- status
725
- sort_order
726
- created_at
727
- updated_at
728
- }
729
- }
730
- total
731
- page
732
- page_size
733
- total_pages
734
- }
735
- }
736
- `;
737
- var GET_STOREFRONT_PRODUCT_BY_SLUG = `
738
- query GetStorefrontProductBySlug($slug: String!) {
739
- storefront_product_by_slug(slug: $slug) {
740
- id
741
- business_id
742
- name
743
- slug
744
- description
745
- brand
746
- product_type
747
- requires_shipping
748
- is_consumable
749
- is_bespoke
750
- base_price
751
- currency
752
- images
753
- tags
754
- custom_fields { label unit required }
755
- is_published
756
- is_featured
757
- is_popular
758
- is_new
759
- is_temporary
760
- digital_file_key
761
- download_limit
762
- download_expires_at
763
- weight_kg
764
- length_cm
765
- width_cm
766
- height_cm
767
- created_at
768
- updated_at
769
- variants {
770
- id
771
- product_id
772
- sku
773
- attributes
774
- price
775
- images
776
- weight_kg
777
- is_active
778
- inventory {
779
- id
780
- product_id
781
- variant_id
782
- quantity
783
- reserved
784
- reorder_point
785
- reorder_quantity
786
- track_quantity
787
- allow_backorder
788
- available
789
- is_in_stock
790
- is_low_stock
791
- updated_at
792
- }
793
- created_at
794
- updated_at
795
- }
796
- collections {
797
- id
798
- business_id
799
- name
800
- slug
801
- description
802
- image
803
- collection_type
804
- rules
805
- status
806
- sort_order
807
- created_at
808
- updated_at
809
- }
810
- }
811
- }
812
- `;
813
- var GET_STOREFRONT_LATEST = `
814
- query GetStorefrontLatest($page: Int!, $pageSize: Int!) {
815
- storefront_latest(page: $page, pageSize: $pageSize) {
816
- items {
817
- id
818
- business_id
819
- name
820
- slug
821
- description
822
- brand
823
- product_type
824
- requires_shipping
825
- is_consumable
826
- is_bespoke
827
- base_price
828
- currency
829
- images
830
- tags
831
- custom_fields { label unit required }
832
- is_published
833
- is_featured
834
- is_popular
835
- is_new
836
- is_temporary
837
- digital_file_key
838
- download_limit
839
- download_expires_at
840
- weight_kg
841
- length_cm
842
- width_cm
843
- height_cm
844
- created_at
845
- updated_at
846
- variants {
847
- id
848
- product_id
849
- sku
850
- attributes
851
- price
852
- images
853
- weight_kg
854
- is_active
855
- inventory {
856
- id
857
- product_id
858
- variant_id
859
- quantity
860
- reserved
861
- reorder_point
862
- reorder_quantity
863
- track_quantity
864
- allow_backorder
865
- available
866
- is_in_stock
867
- is_low_stock
868
- updated_at
869
- }
870
- created_at
871
- updated_at
872
- }
873
- collections {
874
- id
875
- business_id
876
- name
877
- slug
878
- description
879
- image
880
- collection_type
881
- rules
882
- status
883
- sort_order
884
- created_at
885
- updated_at
886
- }
887
- }
888
- total
889
- page
890
- page_size
891
- total_pages
892
- }
893
- }
894
- `;
895
- var GET_STOREFRONT_POPULAR = `
896
- query GetStorefrontPopular($page: Int!, $pageSize: Int!) {
897
- storefront_popular(page: $page, pageSize: $pageSize) {
898
- items {
899
- id
900
- business_id
901
- name
902
- slug
903
- description
904
- brand
905
- product_type
906
- requires_shipping
907
- is_consumable
908
- is_bespoke
909
- base_price
910
- currency
911
- images
912
- tags
913
- custom_fields { label unit required }
914
- is_published
915
- is_featured
916
- is_popular
917
- is_new
918
- is_temporary
919
- digital_file_key
920
- download_limit
921
- download_expires_at
922
- weight_kg
923
- length_cm
924
- width_cm
925
- height_cm
926
- created_at
927
- updated_at
928
- variants {
929
- id
930
- product_id
931
- sku
932
- attributes
933
- price
934
- images
935
- weight_kg
936
- is_active
937
- inventory {
938
- id
939
- product_id
940
- variant_id
941
- quantity
942
- reserved
943
- reorder_point
944
- reorder_quantity
945
- track_quantity
946
- allow_backorder
947
- available
948
- is_in_stock
949
- is_low_stock
950
- updated_at
951
- }
952
- created_at
953
- updated_at
954
- }
955
- collections {
956
- id
957
- business_id
958
- name
959
- slug
960
- description
961
- image
962
- collection_type
963
- rules
964
- status
965
- sort_order
966
- created_at
967
- updated_at
968
- }
969
- }
970
- total
971
- page
972
- page_size
973
- total_pages
974
- }
975
- }
976
- `;
977
- var GET_STOREFRONT_FEATURED = `
978
- query GetStorefrontFeatured($page: Int!, $pageSize: Int!) {
979
- storefront_featured(page: $page, pageSize: $pageSize) {
980
- items {
981
- id
982
- business_id
983
- name
984
- slug
985
- description
986
- brand
987
- product_type
988
- requires_shipping
989
- is_consumable
990
- is_bespoke
991
- base_price
992
- currency
993
- images
994
- tags
995
- custom_fields { label unit required }
996
- is_published
997
- is_featured
998
- is_popular
999
- is_new
1000
- is_temporary
1001
- digital_file_key
1002
- download_limit
1003
- download_expires_at
1004
- weight_kg
1005
- length_cm
1006
- width_cm
1007
- height_cm
1008
- created_at
1009
- updated_at
1010
- variants {
1011
- id
1012
- product_id
1013
- sku
1014
- attributes
1015
- price
1016
- images
1017
- weight_kg
1018
- is_active
1019
- inventory {
1020
- id
1021
- product_id
1022
- variant_id
1023
- quantity
1024
- reserved
1025
- reorder_point
1026
- reorder_quantity
1027
- track_quantity
1028
- allow_backorder
1029
- available
1030
- is_in_stock
1031
- is_low_stock
1032
- updated_at
1033
- }
1034
- created_at
1035
- updated_at
1036
- }
1037
- collections {
1038
- id
1039
- business_id
1040
- name
1041
- slug
1042
- description
1043
- image
1044
- collection_type
1045
- rules
1046
- status
1047
- sort_order
1048
- created_at
1049
- updated_at
1050
- }
1051
- }
1052
- total
1053
- page
1054
- page_size
1055
- total_pages
1056
- }
1057
- }
1058
- `;
1059
- var GET_STOREFRONT_NEW = `
1060
- query GetStorefrontNew($page: Int!, $pageSize: Int!) {
1061
- storefront_new(page: $page, pageSize: $pageSize) {
1062
- items {
1063
- id
1064
- business_id
1065
- name
1066
- slug
1067
- description
1068
- brand
1069
- product_type
1070
- requires_shipping
1071
- is_consumable
1072
- is_bespoke
1073
- base_price
1074
- currency
1075
- sale_price
1076
- sale_start
1077
- sale_end
1078
- images
1079
- tags
1080
- custom_fields { label unit required }
1081
- is_published
1082
- is_featured
1083
- is_popular
1084
- is_new
1085
- is_temporary
1086
- digital_file_key
1087
- download_limit
1088
- download_expires_at
1089
- weight_kg
1090
- length_cm
1091
- width_cm
1092
- height_cm
1093
- created_at
1094
- updated_at
1095
- variants {
1096
- id
1097
- product_id
1098
- sku
1099
- attributes
1100
- price
1101
- images
1102
- weight_kg
1103
- is_active
1104
- inventory {
1105
- id
1106
- product_id
1107
- variant_id
1108
- quantity
1109
- reserved
1110
- reorder_point
1111
- reorder_quantity
1112
- track_quantity
1113
- allow_backorder
1114
- available
1115
- is_in_stock
1116
- is_low_stock
1117
- updated_at
1118
- }
1119
- created_at
1120
- updated_at
1121
- }
1122
- collections {
1123
- id
1124
- business_id
1125
- name
1126
- slug
1127
- description
1128
- image
1129
- collection_type
1130
- rules
1131
- status
1132
- sort_order
1133
- created_at
1134
- updated_at
1135
- }
1136
- }
1137
- total
1138
- page
1139
- page_size
1140
- total_pages
1141
- }
1142
- }
1143
- `;
1144
- var GET_STOREFRONT_ON_SALE = `
1145
- query GetStorefrontOnSale($page: Int!, $pageSize: Int!) {
1146
- storefront_on_sale(page: $page, pageSize: $pageSize) {
1147
- items {
1148
- id
1149
- business_id
1150
- name
1151
- slug
1152
- description
1153
- brand
1154
- product_type
1155
- requires_shipping
1156
- is_consumable
1157
- is_bespoke
1158
- base_price
1159
- currency
1160
- sale_price
1161
- sale_start
1162
- sale_end
1163
- images
1164
- tags
1165
- custom_fields { label unit required }
1166
- is_published
1167
- is_featured
1168
- is_popular
1169
- is_new
1170
- is_temporary
1171
- digital_file_key
1172
- download_limit
1173
- download_expires_at
1174
- weight_kg
1175
- length_cm
1176
- width_cm
1177
- height_cm
1178
- created_at
1179
- updated_at
1180
- variants {
1181
- id
1182
- product_id
1183
- sku
1184
- attributes
1185
- price
1186
- images
1187
- weight_kg
1188
- is_active
1189
- inventory {
1190
- id
1191
- product_id
1192
- variant_id
1193
- quantity
1194
- reserved
1195
- reorder_point
1196
- reorder_quantity
1197
- track_quantity
1198
- allow_backorder
1199
- available
1200
- is_in_stock
1201
- is_low_stock
1202
- updated_at
1203
- }
1204
- created_at
1205
- updated_at
1206
- }
1207
- collections {
1208
- id
1209
- business_id
1210
- name
1211
- slug
1212
- description
1213
- image
1214
- collection_type
1215
- rules
1216
- status
1217
- sort_order
1218
- created_at
1219
- updated_at
1220
- }
1221
- }
1222
- total
1223
- page
1224
- page_size
1225
- total_pages
1226
- }
1227
- }
1228
- `;
1229
- var GET_STOREFRONT_LOW_STOCK = `
1230
- query GetStorefrontLowStock($page: Int!, $pageSize: Int!) {
1231
- storefront_low_stock(page: $page, pageSize: $pageSize) {
1232
- items {
1233
- id
1234
- business_id
1235
- name
1236
- slug
1237
- description
1238
- brand
1239
- product_type
1240
- requires_shipping
1241
- is_consumable
1242
- is_bespoke
1243
- base_price
1244
- currency
1245
- sale_price
1246
- sale_start
1247
- sale_end
1248
- images
1249
- tags
1250
- custom_fields { label unit required }
1251
- is_published
1252
- is_featured
1253
- is_popular
1254
- is_new
1255
- is_temporary
1256
- digital_file_key
1257
- download_limit
1258
- download_expires_at
1259
- weight_kg
1260
- length_cm
1261
- width_cm
1262
- height_cm
1263
- created_at
1264
- updated_at
1265
- variants {
1266
- id
1267
- product_id
1268
- sku
1269
- attributes
1270
- price
1271
- images
1272
- weight_kg
1273
- is_active
1274
- inventory {
1275
- id
1276
- product_id
1277
- variant_id
1278
- quantity
1279
- reserved
1280
- reorder_point
1281
- reorder_quantity
1282
- track_quantity
1283
- allow_backorder
1284
- available
1285
- is_in_stock
1286
- is_low_stock
1287
- updated_at
1288
- }
1289
- created_at
1290
- updated_at
1291
- }
1292
- collections {
1293
- id
1294
- business_id
1295
- name
1296
- slug
1297
- description
1298
- image
1299
- collection_type
1300
- rules
1301
- status
1302
- sort_order
1303
- created_at
1304
- updated_at
1305
- }
1306
- }
1307
- total
1308
- page
1309
- page_size
1310
- total_pages
1311
- }
1018
+ if (accessToken) {
1019
+ headers["Authorization"] = `Bearer ${accessToken}`;
1312
1020
  }
1313
- `;
1314
- var GET_STOREFRONT_RECOMMENDED = `
1315
- query GetStorefrontRecommended($page: Int!, $pageSize: Int!, $excludeProductId: String) {
1316
- storefront_recommended(page: $page, pageSize: $pageSize, excludeProductId: $excludeProductId) {
1317
- items {
1318
- id
1319
- business_id
1320
- name
1321
- slug
1322
- description
1323
- brand
1324
- product_type
1325
- requires_shipping
1326
- is_consumable
1327
- is_bespoke
1328
- base_price
1329
- currency
1330
- images
1331
- tags
1332
- custom_fields { label unit required }
1333
- is_published
1334
- is_featured
1335
- is_popular
1336
- is_new
1337
- is_temporary
1338
- digital_file_key
1339
- download_limit
1340
- download_expires_at
1341
- weight_kg
1342
- length_cm
1343
- width_cm
1344
- height_cm
1345
- created_at
1346
- updated_at
1347
- variants {
1348
- id
1349
- product_id
1350
- sku
1351
- attributes
1352
- price
1353
- images
1354
- weight_kg
1355
- is_active
1356
- inventory {
1357
- id
1358
- product_id
1359
- variant_id
1360
- quantity
1361
- reserved
1362
- reorder_point
1363
- reorder_quantity
1364
- track_quantity
1365
- allow_backorder
1366
- available
1367
- is_in_stock
1368
- is_low_stock
1369
- updated_at
1370
- }
1371
- created_at
1372
- updated_at
1373
- }
1374
- collections {
1375
- id
1376
- business_id
1377
- name
1378
- slug
1379
- description
1380
- image
1381
- collection_type
1382
- rules
1383
- status
1384
- sort_order
1385
- created_at
1386
- updated_at
1387
- }
1388
- }
1389
- total
1390
- page
1391
- page_size
1392
- total_pages
1021
+ return new GraphQLClient(resolveGraphQLEndpoint(`${config.backendUrls.store}/graphql`), { headers });
1022
+ }
1023
+
1024
+ // src/generated/graphql.ts
1025
+ var GetStorefrontCollectionsDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "GetStorefrontCollections" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "page" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Int" } } } }, { "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "pageSize" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Int" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "storefront_collections" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "page" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "page" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "pageSize" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "pageSize" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "items" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "FragmentSpread", "name": { "kind": "Name", "value": "CollectionFields" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "total" } }, { "kind": "Field", "name": { "kind": "Name", "value": "page" } }, { "kind": "Field", "name": { "kind": "Name", "value": "page_size" } }, { "kind": "Field", "name": { "kind": "Name", "value": "total_pages" } }] } }] } }, { "kind": "FragmentDefinition", "name": { "kind": "Name", "value": "CollectionFields" }, "typeCondition": { "kind": "NamedType", "name": { "kind": "Name", "value": "Collection" } }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "business_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "image_url" } }, { "kind": "Field", "name": { "kind": "Name", "value": "collection_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "rules" } }, { "kind": "Field", "name": { "kind": "Name", "value": "status" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sort_order" } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }] };
1026
+ var GetStorefrontCollectionBySlugDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "GetStorefrontCollectionBySlug" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "slug" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "String" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "storefront_collection_by_slug" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "slug" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "slug" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "FragmentSpread", "name": { "kind": "Name", "value": "CollectionFields" } }] } }] } }, { "kind": "FragmentDefinition", "name": { "kind": "Name", "value": "CollectionFields" }, "typeCondition": { "kind": "NamedType", "name": { "kind": "Name", "value": "Collection" } }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "business_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "image_url" } }, { "kind": "Field", "name": { "kind": "Name", "value": "collection_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "rules" } }, { "kind": "Field", "name": { "kind": "Name", "value": "status" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sort_order" } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }] };
1027
+ var GetStorefrontProductsDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "GetStorefrontProducts" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "page" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Int" } } } }, { "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "pageSize" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Int" } } } }, { "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "collectionSlug" } }, "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "String" } } }, { "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "search" } }, "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "String" } } }, { "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "productType" } }, "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "String" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "storefront_products" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "page" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "page" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "pageSize" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "pageSize" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "collectionSlug" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "collectionSlug" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "search" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "search" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "productType" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "productType" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "items" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "FragmentSpread", "name": { "kind": "Name", "value": "ProductFields" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "total" } }, { "kind": "Field", "name": { "kind": "Name", "value": "page" } }, { "kind": "Field", "name": { "kind": "Name", "value": "page_size" } }, { "kind": "Field", "name": { "kind": "Name", "value": "total_pages" } }] } }] } }, { "kind": "FragmentDefinition", "name": { "kind": "Name", "value": "ProductFields" }, "typeCondition": { "kind": "NamedType", "name": { "kind": "Name", "value": "Product" } }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "business_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "brand" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "requires_shipping" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_consumable" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_bespoke" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_physical" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_non_returnable" } }, { "kind": "Field", "name": { "kind": "Name", "value": "deposit_percentage" } }, { "kind": "Field", "name": { "kind": "Name", "value": "base_price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "currency" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_start" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_end" } }, { "kind": "Field", "name": { "kind": "Name", "value": "images" } }, { "kind": "Field", "name": { "kind": "Name", "value": "tags" } }, { "kind": "Field", "name": { "kind": "Name", "value": "custom_fields" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "label" } }, { "kind": "Field", "name": { "kind": "Name", "value": "unit" } }, { "kind": "Field", "name": { "kind": "Name", "value": "required" } }, { "kind": "Field", "name": { "kind": "Name", "value": "field_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "options" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_published" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_featured" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_popular" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_new" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_temporary" } }, { "kind": "Field", "name": { "kind": "Name", "value": "average_rating" } }, { "kind": "Field", "name": { "kind": "Name", "value": "rating_count" } }, { "kind": "Field", "name": { "kind": "Name", "value": "source_channel" } }, { "kind": "Field", "name": { "kind": "Name", "value": "source_channel_ref_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_duration_minutes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_location_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_coverage_radius_km" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_requires_consultation" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_max_capacity_per_slot" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_buffer_minutes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_is_recurring" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_recurring_interval" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_project_based" } }, { "kind": "Field", "name": { "kind": "Name", "value": "digital_files" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "file_key" } }, { "kind": "Field", "name": { "kind": "Name", "value": "display_name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sort_order" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "download_limit" } }, { "kind": "Field", "name": { "kind": "Name", "value": "download_expires_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "weight_kg" } }, { "kind": "Field", "name": { "kind": "Name", "value": "length_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "width_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "height_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "variants" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sku" } }, { "kind": "Field", "name": { "kind": "Name", "value": "attributes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "images" } }, { "kind": "Field", "name": { "kind": "Name", "value": "weight_kg" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_active" } }, { "kind": "Field", "name": { "kind": "Name", "value": "inventory" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "variant_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reserved" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reorder_point" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reorder_quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "track_quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "allow_backorder" } }, { "kind": "Field", "name": { "kind": "Name", "value": "available" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_in_stock" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_low_stock" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "collections" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "business_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "image_url" } }, { "kind": "Field", "name": { "kind": "Name", "value": "collection_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "rules" } }, { "kind": "Field", "name": { "kind": "Name", "value": "status" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sort_order" } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }] } }] };
1028
+ var GetStorefrontProductBySlugDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "GetStorefrontProductBySlug" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "slug" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "String" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "storefront_product_by_slug" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "slug" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "slug" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "FragmentSpread", "name": { "kind": "Name", "value": "ProductFields" } }] } }] } }, { "kind": "FragmentDefinition", "name": { "kind": "Name", "value": "ProductFields" }, "typeCondition": { "kind": "NamedType", "name": { "kind": "Name", "value": "Product" } }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "business_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "brand" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "requires_shipping" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_consumable" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_bespoke" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_physical" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_non_returnable" } }, { "kind": "Field", "name": { "kind": "Name", "value": "deposit_percentage" } }, { "kind": "Field", "name": { "kind": "Name", "value": "base_price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "currency" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_start" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_end" } }, { "kind": "Field", "name": { "kind": "Name", "value": "images" } }, { "kind": "Field", "name": { "kind": "Name", "value": "tags" } }, { "kind": "Field", "name": { "kind": "Name", "value": "custom_fields" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "label" } }, { "kind": "Field", "name": { "kind": "Name", "value": "unit" } }, { "kind": "Field", "name": { "kind": "Name", "value": "required" } }, { "kind": "Field", "name": { "kind": "Name", "value": "field_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "options" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_published" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_featured" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_popular" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_new" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_temporary" } }, { "kind": "Field", "name": { "kind": "Name", "value": "average_rating" } }, { "kind": "Field", "name": { "kind": "Name", "value": "rating_count" } }, { "kind": "Field", "name": { "kind": "Name", "value": "source_channel" } }, { "kind": "Field", "name": { "kind": "Name", "value": "source_channel_ref_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_duration_minutes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_location_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_coverage_radius_km" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_requires_consultation" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_max_capacity_per_slot" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_buffer_minutes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_is_recurring" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_recurring_interval" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_project_based" } }, { "kind": "Field", "name": { "kind": "Name", "value": "digital_files" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "file_key" } }, { "kind": "Field", "name": { "kind": "Name", "value": "display_name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sort_order" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "download_limit" } }, { "kind": "Field", "name": { "kind": "Name", "value": "download_expires_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "weight_kg" } }, { "kind": "Field", "name": { "kind": "Name", "value": "length_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "width_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "height_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "variants" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sku" } }, { "kind": "Field", "name": { "kind": "Name", "value": "attributes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "images" } }, { "kind": "Field", "name": { "kind": "Name", "value": "weight_kg" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_active" } }, { "kind": "Field", "name": { "kind": "Name", "value": "inventory" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "variant_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reserved" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reorder_point" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reorder_quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "track_quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "allow_backorder" } }, { "kind": "Field", "name": { "kind": "Name", "value": "available" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_in_stock" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_low_stock" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "collections" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "business_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "image_url" } }, { "kind": "Field", "name": { "kind": "Name", "value": "collection_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "rules" } }, { "kind": "Field", "name": { "kind": "Name", "value": "status" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sort_order" } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }] } }] };
1029
+ var GetStorefrontPopularDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "GetStorefrontPopular" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "page" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Int" } } } }, { "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "pageSize" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Int" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "storefront_popular" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "page" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "page" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "pageSize" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "pageSize" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "items" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "FragmentSpread", "name": { "kind": "Name", "value": "ProductFields" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "total" } }, { "kind": "Field", "name": { "kind": "Name", "value": "page" } }, { "kind": "Field", "name": { "kind": "Name", "value": "page_size" } }, { "kind": "Field", "name": { "kind": "Name", "value": "total_pages" } }] } }] } }, { "kind": "FragmentDefinition", "name": { "kind": "Name", "value": "ProductFields" }, "typeCondition": { "kind": "NamedType", "name": { "kind": "Name", "value": "Product" } }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "business_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "brand" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "requires_shipping" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_consumable" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_bespoke" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_physical" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_non_returnable" } }, { "kind": "Field", "name": { "kind": "Name", "value": "deposit_percentage" } }, { "kind": "Field", "name": { "kind": "Name", "value": "base_price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "currency" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_start" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_end" } }, { "kind": "Field", "name": { "kind": "Name", "value": "images" } }, { "kind": "Field", "name": { "kind": "Name", "value": "tags" } }, { "kind": "Field", "name": { "kind": "Name", "value": "custom_fields" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "label" } }, { "kind": "Field", "name": { "kind": "Name", "value": "unit" } }, { "kind": "Field", "name": { "kind": "Name", "value": "required" } }, { "kind": "Field", "name": { "kind": "Name", "value": "field_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "options" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_published" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_featured" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_popular" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_new" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_temporary" } }, { "kind": "Field", "name": { "kind": "Name", "value": "average_rating" } }, { "kind": "Field", "name": { "kind": "Name", "value": "rating_count" } }, { "kind": "Field", "name": { "kind": "Name", "value": "source_channel" } }, { "kind": "Field", "name": { "kind": "Name", "value": "source_channel_ref_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_duration_minutes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_location_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_coverage_radius_km" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_requires_consultation" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_max_capacity_per_slot" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_buffer_minutes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_is_recurring" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_recurring_interval" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_project_based" } }, { "kind": "Field", "name": { "kind": "Name", "value": "digital_files" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "file_key" } }, { "kind": "Field", "name": { "kind": "Name", "value": "display_name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sort_order" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "download_limit" } }, { "kind": "Field", "name": { "kind": "Name", "value": "download_expires_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "weight_kg" } }, { "kind": "Field", "name": { "kind": "Name", "value": "length_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "width_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "height_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "variants" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sku" } }, { "kind": "Field", "name": { "kind": "Name", "value": "attributes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "images" } }, { "kind": "Field", "name": { "kind": "Name", "value": "weight_kg" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_active" } }, { "kind": "Field", "name": { "kind": "Name", "value": "inventory" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "variant_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reserved" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reorder_point" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reorder_quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "track_quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "allow_backorder" } }, { "kind": "Field", "name": { "kind": "Name", "value": "available" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_in_stock" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_low_stock" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "collections" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "business_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "image_url" } }, { "kind": "Field", "name": { "kind": "Name", "value": "collection_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "rules" } }, { "kind": "Field", "name": { "kind": "Name", "value": "status" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sort_order" } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }] } }] };
1030
+ var GetStorefrontFeaturedDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "GetStorefrontFeatured" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "page" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Int" } } } }, { "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "pageSize" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Int" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "storefront_featured" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "page" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "page" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "pageSize" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "pageSize" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "items" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "FragmentSpread", "name": { "kind": "Name", "value": "ProductFields" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "total" } }, { "kind": "Field", "name": { "kind": "Name", "value": "page" } }, { "kind": "Field", "name": { "kind": "Name", "value": "page_size" } }, { "kind": "Field", "name": { "kind": "Name", "value": "total_pages" } }] } }] } }, { "kind": "FragmentDefinition", "name": { "kind": "Name", "value": "ProductFields" }, "typeCondition": { "kind": "NamedType", "name": { "kind": "Name", "value": "Product" } }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "business_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "brand" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "requires_shipping" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_consumable" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_bespoke" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_physical" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_non_returnable" } }, { "kind": "Field", "name": { "kind": "Name", "value": "deposit_percentage" } }, { "kind": "Field", "name": { "kind": "Name", "value": "base_price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "currency" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_start" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_end" } }, { "kind": "Field", "name": { "kind": "Name", "value": "images" } }, { "kind": "Field", "name": { "kind": "Name", "value": "tags" } }, { "kind": "Field", "name": { "kind": "Name", "value": "custom_fields" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "label" } }, { "kind": "Field", "name": { "kind": "Name", "value": "unit" } }, { "kind": "Field", "name": { "kind": "Name", "value": "required" } }, { "kind": "Field", "name": { "kind": "Name", "value": "field_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "options" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_published" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_featured" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_popular" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_new" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_temporary" } }, { "kind": "Field", "name": { "kind": "Name", "value": "average_rating" } }, { "kind": "Field", "name": { "kind": "Name", "value": "rating_count" } }, { "kind": "Field", "name": { "kind": "Name", "value": "source_channel" } }, { "kind": "Field", "name": { "kind": "Name", "value": "source_channel_ref_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_duration_minutes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_location_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_coverage_radius_km" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_requires_consultation" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_max_capacity_per_slot" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_buffer_minutes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_is_recurring" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_recurring_interval" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_project_based" } }, { "kind": "Field", "name": { "kind": "Name", "value": "digital_files" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "file_key" } }, { "kind": "Field", "name": { "kind": "Name", "value": "display_name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sort_order" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "download_limit" } }, { "kind": "Field", "name": { "kind": "Name", "value": "download_expires_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "weight_kg" } }, { "kind": "Field", "name": { "kind": "Name", "value": "length_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "width_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "height_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "variants" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sku" } }, { "kind": "Field", "name": { "kind": "Name", "value": "attributes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "images" } }, { "kind": "Field", "name": { "kind": "Name", "value": "weight_kg" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_active" } }, { "kind": "Field", "name": { "kind": "Name", "value": "inventory" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "variant_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reserved" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reorder_point" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reorder_quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "track_quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "allow_backorder" } }, { "kind": "Field", "name": { "kind": "Name", "value": "available" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_in_stock" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_low_stock" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "collections" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "business_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "image_url" } }, { "kind": "Field", "name": { "kind": "Name", "value": "collection_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "rules" } }, { "kind": "Field", "name": { "kind": "Name", "value": "status" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sort_order" } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }] } }] };
1031
+ var GetStorefrontNewDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "GetStorefrontNew" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "page" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Int" } } } }, { "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "pageSize" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Int" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "storefront_new" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "page" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "page" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "pageSize" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "pageSize" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "items" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "FragmentSpread", "name": { "kind": "Name", "value": "ProductFields" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "total" } }, { "kind": "Field", "name": { "kind": "Name", "value": "page" } }, { "kind": "Field", "name": { "kind": "Name", "value": "page_size" } }, { "kind": "Field", "name": { "kind": "Name", "value": "total_pages" } }] } }] } }, { "kind": "FragmentDefinition", "name": { "kind": "Name", "value": "ProductFields" }, "typeCondition": { "kind": "NamedType", "name": { "kind": "Name", "value": "Product" } }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "business_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "brand" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "requires_shipping" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_consumable" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_bespoke" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_physical" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_non_returnable" } }, { "kind": "Field", "name": { "kind": "Name", "value": "deposit_percentage" } }, { "kind": "Field", "name": { "kind": "Name", "value": "base_price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "currency" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_start" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_end" } }, { "kind": "Field", "name": { "kind": "Name", "value": "images" } }, { "kind": "Field", "name": { "kind": "Name", "value": "tags" } }, { "kind": "Field", "name": { "kind": "Name", "value": "custom_fields" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "label" } }, { "kind": "Field", "name": { "kind": "Name", "value": "unit" } }, { "kind": "Field", "name": { "kind": "Name", "value": "required" } }, { "kind": "Field", "name": { "kind": "Name", "value": "field_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "options" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_published" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_featured" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_popular" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_new" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_temporary" } }, { "kind": "Field", "name": { "kind": "Name", "value": "average_rating" } }, { "kind": "Field", "name": { "kind": "Name", "value": "rating_count" } }, { "kind": "Field", "name": { "kind": "Name", "value": "source_channel" } }, { "kind": "Field", "name": { "kind": "Name", "value": "source_channel_ref_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_duration_minutes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_location_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_coverage_radius_km" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_requires_consultation" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_max_capacity_per_slot" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_buffer_minutes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_is_recurring" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_recurring_interval" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_project_based" } }, { "kind": "Field", "name": { "kind": "Name", "value": "digital_files" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "file_key" } }, { "kind": "Field", "name": { "kind": "Name", "value": "display_name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sort_order" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "download_limit" } }, { "kind": "Field", "name": { "kind": "Name", "value": "download_expires_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "weight_kg" } }, { "kind": "Field", "name": { "kind": "Name", "value": "length_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "width_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "height_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "variants" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sku" } }, { "kind": "Field", "name": { "kind": "Name", "value": "attributes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "images" } }, { "kind": "Field", "name": { "kind": "Name", "value": "weight_kg" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_active" } }, { "kind": "Field", "name": { "kind": "Name", "value": "inventory" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "variant_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reserved" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reorder_point" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reorder_quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "track_quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "allow_backorder" } }, { "kind": "Field", "name": { "kind": "Name", "value": "available" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_in_stock" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_low_stock" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "collections" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "business_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "image_url" } }, { "kind": "Field", "name": { "kind": "Name", "value": "collection_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "rules" } }, { "kind": "Field", "name": { "kind": "Name", "value": "status" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sort_order" } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }] } }] };
1032
+ var GetStorefrontOnSaleDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "GetStorefrontOnSale" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "page" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Int" } } } }, { "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "pageSize" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Int" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "storefront_on_sale" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "page" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "page" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "pageSize" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "pageSize" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "items" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "FragmentSpread", "name": { "kind": "Name", "value": "ProductFields" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "total" } }, { "kind": "Field", "name": { "kind": "Name", "value": "page" } }, { "kind": "Field", "name": { "kind": "Name", "value": "page_size" } }, { "kind": "Field", "name": { "kind": "Name", "value": "total_pages" } }] } }] } }, { "kind": "FragmentDefinition", "name": { "kind": "Name", "value": "ProductFields" }, "typeCondition": { "kind": "NamedType", "name": { "kind": "Name", "value": "Product" } }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "business_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "brand" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "requires_shipping" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_consumable" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_bespoke" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_physical" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_non_returnable" } }, { "kind": "Field", "name": { "kind": "Name", "value": "deposit_percentage" } }, { "kind": "Field", "name": { "kind": "Name", "value": "base_price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "currency" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_start" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_end" } }, { "kind": "Field", "name": { "kind": "Name", "value": "images" } }, { "kind": "Field", "name": { "kind": "Name", "value": "tags" } }, { "kind": "Field", "name": { "kind": "Name", "value": "custom_fields" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "label" } }, { "kind": "Field", "name": { "kind": "Name", "value": "unit" } }, { "kind": "Field", "name": { "kind": "Name", "value": "required" } }, { "kind": "Field", "name": { "kind": "Name", "value": "field_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "options" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_published" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_featured" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_popular" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_new" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_temporary" } }, { "kind": "Field", "name": { "kind": "Name", "value": "average_rating" } }, { "kind": "Field", "name": { "kind": "Name", "value": "rating_count" } }, { "kind": "Field", "name": { "kind": "Name", "value": "source_channel" } }, { "kind": "Field", "name": { "kind": "Name", "value": "source_channel_ref_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_duration_minutes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_location_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_coverage_radius_km" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_requires_consultation" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_max_capacity_per_slot" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_buffer_minutes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_is_recurring" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_recurring_interval" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_project_based" } }, { "kind": "Field", "name": { "kind": "Name", "value": "digital_files" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "file_key" } }, { "kind": "Field", "name": { "kind": "Name", "value": "display_name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sort_order" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "download_limit" } }, { "kind": "Field", "name": { "kind": "Name", "value": "download_expires_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "weight_kg" } }, { "kind": "Field", "name": { "kind": "Name", "value": "length_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "width_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "height_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "variants" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sku" } }, { "kind": "Field", "name": { "kind": "Name", "value": "attributes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "images" } }, { "kind": "Field", "name": { "kind": "Name", "value": "weight_kg" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_active" } }, { "kind": "Field", "name": { "kind": "Name", "value": "inventory" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "variant_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reserved" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reorder_point" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reorder_quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "track_quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "allow_backorder" } }, { "kind": "Field", "name": { "kind": "Name", "value": "available" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_in_stock" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_low_stock" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "collections" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "business_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "image_url" } }, { "kind": "Field", "name": { "kind": "Name", "value": "collection_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "rules" } }, { "kind": "Field", "name": { "kind": "Name", "value": "status" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sort_order" } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }] } }] };
1033
+ var GetStorefrontRecommendedDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "GetStorefrontRecommended" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "page" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Int" } } } }, { "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "pageSize" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Int" } } } }, { "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "excludeProductId" } }, "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "String" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "storefront_recommended" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "page" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "page" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "pageSize" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "pageSize" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "excludeProductId" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "excludeProductId" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "items" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "FragmentSpread", "name": { "kind": "Name", "value": "ProductFields" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "total" } }, { "kind": "Field", "name": { "kind": "Name", "value": "page" } }, { "kind": "Field", "name": { "kind": "Name", "value": "page_size" } }, { "kind": "Field", "name": { "kind": "Name", "value": "total_pages" } }] } }] } }, { "kind": "FragmentDefinition", "name": { "kind": "Name", "value": "ProductFields" }, "typeCondition": { "kind": "NamedType", "name": { "kind": "Name", "value": "Product" } }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "business_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "brand" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "requires_shipping" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_consumable" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_bespoke" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_physical" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_non_returnable" } }, { "kind": "Field", "name": { "kind": "Name", "value": "deposit_percentage" } }, { "kind": "Field", "name": { "kind": "Name", "value": "base_price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "currency" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_start" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sale_end" } }, { "kind": "Field", "name": { "kind": "Name", "value": "images" } }, { "kind": "Field", "name": { "kind": "Name", "value": "tags" } }, { "kind": "Field", "name": { "kind": "Name", "value": "custom_fields" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "label" } }, { "kind": "Field", "name": { "kind": "Name", "value": "unit" } }, { "kind": "Field", "name": { "kind": "Name", "value": "required" } }, { "kind": "Field", "name": { "kind": "Name", "value": "field_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "options" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_published" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_featured" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_popular" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_new" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_temporary" } }, { "kind": "Field", "name": { "kind": "Name", "value": "average_rating" } }, { "kind": "Field", "name": { "kind": "Name", "value": "rating_count" } }, { "kind": "Field", "name": { "kind": "Name", "value": "source_channel" } }, { "kind": "Field", "name": { "kind": "Name", "value": "source_channel_ref_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_duration_minutes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_location_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_coverage_radius_km" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_requires_consultation" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_max_capacity_per_slot" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_buffer_minutes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_is_recurring" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_recurring_interval" } }, { "kind": "Field", "name": { "kind": "Name", "value": "service_project_based" } }, { "kind": "Field", "name": { "kind": "Name", "value": "digital_files" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "file_key" } }, { "kind": "Field", "name": { "kind": "Name", "value": "display_name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sort_order" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "download_limit" } }, { "kind": "Field", "name": { "kind": "Name", "value": "download_expires_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "weight_kg" } }, { "kind": "Field", "name": { "kind": "Name", "value": "length_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "width_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "height_cm" } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "variants" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sku" } }, { "kind": "Field", "name": { "kind": "Name", "value": "attributes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "images" } }, { "kind": "Field", "name": { "kind": "Name", "value": "weight_kg" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_active" } }, { "kind": "Field", "name": { "kind": "Name", "value": "inventory" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "variant_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reserved" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reorder_point" } }, { "kind": "Field", "name": { "kind": "Name", "value": "reorder_quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "track_quantity" } }, { "kind": "Field", "name": { "kind": "Name", "value": "allow_backorder" } }, { "kind": "Field", "name": { "kind": "Name", "value": "available" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_in_stock" } }, { "kind": "Field", "name": { "kind": "Name", "value": "is_low_stock" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "collections" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "business_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "image_url" } }, { "kind": "Field", "name": { "kind": "Name", "value": "collection_type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "rules" } }, { "kind": "Field", "name": { "kind": "Name", "value": "status" } }, { "kind": "Field", "name": { "kind": "Name", "value": "sort_order" } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updated_at" } }] } }] } }] };
1034
+ var GetStorefrontWishlistDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "GetStorefrontWishlist" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "page" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Int" } } } }, { "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "pageSize" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Int" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "storefront_wishlist" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "page" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "page" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "pageSize" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "pageSize" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "items" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "notes" } }, { "kind": "Field", "name": { "kind": "Name", "value": "created_at" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_price" } }, { "kind": "Field", "name": { "kind": "Name", "value": "currency" } }, { "kind": "Field", "name": { "kind": "Name", "value": "product_image" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "total" } }, { "kind": "Field", "name": { "kind": "Name", "value": "page" } }, { "kind": "Field", "name": { "kind": "Name", "value": "page_size" } }, { "kind": "Field", "name": { "kind": "Name", "value": "total_pages" } }] } }] } }] };
1035
+ var CheckInStorefrontWishlistDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "CheckInStorefrontWishlist" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "productId" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "String" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "check_in_storefront_wishlist" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "productId" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "productId" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "in_wishlist" } }] } }] } }] };
1036
+
1037
+ // src/services/product.service.ts
1038
+ function mapProductType(raw) {
1039
+ switch (raw) {
1040
+ case "PHYSICAL":
1041
+ return "physical" /* PHYSICAL */;
1042
+ case "DIGITAL":
1043
+ return "digital" /* DIGITAL */;
1044
+ case "SERVICE":
1045
+ return "service" /* SERVICE */;
1046
+ case "BESPOKE":
1047
+ return "bespoke" /* BESPOKE */;
1048
+ default: {
1049
+ const _exhaustive = raw;
1050
+ throw new Error(`Unhandled GraphQL ProductType member: ${_exhaustive}`);
1393
1051
  }
1394
1052
  }
1395
- `;
1053
+ }
1054
+ function mapCustomFieldType(raw) {
1055
+ if (raw === "text" || raw === "choice") return raw;
1056
+ throw new Error(`Unrecognised custom field field_type from backend: "${raw}"`);
1057
+ }
1058
+ function mapProduct(raw) {
1059
+ return {
1060
+ ...raw,
1061
+ product_type: mapProductType(raw.product_type),
1062
+ custom_fields: raw.custom_fields.map((f) => ({
1063
+ ...f,
1064
+ unit: f.unit ?? void 0,
1065
+ field_type: mapCustomFieldType(f.field_type)
1066
+ })),
1067
+ description: raw.description ?? void 0,
1068
+ brand: raw.brand ?? void 0,
1069
+ is_physical: raw.is_physical ?? void 0,
1070
+ sale_price: raw.sale_price ?? void 0,
1071
+ sale_start: raw.sale_start ?? void 0,
1072
+ sale_end: raw.sale_end ?? void 0,
1073
+ source_channel_ref_id: raw.source_channel_ref_id ?? void 0,
1074
+ download_limit: raw.download_limit ?? void 0,
1075
+ download_expires_at: raw.download_expires_at ?? void 0,
1076
+ service_type: raw.service_type ?? void 0,
1077
+ service_duration_minutes: raw.service_duration_minutes ?? void 0,
1078
+ service_location_type: raw.service_location_type ?? void 0,
1079
+ service_coverage_radius_km: raw.service_coverage_radius_km ?? void 0,
1080
+ service_max_capacity_per_slot: raw.service_max_capacity_per_slot ?? void 0,
1081
+ service_recurring_interval: raw.service_recurring_interval ?? void 0,
1082
+ length_cm: raw.length_cm ?? void 0,
1083
+ width_cm: raw.width_cm ?? void 0,
1084
+ height_cm: raw.height_cm ?? void 0,
1085
+ average_rating: raw.average_rating ?? void 0,
1086
+ created_at: raw.created_at,
1087
+ updated_at: raw.updated_at,
1088
+ variants: raw.variants.map((v) => ({
1089
+ ...v,
1090
+ sku: v.sku ?? void 0,
1091
+ attributes: v.attributes ? JSON.parse(v.attributes) : {},
1092
+ price: v.price ?? void 0,
1093
+ weight_kg: v.weight_kg ?? void 0,
1094
+ inventory: v.inventory ? {
1095
+ ...v.inventory,
1096
+ variant_id: v.inventory.variant_id ?? void 0,
1097
+ updated_at: v.inventory.updated_at
1098
+ } : void 0,
1099
+ created_at: v.created_at,
1100
+ updated_at: v.updated_at
1101
+ })),
1102
+ collections: raw.collections.map((c) => ({
1103
+ ...c,
1104
+ business_id: c.business_id ?? void 0,
1105
+ description: c.description ?? void 0,
1106
+ image_url: c.image_url ?? void 0,
1107
+ rules: c.rules ?? void 0,
1108
+ created_at: c.created_at,
1109
+ updated_at: c.updated_at
1110
+ }))
1111
+ };
1112
+ }
1113
+ function mapProductConnection(connection) {
1114
+ return { ...connection, items: connection.items.map(mapProduct) };
1115
+ }
1396
1116
  async function getStorefrontProducts(config, params) {
1397
1117
  const client = createGraphQLClient(config);
1398
- const variables = {
1118
+ const data = await client.request(GetStorefrontProductsDocument, {
1399
1119
  page: params?.page || 1,
1400
1120
  pageSize: params?.pageSize || 20,
1401
1121
  collectionSlug: params?.collectionSlug,
1402
- search: params?.search
1403
- };
1404
- const data = await client.request(GET_STOREFRONT_PRODUCTS, variables);
1405
- return data.storefront_products;
1122
+ search: params?.search,
1123
+ productType: params?.productType
1124
+ });
1125
+ return mapProductConnection(data.storefront_products);
1406
1126
  }
1407
1127
  async function getStorefrontProductBySlug(config, slug) {
1408
1128
  const client = createGraphQLClient(config);
1409
- const data = await client.request(GET_STOREFRONT_PRODUCT_BY_SLUG, { slug });
1410
- return data.storefront_product_by_slug;
1411
- }
1412
- async function getLatestProducts(config, params) {
1413
- const client = createGraphQLClient(config);
1414
- const variables = {
1415
- page: params?.page || 1,
1416
- pageSize: params?.pageSize || 4
1417
- };
1418
- const data = await client.request(GET_STOREFRONT_LATEST, variables);
1419
- return data.storefront_latest;
1129
+ const data = await client.request(GetStorefrontProductBySlugDocument, { slug });
1130
+ if (!data.storefront_product_by_slug) {
1131
+ throw new Error(`Product not found: ${slug}`);
1132
+ }
1133
+ return mapProduct(data.storefront_product_by_slug);
1420
1134
  }
1421
1135
  async function getPopularProducts(config, params) {
1422
1136
  const client = createGraphQLClient(config);
1423
- const variables = {
1137
+ const data = await client.request(GetStorefrontPopularDocument, {
1424
1138
  page: params?.page || 1,
1425
1139
  pageSize: params?.pageSize || 4
1426
- };
1427
- const data = await client.request(GET_STOREFRONT_POPULAR, variables);
1428
- return data.storefront_popular;
1140
+ });
1141
+ return mapProductConnection(data.storefront_popular);
1429
1142
  }
1430
1143
  async function getFeaturedProducts(config, params) {
1431
1144
  const client = createGraphQLClient(config);
1432
- const variables = {
1145
+ const data = await client.request(GetStorefrontFeaturedDocument, {
1433
1146
  page: params?.page || 1,
1434
1147
  pageSize: params?.pageSize || 4
1435
- };
1436
- const data = await client.request(GET_STOREFRONT_FEATURED, variables);
1437
- return data.storefront_featured;
1148
+ });
1149
+ return mapProductConnection(data.storefront_featured);
1438
1150
  }
1439
1151
  async function getNewProducts(config, params) {
1440
1152
  const client = createGraphQLClient(config);
1441
- const variables = {
1153
+ const data = await client.request(GetStorefrontNewDocument, {
1442
1154
  page: params?.page || 1,
1443
1155
  pageSize: params?.pageSize || 4
1444
- };
1445
- const data = await client.request(GET_STOREFRONT_NEW, variables);
1446
- return data.storefront_new;
1156
+ });
1157
+ return mapProductConnection(data.storefront_new);
1447
1158
  }
1448
1159
  async function getOnSaleProducts(config, params) {
1449
1160
  const client = createGraphQLClient(config);
1450
- const variables = {
1161
+ const data = await client.request(GetStorefrontOnSaleDocument, {
1451
1162
  page: params?.page || 1,
1452
1163
  pageSize: params?.pageSize || 4
1453
- };
1454
- const data = await client.request(GET_STOREFRONT_ON_SALE, variables);
1455
- return data.storefront_on_sale;
1456
- }
1457
- async function getLowStockProducts(config, params) {
1458
- const client = createGraphQLClient(config);
1459
- const variables = {
1460
- page: params?.page || 1,
1461
- pageSize: params?.pageSize || 4
1462
- };
1463
- const data = await client.request(GET_STOREFRONT_LOW_STOCK, variables);
1464
- return data.storefront_low_stock;
1164
+ });
1165
+ return mapProductConnection(data.storefront_on_sale);
1465
1166
  }
1466
1167
  async function getRecommendedProducts(config, params) {
1467
1168
  const client = createGraphQLClient(config);
1468
- const variables = {
1169
+ const data = await client.request(GetStorefrontRecommendedDocument, {
1469
1170
  page: params?.page || 1,
1470
1171
  pageSize: params?.pageSize || 4,
1471
1172
  excludeProductId: params?.excludeProductId
1472
- };
1473
- const data = await client.request(GET_STOREFRONT_RECOMMENDED, variables);
1474
- return data.storefront_recommended;
1173
+ });
1174
+ return mapProductConnection(data.storefront_recommended);
1475
1175
  }
1476
1176
 
1477
1177
  // src/services/collection.service.ts
1478
- import { GraphQLClient as GraphQLClient2 } from "graphql-request";
1479
- var GET_STOREFRONT_COLLECTIONS = `
1480
- query GetStorefrontCollections($page: Int!, $pageSize: Int!) {
1481
- storefront_collections(page: $page, pageSize: $pageSize) {
1482
- items {
1483
- id
1484
- business_id
1485
- name
1486
- slug
1487
- description
1488
- image
1489
- collection_type
1490
- rules
1491
- status
1492
- sort_order
1493
- created_at
1494
- updated_at
1495
- }
1496
- total
1497
- page
1498
- page_size
1499
- total_pages
1500
- }
1501
- }
1502
- `;
1503
- var GET_STOREFRONT_COLLECTION_BY_SLUG = `
1504
- query GetStorefrontCollectionBySlug($slug: String!) {
1505
- storefront_collection_by_slug(slug: $slug) {
1506
- id
1507
- business_id
1508
- name
1509
- slug
1510
- description
1511
- image
1512
- collection_type
1513
- rules
1514
- status
1515
- sort_order
1516
- created_at
1517
- updated_at
1518
- }
1519
- }
1520
- `;
1521
- function createGraphQLClient2(config) {
1522
- const headers = {
1523
- "Content-Type": "application/json",
1524
- "Accept": "application/json",
1525
- "X-Storefront-Key": config.storefrontApiKey,
1526
- "X-Business-ID": config.businessId
1178
+ function mapCollection(raw) {
1179
+ return {
1180
+ ...raw,
1181
+ business_id: raw.business_id ?? void 0,
1182
+ description: raw.description ?? void 0,
1183
+ image_url: raw.image_url ?? void 0,
1184
+ rules: raw.rules ?? void 0,
1185
+ created_at: raw.created_at,
1186
+ updated_at: raw.updated_at
1527
1187
  };
1528
- return new GraphQLClient2(`${config.backendUrls.store}/graphql`, { headers });
1529
1188
  }
1530
1189
  async function getStorefrontCollections(config, params) {
1531
- const client = createGraphQLClient2(config);
1532
- const variables = {
1190
+ const client = createGraphQLClient(config);
1191
+ const data = await client.request(GetStorefrontCollectionsDocument, {
1533
1192
  page: params?.page || 1,
1534
1193
  pageSize: params?.pageSize || 20
1535
- };
1536
- const data = await client.request(GET_STOREFRONT_COLLECTIONS, variables);
1537
- return data.storefront_collections;
1194
+ });
1195
+ return { ...data.storefront_collections, items: data.storefront_collections.items.map(mapCollection) };
1538
1196
  }
1539
1197
  async function getStorefrontCollectionBySlug(config, slug) {
1540
- const client = createGraphQLClient2(config);
1541
- const data = await client.request(GET_STOREFRONT_COLLECTION_BY_SLUG, { slug });
1542
- return data.storefront_collection_by_slug;
1198
+ const client = createGraphQLClient(config);
1199
+ const data = await client.request(GetStorefrontCollectionBySlugDocument, { slug });
1200
+ if (!data.storefront_collection_by_slug) {
1201
+ throw new Error(`Collection not found: ${slug}`);
1202
+ }
1203
+ return mapCollection(data.storefront_collection_by_slug);
1543
1204
  }
1544
1205
 
1545
1206
  // src/services/wishlist.service.ts
1546
- import { GraphQLClient as GraphQLClient3 } from "graphql-request";
1547
- var GET_STOREFRONT_WISHLIST = `
1548
- query GetStorefrontWishlist($page: Int!, $pageSize: Int!) {
1549
- storefront_wishlist(page: $page, pageSize: $pageSize) {
1550
- items {
1551
- id
1552
- product_id
1553
- notes
1554
- created_at
1555
- product_name
1556
- product_slug
1557
- product_price
1558
- currency
1559
- product_image
1560
- }
1561
- total
1562
- page
1563
- page_size
1564
- total_pages
1565
- }
1566
- }
1567
- `;
1568
- var CHECK_IN_STOREFRONT_WISHLIST = `
1569
- query CheckInStorefrontWishlist($productId: String!) {
1570
- check_in_storefront_wishlist(productId: $productId) {
1571
- in_wishlist
1572
- }
1573
- }
1574
- `;
1575
- function createGraphQLClient3(config, accessToken) {
1576
- const headers = {
1577
- "Content-Type": "application/json",
1578
- "Accept": "application/json",
1579
- "X-Storefront-Key": config.storefrontApiKey,
1580
- "X-Business-ID": config.businessId
1581
- };
1582
- if (accessToken) {
1583
- headers["Authorization"] = `Bearer ${accessToken}`;
1584
- }
1585
- return new GraphQLClient3(`${config.backendUrls.store}/graphql`, { headers });
1586
- }
1587
1207
  async function getWishlist(config, accessToken, params) {
1588
- const client = createGraphQLClient3(config, accessToken);
1589
- const variables = {
1208
+ const client = createGraphQLClient(config, accessToken);
1209
+ const data = await client.request(GetStorefrontWishlistDocument, {
1590
1210
  page: params?.page || 1,
1591
1211
  pageSize: params?.pageSize || 20
1212
+ });
1213
+ return {
1214
+ ...data.storefront_wishlist,
1215
+ items: data.storefront_wishlist.items.map((item) => ({
1216
+ ...item,
1217
+ created_at: item.created_at,
1218
+ notes: item.notes ?? void 0,
1219
+ product_name: item.product_name ?? void 0,
1220
+ product_slug: item.product_slug ?? void 0,
1221
+ product_price: item.product_price ?? void 0,
1222
+ currency: item.currency ?? void 0,
1223
+ product_image: item.product_image ?? void 0
1224
+ }))
1592
1225
  };
1593
- const data = await client.request(GET_STOREFRONT_WISHLIST, variables);
1594
- return data.storefront_wishlist;
1595
1226
  }
1596
1227
  async function checkInWishlist(config, accessToken, productId) {
1597
- const client = createGraphQLClient3(config, accessToken);
1598
- const data = await client.request(CHECK_IN_STOREFRONT_WISHLIST, { productId });
1228
+ const client = createGraphQLClient(config, accessToken);
1229
+ const data = await client.request(CheckInStorefrontWishlistDocument, { productId });
1599
1230
  return data.check_in_storefront_wishlist;
1600
1231
  }
1601
1232
  async function addToWishlist(config, accessToken, productId, notes) {
1602
- const response = await fetch(`${config.backendUrls.store}/wishlist/`, {
1233
+ return apiRequest(`${config.backendUrls.store}/wishlist/`, {
1603
1234
  method: "POST",
1604
- headers: {
1605
- "Content-Type": "application/json",
1606
- "Authorization": `Bearer ${accessToken}`,
1607
- "X-Storefront-Key": config.storefrontApiKey,
1608
- "X-Business-ID": config.businessId
1609
- },
1235
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1610
1236
  body: JSON.stringify({
1611
1237
  product_id: productId,
1612
1238
  notes: notes || void 0
1613
- })
1239
+ }),
1240
+ fallbackErrorMessage: "Failed to add to wishlist"
1614
1241
  });
1615
- if (!response.ok) throw new Error("Failed to add to wishlist");
1616
- return response.json();
1617
1242
  }
1618
1243
  async function removeFromWishlist(config, accessToken, itemId) {
1619
- const response = await fetch(`${config.backendUrls.store}/wishlist/${itemId}`, {
1244
+ await apiRequest(`${config.backendUrls.store}/wishlist/${itemId}`, {
1620
1245
  method: "DELETE",
1621
- headers: {
1622
- "Authorization": `Bearer ${accessToken}`,
1623
- "X-Storefront-Key": config.storefrontApiKey,
1624
- "X-Business-ID": config.businessId
1625
- }
1246
+ headers: authHeaders(config, accessToken),
1247
+ fallbackErrorMessage: "Failed to remove from wishlist"
1626
1248
  });
1627
- if (!response.ok) throw new Error("Failed to remove from wishlist");
1628
1249
  }
1629
1250
  async function removeProductFromWishlist(config, accessToken, productId) {
1630
- const response = await fetch(`${config.backendUrls.store}/wishlist/product/${productId}`, {
1251
+ await apiRequest(`${config.backendUrls.store}/wishlist/product/${productId}`, {
1631
1252
  method: "DELETE",
1632
- headers: {
1633
- "Authorization": `Bearer ${accessToken}`,
1634
- "X-Storefront-Key": config.storefrontApiKey,
1635
- "X-Business-ID": config.businessId
1636
- }
1253
+ headers: authHeaders(config, accessToken),
1254
+ fallbackErrorMessage: "Failed to remove product from wishlist"
1637
1255
  });
1638
- if (!response.ok) throw new Error("Failed to remove product from wishlist");
1639
1256
  }
1640
1257
 
1641
1258
  // src/services/payment.service.ts
1259
+ function isValidUUID(str) {
1260
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(str);
1261
+ }
1262
+ async function getAvailablePaymentMethods(config, currency, countryCode) {
1263
+ const params = {};
1264
+ if (currency) params.currency = currency;
1265
+ if (countryCode) params.country_code = countryCode;
1266
+ const query = new URLSearchParams(params).toString();
1267
+ return apiRequest(`${config.backendUrls.payment}/payments/storefront/methods${query ? `?${query}` : ""}`, {
1268
+ headers: storefrontHeaders(config),
1269
+ fallbackErrorMessage: "Failed to fetch available payment methods"
1270
+ });
1271
+ }
1642
1272
  async function createDedicatedAccount(config, accessToken, request) {
1643
- const isValidUUID = (str) => {
1644
- const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1645
- return uuidRegex.test(str);
1646
- };
1647
- const response = await fetch(`${config.backendUrls.payment}/payments/dedicated-account`, {
1273
+ const origin = request.storefront_origin || (typeof window !== "undefined" ? window.location.origin : "");
1274
+ const headers = { ...JSON_HEADERS, ...storefrontHeaders(config) };
1275
+ if (accessToken) {
1276
+ headers["Authorization"] = `Bearer ${accessToken}`;
1277
+ }
1278
+ if (origin) {
1279
+ headers["Origin"] = origin;
1280
+ }
1281
+ return apiRequest(`${config.backendUrls.payment}/payments/storefront/dedicated-account`, {
1648
1282
  method: "POST",
1649
- headers: {
1650
- "Content-Type": "application/json",
1651
- "Authorization": `Bearer ${accessToken}`,
1652
- "X-Storefront-Key": config.storefrontApiKey,
1653
- "X-Business-ID": config.businessId
1654
- },
1283
+ headers,
1655
1284
  body: JSON.stringify({
1656
1285
  order_id: request.order_id && isValidUUID(request.order_id) ? request.order_id : void 0,
1657
1286
  amount: request.amount,
1658
1287
  customer_email: request.customer_email,
1659
1288
  customer_name: request.customer_name,
1660
1289
  currency: request.currency,
1290
+ gateway: request.gateway,
1661
1291
  idempotency_key: request.idempotency_key
1662
- })
1292
+ }),
1293
+ fallbackErrorMessage: "Failed to create dedicated account"
1663
1294
  });
1664
- if (!response.ok) throw new Error("Failed to create dedicated account");
1665
- const data = await response.json();
1666
- return data;
1667
1295
  }
1668
1296
  async function initiatePayment(config, accessToken, request) {
1669
- const isValidUUID = (str) => {
1670
- const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1671
- return uuidRegex.test(str);
1672
- };
1673
1297
  const validOrderId = request.order_id && isValidUUID(request.order_id) ? request.order_id : void 0;
1674
- const response = await fetch(`${config.backendUrls.payment}/payments/storefront/initiate`, {
1675
- method: "POST",
1676
- headers: {
1677
- "Content-Type": "application/json",
1678
- "Authorization": `Bearer ${accessToken}`,
1679
- "X-Storefront-Key": config.storefrontApiKey,
1680
- "X-Business-ID": config.businessId
1681
- },
1682
- body: JSON.stringify({
1683
- amount: request.amount,
1684
- currency: request.currency,
1685
- order_id: validOrderId,
1686
- quote_id: request.quote_id,
1687
- metadata_json: {
1688
- customer_email: request.customer_email,
1689
- customer_name: request.customer_name ?? "",
1298
+ const origin = request.storefront_origin || (typeof window !== "undefined" ? window.location.origin : "");
1299
+ const headers = { ...JSON_HEADERS, ...storefrontHeaders(config) };
1300
+ if (accessToken) {
1301
+ headers["Authorization"] = `Bearer ${accessToken}`;
1302
+ }
1303
+ if (origin) {
1304
+ headers["Origin"] = origin;
1305
+ }
1306
+ const data = await apiRequest(
1307
+ `${config.backendUrls.payment}/payments/storefront/initiate`,
1308
+ {
1309
+ method: "POST",
1310
+ headers,
1311
+ body: JSON.stringify({
1312
+ amount: request.amount,
1313
+ currency: request.currency,
1314
+ // Same real backend default as getAvailablePaymentMethods above
1315
+ // (huma's own `default:"NG"` on this field) — omit rather than
1316
+ // hardcode a client-side copy of it.
1317
+ country_code: request.country_code,
1318
+ gateway: request.gateway,
1690
1319
  order_id: validOrderId,
1691
- ...request.metadata ?? {}
1692
- },
1693
- idempotency_key: request.idempotency_key
1694
- })
1695
- });
1696
- if (!response.ok) throw new Error("Failed to initiate payment");
1697
- const data = await response.json();
1320
+ quote_id: request.quote_id,
1321
+ metadata_json: {
1322
+ customer_email: request.customer_email,
1323
+ customer_name: request.customer_name ?? "",
1324
+ order_id: validOrderId,
1325
+ ...request.metadata ?? {}
1326
+ },
1327
+ idempotency_key: request.idempotency_key
1328
+ }),
1329
+ fallbackErrorMessage: "Failed to initiate payment"
1330
+ }
1331
+ );
1698
1332
  return {
1699
1333
  id: data.id,
1700
1334
  gateway_ref: data.gateway_ref,
@@ -1703,311 +1337,241 @@ async function initiatePayment(config, accessToken, request) {
1703
1337
  checkout_url: data.metadata_json?.checkout_url ?? ""
1704
1338
  };
1705
1339
  }
1706
- async function requestQuote(config, amount) {
1707
- const response = await fetch(`${config.backendUrls.payment}/payments/quote`, {
1340
+ async function verifyTransaction(config, reference) {
1341
+ return apiRequest(`${config.backendUrls.payment}/payments/verify/${reference}`, {
1708
1342
  method: "POST",
1709
- headers: {
1710
- "Content-Type": "application/json",
1711
- "X-Storefront-Key": config.storefrontApiKey,
1712
- "X-Business-ID": config.businessId
1713
- },
1714
- body: JSON.stringify({ amount })
1343
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
1344
+ fallbackErrorMessage: "Failed to verify transaction"
1715
1345
  });
1716
- if (!response.ok) throw new Error("Failed to request quote");
1717
- const data = await response.json();
1718
- return data;
1719
1346
  }
1720
- async function verifyTransaction(configOrReference, reference) {
1721
- if (typeof configOrReference === "string") {
1722
- const ref2 = configOrReference;
1723
- const response2 = await fetch(`/api/payments/verify/${ref2}`, {
1724
- method: "POST",
1725
- headers: { "Content-Type": "application/json" }
1726
- });
1727
- if (!response2.ok) throw new Error("Failed to verify transaction");
1728
- return response2.json();
1729
- }
1730
- const config = configOrReference;
1731
- const ref = reference || "";
1732
- const response = await fetch(`${config.backendUrls.payment}/payments/verify/${ref}`, {
1733
- method: "POST",
1734
- headers: {
1735
- "Content-Type": "application/json",
1736
- "X-Storefront-Key": config.storefrontApiKey,
1737
- "X-Business-ID": config.businessId
1738
- }
1347
+ async function getTransactionStatus(config, transactionId) {
1348
+ return apiRequest(`${config.backendUrls.payment}/payments/transactions/${transactionId}`, {
1349
+ headers: storefrontHeaders(config),
1350
+ fallbackErrorMessage: "Failed to get transaction status"
1739
1351
  });
1740
- if (!response.ok) throw new Error("Failed to verify transaction");
1741
- const data = await response.json();
1742
- return data;
1743
1352
  }
1744
- async function getTransactionStatus(config, transactionId) {
1745
- const response = await fetch(`${config.backendUrls.payment}/payments/transactions/${transactionId}`, {
1746
- headers: {
1747
- "X-Storefront-Key": config.storefrontApiKey,
1748
- "X-Business-ID": config.businessId
1353
+ async function getWalletBalance(config, accessToken) {
1354
+ return apiRequest(`${config.backendUrls.payment}/wallet/balance`, {
1355
+ headers: authHeaders(config, accessToken),
1356
+ fallbackErrorMessage: "Failed to fetch wallet balance"
1357
+ });
1358
+ }
1359
+ async function getWalletLedger(config, accessToken, params) {
1360
+ const page = params?.page ?? 1;
1361
+ const pageSize = params?.page_size ?? 20;
1362
+ return apiRequest(
1363
+ `${config.backendUrls.payment}/wallet/ledger?page=${page}&page_size=${pageSize}`,
1364
+ {
1365
+ headers: authHeaders(config, accessToken),
1366
+ fallbackErrorMessage: "Failed to fetch wallet ledger"
1749
1367
  }
1368
+ );
1369
+ }
1370
+ async function claimWalletCredit(config, accessToken, claimToken) {
1371
+ return apiRequest(`${config.backendUrls.payment}/wallet/claim`, {
1372
+ method: "POST",
1373
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1374
+ body: JSON.stringify({ claim_token: claimToken }),
1375
+ fallbackErrorMessage: "Failed to claim wallet credit"
1750
1376
  });
1751
- if (!response.ok) throw new Error("Failed to get transaction status");
1752
- const data = await response.json();
1753
- return data;
1754
1377
  }
1755
1378
  var paymentService = {
1379
+ getAvailablePaymentMethods,
1756
1380
  createDedicatedAccount,
1757
1381
  initiatePayment,
1758
- requestQuote,
1759
1382
  verifyTransaction,
1760
- getTransactionStatus
1383
+ getTransactionStatus,
1384
+ getWalletBalance,
1385
+ getWalletLedger,
1386
+ claimWalletCredit
1761
1387
  };
1762
1388
 
1763
- // src/services/consultation.service.ts
1764
- async function createConsultation(config, accessToken, payload) {
1765
- const response = await fetch(`${config.backendUrls.store}/social/consultations/`, {
1389
+ // src/services/logistics.service.ts
1390
+ async function getShippingRates(config, payload) {
1391
+ return apiRequest(`${config.backendUrls.store}/logistics/shipping/rates`, {
1766
1392
  method: "POST",
1767
- headers: {
1768
- "Content-Type": "application/json",
1769
- "Authorization": `Bearer ${accessToken}`,
1770
- "X-Storefront-Key": config.storefrontApiKey,
1771
- "X-Business-ID": config.businessId
1772
- },
1773
- body: JSON.stringify(payload)
1393
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
1394
+ body: JSON.stringify(payload),
1395
+ fallbackErrorMessage: "Failed to get shipping rates"
1774
1396
  });
1775
- if (!response.ok) throw new Error("Failed to create consultation");
1776
- return response.json();
1777
1397
  }
1778
- async function getConsultations(config, accessToken) {
1779
- const response = await fetch(`${config.backendUrls.store}/social/consultations/`, {
1780
- headers: {
1781
- "Authorization": `Bearer ${accessToken}`,
1782
- "X-Storefront-Key": config.storefrontApiKey,
1783
- "X-Business-ID": config.businessId
1784
- }
1398
+ async function validateShippingAddress(config, payload) {
1399
+ return apiRequest(`${config.backendUrls.store}/logistics/shipping-address/validate`, {
1400
+ method: "POST",
1401
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
1402
+ body: JSON.stringify(payload),
1403
+ fallbackErrorMessage: "Failed to validate shipping address"
1785
1404
  });
1786
- if (!response.ok) throw new Error("Failed to get consultations");
1787
- return response.json();
1788
1405
  }
1789
- async function getConsultation(config, accessToken, id) {
1790
- const response = await fetch(`${config.backendUrls.store}/social/consultations/${id}`, {
1791
- headers: {
1792
- "Authorization": `Bearer ${accessToken}`,
1793
- "X-Storefront-Key": config.storefrontApiKey,
1794
- "X-Business-ID": config.businessId
1795
- }
1406
+ async function fetchLocationList(config, path, coords, fallbackErrorMessage) {
1407
+ const params = new URLSearchParams();
1408
+ if (coords) {
1409
+ params.set("latitude", String(coords.latitude));
1410
+ params.set("longitude", String(coords.longitude));
1411
+ }
1412
+ const qs = params.toString();
1413
+ return apiRequest(`${config.backendUrls.store}/logistics/${path}${qs ? `?${qs}` : ""}`, {
1414
+ headers: storefrontHeaders(config),
1415
+ fallbackErrorMessage
1796
1416
  });
1797
- if (!response.ok) throw new Error("Failed to get consultation");
1798
- return response.json();
1799
1417
  }
1800
- async function sendMessage(config, accessToken, id, payload) {
1801
- await fetch(`${config.backendUrls.store}/social/consultations/${id}/messages`, {
1802
- method: "POST",
1803
- headers: {
1804
- "Content-Type": "application/json",
1805
- "Authorization": `Bearer ${accessToken}`,
1806
- "X-Storefront-Key": config.storefrontApiKey,
1807
- "X-Business-ID": config.businessId
1808
- },
1809
- body: JSON.stringify(payload)
1810
- });
1418
+ async function listPickupLocations(config, coords) {
1419
+ return fetchLocationList(config, "pickup-locations", coords, "Failed to list pickup locations");
1811
1420
  }
1812
- async function acceptQuote(config, accessToken, id, payload) {
1813
- const response = await fetch(`${config.backendUrls.store}/social/consultations/${id}/accept-quote`, {
1814
- method: "POST",
1815
- headers: {
1816
- "Content-Type": "application/json",
1817
- "Authorization": `Bearer ${accessToken}`,
1818
- "X-Storefront-Key": config.storefrontApiKey,
1819
- "X-Business-ID": config.businessId
1820
- },
1821
- body: JSON.stringify(payload)
1822
- });
1823
- if (!response.ok) throw new Error("Failed to accept quote");
1824
- return response.json();
1421
+ async function listBusinessLocations(config, coords) {
1422
+ return fetchLocationList(config, "locations", coords, "Failed to list business locations");
1825
1423
  }
1826
- async function rejectQuote(config, accessToken, id, reason) {
1827
- const response = await fetch(`${config.backendUrls.store}/social/consultations/${id}/reject-quote`, {
1424
+ async function reverseGeocode(config, payload) {
1425
+ return apiRequest(`${config.backendUrls.store}/logistics/reverse-geocode`, {
1828
1426
  method: "POST",
1829
- headers: {
1830
- "Content-Type": "application/json",
1831
- "Authorization": `Bearer ${accessToken}`,
1832
- "X-Storefront-Key": config.storefrontApiKey,
1833
- "X-Business-ID": config.businessId
1834
- },
1835
- body: JSON.stringify({ reason })
1427
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
1428
+ body: JSON.stringify(payload),
1429
+ fallbackErrorMessage: "Failed to reverse geocode coordinates"
1836
1430
  });
1837
- if (!response.ok) throw new Error("Failed to reject quote");
1838
- return response.json();
1839
1431
  }
1840
- var consultationService = {
1841
- createConsultation,
1842
- getConsultations,
1843
- getConsultation,
1844
- sendMessage,
1845
- acceptQuote,
1846
- rejectQuote
1847
- };
1848
-
1849
- // src/services/logistics.service.ts
1850
- async function getShippingRates(config, payload) {
1851
- const response = await fetch(`${config.backendUrls.store}/logistics/shipping/rates`, {
1432
+ async function decodePlusCode(config, payload) {
1433
+ return apiRequest(`${config.backendUrls.store}/logistics/plus-code/decode`, {
1852
1434
  method: "POST",
1853
- headers: {
1854
- "Content-Type": "application/json",
1855
- "X-Storefront-Key": config.storefrontApiKey,
1856
- "X-Business-ID": config.businessId
1857
- },
1858
- body: JSON.stringify(payload)
1859
- });
1860
- if (!response.ok) throw new Error("Failed to get shipping rates");
1861
- return response.json();
1862
- }
1863
- async function getSellerShippingAddress(config) {
1864
- const response = await fetch(`${config.backendUrls.store}/logistics/shipping-address`, {
1865
- headers: {
1866
- "X-Storefront-Key": config.storefrontApiKey,
1867
- "X-Business-ID": config.businessId
1868
- }
1435
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
1436
+ body: JSON.stringify(payload),
1437
+ fallbackErrorMessage: "Failed to decode Plus Code"
1869
1438
  });
1870
- if (!response.ok) throw new Error("Failed to get seller shipping address");
1871
- return response.json();
1872
1439
  }
1873
- async function validateShippingAddress(config, payload) {
1874
- const response = await fetch(`${config.backendUrls.store}/logistics/shipping-address/validate`, {
1875
- method: "POST",
1876
- headers: {
1877
- "Content-Type": "application/json",
1878
- "X-Storefront-Key": config.storefrontApiKey,
1879
- "X-Business-ID": config.businessId
1880
- },
1881
- body: JSON.stringify(payload)
1440
+ async function autocompleteAddress(config, q, country) {
1441
+ const params = new URLSearchParams();
1442
+ params.set("q", q);
1443
+ if (country) params.set("country", country);
1444
+ return apiRequest(`${config.backendUrls.store}/logistics/address/autocomplete?${params.toString()}`, {
1445
+ headers: storefrontHeaders(config),
1446
+ fallbackErrorMessage: "Failed to autocomplete address"
1882
1447
  });
1883
- if (!response.ok) throw new Error("Failed to validate shipping address");
1884
- return response.json();
1885
1448
  }
1886
1449
  var logisticsService = {
1887
1450
  getShippingRates,
1888
- getSellerShippingAddress,
1889
- validateShippingAddress
1451
+ validateShippingAddress,
1452
+ listPickupLocations,
1453
+ listBusinessLocations,
1454
+ reverseGeocode,
1455
+ decodePlusCode,
1456
+ autocompleteAddress
1890
1457
  };
1891
1458
 
1892
1459
  // src/services/bespoke.service.ts
1893
1460
  async function checkAvailability(config, product_id) {
1894
- const response = await fetch(`${config.backendUrls.store}/bespoke/availability/${product_id}`, {
1895
- headers: {
1896
- "X-Storefront-Key": config.storefrontApiKey,
1897
- "X-Business-ID": config.businessId
1898
- }
1461
+ return apiRequest(`${config.backendUrls.store}/bespoke/capacity/availability/${product_id}`, {
1462
+ headers: storefrontHeaders(config),
1463
+ fallbackErrorMessage: "Failed to check bespoke availability"
1464
+ });
1465
+ }
1466
+ async function approveBespokeDeliverable(config, accessToken, orderId) {
1467
+ return apiRequest(`${config.backendUrls.store}/orders/${orderId}/bespoke/approve`, {
1468
+ method: "POST",
1469
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1470
+ body: JSON.stringify({}),
1471
+ fallbackErrorMessage: "Failed to approve deliverable"
1472
+ });
1473
+ }
1474
+ async function requestBespokeRevision(config, accessToken, orderId, notes) {
1475
+ return apiRequest(`${config.backendUrls.store}/orders/${orderId}/bespoke/request-revision`, {
1476
+ method: "POST",
1477
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1478
+ body: JSON.stringify({ notes }),
1479
+ fallbackErrorMessage: "Failed to request revision"
1899
1480
  });
1900
- if (!response.ok) throw new Error("Failed to check bespoke availability");
1901
- return response.json();
1902
1481
  }
1903
1482
  var bespokeService = {
1904
- checkAvailability
1483
+ checkAvailability,
1484
+ approveBespokeDeliverable,
1485
+ requestBespokeRevision
1905
1486
  };
1906
1487
 
1907
1488
  // src/services/service-booking.service.ts
1908
- async function getAvailableSlots(config, product_id, date_from, date_to) {
1909
- const response = await fetch(
1910
- `${config.backendUrls.store}/service/available-slots/${product_id}?date_from=${date_from}&date_to=${date_to}`,
1489
+ async function getAvailableSlots(config, productId, date, params) {
1490
+ const query = new URLSearchParams({ date });
1491
+ if (params?.staffId) query.set("staff_id", params.staffId);
1492
+ if (params?.locationId) query.set("location_id", params.locationId);
1493
+ if (params?.customerLatitude !== void 0) query.set("customer_latitude", String(params.customerLatitude));
1494
+ if (params?.customerLongitude !== void 0) query.set("customer_longitude", String(params.customerLongitude));
1495
+ return apiRequest(
1496
+ `${config.backendUrls.store}/catalog/products/${productId}/available-slots?${query.toString()}`,
1911
1497
  {
1912
- headers: {
1913
- "X-Storefront-Key": config.storefrontApiKey,
1914
- "X-Business-ID": config.businessId
1915
- }
1498
+ headers: storefrontHeaders(config),
1499
+ fallbackErrorMessage: "Failed to get available slots"
1916
1500
  }
1917
1501
  );
1918
- if (!response.ok) throw new Error("Failed to get available slots");
1919
- return response.json();
1920
1502
  }
1921
1503
  async function bookAppointment(config, accessToken, payload) {
1922
- const response = await fetch(`${config.backendUrls.store}/service/appointments/`, {
1504
+ return apiRequest(`${config.backendUrls.store}/service/bookings/schedule`, {
1923
1505
  method: "POST",
1924
- headers: {
1925
- "Content-Type": "application/json",
1926
- "Authorization": `Bearer ${accessToken}`,
1927
- "X-Storefront-Key": config.storefrontApiKey,
1928
- "X-Business-ID": config.businessId
1929
- },
1930
- body: JSON.stringify(payload)
1506
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1507
+ body: JSON.stringify(payload),
1508
+ fallbackErrorMessage: "Failed to book appointment"
1931
1509
  });
1932
- if (!response.ok) throw new Error("Failed to book appointment");
1933
- return response.json();
1934
1510
  }
1935
- async function getAppointments(config, accessToken) {
1936
- const response = await fetch(`${config.backendUrls.store}/service/bookings/my`, {
1937
- headers: {
1938
- "Authorization": `Bearer ${accessToken}`,
1939
- "X-Storefront-Key": config.storefrontApiKey,
1940
- "X-Business-ID": config.businessId
1941
- }
1511
+ async function getAppointments(config, accessToken, params) {
1512
+ const query = new URLSearchParams();
1513
+ if (params?.status) query.set("status", params.status);
1514
+ if (params?.page) query.set("page", String(params.page));
1515
+ if (params?.page_size) query.set("page_size", String(params.page_size));
1516
+ return apiRequest(`${config.backendUrls.store}/service/bookings/my?${query.toString()}`, {
1517
+ headers: authHeaders(config, accessToken),
1518
+ fallbackErrorMessage: "Failed to get appointments"
1942
1519
  });
1943
- if (!response.ok) throw new Error("Failed to get appointments");
1944
- return response.json();
1945
1520
  }
1946
1521
  async function getAppointment(config, accessToken, id) {
1947
- const response = await fetch(`${config.backendUrls.store}/service/bookings/${id}`, {
1948
- headers: {
1949
- "Authorization": `Bearer ${accessToken}`,
1950
- "X-Storefront-Key": config.storefrontApiKey,
1951
- "X-Business-ID": config.businessId
1952
- }
1522
+ return apiRequest(`${config.backendUrls.store}/service/bookings/${id}`, {
1523
+ headers: authHeaders(config, accessToken),
1524
+ fallbackErrorMessage: "Failed to get appointment"
1953
1525
  });
1954
- if (!response.ok) throw new Error("Failed to get appointment");
1955
- return response.json();
1956
1526
  }
1957
- async function cancelAppointment(config, accessToken, id) {
1958
- await fetch(`${config.backendUrls.store}/service/bookings/${id}`, {
1959
- method: "DELETE",
1960
- headers: {
1961
- "Authorization": `Bearer ${accessToken}`,
1962
- "X-Storefront-Key": config.storefrontApiKey,
1963
- "X-Business-ID": config.businessId
1964
- }
1527
+ async function cancelAppointment(config, accessToken, id, payload) {
1528
+ return apiRequest(`${config.backendUrls.store}/service/bookings/${id}/cancel`, {
1529
+ method: "POST",
1530
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1531
+ body: JSON.stringify(payload ?? {}),
1532
+ fallbackErrorMessage: "Failed to cancel appointment"
1965
1533
  });
1966
1534
  }
1967
- async function getDeliverables(config, accessToken, order_id) {
1968
- const response = await fetch(`${config.backendUrls.store}/service/projects/${order_id}/deliverables/`, {
1969
- headers: {
1970
- "Authorization": `Bearer ${accessToken}`,
1971
- "X-Storefront-Key": config.storefrontApiKey,
1972
- "X-Business-ID": config.businessId
1973
- }
1535
+ async function rescheduleAppointment(config, accessToken, id, payload) {
1536
+ return apiRequest(`${config.backendUrls.store}/service/bookings/${id}/reschedule`, {
1537
+ method: "POST",
1538
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1539
+ body: JSON.stringify(payload),
1540
+ fallbackErrorMessage: "Failed to reschedule appointment"
1974
1541
  });
1975
- if (!response.ok) throw new Error("Failed to get deliverables");
1976
- return response.json();
1977
1542
  }
1978
- async function approveDeliverable(config, accessToken, order_id, deliverable_id) {
1979
- const response = await fetch(
1980
- `${config.backendUrls.store}/service/projects/${order_id}/deliverables/${deliverable_id}/approve`,
1981
- {
1982
- method: "POST",
1983
- headers: {
1984
- "Content-Type": "application/json",
1985
- "Authorization": `Bearer ${accessToken}`,
1986
- "X-Storefront-Key": config.storefrontApiKey,
1987
- "X-Business-ID": config.businessId
1988
- },
1989
- body: JSON.stringify({ approved: true })
1990
- }
1991
- );
1992
- if (!response.ok) throw new Error("Failed to approve deliverable");
1993
- return response.json();
1543
+ async function disputeAppointment(config, accessToken, id, payload) {
1544
+ return apiRequest(`${config.backendUrls.store}/service/bookings/${id}/dispute`, {
1545
+ method: "POST",
1546
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1547
+ body: JSON.stringify(payload),
1548
+ fallbackErrorMessage: "Failed to open dispute"
1549
+ });
1550
+ }
1551
+ async function confirmAppointmentCompletion(config, accessToken, id, payload) {
1552
+ return apiRequest(`${config.backendUrls.store}/service/bookings/${id}/confirm`, {
1553
+ method: "POST",
1554
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1555
+ body: JSON.stringify(payload ?? {}),
1556
+ fallbackErrorMessage: "Failed to confirm completion"
1557
+ });
1994
1558
  }
1995
- async function requestRevision(config, accessToken, order_id, deliverable_id, feedback) {
1996
- const response = await fetch(
1997
- `${config.backendUrls.store}/service/projects/${order_id}/deliverables/${deliverable_id}/revision`,
1559
+ async function getMilestones(config, accessToken, bookingId) {
1560
+ return apiRequest(`${config.backendUrls.store}/service/bookings/${bookingId}/milestones`, {
1561
+ headers: authHeaders(config, accessToken),
1562
+ fallbackErrorMessage: "Failed to get milestones"
1563
+ });
1564
+ }
1565
+ async function approveMilestone(config, accessToken, bookingId, milestoneId, payload) {
1566
+ return apiRequest(
1567
+ `${config.backendUrls.store}/service/bookings/${bookingId}/milestones/${milestoneId}/approve`,
1998
1568
  {
1999
1569
  method: "POST",
2000
- headers: {
2001
- "Content-Type": "application/json",
2002
- "Authorization": `Bearer ${accessToken}`,
2003
- "X-Storefront-Key": config.storefrontApiKey,
2004
- "X-Business-ID": config.businessId
2005
- },
2006
- body: JSON.stringify({ approved: false, feedback })
1570
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1571
+ body: JSON.stringify(payload ?? {}),
1572
+ fallbackErrorMessage: "Failed to approve milestone"
2007
1573
  }
2008
1574
  );
2009
- if (!response.ok) throw new Error("Failed to request revision");
2010
- return response.json();
2011
1575
  }
2012
1576
  var serviceBookingService = {
2013
1577
  getAvailableSlots,
@@ -2015,41 +1579,111 @@ var serviceBookingService = {
2015
1579
  getAppointments,
2016
1580
  getAppointment,
2017
1581
  cancelAppointment,
2018
- getDeliverables,
2019
- approveDeliverable,
2020
- requestRevision
1582
+ rescheduleAppointment,
1583
+ disputeAppointment,
1584
+ confirmAppointmentCompletion,
1585
+ getMilestones,
1586
+ approveMilestone
2021
1587
  };
2022
1588
 
2023
1589
  // src/services/notifications.service.ts
1590
+ function bearerHeaders(accessToken) {
1591
+ return { Authorization: `Bearer ${accessToken}` };
1592
+ }
2024
1593
  async function sendContactEmail(config, payload) {
2025
- const response = await fetch(`${config.backendUrls.notifications}/contact/send-contact-email`, {
1594
+ return apiRequest(`${config.backendUrls.notifications}/contact/send-contact-email`, {
2026
1595
  method: "POST",
2027
- headers: {
2028
- "Content-Type": "application/json",
2029
- "X-Storefront-Key": config.storefrontApiKey,
2030
- "X-Business-ID": config.businessId
2031
- },
2032
- body: JSON.stringify(payload)
1596
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
1597
+ body: JSON.stringify(payload),
1598
+ fallbackErrorMessage: "Failed to send contact email"
2033
1599
  });
2034
- if (!response.ok) throw new Error("Failed to send contact email");
2035
- return response.json();
2036
1600
  }
2037
1601
  async function sendContactConfirmation(config, payload) {
2038
- const response = await fetch(`${config.backendUrls.notifications}/contact/send-contact-confirmation`, {
1602
+ return apiRequest(`${config.backendUrls.notifications}/contact/send-contact-confirmation`, {
2039
1603
  method: "POST",
2040
- headers: {
2041
- "Content-Type": "application/json",
2042
- "X-Storefront-Key": config.storefrontApiKey,
2043
- "X-Business-ID": config.businessId
2044
- },
2045
- body: JSON.stringify(payload)
1604
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
1605
+ body: JSON.stringify(payload),
1606
+ fallbackErrorMessage: "Failed to send contact confirmation"
1607
+ });
1608
+ }
1609
+ async function getNotificationPreferences(config, accessToken) {
1610
+ return apiRequest(`${config.backendUrls.notifications}/notifications/preferences/`, {
1611
+ headers: bearerHeaders(accessToken),
1612
+ fallbackErrorMessage: "Failed to fetch notification preferences"
1613
+ });
1614
+ }
1615
+ async function updateNotificationPreferences(config, accessToken, payload) {
1616
+ return apiRequest(`${config.backendUrls.notifications}/notifications/preferences/`, {
1617
+ method: "PATCH",
1618
+ headers: { ...JSON_HEADERS, ...bearerHeaders(accessToken) },
1619
+ body: JSON.stringify(payload),
1620
+ fallbackErrorMessage: "Failed to update notification preferences"
1621
+ });
1622
+ }
1623
+ async function getVapidPublicKey(config) {
1624
+ return apiRequest(`${config.backendUrls.notifications}/push/vapid-public-key`, {
1625
+ fallbackErrorMessage: "Failed to fetch VAPID public key"
1626
+ });
1627
+ }
1628
+ async function subscribeToPush(config, accessToken, params) {
1629
+ return apiRequest(`${config.backendUrls.notifications}/push/subscribe`, {
1630
+ method: "POST",
1631
+ headers: { ...JSON_HEADERS, ...bearerHeaders(accessToken) },
1632
+ body: JSON.stringify(params),
1633
+ fallbackErrorMessage: "Failed to subscribe to push notifications"
1634
+ });
1635
+ }
1636
+ async function unsubscribeFromPush(config, accessToken, endpoint) {
1637
+ return apiRequest(`${config.backendUrls.notifications}/push/subscribe`, {
1638
+ method: "DELETE",
1639
+ headers: { ...JSON_HEADERS, ...bearerHeaders(accessToken) },
1640
+ body: JSON.stringify({ endpoint }),
1641
+ fallbackErrorMessage: "Failed to unsubscribe from push notifications"
1642
+ });
1643
+ }
1644
+ async function getNotificationInbox(config, accessToken, params) {
1645
+ const query = new URLSearchParams();
1646
+ if (params?.page) query.set("page", String(params.page));
1647
+ if (params?.limit) query.set("limit", String(params.limit));
1648
+ if (params?.unread_only) query.set("unread_only", "true");
1649
+ return apiRequest(`${config.backendUrls.notifications}/notifications?${query.toString()}`, {
1650
+ headers: bearerHeaders(accessToken),
1651
+ fallbackErrorMessage: "Failed to fetch notifications"
1652
+ });
1653
+ }
1654
+ async function markNotificationRead(config, accessToken, notificationId) {
1655
+ return apiRequest(`${config.backendUrls.notifications}/notifications/${notificationId}/read`, {
1656
+ method: "POST",
1657
+ headers: bearerHeaders(accessToken),
1658
+ fallbackErrorMessage: "Failed to mark notification as read"
1659
+ });
1660
+ }
1661
+ async function markAllNotificationsRead(config, accessToken) {
1662
+ return apiRequest(`${config.backendUrls.notifications}/notifications/mark-all-read`, {
1663
+ method: "POST",
1664
+ headers: bearerHeaders(accessToken),
1665
+ fallbackErrorMessage: "Failed to mark all notifications as read"
1666
+ });
1667
+ }
1668
+ async function trackNotificationClick(config, accessToken, notificationId) {
1669
+ return apiRequest(`${config.backendUrls.notifications}/notifications/${notificationId}/click`, {
1670
+ method: "POST",
1671
+ headers: bearerHeaders(accessToken),
1672
+ fallbackErrorMessage: "Failed to track notification click"
2046
1673
  });
2047
- if (!response.ok) throw new Error("Failed to send contact confirmation");
2048
- return response.json();
2049
1674
  }
2050
1675
  var notificationsService = {
2051
1676
  sendContactEmail,
2052
- sendContactConfirmation
1677
+ sendContactConfirmation,
1678
+ getNotificationPreferences,
1679
+ updateNotificationPreferences,
1680
+ getVapidPublicKey,
1681
+ subscribeToPush,
1682
+ unsubscribeFromPush,
1683
+ getNotificationInbox,
1684
+ markNotificationRead,
1685
+ markAllNotificationsRead,
1686
+ trackNotificationClick
2053
1687
  };
2054
1688
 
2055
1689
  // src/services/app.service.ts
@@ -2067,34 +1701,22 @@ async function googleTranslate(text, target, source = "en") {
2067
1701
 
2068
1702
  // src/services/config.service.ts
2069
1703
  async function getSupportedLanguages(config) {
2070
- const response = await fetch(`${config.backendUrls.payment}/config/supported-languages`, {
2071
- headers: {
2072
- "X-Storefront-Key": config.storefrontApiKey,
2073
- "X-Business-ID": config.businessId
2074
- }
1704
+ return apiRequest(`${config.backendUrls.payment}/config/supported-languages`, {
1705
+ headers: storefrontHeaders(config),
1706
+ fallbackErrorMessage: "Failed to get supported languages"
2075
1707
  });
2076
- if (!response.ok) throw new Error("Failed to get supported languages");
2077
- return response.json();
2078
1708
  }
2079
1709
  async function getSupportedCurrencies(config) {
2080
- const response = await fetch(`${config.backendUrls.payment}/config/supported-currencies`, {
2081
- headers: {
2082
- "X-Storefront-Key": config.storefrontApiKey,
2083
- "X-Business-ID": config.businessId
2084
- }
1710
+ return apiRequest(`${config.backendUrls.payment}/config/supported-currencies`, {
1711
+ headers: storefrontHeaders(config),
1712
+ fallbackErrorMessage: "Failed to get supported currencies"
2085
1713
  });
2086
- if (!response.ok) throw new Error("Failed to get supported currencies");
2087
- return response.json();
2088
1714
  }
2089
1715
  async function getBusinessConfig(config) {
2090
- const response = await fetch(`${config.backendUrls.auth}/internal/businesses/${config.businessId}/`, {
2091
- headers: {
2092
- "X-Storefront-Key": config.storefrontApiKey,
2093
- "X-Business-ID": config.businessId
2094
- }
1716
+ return apiRequest(`${config.backendUrls.auth}/internal/businesses/${config.businessId}/`, {
1717
+ headers: storefrontHeaders(config),
1718
+ fallbackErrorMessage: "Failed to get business config"
2095
1719
  });
2096
- if (!response.ok) throw new Error("Failed to get business config");
2097
- return response.json();
2098
1720
  }
2099
1721
  var configService = {
2100
1722
  getSupportedLanguages,
@@ -2103,105 +1725,245 @@ var configService = {
2103
1725
  };
2104
1726
 
2105
1727
  // src/services/cms.service.ts
2106
- async function listCmsEntries(config, modelSlug, params) {
1728
+ async function listCmsEntries(config, businessUuid, modelSlug, params) {
2107
1729
  const queryParams = new URLSearchParams();
2108
1730
  queryParams.set("page", (params?.page || 1).toString());
2109
1731
  queryParams.set("page_size", (params?.page_size || 50).toString());
2110
- const response = await fetch(
2111
- `${config.backendUrls.cms}/sdk/${config.businessId}/${modelSlug}/entries?${queryParams.toString()}`,
1732
+ return apiRequest(
1733
+ `${config.backendUrls.cms}/sdk/${businessUuid}/${modelSlug}/entries?${queryParams.toString()}`,
2112
1734
  {
2113
- headers: {
2114
- "X-Storefront-Key": config.storefrontApiKey,
2115
- "X-Business-ID": config.businessId
2116
- }
1735
+ headers: storefrontHeaders(config),
1736
+ fallbackErrorMessage: "Failed to list CMS entries"
2117
1737
  }
2118
1738
  );
2119
- if (!response.ok) throw new Error("Failed to list CMS entries");
2120
- return response.json();
2121
1739
  }
2122
- async function getCmsEntry(config, modelSlug, entryId) {
2123
- const response = await fetch(
2124
- `${config.backendUrls.cms}/sdk/${config.businessId}/${modelSlug}/entries/${entryId}`,
1740
+ async function getCmsEntry(config, businessUuid, modelSlug, entryId) {
1741
+ return apiRequest(
1742
+ `${config.backendUrls.cms}/sdk/${businessUuid}/${modelSlug}/entries/${entryId}`,
2125
1743
  {
2126
- headers: {
2127
- "X-Storefront-Key": config.storefrontApiKey,
2128
- "X-Business-ID": config.businessId
2129
- }
1744
+ headers: storefrontHeaders(config),
1745
+ fallbackErrorMessage: "Failed to get CMS entry"
2130
1746
  }
2131
1747
  );
2132
- if (!response.ok) throw new Error("Failed to get CMS entry");
2133
- return response.json();
2134
1748
  }
2135
- async function createCmsEntry(config, accessToken, modelSlug, params) {
2136
- const response = await fetch(
2137
- `${config.backendUrls.cms}/sdk/${config.businessId}/${modelSlug}/entries`,
1749
+ async function createCmsEntry(config, businessUuid, accessToken, modelSlug, params) {
1750
+ return apiRequest(
1751
+ `${config.backendUrls.cms}/sdk/${businessUuid}/${modelSlug}/entries`,
2138
1752
  {
2139
1753
  method: "POST",
2140
- headers: {
2141
- "Content-Type": "application/json",
2142
- "Authorization": `Bearer ${accessToken}`,
2143
- "X-Storefront-Key": config.storefrontApiKey,
2144
- "X-Business-ID": config.businessId
2145
- },
2146
- body: JSON.stringify(params)
1754
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1755
+ body: JSON.stringify(params),
1756
+ fallbackErrorMessage: "Failed to create CMS entry"
2147
1757
  }
2148
1758
  );
2149
- if (!response.ok) throw new Error("Failed to create CMS entry");
2150
- return response.json();
2151
1759
  }
2152
- async function updateCmsEntry(config, accessToken, modelSlug, entryId, params) {
2153
- const response = await fetch(
2154
- `${config.backendUrls.cms}/sdk/${config.businessId}/${modelSlug}/entries/${entryId}`,
1760
+ async function updateCmsEntry(config, businessUuid, accessToken, modelSlug, entryId, params) {
1761
+ return apiRequest(
1762
+ `${config.backendUrls.cms}/sdk/${businessUuid}/${modelSlug}/entries/${entryId}`,
2155
1763
  {
2156
1764
  method: "PUT",
2157
- headers: {
2158
- "Content-Type": "application/json",
2159
- "Authorization": `Bearer ${accessToken}`,
2160
- "X-Storefront-Key": config.storefrontApiKey,
2161
- "X-Business-ID": config.businessId
2162
- },
2163
- body: JSON.stringify(params)
1765
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1766
+ body: JSON.stringify(params),
1767
+ fallbackErrorMessage: "Failed to update CMS entry"
2164
1768
  }
2165
1769
  );
2166
- if (!response.ok) throw new Error("Failed to update CMS entry");
2167
- return response.json();
2168
1770
  }
2169
- async function deleteCmsEntry(config, accessToken, modelSlug, entryId) {
2170
- const response = await fetch(
2171
- `${config.backendUrls.cms}/sdk/${config.businessId}/${modelSlug}/entries/${entryId}`,
1771
+ async function deleteCmsEntry(config, businessUuid, accessToken, modelSlug, entryId) {
1772
+ await apiRequest(
1773
+ `${config.backendUrls.cms}/sdk/${businessUuid}/${modelSlug}/entries/${entryId}`,
2172
1774
  {
2173
1775
  method: "DELETE",
2174
- headers: {
2175
- "Authorization": `Bearer ${accessToken}`,
2176
- "X-Storefront-Key": config.storefrontApiKey,
2177
- "X-Business-ID": config.businessId
2178
- }
1776
+ headers: authHeaders(config, accessToken),
1777
+ fallbackErrorMessage: "Failed to delete CMS entry"
2179
1778
  }
2180
1779
  );
2181
- if (!response.ok) throw new Error("Failed to delete CMS entry");
2182
1780
  }
2183
1781
 
2184
- // src/utils/cache.ts
2185
- var CACHE_TIERS = {
2186
- none: { staleTime: 0, gcTime: 0 },
2187
- infinity: { staleTime: Infinity, gcTime: 0 },
2188
- low: { staleTime: 1e3 * 30, gcTime: 1e3 * 60 },
2189
- // 30s / 1m
2190
- medium: { staleTime: 1e3 * 60 * 5, gcTime: 1e3 * 60 * 10 },
2191
- // 5m / 10m
2192
- high: { staleTime: 1e3 * 60 * 30, gcTime: 1e3 * 60 * 60 }
2193
- // 30m / 1h
2194
- };
2195
- var isDevelopment = true;
2196
- function setEnvironmentMode(development) {
2197
- isDevelopment = development;
1782
+ // src/services/legal.service.ts
1783
+ async function getMerchantLegalDocument(config, businessUuid, documentType, fetchOptions) {
1784
+ if (!businessUuid || !config?.backendUrls?.cms) return null;
1785
+ try {
1786
+ const res = await fetch(
1787
+ `${config.backendUrls.cms}/cms/legal/storefront/${businessUuid}/${documentType}`,
1788
+ fetchOptions
1789
+ );
1790
+ if (!res.ok) return null;
1791
+ return await res.json();
1792
+ } catch {
1793
+ return null;
1794
+ }
2198
1795
  }
2199
- var getCacheTier = (tier) => {
2200
- if (isDevelopment) return CACHE_TIERS.none;
2201
- return CACHE_TIERS[tier];
2202
- };
2203
1796
 
2204
- // src/utils/logger.ts
1797
+ // src/services/review.service.ts
1798
+ function fetchWithConfig(url, config, options = {}) {
1799
+ const headers = {
1800
+ ...JSON_HEADERS,
1801
+ Accept: "application/json",
1802
+ ...options.headers
1803
+ };
1804
+ if (config.storefrontApiKey) headers["X-Storefront-Key"] = config.storefrontApiKey;
1805
+ if (config.businessId) headers["X-Business-ID"] = config.businessId;
1806
+ if (config.authToken) headers["Authorization"] = `Bearer ${config.authToken}`;
1807
+ return apiRequest(url, { ...options, headers, fallbackErrorMessage: options.fallbackErrorMessage ?? "Request failed" });
1808
+ }
1809
+ async function createReview(reviewData, config) {
1810
+ const url = `${config.backendUrls.crm}/reviews`;
1811
+ return fetchWithConfig(url, config, {
1812
+ method: "POST",
1813
+ body: JSON.stringify(reviewData),
1814
+ fallbackErrorMessage: "Failed to create review"
1815
+ });
1816
+ }
1817
+ async function updateReview(reviewId, reviewData, config) {
1818
+ const url = `${config.backendUrls.crm}/reviews/${reviewId}`;
1819
+ return fetchWithConfig(url, config, {
1820
+ method: "PUT",
1821
+ body: JSON.stringify(reviewData),
1822
+ fallbackErrorMessage: "Failed to update review"
1823
+ });
1824
+ }
1825
+ async function getProductReviews(productId, params = {}, config) {
1826
+ const queryParams = new URLSearchParams();
1827
+ if (params.page) queryParams.append("page", params.page.toString());
1828
+ if (params.page_size) queryParams.append("page_size", params.page_size.toString());
1829
+ if (params.moderation_status) queryParams.append("moderation_status", params.moderation_status);
1830
+ const url = `${config.backendUrls.crm}/products/${productId}/reviews?${queryParams.toString()}`;
1831
+ return fetchWithConfig(url, config, { fallbackErrorMessage: "Failed to fetch product reviews" });
1832
+ }
1833
+ async function createComment(commentData, config) {
1834
+ const url = `${config.backendUrls.crm}/comments`;
1835
+ return fetchWithConfig(url, config, {
1836
+ method: "POST",
1837
+ body: JSON.stringify(commentData),
1838
+ fallbackErrorMessage: "Failed to create comment"
1839
+ });
1840
+ }
1841
+ async function updateComment(commentId, commentData, config) {
1842
+ const url = `${config.backendUrls.crm}/comments/${commentId}`;
1843
+ return fetchWithConfig(url, config, {
1844
+ method: "PUT",
1845
+ body: JSON.stringify(commentData),
1846
+ fallbackErrorMessage: "Failed to update comment"
1847
+ });
1848
+ }
1849
+ async function getProductComments(productId, params = {}, config) {
1850
+ const queryParams = new URLSearchParams();
1851
+ if (params.page) queryParams.append("page", params.page.toString());
1852
+ if (params.page_size) queryParams.append("page_size", params.page_size.toString());
1853
+ if (params.moderation_status) queryParams.append("moderation_status", params.moderation_status);
1854
+ const url = `${config.backendUrls.crm}/products/${productId}/comments?${queryParams.toString()}`;
1855
+ return fetchWithConfig(url, config, { fallbackErrorMessage: "Failed to fetch product comments" });
1856
+ }
1857
+ async function getPendingReviews(config) {
1858
+ const url = `${config.backendUrls.crm}/admin/reviews/pending`;
1859
+ return fetchWithConfig(url, config, { fallbackErrorMessage: "Failed to fetch pending reviews" });
1860
+ }
1861
+ async function getPendingComments(config) {
1862
+ const url = `${config.backendUrls.crm}/admin/comments/pending`;
1863
+ return fetchWithConfig(url, config, { fallbackErrorMessage: "Failed to fetch pending comments" });
1864
+ }
1865
+ async function moderateReview(reviewId, action, config) {
1866
+ const url = `${config.backendUrls.crm}/admin/reviews/${reviewId}/moderate`;
1867
+ return fetchWithConfig(url, config, {
1868
+ method: "POST",
1869
+ body: JSON.stringify(action),
1870
+ fallbackErrorMessage: "Failed to moderate review"
1871
+ });
1872
+ }
1873
+ async function moderateComment(commentId, action, config) {
1874
+ const url = `${config.backendUrls.crm}/admin/comments/${commentId}/moderate`;
1875
+ return fetchWithConfig(url, config, {
1876
+ method: "POST",
1877
+ body: JSON.stringify(action),
1878
+ fallbackErrorMessage: "Failed to moderate comment"
1879
+ });
1880
+ }
1881
+ async function merchantReplyToReview(reviewId, replyData, config) {
1882
+ const url = `${config.backendUrls.crm}/admin/reviews/${reviewId}/reply`;
1883
+ return fetchWithConfig(url, config, {
1884
+ method: "POST",
1885
+ body: JSON.stringify(replyData),
1886
+ fallbackErrorMessage: "Failed to post merchant reply"
1887
+ });
1888
+ }
1889
+
1890
+ // src/services/media.service.ts
1891
+ async function uploadMediaFile(params) {
1892
+ const presignedUrlEndpoint = params.presignedUrlEndpoint ?? "/api/auth/media/presigned-url";
1893
+ const confirmUploadEndpoint = params.confirmUploadEndpoint ?? "/api/auth/media/confirm-upload";
1894
+ const { presigned_url, storage_path } = await apiRequest(presignedUrlEndpoint, {
1895
+ method: "POST",
1896
+ headers: JSON_HEADERS,
1897
+ credentials: "include",
1898
+ body: JSON.stringify({
1899
+ filename: params.file.name,
1900
+ content_type: params.file.type,
1901
+ context: params.context,
1902
+ entity_id: params.entityId,
1903
+ affiliation: params.affiliation,
1904
+ owner_id: params.ownerId
1905
+ }),
1906
+ fallbackErrorMessage: "Could not start upload"
1907
+ });
1908
+ const putResponse = await fetch(presigned_url, {
1909
+ method: "PUT",
1910
+ headers: { "Content-Type": params.file.type },
1911
+ body: params.file
1912
+ });
1913
+ if (!putResponse.ok) {
1914
+ throw new Error(`Failed to upload ${params.file.name}`);
1915
+ }
1916
+ return apiRequest(confirmUploadEndpoint, {
1917
+ method: "POST",
1918
+ headers: JSON_HEADERS,
1919
+ credentials: "include",
1920
+ body: JSON.stringify({
1921
+ storage_path,
1922
+ entity_id: params.entityId,
1923
+ original_filename: params.file.name,
1924
+ content_type: params.file.type,
1925
+ size: params.file.size,
1926
+ context: params.context,
1927
+ affiliation: params.affiliation,
1928
+ owner_id: params.ownerId
1929
+ }),
1930
+ fallbackErrorMessage: `Could not confirm upload of ${params.file.name}`
1931
+ });
1932
+ }
1933
+ async function uploadMediaFiles(files, params) {
1934
+ const assets = [];
1935
+ const errors = [];
1936
+ for (const file of files) {
1937
+ try {
1938
+ assets.push(await uploadMediaFile({ ...params, file }));
1939
+ } catch (err) {
1940
+ errors.push({ file: file.name, message: err instanceof Error ? err.message : "Upload failed" });
1941
+ }
1942
+ }
1943
+ return { assets, errors };
1944
+ }
1945
+
1946
+ // src/utils/cache.ts
1947
+ var CACHE_TIERS = {
1948
+ none: { staleTime: 0, gcTime: 0 },
1949
+ infinity: { staleTime: Infinity, gcTime: 0 },
1950
+ low: { staleTime: 1e3 * 30, gcTime: 1e3 * 60 },
1951
+ // 30s / 1m
1952
+ medium: { staleTime: 1e3 * 60 * 5, gcTime: 1e3 * 60 * 10 },
1953
+ // 5m / 10m
1954
+ high: { staleTime: 1e3 * 60 * 30, gcTime: 1e3 * 60 * 60 }
1955
+ // 30m / 1h
1956
+ };
1957
+ var isDevelopment = true;
1958
+ function setEnvironmentMode(development) {
1959
+ isDevelopment = development;
1960
+ }
1961
+ var getCacheTier = (tier) => {
1962
+ if (isDevelopment) return CACHE_TIERS.none;
1963
+ return CACHE_TIERS[tier];
1964
+ };
1965
+
1966
+ // src/utils/logger.ts
2205
1967
  var isDevelopment2 = true;
2206
1968
  function setLoggerEnvironmentMode(development) {
2207
1969
  isDevelopment2 = development;
@@ -2266,111 +2028,6 @@ var logger = {
2266
2028
  })
2267
2029
  };
2268
2030
 
2269
- // src/utils/sanitize-url.ts
2270
- var InvalidBaseURLError = class extends Error {
2271
- constructor(message) {
2272
- super(message);
2273
- this.name = "InvalidBaseURLError";
2274
- }
2275
- };
2276
- var ALLOWED_SCHEMES = /* @__PURE__ */ new Set(["http:", "https:", "ws:", "wss:"]);
2277
- function sanitizeBaseUrl(url, name = "URL") {
2278
- if (url == null) {
2279
- throw new InvalidBaseURLError(`${name} is not set`);
2280
- }
2281
- const cleaned0 = url.trim();
2282
- if (!cleaned0) {
2283
- throw new InvalidBaseURLError(`${name} is empty`);
2284
- }
2285
- const cleaned = cleaned0.replace(/\/+$/, "");
2286
- let parsed;
2287
- try {
2288
- parsed = new URL(cleaned);
2289
- } catch {
2290
- throw new InvalidBaseURLError(
2291
- `${name} must start with http://, https://, ws://, or wss://, got: ${url}`
2292
- );
2293
- }
2294
- if (!ALLOWED_SCHEMES.has(parsed.protocol)) {
2295
- throw new InvalidBaseURLError(
2296
- `${name} must start with http://, https://, ws://, or wss://, got: ${url}`
2297
- );
2298
- }
2299
- if (!parsed.hostname) {
2300
- throw new InvalidBaseURLError(`${name} is missing a host: ${url}`);
2301
- }
2302
- if (parsed.search || parsed.hash) {
2303
- throw new InvalidBaseURLError(
2304
- `${name} should not contain a query or fragment: ${url}`
2305
- );
2306
- }
2307
- return cleaned;
2308
- }
2309
-
2310
- // src/utils/customer.ts
2311
- function buildFullName2(firstname, lastname) {
2312
- const name = [firstname, lastname].filter(Boolean).join(" ").trim();
2313
- return name || void 0;
2314
- }
2315
- function splitFullName2(fullName) {
2316
- if (!fullName?.trim()) return { firstname: "", lastname: "" };
2317
- const parts = fullName.trim().split(/\s+/);
2318
- return {
2319
- firstname: parts[0] ?? "",
2320
- lastname: parts.slice(1).join(" ")
2321
- };
2322
- }
2323
- function getCustomerDisplayName(user) {
2324
- const fromParts = buildFullName2(user.firstname, user.lastname);
2325
- return fromParts || user.full_name || user.email.split("@")[0];
2326
- }
2327
- function getStorefrontUrl(businessId, customDomain) {
2328
- if (customDomain && customDomain.trim()) {
2329
- const cleanDomain = customDomain.trim().replace(/^https?:\/\//, "");
2330
- return `https://${cleanDomain}`;
2331
- }
2332
- const cleanBid = (businessId || "store").toLowerCase().trim();
2333
- return `https://${cleanBid}.kasuvia.com`;
2334
- }
2335
-
2336
- // src/utils/checkout.ts
2337
- function buildDeliveryAddress(customer, location) {
2338
- return {
2339
- first_name: customer.first_name,
2340
- last_name: customer.last_name,
2341
- phone: customer.phone,
2342
- ...location
2343
- };
2344
- }
2345
- function getCheckoutCustomerName(customer) {
2346
- return buildFullName2(customer.first_name, customer.last_name) ?? customer.email.split("@")[0];
2347
- }
2348
- function enrichCompletedOrder(order, options) {
2349
- const customerName = getCheckoutCustomerName(options.customer);
2350
- return {
2351
- ...order,
2352
- customerName: order.customerName || customerName,
2353
- customerEmail: order.customerEmail || options.customer.email,
2354
- shippingAddress: order.shippingAddress ?? (options.productType === "physical" && options.location ? buildDeliveryAddress(options.customer, options.location) : void 0),
2355
- bespokeBrief: order.bespokeBrief ?? options.bespokeBrief
2356
- };
2357
- }
2358
-
2359
- // src/utils/error-handler.ts
2360
- function getErrorMessage(error, fallback = "Something went wrong") {
2361
- if (error instanceof Error) {
2362
- return error.message;
2363
- }
2364
- if (error instanceof Response) {
2365
- return `HTTP ${error.status}: ${error.statusText}`;
2366
- }
2367
- if (typeof error === "object" && error !== null) {
2368
- const err = error;
2369
- return err.detail || err.message || fallback;
2370
- }
2371
- return fallback;
2372
- }
2373
-
2374
2031
  // src/utils/storage.ts
2375
2032
  function getPrefixedKey(businessId, key) {
2376
2033
  const cleanKey = key.startsWith("kasuvia_") ? key.substring(5) : key;
@@ -2457,6 +2114,454 @@ var localStore = {
2457
2114
  }
2458
2115
  };
2459
2116
 
2117
+ // src/utils/sanitize-url.ts
2118
+ var InvalidBaseURLError = class extends Error {
2119
+ constructor(message) {
2120
+ super(message);
2121
+ this.name = "InvalidBaseURLError";
2122
+ }
2123
+ };
2124
+ var ALLOWED_SCHEMES = /* @__PURE__ */ new Set(["http:", "https:", "ws:", "wss:"]);
2125
+ function sanitizeBaseUrl(url, name = "URL") {
2126
+ if (url == null) {
2127
+ throw new InvalidBaseURLError(`${name} is not set`);
2128
+ }
2129
+ const cleaned0 = url.trim();
2130
+ if (!cleaned0) {
2131
+ throw new InvalidBaseURLError(`${name} is empty`);
2132
+ }
2133
+ const cleaned = cleaned0.replace(/\/+$/, "");
2134
+ let parsed;
2135
+ try {
2136
+ parsed = new URL(cleaned);
2137
+ } catch {
2138
+ throw new InvalidBaseURLError(
2139
+ `${name} must start with http://, https://, ws://, or wss://, got: ${url}`
2140
+ );
2141
+ }
2142
+ if (!ALLOWED_SCHEMES.has(parsed.protocol)) {
2143
+ throw new InvalidBaseURLError(
2144
+ `${name} must start with http://, https://, ws://, or wss://, got: ${url}`
2145
+ );
2146
+ }
2147
+ if (!parsed.hostname) {
2148
+ throw new InvalidBaseURLError(`${name} is missing a host: ${url}`);
2149
+ }
2150
+ if (parsed.search || parsed.hash) {
2151
+ throw new InvalidBaseURLError(
2152
+ `${name} should not contain a query or fragment: ${url}`
2153
+ );
2154
+ }
2155
+ return cleaned;
2156
+ }
2157
+
2158
+ // src/utils/checkout.ts
2159
+ function buildDeliveryAddress(customer, location) {
2160
+ if (!customer.first_name || !customer.last_name || !customer.phone) {
2161
+ throw new Error("Customer first_name, last_name, and phone are required for delivery address");
2162
+ }
2163
+ if (!location.street || !location.city || !location.state || !location.country) {
2164
+ throw new Error("Location street, city, state, and country are required for delivery address");
2165
+ }
2166
+ return {
2167
+ first_name: customer.first_name,
2168
+ last_name: customer.last_name,
2169
+ phone: customer.phone,
2170
+ street: location.street,
2171
+ city: location.city,
2172
+ state: location.state,
2173
+ lga: location.lga,
2174
+ country: location.country,
2175
+ postal: location.postal
2176
+ };
2177
+ }
2178
+ function getCheckoutCustomerName(customer) {
2179
+ return buildFullName(customer.first_name, customer.last_name) ?? customer.email.split("@")[0];
2180
+ }
2181
+ function enrichCompletedOrder(order, options) {
2182
+ const customerName = getCheckoutCustomerName(options.customer);
2183
+ return {
2184
+ ...order,
2185
+ customer_name: order.customer_name || customerName,
2186
+ customer_email: order.customer_email || options.customer.email,
2187
+ customerName: order.customerName || customerName,
2188
+ customerEmail: order.customerEmail || options.customer.email,
2189
+ shippingAddress: order.shippingAddress ?? (options.productType === "physical" /* PHYSICAL */ && options.location ? buildDeliveryAddress(options.customer, options.location) : void 0),
2190
+ bespoke_brief: order.bespoke_brief ?? options.bespokeBrief,
2191
+ bespokeBrief: order.bespokeBrief ?? options.bespokeBrief
2192
+ };
2193
+ }
2194
+
2195
+ // src/utils/error-handler.ts
2196
+ function getErrorMessage(error, fallback = "Something went wrong") {
2197
+ if (error instanceof Error) {
2198
+ return error.message;
2199
+ }
2200
+ if (error instanceof Response) {
2201
+ return `HTTP ${error.status}: ${error.statusText}`;
2202
+ }
2203
+ if (typeof error === "object" && error !== null) {
2204
+ const err = error;
2205
+ return err.detail || err.message || fallback;
2206
+ }
2207
+ return fallback;
2208
+ }
2209
+
2210
+ // src/utils/format.ts
2211
+ var COMPACT_THRESHOLDS = [
2212
+ { value: 1e12, suffixUpper: "T", suffixLower: "t", full: "Trillion" },
2213
+ { value: 1e9, suffixUpper: "B", suffixLower: "b", full: "Billion" },
2214
+ { value: 1e6, suffixUpper: "M", suffixLower: "m", full: "Million" },
2215
+ { value: 1e3, suffixUpper: "K", suffixLower: "k", full: "Thousand" }
2216
+ ];
2217
+ var BYTE_THRESHOLDS = [
2218
+ { value: 1024 ** 5, label: "PB" },
2219
+ { value: 1024 ** 4, label: "TB" },
2220
+ { value: 1024 ** 3, label: "GB" },
2221
+ { value: 1024 ** 2, label: "MB" },
2222
+ { value: 1024 ** 1, label: "KB" }
2223
+ ];
2224
+ function trimDecimals(value, decimals) {
2225
+ if (decimals !== void 0) return value.toFixed(decimals);
2226
+ const fixed = value.toFixed(10);
2227
+ return parseFloat(fixed).toString();
2228
+ }
2229
+ function autoDecimals(value, defaultDecimals, decimals) {
2230
+ return (decimals !== void 0 ? value.toFixed(decimals) : value.toFixed(defaultDecimals)).replace(/\.?0+$/, "");
2231
+ }
2232
+ function applySign(str, num, showSign) {
2233
+ if (showSign && num > 0) return `+${str}`;
2234
+ return str;
2235
+ }
2236
+ function toOrdinal(n) {
2237
+ const abs = Math.abs(Math.round(n));
2238
+ const mod100 = abs % 100;
2239
+ const mod10 = abs % 10;
2240
+ if (mod100 >= 11 && mod100 <= 13) return `${abs}th`;
2241
+ if (mod10 === 1) return `${abs}st`;
2242
+ if (mod10 === 2) return `${abs}nd`;
2243
+ if (mod10 === 3) return `${abs}rd`;
2244
+ return `${abs}th`;
2245
+ }
2246
+ function toRoman(n) {
2247
+ if (n <= 0 || n > 3999 || !Number.isInteger(n)) {
2248
+ throw new RangeError("Roman numerals only support integers between 1 and 3999.");
2249
+ }
2250
+ const vals = [1e3, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
2251
+ const syms = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"];
2252
+ let result = "";
2253
+ let remaining = n;
2254
+ for (let i = 0; i < vals.length; i++) {
2255
+ while (remaining >= vals[i]) {
2256
+ result += syms[i];
2257
+ remaining -= vals[i];
2258
+ }
2259
+ }
2260
+ return result;
2261
+ }
2262
+ function formatCompact(n, suffixKey, lowerFull, decimals) {
2263
+ const abs = Math.abs(n);
2264
+ for (const tier of COMPACT_THRESHOLDS) {
2265
+ if (abs >= tier.value) {
2266
+ const divided = n / tier.value;
2267
+ const suffix = suffixKey === "full" ? lowerFull ? tier.full.toLowerCase() : tier.full : tier[suffixKey];
2268
+ const spacer = suffixKey === "full" ? " " : "";
2269
+ return `${autoDecimals(divided, 2, decimals)}${spacer}${suffix}`;
2270
+ }
2271
+ }
2272
+ return trimDecimals(n, decimals ?? 0);
2273
+ }
2274
+ function formatDecimal(n, separator, decimalChar = ".", decimals) {
2275
+ const fixed = decimals !== void 0 ? Math.abs(n).toFixed(decimals) : Math.abs(n).toFixed(0);
2276
+ const [intPart, decPart] = fixed.split(".");
2277
+ const grouped = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
2278
+ const sign = n < 0 ? "-" : "";
2279
+ return decPart !== void 0 ? `${sign}${grouped}${decimalChar}${decPart}` : `${sign}${grouped}`;
2280
+ }
2281
+ function formatBytes(n, decimals = 2) {
2282
+ if (n === 0) return "0 B";
2283
+ const abs = Math.abs(n);
2284
+ for (const tier of BYTE_THRESHOLDS) {
2285
+ if (abs >= tier.value) {
2286
+ return `${(n / tier.value).toFixed(decimals)} ${tier.label}`;
2287
+ }
2288
+ }
2289
+ return `${n.toFixed(0)} B`;
2290
+ }
2291
+ function formatNumber(value, options = {}) {
2292
+ const {
2293
+ style = "compact",
2294
+ decimals,
2295
+ currencySymbol = "$",
2296
+ locale = "en-US",
2297
+ showSign
2298
+ } = options;
2299
+ if (!isFinite(value)) return String(value);
2300
+ let result;
2301
+ switch (style) {
2302
+ case "compact":
2303
+ result = formatCompact(value, "suffixUpper", false, decimals);
2304
+ break;
2305
+ case "compact-lower":
2306
+ result = formatCompact(value, "suffixLower", false, decimals);
2307
+ break;
2308
+ case "compact-full":
2309
+ result = formatCompact(value, "full", false, decimals);
2310
+ break;
2311
+ case "compact-full-lower":
2312
+ result = formatCompact(value, "full", true, decimals);
2313
+ break;
2314
+ case "currency": {
2315
+ const d = decimals ?? 2;
2316
+ const formatted = formatDecimal(value, ",", ".", d);
2317
+ result = `${currencySymbol}${formatted}`;
2318
+ break;
2319
+ }
2320
+ case "currency-compact": {
2321
+ const compact2 = formatCompact(Math.abs(value), "suffixUpper", false, decimals);
2322
+ const sign = value < 0 ? "-" : "";
2323
+ result = `${sign}${currencySymbol}${compact2}`;
2324
+ break;
2325
+ }
2326
+ case "decimal":
2327
+ result = formatDecimal(value, ",", ".", decimals);
2328
+ break;
2329
+ case "decimal-dot":
2330
+ result = formatDecimal(value, ".", ",", decimals);
2331
+ break;
2332
+ case "decimal-space":
2333
+ result = formatDecimal(value, "\xA0", ".", decimals);
2334
+ break;
2335
+ case "plain":
2336
+ result = decimals !== void 0 ? value.toFixed(decimals) : String(value);
2337
+ break;
2338
+ case "scientific": {
2339
+ const d = decimals ?? 2;
2340
+ result = value.toExponential(d);
2341
+ break;
2342
+ }
2343
+ case "ordinal":
2344
+ return toOrdinal(value);
2345
+ // sign not applicable
2346
+ case "percent": {
2347
+ const d = decimals ?? 1;
2348
+ result = `${(value * 100).toFixed(d).replace(/\.?0+$/, "")}%`;
2349
+ break;
2350
+ }
2351
+ case "bytes":
2352
+ result = formatBytes(value, decimals);
2353
+ break;
2354
+ case "roman":
2355
+ return toRoman(Math.round(value));
2356
+ // sign not applicable
2357
+ default:
2358
+ result = String(value);
2359
+ }
2360
+ return applySign(result, value, showSign);
2361
+ }
2362
+ var compact = (n, decimals) => formatNumber(n, { style: "compact", decimals });
2363
+ var compactLower = (n, decimals) => formatNumber(n, { style: "compact-lower", decimals });
2364
+ var compactFull = (n, decimals) => formatNumber(n, { style: "compact-full", decimals });
2365
+ var currencyFormat = (n, symbol = "$", decimals = 2) => formatNumber(n, { style: "currency", currencySymbol: symbol, decimals });
2366
+ var currencyCompact = (n, symbol = "$") => formatNumber(n, { style: "currency-compact", currencySymbol: symbol });
2367
+ var decimal = (n, decimals) => formatNumber(n, { style: "decimal", decimals });
2368
+ var percent = (n, decimals) => formatNumber(n, { style: "percent", decimals });
2369
+ var bytes = (n, decimals) => formatNumber(n, { style: "bytes", decimals });
2370
+ var roman = (n) => formatNumber(n, { style: "roman" });
2371
+ var ordinal = (n) => formatNumber(n, { style: "ordinal" });
2372
+ function formatTimeString(time) {
2373
+ const [hoursStr, minutesStr] = time.split(":");
2374
+ const hours = parseInt(hoursStr, 10);
2375
+ if (isNaN(hours) || minutesStr === void 0) return time;
2376
+ const period = hours >= 12 ? "PM" : "AM";
2377
+ const displayHours = hours % 12 || 12;
2378
+ return `${displayHours}:${minutesStr} ${period}`;
2379
+ }
2380
+ function formatPrice(amount, currency, locale = "en-US") {
2381
+ if (amount == null || isNaN(amount)) return "";
2382
+ if (!currency) return `N/A ${amount.toLocaleString()}`;
2383
+ const code = currency.toUpperCase();
2384
+ try {
2385
+ return new Intl.NumberFormat(locale, {
2386
+ style: "currency",
2387
+ currency: code,
2388
+ minimumFractionDigits: 0,
2389
+ maximumFractionDigits: 2
2390
+ }).format(amount);
2391
+ } catch {
2392
+ return `${code} ${amount.toLocaleString()}`;
2393
+ }
2394
+ }
2395
+ function getCurrencySymbol(currency, locale = "en-US") {
2396
+ const formatter = new Intl.NumberFormat(locale, {
2397
+ style: "currency",
2398
+ currency
2399
+ });
2400
+ const parts = formatter.formatToParts(0);
2401
+ const symbolPart = parts.find((part) => part.type === "currency");
2402
+ return symbolPart?.value || currency;
2403
+ }
2404
+ function formatCompactPrice(amount, currency, locale = "en-US") {
2405
+ if (!currency) return `N/A ${formatNumber(amount, { style: "compact", decimals: 0 })}`;
2406
+ const symbol = getCurrencySymbol(currency, locale);
2407
+ const compactStr = formatNumber(amount, { style: "compact", decimals: 0 });
2408
+ return `${symbol}${compactStr}`;
2409
+ }
2410
+
2411
+ // src/utils/geolocation.ts
2412
+ async function handlePinDrop(coords, setAddress, setIsReverseGeocoding, config) {
2413
+ setAddress((prev) => ({
2414
+ ...prev,
2415
+ latitude: coords.latitude,
2416
+ longitude: coords.longitude,
2417
+ coordinate_source: "pin_drop"
2418
+ }));
2419
+ if (setIsReverseGeocoding) {
2420
+ setIsReverseGeocoding(true);
2421
+ try {
2422
+ if (config) {
2423
+ const data = await reverseGeocode(config, {
2424
+ latitude: coords.latitude,
2425
+ longitude: coords.longitude
2426
+ });
2427
+ setAddress((prev) => ({
2428
+ ...prev,
2429
+ city: data.city || prev.city,
2430
+ state: data.state || prev.state,
2431
+ country: data.country_code || prev.country
2432
+ }));
2433
+ } else {
2434
+ logger.warn("[GeolocationHelper] SdkConfig is required to perform reverse geocoding.");
2435
+ }
2436
+ } catch (error) {
2437
+ logger.error("[GeolocationHelper] Reverse geocoding failed:", error);
2438
+ } finally {
2439
+ setIsReverseGeocoding(false);
2440
+ }
2441
+ }
2442
+ }
2443
+ async function handlePlusCodeChange(plusCode, setAddress, config) {
2444
+ setAddress((prev) => ({
2445
+ ...prev,
2446
+ plus_code: plusCode
2447
+ }));
2448
+ if (plusCode) {
2449
+ setAddress((prev) => ({
2450
+ ...prev,
2451
+ plus_code: plusCode,
2452
+ coordinate_source: "plus_code"
2453
+ }));
2454
+ try {
2455
+ if (config) {
2456
+ const data = await decodePlusCode(config, { plus_code: plusCode });
2457
+ setAddress((prev) => ({
2458
+ ...prev,
2459
+ latitude: data.latitude,
2460
+ longitude: data.longitude
2461
+ }));
2462
+ } else {
2463
+ logger.warn("[GeolocationHelper] SdkConfig is required to decode Plus Code.");
2464
+ }
2465
+ } catch (error) {
2466
+ logger.error("[GeolocationHelper] Plus Code decoding failed:", error);
2467
+ }
2468
+ } else {
2469
+ setAddress((prev) => ({
2470
+ ...prev,
2471
+ plus_code: "",
2472
+ coordinate_source: void 0
2473
+ }));
2474
+ }
2475
+ }
2476
+ function applyAddressSuggestion(suggestion, setAddress) {
2477
+ setAddress((prev) => ({
2478
+ ...prev,
2479
+ street: suggestion.line1 || prev.street,
2480
+ city: suggestion.city || prev.city,
2481
+ state: suggestion.state || prev.state,
2482
+ postal: suggestion.postal_code || prev.postal,
2483
+ country: suggestion.country || prev.country,
2484
+ latitude: suggestion.latitude ?? prev.latitude,
2485
+ longitude: suggestion.longitude ?? prev.longitude,
2486
+ coordinate_source: suggestion.latitude != null && suggestion.longitude != null ? "manual" : prev.coordinate_source
2487
+ }));
2488
+ }
2489
+
2490
+ // src/utils/status-colors.ts
2491
+ var appointmentStatusColors = {
2492
+ scheduled: "bg-yellow-100 text-yellow-800 hover:bg-yellow-200",
2493
+ confirmed: "bg-green-100 text-green-800 hover:bg-green-200",
2494
+ in_progress: "bg-blue-100 text-blue-800 hover:bg-blue-200",
2495
+ completed: "bg-gray-100 text-gray-800 hover:bg-gray-200",
2496
+ voided: "bg-red-100 text-red-800 hover:bg-red-200",
2497
+ no_show: "bg-red-100 text-red-800 hover:bg-red-200"
2498
+ };
2499
+ var milestoneStatusColors = {
2500
+ pending: "bg-gray-100 text-gray-800",
2501
+ funded: "bg-blue-100 text-blue-800",
2502
+ submitted: "bg-yellow-100 text-yellow-800",
2503
+ approved: "bg-green-100 text-green-800",
2504
+ released: "bg-green-100 text-green-800",
2505
+ disputed: "bg-red-100 text-red-800",
2506
+ refunded: "bg-gray-100 text-gray-800"
2507
+ };
2508
+ var orderStatusColors = {
2509
+ pending: "bg-gray-100 text-gray-800",
2510
+ confirmed: "bg-blue-100 text-blue-800",
2511
+ processing: "bg-yellow-100 text-yellow-800",
2512
+ packed: "bg-yellow-100 text-yellow-800",
2513
+ picked_up: "bg-blue-100 text-blue-800",
2514
+ ready_for_pickup: "bg-blue-100 text-blue-800",
2515
+ picked_up_by_customer: "bg-green-100 text-green-800",
2516
+ in_transit: "bg-blue-100 text-blue-800",
2517
+ shipped: "bg-blue-100 text-blue-800",
2518
+ delivered: "bg-green-100 text-green-800",
2519
+ voided: "bg-red-100 text-red-800",
2520
+ refunded: "bg-gray-100 text-gray-800",
2521
+ failed: "bg-red-100 text-red-800"
2522
+ };
2523
+ var orderStatusLabels = {
2524
+ pending: "Order Placed",
2525
+ confirmed: "Confirmed",
2526
+ processing: "Preparing",
2527
+ packed: "Packed",
2528
+ picked_up: "Picked Up",
2529
+ ready_for_pickup: "Ready for Pickup",
2530
+ picked_up_by_customer: "Collected",
2531
+ in_transit: "In Transit",
2532
+ shipped: "On the Way",
2533
+ delivered: "Delivered",
2534
+ voided: "Voided",
2535
+ refunded: "Refunded",
2536
+ failed: "Failed"
2537
+ };
2538
+
2539
+ // src/utils/youtube.ts
2540
+ function getYouTubeID(url) {
2541
+ const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/;
2542
+ const match = url.match(regExp);
2543
+ return match && match[2].length === 11 ? match[2] : null;
2544
+ }
2545
+
2546
+ // src/utils/cookieConsent.ts
2547
+ var STORAGE_KEY = "kasuvia_cookie_consent";
2548
+ var COOKIE_CONSENT_CHANGED_EVENT = "kasuvia:cookie-consent-changed";
2549
+ function getCookieConsent() {
2550
+ try {
2551
+ const value = window.localStorage.getItem(STORAGE_KEY);
2552
+ return value === "accepted" || value === "rejected" ? value : null;
2553
+ } catch {
2554
+ return null;
2555
+ }
2556
+ }
2557
+ function setCookieConsent(value) {
2558
+ try {
2559
+ window.localStorage.setItem(STORAGE_KEY, value);
2560
+ window.dispatchEvent(new Event(COOKIE_CONSENT_CHANGED_EVENT));
2561
+ } catch {
2562
+ }
2563
+ }
2564
+
2460
2565
  // src/utils/validators.ts
2461
2566
  function validateEmail(email) {
2462
2567
  if (!email || email.trim() === "") {
@@ -2527,69 +2632,142 @@ function validateMessage(message) {
2527
2632
  }
2528
2633
  return null;
2529
2634
  }
2635
+ function isValidCountryCode(code) {
2636
+ if (!code) return false;
2637
+ return /^[A-Z]{2}$/.test(code) && code.length === 2;
2638
+ }
2639
+
2640
+ // src/utils/guest-session.ts
2641
+ var GUEST_SESSION_KEY = "guest_session_id";
2642
+ function getOrCreateGuestSessionId(businessId) {
2643
+ if (typeof window === "undefined") return null;
2644
+ try {
2645
+ const existing = localStore.get(businessId, GUEST_SESSION_KEY);
2646
+ if (existing) return existing;
2647
+ const generated = crypto.randomUUID();
2648
+ localStore.set(businessId, GUEST_SESSION_KEY, generated);
2649
+ return generated;
2650
+ } catch {
2651
+ return null;
2652
+ }
2653
+ }
2654
+ function clearGuestSessionId(businessId) {
2655
+ if (typeof window === "undefined") return;
2656
+ try {
2657
+ localStore.remove(businessId, GUEST_SESSION_KEY);
2658
+ } catch {
2659
+ }
2660
+ }
2530
2661
  export {
2662
+ ApiError,
2663
+ COOKIE_CONSENT_CHANGED_EVENT,
2531
2664
  InvalidBaseURLError,
2665
+ JSON_HEADERS,
2666
+ ModerationStatus,
2532
2667
  PAYMENT_URLS,
2668
+ PaymentGatewayEnum,
2669
+ PaymentMethodTypeEnum,
2670
+ ProductTypeEnum,
2533
2671
  STORAGE_KEYS,
2534
2672
  ServiceLocationTypeEnum,
2535
2673
  ServiceRecurringIntervalEnum,
2536
2674
  ServiceTypeEnum,
2537
- acceptQuote,
2538
2675
  addToCart,
2539
2676
  addToGuestCart,
2540
2677
  addToWishlist,
2541
- approveDeliverable,
2678
+ apiRequest,
2679
+ applyAddressSuggestion,
2680
+ appointmentStatusColors,
2681
+ approveBespokeDeliverable,
2682
+ approveMilestone,
2683
+ authHeaders,
2684
+ autocompleteAddress,
2542
2685
  bespokeService,
2543
2686
  bookAppointment,
2544
2687
  buildDeliveryAddress,
2545
- buildFullName2 as buildFullName,
2688
+ buildFullName,
2689
+ bytes,
2546
2690
  cancelAppointment,
2547
2691
  cancelOrder,
2692
+ changePassword,
2548
2693
  checkAvailability,
2549
2694
  checkInWishlist,
2695
+ claimWalletCredit,
2550
2696
  clearCart,
2551
2697
  clearGuestCart,
2698
+ clearGuestSessionId,
2552
2699
  clearTokens,
2700
+ compact,
2701
+ compactFull,
2702
+ compactLower,
2553
2703
  configService,
2554
- consultationService,
2704
+ confirmAppointmentCompletion,
2705
+ confirmMediaUpload,
2706
+ createBespokeOrder,
2555
2707
  createCmsEntry,
2708
+ createComment,
2556
2709
  createConsultation,
2710
+ createCustomerAddress,
2557
2711
  createDedicatedAccount,
2712
+ createFeedback,
2713
+ createGraphQLClient,
2558
2714
  createOrder,
2715
+ createReview,
2716
+ createServiceBookingOrder,
2717
+ currencyCompact,
2718
+ currencyFormat,
2719
+ deactivateCustomerAccount,
2720
+ decimal,
2721
+ decodePlusCode,
2559
2722
  deleteCmsEntry,
2723
+ deleteCustomerAddress,
2724
+ disputeAppointment,
2560
2725
  enrichCompletedOrder,
2726
+ extractApiErrorMessage,
2561
2727
  findOrder,
2562
- findOrderByToken,
2728
+ forgotPasswordCustomer,
2729
+ formatCompactPrice,
2730
+ formatNumber,
2731
+ formatPrice,
2732
+ formatTimeString,
2563
2733
  getAppointment,
2564
2734
  getAppointments,
2565
2735
  getAuthUser,
2736
+ getAvailablePaymentMethods,
2566
2737
  getAvailableSlots,
2567
2738
  getBusinessConfig,
2568
2739
  getCacheTier,
2569
2740
  getCart,
2570
2741
  getCheckoutCustomerName,
2571
2742
  getCmsEntry,
2572
- getConsultation,
2573
- getConsultations,
2743
+ getCookieConsent,
2744
+ getCurrencySymbol,
2745
+ getCustomerAddresses,
2574
2746
  getCustomerDisplayName,
2575
- getDeliverables,
2576
2747
  getErrorMessage,
2577
2748
  getFeaturedProducts,
2578
2749
  getGuestCart,
2579
- getGuestCartCount,
2580
2750
  getGuestCartProductType,
2581
- getGuestCartSubtotal,
2582
- getLatestProducts,
2583
- getLowStockProducts,
2584
2751
  getMe,
2752
+ getMerchantLegalDocument,
2753
+ getMilestones,
2754
+ getMyFeedback,
2585
2755
  getNewProducts,
2756
+ getNotificationInbox,
2757
+ getNotificationPreferences,
2586
2758
  getOnSaleProducts,
2759
+ getOrCreateGuestSessionId,
2587
2760
  getOrders,
2761
+ getPendingComments,
2762
+ getPendingReviews,
2588
2763
  getPopularProducts,
2764
+ getPresignedUploadUrl,
2765
+ getProductComments,
2766
+ getProductReviews,
2589
2767
  getRecommendedProducts,
2590
- getSellerShippingAddress,
2591
2768
  getShippingRates,
2592
2769
  getStoredTokens,
2770
+ getStorefrontBusinessInfo,
2593
2771
  getStorefrontCollectionBySlug,
2594
2772
  getStorefrontCollections,
2595
2773
  getStorefrontProductBySlug,
@@ -2598,52 +2776,94 @@ export {
2598
2776
  getSupportedCurrencies,
2599
2777
  getSupportedLanguages,
2600
2778
  getTransactionStatus,
2779
+ getVapidPublicKey,
2780
+ getWalletBalance,
2781
+ getWalletLedger,
2601
2782
  getWishlist,
2783
+ getYouTubeID,
2602
2784
  googleTranslate,
2785
+ handlePinDrop,
2786
+ handlePlusCodeChange,
2787
+ hasRefreshHandler,
2603
2788
  initiatePayment,
2789
+ isValidCountryCode,
2790
+ listBusinessLocations,
2604
2791
  listCmsEntries,
2792
+ listPickupLocations,
2605
2793
  localStore,
2606
2794
  logger,
2607
2795
  loginCustomer,
2608
2796
  logisticsService,
2609
2797
  logout,
2610
2798
  mapAuthUserToCustomer,
2799
+ markAllNotificationsRead,
2800
+ markNotificationRead,
2801
+ merchantReplyToReview,
2611
2802
  mergeGuestCart,
2612
2803
  mergeGuestCartIfPresent,
2804
+ milestoneStatusColors,
2805
+ moderateComment,
2806
+ moderateReview,
2613
2807
  notificationsService,
2808
+ orderStatusColors,
2809
+ orderStatusLabels,
2810
+ ordinal,
2614
2811
  paymentService,
2812
+ percent,
2813
+ refreshAccessToken,
2615
2814
  refreshToken,
2616
2815
  registerCustomer,
2617
- rejectQuote,
2816
+ registerRefreshHandler,
2618
2817
  removeFromCart,
2619
2818
  removeFromGuestCart,
2620
2819
  removeFromWishlist,
2621
2820
  removeProductFromWishlist,
2622
- requestQuote,
2623
- requestRevision,
2821
+ requestBespokeRevision,
2822
+ requestCustomerOtp,
2823
+ requestCustomerReactivation,
2824
+ rescheduleAppointment,
2825
+ resetPasswordCustomer,
2826
+ resolveDigitalDownload,
2827
+ resolveDigitalDownloadFile,
2828
+ resolveGraphQLEndpoint,
2829
+ reverseGeocode,
2830
+ roman,
2624
2831
  sanitizeBaseUrl,
2625
2832
  sendContactConfirmation,
2626
2833
  sendContactEmail,
2627
- sendMessage,
2628
2834
  serviceBookingService,
2629
2835
  sessionStore,
2836
+ setCookieConsent,
2630
2837
  setEnvironmentMode,
2631
2838
  setLoggerEnvironmentMode,
2632
2839
  silentRefresh,
2633
- splitFullName2 as splitFullName,
2840
+ splitFullName,
2634
2841
  storeTokens,
2842
+ storefrontHeaders,
2843
+ subscribeToPush,
2844
+ trackNotificationClick,
2635
2845
  trackOrder,
2846
+ unsubscribeFromPush,
2636
2847
  updateCartItem,
2637
2848
  updateCmsEntry,
2849
+ updateComment,
2850
+ updateCustomerAddress,
2638
2851
  updateGuestCartItemQuantity,
2852
+ updateNotificationPreferences,
2639
2853
  updateProfile,
2854
+ updateReview,
2855
+ uploadMediaFile,
2856
+ uploadMediaFiles,
2640
2857
  validateConfirmPassword,
2641
2858
  validateEmail,
2642
2859
  validateMessage,
2643
2860
  validateName,
2644
2861
  validatePassword,
2862
+ validatePromoCode,
2645
2863
  validateRequired,
2646
2864
  validateShippingAddress,
2865
+ verifyCustomerOtp,
2866
+ verifyCustomerReactivation,
2647
2867
  verifySocialToken,
2648
2868
  verifyTransaction
2649
2869
  };