[PUI] Part parameter table (#6599)

* Refactor for PartTable

* Skeleton for ParametricPartTable

* Implement parametric part table

* Table updates
This commit is contained in:
Oliver 2024-02-28 17:43:36 +11:00 committed by GitHub
parent a2a7b60d41
commit 820d7c6a15
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 145 additions and 23 deletions

View File

@ -8,13 +8,14 @@ import {
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { PlaceholderPanel } from '../../components/items/Placeholder';
import { PageDetail } from '../../components/nav/PageDetail'; import { PageDetail } from '../../components/nav/PageDetail';
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup'; import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
import { PartCategoryTree } from '../../components/nav/PartCategoryTree'; import { PartCategoryTree } from '../../components/nav/PartCategoryTree';
import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { ApiEndpoints } from '../../enums/ApiEndpoints';
import { useInstance } from '../../hooks/UseInstance'; import { useInstance } from '../../hooks/UseInstance';
import ParametricPartTable from '../../tables/part/ParametricPartTable';
import { PartCategoryTable } from '../../tables/part/PartCategoryTable'; import { PartCategoryTable } from '../../tables/part/PartCategoryTable';
import { PartParameterTable } from '../../tables/part/PartParameterTable';
import { PartListTable } from '../../tables/part/PartTable'; import { PartListTable } from '../../tables/part/PartTable';
/** /**
@ -70,7 +71,7 @@ export default function CategoryDetail({}: {}) {
name: 'parameters', name: 'parameters',
label: t`Parameters`, label: t`Parameters`,
icon: <IconListDetails />, icon: <IconListDetails />,
content: <PlaceholderPanel /> content: <ParametricPartTable categoryId={id} />
} }
], ],
[category, id] [category, id]

View File

@ -29,7 +29,6 @@ import {
UnlinkBarcodeAction, UnlinkBarcodeAction,
ViewBarcodeAction ViewBarcodeAction
} from '../../components/items/ActionDropdown'; } from '../../components/items/ActionDropdown';
import { PlaceholderPanel } from '../../components/items/Placeholder';
import { PageDetail } from '../../components/nav/PageDetail'; import { PageDetail } from '../../components/nav/PageDetail';
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup'; import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
import { StockLocationTree } from '../../components/nav/StockLocationTree'; import { StockLocationTree } from '../../components/nav/StockLocationTree';
@ -69,20 +68,17 @@ export default function StockDetail() {
{ {
name: 'details', name: 'details',
label: t`Details`, label: t`Details`,
icon: <IconInfoCircle />, icon: <IconInfoCircle />
content: <PlaceholderPanel />
}, },
{ {
name: 'tracking', name: 'tracking',
label: t`Stock Tracking`, label: t`Stock Tracking`,
icon: <IconHistory />, icon: <IconHistory />
content: <PlaceholderPanel />
}, },
{ {
name: 'allocations', name: 'allocations',
label: t`Allocations`, label: t`Allocations`,
icon: <IconBookmark />, icon: <IconBookmark />,
content: <PlaceholderPanel />,
hidden: hidden:
!stockitem?.part_detail?.salable && !stockitem?.part_detail?.component !stockitem?.part_detail?.salable && !stockitem?.part_detail?.component
}, },

View File

@ -0,0 +1,122 @@
import { t } from '@lingui/macro';
import { useQuery } from '@tanstack/react-query';
import { useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { api } from '../../App';
import { ApiEndpoints } from '../../enums/ApiEndpoints';
import { ModelType } from '../../enums/ModelType';
import { getDetailUrl } from '../../functions/urls';
import { useTable } from '../../hooks/UseTable';
import { apiUrl } from '../../states/ApiState';
import { useUserState } from '../../states/UserState';
import { TableColumn } from '../Column';
import { DescriptionColumn, PartColumn } from '../ColumnRenderers';
import { InvenTreeTable } from '../InvenTreeTable';
import { TableHoverCard } from '../TableHoverCard';
export default function ParametricPartTable({
categoryId
}: {
categoryId?: any;
}) {
const table = useTable('parametric-parts');
const user = useUserState();
const navigate = useNavigate();
const categoryParmeters = useQuery({
queryKey: ['category-parameters', categoryId],
queryFn: async () => {
return api
.get(apiUrl(ApiEndpoints.part_parameter_template_list), {
params: {
category: categoryId
}
})
.then((response) => response.data)
.catch((_error) => []);
},
refetchOnMount: true
});
const parameterColumns: TableColumn[] = useMemo(() => {
let data = categoryParmeters.data ?? [];
return data.map((template: any) => {
return {
accessor: `parameter_${template.pk}`,
title: template.name,
sortable: true,
render: (record: any) => {
// Find matching template parameter
let parameter = record.parameters?.find(
(p: any) => p.template == template.pk
);
if (!parameter) {
return '-';
}
let extra: any[] = [];
if (
template.units &&
parameter.data_numeric &&
parameter.data_numeric != parameter.data
) {
extra.push(`${parameter.data_numeric} [${template.units}]`);
}
return (
<TableHoverCard
value={parameter.data}
extra={extra}
title={t`Internal Units`}
/>
);
}
};
});
}, [categoryParmeters.data]);
const tableColumns: TableColumn[] = useMemo(() => {
const partColumns: TableColumn[] = [
{
accessor: 'name',
sortable: true,
switchable: false,
noWrap: true,
render: (record: any) => PartColumn(record)
},
DescriptionColumn({}),
{
accessor: 'IPN',
sortable: true
}
];
return [...partColumns, ...parameterColumns];
}, [parameterColumns]);
return (
<InvenTreeTable
url={apiUrl(ApiEndpoints.part_list)}
tableState={table}
columns={tableColumns}
props={{
enableDownload: false,
params: {
category: categoryId,
cascade: true,
category_detail: true,
parameters: true
},
onRowClick: (record) => {
if (record.pk) {
navigate(getDetailUrl(ModelType.part, record.pk));
}
}
}}
/>
);
}

View File

@ -19,6 +19,7 @@ import { TableColumn } from '../Column';
import { DescriptionColumn, PartColumn } from '../ColumnRenderers'; import { DescriptionColumn, PartColumn } from '../ColumnRenderers';
import { InvenTreeTable } from '../InvenTreeTable'; import { InvenTreeTable } from '../InvenTreeTable';
import { RowDeleteAction, RowEditAction } from '../RowActions'; import { RowDeleteAction, RowEditAction } from '../RowActions';
import { TableHoverCard } from '../TableHoverCard';
/** /**
* Construct a table listing parameters for a given part * Construct a table listing parameters for a given part
@ -65,13 +66,23 @@ export function PartParameterTable({ partId }: { partId: any }) {
return <YesNoButton value={record.data} />; return <YesNoButton value={record.data} />;
} }
if (record.data_numeric) { let extra: any[] = [];
// TODO: Numeric data
if (
template.units &&
record.data_numeric &&
record.data_numeric != record.data
) {
extra.push(`${record.data_numeric} [${template.units}]`);
} }
// TODO: Units return (
<TableHoverCard
return record.data; value={record.data}
extra={extra}
title={t`Internal Units`}
/>
);
} }
}, },
{ {

View File

@ -17,7 +17,7 @@ import { useTable } from '../../hooks/UseTable';
import { apiUrl } from '../../states/ApiState'; import { apiUrl } from '../../states/ApiState';
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { TableColumn } from '../Column'; import { TableColumn } from '../Column';
import { DescriptionColumn, LinkColumn } from '../ColumnRenderers'; import { DescriptionColumn, LinkColumn, PartColumn } from '../ColumnRenderers';
import { TableFilter } from '../Filter'; import { TableFilter } from '../Filter';
import { InvenTreeTable, InvenTreeTableProps } from '../InvenTreeTable'; import { InvenTreeTable, InvenTreeTableProps } from '../InvenTreeTable';
import { TableHoverCard } from '../TableHoverCard'; import { TableHoverCard } from '../TableHoverCard';
@ -31,15 +31,7 @@ function partTableColumns(): TableColumn[] {
accessor: 'name', accessor: 'name',
sortable: true, sortable: true,
noWrap: true, noWrap: true,
render: function (record: any) { render: (record: any) => PartColumn(record)
return (
<Thumbnail
src={record.thumbnail || record.image}
alt={record.name}
text={record.full_name}
/>
);
}
}, },
{ {
accessor: 'IPN', accessor: 'IPN',