diff --git a/Client/package-lock.json b/Client/package-lock.json
index 615da347b..689b4a090 100644
--- a/Client/package-lock.json
+++ b/Client/package-lock.json
@@ -13,7 +13,7 @@
"@fontsource/roboto": "^5.0.13",
"@mui/icons-material": "^5.15.17",
"@mui/lab": "^5.0.0-alpha.170",
- "@mui/material": "^5.16.7",
+ "@mui/material": "6.1.8",
"@mui/x-charts": "^7.5.1",
"@mui/x-data-grid": "7.22.3",
"@mui/x-date-pickers": "7.22.3",
@@ -1135,9 +1135,9 @@
}
},
"node_modules/@mui/core-downloads-tracker": {
- "version": "5.16.7",
- "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.7.tgz",
- "integrity": "sha512-RtsCt4Geed2/v74sbihWzzRs+HsIQCfclHeORh5Ynu2fS4icIKozcSubwuG7vtzq2uW3fOR1zITSP84TNt2GoQ==",
+ "version": "6.1.8",
+ "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.8.tgz",
+ "integrity": "sha512-TGAvzwUg9hybDacwfIGFjI2bXYXrIqky+vMfaeay8rvT56/PNAlvIDUJ54kpT5KRc9AWAihOvtDI7/LJOThOmQ==",
"license": "MIT",
"funding": {
"type": "opencollective",
@@ -1212,26 +1212,26 @@
}
},
"node_modules/@mui/material": {
- "version": "5.16.7",
- "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.16.7.tgz",
- "integrity": "sha512-cwwVQxBhK60OIOqZOVLFt55t01zmarKJiJUWbk0+8s/Ix5IaUzAShqlJchxsIQ4mSrWqgcKCCXKtIlG5H+/Jmg==",
+ "version": "6.1.8",
+ "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.8.tgz",
+ "integrity": "sha512-QZdQFnXct+7NXIzHgT3qt+sQiO7HYGZU2vymP9Xl9tUMXEOA/S1mZMMb7+WGZrk5TzNlU/kP/85K0da5V1jXoQ==",
"license": "MIT",
"dependencies": {
- "@babel/runtime": "^7.23.9",
- "@mui/core-downloads-tracker": "^5.16.7",
- "@mui/system": "^5.16.7",
- "@mui/types": "^7.2.15",
- "@mui/utils": "^5.16.6",
+ "@babel/runtime": "^7.26.0",
+ "@mui/core-downloads-tracker": "^6.1.8",
+ "@mui/system": "^6.1.8",
+ "@mui/types": "^7.2.19",
+ "@mui/utils": "^6.1.8",
"@popperjs/core": "^2.11.8",
- "@types/react-transition-group": "^4.4.10",
- "clsx": "^2.1.0",
+ "@types/react-transition-group": "^4.4.11",
+ "clsx": "^2.1.1",
"csstype": "^3.1.3",
"prop-types": "^15.8.1",
"react-is": "^18.3.1",
"react-transition-group": "^4.4.5"
},
"engines": {
- "node": ">=12.0.0"
+ "node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
@@ -1240,9 +1240,10 @@
"peerDependencies": {
"@emotion/react": "^11.5.0",
"@emotion/styled": "^11.3.0",
- "@types/react": "^17.0.0 || ^18.0.0",
- "react": "^17.0.0 || ^18.0.0",
- "react-dom": "^17.0.0 || ^18.0.0"
+ "@mui/material-pigment-css": "^6.1.8",
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/react": {
@@ -1251,6 +1252,140 @@
"@emotion/styled": {
"optional": true
},
+ "@mui/material-pigment-css": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/material/node_modules/@mui/private-theming": {
+ "version": "6.1.8",
+ "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.8.tgz",
+ "integrity": "sha512-TuKl7msynCNCVvhX3c0ef1sF0Qb3VHcPs8XOGB/8bdOGBr/ynmIG1yTMjZeiFQXk8yN9fzK/FDEKMFxILNn3wg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.26.0",
+ "@mui/utils": "^6.1.8",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/material/node_modules/@mui/styled-engine": {
+ "version": "6.1.8",
+ "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.8.tgz",
+ "integrity": "sha512-ZvEoT0U2nPLSLI+B4by4cVjaZnPT2f20f4JUPkyHdwLv65ZzuoHiTlwyhqX1Ch63p8bcJzKTHQVGisEoMK6PGA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.26.0",
+ "@emotion/cache": "^11.13.1",
+ "@emotion/serialize": "^1.3.2",
+ "@emotion/sheet": "^1.4.0",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.4.1",
+ "@emotion/styled": "^11.3.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/material/node_modules/@mui/system": {
+ "version": "6.1.8",
+ "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.8.tgz",
+ "integrity": "sha512-i1kLfQoWxzFpXTBQIuPoA3xKnAnP3en4I2T8xIolovSolGQX5k8vGjw1JaydQS40td++cFsgCdEU458HDNTGUA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.26.0",
+ "@mui/private-theming": "^6.1.8",
+ "@mui/styled-engine": "^6.1.8",
+ "@mui/types": "^7.2.19",
+ "@mui/utils": "^6.1.8",
+ "clsx": "^2.1.1",
+ "csstype": "^3.1.3",
+ "prop-types": "^15.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.5.0",
+ "@emotion/styled": "^11.3.0",
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/material/node_modules/@mui/utils": {
+ "version": "6.1.8",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.8.tgz",
+ "integrity": "sha512-O2DWb1kz8hiANVcR7Z4gOB3SvPPsSQGUmStpyBDzde6dJIfBzgV9PbEQOBZd3EBsd1pB+Uv1z5LAJAbymmawrA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.26.0",
+ "@mui/types": "^7.2.19",
+ "@types/prop-types": "^15.7.13",
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1",
+ "react-is": "^18.3.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
"@types/react": {
"optional": true
}
diff --git a/Client/package.json b/Client/package.json
index 6a7d436a3..14362f890 100644
--- a/Client/package.json
+++ b/Client/package.json
@@ -16,7 +16,7 @@
"@fontsource/roboto": "^5.0.13",
"@mui/icons-material": "^5.15.17",
"@mui/lab": "^5.0.0-alpha.170",
- "@mui/material": "^5.16.7",
+ "@mui/material": "6.1.8",
"@mui/x-charts": "^7.5.1",
"@mui/x-data-grid": "7.22.3",
"@mui/x-date-pickers": "7.22.3",
diff --git a/Client/src/App.jsx b/Client/src/App.jsx
index 82c8203a8..62b6bd9ff 100644
--- a/Client/src/App.jsx
+++ b/Client/src/App.jsx
@@ -41,6 +41,7 @@ import { logger } from "./Utils/Logger"; // Import the logger
import { networkService } from "./main";
import { Infrastructure } from "./Pages/Infrastructure";
import InfrastructureDetails from "./Pages/Infrastructure/Details";
+import Test from "./Pages/test";
function App() {
const AdminCheckedRegister = withAdminCheck(Register);
const MonitorsWithAdminProp = withAdminProp(Monitors);
@@ -89,6 +90,11 @@ function App() {
path="/"
element={}
>
+ }
+ />
+
{
+ const theme = useTheme();
+ return (
+
+
+ {https ? "https" : "http"}://
+
+
+ );
+};
+
+export const PasswordEndAdornment = ({ fieldType, setFieldType }) => {
+ const theme = useTheme();
+ return (
+
+ setFieldType(fieldType === "password" ? "text" : "password")}
+ sx={{
+ color: theme.palette.border.dark,
+ padding: theme.spacing(1),
+ "&:focus-visible": {
+ outline: `2px solid ${theme.palette.primary.main}`,
+ outlineOffset: `2px`,
+ },
+ "& .MuiTouchRipple-root": {
+ pointerEvents: "none",
+ display: "none",
+ },
+ }}
+ >
+ {fieldType === "password" ? : }
+
+
+ );
+};
+
+HttpAdornment.propTypes = {
+ https: PropTypes.bool.isRequired,
+};
diff --git a/Client/src/Components/Inputs/TextInput/index.jsx b/Client/src/Components/Inputs/TextInput/index.jsx
new file mode 100644
index 000000000..d97d1ebac
--- /dev/null
+++ b/Client/src/Components/Inputs/TextInput/index.jsx
@@ -0,0 +1,130 @@
+import { Stack, TextField, Typography } from "@mui/material";
+import { useTheme } from "@emotion/react";
+import { forwardRef, useState, cloneElement } from "react";
+import PropTypes from "prop-types";
+
+const getSx = (theme, type, maxWidth) => {
+ const sx = {
+ maxWidth: maxWidth,
+ };
+
+ if (type === "url") {
+ return {
+ ...sx,
+ "& .MuiInputBase-root": { padding: 0 },
+ "& .MuiStack-root": {
+ borderTopLeftRadius: theme.shape.borderRadius,
+ borderBottomLeftRadius: theme.shape.borderRadius,
+ },
+ };
+ }
+ return sx;
+};
+
+const Required = () => {
+ const theme = useTheme();
+ return (
+
+ *
+
+ );
+};
+
+const Optional = ({ optionalLabel }) => {
+ const theme = useTheme();
+ return (
+
+ {optionalLabel || "(optional)"}
+
+ );
+};
+
+Optional.propTypes = {
+ optionalLabel: PropTypes.string,
+};
+
+const TextInput = forwardRef(
+ (
+ {
+ type,
+ value,
+ placeholder,
+ isRequired,
+ isOptional,
+ optionalLabel,
+ onChange,
+ error = false,
+ helperText = null,
+ startAdornment = null,
+ endAdornment = null,
+ label = null,
+ maxWidth = "100%",
+ },
+ ref
+ ) => {
+ const [fieldType, setFieldType] = useState(type);
+ const theme = useTheme();
+ return (
+
+
+ {label}
+ {isRequired && }
+ {isOptional && }
+
+
+
+ );
+ }
+);
+
+TextInput.displayName = "TextInput";
+
+TextInput.propTypes = {
+ type: PropTypes.string,
+ value: PropTypes.string,
+ placeholder: PropTypes.string,
+ isRequired: PropTypes.bool,
+ isOptional: PropTypes.bool,
+ optionalLabel: PropTypes.string,
+ onChange: PropTypes.func,
+ error: PropTypes.bool,
+ helperText: PropTypes.string,
+ startAdornment: PropTypes.node,
+ endAdornment: PropTypes.node,
+ label: PropTypes.string,
+ maxWidth: PropTypes.string,
+};
+
+export default TextInput;
diff --git a/Client/src/Pages/test.jsx b/Client/src/Pages/test.jsx
new file mode 100644
index 000000000..8f5bb464e
--- /dev/null
+++ b/Client/src/Pages/test.jsx
@@ -0,0 +1,192 @@
+import { Stack, Typography } from "@mui/material";
+import Field from "../Components/Inputs/Field";
+import TextInput from "../Components/Inputs/TextInput";
+import { useState, useEffect, useRef } from "react";
+import { HttpAdornment } from "../Components/Inputs/TextInput/Adornments";
+import { PasswordEndAdornment } from "../Components/Inputs/TextInput/Adornments";
+const Test = () => {
+ const [originalValue, setOriginalValue] = useState("");
+ const [originalError, setOriginalError] = useState("");
+
+ const [newValue, setNewValue] = useState("");
+ const [newError, setNewError] = useState("");
+
+ const [thresholdValue, setThresholdValue] = useState(20);
+ const [thresholdError, setThresholdError] = useState("");
+
+ const [thresholdValue2, setThresholdValue2] = useState(20);
+ const [thresholdError2, setThresholdError2] = useState("");
+
+ const inputRef = useRef(null);
+
+ useEffect(() => {
+ if (inputRef.current) {
+ inputRef.current.focus();
+ }
+ }, []);
+
+ const checkError = (value) => {
+ if (value !== "clear") {
+ return "This is an error";
+ }
+ return "";
+ };
+
+ const checkThresholdError = (value) => {
+ if (value !== 99) {
+ return "This is a threshold error";
+ }
+ return "";
+ };
+ const checkThresholdError2 = (value) => {
+ if (value !== 99) {
+ return "This is a threshold error 2";
+ }
+ return "";
+ };
+
+ const handleOriginalValue = (e) => {
+ setOriginalError(checkError(e.target.value));
+ setOriginalValue(e.target.value);
+ };
+
+ const handleNewValue = (e) => {
+ setNewError(checkError(e.target.value));
+ setNewValue(e.target.value);
+ };
+
+ const handleThresholdValue = (e) => {
+ const parsedVal = parseInt(e.target.value);
+ setThresholdError(checkThresholdError(parsedVal));
+ setThresholdValue(parsedVal);
+ };
+
+ const handleThresholdValue2 = (e) => {
+ const parsedVal = parseInt(e.target.value);
+ setThresholdError2(checkThresholdError2(parsedVal));
+ setThresholdValue2(parsedVal);
+ };
+
+ return (
+
+
+ This is a test page for the TextInput component. It is a rationalized Input
+ component.
+
+ Type anything for an error.
+ Typing "clear" will clear the error for text based input
+ Typing "99" will clear the error for threshold based input
+
+
+
+
+ }
+ onChange={handleNewValue}
+ error={newError !== ""}
+ helperText={newError}
+ />
+
+
+ }
+ onChange={handleNewValue}
+ error={newError !== ""}
+ helperText={newError}
+ ref={inputRef}
+ />
+
+
+
+
+
+ Short field for threshold. Easily show/hide error text
+
+
+
+ {thresholdError} {thresholdError2}
+
+
+ );
+};
+
+export default Test;
diff --git a/Client/src/Utils/Theme/globalTheme.js b/Client/src/Utils/Theme/globalTheme.js
index 7737d396e..0839cd0e8 100644
--- a/Client/src/Utils/Theme/globalTheme.js
+++ b/Client/src/Utils/Theme/globalTheme.js
@@ -209,6 +209,43 @@ const baseTheme = (palette) => ({
}),
},
},
+ MuiTextField: {
+ styleOverrides: {
+ root: ({ theme }) => ({
+ "& fieldset": {
+ borderColor: theme.palette.border.dark,
+ borderRadius: theme.shape.borderRadius,
+ },
+ "& .MuiInputBase-input": {
+ height: "var(--env-var-height-2)",
+ fontSize: "var(--env-var-font-size-medium)",
+ fontWeight: 400,
+ color: palette.text.secondary, // or any color from your palette
+ },
+ "& .MuiInputBase-input.MuiOutlinedInput-input": {
+ padding: "0 var(--env-var-spacing-1-minus) !important",
+ },
+ "& .MuiOutlinedInput-root": {
+ borderRadius: 4,
+ },
+ "& .MuiOutlinedInput-notchedOutline": {
+ borderRadius: 4,
+ },
+ "& .MuiFormHelperText-root": {
+ color: palette.error.main,
+ opacity: 0.8,
+ fontSize: "var(--env-var-font-size-medium)",
+
+ marginLeft: 0,
+ },
+ "& .MuiFormHelperText-root.Mui-error": {
+ opacity: 0.8,
+ fontSize: "var(--env-var-font-size-medium)",
+ color: palette.error.main,
+ },
+ }),
+ },
+ },
},
shape: {
borderRadius: 2,