diff --git a/client/src/Components/Layouts/HomeLayout/index.css b/client/src/Components/Layouts/HomeLayout/index.css index 2db147f41..56606a935 100644 --- a/client/src/Components/Layouts/HomeLayout/index.css +++ b/client/src/Components/Layouts/HomeLayout/index.css @@ -12,13 +12,13 @@ } } */ -.home-layout aside { +/* .home-layout aside { position: sticky; top: 0; left: 0; height: 100vh; max-width: var(--env-var-side-bar-width); -} +} */ .home-layout > div { min-height: calc(100vh - var(--env-var-spacing-2) * 2); diff --git a/client/src/Components/Sidebar/index.css b/client/src/Components/Sidebar/index.bak similarity index 98% rename from client/src/Components/Sidebar/index.css rename to client/src/Components/Sidebar/index.bak index ef921acca..a14a0f3d6 100644 --- a/client/src/Components/Sidebar/index.css +++ b/client/src/Components/Sidebar/index.bak @@ -1,5 +1,4 @@ -/* TODO */ -aside .MuiList-root svg { +TODO aside .MuiList-root svg { width: 20px; height: 20px; opacity: 0.9; diff --git a/client/src/Components/Sidebar/index.jsx b/client/src/Components/Sidebar/index.jsx index 219ceff1e..cad7d3b7d 100644 --- a/client/src/Components/Sidebar/index.jsx +++ b/client/src/Components/Sidebar/index.jsx @@ -1,19 +1,14 @@ -import React, { useEffect, useState, useRef } from "react"; -import { - Box, - Collapse, - Divider, - IconButton, - List, - ListItemButton, - ListItemIcon, - ListItemText, - Menu, - MenuItem, - Stack, - Tooltip, - Typography, -} from "@mui/material"; +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 StarPrompt from "../StarPrompt"; @@ -30,8 +25,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 ArrowRight from "../../assets/icons/right-arrow.svg?react"; -import ArrowLeft from "../../assets/icons/left-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"; @@ -40,17 +33,13 @@ import Discussions from "../../assets/icons/discussions.svg?react"; import Notifications from "../../assets/icons/notifications.svg?react"; import Logs from "../../assets/icons/logs.svg?react"; -import "./index.css"; - // Utils -import { useLocation, useNavigate } from "react-router"; -import { useTheme } from "@emotion/react"; +import { useTheme } from "@mui/material/styles"; import { useDispatch, useSelector } from "react-redux"; -import { useTranslation } from "react-i18next"; -import { clearAuthState } from "../../Features/Auth/authSlice"; import { toggleSidebar } from "../../Features/UI/uiSlice"; -import { TurnedIn } from "@mui/icons-material"; -import { rules } from "eslint-plugin-react-refresh"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useNavigate } from "react-router"; const getMenu = (t) => [ { name: t("menu.uptime"), path: "uptime", icon: }, @@ -75,207 +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: }, -]; - -/* TODO this could be a key in nested Path would be the link */ -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 PATH_MAP = { - monitors: "Dashboard", - pagespeed: "Dashboard", - infrastructure: "Dashboard", - account: "Account", - settings: "Settings", -}; - -/** - * @component - * Sidebar component serves as a sidebar containing a menu. - * - * @returns {JSX.Element} The JSX element representing the Sidebar component. - */ - -function Sidebar() { +const Sidebar = () => { const theme = useTheme(); - const navigate = useNavigate(); - const location = useLocation(); const dispatch = useDispatch(); const { t } = useTranslation(); - const authState = useSelector((state) => state.auth); - - const otherMenuItems = getOtherMenuItems(t); - const accountMenuItems = getAccountMenuItems(t); + const navigate = useNavigate(); + // Redux state const collapsed = useSelector((state) => state.ui.sidebar.collapsed); + + // Local state const [open, setOpen] = useState({ Dashboard: false, Account: false, Other: false }); - const [anchorEl, setAnchorEl] = useState(null); - const [popup, setPopup] = useState(); - const { user } = useSelector((state) => state.auth); - const sidebarRef = useRef(null); - const [sidebarReady, setSidebarReady] = useState(false); - const TRANSITION_DURATION = 200; - let menu = getMenu(t); - menu = menu.filter((item) => { - if (item.path === "logs") { - return user.role?.includes("admin") || user.role?.includes("superadmin"); - } - return true; - }); - - useEffect(() => { - if (!collapsed) { - setSidebarReady(false); - const timeout = setTimeout(() => { - setSidebarReady(true); - }, TRANSITION_DURATION); - return () => clearTimeout(timeout); - } else { - setSidebarReady(false); - } - }, [collapsed]); - - const renderAccountMenuItems = () => { - let filteredAccountMenuItems = [...accountMenuItems]; - - // 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) => ( - { - closePopup(); - navigate(item.path); - }} - sx={{ - gap: theme.spacing(2), - borderRadius: theme.shape.borderRadius, - pl: theme.spacing(4), - }} - > - {item.icon} - {item.name} - - )); - }; - - const openPopup = (event, id) => { - setAnchorEl(event.currentTarget); - setPopup(id); - }; - const closePopup = () => { - setAnchorEl(null); - }; - - /** - * Handles logging out the user - * - */ - const logout = async () => { - // Clear auth state - dispatch(clearAuthState()); - navigate("/login"); - }; - - useEffect(() => { - const matchedKey = Object.keys(PATH_MAP).find((key) => - location.pathname.includes(key) - ); - - if (matchedKey) { - setOpen((prev) => ({ ...prev, [PATH_MAP[matchedKey]]: true })); - } - }, [location]); - - const iconColor = theme.palette.primary.contrastTextTertiary; - const sidebarClassName = `${collapsed ? "collapsed" : "expanded"} ${sidebarReady ? "sidebar-ready" : ""}`; - - /* TODO refactor this, there are a some ternaries and comments in the return */ + const menu = getMenu(t); + console.log(collapsed); return ( {collapsed ? : } - {/* TODO Alignment done using padding. Use single source of truth to that*/} - - {/* TODO Abstract logo into component */} - {/* TODO Turn logo into a link */} - navigate("/")} - sx={{ cursor: "pointer" }} - > - - C - - - {t("common.appName")} - - - - - - {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} - {item.name} - - - ) : collapsed ? ( - /* TODO Do we ever get here? If item does not have a path and collapsed state is true */ - - - openPopup(event, item.name)} - sx={{ - position: "relative", - gap: theme.spacing(4), - borderRadius: theme.shape.borderRadius, - px: theme.spacing(4), - }} - > - {item.icon} - {item.name} - - - - {item.nested.map((child) => { - if ( - child.name === "Team" && - authState.user?.role && - !authState.user.role.includes("superadmin") - ) { - return null; - } - - return ( - { - const url = URL_MAP[child.path]; - if (url) { - window.open(url, "_blank", "noreferrer"); - } else { - navigate(`/${child.path}`); - } - closePopup(); - }} - sx={{ - gap: theme.spacing(4), - opacity: 0.9, - /* TODO this has no effect? */ - "& svg": { - "& path": { - stroke: theme.palette.primary.contrastTextTertiary, - strokeWidth: 1.1, - }, - }, - }} - > - {child.icon} - {child.name} - - ); - })} - - - ) : ( - /* TODO Do we ever get here? If item does not have a path and collapsed state is false */ - - - setOpen((prev) => ({ - ...Object.fromEntries(Object.keys(prev).map((key) => [key, false])), - [item.name]: !prev[item.name], - })) - } - sx={{ - gap: theme.spacing(4), - borderRadius: theme.shape.borderRadius, - px: theme.spacing(4), - }} - > - {item.icon} - {item.name} - {open[`${item.name}`] ? : } - - - - {item.nested.map((child) => { - if ( - child.name === "Team" && - authState.user?.role && - !authState.user.role.includes("superadmin") - ) { - return null; - } - - return ( - { - const url = URL_MAP[child.path]; - if (url) { - window.open(url, "_blank", "noreferrer"); - } else { - navigate(`/${child.path}`); - } - }} - sx={{ - gap: theme.spacing(4), - borderRadius: theme.shape.borderRadius, - pl: theme.spacing(4), - "&::before": { - content: `""`, - position: "absolute", - top: 0, - left: "-7px", - height: "100%", - borderLeft: 1, - borderLeftColor: theme.palette.primary.lowContrast, - }, - "&:last-child::before": { - height: "50%", - }, - "&::after": { - content: `""`, - position: "absolute", - top: "45%", - left: "-8px", - height: "3px", - width: "3px", - borderRadius: "50%", - backgroundColor: theme.palette.primary.lowContrast, - }, - "&.selected-path::after": { - /* TODO what is this selector doing? */ - backgroundColor: theme.palette.primary.contrastTextTertiary, - transform: "scale(1.2)", - }, - }} - > - {child.icon} - {child.name} - - ); - })} - - - - ); - })} - - - - {!collapsed && } - + - {otherMenuItems.map((item) => { + {menu.map((item) => { return item.path ? ( + /* If item has a path */ { - const url = URL_MAP[item.path]; - if (url) { - window.open(url, "_blank", "noreferrer"); - } else { - navigate(`/${item.path}`); - } - }} + onClick={() => navigate(`/${item.path}`)} sx={{ height: "37px", gap: theme.spacing(4), @@ -666,208 +163,15 @@ function Sidebar() { pl: theme.spacing(5), }} > - {item.icon} - {item.name} {" "} + {item.icon} + {!collapsed && {item.name}} ) : null; })} - - - {collapsed ? ( - <> - - openPopup(event, "logout")} - sx={{ p: 0, "&:focus": { outline: "none" } }} - > - - - - - ) : ( - <> - - - - {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")} - > - - - - - - )} - - {collapsed && ( - - - - {authState.user?.firstName} {authState.user?.lastName} - - - {authState.user?.role} - - - - )} - {/* TODO Do we need two dividers? */} - {collapsed && } - {/* */} - {renderAccountMenuItems()} - - - {t("menu.logOut", "Log out")} - - - ); -} +}; export default Sidebar; diff --git a/client/src/Components/Sidebar/index_old.jsx b/client/src/Components/Sidebar/index_old.jsx new file mode 100644 index 000000000..219ceff1e --- /dev/null +++ b/client/src/Components/Sidebar/index_old.jsx @@ -0,0 +1,873 @@ +import React, { useEffect, useState, useRef } from "react"; +import { + Box, + Collapse, + Divider, + IconButton, + List, + ListItemButton, + ListItemIcon, + ListItemText, + Menu, + MenuItem, + Stack, + Tooltip, + Typography, +} from "@mui/material"; +import ThemeSwitch from "../ThemeSwitch"; +import Avatar from "../Avatar"; +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"; +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"; +import ArrowDown from "../../assets/icons/down-arrow.svg?react"; +import ArrowUp from "../../assets/icons/up-arrow.svg?react"; +import ArrowRight from "../../assets/icons/right-arrow.svg?react"; +import ArrowLeft from "../../assets/icons/left-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"; +import Discussions from "../../assets/icons/discussions.svg?react"; +import Notifications from "../../assets/icons/notifications.svg?react"; +import Logs from "../../assets/icons/logs.svg?react"; + +import "./index.css"; + +// Utils +import { useLocation, useNavigate } from "react-router"; +import { useTheme } from "@emotion/react"; +import { useDispatch, useSelector } from "react-redux"; +import { useTranslation } from "react-i18next"; +import { clearAuthState } from "../../Features/Auth/authSlice"; +import { toggleSidebar } from "../../Features/UI/uiSlice"; +import { TurnedIn } from "@mui/icons-material"; +import { rules } from "eslint-plugin-react-refresh"; + +const getMenu = (t) => [ + { name: t("menu.uptime"), path: "uptime", icon: }, + { name: t("menu.pagespeed"), path: "pagespeed", icon: }, + + { name: t("menu.infrastructure"), path: "infrastructure", icon: }, + { + name: t("menu.notifications"), + path: "notifications", + icon: , + }, + { name: t("menu.incidents"), path: "incidents", icon: }, + + { name: t("menu.statusPages"), path: "status", icon: }, + { name: t("menu.maintenance"), path: "maintenance", icon: }, + { name: t("menu.logs"), path: "logs", icon: }, + + { + name: t("menu.settings"), + icon: , + path: "settings", + }, +]; + +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: }, +]; + +/* TODO this could be a key in nested Path would be the link */ +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 PATH_MAP = { + monitors: "Dashboard", + pagespeed: "Dashboard", + infrastructure: "Dashboard", + account: "Account", + settings: "Settings", +}; + +/** + * @component + * Sidebar component serves as a sidebar containing a menu. + * + * @returns {JSX.Element} The JSX element representing the Sidebar component. + */ + +function Sidebar() { + const theme = useTheme(); + const navigate = useNavigate(); + const location = useLocation(); + const dispatch = useDispatch(); + const { t } = useTranslation(); + const authState = useSelector((state) => state.auth); + + const otherMenuItems = getOtherMenuItems(t); + const accountMenuItems = getAccountMenuItems(t); + const collapsed = useSelector((state) => state.ui.sidebar.collapsed); + const [open, setOpen] = useState({ Dashboard: false, Account: false, Other: false }); + const [anchorEl, setAnchorEl] = useState(null); + const [popup, setPopup] = useState(); + const { user } = useSelector((state) => state.auth); + + const sidebarRef = useRef(null); + const [sidebarReady, setSidebarReady] = useState(false); + const TRANSITION_DURATION = 200; + let menu = getMenu(t); + menu = menu.filter((item) => { + if (item.path === "logs") { + return user.role?.includes("admin") || user.role?.includes("superadmin"); + } + return true; + }); + + useEffect(() => { + if (!collapsed) { + setSidebarReady(false); + const timeout = setTimeout(() => { + setSidebarReady(true); + }, TRANSITION_DURATION); + return () => clearTimeout(timeout); + } else { + setSidebarReady(false); + } + }, [collapsed]); + + const renderAccountMenuItems = () => { + let filteredAccountMenuItems = [...accountMenuItems]; + + // 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) => ( + { + closePopup(); + navigate(item.path); + }} + sx={{ + gap: theme.spacing(2), + borderRadius: theme.shape.borderRadius, + pl: theme.spacing(4), + }} + > + {item.icon} + {item.name} + + )); + }; + + const openPopup = (event, id) => { + setAnchorEl(event.currentTarget); + setPopup(id); + }; + const closePopup = () => { + setAnchorEl(null); + }; + + /** + * Handles logging out the user + * + */ + const logout = async () => { + // Clear auth state + dispatch(clearAuthState()); + navigate("/login"); + }; + + useEffect(() => { + const matchedKey = Object.keys(PATH_MAP).find((key) => + location.pathname.includes(key) + ); + + if (matchedKey) { + setOpen((prev) => ({ ...prev, [PATH_MAP[matchedKey]]: true })); + } + }, [location]); + + const iconColor = theme.palette.primary.contrastTextTertiary; + const sidebarClassName = `${collapsed ? "collapsed" : "expanded"} ${sidebarReady ? "sidebar-ready" : ""}`; + + /* TODO refactor this, there are a some ternaries and comments in the return */ + return ( + + { + setOpen((prev) => + Object.fromEntries(Object.keys(prev).map((key) => [key, false])) + ); + dispatch(toggleSidebar()); + }} + > + {collapsed ? : } + + {/* TODO Alignment done using padding. Use single source of truth to that*/} + + {/* TODO Abstract logo into component */} + {/* TODO Turn logo into a link */} + navigate("/")} + sx={{ cursor: "pointer" }} + > + + C + + + {t("common.appName")} + + + + + + {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} + {item.name} + + + ) : collapsed ? ( + /* TODO Do we ever get here? If item does not have a path and collapsed state is true */ + + + openPopup(event, item.name)} + sx={{ + position: "relative", + gap: theme.spacing(4), + borderRadius: theme.shape.borderRadius, + px: theme.spacing(4), + }} + > + {item.icon} + {item.name} + + + + {item.nested.map((child) => { + if ( + child.name === "Team" && + authState.user?.role && + !authState.user.role.includes("superadmin") + ) { + return null; + } + + return ( + { + const url = URL_MAP[child.path]; + if (url) { + window.open(url, "_blank", "noreferrer"); + } else { + navigate(`/${child.path}`); + } + closePopup(); + }} + sx={{ + gap: theme.spacing(4), + opacity: 0.9, + /* TODO this has no effect? */ + "& svg": { + "& path": { + stroke: theme.palette.primary.contrastTextTertiary, + strokeWidth: 1.1, + }, + }, + }} + > + {child.icon} + {child.name} + + ); + })} + + + ) : ( + /* TODO Do we ever get here? If item does not have a path and collapsed state is false */ + + + setOpen((prev) => ({ + ...Object.fromEntries(Object.keys(prev).map((key) => [key, false])), + [item.name]: !prev[item.name], + })) + } + sx={{ + gap: theme.spacing(4), + borderRadius: theme.shape.borderRadius, + px: theme.spacing(4), + }} + > + {item.icon} + {item.name} + {open[`${item.name}`] ? : } + + + + {item.nested.map((child) => { + if ( + child.name === "Team" && + authState.user?.role && + !authState.user.role.includes("superadmin") + ) { + return null; + } + + return ( + { + const url = URL_MAP[child.path]; + if (url) { + window.open(url, "_blank", "noreferrer"); + } else { + navigate(`/${child.path}`); + } + }} + sx={{ + gap: theme.spacing(4), + borderRadius: theme.shape.borderRadius, + pl: theme.spacing(4), + "&::before": { + content: `""`, + position: "absolute", + top: 0, + left: "-7px", + height: "100%", + borderLeft: 1, + borderLeftColor: theme.palette.primary.lowContrast, + }, + "&:last-child::before": { + height: "50%", + }, + "&::after": { + content: `""`, + position: "absolute", + top: "45%", + left: "-8px", + height: "3px", + width: "3px", + borderRadius: "50%", + backgroundColor: theme.palette.primary.lowContrast, + }, + "&.selected-path::after": { + /* TODO what is this selector doing? */ + backgroundColor: theme.palette.primary.contrastTextTertiary, + transform: "scale(1.2)", + }, + }} + > + {child.icon} + {child.name} + + ); + })} + + + + ); + })} + + + + {!collapsed && } + + + {otherMenuItems.map((item) => { + return item.path ? ( + + { + const url = URL_MAP[item.path]; + if (url) { + window.open(url, "_blank", "noreferrer"); + } else { + navigate(`/${item.path}`); + } + }} + sx={{ + height: "37px", + gap: theme.spacing(4), + borderRadius: theme.shape.borderRadius, + px: theme.spacing(4), + pl: theme.spacing(5), + }} + > + {item.icon} + {item.name} {" "} + + + ) : null; + })} + + + + {collapsed ? ( + <> + + openPopup(event, "logout")} + sx={{ p: 0, "&:focus": { outline: "none" } }} + > + + + + + ) : ( + <> + + + + {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")} + > + + + + + + )} + + {collapsed && ( + + + + {authState.user?.firstName} {authState.user?.lastName} + + + {authState.user?.role} + + + + )} + {/* TODO Do we need two dividers? */} + {collapsed && } + {/* */} + {renderAccountMenuItems()} + + + {t("menu.logOut", "Log out")} + + + + + ); +} + +export default Sidebar; diff --git a/client/src/Components/Sidebar/logo.jsx b/client/src/Components/Sidebar/logo.jsx new file mode 100644 index 000000000..0a8a76f0c --- /dev/null +++ b/client/src/Components/Sidebar/logo.jsx @@ -0,0 +1,57 @@ +import Stack from "@mui/material/Stack"; +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 { t } = useTranslation(); + const theme = useTheme(); + const navigate = useNavigate(); + + return ( + + {/* TODO Abstract logo into component */} + {/* TODO Turn logo into a link */} + navigate("/")} + sx={{ cursor: "pointer" }} + > + + C + + + {t("common.appName")} + + + + ); +}; + +export default Logo;