diff --git a/client/src/Components/v1/Layouts/HomeLayout/index.jsx b/client/src/Components/v1/Layouts/HomeLayout/index.jsx index b078fe048..adadf638f 100644 --- a/client/src/Components/v1/Layouts/HomeLayout/index.jsx +++ b/client/src/Components/v1/Layouts/HomeLayout/index.jsx @@ -14,7 +14,6 @@ const HomeLayout = () => { flexDirection="row" > - {/* Spacer for fixed sidebar */} { const v2theme = mode === "dark" ? darkTheme : lightTheme; return ( - - + + - {children} - - + + {children} + ); }; diff --git a/client/src/Components/v2/layout/RootLayout.tsx b/client/src/Components/v2/layout/RootLayout.tsx index e69de29bb..b0c0effe1 100644 --- a/client/src/Components/v2/layout/RootLayout.tsx +++ b/client/src/Components/v2/layout/RootLayout.tsx @@ -0,0 +1,46 @@ +import { Sidebar } from "@/Components/v2/sidebar"; +import { Outlet } from "react-router"; +import Stack from "@mui/material/Stack"; + +// TEMP +import { useSelector } from "react-redux"; +import type { RootState } from "@/Types/state"; +import { lightTheme, darkTheme } from "@/Utils/Theme/v2Theme"; +import { ThemeProvider, useTheme } from "@mui/material"; + +const RootLayout = () => { + const mode = useSelector((state: RootState) => state.ui.mode); + const v2theme = mode === "dark" ? darkTheme : lightTheme; + const theme = useTheme(); + return ( + + + + + + + + + + + ); +}; + +export default RootLayout; diff --git a/client/src/Components/v2/sidebar/Logo.tsx b/client/src/Components/v2/sidebar/Logo.tsx new file mode 100644 index 000000000..131ac03bc --- /dev/null +++ b/client/src/Components/v2/sidebar/Logo.tsx @@ -0,0 +1,60 @@ +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 { toggleSidebar } from "@/Features/UI/uiSlice.js"; +import { useDispatch } from "react-redux"; +import { useTranslation } from "react-i18next"; +import useSidebar from "@/Hooks/useSidebar"; + +export const Logo = () => { + const { t } = useTranslation(); + const theme = useTheme(); + const dispatch = useDispatch(); + const { collapsed } = useSidebar(); + return ( + { + dispatch(toggleSidebar()); + }} + sx={{ cursor: "pointer" }} + > + + C + + + {" "} + + {t("common.appName")} + + + + ); +}; diff --git a/client/src/Components/v2/sidebar/Menu.tsx b/client/src/Components/v2/sidebar/Menu.tsx new file mode 100644 index 000000000..ad58f4455 --- /dev/null +++ b/client/src/Components/v2/sidebar/Menu.tsx @@ -0,0 +1,41 @@ +import { Icon } from "@/Components/v2/design-elements"; + +import { + Globe, + Gauge, + Link, + Bell, + FileText, + AlertTriangle, + Wifi, + Wrench, + Database, + Settings, +} from "lucide-react"; + +export const getMenu = (t: Function) => { + return [ + { 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.checks"), path: "checks", 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", + }, + ]; +}; diff --git a/client/src/Components/v2/sidebar/NavItem.tsx b/client/src/Components/v2/sidebar/NavItem.tsx new file mode 100644 index 000000000..756b10d60 --- /dev/null +++ b/client/src/Components/v2/sidebar/NavItem.tsx @@ -0,0 +1,110 @@ +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 "@mui/material/styles"; +import useSidebar from "@/Hooks/useSidebar"; + +export interface NavData { + name: string; + icon: React.ReactElement; +} + +export const NavItem = ({ + item, + selected, + onClick, +}: { + item: NavData; + selected: boolean; + onClick: (event: React.MouseEvent) => void; +}) => { + const { collapsed } = useSidebar(); + const theme = useTheme(); + const iconStroke = selected ? theme.palette.primary.main : theme.palette.text.secondary; + + const buttonBgColor = selected ? theme.palette.action.selected : "transparent"; + const buttonBgHoverColor = selected + ? theme.palette.action.selected + : theme.palette.action.hover; + const fontWeight = selected ? 600 : 400; + return ( + + + + {item.icon} + + + + {item.name} + + + + + ); +}; diff --git a/client/src/Components/v2/sidebar/index.tsx b/client/src/Components/v2/sidebar/index.tsx index e69de29bb..10725c6a0 100644 --- a/client/src/Components/v2/sidebar/index.tsx +++ b/client/src/Components/v2/sidebar/index.tsx @@ -0,0 +1,69 @@ +import Stack from "@mui/material/Stack"; +import List from "@mui/material/List"; +import { useSidebar } from "@/Hooks/useSidebar.js"; +import { Logo } from "@/Components/v2/sidebar/Logo"; +import { getMenu } from "@/Components/v2/sidebar/Menu"; +import { NavItem } from "@/Components/v2/sidebar/NavItem"; + +import { useNavigate } from "react-router"; +import { useTranslation } from "react-i18next"; +import { useTheme, useMediaQuery } from "@mui/material"; +import { useDispatch } from "react-redux"; +import { toggleSidebar } from "@/Features/UI/uiSlice"; + +export const Sidebar = () => { + const dispatch = useDispatch(); + const navigate = useNavigate(); + const { t } = useTranslation(); + const { width, transition } = useSidebar(); + const theme = useTheme(); + const isSmall = useMediaQuery(theme.breakpoints.down("md")); + const menu = getMenu(t); + + return ( + t.zIndex.drawer : "auto", + }} + > + + + {menu.map((item) => { + const selected = location.pathname.startsWith(`/${item.path}`); + return ( + { + navigate(`/${item.path}`); + if (isSmall) { + dispatch(toggleSidebar()); + } + }} + /> + ); + })} + + + ); +}; diff --git a/client/src/Hooks/useSidebar.js b/client/src/Hooks/useSidebar.js deleted file mode 100644 index 97724b417..000000000 --- a/client/src/Hooks/useSidebar.js +++ /dev/null @@ -1,29 +0,0 @@ -import { useSelector } from "react-redux"; - -// CSS variable names for sidebar widths -const SIDEBAR_WIDTH_VAR = "var(--env-var-side-bar-width)"; -const SIDEBAR_COLLAPSED_WIDTH_VAR = "var(--env-var-side-bar-collapsed-width)"; - -// Transition timing for sidebar width changes -const SIDEBAR_TRANSITION = "width 650ms cubic-bezier(0.36, -0.01, 0, 0.77)"; - -/** - * Hook to get sidebar state and computed width - * Centralizes sidebar width logic to avoid duplication between Sidebar and HomeLayout - * - * @returns {Object} Sidebar state and styles - * @returns {boolean} collapsed - Whether the sidebar is collapsed - * @returns {string} width - CSS width value based on collapsed state - * @returns {string} transition - CSS transition for width changes - */ -export const useSidebar = () => { - const collapsed = useSelector((state) => state.ui.sidebar?.collapsed ?? false); - - return { - collapsed, - width: collapsed ? SIDEBAR_COLLAPSED_WIDTH_VAR : SIDEBAR_WIDTH_VAR, - transition: SIDEBAR_TRANSITION, - }; -}; - -export default useSidebar; diff --git a/client/src/Hooks/useSidebar.ts b/client/src/Hooks/useSidebar.ts new file mode 100644 index 000000000..1cf705dd1 --- /dev/null +++ b/client/src/Hooks/useSidebar.ts @@ -0,0 +1,25 @@ +import type { RootState } from "@/Types/state"; +import { useSelector } from "react-redux"; + +// CSS variable names for sidebar widths +const SIDEBAR_WIDTH_VAR = 250; +const SIDEBAR_COLLAPSED_WIDTH_VAR = 64; + +// Transition timing for sidebar width changes +const SIDEBAR_TRANSITION = "width 650ms cubic-bezier(0.36, -0.01, 0, 0.77)"; + +export const useSidebar = () => { + const collapsed = useSelector( + (state: RootState) => state.ui.sidebar?.collapsed ?? false + ); + + return { + collapsed, + collapsedWidth: SIDEBAR_COLLAPSED_WIDTH_VAR, + expandedWidth: SIDEBAR_WIDTH_VAR, + width: collapsed ? SIDEBAR_COLLAPSED_WIDTH_VAR : SIDEBAR_WIDTH_VAR, + transition: SIDEBAR_TRANSITION, + }; +}; + +export default useSidebar; diff --git a/client/src/Routes/index.jsx b/client/src/Routes/index.jsx index 07dc1793c..0a66b0cf3 100644 --- a/client/src/Routes/index.jsx +++ b/client/src/Routes/index.jsx @@ -4,7 +4,7 @@ import { lightTheme, darkTheme } from "@/Utils/Theme/v2Theme"; import { useSelector } from "react-redux"; import { Navigate, Route, Routes as LibRoutes } from "react-router"; -import HomeLayout from "@/Components/v1/Layouts/HomeLayout"; +import RootLayout from "@/Components/v2/layout/RootLayout"; import NotFound from "@/Pages/NotFound"; // Auth @@ -67,7 +67,7 @@ const Routes = () => { path="/" element={ - + } >