diff --git a/src/frontend/src/forms/CompanyForms.tsx b/src/frontend/src/forms/CompanyForms.tsx index 2f71c89672..f56ce2aa8c 100644 --- a/src/frontend/src/forms/CompanyForms.tsx +++ b/src/frontend/src/forms/CompanyForms.tsx @@ -41,7 +41,8 @@ export function useSupplierPartFields() { }, supplier: { filters: { - active: true + active: true, + is_supplier: true } }, SKU: { @@ -69,7 +70,12 @@ export function useManufacturerPartFields() { return useMemo(() => { const fields: ApiFormFieldSet = { part: {}, - manufacturer: {}, + manufacturer: { + filters: { + active: true, + is_manufacturer: true + } + }, MPN: {}, description: {}, link: {} diff --git a/src/frontend/src/pages/Index/Settings/AdminCenter/Index.tsx b/src/frontend/src/pages/Index/Settings/AdminCenter/Index.tsx index fc0ad6ad7b..91bc1d8982 100644 --- a/src/frontend/src/pages/Index/Settings/AdminCenter/Index.tsx +++ b/src/frontend/src/pages/Index/Settings/AdminCenter/Index.tsx @@ -167,7 +167,7 @@ export default function AdminCenter() { }, { name: 'location-types', - label: t`Location types`, + label: t`Location Types`, icon: , content: }, diff --git a/src/frontend/src/pages/Index/Settings/SystemSettings.tsx b/src/frontend/src/pages/Index/Settings/SystemSettings.tsx index 59bec1ab13..29c6366ebd 100644 --- a/src/frontend/src/pages/Index/Settings/SystemSettings.tsx +++ b/src/frontend/src/pages/Index/Settings/SystemSettings.tsx @@ -167,12 +167,6 @@ export default function SystemSettings() { /> ) }, - { - name: 'categories', - label: t`Part Categories`, - icon: , - content: - }, { name: 'parts', label: t`Parts`, diff --git a/src/frontend/src/pages/company/ManufacturerPartDetail.tsx b/src/frontend/src/pages/company/ManufacturerPartDetail.tsx index 5e5e5f2ba9..bb97ac4300 100644 --- a/src/frontend/src/pages/company/ManufacturerPartDetail.tsx +++ b/src/frontend/src/pages/company/ManufacturerPartDetail.tsx @@ -9,7 +9,7 @@ import { IconPaperclip } from '@tabler/icons-react'; import { useMemo } from 'react'; -import { useParams } from 'react-router-dom'; +import { useNavigate, useParams } from 'react-router-dom'; import AdminButton from '../../components/buttons/AdminButton'; import { DetailsField, DetailsTable } from '../../components/details/Details'; @@ -29,8 +29,10 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { ModelType } from '../../enums/ModelType'; import { UserRoles } from '../../enums/Roles'; import { useManufacturerPartFields } from '../../forms/CompanyForms'; +import { getDetailUrl } from '../../functions/urls'; import { useCreateApiFormModal, + useDeleteApiFormModal, useEditApiFormModal } from '../../hooks/UseForm'; import { useInstance } from '../../hooks/UseInstance'; @@ -43,6 +45,7 @@ import { SupplierPartTable } from '../../tables/purchasing/SupplierPartTable'; export default function ManufacturerPartDetail() { const { id } = useParams(); const user = useUserState(); + const navigate = useNavigate(); const { instance: manufacturerPart, @@ -218,6 +221,15 @@ export default function ManufacturerPartDetail() { modelType: ModelType.manufacturerpart }); + const deleteManufacturerPart = useDeleteApiFormModal({ + url: ApiEndpoints.manufacturer_part_list, + pk: manufacturerPart?.pk, + title: t`Delete Manufacturer Part`, + onFormSuccess: () => { + navigate(getDetailUrl(ModelType.part, manufacturerPart.part)); + } + }); + const manufacturerPartActions = useMemo(() => { return [ editManufacturerPart.open() }), DeleteItemAction({ - hidden: !user.hasDeleteRole(UserRoles.purchase_order) + hidden: !user.hasDeleteRole(UserRoles.purchase_order), + onClick: () => deleteManufacturerPart.open() }) ]} /> @@ -259,6 +272,8 @@ export default function ManufacturerPartDetail() { return ( <> + {deleteManufacturerPart.modal} + {duplicateManufacturerPart.modal} {editManufacturerPart.modal} diff --git a/src/frontend/src/pages/company/SupplierPartDetail.tsx b/src/frontend/src/pages/company/SupplierPartDetail.tsx index 1746c4bb9b..21eb6b9cee 100644 --- a/src/frontend/src/pages/company/SupplierPartDetail.tsx +++ b/src/frontend/src/pages/company/SupplierPartDetail.tsx @@ -9,7 +9,7 @@ import { IconShoppingCart } from '@tabler/icons-react'; import { ReactNode, useMemo } from 'react'; -import { useParams } from 'react-router-dom'; +import { useNavigate, useParams } from 'react-router-dom'; import AdminButton from '../../components/buttons/AdminButton'; import { DetailsField, DetailsTable } from '../../components/details/Details'; @@ -30,8 +30,10 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { ModelType } from '../../enums/ModelType'; import { UserRoles } from '../../enums/Roles'; import { useSupplierPartFields } from '../../forms/CompanyForms'; +import { getDetailUrl } from '../../functions/urls'; import { useCreateApiFormModal, + useDeleteApiFormModal, useEditApiFormModal } from '../../hooks/UseForm'; import { useInstance } from '../../hooks/UseInstance'; @@ -46,6 +48,8 @@ export default function SupplierPartDetail() { const user = useUserState(); + const navigate = useNavigate(); + const { instance: supplierPart, instanceQuery, @@ -271,10 +275,11 @@ export default function SupplierPartDetail() { }), EditItemAction({ hidden: !user.hasChangeRole(UserRoles.purchase_order), - onClick: () => editSuppliertPart.open() + onClick: () => editSupplierPart.open() }), DeleteItemAction({ - hidden: !user.hasDeleteRole(UserRoles.purchase_order) + hidden: !user.hasDeleteRole(UserRoles.purchase_order), + onClick: () => deleteSupplierPart.open() }) ]} /> @@ -283,7 +288,7 @@ export default function SupplierPartDetail() { const supplierPartFields = useSupplierPartFields(); - const editSuppliertPart = useEditApiFormModal({ + const editSupplierPart = useEditApiFormModal({ url: ApiEndpoints.supplier_part_list, pk: supplierPart?.pk, title: t`Edit Supplier Part`, @@ -291,6 +296,15 @@ export default function SupplierPartDetail() { onFormSuccess: refreshInstance }); + const deleteSupplierPart = useDeleteApiFormModal({ + url: ApiEndpoints.supplier_part_list, + pk: supplierPart?.pk, + title: t`Delete Supplier Part`, + onFormSuccess: () => { + navigate(getDetailUrl(ModelType.part, supplierPart.part)); + } + }); + const duplicateSupplierPart = useCreateApiFormModal({ url: ApiEndpoints.supplier_part_list, title: t`Add Supplier Part`, @@ -327,7 +341,9 @@ export default function SupplierPartDetail() { return ( <> - {editSuppliertPart.modal} + {deleteSupplierPart.modal} + {duplicateSupplierPart.modal} + {editSupplierPart.modal} ({ tableState.setRecords(data ?? []); }, [data]); - // Callback function to delete the selected records in the table - const deleteSelectedRecords = useCallback((ids: number[]) => { - if (ids.length == 0) { - // Ignore if no records are selected - return; - } - - modals.openConfirmModal({ - title: t`Delete selected records`, - children: ( - - {t`This action cannot be undone!`} - - ), - labels: { - confirm: t`Delete`, - cancel: t`Cancel` - }, - confirmProps: { - color: 'red' - }, - onConfirm: () => { - api - .delete(url, { - data: { - items: ids - } - }) - .then((_response) => { - // Refresh the table - refetch(); - - // Show notification - showNotification({ - title: t`Deleted records`, - message: t`Records were deleted successfully`, - color: 'green' - }); - }) - .catch((_error) => { - console.warn(`Bulk delete operation failed at ${url}`); - - showNotification({ - title: t`Error`, - message: t`Failed to delete records`, - color: 'red' - }); - }) - .finally(() => { - tableState.clearSelectedRecords(); - if (props.afterBulkDelete) { - props.afterBulkDelete(); - } - }); + const deleteRecords = useDeleteApiFormModal({ + url: url, + title: t`Delete Selected Items`, + preFormContent: ( + + {t`This action cannot be undone!`} + + ), + initialData: { + items: tableState.selectedIds + }, + fields: { + items: { + hidden: true } - }); - }, []); + }, + onFormSuccess: () => { + tableState.clearSelectedRecords(); + tableState.refreshTable(); + + if (props.afterBulkDelete) { + props.afterBulkDelete(); + } + } + }); // Callback when a row is clicked const handleRowClick = useCallback( @@ -587,6 +556,7 @@ export function InvenTreeTable({ return ( <> + {deleteRecords.modal} {tableProps.enableFilters && (filters.length ?? 0) > 0 && ( ({ icon={} color="red" tooltip={t`Delete selected records`} - onClick={() => deleteSelectedRecords(tableState.selectedIds)} + onClick={() => { + deleteRecords.open(); + }} /> )} {tableProps.tableActions?.map((group, idx) => ( diff --git a/src/frontend/tests/pui_settings.spec.ts b/src/frontend/tests/pui_settings.spec.ts index b0962dcd36..0b6399f312 100644 --- a/src/frontend/tests/pui_settings.spec.ts +++ b/src/frontend/tests/pui_settings.spec.ts @@ -28,7 +28,6 @@ test('PUI - Admin', async ({ page }) => { await page.getByRole('tab', { name: 'Pricing' }).click(); await page.getByRole('tab', { name: 'Labels' }).click(); await page.getByRole('tab', { name: 'Reporting' }).click(); - await page.getByRole('tab', { name: 'Part Categories' }).click(); await page.getByRole('tab', { name: 'Stocktake' }).click(); await page.getByRole('tab', { name: 'Build Orders' }).click();