From 56d8224fb965e994105798f8672e8ff107329d2d Mon Sep 17 00:00:00 2001 From: Jesulayomy Date: Wed, 23 Jul 2025 08:41:50 -0400 Subject: [PATCH 01/83] [Uptime]: Added skeleton file --- client/src/Pages/Uptime/Create/skeleton.jsx | 90 +++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 client/src/Pages/Uptime/Create/skeleton.jsx diff --git a/client/src/Pages/Uptime/Create/skeleton.jsx b/client/src/Pages/Uptime/Create/skeleton.jsx new file mode 100644 index 000000000..9c1014ed4 --- /dev/null +++ b/client/src/Pages/Uptime/Create/skeleton.jsx @@ -0,0 +1,90 @@ +import { Box, Skeleton, Stack } from "@mui/material"; +import { useTheme } from "@emotion/react"; + +/** + * Renders a skeleton layout. + * + * @returns {JSX.Element} + */ +const SkeletonLayout = () => { + const theme = useTheme(); + + return ( + <> + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default SkeletonLayout; From a004b7b450a13daebcdbd2f86ad8fb72bcf2a705 Mon Sep 17 00:00:00 2001 From: Jesulayomy Date: Wed, 23 Jul 2025 09:03:51 -0400 Subject: [PATCH 02/83] [Uptime]: Merged Configure into Create Component --- client/src/Pages/Uptime/Configure/index.css | 11 - client/src/Pages/Uptime/Configure/index.jsx | 526 -------- .../src/Pages/Uptime/Configure/skeleton.jsx | 90 -- client/src/Pages/Uptime/Create/index.jsx | 1179 ++++++++++------- 4 files changed, 702 insertions(+), 1104 deletions(-) delete mode 100644 client/src/Pages/Uptime/Configure/index.css delete mode 100644 client/src/Pages/Uptime/Configure/index.jsx delete mode 100644 client/src/Pages/Uptime/Configure/skeleton.jsx diff --git a/client/src/Pages/Uptime/Configure/index.css b/client/src/Pages/Uptime/Configure/index.css deleted file mode 100644 index 3f24bd011..000000000 --- a/client/src/Pages/Uptime/Configure/index.css +++ /dev/null @@ -1,11 +0,0 @@ -.configure-monitor button.MuiButtonBase-root { - height: var(--env-var-height-2); -} - -.configure-monitor .MuiStack-root:has(span.MuiTypography-root.input-error) { - position: relative; -} -.configure-monitor span.MuiTypography-root.input-error { - position: absolute; - top: 100%; -} diff --git a/client/src/Pages/Uptime/Configure/index.jsx b/client/src/Pages/Uptime/Configure/index.jsx deleted file mode 100644 index 2dacc26ab..000000000 --- a/client/src/Pages/Uptime/Configure/index.jsx +++ /dev/null @@ -1,526 +0,0 @@ -// Components -import Box from "@mui/material/Box"; -import Stack from "@mui/material/Stack"; -import Tooltip from "@mui/material/Tooltip"; -import Typography from "@mui/material/Typography"; -import Button from "@mui/material/Button"; -import FormControlLabel from "@mui/material/FormControlLabel"; -import Switch from "@mui/material/Switch"; -import ConfigBox from "../../../Components/ConfigBox"; -import Breadcrumbs from "../../../Components/Breadcrumbs"; -import TextInput from "../../../Components/Inputs/TextInput"; -import { HttpAdornment } from "../../../Components/Inputs/TextInput/Adornments"; -import Select from "../../../Components/Inputs/Select"; -import Dialog from "../../../Components/Dialog"; -import PulseDot from "../../../Components/Animated/PulseDot"; -import Checkbox from "../../../Components/Inputs/Checkbox"; - -// Utils -import { useParams } from "react-router"; -import { useTheme } from "@emotion/react"; -import { useState } from "react"; -import { monitorValidation } from "../../../Validation/validation"; -import { createToast } from "../../../Utils/toastUtils"; -import { useTranslation } from "react-i18next"; -import PauseOutlinedIcon from "@mui/icons-material/PauseOutlined"; -import PlayArrowOutlinedIcon from "@mui/icons-material/PlayArrowOutlined"; -import { useMonitorUtils } from "../../../Hooks/useMonitorUtils"; -import { useGetNotificationsByTeamId } from "../../../Hooks/useNotifications"; -import { - useDeleteMonitor, - useUpdateMonitor, - usePauseMonitor, - useFetchMonitorById, -} from "../../../Hooks/monitorHooks"; -import NotificationsConfig from "../../../Components/NotificationConfig"; - -/** - * 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 { monitorId } = useParams(); - - // Local state - const [form, setForm] = useState({ - ignoreTlsErrors: false, - interval: 60000, - matchMethod: "equal", - expectedValue: "", - jsonPath: "", - notifications: [], - port: "", - type: "http", - }); - const [useAdvancedMatching, setUseAdvancedMatching] = useState(false); - const [updateTrigger, setUpdateTrigger] = useState(false); - const [isOpen, setIsOpen] = useState(false); - const [errors, setErrors] = useState({}); - - const triggerUpdate = () => { - setUpdateTrigger(!updateTrigger); - }; - - // Network - const [notifications, notificationsAreLoading, notificationsError] = - useGetNotificationsByTeamId(); - const [pauseMonitor, isPausing, pauseError] = usePauseMonitor({}); - const [deleteMonitor, isDeleting] = useDeleteMonitor(); - const [updateMonitor, isUpdating] = useUpdateMonitor(); - const [isLoading] = useFetchMonitorById({ - monitorId, - setMonitor: setForm, - updateTrigger, - }); - - const MS_PER_MINUTE = 60000; - const theme = useTheme(); - - const matchMethodOptions = [ - { _id: "equal", name: "Equal" }, - { _id: "include", name: "Include" }, - { _id: "regex", name: "Regex" }, - ]; - - 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" }, - ]; - - const expectedValuePlaceholders = { - regex: "^(success|ok)$", - equal: "success", - include: "ok", - }; - - // Handlers - const handlePause = async () => { - const res = await pauseMonitor({ monitorId: form?._id, triggerUpdate }); - if (typeof res !== "undefined") { - triggerUpdate(); - } - }; - - const handleRemove = async (event) => { - event.preventDefault(); - await deleteMonitor({ monitor: form, redirect: "/uptime" }); - }; - - const onChange = (event) => { - let { name, value, checked } = event.target; - - if (name === "ignoreTlsErrors") { - value = checked; - } - - if (name === "useAdvancedMatching") { - setForm((prevForm) => { - return { - ...prevForm, - matchMethod: "equal", - }; - }); - setUseAdvancedMatching(!useAdvancedMatching); - return; - } - - if (name === "interval") { - value = value * MS_PER_MINUTE; - } - setForm({ ...form, [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 onSubmit = async (e) => { - e.preventDefault(); - - const toSubmit = { - _id: form._id, - url: form.url, - name: form.name, - type: form.type, - matchMethod: form.matchMethod, - expectedValue: form.expectedValue, - jsonPath: form.jsonPath, - interval: form.interval, - teamId: form.teamId, - userId: form.userId, - port: form.port, - ignoreTlsErrors: form.ignoreTlsErrors, - }; - - if (!useAdvancedMatching) { - toSubmit.matchMethod = ""; - toSubmit.expectedValue = ""; - toSubmit.jsonPath = ""; - } - - const validation = monitorValidation.validate(toSubmit, { - abortEarly: false, - }); - - if (validation.error) { - const newErrors = {}; - validation.error.details.forEach((err) => { - newErrors[err.path[0]] = err.message; - }); - setErrors(newErrors); - createToast({ body: "Please check the form for errors." }); - return; - } - - toSubmit.notifications = form.notifications; - await updateMonitor({ monitor: toSubmit, redirect: "/uptime" }); - }; - - // Parse the URL - const parsedUrl = parseUrl(form?.url); - const protocol = parsedUrl?.protocol?.replace(":", "") || ""; - - const { determineState, statusColor } = useMonitorUtils(); - - const { t } = useTranslation(); - - return ( - - - - - - - {form.name} - - - - - - - - - {form.url?.replace(/^https?:\/\//, "") || "..."} - - - {t("editing")} - - - - - - - - - - - - {t("settingsGeneralSettings")} - - {t("distributedUptimeCreateSelectURL")} - - - - } - id="monitor-url" - label={t("urlMonitor")} - placeholder="google.com" - value={parsedUrl?.host || form?.url || ""} - disabled={true} - /> - - - - - {t("notificationConfig.title")} - {t("notificationConfig.description")} - - - - - - - {t("ignoreTLSError")} - - {t("ignoreTLSErrorDescription")} - - - - } - label={t("tlsErrorIgnored")} - /> - - - - - - {t("distributedUptimeCreateAdvancedSettings")} - - - - - - - - {t("uptimeCreate")} - - - - - - {t("uptimeCreateJsonPath")}  - - jmespath.org - -  {t("uptimeCreateJsonPathQuery")} - - - - )} - - - - - - - setIsOpen(false)} - confirmationButtonLabel="Delete" - onConfirm={handleRemove} - isLoading={isLoading} - /> - - ); -}; - -export default Configure; diff --git a/client/src/Pages/Uptime/Configure/skeleton.jsx b/client/src/Pages/Uptime/Configure/skeleton.jsx deleted file mode 100644 index 9c1014ed4..000000000 --- a/client/src/Pages/Uptime/Configure/skeleton.jsx +++ /dev/null @@ -1,90 +0,0 @@ -import { Box, Skeleton, Stack } from "@mui/material"; -import { useTheme } from "@emotion/react"; - -/** - * Renders a skeleton layout. - * - * @returns {JSX.Element} - */ -const SkeletonLayout = () => { - const theme = useTheme(); - - return ( - <> - - - - - - - - - - - - - - - - - - - - - ); -}; - -export default SkeletonLayout; diff --git a/client/src/Pages/Uptime/Create/index.jsx b/client/src/Pages/Uptime/Create/index.jsx index 761fd6346..c6caeacb1 100644 --- a/client/src/Pages/Uptime/Create/index.jsx +++ b/client/src/Pages/Uptime/Create/index.jsx @@ -1,4 +1,14 @@ //Components +import { + Box, + Button, + ButtonGroup, + FormControlLabel, + Stack, + Switch, + Tooltip, + Typography, +} from "@mui/material"; import Breadcrumbs from "../../../Components/Breadcrumbs"; import TextInput from "../../../Components/Inputs/TextInput"; import { HttpAdornment } from "../../../Components/Inputs/TextInput/Adornments"; @@ -6,494 +16,709 @@ import Radio from "../../../Components/Inputs/Radio"; import Select from "../../../Components/Inputs/Select"; import ConfigBox from "../../../Components/ConfigBox"; import NotificationsConfig from "../../../Components/NotificationConfig"; -import Button from "@mui/material/Button"; -import ButtonGroup from "@mui/material/ButtonGroup"; -import Box from "@mui/material/Box"; -import Stack from "@mui/material/Stack"; -import Typography from "@mui/material/Typography"; -import Switch from "@mui/material/Switch"; -import FormControlLabel from "@mui/material/FormControlLabel"; import Checkbox from "../../../Components/Inputs/Checkbox"; +import Dialog from "../../../Components/Dialog"; +import PulseDot from "../../../Components/Animated/PulseDot"; +import SkeletonLayout from "./skeleton"; // Utils +import { useParams } from "react-router"; import { useTheme } from "@emotion/react"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { monitorValidation } from "../../../Validation/validation"; import { createToast } from "../../../Utils/toastUtils"; +import { useTranslation } from "react-i18next"; +import { + PauseOutlined as PauseOutlinedIcon, + PlayArrowOutlined as PlayArrowOutlinedIcon, +} from "@mui/icons-material"; +import { useMonitorUtils } from "../../../Hooks/useMonitorUtils"; import { useGetNotificationsByTeamId } from "../../../Hooks/useNotifications"; import { useCreateMonitor, useFetchMonitorById } from "../../../Hooks/monitorHooks"; import { useParams } from "react-router-dom"; +import { + useDeleteMonitor, + useUpdateMonitor, + usePauseMonitor, + useFetchMonitorById, +} from "../../../Hooks/monitorHooks"; +import { useCreateMonitor } from "../../../Hooks/monitorHooks"; -const CreateMonitor = () => { - // 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", - expectedValue: "", - jsonPath: "", - notifications: [], - interval: 1, - ignoreTlsErrors: false, - }); - - // Setup - const MS_PER_MINUTE = 60000; - const theme = useTheme(); - const { t } = useTranslation(); - const [notifications, notificationsAreLoading, error] = useGetNotificationsByTeamId(); - const [createMonitor, isCreating] = useCreateMonitor(); - const { monitorId } = useParams(); - - const formatAndSet = (monitor) => { - monitor.interval = monitor.interval / MS_PER_MINUTE; - setMonitor(monitor); - }; - const [isLoading] = useFetchMonitorById({ - monitorId, - setMonitor: formatAndSet, - updateTrigger: true, - }); - - const SELECT_VALUES = [ - { _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" }, - ]; - - const matchMethodOptions = [ - { _id: "equal", name: "Equal" }, - { _id: "include", name: "Include" }, - { _id: "regex", name: "Regex" }, - ]; - - const expectedValuePlaceholders = { - regex: "^(success|ok)$", - equal: "success", - include: "ok", - }; - - const monitorTypeMaps = { - http: { - label: "URL to monitor", - placeholder: "google.com", - namePlaceholder: "Google", - }, - ping: { - label: "IP address to monitor", - placeholder: "1.1.1.1", - namePlaceholder: "Google", - }, - docker: { - label: "Container ID", - placeholder: "abc123", - namePlaceholder: "My Container", - }, - port: { - label: "URL to monitor", - placeholder: "localhost", - namePlaceholder: "Localhost:5173", - }, - }; - - const BREADCRUMBS = [ - { name: "uptime", path: "/uptime" }, - { name: "create", path: `/uptime/create` }, - ]; - - // Handlers - - const onSubmit = async (event) => { - event.preventDefault(); - const { notifications, ...rest } = monitor; - - let form = { - ...rest, - url: - //prepending protocol for url - monitor.type === "http" - ? `http${https ? "s" : ""}://` + monitor.url - : monitor.url, - port: monitor.type === "port" ? monitor.port : undefined, - name: monitor.name || monitor.url.substring(0, 50), - type: monitor.type, - interval: monitor.interval * MS_PER_MINUTE, - }; - - // If not using advanced matching, remove advanced settings - if (!useAdvancedMatching) { - form.matchMethod = undefined; - form.expectedValue = undefined; - form.jsonPath = undefined; - } - - 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: "Please check the form for errors." }); - return; - } - - form = { - ...form, - description: monitor.name || monitor.url, - notifications: monitor.notifications, - }; - - await createMonitor({ monitor: form, redirect: "/uptime" }); - }; - - const onChange = (event) => { - const { name, value, checked } = event.target; - - let newValue = value; - if (name === "ignoreTlsErrors") { - newValue = checked; - } - - if (name === "useAdvancedMatching") { - setUseAdvancedMatching(checked); - return; - } - - const updatedMonitor = { - ...monitor, - [name]: newValue, - }; - - setMonitor(updatedMonitor); - - const { error } = monitorValidation.validate( - { type: monitor.type, [name]: newValue }, - { abortEarly: false } - ); - - setErrors((prev) => ({ - ...prev, - ...(error ? { [name]: error.details[0].message } : { [name]: undefined }), - })); - }; - - return ( - - - - - - {t("createYour")}{" "} - - - {t("monitor")} - - - - - - - {t("distributedUptimeCreateChecks")} - - - {t("distributedUptimeCreateChecksDescription")} - - - - - - {monitor.type === "http" ? ( - - - - - ) : ( - "" - )} - - - - - {errors["type"] ? ( - - - {errors["type"]} - - - ) : ( - "" - )} - - - - - - {t("settingsGeneralSettings")} - - - {t(`uptimeGeneralInstructions.${monitor.type}`)} - - - - : null - } - label={monitorTypeMaps[monitor.type].label || "URL to monitor"} - https={https} - placeholder={monitorTypeMaps[monitor.type].placeholder || ""} - value={monitor.url} - onChange={onChange} - error={errors["url"] ? true : false} - helperText={errors["url"]} - /> - - - - - {t("notificationConfig.title")} - {t("notificationConfig.description")} - - - - - - - {t("ignoreTLSError")} - - {t("ignoreTLSErrorDescription")} - - - - } - label={t("tlsErrorIgnored")} - /> - - - - - - {t("distributedUptimeCreateAdvancedSettings")} - - - - - - - - {t("uptimeCreate")} - - - - - - {t("uptimeCreateJsonPath")}  - - jmespath.org - -  {t("uptimeCreateJsonPathQuery")} - - - - )} - - - - - - - - ); +/** + * 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; + } }; -export default CreateMonitor; +/** + * Create page renders monitor creation or configuration views. + * @component + */ +const UptimeCreate = () => { + const { monitorId } = useParams(); + const isCreate = typeof monitorId === "undefined"; + + // States + const [monitor, setMonitor] = useState({ + type: "http", + matchMethod: "equal", + expectedValue: "", + jsonPath: "", + notifications: [], + interval: 60000, + ignoreTlsErrors: false, + ...(isCreate ? { url: "", name: "" } : { port: undefined }), + }); + const [errors, setErrors] = useState({}); + const [https, setHttps] = useState(true); + const [isOpen, setIsOpen] = useState(false); + const [useAdvancedMatching, setUseAdvancedMatching] = useState(false); + const [updateTrigger, setUpdateTrigger] = useState(false); + const triggerUpdate = () => { + setUpdateTrigger(!updateTrigger); + }; + + // Hooks + const [notifications, notificationsAreLoading, notificationsError] = + useGetNotificationsByTeamId(); + const { determineState, statusColor } = useMonitorUtils(); + // Network + const [isLoading] = useFetchMonitorById({ + monitorId, + setMonitor: formatAndSet, + updateTrigger: true, + }); + const [createMonitor, isCreating] = useCreateMonitor(); + const [pauseMonitor, isPausing] = usePauseMonitor({}); + const [deleteMonitor, isDeleting] = useDeleteMonitor(); + const [updateMonitor, isUpdating] = useUpdateMonitor(); + + // Setup + const theme = useTheme(); + const { t } = useTranslation(); + const formatAndSet = (monitor) => { + monitor.interval = monitor.interval / MS_PER_MINUTE; + setMonitor(monitor); + }; + + // Constants + const MS_PER_MINUTE = 60000; + const FREQUENCIES = [ + { _id: 1, name: t("time.oneMinute") }, + { _id: 2, name: t("time.twoMinutes") }, + { _id: 3, name: t("time.threeMinutes") }, + { _id: 4, name: t("time.fourMinutes") }, + { _id: 5, name: t("time.fiveMinutes") }, + ]; + const CRUMBS = [ + { name: "uptime", path: "/uptime" }, + ...(isCreate + ? [{ name: "create", path: `/uptime/create` }] + : [ + { name: "details", path: `/uptime/${monitorId}` }, + { name: "configure", path: `/uptime/configure/${monitorId}` }, + ]), + ]; + const matchMethodOptions = [ + { _id: "equal", name: t("matchMethodOptions.equal") }, + { _id: "include", name: t("matchMethodOptions.include") }, + { _id: "regex", name: t("matchMethodOptions.regex") }, + ]; + const expectedValuePlaceholders = { + regex: t("matchMethodOptions.regexPlaceholder"), + equal: t("matchMethodOptions.equalPlaceholder"), + include: t("matchMethodOptions.includePlaceholder"), + }; + const monitorTypeMaps = { + http: { + label: t("monitorType.http.label"), + placeholder: t("monitorType.http.placeholder"), + namePlaceholder: t("monitorType.http.namePlaceholder"), + }, + ping: { + label: t("monitorType.ping.label"), + placeholder: t("monitorType.ping.placeholder"), + namePlaceholder: t("monitorType.ping.namePlaceholder"), + }, + docker: { + label: t("monitorType.docker.label"), + placeholder: t("monitorType.docker.placeholder"), + namePlaceholder: t("monitorType.docker.namePlaceholder"), + }, + port: { + label: t("monitorType.port.label"), + placeholder: t("monitorType.port.placeholder"), + namePlaceholder: t("monitorType.port.namePlaceholder"), + }, + }; + + // Handlers + const onSubmit = async (event) => { + event.preventDefault(); + const { notifications, ...rest } = monitor; + + let form = {}; + if (isCreate) { + form = { + ...rest, + url: + monitor.type === "http" + ? `http${https ? "s" : ""}://` + monitor.url + : monitor.url, + port: monitor.type === "port" ? monitor.port : undefined, + name: monitor.name || monitor.url.substring(0, 50), + type: monitor.type, + interval: monitor.interval, + }; + } else { + form = { + _id: monitor._id, + url: monitor.url, + name: monitor.name || monitor.url.substring(0, 50), + type: monitor.type, + matchMethod: monitor.matchMethod, + expectedValue: monitor.expectedValue, + jsonPath: monitor.jsonPath, + interval: monitor.interval, + teamId: monitor.teamId, + userId: monitor.userId, + port: monitor.type === "port" ? monitor.port : undefined, + ignoreTlsErrors: monitor.ignoreTlsErrors, + }; + } + if (!useAdvancedMatching) { + form.matchMethod = isCreate ? undefined : ""; + form.expectedValue = isCreate ? undefined : ""; + form.jsonPath = isCreate ? undefined : ""; + } + + 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: t("checkFormError") }); + return; + } + + form = { + ...form, + description: monitor.name || monitor.url, + notifications: monitor.notifications, + }; + + if (isCreate) { + await createMonitor({ monitor: form, redirect: "/uptime" }); + } else { + await updateMonitor({ monitor: form, redirect: "/uptime" }); + } + }; + + const onChange = (event) => { + let { name, value, checked } = event.target; + + if (name === "ignoreTlsErrors") { + value = checked; + } + + if (name === "useAdvancedMatching") { + setUseAdvancedMatching(checked); + return; + } + + if (name === "interval") { + value = value * MS_PER_MINUTE; + } + + setMonitor((prev) => ({ ...prev, [name]: value })); + + const { error } = monitorValidation.validate( + { type: monitor.type, [name]: value }, + { abortEarly: false } + ); + + setErrors((prev) => ({ + ...prev, + ...(error ? { [name]: error.details[0].message } : { [name]: undefined }), + })); + }; + + const handlePause = async () => { + await pauseMonitor({ monitorId, triggerUpdate }); + }; + + const handleRemove = async (event) => { + event.preventDefault(); + await deleteMonitor({ monitor, redirect: "/uptime" }); + }; + + const isBusy = isLoading || isCreating || isDeleting || isUpdating || isPausing; + const parsedUrl = parseUrl(monitor?.url); + const protocol = parsedUrl?.protocol?.replace(":", "") || ""; + + if (Object.keys(monitor).length === 0) { + return ; + } + useEffect(() => { + if (!isCreate) { + if (monitor.matchMethod) { + setUseAdvancedMatching(true); + } else { + setUseAdvancedMatching(false); + } + } + }, [monitor]); + + return ( + + + + + + + + + {!isCreate ? monitor.name : t("createYour") + " "} + + {isCreate && ( + + {t("monitor")} + + )} + + {!isCreate && ( + + + + + + + + {monitor.url?.replace(/^https?:\/\//, "") || "..."} + + + {t("editing")} + + + )} + + {!isCreate && ( + + + + + )} + + {isCreate && ( + + + + {t("distributedUptimeCreateChecks")} + + + {t("distributedUptimeCreateChecksDescription")} + + + + + + {monitor.type === "http" ? ( + + + + + ) : ( + "" + )} + + + + + {errors["type"] ? ( + + + {errors["type"]} + + + ) : ( + "" + )} + + + )} + + + + {t("settingsGeneralSettings")} + + + {isCreate + ? t(`uptimeGeneralInstructions.${monitor.type}`) + : t("distributedUptimeCreateSelectURL")} + + + + + ) + } + helperText={errors["url"]} + onChange={onChange} + disabled={!isCreate} + /> + + + + + {t("notificationConfig.title")} + {t("notificationConfig.description")} + + + + + + + {t("ignoreTLSError")} + + {t("ignoreTLSErrorDescription")} + + + + } + label={t("tlsErrorIgnored")} + /> + + + + + + {t("distributedUptimeCreateAdvancedSettings")} + + + + + + + + {t("uptimeCreate")} + + + + + + {t("uptimeCreateJsonPath") + " "} + + jmespath.org + + {" " + t("uptimeCreateJsonPathQuery")} + + + + )} + + + + + + + + {!isCreate && ( + setIsOpen(false)} + confirmationButtonLabel={t("delete")} + onConfirm={handleRemove} + isLoading={isLoading} + /> + )} + + ); +}; + +export default UptimeCreate; From 5f36543d44efd656c76cab7a128ab7f6e4e83568 Mon Sep 17 00:00:00 2001 From: Jesulayomy Date: Wed, 23 Jul 2025 09:05:33 -0400 Subject: [PATCH 03/83] [Uptime]: Removed UptimeConfigure component, added extra translations for Uptime and other monitor pages --- client/src/Routes/index.jsx | 5 ++--- client/src/locales/en.json | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/client/src/Routes/index.jsx b/client/src/Routes/index.jsx index 6de07a2de..5bb89db58 100644 --- a/client/src/Routes/index.jsx +++ b/client/src/Routes/index.jsx @@ -14,7 +14,6 @@ import AuthNewPasswordConfirmed from "../Pages/Auth/NewPasswordConfirmed"; import Uptime from "../Pages/Uptime/Monitors"; import UptimeDetails from "../Pages/Uptime/Details"; import UptimeCreate from "../Pages/Uptime/Create"; -import UptimeConfigure from "../Pages/Uptime/Configure"; // PageSpeed import PageSpeed from "../Pages/PageSpeed/Monitors"; @@ -81,7 +80,7 @@ const Routes = () => { /> } /> { /> } + element={} /> Date: Wed, 23 Jul 2025 19:59:45 -0400 Subject: [PATCH 04/83] [Locales]: Sorted translations, fixed a duplicate key from the original branch --- client/src/locales/en.json | 82 +++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 45 deletions(-) diff --git a/client/src/locales/en.json b/client/src/locales/en.json index 6d5ec2948..584894543 100644 --- a/client/src/locales/en.json +++ b/client/src/locales/en.json @@ -378,6 +378,7 @@ "dockerContainerMonitoringDescription": "Check whether your Docker container is running or not.", "duration": "Duration", "edit": "Edit", + "editing": "Editing...", "editMaintenance": "Edit maintenance", "editUserPage": { "form": { @@ -397,7 +398,6 @@ "validationErrors": "Validation errors" } }, - "editing": "Editing...", "emailSent": "Email sent successfully", "errorInvalidFieldId": "Invalid field ID provided", "errorInvalidTypeId": "Invalid notification type provided", @@ -453,8 +453,8 @@ "incidentsOptionsHeaderShow": "Show:", "incidentsOptionsHeaderTotalIncidents": "Total Incidents", "incidentsOptionsPlaceholderAllServers": "All servers", - "incidentsPageActionResolveMonitor": "Resolve monitor incidents", "incidentsPageActionResolveAll": "Resolve all incidents", + "incidentsPageActionResolveMonitor": "Resolve monitor incidents", "incidentsPageTitle": "Incidents", "incidentsTableActionResolve": "Resolve", "incidentsTableDateTime": "Date & Time", @@ -476,13 +476,13 @@ "infrastructureEditYour": "Edit your", "infrastructureMonitor": { "fallback": { + "actionButton": "Let's create your first infrastructure monitor!", "checks": [ "Track the performance of your servers", "Identify bottlenecks and optimize usage", "Ensure reliability with real-time monitoring" ], - "title": "An infrastructure monitor is used to:", - "actionButton": "Let's create your first infrastructure monitor!" + "title": "An infrastructure monitor is used to:" } }, "infrastructureMonitorCreated": "Infrastructure monitor created successfully!", @@ -530,13 +530,13 @@ "maintenanceTableActionMenuDialogTitle": "Do you really want to remove this maintenance window?", "maintenanceWindow": { "fallback": { + "actionButton": "Let's create your first maintenance window!", "checks": [ "Mark your maintenance periods", "Eliminate any misunderstandings", "Stop sending alerts in maintenance windows" ], - "title": "A maintenance window is used to:", - "actionButton": "Let's create your first maintenance window!" + "title": "A maintenance window is used to:" } }, "maintenanceWindowDescription": "Your pings won't be sent during this time frame", @@ -591,6 +591,7 @@ "failureAddDemoMonitors": "Failed to add demo monitors", "successAddDemoMonitors": "Successfully added demo monitors" }, + "monitors": "monitors", "monitorState": { "active": "Active", "paused": "Paused", @@ -605,28 +606,27 @@ }, "monitorStatusDown": "Monitor {name} ({url}) is DOWN and not responding", "monitorStatusUp": "Monitor {name} ({url}) is now UP and responding", - "monitors": "monitors", "monitorsToApply": "Monitors to apply maintenance window to", "monitorType": { "docker": { "label": "Container ID", - "placeholder": "abcd1234", - "namePlaceholder": "My Container" + "namePlaceholder": "My Container", + "placeholder": "abcd1234" }, "http": { "label": "URL to monitor", - "placeholder": "google.com", - "namePlaceholder": "Google" + "namePlaceholder": "Google", + "placeholder": "google.com" }, "ping": { "label": "IP address to monitor", - "placeholder": "1.1.1.1", - "namePlaceholder": "Google" + "namePlaceholder": "Google", + "placeholder": "1.1.1.1" }, "port": { "label": "URL to monitor", - "placeholder": "localhost", - "namePlaceholder": "Localhost:5173" + "namePlaceholder": "Localhost:5173", + "placeholder": "localhost" } }, "ms": "ms", @@ -662,13 +662,13 @@ }, "enableNotifications": "Enable {{platform}} notifications", "fallback": { + "actionButton": "Let's create your first notification channel!", "checks": [ "Alert teams about downtime or performance issues", "Let engineers know when incidents happen", "Keep administrators informed of system changes" ], - "title": "A notification channel is used to:", - "actionButton": "Let's create your first notification channel!" + "title": "A notification channel is used to:" }, "fetch": { "failed": "Failed to fetch notifications", @@ -715,13 +715,13 @@ "os": "OS", "pageSpeed": { "fallback": { + "actionButton": "Let's create your first PageSpeed monitor!", "checks": [ "Report on the user experience of a page", "Help analyze webpage speed", "Give suggestions on how the page can be improved" ], - "title": "A PageSpeed monitor is used to:", - "actionButton": "Let's create your first PageSpeed monitor!" + "title": "A PageSpeed monitor is used to:" } }, "pageSpeedAddApiKey": "to add your API key.", @@ -754,8 +754,8 @@ "queuePage": { "failedJobTable": { "failCountHeader": "Fail count", - "failReasonHeader": "Fail reason", "failedAtHeader": "Last failed at", + "failReasonHeader": "Fail reason", "monitorIdHeader": "Monitor ID", "monitorUrlHeader": "Monitor URL", "title": "Failed jobs" @@ -896,8 +896,8 @@ "settingsTestEmailUnknownError": "Unknown error", "showAdminLoginLink": "Show \"Administrator? Login Here\" link on the status page", "showCharts": "Show charts", - "showUptimePercentage": "Show uptime percentage", "shown": "Shown", + "showUptimePercentage": "Show uptime percentage", "starPromptDescription": "See the latest releases and help grow the community on GitHub", "starPromptTitle": "Star Checkmate", "startTime": "Start time", @@ -917,6 +917,15 @@ "createSuccess": "Status page created successfully", "deleteFailed": "Failed to delete status page", "deleteSuccess": "Status page deleted successfully", + "fallback": { + "actionButton": "Let's create your first status page!", + "checks": [ + "Monitor and display the health of your services in real time", + "Track multiple services and share their status", + "Keep users informed about outages and performance" + ], + "title": "A status page is used to:" + }, "generalSettings": "General settings", "updateSuccess": "Status page updated successfully" }, @@ -939,23 +948,6 @@ "statusPageStatusNoPage": "There's no status page here.", "statusPageStatusNotPublic": "This status page is not public.", "statusPageStatusServiceStatus": "Service status", - "statusPage": { - "deleteSuccess": "Status page deleted successfully", - "deleteFailed": "Failed to delete status page", - "createSuccess": "Status page created successfully", - "updateSuccess": "Status page updated successfully", - "generalSettings": "General settings", - "contents": "Contents", - "fallback": { - "checks": [ - "Monitor and display the health of your services in real time", - "Track multiple services and share their status", - "Keep users informed about outages and performance" - ], - "title": "A status page is used to:", - "actionButton": "Let's create your first status page!" - } - }, "submit": "Submit", "teamPanel": { "cancel": "Cancel", @@ -983,19 +975,19 @@ "testLocale": "testLocale", "testNotificationsDisabled": "There are no notifications setup for this monitor. You need to add one by clicking 'Configure' button", "time": { - "oneMinute": "1 minute", - "twoMinutes": "2 minutes", - "fourMinutes": "4 minutes", "fiveMinutes": "5 minutes", + "fourMinutes": "4 minutes", "oneDay": "1 day", "oneHour": "1 hour", + "oneMinute": "1 minute", "oneWeek": "1 week", "tenMinutes": "10 minutes", "threeMinutes": "3 minutes", - "twentyMinutes": "20 minutes" + "twentyMinutes": "20 minutes", + "twoMinutes": "2 minutes" }, - "timeZoneInfo": "All dates and times are in GMT+0 time zone.", "timezone": "Timezone", + "timeZoneInfo": "All dates and times are in GMT+0 time zone.", "title": "Title", "tlsErrorIgnored": "TLS/SSL errors ignored", "total": "Total", @@ -1016,14 +1008,14 @@ }, "uptimeMonitor": { "fallback": { + "actionButton": "Let's create your first uptime monitor!", "checks": [ "Check if websites or servers are online & responsive", "Alert teams about downtime or performance issues", "Monitor HTTP endpoints, pings, containers & ports", "Track historical uptime and reliability trends" ], - "title": "An uptime monitor is used to:", - "actionButton": "Let's create your first uptime monitor!" + "title": "An uptime monitor is used to:" } }, "url": "URL", From d3493a0af678867610be397affb858912c6719a0 Mon Sep 17 00:00:00 2001 From: Jesulayomy Date: Wed, 23 Jul 2025 20:01:39 -0400 Subject: [PATCH 05/83] [Uptime]: Used latest formatting rules --- client/src/Pages/Uptime/Create/index.jsx | 1301 +++++++++++----------- 1 file changed, 650 insertions(+), 651 deletions(-) diff --git a/client/src/Pages/Uptime/Create/index.jsx b/client/src/Pages/Uptime/Create/index.jsx index c6caeacb1..fcf81f1c3 100644 --- a/client/src/Pages/Uptime/Create/index.jsx +++ b/client/src/Pages/Uptime/Create/index.jsx @@ -1,13 +1,13 @@ //Components import { - Box, - Button, - ButtonGroup, - FormControlLabel, - Stack, - Switch, - Tooltip, - Typography, + Box, + Button, + ButtonGroup, + FormControlLabel, + Stack, + Switch, + Tooltip, + Typography, } from "@mui/material"; import Breadcrumbs from "../../../Components/Breadcrumbs"; import TextInput from "../../../Components/Inputs/TextInput"; @@ -30,20 +30,19 @@ import { monitorValidation } from "../../../Validation/validation"; import { createToast } from "../../../Utils/toastUtils"; import { useTranslation } from "react-i18next"; import { - PauseOutlined as PauseOutlinedIcon, - PlayArrowOutlined as PlayArrowOutlinedIcon, + PauseOutlined as PauseOutlinedIcon, + PlayArrowOutlined as PlayArrowOutlinedIcon, } from "@mui/icons-material"; import { useMonitorUtils } from "../../../Hooks/useMonitorUtils"; import { useGetNotificationsByTeamId } from "../../../Hooks/useNotifications"; import { useCreateMonitor, useFetchMonitorById } from "../../../Hooks/monitorHooks"; import { useParams } from "react-router-dom"; import { - useDeleteMonitor, - useUpdateMonitor, - usePauseMonitor, - useFetchMonitorById, + useDeleteMonitor, + useUpdateMonitor, + usePauseMonitor, + useFetchMonitorById, } from "../../../Hooks/monitorHooks"; -import { useCreateMonitor } from "../../../Hooks/monitorHooks"; /** * Parses a URL string and returns a URL object. @@ -52,11 +51,11 @@ import { useCreateMonitor } from "../../../Hooks/monitorHooks"; * @returns {URL} - The parsed URL object if valid, otherwise an empty string. */ const parseUrl = (url) => { - try { - return new URL(url); - } catch (error) { - return null; - } + try { + return new URL(url); + } catch (error) { + return null; + } }; /** @@ -64,661 +63,661 @@ const parseUrl = (url) => { * @component */ const UptimeCreate = () => { - const { monitorId } = useParams(); - const isCreate = typeof monitorId === "undefined"; + const { monitorId } = useParams(); + const isCreate = typeof monitorId === "undefined"; - // States - const [monitor, setMonitor] = useState({ - type: "http", - matchMethod: "equal", - expectedValue: "", - jsonPath: "", - notifications: [], - interval: 60000, - ignoreTlsErrors: false, - ...(isCreate ? { url: "", name: "" } : { port: undefined }), - }); - const [errors, setErrors] = useState({}); - const [https, setHttps] = useState(true); - const [isOpen, setIsOpen] = useState(false); - const [useAdvancedMatching, setUseAdvancedMatching] = useState(false); - const [updateTrigger, setUpdateTrigger] = useState(false); - const triggerUpdate = () => { - setUpdateTrigger(!updateTrigger); - }; + // States + const [monitor, setMonitor] = useState({ + type: "http", + matchMethod: "equal", + expectedValue: "", + jsonPath: "", + notifications: [], + interval: 60000, + ignoreTlsErrors: false, + ...(isCreate ? { url: "", name: "" } : { port: undefined }), + }); + const [errors, setErrors] = useState({}); + const [https, setHttps] = useState(true); + const [isOpen, setIsOpen] = useState(false); + const [useAdvancedMatching, setUseAdvancedMatching] = useState(false); + const [updateTrigger, setUpdateTrigger] = useState(false); + const triggerUpdate = () => { + setUpdateTrigger(!updateTrigger); + }; - // Hooks - const [notifications, notificationsAreLoading, notificationsError] = - useGetNotificationsByTeamId(); - const { determineState, statusColor } = useMonitorUtils(); - // Network - const [isLoading] = useFetchMonitorById({ - monitorId, - setMonitor: formatAndSet, - updateTrigger: true, - }); - const [createMonitor, isCreating] = useCreateMonitor(); - const [pauseMonitor, isPausing] = usePauseMonitor({}); - const [deleteMonitor, isDeleting] = useDeleteMonitor(); - const [updateMonitor, isUpdating] = useUpdateMonitor(); + // Hooks + const [notifications, notificationsAreLoading, notificationsError] = + useGetNotificationsByTeamId(); + const { determineState, statusColor } = useMonitorUtils(); + // Network + const [isLoading] = useFetchMonitorById({ + monitorId, + setMonitor: formatAndSet, + updateTrigger: true, + }); + const [createMonitor, isCreating] = useCreateMonitor(); + const [pauseMonitor, isPausing] = usePauseMonitor({}); + const [deleteMonitor, isDeleting] = useDeleteMonitor(); + const [updateMonitor, isUpdating] = useUpdateMonitor(); - // Setup - const theme = useTheme(); - const { t } = useTranslation(); - const formatAndSet = (monitor) => { - monitor.interval = monitor.interval / MS_PER_MINUTE; - setMonitor(monitor); - }; + // Setup + const theme = useTheme(); + const { t } = useTranslation(); - // Constants - const MS_PER_MINUTE = 60000; - const FREQUENCIES = [ - { _id: 1, name: t("time.oneMinute") }, - { _id: 2, name: t("time.twoMinutes") }, - { _id: 3, name: t("time.threeMinutes") }, - { _id: 4, name: t("time.fourMinutes") }, - { _id: 5, name: t("time.fiveMinutes") }, - ]; - const CRUMBS = [ - { name: "uptime", path: "/uptime" }, - ...(isCreate - ? [{ name: "create", path: `/uptime/create` }] - : [ - { name: "details", path: `/uptime/${monitorId}` }, - { name: "configure", path: `/uptime/configure/${monitorId}` }, - ]), - ]; - const matchMethodOptions = [ - { _id: "equal", name: t("matchMethodOptions.equal") }, - { _id: "include", name: t("matchMethodOptions.include") }, - { _id: "regex", name: t("matchMethodOptions.regex") }, - ]; - const expectedValuePlaceholders = { - regex: t("matchMethodOptions.regexPlaceholder"), - equal: t("matchMethodOptions.equalPlaceholder"), - include: t("matchMethodOptions.includePlaceholder"), - }; - const monitorTypeMaps = { - http: { - label: t("monitorType.http.label"), - placeholder: t("monitorType.http.placeholder"), - namePlaceholder: t("monitorType.http.namePlaceholder"), - }, - ping: { - label: t("monitorType.ping.label"), - placeholder: t("monitorType.ping.placeholder"), - namePlaceholder: t("monitorType.ping.namePlaceholder"), - }, - docker: { - label: t("monitorType.docker.label"), - placeholder: t("monitorType.docker.placeholder"), - namePlaceholder: t("monitorType.docker.namePlaceholder"), - }, - port: { - label: t("monitorType.port.label"), - placeholder: t("monitorType.port.placeholder"), - namePlaceholder: t("monitorType.port.namePlaceholder"), - }, - }; + // Constants + const MS_PER_MINUTE = 60000; + const FREQUENCIES = [ + { _id: 1, name: t("time.oneMinute") }, + { _id: 2, name: t("time.twoMinutes") }, + { _id: 3, name: t("time.threeMinutes") }, + { _id: 4, name: t("time.fourMinutes") }, + { _id: 5, name: t("time.fiveMinutes") }, + ]; + const CRUMBS = [ + { name: "uptime", path: "/uptime" }, + ...(isCreate + ? [{ name: "create", path: `/uptime/create` }] + : [ + { name: "details", path: `/uptime/${monitorId}` }, + { name: "configure", path: `/uptime/configure/${monitorId}` }, + ]), + ]; + const matchMethodOptions = [ + { _id: "equal", name: t("matchMethodOptions.equal") }, + { _id: "include", name: t("matchMethodOptions.include") }, + { _id: "regex", name: t("matchMethodOptions.regex") }, + ]; + const expectedValuePlaceholders = { + regex: t("matchMethodOptions.regexPlaceholder"), + equal: t("matchMethodOptions.equalPlaceholder"), + include: t("matchMethodOptions.includePlaceholder"), + }; + const monitorTypeMaps = { + http: { + label: t("monitorType.http.label"), + placeholder: t("monitorType.http.placeholder"), + namePlaceholder: t("monitorType.http.namePlaceholder"), + }, + ping: { + label: t("monitorType.ping.label"), + placeholder: t("monitorType.ping.placeholder"), + namePlaceholder: t("monitorType.ping.namePlaceholder"), + }, + docker: { + label: t("monitorType.docker.label"), + placeholder: t("monitorType.docker.placeholder"), + namePlaceholder: t("monitorType.docker.namePlaceholder"), + }, + port: { + label: t("monitorType.port.label"), + placeholder: t("monitorType.port.placeholder"), + namePlaceholder: t("monitorType.port.namePlaceholder"), + }, + }; + const formatAndSet = (monitor) => { + monitor.interval = monitor.interval / MS_PER_MINUTE; + setMonitor(monitor); + }; - // Handlers - const onSubmit = async (event) => { - event.preventDefault(); - const { notifications, ...rest } = monitor; + // Handlers + const onSubmit = async (event) => { + event.preventDefault(); + const { notifications, ...rest } = monitor; - let form = {}; - if (isCreate) { - form = { - ...rest, - url: - monitor.type === "http" - ? `http${https ? "s" : ""}://` + monitor.url - : monitor.url, - port: monitor.type === "port" ? monitor.port : undefined, - name: monitor.name || monitor.url.substring(0, 50), - type: monitor.type, - interval: monitor.interval, - }; - } else { - form = { - _id: monitor._id, - url: monitor.url, - name: monitor.name || monitor.url.substring(0, 50), - type: monitor.type, - matchMethod: monitor.matchMethod, - expectedValue: monitor.expectedValue, - jsonPath: monitor.jsonPath, - interval: monitor.interval, - teamId: monitor.teamId, - userId: monitor.userId, - port: monitor.type === "port" ? monitor.port : undefined, - ignoreTlsErrors: monitor.ignoreTlsErrors, - }; - } - if (!useAdvancedMatching) { - form.matchMethod = isCreate ? undefined : ""; - form.expectedValue = isCreate ? undefined : ""; - form.jsonPath = isCreate ? undefined : ""; - } + let form = {}; + if (isCreate) { + form = { + ...rest, + url: + monitor.type === "http" + ? `http${https ? "s" : ""}://` + monitor.url + : monitor.url, + port: monitor.type === "port" ? monitor.port : undefined, + name: monitor.name || monitor.url.substring(0, 50), + type: monitor.type, + interval: monitor.interval, + }; + } else { + form = { + _id: monitor._id, + url: monitor.url, + name: monitor.name || monitor.url.substring(0, 50), + type: monitor.type, + matchMethod: monitor.matchMethod, + expectedValue: monitor.expectedValue, + jsonPath: monitor.jsonPath, + interval: monitor.interval, + teamId: monitor.teamId, + userId: monitor.userId, + port: monitor.type === "port" ? monitor.port : undefined, + ignoreTlsErrors: monitor.ignoreTlsErrors, + }; + } + if (!useAdvancedMatching) { + form.matchMethod = isCreate ? undefined : ""; + form.expectedValue = isCreate ? undefined : ""; + form.jsonPath = isCreate ? undefined : ""; + } - const { error } = monitorValidation.validate(form, { - abortEarly: false, - }); + 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: t("checkFormError") }); - return; - } + if (error) { + const newErrors = {}; + error.details.forEach((err) => { + newErrors[err.path[0]] = err.message; + }); + setErrors(newErrors); + createToast({ body: t("checkFormError") }); + return; + } - form = { - ...form, - description: monitor.name || monitor.url, - notifications: monitor.notifications, - }; + form = { + ...form, + description: monitor.name || monitor.url, + notifications: monitor.notifications, + }; - if (isCreate) { - await createMonitor({ monitor: form, redirect: "/uptime" }); - } else { - await updateMonitor({ monitor: form, redirect: "/uptime" }); - } - }; + if (isCreate) { + await createMonitor({ monitor: form, redirect: "/uptime" }); + } else { + await updateMonitor({ monitor: form, redirect: "/uptime" }); + } + }; - const onChange = (event) => { - let { name, value, checked } = event.target; + const onChange = (event) => { + let { name, value, checked } = event.target; - if (name === "ignoreTlsErrors") { - value = checked; - } + if (name === "ignoreTlsErrors") { + value = checked; + } - if (name === "useAdvancedMatching") { - setUseAdvancedMatching(checked); - return; - } + if (name === "useAdvancedMatching") { + setUseAdvancedMatching(checked); + return; + } - if (name === "interval") { - value = value * MS_PER_MINUTE; - } + if (name === "interval") { + value = value * MS_PER_MINUTE; + } - setMonitor((prev) => ({ ...prev, [name]: value })); + setMonitor((prev) => ({ ...prev, [name]: value })); - const { error } = monitorValidation.validate( - { type: monitor.type, [name]: value }, - { abortEarly: false } - ); + const { error } = monitorValidation.validate( + { type: monitor.type, [name]: value }, + { abortEarly: false } + ); - setErrors((prev) => ({ - ...prev, - ...(error ? { [name]: error.details[0].message } : { [name]: undefined }), - })); - }; + setErrors((prev) => ({ + ...prev, + ...(error ? { [name]: error.details[0].message } : { [name]: undefined }), + })); + }; - const handlePause = async () => { - await pauseMonitor({ monitorId, triggerUpdate }); - }; + const handlePause = async () => { + await pauseMonitor({ monitorId, triggerUpdate }); + }; - const handleRemove = async (event) => { - event.preventDefault(); - await deleteMonitor({ monitor, redirect: "/uptime" }); - }; + const handleRemove = async (event) => { + event.preventDefault(); + await deleteMonitor({ monitor, redirect: "/uptime" }); + }; - const isBusy = isLoading || isCreating || isDeleting || isUpdating || isPausing; - const parsedUrl = parseUrl(monitor?.url); - const protocol = parsedUrl?.protocol?.replace(":", "") || ""; + const isBusy = isLoading || isCreating || isDeleting || isUpdating || isPausing; + const parsedUrl = parseUrl(monitor?.url); + const protocol = parsedUrl?.protocol?.replace(":", "") || ""; - if (Object.keys(monitor).length === 0) { - return ; - } - useEffect(() => { - if (!isCreate) { - if (monitor.matchMethod) { - setUseAdvancedMatching(true); - } else { - setUseAdvancedMatching(false); - } - } - }, [monitor]); + if (Object.keys(monitor).length === 0) { + return ; + } + useEffect(() => { + if (!isCreate) { + if (monitor.matchMethod) { + setUseAdvancedMatching(true); + } else { + setUseAdvancedMatching(false); + } + } + }, [monitor]); - return ( - - + return ( + + - - - - - - {!isCreate ? monitor.name : t("createYour") + " "} - - {isCreate && ( - - {t("monitor")} - - )} - - {!isCreate && ( - - - - - - - - {monitor.url?.replace(/^https?:\/\//, "") || "..."} - - - {t("editing")} - - - )} - - {!isCreate && ( - - - - - )} - - {isCreate && ( - - - - {t("distributedUptimeCreateChecks")} - - - {t("distributedUptimeCreateChecksDescription")} - - - - - - {monitor.type === "http" ? ( - - - - - ) : ( - "" - )} - - - - - {errors["type"] ? ( - - - {errors["type"]} - - - ) : ( - "" - )} - - - )} - - - - {t("settingsGeneralSettings")} - - - {isCreate - ? t(`uptimeGeneralInstructions.${monitor.type}`) - : t("distributedUptimeCreateSelectURL")} - - - - - ) - } - helperText={errors["url"]} - onChange={onChange} - disabled={!isCreate} - /> - - - - - {t("notificationConfig.title")} - {t("notificationConfig.description")} - - - - - - - {t("ignoreTLSError")} - - {t("ignoreTLSErrorDescription")} - - - - } - label={t("tlsErrorIgnored")} - /> - - - - - - {t("distributedUptimeCreateAdvancedSettings")} - - - - - - - - {t("uptimeCreate")} - - - - - - {t("uptimeCreateJsonPath") + " "} - - jmespath.org - - {" " + t("uptimeCreateJsonPathQuery")} - - - - )} - - - - - - + + + + + + {!isCreate ? monitor.name : t("createYour") + " "} + + {isCreate && ( + + {t("monitor")} + + )} + + {!isCreate && ( + + + + + + + + {monitor.url?.replace(/^https?:\/\//, "") || "..."} + + + {t("editing")} + + + )} + + {!isCreate && ( + + + + + )} + + {isCreate && ( + + + + {t("distributedUptimeCreateChecks")} + + + {t("distributedUptimeCreateChecksDescription")} + + + + + + {monitor.type === "http" ? ( + + + + + ) : ( + "" + )} + + + + + {errors["type"] ? ( + + + {errors["type"]} + + + ) : ( + "" + )} + + + )} + + + + {t("settingsGeneralSettings")} + + + {isCreate + ? t(`uptimeGeneralInstructions.${monitor.type}`) + : t("distributedUptimeCreateSelectURL")} + + + + + ) + } + helperText={errors["url"]} + onChange={onChange} + disabled={!isCreate} + /> + + + + + {t("notificationConfig.title")} + {t("notificationConfig.description")} + + + + + + + {t("ignoreTLSError")} + + {t("ignoreTLSErrorDescription")} + + + + } + label={t("tlsErrorIgnored")} + /> + + + + + + {t("distributedUptimeCreateAdvancedSettings")} + + + + + + + + {t("uptimeCreate")} + + + + + + {t("uptimeCreateJsonPath") + " "} + + jmespath.org + + {" " + t("uptimeCreateJsonPathQuery")} + + + + )} + + + + + + - {!isCreate && ( - setIsOpen(false)} - confirmationButtonLabel={t("delete")} - onConfirm={handleRemove} - isLoading={isLoading} - /> - )} - - ); + {!isCreate && ( + setIsOpen(false)} + confirmationButtonLabel={t("delete")} + onConfirm={handleRemove} + isLoading={isLoading} + /> + )} + + ); }; export default UptimeCreate; From 89f5be2cc9aa3bf715691755735ace397f3ea360 Mon Sep 17 00:00:00 2001 From: Jesulayomy Date: Wed, 23 Jul 2025 21:13:19 -0400 Subject: [PATCH 06/83] [Uptime]: Moved hook, removed duplicate imports, teporarily disabled formatAndSet function due to rerenders --- client/src/Pages/Uptime/Create/index.jsx | 29 ++++++++++++------------ 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/client/src/Pages/Uptime/Create/index.jsx b/client/src/Pages/Uptime/Create/index.jsx index fcf81f1c3..f63a2cb9f 100644 --- a/client/src/Pages/Uptime/Create/index.jsx +++ b/client/src/Pages/Uptime/Create/index.jsx @@ -22,22 +22,20 @@ import PulseDot from "../../../Components/Animated/PulseDot"; import SkeletonLayout from "./skeleton"; // Utils -import { useParams } from "react-router"; import { useTheme } from "@emotion/react"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { monitorValidation } from "../../../Validation/validation"; import { createToast } from "../../../Utils/toastUtils"; -import { useTranslation } from "react-i18next"; import { PauseOutlined as PauseOutlinedIcon, PlayArrowOutlined as PlayArrowOutlinedIcon, } from "@mui/icons-material"; import { useMonitorUtils } from "../../../Hooks/useMonitorUtils"; import { useGetNotificationsByTeamId } from "../../../Hooks/useNotifications"; -import { useCreateMonitor, useFetchMonitorById } from "../../../Hooks/monitorHooks"; import { useParams } from "react-router-dom"; import { + useCreateMonitor, useDeleteMonitor, useUpdateMonitor, usePauseMonitor, @@ -85,6 +83,10 @@ const UptimeCreate = () => { const triggerUpdate = () => { setUpdateTrigger(!updateTrigger); }; + // const formatAndSet = (monitor) => { + // monitor.interval = monitor.interval / MS_PER_MINUTE; + // setMonitor(monitor); + // }; // Hooks const [notifications, notificationsAreLoading, notificationsError] = @@ -93,7 +95,7 @@ const UptimeCreate = () => { // Network const [isLoading] = useFetchMonitorById({ monitorId, - setMonitor: formatAndSet, + setMonitor, // : formatAndSet, updateTrigger: true, }); const [createMonitor, isCreating] = useCreateMonitor(); @@ -155,10 +157,6 @@ const UptimeCreate = () => { namePlaceholder: t("monitorType.port.namePlaceholder"), }, }; - const formatAndSet = (monitor) => { - monitor.interval = monitor.interval / MS_PER_MINUTE; - setMonitor(monitor); - }; // Handlers const onSubmit = async (event) => { @@ -269,9 +267,6 @@ const UptimeCreate = () => { const parsedUrl = parseUrl(monitor?.url); const protocol = parsedUrl?.protocol?.replace(":", "") || ""; - if (Object.keys(monitor).length === 0) { - return ; - } useEffect(() => { if (!isCreate) { if (monitor.matchMethod) { @@ -280,7 +275,11 @@ const UptimeCreate = () => { setUseAdvancedMatching(false); } } - }, [monitor]); + }, [monitor, isCreate]); + + if (Object.keys(monitor).length === 0) { + return ; + } return ( @@ -365,12 +364,12 @@ const UptimeCreate = () => { "&:before": { position: "absolute", content: `""`, - width: 4, - height: 4, + width: theme.spacing(2), + height: theme.spacing(2), borderRadius: "50%", backgroundColor: theme.palette.primary.contrastTextTertiary, opacity: 0.8, - left: -10, + left: theme.spacing(-5), top: "50%", transform: "translateY(-50%)", }, From 47021fa55cbab3fc51e5df6896e4fbfcfe8be52e Mon Sep 17 00:00:00 2001 From: Jesulayomy Date: Thu, 24 Jul 2025 13:38:14 -0400 Subject: [PATCH 07/83] [Uptime]: removed formatAndSet, added displayInterval, moved UI logic out of component view --- client/src/Pages/Uptime/Create/index.jsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/client/src/Pages/Uptime/Create/index.jsx b/client/src/Pages/Uptime/Create/index.jsx index f63a2cb9f..fa8519b1f 100644 --- a/client/src/Pages/Uptime/Create/index.jsx +++ b/client/src/Pages/Uptime/Create/index.jsx @@ -35,7 +35,7 @@ import { useMonitorUtils } from "../../../Hooks/useMonitorUtils"; import { useGetNotificationsByTeamId } from "../../../Hooks/useNotifications"; import { useParams } from "react-router-dom"; import { - useCreateMonitor, + useCreateMonitor, useDeleteMonitor, useUpdateMonitor, usePauseMonitor, @@ -83,10 +83,6 @@ const UptimeCreate = () => { const triggerUpdate = () => { setUpdateTrigger(!updateTrigger); }; - // const formatAndSet = (monitor) => { - // monitor.interval = monitor.interval / MS_PER_MINUTE; - // setMonitor(monitor); - // }; // Hooks const [notifications, notificationsAreLoading, notificationsError] = @@ -95,7 +91,7 @@ const UptimeCreate = () => { // Network const [isLoading] = useFetchMonitorById({ monitorId, - setMonitor, // : formatAndSet, + setMonitor, updateTrigger: true, }); const [createMonitor, isCreating] = useCreateMonitor(); @@ -264,6 +260,7 @@ const UptimeCreate = () => { }; const isBusy = isLoading || isCreating || isDeleting || isUpdating || isPausing; + const displayInterval = monitor?.interval / MS_PER_MINUTE || 1; const parsedUrl = parseUrl(monitor?.url); const protocol = parsedUrl?.protocol?.replace(":", "") || ""; @@ -526,7 +523,7 @@ const UptimeCreate = () => { : monitorTypeMaps[monitor.type].label || t("urlMonitor") } placeholder={monitorTypeMaps[monitor.type].placeholder || ""} - value={parsedUrl?.host || monitor?.url || ""} + value={parsedUrl?.host + parsedUrl?.pathname || monitor?.url || ""} https={isCreate ? https : protocol === "https"} startAdornment={ monitor?.type === "http" && ( @@ -610,7 +607,7 @@ const UptimeCreate = () => {