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