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