mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 11:36:44 +00:00
[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:
parent
ca3f5c096c
commit
58807d575c
@ -41,7 +41,8 @@ export function useSupplierPartFields() {
|
|||||||
},
|
},
|
||||||
supplier: {
|
supplier: {
|
||||||
filters: {
|
filters: {
|
||||||
active: true
|
active: true,
|
||||||
|
is_supplier: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
SKU: {
|
SKU: {
|
||||||
@ -69,7 +70,12 @@ export function useManufacturerPartFields() {
|
|||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const fields: ApiFormFieldSet = {
|
const fields: ApiFormFieldSet = {
|
||||||
part: {},
|
part: {},
|
||||||
manufacturer: {},
|
manufacturer: {
|
||||||
|
filters: {
|
||||||
|
active: true,
|
||||||
|
is_manufacturer: true
|
||||||
|
}
|
||||||
|
},
|
||||||
MPN: {},
|
MPN: {},
|
||||||
description: {},
|
description: {},
|
||||||
link: {}
|
link: {}
|
||||||
|
@ -167,7 +167,7 @@ export default function AdminCenter() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'location-types',
|
name: 'location-types',
|
||||||
label: t`Location types`,
|
label: t`Location Types`,
|
||||||
icon: <IconPackages />,
|
icon: <IconPackages />,
|
||||||
content: <LocationTypesTable />
|
content: <LocationTypesTable />
|
||||||
},
|
},
|
||||||
|
@ -167,12 +167,6 @@ export default function SystemSettings() {
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'categories',
|
|
||||||
label: t`Part Categories`,
|
|
||||||
icon: <IconSitemap />,
|
|
||||||
content: <PlaceholderPanel />
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'parts',
|
name: 'parts',
|
||||||
label: t`Parts`,
|
label: t`Parts`,
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
IconPaperclip
|
IconPaperclip
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import { useMemo } from '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 AdminButton from '../../components/buttons/AdminButton';
|
||||||
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
||||||
@ -29,8 +29,10 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
|||||||
import { ModelType } from '../../enums/ModelType';
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { UserRoles } from '../../enums/Roles';
|
import { UserRoles } from '../../enums/Roles';
|
||||||
import { useManufacturerPartFields } from '../../forms/CompanyForms';
|
import { useManufacturerPartFields } from '../../forms/CompanyForms';
|
||||||
|
import { getDetailUrl } from '../../functions/urls';
|
||||||
import {
|
import {
|
||||||
useCreateApiFormModal,
|
useCreateApiFormModal,
|
||||||
|
useDeleteApiFormModal,
|
||||||
useEditApiFormModal
|
useEditApiFormModal
|
||||||
} from '../../hooks/UseForm';
|
} from '../../hooks/UseForm';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
@ -43,6 +45,7 @@ import { SupplierPartTable } from '../../tables/purchasing/SupplierPartTable';
|
|||||||
export default function ManufacturerPartDetail() {
|
export default function ManufacturerPartDetail() {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const user = useUserState();
|
const user = useUserState();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
instance: manufacturerPart,
|
instance: manufacturerPart,
|
||||||
@ -218,6 +221,15 @@ export default function ManufacturerPartDetail() {
|
|||||||
modelType: ModelType.manufacturerpart
|
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(() => {
|
const manufacturerPartActions = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
<AdminButton
|
<AdminButton
|
||||||
@ -237,7 +249,8 @@ export default function ManufacturerPartDetail() {
|
|||||||
onClick: () => editManufacturerPart.open()
|
onClick: () => editManufacturerPart.open()
|
||||||
}),
|
}),
|
||||||
DeleteItemAction({
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{deleteManufacturerPart.modal}
|
||||||
|
{duplicateManufacturerPart.modal}
|
||||||
{editManufacturerPart.modal}
|
{editManufacturerPart.modal}
|
||||||
<InstanceDetail status={requestStatus} loading={instanceQuery.isFetching}>
|
<InstanceDetail status={requestStatus} loading={instanceQuery.isFetching}>
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
IconShoppingCart
|
IconShoppingCart
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import { ReactNode, useMemo } from '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 AdminButton from '../../components/buttons/AdminButton';
|
||||||
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
import { DetailsField, DetailsTable } from '../../components/details/Details';
|
||||||
@ -30,8 +30,10 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
|||||||
import { ModelType } from '../../enums/ModelType';
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { UserRoles } from '../../enums/Roles';
|
import { UserRoles } from '../../enums/Roles';
|
||||||
import { useSupplierPartFields } from '../../forms/CompanyForms';
|
import { useSupplierPartFields } from '../../forms/CompanyForms';
|
||||||
|
import { getDetailUrl } from '../../functions/urls';
|
||||||
import {
|
import {
|
||||||
useCreateApiFormModal,
|
useCreateApiFormModal,
|
||||||
|
useDeleteApiFormModal,
|
||||||
useEditApiFormModal
|
useEditApiFormModal
|
||||||
} from '../../hooks/UseForm';
|
} from '../../hooks/UseForm';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
@ -46,6 +48,8 @@ export default function SupplierPartDetail() {
|
|||||||
|
|
||||||
const user = useUserState();
|
const user = useUserState();
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
instance: supplierPart,
|
instance: supplierPart,
|
||||||
instanceQuery,
|
instanceQuery,
|
||||||
@ -271,10 +275,11 @@ export default function SupplierPartDetail() {
|
|||||||
}),
|
}),
|
||||||
EditItemAction({
|
EditItemAction({
|
||||||
hidden: !user.hasChangeRole(UserRoles.purchase_order),
|
hidden: !user.hasChangeRole(UserRoles.purchase_order),
|
||||||
onClick: () => editSuppliertPart.open()
|
onClick: () => editSupplierPart.open()
|
||||||
}),
|
}),
|
||||||
DeleteItemAction({
|
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 supplierPartFields = useSupplierPartFields();
|
||||||
|
|
||||||
const editSuppliertPart = useEditApiFormModal({
|
const editSupplierPart = useEditApiFormModal({
|
||||||
url: ApiEndpoints.supplier_part_list,
|
url: ApiEndpoints.supplier_part_list,
|
||||||
pk: supplierPart?.pk,
|
pk: supplierPart?.pk,
|
||||||
title: t`Edit Supplier Part`,
|
title: t`Edit Supplier Part`,
|
||||||
@ -291,6 +296,15 @@ export default function SupplierPartDetail() {
|
|||||||
onFormSuccess: refreshInstance
|
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({
|
const duplicateSupplierPart = useCreateApiFormModal({
|
||||||
url: ApiEndpoints.supplier_part_list,
|
url: ApiEndpoints.supplier_part_list,
|
||||||
title: t`Add Supplier Part`,
|
title: t`Add Supplier Part`,
|
||||||
@ -327,7 +341,9 @@ export default function SupplierPartDetail() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{editSuppliertPart.modal}
|
{deleteSupplierPart.modal}
|
||||||
|
{duplicateSupplierPart.modal}
|
||||||
|
{editSupplierPart.modal}
|
||||||
<InstanceDetail status={requestStatus} loading={instanceQuery.isFetching}>
|
<InstanceDetail status={requestStatus} loading={instanceQuery.isFetching}>
|
||||||
<Stack gap="xs">
|
<Stack gap="xs">
|
||||||
<PageDetail
|
<PageDetail
|
||||||
|
@ -45,6 +45,7 @@ import { cancelEvent } from '../functions/events';
|
|||||||
import { extractAvailableFields, mapFields } from '../functions/forms';
|
import { extractAvailableFields, mapFields } from '../functions/forms';
|
||||||
import { navigateToLink } from '../functions/navigation';
|
import { navigateToLink } from '../functions/navigation';
|
||||||
import { getDetailUrl } from '../functions/urls';
|
import { getDetailUrl } from '../functions/urls';
|
||||||
|
import { useDeleteApiFormModal } from '../hooks/UseForm';
|
||||||
import { TableState } from '../hooks/UseTable';
|
import { TableState } from '../hooks/UseTable';
|
||||||
import { useLocalState } from '../states/LocalState';
|
import { useLocalState } from '../states/LocalState';
|
||||||
import { TableColumn } from './Column';
|
import { TableColumn } from './Column';
|
||||||
@ -495,66 +496,34 @@ export function InvenTreeTable<T = any>({
|
|||||||
tableState.setRecords(data ?? []);
|
tableState.setRecords(data ?? []);
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
// Callback function to delete the selected records in the table
|
const deleteRecords = useDeleteApiFormModal({
|
||||||
const deleteSelectedRecords = useCallback((ids: number[]) => {
|
url: url,
|
||||||
if (ids.length == 0) {
|
title: t`Delete Selected Items`,
|
||||||
// Ignore if no records are selected
|
preFormContent: (
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
modals.openConfirmModal({
|
|
||||||
title: t`Delete selected records`,
|
|
||||||
children: (
|
|
||||||
<Alert
|
<Alert
|
||||||
color="red"
|
color="red"
|
||||||
title={t`Are you sure you want to delete the selected records?`}
|
title={t`Are you sure you want to delete the selected items?`}
|
||||||
>
|
>
|
||||||
{t`This action cannot be undone!`}
|
{t`This action cannot be undone!`}
|
||||||
</Alert>
|
</Alert>
|
||||||
),
|
),
|
||||||
labels: {
|
initialData: {
|
||||||
confirm: t`Delete`,
|
items: tableState.selectedIds
|
||||||
cancel: t`Cancel`
|
|
||||||
},
|
},
|
||||||
confirmProps: {
|
fields: {
|
||||||
color: 'red'
|
items: {
|
||||||
},
|
hidden: true
|
||||||
onConfirm: () => {
|
|
||||||
api
|
|
||||||
.delete(url, {
|
|
||||||
data: {
|
|
||||||
items: ids
|
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
.then((_response) => {
|
onFormSuccess: () => {
|
||||||
// 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();
|
tableState.clearSelectedRecords();
|
||||||
|
tableState.refreshTable();
|
||||||
|
|
||||||
if (props.afterBulkDelete) {
|
if (props.afterBulkDelete) {
|
||||||
props.afterBulkDelete();
|
props.afterBulkDelete();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Callback when a row is clicked
|
// Callback when a row is clicked
|
||||||
const handleRowClick = useCallback(
|
const handleRowClick = useCallback(
|
||||||
@ -587,6 +556,7 @@ export function InvenTreeTable<T = any>({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{deleteRecords.modal}
|
||||||
{tableProps.enableFilters && (filters.length ?? 0) > 0 && (
|
{tableProps.enableFilters && (filters.length ?? 0) > 0 && (
|
||||||
<Boundary label="table-filter-drawer">
|
<Boundary label="table-filter-drawer">
|
||||||
<FilterSelectDrawer
|
<FilterSelectDrawer
|
||||||
@ -623,7 +593,9 @@ export function InvenTreeTable<T = any>({
|
|||||||
icon={<IconTrash />}
|
icon={<IconTrash />}
|
||||||
color="red"
|
color="red"
|
||||||
tooltip={t`Delete selected records`}
|
tooltip={t`Delete selected records`}
|
||||||
onClick={() => deleteSelectedRecords(tableState.selectedIds)}
|
onClick={() => {
|
||||||
|
deleteRecords.open();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{tableProps.tableActions?.map((group, idx) => (
|
{tableProps.tableActions?.map((group, idx) => (
|
||||||
|
@ -28,7 +28,6 @@ test('PUI - Admin', async ({ page }) => {
|
|||||||
await page.getByRole('tab', { name: 'Pricing' }).click();
|
await page.getByRole('tab', { name: 'Pricing' }).click();
|
||||||
await page.getByRole('tab', { name: 'Labels' }).click();
|
await page.getByRole('tab', { name: 'Labels' }).click();
|
||||||
await page.getByRole('tab', { name: 'Reporting' }).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: 'Stocktake' }).click();
|
||||||
await page.getByRole('tab', { name: 'Build Orders' }).click();
|
await page.getByRole('tab', { name: 'Build Orders' }).click();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user