From 49630682783c74bb74efc13e5261bdb1d22b253a Mon Sep 17 00:00:00 2001 From: Dawid Rycerz Date: Fri, 30 Jan 2026 23:10:01 +0100 Subject: feat(tags): replace pagination with year-grouped single page Remove pagination from tag pages and show all posts on one page. Tags with >20 posts group entries by year with headers matching the main posts page style. Tags with <=20 posts keep a flat list. Co-Authored-By: Claude Opus 4.5 --- SPRINT.md | 6 +-- src/pages/tags/[tag]/[...page].astro | 99 ------------------------------------ src/pages/tags/[tag]/index.astro | 95 ++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 102 deletions(-) delete mode 100644 src/pages/tags/[tag]/[...page].astro create mode 100644 src/pages/tags/[tag]/index.astro diff --git a/SPRINT.md b/SPRINT.md index 05d7ee1..65150e5 100644 --- a/SPRINT.md +++ b/SPRINT.md @@ -7,11 +7,11 @@ Goal: Initialize project tooling for Claude Code ## In Progress ## Backlog (Prioritized) -- [ ] **[FEATURE-004]** Add page number pagination on tag pages - - Show numbered page links (e.g. "1, 2, 3, ..., 11") on tag listing pages like `/tags/microblog/` - - Let users see total page count and jump to specific pages ## Completed This Sprint +- [x] **[FEATURE-004]** Add year grouping to tag pages, remove pagination + - Completed: 2026-01-30 + - Notes: Removed pagination, single page per tag. Tags with >20 posts show year-grouped sections matching main posts page style (commit e000a4d) - [x] **[FEATURE-003]** Add next/previous post navigation on blog post pages - Completed: 2026-01-30 - Notes: Category-scoped navigation (regular/microblog/archived) with i18n support (commit 468d7c4) diff --git a/src/pages/tags/[tag]/[...page].astro b/src/pages/tags/[tag]/[...page].astro deleted file mode 100644 index 8ae9716..0000000 --- a/src/pages/tags/[tag]/[...page].astro +++ /dev/null @@ -1,99 +0,0 @@ ---- -import { render } from "astro:content"; -import type { GetStaticPaths, InferGetStaticPropsType } from "astro"; -import { Icon } from "astro-icon/components"; -import PostPreview from "@/components/blog/PostPreview.astro"; -import Pagination from "@/components/Paginator.astro"; -import { getAllPosts, getTagMeta, getUniqueTags } from "@/data/post"; -import PageLayout from "@/layouts/Base.astro"; -import { collectionDateSort } from "@/utils/date"; - -export const getStaticPaths = (async ({ paginate }) => { - const allPosts = await getAllPosts(true); // Include archived posts (now includes pleroma too) - - // Get unique tags from all posts - const allTags = getUniqueTags(allPosts); - - return allTags.flatMap((tag) => { - // Filter posts by tag - const postsWithTag = allPosts.filter((post) => post.data.tags.includes(tag)); - - // Sort chronologically - const sortedPosts = postsWithTag.sort(collectionDateSort); - - return paginate(sortedPosts, { - pageSize: 10, - params: { tag }, - }); - }); -}) satisfies GetStaticPaths; - -type Props = InferGetStaticPropsType; - -const { page } = Astro.props as Props; -const { tag } = Astro.params; -const tagMeta = await getTagMeta(tag); - -const TagContent = tagMeta ? (await render(tagMeta)).Content : null; - -// Capitalize first letter of tag for display -const tagDisplay = tag.charAt(0).toUpperCase() + tag.slice(1); - -const meta = { - description: tagMeta?.data.description ?? `View all posts with the tag - ${tag}`, - title: tagMeta?.data.title ?? `${tagDisplay} posts`, -}; - -const paginationProps = { - ...(page.url.prev && { - prevUrl: { - text: "← Previous posts", - url: page.url.prev, - }, - }), - ...(page.url.next && { - nextUrl: { - text: "Next posts →", - url: page.url.next, - }, - }), -}; ---- - - - -

- {tagMeta?.data.title ?? `${tagDisplay} posts`} - - RSS feed - -

-
- {tagMeta?.data.description &&

{tagMeta.data.description}

} - {TagContent && } -
-
    - { - page.data.map((post) => ( -
  • - -
  • - )) - } -
- -
diff --git a/src/pages/tags/[tag]/index.astro b/src/pages/tags/[tag]/index.astro new file mode 100644 index 0000000..2f7fa47 --- /dev/null +++ b/src/pages/tags/[tag]/index.astro @@ -0,0 +1,95 @@ +--- +import { render } from "astro:content"; +import type { GetStaticPaths } from "astro"; +import { Icon } from "astro-icon/components"; +import PostPreview from "@/components/blog/PostPreview.astro"; +import { getAllPosts, getTagMeta, getUniqueTags, groupPostsByYear } from "@/data/post"; +import PageLayout from "@/layouts/Base.astro"; +import { collectionDateSort } from "@/utils/date"; + +export const getStaticPaths = (async () => { + const allPosts = await getAllPosts(true); + const allTags = getUniqueTags(allPosts); + + return allTags.map((tag) => { + const postsWithTag = allPosts + .filter((post) => post.data.tags.includes(tag)) + .sort(collectionDateSort); + + return { params: { tag }, props: { posts: postsWithTag } }; + }); +}) satisfies GetStaticPaths; + +const { posts } = Astro.props; +const { tag } = Astro.params; +const tagMeta = await getTagMeta(tag); + +const TagContent = tagMeta ? (await render(tagMeta)).Content : null; + +// Capitalize first letter of tag for display +const tagDisplay = tag.charAt(0).toUpperCase() + tag.slice(1); + +const meta = { + description: tagMeta?.data.description ?? `View all posts with the tag - ${tag}`, + title: tagMeta?.data.title ?? `${tagDisplay} posts`, +}; + +const groupedByYear = groupPostsByYear(posts); +const descYearKeys = Object.keys(groupedByYear).sort((a, b) => +b - +a); +const useYearGrouping = posts.length > 20; +--- + + + +

+ {tagMeta?.data.title ?? `${tagDisplay} posts`} + + RSS feed + +

+
+ {tagMeta?.data.description &&

{tagMeta.data.description}

} + {TagContent && } +
+ { + useYearGrouping ? ( + descYearKeys.map((yearKey) => ( +
+

+ Posts in + {yearKey} +

+
    + {groupedByYear[yearKey]?.map((post) => ( +
  • + +
  • + ))} +
+
+ )) + ) : ( +
    + {posts.map((post) => ( +
  • + +
  • + ))} +
+ ) + } +
-- cgit v1.2.3