mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-18 07:39:54 -06:00
implement editing notification channels
This commit is contained in:
@@ -4,6 +4,7 @@ import { networkService } from "../main";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { NOTIFICATION_TYPES } from "../Pages/Notifications/utils";
|
||||
|
||||
const useCreateNotification = () => {
|
||||
const navigate = useNavigate();
|
||||
@@ -88,4 +89,73 @@ const useDeleteNotification = () => {
|
||||
|
||||
return [deleteNotification, isLoading, error];
|
||||
};
|
||||
export { useCreateNotification, useGetNotificationsByTeamId, useDeleteNotification };
|
||||
|
||||
const useGetNotificationById = (id, setNotification) => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
const getNotificationById = useCallback(async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await networkService.getNotificationById({ id });
|
||||
|
||||
const notification = response?.data?.data ?? null;
|
||||
|
||||
const notificationData = {
|
||||
userId: notification?.userId,
|
||||
teamId: notification?.teamId,
|
||||
address: notification?.address,
|
||||
notificationName: notification?.notificationName,
|
||||
type: NOTIFICATION_TYPES.find((type) => type.value === notification?.type)?._id,
|
||||
config: notification?.config,
|
||||
};
|
||||
|
||||
setNotification(notificationData);
|
||||
} catch (error) {
|
||||
setError(error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [id, setNotification]);
|
||||
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
getNotificationById();
|
||||
}
|
||||
}, [getNotificationById, id]);
|
||||
|
||||
return [isLoading, error];
|
||||
};
|
||||
|
||||
const useEditNotification = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const editNotification = async (id, notification) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await networkService.editNotification({ id, notification });
|
||||
createToast({
|
||||
body: t("notifications.edit.success"),
|
||||
});
|
||||
} catch (error) {
|
||||
setError(error);
|
||||
createToast({
|
||||
body: t("notifications.edit.failed"),
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return [editNotification, isLoading, error];
|
||||
};
|
||||
|
||||
export {
|
||||
useCreateNotification,
|
||||
useGetNotificationsByTeamId,
|
||||
useDeleteNotification,
|
||||
useGetNotificationById,
|
||||
useEditNotification,
|
||||
};
|
||||
|
||||
@@ -42,8 +42,8 @@ const ForgotPasswordLabel = ({ email, errorEmail }) => {
|
||||
};
|
||||
|
||||
ForgotPasswordLabel.propTypes = {
|
||||
email: PropTypes.string.isRequired,
|
||||
errorEmail: PropTypes.string.isRequired,
|
||||
email: PropTypes.string,
|
||||
errorEmail: PropTypes.string,
|
||||
};
|
||||
|
||||
export default ForgotPasswordLabel;
|
||||
|
||||
@@ -3,9 +3,7 @@ import { networkService } from "../../../../main";
|
||||
|
||||
const useHardwareMonitorsFetch = ({ monitorId, dateRange }) => {
|
||||
// Abort early if creating monitor
|
||||
if (!monitorId) {
|
||||
return { monitor: undefined, isLoading: false, networkError: undefined };
|
||||
}
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [networkError, setNetworkError] = useState(false);
|
||||
const [monitor, setMonitor] = useState(undefined);
|
||||
@@ -13,6 +11,9 @@ const useHardwareMonitorsFetch = ({ monitorId, dateRange }) => {
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
if (!monitorId) {
|
||||
return { monitor: undefined, isLoading: false, networkError: undefined };
|
||||
}
|
||||
const response = await networkService.getHardwareDetailsByMonitorId({
|
||||
monitorId: monitorId,
|
||||
dateRange: dateRange,
|
||||
|
||||
70
client/src/Pages/Notifications/components/ActionMenu.jsx
Normal file
70
client/src/Pages/Notifications/components/ActionMenu.jsx
Normal file
@@ -0,0 +1,70 @@
|
||||
// Components
|
||||
import Menu from "@mui/material/Menu";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import SettingsOutlinedIcon from "@mui/icons-material/SettingsOutlined";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
|
||||
// Utils
|
||||
import { useState } from "react";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const ActionMenu = ({ notification, onDelete }) => {
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const open = Boolean(anchorEl);
|
||||
|
||||
// Handlers
|
||||
const handleClick = (event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const handleRemove = () => {
|
||||
onDelete(notification._id);
|
||||
handleClose();
|
||||
};
|
||||
|
||||
const handleConfigure = () => {
|
||||
navigate(`/notifications/${notification._id}`);
|
||||
handleClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton
|
||||
aria-label="monitor actions"
|
||||
onClick={handleClick}
|
||||
>
|
||||
<SettingsOutlinedIcon />
|
||||
</IconButton>
|
||||
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
|
||||
>
|
||||
<MenuItem onClick={handleConfigure}>Configure</MenuItem>
|
||||
<MenuItem
|
||||
onClick={handleRemove}
|
||||
sx={{ "&.MuiButtonBase-root": { color: theme.palette.error.main } }}
|
||||
>
|
||||
Remove
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ActionMenu.propTypes = {
|
||||
notification: PropTypes.object,
|
||||
onDelete: PropTypes.func,
|
||||
};
|
||||
|
||||
export default ActionMenu;
|
||||
@@ -12,7 +12,11 @@ import TextInput from "../../../Components/Inputs/TextInput";
|
||||
import { useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useCreateNotification } from "../../../Hooks/useNotifications";
|
||||
import {
|
||||
useCreateNotification,
|
||||
useGetNotificationById,
|
||||
useEditNotification,
|
||||
} from "../../../Hooks/useNotifications";
|
||||
import {
|
||||
notificationEmailValidation,
|
||||
notificationWebhookValidation,
|
||||
@@ -20,19 +24,17 @@ import {
|
||||
} from "../../../Validation/validation";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { NOTIFICATION_TYPES } from "../utils";
|
||||
|
||||
// Setup
|
||||
|
||||
const NOTIFICATION_TYPES = [
|
||||
{ _id: 1, name: "E-mail", value: "email" },
|
||||
{ _id: 2, name: "Slack", value: "webhook" },
|
||||
{ _id: 3, name: "PagerDuty", value: "pager_duty" },
|
||||
{ _id: 4, name: "Webhook", value: "webhook" },
|
||||
];
|
||||
|
||||
const CreateNotifications = () => {
|
||||
const { notificationId } = useParams();
|
||||
const theme = useTheme();
|
||||
const [createNotification, isLoading, error] = useCreateNotification();
|
||||
const [createNotification, isCreating, createNotificationError] =
|
||||
useCreateNotification();
|
||||
const [editNotification, isEditing, editNotificationError] = useEditNotification();
|
||||
const BREADCRUMBS = [
|
||||
{ name: "notifications", path: "/notifications" },
|
||||
{ name: "create", path: "/notifications/create" },
|
||||
@@ -57,6 +59,11 @@ const CreateNotifications = () => {
|
||||
const [errors, setErrors] = useState({});
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [notificationIsLoading, getNotificationError] = useGetNotificationById(
|
||||
notificationId,
|
||||
setNotification
|
||||
);
|
||||
|
||||
// handlers
|
||||
const onSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
@@ -106,7 +113,11 @@ const CreateNotifications = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
createNotification(form);
|
||||
if (notificationId) {
|
||||
editNotification(notificationId, form);
|
||||
} else {
|
||||
createNotification(form);
|
||||
}
|
||||
};
|
||||
|
||||
const onChange = (e) => {
|
||||
@@ -348,7 +359,7 @@ const CreateNotifications = () => {
|
||||
justifyContent="flex-end"
|
||||
>
|
||||
<Button
|
||||
loading={isLoading}
|
||||
loading={isCreating || isEditing || notificationIsLoading}
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="accent"
|
||||
|
||||
@@ -5,6 +5,8 @@ import Breadcrumbs from "../../Components/Breadcrumbs";
|
||||
import Button from "@mui/material/Button";
|
||||
import DataTable from "../../Components/Table";
|
||||
import Fallback from "../../Components/Fallback";
|
||||
import ActionMenu from "./components/ActionMenu";
|
||||
|
||||
// Utils
|
||||
import { useIsAdmin } from "../../Hooks/useIsAdmin";
|
||||
import { useState } from "react";
|
||||
@@ -15,7 +17,7 @@ import {
|
||||
useDeleteNotification,
|
||||
} from "../../Hooks/useNotifications";
|
||||
import { useTranslation } from "react-i18next";
|
||||
// Setup
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
const Notifications = () => {
|
||||
const navigate = useNavigate();
|
||||
@@ -70,13 +72,10 @@ const Notifications = () => {
|
||||
content: "Actions",
|
||||
render: (row) => {
|
||||
return (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
onClick={() => onDelete(row._id)}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
<ActionMenu
|
||||
notification={row}
|
||||
onDelete={onDelete}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
6
client/src/Pages/Notifications/utils.js
Normal file
6
client/src/Pages/Notifications/utils.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export const NOTIFICATION_TYPES = [
|
||||
{ _id: 1, name: "E-mail", value: "email" },
|
||||
{ _id: 2, name: "Slack", value: "webhook" },
|
||||
{ _id: 3, name: "PagerDuty", value: "pager_duty" },
|
||||
{ _id: 4, name: "Webhook", value: "webhook" },
|
||||
];
|
||||
@@ -157,6 +157,12 @@ const Routes = () => {
|
||||
path="notifications/create"
|
||||
element={<CreateNotifications />}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path="notifications/:notificationId"
|
||||
element={<CreateNotifications />}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path="maintenance"
|
||||
element={<Maintenance />}
|
||||
|
||||
@@ -1049,6 +1049,16 @@ class NetworkService {
|
||||
const { id } = config;
|
||||
return this.axiosInstance.delete(`/notifications/${id}`);
|
||||
}
|
||||
|
||||
async getNotificationById(config) {
|
||||
const { id } = config;
|
||||
return this.axiosInstance.get(`/notifications/${id}`);
|
||||
}
|
||||
|
||||
async editNotification(config) {
|
||||
const { id, notification } = config;
|
||||
return this.axiosInstance.put(`/notifications/${id}`, notification);
|
||||
}
|
||||
}
|
||||
|
||||
export default NetworkService;
|
||||
|
||||
@@ -293,6 +293,7 @@
|
||||
"webhookPlaceholder": "https://your-server.com/webhook"
|
||||
}
|
||||
},
|
||||
|
||||
"notificationConfig": {
|
||||
"title": "Notifications",
|
||||
"description": "Select the notifications channels you want to use"
|
||||
@@ -354,6 +355,10 @@
|
||||
"delete": {
|
||||
"success": "Notification deleted successfully",
|
||||
"failed": "Failed to delete notification"
|
||||
},
|
||||
"edit": {
|
||||
"success": "Notification updated successfully",
|
||||
"failed": "Failed to update notification"
|
||||
}
|
||||
},
|
||||
"testLocale": "testLocale",
|
||||
|
||||
@@ -217,6 +217,39 @@ class NotificationController {
|
||||
next(handleError(error, SERVICE_NAME, "deleteNotification"));
|
||||
}
|
||||
};
|
||||
|
||||
getNotificationById = async (req, res, next) => {
|
||||
try {
|
||||
const notification = await this.db.getNotificationById(req.params.id);
|
||||
return res.success({
|
||||
msg: "Notification fetched successfully",
|
||||
data: notification,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getNotificationById"));
|
||||
}
|
||||
};
|
||||
|
||||
editNotification = async (req, res, next) => {
|
||||
try {
|
||||
await createNotificationBodyValidation.validateAsync(req.body, {
|
||||
abortEarly: false,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const notification = await this.db.editNotification(req.params.id, req.body);
|
||||
return res.success({
|
||||
msg: "Notification updated successfully",
|
||||
data: notification,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "editNotification"));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default NotificationController;
|
||||
|
||||
@@ -84,6 +84,30 @@ const deleteNotificationById = async (id) => {
|
||||
}
|
||||
};
|
||||
|
||||
const getNotificationById = async (id) => {
|
||||
try {
|
||||
const notification = await Notification.findById(id);
|
||||
return notification;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getNotificationById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const editNotification = async (id, notificationData) => {
|
||||
try {
|
||||
const notification = await Notification.findByIdAndUpdate(id, notificationData, {
|
||||
new: true,
|
||||
});
|
||||
return notification;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "editNotification";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
createNotification,
|
||||
getNotificationsByTeamId,
|
||||
@@ -91,4 +115,6 @@ export {
|
||||
getNotificationsByMonitorId,
|
||||
deleteNotificationsByMonitorId,
|
||||
deleteNotificationById,
|
||||
getNotificationById,
|
||||
editNotification,
|
||||
};
|
||||
|
||||
@@ -23,6 +23,9 @@ class NotificationRoutes {
|
||||
);
|
||||
|
||||
this.router.delete("/:id", this.notificationController.deleteNotification);
|
||||
|
||||
this.router.get("/:id", this.notificationController.getNotificationById);
|
||||
this.router.put("/:id", this.notificationController.editNotification);
|
||||
}
|
||||
|
||||
getRouter() {
|
||||
|
||||
Reference in New Issue
Block a user