mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-18 13:05:42 +00:00
Update stockitem details page
This commit is contained in:
@ -32,7 +32,7 @@ export type DetailImageProps = {
|
|||||||
appRole: UserRoles;
|
appRole: UserRoles;
|
||||||
src: string;
|
src: string;
|
||||||
apiPath: string;
|
apiPath: string;
|
||||||
refresh: () => void;
|
refresh?: () => void;
|
||||||
imageActions?: DetailImageButtonProps;
|
imageActions?: DetailImageButtonProps;
|
||||||
pk: string;
|
pk: string;
|
||||||
};
|
};
|
||||||
@ -335,7 +335,7 @@ export function DetailsImage(props: DetailImageProps) {
|
|||||||
// Sets a new image, and triggers upstream instance refresh
|
// Sets a new image, and triggers upstream instance refresh
|
||||||
const setAndRefresh = (image: string) => {
|
const setAndRefresh = (image: string) => {
|
||||||
setImg(image);
|
setImg(image);
|
||||||
props.refresh();
|
props.refresh && props.refresh();
|
||||||
};
|
};
|
||||||
|
|
||||||
const permissions = useUserState();
|
const permissions = useUserState();
|
||||||
|
@ -2,8 +2,10 @@ import {
|
|||||||
Icon123,
|
Icon123,
|
||||||
IconBinaryTree2,
|
IconBinaryTree2,
|
||||||
IconBookmarks,
|
IconBookmarks,
|
||||||
|
IconBox,
|
||||||
IconBuilding,
|
IconBuilding,
|
||||||
IconBuildingFactory2,
|
IconBuildingFactory2,
|
||||||
|
IconCalendar,
|
||||||
IconCalendarStats,
|
IconCalendarStats,
|
||||||
IconCheck,
|
IconCheck,
|
||||||
IconClipboardList,
|
IconClipboardList,
|
||||||
@ -18,14 +20,17 @@ import {
|
|||||||
IconLink,
|
IconLink,
|
||||||
IconList,
|
IconList,
|
||||||
IconListTree,
|
IconListTree,
|
||||||
|
IconMapPin,
|
||||||
IconMapPinHeart,
|
IconMapPinHeart,
|
||||||
IconNotes,
|
IconNotes,
|
||||||
IconPackage,
|
IconPackage,
|
||||||
|
IconPackageImport,
|
||||||
IconPackages,
|
IconPackages,
|
||||||
IconPaperclip,
|
IconPaperclip,
|
||||||
IconPhoto,
|
IconPhoto,
|
||||||
IconQuestionMark,
|
IconQuestionMark,
|
||||||
IconRulerMeasure,
|
IconRulerMeasure,
|
||||||
|
IconShape,
|
||||||
IconShoppingCart,
|
IconShoppingCart,
|
||||||
IconShoppingCartHeart,
|
IconShoppingCartHeart,
|
||||||
IconStack2,
|
IconStack2,
|
||||||
@ -99,9 +104,13 @@ const icons: { [key: string]: (props: TablerIconsProps) => React.JSX.Element } =
|
|||||||
saleable: IconCurrencyDollar,
|
saleable: IconCurrencyDollar,
|
||||||
virtual: IconWorldCode,
|
virtual: IconWorldCode,
|
||||||
inactive: IconX,
|
inactive: IconX,
|
||||||
|
part: IconBox,
|
||||||
|
supplier_part: IconPackageImport,
|
||||||
|
|
||||||
|
calendar: IconCalendar,
|
||||||
external: IconExternalLink,
|
external: IconExternalLink,
|
||||||
creation_date: IconCalendarTime,
|
creation_date: IconCalendarTime,
|
||||||
|
location: IconMapPin,
|
||||||
default_location: IconMapPinHeart,
|
default_location: IconMapPinHeart,
|
||||||
default_supplier: IconShoppingCartHeart,
|
default_supplier: IconShoppingCartHeart,
|
||||||
link: IconLink,
|
link: IconLink,
|
||||||
@ -138,3 +147,6 @@ export function InvenTreeIcon(props: IconProps) {
|
|||||||
|
|
||||||
return <Icon {...props.iconProps} />;
|
return <Icon {...props.iconProps} />;
|
||||||
}
|
}
|
||||||
|
function IconShapes(props: TablerIconsProps): Element {
|
||||||
|
throw new Error('Function not implemented.');
|
||||||
|
}
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Alert, LoadingOverlay, Skeleton, Stack, Text } from '@mantine/core';
|
import {
|
||||||
|
Alert,
|
||||||
|
Grid,
|
||||||
|
LoadingOverlay,
|
||||||
|
Skeleton,
|
||||||
|
Stack,
|
||||||
|
Text
|
||||||
|
} from '@mantine/core';
|
||||||
import {
|
import {
|
||||||
IconBookmark,
|
IconBookmark,
|
||||||
IconBoxPadding,
|
IconBoxPadding,
|
||||||
@ -20,6 +27,11 @@ import {
|
|||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { DetailsImage } from '../../components/details/DetailsImage';
|
||||||
|
import {
|
||||||
|
ItemDetails,
|
||||||
|
ItemDetailsGrid
|
||||||
|
} from '../../components/details/ItemDetails';
|
||||||
import {
|
import {
|
||||||
ActionDropdown,
|
ActionDropdown,
|
||||||
BarcodeActionDropdown,
|
BarcodeActionDropdown,
|
||||||
@ -34,10 +46,13 @@ import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
|
|||||||
import { StockLocationTree } from '../../components/nav/StockLocationTree';
|
import { StockLocationTree } from '../../components/nav/StockLocationTree';
|
||||||
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
|
||||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||||
|
import { ModelType } from '../../enums/ModelType';
|
||||||
|
import { UserRoles } from '../../enums/Roles';
|
||||||
import { useEditStockItem } from '../../forms/StockForms';
|
import { useEditStockItem } from '../../forms/StockForms';
|
||||||
import { useInstance } from '../../hooks/UseInstance';
|
import { useInstance } from '../../hooks/UseInstance';
|
||||||
import { apiUrl } from '../../states/ApiState';
|
import { apiUrl } from '../../states/ApiState';
|
||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
|
import { DetailsField, DetailsTable } from '../../tables/Details';
|
||||||
import { AttachmentTable } from '../../tables/general/AttachmentTable';
|
import { AttachmentTable } from '../../tables/general/AttachmentTable';
|
||||||
import { StockItemTable } from '../../tables/stock/StockItemTable';
|
import { StockItemTable } from '../../tables/stock/StockItemTable';
|
||||||
import StockItemTestResultTable from '../../tables/stock/StockItemTestResultTable';
|
import StockItemTestResultTable from '../../tables/stock/StockItemTestResultTable';
|
||||||
@ -63,12 +78,151 @@ export default function StockDetail() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const detailsPanel = useMemo(() => {
|
||||||
|
let data = stockitem;
|
||||||
|
|
||||||
|
data.available_stock = Math.max(0, data.quantity - data.allocated);
|
||||||
|
|
||||||
|
if (instanceQuery.isFetching) {
|
||||||
|
return <Skeleton />;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Top left - core part information
|
||||||
|
let tl: DetailsField[] = [
|
||||||
|
{
|
||||||
|
name: 'part',
|
||||||
|
label: t`Base Part`,
|
||||||
|
type: 'link',
|
||||||
|
model: ModelType.part
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'status',
|
||||||
|
type: 'text',
|
||||||
|
label: t`Stock Status`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
name: 'tests',
|
||||||
|
label: `Completed Tests`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
name: 'updated',
|
||||||
|
icon: 'calendar',
|
||||||
|
label: t`Last Updated`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
name: 'stocktake',
|
||||||
|
icon: 'calendar',
|
||||||
|
label: t`Last Stocktake`,
|
||||||
|
hidden: !stockitem.stocktake
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Top right - available stock information
|
||||||
|
let tr: DetailsField[] = [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
name: 'quantity',
|
||||||
|
label: t`Quantity`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
name: 'serial',
|
||||||
|
label: t`Serial Number`,
|
||||||
|
hidden: !stockitem.serial
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
name: 'available_stock',
|
||||||
|
label: t`In Stock`
|
||||||
|
}
|
||||||
|
// TODO: allocated_to_sales_orders
|
||||||
|
// TODO: allocated_to_build_orders
|
||||||
|
];
|
||||||
|
|
||||||
|
// Bottom left: location information
|
||||||
|
let bl: DetailsField[] = [
|
||||||
|
{
|
||||||
|
name: 'supplier_part',
|
||||||
|
label: t`Supplier Part`,
|
||||||
|
type: 'link',
|
||||||
|
model: ModelType.supplierpart,
|
||||||
|
hidden: !stockitem.supplier_part
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'link',
|
||||||
|
name: 'location',
|
||||||
|
label: t`Location`,
|
||||||
|
model: ModelType.stocklocation,
|
||||||
|
hidden: !stockitem.location
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'link',
|
||||||
|
name: 'belongs_to',
|
||||||
|
label: t`Installed In`,
|
||||||
|
model: ModelType.stockitem,
|
||||||
|
hidden: !stockitem.belongs_to
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'link',
|
||||||
|
name: 'consumed_by',
|
||||||
|
label: t`Consumed By`,
|
||||||
|
model: ModelType.build,
|
||||||
|
hidden: !stockitem.consumed_by
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'link',
|
||||||
|
name: 'sales_order',
|
||||||
|
label: t`Sales Order`,
|
||||||
|
model: ModelType.salesorder,
|
||||||
|
hidden: !stockitem.sales_order
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Bottom right - any other information
|
||||||
|
let br: DetailsField[] = [
|
||||||
|
// TODO: Expiry date
|
||||||
|
// TODO: Ownership
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
name: 'packaging',
|
||||||
|
icon: 'part',
|
||||||
|
label: t`Packaging`,
|
||||||
|
hidden: !stockitem.packaging
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ItemDetailsGrid>
|
||||||
|
<Grid>
|
||||||
|
<Grid.Col span={4}>
|
||||||
|
<DetailsImage
|
||||||
|
appRole={UserRoles.part}
|
||||||
|
apiPath={ApiEndpoints.part_list}
|
||||||
|
src={stockitem.part_detail?.thumbnail}
|
||||||
|
pk={stockitem.part}
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col span={8}>
|
||||||
|
<DetailsTable fields={tl} item={stockitem} />
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
<DetailsTable fields={tr} item={stockitem} />
|
||||||
|
<DetailsTable fields={bl} item={stockitem} />
|
||||||
|
<DetailsTable fields={br} item={stockitem} />
|
||||||
|
</ItemDetailsGrid>
|
||||||
|
);
|
||||||
|
}, [stockitem, instanceQuery]);
|
||||||
|
|
||||||
const stockPanels: PanelType[] = useMemo(() => {
|
const stockPanels: PanelType[] = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
name: 'details',
|
name: 'details',
|
||||||
label: t`Details`,
|
label: t`Details`,
|
||||||
icon: <IconInfoCircle />
|
icon: <IconInfoCircle />,
|
||||||
|
content: detailsPanel
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'tracking',
|
name: 'tracking',
|
||||||
|
Reference in New Issue
Block a user