301 lines
7.9 KiB
TypeScript
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>
|
|
);
|
|
}
|