feat: add vendor optional fields and configuration hooks
- Implemented VendorOptionalFields component to handle optional vendor details (mobile, email, address) with validation. - Created useCustomerConfig hook to load optional customer field settings from Sales configuration. - Developed useProcurementConfig hook to manage Procurement configuration flags. - Introduced accessConfigApi for managing access configuration settings. - Added openingBalancesApi for handling opening balances operations. - Created procurementConfigApi for managing procurement configuration settings. - Implemented nav-routes for permission-based routing. - Developed PDF generation for purchase orders with dynamic vendor and item details. - Added purchase-order tax helpers for tax calculations and formatting. - Defined types for opening balances and related operations.
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
// Which permission opens which page: the route guard in components/auth/AccessGate.tsx uses this so a role
|
||||
// can't open a screen by typing its address (the sidebar only hides the link). Derived from the sidebar's own
|
||||
// list, so a page and its menu entry can never disagree; the API enforces the same codes on every request, so this
|
||||
// is a courtesy that replaces a wall of "Forbidden" toasts with one clear message — never the protection.
|
||||
import { navItems } from "@/components/Layouts/AppSidebar"
|
||||
|
||||
interface RouteRule {
|
||||
prefix: string
|
||||
/** Any one of these opens the page. */
|
||||
codes: string[]
|
||||
}
|
||||
|
||||
/** Pages with no menu entry of their own, but that belong to a permission anyway. */
|
||||
const EXTRA_RULES: RouteRule[] = [
|
||||
{ prefix: "/dashboard/settings/audit-logs", codes: ["settings.audit-logs"] },
|
||||
]
|
||||
|
||||
function buildRules(): RouteRule[] {
|
||||
const rules: RouteRule[] = [...EXTRA_RULES]
|
||||
for (const item of navItems) {
|
||||
// The landing page and Help are open to everyone with a role (they hold no data of their own).
|
||||
if (item.code !== "dashboard" && item.code !== "help") rules.push({ prefix: item.href, codes: [item.code] })
|
||||
for (const child of item.children ?? []) {
|
||||
rules.push({ prefix: child.href, codes: [child.code, ...(child.altCodes ?? [])] })
|
||||
}
|
||||
}
|
||||
// Longest prefix first, so "/dashboard/procurement/purchase-orders/approvals" beats ".../purchase-orders".
|
||||
return rules.sort((a, b) => b.prefix.length - a.prefix.length)
|
||||
}
|
||||
|
||||
let cached: RouteRule[] | null = null
|
||||
|
||||
/**
|
||||
* The permission codes that open `pathname` (any one suffices), or null when the page is unguarded
|
||||
* (the landing page, Help, and anything not in the menu: those are protected by the API alone).
|
||||
*/
|
||||
export function requiredCodesFor(pathname: string): string[] | null {
|
||||
cached ??= buildRules()
|
||||
const path = pathname.length > 1 ? pathname.replace(/\/+$/, "") : pathname
|
||||
const rule = cached.find((r) => path === r.prefix || path.startsWith(r.prefix + "/"))
|
||||
return rule ? rule.codes : null
|
||||
}
|
||||
Reference in New Issue
Block a user