mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-30 16:41:35 +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 description : The description to display for the field
|
||||||
* @param preFieldContent : Content to render before the field
|
* @param preFieldContent : Content to render before the field
|
||||||
* @param postFieldContent : Content to render after 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 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 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
|
* @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;
|
description?: string;
|
||||||
preFieldContent?: JSX.Element;
|
preFieldContent?: JSX.Element;
|
||||||
postFieldContent?: JSX.Element;
|
postFieldContent?: JSX.Element;
|
||||||
|
autoFill?: boolean;
|
||||||
adjustValue?: (value: any) => any;
|
adjustValue?: (value: any) => any;
|
||||||
onValueChange?: (value: any, record?: any) => void;
|
onValueChange?: (value: any, record?: any) => void;
|
||||||
adjustFilters?: (value: ApiFormAdjustFilterType) => any;
|
adjustFilters?: (value: ApiFormAdjustFilterType) => any;
|
||||||
|
@@ -80,6 +80,7 @@ export function PrintingActions({
|
|||||||
// Override field values
|
// Override field values
|
||||||
fields.template = {
|
fields.template = {
|
||||||
...fields.template,
|
...fields.template,
|
||||||
|
autoFill: true,
|
||||||
filters: {
|
filters: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
model_type: modelType,
|
model_type: modelType,
|
||||||
@@ -132,6 +133,7 @@ export function PrintingActions({
|
|||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
fields: {
|
fields: {
|
||||||
template: {
|
template: {
|
||||||
|
autoFill: true,
|
||||||
filters: {
|
filters: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
model_type: modelType,
|
model_type: modelType,
|
||||||
|
@@ -72,6 +72,7 @@ export function ApiFormField({
|
|||||||
const reducedDefinition = useMemo(() => {
|
const reducedDefinition = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
...fieldDefinition,
|
...fieldDefinition,
|
||||||
|
autoFill: undefined,
|
||||||
onValueChange: undefined,
|
onValueChange: undefined,
|
||||||
adjustFilters: undefined,
|
adjustFilters: undefined,
|
||||||
adjustValue: undefined,
|
adjustValue: undefined,
|
||||||
|
@@ -54,6 +54,61 @@ export function RelatedModelField({
|
|||||||
|
|
||||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
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
|
// If an initial value is provided, load from the API
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// If the value is unchanged, do nothing
|
// If the value is unchanged, do nothing
|
||||||
@@ -98,7 +153,12 @@ export function RelatedModelField({
|
|||||||
} else {
|
} else {
|
||||||
setPk(null);
|
setPk(null);
|
||||||
}
|
}
|
||||||
}, [definition.api_url, definition.pk_field, field.value]);
|
}, [
|
||||||
|
definition.api_url,
|
||||||
|
definition.filters,
|
||||||
|
definition.pk_field,
|
||||||
|
field.value
|
||||||
|
]);
|
||||||
|
|
||||||
// Search input query
|
// Search input query
|
||||||
const [value, setValue] = useState<string>('');
|
const [value, setValue] = useState<string>('');
|
||||||
@@ -221,6 +281,7 @@ export function RelatedModelField({
|
|||||||
const fieldDefinition = useMemo(() => {
|
const fieldDefinition = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
...definition,
|
...definition,
|
||||||
|
autoFill: undefined,
|
||||||
onValueChange: undefined,
|
onValueChange: undefined,
|
||||||
adjustFilters: undefined,
|
adjustFilters: undefined,
|
||||||
exclude: undefined,
|
exclude: undefined,
|
||||||
|
@@ -32,13 +32,12 @@ test('Label Printing', async ({ browser }) => {
|
|||||||
|
|
||||||
// Select label template
|
// Select label template
|
||||||
await page.getByLabel('related-field-template').click();
|
await page.getByLabel('related-field-template').click();
|
||||||
await page.getByText('InvenTree Stock Item Label (').click();
|
await page
|
||||||
|
.getByRole('option', { name: 'InvenTree Stock Item Label' })
|
||||||
await page.waitForTimeout(100);
|
.click();
|
||||||
|
|
||||||
await page.getByLabel('related-field-plugin').click();
|
await page.getByLabel('related-field-plugin').click();
|
||||||
|
await page.getByRole('option', { name: 'InvenTreeLabel provides' }).click();
|
||||||
await page.getByText('InvenTreeLabel', { exact: true }).click();
|
|
||||||
|
|
||||||
// Submit the print form (second time should result in success)
|
// Submit the print form (second time should result in success)
|
||||||
await page.getByRole('button', { name: 'Print', exact: true }).isEnabled();
|
await page.getByRole('button', { name: 'Print', exact: true }).isEnabled();
|
||||||
@@ -71,9 +70,7 @@ test('Report Printing', async ({ browser }) => {
|
|||||||
|
|
||||||
// Select template
|
// Select template
|
||||||
await page.getByLabel('related-field-template').click();
|
await page.getByLabel('related-field-template').click();
|
||||||
await page.getByText('InvenTree Purchase Order').click();
|
await page.getByRole('option', { name: 'InvenTree Purchase Order' }).click();
|
||||||
|
|
||||||
await page.waitForTimeout(100);
|
|
||||||
|
|
||||||
// Submit the print form (should result in success)
|
// Submit the print form (should result in success)
|
||||||
await page.getByRole('button', { name: 'Print', exact: true }).isEnabled();
|
await page.getByRole('button', { name: 'Print', exact: true }).isEnabled();
|
||||||
|
Reference in New Issue
Block a user