feat: Add pagination to service table
- Add pagination to the service table in the uptime monitoring dashboard, allowing display of 10 records per page by default. Also add a dropdown to select the number of records to display (10, 30, 50).
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
|
||||
import {
|
||||
Pagination,
|
||||
PaginationContent,
|
||||
PaginationItem,
|
||||
PaginationLink,
|
||||
PaginationNext,
|
||||
PaginationPrevious
|
||||
} from "@/components/ui/pagination";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from "@/components/ui/select";
|
||||
import { PageSize } from "@/hooks/useServicesPagination";
|
||||
import { useLanguage } from "@/contexts/LanguageContext";
|
||||
|
||||
interface ServicesPaginationProps {
|
||||
currentPage: number;
|
||||
totalPages: number;
|
||||
pageSize: PageSize;
|
||||
totalItems: number;
|
||||
onPageChange: (page: number) => void;
|
||||
onPageSizeChange: (size: PageSize) => void;
|
||||
}
|
||||
|
||||
export function ServicesPagination({
|
||||
currentPage,
|
||||
totalPages,
|
||||
pageSize,
|
||||
totalItems,
|
||||
onPageChange,
|
||||
onPageSizeChange,
|
||||
}: ServicesPaginationProps) {
|
||||
const { t } = useLanguage();
|
||||
|
||||
// Generate page numbers to display
|
||||
const getPageNumbers = () => {
|
||||
const pages = [];
|
||||
const maxVisiblePages = 5;
|
||||
const halfVisible = Math.floor(maxVisiblePages / 2);
|
||||
|
||||
let startPage = Math.max(1, currentPage - halfVisible);
|
||||
let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);
|
||||
|
||||
// Adjust start page if we don't have enough pages at the end
|
||||
if (endPage - startPage + 1 < maxVisiblePages) {
|
||||
startPage = Math.max(1, endPage - maxVisiblePages + 1);
|
||||
}
|
||||
|
||||
for (let i = startPage; i <= endPage; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
|
||||
return pages;
|
||||
};
|
||||
|
||||
const startItem = Math.min((currentPage - 1) * pageSize + 1, totalItems);
|
||||
const endItem = Math.min(currentPage * pageSize, totalItems);
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between py-4 px-4 border-t border-border">
|
||||
<div className="flex items-center space-x-2">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{t("rowsPerPage") ?? "Rows per page"}:
|
||||
</span>
|
||||
<Select
|
||||
value={pageSize.toString()}
|
||||
onValueChange={(value) => onPageSizeChange(parseInt(value) as PageSize)}
|
||||
>
|
||||
<SelectTrigger className="h-8 w-[70px]">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="10">10</SelectItem>
|
||||
<SelectItem value="30">30</SelectItem>
|
||||
<SelectItem value="50">50</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<span className="text-sm text-muted-foreground whitespace-nowrap">
|
||||
{totalItems > 0
|
||||
? `${startItem}-${endItem} of ${totalItems} ${t("services") || "services"}`
|
||||
: `0 ${t("services") || "services"}`
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{totalPages > 1 && (
|
||||
<Pagination>
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious
|
||||
onClick={() => onPageChange(Math.max(1, currentPage - 1))}
|
||||
className={
|
||||
currentPage === 1
|
||||
? "pointer-events-none opacity-50"
|
||||
: "cursor-pointer"
|
||||
}
|
||||
/>
|
||||
</PaginationItem>
|
||||
|
||||
{getPageNumbers().map((page) => (
|
||||
<PaginationItem key={page}>
|
||||
<PaginationLink
|
||||
isActive={page === currentPage}
|
||||
onClick={() => onPageChange(page)}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
{page}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
))}
|
||||
|
||||
<PaginationItem>
|
||||
<PaginationNext
|
||||
onClick={() => onPageChange(Math.min(totalPages, currentPage + 1))}
|
||||
className={
|
||||
currentPage === totalPages
|
||||
? "pointer-events-none opacity-50"
|
||||
: "cursor-pointer"
|
||||
}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,10 +2,12 @@
|
||||
import { useEffect } from "react";
|
||||
import { Service } from "@/types/service.types";
|
||||
import { ServicesTableView } from "./ServicesTableView";
|
||||
import { ServicesPagination } from "./ServicesPagination";
|
||||
import { ServiceDeleteDialog } from "./ServiceDeleteDialog";
|
||||
import { ServiceHistoryDialog } from "./ServiceHistoryDialog";
|
||||
import { ServiceEditDialog } from "./ServiceEditDialog";
|
||||
import { useServiceActions, useDialogState } from "./hooks";
|
||||
import { useServicesPagination } from "@/hooks/useServicesPagination";
|
||||
|
||||
interface ServicesTableContainerProps {
|
||||
services: Service[];
|
||||
@@ -36,6 +38,16 @@ export const ServicesTableContainer = ({ services }: ServicesTableContainerProps
|
||||
handleDeleteDialogChange
|
||||
} = useDialogState();
|
||||
|
||||
const {
|
||||
paginatedServices,
|
||||
currentPage,
|
||||
totalPages,
|
||||
pageSize,
|
||||
totalItems,
|
||||
handlePageChange,
|
||||
handlePageSizeChange,
|
||||
} = useServicesPagination({ services: localServices });
|
||||
|
||||
// Update local services state when props change
|
||||
useEffect(() => {
|
||||
updateServices(services);
|
||||
@@ -62,7 +74,7 @@ export const ServicesTableContainer = ({ services }: ServicesTableContainerProps
|
||||
return (
|
||||
<div className="flex-1 flex flex-col h-full">
|
||||
<ServicesTableView
|
||||
services={localServices}
|
||||
services={paginatedServices}
|
||||
onViewDetail={handleViewDetail}
|
||||
onPauseResume={handlePauseResume}
|
||||
onEdit={onEdit}
|
||||
@@ -70,6 +82,15 @@ export const ServicesTableContainer = ({ services }: ServicesTableContainerProps
|
||||
onMuteAlerts={handleMuteAlerts}
|
||||
/>
|
||||
|
||||
<ServicesPagination
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
pageSize={pageSize}
|
||||
totalItems={totalItems}
|
||||
onPageChange={handlePageChange}
|
||||
onPageSizeChange={handlePageSizeChange}
|
||||
/>
|
||||
|
||||
<ServiceHistoryDialog
|
||||
isOpen={isHistoryDialogOpen}
|
||||
onOpenChange={setIsHistoryDialogOpen}
|
||||
@@ -91,4 +112,4 @@ export const ServicesTableContainer = ({ services }: ServicesTableContainerProps
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -26,8 +26,8 @@ export const ServicesTableView = ({
|
||||
const { t } = useLanguage();
|
||||
|
||||
return (
|
||||
<div className={`${theme === 'dark' ? 'bg-gray-900' : 'bg-white'} rounded-lg overflow-hidden border border-border flex-1 flex flex-col shadow-sm`}>
|
||||
<div className="flex-1 overflow-auto">
|
||||
<div className={`${theme === 'dark' ? 'bg-gray-900' : 'bg-white'} rounded-lg overflow-hidden border border-border shadow-sm`}>
|
||||
<div className="overflow-auto">
|
||||
<Table>
|
||||
<TableHeader className={`${theme === 'dark' ? 'bg-gray-800' : 'bg-gray-50'} sticky top-0 z-10`}>
|
||||
<TableRow className={`${theme === 'dark' ? 'border-gray-700 hover:bg-gray-800' : 'border-gray-200 hover:bg-gray-100'}`}>
|
||||
@@ -65,4 +65,4 @@ export const ServicesTableView = ({
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user