From 78ab7ca65572ba9dd8333fe224fc64a853b9a1d1 Mon Sep 17 00:00:00 2001 From: Sukma Gladys Date: Fri, 20 Sep 2024 10:42:02 +0700 Subject: [PATCH] fix: search function, button and pagination --- apps/frontend/src/components/PageTemplate.tsx | 199 ++++++++++-------- apps/frontend/src/routes/_dashboardLayout.tsx | 6 +- 2 files changed, 111 insertions(+), 94 deletions(-) diff --git a/apps/frontend/src/components/PageTemplate.tsx b/apps/frontend/src/components/PageTemplate.tsx index e07be85..e264630 100644 --- a/apps/frontend/src/components/PageTemplate.tsx +++ b/apps/frontend/src/components/PageTemplate.tsx @@ -13,7 +13,7 @@ import { keepPreviousData, useQuery, } from "@tanstack/react-query"; -import { useDebouncedCallback } from "@mantine/hooks"; +import { useDebouncedValue } from "@mantine/hooks"; import { Button } from "@/shadcn/components/ui/button"; import { useNavigate } from "@tanstack/react-router"; import { Card } from "@/shadcn/components/ui/card"; @@ -23,17 +23,16 @@ import { PaginationContent, PaginationEllipsis, PaginationItem, - PaginationLink, PaginationNext, PaginationPrevious, -} from "@/shadcn/components/ui/pagination" +} from "@/shadcn/components/ui/pagination"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, -} from "@/shadcn/components/ui/select" +} from "@/shadcn/components/ui/select"; type PaginatedResponse> = { data: Array; @@ -78,32 +77,32 @@ const createCreateButton = ( // eslint-disable-next-line @typescript-eslint/no-explicit-any property: Props["createButton"] = true ) => { + const navigate = useNavigate(); + + const addQuery = () => { + navigate({ to: `${window.location.pathname}`, search: { create: true } }); + } + if (property === true) { - const navigate = useNavigate(); return ( ); } else if (typeof property === "string") { - const navigate = useNavigate(); return ( ); } else { @@ -120,56 +119,87 @@ const createCreateButton = ( const CustomPagination = ({ currentPage, totalPages, - onPageChange, - hasNextPage, + onChange, }: { currentPage: number; totalPages: number; - onPageChange: (page: number) => void; - hasNextPage: boolean; + onChange: (page: number) => void; }) => { + const getPaginationItems = () => { + let items = []; + + // Determine start and end pages + let startPage = + currentPage == totalPages && currentPage > 3 ? + Math.max(1, currentPage - 2) : + Math.max(1, currentPage - 1); + let endPage = + currentPage == 1 ? + Math.min(totalPages, currentPage + 2) : + Math.min(totalPages, currentPage + 1); + + // Add ellipsis if needed + if (startPage > 2) { + items.push(); + } + + // Add page numbers + for (let i = startPage; i <= endPage; i++) { + items.push( + + ); + } + + // Add ellipsis after + if (endPage < totalPages - 1) { + items.push(); + } + + // Add last page + if (endPage < totalPages) { + items.push( + + ); + } + if (currentPage > 2) { + items.unshift( + + ); + } + + return items; + }; + return ( - - + + onPageChange(currentPage - 1)} - className={`${currentPage === 1 - ? 'bg-white text-muted-foreground cursor-not-allowed' - : 'bg-white text-black hover:bg-muted hover:text-black' - }`} - aria-disabled={currentPage === 1} + onClick={() => onChange(Math.max(1, currentPage - 1))} + aria-disabled={currentPage - 1 == 0 ? true : false} + className="w-full gap-2 md:w-auto" > Previous - - {Array.from({ length: totalPages }, (_, index) => index + 1).map((page) => ( - - onPageChange(page)} - className={page === currentPage ? 'border text-black' : ''} - > - {page} - - - ))} - - {totalPages > 1 && currentPage < totalPages && ( - - - - )} - - +
+ {getPaginationItems().map((item) => ( + + {item} + + ))} +
+ onPageChange(currentPage + 1)} - className={`${!hasNextPage || currentPage === totalPages - ? 'bg-white text-muted-foreground cursor-not-allowed' - : 'bg-white text-black hover:bg-muted hover:text-black' - }`} - aria-disabled={!hasNextPage || currentPage === totalPages} + onClick={() => onChange(Math.min(totalPages, currentPage + 1))} + aria-disabled={currentPage == totalPages ? true : false} + className="w-full gap-2 md:w-auto" > Next @@ -197,14 +227,14 @@ export default function PageTemplate< q: "", }); - // const [deboucedSearchQuery] = useDebouncedValue(filterOptions.q, 500); + const [debouncedSearchQuery] = useDebouncedValue(filterOptions.q, 500); const query = useQuery({ ...(typeof props.queryOptions === "function" ? props.queryOptions( filterOptions.page, filterOptions.limit, - filterOptions.q + debouncedSearchQuery ) : props.queryOptions), placeholderData: keepPreviousData, @@ -216,7 +246,7 @@ export default function PageTemplate< getCoreRowModel: getCoreRowModel(), defaultColumn: { cell: (props) => ( - + {props.getValue() as ReactNode} ), @@ -228,13 +258,13 @@ export default function PageTemplate< * * @param value - The new search query value. */ - const handleSearchQueryChange = useDebouncedCallback((value: string) => { + const handleSearchQueryChange = (value: string) => { setFilterOptions((prev) => ({ page: 0, limit: prev.limit, q: value, })); - }, 500); + }; /** * Handles the change in page number. @@ -242,42 +272,31 @@ export default function PageTemplate< * @param page - The new page number. */ const handlePageChange = (page: number) => { - if (page >= 1 && page <= (query.data?._metadata.totalPages ?? 1)) { - setFilterOptions((prev) => ({ - page: page - 1, // Adjust for zero-based index - limit: prev.limit, - q: prev.q, - })); - } + setFilterOptions((prev) => ({ + page: page - 1, // Adjust for zero-based index + limit: prev.limit, + q: prev.q, + })); }; - // Default values when query.data is undefined - const totalPages = query.data?._metadata.totalPages ?? 1; - const hasNextPage = query.data - ? filterOptions.page < totalPages - 1 - : false; - return (
-

{props.title}

+

{props.title}

{/* Table Functionality */}
{/* Search and Create Button */} -
+
- + - handleSearchQueryChange(e.target.value) - } + onChange={(e) => handleSearchQueryChange(e.target.value)} placeholder="Search..." /> -
{createCreateButton(props.createButton)} @@ -289,9 +308,9 @@ export default function PageTemplate< {/* Pagination */} {query.data && ( -
+
- + Per Page