@bacano/sdk 1.0.0
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/AUTHENTICATION.md +365 -0
- package/LICENSE +21 -0
- package/README.md +1357 -0
- package/client.d.ts +96 -0
- package/errors.d.ts +24 -0
- package/graphql.cjs +2057 -0
- package/graphql.d.ts +17 -0
- package/graphql.js +2034 -0
- package/index.cjs +4956 -0
- package/index.d.ts +8 -0
- package/index.js +4933 -0
- package/media.d.ts +9 -0
- package/modules/auth.d.ts +9 -0
- package/modules/availability.d.ts +6 -0
- package/modules/cart-session.d.ts +13 -0
- package/modules/cart.d.ts +16 -0
- package/modules/catalog.d.ts +24 -0
- package/modules/checkout.d.ts +42 -0
- package/modules/content.d.ts +9 -0
- package/modules/orders.d.ts +9 -0
- package/modules/profile.d.ts +8 -0
- package/modules/website.d.ts +3 -0
- package/package.json +52 -0
- package/react/context.d.ts +7 -0
- package/react/hooks.d.ts +74 -0
- package/react/provider.d.ts +7 -0
- package/react.cjs +5232 -0
- package/react.d.ts +23 -0
- package/react.js +5209 -0
- package/transport/documents.d.ts +83 -0
- package/transport/graphql.d.ts +7 -0
- package/types.d.ts +991 -0
package/index.js
ADDED
|
@@ -0,0 +1,4933 @@
|
|
|
1
|
+
// libs/sdk/src/errors.ts
|
|
2
|
+
var WebsiteError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
constructor(message, code = "WEBSITE_ERROR") {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "WebsiteError";
|
|
7
|
+
this.code = code;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var NotInitializedError = class extends WebsiteError {
|
|
11
|
+
constructor() {
|
|
12
|
+
super(
|
|
13
|
+
"BacanoClient has not been initialized. Call client.init() first.",
|
|
14
|
+
"NOT_INITIALIZED"
|
|
15
|
+
);
|
|
16
|
+
this.name = "NotInitializedError";
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
var AuthError = class extends WebsiteError {
|
|
20
|
+
constructor(message) {
|
|
21
|
+
super(message, "AUTH_ERROR");
|
|
22
|
+
this.name = "AuthError";
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var CheckoutError = class extends WebsiteError {
|
|
26
|
+
errors;
|
|
27
|
+
constructor(errors) {
|
|
28
|
+
super(errors.join("; "), "CHECKOUT_ERROR");
|
|
29
|
+
this.name = "CheckoutError";
|
|
30
|
+
this.errors = errors;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
var GraphQLRequestError = class extends WebsiteError {
|
|
34
|
+
graphqlErrors;
|
|
35
|
+
constructor(errors) {
|
|
36
|
+
const message = errors.map((e) => e.message).join("; ");
|
|
37
|
+
super(message, "GRAPHQL_ERROR");
|
|
38
|
+
this.name = "GraphQLRequestError";
|
|
39
|
+
this.graphqlErrors = errors;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// libs/sdk/src/modules/auth.ts
|
|
44
|
+
function createAuthHeaders(accessToken) {
|
|
45
|
+
return accessToken ? { Authorization: `Bearer ${accessToken}` } : {};
|
|
46
|
+
}
|
|
47
|
+
function createRequestHeadersProvider(websiteSlug, getAccessToken) {
|
|
48
|
+
return async () => {
|
|
49
|
+
const headers = { "X-Bacano-Website-Slug": websiteSlug };
|
|
50
|
+
if (!getAccessToken) return headers;
|
|
51
|
+
try {
|
|
52
|
+
return {
|
|
53
|
+
...headers,
|
|
54
|
+
...createAuthHeaders(await getAccessToken())
|
|
55
|
+
};
|
|
56
|
+
} catch (error) {
|
|
57
|
+
throw new AuthError(
|
|
58
|
+
`Could not obtain the buyer access token: ${error instanceof Error ? error.message : String(error)}`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async function getWebsitePublicConfig(options) {
|
|
64
|
+
const apiUrl = options.apiUrl.replace(/\/+$/, "");
|
|
65
|
+
const headers = options.origin ? { Origin: options.origin } : void 0;
|
|
66
|
+
const params = new URLSearchParams({ websiteSlug: options.websiteSlug });
|
|
67
|
+
let response;
|
|
68
|
+
try {
|
|
69
|
+
response = await fetch(
|
|
70
|
+
`${apiUrl}/api/v1/website/public-config?${params.toString()}`,
|
|
71
|
+
{
|
|
72
|
+
headers,
|
|
73
|
+
cache: "no-store"
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
throw new WebsiteError(
|
|
78
|
+
`Could not load website auth configuration: ${error instanceof Error ? error.message : String(error)}`,
|
|
79
|
+
"NETWORK_ERROR"
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
throw new WebsiteError(
|
|
84
|
+
`Could not load website auth configuration (status ${response.status})`,
|
|
85
|
+
response.status === 403 ? "ORIGIN_NOT_ALLOWED" : "INVALID_RESPONSE"
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
const raw = await response.json();
|
|
89
|
+
const customerIdentification = raw.customer_identification ?? {
|
|
90
|
+
primary_identifier: "AUTO",
|
|
91
|
+
require_email: false,
|
|
92
|
+
require_phone: false,
|
|
93
|
+
require_document: false,
|
|
94
|
+
document_types: []
|
|
95
|
+
};
|
|
96
|
+
return {
|
|
97
|
+
websiteId: raw.website_id,
|
|
98
|
+
companyId: raw.company_id,
|
|
99
|
+
slug: raw.slug,
|
|
100
|
+
name: raw.name,
|
|
101
|
+
domain: raw.domain,
|
|
102
|
+
buyerAccountsEnabled: raw.buyer_accounts_enabled,
|
|
103
|
+
clerkPublishableKey: raw.clerk_publishable_key,
|
|
104
|
+
customerIdentification: {
|
|
105
|
+
primaryIdentifier: customerIdentification.primary_identifier,
|
|
106
|
+
requireEmail: customerIdentification.require_email,
|
|
107
|
+
requirePhone: customerIdentification.require_phone,
|
|
108
|
+
requireDocument: customerIdentification.require_document,
|
|
109
|
+
documentTypes: customerIdentification.document_types.map(
|
|
110
|
+
(documentType) => ({
|
|
111
|
+
code: documentType.code,
|
|
112
|
+
name: documentType.name,
|
|
113
|
+
shortLabel: documentType.short_label,
|
|
114
|
+
isDefault: documentType.is_default
|
|
115
|
+
})
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// libs/sdk/src/transport/graphql.ts
|
|
122
|
+
async function executeGraphQL(url, query, variables, headers) {
|
|
123
|
+
const resolvedHeaders = typeof headers === "function" ? await headers() : headers ?? {};
|
|
124
|
+
let res;
|
|
125
|
+
try {
|
|
126
|
+
res = await fetch(url, {
|
|
127
|
+
method: "POST",
|
|
128
|
+
headers: {
|
|
129
|
+
"Content-Type": "application/json",
|
|
130
|
+
...resolvedHeaders
|
|
131
|
+
},
|
|
132
|
+
body: JSON.stringify({ query, variables })
|
|
133
|
+
});
|
|
134
|
+
} catch (err) {
|
|
135
|
+
throw new WebsiteError(
|
|
136
|
+
`Network error connecting to ${url}: ${err instanceof Error ? err.message : String(err)}`,
|
|
137
|
+
"NETWORK_ERROR"
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
if (res.status === 429) {
|
|
141
|
+
throw new WebsiteError(
|
|
142
|
+
"Rate limit exceeded. Please try again later.",
|
|
143
|
+
"RATE_LIMITED"
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
let body;
|
|
147
|
+
try {
|
|
148
|
+
body = await res.json();
|
|
149
|
+
} catch {
|
|
150
|
+
throw new WebsiteError(
|
|
151
|
+
`Non-JSON response from ${url} (status ${res.status})`,
|
|
152
|
+
"INVALID_RESPONSE"
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
if (body.errors && body.errors.length > 0) {
|
|
156
|
+
throw new GraphQLRequestError(body.errors);
|
|
157
|
+
}
|
|
158
|
+
if (!body.data) {
|
|
159
|
+
throw new WebsiteError(
|
|
160
|
+
"GraphQL response contains no data",
|
|
161
|
+
"EMPTY_RESPONSE"
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
return body.data;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// libs/sdk/src/transport/documents.ts
|
|
168
|
+
var RESOLVE_WEBSITE_BY_SLUG = (
|
|
169
|
+
/* GraphQL */
|
|
170
|
+
`
|
|
171
|
+
query ResolveWebsiteBySlug($slug: String!) {
|
|
172
|
+
websites(
|
|
173
|
+
where: { slug: { _eq: $slug }, is_active: { _eq: true } }
|
|
174
|
+
limit: 1
|
|
175
|
+
) {
|
|
176
|
+
id
|
|
177
|
+
company_id
|
|
178
|
+
chain_id
|
|
179
|
+
name
|
|
180
|
+
slug
|
|
181
|
+
domain
|
|
182
|
+
config
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
`
|
|
186
|
+
);
|
|
187
|
+
var GET_PRODUCTS = (
|
|
188
|
+
/* GraphQL */
|
|
189
|
+
`
|
|
190
|
+
query GetProducts(
|
|
191
|
+
$companyId: uuid!
|
|
192
|
+
$chainId: uuid!
|
|
193
|
+
$websiteId: uuid!
|
|
194
|
+
$limit: Int = 20
|
|
195
|
+
$offset: Int = 0
|
|
196
|
+
$where: products_bool_exp = {}
|
|
197
|
+
$orderBy: [products_order_by!] = [{ name: asc }]
|
|
198
|
+
) {
|
|
199
|
+
products(
|
|
200
|
+
where: {
|
|
201
|
+
_and: [
|
|
202
|
+
{ company_id: { _eq: $companyId } }
|
|
203
|
+
{
|
|
204
|
+
product_website_publications: {
|
|
205
|
+
website_id: { _eq: $websiteId }
|
|
206
|
+
is_visible: { _eq: true }
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
{
|
|
210
|
+
chain_products: {
|
|
211
|
+
chain_id: { _eq: $chainId }
|
|
212
|
+
is_enabled: { _eq: true }
|
|
213
|
+
status: { _eq: active }
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
$where
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
limit: $limit
|
|
220
|
+
offset: $offset
|
|
221
|
+
order_by: $orderBy
|
|
222
|
+
) {
|
|
223
|
+
id
|
|
224
|
+
name
|
|
225
|
+
description
|
|
226
|
+
long_description
|
|
227
|
+
slug
|
|
228
|
+
seo_title
|
|
229
|
+
seo_description
|
|
230
|
+
seo_image_file_id
|
|
231
|
+
category
|
|
232
|
+
subcategory
|
|
233
|
+
color
|
|
234
|
+
measurement_unit
|
|
235
|
+
created_at
|
|
236
|
+
updated_at
|
|
237
|
+
brand {
|
|
238
|
+
id
|
|
239
|
+
name
|
|
240
|
+
}
|
|
241
|
+
seo_image_file {
|
|
242
|
+
mimeType
|
|
243
|
+
name
|
|
244
|
+
}
|
|
245
|
+
product_variants(
|
|
246
|
+
where: { is_disabled: { _eq: false } }
|
|
247
|
+
order_by: { order: asc }
|
|
248
|
+
) {
|
|
249
|
+
id
|
|
250
|
+
sku
|
|
251
|
+
barcode
|
|
252
|
+
order
|
|
253
|
+
weight
|
|
254
|
+
depth
|
|
255
|
+
height
|
|
256
|
+
length
|
|
257
|
+
product_variant_prices(
|
|
258
|
+
where: {
|
|
259
|
+
price_type: { _in: [CLIENT, SALE, WHOLESALE, WHOLESALE_2] }
|
|
260
|
+
_or: [
|
|
261
|
+
{ chain_id: { _eq: $chainId } }
|
|
262
|
+
{ chain_id: { _is_null: true } }
|
|
263
|
+
]
|
|
264
|
+
}
|
|
265
|
+
) {
|
|
266
|
+
id
|
|
267
|
+
price
|
|
268
|
+
price_type
|
|
269
|
+
chain_id
|
|
270
|
+
}
|
|
271
|
+
product_variant_attributes(order_by: { order: asc }) {
|
|
272
|
+
id
|
|
273
|
+
value
|
|
274
|
+
order
|
|
275
|
+
attribute_value {
|
|
276
|
+
id
|
|
277
|
+
code
|
|
278
|
+
sort_order
|
|
279
|
+
metadata
|
|
280
|
+
}
|
|
281
|
+
attribute {
|
|
282
|
+
id
|
|
283
|
+
name
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
product_media(order_by: { order: asc }) {
|
|
288
|
+
id
|
|
289
|
+
file_id
|
|
290
|
+
order
|
|
291
|
+
file {
|
|
292
|
+
mimeType
|
|
293
|
+
name
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
product_related_products(
|
|
297
|
+
where: {
|
|
298
|
+
related_product: {
|
|
299
|
+
chain_products: {
|
|
300
|
+
chain_id: { _eq: $chainId }
|
|
301
|
+
is_enabled: { _eq: true }
|
|
302
|
+
status: { _eq: active }
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
order_by: { sort_order: asc }
|
|
307
|
+
) {
|
|
308
|
+
id
|
|
309
|
+
related_product_id
|
|
310
|
+
display_label
|
|
311
|
+
sort_order
|
|
312
|
+
related_product {
|
|
313
|
+
id
|
|
314
|
+
slug
|
|
315
|
+
name
|
|
316
|
+
sku
|
|
317
|
+
product_media(
|
|
318
|
+
where: { file: { mimeType: { _ilike: "image/%" } } }
|
|
319
|
+
order_by: { order: asc }
|
|
320
|
+
limit: 1
|
|
321
|
+
) {
|
|
322
|
+
id
|
|
323
|
+
file_id
|
|
324
|
+
order
|
|
325
|
+
file {
|
|
326
|
+
mimeType
|
|
327
|
+
name
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
product_attributes {
|
|
333
|
+
id
|
|
334
|
+
value
|
|
335
|
+
attribute_value {
|
|
336
|
+
id
|
|
337
|
+
code
|
|
338
|
+
sort_order
|
|
339
|
+
metadata
|
|
340
|
+
}
|
|
341
|
+
attribute {
|
|
342
|
+
id
|
|
343
|
+
name
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
catalog_attribute_lists(
|
|
348
|
+
where: { company_id: { _eq: $companyId }, status: { _eq: published } }
|
|
349
|
+
order_by: { updated_at: desc }
|
|
350
|
+
) {
|
|
351
|
+
id
|
|
352
|
+
key
|
|
353
|
+
name
|
|
354
|
+
description
|
|
355
|
+
status
|
|
356
|
+
items(
|
|
357
|
+
where: { is_visible: { _eq: true } }
|
|
358
|
+
order_by: [{ sort_order: asc }, { public_label: asc }]
|
|
359
|
+
) {
|
|
360
|
+
id
|
|
361
|
+
attribute_id
|
|
362
|
+
public_label
|
|
363
|
+
public_slug
|
|
364
|
+
sort_order
|
|
365
|
+
attribute {
|
|
366
|
+
id
|
|
367
|
+
name
|
|
368
|
+
type
|
|
369
|
+
}
|
|
370
|
+
values(
|
|
371
|
+
where: { is_visible: { _eq: true } }
|
|
372
|
+
order_by: [{ sort_order: asc }, { public_label: asc }]
|
|
373
|
+
) {
|
|
374
|
+
id
|
|
375
|
+
attribute_value_id
|
|
376
|
+
public_label
|
|
377
|
+
public_slug
|
|
378
|
+
sort_order
|
|
379
|
+
attribute_value {
|
|
380
|
+
id
|
|
381
|
+
code
|
|
382
|
+
metadata
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
`
|
|
389
|
+
);
|
|
390
|
+
var GET_PRODUCT_BY_PK = (
|
|
391
|
+
/* GraphQL */
|
|
392
|
+
`
|
|
393
|
+
query GetProductByPk(
|
|
394
|
+
$productId: uuid!
|
|
395
|
+
$companyId: uuid!
|
|
396
|
+
$chainId: uuid!
|
|
397
|
+
$websiteId: uuid!
|
|
398
|
+
) {
|
|
399
|
+
products(
|
|
400
|
+
where: {
|
|
401
|
+
id: { _eq: $productId }
|
|
402
|
+
company_id: { _eq: $companyId }
|
|
403
|
+
product_website_publications: {
|
|
404
|
+
website_id: { _eq: $websiteId }
|
|
405
|
+
is_visible: { _eq: true }
|
|
406
|
+
}
|
|
407
|
+
chain_products: {
|
|
408
|
+
chain_id: { _eq: $chainId }
|
|
409
|
+
is_enabled: { _eq: true }
|
|
410
|
+
status: { _eq: active }
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
limit: 1
|
|
414
|
+
) {
|
|
415
|
+
id
|
|
416
|
+
name
|
|
417
|
+
description
|
|
418
|
+
long_description
|
|
419
|
+
slug
|
|
420
|
+
seo_title
|
|
421
|
+
seo_description
|
|
422
|
+
seo_image_file_id
|
|
423
|
+
category
|
|
424
|
+
subcategory
|
|
425
|
+
color
|
|
426
|
+
measurement_unit
|
|
427
|
+
created_at
|
|
428
|
+
updated_at
|
|
429
|
+
brand {
|
|
430
|
+
id
|
|
431
|
+
name
|
|
432
|
+
}
|
|
433
|
+
seo_image_file {
|
|
434
|
+
mimeType
|
|
435
|
+
name
|
|
436
|
+
}
|
|
437
|
+
product_variants(
|
|
438
|
+
where: { is_disabled: { _eq: false } }
|
|
439
|
+
order_by: { order: asc }
|
|
440
|
+
) {
|
|
441
|
+
id
|
|
442
|
+
sku
|
|
443
|
+
barcode
|
|
444
|
+
order
|
|
445
|
+
weight
|
|
446
|
+
depth
|
|
447
|
+
height
|
|
448
|
+
length
|
|
449
|
+
product_variant_prices(
|
|
450
|
+
where: {
|
|
451
|
+
price_type: { _in: [CLIENT, SALE, WHOLESALE, WHOLESALE_2] }
|
|
452
|
+
_or: [
|
|
453
|
+
{ chain_id: { _eq: $chainId } }
|
|
454
|
+
{ chain_id: { _is_null: true } }
|
|
455
|
+
]
|
|
456
|
+
}
|
|
457
|
+
) {
|
|
458
|
+
id
|
|
459
|
+
price
|
|
460
|
+
price_type
|
|
461
|
+
chain_id
|
|
462
|
+
}
|
|
463
|
+
product_variant_attributes(order_by: { order: asc }) {
|
|
464
|
+
id
|
|
465
|
+
value
|
|
466
|
+
order
|
|
467
|
+
attribute_value {
|
|
468
|
+
id
|
|
469
|
+
code
|
|
470
|
+
sort_order
|
|
471
|
+
metadata
|
|
472
|
+
}
|
|
473
|
+
attribute {
|
|
474
|
+
id
|
|
475
|
+
name
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
product_media(order_by: { order: asc }) {
|
|
480
|
+
id
|
|
481
|
+
file_id
|
|
482
|
+
order
|
|
483
|
+
file {
|
|
484
|
+
mimeType
|
|
485
|
+
name
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
product_related_products(
|
|
489
|
+
where: {
|
|
490
|
+
related_product: {
|
|
491
|
+
chain_products: {
|
|
492
|
+
chain_id: { _eq: $chainId }
|
|
493
|
+
is_enabled: { _eq: true }
|
|
494
|
+
status: { _eq: active }
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
order_by: { sort_order: asc }
|
|
499
|
+
) {
|
|
500
|
+
id
|
|
501
|
+
related_product_id
|
|
502
|
+
display_label
|
|
503
|
+
sort_order
|
|
504
|
+
related_product {
|
|
505
|
+
id
|
|
506
|
+
slug
|
|
507
|
+
name
|
|
508
|
+
sku
|
|
509
|
+
product_media(
|
|
510
|
+
where: { file: { mimeType: { _ilike: "image/%" } } }
|
|
511
|
+
order_by: { order: asc }
|
|
512
|
+
limit: 1
|
|
513
|
+
) {
|
|
514
|
+
id
|
|
515
|
+
file_id
|
|
516
|
+
order
|
|
517
|
+
file {
|
|
518
|
+
mimeType
|
|
519
|
+
name
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
product_attributes {
|
|
525
|
+
id
|
|
526
|
+
value
|
|
527
|
+
attribute_value {
|
|
528
|
+
id
|
|
529
|
+
code
|
|
530
|
+
sort_order
|
|
531
|
+
metadata
|
|
532
|
+
}
|
|
533
|
+
attribute {
|
|
534
|
+
id
|
|
535
|
+
name
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
catalog_attribute_lists(
|
|
540
|
+
where: { company_id: { _eq: $companyId }, status: { _eq: published } }
|
|
541
|
+
order_by: { updated_at: desc }
|
|
542
|
+
) {
|
|
543
|
+
id
|
|
544
|
+
key
|
|
545
|
+
name
|
|
546
|
+
description
|
|
547
|
+
status
|
|
548
|
+
items(
|
|
549
|
+
where: { is_visible: { _eq: true } }
|
|
550
|
+
order_by: [{ sort_order: asc }, { public_label: asc }]
|
|
551
|
+
) {
|
|
552
|
+
id
|
|
553
|
+
attribute_id
|
|
554
|
+
public_label
|
|
555
|
+
public_slug
|
|
556
|
+
sort_order
|
|
557
|
+
attribute {
|
|
558
|
+
id
|
|
559
|
+
name
|
|
560
|
+
type
|
|
561
|
+
}
|
|
562
|
+
values(
|
|
563
|
+
where: { is_visible: { _eq: true } }
|
|
564
|
+
order_by: [{ sort_order: asc }, { public_label: asc }]
|
|
565
|
+
) {
|
|
566
|
+
id
|
|
567
|
+
attribute_value_id
|
|
568
|
+
public_label
|
|
569
|
+
public_slug
|
|
570
|
+
sort_order
|
|
571
|
+
attribute_value {
|
|
572
|
+
id
|
|
573
|
+
code
|
|
574
|
+
metadata
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
`
|
|
581
|
+
);
|
|
582
|
+
var GET_PRODUCT_BY_SLUG = (
|
|
583
|
+
/* GraphQL */
|
|
584
|
+
GET_PRODUCT_BY_PK.replace(
|
|
585
|
+
"query GetProductByPk(",
|
|
586
|
+
"query GetProductBySlug("
|
|
587
|
+
).replace("$productId: uuid!", "$slug: String!").replace("id: { _eq: $productId }", "slug: { _eq: $slug }")
|
|
588
|
+
);
|
|
589
|
+
var GET_PRODUCT_SEO_ENTRIES = (
|
|
590
|
+
/* GraphQL */
|
|
591
|
+
`
|
|
592
|
+
query GetProductSeoEntries(
|
|
593
|
+
$companyId: uuid!
|
|
594
|
+
$chainId: uuid!
|
|
595
|
+
$websiteId: uuid!
|
|
596
|
+
$limit: Int!
|
|
597
|
+
$offset: Int!
|
|
598
|
+
) {
|
|
599
|
+
products(
|
|
600
|
+
where: {
|
|
601
|
+
company_id: { _eq: $companyId }
|
|
602
|
+
slug: { _is_null: false }
|
|
603
|
+
product_website_publications: {
|
|
604
|
+
website_id: { _eq: $websiteId }
|
|
605
|
+
is_visible: { _eq: true }
|
|
606
|
+
}
|
|
607
|
+
chain_products: {
|
|
608
|
+
chain_id: { _eq: $chainId }
|
|
609
|
+
is_enabled: { _eq: true }
|
|
610
|
+
status: { _eq: active }
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
limit: $limit
|
|
614
|
+
offset: $offset
|
|
615
|
+
order_by: [{ updated_at: desc }, { created_at: desc }]
|
|
616
|
+
) {
|
|
617
|
+
id
|
|
618
|
+
name
|
|
619
|
+
slug
|
|
620
|
+
seo_title
|
|
621
|
+
seo_description
|
|
622
|
+
seo_image_file_id
|
|
623
|
+
category
|
|
624
|
+
subcategory
|
|
625
|
+
created_at
|
|
626
|
+
updated_at
|
|
627
|
+
seo_image_file {
|
|
628
|
+
mimeType
|
|
629
|
+
name
|
|
630
|
+
}
|
|
631
|
+
product_media(
|
|
632
|
+
where: { file: { mimeType: { _ilike: "image/%" } } }
|
|
633
|
+
order_by: { order: asc }
|
|
634
|
+
limit: 1
|
|
635
|
+
) {
|
|
636
|
+
id
|
|
637
|
+
file_id
|
|
638
|
+
order
|
|
639
|
+
file {
|
|
640
|
+
mimeType
|
|
641
|
+
name
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
product_variants(
|
|
645
|
+
where: { is_disabled: { _eq: false } }
|
|
646
|
+
order_by: { order: asc }
|
|
647
|
+
) {
|
|
648
|
+
id
|
|
649
|
+
product_variant_prices(
|
|
650
|
+
where: {
|
|
651
|
+
price_type: { _in: [CLIENT, SALE, WHOLESALE, WHOLESALE_2] }
|
|
652
|
+
_or: [
|
|
653
|
+
{ chain_id: { _eq: $chainId } }
|
|
654
|
+
{ chain_id: { _is_null: true } }
|
|
655
|
+
]
|
|
656
|
+
}
|
|
657
|
+
) {
|
|
658
|
+
id
|
|
659
|
+
price
|
|
660
|
+
price_type
|
|
661
|
+
chain_id
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
`
|
|
667
|
+
);
|
|
668
|
+
var GET_CATEGORIES = (
|
|
669
|
+
/* GraphQL */
|
|
670
|
+
`
|
|
671
|
+
query GetCategories {
|
|
672
|
+
product_categories {
|
|
673
|
+
name
|
|
674
|
+
description
|
|
675
|
+
product_subcategories {
|
|
676
|
+
id
|
|
677
|
+
name
|
|
678
|
+
description
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
`
|
|
683
|
+
);
|
|
684
|
+
var GET_CATALOG_CATEGORY_LIST = (
|
|
685
|
+
/* GraphQL */
|
|
686
|
+
`
|
|
687
|
+
query GetCatalogCategoryList($companyId: uuid!, $key: String!) {
|
|
688
|
+
list: catalog_category_lists(
|
|
689
|
+
where: {
|
|
690
|
+
company_id: { _eq: $companyId }
|
|
691
|
+
key: { _eq: $key }
|
|
692
|
+
status: { _eq: published }
|
|
693
|
+
}
|
|
694
|
+
limit: 1
|
|
695
|
+
) {
|
|
696
|
+
id
|
|
697
|
+
key
|
|
698
|
+
name
|
|
699
|
+
description
|
|
700
|
+
status
|
|
701
|
+
items(
|
|
702
|
+
where: { is_visible: { _eq: true } }
|
|
703
|
+
order_by: [{ sort_order: asc }, { public_label: asc }]
|
|
704
|
+
) {
|
|
705
|
+
id
|
|
706
|
+
source_type
|
|
707
|
+
product_category
|
|
708
|
+
product_subcategory_id
|
|
709
|
+
public_label
|
|
710
|
+
public_slug
|
|
711
|
+
sort_order
|
|
712
|
+
category_ref {
|
|
713
|
+
name
|
|
714
|
+
description
|
|
715
|
+
}
|
|
716
|
+
subcategory_ref {
|
|
717
|
+
id
|
|
718
|
+
name
|
|
719
|
+
description
|
|
720
|
+
category
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
`
|
|
726
|
+
);
|
|
727
|
+
var GET_CATALOG_ATTRIBUTE_LIST = (
|
|
728
|
+
/* GraphQL */
|
|
729
|
+
`
|
|
730
|
+
query GetCatalogAttributeList($companyId: uuid!, $key: String!) {
|
|
731
|
+
list: catalog_attribute_lists(
|
|
732
|
+
where: {
|
|
733
|
+
company_id: { _eq: $companyId }
|
|
734
|
+
key: { _eq: $key }
|
|
735
|
+
status: { _eq: published }
|
|
736
|
+
}
|
|
737
|
+
limit: 1
|
|
738
|
+
) {
|
|
739
|
+
id
|
|
740
|
+
key
|
|
741
|
+
name
|
|
742
|
+
description
|
|
743
|
+
status
|
|
744
|
+
items(
|
|
745
|
+
where: { is_visible: { _eq: true } }
|
|
746
|
+
order_by: [{ sort_order: asc }, { public_label: asc }]
|
|
747
|
+
) {
|
|
748
|
+
id
|
|
749
|
+
attribute_id
|
|
750
|
+
public_label
|
|
751
|
+
public_slug
|
|
752
|
+
sort_order
|
|
753
|
+
attribute {
|
|
754
|
+
id
|
|
755
|
+
name
|
|
756
|
+
type
|
|
757
|
+
}
|
|
758
|
+
values(
|
|
759
|
+
where: { is_visible: { _eq: true } }
|
|
760
|
+
order_by: [{ sort_order: asc }, { public_label: asc }]
|
|
761
|
+
) {
|
|
762
|
+
id
|
|
763
|
+
attribute_value_id
|
|
764
|
+
public_label
|
|
765
|
+
public_slug
|
|
766
|
+
sort_order
|
|
767
|
+
attribute_value {
|
|
768
|
+
id
|
|
769
|
+
code
|
|
770
|
+
metadata
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
`
|
|
777
|
+
);
|
|
778
|
+
var GET_CONTEXTUAL_CATALOG_ATTRIBUTE_LIST = (
|
|
779
|
+
/* GraphQL */
|
|
780
|
+
`
|
|
781
|
+
mutation GetContextualCatalogAttributeList(
|
|
782
|
+
$websiteId: uuid!
|
|
783
|
+
$key: String!
|
|
784
|
+
$onlyApplicable: Boolean
|
|
785
|
+
$filters: WebsiteCatalogFiltersInput
|
|
786
|
+
) {
|
|
787
|
+
websiteGetAttributeList(
|
|
788
|
+
website_id: $websiteId
|
|
789
|
+
key: $key
|
|
790
|
+
only_applicable: $onlyApplicable
|
|
791
|
+
filters: $filters
|
|
792
|
+
) {
|
|
793
|
+
success
|
|
794
|
+
errors
|
|
795
|
+
list {
|
|
796
|
+
id
|
|
797
|
+
key
|
|
798
|
+
name
|
|
799
|
+
description
|
|
800
|
+
status
|
|
801
|
+
items {
|
|
802
|
+
id
|
|
803
|
+
attribute_id
|
|
804
|
+
attribute_name
|
|
805
|
+
attribute_type
|
|
806
|
+
public_label
|
|
807
|
+
public_slug
|
|
808
|
+
sort_order
|
|
809
|
+
values {
|
|
810
|
+
id
|
|
811
|
+
attribute_value_id
|
|
812
|
+
code
|
|
813
|
+
public_label
|
|
814
|
+
public_slug
|
|
815
|
+
sort_order
|
|
816
|
+
metadata
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
`
|
|
823
|
+
);
|
|
824
|
+
var WEBSITE_GET_CATALOG_PAGE = (
|
|
825
|
+
/* GraphQL */
|
|
826
|
+
`
|
|
827
|
+
mutation WebsiteGetCatalogPage(
|
|
828
|
+
$websiteId: uuid!
|
|
829
|
+
$categoryListKey: String!
|
|
830
|
+
$attributeListKey: String!
|
|
831
|
+
$filters: WebsiteCatalogPageFiltersInput
|
|
832
|
+
$include: WebsiteCatalogPageIncludeInput
|
|
833
|
+
) {
|
|
834
|
+
result: websiteGetCatalogPage(
|
|
835
|
+
website_id: $websiteId
|
|
836
|
+
category_list_key: $categoryListKey
|
|
837
|
+
attribute_list_key: $attributeListKey
|
|
838
|
+
filters: $filters
|
|
839
|
+
include: $include
|
|
840
|
+
) {
|
|
841
|
+
success
|
|
842
|
+
products
|
|
843
|
+
category_list
|
|
844
|
+
attribute_list
|
|
845
|
+
filters
|
|
846
|
+
brands
|
|
847
|
+
pagination
|
|
848
|
+
applied_filters
|
|
849
|
+
debug
|
|
850
|
+
errors
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
`
|
|
854
|
+
);
|
|
855
|
+
var WEBSITE_GET_PRODUCT_RECOMMENDATIONS = (
|
|
856
|
+
/* GraphQL */
|
|
857
|
+
`
|
|
858
|
+
mutation WebsiteGetProductRecommendations(
|
|
859
|
+
$websiteId: uuid!
|
|
860
|
+
$productId: uuid!
|
|
861
|
+
$limit: Int
|
|
862
|
+
) {
|
|
863
|
+
result: websiteGetProductRecommendations(
|
|
864
|
+
website_id: $websiteId
|
|
865
|
+
product_id: $productId
|
|
866
|
+
limit: $limit
|
|
867
|
+
) {
|
|
868
|
+
success
|
|
869
|
+
products
|
|
870
|
+
strategy
|
|
871
|
+
errors
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
`
|
|
875
|
+
);
|
|
876
|
+
var GET_BRANDS = (
|
|
877
|
+
/* GraphQL */
|
|
878
|
+
`
|
|
879
|
+
query GetBrands($companyId: uuid!, $chainId: uuid!) {
|
|
880
|
+
brands(
|
|
881
|
+
where: {
|
|
882
|
+
company_id: { _eq: $companyId }
|
|
883
|
+
products: {
|
|
884
|
+
chain_products: {
|
|
885
|
+
chain_id: { _eq: $chainId }
|
|
886
|
+
is_enabled: { _eq: true }
|
|
887
|
+
status: { _eq: active }
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
order_by: { name: asc }
|
|
892
|
+
) {
|
|
893
|
+
id
|
|
894
|
+
name
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
`
|
|
898
|
+
);
|
|
899
|
+
var CONTENT_ENTRY_FIELDS = (
|
|
900
|
+
/* GraphQL */
|
|
901
|
+
`
|
|
902
|
+
id
|
|
903
|
+
key
|
|
904
|
+
category_key
|
|
905
|
+
name
|
|
906
|
+
locale
|
|
907
|
+
status
|
|
908
|
+
tags
|
|
909
|
+
content_json
|
|
910
|
+
resolved_fields
|
|
911
|
+
published_at
|
|
912
|
+
updated_at
|
|
913
|
+
content_type {
|
|
914
|
+
id
|
|
915
|
+
key
|
|
916
|
+
name
|
|
917
|
+
kind
|
|
918
|
+
}
|
|
919
|
+
`
|
|
920
|
+
);
|
|
921
|
+
var WEBSITE_GET_CONTENT = (
|
|
922
|
+
/* GraphQL */
|
|
923
|
+
`
|
|
924
|
+
mutation WebsiteGetContent(
|
|
925
|
+
$websiteId: uuid!
|
|
926
|
+
$key: String
|
|
927
|
+
$categoryKey: String
|
|
928
|
+
$tag: String
|
|
929
|
+
$locale: String
|
|
930
|
+
) {
|
|
931
|
+
result: websiteGetContent(
|
|
932
|
+
website_id: $websiteId
|
|
933
|
+
key: $key
|
|
934
|
+
category_key: $categoryKey
|
|
935
|
+
tag: $tag
|
|
936
|
+
locale: $locale
|
|
937
|
+
) {
|
|
938
|
+
success
|
|
939
|
+
errors
|
|
940
|
+
entries {
|
|
941
|
+
${CONTENT_ENTRY_FIELDS}
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
`
|
|
946
|
+
);
|
|
947
|
+
var WEBSITE_GET_PRODUCT_CONTENT = (
|
|
948
|
+
/* GraphQL */
|
|
949
|
+
`
|
|
950
|
+
mutation WebsiteGetProductContent(
|
|
951
|
+
$websiteId: uuid!
|
|
952
|
+
$productId: uuid!
|
|
953
|
+
$locale: String
|
|
954
|
+
) {
|
|
955
|
+
result: websiteGetProductContent(
|
|
956
|
+
website_id: $websiteId
|
|
957
|
+
product_id: $productId
|
|
958
|
+
locale: $locale
|
|
959
|
+
) {
|
|
960
|
+
success
|
|
961
|
+
errors
|
|
962
|
+
blocks {
|
|
963
|
+
id
|
|
964
|
+
product_id
|
|
965
|
+
locale
|
|
966
|
+
content_json
|
|
967
|
+
resolved_fields
|
|
968
|
+
sort_order
|
|
969
|
+
updated_at
|
|
970
|
+
content_type {
|
|
971
|
+
id
|
|
972
|
+
key
|
|
973
|
+
name
|
|
974
|
+
kind
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
`
|
|
980
|
+
);
|
|
981
|
+
var CHECK_AVAILABILITY = (
|
|
982
|
+
/* GraphQL */
|
|
983
|
+
`
|
|
984
|
+
query CheckAvailability($variantIds: [uuid!]!, $chainId: uuid!) {
|
|
985
|
+
websiteProductAvailability(
|
|
986
|
+
product_variant_ids: $variantIds
|
|
987
|
+
chain_id: $chainId
|
|
988
|
+
) {
|
|
989
|
+
product_variant_id
|
|
990
|
+
in_stock
|
|
991
|
+
label
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
`
|
|
995
|
+
);
|
|
996
|
+
var CREATE_CART = (
|
|
997
|
+
/* GraphQL */
|
|
998
|
+
`
|
|
999
|
+
mutation CreateCart(
|
|
1000
|
+
$websiteId: uuid!
|
|
1001
|
+
$cartToken: String
|
|
1002
|
+
$contact: WebsiteGuestContactInput
|
|
1003
|
+
$fulfillmentFamily: String
|
|
1004
|
+
) {
|
|
1005
|
+
websiteCreateCart(
|
|
1006
|
+
website_id: $websiteId
|
|
1007
|
+
cart_token: $cartToken
|
|
1008
|
+
contact: $contact
|
|
1009
|
+
fulfillment_family: $fulfillmentFamily
|
|
1010
|
+
) {
|
|
1011
|
+
success
|
|
1012
|
+
order_id
|
|
1013
|
+
cart_token
|
|
1014
|
+
cart {
|
|
1015
|
+
id
|
|
1016
|
+
status
|
|
1017
|
+
discount
|
|
1018
|
+
tax
|
|
1019
|
+
total_without_tax
|
|
1020
|
+
total_with_tax
|
|
1021
|
+
total_with_discount
|
|
1022
|
+
created_at
|
|
1023
|
+
updated_at
|
|
1024
|
+
items {
|
|
1025
|
+
id
|
|
1026
|
+
product_variant_id
|
|
1027
|
+
quantity
|
|
1028
|
+
product_variant {
|
|
1029
|
+
id
|
|
1030
|
+
sku
|
|
1031
|
+
product {
|
|
1032
|
+
id
|
|
1033
|
+
name
|
|
1034
|
+
}
|
|
1035
|
+
prices {
|
|
1036
|
+
id
|
|
1037
|
+
price
|
|
1038
|
+
price_type
|
|
1039
|
+
chain_id
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
errors
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
`
|
|
1048
|
+
);
|
|
1049
|
+
var GET_CART = (
|
|
1050
|
+
/* GraphQL */
|
|
1051
|
+
`
|
|
1052
|
+
mutation GetCart($orderId: uuid!, $cartToken: String!) {
|
|
1053
|
+
websiteGetCart(order_id: $orderId, cart_token: $cartToken) {
|
|
1054
|
+
success
|
|
1055
|
+
order_id
|
|
1056
|
+
cart_token
|
|
1057
|
+
cart {
|
|
1058
|
+
id
|
|
1059
|
+
status
|
|
1060
|
+
discount
|
|
1061
|
+
tax
|
|
1062
|
+
total_without_tax
|
|
1063
|
+
total_with_tax
|
|
1064
|
+
total_with_discount
|
|
1065
|
+
created_at
|
|
1066
|
+
updated_at
|
|
1067
|
+
items {
|
|
1068
|
+
id
|
|
1069
|
+
product_variant_id
|
|
1070
|
+
quantity
|
|
1071
|
+
product_variant {
|
|
1072
|
+
id
|
|
1073
|
+
sku
|
|
1074
|
+
product {
|
|
1075
|
+
id
|
|
1076
|
+
name
|
|
1077
|
+
}
|
|
1078
|
+
prices {
|
|
1079
|
+
id
|
|
1080
|
+
price
|
|
1081
|
+
price_type
|
|
1082
|
+
chain_id
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
errors
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
`
|
|
1091
|
+
);
|
|
1092
|
+
var START_GUEST_CHECKOUT = (
|
|
1093
|
+
/* GraphQL */
|
|
1094
|
+
`
|
|
1095
|
+
mutation StartGuestCheckout(
|
|
1096
|
+
$websiteId: uuid!
|
|
1097
|
+
$cartToken: String
|
|
1098
|
+
$contact: WebsiteGuestContactInput!
|
|
1099
|
+
$items: [WebsiteCheckoutItemInput!]!
|
|
1100
|
+
$fulfillmentFamily: String
|
|
1101
|
+
) {
|
|
1102
|
+
websiteStartGuestCheckout(
|
|
1103
|
+
website_id: $websiteId
|
|
1104
|
+
cart_token: $cartToken
|
|
1105
|
+
contact: $contact
|
|
1106
|
+
items: $items
|
|
1107
|
+
fulfillment_family: $fulfillmentFamily
|
|
1108
|
+
) {
|
|
1109
|
+
success
|
|
1110
|
+
order_id
|
|
1111
|
+
cart_token
|
|
1112
|
+
cart {
|
|
1113
|
+
id
|
|
1114
|
+
status
|
|
1115
|
+
discount
|
|
1116
|
+
tax
|
|
1117
|
+
total_without_tax
|
|
1118
|
+
total_with_tax
|
|
1119
|
+
total_with_discount
|
|
1120
|
+
created_at
|
|
1121
|
+
updated_at
|
|
1122
|
+
items {
|
|
1123
|
+
id
|
|
1124
|
+
product_variant_id
|
|
1125
|
+
quantity
|
|
1126
|
+
product_variant {
|
|
1127
|
+
id
|
|
1128
|
+
sku
|
|
1129
|
+
product {
|
|
1130
|
+
id
|
|
1131
|
+
name
|
|
1132
|
+
}
|
|
1133
|
+
prices {
|
|
1134
|
+
id
|
|
1135
|
+
price
|
|
1136
|
+
price_type
|
|
1137
|
+
chain_id
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
errors
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
`
|
|
1146
|
+
);
|
|
1147
|
+
var SET_CART_ITEM = (
|
|
1148
|
+
/* GraphQL */
|
|
1149
|
+
`
|
|
1150
|
+
mutation SetCartItem(
|
|
1151
|
+
$orderId: uuid!
|
|
1152
|
+
$cartToken: String!
|
|
1153
|
+
$variantId: uuid!
|
|
1154
|
+
$quantity: numeric!
|
|
1155
|
+
) {
|
|
1156
|
+
websiteSetCartItem(
|
|
1157
|
+
order_id: $orderId
|
|
1158
|
+
cart_token: $cartToken
|
|
1159
|
+
product_variant_id: $variantId
|
|
1160
|
+
quantity: $quantity
|
|
1161
|
+
) {
|
|
1162
|
+
success
|
|
1163
|
+
order_id
|
|
1164
|
+
cart_token
|
|
1165
|
+
cart {
|
|
1166
|
+
id
|
|
1167
|
+
status
|
|
1168
|
+
discount
|
|
1169
|
+
tax
|
|
1170
|
+
total_without_tax
|
|
1171
|
+
total_with_tax
|
|
1172
|
+
total_with_discount
|
|
1173
|
+
created_at
|
|
1174
|
+
updated_at
|
|
1175
|
+
items {
|
|
1176
|
+
id
|
|
1177
|
+
product_variant_id
|
|
1178
|
+
quantity
|
|
1179
|
+
product_variant {
|
|
1180
|
+
id
|
|
1181
|
+
sku
|
|
1182
|
+
product {
|
|
1183
|
+
id
|
|
1184
|
+
name
|
|
1185
|
+
}
|
|
1186
|
+
prices {
|
|
1187
|
+
id
|
|
1188
|
+
price
|
|
1189
|
+
price_type
|
|
1190
|
+
chain_id
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
errors
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
`
|
|
1199
|
+
);
|
|
1200
|
+
var REMOVE_CART_ITEM = (
|
|
1201
|
+
/* GraphQL */
|
|
1202
|
+
`
|
|
1203
|
+
mutation RemoveCartItem(
|
|
1204
|
+
$orderId: uuid!
|
|
1205
|
+
$cartToken: String!
|
|
1206
|
+
$itemId: uuid!
|
|
1207
|
+
) {
|
|
1208
|
+
websiteRemoveCartItem(
|
|
1209
|
+
order_id: $orderId
|
|
1210
|
+
cart_token: $cartToken
|
|
1211
|
+
item_id: $itemId
|
|
1212
|
+
) {
|
|
1213
|
+
success
|
|
1214
|
+
order_id
|
|
1215
|
+
cart_token
|
|
1216
|
+
cart {
|
|
1217
|
+
id
|
|
1218
|
+
status
|
|
1219
|
+
discount
|
|
1220
|
+
tax
|
|
1221
|
+
total_without_tax
|
|
1222
|
+
total_with_tax
|
|
1223
|
+
total_with_discount
|
|
1224
|
+
created_at
|
|
1225
|
+
updated_at
|
|
1226
|
+
items {
|
|
1227
|
+
id
|
|
1228
|
+
product_variant_id
|
|
1229
|
+
quantity
|
|
1230
|
+
product_variant {
|
|
1231
|
+
id
|
|
1232
|
+
sku
|
|
1233
|
+
product {
|
|
1234
|
+
id
|
|
1235
|
+
name
|
|
1236
|
+
}
|
|
1237
|
+
prices {
|
|
1238
|
+
id
|
|
1239
|
+
price
|
|
1240
|
+
price_type
|
|
1241
|
+
chain_id
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
errors
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
`
|
|
1250
|
+
);
|
|
1251
|
+
var CHECKOUT = (
|
|
1252
|
+
/* GraphQL */
|
|
1253
|
+
`
|
|
1254
|
+
mutation Checkout(
|
|
1255
|
+
$orderId: uuid!
|
|
1256
|
+
$cartToken: String!
|
|
1257
|
+
$contact: WebsiteGuestContactInput!
|
|
1258
|
+
$fulfillmentFamily: String
|
|
1259
|
+
$address: WebsiteAddressInput
|
|
1260
|
+
$shippingQuoteId: uuid
|
|
1261
|
+
$paymentMode: String
|
|
1262
|
+
$paymentMethodId: uuid
|
|
1263
|
+
$buyerAccountOptIn: Boolean
|
|
1264
|
+
$initialPayment: WebsiteInitialPaymentInput
|
|
1265
|
+
) {
|
|
1266
|
+
websiteCheckout(
|
|
1267
|
+
order_id: $orderId
|
|
1268
|
+
cart_token: $cartToken
|
|
1269
|
+
contact: $contact
|
|
1270
|
+
fulfillment_family: $fulfillmentFamily
|
|
1271
|
+
address: $address
|
|
1272
|
+
shipping_quote_id: $shippingQuoteId
|
|
1273
|
+
payment_mode: $paymentMode
|
|
1274
|
+
payment_method_id: $paymentMethodId
|
|
1275
|
+
buyer_account_opt_in: $buyerAccountOptIn
|
|
1276
|
+
initial_payment: $initialPayment
|
|
1277
|
+
) {
|
|
1278
|
+
success
|
|
1279
|
+
order_id
|
|
1280
|
+
order_number
|
|
1281
|
+
payment_method_id
|
|
1282
|
+
public_order_token
|
|
1283
|
+
requires_payment_session
|
|
1284
|
+
buyer_account_invitation_requested
|
|
1285
|
+
errors
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
`
|
|
1289
|
+
);
|
|
1290
|
+
var GET_LOCATION_OPTIONS = (
|
|
1291
|
+
/* GraphQL */
|
|
1292
|
+
`
|
|
1293
|
+
mutation GetLocationOptions($websiteId: uuid!) {
|
|
1294
|
+
websiteGetLocationOptions(website_id: $websiteId) {
|
|
1295
|
+
success
|
|
1296
|
+
country_code
|
|
1297
|
+
departments {
|
|
1298
|
+
department_id
|
|
1299
|
+
department_name
|
|
1300
|
+
department_code
|
|
1301
|
+
municipalities {
|
|
1302
|
+
municipality_id
|
|
1303
|
+
municipality_name
|
|
1304
|
+
municipality_code
|
|
1305
|
+
department_id
|
|
1306
|
+
department_code
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
errors
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
`
|
|
1313
|
+
);
|
|
1314
|
+
var GET_DELIVERY_OPTIONS = (
|
|
1315
|
+
/* GraphQL */
|
|
1316
|
+
`
|
|
1317
|
+
mutation GetDeliveryOptions($websiteId: uuid!) {
|
|
1318
|
+
websiteGetDeliveryOptions(website_id: $websiteId) {
|
|
1319
|
+
success
|
|
1320
|
+
delivery_options {
|
|
1321
|
+
id
|
|
1322
|
+
fulfillment_family
|
|
1323
|
+
label
|
|
1324
|
+
description
|
|
1325
|
+
is_enabled
|
|
1326
|
+
pickup_info {
|
|
1327
|
+
label
|
|
1328
|
+
address
|
|
1329
|
+
schedule
|
|
1330
|
+
notes
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
errors
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
`
|
|
1337
|
+
);
|
|
1338
|
+
var GET_SHIPPING_QUOTES = (
|
|
1339
|
+
/* GraphQL */
|
|
1340
|
+
`
|
|
1341
|
+
mutation GetShippingQuotes(
|
|
1342
|
+
$websiteId: uuid!
|
|
1343
|
+
$orderId: uuid
|
|
1344
|
+
$cartToken: String
|
|
1345
|
+
$items: [WebsiteCheckoutItemInput!]
|
|
1346
|
+
$address: WebsiteAddressInput!
|
|
1347
|
+
) {
|
|
1348
|
+
websiteGetShippingQuotes(
|
|
1349
|
+
website_id: $websiteId
|
|
1350
|
+
order_id: $orderId
|
|
1351
|
+
cart_token: $cartToken
|
|
1352
|
+
items: $items
|
|
1353
|
+
address: $address
|
|
1354
|
+
) {
|
|
1355
|
+
success
|
|
1356
|
+
quotes {
|
|
1357
|
+
quote_id
|
|
1358
|
+
shipping_service_id
|
|
1359
|
+
label
|
|
1360
|
+
service_name
|
|
1361
|
+
base_amount
|
|
1362
|
+
amount
|
|
1363
|
+
currency
|
|
1364
|
+
collection_mode
|
|
1365
|
+
total_policy
|
|
1366
|
+
estimated_delivery
|
|
1367
|
+
free_shipping_eligible
|
|
1368
|
+
free_shipping_applied
|
|
1369
|
+
free_shipping_threshold
|
|
1370
|
+
free_shipping_remaining
|
|
1371
|
+
free_shipping_eligible_subtotal
|
|
1372
|
+
}
|
|
1373
|
+
errors
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
`
|
|
1377
|
+
);
|
|
1378
|
+
var GET_SHIPPING_POLICY = (
|
|
1379
|
+
/* GraphQL */
|
|
1380
|
+
`
|
|
1381
|
+
mutation GetShippingPolicy($websiteId: uuid!) {
|
|
1382
|
+
websiteGetShippingPolicy(website_id: $websiteId) {
|
|
1383
|
+
success
|
|
1384
|
+
policy {
|
|
1385
|
+
free_shipping_enabled
|
|
1386
|
+
free_shipping_threshold
|
|
1387
|
+
currency
|
|
1388
|
+
}
|
|
1389
|
+
errors
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
`
|
|
1393
|
+
);
|
|
1394
|
+
var GET_FREE_SHIPPING_PROGRESS = (
|
|
1395
|
+
/* GraphQL */
|
|
1396
|
+
`
|
|
1397
|
+
mutation GetFreeShippingProgress(
|
|
1398
|
+
$websiteId: uuid!
|
|
1399
|
+
$items: [WebsiteCheckoutItemInput!]!
|
|
1400
|
+
) {
|
|
1401
|
+
websiteGetFreeShippingProgress(website_id: $websiteId, items: $items) {
|
|
1402
|
+
success
|
|
1403
|
+
progress {
|
|
1404
|
+
enabled
|
|
1405
|
+
threshold
|
|
1406
|
+
remaining
|
|
1407
|
+
eligible_subtotal
|
|
1408
|
+
threshold_reached
|
|
1409
|
+
currency
|
|
1410
|
+
}
|
|
1411
|
+
errors
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
`
|
|
1415
|
+
);
|
|
1416
|
+
var GET_PAYMENT_METHODS = (
|
|
1417
|
+
/* GraphQL */
|
|
1418
|
+
`
|
|
1419
|
+
mutation GetPaymentMethods($websiteId: uuid!) {
|
|
1420
|
+
websiteGetPaymentMethods(website_id: $websiteId) {
|
|
1421
|
+
success
|
|
1422
|
+
payment_methods {
|
|
1423
|
+
id
|
|
1424
|
+
code
|
|
1425
|
+
label
|
|
1426
|
+
payment_method_type
|
|
1427
|
+
gateway_code
|
|
1428
|
+
description
|
|
1429
|
+
is_enabled
|
|
1430
|
+
sort_order
|
|
1431
|
+
requires_payment_session
|
|
1432
|
+
public_config
|
|
1433
|
+
}
|
|
1434
|
+
errors
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
`
|
|
1438
|
+
);
|
|
1439
|
+
var GET_PAYMENT_OPTIONS = (
|
|
1440
|
+
/* GraphQL */
|
|
1441
|
+
`
|
|
1442
|
+
mutation GetPaymentOptions($websiteId: uuid!, $fulfillmentFamily: String!) {
|
|
1443
|
+
websiteGetPaymentOptions(
|
|
1444
|
+
website_id: $websiteId
|
|
1445
|
+
fulfillment_family: $fulfillmentFamily
|
|
1446
|
+
) {
|
|
1447
|
+
success
|
|
1448
|
+
payment_options {
|
|
1449
|
+
id
|
|
1450
|
+
mode
|
|
1451
|
+
fulfillment_family
|
|
1452
|
+
label
|
|
1453
|
+
description
|
|
1454
|
+
requires_payment_method
|
|
1455
|
+
allows_initial_payment
|
|
1456
|
+
initial_payment_policy
|
|
1457
|
+
requires_initial_payment
|
|
1458
|
+
min_initial_amount
|
|
1459
|
+
suggested_initial_amount
|
|
1460
|
+
sort_order
|
|
1461
|
+
methods {
|
|
1462
|
+
id
|
|
1463
|
+
payment_method_id
|
|
1464
|
+
code
|
|
1465
|
+
label
|
|
1466
|
+
payment_method_type
|
|
1467
|
+
gateway_code
|
|
1468
|
+
description
|
|
1469
|
+
instructions
|
|
1470
|
+
qr_file_id
|
|
1471
|
+
requires_payment_session
|
|
1472
|
+
public_config
|
|
1473
|
+
sort_order
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
errors
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
`
|
|
1480
|
+
);
|
|
1481
|
+
var VALIDATE_COUPON = (
|
|
1482
|
+
/* GraphQL */
|
|
1483
|
+
`
|
|
1484
|
+
mutation ValidateCoupon(
|
|
1485
|
+
$orderId: uuid!
|
|
1486
|
+
$cartToken: String!
|
|
1487
|
+
$code: String!
|
|
1488
|
+
) {
|
|
1489
|
+
websiteValidateCoupon(
|
|
1490
|
+
order_id: $orderId
|
|
1491
|
+
cart_token: $cartToken
|
|
1492
|
+
code: $code
|
|
1493
|
+
) {
|
|
1494
|
+
success
|
|
1495
|
+
order_id
|
|
1496
|
+
coupon_id
|
|
1497
|
+
code
|
|
1498
|
+
name
|
|
1499
|
+
coupon_type
|
|
1500
|
+
discount_amount
|
|
1501
|
+
eligible_subtotal
|
|
1502
|
+
message
|
|
1503
|
+
cart {
|
|
1504
|
+
id
|
|
1505
|
+
status
|
|
1506
|
+
discount
|
|
1507
|
+
total_without_tax
|
|
1508
|
+
tax
|
|
1509
|
+
total_with_tax
|
|
1510
|
+
total_with_discount
|
|
1511
|
+
created_at
|
|
1512
|
+
updated_at
|
|
1513
|
+
items {
|
|
1514
|
+
id
|
|
1515
|
+
product_variant_id
|
|
1516
|
+
quantity
|
|
1517
|
+
product_variant {
|
|
1518
|
+
id
|
|
1519
|
+
sku
|
|
1520
|
+
product {
|
|
1521
|
+
id
|
|
1522
|
+
name
|
|
1523
|
+
}
|
|
1524
|
+
prices {
|
|
1525
|
+
id
|
|
1526
|
+
price
|
|
1527
|
+
price_type
|
|
1528
|
+
chain_id
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
errors
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
`
|
|
1537
|
+
);
|
|
1538
|
+
var APPLY_COUPON = (
|
|
1539
|
+
/* GraphQL */
|
|
1540
|
+
`
|
|
1541
|
+
mutation ApplyCoupon($orderId: uuid!, $cartToken: String!, $code: String!) {
|
|
1542
|
+
websiteApplyCoupon(
|
|
1543
|
+
order_id: $orderId
|
|
1544
|
+
cart_token: $cartToken
|
|
1545
|
+
code: $code
|
|
1546
|
+
) {
|
|
1547
|
+
success
|
|
1548
|
+
order_id
|
|
1549
|
+
coupon_id
|
|
1550
|
+
code
|
|
1551
|
+
name
|
|
1552
|
+
coupon_type
|
|
1553
|
+
discount_amount
|
|
1554
|
+
eligible_subtotal
|
|
1555
|
+
message
|
|
1556
|
+
cart {
|
|
1557
|
+
id
|
|
1558
|
+
status
|
|
1559
|
+
discount
|
|
1560
|
+
total_without_tax
|
|
1561
|
+
tax
|
|
1562
|
+
total_with_tax
|
|
1563
|
+
total_with_discount
|
|
1564
|
+
created_at
|
|
1565
|
+
updated_at
|
|
1566
|
+
items {
|
|
1567
|
+
id
|
|
1568
|
+
product_variant_id
|
|
1569
|
+
quantity
|
|
1570
|
+
product_variant {
|
|
1571
|
+
id
|
|
1572
|
+
sku
|
|
1573
|
+
product {
|
|
1574
|
+
id
|
|
1575
|
+
name
|
|
1576
|
+
}
|
|
1577
|
+
prices {
|
|
1578
|
+
id
|
|
1579
|
+
price
|
|
1580
|
+
price_type
|
|
1581
|
+
chain_id
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
errors
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
`
|
|
1590
|
+
);
|
|
1591
|
+
var REMOVE_COUPON = (
|
|
1592
|
+
/* GraphQL */
|
|
1593
|
+
`
|
|
1594
|
+
mutation RemoveCoupon(
|
|
1595
|
+
$orderId: uuid!
|
|
1596
|
+
$cartToken: String!
|
|
1597
|
+
$couponId: uuid!
|
|
1598
|
+
) {
|
|
1599
|
+
websiteRemoveCoupon(
|
|
1600
|
+
order_id: $orderId
|
|
1601
|
+
cart_token: $cartToken
|
|
1602
|
+
coupon_id: $couponId
|
|
1603
|
+
) {
|
|
1604
|
+
success
|
|
1605
|
+
order_id
|
|
1606
|
+
coupon_id
|
|
1607
|
+
code
|
|
1608
|
+
name
|
|
1609
|
+
coupon_type
|
|
1610
|
+
discount_amount
|
|
1611
|
+
eligible_subtotal
|
|
1612
|
+
message
|
|
1613
|
+
cart {
|
|
1614
|
+
id
|
|
1615
|
+
status
|
|
1616
|
+
discount
|
|
1617
|
+
total_without_tax
|
|
1618
|
+
tax
|
|
1619
|
+
total_with_tax
|
|
1620
|
+
total_with_discount
|
|
1621
|
+
created_at
|
|
1622
|
+
updated_at
|
|
1623
|
+
items {
|
|
1624
|
+
id
|
|
1625
|
+
product_variant_id
|
|
1626
|
+
quantity
|
|
1627
|
+
product_variant {
|
|
1628
|
+
id
|
|
1629
|
+
sku
|
|
1630
|
+
product {
|
|
1631
|
+
id
|
|
1632
|
+
name
|
|
1633
|
+
}
|
|
1634
|
+
prices {
|
|
1635
|
+
id
|
|
1636
|
+
price
|
|
1637
|
+
price_type
|
|
1638
|
+
chain_id
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
errors
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
`
|
|
1647
|
+
);
|
|
1648
|
+
var CREATE_PAYMENT_SESSION = (
|
|
1649
|
+
/* GraphQL */
|
|
1650
|
+
`
|
|
1651
|
+
mutation CreatePaymentSession(
|
|
1652
|
+
$orderId: uuid!
|
|
1653
|
+
$cartToken: String
|
|
1654
|
+
$publicOrderToken: String
|
|
1655
|
+
$paymentMethodId: uuid!
|
|
1656
|
+
$returnUrl: String!
|
|
1657
|
+
) {
|
|
1658
|
+
websiteCreatePaymentSession(
|
|
1659
|
+
order_id: $orderId
|
|
1660
|
+
cart_token: $cartToken
|
|
1661
|
+
public_order_token: $publicOrderToken
|
|
1662
|
+
payment_method_id: $paymentMethodId
|
|
1663
|
+
return_url: $returnUrl
|
|
1664
|
+
) {
|
|
1665
|
+
success
|
|
1666
|
+
order_id
|
|
1667
|
+
payment_method_id
|
|
1668
|
+
payment_session_id
|
|
1669
|
+
gateway_code
|
|
1670
|
+
integration_mode
|
|
1671
|
+
status
|
|
1672
|
+
redirect_url
|
|
1673
|
+
public_payload
|
|
1674
|
+
errors
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
`
|
|
1678
|
+
);
|
|
1679
|
+
var RECONCILE_PAYMENT_SESSION = (
|
|
1680
|
+
/* GraphQL */
|
|
1681
|
+
`
|
|
1682
|
+
mutation ReconcilePaymentSession(
|
|
1683
|
+
$orderId: uuid!
|
|
1684
|
+
$publicOrderToken: String!
|
|
1685
|
+
$paymentSessionId: uuid!
|
|
1686
|
+
$gatewayTransactionId: String!
|
|
1687
|
+
) {
|
|
1688
|
+
websiteReconcilePaymentSession(
|
|
1689
|
+
order_id: $orderId
|
|
1690
|
+
public_order_token: $publicOrderToken
|
|
1691
|
+
payment_session_id: $paymentSessionId
|
|
1692
|
+
gateway_transaction_id: $gatewayTransactionId
|
|
1693
|
+
) {
|
|
1694
|
+
success
|
|
1695
|
+
order_id
|
|
1696
|
+
payment_method_id
|
|
1697
|
+
payment_session_id
|
|
1698
|
+
gateway_code
|
|
1699
|
+
integration_mode
|
|
1700
|
+
status
|
|
1701
|
+
redirect_url
|
|
1702
|
+
public_payload
|
|
1703
|
+
errors
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
`
|
|
1707
|
+
);
|
|
1708
|
+
var GET_ORDERS = (
|
|
1709
|
+
/* GraphQL */
|
|
1710
|
+
`
|
|
1711
|
+
query GetOrders($limit: Int = 20, $offset: Int = 0) {
|
|
1712
|
+
orders(
|
|
1713
|
+
where: { order_number: { _is_null: false } }
|
|
1714
|
+
order_by: { created_at: desc }
|
|
1715
|
+
limit: $limit
|
|
1716
|
+
offset: $offset
|
|
1717
|
+
) {
|
|
1718
|
+
id
|
|
1719
|
+
order_number
|
|
1720
|
+
status
|
|
1721
|
+
fulfillment_family
|
|
1722
|
+
discount
|
|
1723
|
+
tax
|
|
1724
|
+
total_without_tax
|
|
1725
|
+
total_with_tax
|
|
1726
|
+
total_with_discount
|
|
1727
|
+
created_at
|
|
1728
|
+
updated_at
|
|
1729
|
+
order_product_variants {
|
|
1730
|
+
id
|
|
1731
|
+
product_variant_id
|
|
1732
|
+
quantity
|
|
1733
|
+
product_variant {
|
|
1734
|
+
sku
|
|
1735
|
+
product {
|
|
1736
|
+
id
|
|
1737
|
+
name
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
`
|
|
1744
|
+
);
|
|
1745
|
+
var GET_ORDER_BY_PK = (
|
|
1746
|
+
/* GraphQL */
|
|
1747
|
+
`
|
|
1748
|
+
query GetOrderByPk($orderId: uuid!) {
|
|
1749
|
+
orders(where: { id: { _eq: $orderId } }) {
|
|
1750
|
+
id
|
|
1751
|
+
order_number
|
|
1752
|
+
status
|
|
1753
|
+
fulfillment_family
|
|
1754
|
+
discount
|
|
1755
|
+
tax
|
|
1756
|
+
total_without_tax
|
|
1757
|
+
total_with_tax
|
|
1758
|
+
total_with_discount
|
|
1759
|
+
created_at
|
|
1760
|
+
updated_at
|
|
1761
|
+
order_product_variants {
|
|
1762
|
+
id
|
|
1763
|
+
product_variant_id
|
|
1764
|
+
quantity
|
|
1765
|
+
product_variant {
|
|
1766
|
+
sku
|
|
1767
|
+
product {
|
|
1768
|
+
id
|
|
1769
|
+
name
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
`
|
|
1776
|
+
);
|
|
1777
|
+
var GET_PUBLIC_ORDER = (
|
|
1778
|
+
/* GraphQL */
|
|
1779
|
+
`
|
|
1780
|
+
mutation GetPublicOrder($websiteId: uuid!, $orderId: uuid!, $token: String!) {
|
|
1781
|
+
websiteGetPublicOrder(
|
|
1782
|
+
website_id: $websiteId
|
|
1783
|
+
order_id: $orderId
|
|
1784
|
+
token: $token
|
|
1785
|
+
) {
|
|
1786
|
+
success
|
|
1787
|
+
order {
|
|
1788
|
+
order_id
|
|
1789
|
+
order_number
|
|
1790
|
+
status
|
|
1791
|
+
payment_status
|
|
1792
|
+
fulfillment_family
|
|
1793
|
+
delivery {
|
|
1794
|
+
fulfillment_family
|
|
1795
|
+
shipping {
|
|
1796
|
+
label
|
|
1797
|
+
amount
|
|
1798
|
+
method_type
|
|
1799
|
+
collection_mode
|
|
1800
|
+
total_policy
|
|
1801
|
+
address
|
|
1802
|
+
}
|
|
1803
|
+
pickup {
|
|
1804
|
+
label
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
payment {
|
|
1808
|
+
mode
|
|
1809
|
+
label
|
|
1810
|
+
method {
|
|
1811
|
+
id
|
|
1812
|
+
label
|
|
1813
|
+
payment_method_type
|
|
1814
|
+
gateway_code
|
|
1815
|
+
requires_payment_session
|
|
1816
|
+
}
|
|
1817
|
+
initial_payment {
|
|
1818
|
+
amount
|
|
1819
|
+
payment_method_id
|
|
1820
|
+
method_label
|
|
1821
|
+
payment_method_type
|
|
1822
|
+
gateway_code
|
|
1823
|
+
requires_payment_session
|
|
1824
|
+
}
|
|
1825
|
+
requires_payment_session
|
|
1826
|
+
next_action
|
|
1827
|
+
session {
|
|
1828
|
+
id
|
|
1829
|
+
gateway_code
|
|
1830
|
+
integration_mode
|
|
1831
|
+
status
|
|
1832
|
+
reference
|
|
1833
|
+
amount
|
|
1834
|
+
currency
|
|
1835
|
+
gateway_transaction_id
|
|
1836
|
+
error_message
|
|
1837
|
+
expires_at
|
|
1838
|
+
created_at
|
|
1839
|
+
updated_at
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
totals {
|
|
1843
|
+
subtotal
|
|
1844
|
+
tax
|
|
1845
|
+
discount
|
|
1846
|
+
total
|
|
1847
|
+
total_with_discount
|
|
1848
|
+
}
|
|
1849
|
+
items {
|
|
1850
|
+
id
|
|
1851
|
+
product_variant_id
|
|
1852
|
+
sku
|
|
1853
|
+
name
|
|
1854
|
+
image_file_id
|
|
1855
|
+
quantity
|
|
1856
|
+
unit_price
|
|
1857
|
+
line_total
|
|
1858
|
+
}
|
|
1859
|
+
next_steps
|
|
1860
|
+
created_at
|
|
1861
|
+
updated_at
|
|
1862
|
+
}
|
|
1863
|
+
errors
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
`
|
|
1867
|
+
);
|
|
1868
|
+
var GET_PROFILE = (
|
|
1869
|
+
/* GraphQL */
|
|
1870
|
+
`
|
|
1871
|
+
query GetProfile {
|
|
1872
|
+
clients {
|
|
1873
|
+
id
|
|
1874
|
+
party_id
|
|
1875
|
+
party {
|
|
1876
|
+
first_name
|
|
1877
|
+
last_name
|
|
1878
|
+
email
|
|
1879
|
+
phone_e164
|
|
1880
|
+
phone_national_number
|
|
1881
|
+
address
|
|
1882
|
+
address_complement
|
|
1883
|
+
birthdate
|
|
1884
|
+
legal_name
|
|
1885
|
+
commercial_name
|
|
1886
|
+
country_code
|
|
1887
|
+
department_code
|
|
1888
|
+
municipality_code
|
|
1889
|
+
geo_country {
|
|
1890
|
+
name
|
|
1891
|
+
}
|
|
1892
|
+
geo_department {
|
|
1893
|
+
name
|
|
1894
|
+
}
|
|
1895
|
+
geo_municipality {
|
|
1896
|
+
name
|
|
1897
|
+
}
|
|
1898
|
+
shipping_addresses(
|
|
1899
|
+
where: { is_primary: { _eq: true } }
|
|
1900
|
+
order_by: { created_at: asc }
|
|
1901
|
+
limit: 1
|
|
1902
|
+
) {
|
|
1903
|
+
address
|
|
1904
|
+
address_complement
|
|
1905
|
+
country_code
|
|
1906
|
+
department_code
|
|
1907
|
+
municipality_code
|
|
1908
|
+
geo_country {
|
|
1909
|
+
name
|
|
1910
|
+
}
|
|
1911
|
+
geo_department {
|
|
1912
|
+
name
|
|
1913
|
+
}
|
|
1914
|
+
geo_municipality {
|
|
1915
|
+
name
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
`
|
|
1922
|
+
);
|
|
1923
|
+
var GET_PROFILE_ADDRESSES = (
|
|
1924
|
+
/* GraphQL */
|
|
1925
|
+
`
|
|
1926
|
+
query GetProfileAddresses {
|
|
1927
|
+
clients {
|
|
1928
|
+
party_id
|
|
1929
|
+
party {
|
|
1930
|
+
first_name
|
|
1931
|
+
last_name
|
|
1932
|
+
display_name
|
|
1933
|
+
phone_e164
|
|
1934
|
+
phone_national_number
|
|
1935
|
+
address
|
|
1936
|
+
address_complement
|
|
1937
|
+
country_code
|
|
1938
|
+
department_code
|
|
1939
|
+
municipality_code
|
|
1940
|
+
geo_country {
|
|
1941
|
+
name
|
|
1942
|
+
}
|
|
1943
|
+
geo_department {
|
|
1944
|
+
name
|
|
1945
|
+
}
|
|
1946
|
+
geo_municipality {
|
|
1947
|
+
name
|
|
1948
|
+
}
|
|
1949
|
+
shipping_addresses(
|
|
1950
|
+
order_by: [{ is_primary: desc }, { created_at: asc }]
|
|
1951
|
+
) {
|
|
1952
|
+
id
|
|
1953
|
+
label
|
|
1954
|
+
recipient_name
|
|
1955
|
+
phone_e164
|
|
1956
|
+
address
|
|
1957
|
+
address_complement
|
|
1958
|
+
notes
|
|
1959
|
+
is_primary
|
|
1960
|
+
country_code
|
|
1961
|
+
department_code
|
|
1962
|
+
municipality_code
|
|
1963
|
+
geo_country {
|
|
1964
|
+
name
|
|
1965
|
+
}
|
|
1966
|
+
geo_department {
|
|
1967
|
+
name
|
|
1968
|
+
}
|
|
1969
|
+
geo_municipality {
|
|
1970
|
+
name
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
`
|
|
1977
|
+
);
|
|
1978
|
+
var UPDATE_PROFILE = (
|
|
1979
|
+
/* GraphQL */
|
|
1980
|
+
`
|
|
1981
|
+
mutation UpdateProfile($partyId: uuid!, $data: business_parties_set_input!) {
|
|
1982
|
+
update_business_parties_by_pk(pk_columns: { id: $partyId }, _set: $data) {
|
|
1983
|
+
email
|
|
1984
|
+
first_name
|
|
1985
|
+
last_name
|
|
1986
|
+
phone_e164
|
|
1987
|
+
phone_national_number
|
|
1988
|
+
address
|
|
1989
|
+
address_complement
|
|
1990
|
+
birthdate
|
|
1991
|
+
legal_name
|
|
1992
|
+
commercial_name
|
|
1993
|
+
country_code
|
|
1994
|
+
department_code
|
|
1995
|
+
municipality_code
|
|
1996
|
+
geo_country {
|
|
1997
|
+
name
|
|
1998
|
+
}
|
|
1999
|
+
geo_department {
|
|
2000
|
+
name
|
|
2001
|
+
}
|
|
2002
|
+
geo_municipality {
|
|
2003
|
+
name
|
|
2004
|
+
}
|
|
2005
|
+
shipping_addresses(
|
|
2006
|
+
where: { is_primary: { _eq: true } }
|
|
2007
|
+
order_by: { created_at: asc }
|
|
2008
|
+
limit: 1
|
|
2009
|
+
) {
|
|
2010
|
+
address
|
|
2011
|
+
address_complement
|
|
2012
|
+
country_code
|
|
2013
|
+
department_code
|
|
2014
|
+
municipality_code
|
|
2015
|
+
geo_country {
|
|
2016
|
+
name
|
|
2017
|
+
}
|
|
2018
|
+
geo_department {
|
|
2019
|
+
name
|
|
2020
|
+
}
|
|
2021
|
+
geo_municipality {
|
|
2022
|
+
name
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
`
|
|
2028
|
+
);
|
|
2029
|
+
var GET_PRODUCT_FILTERS = (
|
|
2030
|
+
/* GraphQL */
|
|
2031
|
+
`
|
|
2032
|
+
query GetProductFilters(
|
|
2033
|
+
$companyId: uuid!
|
|
2034
|
+
$chainId: uuid!
|
|
2035
|
+
$where: products_bool_exp = {}
|
|
2036
|
+
$attributeListKey: String!
|
|
2037
|
+
) {
|
|
2038
|
+
products(
|
|
2039
|
+
where: {
|
|
2040
|
+
_and: [
|
|
2041
|
+
{ company_id: { _eq: $companyId } }
|
|
2042
|
+
{
|
|
2043
|
+
chain_products: {
|
|
2044
|
+
chain_id: { _eq: $chainId }
|
|
2045
|
+
is_enabled: { _eq: true }
|
|
2046
|
+
status: { _eq: active }
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
$where
|
|
2050
|
+
]
|
|
2051
|
+
}
|
|
2052
|
+
) {
|
|
2053
|
+
id
|
|
2054
|
+
category
|
|
2055
|
+
subcategory
|
|
2056
|
+
color
|
|
2057
|
+
brand {
|
|
2058
|
+
id
|
|
2059
|
+
name
|
|
2060
|
+
}
|
|
2061
|
+
product_attributes {
|
|
2062
|
+
id
|
|
2063
|
+
value
|
|
2064
|
+
attribute_value {
|
|
2065
|
+
id
|
|
2066
|
+
code
|
|
2067
|
+
sort_order
|
|
2068
|
+
metadata
|
|
2069
|
+
}
|
|
2070
|
+
attribute {
|
|
2071
|
+
id
|
|
2072
|
+
name
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
product_variants(where: { is_disabled: { _eq: false } }) {
|
|
2076
|
+
id
|
|
2077
|
+
product_variant_prices(
|
|
2078
|
+
where: {
|
|
2079
|
+
price_type: { _in: [CLIENT, SALE, WHOLESALE, WHOLESALE_2] }
|
|
2080
|
+
_or: [
|
|
2081
|
+
{ chain_id: { _eq: $chainId } }
|
|
2082
|
+
{ chain_id: { _is_null: true } }
|
|
2083
|
+
]
|
|
2084
|
+
}
|
|
2085
|
+
) {
|
|
2086
|
+
id
|
|
2087
|
+
price
|
|
2088
|
+
price_type
|
|
2089
|
+
chain_id
|
|
2090
|
+
}
|
|
2091
|
+
product_variant_attributes(order_by: { order: asc }) {
|
|
2092
|
+
id
|
|
2093
|
+
value
|
|
2094
|
+
order
|
|
2095
|
+
attribute_value {
|
|
2096
|
+
id
|
|
2097
|
+
code
|
|
2098
|
+
sort_order
|
|
2099
|
+
metadata
|
|
2100
|
+
}
|
|
2101
|
+
attribute {
|
|
2102
|
+
id
|
|
2103
|
+
name
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
catalog_attribute_lists(
|
|
2109
|
+
where: {
|
|
2110
|
+
company_id: { _eq: $companyId }
|
|
2111
|
+
key: { _eq: $attributeListKey }
|
|
2112
|
+
status: { _eq: published }
|
|
2113
|
+
}
|
|
2114
|
+
limit: 1
|
|
2115
|
+
) {
|
|
2116
|
+
id
|
|
2117
|
+
key
|
|
2118
|
+
name
|
|
2119
|
+
description
|
|
2120
|
+
status
|
|
2121
|
+
items(
|
|
2122
|
+
where: { is_visible: { _eq: true } }
|
|
2123
|
+
order_by: [{ sort_order: asc }, { public_label: asc }]
|
|
2124
|
+
) {
|
|
2125
|
+
id
|
|
2126
|
+
attribute_id
|
|
2127
|
+
public_label
|
|
2128
|
+
public_slug
|
|
2129
|
+
sort_order
|
|
2130
|
+
attribute {
|
|
2131
|
+
id
|
|
2132
|
+
name
|
|
2133
|
+
type
|
|
2134
|
+
}
|
|
2135
|
+
values(
|
|
2136
|
+
where: { is_visible: { _eq: true } }
|
|
2137
|
+
order_by: [{ sort_order: asc }, { public_label: asc }]
|
|
2138
|
+
) {
|
|
2139
|
+
id
|
|
2140
|
+
attribute_value_id
|
|
2141
|
+
public_label
|
|
2142
|
+
public_slug
|
|
2143
|
+
sort_order
|
|
2144
|
+
attribute_value {
|
|
2145
|
+
id
|
|
2146
|
+
code
|
|
2147
|
+
metadata
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
`
|
|
2154
|
+
);
|
|
2155
|
+
var GET_SEARCH_SUGGESTIONS = (
|
|
2156
|
+
/* GraphQL */
|
|
2157
|
+
`
|
|
2158
|
+
query GetSearchSuggestions(
|
|
2159
|
+
$companyId: uuid!
|
|
2160
|
+
$chainId: uuid!
|
|
2161
|
+
$term: String!
|
|
2162
|
+
$productLimit: Int = 5
|
|
2163
|
+
$brandLimit: Int = 3
|
|
2164
|
+
) {
|
|
2165
|
+
products(
|
|
2166
|
+
where: {
|
|
2167
|
+
company_id: { _eq: $companyId }
|
|
2168
|
+
chain_products: {
|
|
2169
|
+
chain_id: { _eq: $chainId }
|
|
2170
|
+
is_enabled: { _eq: true }
|
|
2171
|
+
status: { _eq: active }
|
|
2172
|
+
}
|
|
2173
|
+
name: { _ilike: $term }
|
|
2174
|
+
}
|
|
2175
|
+
limit: $productLimit
|
|
2176
|
+
order_by: { name: asc }
|
|
2177
|
+
) {
|
|
2178
|
+
id
|
|
2179
|
+
name
|
|
2180
|
+
}
|
|
2181
|
+
brands(
|
|
2182
|
+
where: {
|
|
2183
|
+
company_id: { _eq: $companyId }
|
|
2184
|
+
name: { _ilike: $term }
|
|
2185
|
+
products: {
|
|
2186
|
+
chain_products: {
|
|
2187
|
+
chain_id: { _eq: $chainId }
|
|
2188
|
+
is_enabled: { _eq: true }
|
|
2189
|
+
status: { _eq: active }
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
limit: $brandLimit
|
|
2194
|
+
order_by: { name: asc }
|
|
2195
|
+
) {
|
|
2196
|
+
id
|
|
2197
|
+
name
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
`
|
|
2201
|
+
);
|
|
2202
|
+
|
|
2203
|
+
// libs/sdk/src/modules/availability.ts
|
|
2204
|
+
var MAX_AVAILABILITY_VARIANT_IDS = 100;
|
|
2205
|
+
function mapAvailability(raw) {
|
|
2206
|
+
return {
|
|
2207
|
+
productVariantId: raw.product_variant_id,
|
|
2208
|
+
inStock: raw.in_stock,
|
|
2209
|
+
label: raw.label
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2212
|
+
function chunkArray(items, chunkSize) {
|
|
2213
|
+
const chunks = [];
|
|
2214
|
+
for (let index = 0; index < items.length; index += chunkSize) {
|
|
2215
|
+
chunks.push(items.slice(index, index + chunkSize));
|
|
2216
|
+
}
|
|
2217
|
+
return chunks;
|
|
2218
|
+
}
|
|
2219
|
+
function uniqueValues(values) {
|
|
2220
|
+
return Array.from(new Set(values));
|
|
2221
|
+
}
|
|
2222
|
+
function createAvailabilityModule(graphqlUrl, getHeaders, getContext) {
|
|
2223
|
+
return {
|
|
2224
|
+
async checkAvailability(productVariantIds) {
|
|
2225
|
+
const ctx = getContext();
|
|
2226
|
+
if (!ctx.chainId) {
|
|
2227
|
+
throw new WebsiteError(
|
|
2228
|
+
"This website has no chain configured. Stock availability requires a chain_id.",
|
|
2229
|
+
"NO_CHAIN"
|
|
2230
|
+
);
|
|
2231
|
+
}
|
|
2232
|
+
if (productVariantIds.length === 0) return [];
|
|
2233
|
+
const variantIds = uniqueValues(productVariantIds);
|
|
2234
|
+
const results = [];
|
|
2235
|
+
for (const chunk of chunkArray(variantIds, MAX_AVAILABILITY_VARIANT_IDS)) {
|
|
2236
|
+
const data = await executeGraphQL(
|
|
2237
|
+
graphqlUrl,
|
|
2238
|
+
CHECK_AVAILABILITY,
|
|
2239
|
+
{
|
|
2240
|
+
variantIds: chunk,
|
|
2241
|
+
chainId: ctx.chainId
|
|
2242
|
+
},
|
|
2243
|
+
getHeaders
|
|
2244
|
+
);
|
|
2245
|
+
results.push(
|
|
2246
|
+
...(data.websiteProductAvailability ?? []).map(mapAvailability)
|
|
2247
|
+
);
|
|
2248
|
+
}
|
|
2249
|
+
return results;
|
|
2250
|
+
}
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
// libs/sdk/src/modules/cart.ts
|
|
2255
|
+
function mapCartItem(raw) {
|
|
2256
|
+
return {
|
|
2257
|
+
id: raw.id,
|
|
2258
|
+
productVariantId: raw.product_variant_id,
|
|
2259
|
+
quantity: raw.quantity,
|
|
2260
|
+
productVariant: {
|
|
2261
|
+
sku: raw.product_variant.sku,
|
|
2262
|
+
product: raw.product_variant.product,
|
|
2263
|
+
prices: raw.product_variant.prices.map((p) => ({
|
|
2264
|
+
id: p.id,
|
|
2265
|
+
price: p.price,
|
|
2266
|
+
priceType: p.price_type,
|
|
2267
|
+
chainId: p.chain_id
|
|
2268
|
+
}))
|
|
2269
|
+
}
|
|
2270
|
+
};
|
|
2271
|
+
}
|
|
2272
|
+
function mapCart(raw, cartToken) {
|
|
2273
|
+
return {
|
|
2274
|
+
id: raw.id,
|
|
2275
|
+
cartToken,
|
|
2276
|
+
items: raw.items.map(mapCartItem),
|
|
2277
|
+
totals: {
|
|
2278
|
+
subtotal: raw.total_without_tax,
|
|
2279
|
+
tax: raw.tax,
|
|
2280
|
+
discount: raw.discount,
|
|
2281
|
+
total: raw.total_with_tax
|
|
2282
|
+
},
|
|
2283
|
+
status: raw.status,
|
|
2284
|
+
createdAt: raw.created_at,
|
|
2285
|
+
updatedAt: raw.updated_at
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
function assertActionSuccess(result, fallbackCode) {
|
|
2289
|
+
if (!result.success || !result.order_id || !result.cart_token || !result.cart) {
|
|
2290
|
+
const message = result.errors?.join("; ") || "Cart action failed";
|
|
2291
|
+
throw new WebsiteError(message, fallbackCode);
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
function requireCartSession(storage) {
|
|
2295
|
+
const session = storage.get();
|
|
2296
|
+
if (!session) {
|
|
2297
|
+
throw new WebsiteError(
|
|
2298
|
+
"No guest cart session found",
|
|
2299
|
+
"CART_SESSION_MISSING"
|
|
2300
|
+
);
|
|
2301
|
+
}
|
|
2302
|
+
return session;
|
|
2303
|
+
}
|
|
2304
|
+
function createCartModule(graphqlUrl, getHeaders, getContext, cartSessionStorage) {
|
|
2305
|
+
async function fetchCart(orderId, cartToken) {
|
|
2306
|
+
const data = await executeGraphQL(graphqlUrl, GET_CART, { orderId, cartToken }, getHeaders);
|
|
2307
|
+
const result = data.websiteGetCart;
|
|
2308
|
+
if (!result.success) {
|
|
2309
|
+
if (result.errors?.includes("CART_NOT_FOUND")) {
|
|
2310
|
+
cartSessionStorage.clear();
|
|
2311
|
+
return null;
|
|
2312
|
+
}
|
|
2313
|
+
throw new WebsiteError(
|
|
2314
|
+
result.errors?.join("; ") || "Cart could not be retrieved",
|
|
2315
|
+
"CART_FETCH_ERROR"
|
|
2316
|
+
);
|
|
2317
|
+
}
|
|
2318
|
+
assertActionSuccess(result, "CART_FETCH_ERROR");
|
|
2319
|
+
cartSessionStorage.set({
|
|
2320
|
+
orderId: result.order_id,
|
|
2321
|
+
cartToken: result.cart_token
|
|
2322
|
+
});
|
|
2323
|
+
return mapCart(result.cart, result.cart_token);
|
|
2324
|
+
}
|
|
2325
|
+
return {
|
|
2326
|
+
async getOrCreate(contact, fulfillmentFamily) {
|
|
2327
|
+
const ctx = getContext();
|
|
2328
|
+
if (!ctx.chainId) {
|
|
2329
|
+
throw new WebsiteError(
|
|
2330
|
+
"This website has no chain configured. Cart requires a chain_id.",
|
|
2331
|
+
"NO_CHAIN"
|
|
2332
|
+
);
|
|
2333
|
+
}
|
|
2334
|
+
const existing = cartSessionStorage.get();
|
|
2335
|
+
const data = await executeGraphQL(
|
|
2336
|
+
graphqlUrl,
|
|
2337
|
+
CREATE_CART,
|
|
2338
|
+
{
|
|
2339
|
+
websiteId: ctx.websiteId,
|
|
2340
|
+
cartToken: existing?.cartToken,
|
|
2341
|
+
contact,
|
|
2342
|
+
fulfillmentFamily
|
|
2343
|
+
},
|
|
2344
|
+
getHeaders
|
|
2345
|
+
);
|
|
2346
|
+
const result = data.websiteCreateCart;
|
|
2347
|
+
assertActionSuccess(result, "CART_CREATE_ERROR");
|
|
2348
|
+
cartSessionStorage.set({
|
|
2349
|
+
orderId: result.order_id,
|
|
2350
|
+
cartToken: result.cart_token
|
|
2351
|
+
});
|
|
2352
|
+
return mapCart(result.cart, result.cart_token);
|
|
2353
|
+
},
|
|
2354
|
+
async startCheckout(input) {
|
|
2355
|
+
const ctx = getContext();
|
|
2356
|
+
if (!ctx.chainId) {
|
|
2357
|
+
throw new WebsiteError(
|
|
2358
|
+
"This website has no chain configured. Checkout requires a chain_id.",
|
|
2359
|
+
"NO_CHAIN"
|
|
2360
|
+
);
|
|
2361
|
+
}
|
|
2362
|
+
const existing = cartSessionStorage.get();
|
|
2363
|
+
const data = await executeGraphQL(
|
|
2364
|
+
graphqlUrl,
|
|
2365
|
+
START_GUEST_CHECKOUT,
|
|
2366
|
+
{
|
|
2367
|
+
websiteId: ctx.websiteId,
|
|
2368
|
+
cartToken: existing?.cartToken,
|
|
2369
|
+
contact: input.contact,
|
|
2370
|
+
fulfillmentFamily: input.fulfillmentFamily,
|
|
2371
|
+
items: input.items.map((item) => ({
|
|
2372
|
+
product_variant_id: item.productVariantId,
|
|
2373
|
+
quantity: item.quantity
|
|
2374
|
+
}))
|
|
2375
|
+
},
|
|
2376
|
+
getHeaders
|
|
2377
|
+
);
|
|
2378
|
+
const result = data.websiteStartGuestCheckout;
|
|
2379
|
+
assertActionSuccess(result, "CHECKOUT_START_ERROR");
|
|
2380
|
+
cartSessionStorage.set({
|
|
2381
|
+
orderId: result.order_id,
|
|
2382
|
+
cartToken: result.cart_token
|
|
2383
|
+
});
|
|
2384
|
+
return mapCart(result.cart, result.cart_token);
|
|
2385
|
+
},
|
|
2386
|
+
async get() {
|
|
2387
|
+
const session = cartSessionStorage.get();
|
|
2388
|
+
if (!session) return null;
|
|
2389
|
+
return fetchCart(session.orderId, session.cartToken);
|
|
2390
|
+
},
|
|
2391
|
+
async setItem(productVariantId, quantity) {
|
|
2392
|
+
if (quantity <= 0) {
|
|
2393
|
+
throw new WebsiteError(
|
|
2394
|
+
"Quantity must be greater than zero",
|
|
2395
|
+
"INVALID_QUANTITY"
|
|
2396
|
+
);
|
|
2397
|
+
}
|
|
2398
|
+
const session = cartSessionStorage.get();
|
|
2399
|
+
if (!session) {
|
|
2400
|
+
throw new WebsiteError(
|
|
2401
|
+
"Start checkout with contact before syncing cart items to Bacano",
|
|
2402
|
+
"CART_SESSION_MISSING"
|
|
2403
|
+
);
|
|
2404
|
+
}
|
|
2405
|
+
const data = await executeGraphQL(
|
|
2406
|
+
graphqlUrl,
|
|
2407
|
+
SET_CART_ITEM,
|
|
2408
|
+
{
|
|
2409
|
+
orderId: session.orderId,
|
|
2410
|
+
cartToken: session.cartToken,
|
|
2411
|
+
variantId: productVariantId,
|
|
2412
|
+
quantity
|
|
2413
|
+
},
|
|
2414
|
+
getHeaders
|
|
2415
|
+
);
|
|
2416
|
+
const result = data.websiteSetCartItem;
|
|
2417
|
+
assertActionSuccess(result, "CART_ITEM_ERROR");
|
|
2418
|
+
cartSessionStorage.set({
|
|
2419
|
+
orderId: result.order_id,
|
|
2420
|
+
cartToken: result.cart_token
|
|
2421
|
+
});
|
|
2422
|
+
return mapCart(result.cart, result.cart_token);
|
|
2423
|
+
},
|
|
2424
|
+
async addItem(productVariantId, quantity) {
|
|
2425
|
+
const cart = await this.setItem(productVariantId, quantity);
|
|
2426
|
+
const item = cart.items.find(
|
|
2427
|
+
(entry) => entry.productVariantId === productVariantId
|
|
2428
|
+
);
|
|
2429
|
+
if (!item) {
|
|
2430
|
+
throw new WebsiteError(
|
|
2431
|
+
"Cart item was not returned by the server",
|
|
2432
|
+
"CART_ITEM_ERROR"
|
|
2433
|
+
);
|
|
2434
|
+
}
|
|
2435
|
+
return item;
|
|
2436
|
+
},
|
|
2437
|
+
async updateItem(cartItemId, quantity) {
|
|
2438
|
+
const cart = await this.get();
|
|
2439
|
+
const item = cart?.items.find((entry) => entry.id === cartItemId);
|
|
2440
|
+
if (!item) {
|
|
2441
|
+
throw new WebsiteError(
|
|
2442
|
+
"Cart item was not found",
|
|
2443
|
+
"CART_ITEM_NOT_FOUND"
|
|
2444
|
+
);
|
|
2445
|
+
}
|
|
2446
|
+
const updatedCart = await this.setItem(item.productVariantId, quantity);
|
|
2447
|
+
const updatedItem = updatedCart.items.find(
|
|
2448
|
+
(entry) => entry.id === cartItemId
|
|
2449
|
+
);
|
|
2450
|
+
return {
|
|
2451
|
+
id: cartItemId,
|
|
2452
|
+
quantity: updatedItem?.quantity ?? quantity
|
|
2453
|
+
};
|
|
2454
|
+
},
|
|
2455
|
+
async removeItem(cartItemId) {
|
|
2456
|
+
const session = requireCartSession(cartSessionStorage);
|
|
2457
|
+
const data = await executeGraphQL(
|
|
2458
|
+
graphqlUrl,
|
|
2459
|
+
REMOVE_CART_ITEM,
|
|
2460
|
+
{
|
|
2461
|
+
orderId: session.orderId,
|
|
2462
|
+
cartToken: session.cartToken,
|
|
2463
|
+
itemId: cartItemId
|
|
2464
|
+
},
|
|
2465
|
+
getHeaders
|
|
2466
|
+
);
|
|
2467
|
+
const result = data.websiteRemoveCartItem;
|
|
2468
|
+
assertActionSuccess(result, "CART_ITEM_ERROR");
|
|
2469
|
+
cartSessionStorage.set({
|
|
2470
|
+
orderId: result.order_id,
|
|
2471
|
+
cartToken: result.cart_token
|
|
2472
|
+
});
|
|
2473
|
+
return mapCart(result.cart, result.cart_token);
|
|
2474
|
+
}
|
|
2475
|
+
};
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
// libs/sdk/src/modules/cart-session.ts
|
|
2479
|
+
function getStorageKey(slug) {
|
|
2480
|
+
return `@bacano/sdk:cart:${slug}`;
|
|
2481
|
+
}
|
|
2482
|
+
function createCartSessionStorage(kind, getSlug) {
|
|
2483
|
+
let memorySession = null;
|
|
2484
|
+
function readKey() {
|
|
2485
|
+
return getStorageKey(getSlug());
|
|
2486
|
+
}
|
|
2487
|
+
return {
|
|
2488
|
+
get() {
|
|
2489
|
+
if (kind === "memory") return memorySession;
|
|
2490
|
+
if (kind === "cookie") {
|
|
2491
|
+
if (typeof document === "undefined") return null;
|
|
2492
|
+
try {
|
|
2493
|
+
const key = readKey();
|
|
2494
|
+
const match = document.cookie.split("; ").find((row) => row.startsWith(`${key}=`));
|
|
2495
|
+
if (!match) return null;
|
|
2496
|
+
return JSON.parse(decodeURIComponent(match.split("=").slice(1).join("=")));
|
|
2497
|
+
} catch {
|
|
2498
|
+
return null;
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
if (typeof localStorage === "undefined") return null;
|
|
2502
|
+
try {
|
|
2503
|
+
const raw = localStorage.getItem(readKey());
|
|
2504
|
+
return raw ? JSON.parse(raw) : null;
|
|
2505
|
+
} catch {
|
|
2506
|
+
return null;
|
|
2507
|
+
}
|
|
2508
|
+
},
|
|
2509
|
+
set(session) {
|
|
2510
|
+
memorySession = session;
|
|
2511
|
+
if (kind === "memory") return;
|
|
2512
|
+
if (kind === "cookie") {
|
|
2513
|
+
if (typeof document === "undefined") return;
|
|
2514
|
+
const maxAge = 30 * 24 * 60 * 60;
|
|
2515
|
+
const value = encodeURIComponent(JSON.stringify(session));
|
|
2516
|
+
document.cookie = `${readKey()}=${value}; max-age=${maxAge}; path=/; SameSite=Lax`;
|
|
2517
|
+
return;
|
|
2518
|
+
}
|
|
2519
|
+
if (typeof localStorage === "undefined") return;
|
|
2520
|
+
localStorage.setItem(readKey(), JSON.stringify(session));
|
|
2521
|
+
},
|
|
2522
|
+
clear() {
|
|
2523
|
+
memorySession = null;
|
|
2524
|
+
if (kind === "memory") return;
|
|
2525
|
+
if (kind === "cookie") {
|
|
2526
|
+
if (typeof document === "undefined") return;
|
|
2527
|
+
document.cookie = `${readKey()}=; max-age=0; path=/; SameSite=Lax`;
|
|
2528
|
+
return;
|
|
2529
|
+
}
|
|
2530
|
+
if (typeof localStorage === "undefined") return;
|
|
2531
|
+
localStorage.removeItem(readKey());
|
|
2532
|
+
}
|
|
2533
|
+
};
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
// libs/sdk/src/media.ts
|
|
2537
|
+
var MEDIA_VARIANTS = {
|
|
2538
|
+
thumbnail: { width: 240, quality: 75, format: "webp" },
|
|
2539
|
+
card: { width: 640, quality: 80, format: "webp" },
|
|
2540
|
+
detail: { width: 1400, quality: 85, format: "webp" }
|
|
2541
|
+
};
|
|
2542
|
+
function toSafeInteger(value) {
|
|
2543
|
+
if (!Number.isFinite(value)) return void 0;
|
|
2544
|
+
return Math.max(1, Math.floor(value));
|
|
2545
|
+
}
|
|
2546
|
+
function buildQuery(options) {
|
|
2547
|
+
const params = new URLSearchParams();
|
|
2548
|
+
const width = toSafeInteger(options.width);
|
|
2549
|
+
const height = toSafeInteger(options.height);
|
|
2550
|
+
const quality = toSafeInteger(options.quality);
|
|
2551
|
+
if (width) params.set("w", String(width));
|
|
2552
|
+
if (height) params.set("h", String(height));
|
|
2553
|
+
if (quality) params.set("q", String(Math.min(100, quality)));
|
|
2554
|
+
if (options.format) params.set("f", options.format);
|
|
2555
|
+
const query = params.toString();
|
|
2556
|
+
return query ? `?${query}` : "";
|
|
2557
|
+
}
|
|
2558
|
+
function buildStorageFileUrl(storageUrl, fileId, variant = "original") {
|
|
2559
|
+
if (!storageUrl || !fileId) return "";
|
|
2560
|
+
const baseUrl = `${storageUrl.replace(/\/+$/, "")}/files/${fileId}`;
|
|
2561
|
+
if (variant === "original") return baseUrl;
|
|
2562
|
+
return `${baseUrl}${buildQuery(MEDIA_VARIANTS[variant])}`;
|
|
2563
|
+
}
|
|
2564
|
+
function buildStorageMediaUrls(storageUrl, fileId) {
|
|
2565
|
+
return {
|
|
2566
|
+
originalUrl: buildStorageFileUrl(storageUrl, fileId, "original"),
|
|
2567
|
+
thumbnailUrl: buildStorageFileUrl(storageUrl, fileId, "thumbnail"),
|
|
2568
|
+
cardUrl: buildStorageFileUrl(storageUrl, fileId, "card"),
|
|
2569
|
+
detailUrl: buildStorageFileUrl(storageUrl, fileId, "detail")
|
|
2570
|
+
};
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2573
|
+
// libs/sdk/src/modules/catalog.ts
|
|
2574
|
+
var MAX_AVAILABILITY_VARIANT_IDS2 = 100;
|
|
2575
|
+
var DEFAULT_FILTER_ATTRIBUTE_LIST_KEY = "atributos";
|
|
2576
|
+
function mapPrice(raw) {
|
|
2577
|
+
return {
|
|
2578
|
+
id: raw.id,
|
|
2579
|
+
price: raw.price,
|
|
2580
|
+
priceType: raw.price_type,
|
|
2581
|
+
chainId: raw.chain_id
|
|
2582
|
+
};
|
|
2583
|
+
}
|
|
2584
|
+
function parseActionJson(value, fallback) {
|
|
2585
|
+
if (value == null) return fallback;
|
|
2586
|
+
if (typeof value !== "string") return value;
|
|
2587
|
+
try {
|
|
2588
|
+
return JSON.parse(value);
|
|
2589
|
+
} catch {
|
|
2590
|
+
return fallback;
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
function getProductMediaKind(mimeType) {
|
|
2594
|
+
if (mimeType?.startsWith("image/")) return "image";
|
|
2595
|
+
if (mimeType?.startsWith("video/")) return "video";
|
|
2596
|
+
if (mimeType?.startsWith("application/")) return "document";
|
|
2597
|
+
return "file";
|
|
2598
|
+
}
|
|
2599
|
+
function getProductMediaByKind(media, kind) {
|
|
2600
|
+
return (media ?? []).filter((item) => item.mediaKind === kind);
|
|
2601
|
+
}
|
|
2602
|
+
function getProductImages(media) {
|
|
2603
|
+
return getProductMediaByKind(media, "image");
|
|
2604
|
+
}
|
|
2605
|
+
function getProductVideos(media) {
|
|
2606
|
+
return getProductMediaByKind(media, "video");
|
|
2607
|
+
}
|
|
2608
|
+
function getPrimaryProductImage(media) {
|
|
2609
|
+
return getProductImages(media)[0] ?? null;
|
|
2610
|
+
}
|
|
2611
|
+
function mapMedia(raw, storageUrl) {
|
|
2612
|
+
const mimeType = raw.file?.mimeType ?? null;
|
|
2613
|
+
const fileName = raw.file?.name ?? null;
|
|
2614
|
+
const urls = buildStorageMediaUrls(storageUrl, raw.file_id);
|
|
2615
|
+
return {
|
|
2616
|
+
id: raw.id,
|
|
2617
|
+
fileId: raw.file_id,
|
|
2618
|
+
order: raw.order ?? 0,
|
|
2619
|
+
url: urls.originalUrl,
|
|
2620
|
+
...urls,
|
|
2621
|
+
mediaKind: getProductMediaKind(mimeType),
|
|
2622
|
+
mimeType,
|
|
2623
|
+
fileName,
|
|
2624
|
+
alt: fileName
|
|
2625
|
+
};
|
|
2626
|
+
}
|
|
2627
|
+
function mapProductRecommendation(raw, storageUrl) {
|
|
2628
|
+
return {
|
|
2629
|
+
id: raw.id,
|
|
2630
|
+
name: raw.name,
|
|
2631
|
+
slug: raw.slug,
|
|
2632
|
+
category: raw.category,
|
|
2633
|
+
subcategory: raw.subcategory,
|
|
2634
|
+
brand: raw.brand,
|
|
2635
|
+
primaryImage: raw.primary_media ? mapMedia(raw.primary_media, storageUrl) : null,
|
|
2636
|
+
pricing: {
|
|
2637
|
+
currentPriceMin: Number(raw.pricing.current_price_min),
|
|
2638
|
+
currentPriceMax: Number(raw.pricing.current_price_max),
|
|
2639
|
+
regularPriceMin: Number(raw.pricing.regular_price_min),
|
|
2640
|
+
regularPriceMax: Number(raw.pricing.regular_price_max),
|
|
2641
|
+
hasDiscount: raw.pricing.has_discount
|
|
2642
|
+
},
|
|
2643
|
+
source: raw.recommendation_source
|
|
2644
|
+
};
|
|
2645
|
+
}
|
|
2646
|
+
function mapSeoMedia(raw, storageUrl) {
|
|
2647
|
+
if (!raw.seo_image_file_id) return null;
|
|
2648
|
+
return mapMedia(
|
|
2649
|
+
{
|
|
2650
|
+
id: `seo:${raw.seo_image_file_id}`,
|
|
2651
|
+
file_id: raw.seo_image_file_id,
|
|
2652
|
+
order: 0,
|
|
2653
|
+
file: raw.seo_image_file ?? null
|
|
2654
|
+
},
|
|
2655
|
+
storageUrl
|
|
2656
|
+
);
|
|
2657
|
+
}
|
|
2658
|
+
function attributeValueCode(raw) {
|
|
2659
|
+
return raw.attribute_value?.code ?? raw.value;
|
|
2660
|
+
}
|
|
2661
|
+
function attributeValueLabelKey(attributeId, valueCode) {
|
|
2662
|
+
return `${attributeId}:${valueCode}`;
|
|
2663
|
+
}
|
|
2664
|
+
function buildPublicAttributeLabels(lists) {
|
|
2665
|
+
const attributeLabelsById = /* @__PURE__ */ new Map();
|
|
2666
|
+
const valueLabelsById = /* @__PURE__ */ new Map();
|
|
2667
|
+
const valueLabelsByAttributeAndCode = /* @__PURE__ */ new Map();
|
|
2668
|
+
for (const list of lists ?? []) {
|
|
2669
|
+
for (const item of list.items ?? []) {
|
|
2670
|
+
if (!attributeLabelsById.has(item.attribute_id)) {
|
|
2671
|
+
attributeLabelsById.set(item.attribute_id, item.public_label);
|
|
2672
|
+
}
|
|
2673
|
+
for (const value of item.values ?? []) {
|
|
2674
|
+
if (!valueLabelsById.has(value.attribute_value_id)) {
|
|
2675
|
+
valueLabelsById.set(value.attribute_value_id, value.public_label);
|
|
2676
|
+
}
|
|
2677
|
+
const valueCode = value.attribute_value.code;
|
|
2678
|
+
const key = attributeValueLabelKey(item.attribute_id, valueCode);
|
|
2679
|
+
if (!valueLabelsByAttributeAndCode.has(key)) {
|
|
2680
|
+
valueLabelsByAttributeAndCode.set(key, value.public_label);
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
return {
|
|
2686
|
+
attributeLabelsById,
|
|
2687
|
+
valueLabelsById,
|
|
2688
|
+
valueLabelsByAttributeAndCode
|
|
2689
|
+
};
|
|
2690
|
+
}
|
|
2691
|
+
function resolvePublicAttributeValueLabel(raw, labels) {
|
|
2692
|
+
const valueCode = attributeValueCode(raw);
|
|
2693
|
+
if (raw.attribute_value?.public_label) {
|
|
2694
|
+
return raw.attribute_value.public_label;
|
|
2695
|
+
}
|
|
2696
|
+
const byId = raw.attribute_value?.id ? labels.valueLabelsById.get(raw.attribute_value.id) : null;
|
|
2697
|
+
const byAttributeAndCode = labels.valueLabelsByAttributeAndCode.get(
|
|
2698
|
+
attributeValueLabelKey(raw.attribute.id, valueCode)
|
|
2699
|
+
);
|
|
2700
|
+
return byId ?? byAttributeAndCode ?? valueCode;
|
|
2701
|
+
}
|
|
2702
|
+
function mapProductAttribute(raw, labels) {
|
|
2703
|
+
const valueCode = attributeValueCode(raw);
|
|
2704
|
+
return {
|
|
2705
|
+
id: raw.id,
|
|
2706
|
+
name: raw.attribute.public_label ?? labels.attributeLabelsById.get(raw.attribute.id) ?? raw.attribute.name,
|
|
2707
|
+
value: resolvePublicAttributeValueLabel(raw, labels),
|
|
2708
|
+
attributeValueId: raw.attribute_value?.id ?? null,
|
|
2709
|
+
code: raw.attribute.name,
|
|
2710
|
+
valueCode,
|
|
2711
|
+
metadata: raw.attribute_value?.metadata ?? null,
|
|
2712
|
+
sortOrder: raw.attribute_value?.sort_order ?? null
|
|
2713
|
+
};
|
|
2714
|
+
}
|
|
2715
|
+
function mapVariantAttribute(raw, labels) {
|
|
2716
|
+
const valueCode = attributeValueCode(raw);
|
|
2717
|
+
return {
|
|
2718
|
+
id: raw.id,
|
|
2719
|
+
name: raw.attribute.public_label ?? labels.attributeLabelsById.get(raw.attribute.id) ?? raw.attribute.name,
|
|
2720
|
+
value: resolvePublicAttributeValueLabel(raw, labels),
|
|
2721
|
+
order: raw.order,
|
|
2722
|
+
attributeValueId: raw.attribute_value?.id ?? null,
|
|
2723
|
+
code: raw.attribute.name,
|
|
2724
|
+
valueCode,
|
|
2725
|
+
metadata: raw.attribute_value?.metadata ?? null,
|
|
2726
|
+
sortOrder: raw.attribute_value?.sort_order ?? null
|
|
2727
|
+
};
|
|
2728
|
+
}
|
|
2729
|
+
function mapVariant(raw, labels, chainId) {
|
|
2730
|
+
const prices = selectPublicPrices(
|
|
2731
|
+
raw.product_variant_prices.map(mapPrice),
|
|
2732
|
+
chainId
|
|
2733
|
+
);
|
|
2734
|
+
return {
|
|
2735
|
+
id: raw.id,
|
|
2736
|
+
sku: raw.sku,
|
|
2737
|
+
barcode: raw.barcode,
|
|
2738
|
+
order: raw.order,
|
|
2739
|
+
weight: raw.weight,
|
|
2740
|
+
depth: raw.depth,
|
|
2741
|
+
height: raw.height,
|
|
2742
|
+
length: raw.length,
|
|
2743
|
+
prices,
|
|
2744
|
+
pricing: resolveProductPricing(prices),
|
|
2745
|
+
attributes: raw.product_variant_attributes.map(
|
|
2746
|
+
(attribute) => mapVariantAttribute(attribute, labels)
|
|
2747
|
+
)
|
|
2748
|
+
};
|
|
2749
|
+
}
|
|
2750
|
+
function mapRelatedProduct(raw, storageUrl) {
|
|
2751
|
+
return {
|
|
2752
|
+
id: raw.id,
|
|
2753
|
+
productId: raw.related_product_id,
|
|
2754
|
+
slug: raw.related_product.slug,
|
|
2755
|
+
name: raw.related_product.name,
|
|
2756
|
+
sku: raw.related_product.sku,
|
|
2757
|
+
displayLabel: raw.display_label,
|
|
2758
|
+
sortOrder: raw.sort_order ?? 0,
|
|
2759
|
+
media: raw.related_product.product_media.map(
|
|
2760
|
+
(m) => mapMedia(m, storageUrl)
|
|
2761
|
+
)
|
|
2762
|
+
};
|
|
2763
|
+
}
|
|
2764
|
+
function mapProduct(raw, storageUrl, labels, chainId) {
|
|
2765
|
+
return {
|
|
2766
|
+
id: raw.id,
|
|
2767
|
+
name: raw.name,
|
|
2768
|
+
description: raw.description,
|
|
2769
|
+
longDescription: raw.long_description ?? null,
|
|
2770
|
+
slug: raw.slug ?? null,
|
|
2771
|
+
seoTitle: raw.seo_title ?? null,
|
|
2772
|
+
seoDescription: raw.seo_description ?? null,
|
|
2773
|
+
seoImage: mapSeoMedia(raw, storageUrl),
|
|
2774
|
+
color: raw.color,
|
|
2775
|
+
category: raw.category,
|
|
2776
|
+
subcategory: raw.subcategory,
|
|
2777
|
+
brand: raw.brand,
|
|
2778
|
+
measurementUnit: raw.measurement_unit,
|
|
2779
|
+
createdAt: raw.created_at,
|
|
2780
|
+
updatedAt: raw.updated_at ?? raw.created_at,
|
|
2781
|
+
variants: raw.product_variants.map(
|
|
2782
|
+
(variant) => mapVariant(variant, labels, chainId)
|
|
2783
|
+
),
|
|
2784
|
+
media: raw.product_media.map((m) => mapMedia(m, storageUrl)),
|
|
2785
|
+
attributes: raw.product_attributes.map(
|
|
2786
|
+
(attribute) => mapProductAttribute(attribute, labels)
|
|
2787
|
+
),
|
|
2788
|
+
relatedProducts: (raw.product_related_products ?? []).map(
|
|
2789
|
+
(relation) => mapRelatedProduct(relation, storageUrl)
|
|
2790
|
+
)
|
|
2791
|
+
};
|
|
2792
|
+
}
|
|
2793
|
+
function mapCategory(raw) {
|
|
2794
|
+
return {
|
|
2795
|
+
name: raw.name,
|
|
2796
|
+
description: raw.description,
|
|
2797
|
+
subcategories: raw.product_subcategories.map((sub) => ({
|
|
2798
|
+
id: sub.id,
|
|
2799
|
+
name: sub.name,
|
|
2800
|
+
description: sub.description
|
|
2801
|
+
}))
|
|
2802
|
+
};
|
|
2803
|
+
}
|
|
2804
|
+
function mapCategoryListItem(raw, categoriesByName) {
|
|
2805
|
+
const parentCategory = raw.subcategory_ref?.category ?? null;
|
|
2806
|
+
const parent = parentCategory ? categoriesByName?.get(parentCategory) ?? null : null;
|
|
2807
|
+
return {
|
|
2808
|
+
id: raw.id,
|
|
2809
|
+
sourceType: raw.source_type,
|
|
2810
|
+
productCategory: raw.product_category,
|
|
2811
|
+
productSubcategoryId: raw.product_subcategory_id,
|
|
2812
|
+
label: raw.public_label,
|
|
2813
|
+
slug: raw.public_slug,
|
|
2814
|
+
sortOrder: raw.sort_order,
|
|
2815
|
+
category: raw.category_ref,
|
|
2816
|
+
subcategory: raw.subcategory_ref,
|
|
2817
|
+
parentCategory,
|
|
2818
|
+
parentId: parent?.id ?? null,
|
|
2819
|
+
parentSlug: parent?.slug ?? null
|
|
2820
|
+
};
|
|
2821
|
+
}
|
|
2822
|
+
function mapCategoryList(raw) {
|
|
2823
|
+
const categoryItems = raw.items.filter((item) => item.source_type === "category" && item.product_category).map((item) => mapCategoryListItem(item));
|
|
2824
|
+
const categoriesByName = new Map(
|
|
2825
|
+
categoryItems.filter((item) => item.productCategory).map((item) => [item.productCategory, item])
|
|
2826
|
+
);
|
|
2827
|
+
const items = raw.items.map(
|
|
2828
|
+
(item) => mapCategoryListItem(item, categoriesByName)
|
|
2829
|
+
);
|
|
2830
|
+
return {
|
|
2831
|
+
id: raw.id,
|
|
2832
|
+
key: raw.key,
|
|
2833
|
+
name: raw.name,
|
|
2834
|
+
description: raw.description,
|
|
2835
|
+
status: raw.status,
|
|
2836
|
+
items,
|
|
2837
|
+
categories: categoryItems.map((category) => ({
|
|
2838
|
+
id: category.id,
|
|
2839
|
+
productCategory: category.productCategory,
|
|
2840
|
+
label: category.label,
|
|
2841
|
+
slug: category.slug,
|
|
2842
|
+
sortOrder: category.sortOrder,
|
|
2843
|
+
category: category.category,
|
|
2844
|
+
subcategories: items.filter(
|
|
2845
|
+
(item) => item.sourceType === "subcategory" && item.parentCategory === category.productCategory
|
|
2846
|
+
).sort(
|
|
2847
|
+
(a, b) => a.sortOrder === b.sortOrder ? a.label.localeCompare(b.label) : a.sortOrder - b.sortOrder
|
|
2848
|
+
)
|
|
2849
|
+
}))
|
|
2850
|
+
};
|
|
2851
|
+
}
|
|
2852
|
+
function filterCategoryListByApplicability(raw, applicableCategories, applicableSubcategoryIds) {
|
|
2853
|
+
const categoryNamesWithApplicableSubcategories = new Set(
|
|
2854
|
+
raw.items.filter(
|
|
2855
|
+
(item) => item.source_type === "subcategory" && item.product_subcategory_id && applicableSubcategoryIds.has(item.product_subcategory_id)
|
|
2856
|
+
).map((item) => item.subcategory_ref?.category).filter((category) => Boolean(category))
|
|
2857
|
+
);
|
|
2858
|
+
const filteredItems = raw.items.filter((item) => {
|
|
2859
|
+
if (item.source_type === "category") {
|
|
2860
|
+
const category = item.product_category;
|
|
2861
|
+
return Boolean(
|
|
2862
|
+
category && (applicableCategories.has(category) || categoryNamesWithApplicableSubcategories.has(category))
|
|
2863
|
+
);
|
|
2864
|
+
}
|
|
2865
|
+
const subcategoryId = item.product_subcategory_id;
|
|
2866
|
+
return Boolean(
|
|
2867
|
+
subcategoryId && applicableSubcategoryIds.has(subcategoryId)
|
|
2868
|
+
);
|
|
2869
|
+
});
|
|
2870
|
+
return mapCategoryList({
|
|
2871
|
+
...raw,
|
|
2872
|
+
items: filteredItems
|
|
2873
|
+
});
|
|
2874
|
+
}
|
|
2875
|
+
function mapAttributeListValue(raw) {
|
|
2876
|
+
return {
|
|
2877
|
+
id: raw.id,
|
|
2878
|
+
attributeValueId: raw.attribute_value_id,
|
|
2879
|
+
code: raw.attribute_value.code,
|
|
2880
|
+
label: raw.public_label,
|
|
2881
|
+
slug: raw.public_slug,
|
|
2882
|
+
sortOrder: raw.sort_order,
|
|
2883
|
+
metadata: raw.attribute_value.metadata
|
|
2884
|
+
};
|
|
2885
|
+
}
|
|
2886
|
+
function mapAttributeListItem(raw) {
|
|
2887
|
+
return {
|
|
2888
|
+
id: raw.id,
|
|
2889
|
+
attributeId: raw.attribute_id,
|
|
2890
|
+
attributeName: raw.attribute.name,
|
|
2891
|
+
attributeType: raw.attribute.type,
|
|
2892
|
+
label: raw.public_label,
|
|
2893
|
+
slug: raw.public_slug,
|
|
2894
|
+
sortOrder: raw.sort_order,
|
|
2895
|
+
values: raw.values.map(mapAttributeListValue)
|
|
2896
|
+
};
|
|
2897
|
+
}
|
|
2898
|
+
function mapAttributeList(raw) {
|
|
2899
|
+
return {
|
|
2900
|
+
id: raw.id,
|
|
2901
|
+
key: raw.key,
|
|
2902
|
+
name: raw.name,
|
|
2903
|
+
description: raw.description,
|
|
2904
|
+
status: raw.status,
|
|
2905
|
+
items: raw.items.map(mapAttributeListItem)
|
|
2906
|
+
};
|
|
2907
|
+
}
|
|
2908
|
+
function mapContextualAttributeListValue(raw) {
|
|
2909
|
+
return {
|
|
2910
|
+
id: raw.id,
|
|
2911
|
+
attributeValueId: raw.attribute_value_id,
|
|
2912
|
+
code: raw.code,
|
|
2913
|
+
label: raw.public_label,
|
|
2914
|
+
slug: raw.public_slug,
|
|
2915
|
+
sortOrder: raw.sort_order,
|
|
2916
|
+
metadata: raw.metadata
|
|
2917
|
+
};
|
|
2918
|
+
}
|
|
2919
|
+
function mapContextualAttributeListItem(raw) {
|
|
2920
|
+
return {
|
|
2921
|
+
id: raw.id,
|
|
2922
|
+
attributeId: raw.attribute_id,
|
|
2923
|
+
attributeName: raw.attribute_name,
|
|
2924
|
+
attributeType: raw.attribute_type,
|
|
2925
|
+
label: raw.public_label,
|
|
2926
|
+
slug: raw.public_slug,
|
|
2927
|
+
sortOrder: raw.sort_order,
|
|
2928
|
+
values: raw.values.map(mapContextualAttributeListValue)
|
|
2929
|
+
};
|
|
2930
|
+
}
|
|
2931
|
+
function mapContextualAttributeList(raw) {
|
|
2932
|
+
return {
|
|
2933
|
+
id: raw.id,
|
|
2934
|
+
key: raw.key,
|
|
2935
|
+
name: raw.name,
|
|
2936
|
+
description: raw.description,
|
|
2937
|
+
status: raw.status,
|
|
2938
|
+
items: raw.items.map(mapContextualAttributeListItem)
|
|
2939
|
+
};
|
|
2940
|
+
}
|
|
2941
|
+
function buildOrderBy(sortBy) {
|
|
2942
|
+
switch (sortBy) {
|
|
2943
|
+
case "name_asc":
|
|
2944
|
+
return [{ name: "asc" }];
|
|
2945
|
+
case "name_desc":
|
|
2946
|
+
return [{ name: "desc" }];
|
|
2947
|
+
case "newest":
|
|
2948
|
+
return [{ created_at: "desc" }];
|
|
2949
|
+
case "price_asc":
|
|
2950
|
+
case "price_desc":
|
|
2951
|
+
return void 0;
|
|
2952
|
+
// Client-side sort after fetch
|
|
2953
|
+
default:
|
|
2954
|
+
return void 0;
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
function scoreSearchRelevance(product, search) {
|
|
2958
|
+
const term = search.toLowerCase();
|
|
2959
|
+
const name = product.name.toLowerCase();
|
|
2960
|
+
if (name.startsWith(term)) return 100;
|
|
2961
|
+
if (name.includes(term)) return 80;
|
|
2962
|
+
if (product.brand?.name.toLowerCase().includes(term)) return 60;
|
|
2963
|
+
if (product.attributes.some((a) => a.value.toLowerCase().includes(term)))
|
|
2964
|
+
return 40;
|
|
2965
|
+
if (product.description?.toLowerCase().includes(term)) return 20;
|
|
2966
|
+
return 0;
|
|
2967
|
+
}
|
|
2968
|
+
function getMinVariantPrice(product) {
|
|
2969
|
+
let min = Infinity;
|
|
2970
|
+
for (const v of product.variants) {
|
|
2971
|
+
if (v.pricing.currentPrice != null && v.pricing.currentPrice < min) {
|
|
2972
|
+
min = v.pricing.currentPrice;
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
return min === Infinity ? 0 : min;
|
|
2976
|
+
}
|
|
2977
|
+
function resolveProductPricing(prices) {
|
|
2978
|
+
const positivePrice = (priceType) => {
|
|
2979
|
+
const price = prices.find((item) => item.priceType === priceType)?.price;
|
|
2980
|
+
return price != null && Number.isFinite(price) && price > 0 ? price : null;
|
|
2981
|
+
};
|
|
2982
|
+
const regularPrice = positivePrice("CLIENT");
|
|
2983
|
+
const candidateSalePrice = positivePrice("SALE");
|
|
2984
|
+
const salePrice = regularPrice != null && candidateSalePrice != null && candidateSalePrice < regularPrice ? candidateSalePrice : null;
|
|
2985
|
+
const hasDiscount = salePrice != null;
|
|
2986
|
+
const discountPercentage = salePrice != null && regularPrice != null ? Math.round((regularPrice - salePrice) / regularPrice * 1e4) / 100 : null;
|
|
2987
|
+
return {
|
|
2988
|
+
currentPrice: salePrice ?? regularPrice,
|
|
2989
|
+
regularPrice,
|
|
2990
|
+
salePrice,
|
|
2991
|
+
hasDiscount,
|
|
2992
|
+
discountPercentage,
|
|
2993
|
+
wholesalePrice: positivePrice("WHOLESALE"),
|
|
2994
|
+
wholesale2Price: positivePrice("WHOLESALE_2")
|
|
2995
|
+
};
|
|
2996
|
+
}
|
|
2997
|
+
function selectPublicPrices(prices, chainId) {
|
|
2998
|
+
const selected = [];
|
|
2999
|
+
for (const priceType of [
|
|
3000
|
+
"SALE",
|
|
3001
|
+
"CLIENT",
|
|
3002
|
+
"WHOLESALE",
|
|
3003
|
+
"WHOLESALE_2"
|
|
3004
|
+
]) {
|
|
3005
|
+
const chainPrice = prices.find(
|
|
3006
|
+
(price2) => price2.priceType === priceType && price2.chainId === chainId
|
|
3007
|
+
);
|
|
3008
|
+
const globalPrice = prices.find(
|
|
3009
|
+
(price2) => price2.priceType === priceType && price2.chainId === null
|
|
3010
|
+
);
|
|
3011
|
+
const price = chainPrice ?? globalPrice;
|
|
3012
|
+
if (price) selected.push(price);
|
|
3013
|
+
}
|
|
3014
|
+
return selected;
|
|
3015
|
+
}
|
|
3016
|
+
function hasMeaningfulAttributeListFilters(filters) {
|
|
3017
|
+
if (!filters) return false;
|
|
3018
|
+
return Boolean(
|
|
3019
|
+
filters.categoryName || filters.subcategoryId || filters.brandId || filters.search || filters.minPrice !== void 0 || filters.maxPrice !== void 0 || (filters.tags?.some((tag) => tag.trim().length > 0) ?? false)
|
|
3020
|
+
);
|
|
3021
|
+
}
|
|
3022
|
+
function chunkArray2(items, chunkSize) {
|
|
3023
|
+
const chunks = [];
|
|
3024
|
+
for (let index = 0; index < items.length; index += chunkSize) {
|
|
3025
|
+
chunks.push(items.slice(index, index + chunkSize));
|
|
3026
|
+
}
|
|
3027
|
+
return chunks;
|
|
3028
|
+
}
|
|
3029
|
+
function uniqueValues2(values) {
|
|
3030
|
+
return Array.from(new Set(values));
|
|
3031
|
+
}
|
|
3032
|
+
function buildProductWhere(filters, chainId) {
|
|
3033
|
+
const whereConditions = [];
|
|
3034
|
+
if (filters.categoryName) {
|
|
3035
|
+
whereConditions.push({ category: { _eq: filters.categoryName } });
|
|
3036
|
+
}
|
|
3037
|
+
if (filters.subcategoryId) {
|
|
3038
|
+
whereConditions.push({ subcategory: { _eq: filters.subcategoryId } });
|
|
3039
|
+
}
|
|
3040
|
+
if (filters.brandId) {
|
|
3041
|
+
whereConditions.push({ brand_id: { _eq: filters.brandId } });
|
|
3042
|
+
}
|
|
3043
|
+
if (filters.search) {
|
|
3044
|
+
const term = `%${filters.search}%`;
|
|
3045
|
+
whereConditions.push({
|
|
3046
|
+
_or: [
|
|
3047
|
+
{ name: { _ilike: term } },
|
|
3048
|
+
{ description: { _ilike: term } },
|
|
3049
|
+
{ brand: { name: { _ilike: term } } },
|
|
3050
|
+
{ product_attributes: { value: { _ilike: term } } }
|
|
3051
|
+
]
|
|
3052
|
+
});
|
|
3053
|
+
}
|
|
3054
|
+
if (filters.colors && filters.colors.length > 0) {
|
|
3055
|
+
whereConditions.push({ color: { _in: filters.colors } });
|
|
3056
|
+
}
|
|
3057
|
+
if (filters.tags && filters.tags.length > 0) {
|
|
3058
|
+
const tags = filters.tags.map((tag) => tag.trim()).filter(Boolean);
|
|
3059
|
+
if (tags.length > 0) {
|
|
3060
|
+
whereConditions.push({
|
|
3061
|
+
product_tag_assignments: {
|
|
3062
|
+
tag: {
|
|
3063
|
+
key: { _in: tags },
|
|
3064
|
+
is_active: { _eq: true }
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
});
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
if (filters.minPrice !== void 0 || filters.maxPrice !== void 0) {
|
|
3071
|
+
const priceCondition = {};
|
|
3072
|
+
if (filters.minPrice !== void 0) priceCondition._gte = filters.minPrice;
|
|
3073
|
+
if (filters.maxPrice !== void 0) priceCondition._lte = filters.maxPrice;
|
|
3074
|
+
whereConditions.push({
|
|
3075
|
+
product_variants: {
|
|
3076
|
+
product_variant_prices: {
|
|
3077
|
+
price: priceCondition,
|
|
3078
|
+
price_type: { _in: ["CLIENT", "SALE"] },
|
|
3079
|
+
_or: [
|
|
3080
|
+
{ chain_id: { _eq: chainId } },
|
|
3081
|
+
{ chain_id: { _is_null: true } }
|
|
3082
|
+
]
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
});
|
|
3086
|
+
}
|
|
3087
|
+
if (filters.attributes) {
|
|
3088
|
+
for (const [attrName, values] of Object.entries(filters.attributes)) {
|
|
3089
|
+
if (values.length > 0) {
|
|
3090
|
+
whereConditions.push({
|
|
3091
|
+
_or: [
|
|
3092
|
+
{
|
|
3093
|
+
product_attributes: {
|
|
3094
|
+
attribute: { name: { _eq: attrName } },
|
|
3095
|
+
value: { _in: values }
|
|
3096
|
+
}
|
|
3097
|
+
},
|
|
3098
|
+
{
|
|
3099
|
+
product_variants: {
|
|
3100
|
+
product_variant_attributes: {
|
|
3101
|
+
attribute: { name: { _eq: attrName } },
|
|
3102
|
+
value: { _in: values }
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
]
|
|
3107
|
+
});
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
return whereConditions.length > 0 ? { _and: whereConditions } : {};
|
|
3112
|
+
}
|
|
3113
|
+
function hasMeaningfulCategoryListFilters(filters) {
|
|
3114
|
+
if (!filters) return false;
|
|
3115
|
+
return Boolean(
|
|
3116
|
+
filters.search || filters.brandId || filters.minPrice !== void 0 || filters.maxPrice !== void 0 || (filters.tags?.some((tag) => tag.trim().length > 0) ?? false) || Object.values(filters.attributes ?? {}).some(
|
|
3117
|
+
(values) => values.length > 0
|
|
3118
|
+
)
|
|
3119
|
+
);
|
|
3120
|
+
}
|
|
3121
|
+
async function getWebsiteAvailabilityByVariantId(graphqlUrl, getHeaders, variantIds, chainId) {
|
|
3122
|
+
const uniqueVariantIds = uniqueValues2(variantIds);
|
|
3123
|
+
const availabilityByVariantId = /* @__PURE__ */ new Map();
|
|
3124
|
+
for (const chunk of chunkArray2(
|
|
3125
|
+
uniqueVariantIds,
|
|
3126
|
+
MAX_AVAILABILITY_VARIANT_IDS2
|
|
3127
|
+
)) {
|
|
3128
|
+
const data = await executeGraphQL(
|
|
3129
|
+
graphqlUrl,
|
|
3130
|
+
CHECK_AVAILABILITY,
|
|
3131
|
+
{ variantIds: chunk, chainId },
|
|
3132
|
+
getHeaders
|
|
3133
|
+
);
|
|
3134
|
+
for (const item of data.websiteProductAvailability ?? []) {
|
|
3135
|
+
availabilityByVariantId.set(item.product_variant_id, {
|
|
3136
|
+
inStock: item.in_stock
|
|
3137
|
+
});
|
|
3138
|
+
}
|
|
3139
|
+
}
|
|
3140
|
+
return availabilityByVariantId;
|
|
3141
|
+
}
|
|
3142
|
+
async function filterProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId) {
|
|
3143
|
+
const variantIds = products.flatMap(
|
|
3144
|
+
(product) => product.variants.map((variant) => variant.id)
|
|
3145
|
+
);
|
|
3146
|
+
if (variantIds.length === 0) return [];
|
|
3147
|
+
const inStockVariantIds = new Set(
|
|
3148
|
+
Array.from(
|
|
3149
|
+
(await getWebsiteAvailabilityByVariantId(
|
|
3150
|
+
graphqlUrl,
|
|
3151
|
+
getHeaders,
|
|
3152
|
+
variantIds,
|
|
3153
|
+
chainId
|
|
3154
|
+
)).entries()
|
|
3155
|
+
).filter(([, item]) => item.inStock).map(([variantId]) => variantId)
|
|
3156
|
+
);
|
|
3157
|
+
return products.map((product) => ({
|
|
3158
|
+
...product,
|
|
3159
|
+
variants: product.variants.map((variant) => {
|
|
3160
|
+
const prices = selectPublicPrices(variant.prices, chainId);
|
|
3161
|
+
return {
|
|
3162
|
+
...variant,
|
|
3163
|
+
prices,
|
|
3164
|
+
pricing: resolveProductPricing(prices)
|
|
3165
|
+
};
|
|
3166
|
+
}).filter(
|
|
3167
|
+
(variant) => inStockVariantIds.has(variant.id) && variant.pricing.currentPrice != null
|
|
3168
|
+
)
|
|
3169
|
+
})).filter((product) => product.variants.length > 0);
|
|
3170
|
+
}
|
|
3171
|
+
async function filterSeoProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId) {
|
|
3172
|
+
const variantIds = products.flatMap(
|
|
3173
|
+
(product) => product.product_variants.map((variant) => variant.id)
|
|
3174
|
+
);
|
|
3175
|
+
if (variantIds.length === 0) return [];
|
|
3176
|
+
const inStockVariantIds = new Set(
|
|
3177
|
+
Array.from(
|
|
3178
|
+
(await getWebsiteAvailabilityByVariantId(
|
|
3179
|
+
graphqlUrl,
|
|
3180
|
+
getHeaders,
|
|
3181
|
+
variantIds,
|
|
3182
|
+
chainId
|
|
3183
|
+
)).entries()
|
|
3184
|
+
).filter(([, item]) => item.inStock).map(([variantId]) => variantId)
|
|
3185
|
+
);
|
|
3186
|
+
return products.filter(
|
|
3187
|
+
(product) => product.product_variants.some((variant) => {
|
|
3188
|
+
const prices = variant.product_variant_prices.map(mapPrice);
|
|
3189
|
+
const pricing = resolveProductPricing(
|
|
3190
|
+
selectPublicPrices(prices, chainId)
|
|
3191
|
+
);
|
|
3192
|
+
return inStockVariantIds.has(variant.id) && pricing.currentPrice != null;
|
|
3193
|
+
})
|
|
3194
|
+
);
|
|
3195
|
+
}
|
|
3196
|
+
function mapProductSeoEntry(raw, storageUrl) {
|
|
3197
|
+
if (!raw.slug) return null;
|
|
3198
|
+
return {
|
|
3199
|
+
id: raw.id,
|
|
3200
|
+
name: raw.name,
|
|
3201
|
+
slug: raw.slug,
|
|
3202
|
+
createdAt: raw.created_at,
|
|
3203
|
+
updatedAt: raw.updated_at,
|
|
3204
|
+
seoTitle: raw.seo_title ?? null,
|
|
3205
|
+
seoDescription: raw.seo_description ?? null,
|
|
3206
|
+
seoImage: mapSeoMedia(raw, storageUrl),
|
|
3207
|
+
primaryImage: raw.product_media[0] ? mapMedia(raw.product_media[0], storageUrl) : null,
|
|
3208
|
+
category: raw.category,
|
|
3209
|
+
subcategory: raw.subcategory
|
|
3210
|
+
};
|
|
3211
|
+
}
|
|
3212
|
+
function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
3213
|
+
const mapSingleProductResult = async (data) => {
|
|
3214
|
+
const ctx = getContext();
|
|
3215
|
+
if (!ctx.chainId) {
|
|
3216
|
+
throw new WebsiteError(
|
|
3217
|
+
"This website has no chain configured. Catalog requires a chain_id.",
|
|
3218
|
+
"NO_CHAIN"
|
|
3219
|
+
);
|
|
3220
|
+
}
|
|
3221
|
+
const product = data.products?.[0];
|
|
3222
|
+
if (!product) return null;
|
|
3223
|
+
const publicAttributeLabels = buildPublicAttributeLabels(
|
|
3224
|
+
data.catalog_attribute_lists
|
|
3225
|
+
);
|
|
3226
|
+
const filteredProducts = await filterProductsForWebsiteStock(
|
|
3227
|
+
graphqlUrl,
|
|
3228
|
+
getHeaders,
|
|
3229
|
+
[mapProduct(product, storageUrl, publicAttributeLabels, ctx.chainId)],
|
|
3230
|
+
ctx.chainId
|
|
3231
|
+
);
|
|
3232
|
+
return filteredProducts[0] ?? null;
|
|
3233
|
+
};
|
|
3234
|
+
return {
|
|
3235
|
+
async getProductRecommendations(opts) {
|
|
3236
|
+
const ctx = getContext();
|
|
3237
|
+
const limit = opts.limit ?? 16;
|
|
3238
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 32) {
|
|
3239
|
+
throw new WebsiteError(
|
|
3240
|
+
"Recommendation limit must be an integer between 1 and 32.",
|
|
3241
|
+
"INVALID_RECOMMENDATION_LIMIT"
|
|
3242
|
+
);
|
|
3243
|
+
}
|
|
3244
|
+
const data = await executeGraphQL(
|
|
3245
|
+
graphqlUrl,
|
|
3246
|
+
WEBSITE_GET_PRODUCT_RECOMMENDATIONS,
|
|
3247
|
+
{
|
|
3248
|
+
websiteId: ctx.websiteId,
|
|
3249
|
+
productId: opts.productId,
|
|
3250
|
+
limit
|
|
3251
|
+
},
|
|
3252
|
+
getHeaders
|
|
3253
|
+
);
|
|
3254
|
+
if (!data.result.success) {
|
|
3255
|
+
throw new WebsiteError(
|
|
3256
|
+
data.result.errors.join("; ") || "Unable to resolve product recommendations",
|
|
3257
|
+
"PRODUCT_RECOMMENDATIONS_ERROR"
|
|
3258
|
+
);
|
|
3259
|
+
}
|
|
3260
|
+
const products = parseActionJson(
|
|
3261
|
+
data.result.products,
|
|
3262
|
+
[]
|
|
3263
|
+
);
|
|
3264
|
+
return {
|
|
3265
|
+
products: products.map(
|
|
3266
|
+
(product) => mapProductRecommendation(product, storageUrl)
|
|
3267
|
+
),
|
|
3268
|
+
strategy: data.result.strategy
|
|
3269
|
+
};
|
|
3270
|
+
},
|
|
3271
|
+
async getProductSeoEntries(opts) {
|
|
3272
|
+
const ctx = getContext();
|
|
3273
|
+
if (!ctx.chainId) {
|
|
3274
|
+
throw new WebsiteError(
|
|
3275
|
+
"This website has no chain configured. Catalog requires a chain_id.",
|
|
3276
|
+
"NO_CHAIN"
|
|
3277
|
+
);
|
|
3278
|
+
}
|
|
3279
|
+
const limit = opts?.limit ?? 100;
|
|
3280
|
+
const offset = opts?.offset ?? 0;
|
|
3281
|
+
const data = await executeGraphQL(
|
|
3282
|
+
graphqlUrl,
|
|
3283
|
+
GET_PRODUCT_SEO_ENTRIES,
|
|
3284
|
+
{
|
|
3285
|
+
companyId: ctx.companyId,
|
|
3286
|
+
chainId: ctx.chainId,
|
|
3287
|
+
websiteId: ctx.websiteId,
|
|
3288
|
+
limit: limit + 1,
|
|
3289
|
+
offset
|
|
3290
|
+
},
|
|
3291
|
+
getHeaders
|
|
3292
|
+
);
|
|
3293
|
+
const availableProducts = await filterSeoProductsForWebsiteStock(
|
|
3294
|
+
graphqlUrl,
|
|
3295
|
+
getHeaders,
|
|
3296
|
+
data.products ?? [],
|
|
3297
|
+
ctx.chainId
|
|
3298
|
+
);
|
|
3299
|
+
const entries = availableProducts.slice(0, limit).map((product) => mapProductSeoEntry(product, storageUrl)).filter((entry) => Boolean(entry));
|
|
3300
|
+
return {
|
|
3301
|
+
entries,
|
|
3302
|
+
pagination: {
|
|
3303
|
+
limit,
|
|
3304
|
+
offset,
|
|
3305
|
+
hasMore: (data.products ?? []).length > limit
|
|
3306
|
+
}
|
|
3307
|
+
};
|
|
3308
|
+
},
|
|
3309
|
+
async getCatalogPage(opts) {
|
|
3310
|
+
const ctx = getContext();
|
|
3311
|
+
if (!ctx.chainId) {
|
|
3312
|
+
throw new WebsiteError(
|
|
3313
|
+
"This website has no chain configured. Catalog requires a chain_id.",
|
|
3314
|
+
"NO_CHAIN"
|
|
3315
|
+
);
|
|
3316
|
+
}
|
|
3317
|
+
const chainId = ctx.chainId;
|
|
3318
|
+
const filters = {
|
|
3319
|
+
category_slug: opts.categorySlug,
|
|
3320
|
+
subcategory_slug: opts.subcategorySlug,
|
|
3321
|
+
tags: opts.tags,
|
|
3322
|
+
search: opts.search,
|
|
3323
|
+
brand_id: opts.brandId,
|
|
3324
|
+
min_price: opts.minPrice,
|
|
3325
|
+
max_price: opts.maxPrice,
|
|
3326
|
+
attributes_by_slug: opts.attributesBySlug,
|
|
3327
|
+
sort_by: opts.sortBy,
|
|
3328
|
+
limit: opts.limit,
|
|
3329
|
+
offset: opts.offset
|
|
3330
|
+
};
|
|
3331
|
+
const data = await executeGraphQL(
|
|
3332
|
+
graphqlUrl,
|
|
3333
|
+
WEBSITE_GET_CATALOG_PAGE,
|
|
3334
|
+
{
|
|
3335
|
+
websiteId: ctx.websiteId,
|
|
3336
|
+
categoryListKey: opts.categoryListKey,
|
|
3337
|
+
attributeListKey: opts.attributeListKey,
|
|
3338
|
+
filters,
|
|
3339
|
+
include: opts.include
|
|
3340
|
+
},
|
|
3341
|
+
getHeaders
|
|
3342
|
+
);
|
|
3343
|
+
if (!data.result.success) {
|
|
3344
|
+
throw new WebsiteError(
|
|
3345
|
+
data.result.errors.join("; ") || "Unable to resolve catalog page",
|
|
3346
|
+
"CATALOG_PAGE_ERROR"
|
|
3347
|
+
);
|
|
3348
|
+
}
|
|
3349
|
+
const products = parseActionJson(data.result.products, []);
|
|
3350
|
+
const categoryList = parseActionJson(
|
|
3351
|
+
data.result.category_list,
|
|
3352
|
+
null
|
|
3353
|
+
);
|
|
3354
|
+
const attributeList = parseActionJson(
|
|
3355
|
+
data.result.attribute_list,
|
|
3356
|
+
null
|
|
3357
|
+
);
|
|
3358
|
+
const filtersResult = parseActionJson(
|
|
3359
|
+
data.result.filters,
|
|
3360
|
+
null
|
|
3361
|
+
);
|
|
3362
|
+
const brands = parseActionJson(data.result.brands, []);
|
|
3363
|
+
const pagination = parseActionJson(
|
|
3364
|
+
data.result.pagination,
|
|
3365
|
+
{ limit: opts.limit ?? 24, offset: opts.offset ?? 0, total: 0, hasMore: false }
|
|
3366
|
+
);
|
|
3367
|
+
const appliedFilters = parseActionJson(
|
|
3368
|
+
data.result.applied_filters,
|
|
3369
|
+
{}
|
|
3370
|
+
);
|
|
3371
|
+
const debug = parseActionJson(
|
|
3372
|
+
data.result.debug,
|
|
3373
|
+
null
|
|
3374
|
+
);
|
|
3375
|
+
const labels = buildPublicAttributeLabels(
|
|
3376
|
+
attributeList ? [attributeList] : []
|
|
3377
|
+
);
|
|
3378
|
+
return {
|
|
3379
|
+
products: products.map(
|
|
3380
|
+
(product) => mapProduct(product, storageUrl, labels, chainId)
|
|
3381
|
+
),
|
|
3382
|
+
categories: categoryList ? mapCategoryList(categoryList) : null,
|
|
3383
|
+
attributes: attributeList ? mapAttributeList(attributeList) : null,
|
|
3384
|
+
filters: filtersResult,
|
|
3385
|
+
brands,
|
|
3386
|
+
pagination,
|
|
3387
|
+
appliedFilters,
|
|
3388
|
+
debug
|
|
3389
|
+
};
|
|
3390
|
+
},
|
|
3391
|
+
async getProducts(opts) {
|
|
3392
|
+
const ctx = getContext();
|
|
3393
|
+
if (!ctx.chainId) {
|
|
3394
|
+
throw new WebsiteError(
|
|
3395
|
+
"This website has no chain configured. Catalog requires a chain_id.",
|
|
3396
|
+
"NO_CHAIN"
|
|
3397
|
+
);
|
|
3398
|
+
}
|
|
3399
|
+
const chainId = ctx.chainId;
|
|
3400
|
+
const where = buildProductWhere(opts ?? {}, chainId);
|
|
3401
|
+
const orderBy = buildOrderBy(opts?.sortBy);
|
|
3402
|
+
const variables = {
|
|
3403
|
+
companyId: ctx.companyId,
|
|
3404
|
+
chainId,
|
|
3405
|
+
websiteId: ctx.websiteId,
|
|
3406
|
+
limit: opts?.limit ?? 20,
|
|
3407
|
+
offset: opts?.offset ?? 0,
|
|
3408
|
+
where
|
|
3409
|
+
};
|
|
3410
|
+
if (orderBy) {
|
|
3411
|
+
variables.orderBy = orderBy;
|
|
3412
|
+
}
|
|
3413
|
+
const data = await executeGraphQL(graphqlUrl, GET_PRODUCTS, variables, getHeaders);
|
|
3414
|
+
const publicAttributeLabels = buildPublicAttributeLabels(
|
|
3415
|
+
data.catalog_attribute_lists
|
|
3416
|
+
);
|
|
3417
|
+
let products = await filterProductsForWebsiteStock(
|
|
3418
|
+
graphqlUrl,
|
|
3419
|
+
getHeaders,
|
|
3420
|
+
(data.products ?? []).map(
|
|
3421
|
+
(p) => mapProduct(p, storageUrl, publicAttributeLabels, chainId)
|
|
3422
|
+
),
|
|
3423
|
+
chainId
|
|
3424
|
+
);
|
|
3425
|
+
if (opts?.minPrice !== void 0 || opts?.maxPrice !== void 0) {
|
|
3426
|
+
products = products.filter(
|
|
3427
|
+
(product) => product.variants.some((variant) => {
|
|
3428
|
+
const currentPrice = variant.pricing.currentPrice;
|
|
3429
|
+
if (currentPrice == null) return false;
|
|
3430
|
+
if (opts.minPrice !== void 0 && currentPrice < opts.minPrice) {
|
|
3431
|
+
return false;
|
|
3432
|
+
}
|
|
3433
|
+
return !(opts.maxPrice !== void 0 && currentPrice > opts.maxPrice);
|
|
3434
|
+
})
|
|
3435
|
+
);
|
|
3436
|
+
}
|
|
3437
|
+
if (opts?.sortBy === "price_asc") {
|
|
3438
|
+
products.sort((a, b) => getMinVariantPrice(a) - getMinVariantPrice(b));
|
|
3439
|
+
} else if (opts?.sortBy === "price_desc") {
|
|
3440
|
+
products.sort((a, b) => getMinVariantPrice(b) - getMinVariantPrice(a));
|
|
3441
|
+
} else if (opts?.search && !opts?.sortBy) {
|
|
3442
|
+
const search = opts.search;
|
|
3443
|
+
products.sort((a, b) => {
|
|
3444
|
+
const diff = scoreSearchRelevance(b, search) - scoreSearchRelevance(a, search);
|
|
3445
|
+
return diff !== 0 ? diff : a.name.localeCompare(b.name);
|
|
3446
|
+
});
|
|
3447
|
+
}
|
|
3448
|
+
return products;
|
|
3449
|
+
},
|
|
3450
|
+
async getProduct(productId) {
|
|
3451
|
+
const ctx = getContext();
|
|
3452
|
+
if (!ctx.chainId) {
|
|
3453
|
+
throw new WebsiteError(
|
|
3454
|
+
"This website has no chain configured. Catalog requires a chain_id.",
|
|
3455
|
+
"NO_CHAIN"
|
|
3456
|
+
);
|
|
3457
|
+
}
|
|
3458
|
+
const data = await executeGraphQL(
|
|
3459
|
+
graphqlUrl,
|
|
3460
|
+
GET_PRODUCT_BY_PK,
|
|
3461
|
+
{
|
|
3462
|
+
productId,
|
|
3463
|
+
companyId: ctx.companyId,
|
|
3464
|
+
chainId: ctx.chainId,
|
|
3465
|
+
websiteId: ctx.websiteId
|
|
3466
|
+
},
|
|
3467
|
+
getHeaders
|
|
3468
|
+
);
|
|
3469
|
+
return mapSingleProductResult(data);
|
|
3470
|
+
},
|
|
3471
|
+
async getProductBySlug(slug) {
|
|
3472
|
+
const ctx = getContext();
|
|
3473
|
+
if (!ctx.chainId) {
|
|
3474
|
+
throw new WebsiteError(
|
|
3475
|
+
"This website has no chain configured. Catalog requires a chain_id.",
|
|
3476
|
+
"NO_CHAIN"
|
|
3477
|
+
);
|
|
3478
|
+
}
|
|
3479
|
+
const data = await executeGraphQL(
|
|
3480
|
+
graphqlUrl,
|
|
3481
|
+
GET_PRODUCT_BY_SLUG,
|
|
3482
|
+
{
|
|
3483
|
+
slug,
|
|
3484
|
+
companyId: ctx.companyId,
|
|
3485
|
+
chainId: ctx.chainId,
|
|
3486
|
+
websiteId: ctx.websiteId
|
|
3487
|
+
},
|
|
3488
|
+
getHeaders
|
|
3489
|
+
);
|
|
3490
|
+
return mapSingleProductResult(data);
|
|
3491
|
+
},
|
|
3492
|
+
async getCategories() {
|
|
3493
|
+
const data = await executeGraphQL(graphqlUrl, GET_CATEGORIES, {}, getHeaders);
|
|
3494
|
+
return (data.product_categories ?? []).map(mapCategory);
|
|
3495
|
+
},
|
|
3496
|
+
async getCategoryList(opts) {
|
|
3497
|
+
const ctx = getContext();
|
|
3498
|
+
const data = await executeGraphQL(
|
|
3499
|
+
graphqlUrl,
|
|
3500
|
+
GET_CATALOG_CATEGORY_LIST,
|
|
3501
|
+
{
|
|
3502
|
+
companyId: ctx.companyId,
|
|
3503
|
+
key: opts.key
|
|
3504
|
+
},
|
|
3505
|
+
getHeaders
|
|
3506
|
+
);
|
|
3507
|
+
const list = data.list?.[0] ?? null;
|
|
3508
|
+
if (!list) return null;
|
|
3509
|
+
if (opts.onlyApplicable || hasMeaningfulCategoryListFilters(opts.filters)) {
|
|
3510
|
+
if (!ctx.chainId) {
|
|
3511
|
+
throw new WebsiteError(
|
|
3512
|
+
"This website has no chain configured. Applicable category lists require a chain_id.",
|
|
3513
|
+
"NO_CHAIN"
|
|
3514
|
+
);
|
|
3515
|
+
}
|
|
3516
|
+
const chainId = ctx.chainId;
|
|
3517
|
+
const applicableProductsData = await executeGraphQL(
|
|
3518
|
+
graphqlUrl,
|
|
3519
|
+
GET_PRODUCT_FILTERS,
|
|
3520
|
+
{
|
|
3521
|
+
companyId: ctx.companyId,
|
|
3522
|
+
chainId,
|
|
3523
|
+
attributeListKey: DEFAULT_FILTER_ATTRIBUTE_LIST_KEY,
|
|
3524
|
+
where: buildProductWhere(opts.filters ?? {}, chainId)
|
|
3525
|
+
},
|
|
3526
|
+
getHeaders
|
|
3527
|
+
);
|
|
3528
|
+
const variantIds = (applicableProductsData.products ?? []).flatMap(
|
|
3529
|
+
(product) => product.product_variants.map((variant) => variant.id)
|
|
3530
|
+
);
|
|
3531
|
+
const availabilityByVariantId = await getWebsiteAvailabilityByVariantId(
|
|
3532
|
+
graphqlUrl,
|
|
3533
|
+
getHeaders,
|
|
3534
|
+
variantIds,
|
|
3535
|
+
chainId
|
|
3536
|
+
);
|
|
3537
|
+
const applicableCategories = /* @__PURE__ */ new Set();
|
|
3538
|
+
const applicableSubcategoryIds = /* @__PURE__ */ new Set();
|
|
3539
|
+
for (const product of applicableProductsData.products ?? []) {
|
|
3540
|
+
const hasVisibleVariant = product.product_variants.some((variant) => {
|
|
3541
|
+
const prices = selectPublicPrices(
|
|
3542
|
+
variant.product_variant_prices.map(mapPrice),
|
|
3543
|
+
chainId
|
|
3544
|
+
);
|
|
3545
|
+
return prices.length > 0 && (availabilityByVariantId.get(variant.id)?.inStock ?? false);
|
|
3546
|
+
});
|
|
3547
|
+
if (!hasVisibleVariant) continue;
|
|
3548
|
+
applicableCategories.add(product.category);
|
|
3549
|
+
if (product.subcategory) {
|
|
3550
|
+
applicableSubcategoryIds.add(product.subcategory);
|
|
3551
|
+
}
|
|
3552
|
+
}
|
|
3553
|
+
return filterCategoryListByApplicability(
|
|
3554
|
+
list,
|
|
3555
|
+
applicableCategories,
|
|
3556
|
+
applicableSubcategoryIds
|
|
3557
|
+
);
|
|
3558
|
+
}
|
|
3559
|
+
return mapCategoryList(list);
|
|
3560
|
+
},
|
|
3561
|
+
async getAttributeList(opts) {
|
|
3562
|
+
const ctx = getContext();
|
|
3563
|
+
if (opts.onlyApplicable || hasMeaningfulAttributeListFilters(opts.filters)) {
|
|
3564
|
+
if (!ctx.websiteId) {
|
|
3565
|
+
throw new WebsiteError(
|
|
3566
|
+
"This client has no website configured. Contextual attribute lists require a website_id.",
|
|
3567
|
+
"NO_WEBSITE"
|
|
3568
|
+
);
|
|
3569
|
+
}
|
|
3570
|
+
const data2 = await executeGraphQL(
|
|
3571
|
+
graphqlUrl,
|
|
3572
|
+
GET_CONTEXTUAL_CATALOG_ATTRIBUTE_LIST,
|
|
3573
|
+
{
|
|
3574
|
+
websiteId: ctx.websiteId,
|
|
3575
|
+
key: opts.key,
|
|
3576
|
+
onlyApplicable: opts.onlyApplicable ? true : void 0,
|
|
3577
|
+
filters: opts.filters
|
|
3578
|
+
},
|
|
3579
|
+
getHeaders
|
|
3580
|
+
);
|
|
3581
|
+
const result = data2.websiteGetAttributeList;
|
|
3582
|
+
if (!result.success) {
|
|
3583
|
+
throw new WebsiteError(
|
|
3584
|
+
result.errors?.join("; ") || "Unable to resolve attribute list",
|
|
3585
|
+
"ATTRIBUTE_LIST_ERROR"
|
|
3586
|
+
);
|
|
3587
|
+
}
|
|
3588
|
+
return result.list ? mapContextualAttributeList(result.list) : null;
|
|
3589
|
+
}
|
|
3590
|
+
const data = await executeGraphQL(
|
|
3591
|
+
graphqlUrl,
|
|
3592
|
+
GET_CATALOG_ATTRIBUTE_LIST,
|
|
3593
|
+
{
|
|
3594
|
+
companyId: ctx.companyId,
|
|
3595
|
+
key: opts.key
|
|
3596
|
+
},
|
|
3597
|
+
getHeaders
|
|
3598
|
+
);
|
|
3599
|
+
const list = data.list?.[0] ?? null;
|
|
3600
|
+
return list ? mapAttributeList(list) : null;
|
|
3601
|
+
},
|
|
3602
|
+
async getBrands() {
|
|
3603
|
+
const ctx = getContext();
|
|
3604
|
+
if (!ctx.chainId) {
|
|
3605
|
+
throw new WebsiteError(
|
|
3606
|
+
"This website has no chain configured. Brands require a chain_id.",
|
|
3607
|
+
"NO_CHAIN"
|
|
3608
|
+
);
|
|
3609
|
+
}
|
|
3610
|
+
const data = await executeGraphQL(
|
|
3611
|
+
graphqlUrl,
|
|
3612
|
+
GET_BRANDS,
|
|
3613
|
+
{ companyId: ctx.companyId, chainId: ctx.chainId },
|
|
3614
|
+
getHeaders
|
|
3615
|
+
);
|
|
3616
|
+
return data.brands ?? [];
|
|
3617
|
+
},
|
|
3618
|
+
async getProductFilters(opts) {
|
|
3619
|
+
const ctx = getContext();
|
|
3620
|
+
if (!ctx.chainId) {
|
|
3621
|
+
throw new WebsiteError(
|
|
3622
|
+
"This website has no chain configured. Catalog filters require a chain_id.",
|
|
3623
|
+
"NO_CHAIN"
|
|
3624
|
+
);
|
|
3625
|
+
}
|
|
3626
|
+
const chainId = ctx.chainId;
|
|
3627
|
+
const data = await executeGraphQL(
|
|
3628
|
+
graphqlUrl,
|
|
3629
|
+
GET_PRODUCT_FILTERS,
|
|
3630
|
+
{
|
|
3631
|
+
companyId: ctx.companyId,
|
|
3632
|
+
chainId,
|
|
3633
|
+
attributeListKey: opts?.attributeListKey ?? DEFAULT_FILTER_ATTRIBUTE_LIST_KEY,
|
|
3634
|
+
where: {
|
|
3635
|
+
...opts?.categoryName ? { category: { _eq: opts.categoryName } } : {},
|
|
3636
|
+
...opts?.subcategoryId ? { subcategory: { _eq: opts.subcategoryId } } : {}
|
|
3637
|
+
}
|
|
3638
|
+
},
|
|
3639
|
+
getHeaders
|
|
3640
|
+
);
|
|
3641
|
+
const categoryCounts = /* @__PURE__ */ new Map();
|
|
3642
|
+
const colorCounts = /* @__PURE__ */ new Map();
|
|
3643
|
+
const brandCounts = /* @__PURE__ */ new Map();
|
|
3644
|
+
const publicPrices = [];
|
|
3645
|
+
const attributeValueCounts = /* @__PURE__ */ new Map();
|
|
3646
|
+
const variantIds = (data.products ?? []).flatMap(
|
|
3647
|
+
(product) => product.product_variants.map((variant) => variant.id)
|
|
3648
|
+
);
|
|
3649
|
+
const availabilityByVariantId = await getWebsiteAvailabilityByVariantId(
|
|
3650
|
+
graphqlUrl,
|
|
3651
|
+
getHeaders,
|
|
3652
|
+
variantIds,
|
|
3653
|
+
chainId
|
|
3654
|
+
);
|
|
3655
|
+
const publicAttributeLabels = buildPublicAttributeLabels(
|
|
3656
|
+
data.catalog_attribute_lists
|
|
3657
|
+
);
|
|
3658
|
+
const configuredAttributeIds = new Set(
|
|
3659
|
+
(data.catalog_attribute_lists?.[0]?.items ?? []).map(
|
|
3660
|
+
(item) => item.attribute_id
|
|
3661
|
+
)
|
|
3662
|
+
);
|
|
3663
|
+
const registerAttributeValue = (raw) => {
|
|
3664
|
+
if (!configuredAttributeIds.has(raw.attribute.id)) return;
|
|
3665
|
+
const valueCode = attributeValueCode(raw);
|
|
3666
|
+
const value = raw.attribute_value ?? null;
|
|
3667
|
+
const counts = attributeValueCounts.get(raw.attribute.id) ?? /* @__PURE__ */ new Map();
|
|
3668
|
+
const current = counts.get(valueCode);
|
|
3669
|
+
counts.set(valueCode, {
|
|
3670
|
+
count: (current?.count ?? 0) + 1,
|
|
3671
|
+
value: value ?? current?.value ?? null
|
|
3672
|
+
});
|
|
3673
|
+
attributeValueCounts.set(raw.attribute.id, counts);
|
|
3674
|
+
};
|
|
3675
|
+
for (const product of data.products ?? []) {
|
|
3676
|
+
if (opts?.categoryName && product.category !== opts.categoryName) {
|
|
3677
|
+
continue;
|
|
3678
|
+
}
|
|
3679
|
+
const visibleVariants = product.product_variants.map((variant) => ({
|
|
3680
|
+
...variant,
|
|
3681
|
+
prices: selectPublicPrices(
|
|
3682
|
+
variant.product_variant_prices.map(mapPrice),
|
|
3683
|
+
chainId
|
|
3684
|
+
),
|
|
3685
|
+
inStock: availabilityByVariantId.get(variant.id)?.inStock ?? false
|
|
3686
|
+
})).filter((variant) => variant.inStock && variant.prices.length > 0);
|
|
3687
|
+
if (visibleVariants.length === 0) continue;
|
|
3688
|
+
categoryCounts.set(
|
|
3689
|
+
product.category,
|
|
3690
|
+
(categoryCounts.get(product.category) ?? 0) + 1
|
|
3691
|
+
);
|
|
3692
|
+
if (product.color) {
|
|
3693
|
+
colorCounts.set(
|
|
3694
|
+
product.color,
|
|
3695
|
+
(colorCounts.get(product.color) ?? 0) + 1
|
|
3696
|
+
);
|
|
3697
|
+
}
|
|
3698
|
+
if (product.brand) {
|
|
3699
|
+
const current = brandCounts.get(product.brand.id);
|
|
3700
|
+
brandCounts.set(product.brand.id, {
|
|
3701
|
+
id: product.brand.id,
|
|
3702
|
+
name: product.brand.name,
|
|
3703
|
+
count: (current?.count ?? 0) + 1
|
|
3704
|
+
});
|
|
3705
|
+
}
|
|
3706
|
+
for (const variant of visibleVariants) {
|
|
3707
|
+
for (const price of variant.prices) {
|
|
3708
|
+
publicPrices.push(price.price);
|
|
3709
|
+
}
|
|
3710
|
+
if (opts?.categoryName) {
|
|
3711
|
+
for (const attribute of variant.product_variant_attributes) {
|
|
3712
|
+
registerAttributeValue(attribute);
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
}
|
|
3716
|
+
if (opts?.categoryName) {
|
|
3717
|
+
for (const attribute of product.product_attributes) {
|
|
3718
|
+
registerAttributeValue(attribute);
|
|
3719
|
+
}
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
const attributes = opts?.categoryName ? (data.catalog_attribute_lists?.[0]?.items ?? []).map((item) => {
|
|
3723
|
+
const counts = attributeValueCounts.get(item.attribute_id);
|
|
3724
|
+
if (!counts || counts.size === 0) return null;
|
|
3725
|
+
return {
|
|
3726
|
+
id: item.attribute_id,
|
|
3727
|
+
name: item.public_label,
|
|
3728
|
+
values: Array.from(counts.entries()).map(([valueCode, countItem]) => ({
|
|
3729
|
+
value: publicAttributeLabels.valueLabelsById.get(
|
|
3730
|
+
countItem.value?.id ?? ""
|
|
3731
|
+
) ?? publicAttributeLabels.valueLabelsByAttributeAndCode.get(
|
|
3732
|
+
attributeValueLabelKey(item.attribute_id, valueCode)
|
|
3733
|
+
) ?? valueCode,
|
|
3734
|
+
count: countItem.count,
|
|
3735
|
+
attributeValueId: countItem.value?.id ?? null,
|
|
3736
|
+
code: countItem.value?.code ?? valueCode,
|
|
3737
|
+
metadata: countItem.value?.metadata ?? null,
|
|
3738
|
+
sortOrder: countItem.value?.sort_order ?? null
|
|
3739
|
+
})).sort((a, b) => {
|
|
3740
|
+
const orderA = a.sortOrder ?? Number.MAX_SAFE_INTEGER;
|
|
3741
|
+
const orderB = b.sortOrder ?? Number.MAX_SAFE_INTEGER;
|
|
3742
|
+
return orderA === orderB ? a.value.localeCompare(b.value) : orderA - orderB;
|
|
3743
|
+
})
|
|
3744
|
+
};
|
|
3745
|
+
}).filter((item) => Boolean(item)) : [];
|
|
3746
|
+
const minPrice = publicPrices.length > 0 ? Math.min(...publicPrices) : 0;
|
|
3747
|
+
const maxPrice = publicPrices.length > 0 ? Math.max(...publicPrices) : 0;
|
|
3748
|
+
return {
|
|
3749
|
+
categories: Array.from(categoryCounts.entries()).map(([name, count]) => ({ name, count })).sort((a, b) => a.name.localeCompare(b.name)),
|
|
3750
|
+
brands: Array.from(brandCounts.values()).sort(
|
|
3751
|
+
(a, b) => a.name.localeCompare(b.name)
|
|
3752
|
+
),
|
|
3753
|
+
colors: Array.from(colorCounts.entries()).map(([value, count]) => ({ value, count })).sort((a, b) => a.value.localeCompare(b.value)),
|
|
3754
|
+
priceRange: {
|
|
3755
|
+
min: minPrice,
|
|
3756
|
+
max: maxPrice
|
|
3757
|
+
},
|
|
3758
|
+
attributes
|
|
3759
|
+
};
|
|
3760
|
+
},
|
|
3761
|
+
async getSearchSuggestions(opts) {
|
|
3762
|
+
if (!opts.query || opts.query.trim().length === 0) return [];
|
|
3763
|
+
const ctx = getContext();
|
|
3764
|
+
if (!ctx.chainId) {
|
|
3765
|
+
throw new WebsiteError(
|
|
3766
|
+
"This website has no chain configured. Search suggestions require a chain_id.",
|
|
3767
|
+
"NO_CHAIN"
|
|
3768
|
+
);
|
|
3769
|
+
}
|
|
3770
|
+
const term = `%${opts.query}%`;
|
|
3771
|
+
const limit = opts.limit ?? 8;
|
|
3772
|
+
const data = await executeGraphQL(
|
|
3773
|
+
graphqlUrl,
|
|
3774
|
+
GET_SEARCH_SUGGESTIONS,
|
|
3775
|
+
{
|
|
3776
|
+
companyId: ctx.companyId,
|
|
3777
|
+
chainId: ctx.chainId,
|
|
3778
|
+
term,
|
|
3779
|
+
productLimit: Math.min(limit, 5),
|
|
3780
|
+
brandLimit: Math.max(limit - 5, 3)
|
|
3781
|
+
},
|
|
3782
|
+
getHeaders
|
|
3783
|
+
);
|
|
3784
|
+
const suggestions = [];
|
|
3785
|
+
for (const p of data.products ?? []) {
|
|
3786
|
+
suggestions.push({ type: "product", text: p.name, id: p.id });
|
|
3787
|
+
}
|
|
3788
|
+
for (const b of data.brands ?? []) {
|
|
3789
|
+
suggestions.push({ type: "brand", text: b.name, id: b.id });
|
|
3790
|
+
}
|
|
3791
|
+
return suggestions.slice(0, limit);
|
|
3792
|
+
}
|
|
3793
|
+
};
|
|
3794
|
+
}
|
|
3795
|
+
|
|
3796
|
+
// libs/sdk/src/modules/checkout.ts
|
|
3797
|
+
function requireCartSession2(storage) {
|
|
3798
|
+
const session = storage.get();
|
|
3799
|
+
if (!session) {
|
|
3800
|
+
throw new WebsiteError(
|
|
3801
|
+
"No guest cart session found",
|
|
3802
|
+
"CART_SESSION_MISSING"
|
|
3803
|
+
);
|
|
3804
|
+
}
|
|
3805
|
+
return session;
|
|
3806
|
+
}
|
|
3807
|
+
function mapCartItem2(raw) {
|
|
3808
|
+
return {
|
|
3809
|
+
id: raw.id,
|
|
3810
|
+
productVariantId: raw.product_variant_id,
|
|
3811
|
+
quantity: raw.quantity,
|
|
3812
|
+
productVariant: {
|
|
3813
|
+
sku: raw.product_variant.sku,
|
|
3814
|
+
product: raw.product_variant.product,
|
|
3815
|
+
prices: raw.product_variant.prices.map((price) => ({
|
|
3816
|
+
id: price.id,
|
|
3817
|
+
price: price.price,
|
|
3818
|
+
priceType: price.price_type,
|
|
3819
|
+
chainId: price.chain_id
|
|
3820
|
+
}))
|
|
3821
|
+
}
|
|
3822
|
+
};
|
|
3823
|
+
}
|
|
3824
|
+
function mapCart2(raw) {
|
|
3825
|
+
return {
|
|
3826
|
+
id: raw.id,
|
|
3827
|
+
items: raw.items.map(mapCartItem2),
|
|
3828
|
+
totals: {
|
|
3829
|
+
subtotal: raw.total_without_tax,
|
|
3830
|
+
tax: raw.tax,
|
|
3831
|
+
discount: raw.discount,
|
|
3832
|
+
total: raw.total_with_tax
|
|
3833
|
+
},
|
|
3834
|
+
status: raw.status,
|
|
3835
|
+
createdAt: raw.created_at,
|
|
3836
|
+
updatedAt: raw.updated_at
|
|
3837
|
+
};
|
|
3838
|
+
}
|
|
3839
|
+
function mapCouponResult(raw) {
|
|
3840
|
+
return {
|
|
3841
|
+
success: raw.success,
|
|
3842
|
+
orderId: raw.order_id,
|
|
3843
|
+
couponId: raw.coupon_id,
|
|
3844
|
+
code: raw.code,
|
|
3845
|
+
name: raw.name,
|
|
3846
|
+
couponType: raw.coupon_type,
|
|
3847
|
+
discountAmount: Number(raw.discount_amount ?? 0),
|
|
3848
|
+
eligibleSubtotal: Number(raw.eligible_subtotal ?? 0),
|
|
3849
|
+
message: raw.message,
|
|
3850
|
+
cart: raw.cart ? mapCart2(raw.cart) : null,
|
|
3851
|
+
errors: raw.errors
|
|
3852
|
+
};
|
|
3853
|
+
}
|
|
3854
|
+
function createCheckoutModule(graphqlUrl, getHeaders, getContext, cartSessionStorage) {
|
|
3855
|
+
return {
|
|
3856
|
+
async getLocationOptions() {
|
|
3857
|
+
const context = getContext();
|
|
3858
|
+
const data = await executeGraphQL(
|
|
3859
|
+
graphqlUrl,
|
|
3860
|
+
GET_LOCATION_OPTIONS,
|
|
3861
|
+
{ websiteId: context.websiteId },
|
|
3862
|
+
getHeaders
|
|
3863
|
+
);
|
|
3864
|
+
const result = data.websiteGetLocationOptions;
|
|
3865
|
+
if (!result.success) {
|
|
3866
|
+
throw new CheckoutError(result.errors ?? ["LOCATION_OPTIONS_FAILED"]);
|
|
3867
|
+
}
|
|
3868
|
+
return {
|
|
3869
|
+
countryCode: result.country_code,
|
|
3870
|
+
departments: result.departments.map((department) => ({
|
|
3871
|
+
departmentId: department.department_id,
|
|
3872
|
+
departmentName: department.department_name,
|
|
3873
|
+
departmentCode: department.department_code,
|
|
3874
|
+
municipalities: department.municipalities.map((municipality) => ({
|
|
3875
|
+
municipalityId: municipality.municipality_id,
|
|
3876
|
+
municipalityName: municipality.municipality_name,
|
|
3877
|
+
municipalityCode: municipality.municipality_code,
|
|
3878
|
+
departmentId: municipality.department_id,
|
|
3879
|
+
departmentCode: municipality.department_code
|
|
3880
|
+
}))
|
|
3881
|
+
}))
|
|
3882
|
+
};
|
|
3883
|
+
},
|
|
3884
|
+
async getDeliveryOptions() {
|
|
3885
|
+
const context = getContext();
|
|
3886
|
+
const data = await executeGraphQL(
|
|
3887
|
+
graphqlUrl,
|
|
3888
|
+
GET_DELIVERY_OPTIONS,
|
|
3889
|
+
{ websiteId: context.websiteId },
|
|
3890
|
+
getHeaders
|
|
3891
|
+
);
|
|
3892
|
+
const result = data.websiteGetDeliveryOptions;
|
|
3893
|
+
if (!result.success) {
|
|
3894
|
+
throw new CheckoutError(result.errors ?? ["DELIVERY_OPTIONS_FAILED"]);
|
|
3895
|
+
}
|
|
3896
|
+
return result.delivery_options.map((option) => ({
|
|
3897
|
+
id: option.id,
|
|
3898
|
+
fulfillmentFamily: option.fulfillment_family,
|
|
3899
|
+
label: option.label,
|
|
3900
|
+
description: option.description,
|
|
3901
|
+
isEnabled: option.is_enabled,
|
|
3902
|
+
pickupInfo: option.pickup_info ? {
|
|
3903
|
+
label: option.pickup_info.label,
|
|
3904
|
+
address: option.pickup_info.address,
|
|
3905
|
+
schedule: option.pickup_info.schedule,
|
|
3906
|
+
notes: option.pickup_info.notes
|
|
3907
|
+
} : null
|
|
3908
|
+
}));
|
|
3909
|
+
},
|
|
3910
|
+
async getShippingQuotes(input) {
|
|
3911
|
+
const context = getContext();
|
|
3912
|
+
const session = cartSessionStorage.get();
|
|
3913
|
+
const items = session ? null : input.items?.length ? input.items : null;
|
|
3914
|
+
const data = await executeGraphQL(
|
|
3915
|
+
graphqlUrl,
|
|
3916
|
+
GET_SHIPPING_QUOTES,
|
|
3917
|
+
{
|
|
3918
|
+
websiteId: context.websiteId,
|
|
3919
|
+
orderId: session?.orderId ?? null,
|
|
3920
|
+
cartToken: session?.cartToken ?? null,
|
|
3921
|
+
items: items?.map((item) => ({
|
|
3922
|
+
product_variant_id: item.productVariantId,
|
|
3923
|
+
quantity: item.quantity
|
|
3924
|
+
})),
|
|
3925
|
+
address: input.address
|
|
3926
|
+
},
|
|
3927
|
+
getHeaders
|
|
3928
|
+
);
|
|
3929
|
+
const result = data.websiteGetShippingQuotes;
|
|
3930
|
+
if (!result.success) {
|
|
3931
|
+
throw new CheckoutError(result.errors ?? ["SHIPPING_QUOTES_FAILED"]);
|
|
3932
|
+
}
|
|
3933
|
+
return result.quotes.map((quote) => ({
|
|
3934
|
+
quoteId: quote.quote_id,
|
|
3935
|
+
shippingServiceId: quote.shipping_service_id,
|
|
3936
|
+
label: quote.label,
|
|
3937
|
+
serviceName: quote.service_name,
|
|
3938
|
+
baseAmount: quote.base_amount,
|
|
3939
|
+
amount: quote.amount,
|
|
3940
|
+
currency: quote.currency,
|
|
3941
|
+
collectionMode: quote.collection_mode,
|
|
3942
|
+
totalPolicy: quote.total_policy,
|
|
3943
|
+
estimatedDelivery: quote.estimated_delivery,
|
|
3944
|
+
freeShippingEligible: quote.free_shipping_eligible,
|
|
3945
|
+
freeShippingApplied: quote.free_shipping_applied,
|
|
3946
|
+
freeShippingThreshold: quote.free_shipping_threshold,
|
|
3947
|
+
freeShippingRemaining: quote.free_shipping_remaining,
|
|
3948
|
+
freeShippingEligibleSubtotal: quote.free_shipping_eligible_subtotal
|
|
3949
|
+
}));
|
|
3950
|
+
},
|
|
3951
|
+
async getShippingPolicy() {
|
|
3952
|
+
const context = getContext();
|
|
3953
|
+
const data = await executeGraphQL(
|
|
3954
|
+
graphqlUrl,
|
|
3955
|
+
GET_SHIPPING_POLICY,
|
|
3956
|
+
{ websiteId: context.websiteId },
|
|
3957
|
+
getHeaders
|
|
3958
|
+
);
|
|
3959
|
+
const result = data.websiteGetShippingPolicy;
|
|
3960
|
+
if (!result.success || !result.policy) {
|
|
3961
|
+
throw new CheckoutError(result.errors ?? ["SHIPPING_POLICY_FAILED"]);
|
|
3962
|
+
}
|
|
3963
|
+
return {
|
|
3964
|
+
freeShippingEnabled: result.policy.free_shipping_enabled,
|
|
3965
|
+
freeShippingThreshold: result.policy.free_shipping_threshold,
|
|
3966
|
+
currency: result.policy.currency
|
|
3967
|
+
};
|
|
3968
|
+
},
|
|
3969
|
+
async getFreeShippingProgress(input) {
|
|
3970
|
+
if (input.items.length === 0) {
|
|
3971
|
+
throw new WebsiteError(
|
|
3972
|
+
"At least one cart item is required",
|
|
3973
|
+
"CART_ITEMS_REQUIRED"
|
|
3974
|
+
);
|
|
3975
|
+
}
|
|
3976
|
+
const context = getContext();
|
|
3977
|
+
const data = await executeGraphQL(
|
|
3978
|
+
graphqlUrl,
|
|
3979
|
+
GET_FREE_SHIPPING_PROGRESS,
|
|
3980
|
+
{
|
|
3981
|
+
websiteId: context.websiteId,
|
|
3982
|
+
items: input.items.map((item) => ({
|
|
3983
|
+
product_variant_id: item.productVariantId,
|
|
3984
|
+
quantity: item.quantity
|
|
3985
|
+
}))
|
|
3986
|
+
},
|
|
3987
|
+
getHeaders
|
|
3988
|
+
);
|
|
3989
|
+
const result = data.websiteGetFreeShippingProgress;
|
|
3990
|
+
if (!result.success || !result.progress) {
|
|
3991
|
+
throw new CheckoutError(
|
|
3992
|
+
result.errors ?? ["FREE_SHIPPING_PROGRESS_FAILED"]
|
|
3993
|
+
);
|
|
3994
|
+
}
|
|
3995
|
+
return {
|
|
3996
|
+
enabled: result.progress.enabled,
|
|
3997
|
+
threshold: result.progress.threshold,
|
|
3998
|
+
remaining: result.progress.remaining,
|
|
3999
|
+
eligibleSubtotal: result.progress.eligible_subtotal,
|
|
4000
|
+
thresholdReached: result.progress.threshold_reached,
|
|
4001
|
+
currency: result.progress.currency
|
|
4002
|
+
};
|
|
4003
|
+
},
|
|
4004
|
+
async getPaymentMethods() {
|
|
4005
|
+
const context = getContext();
|
|
4006
|
+
const data = await executeGraphQL(
|
|
4007
|
+
graphqlUrl,
|
|
4008
|
+
GET_PAYMENT_METHODS,
|
|
4009
|
+
{ websiteId: context.websiteId },
|
|
4010
|
+
getHeaders
|
|
4011
|
+
);
|
|
4012
|
+
const result = data.websiteGetPaymentMethods;
|
|
4013
|
+
if (!result.success) {
|
|
4014
|
+
throw new CheckoutError(result.errors ?? ["PAYMENT_METHODS_FAILED"]);
|
|
4015
|
+
}
|
|
4016
|
+
return result.payment_methods.map((method) => ({
|
|
4017
|
+
id: method.id,
|
|
4018
|
+
code: method.code,
|
|
4019
|
+
label: method.label,
|
|
4020
|
+
paymentMethodType: method.payment_method_type,
|
|
4021
|
+
gatewayCode: method.gateway_code,
|
|
4022
|
+
description: method.description,
|
|
4023
|
+
isEnabled: method.is_enabled,
|
|
4024
|
+
sortOrder: method.sort_order,
|
|
4025
|
+
requiresPaymentSession: method.requires_payment_session,
|
|
4026
|
+
publicConfig: method.public_config
|
|
4027
|
+
}));
|
|
4028
|
+
},
|
|
4029
|
+
async getPaymentOptions(input) {
|
|
4030
|
+
const context = getContext();
|
|
4031
|
+
const data = await executeGraphQL(
|
|
4032
|
+
graphqlUrl,
|
|
4033
|
+
GET_PAYMENT_OPTIONS,
|
|
4034
|
+
{
|
|
4035
|
+
websiteId: context.websiteId,
|
|
4036
|
+
fulfillmentFamily: input.fulfillmentFamily
|
|
4037
|
+
},
|
|
4038
|
+
getHeaders
|
|
4039
|
+
);
|
|
4040
|
+
const result = data.websiteGetPaymentOptions;
|
|
4041
|
+
if (!result.success) {
|
|
4042
|
+
throw new CheckoutError(result.errors ?? ["PAYMENT_OPTIONS_FAILED"]);
|
|
4043
|
+
}
|
|
4044
|
+
return result.payment_options.map((option) => ({
|
|
4045
|
+
id: option.id,
|
|
4046
|
+
mode: option.mode,
|
|
4047
|
+
fulfillmentFamily: option.fulfillment_family,
|
|
4048
|
+
label: option.label,
|
|
4049
|
+
description: option.description,
|
|
4050
|
+
requiresPaymentMethod: option.requires_payment_method,
|
|
4051
|
+
allowsInitialPayment: option.allows_initial_payment,
|
|
4052
|
+
initialPaymentPolicy: option.initial_payment_policy,
|
|
4053
|
+
requiresInitialPayment: option.requires_initial_payment,
|
|
4054
|
+
minInitialAmount: option.min_initial_amount,
|
|
4055
|
+
suggestedInitialAmount: option.suggested_initial_amount,
|
|
4056
|
+
sortOrder: option.sort_order,
|
|
4057
|
+
methods: option.methods.map((method) => ({
|
|
4058
|
+
id: method.id,
|
|
4059
|
+
paymentMethodId: method.payment_method_id,
|
|
4060
|
+
code: method.code,
|
|
4061
|
+
label: method.label,
|
|
4062
|
+
paymentMethodType: method.payment_method_type,
|
|
4063
|
+
gatewayCode: method.gateway_code,
|
|
4064
|
+
description: method.description,
|
|
4065
|
+
instructions: method.instructions,
|
|
4066
|
+
qrFileId: method.qr_file_id,
|
|
4067
|
+
requiresPaymentSession: method.requires_payment_session,
|
|
4068
|
+
publicConfig: method.public_config ?? {},
|
|
4069
|
+
sortOrder: method.sort_order
|
|
4070
|
+
}))
|
|
4071
|
+
}));
|
|
4072
|
+
},
|
|
4073
|
+
async validateCoupon(input) {
|
|
4074
|
+
const session = requireCartSession2(cartSessionStorage);
|
|
4075
|
+
const data = await executeGraphQL(
|
|
4076
|
+
graphqlUrl,
|
|
4077
|
+
VALIDATE_COUPON,
|
|
4078
|
+
{
|
|
4079
|
+
orderId: session.orderId,
|
|
4080
|
+
cartToken: session.cartToken,
|
|
4081
|
+
code: input.code
|
|
4082
|
+
},
|
|
4083
|
+
getHeaders
|
|
4084
|
+
);
|
|
4085
|
+
return mapCouponResult(data.websiteValidateCoupon);
|
|
4086
|
+
},
|
|
4087
|
+
async applyCoupon(input) {
|
|
4088
|
+
const session = requireCartSession2(cartSessionStorage);
|
|
4089
|
+
const data = await executeGraphQL(
|
|
4090
|
+
graphqlUrl,
|
|
4091
|
+
APPLY_COUPON,
|
|
4092
|
+
{
|
|
4093
|
+
orderId: session.orderId,
|
|
4094
|
+
cartToken: session.cartToken,
|
|
4095
|
+
code: input.code
|
|
4096
|
+
},
|
|
4097
|
+
getHeaders
|
|
4098
|
+
);
|
|
4099
|
+
return mapCouponResult(data.websiteApplyCoupon);
|
|
4100
|
+
},
|
|
4101
|
+
async removeCoupon(input) {
|
|
4102
|
+
const session = requireCartSession2(cartSessionStorage);
|
|
4103
|
+
const data = await executeGraphQL(
|
|
4104
|
+
graphqlUrl,
|
|
4105
|
+
REMOVE_COUPON,
|
|
4106
|
+
{
|
|
4107
|
+
orderId: session.orderId,
|
|
4108
|
+
cartToken: session.cartToken,
|
|
4109
|
+
couponId: input.couponId
|
|
4110
|
+
},
|
|
4111
|
+
getHeaders
|
|
4112
|
+
);
|
|
4113
|
+
return mapCouponResult(data.websiteRemoveCoupon);
|
|
4114
|
+
},
|
|
4115
|
+
async submit(input) {
|
|
4116
|
+
const session = requireCartSession2(cartSessionStorage);
|
|
4117
|
+
const data = await executeGraphQL(
|
|
4118
|
+
graphqlUrl,
|
|
4119
|
+
CHECKOUT,
|
|
4120
|
+
{
|
|
4121
|
+
orderId: session.orderId,
|
|
4122
|
+
cartToken: session.cartToken,
|
|
4123
|
+
contact: input.contact,
|
|
4124
|
+
fulfillmentFamily: input.fulfillmentFamily,
|
|
4125
|
+
address: input.address,
|
|
4126
|
+
shippingQuoteId: input.shippingQuoteId,
|
|
4127
|
+
paymentMode: input.paymentMode,
|
|
4128
|
+
paymentMethodId: input.paymentMethodId,
|
|
4129
|
+
buyerAccountOptIn: input.buyerAccountOptIn,
|
|
4130
|
+
initialPayment: input.initialPayment ? {
|
|
4131
|
+
amount: input.initialPayment.amount,
|
|
4132
|
+
payment_method_id: input.initialPayment.paymentMethodId
|
|
4133
|
+
} : void 0
|
|
4134
|
+
},
|
|
4135
|
+
getHeaders
|
|
4136
|
+
);
|
|
4137
|
+
const result = data.websiteCheckout;
|
|
4138
|
+
if (!result.success && result.errors && result.errors.length > 0) {
|
|
4139
|
+
throw new CheckoutError(result.errors);
|
|
4140
|
+
}
|
|
4141
|
+
if (result.success && !result.requires_payment_session) {
|
|
4142
|
+
cartSessionStorage.clear();
|
|
4143
|
+
}
|
|
4144
|
+
return {
|
|
4145
|
+
success: result.success,
|
|
4146
|
+
orderId: result.order_id,
|
|
4147
|
+
orderNumber: result.order_number,
|
|
4148
|
+
paymentMethodId: result.payment_method_id,
|
|
4149
|
+
publicOrderToken: result.public_order_token,
|
|
4150
|
+
requiresPaymentSession: Boolean(result.requires_payment_session),
|
|
4151
|
+
buyerAccountInvitationRequested: Boolean(
|
|
4152
|
+
result.buyer_account_invitation_requested
|
|
4153
|
+
),
|
|
4154
|
+
errors: result.errors
|
|
4155
|
+
};
|
|
4156
|
+
},
|
|
4157
|
+
async createPaymentSession(input) {
|
|
4158
|
+
const session = cartSessionStorage.get();
|
|
4159
|
+
const orderId = input.orderId ?? session?.orderId;
|
|
4160
|
+
if (!orderId || !input.publicOrderToken && !session?.cartToken) {
|
|
4161
|
+
throw new WebsiteError(
|
|
4162
|
+
"Payment retry requires an order and public order token",
|
|
4163
|
+
"PAYMENT_SESSION_CONTEXT_MISSING"
|
|
4164
|
+
);
|
|
4165
|
+
}
|
|
4166
|
+
const data = await executeGraphQL(
|
|
4167
|
+
graphqlUrl,
|
|
4168
|
+
CREATE_PAYMENT_SESSION,
|
|
4169
|
+
{
|
|
4170
|
+
orderId,
|
|
4171
|
+
cartToken: input.publicOrderToken ? null : session?.cartToken,
|
|
4172
|
+
publicOrderToken: input.publicOrderToken ?? null,
|
|
4173
|
+
paymentMethodId: input.paymentMethodId,
|
|
4174
|
+
returnUrl: input.returnUrl
|
|
4175
|
+
},
|
|
4176
|
+
getHeaders
|
|
4177
|
+
);
|
|
4178
|
+
const result = data.websiteCreatePaymentSession;
|
|
4179
|
+
if (result.success && result.payment_session_id && (result.integration_mode !== "redirect" || !result.redirect_url)) {
|
|
4180
|
+
throw new WebsiteError(
|
|
4181
|
+
"Payment gateway did not provide a redirect URL",
|
|
4182
|
+
"PAYMENT_GATEWAY_REDIRECT_NOT_AVAILABLE"
|
|
4183
|
+
);
|
|
4184
|
+
}
|
|
4185
|
+
if (result.success) {
|
|
4186
|
+
cartSessionStorage.clear();
|
|
4187
|
+
}
|
|
4188
|
+
return {
|
|
4189
|
+
success: result.success,
|
|
4190
|
+
orderId: result.order_id,
|
|
4191
|
+
paymentMethodId: result.payment_method_id,
|
|
4192
|
+
paymentSessionId: result.payment_session_id,
|
|
4193
|
+
gatewayCode: result.gateway_code,
|
|
4194
|
+
integrationMode: result.integration_mode === "redirect" ? "redirect" : null,
|
|
4195
|
+
status: result.status,
|
|
4196
|
+
redirectUrl: result.redirect_url,
|
|
4197
|
+
publicPayload: result.public_payload,
|
|
4198
|
+
errors: result.errors
|
|
4199
|
+
};
|
|
4200
|
+
},
|
|
4201
|
+
async reconcilePaymentSession(input) {
|
|
4202
|
+
const data = await executeGraphQL(
|
|
4203
|
+
graphqlUrl,
|
|
4204
|
+
RECONCILE_PAYMENT_SESSION,
|
|
4205
|
+
{
|
|
4206
|
+
orderId: input.orderId,
|
|
4207
|
+
publicOrderToken: input.publicOrderToken,
|
|
4208
|
+
paymentSessionId: input.paymentSessionId,
|
|
4209
|
+
gatewayTransactionId: input.gatewayTransactionId
|
|
4210
|
+
},
|
|
4211
|
+
getHeaders
|
|
4212
|
+
);
|
|
4213
|
+
const result = data.websiteReconcilePaymentSession;
|
|
4214
|
+
return {
|
|
4215
|
+
success: result.success,
|
|
4216
|
+
orderId: result.order_id,
|
|
4217
|
+
paymentMethodId: result.payment_method_id,
|
|
4218
|
+
paymentSessionId: result.payment_session_id,
|
|
4219
|
+
gatewayCode: result.gateway_code,
|
|
4220
|
+
integrationMode: result.integration_mode === "redirect" ? "redirect" : null,
|
|
4221
|
+
status: result.status,
|
|
4222
|
+
redirectUrl: result.redirect_url,
|
|
4223
|
+
publicPayload: result.public_payload,
|
|
4224
|
+
errors: result.errors
|
|
4225
|
+
};
|
|
4226
|
+
}
|
|
4227
|
+
};
|
|
4228
|
+
}
|
|
4229
|
+
|
|
4230
|
+
// libs/sdk/src/modules/content.ts
|
|
4231
|
+
var DEFAULT_LOCALE = "es-CO";
|
|
4232
|
+
function mapEntry(raw) {
|
|
4233
|
+
return {
|
|
4234
|
+
id: raw.id,
|
|
4235
|
+
key: raw.key,
|
|
4236
|
+
categoryKey: raw.category_key,
|
|
4237
|
+
name: raw.name,
|
|
4238
|
+
locale: raw.locale,
|
|
4239
|
+
status: raw.status,
|
|
4240
|
+
tags: raw.tags ?? [],
|
|
4241
|
+
contentJson: raw.content_json ?? {},
|
|
4242
|
+
resolvedFields: raw.resolved_fields ?? {},
|
|
4243
|
+
publishedAt: raw.published_at,
|
|
4244
|
+
updatedAt: raw.updated_at,
|
|
4245
|
+
contentType: {
|
|
4246
|
+
id: raw.content_type.id,
|
|
4247
|
+
key: raw.content_type.key,
|
|
4248
|
+
name: raw.content_type.name,
|
|
4249
|
+
kind: raw.content_type.kind
|
|
4250
|
+
}
|
|
4251
|
+
};
|
|
4252
|
+
}
|
|
4253
|
+
function baseVariables(ctx, opts) {
|
|
4254
|
+
return {
|
|
4255
|
+
websiteId: ctx.websiteId,
|
|
4256
|
+
locale: opts?.locale ?? DEFAULT_LOCALE
|
|
4257
|
+
};
|
|
4258
|
+
}
|
|
4259
|
+
function isRecord(value) {
|
|
4260
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
4261
|
+
}
|
|
4262
|
+
function readOptionalText(value) {
|
|
4263
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
4264
|
+
}
|
|
4265
|
+
function enrichMediaValue(value, storageUrl) {
|
|
4266
|
+
const fileId = typeof value.fileId === "string" && value.fileId.trim() ? value.fileId.trim() : "";
|
|
4267
|
+
if (!fileId) return value;
|
|
4268
|
+
const urls = buildStorageMediaUrls(storageUrl, fileId);
|
|
4269
|
+
return {
|
|
4270
|
+
...value,
|
|
4271
|
+
...urls,
|
|
4272
|
+
url: value.url ?? urls.originalUrl
|
|
4273
|
+
};
|
|
4274
|
+
}
|
|
4275
|
+
function normalizeRichTextNode(value, storageUrl) {
|
|
4276
|
+
const type = value.type;
|
|
4277
|
+
const attrs = isRecord(value.attrs) ? value.attrs : null;
|
|
4278
|
+
if (attrs && (type === "bacanoImage" || type === "bacanoVideo") && typeof attrs.fileId === "string") {
|
|
4279
|
+
const urls = buildStorageMediaUrls(storageUrl, attrs.fileId);
|
|
4280
|
+
const altText = readOptionalText(attrs.altText) ?? readOptionalText(attrs.alt);
|
|
4281
|
+
const caption = readOptionalText(attrs.caption);
|
|
4282
|
+
const showCaption = Boolean(caption) && attrs.showCaption !== false;
|
|
4283
|
+
return {
|
|
4284
|
+
...value,
|
|
4285
|
+
attrs: {
|
|
4286
|
+
...attrs,
|
|
4287
|
+
...urls,
|
|
4288
|
+
alt: altText,
|
|
4289
|
+
altText,
|
|
4290
|
+
caption,
|
|
4291
|
+
showCaption,
|
|
4292
|
+
src: type === "bacanoImage" ? urls.detailUrl : urls.originalUrl
|
|
4293
|
+
}
|
|
4294
|
+
};
|
|
4295
|
+
}
|
|
4296
|
+
return value;
|
|
4297
|
+
}
|
|
4298
|
+
function normalizeContentMedia(value, storageUrl) {
|
|
4299
|
+
if (Array.isArray(value)) {
|
|
4300
|
+
return value.map((item) => normalizeContentMedia(item, storageUrl));
|
|
4301
|
+
}
|
|
4302
|
+
if (!isRecord(value)) return value;
|
|
4303
|
+
const normalized = normalizeRichTextNode(value, storageUrl);
|
|
4304
|
+
const enriched = typeof normalized.fileId === "string" && typeof normalized.mediaKind === "string" ? enrichMediaValue(normalized, storageUrl) : normalized;
|
|
4305
|
+
return Object.fromEntries(
|
|
4306
|
+
Object.entries(enriched).map(([key, entryValue]) => [
|
|
4307
|
+
key,
|
|
4308
|
+
normalizeContentMedia(entryValue, storageUrl)
|
|
4309
|
+
])
|
|
4310
|
+
);
|
|
4311
|
+
}
|
|
4312
|
+
function getEntries(data) {
|
|
4313
|
+
if (!data.result?.success) return [];
|
|
4314
|
+
return data.result.entries ?? [];
|
|
4315
|
+
}
|
|
4316
|
+
function chooseOne(data, storageUrl) {
|
|
4317
|
+
const entry = getEntries(data)[0] ?? null;
|
|
4318
|
+
return entry ? mapEntryWithMedia(entry, storageUrl) : null;
|
|
4319
|
+
}
|
|
4320
|
+
function chooseMany(data, storageUrl) {
|
|
4321
|
+
return getEntries(data).map((entry) => mapEntryWithMedia(entry, storageUrl));
|
|
4322
|
+
}
|
|
4323
|
+
function mapEntryWithMedia(raw, storageUrl) {
|
|
4324
|
+
return {
|
|
4325
|
+
...mapEntry(raw),
|
|
4326
|
+
contentJson: normalizeContentMedia(
|
|
4327
|
+
raw.content_json ?? {},
|
|
4328
|
+
storageUrl
|
|
4329
|
+
),
|
|
4330
|
+
resolvedFields: normalizeContentMedia(
|
|
4331
|
+
raw.resolved_fields ?? {},
|
|
4332
|
+
storageUrl
|
|
4333
|
+
)
|
|
4334
|
+
};
|
|
4335
|
+
}
|
|
4336
|
+
function mapProductContentBlock(raw, storageUrl) {
|
|
4337
|
+
return {
|
|
4338
|
+
id: raw.id,
|
|
4339
|
+
productId: raw.product_id,
|
|
4340
|
+
locale: raw.locale,
|
|
4341
|
+
contentJson: normalizeContentMedia(
|
|
4342
|
+
raw.content_json ?? {},
|
|
4343
|
+
storageUrl
|
|
4344
|
+
),
|
|
4345
|
+
resolvedFields: normalizeContentMedia(
|
|
4346
|
+
raw.resolved_fields ?? {},
|
|
4347
|
+
storageUrl
|
|
4348
|
+
),
|
|
4349
|
+
sortOrder: raw.sort_order,
|
|
4350
|
+
updatedAt: raw.updated_at,
|
|
4351
|
+
contentType: {
|
|
4352
|
+
id: raw.content_type.id,
|
|
4353
|
+
key: raw.content_type.key,
|
|
4354
|
+
name: raw.content_type.name,
|
|
4355
|
+
kind: raw.content_type.kind
|
|
4356
|
+
}
|
|
4357
|
+
};
|
|
4358
|
+
}
|
|
4359
|
+
function createContentModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
4360
|
+
return {
|
|
4361
|
+
async getByKey(key, opts) {
|
|
4362
|
+
const ctx = getContext();
|
|
4363
|
+
const data = await executeGraphQL(
|
|
4364
|
+
graphqlUrl,
|
|
4365
|
+
WEBSITE_GET_CONTENT,
|
|
4366
|
+
{ ...baseVariables(ctx, opts), key },
|
|
4367
|
+
getHeaders
|
|
4368
|
+
);
|
|
4369
|
+
return chooseOne(data, storageUrl);
|
|
4370
|
+
},
|
|
4371
|
+
async getByCategory(categoryKey, opts) {
|
|
4372
|
+
const ctx = getContext();
|
|
4373
|
+
const data = await executeGraphQL(
|
|
4374
|
+
graphqlUrl,
|
|
4375
|
+
WEBSITE_GET_CONTENT,
|
|
4376
|
+
{ ...baseVariables(ctx, opts), categoryKey },
|
|
4377
|
+
getHeaders
|
|
4378
|
+
);
|
|
4379
|
+
return chooseMany(data, storageUrl);
|
|
4380
|
+
},
|
|
4381
|
+
async getByTag(tag, opts) {
|
|
4382
|
+
const ctx = getContext();
|
|
4383
|
+
const data = await executeGraphQL(
|
|
4384
|
+
graphqlUrl,
|
|
4385
|
+
WEBSITE_GET_CONTENT,
|
|
4386
|
+
{ ...baseVariables(ctx, opts), tag },
|
|
4387
|
+
getHeaders
|
|
4388
|
+
);
|
|
4389
|
+
return chooseMany(data, storageUrl);
|
|
4390
|
+
},
|
|
4391
|
+
async getForProduct(productId, opts) {
|
|
4392
|
+
const ctx = getContext();
|
|
4393
|
+
const data = await executeGraphQL(
|
|
4394
|
+
graphqlUrl,
|
|
4395
|
+
WEBSITE_GET_PRODUCT_CONTENT,
|
|
4396
|
+
{
|
|
4397
|
+
websiteId: ctx.websiteId,
|
|
4398
|
+
productId,
|
|
4399
|
+
locale: opts?.locale ?? DEFAULT_LOCALE
|
|
4400
|
+
},
|
|
4401
|
+
getHeaders
|
|
4402
|
+
);
|
|
4403
|
+
if (!data.result?.success) return [];
|
|
4404
|
+
return (data.result.blocks ?? []).map(
|
|
4405
|
+
(block) => mapProductContentBlock(block, storageUrl)
|
|
4406
|
+
);
|
|
4407
|
+
}
|
|
4408
|
+
};
|
|
4409
|
+
}
|
|
4410
|
+
|
|
4411
|
+
// libs/sdk/src/modules/orders.ts
|
|
4412
|
+
function mapOrderItem(raw) {
|
|
4413
|
+
return {
|
|
4414
|
+
id: raw.id,
|
|
4415
|
+
productVariantId: raw.product_variant_id,
|
|
4416
|
+
quantity: raw.quantity,
|
|
4417
|
+
productVariant: {
|
|
4418
|
+
sku: raw.product_variant.sku,
|
|
4419
|
+
product: raw.product_variant.product
|
|
4420
|
+
}
|
|
4421
|
+
};
|
|
4422
|
+
}
|
|
4423
|
+
function mapOrder(raw) {
|
|
4424
|
+
return {
|
|
4425
|
+
id: raw.id,
|
|
4426
|
+
orderNumber: raw.order_number,
|
|
4427
|
+
status: raw.status,
|
|
4428
|
+
fulfillmentFamily: raw.fulfillment_family,
|
|
4429
|
+
discount: raw.discount,
|
|
4430
|
+
tax: raw.tax,
|
|
4431
|
+
subtotal: raw.total_without_tax,
|
|
4432
|
+
total: raw.total_with_tax,
|
|
4433
|
+
totalWithDiscount: raw.total_with_discount,
|
|
4434
|
+
items: raw.order_product_variants.map(mapOrderItem),
|
|
4435
|
+
createdAt: raw.created_at,
|
|
4436
|
+
updatedAt: raw.updated_at
|
|
4437
|
+
};
|
|
4438
|
+
}
|
|
4439
|
+
function mapPublicOrder(raw) {
|
|
4440
|
+
return {
|
|
4441
|
+
orderId: raw.order_id,
|
|
4442
|
+
orderNumber: raw.order_number,
|
|
4443
|
+
status: raw.status,
|
|
4444
|
+
paymentStatus: raw.payment_status,
|
|
4445
|
+
fulfillmentFamily: raw.fulfillment_family,
|
|
4446
|
+
delivery: {
|
|
4447
|
+
fulfillmentFamily: raw.delivery.fulfillment_family,
|
|
4448
|
+
shipping: raw.delivery.shipping ? {
|
|
4449
|
+
label: raw.delivery.shipping.label,
|
|
4450
|
+
amount: raw.delivery.shipping.amount,
|
|
4451
|
+
methodType: raw.delivery.shipping.method_type,
|
|
4452
|
+
collectionMode: raw.delivery.shipping.collection_mode,
|
|
4453
|
+
totalPolicy: raw.delivery.shipping.total_policy,
|
|
4454
|
+
address: raw.delivery.shipping.address
|
|
4455
|
+
} : null,
|
|
4456
|
+
pickup: raw.delivery.pickup
|
|
4457
|
+
},
|
|
4458
|
+
payment: {
|
|
4459
|
+
mode: raw.payment.mode,
|
|
4460
|
+
label: raw.payment.label,
|
|
4461
|
+
method: raw.payment.method ? {
|
|
4462
|
+
id: raw.payment.method.id,
|
|
4463
|
+
label: raw.payment.method.label,
|
|
4464
|
+
paymentMethodType: raw.payment.method.payment_method_type,
|
|
4465
|
+
gatewayCode: raw.payment.method.gateway_code,
|
|
4466
|
+
requiresPaymentSession: raw.payment.method.requires_payment_session
|
|
4467
|
+
} : null,
|
|
4468
|
+
initialPayment: raw.payment.initial_payment ? {
|
|
4469
|
+
amount: raw.payment.initial_payment.amount,
|
|
4470
|
+
paymentMethodId: raw.payment.initial_payment.payment_method_id,
|
|
4471
|
+
methodLabel: raw.payment.initial_payment.method_label,
|
|
4472
|
+
paymentMethodType: raw.payment.initial_payment.payment_method_type,
|
|
4473
|
+
gatewayCode: raw.payment.initial_payment.gateway_code,
|
|
4474
|
+
requiresPaymentSession: raw.payment.initial_payment.requires_payment_session
|
|
4475
|
+
} : null,
|
|
4476
|
+
requiresPaymentSession: raw.payment.requires_payment_session,
|
|
4477
|
+
nextAction: raw.payment.next_action,
|
|
4478
|
+
session: raw.payment.session ? {
|
|
4479
|
+
id: raw.payment.session.id,
|
|
4480
|
+
gatewayCode: raw.payment.session.gateway_code,
|
|
4481
|
+
integrationMode: raw.payment.session.integration_mode,
|
|
4482
|
+
status: raw.payment.session.status,
|
|
4483
|
+
reference: raw.payment.session.reference,
|
|
4484
|
+
amount: raw.payment.session.amount,
|
|
4485
|
+
currency: raw.payment.session.currency,
|
|
4486
|
+
gatewayTransactionId: raw.payment.session.gateway_transaction_id,
|
|
4487
|
+
errorMessage: raw.payment.session.error_message,
|
|
4488
|
+
expiresAt: raw.payment.session.expires_at,
|
|
4489
|
+
createdAt: raw.payment.session.created_at,
|
|
4490
|
+
updatedAt: raw.payment.session.updated_at
|
|
4491
|
+
} : null
|
|
4492
|
+
},
|
|
4493
|
+
totals: {
|
|
4494
|
+
subtotal: raw.totals.subtotal,
|
|
4495
|
+
tax: raw.totals.tax,
|
|
4496
|
+
discount: raw.totals.discount,
|
|
4497
|
+
total: raw.totals.total,
|
|
4498
|
+
totalWithDiscount: raw.totals.total_with_discount
|
|
4499
|
+
},
|
|
4500
|
+
items: raw.items.map((item) => ({
|
|
4501
|
+
id: item.id,
|
|
4502
|
+
productVariantId: item.product_variant_id,
|
|
4503
|
+
sku: item.sku,
|
|
4504
|
+
name: item.name,
|
|
4505
|
+
imageFileId: item.image_file_id,
|
|
4506
|
+
quantity: item.quantity,
|
|
4507
|
+
unitPrice: item.unit_price,
|
|
4508
|
+
lineTotal: item.line_total
|
|
4509
|
+
})),
|
|
4510
|
+
nextSteps: raw.next_steps,
|
|
4511
|
+
createdAt: raw.created_at,
|
|
4512
|
+
updatedAt: raw.updated_at
|
|
4513
|
+
};
|
|
4514
|
+
}
|
|
4515
|
+
function createOrdersModule(graphqlUrl, getHeaders, getContext) {
|
|
4516
|
+
async function requireAuth() {
|
|
4517
|
+
const headers = await getHeaders();
|
|
4518
|
+
if (!headers.Authorization) {
|
|
4519
|
+
throw new AuthError("You must be logged in to view orders");
|
|
4520
|
+
}
|
|
4521
|
+
return headers;
|
|
4522
|
+
}
|
|
4523
|
+
async function fetchPublicOrder(input) {
|
|
4524
|
+
const context = getContext();
|
|
4525
|
+
const data = await executeGraphQL(
|
|
4526
|
+
graphqlUrl,
|
|
4527
|
+
GET_PUBLIC_ORDER,
|
|
4528
|
+
{
|
|
4529
|
+
websiteId: context.websiteId,
|
|
4530
|
+
orderId: input.orderId,
|
|
4531
|
+
token: input.token
|
|
4532
|
+
},
|
|
4533
|
+
getHeaders
|
|
4534
|
+
);
|
|
4535
|
+
const result = data.websiteGetPublicOrder;
|
|
4536
|
+
if (!result.success) {
|
|
4537
|
+
if (result.errors?.includes("ORDER_NOT_FOUND")) return null;
|
|
4538
|
+
throw new CheckoutError(result.errors ?? ["PUBLIC_ORDER_FAILED"]);
|
|
4539
|
+
}
|
|
4540
|
+
return result.order ? mapPublicOrder(result.order) : null;
|
|
4541
|
+
}
|
|
4542
|
+
return {
|
|
4543
|
+
async list(opts) {
|
|
4544
|
+
const headers = await requireAuth();
|
|
4545
|
+
const data = await executeGraphQL(
|
|
4546
|
+
graphqlUrl,
|
|
4547
|
+
GET_ORDERS,
|
|
4548
|
+
{
|
|
4549
|
+
limit: opts?.limit ?? 20,
|
|
4550
|
+
offset: opts?.offset ?? 0
|
|
4551
|
+
},
|
|
4552
|
+
headers
|
|
4553
|
+
);
|
|
4554
|
+
return (data.orders ?? []).map(mapOrder);
|
|
4555
|
+
},
|
|
4556
|
+
async get(orderId) {
|
|
4557
|
+
const headers = await requireAuth();
|
|
4558
|
+
const data = await executeGraphQL(
|
|
4559
|
+
graphqlUrl,
|
|
4560
|
+
GET_ORDER_BY_PK,
|
|
4561
|
+
{ orderId },
|
|
4562
|
+
headers
|
|
4563
|
+
);
|
|
4564
|
+
const order = data.orders?.[0];
|
|
4565
|
+
return order ? mapOrder(order) : null;
|
|
4566
|
+
},
|
|
4567
|
+
async getPublicOrder(input) {
|
|
4568
|
+
return fetchPublicOrder(input);
|
|
4569
|
+
},
|
|
4570
|
+
async waitForPayment(input) {
|
|
4571
|
+
const intervalMs = Math.max(500, input.intervalMs ?? 1500);
|
|
4572
|
+
const timeoutMs = Math.max(intervalMs, input.timeoutMs ?? 12e4);
|
|
4573
|
+
const deadline = Date.now() + timeoutMs;
|
|
4574
|
+
while (Date.now() <= deadline) {
|
|
4575
|
+
const order = await fetchPublicOrder(input);
|
|
4576
|
+
if (!order) {
|
|
4577
|
+
throw new CheckoutError(["ORDER_NOT_FOUND"]);
|
|
4578
|
+
}
|
|
4579
|
+
const sessionStatus = order.payment.session?.status ?? null;
|
|
4580
|
+
if (order.paymentStatus === "PAID" || sessionStatus === "approved" || ["declined", "error", "expired", "cancelled"].includes(
|
|
4581
|
+
sessionStatus ?? ""
|
|
4582
|
+
)) {
|
|
4583
|
+
return order;
|
|
4584
|
+
}
|
|
4585
|
+
await wait(intervalMs);
|
|
4586
|
+
}
|
|
4587
|
+
throw new CheckoutError(["PAYMENT_STATUS_TIMEOUT"]);
|
|
4588
|
+
}
|
|
4589
|
+
};
|
|
4590
|
+
}
|
|
4591
|
+
function wait(ms) {
|
|
4592
|
+
return new Promise((resolve) => {
|
|
4593
|
+
setTimeout(resolve, ms);
|
|
4594
|
+
});
|
|
4595
|
+
}
|
|
4596
|
+
|
|
4597
|
+
// libs/sdk/src/modules/profile.ts
|
|
4598
|
+
function hasCanonicalLocation(address) {
|
|
4599
|
+
return Boolean(
|
|
4600
|
+
address.department_code && address.municipality_code && address.geo_department?.name && address.geo_municipality?.name
|
|
4601
|
+
);
|
|
4602
|
+
}
|
|
4603
|
+
function mapProfile(raw) {
|
|
4604
|
+
const primaryAddress = raw.party.shipping_addresses?.[0];
|
|
4605
|
+
return {
|
|
4606
|
+
id: raw.id,
|
|
4607
|
+
name: raw.party.first_name,
|
|
4608
|
+
lastname: raw.party.last_name,
|
|
4609
|
+
email: raw.party.email,
|
|
4610
|
+
cellphone: raw.party.phone_e164,
|
|
4611
|
+
phone: raw.party.phone_national_number,
|
|
4612
|
+
address: primaryAddress?.address ?? raw.party.address,
|
|
4613
|
+
city: primaryAddress?.geo_municipality?.name ?? raw.party.geo_municipality?.name ?? null,
|
|
4614
|
+
state: primaryAddress?.geo_department?.name ?? raw.party.geo_department?.name ?? null,
|
|
4615
|
+
country: primaryAddress?.geo_country?.name ?? raw.party.geo_country?.name ?? raw.party.country_code ?? null,
|
|
4616
|
+
birthday: raw.party.birthdate,
|
|
4617
|
+
companyName: raw.party.legal_name ?? raw.party.commercial_name
|
|
4618
|
+
};
|
|
4619
|
+
}
|
|
4620
|
+
function mapProfileAddresses(raw) {
|
|
4621
|
+
const party = raw.party;
|
|
4622
|
+
const recipientName = [party.first_name, party.last_name].filter(Boolean).join(" ") || party.display_name;
|
|
4623
|
+
const addresses = [];
|
|
4624
|
+
const shippingAddresses = party.shipping_addresses.filter(hasCanonicalLocation);
|
|
4625
|
+
const preferredShippingAddress = shippingAddresses.find((address) => address.is_primary) ?? shippingAddresses[0];
|
|
4626
|
+
if (party.address?.trim() && hasCanonicalLocation(party)) {
|
|
4627
|
+
addresses.push({
|
|
4628
|
+
id: raw.party_id,
|
|
4629
|
+
type: "PRIMARY",
|
|
4630
|
+
label: "Direcci\xF3n principal",
|
|
4631
|
+
isPrimary: !preferredShippingAddress,
|
|
4632
|
+
recipientName,
|
|
4633
|
+
cellphone: party.phone_e164 ?? party.phone_national_number,
|
|
4634
|
+
line1: party.address,
|
|
4635
|
+
line2: party.address_complement,
|
|
4636
|
+
notes: null,
|
|
4637
|
+
countryCode: party.country_code ?? "CO",
|
|
4638
|
+
countryName: party.geo_country?.name ?? party.country_code ?? "CO",
|
|
4639
|
+
departmentId: party.department_code,
|
|
4640
|
+
departmentCode: party.department_code,
|
|
4641
|
+
departmentName: party.geo_department.name,
|
|
4642
|
+
municipalityId: party.municipality_code,
|
|
4643
|
+
municipalityCode: party.municipality_code,
|
|
4644
|
+
municipalityName: party.geo_municipality.name
|
|
4645
|
+
});
|
|
4646
|
+
}
|
|
4647
|
+
addresses.push(
|
|
4648
|
+
...shippingAddresses.map((address) => ({
|
|
4649
|
+
id: address.id,
|
|
4650
|
+
type: "SHIPPING",
|
|
4651
|
+
label: address.label,
|
|
4652
|
+
isPrimary: address.id === preferredShippingAddress?.id,
|
|
4653
|
+
recipientName: address.recipient_name,
|
|
4654
|
+
cellphone: address.phone_e164,
|
|
4655
|
+
line1: address.address,
|
|
4656
|
+
line2: address.address_complement,
|
|
4657
|
+
notes: address.notes,
|
|
4658
|
+
countryCode: address.country_code,
|
|
4659
|
+
countryName: address.geo_country?.name ?? address.country_code,
|
|
4660
|
+
departmentId: address.department_code,
|
|
4661
|
+
departmentCode: address.department_code,
|
|
4662
|
+
departmentName: address.geo_department.name,
|
|
4663
|
+
municipalityId: address.municipality_code,
|
|
4664
|
+
municipalityCode: address.municipality_code,
|
|
4665
|
+
municipalityName: address.geo_municipality.name
|
|
4666
|
+
}))
|
|
4667
|
+
);
|
|
4668
|
+
return addresses;
|
|
4669
|
+
}
|
|
4670
|
+
function mapProfileUpdate(data) {
|
|
4671
|
+
return {
|
|
4672
|
+
display_name: [data.name, data.lastname].filter(Boolean).join(" ") || void 0,
|
|
4673
|
+
first_name: data.name,
|
|
4674
|
+
last_name: data.lastname,
|
|
4675
|
+
phone_e164: data.cellphone,
|
|
4676
|
+
phone_national_number: data.phone,
|
|
4677
|
+
address: data.address,
|
|
4678
|
+
address_complement: data.addressComplement,
|
|
4679
|
+
country_code: data.countryCode,
|
|
4680
|
+
department_code: data.departmentId,
|
|
4681
|
+
municipality_code: data.municipalityId,
|
|
4682
|
+
birthdate: data.birthday
|
|
4683
|
+
};
|
|
4684
|
+
}
|
|
4685
|
+
function createProfileModule(graphqlUrl, getHeaders) {
|
|
4686
|
+
async function requireAuth() {
|
|
4687
|
+
const headers = await getHeaders();
|
|
4688
|
+
if (!headers.Authorization) {
|
|
4689
|
+
throw new AuthError("You must be logged in to access your profile");
|
|
4690
|
+
}
|
|
4691
|
+
return headers;
|
|
4692
|
+
}
|
|
4693
|
+
return {
|
|
4694
|
+
async get() {
|
|
4695
|
+
const headers = await requireAuth();
|
|
4696
|
+
const data = await executeGraphQL(
|
|
4697
|
+
graphqlUrl,
|
|
4698
|
+
GET_PROFILE,
|
|
4699
|
+
{},
|
|
4700
|
+
headers
|
|
4701
|
+
);
|
|
4702
|
+
const client = data.clients?.[0];
|
|
4703
|
+
if (!client) {
|
|
4704
|
+
throw new AuthError("Customer profile not found");
|
|
4705
|
+
}
|
|
4706
|
+
return mapProfile(client);
|
|
4707
|
+
},
|
|
4708
|
+
async getAddresses() {
|
|
4709
|
+
const headers = await requireAuth();
|
|
4710
|
+
const data = await executeGraphQL(
|
|
4711
|
+
graphqlUrl,
|
|
4712
|
+
GET_PROFILE_ADDRESSES,
|
|
4713
|
+
{},
|
|
4714
|
+
headers
|
|
4715
|
+
);
|
|
4716
|
+
const client = data.clients?.[0];
|
|
4717
|
+
if (!client) {
|
|
4718
|
+
throw new AuthError("Customer profile not found");
|
|
4719
|
+
}
|
|
4720
|
+
return mapProfileAddresses(client);
|
|
4721
|
+
},
|
|
4722
|
+
async update(updateData) {
|
|
4723
|
+
const headers = await requireAuth();
|
|
4724
|
+
const profileData = await executeGraphQL(
|
|
4725
|
+
graphqlUrl,
|
|
4726
|
+
GET_PROFILE,
|
|
4727
|
+
{},
|
|
4728
|
+
headers
|
|
4729
|
+
);
|
|
4730
|
+
const client = profileData.clients?.[0];
|
|
4731
|
+
if (!client) {
|
|
4732
|
+
throw new AuthError("Customer profile not found");
|
|
4733
|
+
}
|
|
4734
|
+
const data = await executeGraphQL(
|
|
4735
|
+
graphqlUrl,
|
|
4736
|
+
UPDATE_PROFILE,
|
|
4737
|
+
{ partyId: client.party_id, data: mapProfileUpdate(updateData) },
|
|
4738
|
+
headers
|
|
4739
|
+
);
|
|
4740
|
+
return mapProfile({
|
|
4741
|
+
id: client.id,
|
|
4742
|
+
party_id: client.party_id,
|
|
4743
|
+
party: data.update_business_parties_by_pk
|
|
4744
|
+
});
|
|
4745
|
+
}
|
|
4746
|
+
};
|
|
4747
|
+
}
|
|
4748
|
+
|
|
4749
|
+
// libs/sdk/src/modules/website.ts
|
|
4750
|
+
function mapWebsite(raw) {
|
|
4751
|
+
return {
|
|
4752
|
+
websiteId: raw.id,
|
|
4753
|
+
companyId: raw.company_id,
|
|
4754
|
+
chainId: raw.chain_id,
|
|
4755
|
+
name: raw.name,
|
|
4756
|
+
slug: raw.slug,
|
|
4757
|
+
domain: raw.domain,
|
|
4758
|
+
config: raw.config
|
|
4759
|
+
};
|
|
4760
|
+
}
|
|
4761
|
+
async function resolveWebsite(graphqlUrl, slug, headers) {
|
|
4762
|
+
if (!slug.trim()) {
|
|
4763
|
+
throw new WebsiteError(
|
|
4764
|
+
"websiteSlug is required",
|
|
4765
|
+
"INVALID_CONFIG"
|
|
4766
|
+
);
|
|
4767
|
+
}
|
|
4768
|
+
const data = await executeGraphQL(
|
|
4769
|
+
graphqlUrl,
|
|
4770
|
+
RESOLVE_WEBSITE_BY_SLUG,
|
|
4771
|
+
{ slug },
|
|
4772
|
+
headers
|
|
4773
|
+
);
|
|
4774
|
+
if (!data.websites || data.websites.length === 0) {
|
|
4775
|
+
throw new WebsiteError(
|
|
4776
|
+
`No active website found with slug "${slug}"`,
|
|
4777
|
+
"WEBSITE_NOT_FOUND"
|
|
4778
|
+
);
|
|
4779
|
+
}
|
|
4780
|
+
return mapWebsite(data.websites[0]);
|
|
4781
|
+
}
|
|
4782
|
+
|
|
4783
|
+
// libs/sdk/src/client.ts
|
|
4784
|
+
function normalizeApiUrl(apiUrl) {
|
|
4785
|
+
return apiUrl.replace(/\/+$/, "");
|
|
4786
|
+
}
|
|
4787
|
+
function createBacanoClient(config) {
|
|
4788
|
+
if (!config.apiUrl)
|
|
4789
|
+
throw new WebsiteError("apiUrl is required", "INVALID_CONFIG");
|
|
4790
|
+
if (!config.websiteSlug?.trim())
|
|
4791
|
+
throw new WebsiteError("websiteSlug is required", "INVALID_CONFIG");
|
|
4792
|
+
const apiUrl = normalizeApiUrl(config.apiUrl);
|
|
4793
|
+
const graphqlUrl = `${apiUrl}/api/v1/website/graphql`;
|
|
4794
|
+
const storageUrl = `${apiUrl}/storage/v1`;
|
|
4795
|
+
let context = null;
|
|
4796
|
+
function getContext() {
|
|
4797
|
+
if (!context) throw new NotInitializedError();
|
|
4798
|
+
return context;
|
|
4799
|
+
}
|
|
4800
|
+
const getHeaders = createRequestHeadersProvider(
|
|
4801
|
+
config.websiteSlug,
|
|
4802
|
+
config.getAccessToken
|
|
4803
|
+
);
|
|
4804
|
+
const getWebsiteHeaders = createRequestHeadersProvider(config.websiteSlug);
|
|
4805
|
+
const catalogModule = createCatalogModule(
|
|
4806
|
+
graphqlUrl,
|
|
4807
|
+
getHeaders,
|
|
4808
|
+
getContext,
|
|
4809
|
+
storageUrl
|
|
4810
|
+
);
|
|
4811
|
+
const availabilityModule = createAvailabilityModule(
|
|
4812
|
+
graphqlUrl,
|
|
4813
|
+
getHeaders,
|
|
4814
|
+
getContext
|
|
4815
|
+
);
|
|
4816
|
+
const cartSessionStorage = createCartSessionStorage(
|
|
4817
|
+
config.tokenStorage ?? "localStorage",
|
|
4818
|
+
() => getContext().slug
|
|
4819
|
+
);
|
|
4820
|
+
const cartModule = createCartModule(
|
|
4821
|
+
graphqlUrl,
|
|
4822
|
+
getHeaders,
|
|
4823
|
+
getContext,
|
|
4824
|
+
cartSessionStorage
|
|
4825
|
+
);
|
|
4826
|
+
const checkoutModule = createCheckoutModule(
|
|
4827
|
+
graphqlUrl,
|
|
4828
|
+
getHeaders,
|
|
4829
|
+
getContext,
|
|
4830
|
+
cartSessionStorage
|
|
4831
|
+
);
|
|
4832
|
+
const ordersModule = createOrdersModule(graphqlUrl, getHeaders, getContext);
|
|
4833
|
+
const profileModule = createProfileModule(graphqlUrl, getHeaders);
|
|
4834
|
+
const contentModule = createContentModule(
|
|
4835
|
+
graphqlUrl,
|
|
4836
|
+
getHeaders,
|
|
4837
|
+
getContext,
|
|
4838
|
+
storageUrl
|
|
4839
|
+
);
|
|
4840
|
+
return {
|
|
4841
|
+
async init() {
|
|
4842
|
+
context = await resolveWebsite(
|
|
4843
|
+
graphqlUrl,
|
|
4844
|
+
config.websiteSlug,
|
|
4845
|
+
getWebsiteHeaders
|
|
4846
|
+
);
|
|
4847
|
+
},
|
|
4848
|
+
getContext,
|
|
4849
|
+
auth: {
|
|
4850
|
+
async getAccessToken() {
|
|
4851
|
+
return config.getAccessToken ? await config.getAccessToken() : null;
|
|
4852
|
+
},
|
|
4853
|
+
async isAuthenticated() {
|
|
4854
|
+
return Boolean(
|
|
4855
|
+
config.getAccessToken ? await config.getAccessToken() : null
|
|
4856
|
+
);
|
|
4857
|
+
}
|
|
4858
|
+
},
|
|
4859
|
+
catalog: {
|
|
4860
|
+
getCatalogPage: catalogModule.getCatalogPage.bind(catalogModule),
|
|
4861
|
+
getProductSeoEntries: catalogModule.getProductSeoEntries.bind(catalogModule),
|
|
4862
|
+
getProductRecommendations: catalogModule.getProductRecommendations.bind(catalogModule),
|
|
4863
|
+
getProducts: catalogModule.getProducts.bind(catalogModule),
|
|
4864
|
+
getProduct: catalogModule.getProduct.bind(catalogModule),
|
|
4865
|
+
getProductBySlug: catalogModule.getProductBySlug.bind(catalogModule),
|
|
4866
|
+
getCategories: catalogModule.getCategories.bind(catalogModule),
|
|
4867
|
+
getCategoryList: catalogModule.getCategoryList.bind(catalogModule),
|
|
4868
|
+
getAttributeList: catalogModule.getAttributeList.bind(catalogModule),
|
|
4869
|
+
getBrands: catalogModule.getBrands.bind(catalogModule),
|
|
4870
|
+
getProductFilters: catalogModule.getProductFilters.bind(catalogModule),
|
|
4871
|
+
getSearchSuggestions: catalogModule.getSearchSuggestions.bind(catalogModule),
|
|
4872
|
+
checkAvailability: availabilityModule.checkAvailability.bind(availabilityModule)
|
|
4873
|
+
},
|
|
4874
|
+
content: {
|
|
4875
|
+
getByKey: contentModule.getByKey.bind(contentModule),
|
|
4876
|
+
getByCategory: contentModule.getByCategory.bind(contentModule),
|
|
4877
|
+
getByTag: contentModule.getByTag.bind(contentModule),
|
|
4878
|
+
getForProduct: contentModule.getForProduct.bind(contentModule)
|
|
4879
|
+
},
|
|
4880
|
+
cart: {
|
|
4881
|
+
getOrCreate: cartModule.getOrCreate.bind(cartModule),
|
|
4882
|
+
startCheckout: cartModule.startCheckout.bind(cartModule),
|
|
4883
|
+
get: cartModule.get.bind(cartModule),
|
|
4884
|
+
setItem: cartModule.setItem.bind(cartModule),
|
|
4885
|
+
addItem: cartModule.addItem.bind(cartModule),
|
|
4886
|
+
updateItem: cartModule.updateItem.bind(cartModule),
|
|
4887
|
+
removeItem: cartModule.removeItem.bind(cartModule)
|
|
4888
|
+
},
|
|
4889
|
+
checkout: {
|
|
4890
|
+
getLocationOptions: checkoutModule.getLocationOptions.bind(checkoutModule),
|
|
4891
|
+
getDeliveryOptions: checkoutModule.getDeliveryOptions.bind(checkoutModule),
|
|
4892
|
+
getShippingQuotes: checkoutModule.getShippingQuotes.bind(checkoutModule),
|
|
4893
|
+
getShippingPolicy: checkoutModule.getShippingPolicy.bind(checkoutModule),
|
|
4894
|
+
getFreeShippingProgress: checkoutModule.getFreeShippingProgress.bind(checkoutModule),
|
|
4895
|
+
getPaymentMethods: checkoutModule.getPaymentMethods.bind(checkoutModule),
|
|
4896
|
+
getPaymentOptions: checkoutModule.getPaymentOptions.bind(checkoutModule),
|
|
4897
|
+
validateCoupon: checkoutModule.validateCoupon.bind(checkoutModule),
|
|
4898
|
+
applyCoupon: checkoutModule.applyCoupon.bind(checkoutModule),
|
|
4899
|
+
removeCoupon: checkoutModule.removeCoupon.bind(checkoutModule),
|
|
4900
|
+
submit: checkoutModule.submit.bind(checkoutModule),
|
|
4901
|
+
createPaymentSession: checkoutModule.createPaymentSession.bind(checkoutModule),
|
|
4902
|
+
reconcilePaymentSession: checkoutModule.reconcilePaymentSession.bind(checkoutModule)
|
|
4903
|
+
},
|
|
4904
|
+
orders: {
|
|
4905
|
+
list: ordersModule.list.bind(ordersModule),
|
|
4906
|
+
get: ordersModule.get.bind(ordersModule),
|
|
4907
|
+
getPublicOrder: ordersModule.getPublicOrder.bind(ordersModule),
|
|
4908
|
+
waitForPayment: ordersModule.waitForPayment.bind(ordersModule)
|
|
4909
|
+
},
|
|
4910
|
+
profile: {
|
|
4911
|
+
get: profileModule.get.bind(profileModule),
|
|
4912
|
+
getAddresses: profileModule.getAddresses.bind(profileModule),
|
|
4913
|
+
update: profileModule.update.bind(profileModule)
|
|
4914
|
+
}
|
|
4915
|
+
};
|
|
4916
|
+
}
|
|
4917
|
+
export {
|
|
4918
|
+
AuthError,
|
|
4919
|
+
CheckoutError,
|
|
4920
|
+
GraphQLRequestError,
|
|
4921
|
+
NotInitializedError,
|
|
4922
|
+
WebsiteError,
|
|
4923
|
+
buildStorageFileUrl,
|
|
4924
|
+
buildStorageMediaUrls,
|
|
4925
|
+
createBacanoClient,
|
|
4926
|
+
getPrimaryProductImage,
|
|
4927
|
+
getProductImages,
|
|
4928
|
+
getProductMediaByKind,
|
|
4929
|
+
getProductMediaKind,
|
|
4930
|
+
getProductVideos,
|
|
4931
|
+
getWebsitePublicConfig,
|
|
4932
|
+
resolveProductPricing
|
|
4933
|
+
};
|