mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 11:36:44 +00:00
Forms fixes (#8722)
* Refactor form fields - Allow error message to be passed through via field definition - Return error information to onFormError * Fix debounce issue for text fields * Fix for useForm hook * Badge fix - Fix badge rendering for SalesOrderShipment * Cleanup unit test
This commit is contained in:
parent
68ac4118e9
commit
aabcf52cd2
@ -94,7 +94,7 @@ export interface ApiFormProps {
|
||||
postFormContent?: JSX.Element;
|
||||
successMessage?: string;
|
||||
onFormSuccess?: (data: any) => void;
|
||||
onFormError?: () => void;
|
||||
onFormError?: (response: any) => void;
|
||||
processFormData?: (data: any) => any;
|
||||
table?: TableState;
|
||||
modelType?: ModelType;
|
||||
@ -482,7 +482,7 @@ export function ApiForm({
|
||||
default:
|
||||
// Unexpected state on form success
|
||||
invalidResponse(response.status);
|
||||
props.onFormError?.();
|
||||
props.onFormError?.(response);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -534,26 +534,30 @@ export function ApiForm({
|
||||
|
||||
processErrors(error.response.data);
|
||||
setNonFieldErrors(_nonFieldErrors);
|
||||
props.onFormError?.(error);
|
||||
|
||||
break;
|
||||
default:
|
||||
// Unexpected state on form error
|
||||
invalidResponse(error.response.status);
|
||||
props.onFormError?.();
|
||||
props.onFormError?.(error);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
showTimeoutNotification();
|
||||
props.onFormError?.();
|
||||
props.onFormError?.(error);
|
||||
}
|
||||
|
||||
return error;
|
||||
});
|
||||
};
|
||||
|
||||
const onFormError = useCallback<SubmitErrorHandler<FieldValues>>(() => {
|
||||
props.onFormError?.();
|
||||
}, [props.onFormError]);
|
||||
const onFormError = useCallback<SubmitErrorHandler<FieldValues>>(
|
||||
(error: any) => {
|
||||
props.onFormError?.(error);
|
||||
},
|
||||
[props.onFormError]
|
||||
);
|
||||
|
||||
if (optionsLoading || initialDataQuery.isFetching) {
|
||||
return (
|
||||
|
@ -46,6 +46,7 @@ export type ApiFormFieldChoice = {
|
||||
* @param required : Whether the field is required
|
||||
* @param hidden : Whether the field is hidden
|
||||
* @param disabled : Whether the field is disabled
|
||||
* @param error : Optional error message to display
|
||||
* @param exclude : Whether to exclude the field from the submitted data
|
||||
* @param placeholder : The placeholder text to display
|
||||
* @param description : The description to display for the field
|
||||
@ -88,6 +89,7 @@ export type ApiFormFieldType = {
|
||||
child?: ApiFormFieldType;
|
||||
children?: { [key: string]: ApiFormFieldType };
|
||||
required?: boolean;
|
||||
error?: string;
|
||||
choices?: ApiFormFieldChoice[];
|
||||
hidden?: boolean;
|
||||
disabled?: boolean;
|
||||
@ -256,7 +258,7 @@ export function ApiFormField({
|
||||
aria-label={`boolean-field-${fieldName}`}
|
||||
radius='lg'
|
||||
size='sm'
|
||||
error={error?.message}
|
||||
error={definition.error ?? error?.message}
|
||||
onChange={(event) => onChange(event.currentTarget.checked)}
|
||||
/>
|
||||
);
|
||||
@ -277,7 +279,7 @@ export function ApiFormField({
|
||||
id={fieldId}
|
||||
aria-label={`number-field-${field.name}`}
|
||||
value={numericalValue}
|
||||
error={error?.message}
|
||||
error={definition.error ?? error?.message}
|
||||
decimalScale={definition.field_type == 'integer' ? 0 : 10}
|
||||
onChange={(value: number | string | null) => onChange(value)}
|
||||
step={1}
|
||||
@ -299,7 +301,7 @@ export function ApiFormField({
|
||||
ref={field.ref}
|
||||
radius='sm'
|
||||
value={value}
|
||||
error={error?.message}
|
||||
error={definition.error ?? error?.message}
|
||||
onChange={(payload: File | null) => onChange(payload)}
|
||||
/>
|
||||
);
|
||||
@ -343,6 +345,7 @@ export function ApiFormField({
|
||||
booleanValue,
|
||||
control,
|
||||
controller,
|
||||
definition,
|
||||
field,
|
||||
fieldId,
|
||||
fieldName,
|
||||
|
@ -63,7 +63,7 @@ export function ChoiceField({
|
||||
<Select
|
||||
id={fieldId}
|
||||
aria-label={`choice-field-${field.name}`}
|
||||
error={error?.message}
|
||||
error={definition.error ?? error?.message}
|
||||
radius='sm'
|
||||
{...field}
|
||||
onChange={onChange}
|
||||
|
@ -61,7 +61,7 @@ export default function DateField({
|
||||
radius='sm'
|
||||
ref={field.ref}
|
||||
type={undefined}
|
||||
error={error?.message}
|
||||
error={definition.error ?? error?.message}
|
||||
value={dateValue ?? null}
|
||||
clearable={!definition.required}
|
||||
onChange={onChange}
|
||||
|
@ -50,7 +50,7 @@ export default function IconField({
|
||||
label={definition.label}
|
||||
description={definition.description}
|
||||
required={definition.required}
|
||||
error={error?.message}
|
||||
error={definition.error ?? error?.message}
|
||||
ref={field.ref}
|
||||
component='button'
|
||||
type='button'
|
||||
|
@ -284,7 +284,7 @@ export function RelatedModelField({
|
||||
return (
|
||||
<Input.Wrapper
|
||||
{...fieldDefinition}
|
||||
error={error?.message}
|
||||
error={definition.error ?? error?.message}
|
||||
styles={{ description: { paddingBottom: '5px' } }}
|
||||
>
|
||||
<Select
|
||||
|
@ -258,7 +258,7 @@ export function TableFieldExtraRow({
|
||||
fieldName={fieldName ?? 'field'}
|
||||
fieldDefinition={field}
|
||||
defaultValue={defaultValue}
|
||||
error={error}
|
||||
error={fieldDefinition.error ?? error}
|
||||
/>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
|
@ -56,7 +56,7 @@ export default function TextField({
|
||||
aria-label={`text-field-${field.name}`}
|
||||
type={definition.field_type}
|
||||
value={rawText || ''}
|
||||
error={error?.message}
|
||||
error={definition.error ?? error?.message}
|
||||
radius='sm'
|
||||
onChange={(event) => onTextChange(event.currentTarget.value)}
|
||||
onBlur={(event) => {
|
||||
@ -64,7 +64,13 @@ export default function TextField({
|
||||
onChange(event.currentTarget.value);
|
||||
}
|
||||
}}
|
||||
onKeyDown={(event) => onKeyDown(event.code)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.code === 'Enter') {
|
||||
// Bypass debounce on enter key
|
||||
onChange(event.currentTarget.value);
|
||||
}
|
||||
onKeyDown(event.code);
|
||||
}}
|
||||
rightSection={
|
||||
value && !definition.required ? (
|
||||
<IconX size='1rem' color='red' onClick={() => onTextChange('')} />
|
||||
|
@ -48,9 +48,8 @@ export function useApiFormModal(props: ApiFormModalProps) {
|
||||
modalClose.current();
|
||||
props.onFormSuccess?.(data);
|
||||
},
|
||||
onFormError: () => {
|
||||
modalClose.current();
|
||||
props.onFormError?.();
|
||||
onFormError: (error: any) => {
|
||||
props.onFormError?.(error);
|
||||
}
|
||||
}),
|
||||
[props]
|
||||
|
@ -90,10 +90,8 @@ test('Build Order - Basic Tests', async ({ page }) => {
|
||||
test('Build Order - Build Outputs', async ({ page }) => {
|
||||
await doQuickLogin(page);
|
||||
|
||||
await page.goto(`${baseUrl}/part/`);
|
||||
|
||||
// Navigate to the correct build order
|
||||
await page.getByRole('tab', { name: 'Manufacturing', exact: true }).click();
|
||||
await page.goto(`${baseUrl}/manufacturing/index/`);
|
||||
await page.getByRole('tab', { name: 'Build Orders', exact: true }).click();
|
||||
|
||||
// We have now loaded the "Build Order" table. Check for some expected texts
|
||||
await page.getByText('On Hold').first().waitFor();
|
||||
|
Loading…
x
Reference in New Issue
Block a user