@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
@@ -1,3 +1,100 @@
1
+ // src/utils/token-refresh.ts
2
+ var GLOBAL_KEY = "__kasuviaSdkTokenRefreshRegistry__";
3
+ function getRegistry() {
4
+ const g = globalThis;
5
+ if (!g[GLOBAL_KEY]) {
6
+ g[GLOBAL_KEY] = { handler: null, inFlight: null };
7
+ }
8
+ return g[GLOBAL_KEY];
9
+ }
10
+ async function refreshAccessToken() {
11
+ const registry = getRegistry();
12
+ if (!registry.handler) return null;
13
+ if (registry.inFlight) return registry.inFlight;
14
+ registry.inFlight = registry.handler().finally(() => {
15
+ registry.inFlight = null;
16
+ });
17
+ return registry.inFlight;
18
+ }
19
+ function hasRefreshHandler() {
20
+ return getRegistry().handler !== null;
21
+ }
22
+
23
+ // src/utils/http-client.ts
24
+ var ApiError = class extends Error {
25
+ constructor(message, status, body) {
26
+ super(message);
27
+ this.name = "ApiError";
28
+ this.status = status;
29
+ this.body = body;
30
+ }
31
+ };
32
+ function extractApiErrorMessage(body, fallback) {
33
+ if (typeof body !== "object" || body === null) return fallback;
34
+ const record = body;
35
+ if (typeof record.detail === "string") return record.detail;
36
+ if (Array.isArray(record.detail) && record.detail.length > 0) {
37
+ const first = record.detail[0];
38
+ if (first && typeof first.msg === "string") {
39
+ const loc = first.loc;
40
+ const field = Array.isArray(loc) ? loc[loc.length - 1] : void 0;
41
+ return typeof field === "string" || typeof field === "number" ? `${field}: ${first.msg}` : first.msg;
42
+ }
43
+ }
44
+ const firstStringOrArrayItem = (value) => {
45
+ if (typeof value === "string") return value;
46
+ if (Array.isArray(value) && typeof value[0] === "string") return value[0];
47
+ return void 0;
48
+ };
49
+ const nonFieldMessage = firstStringOrArrayItem(record.non_field_errors);
50
+ if (nonFieldMessage) return nonFieldMessage;
51
+ for (const [key, value] of Object.entries(record)) {
52
+ if (key === "detail" || key === "non_field_errors") continue;
53
+ const message = firstStringOrArrayItem(value);
54
+ if (message) return message;
55
+ }
56
+ return fallback;
57
+ }
58
+ function storefrontHeaders(config) {
59
+ return {
60
+ "X-Storefront-Key": config.storefrontApiKey,
61
+ "X-Business-ID": config.businessId
62
+ };
63
+ }
64
+ function authHeaders(config, accessToken) {
65
+ return {
66
+ ...storefrontHeaders(config),
67
+ Authorization: `Bearer ${accessToken}`
68
+ };
69
+ }
70
+ var JSON_HEADERS = { "Content-Type": "application/json" };
71
+ async function apiRequest(url, options = {}) {
72
+ const { fallbackErrorMessage = "Request failed", ...init } = options;
73
+ const headers = new Headers(init.headers);
74
+ const hadBearerToken = headers.get("Authorization")?.startsWith("Bearer ") ?? false;
75
+ const response = await fetch(url, { ...init, headers });
76
+ if (response.status === 401 && hadBearerToken && hasRefreshHandler()) {
77
+ const freshToken = await refreshAccessToken();
78
+ if (freshToken) {
79
+ const retryHeaders = new Headers(headers);
80
+ retryHeaders.set("Authorization", `Bearer ${freshToken}`);
81
+ const retryResponse = await fetch(url, { ...init, headers: retryHeaders });
82
+ if (retryResponse.ok) {
83
+ const retryText = await retryResponse.text();
84
+ return retryText ? JSON.parse(retryText) : void 0;
85
+ }
86
+ const retryBody = await retryResponse.json().catch(() => ({}));
87
+ throw new ApiError(extractApiErrorMessage(retryBody, fallbackErrorMessage), retryResponse.status, retryBody);
88
+ }
89
+ }
90
+ if (!response.ok) {
91
+ const body = await response.json().catch(() => ({}));
92
+ throw new ApiError(extractApiErrorMessage(body, fallbackErrorMessage), response.status, body);
93
+ }
94
+ const text = await response.text();
95
+ return text ? JSON.parse(text) : void 0;
96
+ }
97
+
1
98
  // src/services/cart.service.ts
2
99
  function parseItems(items) {
3
100
  return items.map((item) => ({
@@ -5,218 +102,148 @@ function parseItems(items) {
5
102
  variant_attributes: item.variant_attributes && typeof item.variant_attributes === "string" ? JSON.parse(item.variant_attributes) : item.variant_attributes
6
103
  }));
7
104
  }
105
+ function guestHeaders(config, guestSessionId, extra) {
106
+ return { ...extra, ...storefrontHeaders(config), "X-Guest-Session": guestSessionId };
107
+ }
8
108
  async function getGuestCart(config, guestSessionId) {
9
- if (!guestSessionId) return [];
10
- try {
11
- const response = await fetch(`${config.backendUrls.store}/cart/`, {
12
- headers: {
13
- "X-Storefront-Key": config.storefrontApiKey,
14
- "X-Business-ID": config.businessId,
15
- "X-Guest-Session": guestSessionId
16
- }
17
- });
18
- if (!response.ok) return [];
19
- const data = await response.json();
20
- return parseItems(data.items || []);
21
- } catch {
22
- return [];
23
- }
109
+ if (!guestSessionId) throw new Error("Guest session ID is required to fetch guest cart");
110
+ const data = await apiRequest(`${config.backendUrls.cart}/cart/`, {
111
+ headers: guestHeaders(config, guestSessionId),
112
+ fallbackErrorMessage: "Failed to fetch guest cart"
113
+ });
114
+ return { ...data, items: parseItems(data.items || []) };
24
115
  }
25
116
  async function addToGuestCart(config, guestSessionId, item) {
26
- if (!guestSessionId) return [];
117
+ if (!guestSessionId) throw new Error("Guest session ID is required to add to guest cart");
27
118
  const payload = {
28
119
  product_id: item.product_id,
29
- product_slug: item.product_slug,
30
- product_name: item.product_name,
31
- image: item.image,
32
- variant_id: item.variant_id,
33
- variant_attributes: typeof item.variant_attributes === "string" ? item.variant_attributes : JSON.stringify(item.variant_attributes),
34
- unit_price: item.unit_price,
120
+ variant_id: item.variant_id ?? void 0,
35
121
  quantity: item.quantity,
36
- custom_measurements: item.custom_measurements,
37
- is_bespoke: item.is_bespoke
122
+ custom_measurements: item.custom_measurements ?? void 0
38
123
  };
39
- const response = await fetch(`${config.backendUrls.store}/cart/`, {
124
+ const data = await apiRequest(`${config.backendUrls.cart}/cart/`, {
40
125
  method: "POST",
41
- headers: {
42
- "Content-Type": "application/json",
43
- "X-Storefront-Key": config.storefrontApiKey,
44
- "X-Business-ID": config.businessId,
45
- "X-Guest-Session": guestSessionId
46
- },
47
- body: JSON.stringify(payload)
48
- });
49
- if (!response.ok) return [];
50
- const data = await response.json();
51
- return parseItems(data.items || []);
126
+ headers: guestHeaders(config, guestSessionId, JSON_HEADERS),
127
+ body: JSON.stringify(payload),
128
+ fallbackErrorMessage: "Failed to add to guest cart"
129
+ });
130
+ return { ...data, items: parseItems(data.items || []) };
52
131
  }
53
132
  async function updateGuestCartItemQuantity(config, guestSessionId, itemId, quantity) {
54
- if (!guestSessionId) return [];
55
- const response = await fetch(`${config.backendUrls.store}/cart/${itemId}`, {
133
+ if (!guestSessionId) throw new Error("Guest session ID is required to update guest cart item");
134
+ const data = await apiRequest(`${config.backendUrls.cart}/cart/${itemId}`, {
56
135
  method: "PATCH",
57
- headers: {
58
- "Content-Type": "application/json",
59
- "X-Storefront-Key": config.storefrontApiKey,
60
- "X-Business-ID": config.businessId,
61
- "X-Guest-Session": guestSessionId
62
- },
63
- body: JSON.stringify({ quantity })
64
- });
65
- if (!response.ok) return [];
66
- const data = await response.json();
67
- return parseItems(data.items || []);
136
+ headers: guestHeaders(config, guestSessionId, JSON_HEADERS),
137
+ body: JSON.stringify({ quantity }),
138
+ fallbackErrorMessage: "Failed to update guest cart item"
139
+ });
140
+ return { ...data, items: parseItems(data.items || []) };
68
141
  }
69
142
  async function removeFromGuestCart(config, guestSessionId, itemId) {
70
- if (!guestSessionId) return [];
71
- await fetch(`${config.backendUrls.store}/cart/${itemId}`, {
143
+ if (!guestSessionId) throw new Error("Guest session ID is required to remove from guest cart");
144
+ await apiRequest(`${config.backendUrls.cart}/cart/${itemId}`, {
72
145
  method: "DELETE",
73
- headers: {
74
- "X-Storefront-Key": config.storefrontApiKey,
75
- "X-Business-ID": config.businessId,
76
- "X-Guest-Session": guestSessionId
77
- }
146
+ headers: guestHeaders(config, guestSessionId),
147
+ fallbackErrorMessage: "Failed to remove item from guest cart"
78
148
  });
79
- return getGuestCart(config, guestSessionId);
80
149
  }
81
150
  async function clearGuestCart(config, guestSessionId) {
82
- if (!guestSessionId) return;
83
- await fetch(`${config.backendUrls.store}/cart/clear`, {
151
+ if (!guestSessionId) throw new Error("Guest session ID is required to clear guest cart");
152
+ await apiRequest(`${config.backendUrls.cart}/cart/clear`, {
84
153
  method: "DELETE",
85
- headers: {
86
- "X-Storefront-Key": config.storefrontApiKey,
87
- "X-Business-ID": config.businessId,
88
- "X-Guest-Session": guestSessionId
89
- }
154
+ headers: guestHeaders(config, guestSessionId),
155
+ fallbackErrorMessage: "Failed to clear guest cart"
90
156
  });
91
157
  }
92
158
  async function getCart(config, accessToken) {
93
159
  if (!accessToken) {
94
- return { id: "", items: [], subtotal: 0, total: 0, product_type: "physical" };
160
+ throw new Error("Access token is required to fetch authenticated cart");
95
161
  }
96
- const response = await fetch(`${config.backendUrls.store}/cart/`, {
97
- headers: {
98
- "Authorization": `Bearer ${accessToken}`,
99
- "X-Storefront-Key": config.storefrontApiKey,
100
- "X-Business-ID": config.businessId
101
- }
162
+ const data = await apiRequest(`${config.backendUrls.cart}/cart/`, {
163
+ headers: authHeaders(config, accessToken),
164
+ fallbackErrorMessage: "Failed to fetch cart"
102
165
  });
103
- if (!response.ok) throw new Error("Failed to fetch cart");
104
- const data = await response.json();
105
- const parsedItems = parseItems(data.items || []);
106
- return { ...data, items: parsedItems };
166
+ return { ...data, items: parseItems(data.items || []) };
107
167
  }
108
168
  async function addToCart(config, accessToken, item) {
109
- const response = await fetch(`${config.backendUrls.store}/cart/`, {
169
+ const payload = {
170
+ product_id: item.product_id,
171
+ variant_id: item.variant_id ?? void 0,
172
+ quantity: item.quantity,
173
+ custom_measurements: item.custom_measurements ?? void 0
174
+ };
175
+ const data = await apiRequest(`${config.backendUrls.cart}/cart/`, {
110
176
  method: "POST",
111
- headers: {
112
- "Content-Type": "application/json",
113
- "Authorization": `Bearer ${accessToken}`,
114
- "X-Storefront-Key": config.storefrontApiKey,
115
- "X-Business-ID": config.businessId
116
- },
117
- body: JSON.stringify(item)
118
- });
119
- if (!response.ok) throw new Error("Failed to add to cart");
120
- const data = await response.json();
121
- const parsedItems = parseItems(data.items || []);
122
- return { ...data, items: parsedItems };
177
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
178
+ body: JSON.stringify(payload),
179
+ fallbackErrorMessage: "Failed to add to cart"
180
+ });
181
+ return { ...data, items: parseItems(data.items || []) };
123
182
  }
124
183
  async function updateCartItem(config, accessToken, itemId, quantity) {
125
- const response = await fetch(`${config.backendUrls.store}/cart/${itemId}`, {
184
+ const data = await apiRequest(`${config.backendUrls.cart}/cart/${itemId}`, {
126
185
  method: "PATCH",
127
- headers: {
128
- "Content-Type": "application/json",
129
- "Authorization": `Bearer ${accessToken}`,
130
- "X-Storefront-Key": config.storefrontApiKey,
131
- "X-Business-ID": config.businessId
132
- },
133
- body: JSON.stringify({ quantity })
134
- });
135
- if (!response.ok) throw new Error("Failed to update cart item");
136
- const data = await response.json();
137
- const parsedItems = parseItems(data.items || []);
138
- return { ...data, items: parsedItems };
186
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
187
+ body: JSON.stringify({ quantity }),
188
+ fallbackErrorMessage: "Failed to update cart item"
189
+ });
190
+ return { ...data, items: parseItems(data.items || []) };
139
191
  }
140
192
  async function removeFromCart(config, accessToken, itemId) {
141
- await fetch(`${config.backendUrls.store}/cart/${itemId}`, {
193
+ await apiRequest(`${config.backendUrls.cart}/cart/${itemId}`, {
142
194
  method: "DELETE",
143
- headers: {
144
- "Authorization": `Bearer ${accessToken}`,
145
- "X-Storefront-Key": config.storefrontApiKey,
146
- "X-Business-ID": config.businessId
147
- }
195
+ headers: authHeaders(config, accessToken),
196
+ fallbackErrorMessage: "Failed to remove cart item"
148
197
  });
149
- return getCart(config, accessToken);
150
198
  }
151
199
  async function clearCart(config, accessToken) {
152
- await fetch(`${config.backendUrls.store}/cart/clear`, {
200
+ await apiRequest(`${config.backendUrls.cart}/cart/clear`, {
153
201
  method: "DELETE",
154
- headers: {
155
- "Authorization": `Bearer ${accessToken}`,
156
- "X-Storefront-Key": config.storefrontApiKey,
157
- "X-Business-ID": config.businessId
158
- }
202
+ headers: authHeaders(config, accessToken),
203
+ fallbackErrorMessage: "Failed to clear cart"
159
204
  });
160
205
  }
161
- async function mergeGuestCart(config, accessToken, guestSessionId) {
162
- const guestItems = guestSessionId ? await getGuestCart(config, guestSessionId) : [];
163
- if (guestItems.length === 0) {
164
- return getCart(config, accessToken);
165
- }
206
+ async function mergeGuestCart(config, accessToken, guestSessionId, guestItems) {
166
207
  const payloadItems = guestItems.map((item) => ({
167
208
  product_id: item.product_id,
168
- product_slug: item.product_slug,
169
- product_name: item.product_name,
170
- image: item.image,
171
- variant_id: item.variant_id,
172
- variant_attributes: typeof item.variant_attributes === "string" ? item.variant_attributes : JSON.stringify(item.variant_attributes ?? {}),
173
- unit_price: item.unit_price,
209
+ variant_id: item.variant_id ?? void 0,
174
210
  quantity: item.quantity,
175
- custom_measurements: typeof item.custom_measurements === "string" ? item.custom_measurements : JSON.stringify(item.custom_measurements ?? {}),
176
- is_bespoke: item.is_bespoke
211
+ custom_measurements: item.custom_measurements ?? void 0
177
212
  }));
178
- const headers = {
179
- "Content-Type": "application/json",
180
- "Authorization": `Bearer ${accessToken}`,
181
- "X-Storefront-Key": config.storefrontApiKey,
182
- "X-Business-ID": config.businessId
183
- };
213
+ const headers = { ...JSON_HEADERS, ...authHeaders(config, accessToken) };
184
214
  if (guestSessionId) {
185
215
  headers["X-Guest-Session"] = guestSessionId;
186
216
  }
187
- const response = await fetch(`${config.backendUrls.store}/cart/merge`, {
217
+ const data = await apiRequest(`${config.backendUrls.cart}/cart/merge`, {
188
218
  method: "POST",
189
219
  headers,
190
- body: JSON.stringify({ items: payloadItems })
220
+ body: JSON.stringify({ items: payloadItems }),
221
+ fallbackErrorMessage: "Failed to merge guest cart"
191
222
  });
192
- if (!response.ok) throw new Error("Failed to merge guest cart");
193
- const data = await response.json();
194
223
  return { ...data, items: parseItems(data.items || []) };
195
224
  }
196
225
  async function mergeGuestCartIfPresent(config, accessToken, guestSessionId) {
197
- try {
198
- const guestItems = await getGuestCart(config, guestSessionId);
199
- if (guestItems.length === 0) return;
200
- await mergeGuestCart(config, accessToken, guestSessionId);
201
- } catch (error) {
202
- console.warn("[Cart] Failed to merge guest cart:", error);
203
- }
204
- }
205
- function getGuestCartCount() {
206
- return 0;
226
+ const guestCart = await getGuestCart(config, guestSessionId);
227
+ if (!guestCart.items.length) return;
228
+ await mergeGuestCart(config, accessToken, guestSessionId, guestCart.items);
207
229
  }
208
- function getGuestCartSubtotal() {
209
- return 0;
210
- }
211
- function getGuestCartProductType() {
212
- return "physical";
230
+ function getGuestCartProductType(items) {
231
+ if (!items || !items.length) return null;
232
+ const types = new Set(items.map((i) => i.product_type).filter(Boolean));
233
+ if (types.size === 0) return null;
234
+ if (types.size === 1) return [...types][0] ?? null;
235
+ return "physical" /* PHYSICAL */;
213
236
  }
214
237
 
215
238
  // src/services/order.service.ts
239
+ function extractShippingAddress(raw) {
240
+ const val = raw.physical_fulfillment?.shipping_address;
241
+ return typeof val === "object" && val !== null ? val : null;
242
+ }
216
243
  function deriveCustomerName(raw) {
217
244
  if (raw.customer_name) return raw.customer_name;
218
245
  if (raw.guest_name) return raw.guest_name;
219
- const addr = typeof raw.shipping_address === "object" && raw.shipping_address !== null ? raw.shipping_address : null;
246
+ const addr = extractShippingAddress(raw);
220
247
  if (addr) {
221
248
  const full = `${addr.first_name ?? ""} ${addr.last_name ?? ""}`.trim();
222
249
  if (full) return full;
@@ -228,25 +255,123 @@ function deriveCustomerName(raw) {
228
255
  function normaliseStorefrontOrder(raw) {
229
256
  const items = (raw.items ?? []).map((item) => ({
230
257
  id: item.id,
231
- productName: item.product_name ?? item.productName ?? "",
232
- image: item.image ?? "",
233
- quantity: item.quantity ?? 1,
234
- price: item.unit_price ?? item.price ?? 0
258
+ order_id: item.order_id,
259
+ product_id: item.product_id ?? void 0,
260
+ variant_id: item.variant_id ?? void 0,
261
+ product_name: item.product_name ?? "",
262
+ quantity: item.quantity,
263
+ unit_price: item.unit_price,
264
+ variant_details: item.variant_details,
265
+ custom_measurements: item.custom_measurements,
266
+ image_url: item.image_url ?? void 0
235
267
  }));
236
- const addr = typeof raw.shipping_address === "object" && raw.shipping_address !== null ? raw.shipping_address : null;
268
+ const addr = extractShippingAddress(raw);
269
+ const customerName = deriveCustomerName(raw);
270
+ const customerEmail = raw.guest_email ?? raw.customer_email ?? "";
271
+ const physical = raw.physical_fulfillment;
272
+ if (!raw.currency) {
273
+ throw new Error(`Order ${raw.id} response is missing a currency field.`);
274
+ }
275
+ if (!raw.created_at) {
276
+ throw new Error(`Order ${raw.id} response is missing a created_at field.`);
277
+ }
278
+ if (!raw.updated_at) {
279
+ throw new Error(`Order ${raw.id} response is missing an updated_at field.`);
280
+ }
281
+ if (!raw.business_id) {
282
+ throw new Error(`Order ${raw.id} response is missing a business_id field.`);
283
+ }
237
284
  return {
238
285
  id: raw.id,
239
- display_id: raw.display_id,
286
+ display_id: raw.display_id || raw.id,
287
+ business_id: raw.business_id,
288
+ customer_id: raw.customer_id ?? void 0,
240
289
  items,
241
- total: raw.total_amount ?? raw.total ?? 0,
242
290
  status: raw.status,
243
- customerEmail: raw.guest_email ?? raw.customer_email ?? "",
244
- customerName: deriveCustomerName(raw),
245
- createdAt: raw.created_at ? new Date(raw.created_at).getTime() : Date.now(),
246
- updatedAt: raw.updated_at ? new Date(raw.updated_at).getTime() : Date.now(),
247
- deliveryMethod: raw.delivery_method,
248
- trackingNumber: raw.tracking_number,
249
- deliveryAddress: typeof raw.shipping_address === "string" ? raw.shipping_address : addr ? `${addr.street}, ${addr.city}, ${addr.state}, ${addr.country}` : void 0,
291
+ payment_status: raw.payment_status || "pending",
292
+ product_type: raw.product_type ?? void 0,
293
+ // Canonical backend fields
294
+ total_amount: raw.total_amount,
295
+ currency: raw.currency,
296
+ subtotal_amount: raw.subtotal_amount ?? void 0,
297
+ shipping_amount: raw.shipping_amount ?? void 0,
298
+ discount_amount: raw.discount_amount ?? void 0,
299
+ tax_amount: raw.tax_amount ?? void 0,
300
+ customer_name: customerName,
301
+ customer_email: customerEmail,
302
+ // REAL BUG FIXED (2026-09-23) via the REST codegen initiative: every
303
+ // field below this point used to read a flat `raw.<field>` that does
304
+ // NOT exist anywhere on the real OrderResponse - confirmed by reading
305
+ // the actual Pydantic classes, not just the generated types. They
306
+ // genuinely live nested under `physical_fulfillment` (shipping/
307
+ // tracking/carrier/pickup/surcharge/weight) or `digital_fulfillment`
308
+ // (buyer_country) - "One nested block per product type, instead of
309
+ // ~35 flat fields on OrderBase" per that schema file's own Order
310
+ // Schema Refactor comment. Every one of these was silently `undefined`
311
+ // on every real order: a customer's tracking number, carrier, delivery
312
+ // status, and shipping address never actually rendered anywhere on the
313
+ // storefront's order tracking page.
314
+ tracking_number: physical?.tracking_number ?? void 0,
315
+ delivery_status: physical?.delivery_status ?? void 0,
316
+ carrier_name: physical?.carrier_name ?? void 0,
317
+ carrier_slug: physical?.carrier_slug ?? void 0,
318
+ terminal_rate_id: physical?.terminal_rate_id ?? void 0,
319
+ guest_token: raw.guest_token ?? void 0,
320
+ guest_email: raw.guest_email ?? void 0,
321
+ guest_phone: raw.guest_phone ?? void 0,
322
+ guest_name: raw.guest_name ?? void 0,
323
+ buyer_country: raw.digital_fulfillment?.buyer_country ?? void 0,
324
+ created_at: raw.created_at,
325
+ updated_at: raw.updated_at,
326
+ // Weight discrepancy & surcharge fields (Task 2) — physical_fulfillment
327
+ shipping_surcharge_amount: physical?.shipping_surcharge_amount ?? void 0,
328
+ shipping_surcharge_reason: physical?.shipping_surcharge_reason ?? void 0,
329
+ shipping_surcharge_description: physical?.shipping_surcharge_description ?? void 0,
330
+ shipping_surcharge_applied_at: physical?.shipping_surcharge_applied_at ?? void 0,
331
+ declared_weight_kg: physical?.declared_weight_kg ?? void 0,
332
+ actual_weight_kg: physical?.actual_weight_kg ?? void 0,
333
+ // Self-pickup verification fields (Task 3) — physical_fulfillment
334
+ pickup_verification_code: physical?.pickup_verification_code ?? void 0,
335
+ pickup_verification_code_generated_at: physical?.pickup_verification_code_generated_at ?? void 0,
336
+ pickup_verification_code_used_at: physical?.pickup_verification_code_used_at ?? void 0,
337
+ pickup_verification_failed_attempts: physical?.pickup_verification_failed_attempts,
338
+ pickup_verification_locked_until: physical?.pickup_verification_locked_until ?? void 0,
339
+ pickup_qr_payload: physical?.pickup_qr_payload ?? void 0,
340
+ // Bespoke fields — real backend nests these under `bespoke_fulfillment`.
341
+ bespoke_brief: raw.bespoke_fulfillment?.brief ?? void 0,
342
+ bespoke_reference_images: raw.bespoke_fulfillment?.reference_images ?? void 0,
343
+ bespoke_measurements: raw.bespoke_fulfillment?.measurements ?? void 0,
344
+ bespoke_preferred_timeline: raw.bespoke_fulfillment?.preferred_timeline ?? void 0,
345
+ bespoke_special_instructions: raw.bespoke_fulfillment?.special_instructions ?? void 0,
346
+ bespoke_deposit_ratio: raw.bespoke_fulfillment?.deposit_ratio ?? void 0,
347
+ bespoke_deposit_amount: raw.bespoke_fulfillment?.deposit_amount ?? void 0,
348
+ bespoke_balance_amount: raw.bespoke_fulfillment?.balance_amount ?? void 0,
349
+ bespoke_status: raw.bespoke_fulfillment?.status ?? void 0,
350
+ bespoke_deliverable_submitted_at: raw.bespoke_fulfillment?.deliverable_submitted_at ?? void 0,
351
+ bespoke_deliverable_url: raw.bespoke_fulfillment?.deliverable_url ?? void 0,
352
+ bespoke_deliverable_notes: raw.bespoke_fulfillment?.deliverable_notes ?? void 0,
353
+ bespoke_buyer_approved_at: raw.bespoke_fulfillment?.buyer_approved_at ?? void 0,
354
+ bespoke_revision_count: raw.bespoke_fulfillment?.revision_count ?? 0,
355
+ bespoke_revision_notes: raw.bespoke_fulfillment?.revision_notes ?? void 0,
356
+ // SDK camelCase convenience aliases
357
+ total: raw.total_amount ?? void 0,
358
+ shippingAmount: raw.shipping_amount ?? void 0,
359
+ discountAmount: raw.discount_amount ?? void 0,
360
+ customerEmail,
361
+ customerName,
362
+ createdAt: new Date(raw.created_at).getTime(),
363
+ updatedAt: new Date(raw.updated_at).getTime(),
364
+ // REAL BUG FIXED (2026-09-23), found alongside the nesting bug above:
365
+ // `deliveryMethod` reads physical_fulfillment.shipping_method now
366
+ // (was reading a flat `delivery_method` field that never existed at
367
+ // all, anywhere - not even nested). The real value is "carrier" or
368
+ // "self_pickup" (PhysicalFulfillmentCreate.shipping_method's own
369
+ // docstring) - see the matching fix in kasuvia-storefront's
370
+ // OrderDeliveryInfo.tsx, which used to compare against the wrong
371
+ // string "pickup".
372
+ deliveryMethod: physical?.shipping_method ?? void 0,
373
+ trackingNumber: physical?.tracking_number ?? void 0,
374
+ deliveryAddress: addr ? `${addr.street}, ${addr.city}, ${addr.state}, ${addr.country}` : void 0,
250
375
  shippingAddress: addr ? {
251
376
  first_name: addr.first_name ?? "",
252
377
  last_name: addr.last_name ?? "",
@@ -254,62 +379,85 @@ function normaliseStorefrontOrder(raw) {
254
379
  street: addr.street ?? "",
255
380
  city: addr.city ?? "",
256
381
  state: addr.state ?? "",
257
- lga: addr.lga ?? "",
382
+ lga: addr.lga,
258
383
  country: addr.country ?? "",
259
- postal: addr.postal ?? ""
384
+ postal: addr.postal
260
385
  } : void 0,
261
- cancellationReason: raw.cancellation_reason,
386
+ // REAL GAP (2026-09-23), NOT fixed here: confirmed cancellation_reason
387
+ // does not exist anywhere in OrderResponse or any of its fulfillment
388
+ // sub-schemas (kasuvia-store-management's app/schemas/order.py) - the
389
+ // cancel-order endpoint accepts a `reason` in its request body but the
390
+ // backend never persists/returns it back on the order. Permanently
391
+ // undefined until the backend adds real storage for it; kept as a
392
+ // field on the public Order type rather than silently removed, since
393
+ // deleting a public SDK field is a breaking change this fix's scope
394
+ // doesn't cover.
395
+ cancellationReason: void 0,
262
396
  hasBespoke: raw.product_type === "bespoke",
263
- bespokeStage: raw.bespoke_stage,
264
- bespokeBrief: raw.bespoke_brief,
265
- deliveryStatus: raw.delivery_status,
266
- carrierName: raw.carrier_name,
267
- terminalShipmentId: raw.terminal_shipment_id,
268
- guestToken: raw.guest_token,
269
- guestEmail: raw.guest_email,
270
- guestPhone: raw.guest_phone,
271
- guestName: raw.guest_name
397
+ // REAL GAP (2026-09-23), NOT fixed here: same as cancellationReason -
398
+ // confirmed no `bespoke_stage` field exists anywhere; the real,
399
+ // equivalent concept is bespoke_fulfillment.status (already exposed
400
+ // above as `bespoke_status`). Reads that here rather than staying
401
+ // permanently undefined, since it's genuinely the same real data the
402
+ // consuming components (BespokeProgress) want to key off of.
403
+ bespokeStage: raw.bespoke_fulfillment?.status ?? void 0,
404
+ bespokeBrief: raw.bespoke_fulfillment?.brief ?? void 0,
405
+ deliveryStatus: physical?.delivery_status ?? void 0,
406
+ carrierName: physical?.carrier_name ?? void 0,
407
+ terminalShipmentId: physical?.terminal_shipment_id ?? void 0,
408
+ guestToken: raw.guest_token ?? void 0,
409
+ guestEmail: raw.guest_email ?? void 0,
410
+ guestPhone: raw.guest_phone ?? void 0,
411
+ guestName: raw.guest_name ?? void 0
272
412
  };
273
413
  }
274
- async function createOrder(config, accessToken, payload) {
275
- const response = await fetch(`${config.backendUrls.store}/orders/storefront`, {
414
+ async function createOrder(config, accessToken, payload, guestSessionId) {
415
+ const headers = { ...JSON_HEADERS, ...storefrontHeaders(config) };
416
+ if (accessToken) {
417
+ headers["Authorization"] = `Bearer ${accessToken}`;
418
+ } else if (guestSessionId) {
419
+ headers["X-Guest-Session"] = guestSessionId;
420
+ }
421
+ const data = await apiRequest(`${config.backendUrls.store}/orders/storefront`, {
276
422
  method: "POST",
277
- headers: {
278
- "Content-Type": "application/json",
279
- "Authorization": `Bearer ${accessToken}`,
280
- "X-Storefront-Key": config.storefrontApiKey,
281
- "X-Business-ID": config.businessId
282
- },
283
- body: JSON.stringify(payload)
284
- });
285
- if (!response.ok) throw new Error("Failed to create order");
286
- const data = await response.json();
423
+ headers,
424
+ body: JSON.stringify(payload),
425
+ fallbackErrorMessage: "Failed to create order"
426
+ });
287
427
  return normaliseStorefrontOrder(data);
288
428
  }
289
- async function findOrder(config, params) {
290
- const queryParams = new URLSearchParams();
291
- if (params.email) queryParams.set("email", params.email);
292
- if (params.order_id) queryParams.set("order_id", params.order_id);
293
- if (params.guest_token) queryParams.set("guest_token", params.guest_token);
294
- const response = await fetch(`${config.backendUrls.store}/orders/find/?${queryParams.toString()}`, {
295
- headers: {
296
- "X-Storefront-Key": config.storefrontApiKey,
297
- "X-Business-ID": config.businessId
298
- }
429
+ async function createBespokeOrder(config, accessToken, payload) {
430
+ const headers = { ...JSON_HEADERS, ...storefrontHeaders(config) };
431
+ if (accessToken) headers["Authorization"] = `Bearer ${accessToken}`;
432
+ const data = await apiRequest(`${config.backendUrls.store}/orders/storefront/bespoke`, {
433
+ method: "POST",
434
+ headers,
435
+ body: JSON.stringify(payload),
436
+ fallbackErrorMessage: "Failed to create bespoke order"
299
437
  });
300
- if (!response.ok) throw new Error("Failed to find order");
301
- const data = await response.json();
302
438
  return normaliseStorefrontOrder(data);
303
439
  }
304
- async function findOrderByToken(config, token) {
305
- const response = await fetch(`${config.backendUrls.store}/orders/find/?guest_token=${token}`, {
306
- headers: {
307
- "X-Storefront-Key": config.storefrontApiKey,
308
- "X-Business-ID": config.businessId
309
- }
440
+ async function createServiceBookingOrder(config, accessToken, payload) {
441
+ const headers = { ...JSON_HEADERS, ...storefrontHeaders(config) };
442
+ if (accessToken) headers["Authorization"] = `Bearer ${accessToken}`;
443
+ const data = await apiRequest(`${config.backendUrls.store}/orders/storefront/service`, {
444
+ method: "POST",
445
+ headers,
446
+ body: JSON.stringify(payload),
447
+ fallbackErrorMessage: "Failed to create service booking order"
448
+ });
449
+ return normaliseStorefrontOrder(data);
450
+ }
451
+ async function findOrder(config, params) {
452
+ if (!params.email || !params.order_id) {
453
+ throw new Error("Both email and order_id are required to find an order.");
454
+ }
455
+ const data = await apiRequest(`${config.backendUrls.store}/orders/track`, {
456
+ method: "POST",
457
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
458
+ body: JSON.stringify({ order_id: params.order_id, email: params.email }),
459
+ fallbackErrorMessage: "Failed to find order"
310
460
  });
311
- if (!response.ok) throw new Error("Failed to find order by token");
312
- const data = await response.json();
313
461
  return normaliseStorefrontOrder(data);
314
462
  }
315
463
  async function getOrders(config, accessToken, params) {
@@ -317,45 +465,110 @@ async function getOrders(config, accessToken, params) {
317
465
  if (params?.email) queryParams.set("email", params.email);
318
466
  if (params?.page) queryParams.set("page", params.page.toString());
319
467
  if (params?.page_size) queryParams.set("page_size", params.page_size.toString());
320
- const response = await fetch(`${config.backendUrls.store}/orders/?${queryParams.toString()}`, {
321
- headers: {
322
- "Authorization": `Bearer ${accessToken}`,
323
- "X-Storefront-Key": config.storefrontApiKey,
324
- "X-Business-ID": config.businessId
325
- }
468
+ const data = await apiRequest(`${config.backendUrls.store}/orders/?${queryParams.toString()}`, {
469
+ headers: authHeaders(config, accessToken),
470
+ fallbackErrorMessage: "Failed to get orders"
326
471
  });
327
- if (!response.ok) throw new Error("Failed to get orders");
328
- const data = await response.json();
329
472
  const items = Array.isArray(data) ? data : data.items ?? [];
330
473
  return items.map(normaliseStorefrontOrder);
331
474
  }
332
475
  async function cancelOrder(config, accessToken, orderId, reason) {
333
- const response = await fetch(`${config.backendUrls.store}/orders/${orderId}/cancel/`, {
476
+ const data = await apiRequest(`${config.backendUrls.store}/orders/${orderId}/cancel/`, {
334
477
  method: "POST",
335
- headers: {
336
- "Content-Type": "application/json",
337
- "Authorization": `Bearer ${accessToken}`,
338
- "X-Storefront-Key": config.storefrontApiKey,
339
- "X-Business-ID": config.businessId
340
- },
341
- body: JSON.stringify({ reason })
342
- });
343
- if (!response.ok) throw new Error("Failed to cancel order");
344
- const data = await response.json();
478
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
479
+ body: JSON.stringify({ reason }),
480
+ fallbackErrorMessage: "Failed to cancel order"
481
+ });
345
482
  return normaliseStorefrontOrder(data);
346
483
  }
347
- async function trackOrder(config, orderId, token) {
348
- const queryParams = token ? `?token=${token}` : "";
349
- const response = await fetch(`${config.backendUrls.store}/orders/track/${orderId}${queryParams}`, {
350
- headers: {
351
- "X-Storefront-Key": config.storefrontApiKey,
352
- "X-Business-ID": config.businessId
353
- }
484
+ async function trackOrder(config, orderId, accessToken) {
485
+ const data = await apiRequest(`${config.backendUrls.store}/orders/${orderId}`, {
486
+ headers: authHeaders(config, accessToken),
487
+ fallbackErrorMessage: "Failed to track order"
354
488
  });
355
- if (!response.ok) throw new Error("Failed to track order");
356
- const data = await response.json();
357
489
  return normaliseStorefrontOrder(data);
358
490
  }
491
+ async function validatePromoCode(config, code, cartTotal) {
492
+ try {
493
+ return await apiRequest(`${config.backendUrls.store}/sales/discounts/validate`, {
494
+ method: "POST",
495
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
496
+ body: JSON.stringify({ code, cart_total: cartTotal }),
497
+ fallbackErrorMessage: "Invalid promo code"
498
+ });
499
+ } catch (err) {
500
+ const message = err instanceof ApiError ? err.message : "Invalid promo code";
501
+ return { valid: false, discount_amount: 0, discount_type: "fixed", message };
502
+ }
503
+ }
504
+ async function resolveRedirectOrJson(url, fallbackErrorMessage) {
505
+ const response = await fetch(url, { redirect: "manual" });
506
+ if (response.status >= 300 && response.status < 400) {
507
+ const location = response.headers.get("location");
508
+ if (!location) {
509
+ throw new Error(`${fallbackErrorMessage}: server redirected with no destination URL.`);
510
+ }
511
+ return { type: "redirect", url: location };
512
+ }
513
+ const body = await response.json().catch(() => ({}));
514
+ if (!response.ok) {
515
+ throw new ApiError(extractDigitalDownloadError(body, fallbackErrorMessage), response.status, body);
516
+ }
517
+ return { type: "json", data: body };
518
+ }
519
+ function extractDigitalDownloadError(body, fallback) {
520
+ if (typeof body === "object" && body !== null && "detail" in body && typeof body.detail === "string") {
521
+ return body.detail;
522
+ }
523
+ return fallback;
524
+ }
525
+ async function resolveDigitalDownload(config, orderId, token) {
526
+ const result = await resolveRedirectOrJson(
527
+ `${config.backendUrls.store}/orders/${orderId}/download/${token}`,
528
+ "Failed to resolve digital download link"
529
+ );
530
+ if (result.type === "redirect") return result;
531
+ return { type: "listing", ...result.data };
532
+ }
533
+ async function resolveDigitalDownloadFile(config, orderId, token, fileId) {
534
+ const result = await resolveRedirectOrJson(
535
+ `${config.backendUrls.store}/orders/${orderId}/download/${token}/${fileId}`,
536
+ "Failed to resolve this file's download link"
537
+ );
538
+ if (result.type !== "redirect") {
539
+ throw new Error("Failed to resolve this file's download link: server did not return a redirect.");
540
+ }
541
+ return result.url;
542
+ }
543
+
544
+ // src/services/consultation.service.ts
545
+ async function createConsultation(config, accessToken, params) {
546
+ return apiRequest(`${config.backendUrls.crm}/consultations/`, {
547
+ method: "POST",
548
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
549
+ body: JSON.stringify(params),
550
+ fallbackErrorMessage: "Failed to submit your request"
551
+ });
552
+ }
553
+
554
+ // src/services/feedback.service.ts
555
+ async function createFeedback(config, accessToken, params) {
556
+ return apiRequest(`${config.backendUrls.crm}/feedback/`, {
557
+ method: "POST",
558
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
559
+ body: JSON.stringify(params),
560
+ fallbackErrorMessage: "Failed to submit your message"
561
+ });
562
+ }
563
+ async function getMyFeedback(config, accessToken, params) {
564
+ const query = new URLSearchParams();
565
+ if (params?.page) query.set("page", String(params.page));
566
+ if (params?.page_size) query.set("page_size", String(params.page_size));
567
+ return apiRequest(`${config.backendUrls.crm}/feedback/my?${query.toString()}`, {
568
+ headers: authHeaders(config, accessToken),
569
+ fallbackErrorMessage: "Failed to load your requests"
570
+ });
571
+ }
359
572
 
360
573
  // src/constants.ts
361
574
  var STORAGE_KEYS = {
@@ -366,88 +579,145 @@ var STORAGE_KEYS = {
366
579
  STOREFRONT_STATE: "storefront_state_v3"
367
580
  };
368
581
 
582
+ // src/utils/customer.ts
583
+ function buildFullName(firstname, lastname) {
584
+ const name = [firstname, lastname].filter(Boolean).join(" ").trim();
585
+ return name || void 0;
586
+ }
587
+ function splitFullName(fullName) {
588
+ if (!fullName?.trim()) return { firstname: "", lastname: "" };
589
+ const parts = fullName.trim().split(/\s+/);
590
+ return {
591
+ firstname: parts[0] ?? "",
592
+ lastname: parts.slice(1).join(" ")
593
+ };
594
+ }
595
+
369
596
  // src/services/auth.service.ts
370
- async function loginCustomer(config, payload) {
371
- const response = await fetch(`${config.backendUrls.auth}/auth/login/customer/`, {
597
+ async function loginCustomer(config, payload, guestSessionId) {
598
+ const headers = { ...JSON_HEADERS, ...storefrontHeaders(config) };
599
+ if (guestSessionId) {
600
+ headers["X-Guest-Session"] = guestSessionId;
601
+ }
602
+ return apiRequest(`${config.backendUrls.auth}/auth/login/customer/`, {
372
603
  method: "POST",
373
- headers: {
374
- "Content-Type": "application/json",
375
- "X-Storefront-Key": config.storefrontApiKey,
376
- "X-Business-ID": config.businessId
377
- },
378
- body: JSON.stringify({
379
- ...payload,
380
- tenant_id: config.businessId
381
- })
604
+ headers,
605
+ body: JSON.stringify(payload),
606
+ fallbackErrorMessage: "Login failed"
382
607
  });
383
- if (!response.ok) {
384
- throw new Error("Login failed");
385
- }
386
- const data = await response.json();
387
- return data;
388
608
  }
389
- async function registerCustomer(config, payload) {
390
- const response = await fetch(`${config.backendUrls.auth}/auth/register/customer/`, {
609
+ async function registerCustomer(config, payload, guestSessionId) {
610
+ const headers = { ...JSON_HEADERS, ...storefrontHeaders(config) };
611
+ if (guestSessionId) {
612
+ headers["X-Guest-Session"] = guestSessionId;
613
+ }
614
+ return apiRequest(`${config.backendUrls.auth}/auth/register/customer/`, {
391
615
  method: "POST",
392
- headers: {
393
- "Content-Type": "application/json",
394
- "X-Storefront-Key": config.storefrontApiKey,
395
- "X-Business-ID": config.businessId
396
- },
397
- body: JSON.stringify({
398
- ...payload,
399
- tenant_id: config.businessId
400
- })
616
+ headers,
617
+ body: JSON.stringify(payload),
618
+ fallbackErrorMessage: "Registration failed"
401
619
  });
402
- if (!response.ok) {
403
- throw new Error("Registration failed");
404
- }
405
- const data = await response.json();
406
- return data;
407
620
  }
408
621
  async function refreshToken(config, refresh) {
409
- const response = await fetch(`${config.backendUrls.auth}/auth/token/refresh/`, {
622
+ return apiRequest(`${config.backendUrls.auth}/auth/token/refresh/`, {
410
623
  method: "POST",
411
- headers: {
412
- "Content-Type": "application/json"
413
- },
414
- body: JSON.stringify({ refresh })
624
+ headers: JSON_HEADERS,
625
+ body: JSON.stringify({ refresh }),
626
+ fallbackErrorMessage: "Token refresh failed"
415
627
  });
416
- if (!response.ok) {
417
- throw new Error("Token refresh failed");
418
- }
419
- const data = await response.json();
420
- return data;
421
628
  }
422
629
  async function logout(config, refresh) {
423
630
  const payload = refresh ? { refresh } : {};
424
631
  try {
425
- await fetch(`${config.backendUrls.auth}/auth/logout/`, {
632
+ await apiRequest(`${config.backendUrls.auth}/auth/logout/`, {
426
633
  method: "POST",
427
- headers: {
428
- "Content-Type": "application/json"
429
- },
430
- body: JSON.stringify(payload)
634
+ headers: JSON_HEADERS,
635
+ body: JSON.stringify(payload),
636
+ fallbackErrorMessage: "Server-side logout failed"
431
637
  });
432
- } catch {
638
+ } catch (err) {
639
+ if (err instanceof ApiError) {
640
+ console.error(`[auth.service] Server-side logout failed with status ${err.status}; local session will still be cleared.`);
641
+ } else {
642
+ console.error("[auth.service] Server-side logout request failed; local session will still be cleared.", err);
643
+ }
433
644
  }
434
645
  }
435
646
  async function verifySocialToken(config, provider, token) {
436
- const response = await fetch(`${config.backendUrls.auth}/auth/social/verify/${provider}/`, {
647
+ return apiRequest(`${config.backendUrls.auth}/auth/social/verify/${provider}/`, {
437
648
  method: "POST",
438
- headers: {
439
- "Content-Type": "application/json"
440
- },
441
- body: JSON.stringify({
442
- id_token: token,
443
- storefront_key: config.storefrontApiKey
444
- })
649
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
650
+ body: JSON.stringify({ id_token: token }),
651
+ fallbackErrorMessage: "Social token verification failed"
445
652
  });
446
- if (!response.ok) {
447
- throw new Error("Social token verification failed");
653
+ }
654
+ async function requestCustomerOtp(config, payload) {
655
+ await apiRequest(`${config.backendUrls.auth}/auth/login/customer/passwordless/request/`, {
656
+ method: "POST",
657
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
658
+ body: JSON.stringify(payload),
659
+ fallbackErrorMessage: "Failed to send login code"
660
+ });
661
+ }
662
+ async function verifyCustomerOtp(config, payload, guestSessionId) {
663
+ const headers = { ...JSON_HEADERS, ...storefrontHeaders(config) };
664
+ if (guestSessionId) {
665
+ headers["X-Guest-Session"] = guestSessionId;
448
666
  }
449
- const data = await response.json();
450
- return data;
667
+ return apiRequest(`${config.backendUrls.auth}/auth/login/customer/passwordless/verify/`, {
668
+ method: "POST",
669
+ headers,
670
+ body: JSON.stringify(payload),
671
+ fallbackErrorMessage: "Invalid or expired code"
672
+ });
673
+ }
674
+ async function forgotPasswordCustomer(config, payload) {
675
+ return apiRequest(`${config.backendUrls.auth}/auth/forgot-password/customer/`, {
676
+ method: "POST",
677
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
678
+ body: JSON.stringify(payload),
679
+ fallbackErrorMessage: "Failed to request password reset"
680
+ });
681
+ }
682
+ async function resetPasswordCustomer(config, payload) {
683
+ return apiRequest(`${config.backendUrls.auth}/auth/reset-password/customer/`, {
684
+ method: "POST",
685
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
686
+ body: JSON.stringify(payload),
687
+ fallbackErrorMessage: "Invalid or expired reset code"
688
+ });
689
+ }
690
+ async function deactivateCustomerAccount(config, accessToken, payload) {
691
+ try {
692
+ const data = await apiRequest(`${config.backendUrls.auth}/auth/customer/deactivate/`, {
693
+ method: "POST",
694
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
695
+ body: JSON.stringify(payload || {}),
696
+ fallbackErrorMessage: "Failed to deactivate account"
697
+ });
698
+ return { status: "success", detail: data.detail };
699
+ } catch (err) {
700
+ if (err instanceof ApiError && err.status === 409) {
701
+ return { status: "confirmation_required", data: err.body };
702
+ }
703
+ throw err;
704
+ }
705
+ }
706
+ async function requestCustomerReactivation(config, payload) {
707
+ return apiRequest(`${config.backendUrls.auth}/auth/customer/reactivate/`, {
708
+ method: "POST",
709
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
710
+ body: JSON.stringify(payload),
711
+ fallbackErrorMessage: "Failed to request reactivation"
712
+ });
713
+ }
714
+ async function verifyCustomerReactivation(config, payload) {
715
+ return apiRequest(`${config.backendUrls.auth}/auth/customer/reactivate/verify/`, {
716
+ method: "POST",
717
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
718
+ body: JSON.stringify(payload),
719
+ fallbackErrorMessage: "Invalid or expired reactivation code"
720
+ });
451
721
  }
452
722
  function parseJwtPayload(token) {
453
723
  try {
@@ -502,70 +772,94 @@ function buildCustomerFromClaims(claims) {
502
772
  }
503
773
  async function getMe(config, accessToken) {
504
774
  if (!config || !accessToken) {
505
- const response2 = await fetch("/api/auth/me", { credentials: "include" });
506
- if (!response2.ok) throw new Error("Failed to fetch profile");
507
- const data2 = await response2.json();
508
- return data2.user;
775
+ const data2 = await apiRequest("/api/auth/me", {
776
+ credentials: "include",
777
+ fallbackErrorMessage: "Failed to fetch profile"
778
+ });
779
+ return mapAuthUserToCustomer(data2.user);
509
780
  }
510
- const response = await fetch(`${config.backendUrls.auth}/auth/profile/`, {
511
- headers: {
512
- "Authorization": `Bearer ${accessToken}`
513
- }
781
+ const data = await apiRequest(`${config.backendUrls.auth}/auth/profile/`, {
782
+ headers: authHeaders(config, accessToken),
783
+ fallbackErrorMessage: "Failed to fetch profile"
514
784
  });
515
- if (!response.ok) {
516
- throw new Error("Failed to fetch profile");
517
- }
518
- const data = await response.json();
519
785
  return mapAuthUserToCustomer(data);
520
786
  }
521
787
  async function getAuthUser(config, accessToken) {
522
788
  if (!config || !accessToken) {
523
- const response2 = await fetch("/api/auth/me", { credentials: "include" });
524
- if (!response2.ok) throw new Error("Failed to fetch auth user");
525
- const data2 = await response2.json();
526
- return data2.user;
789
+ const data = await apiRequest("/api/auth/me", {
790
+ credentials: "include",
791
+ fallbackErrorMessage: "Failed to fetch auth user"
792
+ });
793
+ return data.user;
527
794
  }
528
- const response = await fetch(`${config.backendUrls.auth}/auth/profile/`, {
529
- headers: {
530
- "Authorization": `Bearer ${accessToken}`
531
- }
795
+ return apiRequest(`${config.backendUrls.auth}/auth/profile/`, {
796
+ headers: authHeaders(config, accessToken),
797
+ fallbackErrorMessage: "Failed to fetch auth user"
532
798
  });
533
- if (!response.ok) {
534
- throw new Error("Failed to fetch auth user");
535
- }
536
- const data = await response.json();
537
- return data;
538
799
  }
539
800
  async function updateProfile(configOrPayload, accessTokenOrPayload, payloadParam) {
540
801
  const isArg1Config = typeof configOrPayload === "object" && configOrPayload !== null && "businessId" in configOrPayload;
541
802
  if (!isArg1Config) {
542
803
  const payload2 = configOrPayload;
543
- const response2 = await fetch("/api/auth/profile", {
804
+ return apiRequest("/api/auth/profile", {
544
805
  method: "PUT",
545
- headers: { "Content-Type": "application/json" },
806
+ headers: JSON_HEADERS,
546
807
  credentials: "include",
547
- body: JSON.stringify(payload2)
808
+ body: JSON.stringify(payload2),
809
+ fallbackErrorMessage: "Failed to update profile"
548
810
  });
549
- if (!response2.ok) throw new Error("Failed to update profile");
550
- return response2.json();
551
811
  }
552
812
  const config = configOrPayload;
553
813
  const accessToken = accessTokenOrPayload;
554
814
  const payload = payloadParam;
555
- const response = await fetch(`${config.backendUrls.auth}/auth/profile/`, {
815
+ const data = await apiRequest(`${config.backendUrls.auth}/auth/profile/`, {
556
816
  method: "PUT",
557
- headers: {
558
- "Content-Type": "application/json",
559
- "Authorization": `Bearer ${accessToken}`
560
- },
561
- body: JSON.stringify(payload)
817
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
818
+ body: JSON.stringify(payload),
819
+ fallbackErrorMessage: "Failed to update profile"
562
820
  });
563
- if (!response.ok) {
564
- throw new Error("Failed to update profile");
565
- }
566
- const data = await response.json();
567
821
  return mapAuthUserToCustomer(data);
568
822
  }
823
+ async function changePassword(config, accessToken, payload) {
824
+ return apiRequest(`${config.backendUrls.auth}/auth/change-password/`, {
825
+ method: "POST",
826
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
827
+ body: JSON.stringify(payload),
828
+ fallbackErrorMessage: "Failed to change password"
829
+ });
830
+ }
831
+ async function getStorefrontBusinessInfo(config) {
832
+ return apiRequest(`${config.backendUrls.auth}/auth/storefront/business-info/`, {
833
+ headers: storefrontHeaders(config),
834
+ fallbackErrorMessage: "Failed to fetch business info"
835
+ });
836
+ }
837
+ async function getPresignedUploadUrl(config, accessToken, params) {
838
+ const query = new URLSearchParams(
839
+ Object.entries(params).filter(([, v]) => v !== void 0)
840
+ );
841
+ return apiRequest(
842
+ `${config.backendUrls.auth}/auth/media/presigned-url/?${query.toString()}`,
843
+ {
844
+ method: "POST",
845
+ headers: { Authorization: `Bearer ${accessToken}` },
846
+ fallbackErrorMessage: "Failed to get presigned upload URL"
847
+ }
848
+ );
849
+ }
850
+ async function confirmMediaUpload(config, accessToken, params) {
851
+ const query = new URLSearchParams(
852
+ Object.entries(params).filter(([, v]) => v !== void 0)
853
+ );
854
+ return apiRequest(
855
+ `${config.backendUrls.auth}/auth/media/confirm-upload/?${query.toString()}`,
856
+ {
857
+ method: "POST",
858
+ headers: { Authorization: `Bearer ${accessToken}` },
859
+ fallbackErrorMessage: "Failed to confirm upload"
860
+ }
861
+ );
862
+ }
569
863
  async function silentRefresh(config, refresh) {
570
864
  try {
571
865
  const data = await refreshToken(config, refresh);
@@ -576,25 +870,11 @@ async function silentRefresh(config, refresh) {
576
870
  const user = buildCustomerFromClaims(claims);
577
871
  const provider = user.provider;
578
872
  return { user, provider, newAccessToken: data.access, newRefreshToken: data.refresh };
579
- } catch {
873
+ } catch (err) {
874
+ console.error("[auth.service] Silent token refresh failed:", err);
580
875
  return null;
581
876
  }
582
877
  }
583
- function buildFullName(firstname, lastname) {
584
- if (firstname && lastname) return `${firstname} ${lastname}`;
585
- return firstname || lastname || "";
586
- }
587
- function splitFullName(fullName) {
588
- if (!fullName) return { firstname: "", lastname: "" };
589
- const parts = fullName.trim().split(/\s+/);
590
- if (parts.length === 1) {
591
- return { firstname: parts[0], lastname: "" };
592
- }
593
- return {
594
- firstname: parts[0],
595
- lastname: parts.slice(1).join(" ")
596
- };
597
- }
598
878
  function storeTokens(accessToken, refreshToken2) {
599
879
  if (typeof window !== "undefined") {
600
880
  localStorage.setItem(STORAGE_KEYS.ACCESS_TOKEN, accessToken);
@@ -616,1061 +896,369 @@ function getStoredTokens() {
616
896
  refreshToken: localStorage.getItem(STORAGE_KEYS.REFRESH_TOKEN)
617
897
  };
618
898
  }
899
+ async function getCustomerAddresses(config, accessToken) {
900
+ const data = await apiRequest(`${config.backendUrls.auth}/auth/profile/addresses/`, {
901
+ headers: authHeaders(config, accessToken),
902
+ fallbackErrorMessage: "Failed to fetch saved addresses"
903
+ });
904
+ return data.results;
905
+ }
906
+ async function createCustomerAddress(config, accessToken, payload) {
907
+ return apiRequest(`${config.backendUrls.auth}/auth/profile/addresses/`, {
908
+ method: "POST",
909
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
910
+ body: JSON.stringify(payload),
911
+ fallbackErrorMessage: "Failed to save address"
912
+ });
913
+ }
914
+ async function updateCustomerAddress(config, accessToken, addressId, payload) {
915
+ return apiRequest(`${config.backendUrls.auth}/auth/profile/addresses/${addressId}/`, {
916
+ method: "PATCH",
917
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
918
+ body: JSON.stringify(payload),
919
+ fallbackErrorMessage: "Failed to update address"
920
+ });
921
+ }
922
+ async function deleteCustomerAddress(config, accessToken, addressId) {
923
+ await apiRequest(`${config.backendUrls.auth}/auth/profile/addresses/${addressId}/`, {
924
+ method: "DELETE",
925
+ headers: authHeaders(config, accessToken),
926
+ fallbackErrorMessage: "Failed to delete address"
927
+ });
928
+ }
619
929
 
620
- // src/services/product.service.ts
930
+ // src/utils/graphql-url.ts
621
931
  import { GraphQLClient } from "graphql-request";
622
- function createGraphQLClient(config) {
932
+ function resolveGraphQLEndpoint(url) {
933
+ if (typeof window === "undefined") return url;
934
+ try {
935
+ new URL(url);
936
+ return url;
937
+ } catch {
938
+ return `${window.location.origin}${url}`;
939
+ }
940
+ }
941
+ function createGraphQLClient(config, accessToken) {
623
942
  const headers = {
624
943
  "Content-Type": "application/json",
625
944
  "Accept": "application/json",
626
945
  "X-Storefront-Key": config.storefrontApiKey,
627
946
  "X-Business-ID": config.businessId
628
947
  };
629
- return new GraphQLClient(`${config.backendUrls.store}/graphql`, { headers });
630
- }
631
- var GET_STOREFRONT_PRODUCTS = `
632
- query GetStorefrontProducts($page: Int!, $pageSize: Int!, $collectionSlug: String, $search: String) {
633
- storefront_products(page: $page, pageSize: $pageSize, collectionSlug: $collectionSlug, search: $search) {
634
- items {
635
- id
636
- business_id
637
- name
638
- slug
639
- description
640
- brand
641
- product_type
642
- requires_shipping
643
- is_consumable
644
- is_bespoke
645
- base_price
646
- currency
647
- images
648
- tags
649
- custom_fields { label unit required }
650
- is_published
651
- is_featured
652
- is_popular
653
- is_new
654
- is_temporary
655
- digital_file_key
656
- download_limit
657
- download_expires_at
658
- weight_kg
659
- length_cm
660
- width_cm
661
- height_cm
662
- created_at
663
- updated_at
664
- variants {
665
- id
666
- product_id
667
- sku
668
- attributes
669
- price
670
- images
671
- weight_kg
672
- is_active
673
- inventory {
674
- id
675
- product_id
676
- variant_id
677
- quantity
678
- reserved
679
- reorder_point
680
- reorder_quantity
681
- track_quantity
682
- allow_backorder
683
- available
684
- is_in_stock
685
- is_low_stock
686
- updated_at
687
- }
688
- created_at
689
- updated_at
690
- }
691
- collections {
692
- id
693
- business_id
694
- name
695
- slug
696
- description
697
- image
698
- collection_type
699
- rules
700
- status
701
- sort_order
702
- created_at
703
- updated_at
704
- }
705
- }
706
- total
707
- page
708
- page_size
709
- total_pages
710
- }
711
- }
712
- `;
713
- var GET_STOREFRONT_PRODUCT_BY_SLUG = `
714
- query GetStorefrontProductBySlug($slug: String!) {
715
- storefront_product_by_slug(slug: $slug) {
716
- id
717
- business_id
718
- name
719
- slug
720
- description
721
- brand
722
- product_type
723
- requires_shipping
724
- is_consumable
725
- is_bespoke
726
- base_price
727
- currency
728
- images
729
- tags
730
- custom_fields { label unit required }
731
- is_published
732
- is_featured
733
- is_popular
734
- is_new
735
- is_temporary
736
- digital_file_key
737
- download_limit
738
- download_expires_at
739
- weight_kg
740
- length_cm
741
- width_cm
742
- height_cm
743
- created_at
744
- updated_at
745
- variants {
746
- id
747
- product_id
748
- sku
749
- attributes
750
- price
751
- images
752
- weight_kg
753
- is_active
754
- inventory {
755
- id
756
- product_id
757
- variant_id
758
- quantity
759
- reserved
760
- reorder_point
761
- reorder_quantity
762
- track_quantity
763
- allow_backorder
764
- available
765
- is_in_stock
766
- is_low_stock
767
- updated_at
768
- }
769
- created_at
770
- updated_at
771
- }
772
- collections {
773
- id
774
- business_id
775
- name
776
- slug
777
- description
778
- image
779
- collection_type
780
- rules
781
- status
782
- sort_order
783
- created_at
784
- updated_at
785
- }
786
- }
787
- }
788
- `;
789
- var GET_STOREFRONT_LATEST = `
790
- query GetStorefrontLatest($page: Int!, $pageSize: Int!) {
791
- storefront_latest(page: $page, pageSize: $pageSize) {
792
- items {
793
- id
794
- business_id
795
- name
796
- slug
797
- description
798
- brand
799
- product_type
800
- requires_shipping
801
- is_consumable
802
- is_bespoke
803
- base_price
804
- currency
805
- images
806
- tags
807
- custom_fields { label unit required }
808
- is_published
809
- is_featured
810
- is_popular
811
- is_new
812
- is_temporary
813
- digital_file_key
814
- download_limit
815
- download_expires_at
816
- weight_kg
817
- length_cm
818
- width_cm
819
- height_cm
820
- created_at
821
- updated_at
822
- variants {
823
- id
824
- product_id
825
- sku
826
- attributes
827
- price
828
- images
829
- weight_kg
830
- is_active
831
- inventory {
832
- id
833
- product_id
834
- variant_id
835
- quantity
836
- reserved
837
- reorder_point
838
- reorder_quantity
839
- track_quantity
840
- allow_backorder
841
- available
842
- is_in_stock
843
- is_low_stock
844
- updated_at
845
- }
846
- created_at
847
- updated_at
848
- }
849
- collections {
850
- id
851
- business_id
852
- name
853
- slug
854
- description
855
- image
856
- collection_type
857
- rules
858
- status
859
- sort_order
860
- created_at
861
- updated_at
862
- }
863
- }
864
- total
865
- page
866
- page_size
867
- total_pages
868
- }
869
- }
870
- `;
871
- var GET_STOREFRONT_POPULAR = `
872
- query GetStorefrontPopular($page: Int!, $pageSize: Int!) {
873
- storefront_popular(page: $page, pageSize: $pageSize) {
874
- items {
875
- id
876
- business_id
877
- name
878
- slug
879
- description
880
- brand
881
- product_type
882
- requires_shipping
883
- is_consumable
884
- is_bespoke
885
- base_price
886
- currency
887
- images
888
- tags
889
- custom_fields { label unit required }
890
- is_published
891
- is_featured
892
- is_popular
893
- is_new
894
- is_temporary
895
- digital_file_key
896
- download_limit
897
- download_expires_at
898
- weight_kg
899
- length_cm
900
- width_cm
901
- height_cm
902
- created_at
903
- updated_at
904
- variants {
905
- id
906
- product_id
907
- sku
908
- attributes
909
- price
910
- images
911
- weight_kg
912
- is_active
913
- inventory {
914
- id
915
- product_id
916
- variant_id
917
- quantity
918
- reserved
919
- reorder_point
920
- reorder_quantity
921
- track_quantity
922
- allow_backorder
923
- available
924
- is_in_stock
925
- is_low_stock
926
- updated_at
927
- }
928
- created_at
929
- updated_at
930
- }
931
- collections {
932
- id
933
- business_id
934
- name
935
- slug
936
- description
937
- image
938
- collection_type
939
- rules
940
- status
941
- sort_order
942
- created_at
943
- updated_at
944
- }
945
- }
946
- total
947
- page
948
- page_size
949
- total_pages
950
- }
951
- }
952
- `;
953
- var GET_STOREFRONT_FEATURED = `
954
- query GetStorefrontFeatured($page: Int!, $pageSize: Int!) {
955
- storefront_featured(page: $page, pageSize: $pageSize) {
956
- items {
957
- id
958
- business_id
959
- name
960
- slug
961
- description
962
- brand
963
- product_type
964
- requires_shipping
965
- is_consumable
966
- is_bespoke
967
- base_price
968
- currency
969
- images
970
- tags
971
- custom_fields { label unit required }
972
- is_published
973
- is_featured
974
- is_popular
975
- is_new
976
- is_temporary
977
- digital_file_key
978
- download_limit
979
- download_expires_at
980
- weight_kg
981
- length_cm
982
- width_cm
983
- height_cm
984
- created_at
985
- updated_at
986
- variants {
987
- id
988
- product_id
989
- sku
990
- attributes
991
- price
992
- images
993
- weight_kg
994
- is_active
995
- inventory {
996
- id
997
- product_id
998
- variant_id
999
- quantity
1000
- reserved
1001
- reorder_point
1002
- reorder_quantity
1003
- track_quantity
1004
- allow_backorder
1005
- available
1006
- is_in_stock
1007
- is_low_stock
1008
- updated_at
1009
- }
1010
- created_at
1011
- updated_at
1012
- }
1013
- collections {
1014
- id
1015
- business_id
1016
- name
1017
- slug
1018
- description
1019
- image
1020
- collection_type
1021
- rules
1022
- status
1023
- sort_order
1024
- created_at
1025
- updated_at
1026
- }
1027
- }
1028
- total
1029
- page
1030
- page_size
1031
- total_pages
1032
- }
1033
- }
1034
- `;
1035
- var GET_STOREFRONT_NEW = `
1036
- query GetStorefrontNew($page: Int!, $pageSize: Int!) {
1037
- storefront_new(page: $page, pageSize: $pageSize) {
1038
- items {
1039
- id
1040
- business_id
1041
- name
1042
- slug
1043
- description
1044
- brand
1045
- product_type
1046
- requires_shipping
1047
- is_consumable
1048
- is_bespoke
1049
- base_price
1050
- currency
1051
- sale_price
1052
- sale_start
1053
- sale_end
1054
- images
1055
- tags
1056
- custom_fields { label unit required }
1057
- is_published
1058
- is_featured
1059
- is_popular
1060
- is_new
1061
- is_temporary
1062
- digital_file_key
1063
- download_limit
1064
- download_expires_at
1065
- weight_kg
1066
- length_cm
1067
- width_cm
1068
- height_cm
1069
- created_at
1070
- updated_at
1071
- variants {
1072
- id
1073
- product_id
1074
- sku
1075
- attributes
1076
- price
1077
- images
1078
- weight_kg
1079
- is_active
1080
- inventory {
1081
- id
1082
- product_id
1083
- variant_id
1084
- quantity
1085
- reserved
1086
- reorder_point
1087
- reorder_quantity
1088
- track_quantity
1089
- allow_backorder
1090
- available
1091
- is_in_stock
1092
- is_low_stock
1093
- updated_at
1094
- }
1095
- created_at
1096
- updated_at
1097
- }
1098
- collections {
1099
- id
1100
- business_id
1101
- name
1102
- slug
1103
- description
1104
- image
1105
- collection_type
1106
- rules
1107
- status
1108
- sort_order
1109
- created_at
1110
- updated_at
1111
- }
1112
- }
1113
- total
1114
- page
1115
- page_size
1116
- total_pages
1117
- }
1118
- }
1119
- `;
1120
- var GET_STOREFRONT_ON_SALE = `
1121
- query GetStorefrontOnSale($page: Int!, $pageSize: Int!) {
1122
- storefront_on_sale(page: $page, pageSize: $pageSize) {
1123
- items {
1124
- id
1125
- business_id
1126
- name
1127
- slug
1128
- description
1129
- brand
1130
- product_type
1131
- requires_shipping
1132
- is_consumable
1133
- is_bespoke
1134
- base_price
1135
- currency
1136
- sale_price
1137
- sale_start
1138
- sale_end
1139
- images
1140
- tags
1141
- custom_fields { label unit required }
1142
- is_published
1143
- is_featured
1144
- is_popular
1145
- is_new
1146
- is_temporary
1147
- digital_file_key
1148
- download_limit
1149
- download_expires_at
1150
- weight_kg
1151
- length_cm
1152
- width_cm
1153
- height_cm
1154
- created_at
1155
- updated_at
1156
- variants {
1157
- id
1158
- product_id
1159
- sku
1160
- attributes
1161
- price
1162
- images
1163
- weight_kg
1164
- is_active
1165
- inventory {
1166
- id
1167
- product_id
1168
- variant_id
1169
- quantity
1170
- reserved
1171
- reorder_point
1172
- reorder_quantity
1173
- track_quantity
1174
- allow_backorder
1175
- available
1176
- is_in_stock
1177
- is_low_stock
1178
- updated_at
1179
- }
1180
- created_at
1181
- updated_at
1182
- }
1183
- collections {
1184
- id
1185
- business_id
1186
- name
1187
- slug
1188
- description
1189
- image
1190
- collection_type
1191
- rules
1192
- status
1193
- sort_order
1194
- created_at
1195
- updated_at
1196
- }
1197
- }
1198
- total
1199
- page
1200
- page_size
1201
- total_pages
1202
- }
1203
- }
1204
- `;
1205
- var GET_STOREFRONT_LOW_STOCK = `
1206
- query GetStorefrontLowStock($page: Int!, $pageSize: Int!) {
1207
- storefront_low_stock(page: $page, pageSize: $pageSize) {
1208
- items {
1209
- id
1210
- business_id
1211
- name
1212
- slug
1213
- description
1214
- brand
1215
- product_type
1216
- requires_shipping
1217
- is_consumable
1218
- is_bespoke
1219
- base_price
1220
- currency
1221
- sale_price
1222
- sale_start
1223
- sale_end
1224
- images
1225
- tags
1226
- custom_fields { label unit required }
1227
- is_published
1228
- is_featured
1229
- is_popular
1230
- is_new
1231
- is_temporary
1232
- digital_file_key
1233
- download_limit
1234
- download_expires_at
1235
- weight_kg
1236
- length_cm
1237
- width_cm
1238
- height_cm
1239
- created_at
1240
- updated_at
1241
- variants {
1242
- id
1243
- product_id
1244
- sku
1245
- attributes
1246
- price
1247
- images
1248
- weight_kg
1249
- is_active
1250
- inventory {
1251
- id
1252
- product_id
1253
- variant_id
1254
- quantity
1255
- reserved
1256
- reorder_point
1257
- reorder_quantity
1258
- track_quantity
1259
- allow_backorder
1260
- available
1261
- is_in_stock
1262
- is_low_stock
1263
- updated_at
1264
- }
1265
- created_at
1266
- updated_at
1267
- }
1268
- collections {
1269
- id
1270
- business_id
1271
- name
1272
- slug
1273
- description
1274
- image
1275
- collection_type
1276
- rules
1277
- status
1278
- sort_order
1279
- created_at
1280
- updated_at
1281
- }
1282
- }
1283
- total
1284
- page
1285
- page_size
1286
- total_pages
1287
- }
948
+ if (accessToken) {
949
+ headers["Authorization"] = `Bearer ${accessToken}`;
1288
950
  }
1289
- `;
1290
- var GET_STOREFRONT_RECOMMENDED = `
1291
- query GetStorefrontRecommended($page: Int!, $pageSize: Int!, $excludeProductId: String) {
1292
- storefront_recommended(page: $page, pageSize: $pageSize, excludeProductId: $excludeProductId) {
1293
- items {
1294
- id
1295
- business_id
1296
- name
1297
- slug
1298
- description
1299
- brand
1300
- product_type
1301
- requires_shipping
1302
- is_consumable
1303
- is_bespoke
1304
- base_price
1305
- currency
1306
- images
1307
- tags
1308
- custom_fields { label unit required }
1309
- is_published
1310
- is_featured
1311
- is_popular
1312
- is_new
1313
- is_temporary
1314
- digital_file_key
1315
- download_limit
1316
- download_expires_at
1317
- weight_kg
1318
- length_cm
1319
- width_cm
1320
- height_cm
1321
- created_at
1322
- updated_at
1323
- variants {
1324
- id
1325
- product_id
1326
- sku
1327
- attributes
1328
- price
1329
- images
1330
- weight_kg
1331
- is_active
1332
- inventory {
1333
- id
1334
- product_id
1335
- variant_id
1336
- quantity
1337
- reserved
1338
- reorder_point
1339
- reorder_quantity
1340
- track_quantity
1341
- allow_backorder
1342
- available
1343
- is_in_stock
1344
- is_low_stock
1345
- updated_at
1346
- }
1347
- created_at
1348
- updated_at
1349
- }
1350
- collections {
1351
- id
1352
- business_id
1353
- name
1354
- slug
1355
- description
1356
- image
1357
- collection_type
1358
- rules
1359
- status
1360
- sort_order
1361
- created_at
1362
- updated_at
1363
- }
1364
- }
1365
- total
1366
- page
1367
- page_size
1368
- total_pages
951
+ return new GraphQLClient(resolveGraphQLEndpoint(`${config.backendUrls.store}/graphql`), { headers });
952
+ }
953
+
954
+ // src/generated/graphql.ts
955
+ 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" } }] } }] };
956
+ 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" } }] } }] };
957
+ 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" } }] } }] } }] };
958
+ 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" } }] } }] } }] };
959
+ 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" } }] } }] } }] };
960
+ 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" } }] } }] } }] };
961
+ 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" } }] } }] } }] };
962
+ 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" } }] } }] } }] };
963
+ 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" } }] } }] } }] };
964
+ 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" } }] } }] } }] };
965
+ 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" } }] } }] } }] };
966
+
967
+ // src/services/product.service.ts
968
+ function mapProductType(raw) {
969
+ switch (raw) {
970
+ case "PHYSICAL":
971
+ return "physical" /* PHYSICAL */;
972
+ case "DIGITAL":
973
+ return "digital" /* DIGITAL */;
974
+ case "SERVICE":
975
+ return "service" /* SERVICE */;
976
+ case "BESPOKE":
977
+ return "bespoke" /* BESPOKE */;
978
+ default: {
979
+ const _exhaustive = raw;
980
+ throw new Error(`Unhandled GraphQL ProductType member: ${_exhaustive}`);
1369
981
  }
1370
982
  }
1371
- `;
983
+ }
984
+ function mapCustomFieldType(raw) {
985
+ if (raw === "text" || raw === "choice") return raw;
986
+ throw new Error(`Unrecognised custom field field_type from backend: "${raw}"`);
987
+ }
988
+ function mapProduct(raw) {
989
+ return {
990
+ ...raw,
991
+ product_type: mapProductType(raw.product_type),
992
+ custom_fields: raw.custom_fields.map((f) => ({
993
+ ...f,
994
+ unit: f.unit ?? void 0,
995
+ field_type: mapCustomFieldType(f.field_type)
996
+ })),
997
+ description: raw.description ?? void 0,
998
+ brand: raw.brand ?? void 0,
999
+ is_physical: raw.is_physical ?? void 0,
1000
+ sale_price: raw.sale_price ?? void 0,
1001
+ sale_start: raw.sale_start ?? void 0,
1002
+ sale_end: raw.sale_end ?? void 0,
1003
+ source_channel_ref_id: raw.source_channel_ref_id ?? void 0,
1004
+ download_limit: raw.download_limit ?? void 0,
1005
+ download_expires_at: raw.download_expires_at ?? void 0,
1006
+ service_type: raw.service_type ?? void 0,
1007
+ service_duration_minutes: raw.service_duration_minutes ?? void 0,
1008
+ service_location_type: raw.service_location_type ?? void 0,
1009
+ service_coverage_radius_km: raw.service_coverage_radius_km ?? void 0,
1010
+ service_max_capacity_per_slot: raw.service_max_capacity_per_slot ?? void 0,
1011
+ service_recurring_interval: raw.service_recurring_interval ?? void 0,
1012
+ length_cm: raw.length_cm ?? void 0,
1013
+ width_cm: raw.width_cm ?? void 0,
1014
+ height_cm: raw.height_cm ?? void 0,
1015
+ average_rating: raw.average_rating ?? void 0,
1016
+ created_at: raw.created_at,
1017
+ updated_at: raw.updated_at,
1018
+ variants: raw.variants.map((v) => ({
1019
+ ...v,
1020
+ sku: v.sku ?? void 0,
1021
+ attributes: v.attributes ? JSON.parse(v.attributes) : {},
1022
+ price: v.price ?? void 0,
1023
+ weight_kg: v.weight_kg ?? void 0,
1024
+ inventory: v.inventory ? {
1025
+ ...v.inventory,
1026
+ variant_id: v.inventory.variant_id ?? void 0,
1027
+ updated_at: v.inventory.updated_at
1028
+ } : void 0,
1029
+ created_at: v.created_at,
1030
+ updated_at: v.updated_at
1031
+ })),
1032
+ collections: raw.collections.map((c) => ({
1033
+ ...c,
1034
+ business_id: c.business_id ?? void 0,
1035
+ description: c.description ?? void 0,
1036
+ image_url: c.image_url ?? void 0,
1037
+ rules: c.rules ?? void 0,
1038
+ created_at: c.created_at,
1039
+ updated_at: c.updated_at
1040
+ }))
1041
+ };
1042
+ }
1043
+ function mapProductConnection(connection) {
1044
+ return { ...connection, items: connection.items.map(mapProduct) };
1045
+ }
1372
1046
  async function getStorefrontProducts(config, params) {
1373
1047
  const client = createGraphQLClient(config);
1374
- const variables = {
1048
+ const data = await client.request(GetStorefrontProductsDocument, {
1375
1049
  page: params?.page || 1,
1376
1050
  pageSize: params?.pageSize || 20,
1377
1051
  collectionSlug: params?.collectionSlug,
1378
- search: params?.search
1379
- };
1380
- const data = await client.request(GET_STOREFRONT_PRODUCTS, variables);
1381
- return data.storefront_products;
1052
+ search: params?.search,
1053
+ productType: params?.productType
1054
+ });
1055
+ return mapProductConnection(data.storefront_products);
1382
1056
  }
1383
1057
  async function getStorefrontProductBySlug(config, slug) {
1384
1058
  const client = createGraphQLClient(config);
1385
- const data = await client.request(GET_STOREFRONT_PRODUCT_BY_SLUG, { slug });
1386
- return data.storefront_product_by_slug;
1387
- }
1388
- async function getLatestProducts(config, params) {
1389
- const client = createGraphQLClient(config);
1390
- const variables = {
1391
- page: params?.page || 1,
1392
- pageSize: params?.pageSize || 4
1393
- };
1394
- const data = await client.request(GET_STOREFRONT_LATEST, variables);
1395
- return data.storefront_latest;
1059
+ const data = await client.request(GetStorefrontProductBySlugDocument, { slug });
1060
+ if (!data.storefront_product_by_slug) {
1061
+ throw new Error(`Product not found: ${slug}`);
1062
+ }
1063
+ return mapProduct(data.storefront_product_by_slug);
1396
1064
  }
1397
1065
  async function getPopularProducts(config, params) {
1398
1066
  const client = createGraphQLClient(config);
1399
- const variables = {
1067
+ const data = await client.request(GetStorefrontPopularDocument, {
1400
1068
  page: params?.page || 1,
1401
1069
  pageSize: params?.pageSize || 4
1402
- };
1403
- const data = await client.request(GET_STOREFRONT_POPULAR, variables);
1404
- return data.storefront_popular;
1070
+ });
1071
+ return mapProductConnection(data.storefront_popular);
1405
1072
  }
1406
1073
  async function getFeaturedProducts(config, params) {
1407
1074
  const client = createGraphQLClient(config);
1408
- const variables = {
1075
+ const data = await client.request(GetStorefrontFeaturedDocument, {
1409
1076
  page: params?.page || 1,
1410
1077
  pageSize: params?.pageSize || 4
1411
- };
1412
- const data = await client.request(GET_STOREFRONT_FEATURED, variables);
1413
- return data.storefront_featured;
1078
+ });
1079
+ return mapProductConnection(data.storefront_featured);
1414
1080
  }
1415
1081
  async function getNewProducts(config, params) {
1416
1082
  const client = createGraphQLClient(config);
1417
- const variables = {
1083
+ const data = await client.request(GetStorefrontNewDocument, {
1418
1084
  page: params?.page || 1,
1419
1085
  pageSize: params?.pageSize || 4
1420
- };
1421
- const data = await client.request(GET_STOREFRONT_NEW, variables);
1422
- return data.storefront_new;
1086
+ });
1087
+ return mapProductConnection(data.storefront_new);
1423
1088
  }
1424
1089
  async function getOnSaleProducts(config, params) {
1425
1090
  const client = createGraphQLClient(config);
1426
- const variables = {
1427
- page: params?.page || 1,
1428
- pageSize: params?.pageSize || 4
1429
- };
1430
- const data = await client.request(GET_STOREFRONT_ON_SALE, variables);
1431
- return data.storefront_on_sale;
1432
- }
1433
- async function getLowStockProducts(config, params) {
1434
- const client = createGraphQLClient(config);
1435
- const variables = {
1091
+ const data = await client.request(GetStorefrontOnSaleDocument, {
1436
1092
  page: params?.page || 1,
1437
1093
  pageSize: params?.pageSize || 4
1438
- };
1439
- const data = await client.request(GET_STOREFRONT_LOW_STOCK, variables);
1440
- return data.storefront_low_stock;
1094
+ });
1095
+ return mapProductConnection(data.storefront_on_sale);
1441
1096
  }
1442
1097
  async function getRecommendedProducts(config, params) {
1443
1098
  const client = createGraphQLClient(config);
1444
- const variables = {
1099
+ const data = await client.request(GetStorefrontRecommendedDocument, {
1445
1100
  page: params?.page || 1,
1446
1101
  pageSize: params?.pageSize || 4,
1447
1102
  excludeProductId: params?.excludeProductId
1448
- };
1449
- const data = await client.request(GET_STOREFRONT_RECOMMENDED, variables);
1450
- return data.storefront_recommended;
1103
+ });
1104
+ return mapProductConnection(data.storefront_recommended);
1451
1105
  }
1452
1106
 
1453
1107
  // src/services/collection.service.ts
1454
- import { GraphQLClient as GraphQLClient2 } from "graphql-request";
1455
- var GET_STOREFRONT_COLLECTIONS = `
1456
- query GetStorefrontCollections($page: Int!, $pageSize: Int!) {
1457
- storefront_collections(page: $page, pageSize: $pageSize) {
1458
- items {
1459
- id
1460
- business_id
1461
- name
1462
- slug
1463
- description
1464
- image
1465
- collection_type
1466
- rules
1467
- status
1468
- sort_order
1469
- created_at
1470
- updated_at
1471
- }
1472
- total
1473
- page
1474
- page_size
1475
- total_pages
1476
- }
1477
- }
1478
- `;
1479
- var GET_STOREFRONT_COLLECTION_BY_SLUG = `
1480
- query GetStorefrontCollectionBySlug($slug: String!) {
1481
- storefront_collection_by_slug(slug: $slug) {
1482
- id
1483
- business_id
1484
- name
1485
- slug
1486
- description
1487
- image
1488
- collection_type
1489
- rules
1490
- status
1491
- sort_order
1492
- created_at
1493
- updated_at
1494
- }
1495
- }
1496
- `;
1497
- function createGraphQLClient2(config) {
1498
- const headers = {
1499
- "Content-Type": "application/json",
1500
- "Accept": "application/json",
1501
- "X-Storefront-Key": config.storefrontApiKey,
1502
- "X-Business-ID": config.businessId
1108
+ function mapCollection(raw) {
1109
+ return {
1110
+ ...raw,
1111
+ business_id: raw.business_id ?? void 0,
1112
+ description: raw.description ?? void 0,
1113
+ image_url: raw.image_url ?? void 0,
1114
+ rules: raw.rules ?? void 0,
1115
+ created_at: raw.created_at,
1116
+ updated_at: raw.updated_at
1503
1117
  };
1504
- return new GraphQLClient2(`${config.backendUrls.store}/graphql`, { headers });
1505
1118
  }
1506
1119
  async function getStorefrontCollections(config, params) {
1507
- const client = createGraphQLClient2(config);
1508
- const variables = {
1120
+ const client = createGraphQLClient(config);
1121
+ const data = await client.request(GetStorefrontCollectionsDocument, {
1509
1122
  page: params?.page || 1,
1510
1123
  pageSize: params?.pageSize || 20
1511
- };
1512
- const data = await client.request(GET_STOREFRONT_COLLECTIONS, variables);
1513
- return data.storefront_collections;
1124
+ });
1125
+ return { ...data.storefront_collections, items: data.storefront_collections.items.map(mapCollection) };
1514
1126
  }
1515
1127
  async function getStorefrontCollectionBySlug(config, slug) {
1516
- const client = createGraphQLClient2(config);
1517
- const data = await client.request(GET_STOREFRONT_COLLECTION_BY_SLUG, { slug });
1518
- return data.storefront_collection_by_slug;
1128
+ const client = createGraphQLClient(config);
1129
+ const data = await client.request(GetStorefrontCollectionBySlugDocument, { slug });
1130
+ if (!data.storefront_collection_by_slug) {
1131
+ throw new Error(`Collection not found: ${slug}`);
1132
+ }
1133
+ return mapCollection(data.storefront_collection_by_slug);
1519
1134
  }
1520
1135
 
1521
1136
  // src/services/wishlist.service.ts
1522
- import { GraphQLClient as GraphQLClient3 } from "graphql-request";
1523
- var GET_STOREFRONT_WISHLIST = `
1524
- query GetStorefrontWishlist($page: Int!, $pageSize: Int!) {
1525
- storefront_wishlist(page: $page, pageSize: $pageSize) {
1526
- items {
1527
- id
1528
- product_id
1529
- notes
1530
- created_at
1531
- product_name
1532
- product_slug
1533
- product_price
1534
- currency
1535
- product_image
1536
- }
1537
- total
1538
- page
1539
- page_size
1540
- total_pages
1541
- }
1542
- }
1543
- `;
1544
- var CHECK_IN_STOREFRONT_WISHLIST = `
1545
- query CheckInStorefrontWishlist($productId: String!) {
1546
- check_in_storefront_wishlist(productId: $productId) {
1547
- in_wishlist
1548
- }
1549
- }
1550
- `;
1551
- function createGraphQLClient3(config, accessToken) {
1552
- const headers = {
1553
- "Content-Type": "application/json",
1554
- "Accept": "application/json",
1555
- "X-Storefront-Key": config.storefrontApiKey,
1556
- "X-Business-ID": config.businessId
1557
- };
1558
- if (accessToken) {
1559
- headers["Authorization"] = `Bearer ${accessToken}`;
1560
- }
1561
- return new GraphQLClient3(`${config.backendUrls.store}/graphql`, { headers });
1562
- }
1563
1137
  async function getWishlist(config, accessToken, params) {
1564
- const client = createGraphQLClient3(config, accessToken);
1565
- const variables = {
1138
+ const client = createGraphQLClient(config, accessToken);
1139
+ const data = await client.request(GetStorefrontWishlistDocument, {
1566
1140
  page: params?.page || 1,
1567
1141
  pageSize: params?.pageSize || 20
1142
+ });
1143
+ return {
1144
+ ...data.storefront_wishlist,
1145
+ items: data.storefront_wishlist.items.map((item) => ({
1146
+ ...item,
1147
+ created_at: item.created_at,
1148
+ notes: item.notes ?? void 0,
1149
+ product_name: item.product_name ?? void 0,
1150
+ product_slug: item.product_slug ?? void 0,
1151
+ product_price: item.product_price ?? void 0,
1152
+ currency: item.currency ?? void 0,
1153
+ product_image: item.product_image ?? void 0
1154
+ }))
1568
1155
  };
1569
- const data = await client.request(GET_STOREFRONT_WISHLIST, variables);
1570
- return data.storefront_wishlist;
1571
1156
  }
1572
1157
  async function checkInWishlist(config, accessToken, productId) {
1573
- const client = createGraphQLClient3(config, accessToken);
1574
- const data = await client.request(CHECK_IN_STOREFRONT_WISHLIST, { productId });
1158
+ const client = createGraphQLClient(config, accessToken);
1159
+ const data = await client.request(CheckInStorefrontWishlistDocument, { productId });
1575
1160
  return data.check_in_storefront_wishlist;
1576
1161
  }
1577
1162
  async function addToWishlist(config, accessToken, productId, notes) {
1578
- const response = await fetch(`${config.backendUrls.store}/wishlist/`, {
1163
+ return apiRequest(`${config.backendUrls.store}/wishlist/`, {
1579
1164
  method: "POST",
1580
- headers: {
1581
- "Content-Type": "application/json",
1582
- "Authorization": `Bearer ${accessToken}`,
1583
- "X-Storefront-Key": config.storefrontApiKey,
1584
- "X-Business-ID": config.businessId
1585
- },
1165
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1586
1166
  body: JSON.stringify({
1587
1167
  product_id: productId,
1588
1168
  notes: notes || void 0
1589
- })
1169
+ }),
1170
+ fallbackErrorMessage: "Failed to add to wishlist"
1590
1171
  });
1591
- if (!response.ok) throw new Error("Failed to add to wishlist");
1592
- return response.json();
1593
1172
  }
1594
1173
  async function removeFromWishlist(config, accessToken, itemId) {
1595
- const response = await fetch(`${config.backendUrls.store}/wishlist/${itemId}`, {
1174
+ await apiRequest(`${config.backendUrls.store}/wishlist/${itemId}`, {
1596
1175
  method: "DELETE",
1597
- headers: {
1598
- "Authorization": `Bearer ${accessToken}`,
1599
- "X-Storefront-Key": config.storefrontApiKey,
1600
- "X-Business-ID": config.businessId
1601
- }
1176
+ headers: authHeaders(config, accessToken),
1177
+ fallbackErrorMessage: "Failed to remove from wishlist"
1602
1178
  });
1603
- if (!response.ok) throw new Error("Failed to remove from wishlist");
1604
1179
  }
1605
1180
  async function removeProductFromWishlist(config, accessToken, productId) {
1606
- const response = await fetch(`${config.backendUrls.store}/wishlist/product/${productId}`, {
1181
+ await apiRequest(`${config.backendUrls.store}/wishlist/product/${productId}`, {
1607
1182
  method: "DELETE",
1608
- headers: {
1609
- "Authorization": `Bearer ${accessToken}`,
1610
- "X-Storefront-Key": config.storefrontApiKey,
1611
- "X-Business-ID": config.businessId
1612
- }
1183
+ headers: authHeaders(config, accessToken),
1184
+ fallbackErrorMessage: "Failed to remove product from wishlist"
1613
1185
  });
1614
- if (!response.ok) throw new Error("Failed to remove product from wishlist");
1615
1186
  }
1616
1187
 
1617
1188
  // src/services/payment.service.ts
1189
+ function isValidUUID(str) {
1190
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(str);
1191
+ }
1192
+ async function getAvailablePaymentMethods(config, currency, countryCode) {
1193
+ const params = {};
1194
+ if (currency) params.currency = currency;
1195
+ if (countryCode) params.country_code = countryCode;
1196
+ const query = new URLSearchParams(params).toString();
1197
+ return apiRequest(`${config.backendUrls.payment}/payments/storefront/methods${query ? `?${query}` : ""}`, {
1198
+ headers: storefrontHeaders(config),
1199
+ fallbackErrorMessage: "Failed to fetch available payment methods"
1200
+ });
1201
+ }
1618
1202
  async function createDedicatedAccount(config, accessToken, request) {
1619
- const isValidUUID = (str) => {
1620
- const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1621
- return uuidRegex.test(str);
1622
- };
1623
- const response = await fetch(`${config.backendUrls.payment}/payments/dedicated-account`, {
1203
+ const origin = request.storefront_origin || (typeof window !== "undefined" ? window.location.origin : "");
1204
+ const headers = { ...JSON_HEADERS, ...storefrontHeaders(config) };
1205
+ if (accessToken) {
1206
+ headers["Authorization"] = `Bearer ${accessToken}`;
1207
+ }
1208
+ if (origin) {
1209
+ headers["Origin"] = origin;
1210
+ }
1211
+ return apiRequest(`${config.backendUrls.payment}/payments/storefront/dedicated-account`, {
1624
1212
  method: "POST",
1625
- headers: {
1626
- "Content-Type": "application/json",
1627
- "Authorization": `Bearer ${accessToken}`,
1628
- "X-Storefront-Key": config.storefrontApiKey,
1629
- "X-Business-ID": config.businessId
1630
- },
1213
+ headers,
1631
1214
  body: JSON.stringify({
1632
1215
  order_id: request.order_id && isValidUUID(request.order_id) ? request.order_id : void 0,
1633
1216
  amount: request.amount,
1634
1217
  customer_email: request.customer_email,
1635
1218
  customer_name: request.customer_name,
1636
1219
  currency: request.currency,
1220
+ gateway: request.gateway,
1637
1221
  idempotency_key: request.idempotency_key
1638
- })
1222
+ }),
1223
+ fallbackErrorMessage: "Failed to create dedicated account"
1639
1224
  });
1640
- if (!response.ok) throw new Error("Failed to create dedicated account");
1641
- const data = await response.json();
1642
- return data;
1643
1225
  }
1644
1226
  async function initiatePayment(config, accessToken, request) {
1645
- const isValidUUID = (str) => {
1646
- const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1647
- return uuidRegex.test(str);
1648
- };
1649
1227
  const validOrderId = request.order_id && isValidUUID(request.order_id) ? request.order_id : void 0;
1650
- const response = await fetch(`${config.backendUrls.payment}/payments/storefront/initiate`, {
1651
- method: "POST",
1652
- headers: {
1653
- "Content-Type": "application/json",
1654
- "Authorization": `Bearer ${accessToken}`,
1655
- "X-Storefront-Key": config.storefrontApiKey,
1656
- "X-Business-ID": config.businessId
1657
- },
1658
- body: JSON.stringify({
1659
- amount: request.amount,
1660
- currency: request.currency,
1661
- order_id: validOrderId,
1662
- quote_id: request.quote_id,
1663
- metadata_json: {
1664
- customer_email: request.customer_email,
1665
- customer_name: request.customer_name ?? "",
1228
+ const origin = request.storefront_origin || (typeof window !== "undefined" ? window.location.origin : "");
1229
+ const headers = { ...JSON_HEADERS, ...storefrontHeaders(config) };
1230
+ if (accessToken) {
1231
+ headers["Authorization"] = `Bearer ${accessToken}`;
1232
+ }
1233
+ if (origin) {
1234
+ headers["Origin"] = origin;
1235
+ }
1236
+ const data = await apiRequest(
1237
+ `${config.backendUrls.payment}/payments/storefront/initiate`,
1238
+ {
1239
+ method: "POST",
1240
+ headers,
1241
+ body: JSON.stringify({
1242
+ amount: request.amount,
1243
+ currency: request.currency,
1244
+ // Same real backend default as getAvailablePaymentMethods above
1245
+ // (huma's own `default:"NG"` on this field) — omit rather than
1246
+ // hardcode a client-side copy of it.
1247
+ country_code: request.country_code,
1248
+ gateway: request.gateway,
1666
1249
  order_id: validOrderId,
1667
- ...request.metadata ?? {}
1668
- },
1669
- idempotency_key: request.idempotency_key
1670
- })
1671
- });
1672
- if (!response.ok) throw new Error("Failed to initiate payment");
1673
- const data = await response.json();
1250
+ quote_id: request.quote_id,
1251
+ metadata_json: {
1252
+ customer_email: request.customer_email,
1253
+ customer_name: request.customer_name ?? "",
1254
+ order_id: validOrderId,
1255
+ ...request.metadata ?? {}
1256
+ },
1257
+ idempotency_key: request.idempotency_key
1258
+ }),
1259
+ fallbackErrorMessage: "Failed to initiate payment"
1260
+ }
1261
+ );
1674
1262
  return {
1675
1263
  id: data.id,
1676
1264
  gateway_ref: data.gateway_ref,
@@ -1679,311 +1267,241 @@ async function initiatePayment(config, accessToken, request) {
1679
1267
  checkout_url: data.metadata_json?.checkout_url ?? ""
1680
1268
  };
1681
1269
  }
1682
- async function requestQuote(config, amount) {
1683
- const response = await fetch(`${config.backendUrls.payment}/payments/quote`, {
1684
- method: "POST",
1685
- headers: {
1686
- "Content-Type": "application/json",
1687
- "X-Storefront-Key": config.storefrontApiKey,
1688
- "X-Business-ID": config.businessId
1689
- },
1690
- body: JSON.stringify({ amount })
1691
- });
1692
- if (!response.ok) throw new Error("Failed to request quote");
1693
- const data = await response.json();
1694
- return data;
1695
- }
1696
- async function verifyTransaction(configOrReference, reference) {
1697
- if (typeof configOrReference === "string") {
1698
- const ref2 = configOrReference;
1699
- const response2 = await fetch(`/api/payments/verify/${ref2}`, {
1700
- method: "POST",
1701
- headers: { "Content-Type": "application/json" }
1702
- });
1703
- if (!response2.ok) throw new Error("Failed to verify transaction");
1704
- return response2.json();
1705
- }
1706
- const config = configOrReference;
1707
- const ref = reference || "";
1708
- const response = await fetch(`${config.backendUrls.payment}/payments/verify/${ref}`, {
1270
+ async function verifyTransaction(config, reference) {
1271
+ return apiRequest(`${config.backendUrls.payment}/payments/verify/${reference}`, {
1709
1272
  method: "POST",
1710
- headers: {
1711
- "Content-Type": "application/json",
1712
- "X-Storefront-Key": config.storefrontApiKey,
1713
- "X-Business-ID": config.businessId
1714
- }
1273
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
1274
+ fallbackErrorMessage: "Failed to verify transaction"
1715
1275
  });
1716
- if (!response.ok) throw new Error("Failed to verify transaction");
1717
- const data = await response.json();
1718
- return data;
1719
1276
  }
1720
1277
  async function getTransactionStatus(config, transactionId) {
1721
- const response = await fetch(`${config.backendUrls.payment}/payments/transactions/${transactionId}`, {
1722
- headers: {
1723
- "X-Storefront-Key": config.storefrontApiKey,
1724
- "X-Business-ID": config.businessId
1278
+ return apiRequest(`${config.backendUrls.payment}/payments/transactions/${transactionId}`, {
1279
+ headers: storefrontHeaders(config),
1280
+ fallbackErrorMessage: "Failed to get transaction status"
1281
+ });
1282
+ }
1283
+ async function getWalletBalance(config, accessToken) {
1284
+ return apiRequest(`${config.backendUrls.payment}/wallet/balance`, {
1285
+ headers: authHeaders(config, accessToken),
1286
+ fallbackErrorMessage: "Failed to fetch wallet balance"
1287
+ });
1288
+ }
1289
+ async function getWalletLedger(config, accessToken, params) {
1290
+ const page = params?.page ?? 1;
1291
+ const pageSize = params?.page_size ?? 20;
1292
+ return apiRequest(
1293
+ `${config.backendUrls.payment}/wallet/ledger?page=${page}&page_size=${pageSize}`,
1294
+ {
1295
+ headers: authHeaders(config, accessToken),
1296
+ fallbackErrorMessage: "Failed to fetch wallet ledger"
1725
1297
  }
1298
+ );
1299
+ }
1300
+ async function claimWalletCredit(config, accessToken, claimToken) {
1301
+ return apiRequest(`${config.backendUrls.payment}/wallet/claim`, {
1302
+ method: "POST",
1303
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1304
+ body: JSON.stringify({ claim_token: claimToken }),
1305
+ fallbackErrorMessage: "Failed to claim wallet credit"
1726
1306
  });
1727
- if (!response.ok) throw new Error("Failed to get transaction status");
1728
- const data = await response.json();
1729
- return data;
1730
1307
  }
1731
1308
  var paymentService = {
1309
+ getAvailablePaymentMethods,
1732
1310
  createDedicatedAccount,
1733
1311
  initiatePayment,
1734
- requestQuote,
1735
1312
  verifyTransaction,
1736
- getTransactionStatus
1313
+ getTransactionStatus,
1314
+ getWalletBalance,
1315
+ getWalletLedger,
1316
+ claimWalletCredit
1737
1317
  };
1738
1318
 
1739
- // src/services/consultation.service.ts
1740
- async function createConsultation(config, accessToken, payload) {
1741
- const response = await fetch(`${config.backendUrls.store}/social/consultations/`, {
1319
+ // src/services/logistics.service.ts
1320
+ async function getShippingRates(config, payload) {
1321
+ return apiRequest(`${config.backendUrls.store}/logistics/shipping/rates`, {
1742
1322
  method: "POST",
1743
- headers: {
1744
- "Content-Type": "application/json",
1745
- "Authorization": `Bearer ${accessToken}`,
1746
- "X-Storefront-Key": config.storefrontApiKey,
1747
- "X-Business-ID": config.businessId
1748
- },
1749
- body: JSON.stringify(payload)
1750
- });
1751
- if (!response.ok) throw new Error("Failed to create consultation");
1752
- return response.json();
1753
- }
1754
- async function getConsultations(config, accessToken) {
1755
- const response = await fetch(`${config.backendUrls.store}/social/consultations/`, {
1756
- headers: {
1757
- "Authorization": `Bearer ${accessToken}`,
1758
- "X-Storefront-Key": config.storefrontApiKey,
1759
- "X-Business-ID": config.businessId
1760
- }
1323
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
1324
+ body: JSON.stringify(payload),
1325
+ fallbackErrorMessage: "Failed to get shipping rates"
1761
1326
  });
1762
- if (!response.ok) throw new Error("Failed to get consultations");
1763
- return response.json();
1764
1327
  }
1765
- async function getConsultation(config, accessToken, id) {
1766
- const response = await fetch(`${config.backendUrls.store}/social/consultations/${id}`, {
1767
- headers: {
1768
- "Authorization": `Bearer ${accessToken}`,
1769
- "X-Storefront-Key": config.storefrontApiKey,
1770
- "X-Business-ID": config.businessId
1771
- }
1328
+ async function validateShippingAddress(config, payload) {
1329
+ return apiRequest(`${config.backendUrls.store}/logistics/shipping-address/validate`, {
1330
+ method: "POST",
1331
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
1332
+ body: JSON.stringify(payload),
1333
+ fallbackErrorMessage: "Failed to validate shipping address"
1772
1334
  });
1773
- if (!response.ok) throw new Error("Failed to get consultation");
1774
- return response.json();
1775
1335
  }
1776
- async function sendMessage(config, accessToken, id, payload) {
1777
- await fetch(`${config.backendUrls.store}/social/consultations/${id}/messages`, {
1778
- method: "POST",
1779
- headers: {
1780
- "Content-Type": "application/json",
1781
- "Authorization": `Bearer ${accessToken}`,
1782
- "X-Storefront-Key": config.storefrontApiKey,
1783
- "X-Business-ID": config.businessId
1784
- },
1785
- body: JSON.stringify(payload)
1336
+ async function fetchLocationList(config, path, coords, fallbackErrorMessage) {
1337
+ const params = new URLSearchParams();
1338
+ if (coords) {
1339
+ params.set("latitude", String(coords.latitude));
1340
+ params.set("longitude", String(coords.longitude));
1341
+ }
1342
+ const qs = params.toString();
1343
+ return apiRequest(`${config.backendUrls.store}/logistics/${path}${qs ? `?${qs}` : ""}`, {
1344
+ headers: storefrontHeaders(config),
1345
+ fallbackErrorMessage
1786
1346
  });
1787
1347
  }
1788
- async function acceptQuote(config, accessToken, id, payload) {
1789
- const response = await fetch(`${config.backendUrls.store}/social/consultations/${id}/accept-quote`, {
1790
- method: "POST",
1791
- headers: {
1792
- "Content-Type": "application/json",
1793
- "Authorization": `Bearer ${accessToken}`,
1794
- "X-Storefront-Key": config.storefrontApiKey,
1795
- "X-Business-ID": config.businessId
1796
- },
1797
- body: JSON.stringify(payload)
1798
- });
1799
- if (!response.ok) throw new Error("Failed to accept quote");
1800
- return response.json();
1801
- }
1802
- async function rejectQuote(config, accessToken, id, reason) {
1803
- const response = await fetch(`${config.backendUrls.store}/social/consultations/${id}/reject-quote`, {
1804
- method: "POST",
1805
- headers: {
1806
- "Content-Type": "application/json",
1807
- "Authorization": `Bearer ${accessToken}`,
1808
- "X-Storefront-Key": config.storefrontApiKey,
1809
- "X-Business-ID": config.businessId
1810
- },
1811
- body: JSON.stringify({ reason })
1812
- });
1813
- if (!response.ok) throw new Error("Failed to reject quote");
1814
- return response.json();
1815
- }
1816
- var consultationService = {
1817
- createConsultation,
1818
- getConsultations,
1819
- getConsultation,
1820
- sendMessage,
1821
- acceptQuote,
1822
- rejectQuote
1823
- };
1824
-
1825
- // src/services/logistics.service.ts
1826
- async function getShippingRates(config, payload) {
1827
- const response = await fetch(`${config.backendUrls.store}/logistics/shipping/rates`, {
1348
+ async function listPickupLocations(config, coords) {
1349
+ return fetchLocationList(config, "pickup-locations", coords, "Failed to list pickup locations");
1350
+ }
1351
+ async function listBusinessLocations(config, coords) {
1352
+ return fetchLocationList(config, "locations", coords, "Failed to list business locations");
1353
+ }
1354
+ async function reverseGeocode(config, payload) {
1355
+ return apiRequest(`${config.backendUrls.store}/logistics/reverse-geocode`, {
1828
1356
  method: "POST",
1829
- headers: {
1830
- "Content-Type": "application/json",
1831
- "X-Storefront-Key": config.storefrontApiKey,
1832
- "X-Business-ID": config.businessId
1833
- },
1834
- body: JSON.stringify(payload)
1835
- });
1836
- if (!response.ok) throw new Error("Failed to get shipping rates");
1837
- return response.json();
1838
- }
1839
- async function getSellerShippingAddress(config) {
1840
- const response = await fetch(`${config.backendUrls.store}/logistics/shipping-address`, {
1841
- headers: {
1842
- "X-Storefront-Key": config.storefrontApiKey,
1843
- "X-Business-ID": config.businessId
1844
- }
1357
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
1358
+ body: JSON.stringify(payload),
1359
+ fallbackErrorMessage: "Failed to reverse geocode coordinates"
1845
1360
  });
1846
- if (!response.ok) throw new Error("Failed to get seller shipping address");
1847
- return response.json();
1848
1361
  }
1849
- async function validateShippingAddress(config, payload) {
1850
- const response = await fetch(`${config.backendUrls.store}/logistics/shipping-address/validate`, {
1362
+ async function decodePlusCode(config, payload) {
1363
+ return apiRequest(`${config.backendUrls.store}/logistics/plus-code/decode`, {
1851
1364
  method: "POST",
1852
- headers: {
1853
- "Content-Type": "application/json",
1854
- "X-Storefront-Key": config.storefrontApiKey,
1855
- "X-Business-ID": config.businessId
1856
- },
1857
- body: JSON.stringify(payload)
1365
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
1366
+ body: JSON.stringify(payload),
1367
+ fallbackErrorMessage: "Failed to decode Plus Code"
1368
+ });
1369
+ }
1370
+ async function autocompleteAddress(config, q, country) {
1371
+ const params = new URLSearchParams();
1372
+ params.set("q", q);
1373
+ if (country) params.set("country", country);
1374
+ return apiRequest(`${config.backendUrls.store}/logistics/address/autocomplete?${params.toString()}`, {
1375
+ headers: storefrontHeaders(config),
1376
+ fallbackErrorMessage: "Failed to autocomplete address"
1858
1377
  });
1859
- if (!response.ok) throw new Error("Failed to validate shipping address");
1860
- return response.json();
1861
1378
  }
1862
1379
  var logisticsService = {
1863
1380
  getShippingRates,
1864
- getSellerShippingAddress,
1865
- validateShippingAddress
1381
+ validateShippingAddress,
1382
+ listPickupLocations,
1383
+ listBusinessLocations,
1384
+ reverseGeocode,
1385
+ decodePlusCode,
1386
+ autocompleteAddress
1866
1387
  };
1867
1388
 
1868
1389
  // src/services/bespoke.service.ts
1869
1390
  async function checkAvailability(config, product_id) {
1870
- const response = await fetch(`${config.backendUrls.store}/bespoke/availability/${product_id}`, {
1871
- headers: {
1872
- "X-Storefront-Key": config.storefrontApiKey,
1873
- "X-Business-ID": config.businessId
1874
- }
1391
+ return apiRequest(`${config.backendUrls.store}/bespoke/capacity/availability/${product_id}`, {
1392
+ headers: storefrontHeaders(config),
1393
+ fallbackErrorMessage: "Failed to check bespoke availability"
1394
+ });
1395
+ }
1396
+ async function approveBespokeDeliverable(config, accessToken, orderId) {
1397
+ return apiRequest(`${config.backendUrls.store}/orders/${orderId}/bespoke/approve`, {
1398
+ method: "POST",
1399
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1400
+ body: JSON.stringify({}),
1401
+ fallbackErrorMessage: "Failed to approve deliverable"
1402
+ });
1403
+ }
1404
+ async function requestBespokeRevision(config, accessToken, orderId, notes) {
1405
+ return apiRequest(`${config.backendUrls.store}/orders/${orderId}/bespoke/request-revision`, {
1406
+ method: "POST",
1407
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1408
+ body: JSON.stringify({ notes }),
1409
+ fallbackErrorMessage: "Failed to request revision"
1875
1410
  });
1876
- if (!response.ok) throw new Error("Failed to check bespoke availability");
1877
- return response.json();
1878
1411
  }
1879
1412
  var bespokeService = {
1880
- checkAvailability
1413
+ checkAvailability,
1414
+ approveBespokeDeliverable,
1415
+ requestBespokeRevision
1881
1416
  };
1882
1417
 
1883
1418
  // src/services/service-booking.service.ts
1884
- async function getAvailableSlots(config, product_id, date_from, date_to) {
1885
- const response = await fetch(
1886
- `${config.backendUrls.store}/service/available-slots/${product_id}?date_from=${date_from}&date_to=${date_to}`,
1419
+ async function getAvailableSlots(config, productId, date, params) {
1420
+ const query = new URLSearchParams({ date });
1421
+ if (params?.staffId) query.set("staff_id", params.staffId);
1422
+ if (params?.locationId) query.set("location_id", params.locationId);
1423
+ if (params?.customerLatitude !== void 0) query.set("customer_latitude", String(params.customerLatitude));
1424
+ if (params?.customerLongitude !== void 0) query.set("customer_longitude", String(params.customerLongitude));
1425
+ return apiRequest(
1426
+ `${config.backendUrls.store}/catalog/products/${productId}/available-slots?${query.toString()}`,
1887
1427
  {
1888
- headers: {
1889
- "X-Storefront-Key": config.storefrontApiKey,
1890
- "X-Business-ID": config.businessId
1891
- }
1428
+ headers: storefrontHeaders(config),
1429
+ fallbackErrorMessage: "Failed to get available slots"
1892
1430
  }
1893
1431
  );
1894
- if (!response.ok) throw new Error("Failed to get available slots");
1895
- return response.json();
1896
1432
  }
1897
1433
  async function bookAppointment(config, accessToken, payload) {
1898
- const response = await fetch(`${config.backendUrls.store}/service/appointments/`, {
1434
+ return apiRequest(`${config.backendUrls.store}/service/bookings/schedule`, {
1899
1435
  method: "POST",
1900
- headers: {
1901
- "Content-Type": "application/json",
1902
- "Authorization": `Bearer ${accessToken}`,
1903
- "X-Storefront-Key": config.storefrontApiKey,
1904
- "X-Business-ID": config.businessId
1905
- },
1906
- body: JSON.stringify(payload)
1907
- });
1908
- if (!response.ok) throw new Error("Failed to book appointment");
1909
- return response.json();
1910
- }
1911
- async function getAppointments(config, accessToken) {
1912
- const response = await fetch(`${config.backendUrls.store}/service/bookings/my`, {
1913
- headers: {
1914
- "Authorization": `Bearer ${accessToken}`,
1915
- "X-Storefront-Key": config.storefrontApiKey,
1916
- "X-Business-ID": config.businessId
1917
- }
1436
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1437
+ body: JSON.stringify(payload),
1438
+ fallbackErrorMessage: "Failed to book appointment"
1439
+ });
1440
+ }
1441
+ async function getAppointments(config, accessToken, params) {
1442
+ const query = new URLSearchParams();
1443
+ if (params?.status) query.set("status", params.status);
1444
+ if (params?.page) query.set("page", String(params.page));
1445
+ if (params?.page_size) query.set("page_size", String(params.page_size));
1446
+ return apiRequest(`${config.backendUrls.store}/service/bookings/my?${query.toString()}`, {
1447
+ headers: authHeaders(config, accessToken),
1448
+ fallbackErrorMessage: "Failed to get appointments"
1918
1449
  });
1919
- if (!response.ok) throw new Error("Failed to get appointments");
1920
- return response.json();
1921
1450
  }
1922
1451
  async function getAppointment(config, accessToken, id) {
1923
- const response = await fetch(`${config.backendUrls.store}/service/bookings/${id}`, {
1924
- headers: {
1925
- "Authorization": `Bearer ${accessToken}`,
1926
- "X-Storefront-Key": config.storefrontApiKey,
1927
- "X-Business-ID": config.businessId
1928
- }
1452
+ return apiRequest(`${config.backendUrls.store}/service/bookings/${id}`, {
1453
+ headers: authHeaders(config, accessToken),
1454
+ fallbackErrorMessage: "Failed to get appointment"
1929
1455
  });
1930
- if (!response.ok) throw new Error("Failed to get appointment");
1931
- return response.json();
1932
1456
  }
1933
- async function cancelAppointment(config, accessToken, id) {
1934
- await fetch(`${config.backendUrls.store}/service/bookings/${id}`, {
1935
- method: "DELETE",
1936
- headers: {
1937
- "Authorization": `Bearer ${accessToken}`,
1938
- "X-Storefront-Key": config.storefrontApiKey,
1939
- "X-Business-ID": config.businessId
1940
- }
1457
+ async function cancelAppointment(config, accessToken, id, payload) {
1458
+ return apiRequest(`${config.backendUrls.store}/service/bookings/${id}/cancel`, {
1459
+ method: "POST",
1460
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1461
+ body: JSON.stringify(payload ?? {}),
1462
+ fallbackErrorMessage: "Failed to cancel appointment"
1941
1463
  });
1942
1464
  }
1943
- async function getDeliverables(config, accessToken, order_id) {
1944
- const response = await fetch(`${config.backendUrls.store}/service/projects/${order_id}/deliverables/`, {
1945
- headers: {
1946
- "Authorization": `Bearer ${accessToken}`,
1947
- "X-Storefront-Key": config.storefrontApiKey,
1948
- "X-Business-ID": config.businessId
1949
- }
1465
+ async function rescheduleAppointment(config, accessToken, id, payload) {
1466
+ return apiRequest(`${config.backendUrls.store}/service/bookings/${id}/reschedule`, {
1467
+ method: "POST",
1468
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1469
+ body: JSON.stringify(payload),
1470
+ fallbackErrorMessage: "Failed to reschedule appointment"
1950
1471
  });
1951
- if (!response.ok) throw new Error("Failed to get deliverables");
1952
- return response.json();
1953
1472
  }
1954
- async function approveDeliverable(config, accessToken, order_id, deliverable_id) {
1955
- const response = await fetch(
1956
- `${config.backendUrls.store}/service/projects/${order_id}/deliverables/${deliverable_id}/approve`,
1957
- {
1958
- method: "POST",
1959
- headers: {
1960
- "Content-Type": "application/json",
1961
- "Authorization": `Bearer ${accessToken}`,
1962
- "X-Storefront-Key": config.storefrontApiKey,
1963
- "X-Business-ID": config.businessId
1964
- },
1965
- body: JSON.stringify({ approved: true })
1966
- }
1967
- );
1968
- if (!response.ok) throw new Error("Failed to approve deliverable");
1969
- return response.json();
1473
+ async function disputeAppointment(config, accessToken, id, payload) {
1474
+ return apiRequest(`${config.backendUrls.store}/service/bookings/${id}/dispute`, {
1475
+ method: "POST",
1476
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1477
+ body: JSON.stringify(payload),
1478
+ fallbackErrorMessage: "Failed to open dispute"
1479
+ });
1480
+ }
1481
+ async function confirmAppointmentCompletion(config, accessToken, id, payload) {
1482
+ return apiRequest(`${config.backendUrls.store}/service/bookings/${id}/confirm`, {
1483
+ method: "POST",
1484
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1485
+ body: JSON.stringify(payload ?? {}),
1486
+ fallbackErrorMessage: "Failed to confirm completion"
1487
+ });
1488
+ }
1489
+ async function getMilestones(config, accessToken, bookingId) {
1490
+ return apiRequest(`${config.backendUrls.store}/service/bookings/${bookingId}/milestones`, {
1491
+ headers: authHeaders(config, accessToken),
1492
+ fallbackErrorMessage: "Failed to get milestones"
1493
+ });
1970
1494
  }
1971
- async function requestRevision(config, accessToken, order_id, deliverable_id, feedback) {
1972
- const response = await fetch(
1973
- `${config.backendUrls.store}/service/projects/${order_id}/deliverables/${deliverable_id}/revision`,
1495
+ async function approveMilestone(config, accessToken, bookingId, milestoneId, payload) {
1496
+ return apiRequest(
1497
+ `${config.backendUrls.store}/service/bookings/${bookingId}/milestones/${milestoneId}/approve`,
1974
1498
  {
1975
1499
  method: "POST",
1976
- headers: {
1977
- "Content-Type": "application/json",
1978
- "Authorization": `Bearer ${accessToken}`,
1979
- "X-Storefront-Key": config.storefrontApiKey,
1980
- "X-Business-ID": config.businessId
1981
- },
1982
- body: JSON.stringify({ approved: false, feedback })
1500
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1501
+ body: JSON.stringify(payload ?? {}),
1502
+ fallbackErrorMessage: "Failed to approve milestone"
1983
1503
  }
1984
1504
  );
1985
- if (!response.ok) throw new Error("Failed to request revision");
1986
- return response.json();
1987
1505
  }
1988
1506
  var serviceBookingService = {
1989
1507
  getAvailableSlots,
@@ -1991,41 +1509,111 @@ var serviceBookingService = {
1991
1509
  getAppointments,
1992
1510
  getAppointment,
1993
1511
  cancelAppointment,
1994
- getDeliverables,
1995
- approveDeliverable,
1996
- requestRevision
1512
+ rescheduleAppointment,
1513
+ disputeAppointment,
1514
+ confirmAppointmentCompletion,
1515
+ getMilestones,
1516
+ approveMilestone
1997
1517
  };
1998
1518
 
1999
1519
  // src/services/notifications.service.ts
1520
+ function bearerHeaders(accessToken) {
1521
+ return { Authorization: `Bearer ${accessToken}` };
1522
+ }
2000
1523
  async function sendContactEmail(config, payload) {
2001
- const response = await fetch(`${config.backendUrls.notifications}/contact/send-contact-email`, {
1524
+ return apiRequest(`${config.backendUrls.notifications}/contact/send-contact-email`, {
2002
1525
  method: "POST",
2003
- headers: {
2004
- "Content-Type": "application/json",
2005
- "X-Storefront-Key": config.storefrontApiKey,
2006
- "X-Business-ID": config.businessId
2007
- },
2008
- body: JSON.stringify(payload)
1526
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
1527
+ body: JSON.stringify(payload),
1528
+ fallbackErrorMessage: "Failed to send contact email"
2009
1529
  });
2010
- if (!response.ok) throw new Error("Failed to send contact email");
2011
- return response.json();
2012
1530
  }
2013
1531
  async function sendContactConfirmation(config, payload) {
2014
- const response = await fetch(`${config.backendUrls.notifications}/contact/send-contact-confirmation`, {
1532
+ return apiRequest(`${config.backendUrls.notifications}/contact/send-contact-confirmation`, {
1533
+ method: "POST",
1534
+ headers: { ...JSON_HEADERS, ...storefrontHeaders(config) },
1535
+ body: JSON.stringify(payload),
1536
+ fallbackErrorMessage: "Failed to send contact confirmation"
1537
+ });
1538
+ }
1539
+ async function getNotificationPreferences(config, accessToken) {
1540
+ return apiRequest(`${config.backendUrls.notifications}/notifications/preferences/`, {
1541
+ headers: bearerHeaders(accessToken),
1542
+ fallbackErrorMessage: "Failed to fetch notification preferences"
1543
+ });
1544
+ }
1545
+ async function updateNotificationPreferences(config, accessToken, payload) {
1546
+ return apiRequest(`${config.backendUrls.notifications}/notifications/preferences/`, {
1547
+ method: "PATCH",
1548
+ headers: { ...JSON_HEADERS, ...bearerHeaders(accessToken) },
1549
+ body: JSON.stringify(payload),
1550
+ fallbackErrorMessage: "Failed to update notification preferences"
1551
+ });
1552
+ }
1553
+ async function getVapidPublicKey(config) {
1554
+ return apiRequest(`${config.backendUrls.notifications}/push/vapid-public-key`, {
1555
+ fallbackErrorMessage: "Failed to fetch VAPID public key"
1556
+ });
1557
+ }
1558
+ async function subscribeToPush(config, accessToken, params) {
1559
+ return apiRequest(`${config.backendUrls.notifications}/push/subscribe`, {
1560
+ method: "POST",
1561
+ headers: { ...JSON_HEADERS, ...bearerHeaders(accessToken) },
1562
+ body: JSON.stringify(params),
1563
+ fallbackErrorMessage: "Failed to subscribe to push notifications"
1564
+ });
1565
+ }
1566
+ async function unsubscribeFromPush(config, accessToken, endpoint) {
1567
+ return apiRequest(`${config.backendUrls.notifications}/push/subscribe`, {
1568
+ method: "DELETE",
1569
+ headers: { ...JSON_HEADERS, ...bearerHeaders(accessToken) },
1570
+ body: JSON.stringify({ endpoint }),
1571
+ fallbackErrorMessage: "Failed to unsubscribe from push notifications"
1572
+ });
1573
+ }
1574
+ async function getNotificationInbox(config, accessToken, params) {
1575
+ const query = new URLSearchParams();
1576
+ if (params?.page) query.set("page", String(params.page));
1577
+ if (params?.limit) query.set("limit", String(params.limit));
1578
+ if (params?.unread_only) query.set("unread_only", "true");
1579
+ return apiRequest(`${config.backendUrls.notifications}/notifications?${query.toString()}`, {
1580
+ headers: bearerHeaders(accessToken),
1581
+ fallbackErrorMessage: "Failed to fetch notifications"
1582
+ });
1583
+ }
1584
+ async function markNotificationRead(config, accessToken, notificationId) {
1585
+ return apiRequest(`${config.backendUrls.notifications}/notifications/${notificationId}/read`, {
1586
+ method: "POST",
1587
+ headers: bearerHeaders(accessToken),
1588
+ fallbackErrorMessage: "Failed to mark notification as read"
1589
+ });
1590
+ }
1591
+ async function markAllNotificationsRead(config, accessToken) {
1592
+ return apiRequest(`${config.backendUrls.notifications}/notifications/mark-all-read`, {
1593
+ method: "POST",
1594
+ headers: bearerHeaders(accessToken),
1595
+ fallbackErrorMessage: "Failed to mark all notifications as read"
1596
+ });
1597
+ }
1598
+ async function trackNotificationClick(config, accessToken, notificationId) {
1599
+ return apiRequest(`${config.backendUrls.notifications}/notifications/${notificationId}/click`, {
2015
1600
  method: "POST",
2016
- headers: {
2017
- "Content-Type": "application/json",
2018
- "X-Storefront-Key": config.storefrontApiKey,
2019
- "X-Business-ID": config.businessId
2020
- },
2021
- body: JSON.stringify(payload)
1601
+ headers: bearerHeaders(accessToken),
1602
+ fallbackErrorMessage: "Failed to track notification click"
2022
1603
  });
2023
- if (!response.ok) throw new Error("Failed to send contact confirmation");
2024
- return response.json();
2025
1604
  }
2026
1605
  var notificationsService = {
2027
1606
  sendContactEmail,
2028
- sendContactConfirmation
1607
+ sendContactConfirmation,
1608
+ getNotificationPreferences,
1609
+ updateNotificationPreferences,
1610
+ getVapidPublicKey,
1611
+ subscribeToPush,
1612
+ unsubscribeFromPush,
1613
+ getNotificationInbox,
1614
+ markNotificationRead,
1615
+ markAllNotificationsRead,
1616
+ trackNotificationClick
2029
1617
  };
2030
1618
 
2031
1619
  // src/services/app.service.ts
@@ -2043,34 +1631,22 @@ async function googleTranslate(text, target, source = "en") {
2043
1631
 
2044
1632
  // src/services/config.service.ts
2045
1633
  async function getSupportedLanguages(config) {
2046
- const response = await fetch(`${config.backendUrls.payment}/config/supported-languages`, {
2047
- headers: {
2048
- "X-Storefront-Key": config.storefrontApiKey,
2049
- "X-Business-ID": config.businessId
2050
- }
1634
+ return apiRequest(`${config.backendUrls.payment}/config/supported-languages`, {
1635
+ headers: storefrontHeaders(config),
1636
+ fallbackErrorMessage: "Failed to get supported languages"
2051
1637
  });
2052
- if (!response.ok) throw new Error("Failed to get supported languages");
2053
- return response.json();
2054
1638
  }
2055
1639
  async function getSupportedCurrencies(config) {
2056
- const response = await fetch(`${config.backendUrls.payment}/config/supported-currencies`, {
2057
- headers: {
2058
- "X-Storefront-Key": config.storefrontApiKey,
2059
- "X-Business-ID": config.businessId
2060
- }
1640
+ return apiRequest(`${config.backendUrls.payment}/config/supported-currencies`, {
1641
+ headers: storefrontHeaders(config),
1642
+ fallbackErrorMessage: "Failed to get supported currencies"
2061
1643
  });
2062
- if (!response.ok) throw new Error("Failed to get supported currencies");
2063
- return response.json();
2064
1644
  }
2065
1645
  async function getBusinessConfig(config) {
2066
- const response = await fetch(`${config.backendUrls.auth}/internal/businesses/${config.businessId}/`, {
2067
- headers: {
2068
- "X-Storefront-Key": config.storefrontApiKey,
2069
- "X-Business-ID": config.businessId
2070
- }
1646
+ return apiRequest(`${config.backendUrls.auth}/internal/businesses/${config.businessId}/`, {
1647
+ headers: storefrontHeaders(config),
1648
+ fallbackErrorMessage: "Failed to get business config"
2071
1649
  });
2072
- if (!response.ok) throw new Error("Failed to get business config");
2073
- return response.json();
2074
1650
  }
2075
1651
  var configService = {
2076
1652
  getSupportedLanguages,
@@ -2079,133 +1655,292 @@ var configService = {
2079
1655
  };
2080
1656
 
2081
1657
  // src/services/cms.service.ts
2082
- async function listCmsEntries(config, modelSlug, params) {
1658
+ async function listCmsEntries(config, businessUuid, modelSlug, params) {
2083
1659
  const queryParams = new URLSearchParams();
2084
1660
  queryParams.set("page", (params?.page || 1).toString());
2085
1661
  queryParams.set("page_size", (params?.page_size || 50).toString());
2086
- const response = await fetch(
2087
- `${config.backendUrls.cms}/sdk/${config.businessId}/${modelSlug}/entries?${queryParams.toString()}`,
1662
+ return apiRequest(
1663
+ `${config.backendUrls.cms}/sdk/${businessUuid}/${modelSlug}/entries?${queryParams.toString()}`,
2088
1664
  {
2089
- headers: {
2090
- "X-Storefront-Key": config.storefrontApiKey,
2091
- "X-Business-ID": config.businessId
2092
- }
1665
+ headers: storefrontHeaders(config),
1666
+ fallbackErrorMessage: "Failed to list CMS entries"
2093
1667
  }
2094
1668
  );
2095
- if (!response.ok) throw new Error("Failed to list CMS entries");
2096
- return response.json();
2097
1669
  }
2098
- async function getCmsEntry(config, modelSlug, entryId) {
2099
- const response = await fetch(
2100
- `${config.backendUrls.cms}/sdk/${config.businessId}/${modelSlug}/entries/${entryId}`,
1670
+ async function getCmsEntry(config, businessUuid, modelSlug, entryId) {
1671
+ return apiRequest(
1672
+ `${config.backendUrls.cms}/sdk/${businessUuid}/${modelSlug}/entries/${entryId}`,
2101
1673
  {
2102
- headers: {
2103
- "X-Storefront-Key": config.storefrontApiKey,
2104
- "X-Business-ID": config.businessId
2105
- }
1674
+ headers: storefrontHeaders(config),
1675
+ fallbackErrorMessage: "Failed to get CMS entry"
2106
1676
  }
2107
1677
  );
2108
- if (!response.ok) throw new Error("Failed to get CMS entry");
2109
- return response.json();
2110
1678
  }
2111
- async function createCmsEntry(config, accessToken, modelSlug, params) {
2112
- const response = await fetch(
2113
- `${config.backendUrls.cms}/sdk/${config.businessId}/${modelSlug}/entries`,
1679
+ async function createCmsEntry(config, businessUuid, accessToken, modelSlug, params) {
1680
+ return apiRequest(
1681
+ `${config.backendUrls.cms}/sdk/${businessUuid}/${modelSlug}/entries`,
2114
1682
  {
2115
1683
  method: "POST",
2116
- headers: {
2117
- "Content-Type": "application/json",
2118
- "Authorization": `Bearer ${accessToken}`,
2119
- "X-Storefront-Key": config.storefrontApiKey,
2120
- "X-Business-ID": config.businessId
2121
- },
2122
- body: JSON.stringify(params)
1684
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1685
+ body: JSON.stringify(params),
1686
+ fallbackErrorMessage: "Failed to create CMS entry"
2123
1687
  }
2124
1688
  );
2125
- if (!response.ok) throw new Error("Failed to create CMS entry");
2126
- return response.json();
2127
1689
  }
2128
- async function updateCmsEntry(config, accessToken, modelSlug, entryId, params) {
2129
- const response = await fetch(
2130
- `${config.backendUrls.cms}/sdk/${config.businessId}/${modelSlug}/entries/${entryId}`,
1690
+ async function updateCmsEntry(config, businessUuid, accessToken, modelSlug, entryId, params) {
1691
+ return apiRequest(
1692
+ `${config.backendUrls.cms}/sdk/${businessUuid}/${modelSlug}/entries/${entryId}`,
2131
1693
  {
2132
1694
  method: "PUT",
2133
- headers: {
2134
- "Content-Type": "application/json",
2135
- "Authorization": `Bearer ${accessToken}`,
2136
- "X-Storefront-Key": config.storefrontApiKey,
2137
- "X-Business-ID": config.businessId
2138
- },
2139
- body: JSON.stringify(params)
1695
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
1696
+ body: JSON.stringify(params),
1697
+ fallbackErrorMessage: "Failed to update CMS entry"
2140
1698
  }
2141
1699
  );
2142
- if (!response.ok) throw new Error("Failed to update CMS entry");
2143
- return response.json();
2144
1700
  }
2145
- async function deleteCmsEntry(config, accessToken, modelSlug, entryId) {
2146
- const response = await fetch(
2147
- `${config.backendUrls.cms}/sdk/${config.businessId}/${modelSlug}/entries/${entryId}`,
1701
+ async function deleteCmsEntry(config, businessUuid, accessToken, modelSlug, entryId) {
1702
+ await apiRequest(
1703
+ `${config.backendUrls.cms}/sdk/${businessUuid}/${modelSlug}/entries/${entryId}`,
2148
1704
  {
2149
1705
  method: "DELETE",
2150
- headers: {
2151
- "Authorization": `Bearer ${accessToken}`,
2152
- "X-Storefront-Key": config.storefrontApiKey,
2153
- "X-Business-ID": config.businessId
2154
- }
1706
+ headers: authHeaders(config, accessToken),
1707
+ fallbackErrorMessage: "Failed to delete CMS entry"
2155
1708
  }
2156
1709
  );
2157
- if (!response.ok) throw new Error("Failed to delete CMS entry");
1710
+ }
1711
+
1712
+ // src/services/legal.service.ts
1713
+ async function getMerchantLegalDocument(config, businessUuid, documentType, fetchOptions) {
1714
+ if (!businessUuid || !config?.backendUrls?.cms) return null;
1715
+ try {
1716
+ const res = await fetch(
1717
+ `${config.backendUrls.cms}/cms/legal/storefront/${businessUuid}/${documentType}`,
1718
+ fetchOptions
1719
+ );
1720
+ if (!res.ok) return null;
1721
+ return await res.json();
1722
+ } catch {
1723
+ return null;
1724
+ }
1725
+ }
1726
+
1727
+ // src/services/review.service.ts
1728
+ function fetchWithConfig(url, config, options = {}) {
1729
+ const headers = {
1730
+ ...JSON_HEADERS,
1731
+ Accept: "application/json",
1732
+ ...options.headers
1733
+ };
1734
+ if (config.storefrontApiKey) headers["X-Storefront-Key"] = config.storefrontApiKey;
1735
+ if (config.businessId) headers["X-Business-ID"] = config.businessId;
1736
+ if (config.authToken) headers["Authorization"] = `Bearer ${config.authToken}`;
1737
+ return apiRequest(url, { ...options, headers, fallbackErrorMessage: options.fallbackErrorMessage ?? "Request failed" });
1738
+ }
1739
+ async function createReview(reviewData, config) {
1740
+ const url = `${config.backendUrls.crm}/reviews`;
1741
+ return fetchWithConfig(url, config, {
1742
+ method: "POST",
1743
+ body: JSON.stringify(reviewData),
1744
+ fallbackErrorMessage: "Failed to create review"
1745
+ });
1746
+ }
1747
+ async function updateReview(reviewId, reviewData, config) {
1748
+ const url = `${config.backendUrls.crm}/reviews/${reviewId}`;
1749
+ return fetchWithConfig(url, config, {
1750
+ method: "PUT",
1751
+ body: JSON.stringify(reviewData),
1752
+ fallbackErrorMessage: "Failed to update review"
1753
+ });
1754
+ }
1755
+ async function getProductReviews(productId, params = {}, config) {
1756
+ const queryParams = new URLSearchParams();
1757
+ if (params.page) queryParams.append("page", params.page.toString());
1758
+ if (params.page_size) queryParams.append("page_size", params.page_size.toString());
1759
+ if (params.moderation_status) queryParams.append("moderation_status", params.moderation_status);
1760
+ const url = `${config.backendUrls.crm}/products/${productId}/reviews?${queryParams.toString()}`;
1761
+ return fetchWithConfig(url, config, { fallbackErrorMessage: "Failed to fetch product reviews" });
1762
+ }
1763
+ async function createComment(commentData, config) {
1764
+ const url = `${config.backendUrls.crm}/comments`;
1765
+ return fetchWithConfig(url, config, {
1766
+ method: "POST",
1767
+ body: JSON.stringify(commentData),
1768
+ fallbackErrorMessage: "Failed to create comment"
1769
+ });
1770
+ }
1771
+ async function updateComment(commentId, commentData, config) {
1772
+ const url = `${config.backendUrls.crm}/comments/${commentId}`;
1773
+ return fetchWithConfig(url, config, {
1774
+ method: "PUT",
1775
+ body: JSON.stringify(commentData),
1776
+ fallbackErrorMessage: "Failed to update comment"
1777
+ });
1778
+ }
1779
+ async function getProductComments(productId, params = {}, config) {
1780
+ const queryParams = new URLSearchParams();
1781
+ if (params.page) queryParams.append("page", params.page.toString());
1782
+ if (params.page_size) queryParams.append("page_size", params.page_size.toString());
1783
+ if (params.moderation_status) queryParams.append("moderation_status", params.moderation_status);
1784
+ const url = `${config.backendUrls.crm}/products/${productId}/comments?${queryParams.toString()}`;
1785
+ return fetchWithConfig(url, config, { fallbackErrorMessage: "Failed to fetch product comments" });
1786
+ }
1787
+ async function getPendingReviews(config) {
1788
+ const url = `${config.backendUrls.crm}/admin/reviews/pending`;
1789
+ return fetchWithConfig(url, config, { fallbackErrorMessage: "Failed to fetch pending reviews" });
1790
+ }
1791
+ async function getPendingComments(config) {
1792
+ const url = `${config.backendUrls.crm}/admin/comments/pending`;
1793
+ return fetchWithConfig(url, config, { fallbackErrorMessage: "Failed to fetch pending comments" });
1794
+ }
1795
+ async function moderateReview(reviewId, action, config) {
1796
+ const url = `${config.backendUrls.crm}/admin/reviews/${reviewId}/moderate`;
1797
+ return fetchWithConfig(url, config, {
1798
+ method: "POST",
1799
+ body: JSON.stringify(action),
1800
+ fallbackErrorMessage: "Failed to moderate review"
1801
+ });
1802
+ }
1803
+ async function moderateComment(commentId, action, config) {
1804
+ const url = `${config.backendUrls.crm}/admin/comments/${commentId}/moderate`;
1805
+ return fetchWithConfig(url, config, {
1806
+ method: "POST",
1807
+ body: JSON.stringify(action),
1808
+ fallbackErrorMessage: "Failed to moderate comment"
1809
+ });
1810
+ }
1811
+ async function merchantReplyToReview(reviewId, replyData, config) {
1812
+ const url = `${config.backendUrls.crm}/admin/reviews/${reviewId}/reply`;
1813
+ return fetchWithConfig(url, config, {
1814
+ method: "POST",
1815
+ body: JSON.stringify(replyData),
1816
+ fallbackErrorMessage: "Failed to post merchant reply"
1817
+ });
1818
+ }
1819
+
1820
+ // src/services/media.service.ts
1821
+ async function uploadMediaFile(params) {
1822
+ const presignedUrlEndpoint = params.presignedUrlEndpoint ?? "/api/auth/media/presigned-url";
1823
+ const confirmUploadEndpoint = params.confirmUploadEndpoint ?? "/api/auth/media/confirm-upload";
1824
+ const { presigned_url, storage_path } = await apiRequest(presignedUrlEndpoint, {
1825
+ method: "POST",
1826
+ headers: JSON_HEADERS,
1827
+ credentials: "include",
1828
+ body: JSON.stringify({
1829
+ filename: params.file.name,
1830
+ content_type: params.file.type,
1831
+ context: params.context,
1832
+ entity_id: params.entityId,
1833
+ affiliation: params.affiliation,
1834
+ owner_id: params.ownerId
1835
+ }),
1836
+ fallbackErrorMessage: "Could not start upload"
1837
+ });
1838
+ const putResponse = await fetch(presigned_url, {
1839
+ method: "PUT",
1840
+ headers: { "Content-Type": params.file.type },
1841
+ body: params.file
1842
+ });
1843
+ if (!putResponse.ok) {
1844
+ throw new Error(`Failed to upload ${params.file.name}`);
1845
+ }
1846
+ return apiRequest(confirmUploadEndpoint, {
1847
+ method: "POST",
1848
+ headers: JSON_HEADERS,
1849
+ credentials: "include",
1850
+ body: JSON.stringify({
1851
+ storage_path,
1852
+ entity_id: params.entityId,
1853
+ original_filename: params.file.name,
1854
+ content_type: params.file.type,
1855
+ size: params.file.size,
1856
+ context: params.context,
1857
+ affiliation: params.affiliation,
1858
+ owner_id: params.ownerId
1859
+ }),
1860
+ fallbackErrorMessage: `Could not confirm upload of ${params.file.name}`
1861
+ });
1862
+ }
1863
+ async function uploadMediaFiles(files, params) {
1864
+ const assets = [];
1865
+ const errors = [];
1866
+ for (const file of files) {
1867
+ try {
1868
+ assets.push(await uploadMediaFile({ ...params, file }));
1869
+ } catch (err) {
1870
+ errors.push({ file: file.name, message: err instanceof Error ? err.message : "Upload failed" });
1871
+ }
1872
+ }
1873
+ return { assets, errors };
2158
1874
  }
2159
1875
  export {
2160
- acceptQuote,
2161
1876
  addToCart,
2162
1877
  addToGuestCart,
2163
1878
  addToWishlist,
2164
- approveDeliverable,
1879
+ approveBespokeDeliverable,
1880
+ approveMilestone,
1881
+ autocompleteAddress,
2165
1882
  bespokeService,
2166
1883
  bookAppointment,
2167
1884
  cancelAppointment,
2168
1885
  cancelOrder,
1886
+ changePassword,
2169
1887
  checkAvailability,
2170
1888
  checkInWishlist,
1889
+ claimWalletCredit,
2171
1890
  clearCart,
2172
1891
  clearGuestCart,
2173
1892
  clearTokens,
2174
1893
  configService,
2175
- consultationService,
1894
+ confirmAppointmentCompletion,
1895
+ confirmMediaUpload,
1896
+ createBespokeOrder,
2176
1897
  createCmsEntry,
1898
+ createComment,
2177
1899
  createConsultation,
1900
+ createCustomerAddress,
2178
1901
  createDedicatedAccount,
1902
+ createFeedback,
2179
1903
  createOrder,
1904
+ createReview,
1905
+ createServiceBookingOrder,
1906
+ deactivateCustomerAccount,
1907
+ decodePlusCode,
2180
1908
  deleteCmsEntry,
1909
+ deleteCustomerAddress,
1910
+ disputeAppointment,
2181
1911
  findOrder,
2182
- findOrderByToken,
1912
+ forgotPasswordCustomer,
2183
1913
  getAppointment,
2184
1914
  getAppointments,
2185
1915
  getAuthUser,
1916
+ getAvailablePaymentMethods,
2186
1917
  getAvailableSlots,
2187
1918
  getBusinessConfig,
2188
1919
  getCart,
2189
1920
  getCmsEntry,
2190
- getConsultation,
2191
- getConsultations,
2192
- getDeliverables,
1921
+ getCustomerAddresses,
2193
1922
  getFeaturedProducts,
2194
1923
  getGuestCart,
2195
- getGuestCartCount,
2196
1924
  getGuestCartProductType,
2197
- getGuestCartSubtotal,
2198
- getLatestProducts,
2199
- getLowStockProducts,
2200
1925
  getMe,
1926
+ getMerchantLegalDocument,
1927
+ getMilestones,
1928
+ getMyFeedback,
2201
1929
  getNewProducts,
1930
+ getNotificationInbox,
1931
+ getNotificationPreferences,
2202
1932
  getOnSaleProducts,
2203
1933
  getOrders,
1934
+ getPendingComments,
1935
+ getPendingReviews,
2204
1936
  getPopularProducts,
1937
+ getPresignedUploadUrl,
1938
+ getProductComments,
1939
+ getProductReviews,
2205
1940
  getRecommendedProducts,
2206
- getSellerShippingAddress,
2207
1941
  getShippingRates,
2208
1942
  getStoredTokens,
1943
+ getStorefrontBusinessInfo,
2209
1944
  getStorefrontCollectionBySlug,
2210
1945
  getStorefrontCollections,
2211
1946
  getStorefrontProductBySlug,
@@ -2213,39 +1948,65 @@ export {
2213
1948
  getSupportedCurrencies,
2214
1949
  getSupportedLanguages,
2215
1950
  getTransactionStatus,
1951
+ getVapidPublicKey,
1952
+ getWalletBalance,
1953
+ getWalletLedger,
2216
1954
  getWishlist,
2217
1955
  googleTranslate,
2218
1956
  initiatePayment,
1957
+ listBusinessLocations,
2219
1958
  listCmsEntries,
1959
+ listPickupLocations,
2220
1960
  loginCustomer,
2221
1961
  logisticsService,
2222
1962
  logout,
2223
1963
  mapAuthUserToCustomer,
1964
+ markAllNotificationsRead,
1965
+ markNotificationRead,
1966
+ merchantReplyToReview,
2224
1967
  mergeGuestCart,
2225
1968
  mergeGuestCartIfPresent,
1969
+ moderateComment,
1970
+ moderateReview,
2226
1971
  notificationsService,
2227
1972
  paymentService,
2228
1973
  refreshToken,
2229
1974
  registerCustomer,
2230
- rejectQuote,
2231
1975
  removeFromCart,
2232
1976
  removeFromGuestCart,
2233
1977
  removeFromWishlist,
2234
1978
  removeProductFromWishlist,
2235
- requestQuote,
2236
- requestRevision,
1979
+ requestBespokeRevision,
1980
+ requestCustomerOtp,
1981
+ requestCustomerReactivation,
1982
+ rescheduleAppointment,
1983
+ resetPasswordCustomer,
1984
+ resolveDigitalDownload,
1985
+ resolveDigitalDownloadFile,
1986
+ reverseGeocode,
2237
1987
  sendContactConfirmation,
2238
1988
  sendContactEmail,
2239
- sendMessage,
2240
1989
  serviceBookingService,
2241
1990
  silentRefresh,
2242
1991
  storeTokens,
1992
+ subscribeToPush,
1993
+ trackNotificationClick,
2243
1994
  trackOrder,
1995
+ unsubscribeFromPush,
2244
1996
  updateCartItem,
2245
1997
  updateCmsEntry,
1998
+ updateComment,
1999
+ updateCustomerAddress,
2246
2000
  updateGuestCartItemQuantity,
2001
+ updateNotificationPreferences,
2247
2002
  updateProfile,
2003
+ updateReview,
2004
+ uploadMediaFile,
2005
+ uploadMediaFiles,
2006
+ validatePromoCode,
2248
2007
  validateShippingAddress,
2008
+ verifyCustomerOtp,
2009
+ verifyCustomerReactivation,
2249
2010
  verifySocialToken,
2250
2011
  verifyTransaction
2251
2012
  };