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 { ModelType } from '../../enums/ModelType';
|
||||
import { Thumbnail } from '../images/Thumbnail';
|
||||
import { ProgressBar } from '../items/ProgressBar';
|
||||
import { YesNoButton } from '../items/YesNoButton';
|
||||
import { TableStatusRenderer } from '../render/StatusRenderer';
|
||||
@ -12,6 +13,11 @@ import { RenderOwner } from '../render/User';
|
||||
import { TableColumn } from './Column';
|
||||
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({
|
||||
accessor,
|
||||
title
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { Group, Text } from '@mantine/core';
|
||||
import { Text } from '@mantine/core';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||
@ -13,9 +13,9 @@ import { useTable } from '../../../hooks/UseTable';
|
||||
import { apiUrl } from '../../../states/ApiState';
|
||||
import { useUserState } from '../../../states/UserState';
|
||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||
import { Thumbnail } from '../../images/Thumbnail';
|
||||
import { YesNoButton } from '../../items/YesNoButton';
|
||||
import { TableColumn } from '../Column';
|
||||
import { PartColumn } from '../ColumnRenderers';
|
||||
import { InvenTreeTable } from '../InvenTreeTable';
|
||||
import { RowDeleteAction, RowEditAction } from '../RowActions';
|
||||
|
||||
@ -34,20 +34,7 @@ export function PartParameterTable({ partId }: { partId: any }) {
|
||||
title: t`Part`,
|
||||
|
||||
sortable: true,
|
||||
render: function (record: any) {
|
||||
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>
|
||||
);
|
||||
}
|
||||
render: (record: any) => PartColumn(record?.part_detail)
|
||||
},
|
||||
{
|
||||
accessor: 'name',
|
||||
|
@ -14,6 +14,7 @@ import { apiUrl } from '../../../states/ApiState';
|
||||
import { useUserState } from '../../../states/UserState';
|
||||
import { AddItemButton } from '../../buttons/AddItemButton';
|
||||
import { TableColumn } from '../Column';
|
||||
import { DescriptionColumn } from '../ColumnRenderers';
|
||||
import { TableFilter } from '../Filter';
|
||||
import { InvenTreeTable } from '../InvenTreeTable';
|
||||
import { RowDeleteAction, RowEditAction } from '../RowActions';
|
||||
@ -56,11 +57,7 @@ export default function PartParameterTemplateTable() {
|
||||
title: t`Units`,
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
accessor: 'description',
|
||||
title: t`Description`,
|
||||
sortable: false
|
||||
},
|
||||
DescriptionColumn(),
|
||||
{
|
||||
accessor: '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 { Thumbnail } from '../../images/Thumbnail';
|
||||
import { TableColumn } from '../Column';
|
||||
import { DescriptionColumn, LinkColumn } from '../ColumnRenderers';
|
||||
import { DescriptionColumn, LinkColumn, PartColumn } from '../ColumnRenderers';
|
||||
import { InvenTreeTable } from '../InvenTreeTable';
|
||||
import { RowDeleteAction, RowEditAction } from '../RowActions';
|
||||
import { TableHoverCard } from '../TableHoverCard';
|
||||
@ -35,13 +35,7 @@ export function SupplierPartTable({ params }: { params: any }): ReactNode {
|
||||
title: t`Part`,
|
||||
switchable: 'part' in params,
|
||||
sortable: true,
|
||||
render: (record: any) => {
|
||||
let part = record?.part_detail ?? {};
|
||||
|
||||
return (
|
||||
<Thumbnail src={part?.thumbnail ?? part.image} text={part.name} />
|
||||
);
|
||||
}
|
||||
render: (record: any) => PartColumn(record?.part_detail)
|
||||
},
|
||||
{
|
||||
accessor: 'supplier',
|
||||
@ -179,7 +173,8 @@ export function SupplierPartTable({ params }: { params: any }): ReactNode {
|
||||
}, [user]);
|
||||
|
||||
const editSupplierPartFields = useSupplierPartFields({
|
||||
hidePart: true
|
||||
hidePart: true,
|
||||
partPk: params?.part
|
||||
});
|
||||
|
||||
// Row action callback
|
||||
|
@ -8,9 +8,8 @@ import { ApiPaths } from '../../../enums/ApiEndpoints';
|
||||
import { ModelType } from '../../../enums/ModelType';
|
||||
import { useTable } from '../../../hooks/UseTable';
|
||||
import { apiUrl } from '../../../states/ApiState';
|
||||
import { Thumbnail } from '../../images/Thumbnail';
|
||||
import { TableColumn } from '../Column';
|
||||
import { StatusColumn } from '../ColumnRenderers';
|
||||
import { PartColumn, StatusColumn } from '../ColumnRenderers';
|
||||
import { StatusFilterOptions, TableFilter } from '../Filter';
|
||||
import { TableHoverCard } from '../TableHoverCard';
|
||||
import { InvenTreeTable } from './../InvenTreeTable';
|
||||
@ -24,24 +23,11 @@ function stockItemTableColumns(): TableColumn[] {
|
||||
accessor: 'part',
|
||||
sortable: true,
|
||||
title: t`Part`,
|
||||
render: function (record: any) {
|
||||
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>
|
||||
);
|
||||
}
|
||||
render: (record: any) => PartColumn(record?.part_detail)
|
||||
},
|
||||
{
|
||||
accessor: 'part_detail.description',
|
||||
sortable: false,
|
||||
|
||||
title: t`Description`
|
||||
},
|
||||
{
|
||||
|
@ -81,6 +81,20 @@ export function useSupplierPartFields({
|
||||
}, [part]);
|
||||
}
|
||||
|
||||
export function useManufacturerPartFields() {
|
||||
return useMemo(() => {
|
||||
const fields: ApiFormFieldSet = {
|
||||
part: {},
|
||||
manufacturer: {},
|
||||
MPN: {},
|
||||
description: {},
|
||||
link: {}
|
||||
};
|
||||
|
||||
return fields;
|
||||
}, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 { PartVariantTable } from '../../components/tables/part/PartVariantTable';
|
||||
import { RelatedPartTable } from '../../components/tables/part/RelatedPartTable';
|
||||
import { ManufacturerPartTable } from '../../components/tables/purchasing/ManufacturerPartTable';
|
||||
import { SupplierPartTable } from '../../components/tables/purchasing/SupplierPartTable';
|
||||
import { SalesOrderTable } from '../../components/tables/sales/SalesOrderTable';
|
||||
import { StockItemTable } from '../../components/tables/stock/StockItemTable';
|
||||
@ -155,7 +156,14 @@ export default function PartDetail() {
|
||||
name: 'manufacturers',
|
||||
label: t`Manufacturers`,
|
||||
icon: <IconBuildingFactory2 />,
|
||||
hidden: !part.purchaseable
|
||||
hidden: !part.purchaseable,
|
||||
content: part.pk && (
|
||||
<ManufacturerPartTable
|
||||
params={{
|
||||
part: part.pk
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: 'suppliers',
|
||||
@ -165,7 +173,7 @@ export default function PartDetail() {
|
||||
content: part.pk && (
|
||||
<SupplierPartTable
|
||||
params={{
|
||||
part: part.pk ?? -1
|
||||
part: part.pk
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user