4a9ebe07b8
- Implemented InventorySalesTab for recording and viewing sales. - Created InventorySuppliersTab for managing suppliers. - Developed InventoryView to consolidate inventory-related tabs. - Added API functions for handling products, suppliers, sales, and GRNs. - Defined necessary TypeScript models for inventory entities. - Updated navigation to include an Inventory section.
100 lines
2.4 KiB
TypeScript
100 lines
2.4 KiB
TypeScript
"use client"
|
|
|
|
import * as React from "react"
|
|
import type { LucideIcon } from "lucide-react"
|
|
import {
|
|
Activity,
|
|
BarChart3,
|
|
CalendarCheck,
|
|
CalendarClock,
|
|
CreditCard,
|
|
LayoutDashboard,
|
|
Package,
|
|
Receipt,
|
|
Rss,
|
|
Settings,
|
|
TicketIcon,
|
|
Users,
|
|
UsersRound,
|
|
Utensils,
|
|
} from "lucide-react"
|
|
import { getNav } from "@/lib/api/nav"
|
|
import type { DashboardNavItem } from "@/lib/nav"
|
|
|
|
const ICON_MAP: Record<string, LucideIcon> = {
|
|
Activity,
|
|
BarChart3,
|
|
CalendarCheck,
|
|
CalendarClock,
|
|
CreditCard,
|
|
LayoutDashboard,
|
|
Package,
|
|
Receipt,
|
|
Rss,
|
|
Settings,
|
|
TicketIcon,
|
|
Users,
|
|
UsersRound,
|
|
Utensils,
|
|
}
|
|
|
|
interface NavState {
|
|
isOwner: boolean
|
|
items: DashboardNavItem[]
|
|
}
|
|
|
|
// Module-level cache: the nav call happens once per dashboard session — app-sidebar.tsx
|
|
// and mobile-nav.tsx both mount together and both need this, so a plain cache avoids
|
|
// wrapping DashboardShell in a second context provider just for this.
|
|
let cache: NavState | null = null
|
|
|
|
/** Clears the per-session nav cache when a user signs in or out. */
|
|
function resetNavItemsCache() {
|
|
cache = null
|
|
}
|
|
|
|
/**
|
|
* Live, permission-filtered sidebar nav for the current caller.
|
|
*
|
|
* Sandbox components pass `false` because they use the static demo navigation
|
|
* and must never make an authenticated permission request.
|
|
*/
|
|
function useNavItems(enabled = true) {
|
|
const [state, setState] = React.useState<NavState>(cache ?? { isOwner: false, items: [] })
|
|
const [loading, setLoading] = React.useState(enabled && !cache)
|
|
|
|
React.useEffect(() => {
|
|
if (!enabled || cache) return
|
|
let cancelled = false
|
|
|
|
getNav()
|
|
.then((res) => {
|
|
if (cancelled) return
|
|
const items = res.items.map((i) => ({
|
|
key: i.key,
|
|
label: i.label,
|
|
shortLabel: i.shortLabel,
|
|
href: i.href,
|
|
icon: ICON_MAP[i.icon] ?? LayoutDashboard,
|
|
}))
|
|
cache = { isOwner: res.isOwner, items }
|
|
setState(cache)
|
|
})
|
|
.catch(() => {
|
|
// Leave state empty — the sidebar/mobile-nav will just show nothing until a retry
|
|
// (e.g. navigating away and back re-mounts the hook, since cache stays null).
|
|
})
|
|
.finally(() => {
|
|
if (!cancelled) setLoading(false)
|
|
})
|
|
|
|
return () => {
|
|
cancelled = true
|
|
}
|
|
}, [enabled])
|
|
|
|
return { ...state, loading: enabled && loading }
|
|
}
|
|
|
|
export { resetNavItemsCache, useNavItems }
|