Added more UI improvements - Hide/Show Sidebar

This commit is contained in:
Mitchell Magro 2025-10-27 14:20:43 +01:00
parent 7c716f5b27
commit fe6ed86a76
8 changed files with 154 additions and 29 deletions

View File

@ -1,12 +1,15 @@
"use client";
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { LayoutWrapper } from "../features/dashboard/layout/layoutWrapper";
import { MainContent } from "../features/dashboard/layout/mainContent";
import SideBar from "../features/dashboard/sidebar/Sidebar";
import Header from "../features/dashboard/header/Header";
import { useTokenExpiration } from "../hooks/useTokenExpiration";
import TokenExpirationInfo from "../components/TokenExpirationInfo";
import { toggleSidebar } from "../redux/ui/uiSlice";
import { RootState } from "../redux/types";
const DashboardLayout: React.FC<{ children: React.ReactNode }> = ({
children,
@ -14,9 +17,16 @@ const DashboardLayout: React.FC<{ children: React.ReactNode }> = ({
// Monitor token expiration and auto-logout
useTokenExpiration();
const dispatch = useDispatch();
const isSidebarOpen = useSelector((state: RootState) => state.ui.sidebarOpen);
const handleToggleSidebar = () => {
dispatch(toggleSidebar());
};
return (
<LayoutWrapper>
<SideBar />
<SideBar isOpen={isSidebarOpen} onClose={handleToggleSidebar} />
<div style={{ flexGrow: 1, display: "flex", flexDirection: "column" }}>
<MainContent>
<Header />

View File

@ -1,23 +1,10 @@
import React from "react";
import { AppBar, Toolbar, IconButton } from "@mui/material";
import MenuIcon from "@mui/icons-material/Menu";
import { AppBar, Toolbar } from "@mui/material";
import Dropdown from "./dropDown/DropDown";
import AccountMenu from "./accountMenu/AccountMenu";
import "./Header.scss";
const Header = () => {
// const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
// // Handle menu open
// const handleMenuClick = (event: React.MouseEvent<HTMLElement>) => {
// setAnchorEl(event.currentTarget);
// };
// // Handle menu close
// const handleMenuClose = () => {
// setAnchorEl(null);
// };
const handleChange = () => {};
return (
@ -30,9 +17,6 @@ const Header = () => {
>
<Toolbar className="header__toolbar">
<div className="header__left-group">
<IconButton edge="start" color="inherit" aria-label="menu">
<MenuIcon />
</IconButton>
<Dropdown onChange={handleChange} />
</div>

View File

@ -1,8 +0,0 @@
import { styled } from "@mui/system";
export const MainContent = styled("div")(({ theme }) => ({
marginLeft: "240px",
padding: theme.spacing(3),
minHeight: "100vh",
width: "calc(100% - 240px)",
}));

View File

@ -0,0 +1,21 @@
import React from "react";
import { useSelector } from "react-redux";
import { RootState } from "../../redux/types";
interface MainContentProps {
children: React.ReactNode;
}
export const MainContent = ({ children }: MainContentProps) => {
const isSidebarOpen = useSelector((state: RootState) => state.ui.sidebarOpen);
const style: React.CSSProperties = {
marginLeft: isSidebarOpen ? "240px" : "30px",
padding: "24px",
minHeight: "100vh",
width: isSidebarOpen ? "calc(100% - 240px)" : "calc(100% - 30px)",
transition: "margin-left 0.3s ease-in-out, width 0.3s ease-in-out",
};
return <div style={style}>{children}</div>;
};

View File

@ -7,8 +7,15 @@ import PageLinks from "../../../components/PageLinks/PageLinks";
import "./sideBar.scss";
import KeyboardArrowRightIcon from "@mui/icons-material/KeyboardArrowRight";
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
import ChevronLeftIcon from "@mui/icons-material/ChevronLeft";
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
const SideBar = () => {
interface SidebarProps {
isOpen?: boolean;
onClose?: () => void;
}
const SideBar = ({ isOpen = true, onClose }: SidebarProps) => {
const [openMenus, setOpenMenus] = useState<Record<string, boolean>>({});
const toggleMenu = (title: string) => {
@ -16,10 +23,13 @@ const SideBar = () => {
};
return (
<aside className="sidebar">
<aside className={`sidebar ${!isOpen ? "sidebar--collapsed" : ""}`}>
<button className="sidebar__toggle-button" onClick={onClose}>
{isOpen ? <ChevronLeftIcon /> : <ChevronRightIcon />}
</button>
<div className="sidebar__header">
<span>
Betrise cashir
Betrise cashier
<DashboardIcon fontSize="small" className="sidebar__icon-spacing" />
</span>
</div>

View File

@ -11,6 +11,55 @@
padding: 16px;
z-index: 1100;
border-right: 1px solid #333;
transition: transform 0.3s ease-in-out;
overflow: hidden;
&--collapsed {
transform: translateX(-210px); // Hide 90% (210px out of 240px)
.sidebar__header,
.sidebar__dropdown-button,
.sidebar__submenu,
a {
opacity: 0;
pointer-events: none;
}
}
&__toggle-button {
position: absolute;
top: 12px;
right: 0;
background: var(--background-primary);
border: 1px solid rgba(255, 255, 255, 0.2);
color: white;
cursor: pointer;
padding: 8px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50% 0 0 50%;
transition: all 0.2s ease;
z-index: 1101;
width: 36px;
height: 36px;
border-left: 1px solid #333;
&:hover {
background-color: rgba(255, 255, 255, 0.1);
transform: scale(1.05);
}
svg {
font-size: 20px;
}
}
&--collapsed &__toggle-button {
border-radius: 50%;
border: 1px solid rgba(255, 255, 255, 0.2);
border-left: 1px solid #333;
}
&__header {
font-size: 20px;
@ -71,4 +120,36 @@
}
}
}
// Mobile responsiveness
@media (max-width: 768px) {
z-index: 1101;
}
}
// Sidebar overlay for mobile
.sidebar-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1100;
opacity: 0;
visibility: hidden;
pointer-events: none;
transition:
opacity 0.3s ease-in-out,
visibility 0.3s ease-in-out;
&--visible {
opacity: 1;
visibility: visible;
pointer-events: all;
}
@media (min-width: 768px) {
display: none;
}
}

View File

@ -8,6 +8,7 @@ import { createTransform, persistReducer, persistStore } from "redux-persist";
import { createEpicMiddleware, combineEpics } from "redux-observable";
import advancedSearchReducer from "./advanedSearch/advancedSearchSlice";
import authReducer from "./auth/authSlice";
import uiReducer from "./ui/uiSlice";
import userEpics from "./user/epic";
import authEpics from "./auth/epic";
@ -36,6 +37,7 @@ const persistConfig = {
const rootReducer = combineReducers({
advancedSearch: advancedSearchReducer,
auth: authReducer,
ui: uiReducer,
});
const rootEpic = combineEpics(...userEpics, ...authEpics);

25
app/redux/ui/uiSlice.ts Normal file
View File

@ -0,0 +1,25 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
interface UIState {
sidebarOpen: boolean;
}
const initialState: UIState = {
sidebarOpen: true,
};
const uiSlice = createSlice({
name: "ui",
initialState,
reducers: {
toggleSidebar: state => {
state.sidebarOpen = !state.sidebarOpen;
},
setSidebarOpen: (state, action: PayloadAction<boolean>) => {
state.sidebarOpen = action.payload;
},
},
});
export const { toggleSidebar, setSidebarOpen } = uiSlice.actions;
export default uiSlice.reducer;