diff --git a/CHANGELOG.md b/CHANGELOG.md
index 767d4c5..8367e68 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
* [UIPQB-168](https://folio-org.atlassian.net/browse/UIPQB-168) Allow editing queries containing no fields.
* [UIPQB-141](https://folio-org.atlassian.net/browse/UIPQB-141) Modal dialog focus inconsistencies across screenreaders.
+* [UIPQB-162](https://folio-org.atlassian.net/browse/UIPQB-162) Errors when query includes a modified custom field.
## [1.2.6](https://github.com/folio-org/ui-plugin-query-builder/tree/v1.2.6) (2024-12-11)
diff --git a/src/QueryBuilder/QueryBuilder/QueryBuilderModal/RepeatableFields/RepeatableFields.js b/src/QueryBuilder/QueryBuilder/QueryBuilderModal/RepeatableFields/RepeatableFields.js
index add54c1..097ed98 100644
--- a/src/QueryBuilder/QueryBuilder/QueryBuilderModal/RepeatableFields/RepeatableFields.js
+++ b/src/QueryBuilder/QueryBuilder/QueryBuilderModal/RepeatableFields/RepeatableFields.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { memo, useEffect, useRef } from 'react';
import {
IconButton,
RepeatableField,
@@ -8,9 +8,11 @@ import {
Row,
getFirstFocusable
} from '@folio/stripes/components';
+import { useShowCallout } from "@folio/stripes-acq-components";
+import {MESSAGE_TYPES} from "@folio/lists/src/hooks/useMessages/useMessages";
import PropTypes from 'prop-types';
-import { useIntl } from 'react-intl';
+import {FormattedMessage, useIntl} from 'react-intl';
import { QueryBuilderTitle } from '../../QueryBuilderTitle';
import css from '../QueryBuilderModal.css';
import { COLUMN_KEYS } from '../../../../constants/columnKeys';
@@ -23,9 +25,12 @@ import {
} from '../../helpers/selectOptions';
import { BOOLEAN_OPERATORS } from '../../../../constants/operators';
import { DataTypeInput } from '../DataTypeInput';
+import {findMissingValues} from "../../helpers/query";
-export const RepeatableFields = ({ source, setSource, getParamsSource, columns }) => {
+export const RepeatableFields = memo(({ source, setSource, getParamsSource, columns }) => {
const intl = useIntl();
+ const callout = useShowCallout();
+ const calloutCalledRef = useRef(false);
const fieldOptions = getFieldOptions(columns);
@@ -84,6 +89,7 @@ export const RepeatableFields = ({ source, setSource, getParamsSource, columns }
},
};
}
+
if (isOperator) {
return {
[COLUMN_KEYS.VALUE]: {
@@ -113,6 +119,27 @@ export const RepeatableFields = ({ source, setSource, getParamsSource, columns }
}));
};
+ useEffect(() => {
+ if (calloutCalledRef.current) return;
+
+ const deletedFields = findMissingValues(fieldOptions, source)
+
+ if(deletedFields.length >= 1) {
+ calloutCalledRef.current = true;
+
+ callout({
+ type: MESSAGE_TYPES.WARNING,
+ message: (
+
+ ),
+ timeout: 0,
+ })
+ }
+ }, []);
+
return (
<>
@@ -201,7 +228,7 @@ export const RepeatableFields = ({ source, setSource, getParamsSource, columns }
/>
>
);
-};
+});
RepeatableFields.propTypes = {
source: PropTypes.arrayOf(PropTypes.object),
diff --git a/src/QueryBuilder/QueryBuilder/helpers/query.js b/src/QueryBuilder/QueryBuilder/helpers/query.js
index d29cc45..eb15c82 100644
--- a/src/QueryBuilder/QueryBuilder/helpers/query.js
+++ b/src/QueryBuilder/QueryBuilder/helpers/query.js
@@ -279,3 +279,20 @@ export const mongoQueryToSource = async ({
return [singleItem];
};
+
+export const findMissingValues = (
+ mainArray,
+ secondaryArray
+) => {
+ const mainValues = new Set(mainArray?.map((item) => item.value));
+
+ const missingValues = [];
+ for (const secondaryItem of secondaryArray) {
+ const currentValue = secondaryItem.field.current;
+ if (currentValue && !mainValues.has(currentValue)) {
+ missingValues.push(currentValue);
+ }
+ }
+
+ return missingValues;
+}
diff --git a/src/QueryBuilder/QueryBuilder/helpers/query.test.js b/src/QueryBuilder/QueryBuilder/helpers/query.test.js
index 1451062..eba8db7 100644
--- a/src/QueryBuilder/QueryBuilder/helpers/query.test.js
+++ b/src/QueryBuilder/QueryBuilder/helpers/query.test.js
@@ -1,4 +1,4 @@
-import { getTransformedValue, isQueryValid, mongoQueryToSource, sourceToMongoQuery } from './query';
+import {findMissingValues, getTransformedValue, isQueryValid, mongoQueryToSource, sourceToMongoQuery} from './query';
import { booleanOptions } from './selectOptions';
import { OPERATORS } from '../../../constants/operators';
import { fieldOptions } from '../../../../test/jest/data/entityType';
@@ -372,3 +372,91 @@ describe('getTransformedValue', () => {
expect(actual).toEqual(expected);
});
});
+
+describe('findMissingValues', () => {
+ it('should return missing values from secondaryArray that are not in mainArray', () => {
+ const mainArray = [
+ { value: 'value1' },
+ { value: 'value2' },
+ { value: 'value3' },
+ ];
+
+ const secondaryArray = [
+ { field: { current: 'value2' } },
+ { field: { current: 'value4' } },
+ { field: { current: 'value5' } },
+ ];
+
+ const result = findMissingValues(mainArray, secondaryArray);
+
+ expect(result).toEqual(['value4', 'value5']);
+ });
+
+ it('should return an empty array when all values are present in mainArray', () => {
+ const mainArray = [
+ { value: 'value1' },
+ { value: 'value2' },
+ ];
+
+ const secondaryArray = [
+ { field: { current: 'value1' } },
+ { field: { current: 'value2' } },
+ ];
+
+ const result = findMissingValues(mainArray, secondaryArray);
+
+ expect(result).toEqual([]);
+ });
+
+ it('should handle cases where mainArray is empty', () => {
+ const mainArray = [];
+
+ const secondaryArray = [
+ { field: { current: 'value1' } },
+ { field: { current: 'value2' } },
+ ];
+
+ const result = findMissingValues(mainArray, secondaryArray);
+
+ expect(result).toEqual(['value1', 'value2']);
+ });
+
+ it('should handle cases where secondaryArray is empty', () => {
+ const mainArray = [
+ { value: 'value1' },
+ { value: 'value2' },
+ ];
+
+ const secondaryArray = [];
+
+ const result = findMissingValues(mainArray, secondaryArray);
+
+ expect(result).toEqual([]);
+ });
+
+ it('should handle cases where both arrays are empty', () => {
+ const mainArray = [];
+ const secondaryArray = [];
+
+ const result = findMissingValues(mainArray, secondaryArray);
+
+ expect(result).toEqual([]);
+ });
+
+ it('should ignore null or undefined values in secondaryArray', () => {
+ const mainArray = [
+ { value: 'value1' },
+ { value: 'value2' },
+ ];
+
+ const secondaryArray = [
+ { field: { current: 'value3' } },
+ { field: { current: null } },
+ { field: { current: undefined } },
+ ];
+
+ const result = findMissingValues(mainArray, secondaryArray);
+
+ expect(result).toEqual(['value3']);
+ });
+});
diff --git a/translations/ui-plugin-query-builder/en.json b/translations/ui-plugin-query-builder/en.json
index e175289..595c4a8 100644
--- a/translations/ui-plugin-query-builder/en.json
+++ b/translations/ui-plugin-query-builder/en.json
@@ -35,5 +35,7 @@
"error.sww": "Something went wrong",
"error.occurredMessage": "An error occurred.",
+ "warning.deletedField": "{value} in your query is unavailable. Please revise your query. ",
+
"ariaLabel.columnFilter": "Column filter input"
}