Merge branch 'bluewave-labs:develop' into Maintenance-start-time-style

This commit is contained in:
Owaise Imdad authored and GitHub committed 2025-03-07 00:30:40 +05:30
commit ffcc32eaf2
21 files changed
+145 -123

No files matched your search

+25 -25
View File
@@ -12,9 +12,9 @@
"@emotion/styled": "^11.13.0",
"@fontsource/roboto": "^5.0.13",
"@hello-pangea/dnd": "^18.0.0",
"@mui/icons-material": "6.4.6",
"@mui/lab": "6.0.0-beta.29",
"@mui/material": "6.4.6",
"@mui/icons-material": "6.4.7",
"@mui/lab": "6.0.0-beta.30",
"@mui/material": "6.4.7",
"@mui/x-charts": "^7.5.1",
"@mui/x-data-grid": "7.27.2",
"@mui/x-date-pickers": "7.27.1",
@@ -3516,9 +3516,9 @@
}
},
"node_modules/@mui/core-downloads-tracker": {
"version": "6.4.6",
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.6.tgz",
"integrity": "sha512-rho5Q4IscbrVmK9rCrLTJmjLjfH6m/NcqKr/mchvck0EIXlyYUB9+Z0oVmkt/+Mben43LMRYBH8q/Uzxj/c4Vw==",
"version": "6.4.7",
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.7.tgz",
"integrity": "sha512-XjJrKFNt9zAKvcnoIIBquXyFyhfrHYuttqMsoDS7lM7VwufYG4fAPw4kINjBFg++fqXM2BNAuWR9J7XVIuKIKg==",
"license": "MIT",
"funding": {
"type": "opencollective",
@@ -3526,9 +3526,9 @@
}
},
"node_modules/@mui/icons-material": {
"version": "6.4.6",
"resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.6.tgz",
"integrity": "sha512-rGJBvIQQbQAlyKYljHQ8wAQS/K2/uYwvemcpygnAmCizmCI4zSF9HQPuiG8Ql4YLZ6V/uKjA3WHIYmF/8sV+pQ==",
"version": "6.4.7",
"resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.7.tgz",
"integrity": "sha512-Rk8cs9ufQoLBw582Rdqq7fnSXXZTqhYRbpe1Y5SAz9lJKZP3CIdrj0PfG8HJLGw1hrsHFN/rkkm70IDzhJsG1g==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.26.0"
@@ -3541,7 +3541,7 @@
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@mui/material": "^6.4.6",
"@mui/material": "^6.4.7",
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
@@ -3552,14 +3552,14 @@
}
},
"node_modules/@mui/lab": {
"version": "6.0.0-beta.29",
"resolved": "https://registry.npmjs.org/@mui/lab/-/lab-6.0.0-beta.29.tgz",
"integrity": "sha512-C5nTm9xfzVi4Zz4DLrZq1Kn+cQ2fGZmSJxyoUbOu+jDPlLZrUpSsgxEkkbjJB5dasiv6fgGKhO3EaKKyfobLAQ==",
"version": "6.0.0-beta.30",
"resolved": "https://registry.npmjs.org/@mui/lab/-/lab-6.0.0-beta.30.tgz",
"integrity": "sha512-ayDYkzTlkm5cnDGa10bvuFygX+2b9Hm1T4QZYMqV8+nSx3frKE0TLAbE7/qQ4vInOO5E4aOkHVBwZjyO+UbMTA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.26.0",
"@mui/base": "5.0.0-beta.69",
"@mui/system": "^6.4.6",
"@mui/system": "^6.4.7",
"@mui/types": "^7.2.21",
"@mui/utils": "^6.4.6",
"clsx": "^2.1.1",
@@ -3575,8 +3575,8 @@
"peerDependencies": {
"@emotion/react": "^11.5.0",
"@emotion/styled": "^11.3.0",
"@mui/material": "^6.4.6",
"@mui/material-pigment-css": "^6.4.6",
"@mui/material": "^6.4.7",
"@mui/material-pigment-css": "^6.4.7",
"@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"
@@ -3597,14 +3597,14 @@
}
},
"node_modules/@mui/material": {
"version": "6.4.6",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.6.tgz",
"integrity": "sha512-6UyAju+DBOdMogfYmLiT3Nu7RgliorimNBny1pN/acOjc+THNFVE7hlxLyn3RDONoZJNDi/8vO4AQQr6dLAXqA==",
"version": "6.4.7",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.7.tgz",
"integrity": "sha512-K65StXUeGAtFJ4ikvHKtmDCO5Ab7g0FZUu2J5VpoKD+O6Y3CjLYzRi+TMlI3kaL4CL158+FccMoOd/eaddmeRQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.26.0",
"@mui/core-downloads-tracker": "^6.4.6",
"@mui/system": "^6.4.6",
"@mui/core-downloads-tracker": "^6.4.7",
"@mui/system": "^6.4.7",
"@mui/types": "^7.2.21",
"@mui/utils": "^6.4.6",
"@popperjs/core": "^2.11.8",
@@ -3625,7 +3625,7 @@
"peerDependencies": {
"@emotion/react": "^11.5.0",
"@emotion/styled": "^11.3.0",
"@mui/material-pigment-css": "^6.4.6",
"@mui/material-pigment-css": "^6.4.7",
"@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"
@@ -3713,9 +3713,9 @@
}
},
"node_modules/@mui/system": {
"version": "6.4.6",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.6.tgz",
"integrity": "sha512-FQjWwPec7pMTtB/jw5f9eyLynKFZ6/Ej9vhm5kGdtmts1z5b7Vyn3Rz6kasfYm1j2TfrfGnSXRvvtwVWxjpz6g==",
"version": "6.4.7",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.7.tgz",
"integrity": "sha512-7wwc4++Ak6tGIooEVA9AY7FhH2p9fvBMORT4vNLMAysH3Yus/9B9RYMbrn3ANgsOyvT3Z7nE+SP8/+3FimQmcg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.26.0",
+3 -3
View File
@@ -18,9 +18,9 @@
"@mui/x-charts": "^7.5.1",
"@mui/x-data-grid": "7.27.2",
"@mui/x-date-pickers": "7.27.1",
"@mui/icons-material": "6.4.6",
"@mui/lab": "6.0.0-beta.29",
"@mui/material": "6.4.6",
"@mui/icons-material": "6.4.7",
"@mui/lab": "6.0.0-beta.30",
"@mui/material": "6.4.7",
"@reduxjs/toolkit": "2.6.0",
"@solana/wallet-adapter-base": "0.9.23",
"@solana/wallet-adapter-material-ui": "0.16.34",
@@ -13,6 +13,7 @@ export const tooltipDateFormatLookup = (dateRange) => {
export const tickDateFormatLookup = (dateRange) => {
const tickFormatLookup = {
recent: "h:mm A",
day: "h:mm A",
week: "MM/D, h:mm A",
month: "ddd. M/D",
+31
View File
@@ -18,6 +18,7 @@ import "./index.css";
* @param {string} props.items.name - The display name of each item.
* @param {function} props.onChange - The function to handle onChange event.
* @param {object} props.sx - The custom styles object for MUI Select component.
* @param {number} props.maxWidth - Maximum width in pixels for the select component. Enables responsive text truncation.
* @returns {JSX.Element}
*
* @example
@@ -50,6 +51,7 @@ const Select = ({
sx,
name = "",
labelControlSpacing = 2,
maxWidth,
}) => {
const theme = useTheme();
const itemStyles = {
@@ -59,6 +61,13 @@ const Select = ({
margin: theme.spacing(2),
};
const responsiveMaxWidth = {
xs: `${maxWidth * 0.5}px`,
sm: `${maxWidth * 0.75}px`,
md: `${maxWidth * 0.9}px`,
lg: `${maxWidth}px`,
};
return (
<Stack
gap={theme.spacing(labelControlSpacing)}
@@ -87,6 +96,7 @@ const Select = ({
sx={{
fontSize: 13,
minWidth: "125px",
...(maxWidth && { maxWidth: responsiveMaxWidth }),
"& fieldset": {
borderRadius: theme.shape.borderRadius,
borderColor: theme.palette.primary.lowContrast,
@@ -99,6 +109,22 @@ const Select = ({
},
...sx,
}}
renderValue={(selected) => {
const selectedItem = items.find((item) => item._id === selected);
const displayName = selectedItem ? selectedItem.name : placeholder;
return (
<Typography
sx={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
}}
title={displayName}
>
{displayName}
</Typography>
);
}}
>
{placeholder && (
<MenuItem
@@ -146,6 +172,11 @@ Select.propTypes = {
onBlur: PropTypes.func,
sx: PropTypes.object,
labelControlSpacing: PropTypes.number,
/**
* Maximum width in pixels. Used to control text truncation and element width.
* Responsive breakpoints will be calculated as percentages of this value.
*/
maxWidth: PropTypes.number,
};
export default Select;
@@ -20,6 +20,13 @@ const MonitorTimeFrameHeader = ({
if (hasDateRange) {
timeFramePicker = (
<ButtonGroup sx={{ height: 32 }}>
<Button
variant="group"
filled={(dateRange === "recent").toString()}
onClick={() => setDateRange("recent")}
>
Recent
</Button>
<Button
variant="group"
filled={(dateRange === "day").toString()}
@@ -55,7 +62,14 @@ const MonitorTimeFrameHeader = ({
>
<Typography variant="body2">
Showing statistics for past{" "}
{dateRange === "day" ? "24 hours" : dateRange === "week" ? "7 days" : "30 days"}.
{dateRange === "recent"
? "2 hours"
: dateRange === "day"
? "24 hours"
: dateRange === "week"
? "7 days"
: "30 days"}
.
</Typography>
{timeFramePicker}
</Stack>
+1 -1
View File
@@ -63,7 +63,7 @@ const menu = [
{ name: "Status pages", path: "status", icon: <StatusPages /> },
{ name: "Maintenance", path: "maintenance", icon: <Maintenance /> },
{ name: "Integrations", path: "integrations", icon: <Integrations /> },
// { name: "Integrations", path: "integrations", icon: <Integrations /> },
{
name: "Settings",
icon: <Settings />,
@@ -5,16 +5,14 @@ import "flag-icons/css/flag-icons.min.css";
import { ColContainer } from "../../../../../Components/StandardContainer";
// Utils
import { useMediaQuery } from "@mui/material";
import { useTheme } from "@emotion/react";
import { safelyParseFloat } from "../../../../../Utils/utils";
const DeviceTicker = ({ data, width = "100%", connectionStatus }) => {
const theme = useTheme();
const isSmallScreen = useMediaQuery(theme.breakpoints.down("sm"));
const statusColor = {
up: theme.palette.success.main,
down: theme.palette.error.main,
undefined: theme.palette.warning.main,
};
return (
@@ -30,7 +28,7 @@ const DeviceTicker = ({ data, width = "100%", connectionStatus }) => {
mb={theme.spacing(8)}
sx={{ alignSelf: "center" }}
>
{connectionStatus === "up" ? "Connected" : "No connection"}
{connectionStatus === "up" ? "Connected" : "Connecting..."}
</Typography>
</Stack>
<div
@@ -63,7 +63,7 @@ const DistributedUptimeResponseAreaChart = ({ checks }) => {
</defs>
<XAxis
stroke={theme.palette.primary.lowContrast}
dataKey="_id"
dataKey="_id.date"
tick={<CustomTick />}
minTickGap={0}
axisLine={false}
@@ -67,7 +67,7 @@ const DistributedUptimeResponseBarChart = ({ checks }) => {
/>
<XAxis
stroke={theme.palette.primary.lowContrast}
dataKey="_id"
dataKey="_id.date"
tick={<CustomTick />}
minTickGap={0}
axisLine={false}
@@ -1,49 +1,7 @@
import { useState, useEffect, useRef } from "react";
import { useState, useEffect } from "react";
import { networkService } from "../../../../main";
import { createToast } from "../../../../Utils/toastUtils";
const getRandomDevice = () => {
const manufacturers = {
Apple: ["iPhone 15 Pro Max", "iPhone 15", "iPhone 14 Pro", "iPhone 14", "iPhone 13"],
Samsung: [
"Galaxy S23 Ultra",
"Galaxy S23+",
"Galaxy S23",
"Galaxy Z Fold5",
"Galaxy Z Flip5",
],
Google: ["Pixel 8 Pro", "Pixel 8", "Pixel 7a", "Pixel 7", "Pixel 6a"],
OnePlus: [
"OnePlus 11",
"OnePlus 10T",
"OnePlus Nord 3",
"OnePlus 10 Pro",
"OnePlus Nord 2T",
],
Xiaomi: ["13 Pro", "13", "Redmi Note 12", "POCO F5", "Redmi 12"],
Huawei: ["P60 Pro", "Mate X3", "Nova 11", "P50 Pro", "Mate 50"],
Sony: ["Xperia 1 V", "Xperia 5 V", "Xperia 10 V", "Xperia Pro-I", "Xperia 1 IV"],
Motorola: ["Edge 40 Pro", "Edge 40", "G84", "G54", "Razr 40 Ultra"],
ASUS: [
"ROG Phone 7",
"Zenfone 10",
"ROG Phone 6",
"Zenfone 9",
"ROG Phone 7 Ultimate",
],
};
const manufacturerNames = Object.keys(manufacturers);
const randomManufacturer =
manufacturerNames[Math.floor(Math.random() * manufacturerNames.length)];
const models = manufacturers[randomManufacturer];
const randomModel = models[Math.floor(Math.random() * models.length)];
return {
manufacturer: randomManufacturer,
model: randomModel,
};
};
const useSubscribeToDetails = ({ monitorId, isPublic, isPublished, dateRange }) => {
const [isLoading, setIsLoading] = useState(true);
const [connectionStatus, setConnectionStatus] = useState(undefined);
@@ -51,9 +9,6 @@ const useSubscribeToDetails = ({ monitorId, isPublic, isPublished, dateRange })
const [networkError, setNetworkError] = useState(false);
const [monitor, setMonitor] = useState(undefined);
const [lastUpdateTrigger, setLastUpdateTrigger] = useState(0);
const [devices, setDevices] = useState(Array.from({ length: 5 }, getRandomDevice));
const prevDateRangeRef = useRef(dateRange);
useEffect(() => {
if (typeof monitorId === "undefined") {
@@ -64,6 +19,36 @@ const useSubscribeToDetails = ({ monitorId, isPublic, isPublished, dateRange })
return;
}
// Get initial data
const fetchInitialData = async () => {
try {
const res = await networkService.getDistributedUptimeDetails({
monitorId,
dateRange: dateRange,
normalize: true,
});
const responseData = res?.data?.data;
if (typeof responseData === "undefined") {
throw new Error("No data");
}
setConnectionStatus("up");
setLastUpdateTrigger(Date.now());
setMonitor(responseData);
} catch (error) {
setNetworkError(true);
setConnectionStatus("down");
createToast({
body: error.message,
});
} finally {
setIsLoading(false);
}
};
fetchInitialData();
try {
const cleanup = networkService.subscribeToDistributedUptimeDetails({
monitorId,
@@ -77,15 +62,7 @@ const useSubscribeToDetails = ({ monitorId, isPublic, isPublished, dateRange })
setNetworkError(false);
}
setLastUpdateTrigger(Date.now());
const latestChecksWithDevice = data?.monitor?.latestChecks.map((check, idx) => {
check.device = devices[idx];
return check;
});
const monitorWithDevice = {
...data.monitor,
latestChecks: latestChecksWithDevice,
};
setMonitor(monitorWithDevice);
setMonitor(data.monitor);
},
onOpen: () => {
setConnectionStatus("up");
@@ -107,21 +84,11 @@ const useSubscribeToDetails = ({ monitorId, isPublic, isPublished, dateRange })
retryCount,
setConnectionStatus,
networkError,
devices,
isLoading,
isPublic,
isPublished,
]);
useEffect(() => {
const hasDateRangeChanged = prevDateRangeRef.current !== dateRange;
prevDateRangeRef.current = dateRange; // Update the ref to the current dateRange
if (!hasDateRangeChanged) {
setDevices(Array.from({ length: 5 }, getRandomDevice));
}
}, [dateRange]);
return [isLoading, networkError, connectionStatus, monitor, lastUpdateTrigger];
};
@@ -26,7 +26,7 @@ import { useNavigate } from "react-router-dom";
const DistributedUptimeDetails = () => {
const { monitorId } = useParams();
// Local State
const [dateRange, setDateRange] = useState("day");
const [dateRange, setDateRange] = useState("recent");
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
// Utils
@@ -75,7 +75,6 @@ const DistributedUptimeDetails = () => {
</Stack>
);
}
return (
<Stack
direction="column"
@@ -25,11 +25,7 @@ const useSubscribeToMonitors = (page, rowsPerPage) => {
const initialDataRes = await networkService.getDistributedUptimeMonitors({
teamId: user.teamId,
limit: 25,
types: [
typeof import.meta.env.VITE_DEPIN_TESTING === "undefined"
? "distributed_http"
: "distributed_test",
],
types: ["distributed_http"],
page,
rowsPerPage,
});
@@ -18,7 +18,6 @@ import InfoBox from "../../../Components/InfoBox";
import StatusHeader from "../../DistributedUptime/Details/Components/StatusHeader";
import MonitorsList from "./Components/MonitorsList";
import { RowContainer } from "../../../Components/StandardContainer";
import ThemeSwitch from "../../../Components/ThemeSwitch";
//Utils
import { useTheme } from "@mui/material/styles";
@@ -50,7 +49,7 @@ const DistributedUptimeStatus = () => {
const originalModeRef = useRef(null);
// Local State
const [dateRange, setDateRange] = useState("day");
const [dateRange, setDateRange] = useState("recent");
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
const [timeFrame, setTimeFrame] = useState(30);
// Utils
@@ -255,7 +254,7 @@ const DistributedUptimeStatus = () => {
/>
<InfoBox
heading={isSmallScreen ? "UPT" : "UPT Burned"}
subHeading={safelyParseFloat(monitor?.totalUptBurnt).toFixed(4)}
subHeading={safelyParseFloat(monitor?.uptBurnt).toFixed(4)}
img={UptLogo}
alt="Upt Logo"
sx={{ width: "50%" }}
@@ -27,8 +27,11 @@ const IncidentTable = ({
//Local state
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const selectedMonitorDetails = monitors?.[selectedMonitor];
const selectedMonitorType = selectedMonitorDetails?.type;
const { isLoading, networkError, checks, checksCount } = useChecksFetch({
selectedMonitor,
selectedMonitorType,
filter,
dateRange,
page,
@@ -52,7 +52,7 @@ const OptionsHeader = ({
backgroundColor: theme.palette.primary.main,
color: theme.palette.primary.contrastTextSecondary,
}}
truncate={true}
maxWidth={250}
/>
</Stack>
<Stack {...stackStyles}>
+3 -2
View File
@@ -2,7 +2,7 @@ import { useState, useEffect } from "react";
import { networkService } from "../../../main";
import { createToast } from "../../../Utils/toastUtils";
import { useSelector } from "react-redux";
const useChecksFetch = ({ selectedMonitor, filter, dateRange, page, rowsPerPage }) => {
const useChecksFetch = ({ selectedMonitor, selectedMonitorType, filter, dateRange, page, rowsPerPage }) => {
//Redux
const { user } = useSelector((state) => state.auth);
@@ -33,6 +33,7 @@ const useChecksFetch = ({ selectedMonitor, filter, dateRange, page, rowsPerPage
res = await networkService.getChecksByMonitor({
status: false,
monitorId: selectedMonitor,
type: selectedMonitorType,
sortOrder: "desc",
limit: null,
dateRange,
@@ -51,7 +52,7 @@ const useChecksFetch = ({ selectedMonitor, filter, dateRange, page, rowsPerPage
}
};
fetchChecks();
}, [user, dateRange, page, rowsPerPage, filter, selectedMonitor]);
}, [user, dateRange, page, rowsPerPage, filter, selectedMonitor, selectedMonitorType]);
return { isLoading, networkError, checks, checksCount };
};
@@ -25,9 +25,13 @@ const useMonitorsFetch = ({ teamId }) => {
field: null,
order: null,
});
if (res?.data?.data?.monitors?.length > 0) {
const monitorLookup = res.data.data.monitors.reduce((acc, monitor) => {
acc[monitor._id] = monitor;
if (res?.data?.data?.filteredMonitors?.length > 0) {
const monitorLookup = res.data.data.filteredMonitors.reduce((acc, monitor) => {
acc[monitor._id] = {
_id: monitor._id,
name: monitor.name,
type: monitor.type
};
return acc;
}, {});
setMonitors(monitorLookup);
@@ -1,5 +1,4 @@
import { useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { networkService } from "../../../../main";
const useHardwareMonitorsFetch = ({ monitorId, dateRange }) => {
+1 -2
View File
@@ -24,11 +24,10 @@ const InfrastructureDetails = () => {
// Redux state
// Local state
const [dateRange, setDateRange] = useState("day");
const [dateRange, setDateRange] = useState("recent");
// Utils
const theme = useTheme();
const isAdmin = useIsAdmin();
const { monitorId } = useParams();
const { isLoading, networkError, monitor } = useHardwareMonitorsFetch({
+3 -2
View File
@@ -34,14 +34,15 @@ const UptimeDetails = () => {
const uiTimezone = useSelector((state) => state.ui.timezone);
// Local state
const [dateRange, setDateRange] = useState("day");
const [dateRange, setDateRange] = useState("recent");
const [hoveredUptimeData, setHoveredUptimeData] = useState(null);
const [hoveredIncidentsData, setHoveredIncidentsData] = useState(null);
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(5);
// Utils
const dateFormat = dateRange === "day" ? "MMM D, h A" : "MMM D";
const dateFormat =
dateRange === "day" || dateRange === "recent" ? "MMM D, h A" : "MMM D";
const { monitorId } = useParams();
const theme = useTheme();
const isAdmin = useIsAdmin();
+10
View File
@@ -567,6 +567,7 @@ class NetworkService {
async getChecksByMonitor(config) {
const params = new URLSearchParams();
if (config.type) params.append("type", config.type);
if (config.sortOrder) params.append("sortOrder", config.sortOrder);
if (config.limit) params.append("limit", config.limit);
if (config.dateRange) params.append("dateRange", config.dateRange);
@@ -897,6 +898,15 @@ class NetworkService {
};
}
getDistributedUptimeDetails(config) {
const params = new URLSearchParams();
const { monitorId, onUpdate, onOpen, onError, dateRange, normalize } = config;
if (dateRange) params.append("dateRange", dateRange);
if (normalize) params.append("normalize", normalize);
const url = `${this.axiosInstance.defaults.baseURL}/distributed-uptime/monitors/details/${monitorId}/initial?${params.toString()}`;
return this.axiosInstance.get(url);
}
subscribeToDistributedUptimeDetails(config) {
const params = new URLSearchParams();
const { monitorId, onUpdate, onOpen, onError, dateRange, normalize } = config;