mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
PUI tweaks (#7144)
* Default progress bars a bit thicker * Implement useFilters hook - Adds "project code" filter for order tables * Add "responsible" filters to backend * Add more filters to tables * Bump API version * Typo fix * Tweak PartTable * Tweaks * remove unused imports
This commit is contained in:
parent
1ef9512f18
commit
a9b932cc32
@ -1,11 +1,14 @@
|
||||
"""InvenTree API version information."""
|
||||
|
||||
# InvenTree API version
|
||||
INVENTREE_API_VERSION = 192
|
||||
INVENTREE_API_VERSION = 193
|
||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||
|
||||
INVENTREE_API_TEXT = """
|
||||
|
||||
v193 - 2024-04-30 : https://github.com/inventree/InvenTree/pull/7144
|
||||
- Adds "assigned_to" filter to PurchaseOrder / SalesOrder / ReturnOrder API endpoints
|
||||
|
||||
v192 - 2024-04-23 : https://github.com/inventree/InvenTree/pull/7106
|
||||
- Adds 'trackable' ordering option to BuildLineLabel API endpoint
|
||||
|
||||
|
@ -148,6 +148,10 @@ class OrderFilter(rest_filters.FilterSet):
|
||||
return queryset.exclude(project_code=None)
|
||||
return queryset.filter(project_code=None)
|
||||
|
||||
assigned_to = rest_filters.ModelChoiceFilter(
|
||||
queryset=Owner.objects.all(), field_name='responsible'
|
||||
)
|
||||
|
||||
|
||||
class LineItemFilter(rest_filters.FilterSet):
|
||||
"""Base class for custom API filters for order line item list(s)."""
|
||||
|
@ -77,16 +77,18 @@ class AbstractOrderSerializer(serializers.Serializer):
|
||||
"""Abstract serializer class which provides fields common to all order types."""
|
||||
|
||||
# Number of line items in this order
|
||||
line_items = serializers.IntegerField(read_only=True)
|
||||
line_items = serializers.IntegerField(read_only=True, label=_('Line Items'))
|
||||
|
||||
# Number of completed line items (this is an annotated field)
|
||||
completed_lines = serializers.IntegerField(read_only=True)
|
||||
completed_lines = serializers.IntegerField(
|
||||
read_only=True, label=_('Completed Lines')
|
||||
)
|
||||
|
||||
# Human-readable status text (read-only)
|
||||
status_text = serializers.CharField(source='get_status_display', read_only=True)
|
||||
|
||||
# status field cannot be set directly
|
||||
status = serializers.IntegerField(read_only=True)
|
||||
status = serializers.IntegerField(read_only=True, label=_('Order Status'))
|
||||
|
||||
# Reference string is *required*
|
||||
reference = serializers.CharField(required=True)
|
||||
@ -114,7 +116,9 @@ class AbstractOrderSerializer(serializers.Serializer):
|
||||
|
||||
barcode_hash = serializers.CharField(read_only=True)
|
||||
|
||||
creation_date = serializers.DateField(required=False, allow_null=True)
|
||||
creation_date = serializers.DateField(
|
||||
required=False, allow_null=True, label=_('Creation Date')
|
||||
)
|
||||
|
||||
def validate_reference(self, reference):
|
||||
"""Custom validation for the reference field."""
|
||||
|
@ -6,6 +6,7 @@ export type ProgressBarProps = {
|
||||
maximum?: number;
|
||||
label?: string;
|
||||
progressLabel?: boolean;
|
||||
size?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -31,8 +32,8 @@ export function ProgressBar(props: ProgressBarProps) {
|
||||
<Progress
|
||||
value={progress}
|
||||
color={progress < 100 ? 'orange' : progress > 100 ? 'blue' : 'green'}
|
||||
size="sm"
|
||||
radius="xs"
|
||||
size={props.size ?? 'md'}
|
||||
radius="sm"
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
|
91
src/frontend/src/hooks/UseFilter.tsx
Normal file
91
src/frontend/src/hooks/UseFilter.tsx
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Custom hook for retrieving a list of items from the API,
|
||||
* and turning them into "filters" for use in the frontend table framework.
|
||||
*/
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import { api } from '../App';
|
||||
import { ApiEndpoints } from '../enums/ApiEndpoints';
|
||||
import { resolveItem } from '../functions/conversion';
|
||||
import { apiUrl } from '../states/ApiState';
|
||||
import { TableFilterChoice } from '../tables/Filter';
|
||||
|
||||
type UseFilterProps = {
|
||||
url: string;
|
||||
method?: 'GET' | 'POST' | 'OPTIONS';
|
||||
params?: any;
|
||||
accessor?: string;
|
||||
transform: (item: any) => TableFilterChoice;
|
||||
};
|
||||
|
||||
export function useFilters(props: UseFilterProps) {
|
||||
const query = useQuery({
|
||||
enabled: true,
|
||||
queryKey: [props.url, props.method, props.params],
|
||||
queryFn: async () => {
|
||||
return await api
|
||||
.request({
|
||||
url: props.url,
|
||||
method: props.method || 'GET',
|
||||
params: props.params
|
||||
})
|
||||
.then((response) => {
|
||||
let data = resolveItem(response, props.accessor ?? 'data');
|
||||
|
||||
if (data == null || data == undefined) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return data;
|
||||
})
|
||||
.catch((error) => []);
|
||||
}
|
||||
});
|
||||
|
||||
const choices: TableFilterChoice[] = useMemo(() => {
|
||||
return query.data?.map(props.transform) ?? [];
|
||||
}, [props.transform, query.data]);
|
||||
|
||||
const refresh = useCallback(() => {
|
||||
query.refetch();
|
||||
}, []);
|
||||
|
||||
return {
|
||||
choices,
|
||||
refresh
|
||||
};
|
||||
}
|
||||
|
||||
// Provide list of project code filters
|
||||
export function useProjectCodeFilters() {
|
||||
return useFilters({
|
||||
url: apiUrl(ApiEndpoints.project_code_list),
|
||||
transform: (item) => ({
|
||||
value: item.pk,
|
||||
label: item.code
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// Provide list of user filters
|
||||
export function useUserFilters() {
|
||||
return useFilters({
|
||||
url: apiUrl(ApiEndpoints.user_list),
|
||||
transform: (item) => ({
|
||||
value: item.pk,
|
||||
label: item.username
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// Provide list of owner filters
|
||||
export function useOwnerFilters() {
|
||||
return useFilters({
|
||||
url: apiUrl(ApiEndpoints.owner_list),
|
||||
transform: (item) => ({
|
||||
value: item.pk,
|
||||
label: item.name
|
||||
})
|
||||
});
|
||||
}
|
@ -10,6 +10,11 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||
import { ModelType } from '../../enums/ModelType';
|
||||
import { UserRoles } from '../../enums/Roles';
|
||||
import { useBuildOrderFields } from '../../forms/BuildForms';
|
||||
import {
|
||||
useOwnerFilters,
|
||||
useProjectCodeFilters,
|
||||
useUserFilters
|
||||
} from '../../hooks/UseFilter';
|
||||
import { useCreateApiFormModal } from '../../hooks/UseForm';
|
||||
import { useTable } from '../../hooks/UseTable';
|
||||
import { apiUrl } from '../../states/ApiState';
|
||||
@ -92,6 +97,10 @@ export function BuildOrderTable({
|
||||
}) {
|
||||
const tableColumns = useMemo(() => buildOrderTableColumns(), []);
|
||||
|
||||
const projectCodeFilters = useProjectCodeFilters();
|
||||
const userFilters = useUserFilters();
|
||||
const responsibleFilters = useOwnerFilters();
|
||||
|
||||
const tableFilters: TableFilter[] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
@ -115,18 +124,36 @@ export function BuildOrderTable({
|
||||
type: 'boolean',
|
||||
label: t`Assigned to me`,
|
||||
description: t`Show orders assigned to me`
|
||||
},
|
||||
{
|
||||
name: 'project_code',
|
||||
label: t`Project Code`,
|
||||
description: t`Filter by project code`,
|
||||
choices: projectCodeFilters.choices
|
||||
},
|
||||
{
|
||||
name: 'has_project_code',
|
||||
label: t`Has Project Code`,
|
||||
description: t`Filter by whether the purchase order has a project code`
|
||||
},
|
||||
{
|
||||
name: 'issued_by',
|
||||
label: t`Issued By`,
|
||||
description: t`Filter by user who issued this order`,
|
||||
choices: userFilters.choices
|
||||
},
|
||||
{
|
||||
name: 'assigned_to',
|
||||
label: t`Responsible`,
|
||||
description: t`Filter by responsible owner`,
|
||||
choices: responsibleFilters.choices
|
||||
}
|
||||
// TODO: 'assigned to' filter
|
||||
// TODO: 'issued by' filter
|
||||
// {
|
||||
// name: 'has_project_code',
|
||||
// title: t`Has Project Code`,
|
||||
// description: t`Show orders with project code`,
|
||||
// }
|
||||
// TODO: 'has project code' filter (see table_filters.js)
|
||||
// TODO: 'project code' filter (see table_filters.js)
|
||||
];
|
||||
}, []);
|
||||
}, [
|
||||
projectCodeFilters.choices,
|
||||
userFilters.choices,
|
||||
responsibleFilters.choices
|
||||
]);
|
||||
|
||||
const user = useUserState();
|
||||
|
||||
|
@ -116,13 +116,13 @@ export default function BuildOutputTable({
|
||||
/>,
|
||||
<ActionButton
|
||||
tooltip={t`Scrap selected outputs`}
|
||||
icon={<InvenTreeIcon icon="cancel" />}
|
||||
icon={<InvenTreeIcon icon="delete" />}
|
||||
color="red"
|
||||
disabled={!table.hasSelectedRecords}
|
||||
/>,
|
||||
<ActionButton
|
||||
tooltip={t`Cancel selected outputs`}
|
||||
icon={<InvenTreeIcon icon="delete" />}
|
||||
icon={<InvenTreeIcon icon="cancel" />}
|
||||
color="red"
|
||||
disabled={!table.hasSelectedRecords}
|
||||
/>
|
||||
@ -153,14 +153,14 @@ export default function BuildOutputTable({
|
||||
{
|
||||
title: t`Scrap`,
|
||||
tooltip: t`Scrap build output`,
|
||||
color: 'red',
|
||||
icon: <InvenTreeIcon icon="cancel" />
|
||||
icon: <InvenTreeIcon icon="delete" />,
|
||||
color: 'red'
|
||||
},
|
||||
{
|
||||
title: t`Delete`,
|
||||
tooltip: t`Delete build output`,
|
||||
color: 'red',
|
||||
icon: <InvenTreeIcon icon="delete" />
|
||||
title: t`Cancel`,
|
||||
tooltip: t`Cancel build output`,
|
||||
icon: <InvenTreeIcon icon="cancel" />,
|
||||
color: 'red'
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { Group, Text } from '@mantine/core';
|
||||
import { ReactNode, useMemo } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { AddItemButton } from '../../components/buttons/AddItemButton';
|
||||
import { formatPriceRange } from '../../defaults/formatters';
|
||||
@ -9,7 +8,6 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||
import { ModelType } from '../../enums/ModelType';
|
||||
import { UserRoles } from '../../enums/Roles';
|
||||
import { usePartFields } from '../../forms/PartForms';
|
||||
import { shortenString } from '../../functions/tables';
|
||||
import { useCreateApiFormModal } from '../../hooks/UseForm';
|
||||
import { useTable } from '../../hooks/UseTable';
|
||||
import { apiUrl } from '../../states/ApiState';
|
||||
@ -43,13 +41,7 @@ function partTableColumns(): TableColumn[] {
|
||||
{
|
||||
accessor: 'category',
|
||||
sortable: true,
|
||||
|
||||
render: function (record: any) {
|
||||
// TODO: Link to the category detail page
|
||||
return shortenString({
|
||||
str: record.category_detail?.pathstring
|
||||
});
|
||||
}
|
||||
render: (record: any) => record.category_detail?.pathstring
|
||||
},
|
||||
{
|
||||
accessor: 'total_in_stock',
|
||||
|
@ -8,6 +8,7 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||
import { ModelType } from '../../enums/ModelType';
|
||||
import { UserRoles } from '../../enums/Roles';
|
||||
import { usePurchaseOrderFields } from '../../forms/PurchaseOrderForms';
|
||||
import { useOwnerFilters, useProjectCodeFilters } from '../../hooks/UseFilter';
|
||||
import { useCreateApiFormModal } from '../../hooks/UseForm';
|
||||
import { useTable } from '../../hooks/UseTable';
|
||||
import { apiUrl } from '../../states/ApiState';
|
||||
@ -44,6 +45,9 @@ export function PurchaseOrderTable({
|
||||
const table = useTable('purchase-order');
|
||||
const user = useUserState();
|
||||
|
||||
const projectCodeFilters = useProjectCodeFilters();
|
||||
const responsibleFilters = useOwnerFilters();
|
||||
|
||||
const tableFilters: TableFilter[] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
@ -54,11 +58,26 @@ export function PurchaseOrderTable({
|
||||
},
|
||||
OutstandingFilter(),
|
||||
OverdueFilter(),
|
||||
AssignedToMeFilter()
|
||||
// TODO: has_project_code
|
||||
// TODO: project_code
|
||||
AssignedToMeFilter(),
|
||||
{
|
||||
name: 'project_code',
|
||||
label: t`Project Code`,
|
||||
description: t`Filter by project code`,
|
||||
choices: projectCodeFilters.choices
|
||||
},
|
||||
{
|
||||
name: 'has_project_code',
|
||||
label: t`Has Project Code`,
|
||||
description: t`Filter by whether the purchase order has a project code`
|
||||
},
|
||||
{
|
||||
name: 'assigned_to',
|
||||
label: t`Responsible`,
|
||||
description: t`Filter by responsible owner`,
|
||||
choices: responsibleFilters.choices
|
||||
}
|
||||
];
|
||||
}, []);
|
||||
}, [projectCodeFilters.choices, responsibleFilters.choices]);
|
||||
|
||||
const tableColumns = useMemo(() => {
|
||||
return [
|
||||
|
@ -8,6 +8,7 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||
import { ModelType } from '../../enums/ModelType';
|
||||
import { UserRoles } from '../../enums/Roles';
|
||||
import { useReturnOrderFields } from '../../forms/SalesOrderForms';
|
||||
import { useOwnerFilters, useProjectCodeFilters } from '../../hooks/UseFilter';
|
||||
import { useCreateApiFormModal } from '../../hooks/UseForm';
|
||||
import { useTable } from '../../hooks/UseTable';
|
||||
import { apiUrl } from '../../states/ApiState';
|
||||
@ -35,6 +36,9 @@ export function ReturnOrderTable({ params }: { params?: any }) {
|
||||
const table = useTable('return-orders');
|
||||
const user = useUserState();
|
||||
|
||||
const projectCodeFilters = useProjectCodeFilters();
|
||||
const responsibleFilters = useOwnerFilters();
|
||||
|
||||
const tableFilters: TableFilter[] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
@ -45,9 +49,26 @@ export function ReturnOrderTable({ params }: { params?: any }) {
|
||||
},
|
||||
OutstandingFilter(),
|
||||
OverdueFilter(),
|
||||
AssignedToMeFilter()
|
||||
AssignedToMeFilter(),
|
||||
{
|
||||
name: 'project_code',
|
||||
label: t`Project Code`,
|
||||
description: t`Filter by project code`,
|
||||
choices: projectCodeFilters.choices
|
||||
},
|
||||
{
|
||||
name: 'has_project_code',
|
||||
label: t`Has Project Code`,
|
||||
description: t`Filter by whether the purchase order has a project code`
|
||||
},
|
||||
{
|
||||
name: 'assigned_to',
|
||||
label: t`Responsible`,
|
||||
description: t`Filter by responsible owner`,
|
||||
choices: responsibleFilters.choices
|
||||
}
|
||||
];
|
||||
}, []);
|
||||
}, [projectCodeFilters.choices, responsibleFilters.choices]);
|
||||
|
||||
const tableColumns = useMemo(() => {
|
||||
return [
|
||||
|
@ -8,6 +8,7 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||
import { ModelType } from '../../enums/ModelType';
|
||||
import { UserRoles } from '../../enums/Roles';
|
||||
import { useSalesOrderFields } from '../../forms/SalesOrderForms';
|
||||
import { useOwnerFilters, useProjectCodeFilters } from '../../hooks/UseFilter';
|
||||
import { useCreateApiFormModal } from '../../hooks/UseForm';
|
||||
import { useTable } from '../../hooks/UseTable';
|
||||
import { apiUrl } from '../../states/ApiState';
|
||||
@ -41,6 +42,9 @@ export function SalesOrderTable({
|
||||
const table = useTable('sales-order');
|
||||
const user = useUserState();
|
||||
|
||||
const projectCodeFilters = useProjectCodeFilters();
|
||||
const responsibleFilters = useOwnerFilters();
|
||||
|
||||
const tableFilters: TableFilter[] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
@ -51,11 +55,26 @@ export function SalesOrderTable({
|
||||
},
|
||||
OutstandingFilter(),
|
||||
OverdueFilter(),
|
||||
AssignedToMeFilter()
|
||||
// TODO: has_project_code
|
||||
// TODO: project_code
|
||||
AssignedToMeFilter(),
|
||||
{
|
||||
name: 'project_code',
|
||||
label: t`Project Code`,
|
||||
description: t`Filter by project code`,
|
||||
choices: projectCodeFilters.choices
|
||||
},
|
||||
{
|
||||
name: 'has_project_code',
|
||||
label: t`Has Project Code`,
|
||||
description: t`Filter by whether the purchase order has a project code`
|
||||
},
|
||||
{
|
||||
name: 'assigned_to',
|
||||
label: t`Responsible`,
|
||||
description: t`Filter by responsible owner`,
|
||||
choices: responsibleFilters.choices
|
||||
}
|
||||
];
|
||||
}, []);
|
||||
}, [projectCodeFilters.choices, responsibleFilters.choices]);
|
||||
|
||||
const salesOrderFields = useSalesOrderFields();
|
||||
|
||||
|
@ -4,7 +4,7 @@ import { ReactNode, useMemo } from 'react';
|
||||
|
||||
import { AddItemButton } from '../../components/buttons/AddItemButton';
|
||||
import { ActionDropdown } from '../../components/items/ActionDropdown';
|
||||
import { formatCurrency, renderDate } from '../../defaults/formatters';
|
||||
import { formatCurrency } from '../../defaults/formatters';
|
||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||
import { ModelType } from '../../enums/ModelType';
|
||||
import { UserRoles } from '../../enums/Roles';
|
||||
|
Loading…
Reference in New Issue
Block a user