Skip to content

Commit 4667c59

Browse files
committed
improve checks fetch and paginaiton
1 parent 6978a7e commit 4667c59

File tree

4 files changed

+131
-21
lines changed

4 files changed

+131
-21
lines changed

client/src/components/design-elements/Table.tsx

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import TableContainer from "@mui/material/TableContainer";
1010
import TableHead from "@mui/material/TableHead";
1111
import TableRow from "@mui/material/TableRow";
1212
import Collapse from "@mui/material/Collapse";
13+
import Tooltip from "@mui/material/Tooltip";
1314

1415
import IconButton from "@mui/material/IconButton";
1516
import {
@@ -18,6 +19,7 @@ import {
1819
ChevronLeft,
1920
ChevronRight,
2021
Coffee,
22+
Ellipsis,
2123
PartyPopper,
2224
} from "lucide-react";
2325

@@ -331,13 +333,118 @@ function TablePaginationActions(props: TablePaginationActionsProps) {
331333
);
332334
}
333335

334-
export const Pagination = ({ ...props }: TablePaginationProps) => {
336+
interface HasMoreTablePaginationActionsProps {
337+
hasMore?: boolean;
338+
page: number;
339+
onPageChange: (
340+
event: React.MouseEvent<HTMLButtonElement>,
341+
newPage: number
342+
) => void;
343+
}
344+
function HasMoreTablePaginationActions(
345+
props: HasMoreTablePaginationActionsProps
346+
) {
347+
const theme = useTheme();
348+
const { hasMore, page, onPageChange } = props;
349+
console.log(JSON.stringify(props, null, 2));
350+
351+
const handleFirstPageButtonClick = (
352+
event: React.MouseEvent<HTMLButtonElement>
353+
) => {
354+
onPageChange(event, 0);
355+
};
356+
357+
const handleBackButtonClick = (
358+
event: React.MouseEvent<HTMLButtonElement>
359+
) => {
360+
onPageChange(event, page - 1);
361+
};
362+
363+
const handleNextButtonClick = (
364+
event: React.MouseEvent<HTMLButtonElement>
365+
) => {
366+
onPageChange(event, page + 1);
367+
};
368+
369+
return (
370+
<Box
371+
sx={{ flexShrink: 0, ml: { xs: 0, md: 2.5 } }}
372+
className="table-pagination-actions"
373+
>
374+
<IconButton
375+
onClick={handleFirstPageButtonClick}
376+
disabled={page === 0}
377+
aria-label="first page"
378+
>
379+
{theme.direction === "rtl" ? (
380+
<ChevronsRight size={20} strokeWidth={1.5} />
381+
) : (
382+
<ChevronsLeft size={20} strokeWidth={1.5} />
383+
)}
384+
</IconButton>
385+
<IconButton
386+
onClick={handleBackButtonClick}
387+
disabled={page === 0}
388+
aria-label="previous page"
389+
>
390+
{theme.direction === "rtl" ? (
391+
<ChevronRight size={20} strokeWidth={1.5} />
392+
) : (
393+
<ChevronLeft size={20} strokeWidth={1.5} />
394+
)}
395+
</IconButton>
396+
<IconButton
397+
onClick={handleNextButtonClick}
398+
disabled={hasMore === false}
399+
aria-label="next page"
400+
>
401+
{theme.direction === "rtl" ? (
402+
<ChevronLeft size={20} strokeWidth={1.5} />
403+
) : (
404+
<ChevronRight size={20} strokeWidth={1.5} />
405+
)}
406+
</IconButton>
407+
<Tooltip
408+
title={hasMore === true ? "More pages available" : "No more pages"}
409+
>
410+
<IconButton disabled={hasMore === false} aria-label="next page">
411+
<Ellipsis size={20} strokeWidth={1.5} />
412+
</IconButton>
413+
</Tooltip>
414+
</Box>
415+
);
416+
}
417+
418+
interface PaginationProps extends TablePaginationProps {
419+
hasMore?: boolean;
420+
}
421+
422+
export const Pagination = ({ ...props }: PaginationProps) => {
423+
const hasMore = props.hasMore;
335424
const isSmall = useMediaQuery((theme: any) => theme.breakpoints.down("sm"));
336425
const theme = useTheme();
426+
const labelDisplayedRows = ({
427+
from,
428+
to,
429+
count,
430+
}: {
431+
from: number;
432+
to: number;
433+
count: number;
434+
}) => {
435+
if (hasMore) {
436+
return to === 0 ? "" : `${from}${to}`;
437+
}
438+
return `${from}${to} of ${count}`;
439+
};
440+
337441
return (
338442
<TablePagination
339-
ActionsComponent={TablePaginationActions}
443+
ActionsComponent={
444+
hasMore ? HasMoreTablePaginationActions : TablePaginationActions
445+
}
340446
rowsPerPageOptions={[5, 10, 25]}
447+
labelDisplayedRows={labelDisplayedRows}
341448
{...props}
342449
sx={{
343450
"& .MuiTablePagination-toolbar": isSmall

client/src/pages/checks/ChecksPage.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const ChecksPage = () => {
2929
const monitors: IMonitor[] = monitorResponse?.data || [];
3030

3131
const { response: checksResponse, loading: checksLoading } = useGet<
32-
ApiResponse<{ count: number; checks: ICheck[] }>
32+
ApiResponse<{ hasMore: boolean; checks: ICheck[] }>
3333
>(
3434
`/checks?page=${page}&rowsPerPage=${rowsPerPage}&range=${range}&status=${status}${
3535
selectedMonitorId !== "all" ? `&monitorId=${selectedMonitorId}` : ""
@@ -44,7 +44,7 @@ const ChecksPage = () => {
4444
);
4545

4646
const checks = checksResponse?.data?.checks || [];
47-
const checksCount = checksResponse?.data?.count || 0;
47+
const hasMore = checksResponse?.data?.hasMore;
4848

4949
return (
5050
<BasePage>
@@ -97,7 +97,7 @@ const ChecksPage = () => {
9797
</Stack>
9898
<ChecksTable
9999
checks={checks}
100-
count={checksCount}
100+
hasMore={hasMore}
101101
page={page}
102102
setPage={setPage}
103103
rowsPerPage={rowsPerPage}

client/src/pages/checks/ChecksTable.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ type CheckWithMonitor = ICheck & {
2020

2121
export const ChecksTable = ({
2222
checks,
23-
count,
23+
hasMore,
2424
page,
2525
setPage,
2626
rowsPerPage,
2727
setRowsPerPage,
2828
}: {
2929
checks: CheckWithMonitor[];
30-
count: number;
30+
hasMore?: boolean;
3131
page: number;
3232
setPage: (page: number) => void;
3333
rowsPerPage: number;
@@ -76,7 +76,7 @@ export const ChecksTable = ({
7676
},
7777
{
7878
id: "message",
79-
content: t("checks.table.headers.message"),
79+
content: t("common.table.headers.message"),
8080
render: (row) => {
8181
return row.message || "N/A";
8282
},
@@ -114,7 +114,8 @@ export const ChecksTable = ({
114114
/>
115115
<Pagination
116116
component="div"
117-
count={count}
117+
count={0}
118+
hasMore={hasMore}
118119
page={page}
119120
rowsPerPage={rowsPerPage}
120121
onPageChange={handlePageChange}

server/src/services/business/CheckService.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export interface ICheckService {
3131
page: number,
3232
rowsPerPage: number,
3333
range: string
34-
) => Promise<{ checks: ICheck[]; count: number }>;
34+
) => Promise<{ checks: ICheck[]; hasMore: boolean }>;
3535

3636
getCheckById: (checkId: string, teamId: string) => Promise<ICheck | null>;
3737

@@ -242,24 +242,26 @@ class CheckService implements ICheckService {
242242
status,
243243
"metadata.teamId": new mongoose.Types.ObjectId(teamId),
244244
"metadata.monitorId": new mongoose.Types.ObjectId(monitorId),
245-
createdAt: { $gte: startDate },
245+
createdAt: { $gte: startDate, $lte: new Date() },
246246
};
247247
} else {
248248
match = {
249249
status,
250250
"metadata.teamId": new mongoose.Types.ObjectId(teamId),
251-
createdAt: { $gte: startDate },
251+
createdAt: { $gte: startDate, $lte: new Date() },
252252
};
253253
}
254-
const [count, checks] = await Promise.all([
255-
Check.countDocuments(match),
256-
Check.find(match)
257-
.populate({ path: "metadata.monitorId", select: "name" })
258-
.sort({ createdAt: -1 })
259-
.skip(page * rowsPerPage)
260-
.limit(rowsPerPage),
261-
]);
262-
return { checks, count };
254+
// Has-more pagination: over-fetch by 1 to infer existence of a next page
255+
const pageSizePlusOne = rowsPerPage + 1;
256+
const docs = await Check.find(match)
257+
.populate({ path: "metadata.monitorId", select: "name" })
258+
.sort({ createdAt: -1 })
259+
.skip(page * rowsPerPage)
260+
.limit(pageSizePlusOne);
261+
262+
const hasMore = docs.length > rowsPerPage;
263+
const checks = hasMore ? docs.slice(0, rowsPerPage) : docs;
264+
return { checks, hasMore };
263265
};
264266

265267
getCheckById = async (checkId: string, teamId: string) => {

0 commit comments

Comments
 (0)