mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-17 12:50:43 +00:00
Merge branch 'feature/search-possible-values-by-label' into integration
This commit is contained in:
23618
package-lock.json
generated
23618
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@
|
||||
"@auth0/auth0-react": "1.10.2",
|
||||
"@emotion/react": "11.7.1",
|
||||
"@emotion/styled": "11.6.0",
|
||||
"@kingsrook/qqq-frontend-core": "1.0.119",
|
||||
"@kingsrook/qqq-frontend-core": "1.0.122",
|
||||
"@mui/icons-material": "5.4.1",
|
||||
"@mui/material": "5.11.1",
|
||||
"@mui/styles": "5.11.1",
|
||||
@ -35,6 +35,7 @@
|
||||
"html-react-parser": "1.4.8",
|
||||
"html-to-text": "^9.0.5",
|
||||
"http-proxy-middleware": "2.0.6",
|
||||
"lodash": "4.17.21",
|
||||
"jwt-decode": "3.1.2",
|
||||
"oidc-client-ts": "2.4.1",
|
||||
"react-oidc-context": "2.3.1",
|
||||
|
2
pom.xml
2
pom.xml
@ -29,7 +29,7 @@
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<revision>0.25.0-SNAPSHOT</revision>
|
||||
<revision>0.26.0-SNAPSHOT</revision>
|
||||
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
|
@ -23,8 +23,10 @@ import {Chip} from "@mui/material";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import {makeStyles} from "@mui/styles";
|
||||
import Downshift from "downshift";
|
||||
import {debounce} from "lodash";
|
||||
import {arrayOf, func, string} from "prop-types";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import Client from "qqq/utils/qqq/Client";
|
||||
import React, {useEffect, useRef, useState} from "react";
|
||||
|
||||
const useStyles = makeStyles((theme: any) => ({
|
||||
chip: {
|
||||
@ -34,21 +36,99 @@ const useStyles = makeStyles((theme: any) => ({
|
||||
|
||||
function ChipTextField({...props})
|
||||
{
|
||||
const qController = Client.getInstance();
|
||||
const classes = useStyles();
|
||||
const {handleChipChange, label, chipType, disabled, placeholder, chipData, multiline, rows, ...other} = props;
|
||||
const {table, field, handleChipChange, label, chipType, disabled, placeholder, chipData, multiline, rows, ...other} = props;
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
const [chips, setChips] = useState([]);
|
||||
const [chipColors, setChipColors] = useState([]);
|
||||
const [chipValidity, setChipValidity] = useState([] as boolean[]);
|
||||
const [chipPVSIds, setChipPVSIds] = useState([] as any[]);
|
||||
const [isMakingRequest, setIsMakingRequest] = useState(false);
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// these refs are used for the async api call for possible values //
|
||||
////////////////////////////////////////////////////////////////////
|
||||
const chipsRef = useRef<string[]>([]);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// use debounce library to not flood server as user types, wait a second before requesting //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
async function fetchPVSLabelsAndColorChips()
|
||||
{
|
||||
//////////////////////////////////////////////////////////
|
||||
// make a request for the possible value labels (chips) //
|
||||
//////////////////////////////////////////////////////////
|
||||
setIsMakingRequest(true);
|
||||
const currentChips = chipsRef.current;
|
||||
setChipColors([]);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Determine chip colors based on whether each chip value appears in results //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
const newChipColors = [] as string[];
|
||||
const chipValidity = [] as boolean[];
|
||||
const chipPVSIds = [] as any[];
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// make the request for all 'chips' with pagination to handle large sizes //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
const BATCH_SIZE = 250;
|
||||
for (let i = 0; i < currentChips.length; i += BATCH_SIZE)
|
||||
{
|
||||
const batch = currentChips.slice(i, i + BATCH_SIZE);
|
||||
const page = await qController.possibleValues(
|
||||
table.name,
|
||||
null,
|
||||
field.name,
|
||||
"",
|
||||
null,
|
||||
batch
|
||||
);
|
||||
for (let j = 0; j < batch.length; j++)
|
||||
{
|
||||
let found = false;
|
||||
for (let k = 0; k < page.length; k++)
|
||||
{
|
||||
const result = page[k];
|
||||
if (result.label === batch[j])
|
||||
{
|
||||
chipPVSIds.push(result.id);
|
||||
newChipColors.push("info");
|
||||
chipValidity.push(true);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
chipPVSIds.push(null);
|
||||
chipValidity.push(false);
|
||||
newChipColors.push("error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setChipPVSIds(chipPVSIds);
|
||||
setChipColors(newChipColors);
|
||||
setChipValidity(chipValidity);
|
||||
setIsMakingRequest(false);
|
||||
}
|
||||
|
||||
const debouncedApiCall = useRef(debounce(fetchPVSLabelsAndColorChips, 500)).current;
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
setChips(chipData);
|
||||
}, [chipData]);
|
||||
chipsRef.current = chipData;
|
||||
determineChipColors();
|
||||
}, [JSON.stringify(chipData)]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
handleChipChange(chips);
|
||||
}, [chips, handleChipChange]);
|
||||
handleChipChange(isMakingRequest, chipValidity, chipPVSIds);
|
||||
}, [chipValidity, chipPVSIds, isMakingRequest]);
|
||||
|
||||
function handleKeyDown(event: any)
|
||||
{
|
||||
@ -64,13 +144,16 @@ function ChipTextField({...props})
|
||||
setInputValue("");
|
||||
return;
|
||||
}
|
||||
if (!event.target.value.replace(/\s/g, "").length) return;
|
||||
if (!event.target.value.replace(/\s/g, "").length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
setInputValue("");
|
||||
newChipList.push(event.target.value.trim());
|
||||
setChips(newChipList);
|
||||
setInputValue("");
|
||||
}
|
||||
else if (chips.length && !inputValue.length && event.key === "Backspace" )
|
||||
else if (chips.length && !inputValue.length && event.key === "Backspace")
|
||||
{
|
||||
setChips(chips.slice(0, chips.length - 1));
|
||||
}
|
||||
@ -87,18 +170,26 @@ function ChipTextField({...props})
|
||||
setChips(newChipList);
|
||||
}
|
||||
|
||||
const handleDelete = (item: any) => () =>
|
||||
{
|
||||
const newChipList = [...chips];
|
||||
newChipList.splice(newChipList.indexOf(item), 1);
|
||||
setChips(newChipList);
|
||||
};
|
||||
|
||||
function handleInputChange(event: { target: { value: React.SetStateAction<string>; }; })
|
||||
{
|
||||
setInputValue(event.target.value);
|
||||
}
|
||||
|
||||
function determineChipColors(): any
|
||||
{
|
||||
if (chipType === "pvs")
|
||||
{
|
||||
debouncedApiCall();
|
||||
}
|
||||
else
|
||||
{
|
||||
const newChipColors = chips.map((chip, i) =>
|
||||
(chipType !== "number" || !Number.isNaN(Number(chips[i]))) ? "info" : "error"
|
||||
);
|
||||
setChipColors(newChipColors);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
@ -116,7 +207,7 @@ function ChipTextField({...props})
|
||||
});
|
||||
// @ts-ignore
|
||||
return (
|
||||
<div id="chip-text-field-container" style={{flexWrap: "wrap", display:"flex"}}>
|
||||
<div id="chip-text-field-container" style={{flexWrap: "wrap", display: "flex"}}>
|
||||
<TextField
|
||||
sx={{width: "99%"}}
|
||||
disabled={disabled}
|
||||
@ -125,16 +216,16 @@ function ChipTextField({...props})
|
||||
startAdornment:
|
||||
<div style={{overflowY: "auto", overflowX: "hidden", margin: "-10px", width: "calc(100% + 20px)", padding: "10px", marginBottom: "-20px", height: "calc(100% + 10px)"}}>
|
||||
{
|
||||
chips.map((item, i) => (
|
||||
chips.map((item, index) => (
|
||||
<Chip
|
||||
color={(chipType !== "number" || ! Number.isNaN(Number(item))) ? "info" : "error"}
|
||||
key={`${item}-${i}`}
|
||||
onChange={determineChipColors}
|
||||
color={chipColors[index]}
|
||||
key={`${item}-${index}`}
|
||||
variant="outlined"
|
||||
tabIndex={-1}
|
||||
label={item}
|
||||
className={classes.chip}
|
||||
/>
|
||||
|
||||
))
|
||||
}
|
||||
</div>,
|
||||
@ -158,6 +249,7 @@ function ChipTextField({...props})
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
ChipTextField.defaultProps = {
|
||||
chipData: []
|
||||
};
|
||||
@ -166,4 +258,4 @@ ChipTextField.propTypes = {
|
||||
chipData: arrayOf(string)
|
||||
};
|
||||
|
||||
export default ChipTextField
|
||||
export default ChipTextField;
|
||||
|
@ -20,17 +20,18 @@
|
||||
*/
|
||||
|
||||
import {QPossibleValue} from "@kingsrook/qqq-frontend-core/lib/model/QPossibleValue";
|
||||
import {Box, InputAdornment, InputLabel} from "@mui/material";
|
||||
import {InputAdornment, InputLabel} from "@mui/material";
|
||||
import Box from "@mui/material/Box";
|
||||
import Switch from "@mui/material/Switch";
|
||||
import {ErrorMessage, Field, useFormikContext} from "formik";
|
||||
import DynamicFormUtils from "qqq/components/forms/DynamicFormUtils";
|
||||
import DynamicSelect from "qqq/components/forms/DynamicSelect";
|
||||
import React, {useMemo, useState} from "react";
|
||||
import AceEditor from "react-ace";
|
||||
import colors from "qqq/assets/theme/base/colors";
|
||||
import BooleanFieldSwitch from "qqq/components/forms/BooleanFieldSwitch";
|
||||
import DynamicFormUtils from "qqq/components/forms/DynamicFormUtils";
|
||||
import DynamicSelect from "qqq/components/forms/DynamicSelect";
|
||||
import MDInput from "qqq/components/legacy/MDInput";
|
||||
import MDTypography from "qqq/components/legacy/MDTypography";
|
||||
import React, {useMemo, useState} from "react";
|
||||
import AceEditor from "react-ace";
|
||||
import {flushSync} from "react-dom";
|
||||
|
||||
// Declaring props types for FormField
|
||||
@ -83,10 +84,10 @@ function QDynamicFormField({
|
||||
|
||||
if (placeholder)
|
||||
{
|
||||
inputProps.placeholder = placeholder
|
||||
inputProps.placeholder = placeholder;
|
||||
}
|
||||
|
||||
if(backgroundColor)
|
||||
if (backgroundColor)
|
||||
{
|
||||
inputProps.sx = {
|
||||
"&.MuiInputBase-root": {
|
||||
@ -124,7 +125,7 @@ function QDynamicFormField({
|
||||
{
|
||||
onChange.onChange = (e: any) =>
|
||||
{
|
||||
if(isToUpperCase || isToLowerCase)
|
||||
if (isToUpperCase || isToLowerCase)
|
||||
{
|
||||
const beforeStart = e.target.selectionStart;
|
||||
const beforeEnd = e.target.selectionEnd;
|
||||
@ -141,7 +142,7 @@ function QDynamicFormField({
|
||||
newValue = newValue.toLowerCase();
|
||||
}
|
||||
setFieldValue(name, newValue);
|
||||
if(onChangeCallback)
|
||||
if (onChangeCallback)
|
||||
{
|
||||
onChangeCallback(newValue);
|
||||
}
|
||||
@ -153,7 +154,7 @@ function QDynamicFormField({
|
||||
input.setSelectionRange(beforeStart, beforeEnd);
|
||||
}
|
||||
}
|
||||
else if(onChangeCallback)
|
||||
else if (onChangeCallback)
|
||||
{
|
||||
onChangeCallback(e.currentTarget.value);
|
||||
}
|
||||
@ -165,15 +166,15 @@ function QDynamicFormField({
|
||||
***************************************************************************/
|
||||
function dynamicSelectOnChange(newValue?: QPossibleValue)
|
||||
{
|
||||
if(onChangeCallback)
|
||||
if (onChangeCallback)
|
||||
{
|
||||
onChangeCallback(newValue == null ? null : newValue.id)
|
||||
onChangeCallback(newValue == null ? null : newValue.id);
|
||||
}
|
||||
}
|
||||
|
||||
let field;
|
||||
let getsBulkEditHtmlLabel = true;
|
||||
if(formFieldObject.possibleValueProps)
|
||||
if (formFieldObject.possibleValueProps)
|
||||
{
|
||||
field = (<DynamicSelect
|
||||
name={name}
|
||||
@ -186,7 +187,7 @@ function QDynamicFormField({
|
||||
onChange={dynamicSelectOnChange}
|
||||
// otherValues={otherValuesMap}
|
||||
useCase="form"
|
||||
/>)
|
||||
/>);
|
||||
}
|
||||
else if (type === "checkbox")
|
||||
{
|
||||
@ -220,7 +221,7 @@ function QDynamicFormField({
|
||||
onChange={(value: string, event: any) =>
|
||||
{
|
||||
setFieldValue(name, value, false);
|
||||
if(onChangeCallback)
|
||||
if (onChangeCallback)
|
||||
{
|
||||
onChangeCallback(value);
|
||||
}
|
||||
|
@ -174,7 +174,7 @@ function DynamicSelect({fieldPossibleValueProps, overrideId, name, fieldLabel, i
|
||||
const filterInlinePossibleValues = (searchTerm: string, possibleValues: QPossibleValue[]): QPossibleValue[] =>
|
||||
{
|
||||
return possibleValues.filter(pv => pv.label?.toLowerCase().startsWith(searchTerm));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
@ -182,15 +182,15 @@ function DynamicSelect({fieldPossibleValueProps, overrideId, name, fieldLabel, i
|
||||
***************************************************************************/
|
||||
const loadResults = async (): Promise<QPossibleValue[]> =>
|
||||
{
|
||||
if(possibleValues)
|
||||
if (possibleValues)
|
||||
{
|
||||
return filterInlinePossibleValues(searchTerm, possibleValues)
|
||||
return filterInlinePossibleValues(searchTerm, possibleValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
return await qController.possibleValues(tableName, processName, possibleValueSourceName ?? fieldName, searchTerm ?? "", null, otherValues, useCase);
|
||||
return await qController.possibleValues(tableName, processName, possibleValueSourceName ?? fieldName, searchTerm ?? "", null, null, otherValues, useCase);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
|
@ -184,9 +184,9 @@ function EntityForm(props: Props): JSX.Element
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// copy values from specified fields in the parent record down into the child record //
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
if(widgetData.defaultValuesForNewChildRecordsFromParentFields)
|
||||
if (widgetData.defaultValuesForNewChildRecordsFromParentFields)
|
||||
{
|
||||
for(let childField in widgetData.defaultValuesForNewChildRecordsFromParentFields)
|
||||
for (let childField in widgetData.defaultValuesForNewChildRecordsFromParentFields)
|
||||
{
|
||||
const parentField = widgetData.defaultValuesForNewChildRecordsFromParentFields[childField];
|
||||
defaultValues[childField] = formValues[parentField];
|
||||
@ -278,21 +278,21 @@ function EntityForm(props: Props): JSX.Element
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// build a map of display values for the new record, specifically, for any possible-values that need translated. //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
const displayValues: {[fieldName: string]: string} = {};
|
||||
if(childTableName && values)
|
||||
const displayValues: { [fieldName: string]: string } = {};
|
||||
if (childTableName && values)
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// this function internally memoizes, so, we could potentially avoid an await here, but, seems ok... //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
const childTableMetaData = await qController.loadTableMetaData(childTableName)
|
||||
const childTableMetaData = await qController.loadTableMetaData(childTableName);
|
||||
for (let key in values)
|
||||
{
|
||||
const value = values[key];
|
||||
const field = childTableMetaData.fields.get(key);
|
||||
if(field.possibleValueSourceName)
|
||||
if (field.possibleValueSourceName)
|
||||
{
|
||||
const possibleValues = await qController.possibleValues(childTableName, null, field.name, null, [value], objectToMap(values), "form")
|
||||
if(possibleValues && possibleValues.length > 0)
|
||||
const possibleValues = await qController.possibleValues(childTableName, null, field.name, null, [value], null, objectToMap(values), "form");
|
||||
if (possibleValues && possibleValues.length > 0)
|
||||
{
|
||||
displayValues[key] = possibleValues[0].label;
|
||||
}
|
||||
@ -516,13 +516,12 @@ function EntityForm(props: Props): JSX.Element
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
function objectToMap(object: { [key: string]: any }): Map<string, any>
|
||||
{
|
||||
if(object == null)
|
||||
if (object == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
@ -532,7 +531,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
{
|
||||
rs.set(key, object[key]);
|
||||
}
|
||||
return rs
|
||||
return rs;
|
||||
}
|
||||
|
||||
|
||||
@ -667,7 +666,7 @@ function EntityForm(props: Props): JSX.Element
|
||||
const defaultValue = (defaultValues && defaultValues[fieldName]) ? defaultValues[fieldName] : fieldMetaData.defaultValue;
|
||||
if (defaultValue && fieldMetaData.possibleValueSourceName)
|
||||
{
|
||||
const results: QPossibleValue[] = await qController.possibleValues(tableName, null, fieldName, null, [initialValues[fieldName]], objectToMap(initialValues), "form");
|
||||
const results: QPossibleValue[] = await qController.possibleValues(tableName, null, fieldName, null, [initialValues[fieldName]], null, objectToMap(initialValues), "form");
|
||||
if (results && results.length > 0)
|
||||
{
|
||||
defaultDisplayValues.set(fieldName, results[0].label);
|
||||
|
@ -26,7 +26,6 @@ import Autocomplete from "@mui/material/Autocomplete";
|
||||
import Box from "@mui/material/Box";
|
||||
import Button from "@mui/material/Button";
|
||||
import Icon from "@mui/material/Icon";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import RadioGroup from "@mui/material/RadioGroup";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import {useFormikContext} from "formik";
|
||||
@ -94,7 +93,7 @@ export default function BulkLoadFileMappingField({bulkLoadField, isRequired, rem
|
||||
// deal with dynamically loading the initial default value for a possible value... //
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
let actuallyDoingInitialLoadOfPossibleValue = doingInitialLoadOfPossibleValue;
|
||||
if(dynamicField.possibleValueProps && bulkLoadField.defaultValue && !doingInitialLoadOfPossibleValue && !everDidInitialLoadOfPossibleValue)
|
||||
if (dynamicField.possibleValueProps && bulkLoadField.defaultValue && !doingInitialLoadOfPossibleValue && !everDidInitialLoadOfPossibleValue)
|
||||
{
|
||||
actuallyDoingInitialLoadOfPossibleValue = true;
|
||||
setDoingInitialLoadOfPossibleValue(true);
|
||||
@ -104,7 +103,7 @@ export default function BulkLoadFileMappingField({bulkLoadField, isRequired, rem
|
||||
{
|
||||
try
|
||||
{
|
||||
const possibleValues = await qController.possibleValues(bulkLoadField.tableStructure.tableName, null, fieldMetaData.name, null, [bulkLoadField.defaultValue], undefined, "filter");
|
||||
const possibleValues = await qController.possibleValues(bulkLoadField.tableStructure.tableName, null, fieldMetaData.name, null, [bulkLoadField.defaultValue], undefined, null, "filter");
|
||||
if (possibleValues && possibleValues.length > 0)
|
||||
{
|
||||
setPossibleValueInitialDisplayValue(possibleValues[0].label);
|
||||
@ -114,9 +113,9 @@ export default function BulkLoadFileMappingField({bulkLoadField, isRequired, rem
|
||||
setPossibleValueInitialDisplayValue(null);
|
||||
}
|
||||
}
|
||||
catch(e)
|
||||
catch (e)
|
||||
{
|
||||
console.log(`Error loading possible value: ${e}`)
|
||||
console.log(`Error loading possible value: ${e}`);
|
||||
}
|
||||
|
||||
actuallyDoingInitialLoadOfPossibleValue = false;
|
||||
@ -124,7 +123,7 @@ export default function BulkLoadFileMappingField({bulkLoadField, isRequired, rem
|
||||
})();
|
||||
}
|
||||
|
||||
if(dynamicField.possibleValueProps && possibleValueInitialDisplayValue)
|
||||
if (dynamicField.possibleValueProps && possibleValueInitialDisplayValue)
|
||||
{
|
||||
dynamicField.possibleValueProps.initialDisplayValue = possibleValueInitialDisplayValue;
|
||||
}
|
||||
@ -134,11 +133,11 @@ export default function BulkLoadFileMappingField({bulkLoadField, isRequired, rem
|
||||
// don't allow duplicates //
|
||||
//////////////////////////////////////////////////////
|
||||
const columnOptions: { value: number, label: string }[] = [];
|
||||
const usedLabels: {[label: string]: boolean} = {};
|
||||
const usedLabels: { [label: string]: boolean } = {};
|
||||
for (let i = 0; i < columnNames.length; i++)
|
||||
{
|
||||
const label = columnNames[i];
|
||||
if(!usedLabels[label])
|
||||
if (!usedLabels[label])
|
||||
{
|
||||
columnOptions.push({label: label, value: i});
|
||||
usedLabels[label] = true;
|
||||
@ -148,9 +147,9 @@ export default function BulkLoadFileMappingField({bulkLoadField, isRequired, rem
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// try to pick up changes in the hasHeaderRow toggle from way above //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
if(bulkLoadField.columnIndex != null && bulkLoadField.columnIndex != undefined && selectedColumn.label && columnNames[bulkLoadField.columnIndex] != selectedColumn.label)
|
||||
if (bulkLoadField.columnIndex != null && bulkLoadField.columnIndex != undefined && selectedColumn.label && columnNames[bulkLoadField.columnIndex] != selectedColumn.label)
|
||||
{
|
||||
setSelectedColumn({label: columnNames[bulkLoadField.columnIndex], value: bulkLoadField.columnIndex})
|
||||
setSelectedColumn({label: columnNames[bulkLoadField.columnIndex], value: bulkLoadField.columnIndex});
|
||||
setSelectedColumnInputValue(columnNames[bulkLoadField.columnIndex]);
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,9 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
|
||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||
import {QPossibleValue} from "@kingsrook/qqq-frontend-core/lib/model/QPossibleValue";
|
||||
import {FormControl, InputLabel, Select, SelectChangeEvent} from "@mui/material";
|
||||
import Box from "@mui/material/Box";
|
||||
import Card from "@mui/material/Card";
|
||||
@ -28,20 +31,23 @@ import Modal from "@mui/material/Modal";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import {GridFilterItem} from "@mui/x-data-grid-pro";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {QCancelButton, QSaveButton} from "qqq/components/buttons/DefaultButtons";
|
||||
|
||||
import ChipTextField from "qqq/components/forms/ChipTextField";
|
||||
import {LoadingState} from "qqq/models/LoadingState";
|
||||
import React, {useEffect, useReducer, useState} from "react";
|
||||
|
||||
interface Props
|
||||
{
|
||||
type: string;
|
||||
onSave: (newValues: any[]) => void;
|
||||
table?: QTableMetaData;
|
||||
field?: QFieldMetaData;
|
||||
}
|
||||
|
||||
FilterCriteriaPaster.defaultProps = {};
|
||||
|
||||
function FilterCriteriaPaster({type, onSave}: Props): JSX.Element
|
||||
function FilterCriteriaPaster({table, field, type, onSave}: Props): JSX.Element
|
||||
{
|
||||
enum Delimiter
|
||||
{
|
||||
@ -68,6 +74,12 @@ function FilterCriteriaPaster({type, onSave}: Props): JSX.Element
|
||||
mainCardStyles.width = "60%";
|
||||
mainCardStyles.minWidth = "500px";
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// add a LoadingState object, in case the initial loads (of meta data and view) are slow //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
||||
const [pageLoadingState, _] = useState(new LoadingState(forceUpdate));
|
||||
|
||||
//x const [gridFilterItem, setGridFilterItem] = useState(props.item);
|
||||
const [pasteModalIsOpen, setPasteModalIsOpen] = useState(false);
|
||||
const [inputText, setInputText] = useState("");
|
||||
@ -75,8 +87,11 @@ function FilterCriteriaPaster({type, onSave}: Props): JSX.Element
|
||||
const [delimiterCharacter, setDelimiterCharacter] = useState("");
|
||||
const [customDelimiterValue, setCustomDelimiterValue] = useState("");
|
||||
const [chipData, setChipData] = useState(undefined);
|
||||
const [chipValidity, setChipValidity] = useState([] as boolean[]);
|
||||
const [chipPVSIds, setChipPVSIds] = useState([] as any[]);
|
||||
const [detectedText, setDetectedText] = useState("");
|
||||
const [errorText, setErrorText] = useState("");
|
||||
const [saveDisabled, setSaveDisabled] = useState(true);
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// handler for when paste icon is clicked in 'any' operator //
|
||||
@ -92,6 +107,7 @@ function FilterCriteriaPaster({type, onSave}: Props): JSX.Element
|
||||
setDelimiter("");
|
||||
setDelimiterCharacter("");
|
||||
setChipData([]);
|
||||
setChipValidity([]);
|
||||
setInputText("");
|
||||
setDetectedText("");
|
||||
setCustomDelimiterValue("");
|
||||
@ -106,18 +122,43 @@ function FilterCriteriaPaster({type, onSave}: Props): JSX.Element
|
||||
|
||||
const handleSaveClicked = () =>
|
||||
{
|
||||
////////////////////////////////////////
|
||||
// if numeric remove any non-numerics //
|
||||
////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////
|
||||
// if numeric remove any non-numerics, or invalid pvs values //
|
||||
///////////////////////////////////////////////////////////////
|
||||
let saveData = [];
|
||||
let usedLabels = new Map<any, boolean>();
|
||||
for (let i = 0; i < chipData.length; i++)
|
||||
{
|
||||
if (type !== "number" || !Number.isNaN(Number(chipData[i])))
|
||||
if (chipValidity[i] === true)
|
||||
{
|
||||
saveData.push(chipData[i]);
|
||||
if (type === "pvs")
|
||||
{
|
||||
/////////////////////////////////////////////
|
||||
// if already used this PVS label, skip it //
|
||||
/////////////////////////////////////////////
|
||||
if (usedLabels.get(chipData[i]) != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
saveData.push(new QPossibleValue({id: chipPVSIds[i], label: chipData[i]}));
|
||||
usedLabels.set(chipData[i], true);
|
||||
}
|
||||
else
|
||||
{
|
||||
saveData.push(chipData[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// for pvs, sort by label before saving //
|
||||
//////////////////////////////////////////
|
||||
if (type === "pvs")
|
||||
{
|
||||
saveData.sort((a: QPossibleValue, b: QPossibleValue) => b.label.localeCompare(a.label));
|
||||
}
|
||||
|
||||
onSave(saveData);
|
||||
|
||||
clearData();
|
||||
@ -259,12 +300,19 @@ function FilterCriteriaPaster({type, onSave}: Props): JSX.Element
|
||||
{
|
||||
chipData.push(part);
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// if numeric, check that first before pushing as a chip //
|
||||
///////////////////////////////////////////////////////////
|
||||
if (type === "number" && Number.isNaN(Number(part)))
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// if numeric or pvs, check validity first before pushing as a chip //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
if (chipValidity[i] !== true)
|
||||
{
|
||||
setErrorText("Some values are not numbers");
|
||||
if (type === "number" && Number.isNaN(Number(part)))
|
||||
{
|
||||
setErrorText("Some values are not numbers");
|
||||
}
|
||||
else if (type === "pvs")
|
||||
{
|
||||
setErrorText("Some values are not valid");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -272,7 +320,7 @@ function FilterCriteriaPaster({type, onSave}: Props): JSX.Element
|
||||
|
||||
setChipData(chipData);
|
||||
|
||||
}, [inputText, delimiterCharacter, customDelimiterValue, detectedText]);
|
||||
}, [inputText, delimiterCharacter, customDelimiterValue, detectedText, chipValidity]);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
@ -283,128 +331,153 @@ function FilterCriteriaPaster({type, onSave}: Props): JSX.Element
|
||||
pasteModalIsOpen &&
|
||||
(
|
||||
<Modal open={pasteModalIsOpen}>
|
||||
<Box sx={{position: "absolute", overflowY: "auto", width: "100%"}}>
|
||||
<Box py={3} justifyContent="center" sx={{display: "flex", mt: 8}}>
|
||||
<Card sx={mainCardStyles}>
|
||||
<Box p={4} pb={2}>
|
||||
<Grid container>
|
||||
<Grid item pr={3} xs={12} lg={12}>
|
||||
<Typography variant="h5">Bulk Add Filter Values</Typography>
|
||||
<Typography sx={{display: "flex", lineHeight: "1.7", textTransform: "revert"}} variant="button">
|
||||
Paste into the box on the left.
|
||||
Review the filter values in the box on the right.
|
||||
If the filter values are not what are expected, try changing the separator using the dropdown below.
|
||||
</Typography>
|
||||
<Box>
|
||||
<Box sx={{position: "absolute", overflowY: "auto", width: "100%"}}>
|
||||
<Box py={3} justifyContent="center" sx={{display: "flex", mt: 8}}>
|
||||
<Card sx={mainCardStyles}>
|
||||
<Box p={4} pb={2}>
|
||||
<Grid container>
|
||||
<Grid item pr={3} xs={12} lg={12}>
|
||||
<Typography variant="h5">Bulk Add Filter Values</Typography>
|
||||
<Typography sx={{display: "flex", lineHeight: "1.7", textTransform: "revert"}} variant="button">
|
||||
Paste into the box on the left.
|
||||
Review the filter values in the box on the right.
|
||||
If the filter values are not what are expected, try changing the separator using the dropdown below.
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
<Grid container pl={3} pr={3} justifyContent="center" alignItems="stretch" sx={{display: "flex", height: "100%"}}>
|
||||
<Grid item pr={3} xs={6} lg={6} sx={{width: "100%", display: "flex", flexDirection: "column", flexGrow: 1}}>
|
||||
<FormControl sx={{m: 1, width: "100%"}}>
|
||||
<TextField
|
||||
id="outlined-multiline-static"
|
||||
label="PASTE TEXT"
|
||||
multiline
|
||||
onChange={handleTextChange}
|
||||
rows={16}
|
||||
value={inputText}
|
||||
/>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={6} lg={6} sx={{display: "flex", flexGrow: 1}}>
|
||||
<FormControl sx={{m: 1, width: "100%"}}>
|
||||
<ChipTextField
|
||||
handleChipChange={(isMakingRequest: boolean, chipValidity: boolean[], chipPVSIds: any[]) =>
|
||||
{
|
||||
setErrorText("");
|
||||
if (isMakingRequest)
|
||||
{
|
||||
pageLoadingState.setLoading();
|
||||
}
|
||||
else
|
||||
{
|
||||
pageLoadingState.setNotLoading();
|
||||
}
|
||||
setSaveDisabled(isMakingRequest);
|
||||
setChipPVSIds(chipPVSIds);
|
||||
setChipValidity(chipValidity);
|
||||
}}
|
||||
table={table}
|
||||
field={field}
|
||||
chipData={chipData}
|
||||
chipValidity={chipValidity}
|
||||
chipType={type}
|
||||
multiline
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
id="tags"
|
||||
rows={0}
|
||||
name="tags"
|
||||
label="FILTER VALUES REVIEW"
|
||||
/>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
<Grid container pl={3} pr={3} justifyContent="center" alignItems="stretch" sx={{display: "flex", height: "100%"}}>
|
||||
<Grid item pr={3} xs={6} lg={6} sx={{width: "100%", display: "flex", flexDirection: "column", flexGrow: 1}}>
|
||||
<FormControl sx={{m: 1, width: "100%"}}>
|
||||
<TextField
|
||||
id="outlined-multiline-static"
|
||||
label="PASTE TEXT"
|
||||
multiline
|
||||
onChange={handleTextChange}
|
||||
rows={16}
|
||||
value={inputText}
|
||||
/>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={6} lg={6} sx={{display: "flex", flexGrow: 1}}>
|
||||
<FormControl sx={{m: 1, width: "100%"}}>
|
||||
<ChipTextField
|
||||
handleChipChange={() =>
|
||||
{
|
||||
}}
|
||||
chipData={chipData}
|
||||
chipType={type}
|
||||
multiline
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
id="tags"
|
||||
rows={0}
|
||||
name="tags"
|
||||
label="FILTER VALUES REVIEW"
|
||||
/>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container pl={3} pr={3} justifyContent="center" alignItems="stretch" sx={{display: "flex", height: "100%"}}>
|
||||
<Grid item pl={1} pr={3} xs={6} lg={6} sx={{width: "100%", display: "flex", flexDirection: "column", flexGrow: 1}}>
|
||||
<Box sx={{display: "inline-flex", alignItems: "baseline"}}>
|
||||
<FormControl sx={{mt: 2, width: "50%"}}>
|
||||
<InputLabel htmlFor="select-native">
|
||||
SEPARATOR
|
||||
</InputLabel>
|
||||
<Select
|
||||
multiline
|
||||
native
|
||||
value={delimiter}
|
||||
onChange={handleDelimiterChange}
|
||||
label="SEPARATOR"
|
||||
size="medium"
|
||||
inputProps={{
|
||||
id: "select-native",
|
||||
}}
|
||||
>
|
||||
{delimiterDropdownOptions.map((delimiter) => (
|
||||
<option key={delimiter} value={delimiter}>
|
||||
{delimiter}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
{delimiter === Delimiter.CUSTOM.valueOf() && (
|
||||
|
||||
<FormControl sx={{pl: 2, top: 5, width: "50%"}}>
|
||||
<TextField
|
||||
name="custom-delimiter-value"
|
||||
placeholder="Custom Separator"
|
||||
label="Custom Separator"
|
||||
variant="standard"
|
||||
value={customDelimiterValue}
|
||||
onChange={handleCustomDelimiterChange}
|
||||
inputProps={{maxLength: 1}}
|
||||
/>
|
||||
<Grid container pl={3} pr={3} justifyContent="center" alignItems="stretch" sx={{display: "flex", height: "100%"}}>
|
||||
<Grid item pl={1} pr={3} xs={6} lg={6} sx={{width: "100%", display: "flex", flexDirection: "column", flexGrow: 1}}>
|
||||
<Box sx={{display: "inline-flex", alignItems: "baseline"}}>
|
||||
<FormControl sx={{mt: 2, width: "50%"}}>
|
||||
<InputLabel htmlFor="select-native">
|
||||
SEPARATOR
|
||||
</InputLabel>
|
||||
<Select
|
||||
multiline
|
||||
native
|
||||
value={delimiter}
|
||||
onChange={handleDelimiterChange}
|
||||
label="SEPARATOR"
|
||||
size="medium"
|
||||
inputProps={{
|
||||
id: "select-native",
|
||||
}}
|
||||
>
|
||||
{delimiterDropdownOptions.map((delimiter) => (
|
||||
<option key={delimiter} value={delimiter}>
|
||||
{delimiter}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
{inputText && delimiter === Delimiter.DETECT_AUTOMATICALLY.valueOf() && (
|
||||
{delimiter === Delimiter.CUSTOM.valueOf() && (
|
||||
|
||||
<Typography pl={2} variant="button" sx={{top: "1px", textTransform: "revert"}}>
|
||||
<i>{detectedText}</i>
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
<FormControl sx={{pl: 2, top: 5, width: "50%"}}>
|
||||
<TextField
|
||||
name="custom-delimiter-value"
|
||||
placeholder="Custom Separator"
|
||||
label="Custom Separator"
|
||||
variant="standard"
|
||||
value={customDelimiterValue}
|
||||
onChange={handleCustomDelimiterChange}
|
||||
inputProps={{maxLength: 1}}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
{inputText && delimiter === Delimiter.DETECT_AUTOMATICALLY.valueOf() && (
|
||||
|
||||
<Typography pl={2} variant="button" sx={{top: "1px", textTransform: "revert"}}>
|
||||
<i>{detectedText}</i>
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid sx={{display: "flex", justifyContent: "flex-start", alignItems: "flex-start"}} item pl={1} xs={3} lg={3}>
|
||||
{
|
||||
errorText && chipData.length > 0 && (
|
||||
<Box sx={{display: "flex", justifyContent: "flex-start", alignItems: "flex-start"}}>
|
||||
<Icon color="error">error</Icon>
|
||||
<Typography sx={{paddingLeft: "4px", textTransform: "revert"}} variant="button">{errorText}</Typography>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
{
|
||||
pageLoadingState.isLoadingSlow() && (
|
||||
<Box sx={{display: "flex", justifyContent: "flex-start", alignItems: "flex-start"}}>
|
||||
<Icon color="warning">warning</Icon>
|
||||
<Typography sx={{paddingLeft: "4px", textTransform: "revert"}} variant="button">Loading...</Typography>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
</Grid>
|
||||
<Grid sx={{display: "flex", justifyContent: "flex-end", alignItems: "flex-start"}} item pr={1} xs={3} lg={3}>
|
||||
{
|
||||
chipData && chipData.length > 0 && (
|
||||
<Typography sx={{textTransform: "revert"}} variant="button">{chipData.length.toLocaleString()} {chipData.length === 1 ? "value" : "values"}</Typography>
|
||||
)
|
||||
}
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid sx={{display: "flex", justifyContent: "flex-start", alignItems: "flex-start"}} item pl={1} xs={3} lg={3}>
|
||||
{
|
||||
errorText && chipData.length > 0 && (
|
||||
<Box sx={{display: "flex", justifyContent: "flex-start", alignItems: "flex-start"}}>
|
||||
<Icon color="error">error</Icon>
|
||||
<Typography sx={{paddingLeft: "4px", textTransform: "revert"}} variant="button">{errorText}</Typography>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
</Grid>
|
||||
<Grid sx={{display: "flex", justifyContent: "flex-end", alignItems: "flex-start"}} item pr={1} xs={3} lg={3}>
|
||||
{
|
||||
chipData && chipData.length > 0 && (
|
||||
<Typography sx={{textTransform: "revert"}} variant="button">{chipData.length.toLocaleString()} {chipData.length === 1 ? "value" : "values"}</Typography>
|
||||
)
|
||||
}
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Box p={3} pt={0}>
|
||||
<Grid container pl={1} pr={1} justifyContent="right" alignItems="stretch" sx={{display: "flex-inline "}}>
|
||||
<QCancelButton
|
||||
onClickHandler={handleCancelClicked}
|
||||
iconName="cancel"
|
||||
disabled={false} />
|
||||
<QSaveButton onClickHandler={handleSaveClicked} label="Add Filters" disabled={false} />
|
||||
</Grid>
|
||||
</Box>
|
||||
</Card>
|
||||
<Box p={3} pt={0}>
|
||||
<Grid container pl={1} pr={1} justifyContent="right" alignItems="stretch" sx={{display: "flex-inline "}}>
|
||||
<QCancelButton
|
||||
onClickHandler={handleCancelClicked}
|
||||
iconName="cancel"
|
||||
disabled={false} />
|
||||
<QSaveButton onClickHandler={handleSaveClicked} label="Add Filters" disabled={saveDisabled} />
|
||||
</Grid>
|
||||
</Box>
|
||||
</Card>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Modal>
|
||||
|
@ -398,20 +398,25 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
|
||||
initialValues = criteria.values;
|
||||
}
|
||||
}
|
||||
return <Box>
|
||||
<DynamicSelect
|
||||
fieldPossibleValueProps={{tableName: table.name, fieldName: field.name, initialDisplayValue: null}}
|
||||
overrideId={field.name + "-multi-" + criteria.id}
|
||||
key={field.name + "-multi-" + criteria.id}
|
||||
isMultiple
|
||||
fieldLabel="Values"
|
||||
initialValues={initialValues}
|
||||
initiallyOpen={false /*initiallyOpenMultiValuePvs*/}
|
||||
inForm={false}
|
||||
onChange={(value: any) => valueChangeHandler(null, "all", value)}
|
||||
variant="standard"
|
||||
useCase="filter"
|
||||
/>
|
||||
return <Box display="flex" alignItems="flex-end" className="multiValue">
|
||||
<Box width={"100%"}>
|
||||
<DynamicSelect
|
||||
fieldPossibleValueProps={{tableName: table.name, fieldName: field.name, initialDisplayValue: null}}
|
||||
overrideId={field.name + "-multi-" + criteria.id}
|
||||
key={field.name + "-multi-" + criteria.id + "-" + criteria.values.length}
|
||||
isMultiple
|
||||
fieldLabel="Values"
|
||||
initialValues={initialValues}
|
||||
initiallyOpen={false /*initiallyOpenMultiValuePvs*/}
|
||||
inForm={false}
|
||||
onChange={(value: any) => valueChangeHandler(null, "all", value)}
|
||||
variant="standard"
|
||||
useCase="filter"
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<FilterCriteriaPaster table={table} field={field} type="pvs" onSave={(newValues: any[]) => saveNewPasterValues(newValues)} />
|
||||
</Box>
|
||||
</Box>;
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ import XIcon from "qqq/components/query/XIcon";
|
||||
import {QueryScreenUsage} from "qqq/pages/records/query/RecordQuery";
|
||||
import FilterUtils from "qqq/utils/qqq/FilterUtils";
|
||||
import TableUtils from "qqq/utils/qqq/TableUtils";
|
||||
import React, {SyntheticEvent, useContext, useReducer, useState} from "react";
|
||||
import React, {SyntheticEvent, useContext, useEffect, useReducer, useState} from "react";
|
||||
|
||||
export type CriteriaParamType = QFilterCriteriaWithId | null | "tooComplex";
|
||||
|
||||
@ -135,7 +135,7 @@ const getOperatorSelectedValue = (operatorOptions: OperatorOption[], criteria: Q
|
||||
return (filteredOptions[0]);
|
||||
}
|
||||
|
||||
if(return0thOptionInsteadOfNull)
|
||||
if (return0thOptionInsteadOfNull)
|
||||
{
|
||||
console.log("Returning 0th operator instead of null - this isn't expected, but has been seen to happen - so here's some additional debugging:");
|
||||
try
|
||||
@ -144,7 +144,7 @@ const getOperatorSelectedValue = (operatorOptions: OperatorOption[], criteria: Q
|
||||
console.log("Criteria: " + JSON.stringify(criteria));
|
||||
console.log("Default Operator: " + JSON.stringify(defaultOperator));
|
||||
}
|
||||
catch(e)
|
||||
catch (e)
|
||||
{
|
||||
console.log(`Error in debug output: ${e}`);
|
||||
}
|
||||
@ -186,6 +186,13 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
|
||||
//////////////////////
|
||||
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// was not seeing criteria changes take place until watching it stringified //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
setCriteria(criteria);
|
||||
}, [JSON.stringify(criteria)]);
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
|
@ -133,7 +133,7 @@ class FilterUtils
|
||||
}
|
||||
else
|
||||
{
|
||||
values = await qController.possibleValues(fieldTable.name, null, field.name, "", values, undefined, "filter");
|
||||
values = await qController.possibleValues(fieldTable.name, null, field.name, "", values, undefined, undefined, "filter");
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user