This commit is contained in:
Dhananjaya99
2026-07-18 23:42:58 +05:30
parent 80b130dffb
commit 92c4b14a6c
55 changed files with 8815 additions and 43 deletions
@@ -16,57 +16,77 @@ import {
PackageCheck,
Ruler,
Settings,
ShieldCheck,
ShoppingCart,
SlidersHorizontal,
SwatchBook,
Tag,
Truck,
Users,
Warehouse,
X,
type LucideIcon,
} from "lucide-react"
import { cn } from "@/lib/utils"
import { useAuth } from "@/components/auth/AuthProvider"
// `code` must match the seeded NavItem/SubNavItem codes in ERPCore
// (Infra/Persistence/Configurations/NavItemConfiguration.cs / SubNavItemConfiguration.cs)
// so role-based filtering (via GET /auth/me's navCodes) can match entries here.
const navItems: {
title: string
code: string
href: string
icon: LucideIcon
chevron?: boolean
children?: { title: string; href: string; icon: LucideIcon }[]
children?: { title: string; code: string; href: string; icon: LucideIcon }[]
}[] = [
{ title: "Dashboard", href: "/dashboard", icon: LayoutGrid },
{ title: "Dashboard", code: "dashboard", href: "/dashboard", icon: LayoutGrid },
{
title: "Products",
code: "products",
href: "/dashboard/products",
icon: Package,
chevron: true,
children: [
{ title: "Item", href: "/dashboard/products", icon: Boxes },
{ title: "Category", href: "/dashboard/products/categories", icon: ListTree },
{ title: "Brand", href: "/dashboard/products/brands", icon: Tag },
{ title: "Item Type", href: "/dashboard/products/item-types", icon: SwatchBook },
{ title: "UOM", href: "/dashboard/products/uoms", icon: Ruler },
{ title: "Configuration", href: "/dashboard/products/settings", icon: SlidersHorizontal },
{ title: "Item", code: "products.item", href: "/dashboard/products", icon: Boxes },
{ title: "Category", code: "products.category", href: "/dashboard/products/categories", icon: ListTree },
{ title: "Brand", code: "products.brand", href: "/dashboard/products/brands", icon: Tag },
{ title: "Item Type", code: "products.item-type", href: "/dashboard/products/item-types", icon: SwatchBook },
{ title: "UOM", code: "products.uom", href: "/dashboard/products/uoms", icon: Ruler },
{ title: "Configuration", code: "products.configuration", href: "/dashboard/products/settings", icon: SlidersHorizontal },
],
},
{ title: "Vendors", href: "/dashboard/vendors", icon: Truck, chevron: true },
{ title: "Procurement", href: "/dashboard/procurement", icon: ClipboardList, chevron: true },
{ title: "Receiving", href: "/dashboard/receiving/grn", icon: PackageCheck, chevron: true },
{ title: "Stock", href: "/dashboard/stock", icon: Warehouse, chevron: true },
{ title: "Warehouses", href: "/dashboard/warehouse", icon: Building2, chevron: true },
{ title: "Orders", href: "/dashboard/orders", icon: ShoppingCart, chevron: true },
{ title: "Settings", href: "/dashboard/settings", icon: Settings, chevron: true },
{ title: "Help", href: "/dashboard/help", icon: HelpCircle },
{ title: "Vendors", code: "vendors", href: "/dashboard/vendors", icon: Truck, chevron: true },
{ title: "Procurement", code: "procurement", href: "/dashboard/procurement", icon: ClipboardList, chevron: true },
{ title: "Receiving", code: "receiving", href: "/dashboard/receiving/grn", icon: PackageCheck, chevron: true },
{ title: "Stock", code: "stock", href: "/dashboard/stock", icon: Warehouse, chevron: true },
{ title: "Warehouses", code: "warehouses", href: "/dashboard/warehouse", icon: Building2, chevron: true },
{ title: "Orders", code: "orders", href: "/dashboard/orders", icon: ShoppingCart, chevron: true },
{
title: "Settings",
code: "settings",
href: "/dashboard/settings",
icon: Settings,
chevron: true,
children: [
{ title: "Roles", code: "settings.roles", href: "/dashboard/settings/roles", icon: ShieldCheck },
{ title: "Users", code: "settings.users", href: "/dashboard/settings/users", icon: Users },
],
},
{ title: "Help", code: "help", href: "/dashboard/help", icon: HelpCircle },
]
function SidebarContent({
items,
collapsed,
onCollapse,
onClose,
pathname,
isMobile,
}: {
items: typeof navItems
collapsed: boolean
onCollapse: () => void
onClose?: () => void
@@ -110,7 +130,7 @@ function SidebarContent({
{/* Nav items */}
<ul className="flex flex-col gap-1">
{navItems.map((item) => {
{items.map((item) => {
const isActive =
item.href === "/dashboard" ? pathname === item.href : pathname.startsWith(item.href)
@@ -195,6 +215,19 @@ export function AppSidebar() {
const pathname = usePathname() || "/"
const [collapsed, setCollapsed] = useState(false)
const [mobileOpen, setMobileOpen] = useState(false)
const { navCodes, loading } = useAuth()
// While /auth/me hasn't resolved yet, show nothing rather than briefly
// flashing the full menu to a restricted role. Once resolved, a nav item
// is visible if its own code is granted, or (for parents) if any child is.
const visibleItems = loading
? []
: navItems
.filter((item) => navCodes.includes(item.code) || item.children?.some((c) => navCodes.includes(c.code)))
.map((item) => ({
...item,
children: item.children?.filter((c) => navCodes.includes(c.code)),
}))
// Close mobile menu on route change
useEffect(() => {
@@ -213,6 +246,7 @@ export function AppSidebar() {
{/* ── Desktop sidebar ─────────────────────────────── */}
<div className="my-3 hidden lg:my-4 lg:flex">
<SidebarContent
items={visibleItems}
collapsed={collapsed}
onCollapse={() => setCollapsed((v) => !v)}
pathname={pathname}
@@ -247,6 +281,7 @@ export function AppSidebar() {
)}
>
<SidebarContent
items={visibleItems}
collapsed={false}
onCollapse={() => {}}
onClose={() => setMobileOpen(false)}