From cf2dae00afdb37b8f830ac1b82611eeca9e1b2d8 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 5 Nov 2024 23:56:22 +0000 Subject: [PATCH] Enhancements for "custom state" form - More intuitive form actions --- src/backend/InvenTree/common/models.py | 10 ++- .../src/components/render/StatusRenderer.tsx | 15 +++-- src/frontend/src/forms/CommonForms.tsx | 61 ++++++++++++++++--- src/frontend/src/states/StatusState.tsx | 6 +- .../src/tables/settings/CustomStateTable.tsx | 9 ++- 5 files changed, 78 insertions(+), 23 deletions(-) diff --git a/src/backend/InvenTree/common/models.py b/src/backend/InvenTree/common/models.py index fb57bfa67d..a118258173 100644 --- a/src/backend/InvenTree/common/models.py +++ b/src/backend/InvenTree/common/models.py @@ -3406,17 +3406,20 @@ class InvenTreeCustomUserStateModel(models.Model): """Custom model to extends any registered state with extra custom, user defined states.""" key = models.IntegerField( - verbose_name=_('Key'), - help_text=_('Value that will be saved in the models database'), + verbose_name=_('Value'), + help_text=_('Numerical value that will be saved in the models database'), ) + name = models.CharField( max_length=250, verbose_name=_('Name'), help_text=_('Name of the state') ) + label = models.CharField( max_length=250, verbose_name=_('Label'), help_text=_('Label that will be displayed in the frontend'), ) + color = models.CharField( max_length=10, choices=state_color_mappings(), @@ -3424,12 +3427,14 @@ class InvenTreeCustomUserStateModel(models.Model): verbose_name=_('Color'), help_text=_('Color that will be displayed in the frontend'), ) + logical_key = models.IntegerField( verbose_name=_('Logical Key'), help_text=_( 'State logical key that is equal to this custom state in business logic' ), ) + model = models.ForeignKey( ContentType, on_delete=models.SET_NULL, @@ -3438,6 +3443,7 @@ class InvenTreeCustomUserStateModel(models.Model): verbose_name=_('Model'), help_text=_('Model this state is associated with'), ) + reference_status = models.CharField( max_length=250, verbose_name=_('Reference Status Set'), diff --git a/src/frontend/src/components/render/StatusRenderer.tsx b/src/frontend/src/components/render/StatusRenderer.tsx index e9cfb806b2..124ae2478d 100644 --- a/src/frontend/src/components/render/StatusRenderer.tsx +++ b/src/frontend/src/components/render/StatusRenderer.tsx @@ -5,7 +5,7 @@ import { ModelType } from '../../enums/ModelType'; import { resolveItem } from '../../functions/conversion'; import { useGlobalStatusState } from '../../states/StatusState'; -interface StatusCodeInterface { +export interface StatusCodeInterface { key: string; label: string; name: string; @@ -13,7 +13,10 @@ interface StatusCodeInterface { } export interface StatusCodeListInterface { - [key: string]: StatusCodeInterface; + statusClass: string; + values: { + [key: string]: StatusCodeInterface; + }; } interface RenderStatusLabelOptionsInterface { @@ -33,10 +36,10 @@ function renderStatusLabel( let color = null; // Find the entry which matches the provided key - for (let name in codes) { - let entry = codes[name]; + for (let name in codes.values) { + let entry: StatusCodeInterface = codes.values[name]; - if (entry.key == key) { + if (entry?.key == key) { text = entry.label; color = entry.color; break; @@ -97,7 +100,7 @@ export function getStatusCodeName( } for (let name in statusCodes) { - let entry = statusCodes[name]; + let entry: StatusCodeInterface = statusCodes.values[name]; if (entry.key == key) { return entry.name; diff --git a/src/frontend/src/forms/CommonForms.tsx b/src/frontend/src/forms/CommonForms.tsx index 40c060b92d..cf6dcbfa9a 100644 --- a/src/frontend/src/forms/CommonForms.tsx +++ b/src/frontend/src/forms/CommonForms.tsx @@ -1,6 +1,12 @@ import { IconUsers } from '@tabler/icons-react'; +import { useMemo, useState } from 'react'; import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField'; +import { + StatusCodeInterface, + StatusCodeListInterface +} from '../components/render/StatusRenderer'; +import { useGlobalStatusState } from '../states/StatusState'; export function projectCodeFields(): ApiFormFieldSet { return { @@ -12,16 +18,51 @@ export function projectCodeFields(): ApiFormFieldSet { }; } -export function customStateFields(): ApiFormFieldSet { - return { - key: {}, - name: {}, - label: {}, - color: {}, - logical_key: {}, - model: {}, - reference_status: {} - }; +export function useCustomStateFields(): ApiFormFieldSet { + // Status codes + const statusCodes = useGlobalStatusState(); + + // Selected base status class + const [statusClass, setStatusClass] = useState(''); + + // Construct a list of status options based on the selected status class + const statusOptions: any[] = useMemo(() => { + let options: any[] = []; + + const valuesList = Object.values(statusCodes.status ?? {}).find( + (value: StatusCodeListInterface) => value.statusClass === statusClass + ); + + Object.values(valuesList?.values ?? {}).forEach( + (value: StatusCodeInterface) => { + options.push({ + value: value.key, + display_name: value.label + }); + } + ); + + return options; + }, [statusCodes, statusClass]); + + return useMemo(() => { + return { + reference_status: { + onValueChange(value) { + setStatusClass(value); + } + }, + logical_key: { + field_type: 'choice', + choices: statusOptions + }, + key: {}, + name: {}, + label: {}, + color: {}, + model: {} + }; + }, [statusOptions]); } export function customUnitsFields(): ApiFormFieldSet { diff --git a/src/frontend/src/states/StatusState.tsx b/src/frontend/src/states/StatusState.tsx index 884b193251..06e351a35f 100644 --- a/src/frontend/src/states/StatusState.tsx +++ b/src/frontend/src/states/StatusState.tsx @@ -35,8 +35,10 @@ export const useGlobalStatusState = create()( .then((response) => { const newStatusLookup: StatusLookup = {} as StatusLookup; for (const key in response.data) { - newStatusLookup[statusCodeList[key] || key] = - response.data[key].values; + newStatusLookup[statusCodeList[key] || key] = { + statusClass: key, + values: response.data[key].values + }; } set({ status: newStatusLookup }); }) diff --git a/src/frontend/src/tables/settings/CustomStateTable.tsx b/src/frontend/src/tables/settings/CustomStateTable.tsx index b5ab497cc2..7125d6bef0 100644 --- a/src/frontend/src/tables/settings/CustomStateTable.tsx +++ b/src/frontend/src/tables/settings/CustomStateTable.tsx @@ -4,7 +4,7 @@ import { useCallback, useMemo, useState } from 'react'; import { AddItemButton } from '../../components/buttons/AddItemButton'; import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { UserRoles } from '../../enums/Roles'; -import { customStateFields } from '../../forms/CommonForms'; +import { useCustomStateFields } from '../../forms/CommonForms'; import { useCreateApiFormModal, useDeleteApiFormModal, @@ -60,10 +60,13 @@ export default function CustomStateTable() { ]; }, []); + const newCustomStateFields = useCustomStateFields(); + const editCustomStateFields = useCustomStateFields(); + const newCustomState = useCreateApiFormModal({ url: ApiEndpoints.custom_state_list, title: t`Add State`, - fields: customStateFields(), + fields: newCustomStateFields, table: table }); @@ -75,7 +78,7 @@ export default function CustomStateTable() { url: ApiEndpoints.custom_state_list, pk: selectedCustomState, title: t`Edit State`, - fields: customStateFields(), + fields: editCustomStateFields, table: table });