Files
inventory/inventory/src/components/layout/AppSidebar.tsx
2025-11-27 15:57:22 -05:00

301 lines
7.9 KiB
TypeScript

import {
Home,
Package,
BarChart2,
Settings,
ClipboardList,
Tags,
PackagePlus,
ShoppingBag,
Truck,
MessageCircle,
LayoutDashboard,
Percent,
FileSearch,
ShoppingCart,
} from "lucide-react";
import { IconCrystalBall } from "@tabler/icons-react";
import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarFooter,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarSeparator,
useSidebar
} from "@/components/ui/sidebar";
import { useLocation, Link } from "react-router-dom";
import { Protected } from "@/components/auth/Protected";
import { useContext } from "react";
import { AuthContext } from "@/contexts/AuthContext";
import { NavUser } from "./NavUser";
const dashboardItems = [
{
title: "Dashboard",
icon: LayoutDashboard,
url: "/dashboard",
permission: "access:dashboard"
},
{
title: "Black Friday",
icon: ShoppingCart,
url: "/dashboard/black-friday",
permission: "access:black_friday_dashboard"
}
];
const inventoryItems = [
{
title: "Overview",
icon: Home,
url: "/overview",
permission: "access:overview"
},
{
title: "Products",
icon: Package,
url: "/products",
permission: "access:products"
},
{
title: "Categories",
icon: Tags,
url: "/categories",
permission: "access:categories"
},
{
title: "Brands",
icon: ShoppingBag,
url: "/brands",
permission: "access:brands"
},
{
title: "Vendors",
icon: Truck,
url: "/vendors",
permission: "access:vendors"
},
{
title: "Purchase Orders",
icon: ClipboardList,
url: "/purchase-orders",
permission: "access:purchase_orders"
},
{
title: "Analytics",
icon: BarChart2,
url: "/analytics",
permission: "access:analytics"
}
];
const toolsItems = [
{
title: "Discount Simulator",
icon: Percent,
url: "/discount-simulator",
permission: "access:discount_simulator"
},
{
title: "HTS Lookup",
icon: FileSearch,
url: "/hts-lookup",
permission: "access:hts_lookup"
},
{
title: "Forecasting",
icon: IconCrystalBall,
url: "/forecasting",
permission: "access:forecasting"
}
];
const productSetupItems = [
{
title: "Create Products",
icon: PackagePlus,
url: "/import",
permission: "access:import"
}
];
const chatItems = [
{
title: "Chat Archive",
icon: MessageCircle,
url: "/chat",
permission: "access:chat"
}
];
export function AppSidebar() {
const location = useLocation();
useSidebar();
const { user } = useContext(AuthContext);
// Check if user has access to any items in a section
const hasAccessToSection = (items: any[]): boolean => {
if (user?.is_admin) return true;
return items.some(item => user?.permissions?.includes(item.permission));
};
const renderMenuItems = (items: any[]) => {
return items.map((item) => {
// Check if current path matches the item URL
// For root path ("/"), only highlight Overview when at exactly "/"
const isActive =
location.pathname === item.url ||
(item.url !== "/" && item.url !== "#" && location.pathname.startsWith(item.url));
return (
<Protected
key={item.title}
permission={item.permission}
fallback={null}
>
<SidebarMenuItem>
<SidebarMenuButton
asChild={item.url !== "#"}
tooltip={item.title}
isActive={isActive}
disabled={item.url === "#"}
>
{item.url === "#" ? (
<div>
<item.icon className="h-4 w-4" />
<span className="group-data-[collapsible=icon]:hidden">
{item.title}
</span>
</div>
) : (
<Link to={item.url}>
<item.icon className="h-4 w-4" />
<span className="group-data-[collapsible=icon]:hidden">
{item.title}
</span>
</Link>
)}
</SidebarMenuButton>
</SidebarMenuItem>
</Protected>
);
});
};
return (
<Sidebar collapsible="icon" variant="sidebar">
<SidebarHeader>
<div className="py-1 flex justify-center items-center">
<div className="flex items-center">
<div className="flex-shrink-0 w-8 h-8 relative flex items-center justify-center">
<img
src="/cherrybottom.png"
alt="Cherry Bottom"
className="w-6 h-6 object-contain -rotate-12 transform hover:rotate-0 transition-transform ease-in-out duration-300"
/>
</div>
<div className="ml-1 transition-all duration-200 whitespace-nowrap group-[.group[data-state=collapsed]]:hidden">
<span className="font-bold text-lg">A Cherry On Bottom</span>
</div>
</div>
</div>
</SidebarHeader>
<SidebarSeparator />
<SidebarContent>
{/* Dashboard Section */}
{hasAccessToSection(dashboardItems) && (
<SidebarGroup>
<SidebarGroupLabel>Dashboard</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{renderMenuItems(dashboardItems)}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
)}
{/* Inventory Section */}
{hasAccessToSection(inventoryItems) && (
<SidebarGroup>
<SidebarGroupLabel>Inventory</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{renderMenuItems(inventoryItems)}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
)}
{/* Tools Section */}
{hasAccessToSection(toolsItems) && (
<SidebarGroup>
<SidebarGroupLabel>Tools</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{renderMenuItems(toolsItems)}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
)}
{/* Product Setup Section */}
{hasAccessToSection(productSetupItems) && (
<SidebarGroup>
<SidebarGroupLabel>Product Setup</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{renderMenuItems(productSetupItems)}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
)}
{/* Chat Section */}
{hasAccessToSection(chatItems) && (
<SidebarGroup>
<SidebarGroupLabel>Chat</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{renderMenuItems(chatItems)}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
)}
{/* Settings Section */}
<Protected permission="access:settings" fallback={null}>
<SidebarGroup>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton
asChild
tooltip="Settings"
isActive={location.pathname === "/settings"}
>
<Link to="/settings">
<Settings className="h-4 w-4" />
<span className="group-data-[collapsible=icon]:hidden">
Settings
</span>
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</Protected>
</SidebarContent>
<SidebarSeparator />
<SidebarFooter>
<NavUser />
</SidebarFooter>
</Sidebar>
);
}