From 2e7e8d5eee4643dbcd968b94afd432be9b7ff017 Mon Sep 17 00:00:00 2001 From: Steve Groesz Date: Sun, 5 Oct 2025 07:11:48 -0500 Subject: [PATCH] add duplicate part action to parts table (#10481) Co-authored-by: Oliver --- src/frontend/src/tables/part/PartTable.tsx | 74 ++++++++++++++++++++-- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/src/frontend/src/tables/part/PartTable.tsx b/src/frontend/src/tables/part/PartTable.tsx index fd449aa9e2..688d51a82f 100644 --- a/src/frontend/src/tables/part/PartTable.tsx +++ b/src/frontend/src/tables/part/PartTable.tsx @@ -4,12 +4,17 @@ import { IconShoppingCart } from '@tabler/icons-react'; import { type ReactNode, useCallback, useMemo, useState } from 'react'; import { AddItemButton } from '@lib/components/AddItemButton'; -import { type RowAction, RowEditAction } from '@lib/components/RowActions'; +import { + type RowAction, + RowDuplicateAction, + RowEditAction +} from '@lib/components/RowActions'; import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; import { ModelType } from '@lib/enums/ModelType'; import { UserRoles } from '@lib/enums/Roles'; import { apiUrl } from '@lib/functions/Api'; import type { TableFilter } from '@lib/types/Filters'; +import type { ApiFormFieldSet } from '@lib/types/Forms'; import type { TableColumn } from '@lib/types/Tables'; import type { InvenTreeTableProps } from '@lib/types/Tables'; import { ActionDropdown } from '../../components/items/ActionDropdown'; @@ -23,6 +28,7 @@ import { useEditApiFormModal } from '../../hooks/UseForm'; import { useTable } from '../../hooks/UseTable'; +import { useGlobalSettingsState } from '../../states/SettingsStates'; import { useUserState } from '../../states/UserState'; import { CategoryColumn, @@ -333,6 +339,7 @@ export function PartListTable({ const table = useTable('part-list'); const user = useUserState(); + const globalSettings = useGlobalSettingsState(); const initialPartData = useMemo(() => { return defaultPartData ?? props.params ?? {}; @@ -347,16 +354,64 @@ export function PartListTable({ modelType: ModelType.part }); - const [selectedPart, setSelectedPart] = useState(-1); + const [selectedPart, setSelectedPart] = useState({}); const editPart = useEditApiFormModal({ url: ApiEndpoints.part_list, - pk: selectedPart, + pk: selectedPart.pk, title: t`Edit Part`, fields: usePartFields({ create: false }), onFormSuccess: table.refreshTable }); + const createPartFields = usePartFields({ create: true }); + + const duplicatePartFields: ApiFormFieldSet = useMemo(() => { + return { + ...createPartFields, + duplicate: { + children: { + part: { + value: selectedPart.pk, + hidden: true + }, + copy_image: { + value: true + }, + copy_bom: { + value: + selectedPart.assembly && globalSettings.isSet('PART_COPY_BOM'), + hidden: !selectedPart.assembly + }, + copy_notes: { + value: true + }, + copy_parameters: { + value: globalSettings.isSet('PART_COPY_PARAMETERS') + }, + copy_tests: { + value: selectedPart.testable, + hidden: !selectedPart.testable + } + } + } + }; + }, [createPartFields, globalSettings, selectedPart]); + + const duplicatePart = useCreateApiFormModal({ + url: ApiEndpoints.part_list, + title: t`Add Part`, + fields: duplicatePartFields, + initialData: { + ...selectedPart, + active: true, + locked: false + }, + follow: false, + modelType: ModelType.part, + onFormSuccess: table.refreshTable + }); + const setCategory = useBulkEditApiFormModal({ url: ApiEndpoints.part_list, items: table.selectedIds, @@ -372,18 +427,26 @@ export function PartListTable({ const rowActions = useCallback( (record: any): RowAction[] => { const can_edit = user.hasChangePermission(ModelType.part); + const can_add = user.hasAddPermission(ModelType.part); return [ RowEditAction({ hidden: !can_edit, onClick: () => { - setSelectedPart(record.pk); + setSelectedPart(record); editPart.open(); } + }), + RowDuplicateAction({ + hidden: !can_add, + onClick: () => { + setSelectedPart(record); + duplicatePart.open(); + } }) ]; }, - [user, editPart] + [user, editPart, duplicatePart] ); const tableActions = useMemo(() => { @@ -426,6 +489,7 @@ export function PartListTable({ return ( <> {newPart.modal} + {duplicatePart.modal} {editPart.modal} {setCategory.modal} {orderPartsWizard.wizard}