diff --git a/src/components/molecules/ProductListingLoadingView/ProductListingLoadingView.tsx b/src/components/molecules/ProductListingLoadingView/ProductListingLoadingView.tsx new file mode 100644 index 00000000..76595c8e --- /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 00000000..e599250b --- /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 00000000..cdcd1188 --- /dev/null +++ b/src/components/molecules/ProductListingProductsView/ProductListingProductsView.tsx @@ -0,0 +1,29 @@ +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) => ( +
+ +
+) + +export default ProductListingProductsView diff --git a/src/components/molecules/index.ts b/src/components/molecules/index.ts index da5c1551..54d0a5da 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/organisms/ProductCard/ProductCard.tsx b/src/components/organisms/ProductCard/ProductCard.tsx index 0414be05..dd019a20 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 (
diff --git a/src/components/sections/ProductListing/AlgoliaProductsListing.tsx b/src/components/sections/ProductListing/AlgoliaProductsListing.tsx index 13be3a2d..ed1f7ade 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" @@ -14,7 +18,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 +35,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 +57,12 @@ export const AlgoliaProductsListing = ({ return ( - + (null) + const [isLoadingProducts, setIsLoadingProducts] = useState(false) const { items, results } = useHits() - const searchParamas = useSearchParams() + const searchParams = useSearchParams() + + const itemsKey = useMemo( + () => items.map((item) => item.objectID).join(","), + [items] + ) async function handleSetProducts() { try { - setApiProducts(null) + setIsLoadingProducts(true) const { response } = await listProducts({ countryCode: locale, queryParams: { @@ -99,34 +115,40 @@ const ProductsListing = ({ ) } catch (error) { setApiProducts(null) + } finally { + setIsLoadingProducts(false) } } useEffect(() => { - handleSetProducts() - }, [items.length]) + if (items.length > 0) { + handleSetProducts() + } else { + setApiProducts([]) + setIsLoadingProducts(false) + } + }, [itemsKey]) if (!results?.processingTimeMS) return - const page: number = +(searchParamas.get("page") || 1) + const isLoading = isLoadingProducts && items.length > 0 + 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( @@ -173,35 +195,23 @@ const ProductsListing = ({
-
- {!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 && ( + )} + +
+ +
-
) }