From fc9018e314bc7de0724c05070ff649a01a0e22f2 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Sat, 27 Sep 2025 15:47:56 -0700 Subject: [PATCH] Add collapse button and logo --- client/src/Components/v2/Arrows/ArrowLeft.tsx | 36 +++++++++++ .../src/Components/v2/Arrows/ArrowRight.tsx | 28 +++++++++ .../src/Components/v2/Layouts/RootLayout.tsx | 35 +---------- .../v2/Layouts/Sidebar/CollapseButton.tsx | 50 ++++++++++++++++ .../Components/v2/Layouts/Sidebar/Logo.tsx | 59 +++++++++++++++++++ .../Components/v2/Layouts/Sidebar/index.tsx | 40 +++++++++++++ client/src/Features/UI/uiSlice.js | 5 ++ client/src/Utils/Theme/v2/palette.ts | 10 ++++ client/src/Utils/Theme/v2/theme.ts | 3 + client/src/types/env.d.ts | 8 +++ client/src/types/mui.d.ts | 3 + 11 files changed, 243 insertions(+), 34 deletions(-) create mode 100644 client/src/Components/v2/Arrows/ArrowLeft.tsx create mode 100644 client/src/Components/v2/Arrows/ArrowRight.tsx create mode 100644 client/src/Components/v2/Layouts/Sidebar/CollapseButton.tsx create mode 100644 client/src/Components/v2/Layouts/Sidebar/Logo.tsx create mode 100644 client/src/Components/v2/Layouts/Sidebar/index.tsx diff --git a/client/src/Components/v2/Arrows/ArrowLeft.tsx b/client/src/Components/v2/Arrows/ArrowLeft.tsx new file mode 100644 index 000000000..01d1342c8 --- /dev/null +++ b/client/src/Components/v2/Arrows/ArrowLeft.tsx @@ -0,0 +1,36 @@ +import LeftArrow from "@/assets/icons/left-arrow.svg?react"; +import LeftArrowDouble from "@/assets/icons/left-arrow-double.svg?react"; +import LeftArrowLong from "@/assets/icons/left-arrow-long.svg?react"; + +export const ArrowLeft = ({ + type, + color = "#667085", + ...props +}: { + type?: string; + color?: string | undefined; + [key: string]: any; +}) => { + if (type === "double") { + return ( + + ); + } else if (type === "long") { + return ( + + ); + } else { + return ( + + ); + } +}; diff --git a/client/src/Components/v2/Arrows/ArrowRight.tsx b/client/src/Components/v2/Arrows/ArrowRight.tsx new file mode 100644 index 000000000..c73165f16 --- /dev/null +++ b/client/src/Components/v2/Arrows/ArrowRight.tsx @@ -0,0 +1,28 @@ +import RightArrow from "@/assets/icons/right-arrow.svg?react"; +import RightArrowDouble from "@/assets/icons/right-arrow-double.svg?react"; + +export const ArrowRight = ({ + type, + color = "#667085", + ...props +}: { + type?: string; + color?: string | undefined; + [key: string]: any; +}) => { + if (type === "double") { + return ( + + ); + } else { + return ( + + ); + } +}; diff --git a/client/src/Components/v2/Layouts/RootLayout.tsx b/client/src/Components/v2/Layouts/RootLayout.tsx index be2f42e30..dbc6f2e5e 100644 --- a/client/src/Components/v2/Layouts/RootLayout.tsx +++ b/client/src/Components/v2/Layouts/RootLayout.tsx @@ -1,39 +1,6 @@ -import { useState, useEffect } from "react"; -import useMediaQuery from "@mui/material/useMediaQuery"; -import { useTheme } from "@mui/material/styles"; - import { Outlet } from "react-router"; import Stack from "@mui/material/Stack"; -import Box from "@mui/material/Box"; - -const COLLAPSED_WIDTH = 50; -const EXPANDED_WIDTH = 250; - -const SideBar = () => { - const theme = useTheme(); - const isSmall = useMediaQuery(theme.breakpoints.down("md")); - - const [collapsed, setCollapsed] = useState(false); - useEffect(() => { - setCollapsed(isSmall); - }, [isSmall]); - return ( - - setCollapsed(!collapsed)} - > - Sidebar Content - - - ); -}; +import { SideBar } from "@/Components/v2/Layouts/Sidebar"; const RootLayout = () => { return ( diff --git a/client/src/Components/v2/Layouts/Sidebar/CollapseButton.tsx b/client/src/Components/v2/Layouts/Sidebar/CollapseButton.tsx new file mode 100644 index 000000000..564c41591 --- /dev/null +++ b/client/src/Components/v2/Layouts/Sidebar/CollapseButton.tsx @@ -0,0 +1,50 @@ +import IconButton from "@mui/material/IconButton"; +import { ArrowRight } from "@/Components/v2/Arrows/ArrowRight"; +import { ArrowLeft } from "@/Components/v2/Arrows/ArrowLeft"; +import { useTheme } from "@mui/material/styles"; +import { useDispatch } from "react-redux"; +import { toggleSidebar } from "../../../../Features/UI/uiSlice.js"; + +export const CollapseButton = ({ collapsed }: { collapsed: boolean }) => { + const theme = useTheme(); + const dispatch = useDispatch(); + const arrowIcon = collapsed ? ( + + ) : ( + + ); + + return ( + { + dispatch(toggleSidebar()); + }} + > + {arrowIcon} + + ); +}; diff --git a/client/src/Components/v2/Layouts/Sidebar/Logo.tsx b/client/src/Components/v2/Layouts/Sidebar/Logo.tsx new file mode 100644 index 000000000..6d190b99d --- /dev/null +++ b/client/src/Components/v2/Layouts/Sidebar/Logo.tsx @@ -0,0 +1,59 @@ +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"; + +export const Logo = ({ collapsed }: { collapsed: boolean }) => { + const { t } = useTranslation(); + const theme = useTheme(); + const navigate = useNavigate(); + + return ( + navigate("/")} + sx={{ cursor: "pointer" }} + > + + C + + + {" "} + + {t("common.appName")} + + + + ); +}; diff --git a/client/src/Components/v2/Layouts/Sidebar/index.tsx b/client/src/Components/v2/Layouts/Sidebar/index.tsx new file mode 100644 index 000000000..43975adfc --- /dev/null +++ b/client/src/Components/v2/Layouts/Sidebar/index.tsx @@ -0,0 +1,40 @@ +import { useEffect } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { setCollapsed } from "@/Features/UI/uiSlice"; +import useMediaQuery from "@mui/material/useMediaQuery"; +import { useTheme } from "@mui/material/styles"; + +import { CollapseButton } from "@/Components/v2/Layouts/Sidebar/CollapseButton"; +import Stack from "@mui/material/Stack"; +import { Logo } from "@/Components/v2/Layouts/Sidebar/Logo"; + +export const COLLAPSED_WIDTH = 64; +export const EXPANDED_WIDTH = 250; + +export const SideBar = () => { + const theme = useTheme(); + const isSmall = useMediaQuery(theme.breakpoints.down("md")); + const dispatch = useDispatch(); + const collapsed = useSelector((state: any) => state.ui.sidebar.collapsed); + + useEffect(() => { + dispatch(setCollapsed({ collapsed: isSmall })); + }, [isSmall]); + + return ( + + + + + ); +}; diff --git a/client/src/Features/UI/uiSlice.js b/client/src/Features/UI/uiSlice.js index 45ba267b0..63f7ae90e 100644 --- a/client/src/Features/UI/uiSlice.js +++ b/client/src/Features/UI/uiSlice.js @@ -47,6 +47,10 @@ const uiSlice = createSlice({ toggleSidebar: (state) => { state.sidebar.collapsed = !state.sidebar.collapsed; }, + setCollapsed: (state, action) => { + const { collapsed } = action.payload; + state.sidebar.collapsed = collapsed; + }, setMode: (state, action) => { state.mode = action.payload; }, @@ -73,6 +77,7 @@ export default uiSlice.reducer; export const { setRowsPerPage, toggleSidebar, + setCollapsed, setMode, setShowURL, setGreeting, diff --git a/client/src/Utils/Theme/v2/palette.ts b/client/src/Utils/Theme/v2/palette.ts index 2875f750d..2e41a159c 100644 --- a/client/src/Utils/Theme/v2/palette.ts +++ b/client/src/Utils/Theme/v2/palette.ts @@ -54,12 +54,17 @@ export const lightPalette = { main: colors.offWhite, contrastText: colors.blueGray800, contrastTextSecondary: colors.blueGray600, + lowContrast: colors.gray250, }, secondary: { main: colors.gray200, light: colors.lightBlueWave, contrastText: colors.blueGray600, }, + tertiary: { + main: colors.gray100, + contrastText: colors.blueGray800, + }, }; export const darkPalette = { @@ -73,10 +78,15 @@ export const darkPalette = { main: colors.offBlack, contrastText: colors.blueGray50, contrastTextSecondary: colors.gray200, + lowContrast: colors.blueGray600, }, secondary: { main: "#313131", light: colors.lightBlueWave, contrastText: colors.gray200, }, + tertiary: { + main: colors.blueGray800, + contrastText: colors.gray100, + }, }; diff --git a/client/src/Utils/Theme/v2/theme.ts b/client/src/Utils/Theme/v2/theme.ts index 55d2b9bb9..77bab3e70 100644 --- a/client/src/Utils/Theme/v2/theme.ts +++ b/client/src/Utils/Theme/v2/theme.ts @@ -67,6 +67,9 @@ export const theme = (mode: string, palette: any) => }, }, }, + shape: { + borderRadius: 2, + }, }); export const lightTheme = createTheme(theme("light", lightPalette)); diff --git a/client/src/types/env.d.ts b/client/src/types/env.d.ts index e2bce23dd..74a2a29e7 100644 --- a/client/src/types/env.d.ts +++ b/client/src/types/env.d.ts @@ -7,3 +7,11 @@ interface ImportMetaEnv { interface ImportMeta { readonly env: ImportMetaEnv; } + +declare module "*.svg?react" { + import * as React from "react"; + const ReactComponent: React.FunctionComponent< + React.SVGProps & { title?: string } + >; + export default ReactComponent; +} diff --git a/client/src/types/mui.d.ts b/client/src/types/mui.d.ts index 851ffea70..5f5c1c38b 100644 --- a/client/src/types/mui.d.ts +++ b/client/src/types/mui.d.ts @@ -3,13 +3,16 @@ import "@mui/material/Button"; declare module "@mui/material/styles" { interface Palette { accent: Palette["primary"]; + tertiary: Palette["primary"]; } interface PaletteOptions { accent?: PaletteOptions["primary"]; + tertiary?: PaletteOptions["primary"]; } interface PaletteColor { contrastTextSecondary?: string; + lowContrast?: string; } }