v0.1.27
Fix: Throughput API v1 available - Details posted to Irving. POWERBI_KEY was missing from the .ENV file, so was not live. Add: Editor now supports editing a mix's resolved formula directly, with % and kg dual entry on ingredient rows Fix: Mix Editor should bring through correct ingredients. New resolved formula (same logic we use in Mix Calculator). Fix: Security headers on all API responses (hardening) Add: New mix button available on the Mix Editor. Add: New ingredient button available on the Ingredient Editor
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
ShieldCheck,
|
||||
ShoppingCart,
|
||||
SlidersHorizontal,
|
||||
Settings,
|
||||
Tags,
|
||||
TrendingUp,
|
||||
Users
|
||||
@@ -90,6 +91,12 @@ export type Crumb = {
|
||||
href?: string;
|
||||
};
|
||||
|
||||
export type PageMeta = {
|
||||
title: string;
|
||||
category: string;
|
||||
icon: ComponentType;
|
||||
};
|
||||
|
||||
export const dashboardItem: NavItem = {
|
||||
href: '/',
|
||||
label: 'Dashboard',
|
||||
@@ -285,9 +292,10 @@ export const baseSearchItems: SearchItem[] = [
|
||||
* 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.
|
||||
* Workflow-family layout: Dashboard, then an "Operations" group (the calculator,
|
||||
* costing, editor, master tools, and throughput), then Ordering and Insights
|
||||
* modules. Costing tools live inside Operations for the time being until they
|
||||
* grow into a family of their own.
|
||||
*/
|
||||
export function buildClientNavEntries(visible: {
|
||||
dashboard?: NavItem | null;
|
||||
@@ -302,10 +310,14 @@ export function buildClientNavEntries(visible: {
|
||||
entries.push({ kind: 'item', item: visible.dashboard });
|
||||
}
|
||||
|
||||
if (visible.costing.length) {
|
||||
const operationsChildren = [
|
||||
...visible.costing,
|
||||
...(visible.throughput ? [visible.throughput] : [])
|
||||
];
|
||||
if (operationsChildren.length) {
|
||||
entries.push({
|
||||
kind: 'group',
|
||||
group: { id: 'costing', label: 'Costing', icon: Layers, children: visible.costing }
|
||||
group: { id: 'operations', label: 'Operations', icon: Layers, children: operationsChildren }
|
||||
});
|
||||
}
|
||||
|
||||
@@ -313,10 +325,6 @@ export function buildClientNavEntries(visible: {
|
||||
entries.push(visible.ordering);
|
||||
}
|
||||
|
||||
if (visible.throughput) {
|
||||
entries.push({ kind: 'item', item: visible.throughput });
|
||||
}
|
||||
|
||||
if (visible.reporting) {
|
||||
entries.push({ kind: 'item', item: visible.reporting });
|
||||
}
|
||||
@@ -358,14 +366,88 @@ export function findOrderingSection(pathname: string): NavItem | null {
|
||||
}
|
||||
|
||||
export function pageTitle(pathname: string) {
|
||||
return pageMeta(pathname).title;
|
||||
}
|
||||
|
||||
export function pageCategory(pathname: string) {
|
||||
return pageMeta(pathname).category;
|
||||
}
|
||||
|
||||
export function pageMeta(pathname: string): PageMeta {
|
||||
if (pathname === '/') {
|
||||
return { title: 'Dashboard', category: 'Overview', icon: dashboardItem.icon };
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/ordering/manage')) {
|
||||
const section = findOrderingSection(pathname);
|
||||
return section && section.href !== '/ordering/manage'
|
||||
? `Order Management · ${section.label}`
|
||||
: 'Order Management';
|
||||
return {
|
||||
title: section?.label ?? 'Orders',
|
||||
category: 'Order Management',
|
||||
icon: section?.icon ?? orderingManageGroup.icon
|
||||
};
|
||||
}
|
||||
if (pathname.startsWith('/ordering')) return 'Ordering';
|
||||
return clientNavigationItems.find((item) => matchesRoute(item.href, pathname))?.label ?? 'Dashboard';
|
||||
|
||||
if (pathname.startsWith('/ordering')) {
|
||||
return { title: 'Ordering', category: 'Ordering', icon: orderingItem.icon };
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/throughput/add')) {
|
||||
return { title: 'Add Entry', category: 'Operations', icon: throughputItem.icon };
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/throughput')) {
|
||||
return { title: throughputItem.label, category: 'Operations', icon: throughputItem.icon };
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/reporting')) {
|
||||
return { title: reportingItem.label, category: 'Insights', icon: reportingItem.icon };
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/mix-calculator')) {
|
||||
return { title: mixCalculatorItem.label, category: 'Operations', icon: mixCalculatorItem.icon };
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/product-costing')) {
|
||||
return { title: productCostingItem.label, category: 'Operations', icon: productCostingItem.icon };
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/editor')) {
|
||||
return { title: editorItem.label, category: 'Operations', icon: editorItem.icon };
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/ingredients')) {
|
||||
return { title: ingredientsEditorItem.label, category: 'Operations', icon: ingredientsEditorItem.icon };
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/raw-materials')) {
|
||||
return { title: 'Raw Materials', category: 'Operations', icon: FlaskConical };
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/products')) {
|
||||
return { title: 'Products', category: 'Operations', icon: Package };
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/mixes/new')) {
|
||||
return { title: 'New Mix', category: 'Operations', icon: ClipboardPenLine };
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/mixes')) {
|
||||
return { title: 'Mix Master', category: 'Operations', icon: Layers };
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/scenarios')) {
|
||||
return { title: 'Scenarios', category: 'Operations', icon: Layers };
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/client-access')) {
|
||||
return { title: accessControlItem.label, category: 'Administration', icon: accessControlItem.icon };
|
||||
}
|
||||
|
||||
if (pathname.startsWith('/settings')) {
|
||||
return { title: 'Settings', category: 'Workspace', icon: Settings };
|
||||
}
|
||||
|
||||
return { title: 'Workspace', category: 'Workspace', icon: LayoutDashboard };
|
||||
}
|
||||
|
||||
export function clientBreadcrumbs(pathname: string, session?: AppSession | null): Crumb[] {
|
||||
|
||||
Reference in New Issue
Block a user