CE-1068: more updates for other operators to support variables

This commit is contained in:
Tim Chamberlain
2024-04-30 19:04:34 -05:00
parent 7316b6141b
commit d17c7f6990
6 changed files with 192 additions and 106 deletions

View File

@ -57,7 +57,7 @@ export default function AssignFilterVariable({valueIndex, field, valueChangeHand
return <Box display="flex" alignItems="flex-end"> return <Box display="flex" alignItems="flex-end">
<Box> <Box>
<Tooltip title={`Use a variable as the value for the ${field.name} field`} placement="bottom"> <Tooltip title={`Use a variable as the value for the ${field.label} field`} placement="bottom">
<Icon fontSize="small" color="info" sx={{mx: 0.25, cursor: "pointer", position: "relative", top: "2px"}} onClick={handleVariableButtonOnClick}>functions</Icon> <Icon fontSize="small" color="info" sx={{mx: 0.25, cursor: "pointer", position: "relative", top: "2px"}} onClick={handleVariableButtonOnClick}>functions</Icon>
</Tooltip> </Tooltip>
</Box> </Box>

View File

@ -196,12 +196,49 @@ export default function CriteriaDateField({valueIndex, label, idPrefix, field, c
setTimeout(() => setForceAdvancedDateTimeDialogOpen(false), 100); setTimeout(() => setForceAdvancedDateTimeDialogOpen(false), 100);
} }
const makeFilterVariableTextField = (expression: FilterVariableExpression, valueIndex: number = 0, label = "Value", idPrefix = "value-") =>
{
const clearValue = (event: React.MouseEvent<HTMLAnchorElement> | React.MouseEvent<HTMLButtonElement>, index: number) =>
{
valueChangeHandler(event, index, "");
document.getElementById(`${idPrefix}${criteria.id}`).focus();
};
const inputProps2: any = {};
inputProps2.endAdornment = (
<InputAdornment position="end">
<IconButton sx={{visibility: expression ? "visible" : "hidden"}} onClick={(event) => clearValue(event, valueIndex)}>
<Icon>closer</Icon>
</IconButton>
</InputAdornment>
);
return <NoWrapTooltip title={<EvaluatedExpression field={field} expression={expression} />} placement="bottom" enterDelay={1000} sx={{marginLeft: "-75px !important", marginTop: "-8px !important"}}><TextField
id={`${idPrefix}${criteria.id}`}
label={label}
variant="standard"
autoComplete="off"
InputProps={{disabled: true, readOnly: true, unselectable: "off", ...inputProps2}}
InputLabelProps={{shrink: true}}
value="${VARIABLE}"
fullWidth
/></NoWrapTooltip>;
};
return <Box display="flex" alignItems="flex-end"> return <Box display="flex" alignItems="flex-end">
{ {
isExpression ? makeDateTimeExpressionTextField(criteria.values[valueIndex], valueIndex, label, idPrefix) isExpression ?
currentExpression?.type == "FilterVariableExpression" ? (
makeFilterVariableTextField(criteria.values[valueIndex], valueIndex, label, idPrefix)
) : (
makeDateTimeExpressionTextField(criteria.values[valueIndex], valueIndex, label, idPrefix)
)
: makeTextField(field, criteria, valueChangeHandler, valueIndex, label, idPrefix, allowVariables) : makeTextField(field, criteria, valueChangeHandler, valueIndex, label, idPrefix, allowVariables)
} }
<Box> {
(!isExpression || currentExpression?.type != "FilterVariableExpression") && (
<><Box>
<Tooltip title={`Choose a common relative ${field.type == QFieldType.DATE ? "date" : "date-time"} expression`} placement="bottom"> <Tooltip title={`Choose a common relative ${field.type == QFieldType.DATE ? "date" : "date-time"} expression`} placement="bottom">
<Icon fontSize="small" color="info" sx={{mx: 0.25, cursor: "pointer", position: "relative", top: "2px"}} onClick={openRelativeDateTimeMenu}>date_range</Icon> <Icon fontSize="small" color="info" sx={{mx: 0.25, cursor: "pointer", position: "relative", top: "2px"}} onClick={openRelativeDateTimeMenu}>date_range</Icon>
</Tooltip> </Tooltip>
@ -211,8 +248,7 @@ export default function CriteriaDateField({valueIndex, label, idPrefix, field, c
transformOrigin={{horizontal: "left", vertical: "top"}} transformOrigin={{horizontal: "left", vertical: "top"}}
onClose={closeRelativeDateTimeMenu} onClose={closeRelativeDateTimeMenu}
> >
{ {field.type == QFieldType.DATE ?
field.type == QFieldType.DATE ?
<Box display="flex"> <Box display="flex">
<Box> <Box>
{tooltipMenuItemFromExpression(valueIndex, "left", newNowWithOffsetExpression("MINUS", 7, "DAYS"))} {tooltipMenuItemFromExpression(valueIndex, "left", newNowWithOffsetExpression("MINUS", 7, "DAYS"))}
@ -267,13 +303,13 @@ export default function CriteriaDateField({valueIndex, label, idPrefix, field, c
{tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("THIS", "YEARS"))} {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("THIS", "YEARS"))}
{tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("LAST", "YEARS"))} {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("LAST", "YEARS"))}
</Box> </Box>
</Box> </Box>}
}
</Menu> </Menu>
</Box> </Box><Box>
<Box>
<AdvancedDateTimeFilterValues type={field.type} expression={currentExpression} onSave={(expression: any) => saveNewDateTimeExpression(valueIndex, expression)} forcedOpen={forceAdvancedDateTimeDialogOpen} /> <AdvancedDateTimeFilterValues type={field.type} expression={currentExpression} onSave={(expression: any) => saveNewDateTimeExpression(valueIndex, expression)} forcedOpen={forceAdvancedDateTimeDialogOpen} />
</Box> </Box></>
)
}
</Box>; </Box>;
} }

View File

@ -28,8 +28,8 @@ import Box from "@mui/material/Box";
import Button from "@mui/material/Button/Button"; import Button from "@mui/material/Button/Button";
import Icon from "@mui/material/Icon/Icon"; import Icon from "@mui/material/Icon/Icon";
import {GridFilterPanelProps, GridSlotsComponentsProps} from "@mui/x-data-grid-pro"; import {GridFilterPanelProps, GridSlotsComponentsProps} from "@mui/x-data-grid-pro";
import React, {forwardRef, useReducer} from "react";
import {FilterCriteriaRow, getDefaultCriteriaValue} from "qqq/components/query/FilterCriteriaRow"; import {FilterCriteriaRow, getDefaultCriteriaValue} from "qqq/components/query/FilterCriteriaRow";
import React, {forwardRef, useReducer} from "react";
declare module "@mui/x-data-grid" declare module "@mui/x-data-grid"
@ -49,7 +49,7 @@ declare module "@mui/x-data-grid"
export class QFilterCriteriaWithId extends QFilterCriteria export class QFilterCriteriaWithId extends QFilterCriteria
{ {
id: number id: number;
} }
@ -62,6 +62,7 @@ export const CustomFilterPanel = forwardRef<any, GridFilterPanelProps>(
const [, forceUpdate] = useReducer((x) => x + 1, 0); const [, forceUpdate] = useReducer((x) => x + 1, 0);
const queryFilter = props.queryFilter; const queryFilter = props.queryFilter;
// console.log(`CustomFilterPanel: filter: ${JSON.stringify(queryFilter)}`); // console.log(`CustomFilterPanel: filter: ${JSON.stringify(queryFilter)}`);
function focusLastField() function focusLastField()
@ -142,7 +143,7 @@ export const CustomFilterPanel = forwardRef<any, GridFilterPanelProps>(
{ {
queryFilter.criteria[index] = newCriteria; queryFilter.criteria[index] = newCriteria;
clearTimeout(debounceTimeout) clearTimeout(debounceTimeout);
debounceTimeout = setTimeout(() => props.updateFilter(queryFilter), needDebounce ? 500 : 1); debounceTimeout = setTimeout(() => props.updateFilter(queryFilter), needDebounce ? 500 : 1);
forceUpdate(); forceUpdate();
@ -178,6 +179,7 @@ export const CustomFilterPanel = forwardRef<any, GridFilterPanelProps>(
updateCriteria={(newCriteria, needDebounce) => updateCriteria(newCriteria, index, needDebounce)} updateCriteria={(newCriteria, needDebounce) => updateCriteria(newCriteria, index, needDebounce)}
removeCriteria={() => removeCriteria(index)} removeCriteria={() => removeCriteria(index)}
updateBooleanOperator={(newValue) => updateBooleanOperator(newValue)} updateBooleanOperator={(newValue) => updateBooleanOperator(newValue)}
queryScreenUsage={props.queryScreenUsage}
/> />
{/*JSON.stringify(criteria)*/} {/*JSON.stringify(criteria)*/}
</Box> </Box>

View File

@ -35,6 +35,7 @@ import TextField from "@mui/material/TextField";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
import FieldAutoComplete from "qqq/components/misc/FieldAutoComplete"; import FieldAutoComplete from "qqq/components/misc/FieldAutoComplete";
import FilterCriteriaRowValues from "qqq/components/query/FilterCriteriaRowValues"; import FilterCriteriaRowValues from "qqq/components/query/FilterCriteriaRowValues";
import {QueryScreenUsage} from "qqq/pages/records/query/RecordQuery";
import FilterUtils from "qqq/utils/qqq/FilterUtils"; import FilterUtils from "qqq/utils/qqq/FilterUtils";
import React, {ReactNode, SyntheticEvent, useState} from "react"; import React, {ReactNode, SyntheticEvent, useState} from "react";
@ -197,6 +198,7 @@ interface FilterCriteriaRowProps
updateCriteria: (newCriteria: QFilterCriteria, needDebounce: boolean) => void; updateCriteria: (newCriteria: QFilterCriteria, needDebounce: boolean) => void;
removeCriteria: () => void; removeCriteria: () => void;
updateBooleanOperator: (newValue: string) => void; updateBooleanOperator: (newValue: string) => void;
queryScreenUsage?: QueryScreenUsage;
} }
FilterCriteriaRow.defaultProps = FilterCriteriaRow.defaultProps =
@ -265,7 +267,7 @@ export function validateCriteria(criteria: QFilterCriteria, operatorSelectedValu
return {criteriaIsValid, criteriaStatusTooltip}; return {criteriaIsValid, criteriaStatusTooltip};
} }
export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, booleanOperator, updateCriteria, removeCriteria, updateBooleanOperator}: FilterCriteriaRowProps): JSX.Element export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, booleanOperator, updateCriteria, removeCriteria, updateBooleanOperator, queryScreenUsage}: FilterCriteriaRowProps): JSX.Element
{ {
// console.log(`FilterCriteriaRow: criteria: ${JSON.stringify(criteria)}`); // console.log(`FilterCriteriaRow: criteria: ${JSON.stringify(criteria)}`);
const [operatorSelectedValue, setOperatorSelectedValue] = useState(null as OperatorOption); const [operatorSelectedValue, setOperatorSelectedValue] = useState(null as OperatorOption);
@ -513,6 +515,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria,
field={field} field={field}
table={fieldTable} table={fieldTable}
valueChangeHandler={(event, valueIndex, newValue) => handleValueChange(event, valueIndex, newValue)} valueChangeHandler={(event, valueIndex, newValue) => handleValueChange(event, valueIndex, newValue)}
queryScreenUsage={queryScreenUsage}
/> />
</Box> </Box>
<Box display="inline-block"> <Box display="inline-block">

View File

@ -220,6 +220,8 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
forceUpdate(); forceUpdate();
} }
const isExpression = criteria.values && criteria.values[0] && criteria.values[0].type;
switch (operatorOption.valueMode) switch (operatorOption.valueMode)
{ {
case ValueMode.NONE: case ValueMode.NONE:
@ -279,7 +281,12 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
{ {
selectedPossibleValue = criteria.values[0]; selectedPossibleValue = criteria.values[0];
} }
return <Box mb={-1.5}> return <Box display="flex">
{
isExpression ? (
makeTextField(field, criteria, valueChangeHandler, 0, undefined, undefined, allowVariables)
) : (
<Box mb={-1.5} width={allowVariables && !isExpression ? "100%" : "100%"}>
<DynamicSelect <DynamicSelect
tableName={table.name} tableName={table.name}
fieldName={field.name} fieldName={field.name}
@ -292,6 +299,12 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
onChange={(value: any) => valueChangeHandler(null, 0, value)} onChange={(value: any) => valueChangeHandler(null, 0, value)}
variant="standard" variant="standard"
/> />
</Box>
)
}
{
allowVariables && !isExpression && <Box mt={2.0}><AssignFilterVariable field={field} valueChangeHandler={valueChangeHandler} valueIndex={0} /></Box>
}
</Box>; </Box>;
case ValueMode.PVS_MULTI: case ValueMode.PVS_MULTI:
console.log("Doing pvs multi: " + criteria.values); console.log("Doing pvs multi: " + criteria.values);
@ -307,7 +320,12 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
initialValues = criteria.values; initialValues = criteria.values;
} }
} }
return <Box mb={-1.5}> return <Box display="flex">
{
isExpression ? (
makeTextField(field, criteria, valueChangeHandler, 0, undefined, undefined, allowVariables)
) : (
<Box mb={-1.5} width={allowVariables && !isExpression ? "90%" : "100%"}>
<DynamicSelect <DynamicSelect
tableName={table.name} tableName={table.name}
fieldName={field.name} fieldName={field.name}
@ -321,6 +339,12 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
onChange={(value: any) => valueChangeHandler(null, "all", value)} onChange={(value: any) => valueChangeHandler(null, "all", value)}
variant="standard" variant="standard"
/> />
</Box>
)
}
{
allowVariables && !isExpression && <Box mt={2.0} sx={{width: "10%"}}><AssignFilterVariable field={field} valueChangeHandler={valueChangeHandler} valueIndex={0} /></Box>
}
</Box>; </Box>;
} }

View File

@ -900,6 +900,26 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init
return; return;
} }
/////////////////////////////////////////////////////////////////////////////////////////////////
// if any values in the query are of type "FilterVariableExpression", display an error showing //
// that a backend query cannot be made because of missing values for that expression //
/////////////////////////////////////////////////////////////////////////////////////////////////
setWarningAlert(null);
for (var i = 0; i < queryFilter?.criteria.length; i++)
{
for (var j = 0; j < queryFilter?.criteria[i].values.length; j++)
{
const value = queryFilter.criteria[i].values[j];
if (value?.type == "FilterVariableExpression")
{
setWarningAlert("Cannot perform query because of a missing value for a variable.");
setLoading(false);
setRows([]);
return;
}
}
}
recordAnalytics({category: "tableEvents", action: "query", label: tableMetaData.label}); recordAnalytics({category: "tableEvents", action: "query", label: tableMetaData.label});
console.log(`In updateTable for ${reason} ${JSON.stringify(queryFilter)}`); console.log(`In updateTable for ${reason} ${JSON.stringify(queryFilter)}`);
@ -2888,6 +2908,7 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init
filterPanel: filterPanel:
{ {
tableMetaData: tableMetaData, tableMetaData: tableMetaData,
queryScreenUsage: usage,
metaData: metaData, metaData: metaData,
queryFilter: queryFilter, queryFilter: queryFilter,
updateFilter: doSetQueryFilter, updateFilter: doSetQueryFilter,