diff --git a/src/frontend/src/forms/PartForms.tsx b/src/frontend/src/forms/PartForms.tsx
index 939ade5a11..7a8aba31b0 100644
--- a/src/frontend/src/forms/PartForms.tsx
+++ b/src/frontend/src/forms/PartForms.tsx
@@ -1,99 +1,94 @@
import { t } from '@lingui/macro';
import { IconPackages } from '@tabler/icons-react';
+import { useMemo } from 'react';
import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
/**
* Construct a set of fields for creating / editing a Part instance
*/
-export function partFields({
- editing = false,
- category_id
+export function usePartFields({
+ create = false
}: {
- editing?: boolean;
- category_id?: number;
+ create?: boolean;
}): ApiFormFieldSet {
- let fields: ApiFormFieldSet = {
- category: {
- filters: {
- structural: false
- }
- },
- name: {},
- IPN: {},
- revision: {},
- description: {},
- variant_of: {},
- keywords: {},
- units: {},
- link: {},
- default_location: {
- filters: {
- structural: false
- }
- },
- default_expiry: {},
- minimum_stock: {},
- responsible: {
- filters: {
- is_active: true
- }
- },
- component: {},
- assembly: {},
- is_template: {},
- trackable: {},
- purchaseable: {},
- salable: {},
- virtual: {},
- active: {}
- };
-
- if (category_id != null) {
- // TODO: Set the value of the category field
- }
-
- // Additional fields for creation
- if (!editing) {
- // TODO: Hide 'active' field
-
- fields.copy_category_parameters = {};
-
- fields.initial_stock = {
- icon: ,
- children: {
- quantity: {},
- location: {}
- }
+ return useMemo(() => {
+ const fields: ApiFormFieldSet = {
+ category: {
+ filters: {
+ structural: false
+ }
+ },
+ name: {},
+ IPN: {},
+ revision: {},
+ description: {},
+ variant_of: {},
+ keywords: {},
+ units: {},
+ link: {},
+ default_location: {
+ filters: {
+ structural: false
+ }
+ },
+ default_expiry: {},
+ minimum_stock: {},
+ responsible: {
+ filters: {
+ is_active: true
+ }
+ },
+ component: {},
+ assembly: {},
+ is_template: {},
+ trackable: {},
+ purchaseable: {},
+ salable: {},
+ virtual: {},
+ active: {}
};
- fields.initial_supplier = {
- children: {
- supplier: {
- filters: {
- is_supplier: true
- }
- },
- sku: {},
- manufacturer: {
- filters: {
- is_manufacturer: true
- }
- },
- mpn: {}
- }
- };
- }
+ // Additional fields for creation
+ if (create) {
+ fields.copy_category_parameters = {};
- // TODO: pop 'expiry' field if expiry not enabled
- delete fields['default_expiry'];
+ fields.initial_stock = {
+ icon: ,
+ children: {
+ quantity: {},
+ location: {}
+ }
+ };
- // TODO: pop 'revision' field if PART_ENABLE_REVISION is False
- delete fields['revision'];
+ fields.initial_supplier = {
+ children: {
+ supplier: {
+ filters: {
+ is_supplier: true
+ }
+ },
+ sku: {},
+ manufacturer: {
+ filters: {
+ is_manufacturer: true
+ }
+ },
+ mpn: {}
+ }
+ };
+ }
- // TODO: handle part duplications
+ // TODO: pop 'expiry' field if expiry not enabled
+ delete fields['default_expiry'];
- return fields;
+ // TODO: pop 'revision' field if PART_ENABLE_REVISION is False
+ delete fields['revision'];
+
+ // TODO: handle part duplications
+
+ return fields;
+ }, [create]);
}
/**
diff --git a/src/frontend/src/pages/Index/Playground.tsx b/src/frontend/src/pages/Index/Playground.tsx
index 96918d555b..3bbf0f527d 100644
--- a/src/frontend/src/pages/Index/Playground.tsx
+++ b/src/frontend/src/pages/Index/Playground.tsx
@@ -10,7 +10,7 @@ import { StylishText } from '../../components/items/StylishText';
import { StatusRenderer } from '../../components/render/StatusRenderer';
import { ApiEndpoints } from '../../enums/ApiEndpoints';
import { ModelType } from '../../enums/ModelType';
-import { partCategoryFields, partFields } from '../../forms/PartForms';
+import { partCategoryFields, usePartFields } from '../../forms/PartForms';
import { useCreateStockItem } from '../../forms/StockForms';
import {
useCreateApiFormModal,
@@ -28,10 +28,13 @@ function ApiFormsPlayground() {
fields: fields
});
+ const createPartFields = usePartFields({ create: true });
+ const editPartFields = usePartFields({ create: false });
+
const newPart = useCreateApiFormModal({
url: ApiEndpoints.part_list,
title: 'Create Part',
- fields: partFields({}),
+ fields: createPartFields,
initialData: {
description: 'A part created via the API'
}
@@ -41,7 +44,7 @@ function ApiFormsPlayground() {
url: ApiEndpoints.part_list,
pk: 1,
title: 'Edit Part',
- fields: partFields({ editing: true })
+ fields: editPartFields
});
const newAttachment = useCreateApiFormModal({
@@ -62,7 +65,7 @@ function ApiFormsPlayground() {
const [name, setName] = useState('Hello');
const partFieldsState: any = useMemo(() => {
- const fields = partFields({});
+ const fields = editPartFields;
fields.name = {
...fields.name,
value: name,
diff --git a/src/frontend/src/pages/part/PartDetail.tsx b/src/frontend/src/pages/part/PartDetail.tsx
index c2ecf2884d..196425b335 100644
--- a/src/frontend/src/pages/part/PartDetail.tsx
+++ b/src/frontend/src/pages/part/PartDetail.tsx
@@ -46,7 +46,7 @@ import { formatPriceRange } from '../../defaults/formatters';
import { ApiEndpoints } from '../../enums/ApiEndpoints';
import { ModelType } from '../../enums/ModelType';
import { UserRoles } from '../../enums/Roles';
-import { partFields } from '../../forms/PartForms';
+import { usePartFields } from '../../forms/PartForms';
import { useEditApiFormModal } from '../../hooks/UseForm';
import { useInstance } from '../../hooks/UseInstance';
import { apiUrl } from '../../states/ApiState';
@@ -630,11 +630,13 @@ export default function PartDetail() {
);
}, [part, id]);
+ const partFields = usePartFields({ create: false });
+
const editPart = useEditApiFormModal({
url: ApiEndpoints.part_list,
pk: part.pk,
title: t`Edit Part`,
- fields: partFields({ editing: true }),
+ fields: partFields,
onFormSuccess: refreshInstance
});
diff --git a/src/frontend/src/tables/part/PartTable.tsx b/src/frontend/src/tables/part/PartTable.tsx
index 4f8f11bf44..7616e7c977 100644
--- a/src/frontend/src/tables/part/PartTable.tsx
+++ b/src/frontend/src/tables/part/PartTable.tsx
@@ -3,14 +3,19 @@ import { Group, Text } from '@mantine/core';
import { ReactNode, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
+import { AddItemButton } from '../../components/buttons/AddItemButton';
import { Thumbnail } from '../../components/images/Thumbnail';
import { formatPriceRange } from '../../defaults/formatters';
import { ApiEndpoints } from '../../enums/ApiEndpoints';
import { ModelType } from '../../enums/ModelType';
+import { UserRoles } from '../../enums/Roles';
+import { usePartFields } from '../../forms/PartForms';
import { shortenString } from '../../functions/tables';
import { getDetailUrl } from '../../functions/urls';
+import { useCreateApiFormModal } from '../../hooks/UseForm';
import { useTable } from '../../hooks/UseTable';
import { apiUrl } from '../../states/ApiState';
+import { useUserState } from '../../states/UserState';
import { TableColumn } from '../Column';
import { DescriptionColumn, LinkColumn } from '../ColumnRenderers';
import { TableFilter } from '../Filter';
@@ -261,25 +266,53 @@ export function PartListTable({ props }: { props: InvenTreeTableProps }) {
const tableFilters = useMemo(() => partTableFilters(), []);
const table = useTable('part-list');
-
const navigate = useNavigate();
+ const user = useUserState();
+
+ const newPart = useCreateApiFormModal({
+ url: ApiEndpoints.part_list,
+ title: t`Add Part`,
+ fields: usePartFields({ create: true }),
+ initialData: {
+ ...(props.params ?? {})
+ },
+ onFormSuccess: (data: any) => {
+ if (data.pk) {
+ navigate(getDetailUrl(ModelType.part, data.pk));
+ }
+ }
+ });
+
+ const tableActions = useMemo(() => {
+ return [
+ newPart.open()}
+ />
+ ];
+ }, [user]);
return (
-
- navigate(getDetailUrl(ModelType.part, record.pk))
- }}
- />
+ <>
+ {newPart.modal}
+
+ navigate(getDetailUrl(ModelType.part, record.pk))
+ }}
+ />
+ >
);
}