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';
|
} from '../../components/details/Details';
|
||||||
import { DetailsImage } from '../../components/details/DetailsImage';
|
import { DetailsImage } from '../../components/details/DetailsImage';
|
||||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||||
|
import { LineItemOverviewTable } from '../../components/details/LineItemOverviewTable';
|
||||||
import { useParameterDetailsGrid } from '../../components/details/ParameterDetailsGrid';
|
import { useParameterDetailsGrid } from '../../components/details/ParameterDetailsGrid';
|
||||||
import { formatCurrency } from '../../defaults/formatters';
|
import { formatCurrency } from '../../defaults/formatters';
|
||||||
import { useGlobalSettingsState } from '../../states/SettingsStates';
|
import { useGlobalSettingsState } from '../../states/SettingsStates';
|
||||||
@@ -261,6 +262,16 @@ export function PurchaseOrderDetailsPanel({
|
|||||||
</Grid>
|
</Grid>
|
||||||
<TagsList tags={instance?.tags} />
|
<TagsList tags={instance?.tags} />
|
||||||
</Stack>
|
</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>
|
</ItemDetailsGrid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
} from '../../components/details/Details';
|
} from '../../components/details/Details';
|
||||||
import { DetailsImage } from '../../components/details/DetailsImage';
|
import { DetailsImage } from '../../components/details/DetailsImage';
|
||||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||||
|
import { LineItemOverviewTable } from '../../components/details/LineItemOverviewTable';
|
||||||
import { useParameterDetailsGrid } from '../../components/details/ParameterDetailsGrid';
|
import { useParameterDetailsGrid } from '../../components/details/ParameterDetailsGrid';
|
||||||
import { RenderAddress } from '../../components/render/Company';
|
import { RenderAddress } from '../../components/render/Company';
|
||||||
import { formatCurrency } from '../../defaults/formatters';
|
import { formatCurrency } from '../../defaults/formatters';
|
||||||
@@ -273,6 +274,16 @@ export function ReturnOrderDetailsPanel({
|
|||||||
</Grid>
|
</Grid>
|
||||||
<TagsList tags={instance?.tags} />
|
<TagsList tags={instance?.tags} />
|
||||||
</Stack>
|
</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>
|
</ItemDetailsGrid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
} from '../../components/details/Details';
|
} from '../../components/details/Details';
|
||||||
import { DetailsImage } from '../../components/details/DetailsImage';
|
import { DetailsImage } from '../../components/details/DetailsImage';
|
||||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||||
|
import { LineItemOverviewTable } from '../../components/details/LineItemOverviewTable';
|
||||||
import { useParameterDetailsGrid } from '../../components/details/ParameterDetailsGrid';
|
import { useParameterDetailsGrid } from '../../components/details/ParameterDetailsGrid';
|
||||||
import { RenderAddress } from '../../components/render/Company';
|
import { RenderAddress } from '../../components/render/Company';
|
||||||
import { formatCurrency } from '../../defaults/formatters';
|
import { formatCurrency } from '../../defaults/formatters';
|
||||||
@@ -273,6 +274,16 @@ export function SalesOrderDetailsPanel({
|
|||||||
</Grid>
|
</Grid>
|
||||||
<TagsList tags={instance?.tags} />
|
<TagsList tags={instance?.tags} />
|
||||||
</Stack>
|
</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>
|
</ItemDetailsGrid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { Grid, Skeleton, Stack } from '@mantine/core';
|
import { Grid, Skeleton, Stack } from '@mantine/core';
|
||||||
|
|
||||||
|
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
|
||||||
import { ModelType } from '@lib/enums/ModelType';
|
import { ModelType } from '@lib/enums/ModelType';
|
||||||
import { TagsList } from '@lib/index';
|
import { TagsList } from '@lib/index';
|
||||||
|
|
||||||
@@ -9,6 +10,7 @@ import {
|
|||||||
DetailsTable
|
DetailsTable
|
||||||
} from '../../components/details/Details';
|
} from '../../components/details/Details';
|
||||||
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
import { ItemDetailsGrid } from '../../components/details/ItemDetails';
|
||||||
|
import { LineItemOverviewTable } from '../../components/details/LineItemOverviewTable';
|
||||||
import { useParameterDetailsGrid } from '../../components/details/ParameterDetailsGrid';
|
import { useParameterDetailsGrid } from '../../components/details/ParameterDetailsGrid';
|
||||||
|
|
||||||
export function TransferOrderDetailsPanel({
|
export function TransferOrderDetailsPanel({
|
||||||
@@ -176,6 +178,16 @@ export function TransferOrderDetailsPanel({
|
|||||||
</Grid>
|
</Grid>
|
||||||
<TagsList tags={instance.tags} />
|
<TagsList tags={instance.tags} />
|
||||||
</Stack>
|
</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>
|
</ItemDetailsGrid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
CreatedByColumn,
|
CreatedByColumn,
|
||||||
CreationDateColumn,
|
CreationDateColumn,
|
||||||
DescriptionColumn,
|
DescriptionColumn,
|
||||||
|
LineItemsProgressColumn,
|
||||||
LocationColumn,
|
LocationColumn,
|
||||||
ProjectCodeColumn,
|
ProjectCodeColumn,
|
||||||
ReferenceColumn,
|
ReferenceColumn,
|
||||||
@@ -57,7 +58,7 @@ export function TransferOrderTable({
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
switchable: true
|
switchable: true
|
||||||
}),
|
}),
|
||||||
// LineItemsProgressColumn({}),
|
LineItemsProgressColumn({}),
|
||||||
StatusColumn({ model: ModelType.transferorder }),
|
StatusColumn({ model: ModelType.transferorder }),
|
||||||
ProjectCodeColumn({
|
ProjectCodeColumn({
|
||||||
defaultVisible: false
|
defaultVisible: false
|
||||||
|
|||||||
Reference in New Issue
Block a user