From c20b11a6e46ea807dcfb9d55bd6c7978d92951be Mon Sep 17 00:00:00 2001 From: Adrian Polak <adrian.polak20@gmail.com> Date: Mon, 12 Dec 2022 10:40:28 +0100 Subject: [PATCH] feat(app): add questions pagination (#403) * Updates * Refactor questions page * Update page.tsx * feat(app): add questions pagination * feat(app): add dark mode to page item * refactor(app): remove activeHref from props * feat(app): add redirect to first category page * feat(app): add current page technology Co-authored-by: Michal Miszczyszyn <michal@mmiszy.pl> --- .../questions/[technology]/[page]/page.tsx | 40 +++++++++++++++++++ .../src/app/questions/[technology]/page.tsx | 28 ++----------- apps/app/src/app/questions/layout.tsx | 2 +- apps/app/src/app/questions/page.tsx | 2 +- apps/app/src/components/ActiveLink.tsx | 2 +- .../src/components/QuestionsPagination.tsx | 26 ++++++++++++ apps/app/src/lib/constants.ts | 1 + 7 files changed, 73 insertions(+), 28 deletions(-) create mode 100644 apps/app/src/app/questions/[technology]/[page]/page.tsx create mode 100644 apps/app/src/components/QuestionsPagination.tsx create mode 100644 apps/app/src/lib/constants.ts diff --git a/apps/app/src/app/questions/[technology]/[page]/page.tsx b/apps/app/src/app/questions/[technology]/[page]/page.tsx new file mode 100644 index 00000000..0ed431a6 --- /dev/null +++ b/apps/app/src/app/questions/[technology]/[page]/page.tsx @@ -0,0 +1,40 @@ +import { redirect } from "next/navigation"; +import { QuestionItem } from "../../../../components/QuestionItem/QuestionItem"; +import { QuestionsPagination } from "../../../../components/QuestionsPagination"; +import { PAGE_SIZE } from "../../../../lib/constants"; +import { technologies } from "../../../../lib/technologies"; +import { getAllQuestions } from "../../../../services/questions.service"; + +export default async function QuestionsPage({ + params, +}: { + params: { technology: string; page: string }; +}) { + const page = parseInt(params.page); + + if (!technologies.includes(params.technology) || isNaN(page)) { + return redirect("/"); + } + + const { data } = await getAllQuestions({ + category: params.technology, + limit: PAGE_SIZE, + offset: (page - 1) * PAGE_SIZE, + }); + + return ( + <div className="flex flex-col gap-y-10"> + {data.data.map(({ id, question, _levelId, acceptedAt, votesCount }) => ( + <QuestionItem + key={id} + title={question} + level={_levelId} + creationDate={new Date(acceptedAt || "")} + votes={votesCount} + voted={id % 2 === 0} + /> + ))} + <QuestionsPagination technology={params.technology} total={data.meta.total} /> + </div> + ); +} diff --git a/apps/app/src/app/questions/[technology]/page.tsx b/apps/app/src/app/questions/[technology]/page.tsx index 67efac0a..1589989a 100644 --- a/apps/app/src/app/questions/[technology]/page.tsx +++ b/apps/app/src/app/questions/[technology]/page.tsx @@ -1,27 +1,5 @@ import { redirect } from "next/navigation"; -import { QuestionItem } from "../../../components/QuestionItem/QuestionItem"; -import { technologies } from "../../../lib/technologies"; -import { getAllQuestions } from "../../../services/questions.service"; -export default async function QuestionsPage({ params }: { params: { technology: string } }) { - if (!technologies.includes(params.technology)) { - return redirect("/"); - } - - const { data } = await getAllQuestions({ category: params.technology, limit: 20 }); - - return ( - <div className="flex flex-col gap-y-10"> - {data.data.map(({ id, question, _levelId, acceptedAt, votesCount }) => ( - <QuestionItem - key={id} - title={question} - level={_levelId} - creationDate={new Date(acceptedAt || "")} - votes={votesCount} - voted={id % 2 === 0} - /> - ))} - </div> - ); -}; +export default function TechnologyPage({ params }: { params: { technology: string } }) { + return redirect(`/questions/${params.technology}/1`); +} diff --git a/apps/app/src/app/questions/layout.tsx b/apps/app/src/app/questions/layout.tsx index 1abe46ac..6d1f9f47 100644 --- a/apps/app/src/app/questions/layout.tsx +++ b/apps/app/src/app/questions/layout.tsx @@ -7,7 +7,7 @@ const QuestionsPageLayout = ({ children }: { readonly children: ReactNode }) => <> <Container className="flex"> <QuestionsSidebar /> - <main className="grow py-8 pl-11">{children}</main> + <main className="grow py-4 pl-0 sm:py-8 sm:pl-11">{children}</main> </Container> <MobileActionButtons /> </> diff --git a/apps/app/src/app/questions/page.tsx b/apps/app/src/app/questions/page.tsx index 7f1cad0a..4fdc893e 100644 --- a/apps/app/src/app/questions/page.tsx +++ b/apps/app/src/app/questions/page.tsx @@ -1,5 +1,5 @@ import { redirect } from "next/navigation"; export default function QuestionsPage() { - return redirect("/questions/js"); + return redirect("/questions/js/1"); } diff --git a/apps/app/src/components/ActiveLink.tsx b/apps/app/src/components/ActiveLink.tsx index a356a7c6..2bface36 100644 --- a/apps/app/src/components/ActiveLink.tsx +++ b/apps/app/src/components/ActiveLink.tsx @@ -18,7 +18,7 @@ export const ActiveLink = ({ ...rest }: ActiveLinkProps) => { const pathname = usePathname(); - const isActive = href === pathname; + const isActive = pathname?.startsWith(href.toString()); return ( <Link href={href} className={twMerge(className, isActive && activeClassName)} {...rest}> diff --git a/apps/app/src/components/QuestionsPagination.tsx b/apps/app/src/components/QuestionsPagination.tsx new file mode 100644 index 00000000..c3b2804f --- /dev/null +++ b/apps/app/src/components/QuestionsPagination.tsx @@ -0,0 +1,26 @@ +import { PAGE_SIZE } from "../lib/constants"; +import { ActiveLink } from "./ActiveLink"; + +type QuestionsPaginationProps = Readonly<{ + technology: string; + total: number; +}>; + +export const QuestionsPagination = ({ technology, total }: QuestionsPaginationProps) => { + const pages = Math.ceil(total / PAGE_SIZE); + + return ( + <div className="flex justify-center gap-x-3"> + {Array.from({ length: pages }).map((_, i) => ( + <ActiveLink + key={i} + href={`/questions/${technology}/${i + 1}`} + className="flex h-7 w-7 cursor-pointer items-center justify-center rounded-full border-2 border-primary text-primary transition-colors duration-300 hover:bg-violet-100 dark:text-white dark:hover:bg-violet-800" + activeClassName="bg-primary text-white" + > + {i + 1} + </ActiveLink> + ))} + </div> + ); +}; diff --git a/apps/app/src/lib/constants.ts b/apps/app/src/lib/constants.ts new file mode 100644 index 00000000..bfab9067 --- /dev/null +++ b/apps/app/src/lib/constants.ts @@ -0,0 +1 @@ +export const PAGE_SIZE = 20;