mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
React updates (#5826)
* Add more panels to StockItem page * Add some placeholder actions for StockItem page * edit stock item * Add info hover card to stocktable * update extra info for part table * Add extra columns to PurchaseOrder table * Fix unused import
This commit is contained in:
parent
2908ad0721
commit
e18b6d38ef
@ -1,4 +1,6 @@
|
|||||||
|
import { t } from '@lingui/macro';
|
||||||
import { ActionIcon, Menu, Tooltip } from '@mantine/core';
|
import { ActionIcon, Menu, Tooltip } from '@mantine/core';
|
||||||
|
import { IconQrcode } from '@tabler/icons-react';
|
||||||
import { ReactNode, useMemo } from 'react';
|
import { ReactNode, useMemo } from 'react';
|
||||||
|
|
||||||
import { notYetImplemented } from '../../functions/notifications';
|
import { notYetImplemented } from '../../functions/notifications';
|
||||||
@ -32,7 +34,7 @@ export function ActionDropdown({
|
|||||||
return hasActions ? (
|
return hasActions ? (
|
||||||
<Menu position="bottom-end">
|
<Menu position="bottom-end">
|
||||||
<Menu.Target>
|
<Menu.Target>
|
||||||
<Tooltip label={tooltip}>
|
<Tooltip label={tooltip} hidden={!tooltip}>
|
||||||
<ActionIcon size="lg" radius="sm" variant="outline">
|
<ActionIcon size="lg" radius="sm" variant="outline">
|
||||||
{icon}
|
{icon}
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
@ -63,3 +65,19 @@ export function ActionDropdown({
|
|||||||
</Menu>
|
</Menu>
|
||||||
) : null;
|
) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dropdown menu for barcode actions
|
||||||
|
export function BarcodeActionDropdown({
|
||||||
|
actions
|
||||||
|
}: {
|
||||||
|
actions: ActionDropdownItem[];
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<ActionDropdown
|
||||||
|
key="barcode"
|
||||||
|
tooltip={t`Barcode Actions`}
|
||||||
|
icon={<IconQrcode />}
|
||||||
|
actions={actions}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -75,16 +75,19 @@ function partTableColumns(): TableColumn[] {
|
|||||||
let extra: ReactNode[] = [];
|
let extra: ReactNode[] = [];
|
||||||
|
|
||||||
let stock = record?.total_in_stock ?? 0;
|
let stock = record?.total_in_stock ?? 0;
|
||||||
|
let allocated =
|
||||||
|
(record?.allocated_to_build_orders ?? 0) +
|
||||||
|
(record?.allocated_to_sales_orders ?? 0);
|
||||||
|
let available = Math.max(0, stock - allocated);
|
||||||
|
let min_stock = record?.minimum_stock ?? 0;
|
||||||
|
|
||||||
let text = String(stock);
|
let text = String(stock);
|
||||||
|
|
||||||
let color: string | undefined = undefined;
|
let color: string | undefined = undefined;
|
||||||
|
|
||||||
if (record.minimum_stock > stock) {
|
if (min_stock > stock) {
|
||||||
extra.push(
|
extra.push(
|
||||||
<Text color="orange">
|
<Text color="orange">{t`Minimum stock` + `: ${min_stock}`}</Text>
|
||||||
{t`Minimum stock` + `: ${record.minimum_stock}`}
|
|
||||||
</Text>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
color = 'orange';
|
color = 'orange';
|
||||||
@ -116,11 +119,19 @@ function partTableColumns(): TableColumn[] {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add extra information on stock "deman"
|
if (available != stock) {
|
||||||
|
extra.push(<Text>{t`Available` + `: ${available}`}</Text>);
|
||||||
|
}
|
||||||
|
|
||||||
if (stock == 0) {
|
// TODO: Add extra information on stock "demand"
|
||||||
|
|
||||||
|
if (stock <= 0) {
|
||||||
color = 'red';
|
color = 'red';
|
||||||
text = t`No stock`;
|
text = t`No stock`;
|
||||||
|
} else if (available <= 0) {
|
||||||
|
color = 'orange';
|
||||||
|
} else if (available < min_stock) {
|
||||||
|
color = 'yellow';
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -129,7 +140,7 @@ function partTableColumns(): TableColumn[] {
|
|||||||
<Group spacing="xs" position="left">
|
<Group spacing="xs" position="left">
|
||||||
<Text color={color}>{text}</Text>
|
<Text color={color}>{text}</Text>
|
||||||
{record.units && (
|
{record.units && (
|
||||||
<Text size="xs" color="color">
|
<Text size="xs" color={color}>
|
||||||
[{record.units}]
|
[{record.units}]
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
@ -61,7 +61,7 @@ export function PurchaseOrderTable({ params }: { params?: any }) {
|
|||||||
accessor: 'project_code',
|
accessor: 'project_code',
|
||||||
title: t`Project Code`,
|
title: t`Project Code`,
|
||||||
switchable: true
|
switchable: true
|
||||||
// TODO: Custom formatter
|
// TODO: Custom project code formatter
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessor: 'status',
|
accessor: 'status',
|
||||||
@ -78,22 +78,34 @@ export function PurchaseOrderTable({ params }: { params?: any }) {
|
|||||||
accessor: 'creation_date',
|
accessor: 'creation_date',
|
||||||
title: t`Created`,
|
title: t`Created`,
|
||||||
switchable: true
|
switchable: true
|
||||||
// TODO: Custom formatter
|
// TODO: Custom date formatter
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessor: 'target_date',
|
accessor: 'target_date',
|
||||||
title: t`Target Date`,
|
title: t`Target Date`,
|
||||||
switchable: true
|
switchable: true
|
||||||
// TODO: Custom formatter
|
// TODO: Custom date formatter
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessor: 'line_items',
|
accessor: 'line_items',
|
||||||
title: t`Line Items`,
|
title: t`Line Items`,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
switchable: true
|
switchable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'total_price',
|
||||||
|
title: t`Total Price`,
|
||||||
|
sortable: true,
|
||||||
|
switchable: true
|
||||||
|
// TODO: Custom money formatter
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'responsible',
|
||||||
|
title: t`Responsible`,
|
||||||
|
sortable: true,
|
||||||
|
switchable: true
|
||||||
|
// TODO: custom 'owner' formatter
|
||||||
}
|
}
|
||||||
// TODO: total_price
|
|
||||||
// TODO: responsible
|
|
||||||
];
|
];
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Group, Text } from '@mantine/core';
|
import { Group, Stack, Text } from '@mantine/core';
|
||||||
import { useMemo } from 'react';
|
import { ReactNode, useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { notYetImplemented } from '../../../functions/notifications';
|
import { notYetImplemented } from '../../../functions/notifications';
|
||||||
@ -12,6 +12,7 @@ import { TableStatusRenderer } from '../../renderers/StatusRenderer';
|
|||||||
import { TableColumn } from '../Column';
|
import { TableColumn } from '../Column';
|
||||||
import { TableFilter } from '../Filter';
|
import { TableFilter } from '../Filter';
|
||||||
import { RowAction } from '../RowActions';
|
import { RowAction } from '../RowActions';
|
||||||
|
import { TableHoverCard } from '../TableHoverCard';
|
||||||
import { InvenTreeTable } from './../InvenTreeTable';
|
import { InvenTreeTable } from './../InvenTreeTable';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,8 +47,109 @@ function stockItemTableColumns(): TableColumn[] {
|
|||||||
{
|
{
|
||||||
accessor: 'quantity',
|
accessor: 'quantity',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
title: t`Stock`
|
title: t`Stock`,
|
||||||
// TODO: Custom renderer for stock quantity
|
render: (record) => {
|
||||||
|
// TODO: Push this out into a custom renderer
|
||||||
|
let quantity = record?.quantity ?? 0;
|
||||||
|
let allocated = record?.allocated ?? 0;
|
||||||
|
let available = quantity - allocated;
|
||||||
|
let text = quantity;
|
||||||
|
let part = record?.part_detail ?? {};
|
||||||
|
let extra: ReactNode[] = [];
|
||||||
|
let color = undefined;
|
||||||
|
|
||||||
|
if (record.serial && quantity == 1) {
|
||||||
|
text = `# ${record.serial}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.is_building) {
|
||||||
|
color = 'blue';
|
||||||
|
extra.push(
|
||||||
|
<Text size="sm">{t`This stock item is in production`}</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.sales_order) {
|
||||||
|
extra.push(
|
||||||
|
<Text size="sm">{t`This stock item has been assigned to a sales order`}</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.customer) {
|
||||||
|
extra.push(
|
||||||
|
<Text size="sm">{t`This stock item has been assigned to a customer`}</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.belongs_to) {
|
||||||
|
extra.push(
|
||||||
|
<Text size="sm">{t`This stock item is installed in another stock item`}</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.consumed_by) {
|
||||||
|
extra.push(
|
||||||
|
<Text size="sm">{t`This stock item has been consumed by a build order`}</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.expired) {
|
||||||
|
extra.push(<Text size="sm">{t`This stock item has expired`}</Text>);
|
||||||
|
} else if (record.stale) {
|
||||||
|
extra.push(<Text size="sm">{t`This stock item is stale`}</Text>);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allocated > 0) {
|
||||||
|
if (allocated >= quantity) {
|
||||||
|
color = 'orange';
|
||||||
|
extra.push(
|
||||||
|
<Text size="sm">{t`This stock item is fully allocated`}</Text>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
extra.push(
|
||||||
|
<Text size="sm">{t`This stock item is partially allocated`}</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (available != quantity) {
|
||||||
|
if (available > 0) {
|
||||||
|
extra.push(
|
||||||
|
<Text size="sm" color="orange">
|
||||||
|
{t`Available` + `: ${available}`}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
extra.push(
|
||||||
|
<Text size="sm" color="red">{t`No stock available`}</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quantity <= 0) {
|
||||||
|
color = 'red';
|
||||||
|
extra.push(
|
||||||
|
<Text size="sm">{t`This stock item has been depleted`}</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableHoverCard
|
||||||
|
value={
|
||||||
|
<Group spacing="xs" position="left">
|
||||||
|
<Text color={color}>{text}</Text>
|
||||||
|
{part.units && (
|
||||||
|
<Text size="xs" color={color}>
|
||||||
|
[{part.units}]
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
}
|
||||||
|
title={t`Stock Information`}
|
||||||
|
extra={extra.length > 0 && <Stack spacing="xs">{extra}</Stack>}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessor: 'status',
|
accessor: 'status',
|
||||||
|
@ -94,8 +94,8 @@ export function editPart({
|
|||||||
title: t`Edit Part`,
|
title: t`Edit Part`,
|
||||||
url: ApiPaths.part_list,
|
url: ApiPaths.part_list,
|
||||||
pk: part_id,
|
pk: part_id,
|
||||||
successMessage: t`Part updated`,
|
|
||||||
fields: partFields({ editing: true }),
|
fields: partFields({ editing: true }),
|
||||||
|
successMessage: t`Part updated`,
|
||||||
onFormSuccess: callback
|
onFormSuccess: callback
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,16 @@ import { openCreateApiForm, openEditApiForm } from '../forms';
|
|||||||
/**
|
/**
|
||||||
* Construct a set of fields for creating / editing a StockItem instance
|
* Construct a set of fields for creating / editing a StockItem instance
|
||||||
*/
|
*/
|
||||||
export function stockFields({}: {}): ApiFormFieldSet {
|
export function stockFields({
|
||||||
|
create = false
|
||||||
|
}: {
|
||||||
|
create: boolean;
|
||||||
|
}): ApiFormFieldSet {
|
||||||
let fields: ApiFormFieldSet = {
|
let fields: ApiFormFieldSet = {
|
||||||
part: {
|
part: {
|
||||||
|
hidden: !create,
|
||||||
onValueChange: (change: ApiFormChangeCallback) => {
|
onValueChange: (change: ApiFormChangeCallback) => {
|
||||||
// TODO: implement remaining functionality from old stock.py
|
// TODO: implement remaining functionality from old stock.py
|
||||||
console.log('part changed: ', change.value);
|
|
||||||
|
|
||||||
// Clear the 'supplier_part' field if the part is changed
|
// Clear the 'supplier_part' field if the part is changed
|
||||||
change.form.setValues({
|
change.form.setValues({
|
||||||
@ -41,15 +45,18 @@ export function stockFields({}: {}): ApiFormFieldSet {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
use_pack_size: {
|
use_pack_size: {
|
||||||
|
hidden: !create,
|
||||||
description: t`Add given quantity as packs instead of individual items`
|
description: t`Add given quantity as packs instead of individual items`
|
||||||
},
|
},
|
||||||
location: {
|
location: {
|
||||||
|
hidden: !create,
|
||||||
filters: {
|
filters: {
|
||||||
structural: false
|
structural: false
|
||||||
}
|
}
|
||||||
// TODO: icon
|
// TODO: icon
|
||||||
},
|
},
|
||||||
quantity: {
|
quantity: {
|
||||||
|
hidden: !create,
|
||||||
description: t`Enter initial quantity for this stock item`
|
description: t`Enter initial quantity for this stock item`
|
||||||
},
|
},
|
||||||
serial_numbers: {
|
serial_numbers: {
|
||||||
@ -57,9 +64,11 @@ export function stockFields({}: {}): ApiFormFieldSet {
|
|||||||
field_type: 'string',
|
field_type: 'string',
|
||||||
label: t`Serial Numbers`,
|
label: t`Serial Numbers`,
|
||||||
description: t`Enter serial numbers for new stock (or leave blank)`,
|
description: t`Enter serial numbers for new stock (or leave blank)`,
|
||||||
required: false
|
required: false,
|
||||||
|
hidden: !create
|
||||||
},
|
},
|
||||||
serial: {
|
serial: {
|
||||||
|
hidden: create
|
||||||
// TODO: icon
|
// TODO: icon
|
||||||
},
|
},
|
||||||
batch: {
|
batch: {
|
||||||
@ -100,7 +109,7 @@ export function createStockItem() {
|
|||||||
openCreateApiForm({
|
openCreateApiForm({
|
||||||
name: 'stockitem-create',
|
name: 'stockitem-create',
|
||||||
url: ApiPaths.stock_item_list,
|
url: ApiPaths.stock_item_list,
|
||||||
fields: stockFields({}),
|
fields: stockFields({ create: true }),
|
||||||
title: t`Create Stock Item`
|
title: t`Create Stock Item`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -109,12 +118,20 @@ export function createStockItem() {
|
|||||||
* Launch a form to edit an existing StockItem instance
|
* Launch a form to edit an existing StockItem instance
|
||||||
* @param item : primary key of the StockItem to edit
|
* @param item : primary key of the StockItem to edit
|
||||||
*/
|
*/
|
||||||
export function editStockItem(item: number) {
|
export function editStockItem({
|
||||||
|
item_id,
|
||||||
|
callback
|
||||||
|
}: {
|
||||||
|
item_id: number;
|
||||||
|
callback?: () => void;
|
||||||
|
}) {
|
||||||
openEditApiForm({
|
openEditApiForm({
|
||||||
name: 'stockitem-edit',
|
name: 'stockitem-edit',
|
||||||
url: ApiPaths.stock_item_list,
|
url: ApiPaths.stock_item_list,
|
||||||
pk: item,
|
pk: item_id,
|
||||||
fields: stockFields({}),
|
fields: stockFields({ create: false }),
|
||||||
title: t`Edit Stock Item`
|
title: t`Edit Stock Item`,
|
||||||
|
successMessage: t`Stock item updated`,
|
||||||
|
onFormSuccess: callback
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,10 @@ import {
|
|||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { ActionDropdown } from '../../components/items/ActionDropdown';
|
import {
|
||||||
|
ActionDropdown,
|
||||||
|
BarcodeActionDropdown
|
||||||
|
} from '../../components/items/ActionDropdown';
|
||||||
import { PageDetail } from '../../components/nav/PageDetail';
|
import { PageDetail } from '../../components/nav/PageDetail';
|
||||||
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
||||||
import { PartCategoryTree } from '../../components/nav/PartCategoryTree';
|
import { PartCategoryTree } from '../../components/nav/PartCategoryTree';
|
||||||
@ -236,10 +239,7 @@ export default function PartDetail() {
|
|||||||
const partActions = useMemo(() => {
|
const partActions = useMemo(() => {
|
||||||
// TODO: Disable actions based on user permissions
|
// TODO: Disable actions based on user permissions
|
||||||
return [
|
return [
|
||||||
<ActionDropdown
|
<BarcodeActionDropdown
|
||||||
key="barcode"
|
|
||||||
tooltip={t`Barcode Actions`}
|
|
||||||
icon={<IconQrcode />}
|
|
||||||
actions={[
|
actions={[
|
||||||
{
|
{
|
||||||
icon: <IconQrcode />,
|
icon: <IconQrcode />,
|
||||||
|
@ -3,27 +3,48 @@ import { Alert, LoadingOverlay, Stack, Text } from '@mantine/core';
|
|||||||
import {
|
import {
|
||||||
IconBookmark,
|
IconBookmark,
|
||||||
IconBoxPadding,
|
IconBoxPadding,
|
||||||
|
IconChecklist,
|
||||||
|
IconCircleCheck,
|
||||||
|
IconCircleMinus,
|
||||||
|
IconCirclePlus,
|
||||||
|
IconCopy,
|
||||||
|
IconDots,
|
||||||
|
IconEdit,
|
||||||
IconHistory,
|
IconHistory,
|
||||||
IconInfoCircle,
|
IconInfoCircle,
|
||||||
|
IconLink,
|
||||||
IconNotes,
|
IconNotes,
|
||||||
|
IconPackages,
|
||||||
IconPaperclip,
|
IconPaperclip,
|
||||||
IconSitemap
|
IconQrcode,
|
||||||
|
IconSitemap,
|
||||||
|
IconTransfer,
|
||||||
|
IconTrash,
|
||||||
|
IconUnlink
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ActionDropdown,
|
||||||
|
BarcodeActionDropdown
|
||||||
|
} from '../../components/items/ActionDropdown';
|
||||||
import { PlaceholderPanel } from '../../components/items/Placeholder';
|
import { PlaceholderPanel } from '../../components/items/Placeholder';
|
||||||
import { PageDetail } from '../../components/nav/PageDetail';
|
import { PageDetail } from '../../components/nav/PageDetail';
|
||||||
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
||||||
import { StockLocationTree } from '../../components/nav/StockLocationTree';
|
import { StockLocationTree } from '../../components/nav/StockLocationTree';
|
||||||
import { AttachmentTable } from '../../components/tables/general/AttachmentTable';
|
import { AttachmentTable } from '../../components/tables/general/AttachmentTable';
|
||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
|
import { editStockItem } from '../../functions/forms/StockForms';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
import { ApiPaths, apiUrl } from '../../states/ApiState';
|
||||||
|
import { useUserState } from '../../states/UserState';
|
||||||
|
|
||||||
export default function StockDetail() {
|
export default function StockDetail() {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
|
||||||
|
const user = useUserState();
|
||||||
|
|
||||||
const [treeOpen, setTreeOpen] = useState(false);
|
const [treeOpen, setTreeOpen] = useState(false);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -58,13 +79,22 @@ export default function StockDetail() {
|
|||||||
name: 'allocations',
|
name: 'allocations',
|
||||||
label: t`Allocations`,
|
label: t`Allocations`,
|
||||||
icon: <IconBookmark />,
|
icon: <IconBookmark />,
|
||||||
content: <PlaceholderPanel />
|
content: <PlaceholderPanel />,
|
||||||
|
hidden:
|
||||||
|
!stockitem?.part_detail?.salable && !stockitem?.part_detail?.component
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'testdata',
|
||||||
|
label: t`Test Data`,
|
||||||
|
icon: <IconChecklist />,
|
||||||
|
hidden: !stockitem?.part_detail?.trackable
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'installed_items',
|
name: 'installed_items',
|
||||||
label: t`Installed Items`,
|
label: t`Installed Items`,
|
||||||
icon: <IconBoxPadding />,
|
icon: <IconBoxPadding />,
|
||||||
content: <PlaceholderPanel />
|
content: <PlaceholderPanel />,
|
||||||
|
hidden: !stockitem?.part_detail?.assembly
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'child_items',
|
name: 'child_items',
|
||||||
@ -110,6 +140,89 @@ export default function StockDetail() {
|
|||||||
[stockitem]
|
[stockitem]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const stockActions = useMemo(
|
||||||
|
() => /* TODO: Disable actions based on user permissions*/ [
|
||||||
|
<BarcodeActionDropdown
|
||||||
|
actions={[
|
||||||
|
{
|
||||||
|
icon: <IconQrcode />,
|
||||||
|
name: t`View`,
|
||||||
|
tooltip: t`View part barcode`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <IconLink />,
|
||||||
|
name: t`Link Barcode`,
|
||||||
|
tooltip: t`Link custom barcode to stock item`,
|
||||||
|
disabled: stockitem?.barcode_hash
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <IconUnlink />,
|
||||||
|
name: t`Unlink Barcode`,
|
||||||
|
tooltip: t`Unlink custom barcode from stock item`,
|
||||||
|
disabled: !stockitem?.barcode_hash
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>,
|
||||||
|
<ActionDropdown
|
||||||
|
key="operations"
|
||||||
|
tooltip={t`Stock Operations`}
|
||||||
|
icon={<IconPackages />}
|
||||||
|
actions={[
|
||||||
|
{
|
||||||
|
name: t`Count`,
|
||||||
|
tooltip: t`Count stock`,
|
||||||
|
icon: <IconCircleCheck color="green" />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: t`Add`,
|
||||||
|
tooltip: t`Add stock`,
|
||||||
|
icon: <IconCirclePlus color="green" />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: t`Remove`,
|
||||||
|
tooltip: t`Remove stock`,
|
||||||
|
icon: <IconCircleMinus color="red" />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: t`Transfer`,
|
||||||
|
tooltip: t`Transfer stock`,
|
||||||
|
icon: <IconTransfer color="blue" />
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>,
|
||||||
|
<ActionDropdown
|
||||||
|
key="stock"
|
||||||
|
// tooltip={t`Stock Actions`}
|
||||||
|
icon={<IconDots />}
|
||||||
|
actions={[
|
||||||
|
{
|
||||||
|
name: t`Duplicate`,
|
||||||
|
tooltip: t`Duplicate stock item`,
|
||||||
|
icon: <IconCopy />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: t`Edit`,
|
||||||
|
tooltip: t`Edit stock item`,
|
||||||
|
icon: <IconEdit color="blue" />,
|
||||||
|
onClick: () => {
|
||||||
|
stockitem.pk &&
|
||||||
|
editStockItem({
|
||||||
|
item_id: stockitem.pk,
|
||||||
|
callback: () => refreshInstance
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: t`Delete`,
|
||||||
|
tooltip: t`Delete stock item`,
|
||||||
|
icon: <IconTrash color="red" />
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
],
|
||||||
|
[id, stockitem, user]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
<LoadingOverlay visible={instanceQuery.isFetching} />
|
<LoadingOverlay visible={instanceQuery.isFetching} />
|
||||||
@ -119,8 +232,9 @@ export default function StockDetail() {
|
|||||||
selectedLocation={stockitem?.location}
|
selectedLocation={stockitem?.location}
|
||||||
/>
|
/>
|
||||||
<PageDetail
|
<PageDetail
|
||||||
title={t`Stock Items`}
|
title={t`Stock Item`}
|
||||||
subtitle={stockitem.part_detail?.full_name ?? 'name goes here'}
|
subtitle={stockitem.part_detail?.full_name}
|
||||||
|
imageUrl={stockitem.part_detail?.thumbnail}
|
||||||
detail={
|
detail={
|
||||||
<Alert color="teal" title="Stock Item">
|
<Alert color="teal" title="Stock Item">
|
||||||
<Text>Quantity: {stockitem.quantity ?? 'idk'}</Text>
|
<Text>Quantity: {stockitem.quantity ?? 'idk'}</Text>
|
||||||
@ -130,6 +244,7 @@ export default function StockDetail() {
|
|||||||
breadcrumbAction={() => {
|
breadcrumbAction={() => {
|
||||||
setTreeOpen(true);
|
setTreeOpen(true);
|
||||||
}}
|
}}
|
||||||
|
actions={stockActions}
|
||||||
/>
|
/>
|
||||||
<PanelGroup pageKey="stockitem" panels={stockPanels} />
|
<PanelGroup pageKey="stockitem" panels={stockPanels} />
|
||||||
</Stack>
|
</Stack>
|
||||||
|
Loading…
Reference in New Issue
Block a user