Skip to content

Commit

Permalink
add pagination to notes collection page (#7)
Browse files Browse the repository at this point in the history
* add pagination to notes collection page

* renames
  • Loading branch information
bercivarga authored Mar 10, 2024
1 parent f270915 commit ba6c05c
Show file tree
Hide file tree
Showing 8 changed files with 243 additions and 17 deletions.
4 changes: 2 additions & 2 deletions app/(marketing)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import Link from "next/link";
import { Button } from "@/components/ui/button";

export const metadata: Metadata = {
title: "CF Showcase Project",
description: "Made for CLEVER°FRANKE by @berci.dev",
title: "Second Brain Showcase Project",
description: "This is a showcase portfolio project. Made by @berci.dev",
};

export default function Home() {
Expand Down
2 changes: 1 addition & 1 deletion app/(platform)/map/interactive-map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { getAllNotes } from "@/helpers/notes/getAllNotes";
import { prepareNoteRelations } from "@/helpers/notes/prepareNoteRelations";

type Props = {
notes: NonNullable<Awaited<ReturnType<typeof getAllNotes>>>;
notes: NonNullable<Awaited<ReturnType<typeof getAllNotes>>>["notes"];
};

type GraphData = {
Expand Down
4 changes: 2 additions & 2 deletions app/(platform)/map/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { getAllNotes } from "@/helpers/notes/getAllNotes";
import InteractiveMap from "./interactive-map";

export default async function MapPage() {
const notes = await getAllNotes();
const allNotes = await getAllNotes();

return (
<main className="relative h-screen w-full">
<InteractiveMap notes={notes ?? []} />
<InteractiveMap notes={allNotes?.notes ?? []} />
</main>
);
}
Expand Down
65 changes: 65 additions & 0 deletions app/(platform)/notes/notes-pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/pagination";

type Props = {
currentPage?: number;
totalPages?: number;
};

export default function NotesPagination({
currentPage = 1,
totalPages = 1,
}: Props) {
if (currentPage === 1 && totalPages === 1) {
return null;
}

const isThereAPreviousPage = currentPage > 1;
const isThereANextPage = totalPages > currentPage;

return (
<Pagination>
<PaginationContent>
{isThereAPreviousPage && (
<>
<PaginationItem>
<PaginationPrevious href={`/notes?page=${currentPage - 1}`} />
</PaginationItem>
<PaginationItem>
<PaginationLink href={`/notes?page=${currentPage - 1}`}>
{currentPage - 1}
</PaginationLink>
</PaginationItem>
</>
)}
<PaginationItem className="pointer-events-none opacity-50">
<PaginationLink href="#">{currentPage}</PaginationLink>
</PaginationItem>
{totalPages && totalPages > currentPage + 2 && (
<PaginationItem>
<PaginationEllipsis />
</PaginationItem>
)}
{isThereANextPage && (
<>
<PaginationItem>
<PaginationLink href={`/notes?page=${currentPage + 1}`}>
{currentPage + 1}
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationNext href={`/notes?page=${currentPage + 1}`} />
</PaginationItem>
</>
)}
</PaginationContent>
</Pagination>
);
}
22 changes: 19 additions & 3 deletions app/(platform)/notes/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,32 @@ import {
} from "@/components/ui/table";
import { getAllNotes } from "@/helpers/notes/getAllNotes";

import NotesPagination from "./notes-pagination";

type Props = {
params: {};
searchParams: { page?: string };
};

export const metadata: Metadata = {
title: "All notes",
};

export default async function AllNotesPage() {
const notes = await getAllNotes();
export default async function AllNotesPage({ searchParams }: Props) {
const currentPage = searchParams.page ? parseInt(searchParams.page) : 1;

const { count, notes, totalPages } =
(await getAllNotes({
page: currentPage,
paginate: true,
})) ?? {};

return (
<main className="px-6 py-4">
<Table>
<TableCaption>A list of your recent notes.</TableCaption>
<TableCaption>
A list of your recent notes ({notes?.length ?? 0} of {count})
</TableCaption>
<TableHeader>
<TableRow>
<TableHead className="w-[200px]">Title</TableHead>
Expand Down Expand Up @@ -60,6 +75,7 @@ export default async function AllNotesPage() {
))}
</TableBody>
</Table>
<NotesPagination currentPage={currentPage} totalPages={totalPages} />
</main>
);
}
Expand Down
125 changes: 125 additions & 0 deletions components/ui/pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/* eslint-disable react/prop-types */
/* eslint-disable react/no-unknown-property */
/* eslint-disable jsx-a11y/anchor-has-content */

import {
ChevronLeftIcon,
ChevronRightIcon,
DotsHorizontalIcon,
} from "@radix-ui/react-icons";
import * as React from "react";

import { ButtonProps, buttonVariants } from "@/components/ui/button";
import { cn } from "@/lib/utils";

const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
<nav
role="navigation"
aria-label="pagination"
className={cn("mx-auto flex w-full justify-center", className)}
{...props}
/>
);
Pagination.displayName = "Pagination";

const PaginationContent = React.forwardRef<
HTMLUListElement,
React.ComponentProps<"ul">
>(({ className, ...props }, ref) => (
<ul
ref={ref}
className={cn("flex flex-row items-center gap-1", className)}
{...props}
/>
));
PaginationContent.displayName = "PaginationContent";

const PaginationItem = React.forwardRef<
HTMLLIElement,
React.ComponentProps<"li">
>(({ className, ...props }, ref) => (
<li ref={ref} className={cn("", className)} {...props} />
));
PaginationItem.displayName = "PaginationItem";

type PaginationLinkProps = {
isActive?: boolean;
} & Pick<ButtonProps, "size"> &
React.ComponentProps<"a">;

const PaginationLink = ({
className,
isActive,
size = "icon",
...props
}: PaginationLinkProps) => (
<a
aria-current={isActive ? "page" : undefined}
className={cn(
buttonVariants({
variant: isActive ? "outline" : "ghost",
size,
}),
className
)}
{...props}
/>
);
PaginationLink.displayName = "PaginationLink";

const PaginationPrevious = ({
className,
...props
}: React.ComponentProps<typeof PaginationLink>) => (
<PaginationLink
aria-label="Go to previous page"
size="default"
className={cn("gap-1 pl-2.5", className)}
{...props}
>
<ChevronLeftIcon className="h-4 w-4" />
<span>Previous</span>
</PaginationLink>
);
PaginationPrevious.displayName = "PaginationPrevious";

const PaginationNext = ({
className,
...props
}: React.ComponentProps<typeof PaginationLink>) => (
<PaginationLink
aria-label="Go to next page"
size="default"
className={cn("gap-1 pr-2.5", className)}
{...props}
>
<span>Next</span>
<ChevronRightIcon className="h-4 w-4" />
</PaginationLink>
);
PaginationNext.displayName = "PaginationNext";

const PaginationEllipsis = ({
className,
...props
}: React.ComponentProps<"span">) => (
<span
aria-hidden
className={cn("flex h-9 w-9 items-center justify-center", className)}
{...props}
>
<DotsHorizontalIcon className="h-4 w-4" />
<span className="sr-only">More pages</span>
</span>
);
PaginationEllipsis.displayName = "PaginationEllipsis";

export {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
};
36 changes: 28 additions & 8 deletions helpers/notes/getAllNotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ import { redirect } from "next/navigation";

import { prisma } from "@/lib/db";

export async function getAllNotes() {
const PAGE_SIZE = 12;

export async function getAllNotes({
page,
paginate = false,
}: {
page?: number;
paginate?: boolean;
} = {}) {
const { userId } = auth();

if (!userId) {
Expand All @@ -20,14 +28,26 @@ export async function getAllNotes() {
return null;
}

const notes = await prisma.note.findMany({
where: { authorId: dbUser.id, deleted: false },
include: { tags: true, relatedNotes: true, relatedTo: true },
orderBy: { updatedAt: "desc" },
take: 10,
});
const transaction = await prisma.$transaction([
prisma.note.count({
where: { authorId: dbUser.id, deleted: false },
}),
prisma.note.findMany({
where: { authorId: dbUser.id, deleted: false },
include: { tags: true, relatedNotes: true, relatedTo: true },
orderBy: { updatedAt: "desc" },
take: paginate ? PAGE_SIZE : undefined,
skip: paginate && page ? (page - 1) * PAGE_SIZE : undefined,
}),
]);

const [count, notes] = transaction;

return notes;
return {
count,
totalPages: paginate ? Math.ceil(count / PAGE_SIZE) : 1,
notes,
};
} catch (error) {
return null;
} finally {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "cf-showcase",
"name": "second-brain-showcase",
"version": "0.1.0",
"private": true,
"scripts": {
Expand Down

0 comments on commit ba6c05c

Please sign in to comment.