2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-28 03:26:45 +00:00

[PUI] Make breadcrumbs adjustable (#8027)

* adjust breadcrumbs to include current item

* Add last breacdrumb to various pages

* Add user settings for last breadcrumb

* add breacrumbs to company subpages

* use getDetailUrl instead

* set default

* change description

* fix styles

* fix merge

* rename to camelCase

---------

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
This commit is contained in:
Matthias Mair 2025-02-11 01:36:59 +01:00 committed by GitHub
parent 047431d67f
commit d569dbec49
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 81 additions and 6 deletions

View File

@ -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'),

View File

@ -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<PageDetailInterface>) {
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 (
<>
<PageTitle title={pageTitleString} />
<Stack gap='xs'>
{breadcrumbs && breadcrumbs.length > 0 && (
{computedBreadcrumbs && computedBreadcrumbs.length > 0 && (
<BreadcrumbList
navCallback={breadcrumbAction}
breadcrumbs={breadcrumbs}
breadcrumbs={computedBreadcrumbs}
/>
)}
<Paper p='xs' radius='xs' shadow='xs'>

View File

@ -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'
]}
/>
)

View File

@ -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)

View File

@ -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<CompanyDetailProps>) {
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)}

View File

@ -7,6 +7,7 @@ export default function CustomerDetail() {
<CompanyDetail
title={t`Customer`}
breadcrumbs={[{ name: t`Sales`, url: '/sales/' }]}
last_crumb_url='/sales/customer'
/>
);
}

View File

@ -7,6 +7,7 @@ export default function ManufacturerDetail() {
<CompanyDetail
title={t`Manufacturer`}
breadcrumbs={[{ name: t`Purchasing`, url: '/purchasing/' }]}
last_crumb_url='/purchasing/manufacturer'
/>
);
}

View File

@ -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}

View File

@ -7,6 +7,7 @@ export default function SupplierDetail() {
<CompanyDetail
title={t`Supplier`}
breadcrumbs={[{ name: t`Purchasing`, url: '/purchasing/' }]}
last_crumb_url='/purchasing/supplier'
/>
);
}

View File

@ -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}

View File

@ -1001,6 +1001,12 @@ export default function PartDetail() {
? breadcrumbs
: undefined
}
lastCrumb={[
{
name: part.name,
url: `/part/${part.pk}/`
}
]}
breadcrumbAction={() => {
setTreeOpen(true);
}}

View File

@ -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}

View File

@ -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)}
/>

View File

@ -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)}
/>

View File

@ -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);
}}

View File

@ -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);
}}