diff --git a/client/src/Hooks/useNotifications.js b/client/src/Hooks/useNotifications.js
index 4ece55acd..c8dc6b3db 100644
--- a/client/src/Hooks/useNotifications.js
+++ b/client/src/Hooks/useNotifications.js
@@ -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,
+};
diff --git a/client/src/Pages/Auth/Login/Components/ForgotPasswordLabel.jsx b/client/src/Pages/Auth/Login/Components/ForgotPasswordLabel.jsx
index 5fc2430c7..fe8c688ac 100644
--- a/client/src/Pages/Auth/Login/Components/ForgotPasswordLabel.jsx
+++ b/client/src/Pages/Auth/Login/Components/ForgotPasswordLabel.jsx
@@ -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;
diff --git a/client/src/Pages/Infrastructure/Details/Hooks/useHardwareMonitorsFetch.jsx b/client/src/Pages/Infrastructure/Details/Hooks/useHardwareMonitorsFetch.jsx
index f7cb2abb1..c0b61ef62 100644
--- a/client/src/Pages/Infrastructure/Details/Hooks/useHardwareMonitorsFetch.jsx
+++ b/client/src/Pages/Infrastructure/Details/Hooks/useHardwareMonitorsFetch.jsx
@@ -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,
diff --git a/client/src/Pages/Notifications/components/ActionMenu.jsx b/client/src/Pages/Notifications/components/ActionMenu.jsx
new file mode 100644
index 000000000..bd719bfee
--- /dev/null
+++ b/client/src/Pages/Notifications/components/ActionMenu.jsx
@@ -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 (
+ <>
+
+
+
+
+
+ >
+ );
+};
+
+ActionMenu.propTypes = {
+ notification: PropTypes.object,
+ onDelete: PropTypes.func,
+};
+
+export default ActionMenu;
diff --git a/client/src/Pages/Notifications/create/index.jsx b/client/src/Pages/Notifications/create/index.jsx
index b1469bff0..b123dcbd7 100644
--- a/client/src/Pages/Notifications/create/index.jsx
+++ b/client/src/Pages/Notifications/create/index.jsx
@@ -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"
>