1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
import { postsPerPage } from '@/app/layout.config';
import { NumberedPagination } from '@/components/numbered-pagination';
import { PostCard } from '@/components/posts/post-card';
import { Section } from '@/components/section';
import { createMetadata } from '@/lib/metadata';
import { getSortedByDatePosts } from '@/lib/source';
import type { Metadata, ResolvingMetadata } from 'next';
import { notFound, redirect } from 'next/navigation';
export const dynamicParams = false;
const totalPosts = getSortedByDatePosts().length;
const pageCount = Math.ceil(totalPosts / postsPerPage);
const CurrentPostsCount = ({
startIndex,
endIndex,
}: {
startIndex: number;
endIndex: number;
}) => {
const start = startIndex + 1;
const end = endIndex < totalPosts ? endIndex : totalPosts;
if (start === end) return <span>({start})</span>;
return (
<span>
({start}-{end})
</span>
);
};
const Pagination = ({ pageIndex }: { pageIndex: number }) => {
const handlePageChange = async (page: number) => {
'use server';
redirect(`/posts?page=${page}`);
};
return (
<Section className='bg-dashed'>
<NumberedPagination
currentPage={pageIndex + 1}
totalPages={pageCount}
paginationItemsToDisplay={5}
onPageChange={handlePageChange}
/>
</Section>
);
};
export default async function Page(props: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}) {
const searchParams = await props.searchParams;
const pageIndex = searchParams.page
? Number.parseInt(searchParams.page[0] ?? '', 10) - 1
: 0;
if (pageIndex < 0 || pageIndex >= pageCount) notFound();
const startIndex = pageIndex * postsPerPage;
const endIndex = startIndex + postsPerPage;
const posts = getSortedByDatePosts().slice(startIndex, endIndex);
return (
<>
<Section className='p-4 lg:p-6'>
<h1 className='font-bold text-3xl leading-tight tracking-tighter md:text-4xl'>
All {totalPosts} Posts{' '}
<CurrentPostsCount startIndex={startIndex} endIndex={endIndex} />
</h1>
</Section>
<Section className='h-full' sectionClassName='flex flex-1'>
<div className='grid divide-y divide-dashed divide-border/70 text-left dark:divide-border'>
{posts.map((post) => {
const date = new Date(post.data.date).toDateString();
return (
<PostCard
title={post.data.title}
description={post.data.description ?? ''}
image={post.data.image}
url={post.url}
date={date}
key={post.url}
author={post.data.author}
tags={post.data.tags}
/>
);
})}
</div>
</Section>
{pageCount > 1 && <Pagination pageIndex={pageIndex} />}
</>
);
}
export const generateStaticParams = () => {
const slugs = Array.from({ length: pageCount }, (_, index) => ({
slug: [(index + 1).toString()],
}));
return [{ slug: [] }, ...slugs];
};
type Props = {
params: Promise<{ slug: string[] }>;
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
};
export async function generateMetadata(
props: Props,
parent: ResolvingMetadata,
): Promise<Metadata> {
const params = await props.params;
const searchParams = await props.searchParams;
const pageIndex = searchParams.page
? Number.parseInt(searchParams.page as string, 10)
: 1;
const isFirstPage = pageIndex === 1 || !searchParams.page;
const pageTitle = isFirstPage ? 'Posts' : `Posts - Page ${pageIndex}`;
const canonicalUrl = isFirstPage ? '/posts' : `/posts?page=${pageIndex}`;
return createMetadata({
title: pageTitle,
description: `Posts${!isFirstPage ? ` - Page ${pageIndex}` : ''}`,
openGraph: {
url: canonicalUrl,
},
alternates: {
canonical: canonicalUrl,
},
});
}
|