diff --git a/Client/src/Hooks/useIsAdmin.js b/Client/src/Hooks/useIsAdmin.js new file mode 100644 index 000000000..7d593b874 --- /dev/null +++ b/Client/src/Hooks/useIsAdmin.js @@ -0,0 +1,11 @@ +import { useSelector } from "react-redux"; + +const useIsAdmin = () => { + const { user } = useSelector((state) => state.auth); + const isAdmin = + (user?.role?.includes("admin") ?? false) || + (user?.role?.includes("superadmin") ?? false); + return isAdmin; +}; + +export { useIsAdmin }; diff --git a/Client/src/Pages/Infrastructure/components/Menu/index.jsx b/Client/src/Pages/Infrastructure/components/Menu/index.jsx new file mode 100644 index 000000000..4bf3cb209 --- /dev/null +++ b/Client/src/Pages/Infrastructure/components/Menu/index.jsx @@ -0,0 +1,235 @@ +/* TODO I basically copied and pasted this component from the actionsMenu. Check how we can make it reusable */ + +import { useRef, useState } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { useTheme } from "@emotion/react"; +import { useNavigate } from "react-router-dom"; +import { createToast } from "../../../../Utils/toastUtils"; +/* import { logger } from "../../../Utils/Logger"; */ +import { IconButton, Menu, MenuItem } from "@mui/material"; +import { + deleteUptimeMonitor, + /* pauseUptimeMonitor, */ + getUptimeMonitorsByTeamId, +} from "../../../../Features/UptimeMonitors/uptimeMonitorsSlice"; +import Settings from "../../../../assets/icons/settings-bold.svg?react"; +import PropTypes from "prop-types"; +import Dialog from "../../../../Components/Dialog"; +import { networkService } from "../../../../Utils/NetworkService.js"; + +const InfrastructureMenu = ({ monitor, isAdmin, updateCallback }) => { + /* const [anchorEl, setAnchorEl] = useState(null); + const [actions, setActions] = useState({}); */ + const actions = { id: monitor.id, url: monitor.url }; + const anchor = useRef(null); + const [isOpen, setIsOpen] = useState(false); + const [isDialogOpen, setIsDialogOpen] = useState(false); + const dispatch = useDispatch(); + const theme = useTheme(); + const authState = useSelector((state) => state.auth); + const authToken = authState.authToken; + const { isLoading } = useSelector((state) => state.uptimeMonitors); + + /* const handlePause = async () => { + try { + const action = await dispatch( + pauseUptimeMonitor({ authToken, monitorId: monitor._id }) + ); + if (pauseUptimeMonitor.fulfilled.match(action)) { + updateCallback(); + const state = action?.payload?.data.isActive === false ? "paused" : "resumed"; + createToast({ body: `Monitor ${state} successfully.` }); + } else { + throw new Error(action?.error?.message ?? "Failed to pause monitor."); + } + } catch (error) { + logger.error("Error pausing monitor:", monitor._id, error); + createToast({ body: "Failed to pause monitor." }); + } + }; */ + + /* TODO +1) Clean component +2) Rerender after removing +3) Add padding to inputs (I took of their height) + */ + + const openMenu = (e) => { + e.stopPropagation(); + setIsOpen(true); + }; + + const closeMenu = (e) => { + e.stopPropagation(); + setIsOpen(false); + }; + + const openRemove = (e) => { + closeMenu(e); + setIsDialogOpen(true); + }; + const cancelRemove = () => { + setIsDialogOpen(false); + }; + + const navigate = useNavigate(); + + function openDetails(id) { + navigate(`/infrastructure/${id}`); + } + + const handleRemove = async () => { + try { + /* TODO not working, dont know why */ + const result = await networkService.deleteMonitorById({ + authToken, + monitorId: actions.id, + }); + console.log(result); + /* Toast success */ + } catch (error) { + console.log(error); + /* Toast error */ + } finally { + setIsDialogOpen(false); + updateCallback(); + } + /* let monitor = { _id: actions.id }; + const action = await dispatch( + deleteUptimeMonitor({ authToken: authState.authToken, monitor }) + ); + if (action.meta.requestStatus === "fulfilled") { + setIsOpen(false); // close modal + dispatch(getUptimeMonitorsByTeamId(authState.authToken)); + updateCallback(); + createToast({ body: "Monitor deleted successfully." }); + } else { + createToast({ body: "Failed to delete monitor." }); + } */ + }; + + return ( + <> + + + + + + {/* + Open sit action. Not necessary for infrastructure? + + {actions.url !== null ? ( + { + closeMenu(e); + e.stopPropagation(); + window.open(actions.url, "_blank", "noreferrer"); + }} + > + Open site + + ) : ( + "" + )} + */} + openDetails(actions.id)}>Details + {/* TODO - pass monitor id to Incidents page */} + {/* { + e.stopPropagation(); + navigate(`/incidents/${actions.id}`); + }} + > + Incidents + */} + {/* {isAdmin && ( + { + e.stopPropagation(); + + navigate(`/monitors/configure/${actions.id}`); + }} + > + Configure + + )} */} + {/* {isAdmin && ( + { + e.stopPropagation(); + navigate(`/monitors/create/${actions.id}`); + }} + > + Clone + + )} */} + {/* {isAdmin && ( + { + e.stopPropagation(); + handlePause(e); + }} + > + {monitor?.isActive === true ? "Pause" : "Resume"} + + )} */} + {isAdmin && Remove} + + + + ); +}; + +InfrastructureMenu.propTypes = { + monitor: PropTypes.shape({ + _id: PropTypes.string, + url: PropTypes.string, + type: PropTypes.string, + isActive: PropTypes.bool, + }).isRequired, + isAdmin: PropTypes.bool, + updateCallback: PropTypes.func, +}; + +export { InfrastructureMenu }; diff --git a/Client/src/Pages/Infrastructure/index.jsx b/Client/src/Pages/Infrastructure/index.jsx index f8627744a..0c7af0718 100644 --- a/Client/src/Pages/Infrastructure/index.jsx +++ b/Client/src/Pages/Infrastructure/index.jsx @@ -4,7 +4,7 @@ import { /* useDispatch, */ useSelector } from "react-redux"; import { useTheme } from "@emotion/react"; import useUtils from "../Monitors/utils"; import { jwtDecode } from "jwt-decode"; -import GearIcon from "../../Assets/icons/settings-bold.svg?react"; +// import GearIcon from "../../Assets/icons/settings-bold.svg?react"; import CPUChipIcon from "../../Assets/icons/cpu-chip.svg?react"; import { Box, @@ -27,6 +27,9 @@ import { Pagination } from "./components/TablePagination"; import { networkService } from "../../Utils/NetworkService.js"; import CustomGauge from "../../Components/Charts/CustomGauge/index.jsx"; import Host from "../Monitors/Home/host.jsx"; +import ActionsMenu from "../Monitors/Home/actionsMenu.jsx"; +import { useIsAdmin } from "../../Hooks/useIsAdmin.js"; +import { InfrastructureMenu } from "./components/Menu"; const columns = [ { label: "Host" }, @@ -71,7 +74,9 @@ Analyze existing BasicTable * @returns {JSX.Element} The infrastructure monitoring page. */ -function Infrastructure(/* {isAdmin} */) { +function Infrastructure() { + /* Adding this custom hook so we can avoid using the HOC approach that can lower performance (we are calling the admin logic N times on initializing the project. using a custom hook will cal it ass needed ) */ + const isAdmin = useIsAdmin(); const theme = useTheme(); const navigate = useNavigate(); @@ -93,7 +98,7 @@ function Infrastructure(/* {isAdmin} */) { const { authToken } = useSelector((state) => state.auth); const user = jwtDecode(authToken); - const fetchMonitors = useCallback(async () => { + const fetchMonitors = async () => { try { const response = await networkService.getMonitorsByTeamId({ authToken, @@ -106,6 +111,7 @@ function Infrastructure(/* {isAdmin} */) { page: page, rowsPerPage: rowsPerPage, }); + console.log({ response }); setMonitorState({ monitors: response?.data?.data?.monitors ?? [], total: response?.data?.data?.monitorCount ?? 0, @@ -113,7 +119,7 @@ function Infrastructure(/* {isAdmin} */) { } catch (error) { console.error(error); } - }, [page, rowsPerPage, authToken]); + }; useEffect(() => { fetchMonitors(); @@ -121,8 +127,6 @@ function Infrastructure(/* {isAdmin} */) { const { determineState } = useUtils(); const { monitors, total: totalMonitors } = monitorState; - console.log({ monitors }); - console.log(monitors[0]?.checks[0]?.memory.usage_percent); const monitorsAsRows = monitors.map((monitor) => ({ id: monitor._id, url: monitor.url, @@ -133,12 +137,21 @@ function Infrastructure(/* {isAdmin} */) { mem: monitor?.checks[0]?.memory.usage_percent * 100, disk: monitor?.checks[0]?.disk[0]?.usage_percent * 100, })); - /* Adding click on row action */ - /* Adding actions to the table */ + + /* + TODO +Clean component (actions component) +Add padding to inputs (I took of their height) +Add toast + + */ function openDetails(id) { navigate(`/infrastructure/${id}`); } + function handleActionMenuDelete() { + fetchMonitors(); + } return ( - + {/* + /> */}