diff --git a/samples/headless-ssr/commerce-nextjs/app/search/page.tsx b/samples/headless-ssr/commerce-nextjs/app/search/page.tsx index 94e45843d60..c95bf58a1af 100644 --- a/samples/headless-ssr/commerce-nextjs/app/search/page.tsx +++ b/samples/headless-ssr/commerce-nextjs/app/search/page.tsx @@ -7,6 +7,7 @@ import DidYouMean from '@/components/did-you-mean'; import FacetGenerator from '@/components/facets/facet-generator'; import ParameterManager from '@/components/parameter-manager'; import ProductList from '@/components/product-list'; +import {AvailabilityProvider} from '@/components/providers/availability-provider'; import {SearchProvider} from '@/components/providers/providers'; import SearchBox from '@/components/search-box'; import ShowMore from '@/components/show-more'; @@ -15,6 +16,10 @@ import NotifyTrigger from '@/components/triggers/notify-trigger'; import QueryTrigger from '@/components/triggers/query-trigger'; import {searchEngineDefinition} from '@/lib/commerce-engine'; import {NextJsNavigatorContext} from '@/lib/navigatorContextProvider'; +import { + type Availability, + fetchAvailability, +} from '@/lib/third-party-api-provider'; import {defaultContext} from '@/utils/context'; export default async function Search({ @@ -48,6 +53,38 @@ export default async function Search({ }, }); + /** + * We elect to not block the rendering of the data of the system that handles our stock. + * Instead: + * - We do enqueue all requests + * - We "race them" against a 600ms dummy promise after_ + * The latter represent the "budget" we allocate to the fake system before we continue on the rendering. + * When we do, future resolved values will be passed along using Suspense & Streaming. + */ + + // The map is provided later on as a context as it will be used by our "suspensed" availability UI component + const productIdToAvailabilityRequests = new Map< + string, + Promise + >(); + + // For each product, we enqueue the request and set it in the map we'll provide as context. + for (const product of staticState.controllers.productList.state.products) { + if (!product.ec_product_id) { + continue; + } + productIdToAvailabilityRequests.set( + product.ec_product_id, + fetchAvailability(product.ec_product_id) + ); + } + + // Then, we wait up to 600ms or for all requests to resolves. Any resolved request by the end of the 600ms will be usable during the initial rendering + // This is mostly done for demo purposes of the streaming process, and arbitrary delay such as this should _probably_ not be used in production implementation + await Promise.race([ + Promise.allSettled(productIdToAvailabilityRequests.values()), + new Promise((resolve) => setTimeout(resolve, 600)), + ]); return ( <>

Search

@@ -69,7 +106,11 @@ export default async function Search({ - + + + {/* The ShowMore and Pagination components showcase two frequent ways to implement pagination. */} {/* Loading...}> + + + ); +} + +const CustomAvailabilityBadge = ({ + availabilityPromise, +}: { + availabilityPromise: Promise | undefined; +}) => { + const availability = availabilityPromise ? use(availabilityPromise) : null; + switch (availability) { + case Availability.High: + return
High Availability
; + case Availability.Medium: + return
Medium Availability
; + case Availability.Low: + return
Low Availability
; + case Availability.OutOfStock: + return
Out of stock
; + default: + return
Unknown Availability
; + } +}; + +export {SuspendedCustomAvailabilityBadge as CustomAvailabilityBadge}; diff --git a/samples/headless-ssr/commerce-nextjs/components/product-list.tsx b/samples/headless-ssr/commerce-nextjs/components/product-list.tsx index e40ead32b53..a6d27a7de28 100644 --- a/samples/headless-ssr/commerce-nextjs/components/product-list.tsx +++ b/samples/headless-ssr/commerce-nextjs/components/product-list.tsx @@ -2,6 +2,7 @@ import {useCart, useProductList} from '@/lib/commerce-engine'; import {addToCart} from '@/utils/cart'; +import {CustomAvailabilityBadge} from './custom-availability-badge'; import ProductButtonWithImage from './product-button-with-image'; export default function ProductList() { @@ -13,7 +14,9 @@ export default function ProductList() { {state.products.map((product) => (
  • - + {product.ec_product_id && ( + + )}