diff --git a/Client/package-lock.json b/Client/package-lock.json index 44e276f63..2f1543bba 100644 --- a/Client/package-lock.json +++ b/Client/package-lock.json @@ -18,7 +18,7 @@ "@mui/x-data-grid": "7.3.2", "@mui/x-date-pickers": "7.3.2", "@reduxjs/toolkit": "2.2.5", - "axios": "1.7.4", + "axios": "^1.7.4", "chart.js": "^4.4.3", "dayjs": "1.11.11", "joi": "17.13.1", diff --git a/Client/package.json b/Client/package.json index 769e1f4a6..22f15f7f9 100644 --- a/Client/package.json +++ b/Client/package.json @@ -20,7 +20,7 @@ "@mui/x-data-grid": "7.3.2", "@mui/x-date-pickers": "7.3.2", "@reduxjs/toolkit": "2.2.5", - "axios": "1.7.4", + "axios": "^1.7.4", "chart.js": "^4.4.3", "dayjs": "1.11.11", "joi": "17.13.1", diff --git a/Client/src/Components/BasicTable/index.css b/Client/src/Components/BasicTable/index.css index 47a47439d..208604021 100644 --- a/Client/src/Components/BasicTable/index.css +++ b/Client/src/Components/BasicTable/index.css @@ -5,8 +5,7 @@ white-space: nowrap; } .MuiTable-root .host span { - font-size: var(--env-var-font-size-small); - margin-left: 8px; + font-size: 11px; } .MuiTable-root .label { @@ -180,3 +179,16 @@ body:has(.pagination-dropdown) p:has(+ .MuiTablePagination-root) { body:has(.pagination-dropdown) p:has(+ .MuiTablePagination-root) { font-size: var(--env-var-font-size-small-plus); } + +.monitors .MuiTable-root .MuiTableHead-root .MuiTableCell-root { + text-transform: uppercase; + color: var(--env-var-color-5); + opacity: 0.8; + font-size: var(--env-var-font-size-small-plus); + font-weight: 400; +} +.monitors .MuiTableCell-root:not(:first-of-type):not(:last-of-type), +.monitors .MuiTableCell-root:not(:first-of-type):not(:last-of-type) { + padding-left: var(--env-var-spacing-1); + padding-right: var(--env-var-spacing-1); +} diff --git a/Client/src/Components/BasicTable/index.jsx b/Client/src/Components/BasicTable/index.jsx index bef5bd651..c723d2bec 100644 --- a/Client/src/Components/BasicTable/index.jsx +++ b/Client/src/Components/BasicTable/index.jsx @@ -233,6 +233,7 @@ const BasicTable = ({ data, paginated, reversed, table }) => { direction="row" alignItems="center" justifyContent="space-between" + px={theme.gap.small} > Showing {getRange()} of {data.rows.length} monitor(s) @@ -277,7 +278,10 @@ const BasicTable = ({ data, paginated, reversed, table }) => { }, }, }} - sx={{ mt: theme.gap.medium }} + sx={{ + mt: theme.gap.medium, + color: theme.palette.otherColors.bluishGray, + }} /> )} diff --git a/Client/src/Components/Charts/Servers/ServerStatus.jsx b/Client/src/Components/Charts/Servers/ServerStatus.jsx deleted file mode 100644 index c06d0436b..000000000 --- a/Client/src/Components/Charts/Servers/ServerStatus.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from "react"; -import "./serverStatus.css"; -import PropTypes from "prop-types"; - -/** - * @component - * @param {Object} props - * @param {string} props.title - The title text for the server status (required) - * @param {number} props.value - The value text to be displayed (required) - * @param {string} props.state - The state of the server (e.g., "online", "offline", "warning") (required) - * @returns {JSX.Element} - Renders the server status component - */ -const ServerStatus = ({ title, value, state }) => { - return ( -
-
{title}
-
{value}
-
- ); -}; - -ServerStatus.propTypes = { - title: PropTypes.string.isRequired, - value: PropTypes.number.isRequired, - state: PropTypes.string.isRequired, -}; - -export default ServerStatus; diff --git a/Client/src/Components/Charts/Servers/serverStatus.css b/Client/src/Components/Charts/Servers/serverStatus.css deleted file mode 100644 index fcbf470aa..000000000 --- a/Client/src/Components/Charts/Servers/serverStatus.css +++ /dev/null @@ -1,46 +0,0 @@ -:root { - --spacing-general-0: 12px 18px; - --spacing-general-1: 20px; - --color-border-0: #eaecf0; - --border-radius-0: 4px; - --font-size-0: 1rem; - --font-size-2: 36px; - - --color-up: #17b26a; - --color-down: #f04438; - --color-pause: #fdb022; -} - -.server-status-tile { - min-width: 100px; - width: calc(100% - 310px); - padding: var(--spacing-general-0); - border: 1px solid var(--color-border-0); - border-radius: var(--border-radius-0); - background-color: var(--env-var-color-8); -} - -.server-status-tile-title { - font-size: var(--font-size-0); - font-weight: 500; - margin-bottom: calc(var(--spacing-general-1) / 2); - color: var(--env-var-color-5); - opacity: 0.6; -} - -.server-status-tile-value { - font-size: var(--font-size-2); - font-weight: bolder; -} - -.up { - color: var(--color-up); -} - -.down { - color: var(--color-down); -} - -.pause { - color: var(--color-pause); -} diff --git a/Client/src/Components/Inputs/Field/index.jsx b/Client/src/Components/Inputs/Field/index.jsx index 335ed178f..850598d3e 100644 --- a/Client/src/Components/Inputs/Field/index.jsx +++ b/Client/src/Components/Inputs/Field/index.jsx @@ -181,7 +181,14 @@ const Field = forwardRef( Field.displayName = "Field"; Field.propTypes = { - type: PropTypes.oneOf(["text", "password", "url", "email", "description"]), + type: PropTypes.oneOf([ + "text", + "password", + "url", + "email", + "description", + "number", + ]), id: PropTypes.string.isRequired, label: PropTypes.string, https: PropTypes.bool, diff --git a/Client/src/Components/Sidebar/index.css b/Client/src/Components/Sidebar/index.css index 8ce0089bc..4c253c781 100644 --- a/Client/src/Components/Sidebar/index.css +++ b/Client/src/Components/Sidebar/index.css @@ -15,6 +15,9 @@ aside span.MuiTypography-root { font-size: var(--env-var-font-size-medium); line-height: 1; } +aside .MuiStack-root + span.MuiTypography-root { + font-size: var(--env-var-font-size-medium-plus); +} aside .MuiListSubheader-root { font-size: var(--env-var-font-size-small); font-weight: 500; diff --git a/Client/src/Components/Sidebar/index.jsx b/Client/src/Components/Sidebar/index.jsx index ae6507b69..b3fd7669f 100644 --- a/Client/src/Components/Sidebar/index.jsx +++ b/Client/src/Components/Sidebar/index.jsx @@ -194,6 +194,7 @@ function Sidebar() { sx={{ pt: theme.gap.small, px: collapsed ? theme.gap.xs : theme.gap.small, + backgroundColor: "transparent" }} > Menu @@ -289,7 +290,7 @@ function Sidebar() { if ( child.name === "Team" && authState.user?.role && - !authState.user.role.includes("admin") + !authState.user.role.includes("superadmin") ) { return null; } @@ -349,7 +350,7 @@ function Sidebar() { if ( child.name === "Team" && authState.user?.role && - !authState.user.role.includes("admin") + !authState.user.role.includes("superadmin") ) { return null; } @@ -394,6 +395,7 @@ function Sidebar() { sx={{ pt: theme.gap.small, px: collapsed ? 0 : theme.gap.small, + backgroundColor: "transparent" }} > Other diff --git a/Client/src/Layouts/HomeLayout/index.css b/Client/src/Layouts/HomeLayout/index.css index dcdcf0535..e85a6362b 100644 --- a/Client/src/Layouts/HomeLayout/index.css +++ b/Client/src/Layouts/HomeLayout/index.css @@ -20,7 +20,7 @@ height: calc(100vh - var(--env-var-spacing-2) * 2); max-width: var(--env-var-side-bar-width); - border: 1px solid var(--color-border-0); + border: 1px solid var(--env-var-color-6); border-radius: var(--env-var-radius-1); background-color: var(--env-var-color-8); diff --git a/Client/src/Pages/Incidents/index.jsx b/Client/src/Pages/Incidents/index.jsx index 46c7d51cf..5e1cffcb0 100644 --- a/Client/src/Pages/Incidents/index.jsx +++ b/Client/src/Pages/Incidents/index.jsx @@ -57,7 +57,6 @@ const Incidents = () => { null, null ); - console.log(res); // Reduce to a lookup object for 0(1) lookup if (res.data && res.data.data.length > 0) { const monitorLookup = res.data.data.reduce((acc, monitor) => { diff --git a/Client/src/Pages/Maintenance/CreateMaintenanceWindow/index.css b/Client/src/Pages/Maintenance/CreateMaintenanceWindow/index.css index e0311d4e7..4ee17db72 100644 --- a/Client/src/Pages/Maintenance/CreateMaintenanceWindow/index.css +++ b/Client/src/Pages/Maintenance/CreateMaintenanceWindow/index.css @@ -23,7 +23,7 @@ } .duration-config .field-text .MuiTextField-root, -.duration-config .field { +.maintenance-options .duration-config .field { width: 70px; min-width: 70px; max-width: 70px; diff --git a/Client/src/Pages/Maintenance/CreateMaintenanceWindow/index.jsx b/Client/src/Pages/Maintenance/CreateMaintenanceWindow/index.jsx index d207bfc98..f97578d5a 100644 --- a/Client/src/Pages/Maintenance/CreateMaintenanceWindow/index.jsx +++ b/Client/src/Pages/Maintenance/CreateMaintenanceWindow/index.jsx @@ -10,6 +10,9 @@ import dayjs from "dayjs"; import { DatePicker } from "@mui/x-date-pickers/DatePicker"; import { MobileTimePicker } from "@mui/x-date-pickers/MobileTimePicker"; import Field from "../../../Components/Inputs/Field"; +import { maintenanceWindowValidation } from "../../../Validation/validation"; +import { logger } from "../../../Utils/Logger"; +import { createToast } from "../../../Utils/toastUtils"; const directory = { title: "Create a maintenance window", @@ -59,9 +62,10 @@ const CreateNewMaintenanceWindow = () => { startTime: dayjs(), duration: "60", unit: "minutes", - friendlyName: "", + displayName: "", AddMonitors: "", }); + const [errors, setErrors] = useState({}); const handleChange = (event, name) => { const { value } = event.target; @@ -71,6 +75,41 @@ const CreateNewMaintenanceWindow = () => { })); }; + const handleSubmit = async () => { + const data = { + repeat: values.repeat, + date: values.date.format("YYYY-MM-DD"), + startTime: values.startTime.format("HH:mm"), + duration: values.duration, + unit: values.unit, + displayName: values.displayName, + addMonitors: values.AddMonitors, + }; + + const { error } = maintenanceWindowValidation.validate(data, { + abortEarly: false, + }); + logger.log("error: ", error); + if (!error || error.details.length === 0) { + setErrors({}); + } else { + const newErrors = {}; + error.details.forEach((err) => { + newErrors[err.path[0]] = err.message; + }); + setErrors(newErrors); + createToast({ + body: + error.details && error.details.length > 0 + ? error.details[0].message + : "Error validating data", + }); + logger.error("Validation errors:", error.details); + } + + logger.log("Submitting data: ", data); + }; + const configOptions = [ { title: "Repeat", @@ -126,6 +165,8 @@ const CreateNewMaintenanceWindow = () => { placeholder="60" onChange={(e) => handleChange(e, "duration")} value={values.duration} + error={errors.duration} + type="number" />