Skip to content

Commit 21fa784

Browse files
committed
feat: Add out-of-stock/backorder message to product lists
1 parent d77f69d commit 21fa784

13 files changed

Lines changed: 197 additions & 54 deletions

File tree

.changeset/tricky-plants-teach.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
"@bigcommerce/catalyst-core": minor
3+
---
4+
5+
Add out-of-stock / backorder message to product cards on PLPs based on store settings:
6+
- Add out of stock message if the product is out of stock and stock is set to display it.
7+
- Add the backorder message if the product has no on-hand stock and is available for backorder and the store/product is set to display the backorder message
8+
9+
## Migration
10+
For existing Catalyst stores, to get the newly added feature, simply rebase the existing code with the new release code. The files to be rebased for this change to be applied are:
11+
- core/app/[locale]/(default)/(faceted)/brand/[slug]/page-data.ts
12+
- core/app/[locale]/(default)/(faceted)/brand/[slug]/page.tsx
13+
- core/app/[locale]/(default)/(faceted)/category/[slug]/page-data.ts
14+
- core/app/[locale]/(default)/(faceted)/category/[slug]/page.tsx
15+
- core/app/[locale]/(default)/(faceted)/search/page-data.ts
16+
- core/app/[locale]/(default)/(faceted)/search/page.tsx
17+
- core/app/[locale]/(default)/page-data.ts
18+
- core/app/[locale]/(default)/page.tsx
19+
- core/components/product-card/fragment.ts
20+
- core/data-transformers/product-card-transformer.ts
21+
- core/vibes/soul/primitives/product-card/index.tsx

core/app/[locale]/(default)/(faceted)/brand/[slug]/page-data.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ const BrandPageQuery = graphql(`
1616
}
1717
}
1818
settings {
19+
inventory {
20+
defaultOutOfStockMessage
21+
showOutOfStockMessage
22+
showBackorderMessage
23+
}
1924
storefront {
2025
catalog {
2126
productComparisonsEnabled

core/app/[locale]/(default)/(faceted)/brand/[slug]/page.tsx

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { getFilterParsers } from '@/vibes/soul/sections/products-list-section/fi
1111
import { getSessionCustomerAccessToken } from '~/auth';
1212
import { facetsTransformer } from '~/data-transformers/facets-transformer';
1313
import { pageInfoTransformer } from '~/data-transformers/page-info-transformer';
14-
import { pricesTransformer } from '~/data-transformers/prices-transformer';
14+
import { productCardTransformer } from '~/data-transformers/product-card-transformer';
1515
import { getPreferredCurrencyCode } from '~/lib/currency';
1616

1717
import { MAX_COMPARE_LIMIT } from '../../../compare/page-data';
@@ -132,16 +132,15 @@ export default async function Brand(props: Props) {
132132
const search = await streamableFacetedSearch;
133133
const products = search.products.items;
134134

135-
return products.map((product) => ({
136-
id: product.entityId.toString(),
137-
title: product.name,
138-
href: product.path,
139-
image: product.defaultImage
140-
? { src: product.defaultImage.url, alt: product.defaultImage.altText }
141-
: undefined,
142-
price: pricesTransformer(product.prices, format),
143-
subtitle: product.brand?.name ?? undefined,
144-
}));
135+
const { defaultOutOfStockMessage, showOutOfStockMessage, showBackorderMessage } =
136+
settings?.inventory || {};
137+
138+
return productCardTransformer(
139+
products,
140+
format,
141+
showOutOfStockMessage ? defaultOutOfStockMessage : undefined,
142+
showBackorderMessage,
143+
);
145144
});
146145

147146
const streamableTotalCount = Streamable.from(async () => {

core/app/[locale]/(default)/(faceted)/category/[slug]/page-data.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ const CategoryPageQuery = graphql(
3535
}
3636
}
3737
settings {
38+
inventory {
39+
defaultOutOfStockMessage
40+
showOutOfStockMessage
41+
showBackorderMessage
42+
}
3843
storefront {
3944
catalog {
4045
productComparisonsEnabled

core/app/[locale]/(default)/(faceted)/category/[slug]/page.tsx

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { getFilterParsers } from '@/vibes/soul/sections/products-list-section/fi
1212
import { getSessionCustomerAccessToken } from '~/auth';
1313
import { facetsTransformer } from '~/data-transformers/facets-transformer';
1414
import { pageInfoTransformer } from '~/data-transformers/page-info-transformer';
15-
import { pricesTransformer } from '~/data-transformers/prices-transformer';
15+
import { productCardTransformer } from '~/data-transformers/product-card-transformer';
1616
import { getPreferredCurrencyCode } from '~/lib/currency';
1717

1818
import { MAX_COMPARE_LIMIT } from '../../../compare/page-data';
@@ -145,16 +145,15 @@ export default async function Category(props: Props) {
145145
const search = await streamableFacetedSearch;
146146
const products = search.products.items;
147147

148-
return products.map((product) => ({
149-
id: product.entityId.toString(),
150-
title: product.name,
151-
href: product.path,
152-
image: product.defaultImage
153-
? { src: product.defaultImage.url, alt: product.defaultImage.altText }
154-
: undefined,
155-
price: pricesTransformer(product.prices, format),
156-
subtitle: product.brand?.name ?? undefined,
157-
}));
148+
const { defaultOutOfStockMessage, showOutOfStockMessage, showBackorderMessage } =
149+
settings?.inventory || {};
150+
151+
return productCardTransformer(
152+
products,
153+
format,
154+
showOutOfStockMessage ? defaultOutOfStockMessage : undefined,
155+
showBackorderMessage,
156+
);
158157
});
159158

160159
const streamableTotalCount = Streamable.from(async () => {

core/app/[locale]/(default)/(faceted)/search/page-data.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ const SearchPageQuery = graphql(`
88
query SearchPageQuery {
99
site {
1010
settings {
11+
inventory {
12+
defaultOutOfStockMessage
13+
showOutOfStockMessage
14+
showBackorderMessage
15+
}
1116
storefront {
1217
catalog {
1318
productComparisonsEnabled

core/app/[locale]/(default)/(faceted)/search/page.tsx

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { getFilterParsers } from '@/vibes/soul/sections/products-list-section/fi
1010
import { getSessionCustomerAccessToken } from '~/auth';
1111
import { facetsTransformer } from '~/data-transformers/facets-transformer';
1212
import { pageInfoTransformer } from '~/data-transformers/page-info-transformer';
13-
import { pricesTransformer } from '~/data-transformers/prices-transformer';
13+
import { productCardTransformer } from '~/data-transformers/product-card-transformer';
1414
import { getPreferredCurrencyCode } from '~/lib/currency';
1515

1616
import { MAX_COMPARE_LIMIT } from '../../compare/page-data';
@@ -117,16 +117,15 @@ export default async function Search(props: Props) {
117117
const search = await streamableFacetedSearch;
118118
const products = search.products.items;
119119

120-
return products.map((product) => ({
121-
id: product.entityId.toString(),
122-
title: product.name,
123-
href: product.path,
124-
image: product.defaultImage
125-
? { src: product.defaultImage.url, alt: product.defaultImage.altText }
126-
: undefined,
127-
price: pricesTransformer(product.prices, format),
128-
subtitle: product.brand?.name ?? undefined,
129-
}));
120+
const { defaultOutOfStockMessage, showOutOfStockMessage, showBackorderMessage } =
121+
settings?.inventory || {};
122+
123+
return productCardTransformer(
124+
products,
125+
format,
126+
showOutOfStockMessage ? defaultOutOfStockMessage : undefined,
127+
showBackorderMessage,
128+
);
130129
});
131130

132131
const streamableTitle = Streamable.from(async () => {

core/app/[locale]/(default)/page-data.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ const HomePageQuery = graphql(
6161
}
6262
}
6363
}
64+
settings {
65+
inventory {
66+
defaultOutOfStockMessage
67+
showOutOfStockMessage
68+
showBackorderMessage
69+
}
70+
}
6471
}
6572
}
6673
`,

core/app/[locale]/(default)/page.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,31 @@ export default async function Home({ params }: Props) {
3636

3737
const featuredProducts = removeEdgesAndNodes(data.site.featuredProducts);
3838

39-
return productCardTransformer(featuredProducts, format);
39+
const { defaultOutOfStockMessage, showOutOfStockMessage, showBackorderMessage } =
40+
data.site.settings?.inventory || {};
41+
42+
return productCardTransformer(
43+
featuredProducts,
44+
format,
45+
showOutOfStockMessage ? defaultOutOfStockMessage : undefined,
46+
showBackorderMessage,
47+
);
4048
});
4149

4250
const streamableNewestProducts = Streamable.from(async () => {
4351
const data = await streamablePageData;
4452

4553
const newestProducts = removeEdgesAndNodes(data.site.newestProducts);
4654

47-
return productCardTransformer(newestProducts, format);
55+
const { defaultOutOfStockMessage, showOutOfStockMessage, showBackorderMessage } =
56+
data.site.settings?.inventory || {};
57+
58+
return productCardTransformer(
59+
newestProducts,
60+
format,
61+
showOutOfStockMessage ? defaultOutOfStockMessage : undefined,
62+
showBackorderMessage,
63+
);
4864
});
4965

5066
return (

core/components/product-card/fragment.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { PricingFragment } from '~/client/fragments/pricing';
22
import { graphql } from '~/client/graphql';
33

4+
import { ProductVariantsInventoryFragment } from '../product-variants-inventory/fragment';
5+
46
export const ProductCardFragment = graphql(
57
`
68
fragment ProductCardFragment on Product {
@@ -15,12 +17,21 @@ export const ProductCardFragment = graphql(
1517
name
1618
path
1719
}
20+
inventory {
21+
hasVariantInventory
22+
isInStock
23+
aggregated {
24+
availableForBackorder
25+
unlimitedBackorder
26+
}
27+
}
1828
reviewSummary {
1929
numberOfReviews
2030
averageRating
2131
}
2232
...PricingFragment
33+
...ProductVariantsInventoryFragment
2334
}
2435
`,
25-
[PricingFragment],
36+
[PricingFragment, ProductVariantsInventoryFragment],
2637
);

0 commit comments

Comments
 (0)