2024-08-16 13:16:04 -04:00
|
|
|
import React, { useEffect, useState } from "react";
|
2024-08-15 20:34:34 -04:00
|
|
|
import {
|
|
|
|
|
Box,
|
2024-08-16 12:13:05 -04:00
|
|
|
Collapse,
|
2024-08-16 14:17:11 -04:00
|
|
|
Divider,
|
2024-08-16 12:53:41 -04:00
|
|
|
IconButton,
|
2024-08-15 20:34:34 -04:00
|
|
|
List,
|
|
|
|
|
ListItemButton,
|
|
|
|
|
ListItemIcon,
|
|
|
|
|
ListItemText,
|
|
|
|
|
ListSubheader,
|
|
|
|
|
Stack,
|
|
|
|
|
Tooltip,
|
|
|
|
|
Typography,
|
|
|
|
|
} from "@mui/material";
|
2024-07-26 12:39:18 -04:00
|
|
|
import { useLocation, useNavigate } from "react-router";
|
|
|
|
|
import { useTheme } from "@emotion/react";
|
2024-08-13 20:17:18 -04:00
|
|
|
import { useDispatch, useSelector } from "react-redux";
|
|
|
|
|
import { clearAuthState } from "../../Features/Auth/authSlice";
|
|
|
|
|
import { clearUptimeMonitorState } from "../../Features/UptimeMonitors/uptimeMonitorsSlice";
|
|
|
|
|
import Avatar from "../Avatar";
|
|
|
|
|
import LockSvg from "../../assets/icons/lock.svg?react";
|
|
|
|
|
import UserSvg from "../../assets/icons/user.svg?react";
|
|
|
|
|
import TeamSvg from "../../assets/icons/user-two.svg?react";
|
|
|
|
|
import LogoutSvg from "../../assets/icons/logout.svg?react";
|
2024-07-26 12:39:18 -04:00
|
|
|
import BWULogo from "../../assets/Images/bwl-logo.svg?react";
|
|
|
|
|
import Support from "../../assets/icons/support.svg?react";
|
2024-08-16 12:19:03 -04:00
|
|
|
import Dashboard from "../../assets/icons/dashboard.svg?react";
|
|
|
|
|
import Account from "../../assets/icons/user-edit.svg?react";
|
2024-07-26 12:39:18 -04:00
|
|
|
import StatusPages from "../../assets/icons/status-pages.svg?react";
|
|
|
|
|
import Maintenance from "../../assets/icons/maintenance.svg?react";
|
|
|
|
|
import Monitors from "../../assets/icons/monitors.svg?react";
|
|
|
|
|
import Incidents from "../../assets/icons/incidents.svg?react";
|
|
|
|
|
import Integrations from "../../assets/icons/integrations.svg?react";
|
|
|
|
|
import PageSpeed from "../../assets/icons/page-speed.svg?react";
|
|
|
|
|
import Settings from "../../assets/icons/settings.svg?react";
|
2024-08-16 12:13:05 -04:00
|
|
|
import ArrowDown from "../../assets/icons/down-arrow.svg?react";
|
|
|
|
|
import ArrowUp from "../../assets/icons/up-arrow.svg?react";
|
2024-08-19 13:23:17 -04:00
|
|
|
import ArrowRight from "../../assets/icons/right-arrow.svg?react";
|
|
|
|
|
import ArrowLeft from "../../assets/icons/left-arrow.svg?react";
|
2024-07-26 12:39:18 -04:00
|
|
|
|
|
|
|
|
import "./index.css";
|
|
|
|
|
|
|
|
|
|
const menu = [
|
2024-08-16 12:13:05 -04:00
|
|
|
{
|
|
|
|
|
name: "Dashboard",
|
2024-08-16 12:19:03 -04:00
|
|
|
icon: <Dashboard />,
|
2024-08-16 12:13:05 -04:00
|
|
|
nested: [
|
|
|
|
|
{ name: "Monitors", path: "monitors", icon: <Monitors /> },
|
|
|
|
|
{ name: "Pagespeed", path: "pagespeed", icon: <PageSpeed /> },
|
|
|
|
|
],
|
|
|
|
|
},
|
2024-07-26 12:39:18 -04:00
|
|
|
{ name: "Incidents", path: "incidents", icon: <Incidents /> },
|
|
|
|
|
{ name: "Status pages", path: "status", icon: <StatusPages /> },
|
|
|
|
|
{ name: "Maintenance", path: "maintenance", icon: <Maintenance /> },
|
2024-07-28 16:51:47 -04:00
|
|
|
{ name: "Integrations", path: "integrations", icon: <Integrations /> },
|
2024-08-16 12:13:05 -04:00
|
|
|
{
|
|
|
|
|
name: "Account",
|
2024-08-16 12:19:03 -04:00
|
|
|
icon: <Account />,
|
2024-08-16 12:13:05 -04:00
|
|
|
nested: [
|
|
|
|
|
{ name: "Profile", path: "account/profile", icon: <UserSvg /> },
|
2024-08-16 12:19:03 -04:00
|
|
|
{ name: "Password", path: "account/password", icon: <LockSvg /> },
|
|
|
|
|
{ name: "Team", path: "account/team", icon: <TeamSvg /> },
|
2024-08-16 12:13:05 -04:00
|
|
|
],
|
|
|
|
|
},
|
2024-08-15 20:34:34 -04:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const other = [
|
2024-07-26 12:39:18 -04:00
|
|
|
{ name: "Support", path: "support", icon: <Support /> },
|
|
|
|
|
{ name: "Settings", path: "settings", icon: <Settings /> },
|
|
|
|
|
];
|
|
|
|
|
|
2024-08-13 20:24:29 -04:00
|
|
|
/**
|
|
|
|
|
* @component
|
|
|
|
|
* Sidebar component serves as a sidebar containing a menu.
|
|
|
|
|
*
|
|
|
|
|
* @returns {JSX.Element} The JSX element representing the Sidebar component.
|
|
|
|
|
*/
|
|
|
|
|
|
2024-07-26 12:39:18 -04:00
|
|
|
function Sidebar() {
|
|
|
|
|
const theme = useTheme();
|
|
|
|
|
const navigate = useNavigate();
|
|
|
|
|
const location = useLocation();
|
2024-08-13 20:17:18 -04:00
|
|
|
const dispatch = useDispatch();
|
|
|
|
|
const authState = useSelector((state) => state.auth);
|
2024-08-16 12:13:05 -04:00
|
|
|
const [open, setOpen] = useState({ Dashboard: false, Account: false });
|
2024-08-19 13:23:17 -04:00
|
|
|
const [collapsed, setCollapsed] = useState(false);
|
2024-08-13 20:17:18 -04:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Handles logging out the user
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
const logout = async () => {
|
2024-08-15 11:39:45 -07:00
|
|
|
// Clear auth state
|
|
|
|
|
dispatch(clearAuthState());
|
|
|
|
|
dispatch(clearUptimeMonitorState());
|
|
|
|
|
navigate("/login");
|
2024-08-13 20:17:18 -04:00
|
|
|
};
|
|
|
|
|
|
2024-08-16 13:16:04 -04:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (
|
|
|
|
|
location.pathname.includes("monitors") ||
|
|
|
|
|
location.pathname.includes("pagespeed")
|
|
|
|
|
)
|
|
|
|
|
setOpen((prev) => ({ ...prev, Dashboard: true }));
|
|
|
|
|
else if (location.pathname.includes("/account"))
|
|
|
|
|
setOpen((prev) => ({ ...prev, Account: true }));
|
|
|
|
|
}, []);
|
|
|
|
|
|
2024-07-26 12:39:18 -04:00
|
|
|
return (
|
2024-08-19 13:23:17 -04:00
|
|
|
<Stack
|
|
|
|
|
component="aside"
|
|
|
|
|
gap={theme.gap.medium}
|
|
|
|
|
sx={{ flex: collapsed ? 0 : 1 }}
|
|
|
|
|
>
|
|
|
|
|
<Box
|
|
|
|
|
width="100%"
|
|
|
|
|
py={theme.gap.large}
|
|
|
|
|
pl={collapsed ? theme.gap.medium : theme.gap.lgplus}
|
|
|
|
|
>
|
|
|
|
|
{!collapsed && <BWULogo alt="BlueWave Uptime Logo" />}
|
|
|
|
|
<IconButton
|
|
|
|
|
sx={{
|
|
|
|
|
position: "absolute",
|
|
|
|
|
right: 0,
|
|
|
|
|
transform: `translate(50%, 0)`,
|
|
|
|
|
backgroundColor: theme.palette.otherColors.fillGray,
|
|
|
|
|
border: `solid 1px ${theme.palette.otherColors.graishWhite}`,
|
|
|
|
|
p: "5px",
|
|
|
|
|
"& svg": {
|
|
|
|
|
width: theme.gap.ml,
|
|
|
|
|
height: theme.gap.ml,
|
|
|
|
|
"& path": {
|
|
|
|
|
stroke: theme.palette.otherColors.bluishGray,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
"&:focus": { outline: "none" },
|
|
|
|
|
"&:hover": {
|
|
|
|
|
backgroundColor: "#e3e3e3",
|
|
|
|
|
borderColor: theme.palette.otherColors.graishWhite,
|
|
|
|
|
},
|
|
|
|
|
}}
|
|
|
|
|
onClick={() => setCollapsed(!collapsed)}
|
|
|
|
|
>
|
|
|
|
|
{collapsed ? <ArrowRight /> : <ArrowLeft />}
|
|
|
|
|
</IconButton>
|
2024-08-15 20:34:34 -04:00
|
|
|
</Box>
|
|
|
|
|
{/* menu */}
|
|
|
|
|
<List
|
|
|
|
|
component="nav"
|
|
|
|
|
aria-labelledby="nested-menu-subheader"
|
2024-08-16 14:17:11 -04:00
|
|
|
disablePadding
|
2024-08-15 20:34:34 -04:00
|
|
|
subheader={
|
2024-08-16 14:17:11 -04:00
|
|
|
<ListSubheader
|
|
|
|
|
component="div"
|
|
|
|
|
id="nested-menu-subheader"
|
2024-08-19 13:23:17 -04:00
|
|
|
sx={{
|
|
|
|
|
pt: theme.gap.small,
|
|
|
|
|
px: collapsed ? theme.gap.xs : theme.gap.ml,
|
|
|
|
|
textAlign: collapsed ? "center" : "left",
|
|
|
|
|
}}
|
2024-08-16 14:17:11 -04:00
|
|
|
>
|
2024-08-15 20:34:34 -04:00
|
|
|
Menu
|
|
|
|
|
</ListSubheader>
|
|
|
|
|
}
|
2024-08-19 13:23:17 -04:00
|
|
|
sx={{ px: collapsed ? theme.gap.xs : theme.gap.ml }}
|
2024-08-15 20:34:34 -04:00
|
|
|
>
|
2024-08-16 12:13:05 -04:00
|
|
|
{menu.map((item) =>
|
|
|
|
|
item.path ? (
|
|
|
|
|
<ListItemButton
|
|
|
|
|
className={
|
|
|
|
|
location.pathname.includes(item.path) ? "selected-path" : ""
|
|
|
|
|
}
|
|
|
|
|
key={item.path}
|
|
|
|
|
onClick={() => navigate(`/${item.path}`)}
|
|
|
|
|
sx={{
|
|
|
|
|
gap: theme.gap.medium,
|
|
|
|
|
borderRadius: `${theme.shape.borderRadius}px`,
|
2024-08-19 13:23:17 -04:00
|
|
|
justifyContent: collapsed ? "center" : "flex-start",
|
2024-08-16 12:13:05 -04:00
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<ListItemIcon sx={{ minWidth: 0 }}>{item.icon}</ListItemIcon>
|
2024-08-19 13:23:17 -04:00
|
|
|
{!collapsed && <ListItemText>{item.name}</ListItemText>}
|
|
|
|
|
</ListItemButton>
|
|
|
|
|
) : collapsed ? (
|
|
|
|
|
<ListItemButton
|
|
|
|
|
key={item.name}
|
|
|
|
|
sx={{
|
|
|
|
|
gap: theme.gap.medium,
|
|
|
|
|
borderRadius: `${theme.shape.borderRadius}px`,
|
|
|
|
|
justifyContent: collapsed ? "center" : "flex-start",
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<ListItemIcon sx={{ minWidth: 0 }}>{item.icon}</ListItemIcon>
|
|
|
|
|
{!collapsed && <ListItemText>{item.name}</ListItemText>}
|
2024-08-16 12:13:05 -04:00
|
|
|
</ListItemButton>
|
|
|
|
|
) : (
|
2024-08-16 12:40:15 -04:00
|
|
|
<React.Fragment key={item.name}>
|
2024-08-16 12:13:05 -04:00
|
|
|
<ListItemButton
|
|
|
|
|
onClick={() =>
|
|
|
|
|
setOpen((prev) => ({
|
|
|
|
|
...prev,
|
|
|
|
|
[`${item.name}`]: !prev[`${item.name}`],
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
sx={{
|
|
|
|
|
gap: theme.gap.medium,
|
|
|
|
|
borderRadius: `${theme.shape.borderRadius}px`,
|
2024-08-19 13:23:17 -04:00
|
|
|
justifyContent: collapsed ? "center" : "flex-start",
|
2024-08-16 12:13:05 -04:00
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<ListItemIcon sx={{ minWidth: 0 }}>{item.icon}</ListItemIcon>
|
|
|
|
|
<ListItemText>{item.name}</ListItemText>
|
|
|
|
|
{open[`${item.name}`] ? <ArrowUp /> : <ArrowDown />}
|
|
|
|
|
</ListItemButton>
|
2024-08-16 12:40:15 -04:00
|
|
|
<Collapse in={open[`${item.name}`]} timeout="auto">
|
2024-08-16 12:19:03 -04:00
|
|
|
<List
|
|
|
|
|
component="div"
|
|
|
|
|
disablePadding
|
2024-08-16 13:16:04 -04:00
|
|
|
sx={{ pl: theme.gap.lgplus }}
|
2024-08-16 12:19:03 -04:00
|
|
|
>
|
2024-08-16 12:40:15 -04:00
|
|
|
{item.nested.map((child) => {
|
|
|
|
|
if (
|
|
|
|
|
child.name === "Team" &&
|
|
|
|
|
authState.user?.role &&
|
|
|
|
|
!authState.user.role.includes("admin")
|
|
|
|
|
) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<ListItemButton
|
|
|
|
|
className={
|
|
|
|
|
location.pathname.includes(child.path)
|
|
|
|
|
? "selected-path"
|
|
|
|
|
: ""
|
|
|
|
|
}
|
|
|
|
|
key={child.path}
|
|
|
|
|
onClick={() => navigate(`/${child.path}`)}
|
|
|
|
|
sx={{
|
2024-08-16 18:38:40 -04:00
|
|
|
gap: theme.gap.small,
|
2024-08-16 12:40:15 -04:00
|
|
|
borderRadius: `${theme.shape.borderRadius}px`,
|
2024-08-16 13:16:04 -04:00
|
|
|
pl: theme.gap.small,
|
2024-08-16 12:40:15 -04:00
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<ListItemIcon sx={{ minWidth: 0 }}>
|
|
|
|
|
{child.icon}
|
|
|
|
|
</ListItemIcon>
|
|
|
|
|
<ListItemText>{child.name}</ListItemText>
|
|
|
|
|
</ListItemButton>
|
|
|
|
|
);
|
|
|
|
|
})}
|
2024-08-16 12:13:05 -04:00
|
|
|
</List>
|
|
|
|
|
</Collapse>
|
2024-08-16 12:40:15 -04:00
|
|
|
</React.Fragment>
|
2024-08-16 12:13:05 -04:00
|
|
|
)
|
|
|
|
|
)}
|
2024-08-15 20:34:34 -04:00
|
|
|
</List>
|
2024-08-16 14:17:11 -04:00
|
|
|
<Divider sx={{ my: theme.gap.small }} />
|
2024-08-15 20:34:34 -04:00
|
|
|
{/* other */}
|
|
|
|
|
<List
|
|
|
|
|
component="nav"
|
|
|
|
|
aria-labelledby="nested-other-subheader"
|
|
|
|
|
subheader={
|
2024-08-16 14:17:11 -04:00
|
|
|
<ListSubheader
|
|
|
|
|
component="div"
|
|
|
|
|
id="nested-other-subheader"
|
2024-08-19 13:23:17 -04:00
|
|
|
sx={{
|
|
|
|
|
pt: theme.gap.small,
|
|
|
|
|
px: collapsed ? theme.gap.xs : theme.gap.ml,
|
|
|
|
|
textAlign: collapsed ? "center" : "left",
|
|
|
|
|
}}
|
2024-08-16 14:17:11 -04:00
|
|
|
>
|
2024-08-15 20:34:34 -04:00
|
|
|
Other
|
|
|
|
|
</ListSubheader>
|
|
|
|
|
}
|
2024-08-19 13:23:17 -04:00
|
|
|
sx={{ px: collapsed ? theme.gap.xs : theme.gap.ml }}
|
2024-08-15 20:34:34 -04:00
|
|
|
>
|
|
|
|
|
{other.map((item) => (
|
|
|
|
|
<ListItemButton
|
|
|
|
|
className={
|
|
|
|
|
location.pathname.includes(item.path) ? "selected-path" : ""
|
|
|
|
|
}
|
|
|
|
|
key={item.path}
|
|
|
|
|
onClick={() =>
|
|
|
|
|
item.path === "support"
|
|
|
|
|
? window.open(
|
|
|
|
|
"https://github.com/bluewave-labs/bluewave-uptime",
|
|
|
|
|
"_blank",
|
|
|
|
|
"noreferrer"
|
|
|
|
|
)
|
|
|
|
|
: navigate(`/${item.path}`)
|
|
|
|
|
}
|
|
|
|
|
sx={{
|
|
|
|
|
gap: theme.gap.medium,
|
|
|
|
|
borderRadius: `${theme.shape.borderRadius}px`,
|
2024-08-19 13:23:17 -04:00
|
|
|
justifyContent: collapsed ? "center" : "flex-start",
|
2024-08-15 20:34:34 -04:00
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<ListItemIcon sx={{ minWidth: 0 }}>{item.icon}</ListItemIcon>
|
2024-08-19 13:23:17 -04:00
|
|
|
{!collapsed && <ListItemText>{item.name}</ListItemText>}
|
2024-08-15 20:34:34 -04:00
|
|
|
</ListItemButton>
|
|
|
|
|
))}
|
|
|
|
|
</List>
|
2024-08-16 14:17:11 -04:00
|
|
|
<Divider sx={{ mt: "auto" }} />
|
2024-08-19 13:23:17 -04:00
|
|
|
|
2024-08-16 12:53:41 -04:00
|
|
|
<Stack
|
|
|
|
|
direction="row"
|
2024-08-19 13:23:17 -04:00
|
|
|
height="50px"
|
|
|
|
|
justifyContent={collapsed ? "center" : "flex-start"}
|
2024-08-16 12:53:41 -04:00
|
|
|
alignItems="center"
|
|
|
|
|
py={theme.gap.small}
|
|
|
|
|
px={theme.gap.medium}
|
|
|
|
|
gap={theme.gap.xs}
|
|
|
|
|
borderRadius={`${theme.shape.borderRadius}px`}
|
2024-08-13 20:17:18 -04:00
|
|
|
>
|
2024-08-16 12:53:41 -04:00
|
|
|
<Avatar small={true} />
|
2024-08-19 13:23:17 -04:00
|
|
|
{!collapsed && (
|
|
|
|
|
<>
|
|
|
|
|
<Box ml={theme.gap.xs}>
|
|
|
|
|
<Typography component="span" fontWeight={500}>
|
|
|
|
|
{authState.user?.firstName} {authState.user?.lastName}
|
|
|
|
|
</Typography>
|
|
|
|
|
<Typography sx={{ textTransform: "capitalize" }}>
|
|
|
|
|
{authState.user?.role}
|
|
|
|
|
</Typography>
|
|
|
|
|
</Box>
|
|
|
|
|
<Tooltip title="Log Out">
|
|
|
|
|
<IconButton
|
|
|
|
|
sx={{ ml: "auto", "&:focus": { outline: "none" } }}
|
|
|
|
|
onClick={logout}
|
|
|
|
|
>
|
|
|
|
|
<LogoutSvg style={{ width: "20px", height: "20px" }} />
|
|
|
|
|
</IconButton>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
2024-08-16 12:53:41 -04:00
|
|
|
</Stack>
|
2024-07-26 12:39:18 -04:00
|
|
|
</Stack>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default Sidebar;
|