feat: added bulk edit notification UI to uptime monitors

This commit is contained in:
ashu130698 committed 2026-05-14 15:45:11 +05:30
1 parent eb29a725c2
commit ff0ef928a6
5 files changed
+185 -1

No files matched your search

@@ -0,0 +1,145 @@
import React, { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { useTheme } from "@mui/material/styles";
import { Dialog, Select, RadioWithDescription } from "@/Components/inputs";
import Stack from "@mui/material/Stack";
import RadioGroup from "@mui/material/RadioGroup";
import MenuItem from "@mui/material/MenuItem";
import Typography from "@mui/material/Typography";
import { useGet, usePatch } from "@/Hooks/UseApi";
import type { Notification } from "@/Types/Notification";
interface BulkEditNotificationsModalProps {
open: boolean;
onClose: () => void;
selectedMonitors: string[];
onSuccess: () => void;
}
export const BulkEditNotificationsModal: React.FC<BulkEditNotificationsModalProps> = ({
open,
onClose,
selectedMonitors,
onSuccess,
}) => {
const { t } = useTranslation();
const theme = useTheme();
const [action, setAction] = useState<"add" | "remove" | "set">("add");
const [notificationIds, setNotificationIds] = useState<string[]>([]);
// Fetch available notifications
const { data: notifications } = useGet<Notification[]>(
"/notifications/team",
undefined,
{
keepPreviousData: true,
}
);
const { patch, loading: isPatching } = usePatch();
// Reset state when modal opens
useEffect(() => {
if (open) {
setAction("add");
setNotificationIds([]);
}
}, [open]);
const handleConfirm = async () => {
const res = await patch("/monitors/notifications", {
monitorIds: selectedMonitors,
notificationIds,
action,
});
if (res) {
onSuccess();
}
};
// Prevent submitting empty arrays unless the action is 'set' (as per PR feedback!)
const isMissingSelection = action !== "set" && notificationIds.length === 0;
return (
<Dialog
open={open}
title={t("pages.common.monitors.bulkEdit.title", {
defaultValue: "Edit Notifications",
})}
onCancel={onClose}
onConfirm={isMissingSelection ? undefined : handleConfirm}
loading={isPatching}
maxWidth="sm"
fullWidth
>
<Stack
spacing={theme.spacing(4)}
mt={theme.spacing(2)}
>
<Typography variant="body1">
{t("pages.common.monitors.bulkEdit.selectedText", {
count: selectedMonitors.length,
defaultValue: `You are applying changes to {{count}} monitor(s).`,
})}
</Typography>
<RadioGroup
value={action}
onChange={(e) => setAction(e.target.value as "add" | "remove" | "set")}
>
<RadioWithDescription
value="add"
label={t("pages.common.monitors.bulkEdit.actionAdd", { defaultValue: "Add" })}
description={t("pages.common.monitors.bulkEdit.actionAddDesc", {
defaultValue:
"Adds the selected notifications to these monitors without removing existing ones.",
})}
/>
<RadioWithDescription
value="remove"
label={t("pages.common.monitors.bulkEdit.actionRemove", {
defaultValue: "Remove",
})}
description={t("pages.common.monitors.bulkEdit.actionRemoveDesc", {
defaultValue:
"Removes exclusively the selected notifications from these monitors.",
})}
/>
<RadioWithDescription
value="set"
label={t("pages.common.monitors.bulkEdit.actionSet", {
defaultValue: "Replace all",
})}
description={t("pages.common.monitors.bulkEdit.actionSetDesc", {
defaultValue:
"Replaces all existing notifications. Leave empty to clear all notifications.",
})}
/>
</RadioGroup>
<Select
multiple
value={notificationIds}
onChange={(e) => setNotificationIds(e.target.value as string[])}
fieldLabel={t("pages.common.monitors.bulkEdit.selectLabel", {
defaultValue: "Notification Channels",
})}
placeholder={t("pages.common.monitors.bulkEdit.selectPlaceholder", {
defaultValue: "Select channels...",
})}
>
{notifications?.map((n) => (
<MenuItem
key={n.id}
value={n.id}
>
{n.name} ({n.type.toUpperCase()})
</MenuItem>
))}
</Select>
</Stack>
</Dialog>
);
};
+1
View File
@@ -15,3 +15,4 @@ export * from "./charts/HistogramPageSpeedDetailsTooltip";
export * from "./charts/HistogramInfrastructure";
export * from "./HeaderMonitorsSummary";
export * from "./BulkActionsBar";
export * from "./BulkEditNotificationsModal";
@@ -44,6 +44,9 @@ export const MonitorTable = ({
monitors,
refetch,
setSelectedMonitor,
selectedMonitors,
onSelectMonitor,
onSelectAll,
sortField,
setSortField,
sortOrder,
+22 -1
View File
@@ -9,6 +9,7 @@ import Stack from "@mui/material/Stack";
import { MonitorTable } from "@/Pages/Uptime/Monitors/Components/UptimeMonitorsTable";
import { HeaderCreate } from "@/Components/common";
import { Play, Pause } from "lucide-react";
import { BulkEditNotificationsModal } from "@/Components/monitors";
import { useTranslation } from "react-i18next";
import useMediaQuery from "@mui/material/useMediaQuery";
@@ -43,7 +44,7 @@ const UptimeMonitorsPage = () => {
const [sortField, setSortField] = useState<string>("");
const [sortOrder, setSortOrder] = useState<"asc" | "desc">("asc");
const [selectedMonitor, setSelectedMonitor] = useState<Monitor | null>(null);
const [isBulkEditModalOpen, setIsBulkEditModalOpen] = useState(false);
const isDialogOpen = Boolean(selectedMonitor);
const debouncedSearch = useDebounce<string>(search, 300);
@@ -136,6 +137,12 @@ const UptimeMonitorsPage = () => {
refetch();
};
const handleBulkEditSuccess = () => {
setIsBulkEditModalOpen(false);
setSelectedRows([]);
refetch();
};
const handleCancel = () => {
setSelectedMonitor(null);
};
@@ -213,6 +220,14 @@ const UptimeMonitorsPage = () => {
>
{t("common.buttons.pause")}
</Button>
<Button
size="small"
onClick={() => setIsBulkEditModalOpen(true)}
>
{t("pages.common.monitors.bulkEdit.editButton", {
defaultValue: "Edit Notifications",
})}
</Button>
</BulkActionsBar>
)}
@@ -248,6 +263,12 @@ const UptimeMonitorsPage = () => {
onCancel={handleCancel}
loading={isDeleting}
/>
<BulkEditNotificationsModal
open={isBulkEditModalOpen}
onClose={() => setIsBulkEditModalOpen(false)}
selectedMonitors={selectedRows}
onSuccess={handleBulkEditSuccess}
/>
</MonitorBasePageWithStates>
);
};
+14
View File
@@ -1,5 +1,6 @@
{
"common": {
"selected": "selected",
"auth": {
"roles": {
"admin": "Admin",
@@ -425,6 +426,19 @@
},
"common": {
"monitors": {
"bulkEdit": {
"editButton": "Edit Notifications",
"title": "Edit Notifications",
"selectedText": "You are applying changes to {{count}} monitor(s).",
"actionAdd": "Add",
"actionAddDesc": "Adds the selected notifications to these monitors without removing existing ones.",
"actionRemove": "Remove",
"actionRemoveDesc": "Removes exclusively the selected notifications from these monitors.",
"actionSet": "Replace all",
"actionSetDesc": "Replaces all existing notifications. Leave empty to clear all notifications.",
"selectLabel": "Notification Channels",
"selectPlaceholder": "Select channels..."
},
"actions": {
"configure": "Configure",
"delete": "Delete",