From 5b219068942a161be9feaf646954190710255a7d Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 9 Jun 2025 12:57:30 +0800 Subject: [PATCH] add notificaion pages --- .../src/Pages/Notifications/create/index.jsx | 324 ++++++++++++++++++ client/src/Pages/Notifications/index.jsx | 121 +++++++ 2 files changed, 445 insertions(+) create mode 100644 client/src/Pages/Notifications/create/index.jsx create mode 100644 client/src/Pages/Notifications/index.jsx diff --git a/client/src/Pages/Notifications/create/index.jsx b/client/src/Pages/Notifications/create/index.jsx new file mode 100644 index 000000000..c188aca22 --- /dev/null +++ b/client/src/Pages/Notifications/create/index.jsx @@ -0,0 +1,324 @@ +// Components +import Stack from "@mui/material/Stack"; +import Typography from "@mui/material/Typography"; +import Breadcrumbs from "../../../Components/Breadcrumbs"; +import Button from "@mui/material/Button"; +import ConfigBox from "../../../Components/ConfigBox"; +import Box from "@mui/material/Box"; +import Select from "../../../Components/Inputs/Select"; +import TextInput from "../../../Components/Inputs/TextInput"; + +// Utils +import { useState } from "react"; +import { useSelector } from "react-redux"; +import { useTheme } from "@emotion/react"; +import { useCreateNotification } from "../../../Hooks/useNotifications"; +import { + notificationEmailValidation, + notificationWebhookValidation, + notificationPagerDutyValidation, +} from "../../../Validation/validation"; +import { createToast } from "../../../Utils/toastUtils"; +import { useTranslation } from "react-i18next"; + +// Setup + +const NOTIFICATION_TYPES = [ + { _id: 1, name: "E-mail", value: "email" }, + { _id: 2, name: "Slack", value: "webhook" }, + { _id: 3, name: "PagerDuty", value: "pager_duty" }, +]; + +const CreateNotifications = () => { + const theme = useTheme(); + const [createNotification, isLoading, error] = useCreateNotification(); + const BREADCRUMBS = [ + { name: "notifications", path: "/notifications" }, + { name: "create", path: "/notifications/create" }, + ]; + + // Redux state + const { user } = useSelector((state) => state.auth); + + // local state + const [notification, setNotification] = useState({ + userId: user._id, + teamId: user.teamId, + notificationName: "", + address: "", + type: NOTIFICATION_TYPES[0]._id, + config: { + webhookUrl: "", + platform: "", + routingKey: "", + }, + }); + const [errors, setErrors] = useState({}); + const { t } = useTranslation(); + + // handlers + const onSubmit = (e) => { + e.preventDefault(); + const form = { + ...notification, + type: NOTIFICATION_TYPES.find((type) => type._id === notification.type).value, + }; + + if (notification.type === 2) { + form.type = "webhook"; + } + + let error = null; + + if (form.type === "email") { + error = notificationEmailValidation.validate( + { notificationName: form.notificationName, address: form.address }, + { abortEarly: false } + ).error; + } else if (form.type === "webhook") { + form.config = { + platform: form.config.platform, + webhookUrl: form.config.webhookUrl, + }; + error = notificationWebhookValidation.validate( + { notificationName: form.notificationName, config: form.config }, + { abortEarly: false } + ).error; + } else if (form.type === "pager_duty") { + form.config = { + platform: form.config.platform, + routingKey: form.config.routingKey, + }; + error = notificationPagerDutyValidation.validate( + { notificationName: form.notificationName, config: form.config }, + { abortEarly: false } + ).error; + } + + if (error) { + const newErrors = {}; + error.details.forEach((err) => { + newErrors[err.path[0]] = err.message; + }); + createToast({ body: "Please check the form for errors." }); + setErrors(newErrors); + return; + } + + createNotification(form); + }; + + const onChange = (e) => { + const { name, value } = e.target; + + const newNotification = { ...notification, [name]: value }; + + // Handle config/platform initialization if type is webhook + + if (newNotification.type === 1) { + newNotification.config = null; + } else if (newNotification.type === 2) { + newNotification.address = ""; + newNotification.config = newNotification.config || {}; + if (name === "config") { + newNotification.config = value; + } + newNotification.config.platform = "slack"; + } else if (newNotification.type === 3) { + newNotification.config = newNotification.config || {}; + if (name === "config") { + newNotification.config = value; + } + newNotification.config.platform = "pager_duty"; + } + + // Field-level validation + let fieldError; + + if (name === "notificationName") { + const { error } = notificationEmailValidation.extract(name).validate(value); + fieldError = error?.message; + } + + if (newNotification.type === 1 && name === "address") { + const { error } = notificationEmailValidation.extract(name).validate(value); + fieldError = error?.message; + } + + if (newNotification.type === 2 && name === "config") { + // Validate only webhookUrl inside config + const { error } = notificationWebhookValidation.extract("config").validate(value); + fieldError = error?.message; + } + + // Set field-level error + setErrors((prev) => ({ + ...prev, + [name]: fieldError, + })); + + setNotification(newNotification); + }; + + return ( + + + {t("createNotifications.title")} + + + + + {t("createNotifications.nameSettings.title")} + + + {t("createNotifications.nameSettings.description")} + + + + + + + + + + {t("createNotifications.typeSettings.title")} + + + {t("createNotifications.typeSettings.description")} + + + +