mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
[PUI] Implement manufacturer part table (#6115)
* Fix supplier part form - Ensure manufacturer parts get filtered by "part" instance * Implement simple ManufacturerPart table * Add table actions * Fix unused imports
This commit is contained in:
parent
a63529a9cf
commit
be1820fb94
@ -5,6 +5,7 @@ import { t } from '@lingui/macro';
|
|||||||
|
|
||||||
import { formatCurrency, renderDate } from '../../defaults/formatters';
|
import { formatCurrency, renderDate } from '../../defaults/formatters';
|
||||||
import { ModelType } from '../../enums/ModelType';
|
import { ModelType } from '../../enums/ModelType';
|
||||||
|
import { Thumbnail } from '../images/Thumbnail';
|
||||||
import { ProgressBar } from '../items/ProgressBar';
|
import { ProgressBar } from '../items/ProgressBar';
|
||||||
import { YesNoButton } from '../items/YesNoButton';
|
import { YesNoButton } from '../items/YesNoButton';
|
||||||
import { TableStatusRenderer } from '../render/StatusRenderer';
|
import { TableStatusRenderer } from '../render/StatusRenderer';
|
||||||
@ -12,6 +13,11 @@ import { RenderOwner } from '../render/User';
|
|||||||
import { TableColumn } from './Column';
|
import { TableColumn } from './Column';
|
||||||
import { ProjectCodeHoverCard } from './TableHoverCard';
|
import { ProjectCodeHoverCard } from './TableHoverCard';
|
||||||
|
|
||||||
|
// Render a Part instance within a table
|
||||||
|
export function PartColumn(part: any) {
|
||||||
|
return <Thumbnail src={part?.thumbnail ?? part.image} text={part.name} />;
|
||||||
|
}
|
||||||
|
|
||||||
export function BooleanColumn({
|
export function BooleanColumn({
|
||||||
accessor,
|
accessor,
|
||||||
title
|
title
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Group, Text } from '@mantine/core';
|
import { Text } from '@mantine/core';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
@ -13,9 +13,9 @@ 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 { AddItemButton } from '../../buttons/AddItemButton';
|
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
|
||||||
import { YesNoButton } from '../../items/YesNoButton';
|
import { YesNoButton } from '../../items/YesNoButton';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
|
import { PartColumn } from '../ColumnRenderers';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
import { RowDeleteAction, RowEditAction } from '../RowActions';
|
import { RowDeleteAction, RowEditAction } from '../RowActions';
|
||||||
|
|
||||||
@ -34,20 +34,7 @@ export function PartParameterTable({ partId }: { partId: any }) {
|
|||||||
title: t`Part`,
|
title: t`Part`,
|
||||||
|
|
||||||
sortable: true,
|
sortable: true,
|
||||||
render: function (record: any) {
|
render: (record: any) => PartColumn(record?.part_detail)
|
||||||
let part = record?.part_detail ?? {};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Group spacing="xs" align="left" noWrap={true}>
|
|
||||||
<Thumbnail
|
|
||||||
src={part?.thumbnail || part?.image}
|
|
||||||
alt={part?.name}
|
|
||||||
size={24}
|
|
||||||
/>
|
|
||||||
<Text>{part?.full_name}</Text>
|
|
||||||
</Group>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessor: 'name',
|
accessor: 'name',
|
||||||
|
@ -14,6 +14,7 @@ import { apiUrl } from '../../../states/ApiState';
|
|||||||
import { useUserState } from '../../../states/UserState';
|
import { useUserState } from '../../../states/UserState';
|
||||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
|
import { DescriptionColumn } from '../ColumnRenderers';
|
||||||
import { TableFilter } from '../Filter';
|
import { TableFilter } from '../Filter';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
import { RowDeleteAction, RowEditAction } from '../RowActions';
|
import { RowDeleteAction, RowEditAction } from '../RowActions';
|
||||||
@ -56,11 +57,7 @@ export default function PartParameterTemplateTable() {
|
|||||||
title: t`Units`,
|
title: t`Units`,
|
||||||
sortable: true
|
sortable: true
|
||||||
},
|
},
|
||||||
{
|
DescriptionColumn(),
|
||||||
accessor: 'description',
|
|
||||||
title: t`Description`,
|
|
||||||
sortable: false
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
accessor: 'checkbox',
|
accessor: 'checkbox',
|
||||||
title: t`Checkbox`
|
title: t`Checkbox`
|
||||||
|
@ -0,0 +1,119 @@
|
|||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { ReactNode, useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
|
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||||
|
import { UserRoles } from '../../../enums/Roles';
|
||||||
|
import { useManufacturerPartFields } from '../../../forms/CompanyForms';
|
||||||
|
import { openDeleteApiForm, openEditApiForm } from '../../../functions/forms';
|
||||||
|
import { useTable } from '../../../hooks/UseTable';
|
||||||
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
|
import { useUserState } from '../../../states/UserState';
|
||||||
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
|
import { TableColumn } from '../Column';
|
||||||
|
import { DescriptionColumn, LinkColumn, PartColumn } from '../ColumnRenderers';
|
||||||
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
import { RowDeleteAction, RowEditAction } from '../RowActions';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Construct a table listing manufacturer parts
|
||||||
|
*/
|
||||||
|
export function ManufacturerPartTable({ params }: { params: any }): ReactNode {
|
||||||
|
const table = useTable('manufacturerparts');
|
||||||
|
|
||||||
|
const user = useUserState();
|
||||||
|
|
||||||
|
// Construct table columns for this table
|
||||||
|
const tableColumns: TableColumn[] = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
accessor: 'part',
|
||||||
|
title: t`Part`,
|
||||||
|
switchable: 'part' in params,
|
||||||
|
sortable: true,
|
||||||
|
render: (record: any) => PartColumn(record?.part_detail)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'manufacturer',
|
||||||
|
title: t`Manufacturer`,
|
||||||
|
sortable: true,
|
||||||
|
render: (record: any) => {
|
||||||
|
let manufacturer = record?.manufacturer_detail ?? {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Thumbnail
|
||||||
|
src={manufacturer?.thumbnail ?? manufacturer.image}
|
||||||
|
text={manufacturer.name}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'MPN',
|
||||||
|
title: t`Manufacturer Part Number`,
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
DescriptionColumn(),
|
||||||
|
LinkColumn()
|
||||||
|
];
|
||||||
|
}, [params]);
|
||||||
|
|
||||||
|
const tableActions = useMemo(() => {
|
||||||
|
// TODO: Custom actions
|
||||||
|
return [];
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
|
const editManufacturerPartFields = useManufacturerPartFields();
|
||||||
|
|
||||||
|
const rowActions = useCallback(
|
||||||
|
(record: any) => {
|
||||||
|
return [
|
||||||
|
RowEditAction({
|
||||||
|
hidden: !user.hasChangeRole(UserRoles.purchase_order),
|
||||||
|
onClick: () => {
|
||||||
|
record.pk &&
|
||||||
|
openEditApiForm({
|
||||||
|
url: ApiPaths.manufacturer_part_list,
|
||||||
|
pk: record.pk,
|
||||||
|
title: t`Edit Manufacturer Part`,
|
||||||
|
fields: editManufacturerPartFields,
|
||||||
|
onFormSuccess: table.refreshTable,
|
||||||
|
successMessage: t`Manufacturer part updated`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
RowDeleteAction({
|
||||||
|
hidden: !user.hasDeleteRole(UserRoles.purchase_order),
|
||||||
|
onClick: () => {
|
||||||
|
record.pk &&
|
||||||
|
openDeleteApiForm({
|
||||||
|
url: ApiPaths.manufacturer_part_list,
|
||||||
|
pk: record.pk,
|
||||||
|
title: t`Delete Manufacturer Part`,
|
||||||
|
successMessage: t`Manufacturer part deleted`,
|
||||||
|
onFormSuccess: table.refreshTable,
|
||||||
|
preFormWarning: t`Are you sure you want to remove this manufacturer part?`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
];
|
||||||
|
},
|
||||||
|
[user]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InvenTreeTable
|
||||||
|
url={apiUrl(ApiPaths.manufacturer_part_list)}
|
||||||
|
tableState={table}
|
||||||
|
columns={tableColumns}
|
||||||
|
props={{
|
||||||
|
params: {
|
||||||
|
...params,
|
||||||
|
part_detail: true,
|
||||||
|
manufacturer_detail: true
|
||||||
|
},
|
||||||
|
rowActions: rowActions,
|
||||||
|
customActionGroups: tableActions
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@ -13,7 +13,7 @@ import { useUserState } from '../../../states/UserState';
|
|||||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
import { Thumbnail } from '../../images/Thumbnail';
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { DescriptionColumn, LinkColumn } from '../ColumnRenderers';
|
import { DescriptionColumn, LinkColumn, 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';
|
import { TableHoverCard } from '../TableHoverCard';
|
||||||
@ -35,13 +35,7 @@ export function SupplierPartTable({ params }: { params: any }): ReactNode {
|
|||||||
title: t`Part`,
|
title: t`Part`,
|
||||||
switchable: 'part' in params,
|
switchable: 'part' in params,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
render: (record: any) => {
|
render: (record: any) => PartColumn(record?.part_detail)
|
||||||
let part = record?.part_detail ?? {};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Thumbnail src={part?.thumbnail ?? part.image} text={part.name} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessor: 'supplier',
|
accessor: 'supplier',
|
||||||
@ -179,7 +173,8 @@ export function SupplierPartTable({ params }: { params: any }): ReactNode {
|
|||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
const editSupplierPartFields = useSupplierPartFields({
|
const editSupplierPartFields = useSupplierPartFields({
|
||||||
hidePart: true
|
hidePart: true,
|
||||||
|
partPk: params?.part
|
||||||
});
|
});
|
||||||
|
|
||||||
// Row action callback
|
// Row action callback
|
||||||
|
@ -8,9 +8,8 @@ import { ApiPaths } from '../../../enums/ApiEndpoints';
|
|||||||
import { ModelType } from '../../../enums/ModelType';
|
import { ModelType } from '../../../enums/ModelType';
|
||||||
import { useTable } from '../../../hooks/UseTable';
|
import { useTable } from '../../../hooks/UseTable';
|
||||||
import { apiUrl } from '../../../states/ApiState';
|
import { apiUrl } from '../../../states/ApiState';
|
||||||
import { Thumbnail } from '../../images/Thumbnail';
|
|
||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { StatusColumn } from '../ColumnRenderers';
|
import { PartColumn, StatusColumn } from '../ColumnRenderers';
|
||||||
import { StatusFilterOptions, TableFilter } from '../Filter';
|
import { StatusFilterOptions, TableFilter } from '../Filter';
|
||||||
import { TableHoverCard } from '../TableHoverCard';
|
import { TableHoverCard } from '../TableHoverCard';
|
||||||
import { InvenTreeTable } from './../InvenTreeTable';
|
import { InvenTreeTable } from './../InvenTreeTable';
|
||||||
@ -24,24 +23,11 @@ function stockItemTableColumns(): TableColumn[] {
|
|||||||
accessor: 'part',
|
accessor: 'part',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
title: t`Part`,
|
title: t`Part`,
|
||||||
render: function (record: any) {
|
render: (record: any) => PartColumn(record?.part_detail)
|
||||||
let part = record.part_detail ?? {};
|
|
||||||
return (
|
|
||||||
<Group spacing="xs" noWrap={true}>
|
|
||||||
<Thumbnail
|
|
||||||
src={part?.thumbnail || part?.image}
|
|
||||||
alt={part?.name}
|
|
||||||
size={24}
|
|
||||||
/>
|
|
||||||
<Text>{part?.full_name}</Text>
|
|
||||||
</Group>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessor: 'part_detail.description',
|
accessor: 'part_detail.description',
|
||||||
sortable: false,
|
sortable: false,
|
||||||
|
|
||||||
title: t`Description`
|
title: t`Description`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -81,6 +81,20 @@ export function useSupplierPartFields({
|
|||||||
}, [part]);
|
}, [part]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useManufacturerPartFields() {
|
||||||
|
return useMemo(() => {
|
||||||
|
const fields: ApiFormFieldSet = {
|
||||||
|
part: {},
|
||||||
|
manufacturer: {},
|
||||||
|
MPN: {},
|
||||||
|
description: {},
|
||||||
|
link: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Field set for editing a company instance
|
* Field set for editing a company instance
|
||||||
*/
|
*/
|
||||||
|
@ -46,6 +46,7 @@ import { AttachmentTable } from '../../components/tables/general/AttachmentTable
|
|||||||
import { PartParameterTable } from '../../components/tables/part/PartParameterTable';
|
import { PartParameterTable } from '../../components/tables/part/PartParameterTable';
|
||||||
import { PartVariantTable } from '../../components/tables/part/PartVariantTable';
|
import { PartVariantTable } from '../../components/tables/part/PartVariantTable';
|
||||||
import { RelatedPartTable } from '../../components/tables/part/RelatedPartTable';
|
import { RelatedPartTable } from '../../components/tables/part/RelatedPartTable';
|
||||||
|
import { ManufacturerPartTable } from '../../components/tables/purchasing/ManufacturerPartTable';
|
||||||
import { SupplierPartTable } from '../../components/tables/purchasing/SupplierPartTable';
|
import { SupplierPartTable } from '../../components/tables/purchasing/SupplierPartTable';
|
||||||
import { SalesOrderTable } from '../../components/tables/sales/SalesOrderTable';
|
import { SalesOrderTable } from '../../components/tables/sales/SalesOrderTable';
|
||||||
import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
||||||
@ -155,7 +156,14 @@ export default function PartDetail() {
|
|||||||
name: 'manufacturers',
|
name: 'manufacturers',
|
||||||
label: t`Manufacturers`,
|
label: t`Manufacturers`,
|
||||||
icon: <IconBuildingFactory2 />,
|
icon: <IconBuildingFactory2 />,
|
||||||
hidden: !part.purchaseable
|
hidden: !part.purchaseable,
|
||||||
|
content: part.pk && (
|
||||||
|
<ManufacturerPartTable
|
||||||
|
params={{
|
||||||
|
part: part.pk
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'suppliers',
|
name: 'suppliers',
|
||||||
@ -165,7 +173,7 @@ export default function PartDetail() {
|
|||||||
content: part.pk && (
|
content: part.pk && (
|
||||||
<SupplierPartTable
|
<SupplierPartTable
|
||||||
params={{
|
params={{
|
||||||
part: part.pk ?? -1
|
part: part.pk
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user