Merge branch 'develop' into feat/fe/refactor/input

This commit is contained in:
Alexander Holliday
2024-11-24 18:37:59 -08:00
committed by GitHub
24 changed files with 676 additions and 265 deletions
+39 -39
View File
@@ -15,8 +15,8 @@
"@mui/lab": "^5.0.0-alpha.170",
"@mui/material": "6.1.8",
"@mui/x-charts": "^7.5.1",
"@mui/x-data-grid": "7.22.2",
"@mui/x-date-pickers": "7.22.2",
"@mui/x-data-grid": "7.22.3",
"@mui/x-date-pickers": "7.22.3",
"@reduxjs/toolkit": "2.3.0",
"axios": "^1.7.4",
"chart.js": "^4.4.3",
@@ -362,16 +362,16 @@
}
},
"node_modules/@emotion/babel-plugin": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz",
"integrity": "sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==",
"version": "11.13.5",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
"integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
"license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.16.7",
"@babel/runtime": "^7.18.3",
"@emotion/hash": "^0.9.2",
"@emotion/memoize": "^0.9.0",
"@emotion/serialize": "^1.2.0",
"@emotion/serialize": "^1.3.3",
"babel-plugin-macros": "^3.1.0",
"convert-source-map": "^1.5.0",
"escape-string-regexp": "^4.0.0",
@@ -381,14 +381,14 @@
}
},
"node_modules/@emotion/cache": {
"version": "11.13.1",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz",
"integrity": "sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==",
"version": "11.13.5",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.5.tgz",
"integrity": "sha512-Z3xbtJ+UcK76eWkagZ1onvn/wAVb1GOMuR15s30Fm2wrMgC7jzpnO2JZXr4eujTTqoQFUrZIw/rT0c6Zzjca1g==",
"license": "MIT",
"dependencies": {
"@emotion/memoize": "^0.9.0",
"@emotion/sheet": "^1.4.0",
"@emotion/utils": "^1.4.0",
"@emotion/utils": "^1.4.2",
"@emotion/weak-memoize": "^0.4.0",
"stylis": "4.2.0"
}
@@ -415,17 +415,17 @@
"license": "MIT"
},
"node_modules/@emotion/react": {
"version": "11.13.3",
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.13.3.tgz",
"integrity": "sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg==",
"version": "11.13.5",
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.13.5.tgz",
"integrity": "sha512-6zeCUxUH+EPF1s+YF/2hPVODeV/7V07YU5x+2tfuRL8MdW6rv5vb2+CBEGTGwBdux0OIERcOS+RzxeK80k2DsQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.12.0",
"@emotion/cache": "^11.13.0",
"@emotion/serialize": "^1.3.1",
"@emotion/babel-plugin": "^11.13.5",
"@emotion/cache": "^11.13.5",
"@emotion/serialize": "^1.3.3",
"@emotion/use-insertion-effect-with-fallbacks": "^1.1.0",
"@emotion/utils": "^1.4.0",
"@emotion/utils": "^1.4.2",
"@emotion/weak-memoize": "^0.4.0",
"hoist-non-react-statics": "^3.3.1"
},
@@ -439,15 +439,15 @@
}
},
"node_modules/@emotion/serialize": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.2.tgz",
"integrity": "sha512-grVnMvVPK9yUVE6rkKfAJlYZgo0cu3l9iMC77V7DW6E1DUIrU68pSEXRmFZFOFB1QFo57TncmOcvcbMDWsL4yA==",
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
"integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
"license": "MIT",
"dependencies": {
"@emotion/hash": "^0.9.2",
"@emotion/memoize": "^0.9.0",
"@emotion/unitless": "^0.10.0",
"@emotion/utils": "^1.4.1",
"@emotion/utils": "^1.4.2",
"csstype": "^3.0.2"
}
},
@@ -458,17 +458,17 @@
"license": "MIT"
},
"node_modules/@emotion/styled": {
"version": "11.13.0",
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.13.0.tgz",
"integrity": "sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==",
"version": "11.13.5",
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.13.5.tgz",
"integrity": "sha512-gnOQ+nGLPvDXgIx119JqGalys64lhMdnNQA9TMxhDA4K0Hq5+++OE20Zs5GxiCV9r814xQ2K5WmtofSpHVW6BQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.12.0",
"@emotion/babel-plugin": "^11.13.5",
"@emotion/is-prop-valid": "^1.3.0",
"@emotion/serialize": "^1.3.0",
"@emotion/serialize": "^1.3.3",
"@emotion/use-insertion-effect-with-fallbacks": "^1.1.0",
"@emotion/utils": "^1.4.0"
"@emotion/utils": "^1.4.2"
},
"peerDependencies": {
"@emotion/react": "^11.0.0-rc.0",
@@ -496,9 +496,9 @@
}
},
"node_modules/@emotion/utils": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.1.tgz",
"integrity": "sha512-BymCXzCG3r72VKJxaYVwOXATqXIZ85cuvg0YOUDxMGNrKc1DJRZk8MgV5wyXRyEayIMd4FuXJIUgTBXvDNW5cA==",
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
"integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
"license": "MIT"
},
"node_modules/@emotion/weak-memoize": {
@@ -1535,9 +1535,9 @@
}
},
"node_modules/@mui/x-charts": {
"version": "7.22.2",
"resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-7.22.2.tgz",
"integrity": "sha512-0Y2du4Ed7gOT53l8vVJ4vKT+Jz4Dh/iHnLy8TtL3+XhbPH9Ndu9Q30WwyyzOn84yt37hSUru/njQ1BWaSvVPHw==",
"version": "7.22.3",
"resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-7.22.3.tgz",
"integrity": "sha512-w23+AwIK86bpNWkuHewyQwOKi1wYbLDzrvUEqvZ9KVYzZvnqpJmbTKideX1pLVgSNt0On8NDXytzCntV48Nobw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.25.7",
@@ -1593,9 +1593,9 @@
}
},
"node_modules/@mui/x-data-grid": {
"version": "7.22.2",
"resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.22.2.tgz",
"integrity": "sha512-yfy2s5A6tbajQZiEdsba49T4FYb9F0WPrzbbG30dl1+sIiX4ZRX7ma44UIDGPZrsZv8xkkE+p8qeJxZ7OaMteA==",
"version": "7.22.3",
"resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.22.3.tgz",
"integrity": "sha512-O6kBf6yt/GkOcWjHca5xWN10qBQ/MkITvJmBuIOtX+LH7YtOAriMgD2zkhNbXxHChi7QdEud3bNC3jw5RLRVCA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.25.7",
@@ -1630,9 +1630,9 @@
}
},
"node_modules/@mui/x-date-pickers": {
"version": "7.22.2",
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.22.2.tgz",
"integrity": "sha512-1KHSlIlnSoY3oHm820By8X344pIdGYqPvCCvfVHrEeeIQ/pHdxDD8tjZFWkFl4Jgm9oVFK90fMcqNZAzc+WaCw==",
"version": "7.22.3",
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.22.3.tgz",
"integrity": "sha512-shNp92IrST5BiVy2f4jbrmRaD32QhyUthjh1Oexvpcn0v6INyuWgxfodoTi5ZCnE5Ue5UVFSs4R9Xre0UbJ5DQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.25.7",
+2 -2
View File
@@ -18,8 +18,8 @@
"@mui/lab": "^5.0.0-alpha.170",
"@mui/material": "6.1.8",
"@mui/x-charts": "^7.5.1",
"@mui/x-data-grid": "7.22.2",
"@mui/x-date-pickers": "7.22.2",
"@mui/x-data-grid": "7.22.3",
"@mui/x-date-pickers": "7.22.3",
"@reduxjs/toolkit": "2.3.0",
"axios": "^1.7.4",
"chart.js": "^4.4.3",
+2 -1
View File
@@ -2,12 +2,13 @@
min-width: var(--env-var-width-3);
}
.field-infrastructure-alert {
.field-infrastructure-alert{
max-width: var(--env-var-width-4);
}
.field-infrastructure-alert .MuiInputBase-root:has(input) {
/* height: var(--env-var-height-2); */
min-width: unset;
}
.field h3.MuiTypography-root,
+27 -17
View File
@@ -7,23 +7,31 @@ import Visibility from "@mui/icons-material/Visibility";
import "./index.css";
/**
* Field component for rendering various types of input fields with customizable properties
*
* @param {Object} props
* @param {string} [props.type] - Type of input field (e.g., 'text', 'password').
* @param {string} props.id - ID of the input field.
* @param {string} props.name - Name of the input field.
* @param {string} [props.label] - Label for the input field.
* @param {boolean} [props.https] - Indicates if it should display http or https.
* @param {boolean} [props.isRequired] - Indicates if the field is required, will display a red asterisk.
* @param {boolean} [props.isOptional] - Indicates if the field is optional, will display optional text.
* @param {string} [props.optionalLabel] - Optional label for the input field.
* @param {string} [props.autoComplete] - Autocomplete value for the input field.
* @param {string} [props.type='text'] - Type of input field (text, password, url, email, description, number).
* @param {string} props.id - Unique identifier for the input field.
* @param {string} props.name - Name attribute for the input field.
* @param {string} [props.label] - Label text displayed above the input field.
* @param {boolean} [props.https=true] - For URL type, determines whether to show https:// or http://.
* @param {boolean} [props.isRequired=false] - Displays a red asterisk if the field is required.
* @param {boolean} [props.isOptional=false] - Displays an optional label next to the field.
* @param {string} [props.optionalLabel='(optional)'] - Custom text for optional label.
* @param {string} [props.autoComplete] - Autocomplete attribute for the input.
* @param {string} [props.placeholder] - Placeholder text for the input field.
* @param {string} props.value - Value of the input field.
* @param {function} props.onChange - Function called on input change.
* @param {string} [props.error] - Error message to display for the input field.
* @param {boolean} [props.disabled] - Indicates if the input field is disabled.
* @param {boolean} [props.hidden] - Indicates if the input field is hidden.
* @param {React.Ref} [ref] - Ref forwarded to the underlying `TextField` component. Allows for direct interactions such as focusing.
* @param {string} props.value - Current value of the input field.
* @param {function} props.onChange - Callback function triggered on input value change.
* @param {function} [props.onBlur] - Callback function triggered when input loses focus.
* @param {function} [props.onInput] - Callback function triggered on input event.
* @param {string} [props.error] - Error message to display below the input field.
* @param {boolean} [props.disabled=false] - Disables the input field if true.
* @param {boolean} [props.hidden=false] - Hides the entire input field if true.
* @param {string} [props.className] - Additional CSS class names for the input container.
* @param {boolean} [props.hideErrorText=false] - Hides the error message if true.
* @param {React.Ref} [ref] - Ref forwarded to the underlying TextField component.
*
* @returns {React.ReactElement} Rendered input field component
*/
const Field = forwardRef(
@@ -47,6 +55,7 @@ const Field = forwardRef(
disabled,
hidden,
className,
hideErrorText = false,
},
ref
) => {
@@ -57,7 +66,7 @@ const Field = forwardRef(
return (
<Stack
gap={theme.spacing(2)}
className={`${className ?? `field field-${type}`}`}
className={`field field-${type} ${className}`}
sx={{
"& fieldset": {
borderColor: theme.palette.border.dark,
@@ -190,7 +199,7 @@ const Field = forwardRef(
<Typography
component="span"
className="input-error"
hidden={className ? true : false}
hidden={hideErrorText}
color={theme.palette.error.main}
mt={theme.spacing(2)}
sx={{
@@ -226,6 +235,7 @@ Field.propTypes = {
disabled: PropTypes.bool,
hidden: PropTypes.bool,
className: PropTypes.string,
hideErrorText: PropTypes.bool,
};
export default Field;
-17
View File
@@ -548,23 +548,6 @@ const Login = () => {
)
)}
</Stack>
<Box
textAlign="center"
p={theme.spacing(12)}
>
<Typography display="inline-block">Don&apos;t have an account? </Typography>
<Typography
component="span"
color={theme.palette.primary.main}
ml={theme.spacing(2)}
onClick={() => {
navigate("/register");
}}
sx={{ userSelect: "none" }}
>
Sign Up
</Typography>
</Box>
</Stack>
);
};
@@ -39,7 +39,7 @@ const IncidentTable = ({ monitors, selectedMonitor, filter }) => {
page: 0,
rowsPerPage: 14,
});
const [isLoading, setIsLoading] = useState(true);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
setPaginationController((prevPaginationController) => ({
@@ -4,9 +4,8 @@ import Checkbox from "../../../../Components/Inputs/Checkbox";
import { useTheme } from "@emotion/react";
import PropTypes from "prop-types";
/**
* `CustomThreshold` is a functional React component that displays a
* `CustomThreshold` is a functional React component that displays a
* group of CheckBox with a label and its correspondant threshold input field.
*
* @param {{ checkboxId: any; checkboxLabel: any; onCheckboxChange: any; fieldId: any; onFieldChange: any; onFieldBlur: any; alertUnit: any; infrastructureMonitor: any; errors: any; }} param0
@@ -20,64 +19,65 @@ import PropTypes from "prop-types";
* @param {object} param0.infrastructureMonitor the form object of the create infrastrcuture monitor page
* @param {object} param0.errors the object that holds all the errors of the form page
* @returns A compound React component that renders the custom threshold alert section
*
*
*/
export const CustomThreshold = ({
checkboxId,
checkboxLabel,
onCheckboxChange,
fieldId,
onFieldChange,
onFieldBlur,
alertUnit,
infrastructureMonitor,
errors
}) =>
{
const theme = useTheme();
return (
<Stack
direction={"row"}
sx={{
width: "50%",
justifyContent: "space-between",
flexWrap: "wrap"
}}
>
<Box>
<Checkbox
id={checkboxId}
label={checkboxLabel}
isChecked={infrastructureMonitor[checkboxId]}
onChange={onCheckboxChange}
/>
</Box>
<Stack
direction={"row"}
sx={{
justifyContent: "flex-end",
}}
>
<Field
type="number"
className="field-infrastructure-alert"
id={fieldId}
value={infrastructureMonitor[fieldId]}
onBlur={onFieldBlur}
onChange={onFieldChange}
error={errors[fieldId]}
disabled={!infrastructureMonitor[checkboxId]}
></Field>
<Typography
component="p"
m={theme.spacing(3)}
>
{alertUnit}
</Typography>
</Stack>
</Stack>
)}
checkboxId,
checkboxLabel,
onCheckboxChange,
fieldId,
onFieldChange,
onFieldBlur,
alertUnit,
infrastructureMonitor,
errors,
}) => {
const theme = useTheme();
return (
<Stack
direction={"row"}
sx={{
width: "50%",
justifyContent: "space-between",
flexWrap: "wrap",
}}
>
<Box>
<Checkbox
id={checkboxId}
label={checkboxLabel}
isChecked={infrastructureMonitor[checkboxId]}
onChange={onCheckboxChange}
/>
</Box>
<Stack
direction={"row"}
sx={{
justifyContent: "flex-end",
}}
>
<Field
type="number"
className="field-infrastructure-alert"
id={fieldId}
value={infrastructureMonitor[fieldId]}
onBlur={onFieldBlur}
onChange={onFieldChange}
error={errors[fieldId]}
disabled={!infrastructureMonitor[checkboxId]}
hideErrorText={true}
></Field>
<Typography
component="p"
m={theme.spacing(3)}
>
{alertUnit}
</Typography>
</Stack>
</Stack>
);
};
CustomThreshold.propTypes = {
checkboxId: PropTypes.string.isRequired,
@@ -89,4 +89,4 @@ CustomThreshold.propTypes = {
alertUnit: PropTypes.string.isRequired,
infrastructureMonitor: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired,
};
};
@@ -132,7 +132,7 @@ const CreateInfrastructureMonitor = () => {
Object.keys(form)
.filter((k) => k.startsWith(THRESHOLD_FIELD_PREFIX))
.map((k) => {
if (form[k]) thresholds[k] = form[k];
if (form[k]) thresholds[k] = form[k] / 100;
delete form[k];
delete form[k.substring(THRESHOLD_FIELD_PREFIX.length)];
});
@@ -158,6 +158,7 @@ const CreateInfrastructureMonitor = () => {
: infrastructureMonitor.name,
interval: infrastructureMonitor.interval * MS_PER_MINUTE,
};
delete form.notifications;
if (hasValidationErrors(form, infrastructureMonitorValidation, setErrors)) {
return;
@@ -194,25 +195,6 @@ const CreateInfrastructureMonitor = () => {
{ _id: 10, name: "10 minutes" },
];
const NOTIFY_MULTIPLE_EMAIL_LABEL = (
<Box>
<Typography mb={theme.spacing(4)}>
Also notify via email to multiple addresses (coming soon)
</Typography>
<Field
id="notify-email-list"
type="text"
placeholder="name@gmail.com"
value=""
onChange={() => logger.warn("disabled")}
onBlur={handleBlur}
/>
<Typography mt={theme.spacing(4)}>
You can separate multiple emails with a comma
</Typography>
</Box>
);
return (
<Box className="create-infrastructure-monitor">
<Breadcrumbs
@@ -253,7 +235,8 @@ const CreateInfrastructureMonitor = () => {
<Box>
<Typography component="h2">General settings</Typography>
<Typography component="p">
Here you can select the URL of the host, together with the type of monitor.
Here you can select the URL of the host, together with the friendly name and
authorization secret to connect to the server agent.
</Typography>
</Box>
<Stack gap={theme.spacing(15)}>
@@ -307,15 +290,6 @@ const CreateInfrastructureMonitor = () => {
onChange={(e) => handleChange(e)}
onBlur={handleBlur}
/>
<Checkbox
id="notify-email"
label={NOTIFY_MULTIPLE_EMAIL_LABEL}
isChecked={false}
value=""
onChange={() => logger.warn("disabled")}
onBlur={handleBlur}
isDisabled={true}
/>
</Stack>
</ConfigBox>
@@ -323,8 +297,7 @@ const CreateInfrastructureMonitor = () => {
<Box>
<Typography component="h2">Customize alerts</Typography>
<Typography component="p">
Send a notification to user(s) When the thresholds exceed a certain number
or percentage.
Send a notification to user(s) when thresholds exceed a specified percentage.
</Typography>
</Box>
<Stack gap={theme.spacing(6)}>
@@ -0,0 +1,37 @@
import { useTheme } from "@emotion/react";
import PlaceholderLight from "../../../assets/Images/data_placeholder.svg?react";
import PlaceholderDark from "../../../assets/Images/data_placeholder_dark.svg?react";
import { Box, Typography, Stack } from "@mui/material";
import PropTypes from "prop-types";
import { useSelector } from "react-redux";
const Empty = ({ styles }) => {
const theme = useTheme();
const mode = useSelector((state) => state.ui.mode);
return (
<Box sx={{ ...styles, marginTop: theme.spacing(24) }}>
<Stack
direction="column"
gap={theme.spacing(8)}
alignItems="center"
>
{mode === "light" ? <PlaceholderLight /> : <PlaceholderDark />}
<Typography variant="h2">Your infrastructure dashboard will show here</Typography>
<Typography
textAlign="center"
color={theme.palette.text.secondary}
>
Hang tight! When we receive data, we'll show it here. Please check back in a few
minutes.
</Typography>
</Stack>
</Box>
);
};
Empty.propTypes = {
styles: PropTypes.object,
mode: PropTypes.string,
};
export default Empty;
@@ -5,10 +5,14 @@ import { Stack, Box, Typography } from "@mui/material";
import { useTheme } from "@emotion/react";
import CustomGauge from "../../../Components/Charts/CustomGauge";
import AreaChart from "../../../Components/Charts/AreaChart";
import { useSelector } from "react-redux";
import { networkService } from "../../../main";
import PulseDot from "../../../Components/Animated/PulseDot";
import useUtils from "../../Monitors/utils";
import { useNavigate } from "react-router-dom";
import Empty from "./empty";
import { logger } from "../../../Utils/Logger";
import { formatDurationRounded, formatDurationSplit } from "../../../Utils/timeUtils";
import axios from "axios";
import {
TzTick,
PercentTick,
@@ -25,6 +29,7 @@ const TYPOGRAPHY_PADDING = 8;
* @returns {number} Converted value in gigabytes
*/
const formatBytes = (bytes) => {
if (bytes === undefined || bytes === null) return "0 GB";
if (typeof bytes !== "number") return "0 GB";
if (bytes === 0) return "0 GB";
@@ -38,6 +43,22 @@ const formatBytes = (bytes) => {
}
};
/**
* Converts a decimal value to a percentage
*
* @function decimalToPercentage
* @param {number} value - Decimal value to convert
* @returns {number} Percentage representation
*
* @example
* decimalToPercentage(0.75) // Returns 75
* decimalToPercentage(null) // Returns 0
*/
const decimalToPercentage = (value) => {
if (value === null || value === undefined) return 0;
return value * 100;
};
/**
* Renders a base box with consistent styling
* @param {Object} props - Component properties
@@ -105,6 +126,7 @@ StatBox.propTypes = {
*/
const GaugeBox = ({ value, heading, metricOne, valueOne, metricTwo, valueTwo }) => {
const theme = useTheme();
return (
<BaseBox>
<Stack
@@ -160,6 +182,7 @@ GaugeBox.propTypes = {
* @returns {React.ReactElement} Infrastructure details page component
*/
const InfrastructureDetails = () => {
const navigate = useNavigate();
const theme = useTheme();
const { monitorId } = useParams();
const navList = [
@@ -167,6 +190,8 @@ const InfrastructureDetails = () => {
{ name: "details", path: `/infrastructure/${monitorId}` },
];
const [monitor, setMonitor] = useState(null);
const { authToken } = useSelector((state) => state.auth);
const [dateRange, setDateRange] = useState("all");
const { statusColor, determineState } = useUtils();
// These calculations are needed because ResponsiveContainer
// doesn't take padding of parent/siblings into account
@@ -183,25 +208,31 @@ const InfrastructureDetails = () => {
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios.get("http://localhost:5000/api/v1/dummy-data", {
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-cache",
},
const response = await networkService.getStatsByMonitorId({
authToken: authToken,
monitorId: monitorId,
sortOrder: "asc",
limit: null,
dateRange: dateRange,
numToDisplay: 50,
normalize: false,
});
setMonitor(response.data.data);
} catch (error) {
console.error(error);
navigate("/not-found", { replace: true });
logger.error(error);
}
};
fetchData();
}, []);
}, [authToken, monitorId, dateRange]);
const statBoxConfigs = [
{
id: 0,
heading: "CPU",
subHeading: `${monitor?.checks[0]?.cpu?.physical_core} cores`,
subHeading: `${monitor?.checks[0]?.cpu?.physical_core ?? 0} cores`,
},
{
id: 1,
@@ -224,7 +255,7 @@ const InfrastructureDetails = () => {
const gaugeBoxConfigs = [
{
type: "memory",
value: monitor?.checks[0]?.memory?.usage_percent * 100,
value: decimalToPercentage(monitor?.checks[0]?.memory?.usage_percent),
heading: "Memory Usage",
metricOne: "Used",
valueOne: formatBytes(monitor?.checks[0]?.memory?.used_bytes),
@@ -233,17 +264,17 @@ const InfrastructureDetails = () => {
},
{
type: "cpu",
value: monitor?.checks[0]?.cpu?.usage_percent * 100,
value: decimalToPercentage(monitor?.checks[0]?.cpu?.usage_percent),
heading: "CPU Usage",
metricOne: "Cores",
valueOne: monitor?.checks[0]?.cpu?.physical_core,
valueOne: monitor?.checks[0]?.cpu?.physical_core ?? 0,
metricTwo: "Frequency",
valueTwo: `${(monitor?.checks[0]?.cpu?.frequency / 1000).toFixed(2)} Ghz`,
valueTwo: `${(monitor?.checks[0]?.cpu?.frequency ?? 0 / 1000).toFixed(2)} Ghz`,
},
...(monitor?.checks[0]?.disk ?? []).map((disk, idx) => ({
...(monitor?.checks?.[0]?.disk ?? []).map((disk, idx) => ({
type: "disk",
diskIndex: idx,
value: disk.usage_percent * 100,
value: decimalToPercentage(disk.usage_percent),
heading: `Disk${idx} usage`,
metricOne: "Used",
valueOne: formatBytes(disk.total_bytes - disk.free_bytes),
@@ -278,9 +309,9 @@ const InfrastructureDetails = () => {
];
return (
monitor && (
<Box>
<Breadcrumbs list={navList} />
<Box>
<Breadcrumbs list={navList} />
{monitor?.checks?.length > 0 ? (
<Stack
direction="column"
gap={theme.spacing(10)}
@@ -386,8 +417,18 @@ const InfrastructureDetails = () => {
))}
</Stack>
</Stack>
</Box>
)
) : (
<Empty
styles={{
border: 1,
borderColor: theme.palette.border.light,
borderRadius: theme.shape.borderRadius,
backgroundColor: theme.palette.background.main,
p: theme.spacing(30),
}}
/>
)}
</Box>
);
};
+8
View File
@@ -44,6 +44,14 @@ class Logger {
this.log = NO_OP;
return;
}
if (logLevel === "debug") {
this.error = console.error.bind(console);
this.warn = console.warn.bind(console);
this.info = console.info.bind(console);
this.log = console.log.bind(console);
return;
}
}
cleanup() {