import { useNavigate, useParams } from "react-router"; import { useTheme } from "@emotion/react"; import { useDispatch, useSelector } from "react-redux"; import { useEffect, useState } from "react"; import { Box, Button, Modal, Stack, Typography } from "@mui/material"; import { monitorValidation } from "../../../Validation/validation"; import { createToast } from "../../../Utils/toastUtils"; import { logger } from "../../../Utils/Logger"; import { ConfigBox } from "../styled"; import { updateUptimeMonitor, pauseUptimeMonitor, getUptimeMonitorById, getUptimeMonitorsByTeamId, deleteUptimeMonitor, } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice"; import Field from "../../../Components/Inputs/Field"; import PauseCircleOutlineIcon from "@mui/icons-material/PauseCircleOutline"; import PlayCircleOutlineRoundedIcon from "@mui/icons-material/PlayCircleOutlineRounded"; import Select from "../../../Components/Inputs/Select"; import Checkbox from "../../../Components/Inputs/Checkbox"; import Breadcrumbs from "../../../Components/Breadcrumbs"; import PulseDot from "../../../Components/Animated/PulseDot"; import SkeletonLayout from "./skeleton"; import LoadingButton from "@mui/lab/LoadingButton"; import "./index.css"; /** * Parses a URL string and returns a URL object. * * @param {string} url - The URL string to parse. * @returns {URL} - The parsed URL object if valid, otherwise an empty string. */ const parseUrl = (url) => { try { return new URL(url); } catch (error) { return null; } }; /** * Configure page displays monitor configurations and allows for editing actions. * @component */ const Configure = () => { const MS_PER_MINUTE = 60000; const navigate = useNavigate(); const theme = useTheme(); const dispatch = useDispatch(); const { user, authToken } = useSelector((state) => state.auth); const { isLoading } = useSelector((state) => state.uptimeMonitors); const [monitor, setMonitor] = useState({}); const [errors, setErrors] = useState({}); const { monitorId } = useParams(); const idMap = { "monitor-url": "url", "monitor-name": "name", "monitor-checks-http": "type", "monitor-checks-ping": "type", "notify-email-default": "notification-email", }; useEffect(() => { const fetchMonitor = async () => { try { const action = await dispatch( getUptimeMonitorById({ authToken, monitorId }) ); if (getUptimeMonitorById.fulfilled.match(action)) { const monitor = action.payload.data; setMonitor(monitor); } else if (getUptimeMonitorById.rejected.match(action)) { throw new Error(action.error.message); } } catch (error) { logger.error("Error fetching monitor of id: " + monitorId); navigate("/not-found", { replace: true }); } }; fetchMonitor(); }, [monitorId, authToken, navigate]); const handleChange = (event, name) => { let { value, id } = event.target; if (!name) name = idMap[id]; if (name.includes("notification-")) { name = name.replace("notification-", ""); let hasNotif = monitor.notifications.some( (notification) => notification.type === name ); setMonitor((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 { if (name === "interval") { value = value * MS_PER_MINUTE; } setMonitor((prev) => ({ ...prev, [name]: value, })); const validation = monitorValidation.validate( { [name]: value }, { abortEarly: false } ); setErrors((prev) => { const updatedErrors = { ...prev }; if (validation.error) updatedErrors[name] = validation.error.details[0].message; else delete updatedErrors[name]; return updatedErrors; }); } }; const handlePause = async () => { try { const action = await dispatch( pauseUptimeMonitor({ authToken, monitorId }) ); if (pauseUptimeMonitor.fulfilled.match(action)) { const monitor = action.payload.data; setMonitor(monitor); } else if (pauseUptimeMonitor.rejected.match(action)) { throw new Error(action.error.message); } } catch (error) { logger.error("Error pausing monitor: " + monitorId); createToast({ body: "Failed to pause monitor" }); } }; const handleSubmit = async (event) => { event.preventDefault(); const action = await dispatch( updateUptimeMonitor({ authToken, monitor: monitor }) ); if (action.meta.requestStatus === "fulfilled") { createToast({ body: "Monitor updated successfully!" }); dispatch(getUptimeMonitorsByTeamId(authToken)); } else { createToast({ body: "Failed to update monitor." }); } }; const [isOpen, setIsOpen] = useState(false); const handleRemove = async (event) => { event.preventDefault(); const action = await dispatch(deleteUptimeMonitor({ authToken, monitor })); if (action.meta.requestStatus === "fulfilled") { navigate("/monitors"); } else { createToast({ body: "Failed to delete monitor." }); } }; 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" }, ]; // Parse the URL const parsedUrl = parseUrl(monitor?.url); const protocol = parsedUrl?.protocol?.replace(":", "") || ""; const statusColor = { true: theme.palette.success.main, false: theme.palette.error.main, undefined: theme.palette.warning.main, }; const statusMsg = { true: "Your site is up.", false: "Your site is down.", undefined: "Pending...", }; return ( {Object.keys(monitor).length === 0 ? ( ) : ( <> {parsedUrl?.host ? ( {parsedUrl.host || "..."} ) : ( "" )} {statusMsg[monitor?.status ?? undefined]} {monitor?.isActive ? ( <> Pause ) : ( <> Resume )} setIsOpen(true)} > Remove General settings Here you can select the URL of the host, together with the type of monitor. Incident notifications When there is an incident, notify users. When there is a new incident, logger.warn("disabled")} isDisabled={true} /> notification.type === "email" ) || false } value={user?.email} onChange={(event) => handleChange(event)} /> logger.warn("disabled")} isDisabled={true} /> {monitor?.notifications?.some( (notification) => notification.type === "emails" ) ? ( logger.warn("disabled")} /> You can separate multiple emails with a comma ) : ( "" )} Advanced settings