diff --git a/app/search/(collection)/[...collection]/page.tsx b/app/search/(collection)/[...collection]/page.tsx index 1f8d4dfbf..67e3c326d 100644 --- a/app/search/(collection)/[...collection]/page.tsx +++ b/app/search/(collection)/[...collection]/page.tsx @@ -4,6 +4,7 @@ import { notFound } from 'next/navigation'; import Grid from 'components/grid'; import ProductGridItems from 'components/layout/product-grid-items'; +import Pagination from 'components/collection/pagination'; import { defaultSort, sorting } from 'lib/constants'; export const runtime = 'edge'; @@ -34,7 +35,7 @@ export default async function CategoryPage({ const { sort, page } = searchParams as { [key: string]: string }; const { sortKey, reverse } = sorting.find((item) => item.slug === sort) || defaultSort; - const products = await getCollectionProducts({ + const { products, total, limit } = await getCollectionProducts({ collection: params.collection, page: page ? parseInt(page) : 1, sortKey, @@ -46,9 +47,14 @@ export default async function CategoryPage({ {products.length === 0 ? (

{`No products found in this collection`}

) : ( - - - +
+ + + + +
)} ); diff --git a/components/collection/pagination.tsx b/components/collection/pagination.tsx new file mode 100644 index 000000000..ad3692498 --- /dev/null +++ b/components/collection/pagination.tsx @@ -0,0 +1,68 @@ +'use client' + +import ReactPaginate from 'react-paginate'; +import { createUrl } from 'lib/utils'; +import { usePathname, useSearchParams, useRouter } from 'next/navigation'; + +export default function Pagination({ itemsPerPage, itemsTotal, currentPage }: { itemsPerPage: number, itemsTotal: number, currentPage: number }) { + const router = useRouter(); + const pathname = usePathname(); + const currentParams = useSearchParams(); + const q = currentParams.get('q'); + const sort = currentParams.get('sort'); + const pageCount = Math.ceil(itemsTotal / itemsPerPage); + + // Invoke when user click to request another page. + const handlePageClick = (event: clickEvent) => { + const page = event.selected; + const newPage = page + 1; + let newUrl = createUrl(pathname, new URLSearchParams({ + ...(q && { q }), + ...(sort && { sort }), + })); + if (page !== 0) { + newUrl = createUrl(pathname, new URLSearchParams({ + ...(q && { q }), + ...(sort && { sort }), + page: newPage.toString(), + })); + } + router.replace(newUrl); + }; + + return ( + <> + + + ); +} + +type clickEvent = { + index: number | null; + selected: number; + nextSelectedPage: number | undefined; + event: object; + isPrevious: boolean; + isNext: boolean; + isBreak: boolean; + isActive: boolean; +} \ No newline at end of file diff --git a/components/grid/three-items.tsx b/components/grid/three-items.tsx index a47af2ed5..a7d2b9010 100644 --- a/components/grid/three-items.tsx +++ b/components/grid/three-items.tsx @@ -37,7 +37,7 @@ function ThreeItemGridItem({ export async function ThreeItemGrid() { // Collections that start with `hidden-*` are hidden from the search page. - const homepageItems = await getCollectionProducts({ + const { products: homepageItems } = await getCollectionProducts({ collection: 'Summer-BBQ/Hidden-Category' }); diff --git a/components/layout/search/filter/item.tsx b/components/layout/search/filter/item.tsx index 1a3b73ad3..6f02b9483 100644 --- a/components/layout/search/filter/item.tsx +++ b/components/layout/search/filter/item.tsx @@ -40,6 +40,7 @@ function SortFilterItem({ item }: { item: SortFilterItem }) { const searchParams = useSearchParams(); const [active, setActive] = useState(searchParams.get('sort') === item.slug); const q = searchParams.get('q'); + const page = searchParams.get('page'); useEffect(() => { setActive(searchParams.get('sort') === item.slug); @@ -51,6 +52,7 @@ function SortFilterItem({ item }: { item: SortFilterItem }) { pathname, new URLSearchParams({ ...(q && { q }), + ...(page && { page }), sort: item.slug }) ) diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index 717380b19..b46bdc920 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -109,7 +109,7 @@ export async function getCollectionProducts(params?: { sortKey?: string; categoryId?: string; defaultSearchCriteria?: Partial; -}): Promise { +}): Promise<{ products: Product[]; total: number; limit: number }> { let res; let category = params?.categoryId; const collectionName = transformHandle(params?.collection ?? ''); @@ -136,7 +136,9 @@ export async function getCollectionProducts(params?: { res = await requestCategoryProductsCollection(category, productsCriteria); } - return res ? transformProducts(res) : []; + return res + ? { products: transformProducts(res), total: res.total ?? 0, limit: res.limit ?? 0 } + : { products: [], total: 0, limit: 0 }; } export async function getCategory( diff --git a/package.json b/package.json index f749df541..e2a1e9fdd 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "next": "13.4.9", "react": "18.2.0", "react-cookie": "^4.1.1", - "react-dom": "18.2.0" + "react-dom": "18.2.0", + "react-paginate": "^8.2.0" }, "devDependencies": { "@playwright/test": "^1.35.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1be262caa..7d0d18cd1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -28,6 +28,9 @@ dependencies: react-dom: specifier: 18.2.0 version: 18.2.0(react@18.2.0) + react-paginate: + specifier: ^8.2.0 + version: 8.2.0(react@18.2.0) devDependencies: '@playwright/test': @@ -2411,7 +2414,6 @@ packages: /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - dev: true /object-hash@3.0.0: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} @@ -2811,7 +2813,6 @@ packages: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 - dev: true /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} @@ -2846,6 +2847,15 @@ packages: /react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + /react-paginate@8.2.0(react@18.2.0): + resolution: {integrity: sha512-sJCz1PW+9PNIjUSn919nlcRVuleN2YPoFBOvL+6TPgrH/3lwphqiSOgdrLafLdyLDxsgK+oSgviqacF4hxsDIw==} + peerDependencies: + react: ^16 || ^17 || ^18 + dependencies: + prop-types: 15.8.1 + react: 18.2.0 + dev: false + /react@18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'}