diff --git a/src/pages/blog/[...page].astro b/src/pages/blog/[...page].astro index 621b3d7..79cbddd 100644 --- a/src/pages/blog/[...page].astro +++ b/src/pages/blog/[...page].astro @@ -4,15 +4,20 @@ import { getCollection } from "astro:content"; import Headline from "@/components/ui/Headline.astro"; import Pagination from "@/components/ui/Pagination.astro"; import PostItem from "@/components/blog/PostItem.astro"; -import type { GetStaticPathsOptions } from "astro"; +import { buildPaginatedPaths } from "@/utils/paginate"; -export async function getStaticPaths({ paginate }: GetStaticPathsOptions) { +export async function getStaticPaths() { const blogEntries = await getCollection("blog"); const sortedPosts = blogEntries .filter((post) => post.data && post.data.pubDate) .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()); - return paginate(sortedPosts, { pageSize: 6 }); + return buildPaginatedPaths(sortedPosts, "/blog").map( + ({ pageParam, page }) => ({ + params: { page: pageParam }, + props: { page }, + }), + ); } const { page } = Astro.props; diff --git a/src/pages/category/[...rest].astro b/src/pages/category/[...rest].astro index 118df7b..61c51e5 100644 --- a/src/pages/category/[...rest].astro +++ b/src/pages/category/[...rest].astro @@ -4,16 +4,14 @@ import { getCollection } from "astro:content"; import Headline from "@/components/ui/Headline.astro"; import PostItem from "@/components/blog/PostItem.astro"; import Pagination from "@/components/ui/Pagination.astro"; -import type { Page } from "astro"; -import type { CollectionEntry } from "astro:content"; import { getTaxonomyMap, getTaxonomyPath, type TaxonomyItem, } from "@/utils/taxonomy"; +import { buildPaginatedPaths } from "@/utils/paginate"; export async function getStaticPaths() { - const PAGE_SIZE = 6; const allPosts = await getCollection("blog"); const posts = allPosts.filter((post) => post.data && post.data.pubDate); @@ -31,32 +29,22 @@ export async function getStaticPaths() { const cat = getTaxonomyMap("category").get(slug) ?? ({ slug, name: slug } as TaxonomyItem); - const totalPages = Math.max(1, Math.ceil(catPosts.length / PAGE_SIZE)); - return Array.from({ length: totalPages }, (_, i) => { - const pageNum = i + 1; - return { - params: { rest: pageNum === 1 ? fullPath : `${fullPath}/${pageNum}` }, + return buildPaginatedPaths(catPosts, `/category/${fullPath}`).map( + ({ pageParam, page }) => ({ + params: { rest: pageParam ? `${fullPath}/${pageParam}` : fullPath }, props: { cat, fullPath, - posts: catPosts.slice(i * PAGE_SIZE, (i + 1) * PAGE_SIZE), - currentPage: pageNum, - lastPage: totalPages, - total: catPosts.length, - prevUrl: - pageNum === 1 - ? undefined - : pageNum === 2 - ? `/category/${fullPath}` - : `/category/${fullPath}/${pageNum - 1}`, - nextUrl: - pageNum === totalPages - ? undefined - : `/category/${fullPath}/${pageNum + 1}`, + posts: page.data, + currentPage: page.currentPage, + lastPage: page.lastPage, + total: page.total, + prevUrl: page.url.prev, + nextUrl: page.url.next, }, - }; - }); + }), + ); }); } diff --git a/src/pages/category/[category]/[...page].astro b/src/pages/category/[category]/[...page].astro index b083be3..9b4fb08 100644 --- a/src/pages/category/[category]/[...page].astro +++ b/src/pages/category/[category]/[...page].astro @@ -4,10 +4,10 @@ import { getCollection } from "astro:content"; import Headline from "@/components/ui/Headline.astro"; import PostItem from "@/components/blog/PostItem.astro"; import Pagination from "@/components/ui/Pagination.astro"; -import type { GetStaticPathsOptions } from "astro"; import { getTaxonomyMap } from "@/utils/taxonomy"; +import { buildPaginatedPaths } from "@/utils/paginate"; -export async function getStaticPaths({ paginate }: GetStaticPathsOptions) { +export async function getStaticPaths() { const allPosts = await getCollection("blog"); const posts = allPosts.filter((post) => post.data && post.data.pubDate); @@ -21,10 +21,12 @@ export async function getStaticPaths({ paginate }: GetStaticPathsOptions) { .filter((post) => post.data.categories?.includes(category)) .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()); - return paginate(categoryPosts, { - params: { category }, - pageSize: 6, - }); + return buildPaginatedPaths(categoryPosts, `/category/${category}`).map( + ({ pageParam, page }) => ({ + params: { category, page: pageParam }, + props: { page }, + }), + ); }); } diff --git a/src/pages/destination/[...rest].astro b/src/pages/destination/[...rest].astro index 3bfef66..bfce47c 100644 --- a/src/pages/destination/[...rest].astro +++ b/src/pages/destination/[...rest].astro @@ -4,16 +4,14 @@ import { getCollection } from "astro:content"; import Headline from "@/components/ui/Headline.astro"; import PostItem from "@/components/blog/PostItem.astro"; import Pagination from "@/components/ui/Pagination.astro"; -import type { Page } from "astro"; -import type { CollectionEntry } from "astro:content"; import { getTaxonomyMap, getTaxonomyPath, type TaxonomyItem, } from "@/utils/taxonomy"; +import { buildPaginatedPaths } from "@/utils/paginate"; export async function getStaticPaths() { - const PAGE_SIZE = 6; const allPosts = await getCollection("blog"); const posts = allPosts.filter((post) => post.data && post.data.pubDate); @@ -31,32 +29,22 @@ export async function getStaticPaths() { const dest = getTaxonomyMap("destination").get(slug) ?? ({ slug, name: slug } as TaxonomyItem); - const totalPages = Math.max(1, Math.ceil(destPosts.length / PAGE_SIZE)); - return Array.from({ length: totalPages }, (_, i) => { - const pageNum = i + 1; - return { - params: { rest: pageNum === 1 ? fullPath : `${fullPath}/${pageNum}` }, + return buildPaginatedPaths(destPosts, `/destination/${fullPath}`).map( + ({ pageParam, page }) => ({ + params: { rest: pageParam ? `${fullPath}/${pageParam}` : fullPath }, props: { dest, fullPath, - posts: destPosts.slice(i * PAGE_SIZE, (i + 1) * PAGE_SIZE), - currentPage: pageNum, - lastPage: totalPages, - total: destPosts.length, - prevUrl: - pageNum === 1 - ? undefined - : pageNum === 2 - ? `/destination/${fullPath}` - : `/destination/${fullPath}/${pageNum - 1}`, - nextUrl: - pageNum === totalPages - ? undefined - : `/destination/${fullPath}/${pageNum + 1}`, + posts: page.data, + currentPage: page.currentPage, + lastPage: page.lastPage, + total: page.total, + prevUrl: page.url.prev, + nextUrl: page.url.next, }, - }; - }); + }), + ); }); } diff --git a/src/pages/tag/[tag]/[...page].astro b/src/pages/tag/[tag]/[...page].astro index 4984bd4..c063ff9 100644 --- a/src/pages/tag/[tag]/[...page].astro +++ b/src/pages/tag/[tag]/[...page].astro @@ -4,10 +4,10 @@ import { getCollection } from "astro:content"; import Headline from "@/components/ui/Headline.astro"; import PostItem from "@/components/blog/PostItem.astro"; import Pagination from "@/components/ui/Pagination.astro"; -import type { GetStaticPathsOptions } from "astro"; import { getTaxonomyMap } from "@/utils/taxonomy"; +import { buildPaginatedPaths } from "@/utils/paginate"; -export async function getStaticPaths({ paginate }: GetStaticPathsOptions) { +export async function getStaticPaths() { const allPosts = await getCollection("blog"); const posts = allPosts.filter((post) => post.data && post.data.pubDate); @@ -21,10 +21,12 @@ export async function getStaticPaths({ paginate }: GetStaticPathsOptions) { .filter((post) => post.data.tags?.includes(tag)) .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()); - return paginate(tagPosts, { - params: { tag }, - pageSize: 6, - }); + return buildPaginatedPaths(tagPosts, `/tag/${tag}`).map( + ({ pageParam, page }) => ({ + params: { tag, page: pageParam }, + props: { page }, + }), + ); }); } diff --git a/src/utils/paginate.ts b/src/utils/paginate.ts new file mode 100644 index 0000000..b7ff203 --- /dev/null +++ b/src/utils/paginate.ts @@ -0,0 +1,51 @@ +export const DEFAULT_PAGE_SIZE = 6; + +export interface PageMeta { + data: unknown[]; + total: number; + currentPage: number; + lastPage: number; + url: { prev?: string; next?: string }; +} + +/** + * Returns paginated static paths using WordPress-style /page/{n} URLs. + * + * @param items Full sorted list of items to paginate. + * @param basePath Base URL without trailing slash, e.g. "/blog" or "/category/travel". + * @param pageSize Number of items per page (default: DEFAULT_PAGE_SIZE). + * @returns Array of { params: { page }, props: { page: PageMeta } }. + */ +export function buildPaginatedPaths( + items: T[], + basePath: string, + pageSize = DEFAULT_PAGE_SIZE, +) { + const totalPages = Math.max(1, Math.ceil(items.length / pageSize)); + + return Array.from({ length: totalPages }, (_, i) => { + const pageNum = i + 1; + const data = items.slice(i * pageSize, (i + 1) * pageSize); + + const prev = + pageNum === 1 + ? undefined + : pageNum === 2 + ? basePath + : `${basePath}/page/${pageNum - 1}`; + const next = + pageNum === totalPages ? undefined : `${basePath}/page/${pageNum + 1}`; + + return { + // page param: undefined for page 1 (matches the base URL), "page/N" for N≥2 + pageParam: pageNum === 1 ? undefined : `page/${pageNum}`, + page: { + data, + total: items.length, + currentPage: pageNum, + lastPage: totalPages, + url: { prev, next }, + } as PageMeta & { data: T[] }, + }; + }); +}