From 4bf3ce67817e3c493baa7cbf87ea2d867127fe0c Mon Sep 17 00:00:00 2001 From: Caio Cabral Date: Fri, 18 Oct 2024 18:38:08 -0400 Subject: [PATCH] feat: incident skeleton adjustement --- .../Incidents/IncidentTable/Empty/Empty.jsx | 32 ++ .../IncidentTable/Skeleton/Skeleton.jsx | 21 + .../Pages/Incidents/IncidentTable/index.jsx | 366 +++++++++--------- Client/src/Pages/Incidents/index.jsx | 226 ++++++----- Client/src/Pages/Maintenance/index.jsx | 188 ++++----- 5 files changed, 457 insertions(+), 376 deletions(-) create mode 100644 Client/src/Pages/Incidents/IncidentTable/Empty/Empty.jsx create mode 100644 Client/src/Pages/Incidents/IncidentTable/Skeleton/Skeleton.jsx diff --git a/Client/src/Pages/Incidents/IncidentTable/Empty/Empty.jsx b/Client/src/Pages/Incidents/IncidentTable/Empty/Empty.jsx new file mode 100644 index 000000000..b8f6a0986 --- /dev/null +++ b/Client/src/Pages/Incidents/IncidentTable/Empty/Empty.jsx @@ -0,0 +1,32 @@ +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 } from "@mui/material"; +import PropTypes from "prop-types"; + +const Empty = ({ styles, mode }) => { + const theme = useTheme(); + return ( + + + {mode === "light" ? : } + + + No incidents recorded yet. + + + ); +}; + +Empty.propTypes = { + styles: PropTypes.object, + mode: PropTypes.string, +}; + +export { Empty }; diff --git a/Client/src/Pages/Incidents/IncidentTable/Skeleton/Skeleton.jsx b/Client/src/Pages/Incidents/IncidentTable/Skeleton/Skeleton.jsx new file mode 100644 index 000000000..a3a23c14c --- /dev/null +++ b/Client/src/Pages/Incidents/IncidentTable/Skeleton/Skeleton.jsx @@ -0,0 +1,21 @@ +import { Skeleton /* , Stack */ } from "@mui/material"; +const IncidentSkeleton = () => { + return ( + <> + + + + ); +}; + +export { IncidentSkeleton }; diff --git a/Client/src/Pages/Incidents/IncidentTable/index.jsx b/Client/src/Pages/Incidents/IncidentTable/index.jsx index ef8356031..da533c7ba 100644 --- a/Client/src/Pages/Incidents/IncidentTable/index.jsx +++ b/Client/src/Pages/Incidents/IncidentTable/index.jsx @@ -1,16 +1,16 @@ import PropTypes from "prop-types"; import { - TableContainer, - Table, - TableHead, - TableRow, - TableCell, - TableBody, - Pagination, - PaginationItem, - Paper, - Typography, - Box, + TableContainer, + Table, + TableHead, + TableRow, + TableCell, + TableBody, + Pagination, + PaginationItem, + Paper, + Typography, + Box, } from "@mui/material"; import ArrowBackRoundedIcon from "@mui/icons-material/ArrowBackRounded"; @@ -24,185 +24,197 @@ import { useTheme } from "@emotion/react"; import { formatDateWithTz } from "../../../Utils/timeUtils"; import PlaceholderLight from "../../../assets/Images/data_placeholder.svg?react"; import PlaceholderDark from "../../../assets/Images/data_placeholder_dark.svg?react"; +import { Empty } from "./Empty/Empty"; +import { IncidentSkeleton } from "./Skeleton/Skeleton"; const IncidentTable = ({ monitors, selectedMonitor, filter }) => { - const uiTimezone = useSelector((state) => state.ui.timezone); + const uiTimezone = useSelector((state) => state.ui.timezone); - const theme = useTheme(); - const { authToken, user } = useSelector((state) => state.auth); - const mode = useSelector((state) => state.ui.mode); - const [checks, setChecks] = useState([]); - const [checksCount, setChecksCount] = useState(0); - const [paginationController, setPaginationController] = useState({ - page: 0, - rowsPerPage: 14, - }); + const theme = useTheme(); + const { authToken, user } = useSelector((state) => state.auth); + const mode = useSelector((state) => state.ui.mode); + const [checks, setChecks] = useState([]); + const [checksCount, setChecksCount] = useState(0); + const [paginationController, setPaginationController] = useState({ + page: 0, + rowsPerPage: 14, + }); + const [isLoading, setIsLoading] = useState(true); - useEffect(() => { - setPaginationController((prevPaginationController) => ({ - ...prevPaginationController, - page: 0, - })); - }, [filter, selectedMonitor]); + useEffect(() => { + setPaginationController((prevPaginationController) => ({ + ...prevPaginationController, + page: 0, + })); + }, [filter, selectedMonitor]); - useEffect(() => { - const fetchPage = async () => { - if (!monitors || Object.keys(monitors).length === 0) { - return; - } - try { - let res; - if (selectedMonitor === "0") { - res = await networkService.getChecksByTeam({ - authToken: authToken, - teamId: user.teamId, - sortOrder: "desc", - limit: null, - dateRange: null, - filter: filter, - page: paginationController.page, - rowsPerPage: paginationController.rowsPerPage, - }); - } else { - res = await networkService.getChecksByMonitor({ - authToken: authToken, - monitorId: selectedMonitor, - sortOrder: "desc", - limit: null, - dateRange: null, - sitler: filter, - page: paginationController.page, - rowsPerPage: paginationController.rowsPerPage, - }); - } - setChecks(res.data.data.checks); - setChecksCount(res.data.data.checksCount); - } catch (error) { - logger.error(error); - } - }; - fetchPage(); - }, [ - authToken, - user, - monitors, - selectedMonitor, - filter, - paginationController.page, - paginationController.rowsPerPage, - ]); + useEffect(() => { + const fetchPage = async () => { + if (!monitors || Object.keys(monitors).length === 0) { + return; + } + try { + setIsLoading(true); + let res; + if (selectedMonitor === "0") { + res = await networkService.getChecksByTeam({ + authToken: authToken, + teamId: user.teamId, + sortOrder: "desc", + limit: null, + dateRange: null, + filter: filter, + page: paginationController.page, + rowsPerPage: paginationController.rowsPerPage, + }); + } else { + res = await networkService.getChecksByMonitor({ + authToken: authToken, + monitorId: selectedMonitor, + sortOrder: "desc", + limit: null, + dateRange: null, + sitler: filter, + page: paginationController.page, + rowsPerPage: paginationController.rowsPerPage, + }); + } + setChecks(res.data.data.checks); + setChecksCount(res.data.data.checksCount); + } catch (error) { + logger.error(error); + } finally { + setIsLoading(false); + } + }; + fetchPage(); + }, [ + authToken, + user, + monitors, + selectedMonitor, + filter, + paginationController.page, + paginationController.rowsPerPage, + ]); - const handlePageChange = (_, newPage) => { - setPaginationController({ - ...paginationController, - page: newPage - 1, // 0-indexed - }); - }; + const handlePageChange = (_, newPage) => { + setPaginationController({ + ...paginationController, + page: newPage - 1, // 0-indexed + }); + }; - let paginationComponent = <>; - if (checksCount > paginationController.rowsPerPage) { - paginationComponent = ( - ( - - )} - sx={{ mt: "auto" }} - /> - ); - } + let paginationComponent = <>; + if (checksCount > paginationController.rowsPerPage) { + paginationComponent = ( + ( + + )} + sx={{ mt: "auto" }} + /> + ); + } - let sharedStyles = { - border: 1, - borderColor: theme.palette.border.light, - borderRadius: theme.shape.borderRadius, - backgroundColor: theme.palette.background.main, - p: theme.spacing(30), - }; + let sharedStyles = { + border: 1, + borderColor: theme.palette.border.light, + borderRadius: theme.shape.borderRadius, + backgroundColor: theme.palette.background.main, + p: theme.spacing(30), + }; - return ( - <> - {checks?.length === 0 && selectedMonitor === "0" ? ( - - - {mode === "light" ? : } - - - No incidents recorded yet. - - - ) : checks?.length === 0 ? ( - - - {mode === "light" ? : } - - - The monitor you have selected has no recorded incidents yet. - - - ) : ( - <> - - - - - Monitor Name - Status - Date & Time - Status Code - Message - - - - {checks.map((check) => { - const status = check.status === true ? "up" : "down"; - const formattedDate = formatDateWithTz( - check.createdAt, - "YYYY-MM-DD HH:mm:ss A", - uiTimezone - ); + const hasChecks = checks?.length === 0; + const noIncidentsRecordedYet = hasChecks && selectedMonitor === "0"; + const noIncidentsForThatMonitor = hasChecks && selectedMonitor !== "0"; - return ( - - {monitors[check.monitorId]?.name} - - - - {formattedDate} - - {check.statusCode ? check.statusCode : "N/A"} - - {check.message} - - ); - })} - -
-
- {paginationComponent} - - )} - - ); + return ( + <> + {isLoading ? ( + + ) : noIncidentsRecordedYet ? ( + + ) : noIncidentsForThatMonitor ? ( + + + {mode === "light" ? : } + + + The monitor you have selected has no recorded incidents yet. + + + ) : ( + <> + + + + + Monitor Name + Status + Date & Time + Status Code + Message + + + + {checks.map((check) => { + const status = check.status === true ? "up" : "down"; + const formattedDate = formatDateWithTz( + check.createdAt, + "YYYY-MM-DD HH:mm:ss A", + uiTimezone + ); + + return ( + + {monitors[check.monitorId]?.name} + + + + {formattedDate} + {check.statusCode ? check.statusCode : "N/A"} + {check.message} + + ); + })} + +
+
+ {paginationComponent} + + )} + + ); }; IncidentTable.propTypes = { - monitors: PropTypes.object.isRequired, - selectedMonitor: PropTypes.string.isRequired, - filter: PropTypes.string.isRequired, + monitors: PropTypes.object.isRequired, + selectedMonitor: PropTypes.string.isRequired, + filter: PropTypes.string.isRequired, }; export default IncidentTable; diff --git a/Client/src/Pages/Incidents/index.jsx b/Client/src/Pages/Incidents/index.jsx index 10ab30ad5..31f853e34 100644 --- a/Client/src/Pages/Incidents/index.jsx +++ b/Client/src/Pages/Incidents/index.jsx @@ -11,119 +11,133 @@ import SkeletonLayout from "./skeleton"; import "./index.css"; const Incidents = () => { - const theme = useTheme(); - const authState = useSelector((state) => state.auth); - const { monitorId } = useParams(); + const theme = useTheme(); + const authState = useSelector((state) => state.auth); + const { monitorId } = useParams(); - const [monitors, setMonitors] = useState({}); - const [selectedMonitor, setSelectedMonitor] = useState("0"); - const [loading, setLoading] = useState(false); + const [monitors, setMonitors] = useState({}); + const [selectedMonitor, setSelectedMonitor] = useState("0"); + const [isLoading, setIsLoading] = useState(true); - // TODO do something with these filters - const [filter, setFilter] = useState("all"); + // TODO do something with these filters + const [filter, setFilter] = useState("all"); - useEffect(() => { - const fetchMonitors = async () => { - setLoading(true); - const res = await networkService.getMonitorsByTeamId({ - authToken: authState.authToken, - teamId: authState.user.teamId, - limit: -1, - types: null, - status: null, - checkOrder: null, - normalize: null, - page: null, - rowsPerPage: null, - filter: null, - field: null, - order: null, - }); - // Reduce to a lookup object for 0(1) lookup - if (res?.data?.data?.monitors?.length > 0) { - const monitorLookup = res.data.data.monitors.reduce((acc, monitor) => { - acc[monitor._id] = monitor; - return acc; - }, {}); - setMonitors(monitorLookup); - monitorId !== undefined && setSelectedMonitor(monitorId); - } - setLoading(false); - }; + useEffect(() => { + const fetchMonitors = async () => { + try { + setIsLoading(true); + const res = await networkService.getMonitorsByTeamId({ + authToken: authState.authToken, + teamId: authState.user.teamId, + limit: -1, + types: null, + status: null, + checkOrder: null, + normalize: null, + page: null, + rowsPerPage: null, + filter: null, + field: null, + order: null, + }); + // Reduce to a lookup object for 0(1) lookup + if (res?.data?.data?.monitors?.length > 0) { + const monitorLookup = res.data.data.monitors.reduce((acc, monitor) => { + acc[monitor._id] = monitor; + return acc; + }, {}); + setMonitors(monitorLookup); + monitorId !== undefined && setSelectedMonitor(monitorId); + } + } catch (error) { + console.info(error); + } finally { + setIsLoading(false); + } + }; + fetchMonitors(); + }, [authState]); - fetchMonitors(); - }, [authState]); + useEffect(() => {}, [monitors]); - useEffect(() => {}, []); + const handleSelect = (event) => { + setSelectedMonitor(event.target.value); + }; - const handleSelect = (event) => { - setSelectedMonitor(event.target.value); - }; + const isActuallyLoading = isLoading && Object.keys(monitors)?.length === 0; - return ( - - {loading ? ( - - ) : ( - <> - - - Incidents for - - + + + + + + + + + )} + + ); }; export default Incidents; diff --git a/Client/src/Pages/Maintenance/index.jsx b/Client/src/Pages/Maintenance/index.jsx index e69711eec..2fc688bb2 100644 --- a/Client/src/Pages/Maintenance/index.jsx +++ b/Client/src/Pages/Maintenance/index.jsx @@ -10,103 +10,105 @@ import Breadcrumbs from "../../Components/Breadcrumbs"; import { useNavigate } from "react-router-dom"; const Maintenance = ({ isAdmin }) => { - const theme = useTheme(); - const navigate = useNavigate(); - const { authToken } = useSelector((state) => state.auth); - const { rowsPerPage } = useSelector((state) => state.ui.maintenance); + const theme = useTheme(); + const navigate = useNavigate(); + const { authToken } = useSelector((state) => state.auth); + const { rowsPerPage } = useSelector((state) => state.ui.maintenance); - const [maintenanceWindows, setMaintenanceWindows] = useState([]); - const [maintenanceWindowCount, setMaintenanceWindowCount] = useState(0); - const [page, setPage] = useState(0); - const [sort, setSort] = useState({}); - const [updateTrigger, setUpdateTrigger] = useState(false); + const [maintenanceWindows, setMaintenanceWindows] = useState([]); + const [maintenanceWindowCount, setMaintenanceWindowCount] = useState(0); + const [page, setPage] = useState(0); + const [sort, setSort] = useState({}); + const [updateTrigger, setUpdateTrigger] = useState(false); + const [isLoading, setIsLoading] = useState(true); - const handleActionMenuDelete = () => { - setUpdateTrigger((prev) => !prev); - }; + const handleActionMenuDelete = () => { + setUpdateTrigger((prev) => !prev); + }; - useEffect(() => { - const fetchMaintenanceWindows = async () => { - try { - const response = await networkService.getMaintenanceWindowsByTeamId({ - authToken: authToken, - page: page, - rowsPerPage: rowsPerPage, - }); - const { maintenanceWindows, maintenanceWindowCount } = - response.data.data; - setMaintenanceWindows(maintenanceWindows); - setMaintenanceWindowCount(maintenanceWindowCount); - } catch (error) { - console.log(error); - } - }; - fetchMaintenanceWindows(); - }, [authToken, page, rowsPerPage, updateTrigger]); + useEffect(() => { + const fetchMaintenanceWindows = async () => { + try { + setIsLoading(true); + const response = await networkService.getMaintenanceWindowsByTeamId({ + authToken: authToken, + page: page, + rowsPerPage: rowsPerPage, + }); + const { maintenanceWindows, maintenanceWindowCount } = response.data.data; + setMaintenanceWindows(maintenanceWindows); + setMaintenanceWindowCount(maintenanceWindowCount); + } catch (error) { + console.log(error); + } finally { + setIsLoading(false); + } + }; + fetchMaintenanceWindows(); + }, [authToken, page, rowsPerPage, updateTrigger]); - 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", - }, - }} - > - {maintenanceWindows.length > 0 && ( - - - - - - - - )} - {maintenanceWindows.length === 0 && ( - - )} - - ); + const isActuallyLoading = isLoading && maintenanceWindows?.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", + }, + }} + > + {maintenanceWindows.length > 0 && ( + + + + + + + + )} + {maintenanceWindows.length === 0 && ( + + )} + + ); }; export default Maintenance;