diff --git a/client/src/Pages/Uptime/Create/index.jsx b/client/src/Pages/Uptime/Create/index.jsx index 6e45230f6..b9e1e8423 100644 --- a/client/src/Pages/Uptime/Create/index.jsx +++ b/client/src/Pages/Uptime/Create/index.jsx @@ -1,15 +1,12 @@ // React, Redux, Router import { useTheme } from "@emotion/react"; -import { useNavigate, useParams } from "react-router-dom"; -import { useEffect } from "react"; +import { useNavigate } from "react-router-dom"; import { useState } from "react"; import { useSelector, useDispatch } from "react-redux"; import { useTranslation } from "react-i18next"; // Utility and Network -import { checkEndpointResolution } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice"; import { monitorValidation } from "../../../Validation/validation"; -import { getUptimeMonitorById } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice"; import { createUptimeMonitor } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice"; // MUI import { Box, Stack, Typography, Button, ButtonGroup } from "@mui/material"; @@ -22,11 +19,37 @@ import TextInput from "../../../Components/Inputs/TextInput"; import { HttpAdornment } from "../../../Components/Inputs/TextInput/Adornments"; import { createToast } from "../../../Utils/toastUtils"; import Radio from "../../../Components/Inputs/Radio"; -import Checkbox from "../../../Components/Inputs/Checkbox"; import Select from "../../../Components/Inputs/Select"; import ConfigBox from "../../../Components/ConfigBox"; -import NotificationIntegrationModal from "../../../Components/NotificationIntegrationModal/Components/NotificationIntegrationModal"; +import { useGetNotificationsByTeamId } from "../../../Hooks/useNotifications"; +import NotificationsConfig from "../../../Components/NotificationConfig"; + const CreateMonitor = () => { + // Redux state + const { user } = useSelector((state) => state.auth); + const { isLoading } = useSelector((state) => state.uptimeMonitors); + const dispatch = useDispatch(); + + // Local state + const [errors, setErrors] = useState({}); + const [https, setHttps] = useState(true); + const [useAdvancedMatching, setUseAdvancedMatching] = useState(false); + const [monitor, setMonitor] = useState({ + url: "", + name: "", + type: "http", + matchMethod: "equal", + notifications: [], + interval: 1, + ignoreTlsErrors: false, + }); + + // Setup + const theme = useTheme(); + const { t } = useTranslation(); + const navigate = useNavigate(); + const [notifications, notificationsAreLoading, error] = useGetNotificationsByTeamId(); + const MS_PER_MINUTE = 60000; const SELECT_VALUES = [ { _id: 1, name: "1 minute" }, @@ -71,37 +94,19 @@ const CreateMonitor = () => { }, }; - const { user } = useSelector((state) => state.auth); - const { isLoading } = useSelector((state) => state.uptimeMonitors); - const dispatch = useDispatch(); - const navigate = useNavigate(); - const theme = useTheme(); - const { monitorId } = useParams(); - const crumbs = [ + const BREADCRUMBS = [ { name: "uptime", path: "/uptime" }, { name: "create", path: `/uptime/create` }, ]; - // State - const [isNotificationModalOpen, setIsNotificationModalOpen] = useState(false); + // Handlers - const handleOpenNotificationModal = () => { - setIsNotificationModalOpen(true); - }; - const [errors, setErrors] = useState({}); - const [https, setHttps] = useState(true); - const [monitor, setMonitor] = useState({ - url: "", - name: "", - type: "http", - ignoreTlsErrors: false, - notifications: [], - interval: 1, - }); - - const handleCreateMonitor = async (event) => { + const onSubmit = async (event) => { event.preventDefault(); + const { notifications, ...rest } = monitor; + let form = { + ...rest, url: //prepending protocol for url monitor.type === "http" @@ -110,14 +115,14 @@ const CreateMonitor = () => { port: monitor.type === "port" ? monitor.port : undefined, name: monitor.name || monitor.url.substring(0, 50), type: monitor.type, - ignoreTlsErrors: monitor.ignoreTlsErrors, interval: monitor.interval * MS_PER_MINUTE, }; - if (monitor.type === "http") { - form.expectedValue = monitor.expectedValue; - form.jsonPath = monitor.jsonPath; - form.matchMethod = monitor.matchMethod; + // If not using advanced matching, remove advanced settings + if (!useAdvancedMatching) { + form.matchMethod = undefined; + form.expectedValue = undefined; + form.jsonPath = undefined; } const { error } = monitorValidation.validate(form, { @@ -141,6 +146,7 @@ const CreateMonitor = () => { userId: user._id, notifications: monitor.notifications, }; + const action = await dispatch(createUptimeMonitor({ monitor: form })); if (action.meta.requestStatus === "fulfilled") { createToast({ body: "Monitor created successfully!" }); @@ -150,115 +156,60 @@ const CreateMonitor = () => { } }; - const handleChange = (event, formName) => { - const { type, checked, value } = event.target; - - const newVal = type === "checkbox" ? checked : value; - - const newMonitor = { - ...monitor, - [formName]: newVal, - }; - if (formName === "type") { - newMonitor.url = ""; + const onChange = (event) => { + const { name, value, checked } = event.target; + let newValue = value; + if (name === "ignoreTlsErrors") { + newValue = checked; } - setMonitor(newMonitor); + const updatedMonitor = { + ...monitor, + [name]: newValue, + }; + + setMonitor(updatedMonitor); const { error } = monitorValidation.validate( - { type: monitor.type, [formName]: newVal }, + { type: monitor.type, [name]: newValue }, { abortEarly: false } ); + setErrors((prev) => ({ ...prev, - url: undefined, - ...(error ? { [formName]: error.details[0].message } : { [formName]: undefined }), + ...(error ? { [name]: error.details[0].message } : { [name]: undefined }), })); }; - const handleNotifications = (event, type) => { - const { value } = event.target; - let notifications = [...monitor.notifications]; - const notificationExists = notifications.some((notification) => { - if (notification.type === type && notification.address === value) { - return true; - } - return false; - }); - if (notificationExists) { - notifications = notifications.filter((notification) => { - if (notification.type === type && notification.address === value) { - return false; - } - return true; - }); - } else { - notifications.push({ type, address: value }); - } - - setMonitor((prev) => ({ - ...prev, - notifications, - })); - }; - - useEffect(() => { - const fetchMonitor = async () => { - if (monitorId) { - const action = await dispatch(getUptimeMonitorById({ monitorId })); - - if (action.payload.success) { - const data = action.payload.data; - const { name, ...rest } = data; //data.name is read-only - if (rest.type === "http") { - const url = new URL(rest.url); - rest.url = url.host; - } - rest.name = `${name} (Clone)`; - rest.interval /= MS_PER_MINUTE; - setMonitor({ - ...rest, - }); - } else { - navigate("/not-found", { replace: true }); - createToast({ - body: "There was an error cloning the monitor.", - }); - } - } - }; - fetchMonitor(); - }, [monitorId, dispatch, navigate]); - - const { t } = useTranslation(); - return ( - - - + + + - - {t("createYour")}{" "} - - - {t("monitor")} - + {t("createYour")}{" "} + + {t("monitor")} + + + { handleChange(event, "type")} + onChange={onChange} /> {monitor.type === "http" ? ( @@ -304,31 +255,31 @@ const CreateMonitor = () => { )} handleChange(event, "type")} + onChange={onChange} /> handleChange(event, "type")} + onChange={onChange} /> handleChange(event, "type")} + onChange={onChange} /> {errors["type"] ? ( @@ -359,8 +310,8 @@ const CreateMonitor = () => { : null } @@ -368,29 +319,29 @@ const CreateMonitor = () => { https={https} placeholder={monitorTypeMaps[monitor.type].placeholder || ""} value={monitor.url} - onChange={(event) => handleChange(event, "url")} + onChange={onChange} error={errors["url"] ? true : false} helperText={errors["url"]} /> handleChange(event, "port")} + onChange={onChange} error={errors["port"] ? true : false} helperText={errors["port"]} hidden={monitor.type !== "port"} /> handleChange(event, "name")} + onChange={onChange} error={errors["name"] ? true : false} helperText={errors["name"]} /> @@ -398,37 +349,15 @@ const CreateMonitor = () => { - - {t("distributedUptimeCreateIncidentNotification")} - + Notifications - {t("distributedUptimeCreateIncidentDescription")} + Select the notifications you want to send out - - notification.type === "email" - )} - value={user?.email} - onChange={(event) => handleNotifications(event, "email")} - /> - - - - - + @@ -445,9 +374,9 @@ const CreateMonitor = () => { sx={{ marginLeft: 0 }} control={ handleChange(event, "ignoreTlsErrors")} + onChange={onChange} sx={{ mr: theme.spacing(2) }} /> } @@ -466,32 +395,32 @@ const CreateMonitor = () => { handleChange(event, "matchMethod")} + onChange={onChange} items={matchMethodOptions} /> handleChange(event, "expectedValue")} + onChange={onChange} error={errors["expectedValue"] ? true : false} helperText={errors["expectedValue"]} /> @@ -505,13 +434,13 @@ const CreateMonitor = () => { handleChange(event, "jsonPath")} + onChange={onChange} error={errors["jsonPath"] ? true : false} helperText={errors["jsonPath"]} /> @@ -541,9 +470,9 @@ const CreateMonitor = () => { justifyContent="flex-end" > - - setIsNotificationModalOpen(false)} - monitor={monitor} - setMonitor={setMonitor} - /> - + ); };