@kasuvia/sdk 0.0.8 → 0.0.9

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