improve checks fetch and paginaiton

This commit is contained in:
Alex Holliday committed 2025-12-23 19:26:07 +00:00
1 parent 6978a7e2bb
commit 4667c59573
4 files changed
+131 -21

No files matched your search

+109 -2
View File
@@ -10,6 +10,7 @@ import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import Collapse from "@mui/material/Collapse";
import Tooltip from "@mui/material/Tooltip";
import IconButton from "@mui/material/IconButton";
import {
@@ -18,6 +19,7 @@ import {
ChevronLeft,
ChevronRight,
Coffee,
Ellipsis,
PartyPopper,
} from "lucide-react";
@@ -331,13 +333,118 @@ function TablePaginationActions(props: TablePaginationActionsProps) {
);
}
export const Pagination = ({ ...props }: TablePaginationProps) => {
interface HasMoreTablePaginationActionsProps {
hasMore?: boolean;
page: number;
onPageChange: (
event: React.MouseEvent<HTMLButtonElement>,
newPage: number
) => void;
}
function HasMoreTablePaginationActions(
props: HasMoreTablePaginationActionsProps
) {
const theme = useTheme();
const { hasMore, page, onPageChange } = props;
console.log(JSON.stringify(props, null, 2));
const handleFirstPageButtonClick = (
event: React.MouseEvent<HTMLButtonElement>
) => {
onPageChange(event, 0);
};
const handleBackButtonClick = (
event: React.MouseEvent<HTMLButtonElement>
) => {
onPageChange(event, page - 1);
};
const handleNextButtonClick = (
event: React.MouseEvent<HTMLButtonElement>
) => {
onPageChange(event, page + 1);
};
return (
<Box
sx={{ flexShrink: 0, ml: { xs: 0, md: 2.5 } }}
className="table-pagination-actions"
>
<IconButton
onClick={handleFirstPageButtonClick}
disabled={page === 0}
aria-label="first page"
>
{theme.direction === "rtl" ? (
<ChevronsRight size={20} strokeWidth={1.5} />
) : (
<ChevronsLeft size={20} strokeWidth={1.5} />
)}
</IconButton>
<IconButton
onClick={handleBackButtonClick}
disabled={page === 0}
aria-label="previous page"
>
{theme.direction === "rtl" ? (
<ChevronRight size={20} strokeWidth={1.5} />
) : (
<ChevronLeft size={20} strokeWidth={1.5} />
)}
</IconButton>
<IconButton
onClick={handleNextButtonClick}
disabled={hasMore === false}
aria-label="next page"
>
{theme.direction === "rtl" ? (
<ChevronLeft size={20} strokeWidth={1.5} />
) : (
<ChevronRight size={20} strokeWidth={1.5} />
)}
</IconButton>
<Tooltip
title={hasMore === true ? "More pages available" : "No more pages"}
>
<IconButton disabled={hasMore === false} aria-label="next page">
<Ellipsis size={20} strokeWidth={1.5} />
</IconButton>
</Tooltip>
</Box>
);
}
interface PaginationProps extends TablePaginationProps {
hasMore?: boolean;
}
export const Pagination = ({ ...props }: PaginationProps) => {
const hasMore = props.hasMore;
const isSmall = useMediaQuery((theme: any) => theme.breakpoints.down("sm"));
const theme = useTheme();
const labelDisplayedRows = ({
from,
to,
count,
}: {
from: number;
to: number;
count: number;
}) => {
if (hasMore) {
return to === 0 ? "" : `${from}–${to}`;
}
return `${from}–${to} of ${count}`;
};
return (
<TablePagination
ActionsComponent={TablePaginationActions}
ActionsComponent={
hasMore ? HasMoreTablePaginationActions : TablePaginationActions
}
rowsPerPageOptions={[5, 10, 25]}
labelDisplayedRows={labelDisplayedRows}
{...props}
sx={{
"& .MuiTablePagination-toolbar": isSmall
+3 -3
View File
@@ -29,7 +29,7 @@ const ChecksPage = () => {
const monitors: IMonitor[] = monitorResponse?.data || [];
const { response: checksResponse, loading: checksLoading } = useGet<
ApiResponse<{ count: number; checks: ICheck[] }>
ApiResponse<{ hasMore: boolean; checks: ICheck[] }>
>(
`/checks?page=${page}&rowsPerPage=${rowsPerPage}&range=${range}&status=${status}${
selectedMonitorId !== "all" ? `&monitorId=${selectedMonitorId}` : ""
@@ -44,7 +44,7 @@ const ChecksPage = () => {
);
const checks = checksResponse?.data?.checks || [];
const checksCount = checksResponse?.data?.count || 0;
const hasMore = checksResponse?.data?.hasMore;
return (
<BasePage>
@@ -97,7 +97,7 @@ const ChecksPage = () => {
</Stack>
<ChecksTable
checks={checks}
count={checksCount}
hasMore={hasMore}
page={page}
setPage={setPage}
rowsPerPage={rowsPerPage}
+5 -4
View File
@@ -20,14 +20,14 @@ type CheckWithMonitor = ICheck & {
export const ChecksTable = ({
checks,
count,
hasMore,
page,
setPage,
rowsPerPage,
setRowsPerPage,
}: {
checks: CheckWithMonitor[];
count: number;
hasMore?: boolean;
page: number;
setPage: (page: number) => void;
rowsPerPage: number;
@@ -76,7 +76,7 @@ export const ChecksTable = ({
},
{
id: "message",
content: t("checks.table.headers.message"),
content: t("common.table.headers.message"),
render: (row) => {
return row.message || "N/A";
},
@@ -114,7 +114,8 @@ export const ChecksTable = ({
/>
<Pagination
component="div"
count={count}
count={0}
hasMore={hasMore}
page={page}
rowsPerPage={rowsPerPage}
onPageChange={handlePageChange}
+14 -12
View File
@@ -31,7 +31,7 @@ export interface ICheckService {
page: number,
rowsPerPage: number,
range: string
) => Promise<{ checks: ICheck[]; count: number }>;
) => Promise<{ checks: ICheck[]; hasMore: boolean }>;
getCheckById: (checkId: string, teamId: string) => Promise<ICheck | null>;
@@ -242,24 +242,26 @@ class CheckService implements ICheckService {
status,
"metadata.teamId": new mongoose.Types.ObjectId(teamId),
"metadata.monitorId": new mongoose.Types.ObjectId(monitorId),
createdAt: { $gte: startDate },
createdAt: { $gte: startDate, $lte: new Date() },
};
} else {
match = {
status,
"metadata.teamId": new mongoose.Types.ObjectId(teamId),
createdAt: { $gte: startDate },
createdAt: { $gte: startDate, $lte: new Date() },
};
}
const [count, checks] = await Promise.all([
Check.countDocuments(match),
Check.find(match)
.populate({ path: "metadata.monitorId", select: "name" })
.sort({ createdAt: -1 })
.skip(page * rowsPerPage)
.limit(rowsPerPage),
]);
return { checks, count };
// Has-more pagination: over-fetch by 1 to infer existence of a next page
const pageSizePlusOne = rowsPerPage + 1;
const docs = await Check.find(match)
.populate({ path: "metadata.monitorId", select: "name" })
.sort({ createdAt: -1 })
.skip(page * rowsPerPage)
.limit(pageSizePlusOne);
const hasMore = docs.length > rowsPerPage;
const checks = hasMore ? docs.slice(0, rowsPerPage) : docs;
return { checks, hasMore };
};
getCheckById = async (checkId: string, teamId: string) => {