@bacano/sdk 1.2.5 → 1.2.7
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/CONTENT_ICONS.md +113 -0
- package/CONTENT_TYPE_MANAGEMENT.md +8 -2
- package/README.md +11 -7
- package/STOREFRONT_PAYMENTS.md +8 -0
- package/STOREFRONT_PERFORMANCE.md +47 -5
- package/content-management-types.d.ts +9 -1
- package/graphql.cjs +59 -0
- package/graphql.js +59 -0
- package/icons.cjs +5515 -0
- package/icons.d.ts +20 -0
- package/icons.js +5496 -0
- package/index.cjs +102 -11
- package/index.js +102 -11
- package/package.json +11 -1
- package/react/icon.cjs +5555 -0
- package/react/icon.d.ts +10 -0
- package/react/icon.js +5534 -0
- package/react.cjs +102 -11
- package/react.js +102 -11
- package/skills/bacano-web/references/common.md +13 -0
- package/skills/bacano-web/references/storefront.md +16 -0
- package/transport/documents.d.ts +10 -10
- package/types.d.ts +5 -2
package/react.cjs
CHANGED
|
@@ -259,6 +259,12 @@ var GET_PRODUCTS = (
|
|
|
259
259
|
id
|
|
260
260
|
name
|
|
261
261
|
description
|
|
262
|
+
product_website_publications(
|
|
263
|
+
where: { website_id: { _eq: $websiteId }, is_visible: { _eq: true } }
|
|
264
|
+
limit: 1
|
|
265
|
+
) {
|
|
266
|
+
show_when_out_of_stock
|
|
267
|
+
}
|
|
262
268
|
long_description
|
|
263
269
|
slug
|
|
264
270
|
seo_title
|
|
@@ -494,6 +500,12 @@ var GET_PRODUCT_BY_PK = (
|
|
|
494
500
|
limit: 1
|
|
495
501
|
) {
|
|
496
502
|
id
|
|
503
|
+
product_website_publications(
|
|
504
|
+
where: { website_id: { _eq: $websiteId }, is_visible: { _eq: true } }
|
|
505
|
+
limit: 1
|
|
506
|
+
) {
|
|
507
|
+
show_when_out_of_stock
|
|
508
|
+
}
|
|
497
509
|
product_option_group_members(limit: 1) {
|
|
498
510
|
group {
|
|
499
511
|
id
|
|
@@ -743,6 +755,12 @@ var GET_PRODUCT_SEO_ENTRIES = (
|
|
|
743
755
|
id
|
|
744
756
|
name
|
|
745
757
|
slug
|
|
758
|
+
product_website_publications(
|
|
759
|
+
where: { website_id: { _eq: $websiteId }, is_visible: { _eq: true } }
|
|
760
|
+
limit: 1
|
|
761
|
+
) {
|
|
762
|
+
show_when_out_of_stock
|
|
763
|
+
}
|
|
746
764
|
seo_title
|
|
747
765
|
seo_description
|
|
748
766
|
seo_image_file_id
|
|
@@ -2376,6 +2394,7 @@ var GET_PRODUCT_FILTERS = (
|
|
|
2376
2394
|
query GetProductFilters(
|
|
2377
2395
|
$companyId: uuid!
|
|
2378
2396
|
$chainId: uuid!
|
|
2397
|
+
$websiteId: uuid!
|
|
2379
2398
|
$where: products_bool_exp = {}
|
|
2380
2399
|
$attributeListKey: String!
|
|
2381
2400
|
) {
|
|
@@ -2383,6 +2402,12 @@ var GET_PRODUCT_FILTERS = (
|
|
|
2383
2402
|
where: {
|
|
2384
2403
|
_and: [
|
|
2385
2404
|
{ company_id: { _eq: $companyId } }
|
|
2405
|
+
{
|
|
2406
|
+
product_website_publications: {
|
|
2407
|
+
website_id: { _eq: $websiteId }
|
|
2408
|
+
is_visible: { _eq: true }
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2386
2411
|
{
|
|
2387
2412
|
chain_products: {
|
|
2388
2413
|
chain_id: { _eq: $chainId }
|
|
@@ -2395,6 +2420,12 @@ var GET_PRODUCT_FILTERS = (
|
|
|
2395
2420
|
}
|
|
2396
2421
|
) {
|
|
2397
2422
|
id
|
|
2423
|
+
product_website_publications(
|
|
2424
|
+
where: { website_id: { _eq: $websiteId }, is_visible: { _eq: true } }
|
|
2425
|
+
limit: 1
|
|
2426
|
+
) {
|
|
2427
|
+
show_when_out_of_stock
|
|
2428
|
+
}
|
|
2398
2429
|
category
|
|
2399
2430
|
subcategory
|
|
2400
2431
|
color
|
|
@@ -2502,6 +2533,7 @@ var GET_SEARCH_SUGGESTIONS = (
|
|
|
2502
2533
|
query GetSearchSuggestions(
|
|
2503
2534
|
$companyId: uuid!
|
|
2504
2535
|
$chainId: uuid!
|
|
2536
|
+
$websiteId: uuid!
|
|
2505
2537
|
$term: String!
|
|
2506
2538
|
$productLimit: Int = 5
|
|
2507
2539
|
$brandLimit: Int = 3
|
|
@@ -2509,6 +2541,10 @@ var GET_SEARCH_SUGGESTIONS = (
|
|
|
2509
2541
|
products(
|
|
2510
2542
|
where: {
|
|
2511
2543
|
company_id: { _eq: $companyId }
|
|
2544
|
+
product_website_publications: {
|
|
2545
|
+
website_id: { _eq: $websiteId }
|
|
2546
|
+
is_visible: { _eq: true }
|
|
2547
|
+
}
|
|
2512
2548
|
chain_products: {
|
|
2513
2549
|
chain_id: { _eq: $chainId }
|
|
2514
2550
|
is_enabled: { _eq: true }
|
|
@@ -2521,6 +2557,29 @@ var GET_SEARCH_SUGGESTIONS = (
|
|
|
2521
2557
|
) {
|
|
2522
2558
|
id
|
|
2523
2559
|
name
|
|
2560
|
+
product_website_publications(
|
|
2561
|
+
where: { website_id: { _eq: $websiteId }, is_visible: { _eq: true } }
|
|
2562
|
+
limit: 1
|
|
2563
|
+
) {
|
|
2564
|
+
show_when_out_of_stock
|
|
2565
|
+
}
|
|
2566
|
+
product_variants(where: { is_disabled: { _eq: false } }) {
|
|
2567
|
+
id
|
|
2568
|
+
product_variant_prices(
|
|
2569
|
+
where: {
|
|
2570
|
+
price_type: { _eq: CLIENT }
|
|
2571
|
+
_or: [
|
|
2572
|
+
{ chain_id: { _eq: $chainId } }
|
|
2573
|
+
{ chain_id: { _is_null: true } }
|
|
2574
|
+
]
|
|
2575
|
+
}
|
|
2576
|
+
) {
|
|
2577
|
+
id
|
|
2578
|
+
price
|
|
2579
|
+
price_type
|
|
2580
|
+
chain_id
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2524
2583
|
}
|
|
2525
2584
|
brands(
|
|
2526
2585
|
where: {
|
|
@@ -3002,6 +3061,7 @@ function mapProduct(raw, storageUrl, labels, chainId) {
|
|
|
3002
3061
|
createdAt: raw.created_at,
|
|
3003
3062
|
updatedAt: raw.updated_at ?? raw.created_at,
|
|
3004
3063
|
variants,
|
|
3064
|
+
outOfStockVisibilityOverride: raw.product_website_publications?.[0]?.show_when_out_of_stock ?? null,
|
|
3005
3065
|
detailVariants,
|
|
3006
3066
|
media: raw.product_media.map((m) => mapMedia(m, storageUrl)),
|
|
3007
3067
|
attributes: raw.product_attributes.map(
|
|
@@ -3363,7 +3423,7 @@ async function getWebsiteAvailabilityByVariantId(graphqlUrl, getHeaders, variant
|
|
|
3363
3423
|
}
|
|
3364
3424
|
return availabilityByVariantId;
|
|
3365
3425
|
}
|
|
3366
|
-
async function filterProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId, websiteId) {
|
|
3426
|
+
async function filterProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId, websiteId, showOutOfStockByDefault = false, onlyInStock = false) {
|
|
3367
3427
|
const variantIds = products.flatMap(
|
|
3368
3428
|
(product) => product.variants.map((variant) => variant.id)
|
|
3369
3429
|
);
|
|
@@ -3389,11 +3449,11 @@ async function filterProductsForWebsiteStock(graphqlUrl, getHeaders, products, c
|
|
|
3389
3449
|
pricing: resolveProductPricing(prices)
|
|
3390
3450
|
};
|
|
3391
3451
|
}).filter(
|
|
3392
|
-
(variant) => inStockVariantIds.has(variant.id) && variant.pricing.currentPrice != null
|
|
3452
|
+
(variant) => (inStockVariantIds.has(variant.id) || !onlyInStock && (product.outOfStockVisibilityOverride ?? showOutOfStockByDefault)) && variant.pricing.currentPrice != null
|
|
3393
3453
|
)
|
|
3394
3454
|
})).filter((product) => product.variants.length > 0);
|
|
3395
3455
|
}
|
|
3396
|
-
async function filterSeoProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId, websiteId) {
|
|
3456
|
+
async function filterSeoProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId, websiteId, showOutOfStockByDefault = false) {
|
|
3397
3457
|
const variantIds = products.flatMap(
|
|
3398
3458
|
(product) => product.product_variants.map((variant) => variant.id)
|
|
3399
3459
|
);
|
|
@@ -3415,7 +3475,7 @@ async function filterSeoProductsForWebsiteStock(graphqlUrl, getHeaders, products
|
|
|
3415
3475
|
const pricing = resolveProductPricing(
|
|
3416
3476
|
selectPublicPrices(prices, chainId)
|
|
3417
3477
|
);
|
|
3418
|
-
return inStockVariantIds.has(variant.id) && pricing.currentPrice != null;
|
|
3478
|
+
return (inStockVariantIds.has(variant.id) || (product.product_website_publications?.[0]?.show_when_out_of_stock ?? showOutOfStockByDefault)) && pricing.currentPrice != null;
|
|
3419
3479
|
})
|
|
3420
3480
|
);
|
|
3421
3481
|
}
|
|
@@ -3486,7 +3546,8 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
3486
3546
|
getHeaders,
|
|
3487
3547
|
[mappedProduct],
|
|
3488
3548
|
ctx.chainId,
|
|
3489
|
-
ctx.websiteId
|
|
3549
|
+
ctx.websiteId,
|
|
3550
|
+
ctx.config?.show_out_of_stock_products === true
|
|
3490
3551
|
);
|
|
3491
3552
|
if (!filteredProducts[0]) return null;
|
|
3492
3553
|
const specifications = await loadProductSpecifications([product.id]);
|
|
@@ -3719,7 +3780,8 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
3719
3780
|
getHeaders,
|
|
3720
3781
|
data.products ?? [],
|
|
3721
3782
|
ctx.chainId,
|
|
3722
|
-
ctx.websiteId
|
|
3783
|
+
ctx.websiteId,
|
|
3784
|
+
ctx.config?.show_out_of_stock_products === true
|
|
3723
3785
|
);
|
|
3724
3786
|
const entries = availableProducts.slice(0, limit).map((product) => mapProductSeoEntry(product, storageUrl)).filter((entry) => Boolean(entry));
|
|
3725
3787
|
return {
|
|
@@ -3796,7 +3858,9 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
3796
3858
|
(p) => mapProduct(p, storageUrl, publicAttributeLabels, chainId)
|
|
3797
3859
|
),
|
|
3798
3860
|
chainId,
|
|
3799
|
-
ctx.websiteId
|
|
3861
|
+
ctx.websiteId,
|
|
3862
|
+
ctx.config?.show_out_of_stock_products === true,
|
|
3863
|
+
opts?.inStock === true
|
|
3800
3864
|
);
|
|
3801
3865
|
const specifications = await loadProductSpecifications(
|
|
3802
3866
|
products.map((product) => product.id)
|
|
@@ -3903,6 +3967,7 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
3903
3967
|
{
|
|
3904
3968
|
companyId: ctx.companyId,
|
|
3905
3969
|
chainId,
|
|
3970
|
+
websiteId: ctx.websiteId,
|
|
3906
3971
|
attributeListKey: DEFAULT_FILTER_ATTRIBUTE_LIST_KEY,
|
|
3907
3972
|
where: buildProductWhere(opts.filters ?? {}, chainId)
|
|
3908
3973
|
},
|
|
@@ -3926,7 +3991,7 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
3926
3991
|
variant.product_variant_prices.map(mapPrice),
|
|
3927
3992
|
chainId
|
|
3928
3993
|
);
|
|
3929
|
-
return prices.length > 0 && (availabilityByVariantId.get(variant.id)?.inStock ?? false);
|
|
3994
|
+
return prices.length > 0 && ((availabilityByVariantId.get(variant.id)?.inStock ?? false) || (product.product_website_publications?.[0]?.show_when_out_of_stock ?? ctx.config?.show_out_of_stock_products === true));
|
|
3930
3995
|
});
|
|
3931
3996
|
if (!hasVisibleVariant) continue;
|
|
3932
3997
|
applicableCategories.add(product.category);
|
|
@@ -4014,6 +4079,7 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
4014
4079
|
{
|
|
4015
4080
|
companyId: ctx.companyId,
|
|
4016
4081
|
chainId,
|
|
4082
|
+
websiteId: ctx.websiteId,
|
|
4017
4083
|
attributeListKey: opts?.attributeListKey ?? DEFAULT_FILTER_ATTRIBUTE_LIST_KEY,
|
|
4018
4084
|
where: {
|
|
4019
4085
|
...opts?.categoryName ? { category: { _eq: opts.categoryName } } : {},
|
|
@@ -4068,7 +4134,9 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
4068
4134
|
chainId
|
|
4069
4135
|
),
|
|
4070
4136
|
inStock: availabilityByVariantId.get(variant.id)?.inStock ?? false
|
|
4071
|
-
})).filter(
|
|
4137
|
+
})).filter(
|
|
4138
|
+
(variant) => (variant.inStock || (product.product_website_publications?.[0]?.show_when_out_of_stock ?? ctx.config?.show_out_of_stock_products === true)) && variant.prices.length > 0
|
|
4139
|
+
);
|
|
4072
4140
|
if (visibleVariants.length === 0) continue;
|
|
4073
4141
|
categoryCounts.set(
|
|
4074
4142
|
product.category,
|
|
@@ -4153,6 +4221,7 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
4153
4221
|
"NO_CHAIN"
|
|
4154
4222
|
);
|
|
4155
4223
|
}
|
|
4224
|
+
const chainId = ctx.chainId;
|
|
4156
4225
|
const term = `%${opts.query}%`;
|
|
4157
4226
|
const limit = opts.limit ?? 8;
|
|
4158
4227
|
const data = await executeGraphQL(
|
|
@@ -4160,7 +4229,8 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
4160
4229
|
GET_SEARCH_SUGGESTIONS,
|
|
4161
4230
|
{
|
|
4162
4231
|
companyId: ctx.companyId,
|
|
4163
|
-
chainId
|
|
4232
|
+
chainId,
|
|
4233
|
+
websiteId: ctx.websiteId,
|
|
4164
4234
|
term,
|
|
4165
4235
|
productLimit: Math.min(limit, 5),
|
|
4166
4236
|
brandLimit: Math.max(limit - 5, 3)
|
|
@@ -4168,8 +4238,29 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
4168
4238
|
getHeaders
|
|
4169
4239
|
);
|
|
4170
4240
|
const suggestions = [];
|
|
4241
|
+
const availability = await getWebsiteAvailabilityByVariantId(
|
|
4242
|
+
graphqlUrl,
|
|
4243
|
+
getHeaders,
|
|
4244
|
+
(data.products ?? []).flatMap(
|
|
4245
|
+
(product) => product.product_variants.map((variant) => variant.id)
|
|
4246
|
+
),
|
|
4247
|
+
chainId,
|
|
4248
|
+
ctx.websiteId
|
|
4249
|
+
);
|
|
4171
4250
|
for (const p of data.products ?? []) {
|
|
4172
|
-
|
|
4251
|
+
const showWithoutStock = p.product_website_publications?.[0]?.show_when_out_of_stock ?? ctx.config?.show_out_of_stock_products === true;
|
|
4252
|
+
const isVisible = p.product_variants.some((variant) => {
|
|
4253
|
+
const hasPrice = resolveProductPricing(
|
|
4254
|
+
selectPublicPrices(
|
|
4255
|
+
variant.product_variant_prices.map(mapPrice),
|
|
4256
|
+
chainId
|
|
4257
|
+
)
|
|
4258
|
+
).currentPrice != null;
|
|
4259
|
+
return hasPrice && (showWithoutStock || availability.get(variant.id)?.inStock === true);
|
|
4260
|
+
});
|
|
4261
|
+
if (isVisible) {
|
|
4262
|
+
suggestions.push({ type: "product", text: p.name, id: p.id });
|
|
4263
|
+
}
|
|
4173
4264
|
}
|
|
4174
4265
|
for (const b of data.brands ?? []) {
|
|
4175
4266
|
suggestions.push({ type: "brand", text: b.name, id: b.id });
|
package/react.js
CHANGED
|
@@ -202,6 +202,12 @@ var GET_PRODUCTS = (
|
|
|
202
202
|
id
|
|
203
203
|
name
|
|
204
204
|
description
|
|
205
|
+
product_website_publications(
|
|
206
|
+
where: { website_id: { _eq: $websiteId }, is_visible: { _eq: true } }
|
|
207
|
+
limit: 1
|
|
208
|
+
) {
|
|
209
|
+
show_when_out_of_stock
|
|
210
|
+
}
|
|
205
211
|
long_description
|
|
206
212
|
slug
|
|
207
213
|
seo_title
|
|
@@ -437,6 +443,12 @@ var GET_PRODUCT_BY_PK = (
|
|
|
437
443
|
limit: 1
|
|
438
444
|
) {
|
|
439
445
|
id
|
|
446
|
+
product_website_publications(
|
|
447
|
+
where: { website_id: { _eq: $websiteId }, is_visible: { _eq: true } }
|
|
448
|
+
limit: 1
|
|
449
|
+
) {
|
|
450
|
+
show_when_out_of_stock
|
|
451
|
+
}
|
|
440
452
|
product_option_group_members(limit: 1) {
|
|
441
453
|
group {
|
|
442
454
|
id
|
|
@@ -686,6 +698,12 @@ var GET_PRODUCT_SEO_ENTRIES = (
|
|
|
686
698
|
id
|
|
687
699
|
name
|
|
688
700
|
slug
|
|
701
|
+
product_website_publications(
|
|
702
|
+
where: { website_id: { _eq: $websiteId }, is_visible: { _eq: true } }
|
|
703
|
+
limit: 1
|
|
704
|
+
) {
|
|
705
|
+
show_when_out_of_stock
|
|
706
|
+
}
|
|
689
707
|
seo_title
|
|
690
708
|
seo_description
|
|
691
709
|
seo_image_file_id
|
|
@@ -2319,6 +2337,7 @@ var GET_PRODUCT_FILTERS = (
|
|
|
2319
2337
|
query GetProductFilters(
|
|
2320
2338
|
$companyId: uuid!
|
|
2321
2339
|
$chainId: uuid!
|
|
2340
|
+
$websiteId: uuid!
|
|
2322
2341
|
$where: products_bool_exp = {}
|
|
2323
2342
|
$attributeListKey: String!
|
|
2324
2343
|
) {
|
|
@@ -2326,6 +2345,12 @@ var GET_PRODUCT_FILTERS = (
|
|
|
2326
2345
|
where: {
|
|
2327
2346
|
_and: [
|
|
2328
2347
|
{ company_id: { _eq: $companyId } }
|
|
2348
|
+
{
|
|
2349
|
+
product_website_publications: {
|
|
2350
|
+
website_id: { _eq: $websiteId }
|
|
2351
|
+
is_visible: { _eq: true }
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2329
2354
|
{
|
|
2330
2355
|
chain_products: {
|
|
2331
2356
|
chain_id: { _eq: $chainId }
|
|
@@ -2338,6 +2363,12 @@ var GET_PRODUCT_FILTERS = (
|
|
|
2338
2363
|
}
|
|
2339
2364
|
) {
|
|
2340
2365
|
id
|
|
2366
|
+
product_website_publications(
|
|
2367
|
+
where: { website_id: { _eq: $websiteId }, is_visible: { _eq: true } }
|
|
2368
|
+
limit: 1
|
|
2369
|
+
) {
|
|
2370
|
+
show_when_out_of_stock
|
|
2371
|
+
}
|
|
2341
2372
|
category
|
|
2342
2373
|
subcategory
|
|
2343
2374
|
color
|
|
@@ -2445,6 +2476,7 @@ var GET_SEARCH_SUGGESTIONS = (
|
|
|
2445
2476
|
query GetSearchSuggestions(
|
|
2446
2477
|
$companyId: uuid!
|
|
2447
2478
|
$chainId: uuid!
|
|
2479
|
+
$websiteId: uuid!
|
|
2448
2480
|
$term: String!
|
|
2449
2481
|
$productLimit: Int = 5
|
|
2450
2482
|
$brandLimit: Int = 3
|
|
@@ -2452,6 +2484,10 @@ var GET_SEARCH_SUGGESTIONS = (
|
|
|
2452
2484
|
products(
|
|
2453
2485
|
where: {
|
|
2454
2486
|
company_id: { _eq: $companyId }
|
|
2487
|
+
product_website_publications: {
|
|
2488
|
+
website_id: { _eq: $websiteId }
|
|
2489
|
+
is_visible: { _eq: true }
|
|
2490
|
+
}
|
|
2455
2491
|
chain_products: {
|
|
2456
2492
|
chain_id: { _eq: $chainId }
|
|
2457
2493
|
is_enabled: { _eq: true }
|
|
@@ -2464,6 +2500,29 @@ var GET_SEARCH_SUGGESTIONS = (
|
|
|
2464
2500
|
) {
|
|
2465
2501
|
id
|
|
2466
2502
|
name
|
|
2503
|
+
product_website_publications(
|
|
2504
|
+
where: { website_id: { _eq: $websiteId }, is_visible: { _eq: true } }
|
|
2505
|
+
limit: 1
|
|
2506
|
+
) {
|
|
2507
|
+
show_when_out_of_stock
|
|
2508
|
+
}
|
|
2509
|
+
product_variants(where: { is_disabled: { _eq: false } }) {
|
|
2510
|
+
id
|
|
2511
|
+
product_variant_prices(
|
|
2512
|
+
where: {
|
|
2513
|
+
price_type: { _eq: CLIENT }
|
|
2514
|
+
_or: [
|
|
2515
|
+
{ chain_id: { _eq: $chainId } }
|
|
2516
|
+
{ chain_id: { _is_null: true } }
|
|
2517
|
+
]
|
|
2518
|
+
}
|
|
2519
|
+
) {
|
|
2520
|
+
id
|
|
2521
|
+
price
|
|
2522
|
+
price_type
|
|
2523
|
+
chain_id
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2467
2526
|
}
|
|
2468
2527
|
brands(
|
|
2469
2528
|
where: {
|
|
@@ -2945,6 +3004,7 @@ function mapProduct(raw, storageUrl, labels, chainId) {
|
|
|
2945
3004
|
createdAt: raw.created_at,
|
|
2946
3005
|
updatedAt: raw.updated_at ?? raw.created_at,
|
|
2947
3006
|
variants,
|
|
3007
|
+
outOfStockVisibilityOverride: raw.product_website_publications?.[0]?.show_when_out_of_stock ?? null,
|
|
2948
3008
|
detailVariants,
|
|
2949
3009
|
media: raw.product_media.map((m) => mapMedia(m, storageUrl)),
|
|
2950
3010
|
attributes: raw.product_attributes.map(
|
|
@@ -3306,7 +3366,7 @@ async function getWebsiteAvailabilityByVariantId(graphqlUrl, getHeaders, variant
|
|
|
3306
3366
|
}
|
|
3307
3367
|
return availabilityByVariantId;
|
|
3308
3368
|
}
|
|
3309
|
-
async function filterProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId, websiteId) {
|
|
3369
|
+
async function filterProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId, websiteId, showOutOfStockByDefault = false, onlyInStock = false) {
|
|
3310
3370
|
const variantIds = products.flatMap(
|
|
3311
3371
|
(product) => product.variants.map((variant) => variant.id)
|
|
3312
3372
|
);
|
|
@@ -3332,11 +3392,11 @@ async function filterProductsForWebsiteStock(graphqlUrl, getHeaders, products, c
|
|
|
3332
3392
|
pricing: resolveProductPricing(prices)
|
|
3333
3393
|
};
|
|
3334
3394
|
}).filter(
|
|
3335
|
-
(variant) => inStockVariantIds.has(variant.id) && variant.pricing.currentPrice != null
|
|
3395
|
+
(variant) => (inStockVariantIds.has(variant.id) || !onlyInStock && (product.outOfStockVisibilityOverride ?? showOutOfStockByDefault)) && variant.pricing.currentPrice != null
|
|
3336
3396
|
)
|
|
3337
3397
|
})).filter((product) => product.variants.length > 0);
|
|
3338
3398
|
}
|
|
3339
|
-
async function filterSeoProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId, websiteId) {
|
|
3399
|
+
async function filterSeoProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId, websiteId, showOutOfStockByDefault = false) {
|
|
3340
3400
|
const variantIds = products.flatMap(
|
|
3341
3401
|
(product) => product.product_variants.map((variant) => variant.id)
|
|
3342
3402
|
);
|
|
@@ -3358,7 +3418,7 @@ async function filterSeoProductsForWebsiteStock(graphqlUrl, getHeaders, products
|
|
|
3358
3418
|
const pricing = resolveProductPricing(
|
|
3359
3419
|
selectPublicPrices(prices, chainId)
|
|
3360
3420
|
);
|
|
3361
|
-
return inStockVariantIds.has(variant.id) && pricing.currentPrice != null;
|
|
3421
|
+
return (inStockVariantIds.has(variant.id) || (product.product_website_publications?.[0]?.show_when_out_of_stock ?? showOutOfStockByDefault)) && pricing.currentPrice != null;
|
|
3362
3422
|
})
|
|
3363
3423
|
);
|
|
3364
3424
|
}
|
|
@@ -3429,7 +3489,8 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
3429
3489
|
getHeaders,
|
|
3430
3490
|
[mappedProduct],
|
|
3431
3491
|
ctx.chainId,
|
|
3432
|
-
ctx.websiteId
|
|
3492
|
+
ctx.websiteId,
|
|
3493
|
+
ctx.config?.show_out_of_stock_products === true
|
|
3433
3494
|
);
|
|
3434
3495
|
if (!filteredProducts[0]) return null;
|
|
3435
3496
|
const specifications = await loadProductSpecifications([product.id]);
|
|
@@ -3662,7 +3723,8 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
3662
3723
|
getHeaders,
|
|
3663
3724
|
data.products ?? [],
|
|
3664
3725
|
ctx.chainId,
|
|
3665
|
-
ctx.websiteId
|
|
3726
|
+
ctx.websiteId,
|
|
3727
|
+
ctx.config?.show_out_of_stock_products === true
|
|
3666
3728
|
);
|
|
3667
3729
|
const entries = availableProducts.slice(0, limit).map((product) => mapProductSeoEntry(product, storageUrl)).filter((entry) => Boolean(entry));
|
|
3668
3730
|
return {
|
|
@@ -3739,7 +3801,9 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
3739
3801
|
(p) => mapProduct(p, storageUrl, publicAttributeLabels, chainId)
|
|
3740
3802
|
),
|
|
3741
3803
|
chainId,
|
|
3742
|
-
ctx.websiteId
|
|
3804
|
+
ctx.websiteId,
|
|
3805
|
+
ctx.config?.show_out_of_stock_products === true,
|
|
3806
|
+
opts?.inStock === true
|
|
3743
3807
|
);
|
|
3744
3808
|
const specifications = await loadProductSpecifications(
|
|
3745
3809
|
products.map((product) => product.id)
|
|
@@ -3846,6 +3910,7 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
3846
3910
|
{
|
|
3847
3911
|
companyId: ctx.companyId,
|
|
3848
3912
|
chainId,
|
|
3913
|
+
websiteId: ctx.websiteId,
|
|
3849
3914
|
attributeListKey: DEFAULT_FILTER_ATTRIBUTE_LIST_KEY,
|
|
3850
3915
|
where: buildProductWhere(opts.filters ?? {}, chainId)
|
|
3851
3916
|
},
|
|
@@ -3869,7 +3934,7 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
3869
3934
|
variant.product_variant_prices.map(mapPrice),
|
|
3870
3935
|
chainId
|
|
3871
3936
|
);
|
|
3872
|
-
return prices.length > 0 && (availabilityByVariantId.get(variant.id)?.inStock ?? false);
|
|
3937
|
+
return prices.length > 0 && ((availabilityByVariantId.get(variant.id)?.inStock ?? false) || (product.product_website_publications?.[0]?.show_when_out_of_stock ?? ctx.config?.show_out_of_stock_products === true));
|
|
3873
3938
|
});
|
|
3874
3939
|
if (!hasVisibleVariant) continue;
|
|
3875
3940
|
applicableCategories.add(product.category);
|
|
@@ -3957,6 +4022,7 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
3957
4022
|
{
|
|
3958
4023
|
companyId: ctx.companyId,
|
|
3959
4024
|
chainId,
|
|
4025
|
+
websiteId: ctx.websiteId,
|
|
3960
4026
|
attributeListKey: opts?.attributeListKey ?? DEFAULT_FILTER_ATTRIBUTE_LIST_KEY,
|
|
3961
4027
|
where: {
|
|
3962
4028
|
...opts?.categoryName ? { category: { _eq: opts.categoryName } } : {},
|
|
@@ -4011,7 +4077,9 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
4011
4077
|
chainId
|
|
4012
4078
|
),
|
|
4013
4079
|
inStock: availabilityByVariantId.get(variant.id)?.inStock ?? false
|
|
4014
|
-
})).filter(
|
|
4080
|
+
})).filter(
|
|
4081
|
+
(variant) => (variant.inStock || (product.product_website_publications?.[0]?.show_when_out_of_stock ?? ctx.config?.show_out_of_stock_products === true)) && variant.prices.length > 0
|
|
4082
|
+
);
|
|
4015
4083
|
if (visibleVariants.length === 0) continue;
|
|
4016
4084
|
categoryCounts.set(
|
|
4017
4085
|
product.category,
|
|
@@ -4096,6 +4164,7 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
4096
4164
|
"NO_CHAIN"
|
|
4097
4165
|
);
|
|
4098
4166
|
}
|
|
4167
|
+
const chainId = ctx.chainId;
|
|
4099
4168
|
const term = `%${opts.query}%`;
|
|
4100
4169
|
const limit = opts.limit ?? 8;
|
|
4101
4170
|
const data = await executeGraphQL(
|
|
@@ -4103,7 +4172,8 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
4103
4172
|
GET_SEARCH_SUGGESTIONS,
|
|
4104
4173
|
{
|
|
4105
4174
|
companyId: ctx.companyId,
|
|
4106
|
-
chainId
|
|
4175
|
+
chainId,
|
|
4176
|
+
websiteId: ctx.websiteId,
|
|
4107
4177
|
term,
|
|
4108
4178
|
productLimit: Math.min(limit, 5),
|
|
4109
4179
|
brandLimit: Math.max(limit - 5, 3)
|
|
@@ -4111,8 +4181,29 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
|
|
|
4111
4181
|
getHeaders
|
|
4112
4182
|
);
|
|
4113
4183
|
const suggestions = [];
|
|
4184
|
+
const availability = await getWebsiteAvailabilityByVariantId(
|
|
4185
|
+
graphqlUrl,
|
|
4186
|
+
getHeaders,
|
|
4187
|
+
(data.products ?? []).flatMap(
|
|
4188
|
+
(product) => product.product_variants.map((variant) => variant.id)
|
|
4189
|
+
),
|
|
4190
|
+
chainId,
|
|
4191
|
+
ctx.websiteId
|
|
4192
|
+
);
|
|
4114
4193
|
for (const p of data.products ?? []) {
|
|
4115
|
-
|
|
4194
|
+
const showWithoutStock = p.product_website_publications?.[0]?.show_when_out_of_stock ?? ctx.config?.show_out_of_stock_products === true;
|
|
4195
|
+
const isVisible = p.product_variants.some((variant) => {
|
|
4196
|
+
const hasPrice = resolveProductPricing(
|
|
4197
|
+
selectPublicPrices(
|
|
4198
|
+
variant.product_variant_prices.map(mapPrice),
|
|
4199
|
+
chainId
|
|
4200
|
+
)
|
|
4201
|
+
).currentPrice != null;
|
|
4202
|
+
return hasPrice && (showWithoutStock || availability.get(variant.id)?.inStock === true);
|
|
4203
|
+
});
|
|
4204
|
+
if (isVisible) {
|
|
4205
|
+
suggestions.push({ type: "product", text: p.name, id: p.id });
|
|
4206
|
+
}
|
|
4116
4207
|
}
|
|
4117
4208
|
for (const b of data.brands ?? []) {
|
|
4118
4209
|
suggestions.push({ type: "brand", text: b.name, id: b.id });
|
|
@@ -40,11 +40,21 @@ función apagada en un bloqueo artificial.
|
|
|
40
40
|
## SEO y compartición
|
|
41
41
|
|
|
42
42
|
- Título, descripción, canonical, robots, Open Graph y Twitter por ruta.
|
|
43
|
+
- Conservar los campos SEO editados de verdad en Bacano, pero no publicar
|
|
44
|
+
`lorem ipsum` u otros borradores visibles como metadata. Usar fallbacks
|
|
45
|
+
específicos para cada página y evitar duplicar la marca en el título.
|
|
43
46
|
- `robots.txt`, sitemap con URLs canónicas y ausencia de rutas privadas.
|
|
47
|
+
- En el dominio publicado, comprobar con `GET` el código, tipo de contenido y
|
|
48
|
+
política de caché de sitemap y robots. Las rutas estables de descubrimiento
|
|
49
|
+
no deben heredar `immutable` de los assets versionados; contrastar un error
|
|
50
|
+
de Search Console con su prueba en tiempo real antes de culpar al XML. Ver
|
|
51
|
+
`STOREFRONT_PERFORMANCE.md`.
|
|
44
52
|
- Jerarquía de encabezados, idioma, texto alternativo, enlaces internos y
|
|
45
53
|
JSON-LD coherente con el contenido visible.
|
|
46
54
|
- Confirmar 404 real, canonical único, ausencia de slugs/URLs duplicadas y
|
|
47
55
|
redirección consistente entre `www`, dominio raíz y HTTP/HTTPS.
|
|
56
|
+
- El host del sitemap, canonical, Open Graph y redirección final debe coincidir;
|
|
57
|
+
no basta con que cada URL funcione por separado.
|
|
48
58
|
- Usar `hreflang` solo cuando existan versiones regionales o de idioma reales.
|
|
49
59
|
- Favicon, iconos, manifest y vista previa al compartir.
|
|
50
60
|
|
|
@@ -56,6 +66,9 @@ función apagada en un bloqueo artificial.
|
|
|
56
66
|
- Revisar desktop y mobile, DPR 1/2, CLS, LCP, fuentes y JavaScript inicial.
|
|
57
67
|
Tratar una medición sintética aislada como diagnóstico, no como verdad de
|
|
58
68
|
campo; repetir y distinguir regresión sostenida de ruido.
|
|
69
|
+
- En carruseles, contar imágenes descargadas, no solo elementos visibles;
|
|
70
|
+
comprobar navegación a fotos lejanas y compatibilidad con los navegadores
|
|
71
|
+
móviles soportados. Ver `STOREFRONT_PERFORMANCE.md`.
|
|
59
72
|
- Confirmar caché y CORS/CORP de media pública sin proxies locales.
|
|
60
73
|
|
|
61
74
|
## Accesibilidad y experiencia
|
|
@@ -18,6 +18,17 @@ Aplicar además de los controles comunes.
|
|
|
18
18
|
alterar canonical.
|
|
19
19
|
- Precio, SKU, stock, variante seleccionada, JSON-LD, carrito, WhatsApp y
|
|
20
20
|
eventos deben referirse a la misma variante.
|
|
21
|
+
- Verificar la regla de **productos agotados** del website y las excepciones
|
|
22
|
+
por producto y por web. La regla predeterminada oculta agotados; una excepción
|
|
23
|
+
`true` los muestra, `false` los oculta y `null` hereda la regla. La publicación
|
|
24
|
+
del producto, cadena activa y precio público siguen siendo requisitos. El
|
|
25
|
+
SDK aplica la visibilidad en catálogo, rutas, SEO, filtros, recomendaciones y
|
|
26
|
+
búsqueda: la tienda no debe duplicar ese filtrado ni consultar Hasura.
|
|
27
|
+
- Un producto agotado visible no es comprable: comprobar `inStock: false` y
|
|
28
|
+
`maxPurchasableQuantity: 0` en estado vivo, controles de cantidad y checkout.
|
|
29
|
+
`getProducts({ inStock: true })` debe seguir excluyéndolo. Si el sitio es
|
|
30
|
+
estático, cambiar esta regla exige rebuild para que rutas, sitemap y HTML
|
|
31
|
+
reflejen los productos ahora visibles u ocultos.
|
|
21
32
|
- El HTML inicial debe contener producto, precio y enlaces reales sin depender
|
|
22
33
|
de JavaScript. Después de hidratar, refrescar únicamente estado volátil por
|
|
23
34
|
lotes con `getProductLiveState()` y respetar `maxPurchasableQuantity`.
|
|
@@ -45,6 +56,8 @@ Aplicar además de los controles comunes.
|
|
|
45
56
|
## Carrito y checkout
|
|
46
57
|
|
|
47
58
|
- Carrito invitado persiste y sincroniza sin duplicar líneas.
|
|
59
|
+
- Si una operación bloquea temporalmente otras mutaciones del carrito, mostrar
|
|
60
|
+
el indicador de carga en el producto accionado, no en todas las tarjetas.
|
|
48
61
|
- Probar alta, baja, cambios de cantidad, límites de stock, cupón, envío gratis,
|
|
49
62
|
impuestos, moneda y redondeo.
|
|
50
63
|
- Probar solo los modos activos entre COUNTER, PICKUP y SHIPPING; propia y
|
|
@@ -56,6 +69,9 @@ Aplicar además de los controles comunes.
|
|
|
56
69
|
|
|
57
70
|
- Leer y aplicar `STOREFRONT_PAYMENTS.md`; no reconstruir en la tienda URLs,
|
|
58
71
|
credenciales ni payloads privados de Wompi, Mercado Pago o Addi.
|
|
72
|
+
- Si se muestran marcas de pasarela o redes, comprobar que corresponden al
|
|
73
|
+
método efectivamente devuelto por Bacano y no sugieren un método adicional
|
|
74
|
+
ni un medio que el checkout alojado no tenga confirmado.
|
|
59
75
|
- Ejecutar al menos un happy path real o sandbox por cada método crítico
|
|
60
76
|
publicado. Probar pago inmediato, transferencia, Wompi y contraentrega solo
|
|
61
77
|
si están activos; los estados alternos de cada proveedor quedan en su matriz
|