mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-27 01:00:53 +00:00
Refactoring for DetailsTable
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
import { Grid, Group, Paper, SimpleGrid } from '@mantine/core';
|
||||
import React from 'react';
|
||||
|
||||
import { UserRoles } from '../../enums/Roles';
|
||||
import { DetailsField, DetailsTable } from '../../tables/Details';
|
||||
@ -23,6 +24,16 @@ export type DetailsImageType = {
|
||||
imageActions: DetailImageButtonProps;
|
||||
};
|
||||
|
||||
export function ItemDetailsGrid(props: React.PropsWithChildren<{}>) {
|
||||
return (
|
||||
<Paper p="xs">
|
||||
<SimpleGrid cols={2} spacing="xs" verticalSpacing="xs">
|
||||
{props.children}
|
||||
</SimpleGrid>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a Details panel of the given model
|
||||
* @param params Object with the data of the model to render
|
||||
|
@ -7,7 +7,7 @@ import { ModelType } from '../enums/ModelType';
|
||||
export function getDetailUrl(model: ModelType, pk: number | string): string {
|
||||
const modelInfo = ModelInformationDict[model];
|
||||
|
||||
if (modelInfo && modelInfo.url_detail) {
|
||||
if (!!pk && modelInfo && modelInfo.url_detail) {
|
||||
return modelInfo.url_detail.replace(':pk', pk.toString());
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,8 @@ import { api } from '../../App';
|
||||
import {
|
||||
DetailsImageType,
|
||||
ItemDetailFields,
|
||||
ItemDetails
|
||||
ItemDetails,
|
||||
ItemDetailsGrid
|
||||
} from '../../components/details/ItemDetails';
|
||||
import {
|
||||
ActionDropdown,
|
||||
@ -56,7 +57,7 @@ import { useEditApiFormModal } from '../../hooks/UseForm';
|
||||
import { useInstance } from '../../hooks/UseInstance';
|
||||
import { apiUrl } from '../../states/ApiState';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
import { DetailsField } from '../../tables/Details';
|
||||
import { DetailsField, DetailsTable } from '../../tables/Details';
|
||||
import { BomTable } from '../../tables/bom/BomTable';
|
||||
import { UsedInTable } from '../../tables/bom/UsedInTable';
|
||||
import { BuildOrderTable } from '../../tables/build/BuildOrderTable';
|
||||
@ -441,6 +442,192 @@ export default function PartDetail() {
|
||||
return fields;
|
||||
};
|
||||
|
||||
const detailsPanel = useMemo(() => {
|
||||
if (instanceQuery.isFetching) {
|
||||
return <Skeleton />;
|
||||
}
|
||||
|
||||
// Construct the details tables
|
||||
let tl: DetailsField[] = [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'description',
|
||||
label: t`Description`,
|
||||
copy: true
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
name: 'variant_of',
|
||||
label: t`Variant of`,
|
||||
model: ModelType.part,
|
||||
hidden: !part.variant_of
|
||||
}
|
||||
];
|
||||
|
||||
let tr: DetailsField[] = [
|
||||
{
|
||||
type: 'string',
|
||||
name: 'unallocated_stock',
|
||||
unit: true,
|
||||
label: t`Available Stock`
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'total_in_stock',
|
||||
unit: true,
|
||||
label: t`In Stock`
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'minimum_stock',
|
||||
unit: true,
|
||||
label: t`Minimum Stock`,
|
||||
hidden: part.minimum_stock <= 0
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'ordering',
|
||||
label: t`On order`,
|
||||
unit: true,
|
||||
hidden: part.ordering <= 0
|
||||
},
|
||||
{
|
||||
type: 'progressbar',
|
||||
name: 'allocated_to_build_orders',
|
||||
total: part.required_for_build_orders,
|
||||
progress: part.allocated_to_build_orders,
|
||||
label: t`Allocated to Build Orders`,
|
||||
hidden:
|
||||
!part.assembly ||
|
||||
(part.allocated_to_build_orders <= 0 &&
|
||||
part.required_for_build_orders <= 0)
|
||||
},
|
||||
{
|
||||
type: 'progressbar',
|
||||
name: 'allocated_to_sales_orders',
|
||||
total: part.required_for_sales_orders,
|
||||
progress: part.allocated_to_sales_orders,
|
||||
label: t`Allocated to Sales Orders`,
|
||||
hidden:
|
||||
!part.salable ||
|
||||
(part.allocated_to_sales_orders <= 0 &&
|
||||
part.required_for_sales_orders <= 0)
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'can_build',
|
||||
unit: true,
|
||||
label: t`Can Build`,
|
||||
hidden: !part.assembly
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'building',
|
||||
unit: true,
|
||||
label: t`Building`,
|
||||
hidden: !part.assembly
|
||||
}
|
||||
];
|
||||
|
||||
let bl: DetailsField[] = [
|
||||
{
|
||||
type: 'link',
|
||||
name: 'category',
|
||||
label: t`Category`,
|
||||
model: ModelType.partcategory
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'IPN',
|
||||
label: t`IPN`,
|
||||
copy: true,
|
||||
hidden: !part.IPN
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'revision',
|
||||
label: t`Revision`,
|
||||
copy: true,
|
||||
hidden: !part.revision
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'units',
|
||||
label: t`Units`,
|
||||
hidden: !part.units
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'keywords',
|
||||
label: t`Keywords`,
|
||||
copy: true,
|
||||
hidden: !part.keywords
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'responsible',
|
||||
label: t`Responsible`,
|
||||
badge: 'owner',
|
||||
hidden: !part.responsible
|
||||
}
|
||||
];
|
||||
|
||||
let br: DetailsField[] = [
|
||||
{
|
||||
type: 'string',
|
||||
name: 'creation_date',
|
||||
label: t`Creation Date`
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'creation_user',
|
||||
badge: 'user'
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
name: 'default_location',
|
||||
label: t`Default Location`,
|
||||
model: ModelType.stocklocation,
|
||||
hidden: !part.default_location
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
name: 'default_supplier',
|
||||
label: t`Default Supplier`,
|
||||
model: ModelType.supplierpart,
|
||||
hidden: !part.default_supplier
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
name: 'link',
|
||||
label: t`Link`,
|
||||
external: true,
|
||||
copy: true,
|
||||
hidden: !part.link
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<ItemDetailsGrid>
|
||||
<DetailsTable fields={tl} item={part} />
|
||||
<DetailsTable fields={tr} item={part} />
|
||||
<DetailsTable fields={bl} item={part} />
|
||||
<DetailsTable fields={br} item={part} />
|
||||
</ItemDetailsGrid>
|
||||
);
|
||||
|
||||
// content: !instanceQuery.isFetching && (
|
||||
// <ItemDetails
|
||||
// appRole={UserRoles.part}
|
||||
// params={part}
|
||||
// apiPath={apiUrl(ApiEndpoints.part_list, part.pk)}
|
||||
// refresh={refreshInstance}
|
||||
// fields={detailFields(part)}
|
||||
// partModel
|
||||
// />
|
||||
// )
|
||||
}, [part, instanceQuery]);
|
||||
|
||||
// Part data panels (recalculate when part data changes)
|
||||
const partPanels: PanelType[] = useMemo(() => {
|
||||
return [
|
||||
@ -448,16 +635,7 @@ export default function PartDetail() {
|
||||
name: 'details',
|
||||
label: t`Details`,
|
||||
icon: <IconInfoCircle />,
|
||||
content: !instanceQuery.isFetching && (
|
||||
<ItemDetails
|
||||
appRole={UserRoles.part}
|
||||
params={part}
|
||||
apiPath={apiUrl(ApiEndpoints.part_list, part.pk)}
|
||||
refresh={refreshInstance}
|
||||
fields={detailFields(part)}
|
||||
partModel
|
||||
/>
|
||||
)
|
||||
content: detailsPanel
|
||||
},
|
||||
{
|
||||
name: 'parameters',
|
||||
|
@ -37,6 +37,7 @@ export type PartIconsType = {
|
||||
|
||||
export type DetailsField =
|
||||
| {
|
||||
hidden?: boolean;
|
||||
name: string;
|
||||
label?: string;
|
||||
badge?: BadgeType;
|
||||
@ -299,7 +300,7 @@ function TableAnchorValue(props: FieldProps) {
|
||||
queryFn: async () => {
|
||||
const modelDef = getModelInfo(props.field_data.model);
|
||||
|
||||
if (!modelDef.api_endpoint) {
|
||||
if (!modelDef?.api_endpoint) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -366,12 +367,12 @@ function CopyField({ value }: { value: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
function TableField({
|
||||
function xTableField({
|
||||
field_data,
|
||||
field_value,
|
||||
unit = null
|
||||
}: {
|
||||
field_data: DetailsField[];
|
||||
field_data: DetailsField;
|
||||
field_value: FieldValueType[];
|
||||
unit?: string | null;
|
||||
}) {
|
||||
@ -397,8 +398,8 @@ function TableField({
|
||||
justifyContent: 'flex-start'
|
||||
}}
|
||||
>
|
||||
<InvenTreeIcon icon={field_data[0].name} />
|
||||
<Text>{field_data[0].label}</Text>
|
||||
<InvenTreeIcon icon={field_data.name} />
|
||||
<Text>{field_data.label}</Text>
|
||||
</td>
|
||||
<td style={{ minWidth: '40%' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
@ -428,52 +429,64 @@ function TableField({
|
||||
);
|
||||
}
|
||||
|
||||
export function DetailsTable({
|
||||
export function DetailsTableField({
|
||||
item,
|
||||
fields,
|
||||
partIcons = false
|
||||
field
|
||||
}: {
|
||||
item: any;
|
||||
fields: DetailsField[][];
|
||||
partIcons?: boolean;
|
||||
field: DetailsField;
|
||||
}) {
|
||||
function getFieldType(type: string) {
|
||||
switch (type) {
|
||||
case 'text':
|
||||
case 'string':
|
||||
return TableStringValue;
|
||||
case 'link':
|
||||
return TableAnchorValue;
|
||||
case 'progressbar':
|
||||
return ProgressBarValue;
|
||||
default:
|
||||
return TableStringValue;
|
||||
}
|
||||
}
|
||||
|
||||
const FieldType: any = getFieldType(field.type);
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '20px',
|
||||
justifyContent: 'flex-start'
|
||||
}}
|
||||
>
|
||||
<InvenTreeIcon icon={field.name} />
|
||||
<Text>{field.label}</Text>
|
||||
</td>
|
||||
<td style={{ minWidth: '40%' }}>
|
||||
<FieldType field_data={field} field_value={item[field.name]} />
|
||||
</td>
|
||||
<td>{field.copy && <CopyField value={'hello world'} />}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
export function DetailsTable({
|
||||
item,
|
||||
fields
|
||||
}: {
|
||||
item: any;
|
||||
fields: DetailsField[];
|
||||
}) {
|
||||
return (
|
||||
<Paper p="xs" withBorder radius="xs">
|
||||
<Table striped>
|
||||
<tbody>
|
||||
{partIcons && (
|
||||
<tr>
|
||||
<PartIcons
|
||||
assembly={item.assembly}
|
||||
template={item.is_template}
|
||||
component={item.component}
|
||||
trackable={item.trackable}
|
||||
purchaseable={item.purchaseable}
|
||||
saleable={item.salable}
|
||||
virtual={item.virtual}
|
||||
active={item.active}
|
||||
/>
|
||||
</tr>
|
||||
)}
|
||||
{fields.map((data: DetailsField[], index: number) => {
|
||||
let value: FieldValueType[] = [];
|
||||
for (const val of data) {
|
||||
if (val.value_formatter) {
|
||||
value.push(undefined);
|
||||
} else {
|
||||
value.push(item[val.name]);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<TableField
|
||||
field_data={data}
|
||||
field_value={value}
|
||||
key={index}
|
||||
unit={item.units}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{fields.map((field: DetailsField, index: number) => (
|
||||
<DetailsTableField field={field} item={item} key={index} />
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
</Paper>
|
||||
|
Reference in New Issue
Block a user