diff --git a/apps/storefront/app/routes/categories.$categoryHandle.tsx b/apps/storefront/app/routes/categories.$categoryHandle.tsx index 2eedc11a6..ee3b1846d 100644 --- a/apps/storefront/app/routes/categories.$categoryHandle.tsx +++ b/apps/storefront/app/routes/categories.$categoryHandle.tsx @@ -2,7 +2,7 @@ import { Container } from '@app/components/common/container'; import { ProductListWithPagination } from '@app/components/product/ProductListWithPagination'; import { PageHeading } from '@app/components/sections/PageHeading'; import { listCategories } from '@libs/util/server/data/categories.server'; -import { fetchProducts } from '@libs/util/server/products.server'; +import { fetchProducts, getProductsPaginationParams } from '@libs/util/server/products.server'; import clsx from 'clsx'; import { LoaderFunctionArgs, NavLink, redirect, useLoaderData } from 'react-router'; @@ -18,6 +18,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { } const { products, count, limit, offset } = await fetchProducts(request, { + ...getProductsPaginationParams(request), category_id: category.id, }); @@ -38,7 +39,7 @@ export default function ProductCategoryRoute() { if (!data) return null; - const { products, count, limit, offset, categories } = data; + const { products, count, limit, offset, category, categories } = data; return ( @@ -75,7 +76,7 @@ export default function ProductCategoryRoute() { diff --git a/apps/storefront/app/routes/collections.$collectionHandle.tsx b/apps/storefront/app/routes/collections.$collectionHandle.tsx index d9ec95be3..c6c97474e 100644 --- a/apps/storefront/app/routes/collections.$collectionHandle.tsx +++ b/apps/storefront/app/routes/collections.$collectionHandle.tsx @@ -2,7 +2,7 @@ import { Container } from '@app/components/common/container'; import { ProductListWithPagination } from '@app/components/product/ProductListWithPagination'; import { PageHeading } from '@app/components/sections/PageHeading'; import { fetchCollections } from '@libs/util/server/data/collections.server'; -import { fetchProducts } from '@libs/util/server/products.server'; +import { fetchProducts, getProductsPaginationParams } from '@libs/util/server/products.server'; import clsx from 'clsx'; import { LoaderFunctionArgs, NavLink, redirect, useLoaderData } from 'react-router'; @@ -16,6 +16,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { if (!collection) throw redirect('/products'); const { products, count, limit, offset } = await fetchProducts(request, { + ...getProductsPaginationParams(request), collection_id: collection.id, }); @@ -65,7 +66,7 @@ export default function ProductCollectionRoute() { diff --git a/apps/storefront/app/routes/products._index.tsx b/apps/storefront/app/routes/products._index.tsx index 38cbe8a1c..c251a48a3 100644 --- a/apps/storefront/app/routes/products._index.tsx +++ b/apps/storefront/app/routes/products._index.tsx @@ -2,11 +2,11 @@ import { Breadcrumbs } from '@app/components/common/breadcrumbs'; import { Container } from '@app/components/common/container'; import { ProductListWithPagination } from '@app/components/product/ProductListWithPagination'; import HomeIcon from '@heroicons/react/24/solid/HomeIcon'; -import { fetchProducts } from '@libs/util/server/products.server'; +import { fetchProducts, getProductsPaginationParams } from '@libs/util/server/products.server'; import { LoaderFunctionArgs, useLoaderData } from 'react-router'; export const loader = async ({ request }: LoaderFunctionArgs) => { - const { products, count, limit, offset } = await fetchProducts(request, {}); + const { products, count, limit, offset } = await fetchProducts(request, getProductsPaginationParams(request)); return { products, count, limit, offset }; }; diff --git a/apps/storefront/libs/util/server/products.server.ts b/apps/storefront/libs/util/server/products.server.ts index 063298ec2..b21562190 100644 --- a/apps/storefront/libs/util/server/products.server.ts +++ b/apps/storefront/libs/util/server/products.server.ts @@ -4,6 +4,20 @@ import { HttpTypes } from '@medusajs/types'; import { MILLIS } from './cache-builder.server'; import { getSelectedRegion } from './data/regions.server'; +// Same page size the Medusa store API uses when no limit is sent. +export const PRODUCTS_PAGE_SIZE = 50; + +// Reads the `page` search param written by the pagination links and turns it into limit/offset. +// Only a whole number made of digits counts as a page. Anything else, such as "2abc", "1.5", "-1", +// "0" or an empty value, falls back to the first page. +export const getProductsPaginationParams = (request: Request, limit = PRODUCTS_PAGE_SIZE) => { + const pageParam = new URL(request.url).searchParams.get('page') ?? ''; + const page = /^\d+$/.test(pageParam) ? Number(pageParam) : 1; + const offset = (page - 1) * limit; + + return { limit, offset: page > 1 && Number.isSafeInteger(offset) ? offset : 0 }; +}; + export const fetchProducts = async (request: Request, { ...query }: HttpTypes.StoreProductListParams = {}) => { const region = await getSelectedRegion(request.headers);