diff --git a/Client/src/Components/Inputs/Search/index.jsx b/Client/src/Components/Inputs/Search/index.jsx
index b1e5d74ae..3de2cbe07 100644
--- a/Client/src/Components/Inputs/Search/index.jsx
+++ b/Client/src/Components/Inputs/Search/index.jsx
@@ -1,12 +1,5 @@
import PropTypes from "prop-types";
-import {
- Box,
- ListItem,
- Autocomplete,
- TextField,
- Stack,
- Typography,
-} from "@mui/material";
+import { Box, ListItem, Autocomplete, TextField, Stack, Typography } from "@mui/material";
import { useTheme } from "@emotion/react";
import SearchIcon from "../../../assets/icons/search.svg?react";
@@ -24,172 +17,172 @@ import SearchIcon from "../../../assets/icons/search.svg?react";
*/
const SearchAdornment = () => {
- const theme = useTheme();
- return (
-
-
-
- );
+ const theme = useTheme();
+ return (
+
+
+
+ );
};
+//TODO keep search state inside of component
const Search = ({
- id,
- options,
- filteredBy,
- secondaryLabel,
- value,
- inputValue,
- handleInputChange,
- handleChange,
- sx,
- multiple = false,
- isAdorned = true,
- error,
- disabled,
+ id,
+ options,
+ filteredBy,
+ secondaryLabel,
+ value,
+ inputValue,
+ handleInputChange,
+ handleChange,
+ sx,
+ multiple = false,
+ isAdorned = true,
+ error,
+ disabled,
}) => {
- const theme = useTheme();
+ const theme = useTheme();
- return (
- {
- handleInputChange(newValue);
- }}
- onChange={(_, newValue) => {
- handleChange && handleChange(newValue);
- }}
- fullWidth
- freeSolo
- disabled={disabled}
- disableClearable
- options={options}
- getOptionLabel={(option) => option[filteredBy]}
- renderInput={(params) => (
-
- }),
- }}
- sx={{
- "& fieldset": {
- borderColor: theme.palette.border.light,
- borderRadius: theme.shape.borderRadius,
- },
- "& .MuiOutlinedInput-root:hover:not(:has(input:focus)):not(:has(textarea:focus)) fieldset":
- {
- borderColor: theme.palette.border.light,
- },
- }}
- />
- {error && (
-
- {error}
-
- )}
-
- )}
- filterOptions={(options, { inputValue }) => {
- const filtered = options.filter((option) =>
- option[filteredBy].toLowerCase().includes(inputValue.toLowerCase())
- );
+ return (
+ {
+ handleInputChange(newValue);
+ }}
+ onChange={(_, newValue) => {
+ handleChange && handleChange(newValue);
+ }}
+ fullWidth
+ freeSolo
+ disabled={disabled}
+ disableClearable
+ options={options}
+ getOptionLabel={(option) => option[filteredBy]}
+ renderInput={(params) => (
+
+ }),
+ }}
+ sx={{
+ "& fieldset": {
+ borderColor: theme.palette.border.light,
+ borderRadius: theme.shape.borderRadius,
+ },
+ "& .MuiOutlinedInput-root:hover:not(:has(input:focus)):not(:has(textarea:focus)) fieldset":
+ {
+ borderColor: theme.palette.border.light,
+ },
+ }}
+ />
+ {error && (
+
+ {error}
+
+ )}
+
+ )}
+ filterOptions={(options, { inputValue }) => {
+ const filtered = options.filter((option) =>
+ option[filteredBy].toLowerCase().includes(inputValue.toLowerCase())
+ );
- if (filtered.length === 0) {
- return [{ [filteredBy]: "No monitors found", noOptions: true }];
- }
- return filtered;
- }}
- renderOption={(props, option) => {
- const { key, ...optionProps } = props;
- return (
-
- {option[filteredBy] +
- (secondaryLabel ? ` (${option[secondaryLabel]})` : "")}
-
- );
- }}
- slotProps={{
- popper: {
- keepMounted: true,
- sx: {
- "& ul": { p: 2 },
- "& li.MuiAutocomplete-option": {
- color: theme.palette.text.secondary,
- px: 4,
- borderRadius: theme.shape.borderRadius,
- },
- "& .MuiAutocomplete-listbox .MuiAutocomplete-option[aria-selected='true'], & .MuiAutocomplete-listbox .MuiAutocomplete-option[aria-selected='true'].Mui-focused, & .MuiAutocomplete-listbox .MuiAutocomplete-option[aria-selected='true']:hover":
- {
- backgroundColor: theme.palette.background.fill,
- },
- "& .MuiAutocomplete-noOptions": {
- px: theme.spacing(6),
- py: theme.spacing(5),
- },
- },
- },
- }}
- sx={{
- height: 34,
- "&.MuiAutocomplete-root .MuiAutocomplete-input": { p: 0 },
- ...sx,
- }}
- />
- );
+ if (filtered.length === 0) {
+ return [{ [filteredBy]: "No monitors found", noOptions: true }];
+ }
+ return filtered;
+ }}
+ renderOption={(props, option) => {
+ const { key, ...optionProps } = props;
+ return (
+
+ {option[filteredBy] + (secondaryLabel ? ` (${option[secondaryLabel]})` : "")}
+
+ );
+ }}
+ slotProps={{
+ popper: {
+ keepMounted: true,
+ sx: {
+ "& ul": { p: 2 },
+ "& li.MuiAutocomplete-option": {
+ color: theme.palette.text.secondary,
+ px: 4,
+ borderRadius: theme.shape.borderRadius,
+ },
+ "& .MuiAutocomplete-listbox .MuiAutocomplete-option[aria-selected='true'], & .MuiAutocomplete-listbox .MuiAutocomplete-option[aria-selected='true'].Mui-focused, & .MuiAutocomplete-listbox .MuiAutocomplete-option[aria-selected='true']:hover":
+ {
+ backgroundColor: theme.palette.background.fill,
+ },
+ "& .MuiAutocomplete-noOptions": {
+ px: theme.spacing(6),
+ py: theme.spacing(5),
+ },
+ },
+ },
+ }}
+ sx={{
+ height: 34,
+ "&.MuiAutocomplete-root .MuiAutocomplete-input": { p: 0 },
+ ...sx,
+ }}
+ />
+ );
};
Search.propTypes = {
- id: PropTypes.string,
- multiple: PropTypes.bool,
- options: PropTypes.array.isRequired,
- filteredBy: PropTypes.string.isRequired,
- secondaryLabel: PropTypes.string,
- value: PropTypes.array,
- inputValue: PropTypes.string.isRequired,
- handleInputChange: PropTypes.func.isRequired,
- handleChange: PropTypes.func,
- isAdorned: PropTypes.bool,
- sx: PropTypes.object,
- error: PropTypes.string,
- disabled: PropTypes.bool,
+ id: PropTypes.string,
+ multiple: PropTypes.bool,
+ options: PropTypes.array.isRequired,
+ filteredBy: PropTypes.string.isRequired,
+ secondaryLabel: PropTypes.string,
+ value: PropTypes.array,
+ inputValue: PropTypes.string.isRequired,
+ handleInputChange: PropTypes.func.isRequired,
+ handleChange: PropTypes.func,
+ isAdorned: PropTypes.bool,
+ sx: PropTypes.object,
+ error: PropTypes.string,
+ disabled: PropTypes.bool,
};
export default Search;
diff --git a/Client/src/Pages/Monitors/Home/CurrentMonitoring/index.jsx b/Client/src/Pages/Monitors/Home/CurrentMonitoring/index.jsx
new file mode 100644
index 000000000..613c280a4
--- /dev/null
+++ b/Client/src/Pages/Monitors/Home/CurrentMonitoring/index.jsx
@@ -0,0 +1,79 @@
+import { useTheme } from "@emotion/react";
+import { Box, Stack, Typography } from "@mui/material";
+import Search from "../../../../Components/Inputs/Search";
+import MemoizedMonitorTable from "../MonitorTable";
+import { useState } from "react";
+import useDebounce from "../../../../Utils/debounce";
+import PropTypes from "prop-types";
+
+const CurrentMonitoring = ({ totalMonitors, monitors, isAdmin }) => {
+ const theme = useTheme();
+ const [search, setSearch] = useState("");
+ const [isSearching, setIsSearching] = useState(false);
+ const debouncedFilter = useDebounce(search, 500);
+ const handleSearch = (value) => {
+ setIsSearching(true);
+ setSearch(value);
+ };
+ return (
+
+
+
+ Actively monitoring
+
+
+ {totalMonitors}
+
+
+
+
+
+
+
+ );
+};
+
+CurrentMonitoring.propTypes = {
+ totalMonitors: PropTypes.number,
+ monitors: PropTypes.array,
+ isAdmin: PropTypes.bool,
+};
+
+export { CurrentMonitoring };
diff --git a/Client/src/Pages/Monitors/Home/MonitorTable/index.jsx b/Client/src/Pages/Monitors/Home/MonitorTable/index.jsx
index b5e36f634..d9fbc34b2 100644
--- a/Client/src/Pages/Monitors/Home/MonitorTable/index.jsx
+++ b/Client/src/Pages/Monitors/Home/MonitorTable/index.jsx
@@ -109,7 +109,7 @@ TablePaginationActions.propTypes = {
onPageChange: PropTypes.func.isRequired,
};
-const MonitorTable = ({ isAdmin, filter, setLoading, isSearching }) => {
+const MonitorTable = ({ isAdmin, filter, setIsSearching, isSearching }) => {
const theme = useTheme();
const navigate = useNavigate();
const dispatch = useDispatch();
@@ -162,15 +162,25 @@ const MonitorTable = ({ isAdmin, filter, setLoading, isSearching }) => {
});
setMonitors(res?.data?.data?.monitors ?? []);
setMonitorCount(res?.data?.data?.monitorCount ?? 0);
- setLoading(false);
} catch (error) {
logger.error(error);
+ } finally {
+ setIsSearching(false);
}
- }, [authState, page, rowsPerPage, filter, sort, setLoading]);
+ }, [authState, page, rowsPerPage, filter, sort, setIsSearching]);
useEffect(() => {
fetchPage();
- }, [updateTrigger, authState, page, rowsPerPage, filter, sort, setLoading, fetchPage]);
+ }, [
+ updateTrigger,
+ authState,
+ page,
+ rowsPerPage,
+ filter,
+ sort,
+ setIsSearching,
+ fetchPage,
+ ]);
// Listen for changes in filter, if new value reset the page
useEffect(() => {
@@ -459,7 +469,7 @@ const MonitorTable = ({ isAdmin, filter, setLoading, isSearching }) => {
MonitorTable.propTypes = {
isAdmin: PropTypes.bool,
filter: PropTypes.string,
- setLoading: PropTypes.func,
+ setIsSearching: PropTypes.func,
isSearching: PropTypes.bool,
};
diff --git a/Client/src/Pages/Monitors/Home/index.jsx b/Client/src/Pages/Monitors/Home/index.jsx
index c2413f6cc..8bd1d7275 100644
--- a/Client/src/Pages/Monitors/Home/index.jsx
+++ b/Client/src/Pages/Monitors/Home/index.jsx
@@ -1,19 +1,17 @@
import "./index.css";
-import { useEffect, useState } from "react";
+import { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { getUptimeMonitorsByTeamId } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice";
import { useNavigate } from "react-router-dom";
import { useTheme } from "@emotion/react";
-import { Box, Button, Stack, Typography } from "@mui/material";
+import { Box, Button, Stack } from "@mui/material";
import PropTypes from "prop-types";
import SkeletonLayout from "./skeleton";
import Fallback from "./fallback";
import StatusBox from "./StatusBox";
import Breadcrumbs from "../../../Components/Breadcrumbs";
import Greeting from "../../../Utils/greeting";
-import MonitorTable from "./MonitorTable";
-import Search from "../../../Components/Inputs/Search";
-import useDebounce from "../../../Utils/debounce";
+import { CurrentMonitoring } from "./CurrentMonitoring";
const Monitors = ({ isAdmin }) => {
const theme = useTheme();
@@ -22,20 +20,12 @@ const Monitors = ({ isAdmin }) => {
const authState = useSelector((state) => state.auth);
const dispatch = useDispatch({});
- //TODO create components, and lower these states.
- const [search, setSearch] = useState("");
- const [isSearching, setIsSearching] = useState(false);
- const debouncedFilter = useDebounce(search, 500);
-
- const handleSearch = (value) => {
- setIsSearching(true);
- setSearch(value);
- };
-
useEffect(() => {
dispatch(getUptimeMonitorsByTeamId(authState.authToken));
}, [authState.authToken, dispatch]);
+ //TODO bring fetching to this component, like on pageSpeed
+
const loading = monitorState?.isLoading;
const totalMonitors = monitorState?.monitorsSummary?.monitorCounts?.total;
@@ -100,57 +90,11 @@ const Monitors = ({ isAdmin }) => {
value={monitorState?.monitorsSummary?.monitorCounts?.paused ?? 0}
/>
-
-
-
- Actively monitoring
-
-
- {totalMonitors}
-
-
-
-
-
-
-
+
>
)}
>
diff --git a/Client/src/Pages/PageSpeed/index.jsx b/Client/src/Pages/PageSpeed/index.jsx
index 3174013ef..2b5e60aad 100644
--- a/Client/src/Pages/PageSpeed/index.jsx
+++ b/Client/src/Pages/PageSpeed/index.jsx
@@ -14,114 +14,121 @@ import Card from "./card";
import { networkService } from "../../main";
const PageSpeed = ({ isAdmin }) => {
- const theme = useTheme();
- const dispatch = useDispatch();
- const navigate = useNavigate();
+ const theme = useTheme();
+ const dispatch = useDispatch();
+ const navigate = useNavigate();
- const { user, authToken } = useSelector((state) => state.auth);
- const [isLoading, setIsLoading] = useState(false);
- const [monitors, setMonitors] = useState([]);
- useEffect(() => {
- dispatch(getPageSpeedByTeamId(authToken));
- }, [authToken, dispatch]);
+ const { user, authToken } = useSelector((state) => state.auth);
+ const [isLoading, setIsLoading] = useState(true);
+ const [monitors, setMonitors] = useState([]);
+ useEffect(() => {
+ dispatch(getPageSpeedByTeamId(authToken));
+ }, [authToken, dispatch]);
- useEffect(() => {
- const fetchMonitors = async () => {
- try {
- setIsLoading(true);
- const res = await networkService.getMonitorsByTeamId({
- authToken: authToken,
- teamId: user.teamId,
- limit: 10,
- types: ["pagespeed"],
- status: null,
- checkOrder: "desc",
- normalize: true,
- page: null,
- rowsPerPage: null,
- filter: null,
- field: null,
- order: null,
- });
- if (res?.data?.data?.monitors) {
- setMonitors(res.data.data.monitors);
- }
- } catch (error) {
- console.log(error);
- } finally {
- setIsLoading(false);
- }
- };
+ useEffect(() => {
+ const fetchMonitors = async () => {
+ try {
+ setIsLoading(true);
+ const res = await networkService.getMonitorsByTeamId({
+ authToken: authToken,
+ teamId: user.teamId,
+ limit: 10,
+ types: ["pagespeed"],
+ status: null,
+ checkOrder: "desc",
+ normalize: true,
+ page: null,
+ rowsPerPage: null,
+ filter: null,
+ field: null,
+ order: null,
+ });
+ if (res?.data?.data?.monitors) {
+ setMonitors(res.data.data.monitors);
+ }
+ } catch (error) {
+ console.log(error);
+ } finally {
+ setIsLoading(false);
+ }
+ };
- fetchMonitors();
- }, []);
+ fetchMonitors();
+ }, []);
- // will show skeletons only on initial load
- // since monitor state is being added to redux persist, there's no reason to display skeletons on every render
- let isActuallyLoading = isLoading && monitors?.length === 0;
- return (
- [class*="fallback__"])': {
- position: "relative",
- border: 1,
- borderColor: theme.palette.border.light,
- borderRadius: theme.shape.borderRadius,
- borderStyle: "dashed",
- backgroundColor: theme.palette.background.main,
- overflow: "hidden",
- },
- }}
- >
- {isActuallyLoading ? (
-
- ) : monitors?.length !== 0 ? (
-
-
-
-
-
- {isAdmin && (
-
- )}
-
-
-
- {monitors?.map((monitor) => (
-
- ))}
-
-
- ) : (
-
- )}
-
- );
+ // will show skeletons only on initial load
+ // since monitor state is being added to redux persist, there's no reason to display skeletons on every render
+ let isActuallyLoading = isLoading && monitors?.length === 0;
+ console.log({ isActuallyLoading });
+ return (
+ [class*="fallback__"])': {
+ position: "relative",
+ border: 1,
+ borderColor: theme.palette.border.light,
+ borderRadius: theme.shape.borderRadius,
+ borderStyle: "dashed",
+ backgroundColor: theme.palette.background.main,
+ overflow: "hidden",
+ },
+ }}
+ >
+
+
+
+
+ {isAdmin && (
+
+ )}
+
+
+ {isActuallyLoading ? (
+
+ ) : monitors?.length !== 0 ? (
+
+
+ {monitors?.map((monitor) => (
+
+ ))}
+
+
+ ) : (
+
+ )}
+
+ );
};
PageSpeed.propTypes = {
- isAdmin: PropTypes.bool,
+ isAdmin: PropTypes.bool,
};
export default PageSpeed;