[PUI] Tweaks (#7673)

* Fix typo

* Filter fix for "supplier" field

* Fix supplier part actions

* Fix actions for manufacturer part

* Improve bulk-delete modal

- Prevents duplicate copies of modal being displayed

* Cleanup admin / settings panels

* Adjust playwright test
This commit is contained in:
Oliver 2024-07-18 10:38:41 +10:00 committed by GitHub
parent ca3f5c096c
commit 58807d575c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 79 additions and 77 deletions

View File

@ -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: {}

View File

@ -167,7 +167,7 @@ export default function AdminCenter() {
},
{
name: 'location-types',
label: t`Location types`,
label: t`Location Types`,
icon: <IconPackages />,
content: <LocationTypesTable />
},

View File

@ -167,12 +167,6 @@ export default function SystemSettings() {
/>
)
},
{
name: 'categories',
label: t`Part Categories`,
icon: <IconSitemap />,
content: <PlaceholderPanel />
},
{
name: 'parts',
label: t`Parts`,

View File

@ -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 [
<AdminButton
@ -237,7 +249,8 @@ export default function ManufacturerPartDetail() {
onClick: () => 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}
<InstanceDetail status={requestStatus} loading={instanceQuery.isFetching}>
<Stack gap="xs">

View File

@ -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}
<InstanceDetail status={requestStatus} loading={instanceQuery.isFetching}>
<Stack gap="xs">
<PageDetail

View File

@ -45,6 +45,7 @@ import { cancelEvent } from '../functions/events';
import { extractAvailableFields, mapFields } from '../functions/forms';
import { navigateToLink } from '../functions/navigation';
import { getDetailUrl } from '../functions/urls';
import { useDeleteApiFormModal } from '../hooks/UseForm';
import { TableState } from '../hooks/UseTable';
import { useLocalState } from '../states/LocalState';
import { TableColumn } from './Column';
@ -495,66 +496,34 @@ export function InvenTreeTable<T = any>({
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: (
<Alert
color="red"
title={t`Are you sure you want to delete the selected records?`}
>
{t`This action cannot be undone!`}
</Alert>
),
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: (
<Alert
color="red"
title={t`Are you sure you want to delete the selected items?`}
>
{t`This action cannot be undone!`}
</Alert>
),
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<T = any>({
return (
<>
{deleteRecords.modal}
{tableProps.enableFilters && (filters.length ?? 0) > 0 && (
<Boundary label="table-filter-drawer">
<FilterSelectDrawer
@ -623,7 +593,9 @@ export function InvenTreeTable<T = any>({
icon={<IconTrash />}
color="red"
tooltip={t`Delete selected records`}
onClick={() => deleteSelectedRecords(tableState.selectedIds)}
onClick={() => {
deleteRecords.open();
}}
/>
)}
{tableProps.tableActions?.map((group, idx) => (

View File

@ -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();