mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +00:00 
			
		
		
		
	[ui] Enable form auto-fill (#10061)
* Enable form auto-fill - If a single value is available, pre-fill - Must be enabled per-field * Tweak playwright tests for increased reliability * Fix deps
This commit is contained in:
		| @@ -56,6 +56,7 @@ export type ApiFormFieldHeader = { | ||||
|  * @param description : The description to display for the field | ||||
|  * @param preFieldContent : Content to render before the field | ||||
|  * @param postFieldContent : Content to render after the field | ||||
|  * @param autoFill: Whether to automatically fill the field with data from the API | ||||
|  * @param onValueChange : Callback function to call when the field value changes | ||||
|  * @param adjustFilters : Callback function to adjust the filters for a related field before a query is made | ||||
|  * @param adjustValue : Callback function to adjust the value of the field before it is sent to the API | ||||
| @@ -104,6 +105,7 @@ export type ApiFormFieldType = { | ||||
|   description?: string; | ||||
|   preFieldContent?: JSX.Element; | ||||
|   postFieldContent?: JSX.Element; | ||||
|   autoFill?: boolean; | ||||
|   adjustValue?: (value: any) => any; | ||||
|   onValueChange?: (value: any, record?: any) => void; | ||||
|   adjustFilters?: (value: ApiFormAdjustFilterType) => any; | ||||
|   | ||||
| @@ -80,6 +80,7 @@ export function PrintingActions({ | ||||
|     // Override field values | ||||
|     fields.template = { | ||||
|       ...fields.template, | ||||
|       autoFill: true, | ||||
|       filters: { | ||||
|         enabled: true, | ||||
|         model_type: modelType, | ||||
| @@ -132,6 +133,7 @@ export function PrintingActions({ | ||||
|     timeout: 5000, | ||||
|     fields: { | ||||
|       template: { | ||||
|         autoFill: true, | ||||
|         filters: { | ||||
|           enabled: true, | ||||
|           model_type: modelType, | ||||
|   | ||||
| @@ -72,6 +72,7 @@ export function ApiFormField({ | ||||
|   const reducedDefinition = useMemo(() => { | ||||
|     return { | ||||
|       ...fieldDefinition, | ||||
|       autoFill: undefined, | ||||
|       onValueChange: undefined, | ||||
|       adjustFilters: undefined, | ||||
|       adjustValue: undefined, | ||||
|   | ||||
| @@ -54,6 +54,61 @@ export function RelatedModelField({ | ||||
|  | ||||
|   const [isOpen, setIsOpen] = useState<boolean>(false); | ||||
|  | ||||
|   // Auto-fill the field with data from the API | ||||
|   useEffect(() => { | ||||
|     // If there is *no value defined*, and autoFill is enabled, then fetch data from the API | ||||
|     if (!definition.autoFill || !definition.api_url) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (field.value != undefined) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     const params = definition?.filters ?? {}; | ||||
|  | ||||
|     api | ||||
|       .get(definition.api_url, { | ||||
|         params: { | ||||
|           ...params, | ||||
|           limit: 1, | ||||
|           offset: 0 | ||||
|         } | ||||
|       }) | ||||
|       .then((response) => { | ||||
|         const data: any = response?.data ?? {}; | ||||
|  | ||||
|         if (data.count === 1 && data.results?.length === 1) { | ||||
|           // If there is only a single result, set the field value to that result | ||||
|           const pk_field = definition.pk_field ?? 'pk'; | ||||
|           if (data.results[0][pk_field]) { | ||||
|             const value = { | ||||
|               value: data.results[0][pk_field], | ||||
|               data: data.results[0] | ||||
|             }; | ||||
|  | ||||
|             // Run custom callback for this field (if provided) | ||||
|             if (definition.onValueChange) { | ||||
|               definition.onValueChange( | ||||
|                 data.results[0][pk_field], | ||||
|                 data.results[0] | ||||
|               ); | ||||
|             } | ||||
|  | ||||
|             setInitialData(value); | ||||
|             dataRef.current = [value]; | ||||
|             setPk(data.results[0][pk_field]); | ||||
|           } | ||||
|         } | ||||
|       }); | ||||
|   }, [ | ||||
|     definition.autoFill, | ||||
|     definition.api_url, | ||||
|     definition.filters, | ||||
|     definition.pk_field, | ||||
|     field.value | ||||
|   ]); | ||||
|  | ||||
|   // If an initial value is provided, load from the API | ||||
|   useEffect(() => { | ||||
|     // If the value is unchanged, do nothing | ||||
| @@ -98,7 +153,12 @@ export function RelatedModelField({ | ||||
|     } else { | ||||
|       setPk(null); | ||||
|     } | ||||
|   }, [definition.api_url, definition.pk_field, field.value]); | ||||
|   }, [ | ||||
|     definition.api_url, | ||||
|     definition.filters, | ||||
|     definition.pk_field, | ||||
|     field.value | ||||
|   ]); | ||||
|  | ||||
|   // Search input query | ||||
|   const [value, setValue] = useState<string>(''); | ||||
| @@ -221,6 +281,7 @@ export function RelatedModelField({ | ||||
|   const fieldDefinition = useMemo(() => { | ||||
|     return { | ||||
|       ...definition, | ||||
|       autoFill: undefined, | ||||
|       onValueChange: undefined, | ||||
|       adjustFilters: undefined, | ||||
|       exclude: undefined, | ||||
|   | ||||
| @@ -32,13 +32,12 @@ test('Label Printing', async ({ browser }) => { | ||||
|  | ||||
|   // Select label template | ||||
|   await page.getByLabel('related-field-template').click(); | ||||
|   await page.getByText('InvenTree Stock Item Label (').click(); | ||||
|  | ||||
|   await page.waitForTimeout(100); | ||||
|   await page | ||||
|     .getByRole('option', { name: 'InvenTree Stock Item Label' }) | ||||
|     .click(); | ||||
|  | ||||
|   await page.getByLabel('related-field-plugin').click(); | ||||
|  | ||||
|   await page.getByText('InvenTreeLabel', { exact: true }).click(); | ||||
|   await page.getByRole('option', { name: 'InvenTreeLabel provides' }).click(); | ||||
|  | ||||
|   // Submit the print form (second time should result in success) | ||||
|   await page.getByRole('button', { name: 'Print', exact: true }).isEnabled(); | ||||
| @@ -71,9 +70,7 @@ test('Report Printing', async ({ browser }) => { | ||||
|  | ||||
|   // Select template | ||||
|   await page.getByLabel('related-field-template').click(); | ||||
|   await page.getByText('InvenTree Purchase Order').click(); | ||||
|  | ||||
|   await page.waitForTimeout(100); | ||||
|   await page.getByRole('option', { name: 'InvenTree Purchase Order' }).click(); | ||||
|  | ||||
|   // Submit the print form (should result in success) | ||||
|   await page.getByRole('button', { name: 'Print', exact: true }).isEnabled(); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user