Merged feature/CE-1179-add-user-defined-inputs-to into feature/CE-1068-add-basic-functionality-of

This commit is contained in:
2024-04-30 10:29:57 -05:00
13 changed files with 294 additions and 159 deletions

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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 <Box display="flex" alignItems="flex-end">
<Box>
<Tooltip title={`Use a variable as the value for the ${field.name} field`} placement="bottom">
<Icon fontSize="small" color="info" sx={{mx: 0.25, cursor: "pointer", position: "relative", top: "2px"}} onClick={handleVariableButtonOnClick}>functions</Icon>
</Tooltip>
</Box>
</Box>;
}

View File

@ -114,7 +114,7 @@ export function getCurrentSortIndicator(queryFilter: QQueryFilter, tableMetaData
*******************************************************************************/ *******************************************************************************/
const BasicAndAdvancedQueryControls = forwardRef((props: BasicAndAdvancedQueryControlsProps, ref) => 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 // // state variables //
@ -682,6 +682,7 @@ const BasicAndAdvancedQueryControls = forwardRef((props: BasicAndAdvancedQueryCo
criteriaParam={getQuickCriteriaParam(fieldName)} criteriaParam={getQuickCriteriaParam(fieldName)}
fieldMetaData={field} fieldMetaData={field}
defaultOperator={defaultOperator} defaultOperator={defaultOperator}
queryScreenUsage={queryScreenUsage}
handleRemoveQuickFilterField={null} />); handleRemoveQuickFilterField={null} />);
}) })
} }
@ -701,6 +702,7 @@ const BasicAndAdvancedQueryControls = forwardRef((props: BasicAndAdvancedQueryCo
criteriaParam={getQuickCriteriaParam(fieldName)} criteriaParam={getQuickCriteriaParam(fieldName)}
fieldMetaData={field} fieldMetaData={field}
defaultOperator={defaultOperator} defaultOperator={defaultOperator}
queryScreenUsage={queryScreenUsage}
handleRemoveQuickFilterField={handleRemoveQuickFilterField} />); handleRemoveQuickFilterField={handleRemoveQuickFilterField} />);
}) })
} }

View File

@ -21,6 +21,7 @@
import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData"; import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType"; 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 {NowExpression} from "@kingsrook/qqq-frontend-core/lib/model/query/NowExpression";
import {NowWithOffsetExpression, NowWithOffsetOperator, NowWithOffsetUnit} from "@kingsrook/qqq-frontend-core/lib/model/query/NowWithOffsetExpression"; 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"; 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 {styled} from "@mui/material/styles";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import Tooltip, {tooltipClasses, TooltipProps} from "@mui/material/Tooltip"; import Tooltip, {tooltipClasses, TooltipProps} from "@mui/material/Tooltip";
import React, {SyntheticEvent, useEffect, useReducer, useState} from "react";
import AdvancedDateTimeFilterValues from "qqq/components/query/AdvancedDateTimeFilterValues"; import AdvancedDateTimeFilterValues from "qqq/components/query/AdvancedDateTimeFilterValues";
import {QFilterCriteriaWithId} from "qqq/components/query/CustomFilterPanel"; import {QFilterCriteriaWithId} from "qqq/components/query/CustomFilterPanel";
import {EvaluatedExpression} from "qqq/components/query/EvaluatedExpression"; import {EvaluatedExpression} from "qqq/components/query/EvaluatedExpression";
import {makeTextField} from "qqq/components/query/FilterCriteriaRowValues"; 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 interface CriteriaDateFieldProps
@ -52,6 +53,7 @@ interface CriteriaDateFieldProps
field: QFieldMetaData; field: QFieldMetaData;
criteria: QFilterCriteriaWithId; criteria: QFilterCriteriaWithId;
valueChangeHandler: (event: React.ChangeEvent | SyntheticEvent, valueIndex?: number | "all", newValue?: any) => void; valueChangeHandler: (event: React.ChangeEvent | SyntheticEvent, valueIndex?: number | "all", newValue?: any) => void;
allowVariables?: boolean;
} }
CriteriaDateField.defaultProps = { CriteriaDateField.defaultProps = {
@ -60,19 +62,30 @@ CriteriaDateField.defaultProps = {
idPrefix: "value-" idPrefix: "value-"
}; };
export default function CriteriaDateField({valueIndex, label, idPrefix, field, criteria, valueChangeHandler}: CriteriaDateFieldProps): JSX.Element export const NoWrapTooltip = styled(({className, children, ...props}: TooltipProps) => (
<Tooltip {...props} classes={{popper: className}}>{children}</Tooltip>
))({
[`& .${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 [relativeDateTimeMenuAnchorElement, setRelativeDateTimeMenuAnchorElement] = useState(null);
const [forceAdvancedDateTimeDialogOpen, setForceAdvancedDateTimeDialogOpen] = useState(false) const [forceAdvancedDateTimeDialogOpen, setForceAdvancedDateTimeDialogOpen] = useState(false);
const [, forceUpdate] = useReducer((x) => x + 1, 0); const [, forceUpdate] = useReducer((x) => x + 1, 0);
const openRelativeDateTimeMenu = (event: React.MouseEvent<HTMLElement>) => const openRelativeDateTimeMenu = (event: React.MouseEvent<HTMLElement>) =>
{ {
setRelativeDateTimeOpen(true);
setRelativeDateTimeMenuAnchorElement(event.currentTarget); setRelativeDateTimeMenuAnchorElement(event.currentTarget);
}; };
const closeRelativeDateTimeMenu = () => const closeRelativeDateTimeMenu = () =>
{ {
setRelativeDateTimeOpen(false);
setRelativeDateTimeMenuAnchorElement(null); setRelativeDateTimeMenuAnchorElement(null);
}; };
@ -137,14 +150,6 @@ export default function CriteriaDateField({valueIndex, label, idPrefix, field, c
const isExpression = criteria.values && criteria.values[valueIndex] && criteria.values[valueIndex].type; const isExpression = criteria.values && criteria.values[valueIndex] && criteria.values[valueIndex].type;
const currentExpression = isExpression ? criteria.values[valueIndex] : null; const currentExpression = isExpression ? criteria.values[valueIndex] : null;
const NoWrapTooltip = styled(({className, children, ...props}: TooltipProps) => (
<Tooltip {...props} classes={{popper: className}}>{children}</Tooltip>
))({
[`& .${tooltipClasses.tooltip}`]: {
whiteSpace: "nowrap"
},
});
const tooltipMenuItemFromExpression = (valueIndex: number, tooltipPlacement: "left" | "right", expression: Expression) => const tooltipMenuItemFromExpression = (valueIndex: number, tooltipPlacement: "left" | "right", expression: Expression) =>
{ {
let startOfPrefix = ""; let startOfPrefix = "";
@ -194,14 +199,14 @@ export default function CriteriaDateField({valueIndex, label, idPrefix, field, c
return <Box display="flex" alignItems="flex-end"> return <Box display="flex" alignItems="flex-end">
{ {
isExpression ? makeDateTimeExpressionTextField(criteria.values[valueIndex], valueIndex, label, idPrefix) isExpression ? makeDateTimeExpressionTextField(criteria.values[valueIndex], valueIndex, label, idPrefix)
: makeTextField(field, criteria, valueChangeHandler, valueIndex, label, idPrefix) : makeTextField(field, criteria, valueChangeHandler, valueIndex, label, idPrefix, allowVariables)
} }
<Box> <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>
<Menu <Menu
open={relativeDateTimeMenuAnchorElement} open={relativeDateTimeOpen}
anchorEl={relativeDateTimeMenuAnchorElement} anchorEl={relativeDateTimeMenuAnchorElement}
transformOrigin={{horizontal: "left", vertical: "top"}} transformOrigin={{horizontal: "left", vertical: "top"}}
onClose={closeRelativeDateTimeMenu} onClose={closeRelativeDateTimeMenu}

View File

@ -21,7 +21,9 @@
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance"; import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData"; import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
import {Box, FormControlLabel, FormGroup} from "@mui/material";
import {FormControlLabel, FormGroup} from "@mui/material";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import Icon from "@mui/material/Icon"; import Icon from "@mui/material/Icon";
import IconButton from "@mui/material/IconButton"; import IconButton from "@mui/material/IconButton";
@ -56,7 +58,7 @@ export const CustomColumnsPanel = forwardRef<any, GridColumnsPanelProps>(
const someRef = createRef(); const someRef = createRef();
const textRef = useRef(null); const textRef = useRef(null);
const [didInitialFocus, setDidInitialFocus] = useState(false) const [didInitialFocus, setDidInitialFocus] = useState(false);
const [openGroups, setOpenGroups] = useState(props.initialOpenedGroups || {}); const [openGroups, setOpenGroups] = useState(props.initialOpenedGroups || {});
const openGroupsBecauseOfFilter = {} as { [name: string]: boolean }; const openGroupsBecauseOfFilter = {} as { [name: string]: boolean };
@ -193,7 +195,7 @@ export const CustomColumnsPanel = forwardRef<any, GridColumnsPanelProps>(
sortedColumns.sort((a, b): number => sortedColumns.sort((a, b): number =>
{ {
return a.headerName.localeCompare(b.headerName); return a.headerName.localeCompare(b.headerName);
}) });
for (let i = 0; i < sortedColumns.length; i++) for (let i = 0; i < sortedColumns.length; i++)
{ {
@ -361,7 +363,7 @@ export const CustomColumnsPanel = forwardRef<any, GridColumnsPanelProps>(
const changeFilterText = (newValue: string) => const changeFilterText = (newValue: string) =>
{ {
setFilterText(newValue); setFilterText(newValue);
props.filterTextChanger(newValue) props.filterTextChanger(newValue);
}; };
const filterTextChanged = (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => const filterTextChanged = (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) =>

View File

@ -21,9 +21,9 @@
import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData"; import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType"; 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 {Expression} from "qqq/components/query/CriteriaDateField";
import ValueUtils from "qqq/utils/qqq/ValueUtils"; 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. ** 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 DAY_MS = 24 * 60 * 60 * 1000;
const evaluateExpression = (time: Date, field: QFieldMetaData, expression: Expression): string => const evaluateExpression = (time: Date, field: QFieldMetaData, expression: Expression): string =>
{ {
if (expression.type == "FilterVariableExpression")
{
return (expression.toString());
}
let rs: Date = null; let rs: Date = null;
if (expression.type == "NowWithOffset") if (expression.type == "NowWithOffset")
{ {

View File

@ -72,7 +72,7 @@ export const getValueModeRequiredCount = (valueMode: ValueMode): number =>
case ValueMode.PVS_MULTI: case ValueMode.PVS_MULTI:
return (null); return (null);
} }
} };
export interface OperatorOption export interface OperatorOption
{ {
@ -183,7 +183,7 @@ export const getOperatorOptions = (tableMetaData: QTableMetaData, fieldName: str
} }
return (operatorOptions); return (operatorOptions);
} };
interface FilterCriteriaRowProps interface FilterCriteriaRowProps
@ -200,8 +200,7 @@ interface FilterCriteriaRowProps
} }
FilterCriteriaRow.defaultProps = FilterCriteriaRow.defaultProps =
{ {};
};
export function validateCriteria(criteria: QFilterCriteria, operatorSelectedValue?: OperatorOption): { criteriaIsValid: boolean, criteriaStatusTooltip: string } export function validateCriteria(criteria: QFilterCriteria, operatorSelectedValue?: OperatorOption): { criteriaIsValid: boolean, criteriaStatusTooltip: string }
{ {

View File

@ -23,19 +23,23 @@
import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData"; import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType"; import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType";
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData"; 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 Autocomplete from "@mui/material/Autocomplete";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Icon from "@mui/material/Icon"; import Icon from "@mui/material/Icon";
import IconButton from "@mui/material/IconButton"; import IconButton from "@mui/material/IconButton";
import InputAdornment from "@mui/material/InputAdornment/InputAdornment"; import InputAdornment from "@mui/material/InputAdornment/InputAdornment";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import React, {SyntheticEvent, useReducer} from "react";
import DynamicSelect from "qqq/components/forms/DynamicSelect"; 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 {QFilterCriteriaWithId} from "qqq/components/query/CustomFilterPanel";
import {EvaluatedExpression} from "qqq/components/query/EvaluatedExpression";
import FilterCriteriaPaster from "qqq/components/query/FilterCriteriaPaster"; import FilterCriteriaPaster from "qqq/components/query/FilterCriteriaPaster";
import {OperatorOption, ValueMode} from "qqq/components/query/FilterCriteriaRow"; import {OperatorOption, ValueMode} from "qqq/components/query/FilterCriteriaRow";
import {QueryScreenUsage} from "qqq/pages/records/query/RecordQuery";
import ValueUtils from "qqq/utils/qqq/ValueUtils"; import ValueUtils from "qqq/utils/qqq/ValueUtils";
import React, {SyntheticEvent, useReducer, useState} from "react";
interface Props interface Props
{ {
@ -44,7 +48,8 @@ interface Props
field: QFieldMetaData; field: QFieldMetaData;
table: QTableMetaData; table: QTableMetaData;
valueChangeHandler: (event: React.ChangeEvent | SyntheticEvent, valueIndex?: number | "all", newValue?: any) => void; valueChangeHandler: (event: React.ChangeEvent | SyntheticEvent, valueIndex?: number | "all", newValue?: any) => void;
initiallyOpenMultiValuePvs?: boolean initiallyOpenMultiValuePvs?: boolean;
queryScreenUsage?: QueryScreenUsage;
} }
FilterCriteriaRowValues.defaultProps = FilterCriteriaRowValues.defaultProps =
@ -72,8 +77,10 @@ export const getTypeForTextField = (field: QFieldMetaData): string =>
return (type); 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); let type = getTypeForTextField(field);
const inputLabelProps: any = {}; 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 ** Event handler for key-down events - specifically added here, to stop pressing
** 'tab' in a date or date-time from closing the quick-filter... ** 'tab' in a date or date-time from closing the quick-filter...
@ -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<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>;
};
const inputProps: any = {}; const inputProps: any = {};
inputProps.endAdornment = ( inputProps.endAdornment = (
<InputAdornment position="end"> <InputAdornment position="end">
@ -121,7 +157,12 @@ export const makeTextField = (field: QFieldMetaData, criteria: QFilterCriteriaWi
</InputAdornment> </InputAdornment>
); );
return <TextField return <Box sx={{margin: 0, padding: 0, display: "flex"}}>
{
isExpression ? (
makeFilterVariableTextField(criteria.values[valueIndex], valueIndex, label, idPrefix)
) : (
<TextField
id={`${idPrefix}${criteria.id}`} id={`${idPrefix}${criteria.id}`}
label={label} label={label}
variant="standard" variant="standard"
@ -134,12 +175,22 @@ export const makeTextField = (field: QFieldMetaData, criteria: QFilterCriteriaWi
InputProps={inputProps} InputProps={inputProps}
fullWidth fullWidth
autoFocus={true} autoFocus={true}
/>; />
)
}
{
allowVariables && (
<AssignFilterVariable field={field} valueChangeHandler={valueChangeHandler} valueIndex={valueIndex} />
)
}
</Box>;
}; };
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 [, forceUpdate] = useReducer((x) => x + 1, 0);
const [allowVariables, setAllowVariables] = useState(queryScreenUsage == "reportSetup");
if (!operatorOption) if (!operatorOption)
{ {
@ -174,7 +225,7 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
case ValueMode.NONE: case ValueMode.NONE:
return null; return null;
case ValueMode.SINGLE: case ValueMode.SINGLE:
return makeTextField(field, criteria, valueChangeHandler); return makeTextField(field, criteria, valueChangeHandler, 0, undefined, undefined, allowVariables);
case ValueMode.SINGLE_DATE: case ValueMode.SINGLE_DATE:
return <CriteriaDateField field={field} valueChangeHandler={valueChangeHandler} criteria={criteria} />; return <CriteriaDateField field={field} valueChangeHandler={valueChangeHandler} criteria={criteria} />;
case ValueMode.DOUBLE_DATE: case ValueMode.DOUBLE_DATE:
@ -183,7 +234,7 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
<CriteriaDateField field={field} valueChangeHandler={valueChangeHandler} criteria={criteria} valueIndex={1} label="To" idPrefix="to-" /> <CriteriaDateField field={field} valueChangeHandler={valueChangeHandler} criteria={criteria} valueIndex={1} label="To" idPrefix="to-" />
</Box>; </Box>;
case ValueMode.SINGLE_DATE_TIME: case ValueMode.SINGLE_DATE_TIME:
return <CriteriaDateField field={field} valueChangeHandler={valueChangeHandler} criteria={criteria} />; return <CriteriaDateField field={field} valueChangeHandler={valueChangeHandler} criteria={criteria} allowVariables={allowVariables} />;
case ValueMode.DOUBLE_DATE_TIME: case ValueMode.DOUBLE_DATE_TIME:
return <Box> return <Box>
<CriteriaDateField field={field} valueChangeHandler={valueChangeHandler} criteria={criteria} valueIndex={0} label="From" idPrefix="from-" /> <CriteriaDateField field={field} valueChangeHandler={valueChangeHandler} criteria={criteria} valueIndex={0} label="From" idPrefix="from-" />
@ -192,10 +243,10 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
case ValueMode.DOUBLE: case ValueMode.DOUBLE:
return <Box> return <Box>
<Box width="50%" display="inline-block"> <Box width="50%" display="inline-block">
{makeTextField(field, criteria, valueChangeHandler, 0, "From", "from-")} {makeTextField(field, criteria, valueChangeHandler, 0, "From", "from-", allowVariables)}
</Box> </Box>
<Box width="50%" display="inline-block"> <Box width="50%" display="inline-block">
{makeTextField(field, criteria, valueChangeHandler, 1, "To", "to-")} {makeTextField(field, criteria, valueChangeHandler, 1, "To", "to-", allowVariables)}
</Box> </Box>
</Box>; </Box>;
case ValueMode.MULTI: case ValueMode.MULTI:

View File

@ -30,14 +30,15 @@ import Box from "@mui/material/Box";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import Menu from "@mui/material/Menu"; import Menu from "@mui/material/Menu";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import React, {SyntheticEvent, useContext, useReducer, useState} from "react";
import QContext from "QContext"; import QContext from "QContext";
import {QFilterCriteriaWithId} from "qqq/components/query/CustomFilterPanel"; import {QFilterCriteriaWithId} from "qqq/components/query/CustomFilterPanel";
import {getDefaultCriteriaValue, getOperatorOptions, getValueModeRequiredCount, OperatorOption, validateCriteria} from "qqq/components/query/FilterCriteriaRow"; import {getDefaultCriteriaValue, getOperatorOptions, getValueModeRequiredCount, OperatorOption, validateCriteria} from "qqq/components/query/FilterCriteriaRow";
import FilterCriteriaRowValues from "qqq/components/query/FilterCriteriaRowValues"; import FilterCriteriaRowValues from "qqq/components/query/FilterCriteriaRowValues";
import XIcon from "qqq/components/query/XIcon"; import XIcon from "qqq/components/query/XIcon";
import {QueryScreenUsage} from "qqq/pages/records/query/RecordQuery";
import FilterUtils from "qqq/utils/qqq/FilterUtils"; import FilterUtils from "qqq/utils/qqq/FilterUtils";
import TableUtils from "qqq/utils/qqq/TableUtils"; import TableUtils from "qqq/utils/qqq/TableUtils";
import React, {SyntheticEvent, useContext, useReducer, useState} from "react";
export type CriteriaParamType = QFilterCriteriaWithId | null | "tooComplex"; export type CriteriaParamType = QFilterCriteriaWithId | null | "tooComplex";
@ -50,6 +51,7 @@ interface QuickFilterProps
updateCriteria: (newCriteria: QFilterCriteria, needDebounce: boolean, doRemoveCriteria: boolean) => void; updateCriteria: (newCriteria: QFilterCriteria, needDebounce: boolean, doRemoveCriteria: boolean) => void;
defaultOperator?: QCriteriaOperator; defaultOperator?: QCriteriaOperator;
handleRemoveQuickFilterField?: (fieldName: string) => void; handleRemoveQuickFilterField?: (fieldName: string) => void;
queryScreenUsage?: QueryScreenUsage;
} }
QuickFilter.defaultProps = QuickFilter.defaultProps =
@ -71,7 +73,7 @@ export const quickFilterButtonStyles = {
minHeight: "auto", minHeight: "auto",
padding: "0.375rem 0.625rem", whiteSpace: "nowrap", padding: "0.375rem 0.625rem", whiteSpace: "nowrap",
marginBottom: "0.5rem" marginBottom: "0.5rem"
} };
/******************************************************************************* /*******************************************************************************
** Test if a CriteriaParamType represents an actual query criteria - or, if it's ** Test if a CriteriaParamType represents an actual query criteria - or, if it's
@ -107,7 +109,7 @@ const doesOperatorOptionEqualCriteria = (operatorOption: OperatorOption, criteri
} }
return (false); return (false);
} };
/******************************************************************************* /*******************************************************************************
@ -133,13 +135,13 @@ const getOperatorSelectedValue = (operatorOptions: OperatorOption[], criteria: Q
} }
return (null); return (null);
} };
/******************************************************************************* /*******************************************************************************
** Component to render a QuickFilter - that is - a button, with a Menu under it, ** Component to render a QuickFilter - that is - a button, with a Menu under it,
** with Operator and Value controls. ** 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 operatorOptions = fieldMetaData ? getOperatorOptions(tableMetaData, fullFieldName) : [];
const [_, tableForField] = TableUtils.getFieldAndTable(tableMetaData, fullFieldName); const [_, tableForField] = TableUtils.getFieldAndTable(tableMetaData, fullFieldName);
@ -237,7 +239,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
} }
return (false); return (false);
} };
/******************************************************************************* /*******************************************************************************
** Construct a new criteria object - resetting the values tied to the operator ** Construct a new criteria object - resetting the values tied to the operator
@ -252,7 +254,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
setOperatorInputValue(operatorOption?.label); setOperatorInputValue(operatorOption?.label);
setCriteria(criteria); setCriteria(criteria);
return (criteria); return (criteria);
} };
/******************************************************************************* /*******************************************************************************
** event handler to open the menu in response to the button being clicked. ** 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); const element = document.getElementById("value-" + criteria.id);
element?.focus(); element?.focus();
}) });
}; };
/******************************************************************************* /*******************************************************************************
@ -345,6 +347,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
// @ts-ignore // @ts-ignore
const value = newValue !== undefined ? newValue : event ? event.target.value : null; const value = newValue !== undefined ? newValue : event ? event.target.value : null;
console.log("IN HERE");
if (!criteria.values) if (!criteria.values)
{ {
criteria.values = []; criteria.values = [];
@ -382,7 +385,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
const newCriteria = makeNewCriteria(); const newCriteria = makeNewCriteria();
updateCriteria(newCriteria, false, true); updateCriteria(newCriteria, false, true);
} }
} };
/******************************************************************************* /*******************************************************************************
** event handler for clicking the (x) icon that turns off this quick filter field. ** event handler for clicking the (x) icon that turns off this quick filter field.
@ -390,12 +393,12 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
*******************************************************************************/ *******************************************************************************/
const handleTurningOffQuickFilterField = () => const handleTurningOffQuickFilterField = () =>
{ {
closeMenu() closeMenu();
if (handleRemoveQuickFilterField) if (handleRemoveQuickFilterField)
{ {
handleRemoveQuickFilterField(criteria?.fieldName); handleRemoveQuickFilterField(criteria?.fieldName);
} }
} };
//////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////
// if no field was input (e.g., record-query is still loading), return null early // // if no field was input (e.g., record-query is still loading), return null early //
@ -412,8 +415,8 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
const maybeNewOperatorSelectedValue = getOperatorSelectedValue(operatorOptions, criteria, defaultOperator); const maybeNewOperatorSelectedValue = getOperatorSelectedValue(operatorOptions, criteria, defaultOperator);
if (JSON.stringify(maybeNewOperatorSelectedValue) !== JSON.stringify(operatorSelectedValue)) if (JSON.stringify(maybeNewOperatorSelectedValue) !== JSON.stringify(operatorSelectedValue))
{ {
setOperatorSelectedValue(maybeNewOperatorSelectedValue) setOperatorSelectedValue(maybeNewOperatorSelectedValue);
setOperatorInputValue(maybeNewOperatorSelectedValue?.label) setOperatorInputValue(maybeNewOperatorSelectedValue?.label);
} }
///////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////
@ -431,7 +434,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
const tooltipEnterDelay = 500; const tooltipEnterDelay = 500;
let buttonAdditionalStyles: any = {}; let buttonAdditionalStyles: any = {};
let buttonContent = <span>{tableForField?.name != tableMetaData.name ? `${tableForField.label}: ` : ""}{fieldMetaData.label}</span> let buttonContent = <span>{tableForField?.name != tableMetaData.name ? `${tableForField.label}: ` : ""}{fieldMetaData.label}</span>;
let buttonClassName = "filterNotActive"; let buttonClassName = "filterNotActive";
if (criteriaIsValid) if (criteriaIsValid)
{ {
@ -448,7 +451,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
let operatorString = (<>{operatorSelectedValue.label}&nbsp;</>); let operatorString = (<>{operatorSelectedValue.label}&nbsp;</>);
if (operatorSelectedValue.value == QCriteriaOperator.EQUALS || operatorSelectedValue.value == QCriteriaOperator.IN) if (operatorSelectedValue.value == QCriteriaOperator.EQUALS || operatorSelectedValue.value == QCriteriaOperator.IN)
{ {
operatorString = (<></>) operatorString = (<></>);
} }
buttonContent = (<><span style={{fontWeight: 700}}>{buttonContent}:</span>&nbsp;<span style={{fontWeight: 400}}>{operatorString}{valuesString}</span></>); buttonContent = (<><span style={{fontWeight: 700}}>{buttonContent}:</span>&nbsp;<span style={{fontWeight: 400}}>{operatorString}{valuesString}</span></>);
@ -499,12 +502,12 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
{ {
handleTurningOffQuickFilterField(); handleTurningOffQuickFilterField();
} }
} };
////////////////////////////// //////////////////////////////
// return the button & menu // // return the button & menu //
////////////////////////////// //////////////////////////////
const widthAndMaxWidth = fieldMetaData?.type == QFieldType.DATE_TIME ? 275 : 250 const widthAndMaxWidth = (fieldMetaData?.type == QFieldType.DATE_TIME) ? 295 : 250;
return ( return (
<> <>
{button} {button}
@ -541,6 +544,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
</Box> </Box>
<Box width={widthAndMaxWidth} maxWidth={widthAndMaxWidth} className="quickFilter filterValuesColumn"> <Box width={widthAndMaxWidth} maxWidth={widthAndMaxWidth} className="quickFilter filterValuesColumn">
<FilterCriteriaRowValues <FilterCriteriaRowValues
queryScreenUsage={queryScreenUsage}
operatorOption={operatorSelectedValue} operatorOption={operatorSelectedValue}
criteria={criteria} criteria={criteria}
field={fieldMetaData} field={fieldMetaData}

View File

@ -195,8 +195,6 @@ export function HeaderLinkButtonComponent({label, onClickCallback, disabled, dis
} }
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -219,7 +217,7 @@ export function HeaderToggleComponent({label, getValue, onClickCallback, disable
const onClick = () => const onClick = () =>
{ {
onClickCallback(); onClickCallback();
} };
return ( return (
<Box alignItems="baseline" mr="-0.75rem"> <Box alignItems="baseline" mr="-0.75rem">
@ -235,7 +233,6 @@ export function HeaderToggleComponent({label, getValue, onClickCallback, disable
} }
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -697,7 +694,7 @@ function Widget(props: React.PropsWithChildren<Props>): JSX.Element
); );
let sublabelElement = ( let sublabelElement = (
<Box height="20px"> <Box key="sublabel" height="20px">
<Typography sx={{position: "relative", top: "-18px"}} variant="caption"> <Typography sx={{position: "relative", top: "-18px"}} variant="caption">
{props.widgetData?.sublabel} {props.widgetData?.sublabel}
</Typography> </Typography>
@ -784,7 +781,7 @@ function Widget(props: React.PropsWithChildren<Props>): JSX.Element
} }
{localLabelAdditionalElementsLeft} {localLabelAdditionalElementsLeft}
</Box> </Box>
<Box display="flex"> <Box key="sublabelContainer" display="flex">
{ {
hasPermission && props.widgetData?.sublabel && (sublabelElement) hasPermission && props.widgetData?.sublabel && (sublabelElement)
} }

View File

@ -280,7 +280,7 @@ export default function PivotTableSetupWidget({isEditable, widgetMetaData, recor
} }
modalPivotTableDefinition[rowsOrColumns].push(new PivotTableGroupBy()); modalPivotTableDefinition[rowsOrColumns].push(new PivotTableGroupBy());
validateForm() validateForm();
forceUpdate(); forceUpdate();
} }
@ -292,7 +292,7 @@ export default function PivotTableSetupWidget({isEditable, widgetMetaData, recor
{ {
updateUsedGroupByFieldNames(modalPivotTableDefinition); updateUsedGroupByFieldNames(modalPivotTableDefinition);
updateUsedValueFieldNames(modalPivotTableDefinition); updateUsedValueFieldNames(modalPivotTableDefinition);
validateForm() validateForm();
forceUpdate(); forceUpdate();
} }
@ -308,7 +308,7 @@ export default function PivotTableSetupWidget({isEditable, widgetMetaData, recor
} }
modalPivotTableDefinition.values.push(new PivotTableValue()); modalPivotTableDefinition.values.push(new PivotTableValue());
validateForm() validateForm();
forceUpdate(); forceUpdate();
} }
@ -319,7 +319,7 @@ export default function PivotTableSetupWidget({isEditable, widgetMetaData, recor
function removeValue(index: number) function removeValue(index: number)
{ {
modalPivotTableDefinition.values.splice(index, 1); modalPivotTableDefinition.values.splice(index, 1);
validateForm() validateForm();
forceUpdate(); forceUpdate();
} }
@ -503,7 +503,7 @@ export default function PivotTableSetupWidget({isEditable, widgetMetaData, recor
const labelAdditionalElementsRight: JSX.Element[] = []; const labelAdditionalElementsRight: JSX.Element[] = [];
if (isEditable) if (isEditable)
{ {
labelAdditionalElementsRight.push(<HeaderToggleComponent disabled={editPopupDisabled} disabledTooltip={selectTableFirstTooltipTitle ?? selectColumnsFirstTooltipTitle} label="Use Pivot Table?" getValue={() => enabled} onClickCallback={toggleEnabled} />); labelAdditionalElementsRight.push(<HeaderToggleComponent key="pivotTableHeader" disabled={editPopupDisabled} disabledTooltip={selectTableFirstTooltipTitle ?? selectColumnsFirstTooltipTitle} label="Use Pivot Table?" getValue={() => enabled} onClickCallback={toggleEnabled} />);
} }

View File

@ -124,12 +124,12 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal
{ {
(async () => (async () =>
{ {
const tableMetaData = await qController.loadTableMetaData(recordValues["tableName"]) const tableMetaData = await qController.loadTableMetaData(recordValues["tableName"]);
setTableMetaData(tableMetaData); setTableMetaData(tableMetaData);
const queryFilterForFrontend = Object.assign({}, queryFilter); const queryFilterForFrontend = Object.assign({}, queryFilter);
await FilterUtils.cleanupValuesInFilerFromQueryString(qController, tableMetaData, queryFilterForFrontend) await FilterUtils.cleanupValuesInFilerFromQueryString(qController, tableMetaData, queryFilterForFrontend);
setFrontendQueryFilter(queryFilterForFrontend) setFrontendQueryFilter(queryFilterForFrontend);
})(); })();
} }
}, [recordValues]); }, [recordValues]);
@ -195,7 +195,7 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal
*******************************************************************************/ *******************************************************************************/
function renderColumn(column: Column): JSX.Element 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)
{ {
@ -269,10 +269,10 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal
// add link to widget header for opening modal // // add link to widget header for opening modal //
///////////////////////////////////////////////// /////////////////////////////////////////////////
const selectTableFirstTooltipTitle = tableMetaData ? null : "You must select a table before you can set up your report filters and columns"; const selectTableFirstTooltipTitle = tableMetaData ? null : "You must select a table before you can set up your report filters and columns";
const labelAdditionalElementsRight: JSX.Element[] = [] const labelAdditionalElementsRight: JSX.Element[] = [];
if (isEditable) if (isEditable)
{ {
labelAdditionalElementsRight.push(<HeaderLinkButtonComponent label="Edit Filters and Columns" onClickCallback={openEditor} disabled={tableMetaData == null} disabledTooltip={selectTableFirstTooltipTitle} />) labelAdditionalElementsRight.push(<HeaderLinkButtonComponent key="filterAndColumnsHeader" label="Edit Filters and Columns" onClickCallback={openEditor} disabled={tableMetaData == null} disabledTooltip={selectTableFirstTooltipTitle} />);
} }
@ -316,7 +316,7 @@ export default function ReportSetupWidget({isEditable, widgetMetaData, recordVal
<Box display="flex" flexWrap="wrap" fontSize="1rem"> <Box display="flex" flexWrap="wrap" fontSize="1rem">
{ {
mayShowColumnsPreview() && mayShowColumnsPreview() &&
columns.columns.map((column, i) => <React.Fragment key={i}>{renderColumn(column)}</React.Fragment>) columns.columns.map((column, i) => <React.Fragment key={`column-${i}`}>{renderColumn(column)}</React.Fragment>)
} }
{ {
!mayShowColumnsPreview() && !mayShowColumnsPreview() &&

View File

@ -176,7 +176,7 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init
{ {
return view; return view;
} }
} };
}); });
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -486,7 +486,6 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init
}; };
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -750,13 +749,13 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init
const viewForLocalStorage: RecordQueryView = JSON.parse(viewAsJSON); const viewForLocalStorage: RecordQueryView = JSON.parse(viewAsJSON);
if (viewForLocalStorage?.queryFilter?.criteria?.length > 0) if (viewForLocalStorage?.queryFilter?.criteria?.length > 0)
{ {
FilterUtils.stripAwayIncompleteCriteria(viewForLocalStorage.queryFilter) FilterUtils.stripAwayIncompleteCriteria(viewForLocalStorage.queryFilter);
} }
localStorageSet(viewLocalStorageKey, JSON.stringify(viewForLocalStorage)); 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);
} }
}; };
@ -1437,7 +1436,6 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init
return (selectedIds.length); return (selectedIds.length);
}; };
/******************************************************************************* /*******************************************************************************
** get a query-string to put on the url to indicate what records are going into ** get a query-string to put on the url to indicate what records are going into
** a process. ** a process.
@ -2983,8 +2981,8 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init
return ( return (
<BaseLayout>{body}</BaseLayout> <BaseLayout>{body}</BaseLayout>
) );
}) });
RecordQuery.defaultProps = { RecordQuery.defaultProps = {

View File

@ -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 {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType"; import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType";
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData"; 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 {NowExpression} from "@kingsrook/qqq-frontend-core/lib/model/query/NowExpression";
import {NowWithOffsetExpression} from "@kingsrook/qqq-frontend-core/lib/model/query/NowWithOffsetExpression"; import {NowWithOffsetExpression} from "@kingsrook/qqq-frontend-core/lib/model/query/NowWithOffsetExpression";
import {QCriteriaOperator} from "@kingsrook/qqq-frontend-core/lib/model/query/QCriteriaOperator"; import {QCriteriaOperator} from "@kingsrook/qqq-frontend-core/lib/model/query/QCriteriaOperator";
@ -365,7 +366,12 @@ class FilterUtils
for (let i = 0; i < maxLoops; i++) for (let i = 0; i < maxLoops; i++)
{ {
const value = criteria.values[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); const expression = new NowWithOffsetExpression(value);
labels.push(expression.toString()); labels.push(expression.toString());