mirror of
https://github.com/inventree/InvenTree.git
synced 2026-09-27 14:16:02 +00:00
[UI] Order lines table (#12362)
* Add line items progress column to TransferOrderTable * Add line item overview to order detail pages
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Anchor, Group, Paper, Stack } from '@mantine/core';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { DataTable } from 'mantine-datatable';
|
||||
import { useMemo } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { ProgressBar } from '@lib/components/ProgressBar';
|
||||
import { StylishText } from '@lib/components/StylishText';
|
||||
import type { ApiEndpoints } from '@lib/enums/ApiEndpoints';
|
||||
import { apiUrl } from '@lib/functions/Api';
|
||||
import { resolveItem } from '@lib/functions/Conversion';
|
||||
import { formatDecimal } from '@lib/functions/Formatting';
|
||||
import { useApi } from '../../contexts/ApiContext';
|
||||
import { RenderPartColumn } from '../tables/ColumnRenderers';
|
||||
|
||||
export interface LineItemProgress {
|
||||
value: number;
|
||||
maximum: number;
|
||||
}
|
||||
|
||||
export interface LineItemOverviewTableProps {
|
||||
// API endpoint which returns the line items for this order
|
||||
endpoint: ApiEndpoints;
|
||||
// Extra query parameters to filter the line item list (e.g. the order pk)
|
||||
params?: Record<string, any>;
|
||||
// Accessor for the "part" detail object on each line item record
|
||||
partAccessor?: string;
|
||||
// Accessor for the target quantity on each line item record
|
||||
quantityAccessor?: string;
|
||||
// Extract progress information from a line item record.
|
||||
// If not provided (or returns null for a record), the quantity is shown as plain text
|
||||
progress?: (record: any) => LineItemProgress | null;
|
||||
// Title for the progress column
|
||||
progressLabel?: string;
|
||||
// Name of the panel (within the same PanelGroup) which shows the full line item table
|
||||
linkToPanel?: string;
|
||||
// Maximum number of line items to display
|
||||
limit?: number;
|
||||
// Optional title, displayed above the table
|
||||
title?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A minimal, read-only overview of an order's line items - not selectable,
|
||||
* searchable, sortable or paginated. Intended for embedding in an order's
|
||||
* "Details" panel, alongside the full interactive line item table on a separate panel.
|
||||
*/
|
||||
export function LineItemOverviewTable({
|
||||
endpoint,
|
||||
params,
|
||||
partAccessor = 'part_detail',
|
||||
quantityAccessor = 'quantity',
|
||||
progress,
|
||||
progressLabel,
|
||||
linkToPanel = 'line-items',
|
||||
limit = 10,
|
||||
title
|
||||
}: Readonly<LineItemOverviewTableProps>) {
|
||||
const api = useApi();
|
||||
|
||||
const { data, isFetching } = useQuery({
|
||||
queryKey: ['line-item-overview', endpoint, params, limit],
|
||||
queryFn: () =>
|
||||
api
|
||||
.get(apiUrl(endpoint), {
|
||||
params: { ...params, limit, offset: 0 }
|
||||
})
|
||||
.then((response) => response.data),
|
||||
placeholderData: (previous) => previous
|
||||
});
|
||||
|
||||
const records: any[] = data?.results ?? [];
|
||||
const count: number = data?.count ?? records.length;
|
||||
|
||||
const columns = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
accessor: 'part',
|
||||
title: t`Part`,
|
||||
render: (record: any) => (
|
||||
<RenderPartColumn part={resolveItem(record, partAccessor)} />
|
||||
)
|
||||
},
|
||||
{
|
||||
accessor: 'progress',
|
||||
title: progressLabel ?? t`Progress`,
|
||||
render: (record: any) => {
|
||||
const p = progress?.(record);
|
||||
|
||||
if (p) {
|
||||
return (
|
||||
<ProgressBar progressLabel value={p.value} maximum={p.maximum} />
|
||||
);
|
||||
}
|
||||
|
||||
return formatDecimal(resolveItem(record, quantityAccessor));
|
||||
}
|
||||
}
|
||||
];
|
||||
}, [partAccessor, quantityAccessor, progress, progressLabel]);
|
||||
|
||||
return (
|
||||
<Paper p='xs' withBorder style={{ width: '100%' }}>
|
||||
<Stack gap='xs'>
|
||||
{title && <StylishText size='lg'>{title}</StylishText>}
|
||||
<DataTable
|
||||
idAccessor='pk'
|
||||
minHeight={records.length ? undefined : 100}
|
||||
records={records}
|
||||
columns={columns}
|
||||
fetching={isFetching}
|
||||
noRecordsText={t`No line items`}
|
||||
/>
|
||||
{count > records.length && (
|
||||
<Group justify='right'>
|
||||
<Anchor component={Link} to={`../${linkToPanel}`} size='sm'>
|
||||
{t`View all ${count} line items`}
|
||||
</Anchor>
|
||||
</Group>
|
||||
)}
|
||||
</Stack>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
} from '../../components/details/Details';
|
||||
import { DetailsImage } from '../../components/details/DetailsImage';
|
||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||
import { LineItemOverviewTable } from '../../components/details/LineItemOverviewTable';
|
||||
import { useParameterDetailsGrid } from '../../components/details/ParameterDetailsGrid';
|
||||
import { formatCurrency } from '../../defaults/formatters';
|
||||
import { useGlobalSettingsState } from '../../states/SettingsStates';
|
||||
@@ -261,6 +262,16 @@ export function PurchaseOrderDetailsPanel({
|
||||
</Grid>
|
||||
<TagsList tags={instance?.tags} />
|
||||
</Stack>
|
||||
<LineItemOverviewTable
|
||||
title={t`Line Items`}
|
||||
endpoint={ApiEndpoints.purchase_order_line_list}
|
||||
params={{ order: instance?.pk, part_detail: true }}
|
||||
progressLabel={t`Received`}
|
||||
progress={(record: any) => ({
|
||||
value: record.received,
|
||||
maximum: record.quantity
|
||||
})}
|
||||
/>
|
||||
</ItemDetailsGrid>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
} from '../../components/details/Details';
|
||||
import { DetailsImage } from '../../components/details/DetailsImage';
|
||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||
import { LineItemOverviewTable } from '../../components/details/LineItemOverviewTable';
|
||||
import { useParameterDetailsGrid } from '../../components/details/ParameterDetailsGrid';
|
||||
import { RenderAddress } from '../../components/render/Company';
|
||||
import { formatCurrency } from '../../defaults/formatters';
|
||||
@@ -273,6 +274,16 @@ export function ReturnOrderDetailsPanel({
|
||||
</Grid>
|
||||
<TagsList tags={instance?.tags} />
|
||||
</Stack>
|
||||
<LineItemOverviewTable
|
||||
title={t`Line Items`}
|
||||
endpoint={ApiEndpoints.return_order_line_list}
|
||||
params={{ order: instance?.pk, part_detail: true }}
|
||||
progressLabel={t`Received`}
|
||||
progress={(record: any) => ({
|
||||
value: record.received_date ? 1 : 0,
|
||||
maximum: 1
|
||||
})}
|
||||
/>
|
||||
</ItemDetailsGrid>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
} from '../../components/details/Details';
|
||||
import { DetailsImage } from '../../components/details/DetailsImage';
|
||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||
import { LineItemOverviewTable } from '../../components/details/LineItemOverviewTable';
|
||||
import { useParameterDetailsGrid } from '../../components/details/ParameterDetailsGrid';
|
||||
import { RenderAddress } from '../../components/render/Company';
|
||||
import { formatCurrency } from '../../defaults/formatters';
|
||||
@@ -273,6 +274,16 @@ export function SalesOrderDetailsPanel({
|
||||
</Grid>
|
||||
<TagsList tags={instance?.tags} />
|
||||
</Stack>
|
||||
<LineItemOverviewTable
|
||||
title={t`Line Items`}
|
||||
endpoint={ApiEndpoints.sales_order_line_list}
|
||||
params={{ order: instance?.pk, part_detail: true }}
|
||||
progressLabel={t`Shipped`}
|
||||
progress={(record: any) => ({
|
||||
value: record.shipped,
|
||||
maximum: record.quantity
|
||||
})}
|
||||
/>
|
||||
</ItemDetailsGrid>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Grid, Skeleton, Stack } from '@mantine/core';
|
||||
|
||||
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
|
||||
import { ModelType } from '@lib/enums/ModelType';
|
||||
import { TagsList } from '@lib/index';
|
||||
|
||||
@@ -9,6 +10,7 @@ import {
|
||||
DetailsTable
|
||||
} from '../../components/details/Details';
|
||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||
import { LineItemOverviewTable } from '../../components/details/LineItemOverviewTable';
|
||||
import { useParameterDetailsGrid } from '../../components/details/ParameterDetailsGrid';
|
||||
|
||||
export function TransferOrderDetailsPanel({
|
||||
@@ -176,6 +178,16 @@ export function TransferOrderDetailsPanel({
|
||||
</Grid>
|
||||
<TagsList tags={instance.tags} />
|
||||
</Stack>
|
||||
<LineItemOverviewTable
|
||||
title={t`Line Items`}
|
||||
endpoint={ApiEndpoints.transfer_order_line_list}
|
||||
params={{ order: instance?.pk, part_detail: true }}
|
||||
progressLabel={t`Transferred`}
|
||||
progress={(record: any) => ({
|
||||
value: record.transferred,
|
||||
maximum: record.quantity
|
||||
})}
|
||||
/>
|
||||
</ItemDetailsGrid>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
CreatedByColumn,
|
||||
CreationDateColumn,
|
||||
DescriptionColumn,
|
||||
LineItemsProgressColumn,
|
||||
LocationColumn,
|
||||
ProjectCodeColumn,
|
||||
ReferenceColumn,
|
||||
@@ -57,7 +58,7 @@ export function TransferOrderTable({
|
||||
sortable: true,
|
||||
switchable: true
|
||||
}),
|
||||
// LineItemsProgressColumn({}),
|
||||
LineItemsProgressColumn({}),
|
||||
StatusColumn({ model: ModelType.transferorder }),
|
||||
ProjectCodeColumn({
|
||||
defaultVisible: false
|
||||
|
||||
Reference in New Issue
Block a user