diff --git a/src/frontend/src/components/tables/ColumnRenderers.tsx b/src/frontend/src/components/tables/ColumnRenderers.tsx
index daf81c011c..0913f87efa 100644
--- a/src/frontend/src/components/tables/ColumnRenderers.tsx
+++ b/src/frontend/src/components/tables/ColumnRenderers.tsx
@@ -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 ;
+}
+
export function BooleanColumn({
accessor,
title
diff --git a/src/frontend/src/components/tables/part/PartParameterTable.tsx b/src/frontend/src/components/tables/part/PartParameterTable.tsx
index ffaa672522..4a04964521 100644
--- a/src/frontend/src/components/tables/part/PartParameterTable.tsx
+++ b/src/frontend/src/components/tables/part/PartParameterTable.tsx
@@ -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 (
-
-
- {part?.full_name}
-
- );
- }
+ render: (record: any) => PartColumn(record?.part_detail)
},
{
accessor: 'name',
diff --git a/src/frontend/src/components/tables/part/PartParameterTemplateTable.tsx b/src/frontend/src/components/tables/part/PartParameterTemplateTable.tsx
index dc028c0db3..de3fbff064 100644
--- a/src/frontend/src/components/tables/part/PartParameterTemplateTable.tsx
+++ b/src/frontend/src/components/tables/part/PartParameterTemplateTable.tsx
@@ -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`
diff --git a/src/frontend/src/components/tables/purchasing/ManufacturerPartTable.tsx b/src/frontend/src/components/tables/purchasing/ManufacturerPartTable.tsx
new file mode 100644
index 0000000000..f5c5c56265
--- /dev/null
+++ b/src/frontend/src/components/tables/purchasing/ManufacturerPartTable.tsx
@@ -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 (
+
+ );
+ }
+ },
+ {
+ 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 (
+
+ );
+}
diff --git a/src/frontend/src/components/tables/purchasing/SupplierPartTable.tsx b/src/frontend/src/components/tables/purchasing/SupplierPartTable.tsx
index 3ac913ef14..b8456df01e 100644
--- a/src/frontend/src/components/tables/purchasing/SupplierPartTable.tsx
+++ b/src/frontend/src/components/tables/purchasing/SupplierPartTable.tsx
@@ -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 (
-
- );
- }
+ 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
diff --git a/src/frontend/src/components/tables/stock/StockItemTable.tsx b/src/frontend/src/components/tables/stock/StockItemTable.tsx
index da2fc6d1a2..c2ab5bedb0 100644
--- a/src/frontend/src/components/tables/stock/StockItemTable.tsx
+++ b/src/frontend/src/components/tables/stock/StockItemTable.tsx
@@ -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 (
-
-
- {part?.full_name}
-
- );
- }
+ render: (record: any) => PartColumn(record?.part_detail)
},
{
accessor: 'part_detail.description',
sortable: false,
-
title: t`Description`
},
{
diff --git a/src/frontend/src/forms/CompanyForms.tsx b/src/frontend/src/forms/CompanyForms.tsx
index 344b6056b6..0477367a13 100644
--- a/src/frontend/src/forms/CompanyForms.tsx
+++ b/src/frontend/src/forms/CompanyForms.tsx
@@ -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
*/
diff --git a/src/frontend/src/pages/part/PartDetail.tsx b/src/frontend/src/pages/part/PartDetail.tsx
index 7efe90d47c..6ef88f759f 100644
--- a/src/frontend/src/pages/part/PartDetail.tsx
+++ b/src/frontend/src/pages/part/PartDetail.tsx
@@ -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: ,
- hidden: !part.purchaseable
+ hidden: !part.purchaseable,
+ content: part.pk && (
+
+ )
},
{
name: 'suppliers',
@@ -165,7 +173,7 @@ export default function PartDetail() {
content: part.pk && (
)