mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +00:00 
			
		
		
		
	Dynamic filters (#9290)
* Add attributes to TableFilter type def * Refactoring * Refactor ProjectCodeFilter * Provide simple string rendering of a dynamic filter * Refactor ResponsibleFilter * Further refactoring * More refactoring * Fix placeholder value
This commit is contained in:
		| @@ -6,9 +6,7 @@ import { useQuery } from '@tanstack/react-query'; | |||||||
| import { useCallback, useMemo } from 'react'; | import { useCallback, useMemo } from 'react'; | ||||||
|  |  | ||||||
| import { useApi } from '../contexts/ApiContext'; | import { useApi } from '../contexts/ApiContext'; | ||||||
| import { ApiEndpoints } from '../enums/ApiEndpoints'; |  | ||||||
| import { resolveItem } from '../functions/conversion'; | import { resolveItem } from '../functions/conversion'; | ||||||
| import { apiUrl } from '../states/ApiState'; |  | ||||||
| import type { TableFilterChoice } from '../tables/Filter'; | import type { TableFilterChoice } from '../tables/Filter'; | ||||||
|  |  | ||||||
| type UseFilterProps = { | type UseFilterProps = { | ||||||
| @@ -67,42 +65,3 @@ export function useFilters(props: UseFilterProps) { | |||||||
|     refresh |     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), |  | ||||||
|     params: { |  | ||||||
|       is_active: true |  | ||||||
|     }, |  | ||||||
|     transform: (item) => ({ |  | ||||||
|       value: item.pk, |  | ||||||
|       label: item.username |  | ||||||
|     }) |  | ||||||
|   }); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Provide list of owner filters |  | ||||||
| export function useOwnerFilters() { |  | ||||||
|   return useFilters({ |  | ||||||
|     url: apiUrl(ApiEndpoints.owner_list), |  | ||||||
|     params: { |  | ||||||
|       is_active: true |  | ||||||
|     }, |  | ||||||
|     transform: (item) => ({ |  | ||||||
|       value: item.pk, |  | ||||||
|       label: item.name |  | ||||||
|     }) |  | ||||||
|   }); |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -4,7 +4,9 @@ import type { | |||||||
|   StatusCodeInterface, |   StatusCodeInterface, | ||||||
|   StatusCodeListInterface |   StatusCodeListInterface | ||||||
| } from '../components/render/StatusRenderer'; | } from '../components/render/StatusRenderer'; | ||||||
| import type { ModelType } from '../enums/ModelType'; | import { ApiEndpoints } from '../enums/ApiEndpoints'; | ||||||
|  | import { ModelType } from '../enums/ModelType'; | ||||||
|  | import { apiUrl } from '../states/ApiState'; | ||||||
| import { useGlobalSettingsState } from '../states/SettingsState'; | import { useGlobalSettingsState } from '../states/SettingsState'; | ||||||
| import { type StatusLookup, useGlobalStatusState } from '../states/StatusState'; | import { type StatusLookup, useGlobalStatusState } from '../states/StatusState'; | ||||||
|  |  | ||||||
| @@ -23,8 +25,9 @@ export type TableFilterChoice = { | |||||||
|  * choice: A filter which allows selection from a list of (supplied) |  * choice: A filter which allows selection from a list of (supplied) | ||||||
|  * date: A filter which allows selection from a date input |  * date: A filter which allows selection from a date input | ||||||
|  * text: A filter which allows raw text input |  * text: A filter which allows raw text input | ||||||
|  |  * api: A filter which fetches its options from an API endpoint | ||||||
|  */ |  */ | ||||||
| export type TableFilterType = 'boolean' | 'choice' | 'date' | 'text'; | export type TableFilterType = 'boolean' | 'choice' | 'date' | 'text' | 'api'; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Interface for the table filter type. Provides a number of options for selecting filter value: |  * Interface for the table filter type. Provides a number of options for selecting filter value: | ||||||
| @@ -39,6 +42,9 @@ export type TableFilterType = 'boolean' | 'choice' | 'date' | 'text'; | |||||||
|  * value: The current value of the filter |  * value: The current value of the filter | ||||||
|  * displayValue: The current display value of the filter |  * displayValue: The current display value of the filter | ||||||
|  * active: Whether the filter is active (false = hidden, not used) |  * active: Whether the filter is active (false = hidden, not used) | ||||||
|  |  * apiUrl: The API URL to use for fetching dynamic filter options | ||||||
|  |  * model: The model type to use for fetching dynamic filter options | ||||||
|  |  * modelRenderer: A function to render a simple text version of the model type | ||||||
|  */ |  */ | ||||||
| export type TableFilter = { | export type TableFilter = { | ||||||
|   name: string; |   name: string; | ||||||
| @@ -51,6 +57,9 @@ export type TableFilter = { | |||||||
|   value?: any; |   value?: any; | ||||||
|   displayValue?: any; |   displayValue?: any; | ||||||
|   active?: boolean; |   active?: boolean; | ||||||
|  |   apiUrl?: string; | ||||||
|  |   model?: ModelType; | ||||||
|  |   modelRenderer?: (instance: any) => string; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -247,9 +256,7 @@ export function OrderStatusFilter({ | |||||||
|   }; |   }; | ||||||
| } | } | ||||||
|  |  | ||||||
| export function ProjectCodeFilter({ | export function ProjectCodeFilter(): TableFilter { | ||||||
|   choices |  | ||||||
| }: { choices: TableFilterChoice[] }): TableFilter { |  | ||||||
|   const globalSettings = useGlobalSettingsState.getState(); |   const globalSettings = useGlobalSettingsState.getState(); | ||||||
|   const enabled = globalSettings.isSet('PROJECT_CODES_ENABLED', true); |   const enabled = globalSettings.isSet('PROJECT_CODES_ENABLED', true); | ||||||
|  |  | ||||||
| @@ -258,28 +265,73 @@ export function ProjectCodeFilter({ | |||||||
|     label: t`Project Code`, |     label: t`Project Code`, | ||||||
|     description: t`Filter by project code`, |     description: t`Filter by project code`, | ||||||
|     active: enabled, |     active: enabled, | ||||||
|     choices: choices |     type: 'api', | ||||||
|  |     apiUrl: apiUrl(ApiEndpoints.project_code_list), | ||||||
|  |     model: ModelType.projectcode, | ||||||
|  |     modelRenderer: (instance) => instance.code | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
|  |  | ||||||
| export function ResponsibleFilter({ | export function OwnerFilter({ | ||||||
|   choices |   name, | ||||||
| }: { choices: TableFilterChoice[] }): TableFilter { |   label, | ||||||
|  |   description | ||||||
|  | }: { | ||||||
|  |   name: string; | ||||||
|  |   label: string; | ||||||
|  |   description: string; | ||||||
|  | }): TableFilter { | ||||||
|   return { |   return { | ||||||
|  |     name: name, | ||||||
|  |     label: label, | ||||||
|  |     description: description, | ||||||
|  |     type: 'api', | ||||||
|  |     apiUrl: apiUrl(ApiEndpoints.owner_list), | ||||||
|  |     model: ModelType.owner, | ||||||
|  |     modelRenderer: (instance: any) => instance.name | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function ResponsibleFilter(): TableFilter { | ||||||
|  |   return OwnerFilter({ | ||||||
|     name: 'assigned_to', |     name: 'assigned_to', | ||||||
|     label: t`Responsible`, |     label: t`Responsible`, | ||||||
|     description: t`Filter by responsible owner`, |     description: t`Filter by responsible owner` | ||||||
|     choices: choices |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function UserFilter({ | ||||||
|  |   name, | ||||||
|  |   label, | ||||||
|  |   description | ||||||
|  | }: { | ||||||
|  |   name?: string; | ||||||
|  |   label?: string; | ||||||
|  |   description?: string; | ||||||
|  | }): TableFilter { | ||||||
|  |   return { | ||||||
|  |     name: name ?? 'user', | ||||||
|  |     label: label ?? t`User`, | ||||||
|  |     description: description ?? t`Filter by user`, | ||||||
|  |     type: 'api', | ||||||
|  |     apiUrl: apiUrl(ApiEndpoints.user_list), | ||||||
|  |     model: ModelType.user, | ||||||
|  |     modelRenderer: (instance: any) => instance.username | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
|  |  | ||||||
| export function CreatedByFilter({ | export function CreatedByFilter(): TableFilter { | ||||||
|   choices |   return UserFilter({ | ||||||
| }: { choices: TableFilterChoice[] }): TableFilter { |  | ||||||
|   return { |  | ||||||
|     name: 'created_by', |     name: 'created_by', | ||||||
|     label: t`Created By`, |     label: t`Created By`, | ||||||
|     description: t`Filter by user who created the order`, |     description: t`Filter by user who created the order` | ||||||
|     choices: choices |   }); | ||||||
|   }; | } | ||||||
|  |  | ||||||
|  | export function IssuedByFilter(): TableFilter { | ||||||
|  |   return UserFilter({ | ||||||
|  |     name: 'issued_by', | ||||||
|  |     label: t`Issued By`, | ||||||
|  |     description: t`Filter by user who issued the order` | ||||||
|  |   }); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -19,6 +19,7 @@ import dayjs from 'dayjs'; | |||||||
| import { useCallback, useEffect, useMemo, useState } from 'react'; | import { useCallback, useEffect, useMemo, useState } from 'react'; | ||||||
|  |  | ||||||
| import { IconCheck } from '@tabler/icons-react'; | import { IconCheck } from '@tabler/icons-react'; | ||||||
|  | import { StandaloneField } from '../components/forms/StandaloneField'; | ||||||
| import { StylishText } from '../components/items/StylishText'; | import { StylishText } from '../components/items/StylishText'; | ||||||
| import type { TableState } from '../hooks/UseTable'; | import type { TableState } from '../hooks/UseTable'; | ||||||
| import { | import { | ||||||
| @@ -64,13 +65,15 @@ function FilterItem({ | |||||||
| } | } | ||||||
|  |  | ||||||
| function FilterElement({ | function FilterElement({ | ||||||
|   filterType, |   filterName, | ||||||
|  |   filterProps, | ||||||
|   valueOptions, |   valueOptions, | ||||||
|   onValueChange |   onValueChange | ||||||
| }: { | }: { | ||||||
|   filterType: TableFilterType; |   filterName: string; | ||||||
|  |   filterProps: TableFilter; | ||||||
|   valueOptions: TableFilterChoice[]; |   valueOptions: TableFilterChoice[]; | ||||||
|   onValueChange: (value: string | null) => void; |   onValueChange: (value: string | null, displayValue?: any) => void; | ||||||
| }) { | }) { | ||||||
|   const setDateValue = useCallback( |   const setDateValue = useCallback( | ||||||
|     (value: DateValue) => { |     (value: DateValue) => { | ||||||
| @@ -86,7 +89,23 @@ function FilterElement({ | |||||||
|  |  | ||||||
|   const [textValue, setTextValue] = useState<string>(''); |   const [textValue, setTextValue] = useState<string>(''); | ||||||
|  |  | ||||||
|   switch (filterType) { |   switch (filterProps.type) { | ||||||
|  |     case 'api': | ||||||
|  |       return ( | ||||||
|  |         <StandaloneField | ||||||
|  |           fieldName={`filter_value_${filterName}`} | ||||||
|  |           fieldDefinition={{ | ||||||
|  |             field_type: 'related field', | ||||||
|  |             api_url: filterProps.apiUrl, | ||||||
|  |             placeholder: t`Select filter value`, | ||||||
|  |             model: filterProps.model, | ||||||
|  |             label: t`Select filter value`, | ||||||
|  |             onValueChange: (value: any, instance: any) => { | ||||||
|  |               onValueChange(value, filterProps.modelRenderer?.(instance)); | ||||||
|  |             } | ||||||
|  |           }} | ||||||
|  |         /> | ||||||
|  |       ); | ||||||
|     case 'text': |     case 'text': | ||||||
|       return ( |       return ( | ||||||
|         <TextInput |         <TextInput | ||||||
| @@ -124,7 +143,7 @@ function FilterElement({ | |||||||
|       return ( |       return ( | ||||||
|         <Select |         <Select | ||||||
|           data={valueOptions} |           data={valueOptions} | ||||||
|           searchable={filterType != 'boolean'} |           searchable={filterProps.type == 'choice'} | ||||||
|           label={t`Value`} |           label={t`Value`} | ||||||
|           placeholder={t`Select filter value`} |           placeholder={t`Select filter value`} | ||||||
|           onChange={(value: string | null) => onValueChange(value)} |           onChange={(value: string | null) => onValueChange(value)} | ||||||
| @@ -177,23 +196,32 @@ function FilterAddGroup({ | |||||||
|     return getTableFilterOptions(filter); |     return getTableFilterOptions(filter); | ||||||
|   }, [selectedFilter]); |   }, [selectedFilter]); | ||||||
|  |  | ||||||
|   // Determine the "type" of filter (default = boolean) |   // Determine the filter "type" - if it is not supplied | ||||||
|   const filterType: TableFilterType = useMemo(() => { |   const getFilterType = (filter: TableFilter): TableFilterType => { | ||||||
|     const filter = availableFilters?.find((flt) => flt.name === selectedFilter); |     if (filter.type) { | ||||||
|  |  | ||||||
|     if (filter?.type) { |  | ||||||
|       return filter.type; |       return filter.type; | ||||||
|     } else if (filter?.choices) { |     } else if (filter.apiUrl && filter.model) { | ||||||
|       // If choices are provided, it is a choice filter |       return 'api'; | ||||||
|  |     } else if (filter.choices || filter.choiceFunction) { | ||||||
|       return 'choice'; |       return 'choice'; | ||||||
|     } else { |     } else { | ||||||
|       // Default fallback |  | ||||||
|       return 'boolean'; |       return 'boolean'; | ||||||
|     } |     } | ||||||
|   }, [selectedFilter]); |   }; | ||||||
|  |  | ||||||
|  |   // Extract filter definition | ||||||
|  |   const filterProps: TableFilter | undefined = useMemo(() => { | ||||||
|  |     const filter = availableFilters?.find((flt) => flt.name === selectedFilter); | ||||||
|  |  | ||||||
|  |     if (filter) { | ||||||
|  |       filter.type = getFilterType(filter); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return filter; | ||||||
|  |   }, [availableFilters, selectedFilter]); | ||||||
|  |  | ||||||
|   const setSelectedValue = useCallback( |   const setSelectedValue = useCallback( | ||||||
|     (value: string | null) => { |     (value: string | null, displayValue?: any) => { | ||||||
|       // Find the matching filter |       // Find the matching filter | ||||||
|       const filter: TableFilter | undefined = availableFilters.find( |       const filter: TableFilter | undefined = availableFilters.find( | ||||||
|         (flt) => flt.name === selectedFilter |         (flt) => flt.name === selectedFilter | ||||||
| @@ -211,7 +239,8 @@ function FilterAddGroup({ | |||||||
|       const newFilter: TableFilter = { |       const newFilter: TableFilter = { | ||||||
|         ...filter, |         ...filter, | ||||||
|         value: value, |         value: value, | ||||||
|         displayValue: valueOptions.find((v) => v.value === value)?.label |         displayValue: | ||||||
|  |           displayValue ?? valueOptions.find((v) => v.value === value)?.label | ||||||
|       }; |       }; | ||||||
|  |  | ||||||
|       tableState.setActiveFilters([...filters, newFilter]); |       tableState.setActiveFilters([...filters, newFilter]); | ||||||
| @@ -233,9 +262,10 @@ function FilterAddGroup({ | |||||||
|         onChange={(value: string | null) => setSelectedFilter(value)} |         onChange={(value: string | null) => setSelectedFilter(value)} | ||||||
|         maxDropdownHeight={800} |         maxDropdownHeight={800} | ||||||
|       /> |       /> | ||||||
|       {selectedFilter && ( |       {selectedFilter && filterProps && ( | ||||||
|         <FilterElement |         <FilterElement | ||||||
|           filterType={filterType} |           filterName={selectedFilter} | ||||||
|  |           filterProps={filterProps} | ||||||
|           valueOptions={valueOptions} |           valueOptions={valueOptions} | ||||||
|           onValueChange={setSelectedValue} |           onValueChange={setSelectedValue} | ||||||
|         /> |         /> | ||||||
|   | |||||||
| @@ -8,13 +8,6 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints'; | |||||||
| import { ModelType } from '../../enums/ModelType'; | import { ModelType } from '../../enums/ModelType'; | ||||||
| import { UserRoles } from '../../enums/Roles'; | import { UserRoles } from '../../enums/Roles'; | ||||||
| import { useBuildOrderFields } from '../../forms/BuildForms'; | import { useBuildOrderFields } from '../../forms/BuildForms'; | ||||||
| import { shortenString } from '../../functions/tables'; |  | ||||||
| import { |  | ||||||
|   useFilters, |  | ||||||
|   useOwnerFilters, |  | ||||||
|   useProjectCodeFilters, |  | ||||||
|   useUserFilters |  | ||||||
| } from '../../hooks/UseFilter'; |  | ||||||
| import { useCreateApiFormModal } from '../../hooks/UseForm'; | import { useCreateApiFormModal } from '../../hooks/UseForm'; | ||||||
| import { useTable } from '../../hooks/UseTable'; | import { useTable } from '../../hooks/UseTable'; | ||||||
| import { apiUrl } from '../../states/ApiState'; | import { apiUrl } from '../../states/ApiState'; | ||||||
| @@ -37,6 +30,7 @@ import { | |||||||
|   CreatedAfterFilter, |   CreatedAfterFilter, | ||||||
|   CreatedBeforeFilter, |   CreatedBeforeFilter, | ||||||
|   HasProjectCodeFilter, |   HasProjectCodeFilter, | ||||||
|  |   IssuedByFilter, | ||||||
|   MaxDateFilter, |   MaxDateFilter, | ||||||
|   MinDateFilter, |   MinDateFilter, | ||||||
|   OrderStatusFilter, |   OrderStatusFilter, | ||||||
| @@ -128,21 +122,6 @@ export function BuildOrderTable({ | |||||||
|     ]; |     ]; | ||||||
|   }, [parentBuildId]); |   }, [parentBuildId]); | ||||||
|  |  | ||||||
|   const projectCodeFilters = useProjectCodeFilters(); |  | ||||||
|   const ownerFilters = useOwnerFilters(); |  | ||||||
|   const userFilters = useUserFilters(); |  | ||||||
|  |  | ||||||
|   const categoryFilters = useFilters({ |  | ||||||
|     url: apiUrl(ApiEndpoints.category_list), |  | ||||||
|     transform: (item) => ({ |  | ||||||
|       value: item.pk, |  | ||||||
|       label: shortenString({ |  | ||||||
|         str: item.pathstring, |  | ||||||
|         len: 50 |  | ||||||
|       }) |  | ||||||
|     }) |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   const tableFilters: TableFilter[] = useMemo(() => { |   const tableFilters: TableFilter[] = useMemo(() => { | ||||||
|     const filters: TableFilter[] = [ |     const filters: TableFilter[] = [ | ||||||
|       OutstandingFilter(), |       OutstandingFilter(), | ||||||
| @@ -171,20 +150,17 @@ export function BuildOrderTable({ | |||||||
|       }, |       }, | ||||||
|       CompletedBeforeFilter(), |       CompletedBeforeFilter(), | ||||||
|       CompletedAfterFilter(), |       CompletedAfterFilter(), | ||||||
|       ProjectCodeFilter({ choices: projectCodeFilters.choices }), |       ProjectCodeFilter(), | ||||||
|       HasProjectCodeFilter(), |       HasProjectCodeFilter(), | ||||||
|       { |       IssuedByFilter(), | ||||||
|         name: 'issued_by', |       ResponsibleFilter(), | ||||||
|         label: t`Issued By`, |  | ||||||
|         description: t`Filter by user who issued this order`, |  | ||||||
|         choices: userFilters.choices |  | ||||||
|       }, |  | ||||||
|       ResponsibleFilter({ choices: ownerFilters.choices }), |  | ||||||
|       { |       { | ||||||
|         name: 'category', |         name: 'category', | ||||||
|         label: t`Category`, |         label: t`Category`, | ||||||
|         description: t`Filter by part category`, |         description: t`Filter by part category`, | ||||||
|         choices: categoryFilters.choices |         apiUrl: apiUrl(ApiEndpoints.category_list), | ||||||
|  |         model: ModelType.partcategory, | ||||||
|  |         modelRenderer: (instance: any) => instance.name | ||||||
|       } |       } | ||||||
|     ]; |     ]; | ||||||
|  |  | ||||||
| @@ -199,13 +175,7 @@ export function BuildOrderTable({ | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     return filters; |     return filters; | ||||||
|   }, [ |   }, [partId]); | ||||||
|     partId, |  | ||||||
|     categoryFilters.choices, |  | ||||||
|     projectCodeFilters.choices, |  | ||||||
|     ownerFilters.choices, |  | ||||||
|     userFilters.choices |  | ||||||
|   ]); |  | ||||||
|  |  | ||||||
|   const user = useUserState(); |   const user = useUserState(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,11 +8,6 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints'; | |||||||
| import { ModelType } from '../../enums/ModelType'; | import { ModelType } from '../../enums/ModelType'; | ||||||
| import { UserRoles } from '../../enums/Roles'; | import { UserRoles } from '../../enums/Roles'; | ||||||
| import { usePurchaseOrderFields } from '../../forms/PurchaseOrderForms'; | import { usePurchaseOrderFields } from '../../forms/PurchaseOrderForms'; | ||||||
| import { |  | ||||||
|   useOwnerFilters, |  | ||||||
|   useProjectCodeFilters, |  | ||||||
|   useUserFilters |  | ||||||
| } from '../../hooks/UseFilter'; |  | ||||||
| import { useCreateApiFormModal } from '../../hooks/UseForm'; | import { useCreateApiFormModal } from '../../hooks/UseForm'; | ||||||
| import { useTable } from '../../hooks/UseTable'; | import { useTable } from '../../hooks/UseTable'; | ||||||
| import { apiUrl } from '../../states/ApiState'; | import { apiUrl } from '../../states/ApiState'; | ||||||
| @@ -66,10 +61,6 @@ export function PurchaseOrderTable({ | |||||||
|   const table = useTable('purchase-order'); |   const table = useTable('purchase-order'); | ||||||
|   const user = useUserState(); |   const user = useUserState(); | ||||||
|  |  | ||||||
|   const projectCodeFilters = useProjectCodeFilters(); |  | ||||||
|   const responsibleFilters = useOwnerFilters(); |  | ||||||
|   const createdByFilters = useUserFilters(); |  | ||||||
|  |  | ||||||
|   const tableFilters: TableFilter[] = useMemo(() => { |   const tableFilters: TableFilter[] = useMemo(() => { | ||||||
|     return [ |     return [ | ||||||
|       OrderStatusFilter({ model: ModelType.purchaseorder }), |       OrderStatusFilter({ model: ModelType.purchaseorder }), | ||||||
| @@ -98,16 +89,12 @@ export function PurchaseOrderTable({ | |||||||
|       }, |       }, | ||||||
|       CompletedBeforeFilter(), |       CompletedBeforeFilter(), | ||||||
|       CompletedAfterFilter(), |       CompletedAfterFilter(), | ||||||
|       ProjectCodeFilter({ choices: projectCodeFilters.choices }), |       ProjectCodeFilter(), | ||||||
|       HasProjectCodeFilter(), |       HasProjectCodeFilter(), | ||||||
|       ResponsibleFilter({ choices: responsibleFilters.choices }), |       ResponsibleFilter(), | ||||||
|       CreatedByFilter({ choices: createdByFilters.choices }) |       CreatedByFilter() | ||||||
|     ]; |     ]; | ||||||
|   }, [ |   }, []); | ||||||
|     projectCodeFilters.choices, |  | ||||||
|     responsibleFilters.choices, |  | ||||||
|     createdByFilters.choices |  | ||||||
|   ]); |  | ||||||
|  |  | ||||||
|   const tableColumns = useMemo(() => { |   const tableColumns = useMemo(() => { | ||||||
|     return [ |     return [ | ||||||
|   | |||||||
| @@ -8,11 +8,6 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints'; | |||||||
| import { ModelType } from '../../enums/ModelType'; | import { ModelType } from '../../enums/ModelType'; | ||||||
| import { UserRoles } from '../../enums/Roles'; | import { UserRoles } from '../../enums/Roles'; | ||||||
| import { useReturnOrderFields } from '../../forms/ReturnOrderForms'; | import { useReturnOrderFields } from '../../forms/ReturnOrderForms'; | ||||||
| import { |  | ||||||
|   useOwnerFilters, |  | ||||||
|   useProjectCodeFilters, |  | ||||||
|   useUserFilters |  | ||||||
| } from '../../hooks/UseFilter'; |  | ||||||
| import { useCreateApiFormModal } from '../../hooks/UseForm'; | import { useCreateApiFormModal } from '../../hooks/UseForm'; | ||||||
| import { useTable } from '../../hooks/UseTable'; | import { useTable } from '../../hooks/UseTable'; | ||||||
| import { apiUrl } from '../../states/ApiState'; | import { apiUrl } from '../../states/ApiState'; | ||||||
| @@ -63,10 +58,6 @@ export function ReturnOrderTable({ | |||||||
|   const table = useTable(!!partId ? 'returnorders-part' : 'returnorders-index'); |   const table = useTable(!!partId ? 'returnorders-part' : 'returnorders-index'); | ||||||
|   const user = useUserState(); |   const user = useUserState(); | ||||||
|  |  | ||||||
|   const projectCodeFilters = useProjectCodeFilters(); |  | ||||||
|   const responsibleFilters = useOwnerFilters(); |  | ||||||
|   const createdByFilters = useUserFilters(); |  | ||||||
|  |  | ||||||
|   const tableFilters: TableFilter[] = useMemo(() => { |   const tableFilters: TableFilter[] = useMemo(() => { | ||||||
|     const filters: TableFilter[] = [ |     const filters: TableFilter[] = [ | ||||||
|       OrderStatusFilter({ model: ModelType.returnorder }), |       OrderStatusFilter({ model: ModelType.returnorder }), | ||||||
| @@ -96,9 +87,9 @@ export function ReturnOrderTable({ | |||||||
|       CompletedBeforeFilter(), |       CompletedBeforeFilter(), | ||||||
|       CompletedAfterFilter(), |       CompletedAfterFilter(), | ||||||
|       HasProjectCodeFilter(), |       HasProjectCodeFilter(), | ||||||
|       ProjectCodeFilter({ choices: projectCodeFilters.choices }), |       ProjectCodeFilter(), | ||||||
|       ResponsibleFilter({ choices: responsibleFilters.choices }), |       ResponsibleFilter(), | ||||||
|       CreatedByFilter({ choices: createdByFilters.choices }) |       CreatedByFilter() | ||||||
|     ]; |     ]; | ||||||
|  |  | ||||||
|     if (!!partId) { |     if (!!partId) { | ||||||
| @@ -111,12 +102,7 @@ export function ReturnOrderTable({ | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     return filters; |     return filters; | ||||||
|   }, [ |   }, [partId]); | ||||||
|     partId, |  | ||||||
|     projectCodeFilters.choices, |  | ||||||
|     responsibleFilters.choices, |  | ||||||
|     createdByFilters.choices |  | ||||||
|   ]); |  | ||||||
|  |  | ||||||
|   const tableColumns = useMemo(() => { |   const tableColumns = useMemo(() => { | ||||||
|     return [ |     return [ | ||||||
|   | |||||||
| @@ -9,11 +9,6 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints'; | |||||||
| import { ModelType } from '../../enums/ModelType'; | import { ModelType } from '../../enums/ModelType'; | ||||||
| import { UserRoles } from '../../enums/Roles'; | import { UserRoles } from '../../enums/Roles'; | ||||||
| import { useSalesOrderFields } from '../../forms/SalesOrderForms'; | import { useSalesOrderFields } from '../../forms/SalesOrderForms'; | ||||||
| import { |  | ||||||
|   useOwnerFilters, |  | ||||||
|   useProjectCodeFilters, |  | ||||||
|   useUserFilters |  | ||||||
| } from '../../hooks/UseFilter'; |  | ||||||
| import { useCreateApiFormModal } from '../../hooks/UseForm'; | import { useCreateApiFormModal } from '../../hooks/UseForm'; | ||||||
| import { useTable } from '../../hooks/UseTable'; | import { useTable } from '../../hooks/UseTable'; | ||||||
| import { apiUrl } from '../../states/ApiState'; | import { apiUrl } from '../../states/ApiState'; | ||||||
| @@ -64,10 +59,6 @@ export function SalesOrderTable({ | |||||||
|   const table = useTable(!!partId ? 'salesorder-part' : 'salesorder-index'); |   const table = useTable(!!partId ? 'salesorder-part' : 'salesorder-index'); | ||||||
|   const user = useUserState(); |   const user = useUserState(); | ||||||
|  |  | ||||||
|   const projectCodeFilters = useProjectCodeFilters(); |  | ||||||
|   const responsibleFilters = useOwnerFilters(); |  | ||||||
|   const createdByFilters = useUserFilters(); |  | ||||||
|  |  | ||||||
|   const tableFilters: TableFilter[] = useMemo(() => { |   const tableFilters: TableFilter[] = useMemo(() => { | ||||||
|     const filters: TableFilter[] = [ |     const filters: TableFilter[] = [ | ||||||
|       OrderStatusFilter({ model: ModelType.salesorder }), |       OrderStatusFilter({ model: ModelType.salesorder }), | ||||||
| @@ -97,9 +88,9 @@ export function SalesOrderTable({ | |||||||
|       CompletedBeforeFilter(), |       CompletedBeforeFilter(), | ||||||
|       CompletedAfterFilter(), |       CompletedAfterFilter(), | ||||||
|       HasProjectCodeFilter(), |       HasProjectCodeFilter(), | ||||||
|       ProjectCodeFilter({ choices: projectCodeFilters.choices }), |       ProjectCodeFilter(), | ||||||
|       ResponsibleFilter({ choices: responsibleFilters.choices }), |       ResponsibleFilter(), | ||||||
|       CreatedByFilter({ choices: createdByFilters.choices }) |       CreatedByFilter() | ||||||
|     ]; |     ]; | ||||||
|  |  | ||||||
|     if (!!partId) { |     if (!!partId) { | ||||||
| @@ -112,12 +103,7 @@ export function SalesOrderTable({ | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     return filters; |     return filters; | ||||||
|   }, [ |   }, [partId]); | ||||||
|     partId, |  | ||||||
|     projectCodeFilters.choices, |  | ||||||
|     responsibleFilters.choices, |  | ||||||
|     createdByFilters.choices |  | ||||||
|   ]); |  | ||||||
|  |  | ||||||
|   const salesOrderFields = useSalesOrderFields({}); |   const salesOrderFields = useSalesOrderFields({}); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -21,14 +21,13 @@ import { RenderUser } from '../../components/render/User'; | |||||||
| import { ApiEndpoints } from '../../enums/ApiEndpoints'; | import { ApiEndpoints } from '../../enums/ApiEndpoints'; | ||||||
| import { UserRoles } from '../../enums/Roles'; | import { UserRoles } from '../../enums/Roles'; | ||||||
| import { shortenString } from '../../functions/tables'; | import { shortenString } from '../../functions/tables'; | ||||||
| import { useUserFilters } from '../../hooks/UseFilter'; |  | ||||||
| import { useDeleteApiFormModal } from '../../hooks/UseForm'; | import { useDeleteApiFormModal } from '../../hooks/UseForm'; | ||||||
| import { useTable } from '../../hooks/UseTable'; | import { useTable } from '../../hooks/UseTable'; | ||||||
| import { apiUrl } from '../../states/ApiState'; | import { apiUrl } from '../../states/ApiState'; | ||||||
| import { useGlobalSettingsState } from '../../states/SettingsState'; | import { useGlobalSettingsState } from '../../states/SettingsState'; | ||||||
| import { useUserState } from '../../states/UserState'; | import { useUserState } from '../../states/UserState'; | ||||||
| import type { TableColumn } from '../Column'; | import type { TableColumn } from '../Column'; | ||||||
| import type { TableFilter } from '../Filter'; | import { type TableFilter, UserFilter } from '../Filter'; | ||||||
| import { InvenTreeTable } from '../InvenTreeTable'; | import { InvenTreeTable } from '../InvenTreeTable'; | ||||||
| import { RowDeleteAction } from '../RowActions'; | import { RowDeleteAction } from '../RowActions'; | ||||||
|  |  | ||||||
| @@ -148,8 +147,6 @@ export default function BarcodeScanHistoryTable() { | |||||||
|  |  | ||||||
|   const globalSettings = useGlobalSettingsState(); |   const globalSettings = useGlobalSettingsState(); | ||||||
|  |  | ||||||
|   const userFilters = useUserFilters(); |  | ||||||
|  |  | ||||||
|   const [opened, { open, close }] = useDisclosure(false); |   const [opened, { open, close }] = useDisclosure(false); | ||||||
|  |  | ||||||
|   const tableColumns: TableColumn[] = useMemo(() => { |   const tableColumns: TableColumn[] = useMemo(() => { | ||||||
| @@ -204,19 +201,14 @@ export default function BarcodeScanHistoryTable() { | |||||||
|  |  | ||||||
|   const filters: TableFilter[] = useMemo(() => { |   const filters: TableFilter[] = useMemo(() => { | ||||||
|     return [ |     return [ | ||||||
|       { |       UserFilter({}), | ||||||
|         name: 'user', |  | ||||||
|         label: t`User`, |  | ||||||
|         choices: userFilters.choices, |  | ||||||
|         description: t`Filter by user` |  | ||||||
|       }, |  | ||||||
|       { |       { | ||||||
|         name: 'result', |         name: 'result', | ||||||
|         label: t`Result`, |         label: t`Result`, | ||||||
|         description: t`Filter by result` |         description: t`Filter by result` | ||||||
|       } |       } | ||||||
|     ]; |     ]; | ||||||
|   }, [userFilters]); |   }, []); | ||||||
|  |  | ||||||
|   const canDelete: boolean = useMemo(() => { |   const canDelete: boolean = useMemo(() => { | ||||||
|     return user.isStaff() && user.hasDeleteRole(UserRoles.admin); |     return user.isStaff() && user.hasDeleteRole(UserRoles.admin); | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ import { RenderUser } from '../../components/render/User'; | |||||||
| import { ApiEndpoints } from '../../enums/ApiEndpoints'; | import { ApiEndpoints } from '../../enums/ApiEndpoints'; | ||||||
| import { ModelType } from '../../enums/ModelType'; | import { ModelType } from '../../enums/ModelType'; | ||||||
| import { dataImporterSessionFields } from '../../forms/ImporterForms'; | import { dataImporterSessionFields } from '../../forms/ImporterForms'; | ||||||
| import { useFilters, useUserFilters } from '../../hooks/UseFilter'; | import { useFilters } from '../../hooks/UseFilter'; | ||||||
| import { | import { | ||||||
|   useCreateApiFormModal, |   useCreateApiFormModal, | ||||||
|   useDeleteApiFormModal |   useDeleteApiFormModal | ||||||
| @@ -18,7 +18,7 @@ import { useTable } from '../../hooks/UseTable'; | |||||||
| import { apiUrl } from '../../states/ApiState'; | import { apiUrl } from '../../states/ApiState'; | ||||||
| import type { TableColumn } from '../Column'; | import type { TableColumn } from '../Column'; | ||||||
| import { DateColumn, StatusColumn } from '../ColumnRenderers'; | import { DateColumn, StatusColumn } from '../ColumnRenderers'; | ||||||
| import { StatusFilterOptions, type TableFilter } from '../Filter'; | import { StatusFilterOptions, type TableFilter, UserFilter } from '../Filter'; | ||||||
| import { InvenTreeTable } from '../InvenTreeTable'; | import { InvenTreeTable } from '../InvenTreeTable'; | ||||||
| import { type RowAction, RowDeleteAction } from '../RowActions'; | import { type RowAction, RowDeleteAction } from '../RowActions'; | ||||||
|  |  | ||||||
| @@ -88,8 +88,6 @@ export default function ImportSesssionTable() { | |||||||
|     ]; |     ]; | ||||||
|   }, []); |   }, []); | ||||||
|  |  | ||||||
|   const userFilter = useUserFilters(); |  | ||||||
|  |  | ||||||
|   const modelTypeFilters = useFilters({ |   const modelTypeFilters = useFilters({ | ||||||
|     url: apiUrl(ApiEndpoints.import_session_list), |     url: apiUrl(ApiEndpoints.import_session_list), | ||||||
|     method: 'OPTIONS', |     method: 'OPTIONS', | ||||||
| @@ -116,14 +114,9 @@ export default function ImportSesssionTable() { | |||||||
|         description: t`Filter by import session status`, |         description: t`Filter by import session status`, | ||||||
|         choiceFunction: StatusFilterOptions(ModelType.importsession) |         choiceFunction: StatusFilterOptions(ModelType.importsession) | ||||||
|       }, |       }, | ||||||
|       { |       UserFilter({}) | ||||||
|         name: 'user', |  | ||||||
|         label: t`User`, |  | ||||||
|         description: t`Filter by user`, |  | ||||||
|         choices: userFilter.choices |  | ||||||
|       } |  | ||||||
|     ]; |     ]; | ||||||
|   }, [modelTypeFilters.choices, userFilter.choices]); |   }, [modelTypeFilters.choices]); | ||||||
|  |  | ||||||
|   const tableActions = useMemo(() => { |   const tableActions = useMemo(() => { | ||||||
|     return [ |     return [ | ||||||
|   | |||||||
| @@ -8,7 +8,6 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints'; | |||||||
| import { ModelType } from '../../enums/ModelType'; | import { ModelType } from '../../enums/ModelType'; | ||||||
| import { UserRoles } from '../../enums/Roles'; | import { UserRoles } from '../../enums/Roles'; | ||||||
| import { stockLocationFields } from '../../forms/StockForms'; | import { stockLocationFields } from '../../forms/StockForms'; | ||||||
| import { useFilters } from '../../hooks/UseFilter'; |  | ||||||
| import { | import { | ||||||
|   useCreateApiFormModal, |   useCreateApiFormModal, | ||||||
|   useEditApiFormModal |   useEditApiFormModal | ||||||
| @@ -29,14 +28,6 @@ export function StockLocationTable({ parentId }: Readonly<{ parentId?: any }>) { | |||||||
|   const table = useTable('stocklocation'); |   const table = useTable('stocklocation'); | ||||||
|   const user = useUserState(); |   const user = useUserState(); | ||||||
|  |  | ||||||
|   const locationTypeFilters = useFilters({ |  | ||||||
|     url: apiUrl(ApiEndpoints.stock_location_type_list), |  | ||||||
|     transform: (item) => ({ |  | ||||||
|       value: item.pk, |  | ||||||
|       label: item.name |  | ||||||
|     }) |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   const tableFilters: TableFilter[] = useMemo(() => { |   const tableFilters: TableFilter[] = useMemo(() => { | ||||||
|     return [ |     return [ | ||||||
|       { |       { | ||||||
| @@ -62,10 +53,12 @@ export function StockLocationTable({ parentId }: Readonly<{ parentId?: any }>) { | |||||||
|         name: 'location_type', |         name: 'location_type', | ||||||
|         label: t`Location Type`, |         label: t`Location Type`, | ||||||
|         description: t`Filter by location type`, |         description: t`Filter by location type`, | ||||||
|         choices: locationTypeFilters.choices |         apiUrl: apiUrl(ApiEndpoints.stock_location_type_list), | ||||||
|  |         model: ModelType.stocklocationtype, | ||||||
|  |         modelRenderer: (instance: any) => instance.name | ||||||
|       } |       } | ||||||
|     ]; |     ]; | ||||||
|   }, [locationTypeFilters.choices]); |   }, []); | ||||||
|  |  | ||||||
|   const tableColumns: TableColumn[] = useMemo(() => { |   const tableColumns: TableColumn[] = useMemo(() => { | ||||||
|     return [ |     return [ | ||||||
|   | |||||||
| @@ -19,12 +19,11 @@ import { | |||||||
| import { RenderUser } from '../../components/render/User'; | import { RenderUser } from '../../components/render/User'; | ||||||
| import { ApiEndpoints } from '../../enums/ApiEndpoints'; | import { ApiEndpoints } from '../../enums/ApiEndpoints'; | ||||||
| import { ModelType } from '../../enums/ModelType'; | import { ModelType } from '../../enums/ModelType'; | ||||||
| import { useUserFilters } from '../../hooks/UseFilter'; |  | ||||||
| import { useTable } from '../../hooks/UseTable'; | import { useTable } from '../../hooks/UseTable'; | ||||||
| import { apiUrl } from '../../states/ApiState'; | import { apiUrl } from '../../states/ApiState'; | ||||||
| import type { TableColumn } from '../Column'; | import type { TableColumn } from '../Column'; | ||||||
| import { DateColumn, DescriptionColumn } from '../ColumnRenderers'; | import { DateColumn, DescriptionColumn } from '../ColumnRenderers'; | ||||||
| import type { TableFilter } from '../Filter'; | import { type TableFilter, UserFilter } from '../Filter'; | ||||||
| import { InvenTreeTable } from '../InvenTreeTable'; | import { InvenTreeTable } from '../InvenTreeTable'; | ||||||
|  |  | ||||||
| type StockTrackingEntry = { | type StockTrackingEntry = { | ||||||
| @@ -37,8 +36,6 @@ export function StockTrackingTable({ itemId }: Readonly<{ itemId: number }>) { | |||||||
|   const navigate = useNavigate(); |   const navigate = useNavigate(); | ||||||
|   const table = useTable('stock_tracking'); |   const table = useTable('stock_tracking'); | ||||||
|  |  | ||||||
|   const userFilters = useUserFilters(); |  | ||||||
|  |  | ||||||
|   // Render "details" for a stock tracking record |   // Render "details" for a stock tracking record | ||||||
|   const renderDetails = useCallback( |   const renderDetails = useCallback( | ||||||
|     (record: any) => { |     (record: any) => { | ||||||
| @@ -186,14 +183,13 @@ export function StockTrackingTable({ itemId }: Readonly<{ itemId: number }>) { | |||||||
|  |  | ||||||
|   const filters: TableFilter[] = useMemo(() => { |   const filters: TableFilter[] = useMemo(() => { | ||||||
|     return [ |     return [ | ||||||
|       { |       UserFilter({ | ||||||
|         name: 'user', |         name: 'user', | ||||||
|         label: t`User`, |         label: t`User`, | ||||||
|         choices: userFilters.choices, |  | ||||||
|         description: t`Filter by user` |         description: t`Filter by user` | ||||||
|       } |       }) | ||||||
|     ]; |     ]; | ||||||
|   }, [userFilters]); |   }, []); | ||||||
|  |  | ||||||
|   const tableColumns: TableColumn[] = useMemo(() => { |   const tableColumns: TableColumn[] = useMemo(() => { | ||||||
|     return [ |     return [ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user