feat: add warehouse and bin management API with mock data implementation
- Implemented warehouses API with methods for listing, creating, and managing bins. - Added wastage API to handle stock write-offs and integrate with stock adjustments. - Created auth token management for storing and retrieving access tokens. - Developed error mapping for consistent user-facing error messages. - Introduced client-side validations for GRN, master data, and procurement processes. - Defined common types for pagination, problem details, and various master data entities. - Established procurement and stock management types to support frontend functionality.
This commit is contained in:
@@ -4,13 +4,18 @@ import { useEffect, useState } from "react"
|
||||
import Link from "next/link"
|
||||
import { usePathname } from "next/navigation"
|
||||
import {
|
||||
Building2,
|
||||
ChevronRight,
|
||||
ClipboardList,
|
||||
HelpCircle,
|
||||
LayoutGrid,
|
||||
Menu,
|
||||
Package,
|
||||
PackageCheck,
|
||||
Settings,
|
||||
ShoppingCart,
|
||||
Truck,
|
||||
Warehouse,
|
||||
X,
|
||||
type LucideIcon,
|
||||
} from "lucide-react"
|
||||
@@ -20,6 +25,11 @@ import { cn } from "@/lib/utils"
|
||||
const navItems: { title: string; href: string; icon: LucideIcon; chevron?: boolean }[] = [
|
||||
{ title: "Dashboard", href: "/dashboard", icon: LayoutGrid },
|
||||
{ title: "Products", href: "/dashboard/products", icon: Package, chevron: true },
|
||||
{ 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 },
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
"use client"
|
||||
|
||||
import { Fragment } from "react"
|
||||
import Link from "next/link"
|
||||
import { usePathname } from "next/navigation"
|
||||
|
||||
import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "@/components/ui/breadcrumb"
|
||||
|
||||
// Per-segment display labels. Deliberately separate from Header.tsx's
|
||||
// titleFromPath (which resolves one *full* path to a page title) — a
|
||||
// breadcrumb needs a label per *segment*, including grouping segments that
|
||||
// have no page of their own (e.g. "receiving", "grn").
|
||||
const SEGMENT_LABELS: Record<string, string> = {
|
||||
dashboard: "Dashboard",
|
||||
products: "Items",
|
||||
uoms: "UOMs",
|
||||
categories: "Categories",
|
||||
new: "New",
|
||||
edit: "Edit",
|
||||
vendors: "Vendors",
|
||||
warehouse: "Warehouses",
|
||||
receiving: "Receiving",
|
||||
grn: "Goods Receipt Notes",
|
||||
stock: "Stock",
|
||||
enquiry: "Enquiry",
|
||||
ledger: "Ledger",
|
||||
valuation: "Valuation",
|
||||
transfers: "Transfers",
|
||||
adjustments: "Adjustments",
|
||||
counts: "Counts",
|
||||
"reorder-alerts": "Reorder Alerts",
|
||||
wastage: "Wastage",
|
||||
procurement: "Procurement",
|
||||
requisitions: "Requisitions",
|
||||
rfqs: "RFQs",
|
||||
"purchase-orders": "Purchase Orders",
|
||||
"purchase-returns": "Purchase Returns",
|
||||
}
|
||||
|
||||
function labelFor(segment: string): string {
|
||||
if (SEGMENT_LABELS[segment]) return SEGMENT_LABELS[segment]
|
||||
if (/^[0-9]+$/.test(segment)) return `#${segment}`
|
||||
return segment.charAt(0).toUpperCase() + segment.slice(1)
|
||||
}
|
||||
|
||||
const linkClass =
|
||||
"truncate transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm"
|
||||
|
||||
export function Breadcrumbs() {
|
||||
const pathname = usePathname() || "/dashboard"
|
||||
const segments = pathname.split("/").filter(Boolean)
|
||||
|
||||
// Nothing to show above the dashboard root itself.
|
||||
if (segments.length <= 1) return null
|
||||
|
||||
const crumbs = segments.map((segment, i) => ({
|
||||
href: "/" + segments.slice(0, i + 1).join("/"),
|
||||
label: labelFor(segment),
|
||||
}))
|
||||
|
||||
return (
|
||||
<Breadcrumb className="mb-4">
|
||||
<BreadcrumbList>
|
||||
{crumbs.map((crumb, i) => {
|
||||
const isLast = i === crumbs.length - 1
|
||||
return (
|
||||
<Fragment key={crumb.href}>
|
||||
<BreadcrumbItem>
|
||||
{isLast ? (
|
||||
<BreadcrumbPage>{crumb.label}</BreadcrumbPage>
|
||||
) : (
|
||||
<Link href={crumb.href} className={linkClass}>
|
||||
{crumb.label}
|
||||
</Link>
|
||||
)}
|
||||
</BreadcrumbItem>
|
||||
{!isLast && <BreadcrumbSeparator />}
|
||||
</Fragment>
|
||||
)
|
||||
})}
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
)
|
||||
}
|
||||
@@ -17,7 +17,60 @@ import {
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
|
||||
|
||||
const PROCUREMENT_TITLES: Record<string, string> = {
|
||||
"/dashboard/procurement": "Procurement",
|
||||
"/dashboard/procurement/requisitions": "Requisitions",
|
||||
"/dashboard/procurement/requisitions/new": "New Requisition",
|
||||
"/dashboard/procurement/rfqs": "RFQs",
|
||||
"/dashboard/procurement/rfqs/new": "New RFQ",
|
||||
"/dashboard/procurement/purchase-orders": "Purchase Orders",
|
||||
"/dashboard/procurement/purchase-orders/new": "New Purchase Order",
|
||||
"/dashboard/procurement/purchase-returns": "Purchase Returns",
|
||||
"/dashboard/procurement/purchase-returns/new": "New Purchase Return",
|
||||
}
|
||||
|
||||
const STOCK_TITLES: Record<string, string> = {
|
||||
"/dashboard/stock": "Stock Management",
|
||||
"/dashboard/stock/enquiry": "Stock Enquiry",
|
||||
"/dashboard/stock/ledger": "Stock Ledger",
|
||||
"/dashboard/stock/valuation": "Valuation",
|
||||
"/dashboard/stock/transfers": "Stock Transfers",
|
||||
"/dashboard/stock/transfers/new": "New Transfer",
|
||||
"/dashboard/stock/adjustments": "Stock Adjustments",
|
||||
"/dashboard/stock/adjustments/new": "New Adjustment",
|
||||
"/dashboard/stock/counts": "Stock Counts",
|
||||
"/dashboard/stock/counts/new": "New Count",
|
||||
"/dashboard/stock/reorder-alerts": "Reorder Alerts",
|
||||
"/dashboard/stock/wastage": "Wastage",
|
||||
"/dashboard/stock/wastage/new": "Record Wastage",
|
||||
}
|
||||
|
||||
function titleFromPath(pathname: string) {
|
||||
if (pathname === "/dashboard/receiving/grn") return "Goods Receipt Notes"
|
||||
if (pathname === "/dashboard/receiving/grn/new") return "Create Goods Receipt Note"
|
||||
if (/^\/dashboard\/receiving\/grn\/[^/]+$/.test(pathname)) return "Goods Receipt Note"
|
||||
|
||||
if (STOCK_TITLES[pathname]) return STOCK_TITLES[pathname]
|
||||
if (/^\/dashboard\/stock\/transfers\/[^/]+$/.test(pathname)) return "Stock Transfer"
|
||||
if (/^\/dashboard\/stock\/counts\/[^/]+$/.test(pathname)) return "Stock Count"
|
||||
|
||||
if (PROCUREMENT_TITLES[pathname]) return PROCUREMENT_TITLES[pathname]
|
||||
if (/^\/dashboard\/procurement\/requisitions\/[^/]+$/.test(pathname)) return "Requisition"
|
||||
if (/^\/dashboard\/procurement\/rfqs\/[^/]+$/.test(pathname)) return "RFQ"
|
||||
if (/^\/dashboard\/procurement\/purchase-orders\/[^/]+$/.test(pathname)) return "Purchase Order"
|
||||
|
||||
if (pathname === "/dashboard/warehouse") return "Warehouses"
|
||||
if (/^\/dashboard\/warehouse\/[^/]+$/.test(pathname)) return "Warehouse"
|
||||
|
||||
if (pathname === "/dashboard/vendors") return "Vendors"
|
||||
if (/^\/dashboard\/vendors\/[^/]+$/.test(pathname)) return "View Vendor"
|
||||
|
||||
if (pathname === "/dashboard/products") return "Items"
|
||||
if (pathname === "/dashboard/products/new") return "New Item"
|
||||
if (pathname === "/dashboard/products/uoms") return "Units of Measure"
|
||||
if (pathname === "/dashboard/products/categories") return "Categories"
|
||||
if (/^\/dashboard\/products\/[^/]+$/.test(pathname)) return "Item"
|
||||
|
||||
const segment = pathname.split("/").filter(Boolean).pop() ?? "dashboard"
|
||||
return segment.charAt(0).toUpperCase() + segment.slice(1)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user