diff --git a/Client/src/Features/InfrastructureMonitors /infrastructureMonitorsSlice.js b/Client/src/Features/InfrastructureMonitors/infrastructureMonitorsSlice.js similarity index 96% rename from Client/src/Features/InfrastructureMonitors /infrastructureMonitorsSlice.js rename to Client/src/Features/InfrastructureMonitors/infrastructureMonitorsSlice.js index abcb31adf..24b3500ac 100644 --- a/Client/src/Features/InfrastructureMonitors /infrastructureMonitorsSlice.js +++ b/Client/src/Features/InfrastructureMonitors/infrastructureMonitorsSlice.js @@ -277,15 +277,15 @@ const infrastructureMonitorsSlice = createSlice({ // ***************************************************** // Resolve Endpoint // ***************************************************** - .addCase(checkEndpointResolution.pending, (state) => { + .addCase(checkInfrastructureEndpointResolution.pending, (state) => { state.isLoading = true; }) - .addCase(checkEndpointResolution.fulfilled, (state, action) => { + .addCase(checkInfrastructureEndpointResolution.fulfilled, (state, action) => { state.isLoading = false; state.success = action.payload.success; state.msg = action.payload.msg; }) - .addCase(checkEndpointResolution.rejected, (state, action) => { + .addCase(checkInfrastructureEndpointResolution.rejected, (state, action) => { state.isLoading = false; state.success = false; state.msg = action.payload @@ -348,15 +348,15 @@ const infrastructureMonitorsSlice = createSlice({ // ***************************************************** // Delete Monitor checks by Team ID // ***************************************************** - .addCase(deleteMonitorChecksByTeamId.pending, (state) => { + .addCase(deleteInfrastructureMonitorChecksByTeamId.pending, (state) => { state.isLoading = true; }) - .addCase(deleteMonitorChecksByTeamId.fulfilled, (state, action) => { + .addCase(deleteInfrastructureMonitorChecksByTeamId.fulfilled, (state, action) => { state.isLoading = false; state.success = action.payload.success; state.msg = action.payload.msg; }) - .addCase(deleteMonitorChecksByTeamId.rejected, (state, action) => { + .addCase(deleteInfrastructureMonitorChecksByTeamId.rejected, (state, action) => { state.isLoading = false; state.success = false; state.msg = action.payload diff --git a/Client/src/Pages/InfrastructureMonitors/CreateMonitor/index.jsx b/Client/src/Pages/InfrastructureMonitors/CreateMonitor/index.jsx new file mode 100644 index 000000000..87c4c537c --- /dev/null +++ b/Client/src/Pages/InfrastructureMonitors/CreateMonitor/index.jsx @@ -0,0 +1,378 @@ +import { useState } from "react"; +import { Box, Button, ButtonGroup, Stack, Typography } from "@mui/material"; +import LoadingButton from "@mui/lab/LoadingButton"; +import { useSelector, useDispatch } from "react-redux"; +import { monitorValidation } from "../../../Validation/validation"; +import { createUptimeMonitor } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice"; +import { checkEndpointResolution } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice"; +import { useNavigate } from "react-router-dom"; +import { useTheme } from "@emotion/react"; +import { createToast } from "../../../Utils/toastUtils"; +import { logger } from "../../../Utils/Logger"; +import { ConfigBox } from "../../Monitors/styled"; +import Radio from "../../../Components/Inputs/Radio"; +import Field from "../../../Components/Inputs/Field"; +import Select from "../../../Components/Inputs/Select"; +import Checkbox from "../../../Components/Inputs/Checkbox"; +import Breadcrumbs from "../../../Components/Breadcrumbs"; + +const CreateInfrastructureMonitor = () => { + const MS_PER_MINUTE = 60000; + const { user, authToken } = useSelector((state) => state.auth); + const monitorState = useSelector((state) => state.infrastructureMonitor); + const dispatch = useDispatch(); + const navigate = useNavigate(); + const theme = useTheme(); + + const idMap = { + "monitor-url": "url", + "monitor-name": "name", + "monitor-checks-http": "type", + "monitor-checks-ping": "type", + "notify-email-default": "notification-email", + }; + + const [infrastructureMonitor, setInfrastructureMonitor] = useState({ + url: "", + name: "", + type: "http", + notifications: [], + interval: 1, + }); + const [https, setHttps] = useState(true); + const [errors, setErrors] = useState({}); + + const handleChange = (event, name) => { + const { value, id } = event.target; + if (!name) name = idMap[id]; + + if (name.includes("notification-")) { + name = name.replace("notification-", ""); + let hasNotif = infrastructureMonitor.notifications.some( + (notification) => notification.type === name + ); + setInfrastructureMonitor((prev) => { + const notifs = [...prev.notifications]; + if (hasNotif) { + return { + ...prev, + notifications: notifs.filter((notif) => notif.type !== name), + }; + } else { + return { + ...prev, + notifications: [ + ...notifs, + name === "email" + ? { type: name, address: value } + : // TODO - phone number + { type: name, phone: value }, + ], + }; + } + }); + } else { + setInfrastructureMonitor((prev) => ({ + ...prev, + [name]: value, + })); + + const { error } = monitorValidation.validate( + { [name]: value }, + { abortEarly: false } + ); + console.log(error); + setErrors((prev) => { + const updatedErrors = { ...prev }; + if (error) updatedErrors[name] = error.details[0].message; + else delete updatedErrors[name]; + return updatedErrors; + }); + } + }; + + const handleCreateInfrastructureMonitor = async (event) => { + event.preventDefault(); + //obj to submit + let form = { + url: + //preprending protocol for url + infrastructureMonitor.type === "http" + ? `http${https ? "s" : ""}://` + infrastructureMonitor.url + : infrastructureMonitor.url, + name: + infrastructureMonitor.name === "" + ? infrastructureMonitor.url + : infrastructureMonitor.name, + type: infrastructureMonitor.type, + interval: infrastructureMonitor.interval * MS_PER_MINUTE, + }; + + const { error } = monitorValidation.validate(form, { + abortEarly: false, + }); + + if (error) { + const newErrors = {}; + error.details.forEach((err) => { + newErrors[err.path[0]] = err.message; + }); + setErrors(newErrors); + createToast({ body: "Error validation data." }); + } else { + if (infrastructureMonitor.type === "http") { + const checkEndpointAction = await dispatch( + checkEndpointResolution({ authToken, monitorURL: form.url }) + ); + if (checkEndpointAction.meta.requestStatus === "rejected") { + createToast({ + body: "The endpoint you entered doesn't resolve. Check the URL again.", + }); + setErrors({ url: "The entered URL is not reachable." }); + return; + } + } + + form = { + ...form, + description: form.name, + teamId: user.teamId, + userId: user._id, + notifications: infrastructureMonitor.notifications, + }; + const action = await dispatch(createUptimeMonitor({ authToken, monitor: form })); + if (action.meta.requestStatus === "fulfilled") { + createToast({ body: "Monitor created successfully!" }); + navigate("/monitors"); + } else { + createToast({ body: "Failed to create monitor." }); + } + } + }; + + //select values + const frequencies = [ + { _id: 1, name: "1 minute" }, + { _id: 2, name: "2 minutes" }, + { _id: 3, name: "3 minutes" }, + { _id: 4, name: "4 minutes" }, + { _id: 5, name: "5 minutes" }, + ]; + + return ( + + + + + + Create your{" "} + + + infrastructure monitor + + + + + General settings + + Here you can select the URL of the host, together with the type of monitor. + + + + + + + + + + Checks to perform + + You can always add or remove checks after adding your site. + + + + + handleChange(event)} + /> + {infrastructureMonitor.type === "http" ? ( + + + + + ) : ( + "" + )} + + handleChange(event)} + /> + {errors["type"] ? ( + + + {errors["type"]} + + + ) : ( + "" + )} + + + + + Incident notifications + + When there is an incident, notify users. + + + + When there is a new incident, + logger.warn("disabled")} + isDisabled={true} + /> + notification.type === "email" + )} + value={user?.email} + onChange={(event) => handleChange(event)} + /> + logger.warn("disabled")} + isDisabled={true} + /> + {infrastructureMonitor.notifications.some( + (notification) => notification.type === "emails" + ) ? ( + + logger.warn("disabled")} + /> + + You can separate multiple emails with a comma + + + ) : ( + "" + )} + + + + + Advanced settings + + +