import { useState } from "react"; import { Box, Stack, Typography } from "@mui/material"; import LoadingButton from "@mui/lab/LoadingButton"; import { useSelector, useDispatch } from "react-redux"; import { infrastructureMonitorValidation } from "../../../Validation/validation"; import { createInfrastructureMonitor, checkInfrastructureEndpointResolution, } from "../../../Features/InfrastructureMonitors/infrastructureMonitorsSlice"; 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 Field from "../../../Components/Inputs/Field"; import Select from "../../../Components/Inputs/Select"; import Checkbox from "../../../Components/Inputs/Checkbox"; import Breadcrumbs from "../../../Components/Breadcrumbs"; import { buildErrors, hasValidationErrors } from "../../../Validation/error"; import { capitalizeFirstLetter } from "../../../Utils/stringUtils"; import { CustomThreshold } from "../CreateMonitor/CustomThreshold"; const CreateInfrastructureMonitor = () => { const [infrastructureMonitor, setInfrastructureMonitor] = useState({ url: "", name: "", notifications: [], interval: 0.25, cpu: false, usage_cpu: "", memory: false, usage_memory: "", disk: false, usage_disk: "", secret: "", }); const MS_PER_MINUTE = 60000; const THRESHOLD_FIELD_PREFIX = "usage_"; const HARDWARE_MONITOR_TYPES = ["cpu", "memory", "disk"]; const { user, authToken } = useSelector((state) => state.auth); const monitorState = useSelector((state) => state.infrastructureMonitor); const dispatch = useDispatch(); const navigate = useNavigate(); const theme = useTheme(); const idMap = { "notify-email-default": "notification-email", }; const [errors, setErrors] = useState({}); const alertErrKeyLen = Object.keys(errors).filter((k) => k.startsWith(THRESHOLD_FIELD_PREFIX) ).length; const handleCustomAlertCheckChange = (event) => { const { value, id } = event.target; setInfrastructureMonitor((prev) => { const newState = { [id]: prev[id] == undefined && value == "on" ? true : !prev[id], }; return { ...prev, ...newState, [THRESHOLD_FIELD_PREFIX + id]: newState[id] ? prev[THRESHOLD_FIELD_PREFIX + id] : "", }; }); // Remove the error if unchecked setErrors((prev) => { return buildErrors(prev, [THRESHOLD_FIELD_PREFIX + id]); }); }; const handleBlur = (event, appenedID) => { event.preventDefault(); const { value, id } = event.target; if (id?.startsWith("notify-email-")) return; const { error } = infrastructureMonitorValidation.validate( { [id ?? appenedID]: value }, { abortEarly: false, } ); setErrors((prev) => { return buildErrors(prev, id ?? appenedID, error); }); }; const handleChange = (event, appendedId) => { event.preventDefault(); const { value, id } = event.target; let name = appendedId ?? idMap[id] ?? 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 generatePayload = (form) => { let thresholds = {}; Object.keys(form) .filter((k) => k.startsWith(THRESHOLD_FIELD_PREFIX)) .map((k) => { if (form[k]) thresholds[k] = form[k] / 100; delete form[k]; delete form[k.substring(THRESHOLD_FIELD_PREFIX.length)]; }); form = { ...form, description: form.name, teamId: user.teamId, userId: user._id, type: "hardware", notifications: infrastructureMonitor.notifications, thresholds, }; return form; }; const handleCreateInfrastructureMonitor = async (event) => { event.preventDefault(); let form = { ...infrastructureMonitor, name: infrastructureMonitor.name === "" ? infrastructureMonitor.url : infrastructureMonitor.name, interval: infrastructureMonitor.interval * MS_PER_MINUTE, }; delete form.notifications; if (hasValidationErrors(form, infrastructureMonitorValidation, setErrors)) { return; } else { const checkEndpointAction = await dispatch( checkInfrastructureEndpointResolution({ 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; } const action = await dispatch( createInfrastructureMonitor({ authToken, monitor: generatePayload(form) }) ); if (action.meta.requestStatus === "fulfilled") { createToast({ body: "Infrastructure monitor created successfully!" }); navigate("/infrastructure"); } else { createToast({ body: "Failed to create monitor." }); } } }; //select values const frequencies = [ { _id: 0.25, name: "15 seconds" }, { _id: 0.5, name: "30 seconds" }, { _id: 1, name: "1 minute" }, { _id: 2, name: "2 minutes" }, { _id: 5, name: "5 minutes" }, { _id: 10, name: "10 minutes" }, ]; return ( Create your{" "} infrastructure monitor General settings Here you can select the URL of the host, together with the friendly name and authorization secret to connect to the server agent. Incident notifications When there is an incident, notify users. When there is a new incident, notification.type === "email" )} value={user?.email} onChange={(e) => handleChange(e)} onBlur={handleBlur} /> Customize alerts Send a notification to user(s) when thresholds exceed a specified percentage. {HARDWARE_MONITOR_TYPES.map((type, idx) => ( ))} {alertErrKeyLen > 0 && ( { errors[ THRESHOLD_FIELD_PREFIX + HARDWARE_MONITOR_TYPES.filter( (type) => errors[THRESHOLD_FIELD_PREFIX + type] )[0] ] } )} Advanced settings