2026-05-08 23:07:01 +12:00
|
|
|
import {
|
|
|
|
|
Boxes,
|
|
|
|
|
Calculator,
|
|
|
|
|
ClipboardList,
|
|
|
|
|
DollarSign,
|
|
|
|
|
FlaskConical,
|
2026-05-31 20:19:44 +12:00
|
|
|
Gauge,
|
2026-05-08 23:07:01 +12:00
|
|
|
LayoutDashboard,
|
|
|
|
|
ShieldCheck,
|
|
|
|
|
TrendingUp,
|
|
|
|
|
Wheat,
|
|
|
|
|
Workflow
|
|
|
|
|
} from 'lucide-svelte';
|
|
|
|
|
import type { ComponentType } from 'svelte';
|
|
|
|
|
|
|
|
|
|
import { featureFlags } from '$lib/features';
|
2026-05-31 20:19:44 +12:00
|
|
|
import {
|
|
|
|
|
canOpenDashboard
|
|
|
|
|
} from '$lib/workspace-access';
|
|
|
|
|
import type { AppSession } from '$lib/session';
|
2026-05-08 23:07:01 +12:00
|
|
|
|
|
|
|
|
export type SearchItem = {
|
|
|
|
|
href: string;
|
|
|
|
|
label: string;
|
|
|
|
|
description: string;
|
|
|
|
|
keywords: string;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type NavItem = {
|
|
|
|
|
href: string;
|
|
|
|
|
label: string;
|
|
|
|
|
shortLabel: string;
|
|
|
|
|
icon: ComponentType;
|
|
|
|
|
moduleKey?: string;
|
2026-05-31 20:19:44 +12:00
|
|
|
badge?: string;
|
2026-05-08 23:07:01 +12:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type FooterLink = {
|
|
|
|
|
href: string;
|
|
|
|
|
label: string;
|
|
|
|
|
shortLabel: string;
|
|
|
|
|
icon: ComponentType;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type Crumb = {
|
|
|
|
|
label: string;
|
|
|
|
|
href?: string;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const dashboardItem: NavItem = {
|
|
|
|
|
href: '/',
|
|
|
|
|
label: 'Dashboard',
|
|
|
|
|
shortLabel: 'DB',
|
|
|
|
|
icon: LayoutDashboard,
|
|
|
|
|
moduleKey: 'dashboard'
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const mixCalculatorItem: NavItem = {
|
2026-05-31 20:19:44 +12:00
|
|
|
href: '/mix-calculator',
|
2026-05-08 23:07:01 +12:00
|
|
|
label: 'Mix Calculator',
|
|
|
|
|
shortLabel: 'MC',
|
|
|
|
|
icon: Calculator,
|
|
|
|
|
moduleKey: 'mix_calculator'
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const reportingItem: NavItem = {
|
|
|
|
|
href: '/reporting',
|
|
|
|
|
label: 'Reporting',
|
|
|
|
|
shortLabel: 'RP',
|
|
|
|
|
icon: TrendingUp,
|
|
|
|
|
moduleKey: 'products'
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-31 20:19:44 +12:00
|
|
|
export const throughputItem: NavItem = {
|
|
|
|
|
href: '/throughput',
|
|
|
|
|
label: 'Throughput',
|
|
|
|
|
shortLabel: 'OT',
|
|
|
|
|
icon: Gauge,
|
|
|
|
|
moduleKey: 'operations_throughput',
|
|
|
|
|
badge: 'test'
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-08 23:07:01 +12:00
|
|
|
export const workingDocumentItems: NavItem[] = [
|
|
|
|
|
{ href: '/raw-materials', label: 'Raw Materials', shortLabel: 'RM', icon: Wheat, moduleKey: 'raw_materials' },
|
|
|
|
|
{ href: '/mixes', label: 'Mix Master', shortLabel: 'MM', icon: FlaskConical, moduleKey: 'mix_master' },
|
|
|
|
|
{ href: '/products', label: 'Products', shortLabel: 'PR', icon: Boxes, moduleKey: 'products' },
|
|
|
|
|
{ href: '/scenarios', label: 'Scenarios', shortLabel: 'SC', icon: Workflow, moduleKey: 'scenarios' }
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
export const accessControlItem: NavItem = {
|
|
|
|
|
href: '/client-access',
|
|
|
|
|
label: 'Client Access',
|
|
|
|
|
shortLabel: 'AC',
|
|
|
|
|
icon: ShieldCheck,
|
|
|
|
|
moduleKey: 'client_access'
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const clientNavigationItems: NavItem[] = [
|
|
|
|
|
dashboardItem,
|
|
|
|
|
mixCalculatorItem,
|
2026-05-31 20:19:44 +12:00
|
|
|
throughputItem,
|
2026-05-08 23:07:01 +12:00
|
|
|
...workingDocumentItems,
|
|
|
|
|
accessControlItem
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
export const footerLinks: FooterLink[] = [
|
|
|
|
|
{ href: '/products', label: 'Delivered Pricing', shortLabel: 'DP', icon: DollarSign },
|
|
|
|
|
{ href: '/scenarios', label: 'Planning View', shortLabel: 'PV', icon: ClipboardList }
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
export const baseSearchItems: SearchItem[] = [
|
|
|
|
|
{
|
|
|
|
|
href: '/',
|
|
|
|
|
label: 'Open Dashboard',
|
|
|
|
|
description: 'Jump to the Hunter Premium Produce workspace summary.',
|
|
|
|
|
keywords: 'hunter premium produce overview dashboard workspace home'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
href: '/raw-materials',
|
|
|
|
|
label: 'Open Raw Materials',
|
|
|
|
|
description: 'Review live input costs that feed the pricing model.',
|
|
|
|
|
keywords: 'raw materials pricing inputs costs supplier'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
href: '/mixes',
|
|
|
|
|
label: 'Open Mix Master',
|
|
|
|
|
description: 'Browse saved mixes and their costing outputs.',
|
|
|
|
|
keywords: 'mix master mixes recipes spreadsheet'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
href: '/mixes/new',
|
|
|
|
|
label: 'Create New Mix',
|
|
|
|
|
description: 'Start a new costing worksheet for Hunter Premium Produce.',
|
|
|
|
|
keywords: 'new mix create worksheet hunter premium produce formula'
|
|
|
|
|
},
|
|
|
|
|
...(featureFlags.mixCalculatorSessionHistory
|
|
|
|
|
? [
|
|
|
|
|
{
|
|
|
|
|
href: '/mix-calculator',
|
|
|
|
|
label: 'Open Mix Calculator',
|
|
|
|
|
description: 'Review saved production sessions and batch calculations.',
|
|
|
|
|
keywords: 'mix calculator production sessions batch bags client product'
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
: []),
|
|
|
|
|
{
|
2026-05-31 20:19:44 +12:00
|
|
|
href: '/mix-calculator',
|
|
|
|
|
label: 'Open Mix Calculator',
|
2026-05-08 23:07:01 +12:00
|
|
|
description: 'Run a new client-specific mix calculation session.',
|
|
|
|
|
keywords: 'new mix calculator session client batch size product bags print'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
href: '/products',
|
|
|
|
|
label: 'Open Products',
|
|
|
|
|
description: 'Review delivered product pricing and margins.',
|
|
|
|
|
keywords: 'products pricing margins delivered outputs'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
href: '/reporting',
|
|
|
|
|
label: 'Open Reporting',
|
|
|
|
|
description: 'View raw material costs, mix summaries, product pricing, and data quality reports.',
|
|
|
|
|
keywords: 'reporting reports raw materials mix cost product pricing data quality price review'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
href: '/settings',
|
|
|
|
|
label: 'Open Workspace Settings',
|
|
|
|
|
description: 'Review account details and workspace preferences.',
|
|
|
|
|
keywords: 'settings account preferences profile workspace'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
href: '/scenarios',
|
|
|
|
|
label: 'Open Scenarios',
|
|
|
|
|
description: 'Inspect planning scenarios and overrides.',
|
|
|
|
|
keywords: 'scenarios sandbox overrides compare planning'
|
|
|
|
|
}
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
export function matchesRoute(href: string, pathname: string) {
|
|
|
|
|
return href === '/' ? pathname === '/' : pathname.startsWith(href);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function pageTitle(pathname: string) {
|
|
|
|
|
return clientNavigationItems.find((item) => matchesRoute(item.href, pathname))?.label ?? 'Dashboard';
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-31 20:19:44 +12:00
|
|
|
export function clientBreadcrumbs(pathname: string, session?: AppSession | null): Crumb[] {
|
|
|
|
|
const crumbs: Crumb[] = [];
|
|
|
|
|
|
|
|
|
|
if (canOpenDashboard(session)) {
|
|
|
|
|
crumbs.push({ label: 'Workspace', href: '/' });
|
|
|
|
|
}
|
2026-05-08 23:07:01 +12:00
|
|
|
|
|
|
|
|
if (pathname === '/') {
|
2026-05-31 20:19:44 +12:00
|
|
|
return crumbs.length ? [...crumbs, { label: 'Dashboard' }] : [{ label: 'Dashboard' }];
|
2026-05-08 23:07:01 +12:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pathname.startsWith('/mix-calculator')) {
|
2026-05-31 20:19:44 +12:00
|
|
|
return [...crumbs, { label: 'Mix Calculator' }];
|
2026-05-08 23:07:01 +12:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pathname.startsWith('/mixes')) {
|
2026-05-31 20:19:44 +12:00
|
|
|
return [...crumbs, { label: 'Mix Master' }];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pathname.startsWith('/throughput')) {
|
|
|
|
|
const base: Crumb[] = [...crumbs, { label: 'Throughput', href: '/throughput' }];
|
|
|
|
|
if (pathname === '/throughput/add') return [...base, { label: 'Add Entry' }];
|
|
|
|
|
if (pathname === '/throughput') return base.slice(0, -1).concat([{ label: 'Throughput' }]);
|
|
|
|
|
return base;
|
2026-05-08 23:07:01 +12:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sectionMap: Record<string, string> = {
|
|
|
|
|
'/raw-materials': 'Raw Materials',
|
|
|
|
|
'/products': 'Products',
|
|
|
|
|
'/scenarios': 'Scenarios',
|
|
|
|
|
'/client-access': 'Client Access',
|
|
|
|
|
'/reporting': 'Reporting',
|
2026-05-31 20:19:44 +12:00
|
|
|
'/settings': 'Settings',
|
|
|
|
|
'/throughput': 'Throughput'
|
2026-05-08 23:07:01 +12:00
|
|
|
};
|
|
|
|
|
const section = sectionMap[pathname];
|
2026-05-31 20:19:44 +12:00
|
|
|
if (section) return [...crumbs, { label: section }];
|
2026-05-08 23:07:01 +12:00
|
|
|
|
2026-05-31 20:19:44 +12:00
|
|
|
return [...crumbs, { label: pageTitle(pathname) }];
|
2026-05-08 23:07:01 +12:00
|
|
|
}
|