2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-08-10 22:00:56 +00:00

Enhancements for "custom state" form

- More intuitive form actions
This commit is contained in:
Oliver Walters
2024-11-05 23:56:22 +00:00
parent 245803b0d4
commit cf2dae00af
5 changed files with 78 additions and 23 deletions

View File

@@ -3406,17 +3406,20 @@ class InvenTreeCustomUserStateModel(models.Model):
"""Custom model to extends any registered state with extra custom, user defined states.""" """Custom model to extends any registered state with extra custom, user defined states."""
key = models.IntegerField( key = models.IntegerField(
verbose_name=_('Key'), verbose_name=_('Value'),
help_text=_('Value that will be saved in the models database'), help_text=_('Numerical value that will be saved in the models database'),
) )
name = models.CharField( name = models.CharField(
max_length=250, verbose_name=_('Name'), help_text=_('Name of the state') max_length=250, verbose_name=_('Name'), help_text=_('Name of the state')
) )
label = models.CharField( label = models.CharField(
max_length=250, max_length=250,
verbose_name=_('Label'), verbose_name=_('Label'),
help_text=_('Label that will be displayed in the frontend'), help_text=_('Label that will be displayed in the frontend'),
) )
color = models.CharField( color = models.CharField(
max_length=10, max_length=10,
choices=state_color_mappings(), choices=state_color_mappings(),
@@ -3424,12 +3427,14 @@ class InvenTreeCustomUserStateModel(models.Model):
verbose_name=_('Color'), verbose_name=_('Color'),
help_text=_('Color that will be displayed in the frontend'), help_text=_('Color that will be displayed in the frontend'),
) )
logical_key = models.IntegerField( logical_key = models.IntegerField(
verbose_name=_('Logical Key'), verbose_name=_('Logical Key'),
help_text=_( help_text=_(
'State logical key that is equal to this custom state in business logic' 'State logical key that is equal to this custom state in business logic'
), ),
) )
model = models.ForeignKey( model = models.ForeignKey(
ContentType, ContentType,
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
@@ -3438,6 +3443,7 @@ class InvenTreeCustomUserStateModel(models.Model):
verbose_name=_('Model'), verbose_name=_('Model'),
help_text=_('Model this state is associated with'), help_text=_('Model this state is associated with'),
) )
reference_status = models.CharField( reference_status = models.CharField(
max_length=250, max_length=250,
verbose_name=_('Reference Status Set'), verbose_name=_('Reference Status Set'),

View File

@@ -5,7 +5,7 @@ import { ModelType } from '../../enums/ModelType';
import { resolveItem } from '../../functions/conversion'; import { resolveItem } from '../../functions/conversion';
import { useGlobalStatusState } from '../../states/StatusState'; import { useGlobalStatusState } from '../../states/StatusState';
interface StatusCodeInterface { export interface StatusCodeInterface {
key: string; key: string;
label: string; label: string;
name: string; name: string;
@@ -13,7 +13,10 @@ interface StatusCodeInterface {
} }
export interface StatusCodeListInterface { export interface StatusCodeListInterface {
statusClass: string;
values: {
[key: string]: StatusCodeInterface; [key: string]: StatusCodeInterface;
};
} }
interface RenderStatusLabelOptionsInterface { interface RenderStatusLabelOptionsInterface {
@@ -33,10 +36,10 @@ function renderStatusLabel(
let color = null; let color = null;
// Find the entry which matches the provided key // Find the entry which matches the provided key
for (let name in codes) { for (let name in codes.values) {
let entry = codes[name]; let entry: StatusCodeInterface = codes.values[name];
if (entry.key == key) { if (entry?.key == key) {
text = entry.label; text = entry.label;
color = entry.color; color = entry.color;
break; break;
@@ -97,7 +100,7 @@ export function getStatusCodeName(
} }
for (let name in statusCodes) { for (let name in statusCodes) {
let entry = statusCodes[name]; let entry: StatusCodeInterface = statusCodes.values[name];
if (entry.key == key) { if (entry.key == key) {
return entry.name; return entry.name;

View File

@@ -1,6 +1,12 @@
import { IconUsers } from '@tabler/icons-react'; import { IconUsers } from '@tabler/icons-react';
import { useMemo, useState } from 'react';
import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField'; import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
import {
StatusCodeInterface,
StatusCodeListInterface
} from '../components/render/StatusRenderer';
import { useGlobalStatusState } from '../states/StatusState';
export function projectCodeFields(): ApiFormFieldSet { export function projectCodeFields(): ApiFormFieldSet {
return { return {
@@ -12,16 +18,51 @@ export function projectCodeFields(): ApiFormFieldSet {
}; };
} }
export function customStateFields(): ApiFormFieldSet { export function useCustomStateFields(): ApiFormFieldSet {
// Status codes
const statusCodes = useGlobalStatusState();
// Selected base status class
const [statusClass, setStatusClass] = useState<string>('');
// 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 { return {
reference_status: {
onValueChange(value) {
setStatusClass(value);
}
},
logical_key: {
field_type: 'choice',
choices: statusOptions
},
key: {}, key: {},
name: {}, name: {},
label: {}, label: {},
color: {}, color: {},
logical_key: {}, model: {}
model: {},
reference_status: {}
}; };
}, [statusOptions]);
} }
export function customUnitsFields(): ApiFormFieldSet { export function customUnitsFields(): ApiFormFieldSet {

View File

@@ -35,8 +35,10 @@ export const useGlobalStatusState = create<ServerStateProps>()(
.then((response) => { .then((response) => {
const newStatusLookup: StatusLookup = {} as StatusLookup; const newStatusLookup: StatusLookup = {} as StatusLookup;
for (const key in response.data) { for (const key in response.data) {
newStatusLookup[statusCodeList[key] || key] = newStatusLookup[statusCodeList[key] || key] = {
response.data[key].values; statusClass: key,
values: response.data[key].values
};
} }
set({ status: newStatusLookup }); set({ status: newStatusLookup });
}) })

View File

@@ -4,7 +4,7 @@ import { useCallback, useMemo, useState } from 'react';
import { AddItemButton } from '../../components/buttons/AddItemButton'; import { AddItemButton } from '../../components/buttons/AddItemButton';
import { ApiEndpoints } from '../../enums/ApiEndpoints'; import { ApiEndpoints } from '../../enums/ApiEndpoints';
import { UserRoles } from '../../enums/Roles'; import { UserRoles } from '../../enums/Roles';
import { customStateFields } from '../../forms/CommonForms'; import { useCustomStateFields } from '../../forms/CommonForms';
import { import {
useCreateApiFormModal, useCreateApiFormModal,
useDeleteApiFormModal, useDeleteApiFormModal,
@@ -60,10 +60,13 @@ export default function CustomStateTable() {
]; ];
}, []); }, []);
const newCustomStateFields = useCustomStateFields();
const editCustomStateFields = useCustomStateFields();
const newCustomState = useCreateApiFormModal({ const newCustomState = useCreateApiFormModal({
url: ApiEndpoints.custom_state_list, url: ApiEndpoints.custom_state_list,
title: t`Add State`, title: t`Add State`,
fields: customStateFields(), fields: newCustomStateFields,
table: table table: table
}); });
@@ -75,7 +78,7 @@ export default function CustomStateTable() {
url: ApiEndpoints.custom_state_list, url: ApiEndpoints.custom_state_list,
pk: selectedCustomState, pk: selectedCustomState,
title: t`Edit State`, title: t`Edit State`,
fields: customStateFields(), fields: editCustomStateFields,
table: table table: table
}); });