Files
stackpulse/frontend/src/widgets/layout/sidenav.jsx
T
2025-11-12 10:48:25 +00:00

124 lines
4.2 KiB
React

import PropTypes from "prop-types";
import { Link, NavLink } from "react-router-dom";
import { XMarkIcon } from "@heroicons/react/24/outline";
import { Avatar, Button, IconButton, Typography } from "@material-tailwind/react";
import { useMaterialTailwindController, setOpenSidenav } from "@/components";
import { useAuth } from "@/components/AuthProvider.jsx";
import logo from "@/assets/images/stackpulse.png";
export function Sidenav({ brandImg, brandName, routes }) {
const [controller, dispatch] = useMaterialTailwindController();
const { sidenavColor, sidenavType, openSidenav } = controller;
const { initialized: authInitialized, hasPermission } = useAuth();
const sidenavTypes = {
dark: "bg-gradient-to-br from-gray-800 to-gray-900",
white: "bg-white shadow-sm",
transparent: "bg-transparent",
};
return (
<aside
className={`${sidenavTypes[sidenavType]} ${openSidenav ? "translate-x-0" : "-translate-x-80"
} fixed inset-0 z-50 my-4 ml-4 h-[calc(100vh-32px)] w-72 rounded-xl transition-transform duration-300 xl:translate-x-0 border border-blue-gray-100`}
>
<div className="relative py-6 px-8 text-right">
<img src={logo} alt="StackPulse" className="w-auto" /><span className="mt-1 text-xs text-stormGrey-500 block antialiased font-sans">v0.5</span>
<IconButton
variant="text"
size="sm"
ripple={false}
className="absolute right-0 top-0 grid rounded-br-none rounded-tl-none"
onClick={() => setOpenSidenav(dispatch, false)}
>
<XMarkIcon strokeWidth={2.5} className="h-5 w-5 text-black" />
</IconButton>
</div>
<div className="m-4">
{routes.map(({ layout, title, pages }, key) => {
const visiblePages = pages.filter(({ permission, hidden }) => {
if (hidden) {
return false;
}
if (!permission || !permission.key) {
return true;
}
if (!authInitialized) {
return false;
}
const requiredLevel = permission.requiredLevel || permission.level || "full";
return hasPermission(permission.key, requiredLevel);
});
if (layout !== "dashboard" && layout !== "auth") {
return null;
}
if (visiblePages.length === 0 && !title) {
return null;
}
return (
<ul key={key} className="mb-4 flex flex-col gap-1">
{title && (
<li className="mx-3.5 mt-4 mb-2">
<Typography
variant="small"
color={sidenavType === "dark" ? "white" : "blue-gray"}
className="font-black uppercase opacity-75"
>
{title}
</Typography>
</li>
)}
{visiblePages.map(({ icon, name, path }) => (
<li key={name}>
<NavLink to={`/${layout}${path}`}>
{({ isActive }) => (
<Button
variant={isActive ? "gradient" : "text"}
color={
isActive
? sidenavColor
: sidenavType === "dark"
? "white"
: "blue-gray"
}
className="flex items-center gap-4 px-4 capitalize"
fullWidth
>
{icon}
<Typography
color="inherit"
className="font-medium capitalize"
>
{name}
</Typography>
</Button>
)}
</NavLink>
</li>
))}
</ul>
);
})}
</div>
</aside>
);
}
Sidenav.defaultProps = {
brandImg: "/img/logo-ct.png",
brandName: "Material Tailwind React",
};
Sidenav.propTypes = {
brandImg: PropTypes.string,
brandName: PropTypes.string,
routes: PropTypes.arrayOf(PropTypes.object).isRequired,
};
Sidenav.displayName = "/src/widgets/layout/sidnave.jsx";
export default Sidenav;