diff --git a/src/frontend/src/tables/ColumnRenderers.tsx b/src/frontend/src/tables/ColumnRenderers.tsx index 473dd59ba7..c2a157d315 100644 --- a/src/frontend/src/tables/ColumnRenderers.tsx +++ b/src/frontend/src/tables/ColumnRenderers.tsx @@ -21,7 +21,7 @@ import { formatCurrency, formatDate } from '../defaults/formatters'; import { resolveItem } from '../functions/conversion'; import { useGlobalSettingsState } from '../states/SettingsStates'; import type { TableColumn, TableColumnProps } from './Column'; -import { ProjectCodeHoverCard } from './TableHoverCard'; +import { ProjectCodeHoverCard, TableHoverCard } from './TableHoverCard'; // Render a Part instance within a table 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 { + ...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 {name}; + } + + return ( + {instance.name}} + icon='sitemap' + title={props.title} + extra={[{instance.pathstring}]} + /> + ); + } + }; +} + +export function LocationColumn(props: TableColumnProps): TableColumn { + return PathColumn({ accessor: 'location', title: t`Location`, sortable: true, ordering: 'location', - render: (record: any) => { - const location = resolveItem(record, props.accessor ?? ''); - - if (!location) { - return ( - {t`No location set`} - ); - } - - return {location.pathstring}; - }, ...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 { diff --git a/src/frontend/src/tables/TableHoverCard.tsx b/src/frontend/src/tables/TableHoverCard.tsx index 44ef6e822f..31351e29d5 100644 --- a/src/frontend/src/tables/TableHoverCard.tsx +++ b/src/frontend/src/tables/TableHoverCard.tsx @@ -1,6 +1,5 @@ import { t } from '@lingui/core/macro'; import { Divider, Group, HoverCard, Stack, Text } from '@mantine/core'; -import { IconInfoCircle } from '@tabler/icons-react'; import { type ReactNode, useMemo } from 'react'; import type { InvenTreeIconType } from '@lib/types/Icons'; @@ -61,7 +60,10 @@ export function TableHoverCard({ - + {title} diff --git a/src/frontend/src/tables/part/PartTable.tsx b/src/frontend/src/tables/part/PartTable.tsx index f4cebf32d1..d221bb0514 100644 --- a/src/frontend/src/tables/part/PartTable.tsx +++ b/src/frontend/src/tables/part/PartTable.tsx @@ -22,7 +22,12 @@ import { import { useTable } from '../../hooks/UseTable'; import { useUserState } from '../../states/UserState'; 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 { type RowAction, RowEditAction } from '../RowActions'; import { TableHoverCard } from '../TableHoverCard'; @@ -53,11 +58,9 @@ function partTableColumns(): TableColumn[] { sortable: true }, DescriptionColumn({}), - { - accessor: 'category', - sortable: true, - render: (record: any) => record.category_detail?.pathstring - }, + CategoryColumn({ + accessor: 'category_detail' + }), { accessor: 'default_location', sortable: true, diff --git a/src/frontend/src/tables/stock/StockItemTable.tsx b/src/frontend/src/tables/stock/StockItemTable.tsx index 301f767318..3a5168eef7 100644 --- a/src/frontend/src/tables/stock/StockItemTable.tsx +++ b/src/frontend/src/tables/stock/StockItemTable.tsx @@ -32,8 +32,8 @@ import type { TableColumn } from '../Column'; import { DateColumn, DescriptionColumn, - LocationColumn, PartColumn, + PathColumn, StatusColumn } from '../ColumnRenderers'; import { StatusFilterOptions } from '../Filter'; @@ -222,9 +222,10 @@ function stockItemTableColumns({ accessor: 'batch', sortable: true }, - LocationColumn({ - hidden: !showLocation, - accessor: 'location_detail' + PathColumn({ + accessor: 'location_detail', + title: t`Location`, + hidden: !showLocation }), { accessor: 'purchase_order', diff --git a/src/frontend/tests/pages/pui_build.spec.ts b/src/frontend/tests/pages/pui_build.spec.ts index 17f7e2a687..b9efd6b433 100644 --- a/src/frontend/tests/pages/pui_build.spec.ts +++ b/src/frontend/tests/pages/pui_build.spec.ts @@ -80,7 +80,7 @@ test('Build Order - Basic Tests', async ({ browser }) => { await page.getByText('Quantity: 25').waitFor(); await page.getByText('Continuity Checks').waitFor(); await page - .getByRole('row', { name: 'Quantity: 16 No location set' }) + .getByRole('row', { name: 'Quantity: 16' }) .getByRole('button') .hover(); await page.getByText('Add Test Result').waitFor(); @@ -241,9 +241,7 @@ test('Build Order - Allocation', async ({ browser }) => { // Expand this row await cell.click(); await page.getByRole('cell', { name: '2022-4-27', exact: true }).waitFor(); - await page - .getByRole('cell', { name: 'Electronics Lab/Reel Storage', exact: true }) - .waitFor(); + await page.getByRole('cell', { name: 'Reel Storage', exact: true }).waitFor(); // Navigate to the "Incomplete Outputs" tab await loadTab(page, 'Incomplete Outputs'); diff --git a/src/frontend/tests/pages/pui_part.spec.ts b/src/frontend/tests/pages/pui_part.spec.ts index 97c32f2825..fa81010734 100644 --- a/src/frontend/tests/pages/pui_part.spec.ts +++ b/src/frontend/tests/pages/pui_part.spec.ts @@ -225,9 +225,7 @@ test('Parts - Allocations', async ({ browser }) => { // Expand allocations against BO0001 await build_order_cell.click(); await page.getByRole('cell', { name: '# 3', exact: true }).waitFor(); - await page - .getByRole('cell', { name: 'Factory/Office Block/Room 101', exact: true }) - .waitFor(); + await page.getByRole('cell', { name: 'Room 101', exact: true }).waitFor(); await build_order_cell.click(); // Check row options for BO0001