Skip to content

Conversation

@mospell
Copy link

@mospell mospell commented Oct 29, 2025

Describe your changes

This PR adds pagination support for the PageSpeed chart on the backend by introducing an optional variable to FetchStatsByMonitorId/GetMonitorStatsById called page, which shifts back the date range depending on it's value.

For example: If dateRange is set to "day" and page is 4, then it will query data from 4 days ago.

The returned monitor object now also includes the number of remaining checks to be used for the frontend implementation.

Write your issue number after "Fixes "

Fixes #606

Please ensure all items are checked off before requesting a review. "Checked off" means you need to add an "x" character between brackets so they turn into checkmarks.

  • (Do not skip this or your PR will be closed) I deployed the application locally.
  • (Do not skip this or your PR will be closed) I have performed a self-review and testing of my code.
  • I have included the issue # in the PR.
  • I have added i18n support to visible strings (instead of <div>Add</div>, use):
const { t } = useTranslation();
<div>{t('add')}</div>
  • I have not included any files that are not related to my pull request, including package-lock and package-json if dependencies have not changed
  • I didn't use any hardcoded values (otherwise it will not scale, and will make it difficult to maintain consistency across the application).
  • I made sure font sizes, color choices etc are all referenced from the theme. I don't have any hardcoded dimensions.
  • My PR is granular and targeted to one specific feature.
  • I ran npm run format in server and client directories, which automatically formats your code.
  • I took a screenshot or a video and attached to this PR if there is a UI change.

Summary by CodeRabbit

  • New Features

    • Added page-based pagination for monitor statistics so users can navigate older/newer data when reviewing monitor performance.
    • Monitor statistics now include a "remaining checks" count showing how many checks exist beyond the current page.
    • Paging is preserved across requests and respects selected date ranges, improving navigation through historical data.
  • Validation

    • Query accepts a non-negative integer page parameter to control paging.

@coderabbitai
Copy link

coderabbitai bot commented Oct 29, 2025

Note

.coderabbit.yml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'release_notes'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

Adds a new numeric page parameter threaded from client hook through NetworkService, controller, service, and DB. DB gets offsetDatesByPage to compute paged date ranges, applies paging-aware filtering (including checksSkipped), and monitorStats now includes remainingChecks.

Changes

Cohort / File(s) Summary
Client: hook & network
client/src/Hooks/v1/monitorHooks.js, client/src/Utils/NetworkService.js
useFetchStatsByMonitorId signature adds page; hook passes page to networkService.getStatsByMonitorId; NetworkService appends page to URLSearchParams.
API layer: controller & validation
server/src/controllers/v1/monitorController.js, server/src/validation/joi.js
Controller extracts page from query and forwards it to service; Joi validation adds page: joi.number().integer().min(0).
Business & DB logic
server/src/service/v1/business/monitorService.js, server/src/db/v1/modules/monitorModule.js
getMonitorStatsById signatures extended to accept page and forward it; new offsetDatesByPage(dates, dateRange, page) added; DB applies offset dates when page provided, adjusts date filtering (including checksSkipped) and returns remainingChecks in monitorStats.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Hook as useFetchStatsByMonitorId
    participant Network as NetworkService
    participant Controller as monitorController
    participant Service as monitorService
    participant DB as monitorModule

    Client->>Hook: set/use page (N)
    Hook->>Network: getStatsByMonitorId(monitorId,..., page=N)
    Network->>Controller: GET /monitors/{id}/stats?page=N
    Controller->>Service: getMonitorStatsById(..., page=N)
    Service->>DB: getMonitorStatsById(..., page=N)

    rect rgb(235,245,255)
    Note over DB: Pagination-aware date computation
    DB->>DB: getDateRange(dateRange)
    DB->>DB: offsetDatesByPage(dates, dateRange, page)
    DB->>DB: filter checksForDateRange and checksSkipped
    DB->>DB: compute remainingChecks
    end

    DB-->>Service: monitorStats + remainingChecks
    Service-->>Controller: monitorStats + remainingChecks
    Controller-->>Network: response
    Network-->>Hook: data
    Hook-->>Client: paginated stats
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Review offsetDatesByPage() for correctness across dateRange branches (recent/day/week/month/all).
  • Verify checksSkipped and remainingChecks calculations and boundary conditions.
  • Confirm page is validated and correctly threaded controller → service → DB and that behavior is unchanged when page is absent.

Poem

🐰 I hop through pages, one by one,
Offsetting days in morning sun.
Skipped and shown, the counts align,
Chunks arrive—smooth scroll, just fine.
📊✨

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Feat: Pagespeed Graph Pagination Backend' clearly and concisely describes the main change: adding backend pagination support for pagespeed graph.
Description check ✅ Passed The PR description includes a clear explanation of changes, covers the new pageOffset parameter, mentions the remaining checks addition, and includes the issue number #606 with several checklist items completed.
Linked Issues check ✅ Passed The changes implement pagination for PageSpeed checks via a pageOffset parameter that shifts date ranges, allowing paginated data fetching as required by issue #606, though preloading logic appears frontend-focused.
Out of Scope Changes check ✅ Passed All changes are narrowly focused on adding pagination support to monitor stats retrieval across client/server layers; no unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec93b60 and 9c5758d.

📒 Files selected for processing (6)
  • client/src/Hooks/v1/monitorHooks.js (3 hunks)
  • client/src/Utils/NetworkService.js (1 hunks)
  • server/src/controllers/v1/monitorController.js (2 hunks)
  • server/src/db/v1/modules/monitorModule.js (4 hunks)
  • server/src/service/v1/business/monitorService.js (2 hunks)
  • server/src/validation/joi.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • server/src/service/v1/business/monitorService.js
  • client/src/Utils/NetworkService.js
  • server/src/db/v1/modules/monitorModule.js
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-24T17:52:55.506Z
Learnt from: Jesulayomy
Repo: bluewave-labs/Checkmate PR: 2664
File: client/src/Pages/Uptime/Create/index.jsx:92-96
Timestamp: 2025-07-24T17:52:55.506Z
Learning: In the Uptime monitor components, the `formatAndSet` function was deprecated due to rendering issues caused by state mutations. The current approach stores intervals internally in milliseconds (API format) but converts for display using `const displayInterval = monitor?.interval / MS_PER_MINUTE || 1;` and converts user input back to milliseconds using `value = value * MS_PER_MINUTE` in the onChange handler.

Applied to files:

  • server/src/controllers/v1/monitorController.js
  • client/src/Hooks/v1/monitorHooks.js
🧬 Code graph analysis (1)
server/src/controllers/v1/monitorController.js (1)
server/src/db/v1/modules/monitorModuleQueries.js (1)
  • req (1079-1079)
🔇 Additional comments (6)
server/src/validation/joi.js (1)

147-147: LGTM! Validation correctly implemented.

The page parameter validation properly constrains the value to non-negative integers and addresses previous feedback. Making it optional ensures backward compatibility.

server/src/controllers/v1/monitorController.js (2)

85-85: LGTM! Parameter extraction follows established patterns.

The page parameter is correctly extracted from the validated query object alongside other parameters.


101-101: LGTM! Parameter correctly threaded to service layer.

The page parameter is properly passed through to the monitor service for processing.

client/src/Hooks/v1/monitorHooks.js (3)

173-173: LGTM! Parameter correctly added to hook signature.

The page parameter is properly included in the hook's destructured parameters, following the established pattern.


191-191: LGTM! Parameter correctly passed to network service.

The page parameter is properly threaded through to the network service call for fetching paginated statistics.


203-212: LGTM! Dependency array correctly updated.

The page parameter is properly included in the dependency array to trigger refetching when pagination changes. The reformatting to one dependency per line improves readability and makes future diffs clearer.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@mospell mospell marked this pull request as ready for review October 29, 2025 11:08
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
client/src/Utils/NetworkService.js (1)

169-195: Update JSDoc to document the pageOffset parameter.

The JSDoc comment for getStatsByMonitorId is missing documentation for the new pageOffset parameter.

Apply this diff to update the JSDoc:

 	/**
 	 * ************************************
 	 * Get stats for a monitor
 	 * ************************************
 	 *
 	 * @async
 	 * @param {Object} config - The configuration object.
 	 * @param {string} config.monitorId - The ID of the monitor whose statistics are to be retrieved.
 	 * @param {string} config.sortOrder - The order in which to sort the retrieved statistics.
 	 * @param {number} config.limit - The maximum number of statistics to retrieve.
 	 * @param {string} config.dateRange - The date range for which to retrieve statistics.
 	 * @param {number} config.numToDisplay - The number of checks to display.
 	 * @param {boolean} config.normalize - Whether to normalize the retrieved statistics.
+	 * @param {number} [config.pageOffset] - The page offset for pagination (shifts date range backward).
 	 * @returns {Promise<AxiosResponse>} The response from the axios GET request.
 	 */
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a9a8686 and 8f57bec.

📒 Files selected for processing (6)
  • client/src/Hooks/v1/monitorHooks.js (3 hunks)
  • client/src/Utils/NetworkService.js (1 hunks)
  • server/src/controllers/v1/monitorController.js (2 hunks)
  • server/src/db/v1/modules/monitorModule.js (4 hunks)
  • server/src/service/v1/business/monitorService.js (2 hunks)
  • server/src/validation/joi.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
server/src/controllers/v1/monitorController.js (1)
server/src/db/v1/modules/monitorModuleQueries.js (1)
  • req (1079-1079)
🔇 Additional comments (4)
client/src/Hooks/v1/monitorHooks.js (1)

166-205: LGTM! Pagination parameter correctly integrated.

The pageOffset parameter is properly threaded through the hook signature, passed to the network service call, and included in the dependency array to trigger refetching when the offset changes.

server/src/controllers/v1/monitorController.js (1)

80-111: LGTM! Controller properly extracts and forwards pagination parameter.

The pageOffset query parameter is correctly extracted and passed to the service layer, consistent with the existing parameter handling pattern.

server/src/service/v1/business/monitorService.js (1)

46-59: LGTM! Service layer correctly propagates pagination parameter.

The pageOffset parameter is properly added to the service signature and forwarded to the database module without additional processing, which is appropriate for the service layer.

server/src/db/v1/modules/monitorModule.js (1)

276-323: LGTM! Pagination logic correctly implemented.

The pagination implementation properly:

  • Applies date offset when pageOffset is provided (lines 289-291)
  • Calculates checksSkipped to track checks newer than the current page (line 295)
  • Computes remainingChecks to indicate how many older checks exist for future pages (line 305)

The remainingChecks calculation is correct: it represents checks that are older than the current page by subtracting both the current page checks and future page checks from the total.

Copy link

@llamapreview llamapreview bot left a comment

Choose a reason for hiding this comment

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

AI Code Review by LlamaPReview

🎯 TL;DR & Recommendation

Recommendation: Request Changes

This PR adds pagination support but introduces critical bugs in date range handling and remaining checks calculation, and the main frontend component is not updated, making the feature unusable.

📄 Documentation Diagram

This diagram documents the paginated data fetching workflow for the PageSpeed graph.

sequenceDiagram
    participant U as User
    participant F as Frontend
    participant B as Backend
    participant D as Database
    U->>F: Navigate to PageSpeed graph
    F->>B: getMonitorStatsById(monitorId, pageOffset, ...)
    note over B: PR #35;3045: pageOffset shifts date range
    B->>B: offsetDatesByPage(dates, dateRange, pageOffset)
    B->>D: Query checks within date range
    D-->>B: Return checks
    B->>B: Calculate remainingChecks
    B-->>F: Return monitor stats with remainingChecks
    F-->>U: Display paginated data
Loading

🌟 Strengths

  • Adds requested pagination feature for PageSpeed graph with remaining checks count.
  • Well-structured backend changes with validation updates.
Priority File Category Impact Summary Anchors
P1 server/src/db/v1/modules/monitorModule.js Bug Missing month/hour dateRange support causing runtime errors symbol:offsetDatesByPage
P1 server/src/db/v1/modules/monitorModule.js Bug Incorrect remainingChecks calculation breaking pagination symbol:getMonitorStatsById
P1 client/src/Pages/v1/PageSpeed/.../index.jsx Architecture Frontend not updated for pagination, feature unusable symbol:useFetchStatsByMonitorId
P2 server/src/db/v1/modules/monitorModule.js Bug pageOffset=0 not handled, inconsistent behavior symbol:getMonitorStatsById
P2 server/src/validation/joi.js Maintainability Negative pageOffset allowed, confusing API symbol:getMonitorStatsByIdQueryValidation
P2 server/src/db/v1/modules/monitorModule.js Performance Potential performance issue from loading all checks symbol:getMonitorStatsById

🔍 Notable Themes

  • Inconsistent Date Range Handling: Gaps in date range support across functions risk runtime errors and broken queries.
  • Frontend-Backend Contract Mismatch: Pagination implemented backend-side but not consumed by primary frontend component, hindering feature usability.

📈 Risk Diagram

This diagram illustrates the risks in date range handling and remaining checks calculation.

sequenceDiagram
    participant F as Frontend
    participant B as Backend
    F->>B: getMonitorStatsById with pageOffset
    B->>B: offsetDatesByPage
    note over B: R1(P1): Missing month/hour dateRange support
    B->>B: getMonitorChecks
    B->>B: Calculate remainingChecks
    note over B: R2(P1): Incorrect checksSkipped filter
    B-->>F: Return data with remainingChecks
    note over F: R3(P1): Frontend may not pass pageOffset
Loading
⚠️ **Unanchored Suggestions (Manual Review Recommended)**

The following suggestions could not be precisely anchored to a specific line in the diff. This can happen if the code is outside the changed lines, has been significantly refactored, or if the suggestion is a general observation. Please review them carefully in the context of the full file.


📁 File: server/src/db/v1/modules/monitorModule.js

The remainingChecks calculation is flawed. checksSkipped filters checks after dates.end, but dates.end is already offset into the past, so this incorrectly counts future checks. The intended behavior should count checks before the current page's date range. This will cause incorrect pagination state on the frontend.

Suggestion:

const checksBeforeCurrentPage = checksAll.filter((check) => check.createdAt < dates.start);
remainingChecks: checksBeforeCurrentPage.length

Related Code:

const checksSkipped = checksAll.filter((check) => check.createdAt > dates.end);
remainingChecks: checksAll.length - checksForDateRange.length - checksSkipped.length

📁 File: client/src/Pages/v1/PageSpeed/Details/index.jsx

The primary consumer of useFetchStatsByMonitorId (PageSpeed Details page) is not updated to pass the new pageOffset parameter. This creates an API contract break where the backend expects pagination support but the main frontend component doesn't implement it. The feature will be unusable without updating this component.

Suggestion:

const [pageOffset, setPageOffset] = useState(0);
const [monitor, audits, isLoading, networkError] = useFetchStatsByMonitorId({
  monitorId,
  sortOrder: "desc",
  limit: 50,
  pageOffset,
});

Related Code:

const [monitor, audits, isLoading, networkError] = useFetchStatsByMonitorId({
  monitorId,
  sortOrder: "desc",
  limit: 50,
});


💡 Have feedback? We'd love to hear it in our GitHub Discussions.
✨ This review was generated by LlamaPReview Advanced, which is free for all open-source projects. Learn more.

};

// Helper
offsetDatesByPage = (dates, dateRange, pageOffset) => {
Copy link

Choose a reason for hiding this comment

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

P1 | Confidence: High

The date offset calculation is missing support for "month" dateRange and incorrectly handles "hour" dateRange. When dateRange is "month", dateOffsets[dateRange] will be undefined, resulting in NaN date calculations that will break the query. The "hour" dateRange is supported by getDateRange() but not handled here, creating inconsistency. This will cause runtime errors for these date ranges.

Suggested change
offsetDatesByPage = (dates, dateRange, pageOffset) => {
offsetDatesByPage = (dates, dateRange, pageOffset) => {
const dateOffsets = {
hour: 60 * 60 * pageOffset * 1000,
recent: 60 * 60 * 2 * pageOffset * 1000,
day: 60 * 60 * 24 * pageOffset * 1000,
week: 60 * 60 * 24 * 7 * pageOffset * 1000,
month: 60 * 60 * 24 * 30 * pageOffset * 1000, // Approximate month as 30 days
all: 0,
};
return {
start: new Date(dates.start.getTime() - dateOffsets[dateRange]),
end: new Date(new Date().getTime() - dateOffsets[dateRange]),
};
};

Evidence: symbol:offsetDatesByPage, method:getMonitorStatsById

Comment on lines 287 to 290
let dates = this.getDateRange(dateRange);

if (pageOffset) {
dates = this.offsetDatesByPage(dates, dateRange, pageOffset);
Copy link

Choose a reason for hiding this comment

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

P2 | Confidence: Medium

The condition if (pageOffset) treats 0 as falsy, preventing the first page (pageOffset=0) from applying date offsets. This creates inconsistent behavior where page 0 uses current dates but subsequent pages use offset dates. The pagination should work consistently starting from page 0.

Suggested change
let dates = this.getDateRange(dateRange);
if (pageOffset) {
dates = this.offsetDatesByPage(dates, dateRange, pageOffset);
if (pageOffset !== undefined && pageOffset !== null) {
dates = this.offsetDatesByPage(dates, dateRange, pageOffset);
}

Evidence: symbol:getMonitorStatsById

dateRange: joi.string().valid("hour", "day", "week", "month", "all"),
numToDisplay: joi.number(),
normalize: joi.boolean(),
pageOffset: joi.number().integer(),
Copy link

Choose a reason for hiding this comment

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

P2 | Confidence: Medium

The validation allows negative pageOffset values which don't make logical sense for pagination (can't page into the future). This could lead to confusing API behavior. Adding a minimum constraint would make the API more robust.

Suggested change
pageOffset: joi.number().integer(),
pageOffset: joi.number().integer().min(0),

Evidence: symbol:getMonitorStatsByIdQueryValidation


// Get Checks for monitor in date range requested
const dates = this.getDateRange(dateRange);
const { checksAll, checksForDateRange } = await this.getMonitorChecks(monitorId, dates, sort);
Copy link

Choose a reason for hiding this comment

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

P2 | Confidence: Medium

Speculative: The current implementation loads all checks (checksAll) into memory for every paginated request. For monitors with extensive history, this could cause performance issues. Consider implementing database-level pagination or counting instead of loading all records.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
server/src/validation/joi.js (1)

147-147: Previous validation concerns have been addressed.

The pageOffset validation now correctly includes the .min(0) constraint, preventing negative integers that would be semantically invalid for pagination. The implementation is sound.

As an optional enhancement, consider adding an upper bound constraint (e.g., .max(365)) to prevent excessively large offset values that might impact performance or result in empty datasets. However, if this is already handled at the business logic layer, the current validation is sufficient.

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8f57bec and 0e48593.

📒 Files selected for processing (1)
  • server/src/validation/joi.js (1 hunks)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
server/src/db/v1/modules/monitorModule.js (1)

296-298: Verify that pageOffset=0 is handled correctly.

The condition if (pageOffset) treats 0 as falsy. If pageOffset=0 represents the first page, the current code won't apply date offsets for it, creating inconsistent pagination behavior. Ensure this aligns with the intended pagination logic.

As noted in past reviews, if page 0 should apply offsets, use:

-if (pageOffset) {
+if (pageOffset !== undefined && pageOffset !== null) {
   dates = this.offsetDatesByPage(dates, dateRange, pageOffset);
 }
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e48593 and 38631d2.

📒 Files selected for processing (1)
  • server/src/db/v1/modules/monitorModule.js (4 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-24T17:52:55.506Z
Learnt from: Jesulayomy
Repo: bluewave-labs/Checkmate PR: 2664
File: client/src/Pages/Uptime/Create/index.jsx:92-96
Timestamp: 2025-07-24T17:52:55.506Z
Learning: In the Uptime monitor components, the `formatAndSet` function was deprecated due to rendering issues caused by state mutations. The current approach stores intervals internally in milliseconds (API format) but converts for display using `const displayInterval = monitor?.interval / MS_PER_MINUTE || 1;` and converts user input back to milliseconds using `value = value * MS_PER_MINUTE` in the onChange handler.

Applied to files:

  • server/src/db/v1/modules/monitorModule.js
🔇 Additional comments (1)
server/src/db/v1/modules/monitorModule.js (1)

302-313: LGTM: Pagination metadata is correctly calculated.

The logic for checksSkipped and remainingChecks correctly supports backward pagination:

  • checksSkipped counts checks newer than the current page window (after dates.end)
  • remainingChecks represents older checks available for further pagination

This aligns with the PR objective of implementing scrollable graph pagination.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
server/src/db/v1/modules/monitorModule.js (1)

296-298: Consider pageOffset=0 edge case.

The condition if (pageOffset) treats 0 as falsy, meaning when pageOffset is 0, the date offset won't be applied. This could be intentional if page 0 represents "current data with no offset," but it creates an inconsistency where page 0 behaves differently from page 1+.

If pageOffset: 0 should apply the same logic (i.e., no shift, which the math handles correctly), consider:

-if (pageOffset) {
+if (pageOffset !== undefined && pageOffset !== null) {
   dates = this.offsetDatesByPage(dates, dateRange, pageOffset);
 }

This was previously flagged in past reviews.

🧹 Nitpick comments (1)
server/src/db/v1/modules/monitorModule.js (1)

124-144: Month handling correctly avoids mutation.

The use of new Date(dates.start) and new Date(dates.end) at lines 135-136 properly prevents mutation of the original dates object, addressing the concern from previous reviews.

However, note that if an unexpected dateRange value is passed (not in dateOffsets and not "month"), dateOffsets[dateRange] will be undefined, leading to NaN date values. Since dateRange is validated by the Joi schema, this is acceptable defensive programming, but consider adding a fallback or validation within this helper for robustness.

If you want to add defensive validation:

 offsetDatesByPage = (dates, dateRange, pageOffset) => {
   const dateOffsets = {
     recent: 60 * 60 * 2 * pageOffset * 1000,
     day: 60 * 60 * 24 * pageOffset * 1000,
     week: 60 * 60 * 24 * 7 * pageOffset * 1000,
     all: 0,
   };

   if (dateRange == "month") {
     return {
       start: new Date(new Date(dates.start).setMonth(dates.start.getMonth() - pageOffset)),
       end: new Date(new Date(dates.end).setMonth(dates.end.getMonth() - pageOffset)),
     };
   } else {
+    const offset = dateOffsets[dateRange];
+    if (offset === undefined) {
+      throw new Error(`Invalid dateRange: ${dateRange}`);
+    }
     return {
-      start: new Date(dates.start.getTime() - dateOffsets[dateRange]),
-      end: new Date(dates.end.getTime() - dateOffsets[dateRange]),
+      start: new Date(dates.start.getTime() - offset),
+      end: new Date(dates.end.getTime() - offset),
     };
   }
 };
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 38631d2 and ec93b60.

📒 Files selected for processing (1)
  • server/src/db/v1/modules/monitorModule.js (4 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-24T17:52:55.506Z
Learnt from: Jesulayomy
Repo: bluewave-labs/Checkmate PR: 2664
File: client/src/Pages/Uptime/Create/index.jsx:92-96
Timestamp: 2025-07-24T17:52:55.506Z
Learning: In the Uptime monitor components, the `formatAndSet` function was deprecated due to rendering issues caused by state mutations. The current approach stores intervals internally in milliseconds (API format) but converts for display using `const displayInterval = monitor?.interval / MS_PER_MINUTE || 1;` and converts user input back to milliseconds using `value = value * MS_PER_MINUTE` in the onChange handler.

Applied to files:

  • server/src/db/v1/modules/monitorModule.js
🔇 Additional comments (2)
server/src/db/v1/modules/monitorModule.js (2)

283-283: LGTM: Method signature updated for pagination.

The addition of pageOffset to the method signature is clean and maintains backward compatibility since it's optional.


302-302: Pagination logic correctly computes remaining checks.

The calculations for checksSkipped (line 302) and remainingChecks (line 312) properly account for the paginated date window:

  • checksSkipped: Checks after the current page window (created after dates.end)
  • remainingChecks: Checks before the current page window (older data available for pagination)

This provides the frontend with the necessary information for pagination UI.

Note: Loading all checks into memory (line 301 via getMonitorChecks) may have performance implications for monitors with extensive history. This was flagged in previous reviews and remains a consideration for future optimization, but is acceptable for the current implementation scope.

Also applies to: 312-312

Copy link
Collaborator

@ajhollid ajhollid left a comment

Choose a reason for hiding this comment

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

Hey @mospell ,

Thanks for your contribution here.

If the backend only takes a page offset that leaves calculating the page offset to the frontend. I believe that this should be a backend operation, and the frontend shouldn't know or care what the page offset is.

The frontend should only care about what page it is on, and how many rows per page to display.

In light of that, the backend should take two variables, the page and rowsPerPage, and multiplying the two together will yield the page offset.

You can see how pagination is handled for other monitor types on both the frontend and the backend for reference.

@mospell
Copy link
Author

mospell commented Nov 4, 2025

Hi @ajhollid,

pageOffset would basically increment by 1 until it runs out of remainingChecks blocking you from scrolling further when there is no more data. I probably should've just named it page instead now that I think about it.

The backend should take two variables, the page and rowsPerPage, and multiplying the two together will yield the page offset.

My initial idea was time-based pagination, as querying an x amount of checks doesn't really make sense on a graph. How would rowsPerPage apply here? Could you give an example?

@gorkem-bwl
Copy link
Contributor

@mospell you have a CI/CD check issue here.

@mospell
Copy link
Author

mospell commented Nov 12, 2025

Hi @ajhollid, just pinging you again

The way it currently works is that there is no pageOffset calculation in the frontend. All offset calculations happen in the backend as you requested. To clarify this I have renamed the pageOffset variable to just page.

I believe it would make more sense to have the user scroll through previous dates instead of displaying a number of rows per page. I think the way it currently works should satisfy your requirements. If you'd like me to change it anyway, please let me know.

@mospell
Copy link
Author

mospell commented Nov 12, 2025

@gorkem-bwl this should be resolved now, thanks for the notice!

@ajhollid
Copy link
Collaborator

ajhollid commented Nov 12, 2025

@mospell perhaps I'm misunderstanding somethig here, pagination of results should depend on a page and a rowsPerPage input from the front end should it not?

ie on the frontend, the user selects "5 rows per page" and then paginates thorugh results, starting from page 0.

Offset is then calcualted by multiplying rowsPerPage * page on the backend.

Example:

let skip = 0;
if (page && rowsPerPage) {
	skip = page * rowsPerPage;
}

const checks = await this.Check.aggregate([
	{ $match: matchStage },
	{ $sort: { createdAt: sortOrder } },
	{
		$facet: {
			summary: [{ $count: "checksCount" }],
			checks: [{ $skip: skip }, { $limit: rowsPerPage }],
		},
	},
	{
		$project: {
			checksCount: {
				$ifNull: [{ $arrayElemAt: ["$summary.checksCount", 0] }, 0],
			},
			checks: {
				$ifNull: ["$checks", []],
			},
		},
	},
]);

@mospell
Copy link
Author

mospell commented Nov 12, 2025

@ajhollid Usually yes, but I decided to go for time-based pagination here. In this case dateRange would kind of act as rowsPerPage here.

On the frontend the user would pick a date range and then paginate through results that way, starting from page 0 (today).

Instead of calculating an offset based on rowsPerPage and page, the backend shifts back the selected dateRange by the page number.

So for example:
dateRange:"day" and page:5 - returns data from 5 days ago
dateRange:"week" and page:2 - returns data from 2 weeks ago.
and so on, until there are no more checks.

This is done by the offsetDatesByPage helper function in monitorService.js, feel free to take a look at it if this reply still ends up being a bit confusing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FE&BE - Scrollable Pagespeed Graph

3 participants