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 [ +