// React, Redux, Router import { useTheme } from "@emotion/react"; import { useParams } from "react-router-dom"; import { useState, useEffect } from "react"; import { useSelector } from "react-redux"; // Utility and Network import { infrastructureMonitorValidation } from "../../../Validation/validation"; import { useFetchHardwareMonitorById } from "../../../Hooks/monitorHooks"; import { capitalizeFirstLetter } from "../../../Utils/stringUtils"; import { useTranslation } from "react-i18next"; import { useGetNotificationsByTeamId } from "../../../Hooks/useNotifications"; import NotificationsConfig from "../../../Components/NotificationConfig"; import { useUpdateMonitor, useCreateMonitor } from "../../../Hooks/monitorHooks"; // MUI import { Box, Stack, Typography, Button, ButtonGroup } from "@mui/material"; //Components import Breadcrumbs from "../../../Components/Breadcrumbs"; import Link from "../../../Components/Link"; import ConfigBox from "../../../Components/ConfigBox"; import TextInput from "../../../Components/Inputs/TextInput"; import { HttpAdornment } from "../../../Components/Inputs/TextInput/Adornments"; import { createToast } from "../../../Utils/toastUtils"; import Select from "../../../Components/Inputs/Select"; import { CustomThreshold } from "./Components/CustomThreshold"; const SELECT_VALUES = [ { _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" }, ]; const METRICS = ["cpu", "memory", "disk", "temperature"]; const METRIC_PREFIX = "usage_"; const MS_PER_MINUTE = 60000; const hasAlertError = (errors) => { return Object.keys(errors).filter((k) => k.startsWith(METRIC_PREFIX)).length > 0; }; const getAlertError = (errors) => { return Object.keys(errors).find((key) => key.startsWith(METRIC_PREFIX)) ? errors[Object.keys(errors).find((key) => key.startsWith(METRIC_PREFIX))] : null; }; const CreateInfrastructureMonitor = () => { const theme = useTheme(); const { user } = useSelector((state) => state.auth); const { monitorId } = useParams(); const { t } = useTranslation(); // Determine if we are creating or editing const isCreate = typeof monitorId === "undefined"; // Fetch monitor details if editing const [monitor, isLoading, networkError] = useFetchHardwareMonitorById({ monitorId }); const [notifications, notificationsAreLoading, notificationsError] = useGetNotificationsByTeamId(); const [updateMonitor, isUpdating] = useUpdateMonitor(); const [createMonitor, isCreating] = useCreateMonitor(); // State const [errors, setErrors] = useState({}); const [https, setHttps] = useState(false); const [infrastructureMonitor, setInfrastructureMonitor] = useState({ url: "", name: "", notifications: [], notify_email: false, interval: 0.25, cpu: false, usage_cpu: "", memory: false, usage_memory: "", disk: false, usage_disk: "", temperature: false, usage_temperature: "", secret: "", }); // Populate form fields if editing useEffect(() => { if (isCreate || !monitor) return; setInfrastructureMonitor({ url: monitor.url.replace(/^https?:\/\//, ""), name: monitor.name || "", notifications: monitor.notifications, interval: monitor.interval / MS_PER_MINUTE, cpu: monitor.thresholds?.usage_cpu !== undefined, usage_cpu: monitor.thresholds?.usage_cpu ? monitor.thresholds.usage_cpu * 100 : "", memory: monitor.thresholds?.usage_memory !== undefined, usage_memory: monitor.thresholds?.usage_memory ? monitor.thresholds.usage_memory * 100 : "", disk: monitor.thresholds?.usage_disk !== undefined, usage_disk: monitor.thresholds?.usage_disk ? monitor.thresholds.usage_disk * 100 : "", temperature: monitor.thresholds?.usage_temperature !== undefined, usage_temperature: monitor.thresholds?.usage_temperature ? monitor.thresholds.usage_temperature * 100 : "", secret: monitor.secret || "", }); setHttps(monitor.url.startsWith("https")); }, [isCreate, monitor]); // Handlers const onSubmit = async (event) => { event.preventDefault(); // Build the form let form = { url: `http${https ? "s" : ""}://` + infrastructureMonitor.url, name: infrastructureMonitor.name === "" ? infrastructureMonitor.url : infrastructureMonitor.name, interval: infrastructureMonitor.interval * MS_PER_MINUTE, cpu: infrastructureMonitor.cpu, ...(infrastructureMonitor.cpu ? { usage_cpu: infrastructureMonitor.usage_cpu } : {}), memory: infrastructureMonitor.memory, ...(infrastructureMonitor.memory ? { usage_memory: infrastructureMonitor.usage_memory } : {}), disk: infrastructureMonitor.disk, ...(infrastructureMonitor.disk ? { usage_disk: infrastructureMonitor.usage_disk } : {}), temperature: infrastructureMonitor.temperature, ...(infrastructureMonitor.temperature ? { usage_temperature: infrastructureMonitor.usage_temperature } : {}), secret: infrastructureMonitor.secret, }; const { error } = infrastructureMonitorValidation.validate(form, { abortEarly: false, }); if (error) { const newErrors = {}; error.details.forEach((err) => { newErrors[err.path[0]] = err.message; }); console.log(newErrors); setErrors(newErrors); createToast({ body: "Please check the form for errors." }); return; } // Build the thresholds for the form const { cpu, usage_cpu, memory, usage_memory, disk, usage_disk, temperature, usage_temperature, ...rest } = form; const thresholds = { ...(cpu ? { usage_cpu: usage_cpu / 100 } : {}), ...(memory ? { usage_memory: usage_memory / 100 } : {}), ...(disk ? { usage_disk: usage_disk / 100 } : {}), ...(temperature ? { usage_temperature: usage_temperature / 100 } : {}), }; form = { ...(isCreate ? {} : { _id: monitorId }), ...rest, description: form.name, type: "hardware", notifications: infrastructureMonitor.notifications, thresholds, }; // Handle create or update isCreate ? await createMonitor({ monitor: form, redirect: "/infrastructure" }) : await updateMonitor({ monitor: form, redirect: "/infrastructure" }); }; const onChange = (event) => { const { value, name } = event.target; setInfrastructureMonitor({ ...infrastructureMonitor, [name]: value, }); const { error } = infrastructureMonitorValidation.validate( { [name]: value }, { abortEarly: false } ); setErrors((prev) => ({ ...prev, ...(error ? { [name]: error.details[0].message } : { [name]: undefined }), })); }; const handleCheckboxChange = (event) => { const { name } = event.target; const { checked } = event.target; setInfrastructureMonitor({ ...infrastructureMonitor, [name]: checked, }); }; return ( {t(isCreate ? "infrastructureCreateYour" : "infrastructureEditYour")}{" "} {t("monitor")} {t("settingsGeneralSettings")} {t("infrastructureCreateGeneralSettingsDescription")} {t("infrastructureServerRequirement")}{" "} } placeholder={"localhost:59232/api/v1/metrics"} label={t("infrastructureServerUrlLabel")} https={https} value={infrastructureMonitor.url} onChange={onChange} error={errors["url"] ? true : false} helperText={errors["url"]} disabled={!isCreate} /> {isCreate && ( {t("infrastructureProtocol")} )} {t("notificationConfig.title")} {t("notificationConfig.description")} {t("infrastructureCustomizeAlerts")} {t("infrastructureAlertNotificationDescription")} {METRICS.map((metric) => { return ( ); })} {/* Error text */} {hasAlertError(errors) && ( {getAlertError(errors)} )} {t("distributedUptimeCreateAdvancedSettings")}