diff --git a/client/src/Components/Sidebar/collapseButton.jsx b/client/src/Components/Sidebar/collapseButton.jsx
new file mode 100644
index 000000000..551bcf25d
--- /dev/null
+++ b/client/src/Components/Sidebar/collapseButton.jsx
@@ -0,0 +1,48 @@
+import IconButton from "@mui/material/IconButton";
+import ArrowRight from "../../assets/icons/right-arrow.svg?react";
+import ArrowLeft from "../../assets/icons/left-arrow.svg?react";
+import { useTheme } from "@mui/material/styles";
+import { useDispatch } from "react-redux";
+import { toggleSidebar } from "../../Features/UI/uiSlice";
+
+const CollapseButton = ({ collapsed, setOpen }) => {
+ const theme = useTheme();
+ const dispatch = useDispatch();
+ return (
+ {
+ setOpen((prev) =>
+ Object.fromEntries(Object.keys(prev).map((key) => [key, false]))
+ );
+ dispatch(toggleSidebar());
+ }}
+ >
+ {collapsed ? : }
+
+ );
+};
+
+export default CollapseButton;
diff --git a/client/src/Components/Sidebar/components/authFooter.jsx b/client/src/Components/Sidebar/components/authFooter.jsx
new file mode 100644
index 000000000..7ff2f36ef
--- /dev/null
+++ b/client/src/Components/Sidebar/components/authFooter.jsx
@@ -0,0 +1,260 @@
+import Stack from "@mui/material/Stack";
+import Box from "@mui/material/Box";
+import Typography from "@mui/material/Typography";
+import Tooltip from "@mui/material/Tooltip";
+import IconButton from "@mui/material/IconButton";
+import Avatar from "../../Avatar";
+import ThemeSwitch from "../../ThemeSwitch";
+import Menu from "@mui/material/Menu";
+import MenuItem from "@mui/material/MenuItem";
+import Divider from "@mui/material/Divider";
+import DotsVertical from "../../../assets/icons/dots-vertical.svg?react";
+import LogoutSvg from "../../../assets/icons/logout.svg?react";
+
+import { useTheme } from "@emotion/react";
+import { useSelector } from "react-redux";
+import { useTranslation } from "react-i18next";
+import { useState } from "react";
+import { useNavigate } from "react-router";
+import { clearAuthState } from "../../../Features/Auth/authSlice";
+import { useDispatch } from "react-redux";
+
+const AuthFooter = ({ collapsed, accountMenuItems }) => {
+ const { t } = useTranslation();
+ const theme = useTheme();
+ const authState = useSelector((state) => state.auth);
+ const navigate = useNavigate();
+ const dispatch = useDispatch();
+
+ const [anchorEl, setAnchorEl] = useState(null);
+ const [popup, setPopup] = useState();
+
+ const openPopup = (event, id) => {
+ setAnchorEl(event.currentTarget);
+ setPopup(id);
+ };
+
+ const closePopup = () => {
+ setAnchorEl(null);
+ };
+
+ const logout = async () => {
+ // Clear auth state
+ dispatch(clearAuthState());
+ navigate("/login");
+ };
+ const renderAccountMenuItems = (user, items) => {
+ let filteredAccountMenuItems = [...items];
+
+ // If the user is in demo mode, remove the "Password" option
+ if (user.role?.includes("demo")) {
+ filteredAccountMenuItems = filteredAccountMenuItems.filter(
+ (item) => item.name !== "Password"
+ );
+ }
+
+ // If the user is NOT a superadmin, remove the "Team" option
+ if (user.role && !user.role.includes("superadmin")) {
+ filteredAccountMenuItems = filteredAccountMenuItems.filter(
+ (item) => item.name !== "Team"
+ );
+ }
+
+ return filteredAccountMenuItems.map((item) => (
+
+ ));
+ };
+ return (
+
+ <>
+
+
+
+
+ {authState.user?.firstName} {authState.user?.lastName}
+
+
+ {authState.user?.role?.includes("superadmin")
+ ? t("roles.superAdmin")
+ : authState.user?.role?.includes("admin")
+ ? t("roles.admin")
+ : authState.user?.role?.includes("user")
+ ? t("roles.teamMember")
+ : authState.user?.role?.includes("demo")
+ ? t("roles.demoUser")
+ : authState.user?.role}
+
+
+
+
+
+ openPopup(event, "logout")}
+ >
+
+
+
+
+
+ >
+
+
+ );
+};
+
+export default AuthFooter;
diff --git a/client/src/Components/Sidebar/components/navItem.jsx b/client/src/Components/Sidebar/components/navItem.jsx
new file mode 100644
index 000000000..f43b1bf1e
--- /dev/null
+++ b/client/src/Components/Sidebar/components/navItem.jsx
@@ -0,0 +1,89 @@
+import Tooltip from "@mui/material/Tooltip";
+import ListItemButton from "@mui/material/ListItemButton";
+import ListItemIcon from "@mui/material/ListItemIcon";
+import Box from "@mui/material/Box";
+import Typography from "@mui/material/Typography";
+
+import { useTheme } from "@emotion/react";
+import { useNavigate } from "react-router";
+
+const NavItem = ({ item, collapsed, selected, onClick }) => {
+ const theme = useTheme();
+ const navigate = useNavigate();
+
+ return (
+
+
+
+ {item.icon}
+
+
+
+ {item.name}
+
+
+
+
+ );
+};
+
+export default NavItem;
diff --git a/client/src/Components/Sidebar/index.jsx b/client/src/Components/Sidebar/index.jsx
index cad7d3b7d..ac4a68c0f 100644
--- a/client/src/Components/Sidebar/index.jsx
+++ b/client/src/Components/Sidebar/index.jsx
@@ -1,21 +1,16 @@
-import IconButton from "@mui/material/IconButton";
import Stack from "@mui/material/Stack";
-import ArrowRight from "../../assets/icons/right-arrow.svg?react";
-import ArrowLeft from "../../assets/icons/left-arrow.svg?react";
-import List from "@mui/material/List";
-import ListItemButton from "@mui/material/ListItemButton";
-import ListItemIcon from "@mui/material/ListItemIcon";
-import ListItemText from "@mui/material/ListItemText";
-import Tooltip from "@mui/material/Tooltip";
-import Logo from "./logo";
-import ThemeSwitch from "../ThemeSwitch";
-import Avatar from "../Avatar";
+import List from "@mui/material/List";
+import Logo from "./logo";
+import CollapseButton from "./collapseButton";
+import Divider from "@mui/material/Divider";
+import NavItem from "./components/navItem";
+import AuthFooter from "./components/authFooter";
+
import StarPrompt from "../StarPrompt";
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";
import Support from "../../assets/icons/support.svg?react";
import Maintenance from "../../assets/icons/maintenance.svg?react";
import Monitors from "../../assets/icons/monitors.svg?react";
@@ -25,7 +20,6 @@ import PageSpeed from "../../assets/icons/page-speed.svg?react";
import Settings from "../../assets/icons/settings.svg?react";
import ArrowDown from "../../assets/icons/down-arrow.svg?react";
import ArrowUp from "../../assets/icons/up-arrow.svg?react";
-import DotsVertical from "../../assets/icons/dots-vertical.svg?react";
import ChangeLog from "../../assets/icons/changeLog.svg?react";
import Docs from "../../assets/icons/docs.svg?react";
import StatusPages from "../../assets/icons/status-pages.svg?react";
@@ -35,12 +29,18 @@ import Logs from "../../assets/icons/logs.svg?react";
// Utils
import { useTheme } from "@mui/material/styles";
-import { useDispatch, useSelector } from "react-redux";
-import { toggleSidebar } from "../../Features/UI/uiSlice";
+import { useSelector } from "react-redux";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router";
+const URL_MAP = {
+ support: "https://discord.com/invite/NAb6H3UTjK",
+ discussions: "https://github.com/bluewave-labs/checkmate/discussions",
+ docs: "https://bluewavelabs.gitbook.io/checkmate",
+ changelog: "https://github.com/bluewave-labs/checkmate/releases",
+};
+
const getMenu = (t) => [
{ name: t("menu.uptime"), path: "uptime", icon: },
{ name: t("menu.pagespeed"), path: "pagespeed", icon: },
@@ -64,9 +64,25 @@ const getMenu = (t) => [
},
];
+const getOtherMenuItems = (t) => [
+ { name: t("menu.support"), path: "support", icon: },
+ {
+ name: t("menu.discussions"),
+ path: "discussions",
+ icon: ,
+ },
+ { name: t("menu.docs"), path: "docs", icon: },
+ { name: t("menu.changelog"), path: "changelog", icon: },
+];
+
+const getAccountMenuItems = (t) => [
+ { name: t("menu.profile"), path: "account/profile", icon: },
+ { name: t("menu.password"), path: "account/password", icon: },
+ { name: t("menu.team"), path: "account/team", icon: },
+];
+
const Sidebar = () => {
const theme = useTheme();
- const dispatch = useDispatch();
const { t } = useTranslation();
const navigate = useNavigate();
// Redux state
@@ -76,100 +92,82 @@ const Sidebar = () => {
const [open, setOpen] = useState({ Dashboard: false, Account: false, Other: false });
const menu = getMenu(t);
- console.log(collapsed);
+ const otherMenuItems = getOtherMenuItems(t);
+ const accountMenuItems = getAccountMenuItems(t);
+
return (
- {
- setOpen((prev) =>
- Object.fromEntries(Object.keys(prev).map((key) => [key, false]))
- );
- dispatch(toggleSidebar());
- }}
- >
- {collapsed ? : }
-
-
+
+
{menu.map((item) => {
- return item.path ? (
- /* If item has a path */
-
- navigate(`/${item.path}`)}
- sx={{
- height: "37px",
- gap: theme.spacing(4),
- borderRadius: theme.shape.borderRadius,
- px: theme.spacing(4),
- pl: theme.spacing(5),
- }}
- >
- {item.icon}
- {!collapsed && {item.name}}
-
-
- ) : null;
+ item={item}
+ collapsed={collapsed}
+ selected={selected}
+ onClick={() => navigate(`/${item.path}`)}
+ />
+ );
})}
+ {!collapsed && }
+
+ {otherMenuItems.map((item) => {
+ const selected = location.pathname.startsWith(`/${item.path}`);
+
+ return (
+ {
+ const url = URL_MAP[item.path];
+ if (url) {
+ window.open(url, "_blank", "noreferrer");
+ } else {
+ navigate(`/${item.path}`);
+ }
+ }}
+ />
+ );
+ })}
+
+
+
);
};
diff --git a/client/src/Components/Sidebar/logo.jsx b/client/src/Components/Sidebar/logo.jsx
index 0a8a76f0c..0d7adf54f 100644
--- a/client/src/Components/Sidebar/logo.jsx
+++ b/client/src/Components/Sidebar/logo.jsx
@@ -1,10 +1,11 @@
import Stack from "@mui/material/Stack";
+import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import { useTheme } from "@mui/material/styles";
import { useNavigate } from "react-router";
import { useTranslation } from "react-i18next";
-const Logo = () => {
+const Logo = ({ collapsed }) => {
const { t } = useTranslation();
const theme = useTheme();
const navigate = useNavigate();
@@ -35,20 +36,31 @@ const Logo = () => {
sx={{
position: "relative",
backgroundColor: theme.palette.accent.main,
- color: theme.palette.accent.contrastText,
borderRadius: theme.shape.borderRadius,
userSelect: "none",
}}
>
C
-
- {t("common.appName")}
-
+ {" "}
+
+ {t("common.appName")}
+
+
);
diff --git a/client/src/Utils/Theme/globalTheme.js b/client/src/Utils/Theme/globalTheme.js
index ec292142d..64ac46fd1 100644
--- a/client/src/Utils/Theme/globalTheme.js
+++ b/client/src/Utils/Theme/globalTheme.js
@@ -233,16 +233,16 @@ const baseTheme = (palette) => ({
},
MuiList: {
styleOverrides: {
- root: {
+ root: ({ theme }) => ({
padding: 0,
- },
+ }),
},
},
MuiListItemButton: {
styleOverrides: {
- root: {
- transition: "none",
- },
+ root: ({ theme }) => ({
+ transition: "background-color .3s",
+ }),
},
},
MuiListItemText: {
@@ -279,6 +279,7 @@ const baseTheme = (palette) => ({
}),
},
},
+
MuiTableHead: {
styleOverrides: {
root: ({ theme }) => ({