mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
[PUI] Tweaks (#7843)
* Part test template table updates * Allow export from part test template table * Allow actions for build lines * Updates to BuildLine table
This commit is contained in:
parent
21f623eea8
commit
d68d52ba88
@ -675,7 +675,7 @@ export default function PartDetail() {
|
|||||||
icon: <IconTestPipe />,
|
icon: <IconTestPipe />,
|
||||||
hidden: !part.trackable,
|
hidden: !part.trackable,
|
||||||
content: part?.pk ? (
|
content: part?.pk ? (
|
||||||
<PartTestTemplateTable partId={part?.pk} />
|
<PartTestTemplateTable partId={part?.pk} partLocked={part.locked} />
|
||||||
) : (
|
) : (
|
||||||
<Skeleton />
|
<Skeleton />
|
||||||
)
|
)
|
||||||
|
@ -24,9 +24,11 @@ import { TableHoverCard } from '../TableHoverCard';
|
|||||||
|
|
||||||
export default function BuildLineTable({
|
export default function BuildLineTable({
|
||||||
buildId,
|
buildId,
|
||||||
|
outputId,
|
||||||
params = {}
|
params = {}
|
||||||
}: {
|
}: {
|
||||||
buildId: number;
|
buildId: number;
|
||||||
|
outputId?: number;
|
||||||
params?: any;
|
params?: any;
|
||||||
}) {
|
}) {
|
||||||
const table = useTable('buildline');
|
const table = useTable('buildline');
|
||||||
@ -237,35 +239,42 @@ export default function BuildLineTable({
|
|||||||
|
|
||||||
const rowActions = useCallback(
|
const rowActions = useCallback(
|
||||||
(record: any) => {
|
(record: any) => {
|
||||||
let part = record.part_detail;
|
let part = record.part_detail ?? {};
|
||||||
|
|
||||||
// Consumable items have no appropriate actions
|
// Consumable items have no appropriate actions
|
||||||
if (record?.bom_item_detail?.consumable) {
|
if (record?.bom_item_detail?.consumable) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tracked items must be allocated to a particular output
|
const hasOutput = !!outputId;
|
||||||
if (record?.part_detail?.trackable) {
|
|
||||||
return [];
|
// Can allocate
|
||||||
}
|
let canAllocate =
|
||||||
|
user.hasChangeRole(UserRoles.build) &&
|
||||||
|
record.allocated < record.quantity &&
|
||||||
|
record.trackable == hasOutput;
|
||||||
|
|
||||||
|
let canOrder =
|
||||||
|
user.hasAddRole(UserRoles.purchase_order) && part.purchaseable;
|
||||||
|
let canBuild = user.hasAddRole(UserRoles.build) && part.assembly;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
icon: <IconArrowRight />,
|
icon: <IconArrowRight />,
|
||||||
title: t`Allocate Stock`,
|
title: t`Allocate Stock`,
|
||||||
hidden: record.allocated >= record.quantity,
|
hidden: !canAllocate,
|
||||||
color: 'green'
|
color: 'green'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <IconShoppingCart />,
|
icon: <IconShoppingCart />,
|
||||||
title: t`Order Stock`,
|
title: t`Order Stock`,
|
||||||
hidden: !part?.purchaseable,
|
hidden: !canOrder,
|
||||||
color: 'blue'
|
color: 'blue'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <IconTool />,
|
icon: <IconTool />,
|
||||||
title: t`Build Stock`,
|
title: t`Build Stock`,
|
||||||
hidden: !part?.assembly || !user.hasAddRole(UserRoles.build),
|
hidden: !canBuild,
|
||||||
color: 'blue',
|
color: 'blue',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
setInitialData({
|
setInitialData({
|
||||||
@ -278,7 +287,7 @@ export default function BuildLineTable({
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
[user]
|
[user, outputId]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Trans, t } from '@lingui/macro';
|
import { Trans, t } from '@lingui/macro';
|
||||||
import { Alert, Badge, Text } from '@mantine/core';
|
import { Alert, Badge, Stack, Text } from '@mantine/core';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { IconArrowRight, IconLock } from '@tabler/icons-react';
|
||||||
|
import { ReactNode, useCallback, useMemo, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { AddItemButton } from '../../components/buttons/AddItemButton';
|
import { AddItemButton } from '../../components/buttons/AddItemButton';
|
||||||
@ -22,8 +23,15 @@ import { BooleanColumn, DescriptionColumn } from '../ColumnRenderers';
|
|||||||
import { TableFilter } from '../Filter';
|
import { TableFilter } from '../Filter';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
import { RowDeleteAction, RowEditAction } from '../RowActions';
|
import { RowDeleteAction, RowEditAction } from '../RowActions';
|
||||||
|
import { TableHoverCard } from '../TableHoverCard';
|
||||||
|
|
||||||
export default function PartTestTemplateTable({ partId }: { partId: number }) {
|
export default function PartTestTemplateTable({
|
||||||
|
partId,
|
||||||
|
partLocked
|
||||||
|
}: {
|
||||||
|
partId: number;
|
||||||
|
partLocked?: boolean;
|
||||||
|
}) {
|
||||||
const table = useTable('part-test-template');
|
const table = useTable('part-test-template');
|
||||||
const user = useUserState();
|
const user = useUserState();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -35,13 +43,27 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) {
|
|||||||
switchable: false,
|
switchable: false,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
render: (record: any) => {
|
render: (record: any) => {
|
||||||
|
let extra: ReactNode[] = [];
|
||||||
|
|
||||||
|
if (record.part != partId) {
|
||||||
|
extra.push(
|
||||||
|
<Text size="sm">{t`Test is defined for a parent template part`}</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text
|
<TableHoverCard
|
||||||
fw={record.required && 700}
|
value={
|
||||||
c={record.enabled ? undefined : 'red'}
|
<Text
|
||||||
>
|
fw={record.required && 700}
|
||||||
{record.test_name}
|
c={record.enabled ? undefined : 'red'}
|
||||||
</Text>
|
>
|
||||||
|
{record.test_name}
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
title={t`Template Details`}
|
||||||
|
extra={extra}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -175,20 +197,28 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) {
|
|||||||
const can_delete = user.hasDeleteRole(UserRoles.part);
|
const can_delete = user.hasDeleteRole(UserRoles.part);
|
||||||
|
|
||||||
if (record.part != partId) {
|
if (record.part != partId) {
|
||||||
// No actions, as this test is defined for a parent part
|
// This test is defined for a parent part
|
||||||
return [];
|
return [
|
||||||
|
{
|
||||||
|
icon: <IconArrowRight />,
|
||||||
|
title: t`View Parent Part`,
|
||||||
|
onClick: () => {
|
||||||
|
navigate(getDetailUrl(ModelType.part, record.part));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
RowEditAction({
|
RowEditAction({
|
||||||
hidden: !can_edit,
|
hidden: partLocked || !can_edit,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
setSelectedTest(record.pk);
|
setSelectedTest(record.pk);
|
||||||
editTestTemplate.open();
|
editTestTemplate.open();
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
RowDeleteAction({
|
RowDeleteAction({
|
||||||
hidden: !can_delete,
|
hidden: partLocked || !can_delete,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
setSelectedTest(record.pk);
|
setSelectedTest(record.pk);
|
||||||
deleteTestTemplate.open();
|
deleteTestTemplate.open();
|
||||||
@ -196,7 +226,7 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) {
|
|||||||
})
|
})
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
[user, partId]
|
[user, partId, partLocked]
|
||||||
);
|
);
|
||||||
|
|
||||||
const tableActions = useMemo(() => {
|
const tableActions = useMemo(() => {
|
||||||
@ -206,36 +236,49 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) {
|
|||||||
<AddItemButton
|
<AddItemButton
|
||||||
tooltip={t`Add Test Template`}
|
tooltip={t`Add Test Template`}
|
||||||
onClick={() => newTestTemplate.open()}
|
onClick={() => newTestTemplate.open()}
|
||||||
hidden={!can_add}
|
hidden={partLocked || !can_add}
|
||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
}, [user]);
|
}, [user, partLocked]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{newTestTemplate.modal}
|
{newTestTemplate.modal}
|
||||||
{editTestTemplate.modal}
|
{editTestTemplate.modal}
|
||||||
{deleteTestTemplate.modal}
|
{deleteTestTemplate.modal}
|
||||||
<InvenTreeTable
|
<Stack gap="xs">
|
||||||
url={apiUrl(ApiEndpoints.part_test_template_list)}
|
{partLocked && (
|
||||||
tableState={table}
|
<Alert
|
||||||
columns={tableColumns}
|
title={t`Part is Locked`}
|
||||||
props={{
|
color="orange"
|
||||||
params: {
|
icon={<IconLock />}
|
||||||
part: partId,
|
p="xs"
|
||||||
part_detail: true
|
>
|
||||||
},
|
<Text>{t`Part templates cannot be edited, as the part is locked`}</Text>
|
||||||
tableFilters: tableFilters,
|
</Alert>
|
||||||
tableActions: tableActions,
|
)}
|
||||||
rowActions: rowActions,
|
<InvenTreeTable
|
||||||
onRowClick: (row) => {
|
url={apiUrl(ApiEndpoints.part_test_template_list)}
|
||||||
if (row.part && row.part != partId) {
|
tableState={table}
|
||||||
// This test is defined for a different part
|
columns={tableColumns}
|
||||||
navigate(getDetailUrl(ModelType.part, row.part));
|
props={{
|
||||||
|
params: {
|
||||||
|
part: partId,
|
||||||
|
part_detail: true
|
||||||
|
},
|
||||||
|
tableFilters: tableFilters,
|
||||||
|
tableActions: tableActions,
|
||||||
|
enableDownload: true,
|
||||||
|
rowActions: rowActions,
|
||||||
|
onRowClick: (row) => {
|
||||||
|
if (row.part && row.part != partId) {
|
||||||
|
// This test is defined for a different part
|
||||||
|
navigate(getDetailUrl(ModelType.part, row.part));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
</Stack>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user