feat: add brands and variant categories management
- Implemented CRUD operations for brands and variant categories in the API. - Created UI components for managing brands and variant categories, including listing, creating, editing, and deleting. - Enhanced the sidebar navigation to include links for brands and variant categories. - Updated the categories API to support pagination and filtering. - Added validation for brand and variant category names. - Integrated toast notifications for user feedback on actions.
This commit is contained in:
@@ -4,16 +4,20 @@ import { useEffect, useState } from "react"
|
||||
import Link from "next/link"
|
||||
import { usePathname } from "next/navigation"
|
||||
import {
|
||||
Boxes,
|
||||
Building2,
|
||||
ChevronRight,
|
||||
ClipboardList,
|
||||
HelpCircle,
|
||||
LayoutGrid,
|
||||
ListTree,
|
||||
Menu,
|
||||
Package,
|
||||
PackageCheck,
|
||||
Settings,
|
||||
ShoppingCart,
|
||||
SwatchBook,
|
||||
Tag,
|
||||
Truck,
|
||||
Warehouse,
|
||||
X,
|
||||
@@ -22,9 +26,26 @@ import {
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const navItems: { title: string; href: string; icon: LucideIcon; chevron?: boolean }[] = [
|
||||
const navItems: {
|
||||
title: string
|
||||
href: string
|
||||
icon: LucideIcon
|
||||
chevron?: boolean
|
||||
children?: { title: string; href: string; icon: LucideIcon }[]
|
||||
}[] = [
|
||||
{ title: "Dashboard", href: "/dashboard", icon: LayoutGrid },
|
||||
{ title: "Products", href: "/dashboard/products", icon: Package, chevron: true },
|
||||
{
|
||||
title: "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: "Variant", href: "/dashboard/products/variants", icon: SwatchBook },
|
||||
],
|
||||
},
|
||||
{ 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 },
|
||||
@@ -109,12 +130,47 @@ function SidebarContent({
|
||||
{(!collapsed || isMobile) && (
|
||||
<>
|
||||
<span className="flex-1">{item.title}</span>
|
||||
{item.chevron && !isActive && (
|
||||
{item.chevron && !item.children && !isActive && (
|
||||
<ChevronRight className="size-4 shrink-0 text-slate-300" />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Link>
|
||||
|
||||
{item.children && (!collapsed || isMobile) && (
|
||||
<ul className="mt-1 flex flex-col gap-0.5 pl-11">
|
||||
{(() => {
|
||||
// Longest-matching href wins so a shared prefix (e.g. "Item" and
|
||||
// "Category" both live under /dashboard/products) doesn't light up
|
||||
// more than one sub-item at once.
|
||||
const activeChild = [...item.children]
|
||||
.filter((c) => pathname === c.href || pathname.startsWith(`${c.href}/`))
|
||||
.sort((a, b) => b.href.length - a.href.length)[0]
|
||||
return item.children.map((child) => {
|
||||
const childActive = child.href === activeChild?.href
|
||||
return (
|
||||
<li key={child.href}>
|
||||
<Link
|
||||
href={child.href}
|
||||
onClick={onClose}
|
||||
className={cn(
|
||||
"flex items-center gap-2.5 rounded-xl px-3 py-2 text-sm font-medium transition-colors",
|
||||
childActive
|
||||
? "bg-indigo-50 text-indigo-600"
|
||||
: "text-slate-500 hover:bg-slate-50 hover:text-slate-700"
|
||||
)}
|
||||
>
|
||||
<child.icon
|
||||
className={cn("size-4 shrink-0", childActive ? "text-indigo-600" : "text-slate-400")}
|
||||
/>
|
||||
{child.title}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
})()}
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user