mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 12:06:44 +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:
parent
047431d67f
commit
d569dbec49
@ -217,6 +217,12 @@ USER_SETTINGS: dict[str, InvenTreeSettingsKeyType] = {
|
|||||||
'validator': [int, MinValueValidator(0)],
|
'validator': [int, MinValueValidator(0)],
|
||||||
'default': 100,
|
'default': 100,
|
||||||
},
|
},
|
||||||
|
'ENABLE_LAST_BREADCRUMB': {
|
||||||
|
'name': _('Show Last Breadcrumb'),
|
||||||
|
'description': _('Show the current page in breadcrumbs'),
|
||||||
|
'default': False,
|
||||||
|
'validator': bool,
|
||||||
|
},
|
||||||
'NOTIFICATION_ERROR_REPORT': {
|
'NOTIFICATION_ERROR_REPORT': {
|
||||||
'name': _('Receive error reports'),
|
'name': _('Receive error reports'),
|
||||||
'description': _('Receive notifications for system errors'),
|
'description': _('Receive notifications for system errors'),
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { Group, Paper, SimpleGrid, Stack, Text } from '@mantine/core';
|
import { Group, Paper, SimpleGrid, Stack, Text } from '@mantine/core';
|
||||||
import { useHotkeys } from '@mantine/hooks';
|
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 { shortenString } from '../../functions/tables';
|
||||||
|
import { useUserSettingsState } from '../../states/SettingsState';
|
||||||
import { ApiImage } from '../images/ApiImage';
|
import { ApiImage } from '../images/ApiImage';
|
||||||
import { StylishText } from '../items/StylishText';
|
import { StylishText } from '../items/StylishText';
|
||||||
import { type Breadcrumb, BreadcrumbList } from './BreadcrumbList';
|
import { type Breadcrumb, BreadcrumbList } from './BreadcrumbList';
|
||||||
@ -16,6 +17,7 @@ interface PageDetailInterface {
|
|||||||
detail?: ReactNode;
|
detail?: ReactNode;
|
||||||
badges?: ReactNode[];
|
badges?: ReactNode[];
|
||||||
breadcrumbs?: Breadcrumb[];
|
breadcrumbs?: Breadcrumb[];
|
||||||
|
lastCrumb?: Breadcrumb[];
|
||||||
breadcrumbAction?: () => void;
|
breadcrumbAction?: () => void;
|
||||||
actions?: ReactNode[];
|
actions?: ReactNode[];
|
||||||
editAction?: () => void;
|
editAction?: () => void;
|
||||||
@ -36,11 +38,13 @@ export function PageDetail({
|
|||||||
badges,
|
badges,
|
||||||
imageUrl,
|
imageUrl,
|
||||||
breadcrumbs,
|
breadcrumbs,
|
||||||
|
lastCrumb: last_crumb,
|
||||||
breadcrumbAction,
|
breadcrumbAction,
|
||||||
actions,
|
actions,
|
||||||
editAction,
|
editAction,
|
||||||
editEnabled
|
editEnabled
|
||||||
}: Readonly<PageDetailInterface>) {
|
}: Readonly<PageDetailInterface>) {
|
||||||
|
const userSettings = useUserSettingsState();
|
||||||
useHotkeys([
|
useHotkeys([
|
||||||
[
|
[
|
||||||
'mod+E',
|
'mod+E',
|
||||||
@ -84,14 +88,23 @@ export function PageDetail({
|
|||||||
return cols;
|
return cols;
|
||||||
}, [detail, badges]);
|
}, [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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageTitle title={pageTitleString} />
|
<PageTitle title={pageTitleString} />
|
||||||
<Stack gap='xs'>
|
<Stack gap='xs'>
|
||||||
{breadcrumbs && breadcrumbs.length > 0 && (
|
{computedBreadcrumbs && computedBreadcrumbs.length > 0 && (
|
||||||
<BreadcrumbList
|
<BreadcrumbList
|
||||||
navCallback={breadcrumbAction}
|
navCallback={breadcrumbAction}
|
||||||
breadcrumbs={breadcrumbs}
|
breadcrumbs={computedBreadcrumbs}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Paper p='xs' radius='xs' shadow='xs'>
|
<Paper p='xs' radius='xs' shadow='xs'>
|
||||||
|
@ -55,7 +55,8 @@ export default function UserSettings() {
|
|||||||
'PART_SHOW_QUANTITY_IN_FORMS',
|
'PART_SHOW_QUANTITY_IN_FORMS',
|
||||||
'DISPLAY_SCHEDULE_TAB',
|
'DISPLAY_SCHEDULE_TAB',
|
||||||
'DISPLAY_STOCKTAKE_TAB',
|
'DISPLAY_STOCKTAKE_TAB',
|
||||||
'TABLE_STRING_MAX_LENGTH'
|
'TABLE_STRING_MAX_LENGTH',
|
||||||
|
'ENABLE_LAST_BREADCRUMB'
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -542,8 +542,8 @@ export default function BuildDetail() {
|
|||||||
editAction={editBuild.open}
|
editAction={editBuild.open}
|
||||||
editEnabled={user.hasChangePermission(ModelType.part)}
|
editEnabled={user.hasChangePermission(ModelType.part)}
|
||||||
imageUrl={build.part_detail?.image ?? build.part_detail?.thumbnail}
|
imageUrl={build.part_detail?.image ?? build.part_detail?.thumbnail}
|
||||||
breadcrumbs={[
|
breadcrumbs={[{ name: t`Manufacturing`, url: '/manufacturing' }]}
|
||||||
{ name: t`Manufacturing`, url: '/manufacturing' },
|
lastCrumb={[
|
||||||
{
|
{
|
||||||
name: build.reference,
|
name: build.reference,
|
||||||
url: getDetailUrl(ModelType.build, build.pk)
|
url: getDetailUrl(ModelType.build, build.pk)
|
||||||
|
@ -58,6 +58,7 @@ import { StockItemTable } from '../../tables/stock/StockItemTable';
|
|||||||
export type CompanyDetailProps = {
|
export type CompanyDetailProps = {
|
||||||
title: string;
|
title: string;
|
||||||
breadcrumbs: Breadcrumb[];
|
breadcrumbs: Breadcrumb[];
|
||||||
|
last_crumb_url: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -328,6 +329,12 @@ export default function CompanyDetail(props: Readonly<CompanyDetailProps>) {
|
|||||||
actions={companyActions}
|
actions={companyActions}
|
||||||
imageUrl={company.image}
|
imageUrl={company.image}
|
||||||
breadcrumbs={props.breadcrumbs}
|
breadcrumbs={props.breadcrumbs}
|
||||||
|
lastCrumb={[
|
||||||
|
{
|
||||||
|
name: company.name,
|
||||||
|
url: `${props.last_crumb_url}/${company.pk}/`
|
||||||
|
}
|
||||||
|
]}
|
||||||
badges={badges}
|
badges={badges}
|
||||||
editAction={editCompany.open}
|
editAction={editCompany.open}
|
||||||
editEnabled={user.hasChangePermission(ModelType.company)}
|
editEnabled={user.hasChangePermission(ModelType.company)}
|
||||||
|
@ -7,6 +7,7 @@ export default function CustomerDetail() {
|
|||||||
<CompanyDetail
|
<CompanyDetail
|
||||||
title={t`Customer`}
|
title={t`Customer`}
|
||||||
breadcrumbs={[{ name: t`Sales`, url: '/sales/' }]}
|
breadcrumbs={[{ name: t`Sales`, url: '/sales/' }]}
|
||||||
|
last_crumb_url='/sales/customer'
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ export default function ManufacturerDetail() {
|
|||||||
<CompanyDetail
|
<CompanyDetail
|
||||||
title={t`Manufacturer`}
|
title={t`Manufacturer`}
|
||||||
breadcrumbs={[{ name: t`Purchasing`, url: '/purchasing/' }]}
|
breadcrumbs={[{ name: t`Purchasing`, url: '/purchasing/' }]}
|
||||||
|
last_crumb_url='/purchasing/manufacturer'
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -279,6 +279,15 @@ export default function ManufacturerPartDetail() {
|
|||||||
title={t`ManufacturerPart`}
|
title={t`ManufacturerPart`}
|
||||||
subtitle={`${manufacturerPart.MPN} - ${manufacturerPart.part_detail?.name}`}
|
subtitle={`${manufacturerPart.MPN} - ${manufacturerPart.part_detail?.name}`}
|
||||||
breadcrumbs={breadcrumbs}
|
breadcrumbs={breadcrumbs}
|
||||||
|
lastCrumb={[
|
||||||
|
{
|
||||||
|
name: manufacturerPart.MPN,
|
||||||
|
url: getDetailUrl(
|
||||||
|
ModelType.manufacturerpart,
|
||||||
|
manufacturerPart.pk
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]}
|
||||||
actions={manufacturerPartActions}
|
actions={manufacturerPartActions}
|
||||||
imageUrl={manufacturerPart?.part_detail?.thumbnail}
|
imageUrl={manufacturerPart?.part_detail?.thumbnail}
|
||||||
editAction={editManufacturerPart.open}
|
editAction={editManufacturerPart.open}
|
||||||
|
@ -7,6 +7,7 @@ export default function SupplierDetail() {
|
|||||||
<CompanyDetail
|
<CompanyDetail
|
||||||
title={t`Supplier`}
|
title={t`Supplier`}
|
||||||
breadcrumbs={[{ name: t`Purchasing`, url: '/purchasing/' }]}
|
breadcrumbs={[{ name: t`Purchasing`, url: '/purchasing/' }]}
|
||||||
|
last_crumb_url='/purchasing/supplier'
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -407,6 +407,12 @@ export default function SupplierPartDetail() {
|
|||||||
title={t`Supplier Part`}
|
title={t`Supplier Part`}
|
||||||
subtitle={`${supplierPart.SKU} - ${supplierPart?.part_detail?.name}`}
|
subtitle={`${supplierPart.SKU} - ${supplierPart?.part_detail?.name}`}
|
||||||
breadcrumbs={breadcrumbs}
|
breadcrumbs={breadcrumbs}
|
||||||
|
lastCrumb={[
|
||||||
|
{
|
||||||
|
name: supplierPart.SKU,
|
||||||
|
url: `/purchasing/supplier-part/${supplierPart.pk}/`
|
||||||
|
}
|
||||||
|
]}
|
||||||
badges={badges}
|
badges={badges}
|
||||||
actions={supplierPartActions}
|
actions={supplierPartActions}
|
||||||
imageUrl={supplierPart?.part_detail?.thumbnail}
|
imageUrl={supplierPart?.part_detail?.thumbnail}
|
||||||
|
@ -1001,6 +1001,12 @@ export default function PartDetail() {
|
|||||||
? breadcrumbs
|
? breadcrumbs
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
lastCrumb={[
|
||||||
|
{
|
||||||
|
name: part.name,
|
||||||
|
url: `/part/${part.pk}/`
|
||||||
|
}
|
||||||
|
]}
|
||||||
breadcrumbAction={() => {
|
breadcrumbAction={() => {
|
||||||
setTreeOpen(true);
|
setTreeOpen(true);
|
||||||
}}
|
}}
|
||||||
|
@ -502,6 +502,12 @@ export default function PurchaseOrderDetail() {
|
|||||||
subtitle={order.description}
|
subtitle={order.description}
|
||||||
imageUrl={order.supplier_detail?.image}
|
imageUrl={order.supplier_detail?.image}
|
||||||
breadcrumbs={[{ name: t`Purchasing`, url: '/purchasing/' }]}
|
breadcrumbs={[{ name: t`Purchasing`, url: '/purchasing/' }]}
|
||||||
|
lastCrumb={[
|
||||||
|
{
|
||||||
|
name: order.reference,
|
||||||
|
url: `/purchasing/purchase-order/${order.pk}`
|
||||||
|
}
|
||||||
|
]}
|
||||||
actions={poActions}
|
actions={poActions}
|
||||||
badges={orderBadges}
|
badges={orderBadges}
|
||||||
editAction={editPurchaseOrder.open}
|
editAction={editPurchaseOrder.open}
|
||||||
|
@ -489,6 +489,9 @@ export default function ReturnOrderDetail() {
|
|||||||
badges={orderBadges}
|
badges={orderBadges}
|
||||||
actions={orderActions}
|
actions={orderActions}
|
||||||
breadcrumbs={[{ name: t`Sales`, url: '/sales/' }]}
|
breadcrumbs={[{ name: t`Sales`, url: '/sales/' }]}
|
||||||
|
lastCrumb={[
|
||||||
|
{ name: order.reference, url: `/sales/return-order/${order.pk}` }
|
||||||
|
]}
|
||||||
editAction={editReturnOrder.open}
|
editAction={editReturnOrder.open}
|
||||||
editEnabled={user.hasChangePermission(ModelType.returnorder)}
|
editEnabled={user.hasChangePermission(ModelType.returnorder)}
|
||||||
/>
|
/>
|
||||||
|
@ -553,6 +553,9 @@ export default function SalesOrderDetail() {
|
|||||||
badges={orderBadges}
|
badges={orderBadges}
|
||||||
actions={soActions}
|
actions={soActions}
|
||||||
breadcrumbs={[{ name: t`Sales`, url: '/sales/' }]}
|
breadcrumbs={[{ name: t`Sales`, url: '/sales/' }]}
|
||||||
|
lastCrumb={[
|
||||||
|
{ name: order.reference, url: `/sales/sales-order/${order.pk}` }
|
||||||
|
]}
|
||||||
editAction={editSalesOrder.open}
|
editAction={editSalesOrder.open}
|
||||||
editEnabled={user.hasChangePermission(ModelType.salesorder)}
|
editEnabled={user.hasChangePermission(ModelType.salesorder)}
|
||||||
/>
|
/>
|
||||||
|
@ -384,6 +384,12 @@ export default function Stock() {
|
|||||||
editAction={editLocation.open}
|
editAction={editLocation.open}
|
||||||
editEnabled={user.hasChangePermission(ModelType.stocklocation)}
|
editEnabled={user.hasChangePermission(ModelType.stocklocation)}
|
||||||
breadcrumbs={breadcrumbs}
|
breadcrumbs={breadcrumbs}
|
||||||
|
lastCrumb={[
|
||||||
|
{
|
||||||
|
name: location.name,
|
||||||
|
url: `/stock/location/${location.pk}/`
|
||||||
|
}
|
||||||
|
]}
|
||||||
breadcrumbAction={() => {
|
breadcrumbAction={() => {
|
||||||
setTreeOpen(true);
|
setTreeOpen(true);
|
||||||
}}
|
}}
|
||||||
|
@ -916,6 +916,12 @@ export default function StockDetail() {
|
|||||||
breadcrumbs={
|
breadcrumbs={
|
||||||
user.hasViewRole(UserRoles.stock_location) ? breadcrumbs : undefined
|
user.hasViewRole(UserRoles.stock_location) ? breadcrumbs : undefined
|
||||||
}
|
}
|
||||||
|
lastCrumb={[
|
||||||
|
{
|
||||||
|
name: stockitem.name,
|
||||||
|
url: `/stock/item/${stockitem.pk}/`
|
||||||
|
}
|
||||||
|
]}
|
||||||
breadcrumbAction={() => {
|
breadcrumbAction={() => {
|
||||||
setTreeOpen(true);
|
setTreeOpen(true);
|
||||||
}}
|
}}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user