Skip to content

feat(ui): add bulk edit notifications modal and floating action bar to uptime monitors#3448

Open
ashu130698 wants to merge 11 commits intobluewave-labs:developfrom
ashu130698:feat/frontend-bulk-notifications
Open

feat(ui): add bulk edit notifications modal and floating action bar to uptime monitors#3448
ashu130698 wants to merge 11 commits intobluewave-labs:developfrom
ashu130698:feat/frontend-bulk-notifications

Conversation

@ashu130698
Copy link
Copy Markdown
Contributor

Changes

This PR implements the frontend user interface for bulk-editing monitor notifications, building seamlessly on the backend work from my previous PR (#3376).

As requested by @gorkem-bwl in the issue thread, this introduces a clean, non-intrusive Floating Action Bar pattern that appears only when multiple monitors are selected in the table, rather than a bulky static "bulk edit" button.

Core components introduced:

  • FloatingActionBar.tsx: A reusable slide-up bar component.
  • BulkEditNotificationsModal.tsx: A dialog to select the action (add, remove, set) and query your available notification channels via useGet.
  • UptimeMonitorsTable.tsx Integration: Safely injected a checkbox column to the generic table headers to handle selection states.
  • Lifted the selectedMonitors array into the Uptime/Monitors/index.tsx page to sync the bar and table.

(Note: Currently applied to the Uptime Monitors page as an MVP to gather feedback on the design. If approved, I will follow up and apply this exact same logic to the Infrastructure and PageSpeed tables!)

Fixes #2320

  • I deployed the application locally.
  • I have performed a self-review and testing of my code.
  • I have included the issue Bulk edit monitor notification settings #2320 in the PR.
  • I have added i18n support to visible strings.
  • I have not included any files that are not related to my pull request.
  • I didn't use any hardcoded values.
  • I made sure font sizes, colour 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 and attached it to this PR if there is a UI change.
Screenshot 2026-03-27 220658

@gorkem-bwl
Copy link
Copy Markdown
Contributor

LGTM UI wise.

Can you make sure those margins are the same? (the ones on the right are the right margins):

image

Copy link
Copy Markdown
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.

Visually looks great 👍

There are some UX issues that need to be resolved, and most importantly selection is not cleared on page change.

Please see my comments in the code review and revise as needed, thanks!

left: "50%",
transform: "translateX(-50%)",
zIndex: theme.zIndex.snackbar,
padding: theme.spacing(1.5, 3),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please use the Utils/Theme/constants util for all spacing values

const handleBulkEditSuccess = () => {
setIsBulkEditModalOpen(false);
setSelectedMonitors([]);
refetch();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

What about failure? Modal stays open on failure right now.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This hasn't been addressed yet

@@ -1,5 +1,6 @@
import Stack from "@mui/material/Stack";
Copy link
Copy Markdown
Collaborator

@ajhollid ajhollid Mar 27, 2026

Choose a reason for hiding this comment

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

What about all the other monitor pages? Currently only uptime monitors are bulk editable 🤔

EDIT
I see you addressed this in your PR statement, noted 👍

};

const handleClearSelection = () => {
setSelectedMonitors([]);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This should be called on pagination change too

onClick={() => setIsBulkEditModalOpen(true)}
>
{t("pages.common.monitors.bulkEdit.editButton", {
defaultValue: "Edit Notifications",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No need for defaultValue, please remove all these

Comment on lines +164 to +188
<Checkbox
checked={monitors.length > 0 && selectedMonitors.length === monitors.length}
indeterminate={
selectedMonitors.length > 0 && selectedMonitors.length < monitors.length
}
onChange={(e) => {
if (e.target.checked) {
onSelectAll(monitors.map((m) => m.id));
} else {
onSelectAll([]);
}
}}
onClick={(e) => e.stopPropagation()}
/>
),
render: (row) => {
return (
<Checkbox
checked={selectedMonitors.includes(row.id)}
onChange={() => onSelectMonitor(row.id)}
onClick={(e) => e.stopPropagation()}
/>
);
},
},
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This needs to be adjusted to account for small screen card view:

Image

Please see the table class to see how the cards work on small screens.

@ashu130698
Copy link
Copy Markdown
Contributor Author

@ajhollid @gorkem-bwl Thank you so much for the detailed review and catching those quirks! I have just pushed an updated commit addressing all of your feedback.

Here is a breakdown of the fixes:

  1. Selection Memory Sink: Pagination state behaves correctly now via a new handleClearSelection() trigger injected safely into page and rows-per-page modification events.
  2. Modal Error Transparency: Integrated a dynamic MUI block inside the bulk-edit modal that gracefully handles usePatch failures, keeping the modal open so users retain their pre-filled selection parameters without hanging ambiguously on a failed spin state.
  3. Spacing Parity: I traced down the discrepancy in padding. I replaced all hardcoded dimensions with Checkmate's globally scaled LAYOUT.MD and LAYOUT.XS constants mapped across the dimensions and internal grouping gaps. It dynamically resolves the UI identically to the 16px constraint in the screenshots.
  4. i18n Housekeeping: Comprehensively audited every newly added component string and cleanly scrubbed all { defaultValue } overrides from t() lookups per your coding guidelines!
  5. Mobile Grid Resizing: Deployed a responsive isSmall boundary (useMediaQuery) in UptimeMonitorsTable.tsx to conditionally swap out the raw checkbox array on smaller viewports. Our small screen cards now render the clean string Selected securely matching your Actions pattern instead of an awkward side-by-side array!

Copy link
Copy Markdown
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.

There's an outstanding item from the last review, and two other issues I've noticed

Image

The floating action button doesn't close when the modal opens

and

Image

There doesn't appear to be a "select all" option on mobile, and the checkbox for individual cards should be left aligned, not center aligned.

const handleBulkEditSuccess = () => {
setIsBulkEditModalOpen(false);
setSelectedMonitors([]);
refetch();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This hasn't been addressed yet

@ashu130698
Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review @ajhollid! I've pushed a new commit that addresses all three issues:

Floating Action Bar: It now properly hides behind a !isBulkEditModalOpen condition so it doesn't stay awkwardly visible when the modal mounts.

Mobile Layout: I updated the underlying

component so that an empty header label allows the row content (like our checkbox) to cleanly expand to size=12 and naturally left-align. I also added a missing "Select All" option explicitly for the mobile card view!

Modal Error Handling: To address the poor UX of the modal staying open on an API failure, I updated the logic so it now always closes the modal upon API completion (whether it succeeds or errors). This way, the user relies on the global error toast for the failure feedback, but their underlying checkbox selection is safely preserved so they can easily click "Edit" and try again!

(Just let me know if you actually preferred the modal to stay open on failure while just handling the error state completely differently—I can easily swap that back if I misunderstood that piece of feedback!)

Copy link
Copy Markdown
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.

Almost there!

The Floating action button isn't displaying properly in mobile mode:

Image

This should be centered in the screen, but it is offset to the right and overflowing offscreen

@ashu130698
Copy link
Copy Markdown
Contributor Author

Great catch on the mobile view overflow! The Floating Action Bar's slide animation was unintentionally stripping our CSS transform centering on small screens. I've switched the layout to use auto-margins and pushed the fix—it now centers perfectly on mobile. The PR should be fully good to go now!

Copy link
Copy Markdown
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.

Unfortunately, the changes don't seem to solve the issue. In fact it seems to be broken in both mobile and desktop now:

Image Image

the FAB should be centered in the screen.

…rapper to permanently center floating action bars
@ashu130698
Copy link
Copy Markdown
Contributor Author

Thanks for catching this @ajhollid! You're absolutely right - the FAB should be
centered horizontally on the screen, not pushed to the left.

I see the issue now - the margin-based centering approach isn't working reliably
across viewports. I've pushed a commit that replaces the buggy margin logic with
a more reliable flexbox-based approach that centers the component properly on both
mobile and desktop.

Could you please verify that the latest commit fixes the alignment issue on your
end? I'd appreciate your feedback to confirm the positioning looks correct now.

Copy link
Copy Markdown
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.

FAB is displaying properly now, but there are issues with the localization file I missed in earlier reviews, please address those and this should be good to merge.

Comment thread client/src/locales/en.json Outdated
@@ -1,5 +1,6 @@
{
"common": {
"selected": "selected",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is not the correct structure for a key. Since it is used in a button, let's put it in common.buttons

These strings need to be logically organized or this file becomes unmanageable very quickly.

variant="body2"
color="text.secondary"
>
{t("common.selectAll", "Select All")}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This string does not exist in the translation file:

i18next::translator: missingKey en translation common.selectAll Select All

and if it did it is not in the logically correct location. It should belong to the common monitor strings object, probably as an option in common.monitors.bulkEdit

@ashu130698
Copy link
Copy Markdown
Contributor Author

Thanks again @ajhollid! You were totally right—the translation file needed to be properly structured before it got unmanageable.

I've successfully migrated the selected key into common.buttons.selected and added the missing selectAll string exactly where you requested in pages.common.monitors.bulkEdit. I also updated all the React components to properly fetch from this new nested structure.

Everything should be tightly organized and good to go now! Thanks for taking the time to catch these.

@ashu130698
Copy link
Copy Markdown
Contributor Author

@ajhollid - Just wanted to follow up! I've implemented all your suggestions. Would appreciate another review when you get a moment. Thanks!

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.

Bulk edit monitor notification settings

3 participants