mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-07 06:00:57 +00:00
Enhance "path" columns (#9918)
* Enhance "path" columns - Show "short" version - Hovercard for full "pathstring" * Remove old code * adjust unit tests
This commit is contained in:
@ -21,7 +21,7 @@ import { formatCurrency, formatDate } from '../defaults/formatters';
|
|||||||
import { resolveItem } from '../functions/conversion';
|
import { resolveItem } from '../functions/conversion';
|
||||||
import { useGlobalSettingsState } from '../states/SettingsStates';
|
import { useGlobalSettingsState } from '../states/SettingsStates';
|
||||||
import type { TableColumn, TableColumnProps } from './Column';
|
import type { TableColumn, TableColumnProps } from './Column';
|
||||||
import { ProjectCodeHoverCard } from './TableHoverCard';
|
import { ProjectCodeHoverCard, TableHoverCard } from './TableHoverCard';
|
||||||
|
|
||||||
// Render a Part instance within a table
|
// Render a Part instance within a table
|
||||||
export function PartColumn({
|
export function PartColumn({
|
||||||
@ -81,28 +81,57 @@ export function CompanyColumn({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LocationColumn(props: TableColumnProps): TableColumn {
|
/**
|
||||||
|
* Return a column which displays a tree path for a given record.
|
||||||
|
*/
|
||||||
|
export function PathColumn(props: TableColumnProps): TableColumn {
|
||||||
return {
|
return {
|
||||||
|
...props,
|
||||||
|
accessor: props.accessor ?? 'path',
|
||||||
|
render: (record: any) => {
|
||||||
|
const instance = resolveItem(record, props.accessor ?? '');
|
||||||
|
|
||||||
|
if (!instance || !instance.name) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = instance.name ?? '';
|
||||||
|
const pathstring = instance.pathstring || name;
|
||||||
|
|
||||||
|
if (name == pathstring) {
|
||||||
|
return <Text>{name}</Text>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableHoverCard
|
||||||
|
value={<Text>{instance.name}</Text>}
|
||||||
|
icon='sitemap'
|
||||||
|
title={props.title}
|
||||||
|
extra={[<Text>{instance.pathstring}</Text>]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LocationColumn(props: TableColumnProps): TableColumn {
|
||||||
|
return PathColumn({
|
||||||
accessor: 'location',
|
accessor: 'location',
|
||||||
title: t`Location`,
|
title: t`Location`,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
ordering: 'location',
|
ordering: 'location',
|
||||||
render: (record: any) => {
|
|
||||||
const location = resolveItem(record, props.accessor ?? '');
|
|
||||||
|
|
||||||
if (!location) {
|
|
||||||
return (
|
|
||||||
<Text
|
|
||||||
size='sm'
|
|
||||||
style={{ fontStyle: 'italic' }}
|
|
||||||
>{t`No location set`}</Text>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Text size='sm'>{location.pathstring}</Text>;
|
|
||||||
},
|
|
||||||
...props
|
...props
|
||||||
};
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CategoryColumn(props: TableColumnProps): TableColumn {
|
||||||
|
return PathColumn({
|
||||||
|
accessor: 'category',
|
||||||
|
title: t`Category`,
|
||||||
|
sortable: true,
|
||||||
|
ordering: 'category',
|
||||||
|
...props
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BooleanColumn(props: TableColumn): TableColumn {
|
export function BooleanColumn(props: TableColumn): TableColumn {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { Divider, Group, HoverCard, Stack, Text } from '@mantine/core';
|
import { Divider, Group, HoverCard, Stack, Text } from '@mantine/core';
|
||||||
import { IconInfoCircle } from '@tabler/icons-react';
|
|
||||||
import { type ReactNode, useMemo } from 'react';
|
import { type ReactNode, useMemo } from 'react';
|
||||||
|
|
||||||
import type { InvenTreeIconType } from '@lib/types/Icons';
|
import type { InvenTreeIconType } from '@lib/types/Icons';
|
||||||
@ -61,7 +60,10 @@ export function TableHoverCard({
|
|||||||
<HoverCard.Dropdown>
|
<HoverCard.Dropdown>
|
||||||
<Stack gap='xs'>
|
<Stack gap='xs'>
|
||||||
<Group gap='xs' justify='left'>
|
<Group gap='xs' justify='left'>
|
||||||
<IconInfoCircle size='16' color='blue' />
|
<InvenTreeIcon
|
||||||
|
icon={icon ?? 'info'}
|
||||||
|
iconProps={{ size: 16, color: iconColor ?? 'blue' }}
|
||||||
|
/>
|
||||||
<Text fw='bold'>{title}</Text>
|
<Text fw='bold'>{title}</Text>
|
||||||
</Group>
|
</Group>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
@ -22,7 +22,12 @@ import {
|
|||||||
import { useTable } from '../../hooks/UseTable';
|
import { useTable } from '../../hooks/UseTable';
|
||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
import type { TableColumn } from '../Column';
|
import type { TableColumn } from '../Column';
|
||||||
import { DescriptionColumn, LinkColumn, PartColumn } from '../ColumnRenderers';
|
import {
|
||||||
|
CategoryColumn,
|
||||||
|
DescriptionColumn,
|
||||||
|
LinkColumn,
|
||||||
|
PartColumn
|
||||||
|
} from '../ColumnRenderers';
|
||||||
import { InvenTreeTable, type InvenTreeTableProps } from '../InvenTreeTable';
|
import { InvenTreeTable, type InvenTreeTableProps } from '../InvenTreeTable';
|
||||||
import { type RowAction, RowEditAction } from '../RowActions';
|
import { type RowAction, RowEditAction } from '../RowActions';
|
||||||
import { TableHoverCard } from '../TableHoverCard';
|
import { TableHoverCard } from '../TableHoverCard';
|
||||||
@ -53,11 +58,9 @@ function partTableColumns(): TableColumn[] {
|
|||||||
sortable: true
|
sortable: true
|
||||||
},
|
},
|
||||||
DescriptionColumn({}),
|
DescriptionColumn({}),
|
||||||
{
|
CategoryColumn({
|
||||||
accessor: 'category',
|
accessor: 'category_detail'
|
||||||
sortable: true,
|
}),
|
||||||
render: (record: any) => record.category_detail?.pathstring
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
accessor: 'default_location',
|
accessor: 'default_location',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
|
@ -32,8 +32,8 @@ import type { TableColumn } from '../Column';
|
|||||||
import {
|
import {
|
||||||
DateColumn,
|
DateColumn,
|
||||||
DescriptionColumn,
|
DescriptionColumn,
|
||||||
LocationColumn,
|
|
||||||
PartColumn,
|
PartColumn,
|
||||||
|
PathColumn,
|
||||||
StatusColumn
|
StatusColumn
|
||||||
} from '../ColumnRenderers';
|
} from '../ColumnRenderers';
|
||||||
import { StatusFilterOptions } from '../Filter';
|
import { StatusFilterOptions } from '../Filter';
|
||||||
@ -222,9 +222,10 @@ function stockItemTableColumns({
|
|||||||
accessor: 'batch',
|
accessor: 'batch',
|
||||||
sortable: true
|
sortable: true
|
||||||
},
|
},
|
||||||
LocationColumn({
|
PathColumn({
|
||||||
hidden: !showLocation,
|
accessor: 'location_detail',
|
||||||
accessor: 'location_detail'
|
title: t`Location`,
|
||||||
|
hidden: !showLocation
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
accessor: 'purchase_order',
|
accessor: 'purchase_order',
|
||||||
|
@ -80,7 +80,7 @@ test('Build Order - Basic Tests', async ({ browser }) => {
|
|||||||
await page.getByText('Quantity: 25').waitFor();
|
await page.getByText('Quantity: 25').waitFor();
|
||||||
await page.getByText('Continuity Checks').waitFor();
|
await page.getByText('Continuity Checks').waitFor();
|
||||||
await page
|
await page
|
||||||
.getByRole('row', { name: 'Quantity: 16 No location set' })
|
.getByRole('row', { name: 'Quantity: 16' })
|
||||||
.getByRole('button')
|
.getByRole('button')
|
||||||
.hover();
|
.hover();
|
||||||
await page.getByText('Add Test Result').waitFor();
|
await page.getByText('Add Test Result').waitFor();
|
||||||
@ -241,9 +241,7 @@ test('Build Order - Allocation', async ({ browser }) => {
|
|||||||
// Expand this row
|
// Expand this row
|
||||||
await cell.click();
|
await cell.click();
|
||||||
await page.getByRole('cell', { name: '2022-4-27', exact: true }).waitFor();
|
await page.getByRole('cell', { name: '2022-4-27', exact: true }).waitFor();
|
||||||
await page
|
await page.getByRole('cell', { name: 'Reel Storage', exact: true }).waitFor();
|
||||||
.getByRole('cell', { name: 'Electronics Lab/Reel Storage', exact: true })
|
|
||||||
.waitFor();
|
|
||||||
|
|
||||||
// Navigate to the "Incomplete Outputs" tab
|
// Navigate to the "Incomplete Outputs" tab
|
||||||
await loadTab(page, 'Incomplete Outputs');
|
await loadTab(page, 'Incomplete Outputs');
|
||||||
|
@ -225,9 +225,7 @@ test('Parts - Allocations', async ({ browser }) => {
|
|||||||
// Expand allocations against BO0001
|
// Expand allocations against BO0001
|
||||||
await build_order_cell.click();
|
await build_order_cell.click();
|
||||||
await page.getByRole('cell', { name: '# 3', exact: true }).waitFor();
|
await page.getByRole('cell', { name: '# 3', exact: true }).waitFor();
|
||||||
await page
|
await page.getByRole('cell', { name: 'Room 101', exact: true }).waitFor();
|
||||||
.getByRole('cell', { name: 'Factory/Office Block/Room 101', exact: true })
|
|
||||||
.waitFor();
|
|
||||||
await build_order_cell.click();
|
await build_order_cell.click();
|
||||||
|
|
||||||
// Check row options for BO0001
|
// Check row options for BO0001
|
||||||
|
Reference in New Issue
Block a user