Skip to content

Commit

Permalink
Fix pagination bugs and refactor/abstract styling logic (#205)
Browse files Browse the repository at this point in the history
* Simplify TS

* Abstract away pagination logic

* Clean up

* Fix bugs and refactor styling logic

* Add comments

* Fix border bug for single page

* Fix search pagination
  • Loading branch information
delbaoliveira authored Oct 9, 2023
1 parent 1e59e86 commit 728368d
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 88 deletions.
10 changes: 4 additions & 6 deletions dashboard/15-final/app/dashboard/customers/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ import CustomersTable from '@/app/ui/customers/table';
export default async function Page({
searchParams,
}: {
searchParams:
| {
query: string | undefined;
page: string | undefined;
}
| undefined;
searchParams?: {
query?: string;
page?: string;
};
}) {
const query = searchParams?.query || '';

Expand Down
12 changes: 5 additions & 7 deletions dashboard/15-final/app/dashboard/invoices/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@ import { lusitana } from '@/app/ui/fonts';
export default async function Page({
searchParams,
}: {
searchParams:
| {
query: string | undefined;
page: string | undefined;
}
| undefined;
searchParams?: {
query?: string;
page?: string;
};
}) {
const query = searchParams?.query || '';
const currentPage = query ? 1 : Number(searchParams?.page || '1');
const currentPage = Number(searchParams?.page || '1');

const { invoices, totalPages } = await fetchFilteredInvoices(
query,
Expand Down
33 changes: 33 additions & 0 deletions dashboard/15-final/app/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,36 @@ export const generateYAxis = (revenue: Revenue[]) => {

return { yAxisLabels, topLabel };
};

export const generatePagination = (currentPage: number, totalPages: number) => {
// If the total number of pages is 7 or less,
// display all pages without any ellipsis.
if (totalPages <= 7) {
return Array.from({ length: totalPages }, (_, i) => i + 1);
}

// If the current page is among the first 3 pages,
// show the first 3, an ellipsis, and the last 2 pages.
if (currentPage <= 3) {
return [1, 2, 3, '...', totalPages - 1, totalPages];
}

// If the current page is among the last 3 pages,
// show the first 2, an ellipsis, and the last 3 pages.
if (currentPage >= totalPages - 2) {
return [1, 2, '...', totalPages - 2, totalPages - 1, totalPages];
}

// If the current page is somewhere in the middle,
// show the first page, an ellipsis, the current page and its neighbors,
// another ellipsis, and the last page.
return [
1,
'...',
currentPage - 1,
currentPage,
currentPage + 1,
'...',
totalPages,
];
};
171 changes: 96 additions & 75 deletions dashboard/15-final/app/ui/invoices/pagination.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
'use client';

import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline';
import { usePathname, useSearchParams } from 'next/navigation';
import clsx from 'clsx';
import Link from 'next/link';
import { generatePagination } from '@/app/lib/utils';
import { usePathname, useSearchParams } from 'next/navigation';

export default function Pagination({
currentPage,
Expand All @@ -15,94 +16,114 @@ export default function Pagination({
const pathname = usePathname();
const searchParams = useSearchParams();

const allPages = Array.from({ length: totalPages }, (_, i) => i + 1);
const PreviousPageTag = Link;
const NextPageTag = Link;

const createPageUrl = (pageNumber: number | string) => {
const createPageURL = (pageNumber: number | string) => {
const params = new URLSearchParams(searchParams);
params.set('page', pageNumber.toString());
return `${pathname}?${params.toString()}`;
};

let pagesToShow = [];

if (totalPages <= 7) {
pagesToShow = allPages;
} else if (currentPage <= 3) {
pagesToShow = [1, 2, 3, '...', totalPages - 1, totalPages];
} else if (currentPage >= totalPages - 2) {
pagesToShow = [1, 2, '...', totalPages - 2, totalPages - 1, totalPages];
} else {
pagesToShow = [
1,
'...',
currentPage - 1,
currentPage,
currentPage + 1,
'...',
totalPages,
];
}
const allPages = generatePagination(currentPage, totalPages);

return (
<div className="inline-flex">
<PreviousPageTag
href={createPageUrl(currentPage - 1)}
className={clsx(
'mr-2 flex h-10 w-10 items-center justify-center rounded-md ring-1 ring-inset ring-gray-300 hover:bg-gray-100 md:mr-4',
{
'pointer-events-none text-gray-300 hover:bg-transparent':
currentPage === 1,
},
)}
>
<ArrowLeftIcon className="w-4" />
</PreviousPageTag>
<div className="flex -space-x-px ">
{pagesToShow.map((page, i) => {
if (page === '...') {
return (
<span
key={`ellipsis-${i}`}
className="flex h-10 w-10 items-center justify-center text-sm ring-1 ring-inset ring-gray-300"
>
...
</span>
);
}
<PaginationArrow
direction="left"
href={createPageURL(currentPage - 1)}
isDisabled={currentPage <= 1}
/>

<div className="flex -space-x-px">
{allPages.map((page, index) => {
let position: 'first' | 'last' | 'single' | 'middle' | undefined;

if (index === 0) position = 'first';
if (index === allPages.length - 1) position = 'last';
if (allPages.length === 1) position = 'single';
if (page === '...') position = 'middle';

const PageTag = page === currentPage || page === '...' ? 'p' : Link;
return (
<PageTag
<PaginationNumber
key={page}
href={createPageUrl(page)}
className={clsx(
i === 0 && 'rounded-l-md',
i === pagesToShow.length - 1 && 'rounded-r-md',
'flex h-10 w-10 items-center justify-center text-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-100',
{
'z-10 bg-blue-600 text-white ring-blue-600 hover:bg-blue-600':
currentPage === page,
},
)}
>
{page}
</PageTag>
href={createPageURL(page)}
page={page}
position={position}
isActive={currentPage === page}
/>
);
})}
</div>
<NextPageTag
href={createPageUrl(currentPage + 1)}
className={clsx(
'ml-2 flex h-10 w-10 items-center justify-center rounded-md ring-1 ring-inset ring-gray-300 hover:bg-gray-100 md:ml-4',
{
'text-gray-300': currentPage === totalPages,
},
)}
>
<ArrowRightIcon className="w-4" />
</NextPageTag>

<PaginationArrow
direction="right"
href={createPageURL(currentPage + 1)}
isDisabled={currentPage >= totalPages}
/>
</div>
);
}

function PaginationNumber({
page,
href,
isActive,
position,
}: {
page: number | string;
href: string;
position?: 'first' | 'last' | 'middle' | 'single';
isActive: boolean;
}) {
const className = clsx(
'flex h-10 w-10 items-center justify-center text-sm border',
{
'rounded-l-md': position === 'first' || position === 'single',
'rounded-r-md': position === 'last' || position === 'single',
'z-10 bg-blue-600 border-blue-600 text-white': isActive,
'hover:bg-gray-100': !isActive && position !== 'middle',
'text-gray-300': position === 'middle',
},
);

return isActive || position === 'middle' ? (
<div className={className}>{page}</div>
) : (
<Link href={href} className={className}>
{page}
</Link>
);
}

function PaginationArrow({
href,
direction,
isDisabled,
}: {
href: string;
direction: 'left' | 'right';
isDisabled?: boolean;
}) {
const className = clsx(
'flex h-10 w-10 items-center justify-center rounded-md border',
{
'pointer-events-none text-gray-300': isDisabled,
'hover:bg-gray-100': !isDisabled,
'mr-2 md:mr-4': direction === 'left',
'ml-2 md:ml-4': direction === 'right',
},
);

const icon =
direction === 'left' ? (
<ArrowLeftIcon className="w-4" />
) : (
<ArrowRightIcon className="w-4" />
);

return isDisabled ? (
<div className={className}>{icon}</div>
) : (
<Link className={className} href={href}>
{icon}
</Link>
);
}
2 changes: 2 additions & 0 deletions dashboard/15-final/app/ui/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export default function Search({ placeholder }: { placeholder: string }) {
console.log(`Searching... ${term}`);

const params = new URLSearchParams(searchParams);

params.set('page', '1');
if (term) {
params.set('query', term);
} else {
Expand Down

1 comment on commit 728368d

@vercel
Copy link

@vercel vercel bot commented on 728368d Oct 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

next-learn-dashboard – ./dashboard/15-final

next-learn-dashboard-git-main.vercel.sh
next-learn-dashboard.vercel.sh

Please sign in to comment.