2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-17 20:45:44 +00:00

Update stockitem details page

This commit is contained in:
Oliver
2024-03-01 00:31:02 +00:00
parent 40df99eea2
commit a652ed85aa
3 changed files with 170 additions and 4 deletions

View File

@ -32,7 +32,7 @@ export type DetailImageProps = {
appRole: UserRoles;
src: string;
apiPath: string;
refresh: () => void;
refresh?: () => void;
imageActions?: DetailImageButtonProps;
pk: string;
};
@ -335,7 +335,7 @@ export function DetailsImage(props: DetailImageProps) {
// Sets a new image, and triggers upstream instance refresh
const setAndRefresh = (image: string) => {
setImg(image);
props.refresh();
props.refresh && props.refresh();
};
const permissions = useUserState();

View File

@ -2,8 +2,10 @@ import {
Icon123,
IconBinaryTree2,
IconBookmarks,
IconBox,
IconBuilding,
IconBuildingFactory2,
IconCalendar,
IconCalendarStats,
IconCheck,
IconClipboardList,
@ -18,14 +20,17 @@ import {
IconLink,
IconList,
IconListTree,
IconMapPin,
IconMapPinHeart,
IconNotes,
IconPackage,
IconPackageImport,
IconPackages,
IconPaperclip,
IconPhoto,
IconQuestionMark,
IconRulerMeasure,
IconShape,
IconShoppingCart,
IconShoppingCartHeart,
IconStack2,
@ -99,9 +104,13 @@ const icons: { [key: string]: (props: TablerIconsProps) => React.JSX.Element } =
saleable: IconCurrencyDollar,
virtual: IconWorldCode,
inactive: IconX,
part: IconBox,
supplier_part: IconPackageImport,
calendar: IconCalendar,
external: IconExternalLink,
creation_date: IconCalendarTime,
location: IconMapPin,
default_location: IconMapPinHeart,
default_supplier: IconShoppingCartHeart,
link: IconLink,
@ -138,3 +147,6 @@ export function InvenTreeIcon(props: IconProps) {
return <Icon {...props.iconProps} />;
}
function IconShapes(props: TablerIconsProps): Element {
throw new Error('Function not implemented.');
}

View File

@ -1,5 +1,12 @@
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 {
IconBookmark,
IconBoxPadding,
@ -20,6 +27,11 @@ import {
import { useMemo, useState } from 'react';
import { useParams } from 'react-router-dom';
import { DetailsImage } from '../../components/details/DetailsImage';
import {
ItemDetails,
ItemDetailsGrid
} from '../../components/details/ItemDetails';
import {
ActionDropdown,
BarcodeActionDropdown,
@ -34,10 +46,13 @@ import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
import { StockLocationTree } from '../../components/nav/StockLocationTree';
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
import { ApiEndpoints } from '../../enums/ApiEndpoints';
import { ModelType } from '../../enums/ModelType';
import { UserRoles } from '../../enums/Roles';
import { useEditStockItem } from '../../forms/StockForms';
import { useInstance } from '../../hooks/UseInstance';
import { apiUrl } from '../../states/ApiState';
import { useUserState } from '../../states/UserState';
import { DetailsField, DetailsTable } from '../../tables/Details';
import { AttachmentTable } from '../../tables/general/AttachmentTable';
import { StockItemTable } from '../../tables/stock/StockItemTable';
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(() => {
return [
{
name: 'details',
label: t`Details`,
icon: <IconInfoCircle />
icon: <IconInfoCircle />,
content: detailsPanel
},
{
name: 'tracking',