diff --git a/src/backend/InvenTree/common/setting/user.py b/src/backend/InvenTree/common/setting/user.py index a39ae48f95..6581cacc22 100644 --- a/src/backend/InvenTree/common/setting/user.py +++ b/src/backend/InvenTree/common/setting/user.py @@ -217,6 +217,12 @@ USER_SETTINGS: dict[str, InvenTreeSettingsKeyType] = { 'validator': [int, MinValueValidator(0)], 'default': 100, }, + 'ENABLE_LAST_BREADCRUMB': { + 'name': _('Show Last Breadcrumb'), + 'description': _('Show the current page in breadcrumbs'), + 'default': False, + 'validator': bool, + }, 'NOTIFICATION_ERROR_REPORT': { 'name': _('Receive error reports'), 'description': _('Receive notifications for system errors'), diff --git a/src/frontend/src/components/nav/PageDetail.tsx b/src/frontend/src/components/nav/PageDetail.tsx index 0382516068..174ecd701e 100644 --- a/src/frontend/src/components/nav/PageDetail.tsx +++ b/src/frontend/src/components/nav/PageDetail.tsx @@ -1,8 +1,9 @@ import { Group, Paper, SimpleGrid, Stack, Text } from '@mantine/core'; import { useHotkeys } from '@mantine/hooks'; -import { Fragment, type ReactNode, useMemo } from 'react'; +import { Fragment, type ReactNode, useMemo } from 'react'; import { shortenString } from '../../functions/tables'; +import { useUserSettingsState } from '../../states/SettingsState'; import { ApiImage } from '../images/ApiImage'; import { StylishText } from '../items/StylishText'; import { type Breadcrumb, BreadcrumbList } from './BreadcrumbList'; @@ -16,6 +17,7 @@ interface PageDetailInterface { detail?: ReactNode; badges?: ReactNode[]; breadcrumbs?: Breadcrumb[]; + lastCrumb?: Breadcrumb[]; breadcrumbAction?: () => void; actions?: ReactNode[]; editAction?: () => void; @@ -36,11 +38,13 @@ export function PageDetail({ badges, imageUrl, breadcrumbs, + lastCrumb: last_crumb, breadcrumbAction, actions, editAction, editEnabled }: Readonly) { + const userSettings = useUserSettingsState(); useHotkeys([ [ 'mod+E', @@ -84,14 +88,23 @@ export function PageDetail({ return cols; }, [detail, badges]); + // breadcrumb caching + const computedBreadcrumbs = useMemo(() => { + if (userSettings.isSet('ENABLE_LAST_BREADCRUMB', false)) { + return [...(breadcrumbs ?? []), ...(last_crumb ?? [])]; + } else { + return breadcrumbs; + } + }, [breadcrumbs, last_crumb, userSettings]); + return ( <> - {breadcrumbs && breadcrumbs.length > 0 && ( + {computedBreadcrumbs && computedBreadcrumbs.length > 0 && ( )} diff --git a/src/frontend/src/pages/Index/Settings/UserSettings.tsx b/src/frontend/src/pages/Index/Settings/UserSettings.tsx index 689a5dcdae..60a6a43805 100644 --- a/src/frontend/src/pages/Index/Settings/UserSettings.tsx +++ b/src/frontend/src/pages/Index/Settings/UserSettings.tsx @@ -55,7 +55,8 @@ export default function UserSettings() { 'PART_SHOW_QUANTITY_IN_FORMS', 'DISPLAY_SCHEDULE_TAB', 'DISPLAY_STOCKTAKE_TAB', - 'TABLE_STRING_MAX_LENGTH' + 'TABLE_STRING_MAX_LENGTH', + 'ENABLE_LAST_BREADCRUMB' ]} /> ) diff --git a/src/frontend/src/pages/build/BuildDetail.tsx b/src/frontend/src/pages/build/BuildDetail.tsx index 916be8f861..2f44d9c899 100644 --- a/src/frontend/src/pages/build/BuildDetail.tsx +++ b/src/frontend/src/pages/build/BuildDetail.tsx @@ -542,8 +542,8 @@ export default function BuildDetail() { editAction={editBuild.open} editEnabled={user.hasChangePermission(ModelType.part)} imageUrl={build.part_detail?.image ?? build.part_detail?.thumbnail} - breadcrumbs={[ - { name: t`Manufacturing`, url: '/manufacturing' }, + breadcrumbs={[{ name: t`Manufacturing`, url: '/manufacturing' }]} + lastCrumb={[ { name: build.reference, url: getDetailUrl(ModelType.build, build.pk) diff --git a/src/frontend/src/pages/company/CompanyDetail.tsx b/src/frontend/src/pages/company/CompanyDetail.tsx index 11e4c8dce6..8b7a63e983 100644 --- a/src/frontend/src/pages/company/CompanyDetail.tsx +++ b/src/frontend/src/pages/company/CompanyDetail.tsx @@ -58,6 +58,7 @@ import { StockItemTable } from '../../tables/stock/StockItemTable'; export type CompanyDetailProps = { title: string; breadcrumbs: Breadcrumb[]; + last_crumb_url: string; }; /** @@ -328,6 +329,12 @@ export default function CompanyDetail(props: Readonly) { actions={companyActions} imageUrl={company.image} breadcrumbs={props.breadcrumbs} + lastCrumb={[ + { + name: company.name, + url: `${props.last_crumb_url}/${company.pk}/` + } + ]} badges={badges} editAction={editCompany.open} editEnabled={user.hasChangePermission(ModelType.company)} diff --git a/src/frontend/src/pages/company/CustomerDetail.tsx b/src/frontend/src/pages/company/CustomerDetail.tsx index 2725180dbe..c63b6edf90 100644 --- a/src/frontend/src/pages/company/CustomerDetail.tsx +++ b/src/frontend/src/pages/company/CustomerDetail.tsx @@ -7,6 +7,7 @@ export default function CustomerDetail() { ); } diff --git a/src/frontend/src/pages/company/ManufacturerDetail.tsx b/src/frontend/src/pages/company/ManufacturerDetail.tsx index aa04b0405c..03a707bfb5 100644 --- a/src/frontend/src/pages/company/ManufacturerDetail.tsx +++ b/src/frontend/src/pages/company/ManufacturerDetail.tsx @@ -7,6 +7,7 @@ export default function ManufacturerDetail() { ); } diff --git a/src/frontend/src/pages/company/ManufacturerPartDetail.tsx b/src/frontend/src/pages/company/ManufacturerPartDetail.tsx index 6b8350eab9..96837b10a3 100644 --- a/src/frontend/src/pages/company/ManufacturerPartDetail.tsx +++ b/src/frontend/src/pages/company/ManufacturerPartDetail.tsx @@ -279,6 +279,15 @@ export default function ManufacturerPartDetail() { title={t`ManufacturerPart`} subtitle={`${manufacturerPart.MPN} - ${manufacturerPart.part_detail?.name}`} breadcrumbs={breadcrumbs} + lastCrumb={[ + { + name: manufacturerPart.MPN, + url: getDetailUrl( + ModelType.manufacturerpart, + manufacturerPart.pk + ) + } + ]} actions={manufacturerPartActions} imageUrl={manufacturerPart?.part_detail?.thumbnail} editAction={editManufacturerPart.open} diff --git a/src/frontend/src/pages/company/SupplierDetail.tsx b/src/frontend/src/pages/company/SupplierDetail.tsx index 5be35dda8e..7616f78840 100644 --- a/src/frontend/src/pages/company/SupplierDetail.tsx +++ b/src/frontend/src/pages/company/SupplierDetail.tsx @@ -7,6 +7,7 @@ export default function SupplierDetail() { ); } diff --git a/src/frontend/src/pages/company/SupplierPartDetail.tsx b/src/frontend/src/pages/company/SupplierPartDetail.tsx index 679673ddbe..da3849ab42 100644 --- a/src/frontend/src/pages/company/SupplierPartDetail.tsx +++ b/src/frontend/src/pages/company/SupplierPartDetail.tsx @@ -407,6 +407,12 @@ export default function SupplierPartDetail() { title={t`Supplier Part`} subtitle={`${supplierPart.SKU} - ${supplierPart?.part_detail?.name}`} breadcrumbs={breadcrumbs} + lastCrumb={[ + { + name: supplierPart.SKU, + url: `/purchasing/supplier-part/${supplierPart.pk}/` + } + ]} badges={badges} actions={supplierPartActions} imageUrl={supplierPart?.part_detail?.thumbnail} diff --git a/src/frontend/src/pages/part/PartDetail.tsx b/src/frontend/src/pages/part/PartDetail.tsx index 90953b271f..69f340fc86 100644 --- a/src/frontend/src/pages/part/PartDetail.tsx +++ b/src/frontend/src/pages/part/PartDetail.tsx @@ -1001,6 +1001,12 @@ export default function PartDetail() { ? breadcrumbs : undefined } + lastCrumb={[ + { + name: part.name, + url: `/part/${part.pk}/` + } + ]} breadcrumbAction={() => { setTreeOpen(true); }} diff --git a/src/frontend/src/pages/purchasing/PurchaseOrderDetail.tsx b/src/frontend/src/pages/purchasing/PurchaseOrderDetail.tsx index 2edc54374d..4818ac6a78 100644 --- a/src/frontend/src/pages/purchasing/PurchaseOrderDetail.tsx +++ b/src/frontend/src/pages/purchasing/PurchaseOrderDetail.tsx @@ -502,6 +502,12 @@ export default function PurchaseOrderDetail() { subtitle={order.description} imageUrl={order.supplier_detail?.image} breadcrumbs={[{ name: t`Purchasing`, url: '/purchasing/' }]} + lastCrumb={[ + { + name: order.reference, + url: `/purchasing/purchase-order/${order.pk}` + } + ]} actions={poActions} badges={orderBadges} editAction={editPurchaseOrder.open} diff --git a/src/frontend/src/pages/sales/ReturnOrderDetail.tsx b/src/frontend/src/pages/sales/ReturnOrderDetail.tsx index 4925ea1e9e..764bac95e5 100644 --- a/src/frontend/src/pages/sales/ReturnOrderDetail.tsx +++ b/src/frontend/src/pages/sales/ReturnOrderDetail.tsx @@ -489,6 +489,9 @@ export default function ReturnOrderDetail() { badges={orderBadges} actions={orderActions} breadcrumbs={[{ name: t`Sales`, url: '/sales/' }]} + lastCrumb={[ + { name: order.reference, url: `/sales/return-order/${order.pk}` } + ]} editAction={editReturnOrder.open} editEnabled={user.hasChangePermission(ModelType.returnorder)} /> diff --git a/src/frontend/src/pages/sales/SalesOrderDetail.tsx b/src/frontend/src/pages/sales/SalesOrderDetail.tsx index 4abc8f1e06..fb226a9a82 100644 --- a/src/frontend/src/pages/sales/SalesOrderDetail.tsx +++ b/src/frontend/src/pages/sales/SalesOrderDetail.tsx @@ -553,6 +553,9 @@ export default function SalesOrderDetail() { badges={orderBadges} actions={soActions} breadcrumbs={[{ name: t`Sales`, url: '/sales/' }]} + lastCrumb={[ + { name: order.reference, url: `/sales/sales-order/${order.pk}` } + ]} editAction={editSalesOrder.open} editEnabled={user.hasChangePermission(ModelType.salesorder)} /> diff --git a/src/frontend/src/pages/stock/LocationDetail.tsx b/src/frontend/src/pages/stock/LocationDetail.tsx index 75f1202177..d67e59da6d 100644 --- a/src/frontend/src/pages/stock/LocationDetail.tsx +++ b/src/frontend/src/pages/stock/LocationDetail.tsx @@ -384,6 +384,12 @@ export default function Stock() { editAction={editLocation.open} editEnabled={user.hasChangePermission(ModelType.stocklocation)} breadcrumbs={breadcrumbs} + lastCrumb={[ + { + name: location.name, + url: `/stock/location/${location.pk}/` + } + ]} breadcrumbAction={() => { setTreeOpen(true); }} diff --git a/src/frontend/src/pages/stock/StockDetail.tsx b/src/frontend/src/pages/stock/StockDetail.tsx index eab214acce..b6f026943b 100644 --- a/src/frontend/src/pages/stock/StockDetail.tsx +++ b/src/frontend/src/pages/stock/StockDetail.tsx @@ -916,6 +916,12 @@ export default function StockDetail() { breadcrumbs={ user.hasViewRole(UserRoles.stock_location) ? breadcrumbs : undefined } + lastCrumb={[ + { + name: stockitem.name, + url: `/stock/item/${stockitem.pk}/` + } + ]} breadcrumbAction={() => { setTreeOpen(true); }}