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