diff --git a/src/frontend/lib/types/Tables.tsx b/src/frontend/lib/types/Tables.tsx index 798b72329d..aa8272f727 100644 --- a/src/frontend/lib/types/Tables.tsx +++ b/src/frontend/lib/types/Tables.tsx @@ -173,6 +173,7 @@ export type RowViewProps = RowAction & RowModelProps; * @param barcodeActions : any[] - List of barcode actions * @param tableFilters : TableFilter[] - List of custom filters * @param tableActions : any[] - List of custom action groups + * @param isRecordSelectable : (record: any, index: number) => boolean - Callback function to determine if a row is selectable * @param detailAction: boolean - Enable detail action for each row (default = true) * @param dataFormatter : (data: any) => any - Callback function to reformat data returned by server (if not in default format) * @param rowActions : (record: any) => RowAction[] - Callback function to generate row actions @@ -203,6 +204,7 @@ export type InvenTreeTableProps = { barcodeActions?: React.ReactNode[]; tableFilters?: TableFilter[]; tableActions?: React.ReactNode[]; + isRecordSelectable?: (record: T, index: number) => boolean; rowExpansion?: DataTableRowExpansionProps; dataFormatter?: (data: any) => any; rowActions?: (record: T) => RowAction[]; diff --git a/src/frontend/src/tables/InvenTreeTable.tsx b/src/frontend/src/tables/InvenTreeTable.tsx index 89f1117833..c8b78770ba 100644 --- a/src/frontend/src/tables/InvenTreeTable.tsx +++ b/src/frontend/src/tables/InvenTreeTable.tsx @@ -860,6 +860,7 @@ export function InvenTreeTableInternal>({ onSelectedRecordsChange={ enableSelection ? onSelectedRecordsChange : undefined } + isRecordSelectable={tableProps.isRecordSelectable} rowExpansion={rowExpansion} fetching={isFetching} noRecordsText={missingRecordsText} diff --git a/src/frontend/src/tables/bom/UsedInTable.tsx b/src/frontend/src/tables/bom/UsedInTable.tsx index 034b05c8b8..1d8c665640 100644 --- a/src/frontend/src/tables/bom/UsedInTable.tsx +++ b/src/frontend/src/tables/bom/UsedInTable.tsx @@ -9,7 +9,7 @@ import useTable from '@lib/hooks/UseTable'; import { ActionButton, RowEditAction, UserRoles } from '@lib/index'; import type { TableFilter } from '@lib/types/Filters'; import type { RowAction, TableColumn } from '@lib/types/Tables'; -import { IconReplace } from '@tabler/icons-react'; +import { IconExclamationCircle, IconReplace } from '@tabler/icons-react'; import { formatDecimal } from '../../defaults/formatters'; import { bomItemFields } from '../../forms/BomForms'; import { @@ -132,7 +132,10 @@ export function UsedInTable({ return [ RowEditAction({ - hidden: locked || !user.hasChangeRole(UserRoles.bom), + hidden: + locked || + record.sub_part != partId || + !user.hasChangeRole(UserRoles.bom), onClick: () => { setSelectedBomItem(record); editBomItem.open(); @@ -140,14 +143,19 @@ export function UsedInTable({ }) ]; }, - [user] + [user, partId] ); - const bulkReplaceParts = useMemo(() => {}, [table.selectedRecords]); + const bulkReplaceRecords: any[] = useMemo(() => { + // Only allow replacements of BomItem entries which point to this part + return table.selectedRecords.filter( + (record: any) => record.sub_part === partId + ); + }, [table.selectedRecords, partId]); const bulkReplace = useBulkEditApiFormModal({ url: ApiEndpoints.bom_list, - items: table.selectedIds, + items: bulkReplaceRecords.map((record: any) => record.pk), title: t`Replace Component`, submitText: t`Replace`, preFormContent: ( @@ -160,8 +168,18 @@ export function UsedInTable({ > {t`This action cannot be easily undone, so please ensure you have selected the correct assemblies.`} - {t`The selected assemblies will be updated with the new component.`} - {table.selectedRecords.map((record: any) => { + {bulkReplaceRecords.length ? ( + {t`The selected assemblies will be updated with the new component.`} + ) : ( + } + title={t`No valid items selected`} + > + {t`Please select one or more valid assemblies to replace the component.`} + + )} + {bulkReplaceRecords?.map((record: any) => { return ; })} @@ -212,7 +230,8 @@ export function UsedInTable({ modelType: ModelType.part, modelField: 'part', tableActions: tableActions, - tableFilters: tableFilters + tableFilters: tableFilters, + isRecordSelectable: (record: any) => record.sub_part === partId }} />