2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-07-30 00:21:34 +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:
Oliver
2025-07-23 18:32:04 +10:00
committed by GitHub
parent 89279ef091
commit 73231ce921
5 changed files with 72 additions and 9 deletions

View File

@@ -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;

View File

@@ -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,

View File

@@ -72,6 +72,7 @@ export function ApiFormField({
const reducedDefinition = useMemo(() => {
return {
...fieldDefinition,
autoFill: undefined,
onValueChange: undefined,
adjustFilters: undefined,
adjustValue: undefined,

View File

@@ -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,

View File

@@ -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();