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