v0.1.12
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
BadgeDollarSign,
|
||||
Calculator,
|
||||
ClipboardPenLine,
|
||||
Gauge,
|
||||
Layers,
|
||||
LayoutDashboard,
|
||||
ShieldCheck,
|
||||
TrendingUp
|
||||
@@ -37,6 +39,23 @@ export type FooterLink = {
|
||||
icon: ComponentType;
|
||||
};
|
||||
|
||||
/**
|
||||
* A collapsible family of related modules in the primary rail. Groups keep the
|
||||
* top level short as more modules ship: a new costing tool becomes another child
|
||||
* here rather than another peer in a flat stack.
|
||||
*/
|
||||
export type NavGroup = {
|
||||
id: string;
|
||||
label: string;
|
||||
icon: ComponentType;
|
||||
children: NavItem[];
|
||||
};
|
||||
|
||||
/** The rail is a sequence of standalone items and collapsible groups. */
|
||||
export type NavEntry =
|
||||
| { kind: 'item'; item: NavItem }
|
||||
| { kind: 'group'; group: NavGroup };
|
||||
|
||||
export type Crumb = {
|
||||
label: string;
|
||||
href?: string;
|
||||
@@ -58,9 +77,18 @@ export const mixCalculatorItem: NavItem = {
|
||||
moduleKey: 'mix_calculator'
|
||||
};
|
||||
|
||||
export const productCostingItem: NavItem = {
|
||||
href: '/product-costing',
|
||||
label: 'Product Costing',
|
||||
shortLabel: 'PC',
|
||||
icon: BadgeDollarSign,
|
||||
moduleKey: 'products',
|
||||
badge: 'Alpha'
|
||||
};
|
||||
|
||||
export const editorItem: NavItem = {
|
||||
href: '/editor',
|
||||
label: 'Editor',
|
||||
label: 'Mix Editor',
|
||||
shortLabel: 'ED',
|
||||
icon: ClipboardPenLine,
|
||||
moduleKey: 'products',
|
||||
@@ -100,6 +128,7 @@ export const accessControlItem: NavItem = {
|
||||
export const clientNavigationItems: NavItem[] = [
|
||||
dashboardItem,
|
||||
mixCalculatorItem,
|
||||
productCostingItem,
|
||||
throughputItem,
|
||||
editorItem,
|
||||
accessControlItem
|
||||
@@ -108,9 +137,15 @@ export const clientNavigationItems: NavItem[] = [
|
||||
export const footerLinks: FooterLink[] = [];
|
||||
|
||||
export const baseSearchItems: SearchItem[] = [
|
||||
{
|
||||
href: '/product-costing',
|
||||
label: 'Open Product Costing',
|
||||
description: 'Maintain product costing records, assumptions, and calculated pricing.',
|
||||
keywords: 'alpha product costing pricing finished delivered distributor wholesale margin spreadsheet'
|
||||
},
|
||||
{
|
||||
href: '/editor',
|
||||
label: 'Open Editor',
|
||||
label: 'Open Mix Editor',
|
||||
description: 'Edit client, product, and mix naming from one table.',
|
||||
keywords: 'editor products mixes clients names bulk table phf horse manning'
|
||||
},
|
||||
@@ -162,6 +197,50 @@ export const baseSearchItems: SearchItem[] = [
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Assemble the grouped primary rail from already access-filtered items.
|
||||
* Callers pass only the modules the current session may see; empty families
|
||||
* collapse away so a role with one costing tool never gets an empty group.
|
||||
*
|
||||
* Workflow-family layout: Dashboard, then a "Costing" group (the calculator,
|
||||
* costing, editor, and master tools), then Operations and Insights modules at
|
||||
* the top level until each grows into a family of its own.
|
||||
*/
|
||||
export function buildClientNavEntries(visible: {
|
||||
dashboard?: NavItem | null;
|
||||
costing: NavItem[];
|
||||
throughput?: NavItem | null;
|
||||
reporting?: NavItem | null;
|
||||
}): NavEntry[] {
|
||||
const entries: NavEntry[] = [];
|
||||
|
||||
if (visible.dashboard) {
|
||||
entries.push({ kind: 'item', item: visible.dashboard });
|
||||
}
|
||||
|
||||
if (visible.costing.length) {
|
||||
entries.push({
|
||||
kind: 'group',
|
||||
group: { id: 'costing', label: 'Costing', icon: Layers, children: visible.costing }
|
||||
});
|
||||
}
|
||||
|
||||
if (visible.throughput) {
|
||||
entries.push({ kind: 'item', item: visible.throughput });
|
||||
}
|
||||
|
||||
if (visible.reporting) {
|
||||
entries.push({ kind: 'item', item: visible.reporting });
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
/** True when any of a group's children matches the current route. */
|
||||
export function groupHasActiveChild(group: NavGroup, pathname: string) {
|
||||
return group.children.some((child) => matchesRoute(child.href, pathname));
|
||||
}
|
||||
|
||||
export function matchesRoute(href: string, pathname: string) {
|
||||
return href === '/' ? pathname === '/' : pathname.startsWith(href);
|
||||
}
|
||||
@@ -185,8 +264,12 @@ export function clientBreadcrumbs(pathname: string, session?: AppSession | null)
|
||||
return [...crumbs, { label: 'Mix Calculator' }];
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/product-costing')) {
|
||||
return [...crumbs, { label: 'Product Costing' }];
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/editor')) {
|
||||
return [...crumbs, { label: 'Editor' }];
|
||||
return [...crumbs, { label: 'Mix Editor' }];
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/mixes')) {
|
||||
@@ -202,6 +285,7 @@ export function clientBreadcrumbs(pathname: string, session?: AppSession | null)
|
||||
|
||||
const sectionMap: Record<string, string> = {
|
||||
'/raw-materials': 'Raw Materials',
|
||||
'/product-costing': 'Product Costing',
|
||||
'/products': 'Products',
|
||||
'/scenarios': 'Scenarios',
|
||||
'/client-access': 'Client Access',
|
||||
|
||||
Reference in New Issue
Block a user