From bdb11406e7ac7f3059d661811ba65c68e721200a Mon Sep 17 00:00:00 2001 From: sylwia-werner Date: Wed, 5 Nov 2025 13:05:58 +0100 Subject: [PATCH 1/6] fix: remove limit to 20 products, fix pagination --- .../ProductListing/AlgoliaProductsListing.tsx | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/components/sections/ProductListing/AlgoliaProductsListing.tsx b/src/components/sections/ProductListing/AlgoliaProductsListing.tsx index 13be3a2..14f0623 100644 --- a/src/components/sections/ProductListing/AlgoliaProductsListing.tsx +++ b/src/components/sections/ProductListing/AlgoliaProductsListing.tsx @@ -14,7 +14,7 @@ import { useSearchParams } from "next/navigation" import { getFacedFilters } from "@/lib/helpers/get-faced-filters" import { PRODUCT_LIMIT } from "@/const" import { ProductListingSkeleton } from "@/components/organisms/ProductListingSkeleton/ProductListingSkeleton" -import { useEffect, useState } from "react" +import { useEffect, useMemo, useState } from "react" import { listProducts } from "@/lib/data/products" import { getProductPrice } from "@/lib/helpers/get-product-price" @@ -31,10 +31,11 @@ export const AlgoliaProductsListing = ({ seller_handle?: string currency_code: string }) => { - const searchParamas = useSearchParams() + const searchParams = useSearchParams() - const facetFilters: string = getFacedFilters(searchParamas) - const query: string = searchParamas.get("query") || "" + const facetFilters: string = getFacedFilters(searchParams) + const query: string = searchParams.get("query") || "" + const page: number = +(searchParams.get("page") || 1) const filters = `${ seller_handle @@ -52,7 +53,12 @@ export const AlgoliaProductsListing = ({ return ( - + (null) const { items, results } = useHits() - const searchParamas = useSearchParams() + const searchParams = useSearchParams() + + const itemsKey = useMemo( + () => items.map((item) => item.objectID).join(","), + [items] + ) async function handleSetProducts() { try { @@ -103,30 +114,29 @@ const ProductsListing = ({ } useEffect(() => { - handleSetProducts() - }, [items.length]) + if (items.length > 0) { + handleSetProducts() + } + }, [itemsKey]) if (!results?.processingTimeMS) return - const page: number = +(searchParamas.get("page") || 1) const filteredProducts = items.filter((pr) => - apiProducts?.some((p: any) => p.id === pr.objectID) + apiProducts?.some((p) => p.id === pr.objectID) ) - const products = filteredProducts - .filter((pr) => - apiProducts?.some( - (p: any) => p.id === pr.objectID && filterProductsByCurrencyCode(p) - ) + const products = filteredProducts.filter((pr) => + apiProducts?.some( + (p) => p.id === pr.objectID && filterProductsByCurrencyCode(p) ) - .slice((page - 1) * PRODUCT_LIMIT, page * PRODUCT_LIMIT) + ) - const count = filteredProducts?.length || 0 - const pages = Math.ceil(count / PRODUCT_LIMIT) || 1 + const count = results?.nbHits || 0 + const pages = results?.nbPages || 1 function filterProductsByCurrencyCode(product: HttpTypes.StoreProduct) { - const minPrice = searchParamas.get("min_price") - const maxPrice = searchParamas.get("max_price") + const minPrice = searchParams.get("min_price") + const maxPrice = searchParams.get("max_price") if ([minPrice, maxPrice].some((price) => typeof price === "string")) { const variantsWithCurrencyCode = product?.variants?.filter( From a3c727b5f26d7744843f051c2e2fe9dbd61ef358 Mon Sep 17 00:00:00 2001 From: sylwia-werner Date: Wed, 5 Nov 2025 13:24:36 +0100 Subject: [PATCH 2/6] fix: add skeleton for algolia product listing to avoid layout shifts --- .../ProductListing/AlgoliaProductsListing.tsx | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/components/sections/ProductListing/AlgoliaProductsListing.tsx b/src/components/sections/ProductListing/AlgoliaProductsListing.tsx index 14f0623..7b7ec08 100644 --- a/src/components/sections/ProductListing/AlgoliaProductsListing.tsx +++ b/src/components/sections/ProductListing/AlgoliaProductsListing.tsx @@ -80,6 +80,7 @@ const ProductsListing = ({ const [apiProducts, setApiProducts] = useState< HttpTypes.StoreProduct[] | null >(null) + const [isLoadingProducts, setIsLoadingProducts] = useState(false) const { items, results } = useHits() const searchParams = useSearchParams() @@ -91,7 +92,7 @@ const ProductsListing = ({ async function handleSetProducts() { try { - setApiProducts(null) + setIsLoadingProducts(true) const { response } = await listProducts({ countryCode: locale, queryParams: { @@ -110,17 +111,24 @@ const ProductsListing = ({ ) } catch (error) { setApiProducts(null) + } finally { + setIsLoadingProducts(false) } } useEffect(() => { if (items.length > 0) { handleSetProducts() + } else { + setApiProducts([]) + setIsLoadingProducts(false) } }, [itemsKey]) if (!results?.processingTimeMS) return + const isLoading = isLoadingProducts && items.length > 0 + const filteredProducts = items.filter((pr) => apiProducts?.some((p) => p.id === pr.objectID) ) @@ -184,7 +192,24 @@ const ProductsListing = ({
- {!items.length ? ( + {isLoading ? ( +
+ {Array.from({ length: PRODUCT_LIMIT }).map((_, idx) => ( +
+
+
+
+
+
+
+
+
+ ))} +
+ ) : !items.length ? (

no results

From ff55654a29411dc0b96ae6879a22b801bba80581 Mon Sep 17 00:00:00 2001 From: sylwia-werner Date: Wed, 5 Nov 2025 13:57:29 +0100 Subject: [PATCH 3/6] fix: add existing product card skeleton component to algolia product listing --- .../ProductListing/AlgoliaProductsListing.tsx | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/components/sections/ProductListing/AlgoliaProductsListing.tsx b/src/components/sections/ProductListing/AlgoliaProductsListing.tsx index 7b7ec08..c8e0bdc 100644 --- a/src/components/sections/ProductListing/AlgoliaProductsListing.tsx +++ b/src/components/sections/ProductListing/AlgoliaProductsListing.tsx @@ -17,6 +17,7 @@ import { ProductListingSkeleton } from "@/components/organisms/ProductListingSke import { useEffect, useMemo, useState } from "react" import { listProducts } from "@/lib/data/products" import { getProductPrice } from "@/lib/helpers/get-product-price" +import { SkeletonProductCard } from "@/components/organisms/ProductCard/SkeletonProductCard" export const AlgoliaProductsListing = ({ category_id, @@ -195,18 +196,7 @@ const ProductsListing = ({ {isLoading ? (

{Array.from({ length: PRODUCT_LIMIT }).map((_, idx) => ( -
-
-
-
-
-
-
-
-
+ ))}
) : !items.length ? ( From 8879a55cd6a3cf7541e4727cb401c24afc53ba2f Mon Sep 17 00:00:00 2001 From: sylwia-werner Date: Wed, 12 Nov 2025 09:57:22 +0100 Subject: [PATCH 4/6] refactor: separate product listing components --- .../ProductListingLoadingView.tsx | 12 +++++ .../ProductListingNoResultsView.tsx | 10 ++++ .../ProductListingProductsView.tsx | 30 +++++++++++ src/components/molecules/index.ts | 6 +++ .../ProductListing/AlgoliaProductsListing.tsx | 53 +++++++------------ 5 files changed, 77 insertions(+), 34 deletions(-) create mode 100644 src/components/molecules/ProductListingLoadingView/ProductListingLoadingView.tsx create mode 100644 src/components/molecules/ProductListingNoResultsView/ProductListingNoResultsView.tsx create mode 100644 src/components/molecules/ProductListingProductsView/ProductListingProductsView.tsx diff --git a/src/components/molecules/ProductListingLoadingView/ProductListingLoadingView.tsx b/src/components/molecules/ProductListingLoadingView/ProductListingLoadingView.tsx new file mode 100644 index 0000000..76595c8 --- /dev/null +++ b/src/components/molecules/ProductListingLoadingView/ProductListingLoadingView.tsx @@ -0,0 +1,12 @@ +import { SkeletonProductCard } from "@/components/organisms/ProductCard/SkeletonProductCard" +import { PRODUCT_LIMIT } from "@/const" + +const ProductListingLoadingView = () => ( +
+ {Array.from({ length: PRODUCT_LIMIT }).map((_, idx) => ( + + ))} +
+) + +export default ProductListingLoadingView \ No newline at end of file diff --git a/src/components/molecules/ProductListingNoResultsView/ProductListingNoResultsView.tsx b/src/components/molecules/ProductListingNoResultsView/ProductListingNoResultsView.tsx new file mode 100644 index 0000000..e599250 --- /dev/null +++ b/src/components/molecules/ProductListingNoResultsView/ProductListingNoResultsView.tsx @@ -0,0 +1,10 @@ +const ProductListingNoResultsView = () => ( +
+

No results

+

+ Sorry, we can't find any results for your criteria +

+
+) + +export default ProductListingNoResultsView \ No newline at end of file diff --git a/src/components/molecules/ProductListingProductsView/ProductListingProductsView.tsx b/src/components/molecules/ProductListingProductsView/ProductListingProductsView.tsx new file mode 100644 index 0000000..49459e7 --- /dev/null +++ b/src/components/molecules/ProductListingProductsView/ProductListingProductsView.tsx @@ -0,0 +1,30 @@ +import { HttpTypes } from "@medusajs/types" +import { BaseHit, Hit } from "instantsearch.js" +import { ProductCard } from "@/components/organisms" + +interface Props { + products: Hit[] + apiProducts: HttpTypes.StoreProduct[] | null +} + +const ProductListingProductsView = ({ + products, + apiProducts, +}: Props) => ( +
+
    + {products.map( + (hit) => + apiProducts?.find((p) => p.id === hit.objectID) && ( + p.id === hit.objectID)} + key={hit.objectID} + product={hit} + /> + ) + )} +
+
+) + +export default ProductListingProductsView \ No newline at end of file diff --git a/src/components/molecules/index.ts b/src/components/molecules/index.ts index da5c155..54d0a5d 100644 --- a/src/components/molecules/index.ts +++ b/src/components/molecules/index.ts @@ -32,6 +32,9 @@ import { ParcelAccordion } from "./ParcelAccordion/ParcelAccordion" import { AddressForm } from "./AddressForm/AddressForm" import { ReviewForm } from "./ReviewForm/ReviewForm" import { ProfileDetails } from "./ProfileDetails/ProfileDetails" +import ProductListingLoadingView from "./ProductListingLoadingView/ProductListingLoadingView" +import ProductListingNoResultsView from "./ProductListingNoResultsView/ProductListingNoResultsView" +import ProductListingProductsView from "./ProductListingProductsView/ProductListingProductsView" export { PrimeCategoryNavbar, @@ -68,4 +71,7 @@ export { AddressForm, ReviewForm, ProfileDetails, + ProductListingLoadingView, + ProductListingNoResultsView, + ProductListingProductsView, } diff --git a/src/components/sections/ProductListing/AlgoliaProductsListing.tsx b/src/components/sections/ProductListing/AlgoliaProductsListing.tsx index c8e0bdc..ed1f7ad 100644 --- a/src/components/sections/ProductListing/AlgoliaProductsListing.tsx +++ b/src/components/sections/ProductListing/AlgoliaProductsListing.tsx @@ -3,10 +3,14 @@ import { HttpTypes } from "@medusajs/types" import { AlgoliaProductSidebar, - ProductCard, ProductListingActiveFilters, ProductsPagination, } from "@/components/organisms" +import { + ProductListingLoadingView, + ProductListingNoResultsView, + ProductListingProductsView, +} from "@/components/molecules" import { client } from "@/lib/client" import { Configure, useHits } from "react-instantsearch" import { InstantSearchNext } from "react-instantsearch-nextjs" @@ -17,7 +21,6 @@ import { ProductListingSkeleton } from "@/components/organisms/ProductListingSke import { useEffect, useMemo, useState } from "react" import { listProducts } from "@/lib/data/products" import { getProductPrice } from "@/lib/helpers/get-product-price" -import { SkeletonProductCard } from "@/components/organisms/ProductCard/SkeletonProductCard" export const AlgoliaProductsListing = ({ category_id, @@ -192,41 +195,23 @@ const ProductsListing = ({
-
- {isLoading ? ( -
- {Array.from({ length: PRODUCT_LIMIT }).map((_, idx) => ( - - ))} -
- ) : !items.length ? ( -
-

no results

-

- Sorry, we can't find any results for your criteria -

-
- ) : ( -
-
    - {products.map( - (hit) => - apiProducts?.find((p: any) => p.id === hit.objectID) && ( - p.id === hit.objectID - )} - key={hit.objectID} - product={hit} - /> - ) - )} -
-
+
+ {isLoading && } + + {!isLoading && !items.length && } + + {!isLoading && items.length > 0 && ( + )} + +
+ +
-
) } From 9cdc6a4248eceb42d5a68b1db9e964aa8fdc2bb3 Mon Sep 17 00:00:00 2001 From: sylwia-werner Date: Mon, 17 Nov 2025 12:46:35 +0100 Subject: [PATCH 5/6] fix: PR fixes --- .../ProductListingProductsView/ProductListingProductsView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/ProductListingProductsView/ProductListingProductsView.tsx b/src/components/molecules/ProductListingProductsView/ProductListingProductsView.tsx index 49459e7..d3aa8fc 100644 --- a/src/components/molecules/ProductListingProductsView/ProductListingProductsView.tsx +++ b/src/components/molecules/ProductListingProductsView/ProductListingProductsView.tsx @@ -12,7 +12,7 @@ const ProductListingProductsView = ({ apiProducts, }: Props) => (
-
    +
    {products.map( (hit) => apiProducts?.find((p) => p.id === hit.objectID) && ( @@ -23,7 +23,7 @@ const ProductListingProductsView = ({ /> ) )} -
+
) From 3d577c4770d9d4cbe992dba1d6c5de3b09091d74 Mon Sep 17 00:00:00 2001 From: sylwia-werner Date: Mon, 17 Nov 2025 13:13:56 +0100 Subject: [PATCH 6/6] fix: PR fixes, add li to element to ensure semantics --- .../ProductListingProductsView.tsx | 23 +++++++++---------- .../organisms/ProductCard/ProductCard.tsx | 9 +++++--- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/components/molecules/ProductListingProductsView/ProductListingProductsView.tsx b/src/components/molecules/ProductListingProductsView/ProductListingProductsView.tsx index d3aa8fc..cdcd118 100644 --- a/src/components/molecules/ProductListingProductsView/ProductListingProductsView.tsx +++ b/src/components/molecules/ProductListingProductsView/ProductListingProductsView.tsx @@ -7,24 +7,23 @@ interface Props { apiProducts: HttpTypes.StoreProduct[] | null } -const ProductListingProductsView = ({ - products, - apiProducts, -}: Props) => ( +const ProductListingProductsView = ({ products, apiProducts }: Props) => (
-
+
    {products.map( (hit) => apiProducts?.find((p) => p.id === hit.objectID) && ( - p.id === hit.objectID)} - key={hit.objectID} - product={hit} - /> +
  • + p.id === hit.objectID)} + product={hit} + className="w-full h-full lg:w-full min-w-0" + /> +
  • ) )} -
+
) -export default ProductListingProductsView \ No newline at end of file +export default ProductListingProductsView diff --git a/src/components/organisms/ProductCard/ProductCard.tsx b/src/components/organisms/ProductCard/ProductCard.tsx index 0414be0..dd019a2 100644 --- a/src/components/organisms/ProductCard/ProductCard.tsx +++ b/src/components/organisms/ProductCard/ProductCard.tsx @@ -4,16 +4,18 @@ import Image from "next/image" import { Button } from "@/components/atoms" import { HttpTypes } from "@medusajs/types" import { BaseHit, Hit } from "instantsearch.js" -import clsx from "clsx" +import { cn } from "@/lib/utils" import LocalizedClientLink from "@/components/molecules/LocalizedLink/LocalizedLink" import { getProductPrice } from "@/lib/helpers/get-product-price" export const ProductCard = ({ product, api_product, + className, }: { product: Hit | Partial> api_product?: HttpTypes.StoreProduct | null + className?: string }) => { if (!api_product) { return null @@ -27,8 +29,9 @@ export const ProductCard = ({ return (