158 lines
5.7 KiB
TypeScript
158 lines
5.7 KiB
TypeScript
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Separator } from "@/components/ui/separator";
|
|
import { Footer } from "@/components/utils/Footer";
|
|
import { ContentContainer, Page } from "@/components/utils/Layout";
|
|
import { NavBar } from "@/components/utils/NavBar";
|
|
import { PostList } from "@/components/utils/PostList";
|
|
import { SEO } from "@/components/utils/SEO";
|
|
import { PostCountPerPagination } from "@/consts/consts";
|
|
import { Config } from "@/data/config";
|
|
import { sortedPosts } from "@/lib/post-process";
|
|
import { isEmptyArray, paginateArray } from "@/lib/utils";
|
|
import type { TPostListItem } from "@/types/docs.type";
|
|
import { nanoid } from "nanoid";
|
|
import type { GetStaticPaths, GetStaticProps } from "next";
|
|
import Link from "next/link";
|
|
import { useRouter } from "next/navigation";
|
|
import { type ChangeEvent, type KeyboardEvent, useEffect, useState } from "react";
|
|
import { LuPenTool } from "react-icons/lu";
|
|
|
|
type PostsPageProps = {
|
|
pageAmount: number;
|
|
pageNumber: number;
|
|
postList: TPostListItem[];
|
|
tagList: { name: string; count: number }[];
|
|
};
|
|
|
|
export default function PostsPage(props: PostsPageProps) {
|
|
const router = useRouter();
|
|
const [pageNumber, setPageNumber] = useState<string>(props.pageNumber.toString());
|
|
|
|
const handleEnterKeyJump = (event: KeyboardEvent<HTMLInputElement>) => {
|
|
setPageNumber(pageNumber.replace(/[^\d]/g, ""));
|
|
if (Number.parseInt(pageNumber) > 0 && Number.parseInt(pageNumber) < props.pageAmount + 1) {
|
|
(event.key === "Go" || event.key === "Enter") && router.push(`/posts/${pageNumber}`);
|
|
return;
|
|
}
|
|
};
|
|
|
|
const handleInputPageNumber = (event: ChangeEvent<HTMLInputElement>) => {
|
|
setPageNumber(event.target.value);
|
|
};
|
|
|
|
useEffect(() => {
|
|
setPageNumber(props.pageNumber.toString().replace(/[^\d]/g, ""));
|
|
}, [props.pageNumber]);
|
|
|
|
return (
|
|
<Page>
|
|
<SEO
|
|
coverURL={Config.PageCovers.websiteCoverURL}
|
|
description={"Here is the list page for all published posts. Click here for more details."}
|
|
title={`${Config.SiteTitle} - All published posts`}
|
|
/>
|
|
<NavBar />
|
|
<ContentContainer>
|
|
<h2 className={"caption-font my-5 flex justify-center font-bold text-2xl"}>
|
|
<LuPenTool className="mx-2 my-auto" />
|
|
{"ALL POSTS"}
|
|
</h2>
|
|
{!isEmptyArray(props.tagList) && (
|
|
<Accordion collapsible type="single">
|
|
<AccordionItem className="border-t" value="item-1">
|
|
<AccordionTrigger className="font-bold hover:no-underline">{"TAG FILTER"}</AccordionTrigger>
|
|
<AccordionContent>
|
|
<Separator />
|
|
<div className={"my-5 flex flex-wrap justify-center text-wrap px-2 text-sm"}>
|
|
{props.tagList.map((item) => (
|
|
<Link
|
|
className="m-1 my-auto p-1 font-bold text-gray-700 underline decoration-2 underline-offset-[5px] hover:text-black dark:text-gray-300 dark:hover:text-white"
|
|
href={`/tags/${item.name}`}
|
|
key={`tags-${nanoid()}`}
|
|
>
|
|
{`${item.name} (${item.count})`}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</AccordionContent>
|
|
</AccordionItem>
|
|
</Accordion>
|
|
)}
|
|
<Separator />
|
|
<PostList data={props.postList} />
|
|
<Separator />
|
|
<div className="my-5 flex justify-between font-bold text-base">
|
|
{props.pageNumber !== 1 && (
|
|
<Button asChild>
|
|
<Link className="font-bold" href={`/posts/${props.pageNumber - 1}/`}>
|
|
{"< PREV"}
|
|
</Link>
|
|
</Button>
|
|
)}
|
|
<div className="my-auto flex justify-center font-bold">
|
|
<Input
|
|
className="mx-2 my-auto h-6 w-11"
|
|
onChange={handleInputPageNumber}
|
|
onKeyDown={handleEnterKeyJump}
|
|
title="Type the specified page number and press Enter to jump."
|
|
value={pageNumber}
|
|
/>
|
|
<div className="my-auto">{` / ${props.pageAmount}`}</div>
|
|
</div>
|
|
{props.pageNumber !== props.pageAmount && (
|
|
<Button asChild>
|
|
<Link className="font-bold" href={`/posts/${props.pageNumber + 1}/`}>
|
|
{"NEXT >"}
|
|
</Link>
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</ContentContainer>
|
|
<Footer />
|
|
</Page>
|
|
);
|
|
}
|
|
|
|
export const getStaticPaths: GetStaticPaths = () => {
|
|
const allPaths: { params: { slug?: string[] } }[] = [{ params: { slug: [] } }];
|
|
|
|
const pageAmount = Math.ceil(sortedPosts.allPostList.length / PostCountPerPagination);
|
|
|
|
for (let i = 0; i < pageAmount; i++) {
|
|
allPaths.push({ params: { slug: [(i + 1).toString()] } });
|
|
}
|
|
|
|
return { paths: allPaths, fallback: false };
|
|
};
|
|
|
|
export const getStaticProps: GetStaticProps<PostsPageProps> = async (context) => {
|
|
const params = (context.params?.slug as string[]) ?? [];
|
|
|
|
const pageNumber = params[0] ? Number.parseInt(params[0]) : 1;
|
|
|
|
const postList: TPostListItem[] = paginateArray(sortedPosts.allPostList, PostCountPerPagination, pageNumber);
|
|
|
|
const pageAmount = Math.ceil(sortedPosts.allPostList.length / PostCountPerPagination);
|
|
|
|
const tagList: {
|
|
name: string;
|
|
count: number;
|
|
}[] = Object.keys(sortedPosts.postsByTag)
|
|
.map((tagName) => ({
|
|
name: tagName,
|
|
count: sortedPosts.postsByTag[tagName].length,
|
|
}))
|
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
|
|
return {
|
|
props: {
|
|
pageAmount: pageAmount,
|
|
pageNumber: pageNumber,
|
|
postList: postList,
|
|
tagList: tagList,
|
|
},
|
|
};
|
|
};
|