2
0
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:
Oliver
2024-02-29 05:16:08 +00:00
parent 38e5e3686d
commit bbcc256f28
4 changed files with 258 additions and 56 deletions
src/frontend/src
components
functions
pages
tables

@ -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>