diff --git a/src/qqq/components/query/AssignFilterVariable.tsx b/src/qqq/components/query/AssignFilterVariable.tsx new file mode 100644 index 0000000..642f157 --- /dev/null +++ b/src/qqq/components/query/AssignFilterVariable.tsx @@ -0,0 +1,66 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2023. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData"; +import {FilterVariableExpression} from "@kingsrook/qqq-frontend-core/lib/model/query/FilterVariableExpression"; +import Box from "@mui/material/Box"; +import Icon from "@mui/material/Icon"; +import Tooltip from "@mui/material/Tooltip"; +import CriteriaDateField from "qqq/components/query/CriteriaDateField"; +import React, {SyntheticEvent, useState} from "react"; + + +export type Expression = FilterVariableExpression; + + +interface AssignFilterButtonProps +{ + valueIndex: number; + field: QFieldMetaData; + valueChangeHandler: (event: React.ChangeEvent | SyntheticEvent, valueIndex?: number | "all", newValue?: any) => void; +} + +CriteriaDateField.defaultProps = { + valueIndex: 0, + label: "Value", + idPrefix: "value-" +}; + +export default function AssignFilterVariable({valueIndex, field, valueChangeHandler}: AssignFilterButtonProps): JSX.Element +{ + const [isValueAVariable, setIsValueAVariable] = useState(false); + + const handleVariableButtonOnClick = () => + { + setIsValueAVariable(!isValueAVariable); + const expression = new FilterVariableExpression({fieldName: field.name, valueIndex: valueIndex}); + valueChangeHandler(null, valueIndex, expression); + }; + + return + + + functions + + + ; +} + diff --git a/src/qqq/components/query/BasicAndAdvancedQueryControls.tsx b/src/qqq/components/query/BasicAndAdvancedQueryControls.tsx index 7c13dff..2f45a95 100644 --- a/src/qqq/components/query/BasicAndAdvancedQueryControls.tsx +++ b/src/qqq/components/query/BasicAndAdvancedQueryControls.tsx @@ -114,7 +114,7 @@ export function getCurrentSortIndicator(queryFilter: QQueryFilter, tableMetaData *******************************************************************************/ const BasicAndAdvancedQueryControls = forwardRef((props: BasicAndAdvancedQueryControlsProps, ref) => { - const {metaData, tableMetaData, savedViewsComponent, columnMenuComponent, quickFilterFieldNames, setQuickFilterFieldNames, setQueryFilter, queryFilter, gridApiRef, queryFilterJSON, mode, setMode} = props; + const {metaData, tableMetaData, savedViewsComponent, columnMenuComponent, quickFilterFieldNames, setQuickFilterFieldNames, setQueryFilter, queryFilter, gridApiRef, queryFilterJSON, mode, setMode, queryScreenUsage} = props; ///////////////////// // state variables // @@ -682,6 +682,7 @@ const BasicAndAdvancedQueryControls = forwardRef((props: BasicAndAdvancedQueryCo criteriaParam={getQuickCriteriaParam(fieldName)} fieldMetaData={field} defaultOperator={defaultOperator} + queryScreenUsage={queryScreenUsage} handleRemoveQuickFilterField={null} />); }) } @@ -701,6 +702,7 @@ const BasicAndAdvancedQueryControls = forwardRef((props: BasicAndAdvancedQueryCo criteriaParam={getQuickCriteriaParam(fieldName)} fieldMetaData={field} defaultOperator={defaultOperator} + queryScreenUsage={queryScreenUsage} handleRemoveQuickFilterField={handleRemoveQuickFilterField} />); }) } diff --git a/src/qqq/components/query/CriteriaDateField.tsx b/src/qqq/components/query/CriteriaDateField.tsx index 7040c5d..1c61f5d 100644 --- a/src/qqq/components/query/CriteriaDateField.tsx +++ b/src/qqq/components/query/CriteriaDateField.tsx @@ -21,6 +21,7 @@ import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData"; import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType"; +import {FilterVariableExpression} from "@kingsrook/qqq-frontend-core/lib/model/query/FilterVariableExpression"; import {NowExpression} from "@kingsrook/qqq-frontend-core/lib/model/query/NowExpression"; import {NowWithOffsetExpression, NowWithOffsetOperator, NowWithOffsetUnit} from "@kingsrook/qqq-frontend-core/lib/model/query/NowWithOffsetExpression"; import {ThisOrLastPeriodExpression, ThisOrLastPeriodOperator, ThisOrLastPeriodUnit} from "@kingsrook/qqq-frontend-core/lib/model/query/ThisOrLastPeriodExpression"; @@ -34,14 +35,14 @@ import MenuItem from "@mui/material/MenuItem"; import {styled} from "@mui/material/styles"; import TextField from "@mui/material/TextField"; import Tooltip, {tooltipClasses, TooltipProps} from "@mui/material/Tooltip"; -import React, {SyntheticEvent, useEffect, useReducer, useState} from "react"; import AdvancedDateTimeFilterValues from "qqq/components/query/AdvancedDateTimeFilterValues"; import {QFilterCriteriaWithId} from "qqq/components/query/CustomFilterPanel"; import {EvaluatedExpression} from "qqq/components/query/EvaluatedExpression"; import {makeTextField} from "qqq/components/query/FilterCriteriaRowValues"; +import React, {SyntheticEvent, useReducer, useState} from "react"; -export type Expression = NowWithOffsetExpression | ThisOrLastPeriodExpression | NowExpression; +export type Expression = NowWithOffsetExpression | ThisOrLastPeriodExpression | NowExpression | FilterVariableExpression; interface CriteriaDateFieldProps @@ -52,6 +53,7 @@ interface CriteriaDateFieldProps field: QFieldMetaData; criteria: QFilterCriteriaWithId; valueChangeHandler: (event: React.ChangeEvent | SyntheticEvent, valueIndex?: number | "all", newValue?: any) => void; + allowVariables?: boolean; } CriteriaDateField.defaultProps = { @@ -60,19 +62,30 @@ CriteriaDateField.defaultProps = { idPrefix: "value-" }; -export default function CriteriaDateField({valueIndex, label, idPrefix, field, criteria, valueChangeHandler}: CriteriaDateFieldProps): JSX.Element +export const NoWrapTooltip = styled(({className, children, ...props}: TooltipProps) => ( + {children} +))({ + [`& .${tooltipClasses.tooltip}`]: { + whiteSpace: "nowrap" + }, +}); + +export default function CriteriaDateField({valueIndex, label, idPrefix, field, criteria, valueChangeHandler, allowVariables}: CriteriaDateFieldProps): JSX.Element { + const [relativeDateTimeOpen, setRelativeDateTimeOpen] = useState(false); const [relativeDateTimeMenuAnchorElement, setRelativeDateTimeMenuAnchorElement] = useState(null); - const [forceAdvancedDateTimeDialogOpen, setForceAdvancedDateTimeDialogOpen] = useState(false) + const [forceAdvancedDateTimeDialogOpen, setForceAdvancedDateTimeDialogOpen] = useState(false); const [, forceUpdate] = useReducer((x) => x + 1, 0); const openRelativeDateTimeMenu = (event: React.MouseEvent) => { + setRelativeDateTimeOpen(true); setRelativeDateTimeMenuAnchorElement(event.currentTarget); }; const closeRelativeDateTimeMenu = () => { + setRelativeDateTimeOpen(false); setRelativeDateTimeMenuAnchorElement(null); }; @@ -137,20 +150,12 @@ export default function CriteriaDateField({valueIndex, label, idPrefix, field, c const isExpression = criteria.values && criteria.values[valueIndex] && criteria.values[valueIndex].type; const currentExpression = isExpression ? criteria.values[valueIndex] : null; - const NoWrapTooltip = styled(({className, children, ...props}: TooltipProps) => ( - {children} - ))({ - [`& .${tooltipClasses.tooltip}`]: { - whiteSpace: "nowrap" - }, - }); - const tooltipMenuItemFromExpression = (valueIndex: number, tooltipPlacement: "left" | "right", expression: Expression) => { let startOfPrefix = ""; - if(expression.type == "ThisOrLastPeriod") + if (expression.type == "ThisOrLastPeriod") { - if(field.type == QFieldType.DATE_TIME || expression.timeUnit != "DAYS") + if (field.type == QFieldType.DATE_TIME || expression.timeUnit != "DAYS") { startOfPrefix = "start of "; } @@ -194,14 +199,14 @@ export default function CriteriaDateField({valueIndex, label, idPrefix, field, c return { isExpression ? makeDateTimeExpressionTextField(criteria.values[valueIndex], valueIndex, label, idPrefix) - : makeTextField(field, criteria, valueChangeHandler, valueIndex, label, idPrefix) + : makeTextField(field, criteria, valueChangeHandler, valueIndex, label, idPrefix, allowVariables) } date_range ( const someRef = createRef(); const textRef = useRef(null); - const [didInitialFocus, setDidInitialFocus] = useState(false) + const [didInitialFocus, setDidInitialFocus] = useState(false); const [openGroups, setOpenGroups] = useState(props.initialOpenedGroups || {}); const openGroupsBecauseOfFilter = {} as { [name: string]: boolean }; @@ -71,9 +73,9 @@ export const CustomColumnsPanel = forwardRef( console.log(`Open groups: ${JSON.stringify(openGroups)}`); - if(!didInitialFocus) + if (!didInitialFocus) { - if(textRef.current) + if (textRef.current) { textRef.current.select(); setDidInitialFocus(true); @@ -189,11 +191,11 @@ export const CustomColumnsPanel = forwardRef( /////////////////////////////////////////////////////////////////////////////////////////////////////// // always sort columns by label. note, in future may offer different sorts - here's where to do it. // /////////////////////////////////////////////////////////////////////////////////////////////////////// - const sortedColumns = [... columns]; + const sortedColumns = [...columns]; sortedColumns.sort((a, b): number => { return a.headerName.localeCompare(b.headerName); - }) + }); for (let i = 0; i < sortedColumns.length; i++) { @@ -361,7 +363,7 @@ export const CustomColumnsPanel = forwardRef( const changeFilterText = (newValue: string) => { setFilterText(newValue); - props.filterTextChanger(newValue) + props.filterTextChanger(newValue); }; const filterTextChanged = (event: React.ChangeEvent) => diff --git a/src/qqq/components/query/EvaluatedExpression.tsx b/src/qqq/components/query/EvaluatedExpression.tsx index 9ab4f53..23c3065 100644 --- a/src/qqq/components/query/EvaluatedExpression.tsx +++ b/src/qqq/components/query/EvaluatedExpression.tsx @@ -21,9 +21,9 @@ import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData"; import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType"; -import React, {useEffect, useState} from "react"; import {Expression} from "qqq/components/query/CriteriaDateField"; import ValueUtils from "qqq/utils/qqq/ValueUtils"; +import React, {useEffect, useState} from "react"; /******************************************************************************* ** Helper component to show value inside tooltips that ticks up every second. @@ -57,6 +57,11 @@ const HOUR_MS = 60 * 60 * 1000; const DAY_MS = 24 * 60 * 60 * 1000; const evaluateExpression = (time: Date, field: QFieldMetaData, expression: Expression): string => { + if (expression.type == "FilterVariableExpression") + { + return (expression.toString()); + } + let rs: Date = null; if (expression.type == "NowWithOffset") { diff --git a/src/qqq/components/query/FilterCriteriaRow.tsx b/src/qqq/components/query/FilterCriteriaRow.tsx index ff720a2..2f82085 100644 --- a/src/qqq/components/query/FilterCriteriaRow.tsx +++ b/src/qqq/components/query/FilterCriteriaRow.tsx @@ -72,7 +72,7 @@ export const getValueModeRequiredCount = (valueMode: ValueMode): number => case ValueMode.PVS_MULTI: return (null); } -} +}; export interface OperatorOption { @@ -183,7 +183,7 @@ export const getOperatorOptions = (tableMetaData: QTableMetaData, fieldName: str } return (operatorOptions); -} +}; interface FilterCriteriaRowProps @@ -200,10 +200,9 @@ interface FilterCriteriaRowProps } FilterCriteriaRow.defaultProps = - { - }; + {}; -export function validateCriteria(criteria: QFilterCriteria, operatorSelectedValue?: OperatorOption): {criteriaIsValid: boolean, criteriaStatusTooltip: string} +export function validateCriteria(criteria: QFilterCriteria, operatorSelectedValue?: OperatorOption): { criteriaIsValid: boolean, criteriaStatusTooltip: string } { let criteriaIsValid = true; let criteriaStatusTooltip = "This condition is fully defined and is part of your filter."; @@ -213,7 +212,7 @@ export function validateCriteria(criteria: QFilterCriteria, operatorSelectedValu return (value === null || value == undefined || String(value).trim() === ""); } - if(!criteria) + if (!criteria) { criteriaIsValid = false; criteriaStatusTooltip = "This condition is not defined."; @@ -284,7 +283,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, let defaultFieldValue; let field = null; let fieldTable = null; - if(criteria && criteria.fieldName) + if (criteria && criteria.fieldName) { [field, fieldTable] = FilterUtils.getField(tableMetaData, criteria.fieldName); if (field && fieldTable) @@ -303,9 +302,9 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, let newOperatorSelectedValue = operatorOptions.filter(option => { - if(option.value == criteria.operator) + if (option.value == criteria.operator) { - if(option.implicitValues) + if (option.implicitValues) { return (JSON.stringify(option.implicitValues) == JSON.stringify(criteria.values)); } @@ -316,7 +315,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, } return (false); })[0]; - if(newOperatorSelectedValue?.label !== operatorSelectedValue?.label) + if (newOperatorSelectedValue?.label !== operatorSelectedValue?.label) { setOperatorSelectedValue(newOperatorSelectedValue); setOperatorInputValue(newOperatorSelectedValue?.label); @@ -379,12 +378,12 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, { criteria.operator = newValue ? newValue.value : null; - if(newValue) + if (newValue) { setOperatorSelectedValue(newValue); setOperatorInputValue(newValue.label); - if(newValue.implicitValues) + if (newValue.implicitValues) { criteria.values = newValue.implicitValues; } @@ -393,15 +392,15 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, // we've seen cases where switching operators can sometimes put a null in as the first value... // // that just causes a bad time (e.g., null pointers in Autocomplete), so, get rid of that. // ////////////////////////////////////////////////////////////////////////////////////////////////// - if(criteria.values && criteria.values.length == 1 && criteria.values[0] == null) + if (criteria.values && criteria.values.length == 1 && criteria.values[0] == null) { criteria.values = []; } - if(newValue.valueMode && !newValue.implicitValues) + if (newValue.valueMode && !newValue.implicitValues) { const requiredValueCount = getValueModeRequiredCount(newValue.valueMode); - if(requiredValueCount != null && criteria.values.length > requiredValueCount) + if (requiredValueCount != null && criteria.values.length > requiredValueCount) { criteria.values.splice(requiredValueCount); } @@ -424,12 +423,12 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, // @ts-ignore const value = newValue !== undefined ? newValue : event ? event.target.value : null; - if(!criteria.values) + if (!criteria.values) { criteria.values = []; } - if(valueIndex == "all") + if (valueIndex == "all") { criteria.values = value; } diff --git a/src/qqq/components/query/FilterCriteriaRowValues.tsx b/src/qqq/components/query/FilterCriteriaRowValues.tsx index 38f0e66..5e5a653 100644 --- a/src/qqq/components/query/FilterCriteriaRowValues.tsx +++ b/src/qqq/components/query/FilterCriteriaRowValues.tsx @@ -23,19 +23,23 @@ import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData"; import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType"; import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData"; +import {FilterVariableExpression} from "@kingsrook/qqq-frontend-core/lib/model/query/FilterVariableExpression"; import Autocomplete from "@mui/material/Autocomplete"; import Box from "@mui/material/Box"; import Icon from "@mui/material/Icon"; import IconButton from "@mui/material/IconButton"; import InputAdornment from "@mui/material/InputAdornment/InputAdornment"; import TextField from "@mui/material/TextField"; -import React, {SyntheticEvent, useReducer} from "react"; import DynamicSelect from "qqq/components/forms/DynamicSelect"; -import CriteriaDateField from "qqq/components/query/CriteriaDateField"; +import AssignFilterVariable from "qqq/components/query/AssignFilterVariable"; +import CriteriaDateField, {NoWrapTooltip} from "qqq/components/query/CriteriaDateField"; import {QFilterCriteriaWithId} from "qqq/components/query/CustomFilterPanel"; +import {EvaluatedExpression} from "qqq/components/query/EvaluatedExpression"; import FilterCriteriaPaster from "qqq/components/query/FilterCriteriaPaster"; import {OperatorOption, ValueMode} from "qqq/components/query/FilterCriteriaRow"; +import {QueryScreenUsage} from "qqq/pages/records/query/RecordQuery"; import ValueUtils from "qqq/utils/qqq/ValueUtils"; +import React, {SyntheticEvent, useReducer, useState} from "react"; interface Props { @@ -44,7 +48,8 @@ interface Props field: QFieldMetaData; table: QTableMetaData; valueChangeHandler: (event: React.ChangeEvent | SyntheticEvent, valueIndex?: number | "all", newValue?: any) => void; - initiallyOpenMultiValuePvs?: boolean + initiallyOpenMultiValuePvs?: boolean; + queryScreenUsage?: QueryScreenUsage; } FilterCriteriaRowValues.defaultProps = @@ -72,8 +77,10 @@ export const getTypeForTextField = (field: QFieldMetaData): string => return (type); }; -export const makeTextField = (field: QFieldMetaData, criteria: QFilterCriteriaWithId, valueChangeHandler?: (event: (React.ChangeEvent | React.SyntheticEvent), valueIndex?: (number | "all"), newValue?: any) => void, valueIndex: number = 0, label = "Value", idPrefix = "value-") => +export const makeTextField = (field: QFieldMetaData, criteria: QFilterCriteriaWithId, valueChangeHandler?: (event: (React.ChangeEvent | React.SyntheticEvent), valueIndex?: (number | "all"), newValue?: any) => void, valueIndex: number = 0, label = "Value", idPrefix = "value-", allowVariables = false) => { + const isExpression = criteria.values && criteria.values[valueIndex] && criteria.values[valueIndex].type; + let type = getTypeForTextField(field); const inputLabelProps: any = {}; @@ -95,7 +102,6 @@ export const makeTextField = (field: QFieldMetaData, criteria: QFilterCriteriaWi }; - /******************************************************************************* ** Event handler for key-down events - specifically added here, to stop pressing ** 'tab' in a date or date-time from closing the quick-filter... @@ -104,7 +110,7 @@ export const makeTextField = (field: QFieldMetaData, criteria: QFilterCriteriaWi { if (field.type == QFieldType.DATE || field.type == QFieldType.DATE_TIME) { - if(e.code == "Tab") + if (e.code == "Tab") { console.log("Tab on date or date-time - don't close me, just move to the next sub-field!..."); e.stopPropagation(); @@ -112,6 +118,36 @@ export const makeTextField = (field: QFieldMetaData, criteria: QFilterCriteriaWi } }; + + const makeFilterVariableTextField = (expression: FilterVariableExpression, valueIndex: number = 0, label = "Value", idPrefix = "value-") => + { + const clearValue = (event: React.MouseEvent | React.MouseEvent, index: number) => + { + valueChangeHandler(event, index, ""); + document.getElementById(`${idPrefix}${criteria.id}`).focus(); + }; + + const inputProps2: any = {}; + inputProps2.endAdornment = ( + + clearValue(event, valueIndex)}> + closer + + + ); + + return } placement="bottom" enterDelay={1000} sx={{marginLeft: "-75px !important", marginTop: "-8px !important"}}>; + }; + const inputProps: any = {}; inputProps.endAdornment = ( @@ -121,25 +157,40 @@ export const makeTextField = (field: QFieldMetaData, criteria: QFilterCriteriaWi ); - return valueChangeHandler(event, valueIndex)} - onKeyDown={handleKeyDown} - value={value} - InputLabelProps={inputLabelProps} - InputProps={inputProps} - fullWidth - autoFocus={true} - />; + return + { + isExpression ? ( + makeFilterVariableTextField(criteria.values[valueIndex], valueIndex, label, idPrefix) + ) : ( + valueChangeHandler(event, valueIndex)} + onKeyDown={handleKeyDown} + value={value} + InputLabelProps={inputLabelProps} + InputProps={inputProps} + fullWidth + autoFocus={true} + /> + ) + } + { + allowVariables && ( + + ) + } + ; }; -function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueChangeHandler, initiallyOpenMultiValuePvs}: Props): JSX.Element + +function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueChangeHandler, initiallyOpenMultiValuePvs, queryScreenUsage}: Props): JSX.Element { const [, forceUpdate] = useReducer((x) => x + 1, 0); + const [allowVariables, setAllowVariables] = useState(queryScreenUsage == "reportSetup"); if (!operatorOption) { @@ -174,7 +225,7 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC case ValueMode.NONE: return null; case ValueMode.SINGLE: - return makeTextField(field, criteria, valueChangeHandler); + return makeTextField(field, criteria, valueChangeHandler, 0, undefined, undefined, allowVariables); case ValueMode.SINGLE_DATE: return ; case ValueMode.DOUBLE_DATE: @@ -183,7 +234,7 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC ; case ValueMode.SINGLE_DATE_TIME: - return ; + return ; case ValueMode.DOUBLE_DATE_TIME: return @@ -192,10 +243,10 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC case ValueMode.DOUBLE: return - {makeTextField(field, criteria, valueChangeHandler, 0, "From", "from-")} + {makeTextField(field, criteria, valueChangeHandler, 0, "From", "from-", allowVariables)} - {makeTextField(field, criteria, valueChangeHandler, 1, "To", "to-")} + {makeTextField(field, criteria, valueChangeHandler, 1, "To", "to-", allowVariables)} ; case ValueMode.MULTI: @@ -276,4 +327,4 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC return (
); } -export default FilterCriteriaRowValues; \ No newline at end of file +export default FilterCriteriaRowValues; diff --git a/src/qqq/components/query/QuickFilter.tsx b/src/qqq/components/query/QuickFilter.tsx index 3a3362b..9de8049 100644 --- a/src/qqq/components/query/QuickFilter.tsx +++ b/src/qqq/components/query/QuickFilter.tsx @@ -30,14 +30,15 @@ import Box from "@mui/material/Box"; import Button from "@mui/material/Button"; import Menu from "@mui/material/Menu"; import TextField from "@mui/material/TextField"; -import React, {SyntheticEvent, useContext, useReducer, useState} from "react"; import QContext from "QContext"; import {QFilterCriteriaWithId} from "qqq/components/query/CustomFilterPanel"; import {getDefaultCriteriaValue, getOperatorOptions, getValueModeRequiredCount, OperatorOption, validateCriteria} from "qqq/components/query/FilterCriteriaRow"; import FilterCriteriaRowValues from "qqq/components/query/FilterCriteriaRowValues"; 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"; export type CriteriaParamType = QFilterCriteriaWithId | null | "tooComplex"; @@ -50,6 +51,7 @@ interface QuickFilterProps updateCriteria: (newCriteria: QFilterCriteria, needDebounce: boolean, doRemoveCriteria: boolean) => void; defaultOperator?: QCriteriaOperator; handleRemoveQuickFilterField?: (fieldName: string) => void; + queryScreenUsage?: QueryScreenUsage; } QuickFilter.defaultProps = @@ -71,7 +73,7 @@ export const quickFilterButtonStyles = { minHeight: "auto", padding: "0.375rem 0.625rem", whiteSpace: "nowrap", marginBottom: "0.5rem" -} +}; /******************************************************************************* ** Test if a CriteriaParamType represents an actual query criteria - or, if it's @@ -89,11 +91,11 @@ const criteriaParamIsCriteria = (param: CriteriaParamType): boolean => *******************************************************************************/ const doesOperatorOptionEqualCriteria = (operatorOption: OperatorOption, criteria: QFilterCriteriaWithId): boolean => { - if(operatorOption.value == criteria.operator) + if (operatorOption.value == criteria.operator) { - if(operatorOption.implicitValues) + if (operatorOption.implicitValues) { - if(JSON.stringify(operatorOption.implicitValues) == JSON.stringify(criteria.values)) + if (JSON.stringify(operatorOption.implicitValues) == JSON.stringify(criteria.values)) { return (true); } @@ -107,7 +109,7 @@ const doesOperatorOptionEqualCriteria = (operatorOption: OperatorOption, criteri } return (false); -} +}; /******************************************************************************* @@ -117,29 +119,29 @@ const doesOperatorOptionEqualCriteria = (operatorOption: OperatorOption, criteri *******************************************************************************/ const getOperatorSelectedValue = (operatorOptions: OperatorOption[], criteria: QFilterCriteriaWithId, defaultOperator: QCriteriaOperator): OperatorOption => { - if(criteria) + if (criteria) { const filteredOptions = operatorOptions.filter(o => doesOperatorOptionEqualCriteria(o, criteria)); - if(filteredOptions.length > 0) + if (filteredOptions.length > 0) { return (filteredOptions[0]); } } const filteredOptions = operatorOptions.filter(o => o.value == defaultOperator); - if(filteredOptions.length > 0) + if (filteredOptions.length > 0) { return (filteredOptions[0]); } return (null); -} +}; /******************************************************************************* ** Component to render a QuickFilter - that is - a button, with a Menu under it, ** with Operator and Value controls. *******************************************************************************/ -export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData, criteriaParam, updateCriteria, defaultOperator, handleRemoveQuickFilterField}: QuickFilterProps): JSX.Element +export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData, criteriaParam, updateCriteria, defaultOperator, handleRemoveQuickFilterField, queryScreenUsage}: QuickFilterProps): JSX.Element { const operatorOptions = fieldMetaData ? getOperatorOptions(tableMetaData, fullFieldName) : []; const [_, tableForField] = TableUtils.getFieldAndTable(tableMetaData, fullFieldName); @@ -190,7 +192,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData ////////////////////////////////////////////////////////////////////////////////////////////////////////////// if (criteriaParamIsCriteria(criteriaParam) && JSON.stringify(criteriaParam) !== JSON.stringify(criteria)) { - if(isOpen) + if (isOpen) { //////////////////////////////////////////////////////////////////////////////// // this was firing too-often for case where: there was a criteria originally // @@ -217,12 +219,12 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData *******************************************************************************/ const criteriaNeedsReset = (): boolean => { - if(criteria != null && criteriaParam == null) + if (criteria != null && criteriaParam == null) { const defaultOperatorOption = operatorOptions.filter(o => o.value == defaultOperator)[0]; - if(criteria.operator !== defaultOperatorOption?.value || JSON.stringify(criteria.values) !== JSON.stringify(getDefaultCriteriaValue())) + if (criteria.operator !== defaultOperatorOption?.value || JSON.stringify(criteria.values) !== JSON.stringify(getDefaultCriteriaValue())) { - if(isOpen) + if (isOpen) { ////////////////////////////////////////////////////////////////////////////////// // this was firing too-often for case where: there was no criteria originally, // @@ -237,7 +239,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData } return (false); - } + }; /******************************************************************************* ** Construct a new criteria object - resetting the values tied to the operator @@ -251,8 +253,8 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData setOperatorSelectedValue(operatorOption); setOperatorInputValue(operatorOption?.label); setCriteria(criteria); - return(criteria); - } + return (criteria); + }; /******************************************************************************* ** event handler to open the menu in response to the button being clicked. @@ -266,7 +268,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData { const element = document.getElementById("value-" + criteria.id); element?.focus(); - }) + }); }; /******************************************************************************* @@ -304,15 +306,15 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData // we've seen cases where switching operators can sometimes put a null in as the first value... // // that just causes a bad time (e.g., null pointers in Autocomplete), so, get rid of that. // ////////////////////////////////////////////////////////////////////////////////////////////////// - if(criteria.values && criteria.values.length == 1 && criteria.values[0] == null) + if (criteria.values && criteria.values.length == 1 && criteria.values[0] == null) { criteria.values = []; } - if(newValue.valueMode && !newValue.implicitValues) + if (newValue.valueMode && !newValue.implicitValues) { const requiredValueCount = getValueModeRequiredCount(newValue.valueMode); - if(requiredValueCount != null && criteria.values.length > requiredValueCount) + if (requiredValueCount != null && criteria.values.length > requiredValueCount) { criteria.values.splice(requiredValueCount); } @@ -345,6 +347,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData // @ts-ignore const value = newValue !== undefined ? newValue : event ? event.target.value : null; + console.log("IN HERE"); if (!criteria.values) { criteria.values = []; @@ -376,13 +379,13 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData *******************************************************************************/ const resetCriteria = (e: React.MouseEvent) => { - if(criteriaIsValid) + if (criteriaIsValid) { e.stopPropagation(); const newCriteria = makeNewCriteria(); updateCriteria(newCriteria, false, true); } - } + }; /******************************************************************************* ** event handler for clicking the (x) icon that turns off this quick filter field. @@ -390,17 +393,17 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData *******************************************************************************/ const handleTurningOffQuickFilterField = () => { - closeMenu() - if(handleRemoveQuickFilterField) + closeMenu(); + if (handleRemoveQuickFilterField) { handleRemoveQuickFilterField(criteria?.fieldName); } - } + }; //////////////////////////////////////////////////////////////////////////////////// // if no field was input (e.g., record-query is still loading), return null early // //////////////////////////////////////////////////////////////////////////////////// - if(!fieldMetaData) + if (!fieldMetaData) { return (null); } @@ -410,10 +413,10 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData // from the last selected one, then set the state vars that control that autocomplete // ////////////////////////////////////////////////////////////////////////////////////////// const maybeNewOperatorSelectedValue = getOperatorSelectedValue(operatorOptions, criteria, defaultOperator); - if(JSON.stringify(maybeNewOperatorSelectedValue) !== JSON.stringify(operatorSelectedValue)) + if (JSON.stringify(maybeNewOperatorSelectedValue) !== JSON.stringify(operatorSelectedValue)) { - setOperatorSelectedValue(maybeNewOperatorSelectedValue) - setOperatorInputValue(maybeNewOperatorSelectedValue?.label) + setOperatorSelectedValue(maybeNewOperatorSelectedValue); + setOperatorInputValue(maybeNewOperatorSelectedValue?.label); } ///////////////////////////////////////////////////////////////////////////////////// @@ -431,7 +434,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData const tooltipEnterDelay = 500; let buttonAdditionalStyles: any = {}; - let buttonContent = {tableForField?.name != tableMetaData.name ? `${tableForField.label}: ` : ""}{fieldMetaData.label} + let buttonContent = {tableForField?.name != tableMetaData.name ? `${tableForField.label}: ` : ""}{fieldMetaData.label}; let buttonClassName = "filterNotActive"; if (criteriaIsValid) { @@ -446,9 +449,9 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData // don't show the Equals or In operators // /////////////////////////////////////////// let operatorString = (<>{operatorSelectedValue.label} ); - if(operatorSelectedValue.value == QCriteriaOperator.EQUALS || operatorSelectedValue.value == QCriteriaOperator.IN) + if (operatorSelectedValue.value == QCriteriaOperator.EQUALS || operatorSelectedValue.value == QCriteriaOperator.IN) { - operatorString = (<>) + operatorString = (<>); } buttonContent = (<>{buttonContent}: {operatorString}{valuesString}); @@ -491,7 +494,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData const xClicked = (e: React.MouseEvent) => { e.stopPropagation(); - if(criteriaIsValid) + if (criteriaIsValid) { resetCriteria(e); } @@ -499,12 +502,12 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData { handleTurningOffQuickFilterField(); } - } + }; ////////////////////////////// // return the button & menu // ////////////////////////////// - const widthAndMaxWidth = fieldMetaData?.type == QFieldType.DATE_TIME ? 275 : 250 + const widthAndMaxWidth = (fieldMetaData?.type == QFieldType.DATE_TIME) ? 295 : 250; return ( <> {button} @@ -541,6 +544,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
{ onClickCallback(); - } + }; return ( @@ -235,7 +233,6 @@ export function HeaderToggleComponent({label, getValue, onClickCallback, disable } - /******************************************************************************* ** *******************************************************************************/ @@ -697,7 +694,7 @@ function Widget(props: React.PropsWithChildren): JSX.Element ); let sublabelElement = ( - + {props.widgetData?.sublabel} @@ -784,7 +781,7 @@ function Widget(props: React.PropsWithChildren): JSX.Element } {localLabelAdditionalElementsLeft} - + { hasPermission && props.widgetData?.sublabel && (sublabelElement) } diff --git a/src/qqq/components/widgets/misc/PivotTableSetupWidget.tsx b/src/qqq/components/widgets/misc/PivotTableSetupWidget.tsx index 7b243e4..9c22a98 100644 --- a/src/qqq/components/widgets/misc/PivotTableSetupWidget.tsx +++ b/src/qqq/components/widgets/misc/PivotTableSetupWidget.tsx @@ -280,7 +280,7 @@ export default function PivotTableSetupWidget({isEditable, widgetMetaData, recor } modalPivotTableDefinition[rowsOrColumns].push(new PivotTableGroupBy()); - validateForm() + validateForm(); forceUpdate(); } @@ -292,7 +292,7 @@ export default function PivotTableSetupWidget({isEditable, widgetMetaData, recor { updateUsedGroupByFieldNames(modalPivotTableDefinition); updateUsedValueFieldNames(modalPivotTableDefinition); - validateForm() + validateForm(); forceUpdate(); } @@ -308,7 +308,7 @@ export default function PivotTableSetupWidget({isEditable, widgetMetaData, recor } modalPivotTableDefinition.values.push(new PivotTableValue()); - validateForm() + validateForm(); forceUpdate(); } @@ -319,7 +319,7 @@ export default function PivotTableSetupWidget({isEditable, widgetMetaData, recor function removeValue(index: number) { modalPivotTableDefinition.values.splice(index, 1); - validateForm() + validateForm(); forceUpdate(); } @@ -503,7 +503,7 @@ export default function PivotTableSetupWidget({isEditable, widgetMetaData, recor const labelAdditionalElementsRight: JSX.Element[] = []; if (isEditable) { - labelAdditionalElementsRight.push( enabled} onClickCallback={toggleEnabled} />); + labelAdditionalElementsRight.push( enabled} onClickCallback={toggleEnabled} />); } @@ -659,7 +659,7 @@ export default function PivotTableSetupWidget({isEditable, widgetMetaData, recor // if this isn't a call from the on-submit handler, and we haven't previously attempted a submit, then return w/o setting any alerts // // this is like a version of considering "touched"... // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if(!submitting && !attemptedSubmit) + if (!submitting && !attemptedSubmit) { return; } @@ -703,7 +703,7 @@ export default function PivotTableSetupWidget({isEditable, widgetMetaData, recor // now they've fixed 'em - so go back to a 'clean' state - so if they add more // // boxes, they won't immediately show errors, until a re-submit // //////////////////////////////////////////////////////////////////////////////////// - if(attemptedSubmit) + if (attemptedSubmit) { setAttemptedSubmit(false); } diff --git a/src/qqq/components/widgets/misc/ReportSetupWidget.tsx b/src/qqq/components/widgets/misc/ReportSetupWidget.tsx index 3466088..05ebfa2 100644 --- a/src/qqq/components/widgets/misc/ReportSetupWidget.tsx +++ b/src/qqq/components/widgets/misc/ReportSetupWidget.tsx @@ -46,8 +46,8 @@ interface ReportSetupWidgetProps { isEditable: boolean; widgetMetaData: QWidgetMetaData; - recordValues: {[name: string]: any}; - onSaveCallback?: (values: {[name: string]: any}) => void; + recordValues: { [name: string]: any }; + onSaveCallback?: (values: { [name: string]: any }) => void; } ReportSetupWidget.defaultProps = { @@ -103,14 +103,14 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal ///////////////////////////// let queryFilter = recordValues["queryFilterJson"] && JSON.parse(recordValues["queryFilterJson"]) as QQueryFilter; let usingDefaultEmptyFilter = false; - if(!queryFilter) + if (!queryFilter) { queryFilter = new QQueryFilter(); usingDefaultEmptyFilter = true; } let columns: QQueryColumns = null; - if(recordValues["columnsJson"]) + if (recordValues["columnsJson"]) { columns = QQueryColumns.buildFromJSON(recordValues["columnsJson"]); } @@ -124,12 +124,12 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal { (async () => { - const tableMetaData = await qController.loadTableMetaData(recordValues["tableName"]) + const tableMetaData = await qController.loadTableMetaData(recordValues["tableName"]); setTableMetaData(tableMetaData); const queryFilterForFrontend = Object.assign({}, queryFilter); - await FilterUtils.cleanupValuesInFilerFromQueryString(qController, tableMetaData, queryFilterForFrontend) - setFrontendQueryFilter(queryFilterForFrontend) + await FilterUtils.cleanupValuesInFilerFromQueryString(qController, tableMetaData, queryFilterForFrontend); + setFrontendQueryFilter(queryFilterForFrontend); })(); } }, [recordValues]); @@ -140,7 +140,7 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal *******************************************************************************/ function openEditor() { - if(recordValues["tableName"]) + if (recordValues["tableName"]) { setModalOpen(true); } @@ -152,7 +152,7 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal *******************************************************************************/ function saveClicked() { - if(!onSaveCallback) + if (!onSaveCallback) { console.log("onSaveCallback was not defined"); return; @@ -181,7 +181,7 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal *******************************************************************************/ function closeEditor(event?: {}, reason?: "backdropClick" | "escapeKeyDown") { - if(reason == "backdropClick" || reason == "escapeKeyDown") + if (reason == "backdropClick" || reason == "escapeKeyDown") { return; } @@ -195,9 +195,9 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal *******************************************************************************/ function renderColumn(column: Column): JSX.Element { - const [field, table] = FilterUtils.getField(tableMetaData, column.name) + const [field, table] = FilterUtils.getField(tableMetaData, column.name); - if(!column || !column.isVisible || column.name == "__check__" || !field) + if (!column || !column.isVisible || column.name == "__check__" || !field) { return (); } @@ -215,9 +215,9 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal *******************************************************************************/ function mayShowQueryPreview(): boolean { - if(tableMetaData) + if (tableMetaData) { - if(frontendQueryFilter?.criteria?.length > 0 || frontendQueryFilter?.subFilters?.length > 0) + if (frontendQueryFilter?.criteria?.length > 0 || frontendQueryFilter?.subFilters?.length > 0) { return (true); } @@ -231,11 +231,11 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal *******************************************************************************/ function mayShowColumnsPreview(): boolean { - if(tableMetaData) + if (tableMetaData) { - for(let i = 0; i) + labelAdditionalElementsRight.push(); } @@ -316,7 +316,7 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal { mayShowColumnsPreview() && - columns.columns.map((column, i) => {renderColumn(column)}) + columns.columns.map((column, i) => {renderColumn(column)}) } { !mayShowColumnsPreview() && diff --git a/src/qqq/pages/records/query/RecordQuery.tsx b/src/qqq/pages/records/query/RecordQuery.tsx index 0b3fd57..25609ec 100644 --- a/src/qqq/pages/records/query/RecordQuery.tsx +++ b/src/qqq/pages/records/query/RecordQuery.tsx @@ -109,7 +109,7 @@ const qController = Client.getInstance(); *******************************************************************************/ const getLoadingScreen = (isModal: boolean) => { - if(isModal) + if (isModal) { return ( ); } @@ -151,7 +151,7 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init *******************************************************************************/ function localStorageSet(key: string, value: string) { - if(mayWriteLocalStorage) + if (mayWriteLocalStorage) { localStorage.setItem(key, value); } @@ -163,7 +163,7 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init *******************************************************************************/ function localStorageRemove(key: string) { - if(mayWriteLocalStorage) + if (mayWriteLocalStorage) { localStorage.removeItem(key); } @@ -176,7 +176,7 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init { return view; } - } + }; }); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -256,7 +256,7 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init defaultView.mode = defaultMode; } - if(firstRender) + if (firstRender) { ///////////////////////////////////////////////////////////////////////// // allow a caller to send in an initial filter & set of columns. // @@ -408,7 +408,7 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init ////////////////////////////////////////////////////////////////// // we use our own header - so clear out the context page header // ////////////////////////////////////////////////////////////////// - if(!isModal) + if (!isModal) { setPageHeader(null); } @@ -486,7 +486,6 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init }; - /******************************************************************************* ** *******************************************************************************/ @@ -711,7 +710,7 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init { if (localStorage.getItem(currentSavedViewLocalStorageKey)) { - if(usage == "queryScreen") + if (usage == "queryScreen") { currentSavedViewId = Number.parseInt(localStorage.getItem(currentSavedViewLocalStorageKey)); navigate(`${metaData.getTablePathByName(tableName)}/savedView/${currentSavedViewId}`); @@ -750,13 +749,13 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init const viewForLocalStorage: RecordQueryView = JSON.parse(viewAsJSON); if (viewForLocalStorage?.queryFilter?.criteria?.length > 0) { - FilterUtils.stripAwayIncompleteCriteria(viewForLocalStorage.queryFilter) + FilterUtils.stripAwayIncompleteCriteria(viewForLocalStorage.queryFilter); } localStorageSet(viewLocalStorageKey, JSON.stringify(viewForLocalStorage)); } - catch(e) + catch (e) { - console.log("Error storing view in local storage: " + e) + console.log("Error storing view in local storage: " + e); } }; @@ -939,7 +938,7 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init console.log(`Issuing query: ${thisQueryId}`); if (tableMetaData.capabilities.has(Capability.TABLE_COUNT)) { - if(clearOutCount) + if (clearOutCount) { setTotalRecords(null); setDistinctRecords(null); @@ -1437,7 +1436,6 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init return (selectedIds.length); }; - /******************************************************************************* ** get a query-string to put on the url to indicate what records are going into ** a process. @@ -2527,7 +2525,7 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init { const currentSavedViewId = Number.parseInt(localStorage.getItem(currentSavedViewLocalStorageKey)); console.log(`returning to previously active saved view ${currentSavedViewId}`); - if(usage == "queryScreen") + if (usage == "queryScreen") { navigate(`${metaData.getTablePathByName(tableName)}/savedView/${currentSavedViewId}`); } @@ -2770,7 +2768,7 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init spaceAboveGrid += 60; } - if(isModal) + if (isModal) { spaceAboveGrid += 130; } @@ -2976,15 +2974,15 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init ); - if(isModal) + if (isModal) { return body; } return ( {body} - ) -}) + ); +}); RecordQuery.defaultProps = { diff --git a/src/qqq/utils/qqq/FilterUtils.tsx b/src/qqq/utils/qqq/FilterUtils.tsx index eaa8940..b2bbfd7 100644 --- a/src/qqq/utils/qqq/FilterUtils.tsx +++ b/src/qqq/utils/qqq/FilterUtils.tsx @@ -23,6 +23,7 @@ import {QController} from "@kingsrook/qqq-frontend-core/lib/controllers/QControl import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData"; import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType"; import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData"; +import {FilterVariableExpression} from "@kingsrook/qqq-frontend-core/lib/model/query/FilterVariableExpression"; import {NowExpression} from "@kingsrook/qqq-frontend-core/lib/model/query/NowExpression"; import {NowWithOffsetExpression} from "@kingsrook/qqq-frontend-core/lib/model/query/NowWithOffsetExpression"; import {QCriteriaOperator} from "@kingsrook/qqq-frontend-core/lib/model/query/QCriteriaOperator"; @@ -365,7 +366,12 @@ class FilterUtils for (let i = 0; i < maxLoops; i++) { const value = criteria.values[i]; - if (value.type == "NowWithOffset") + if (value.type == "FilterVariableExpression") + { + const expression = new FilterVariableExpression(value); + labels.push(expression.toString()); + } + else if (value.type == "NowWithOffset") { const expression = new NowWithOffsetExpression(value); labels.push(expression.toString()); @@ -657,7 +663,7 @@ class FilterUtils filterForBackend.subFilters = subFilters; - if(pageNumber !== undefined && rowsPerPage !== undefined) + if (pageNumber !== undefined && rowsPerPage !== undefined) { filterForBackend.skip = pageNumber * rowsPerPage; filterForBackend.limit = rowsPerPage;