From 50ad1760d52086a5997ee949ad5674b3c249f09f Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Thu, 6 Jul 2023 18:56:20 -0500 Subject: [PATCH] Initial qfmd work to support datetime query expressions from frontend --- .../query/AdvancedDateTimeFilterValues.tsx | 207 ++++++++++++++++++ .../components/query/FilterCriteriaRow.tsx | 32 ++- .../query/FilterCriteriaRowValues.tsx | 165 +++++++++++++- src/qqq/styles/qqq-override-styles.css | 28 ++- src/qqq/utils/qqq/FilterUtils.ts | 66 +++++- 5 files changed, 476 insertions(+), 22 deletions(-) create mode 100644 src/qqq/components/query/AdvancedDateTimeFilterValues.tsx diff --git a/src/qqq/components/query/AdvancedDateTimeFilterValues.tsx b/src/qqq/components/query/AdvancedDateTimeFilterValues.tsx new file mode 100644 index 0000000..fe08c19 --- /dev/null +++ b/src/qqq/components/query/AdvancedDateTimeFilterValues.tsx @@ -0,0 +1,207 @@ +/* + * 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 {NowWithOffsetExpression, NowWithOffsetOperator, NowWithOffsetUnit} from "@kingsrook/qqq-frontend-core/lib/model/query/NowWithOffsetExpression"; +import {FormControl, FormControlLabel, Radio, RadioGroup, Select} from "@mui/material"; +import Box from "@mui/material/Box"; +import Card from "@mui/material/Card"; +import Grid from "@mui/material/Grid"; +import Icon from "@mui/material/Icon"; +import MenuItem from "@mui/material/MenuItem"; +import Modal from "@mui/material/Modal"; +import {SelectChangeEvent} from "@mui/material/Select/Select"; +import TextField from "@mui/material/TextField"; +import Tooltip from "@mui/material/Tooltip"; +import Typography from "@mui/material/Typography"; +import React, {ReactNode, useState} from "react"; +import {QCancelButton, QSaveButton} from "qqq/components/buttons/DefaultButtons"; + + +interface Props +{ + type: "date" | "datetime"; + expression: any; + onSave: (expression: any) => void; +} + +AdvancedDateTimeFilterValues.defaultProps = {}; + +function AdvancedDateTimeFilterValues({type, expression, onSave}: Props): JSX.Element +{ + const [originalExpression, setOriginalExpression] = useState(JSON.stringify(expression)); + + const [expressionType, setExpressionType] = useState(expression?.type ?? "NowWithOffset") + + const [amount, setAmount] = useState(expression?.amount ?? 1) + const [timeUnit, setTimeUnit] = useState(expression?.timeUnit ?? "DAYS" as NowWithOffsetUnit); + const [operator, setOperator] = useState(expression?.operator ?? "MINUS" as NowWithOffsetOperator); + const [isOpen, setIsOpen] = useState(false) + + ////////////////////////////////////////////////////////////////////////////////// + // if the expression (prop) has changed, re-set the state variables based on it // + ////////////////////////////////////////////////////////////////////////////////// + if(JSON.stringify(expression) !== originalExpression) + { + setExpressionType(expression?.type ?? "NowWithOffset") + setAmount(expression?.amount ?? 1) + setTimeUnit(expression?.timeUnit ?? "DAYS") + setOperator(expression?.operator ?? "MINUS") + setOriginalExpression(JSON.stringify(expression)) + } + + const openDialog = () => + { + setIsOpen(true); + } + + const handleSaveClicked = () => + { + switch(expressionType) + { + case "NowWithOffset": + { + const expression = new NowWithOffsetExpression() + expression.operator = operator; + expression.amount = amount; + expression.timeUnit = timeUnit; + onSave(expression); + } + } + + close(); + } + + const close = () => + { + setIsOpen(false); + } + + function handleExpressionTypeChange(e: React.ChangeEvent) + { + setExpressionType(e.target.value); + } + + function handleAmountChange(event: React.ChangeEvent) + { + setAmount(parseInt(event.target.value)); + } + + function handleTimeUnitChange(event: SelectChangeEvent, child: ReactNode) + { + // @ts-ignore + setTimeUnit(event.target.value) + } + + function handleOperatorChange(event: SelectChangeEvent, child: ReactNode) + { + // @ts-ignore + setOperator(event.target.value) + } + + + const mainCardStyles: any = {}; + mainCardStyles.width = "600px"; + + ///////////////////////////////////////////////////////////////////////// + // for the time units, have them end in an 's' if the amount is plural // + ///////////////////////////////////////////////////////////////////////// + const tuS = (amount == 1 ? "" : "s"); + + return ( + + + settings + + { + isOpen && + ( + + + + + + + + Advanced Date Filter Condition + + Select the type of expression you want for your condition.
+ Then enter values to express your condition. +
+
+
+
+ + + + } label="Relative Expression" /> + + + handleAmountChange(event)} + fullWidth + /> + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + ) + } +
+ ); +} + +export default AdvancedDateTimeFilterValues; diff --git a/src/qqq/components/query/FilterCriteriaRow.tsx b/src/qqq/components/query/FilterCriteriaRow.tsx index 850a065..ff6dd2d 100644 --- a/src/qqq/components/query/FilterCriteriaRow.tsx +++ b/src/qqq/components/query/FilterCriteriaRow.tsx @@ -159,7 +159,9 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE_DATE}); operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.SINGLE_DATE}); operatorOptions.push({label: "is after", value: QCriteriaOperator.GREATER_THAN, valueMode: ValueMode.SINGLE_DATE}); + operatorOptions.push({label: "is on or after", value: QCriteriaOperator.GREATER_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE_DATE}); operatorOptions.push({label: "is before", value: QCriteriaOperator.LESS_THAN, valueMode: ValueMode.SINGLE_DATE}); + operatorOptions.push({label: "is on or before", value: QCriteriaOperator.LESS_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE_DATE}); operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE}); operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE}); //? operatorOptions.push({label: "is between", value: QCriteriaOperator.BETWEEN}); @@ -171,9 +173,9 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, operatorOptions.push({label: "equals", value: QCriteriaOperator.EQUALS, valueMode: ValueMode.SINGLE_DATE_TIME}); operatorOptions.push({label: "does not equal", value: QCriteriaOperator.NOT_EQUALS_OR_IS_NULL, valueMode: ValueMode.SINGLE_DATE_TIME}); operatorOptions.push({label: "is after", value: QCriteriaOperator.GREATER_THAN, valueMode: ValueMode.SINGLE_DATE_TIME}); - operatorOptions.push({label: "is on or after", value: QCriteriaOperator.GREATER_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE_DATE_TIME}); + operatorOptions.push({label: "is at or after", value: QCriteriaOperator.GREATER_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE_DATE_TIME}); operatorOptions.push({label: "is before", value: QCriteriaOperator.LESS_THAN, valueMode: ValueMode.SINGLE_DATE_TIME}); - operatorOptions.push({label: "is on or before", value: QCriteriaOperator.LESS_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE_DATE_TIME}); + operatorOptions.push({label: "is at or before", value: QCriteriaOperator.LESS_THAN_OR_EQUALS, valueMode: ValueMode.SINGLE_DATE_TIME}); operatorOptions.push({label: "is empty", value: QCriteriaOperator.IS_BLANK, valueMode: ValueMode.NONE}); operatorOptions.push({label: "is not empty", value: QCriteriaOperator.IS_NOT_BLANK, valueMode: ValueMode.NONE}); //? operatorOptions.push({label: "is between", value: QCriteriaOperator.BETWEEN}); @@ -335,8 +337,24 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, ////////////////////////////////////////////////// // event handler for value field (of all types) // ////////////////////////////////////////////////// - const handleValueChange = (event: React.ChangeEvent | SyntheticEvent, valueIndex: number | "all" = 0, newValue?: any) => + const handleValueChange = (event: React.ChangeEvent | SyntheticEvent, valueIndex: number | "all" = 0, newValue?: any, newExpression?: any) => { + /////////////////////////////////////////////////////////////////////////////////////////////////////// + // if an expression was passed in - put it on the criteria, removing the values. // + // else - if no expression - make sure criteria.expression is null, and do the various values logics // + /////////////////////////////////////////////////////////////////////////////////////////////////////// + if(newExpression) + { + criteria.expression = newExpression; + criteria.values = null; + updateCriteria(criteria, true); + return; + } + else + { + criteria.expression = null; + } + // @ts-ignore const value = newValue !== undefined ? newValue : event ? event.target.value : null; @@ -447,6 +465,12 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, // don't need to look at values // ////////////////////////////////// } + else if (criteria.expression) + { + //////////////////////////////////////////////////////// + // if there's an expression - let's assume it's valid // + //////////////////////////////////////////////////////// + } else if(operatorSelectedValue.valueMode == ValueMode.DOUBLE) { if(criteria.values.length < 2) @@ -533,7 +557,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, criteria={{id: id, ...criteria}} field={field} table={fieldTable} - valueChangeHandler={(event, valueIndex, newValue) => handleValueChange(event, valueIndex, newValue)} + valueChangeHandler={(event, valueIndex, newValue, newExpression) => handleValueChange(event, valueIndex, newValue, newExpression)} /> diff --git a/src/qqq/components/query/FilterCriteriaRowValues.tsx b/src/qqq/components/query/FilterCriteriaRowValues.tsx index ef1452b..b0a70e7 100644 --- a/src/qqq/components/query/FilterCriteriaRowValues.tsx +++ b/src/qqq/components/query/FilterCriteriaRowValues.tsx @@ -23,14 +23,21 @@ 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 {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 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 Menu from "@mui/material/Menu"; +import MenuItem from "@mui/material/MenuItem"; +import Select, {SelectChangeEvent} from "@mui/material/Select/Select"; import TextField from "@mui/material/TextField"; -import React, {SyntheticEvent, useReducer} from "react"; +import Tooltip from "@mui/material/Tooltip"; +import React, {ReactNode, SyntheticEvent, useReducer, useState} from "react"; import DynamicSelect from "qqq/components/forms/DynamicSelect"; +import AdvancedDateTimeFilterValues from "qqq/components/query/AdvancedDateTimeFilterValues"; import {QFilterCriteriaWithId} from "qqq/components/query/CustomFilterPanel"; import FilterCriteriaPaster from "qqq/components/query/FilterCriteriaPaster"; import {OperatorOption, ValueMode} from "qqq/components/query/FilterCriteriaRow"; @@ -42,7 +49,7 @@ interface Props criteria: QFilterCriteriaWithId; field: QFieldMetaData; table: QTableMetaData; - valueChangeHandler: (event: React.ChangeEvent | SyntheticEvent, valueIndex?: number | "all", newValue?: any) => void; + valueChangeHandler: (event: React.ChangeEvent | SyntheticEvent, valueIndex?: number | "all", newValue?: any, newExpression?: any) => void; } FilterCriteriaRowValues.defaultProps = { @@ -50,6 +57,8 @@ FilterCriteriaRowValues.defaultProps = { function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueChangeHandler}: Props): JSX.Element { + const [relativeDateTimeMenuAnchorElement, setRelativeDateTimeMenuAnchorElement] = useState(null); + const [, forceUpdate] = useReducer((x) => x + 1, 0); if (!operatorOption) @@ -122,6 +131,35 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC />; }; + const makeDateTimeExpressionTextField = (value: string, valueIndex: number = 0, label = "Value", idPrefix = "value-") => + { + const clearValue = (event: React.MouseEvent | React.MouseEvent, index: number) => + { + valueChangeHandler(event, index, ""); + forceUpdate() + document.getElementById(`${idPrefix}${criteria.id}`).focus(); + }; + + const inputProps: any = {}; + inputProps.endAdornment = ( + + clearValue(event, valueIndex)}> + close + + + ); + + return ; + } + function saveNewPasterValues(newValues: any[]) { if (criteria.values) @@ -145,6 +183,47 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC forceUpdate(); } + const openRelativeDateTimeMenu = (event: React.MouseEvent) => + { + setRelativeDateTimeMenuAnchorElement(event.currentTarget); + }; + + const closeRelativeDateTimeMenu = () => + { + setRelativeDateTimeMenuAnchorElement(null); + }; + + const setExpressionNowWithOffset = (operator: NowWithOffsetOperator, amount: number, timeUnit: NowWithOffsetUnit) => + { + const expression = new NowWithOffsetExpression() + expression.operator = operator; + expression.amount = amount; + expression.timeUnit = timeUnit; + + saveNewDateTimeExpression(expression); + + closeRelativeDateTimeMenu(); + }; + + const setExpressionThisOrLastPeriod = (operator: ThisOrLastPeriodOperator, timeUnit: ThisOrLastPeriodUnit) => + { + const expression = new ThisOrLastPeriodExpression() + expression.operator = operator; + expression.timeUnit = timeUnit; + + saveNewDateTimeExpression(expression); + + closeRelativeDateTimeMenu(); + }; + + function saveNewDateTimeExpression(expression: any) + { + criteria.expression = expression; + criteria.values = null; + valueChangeHandler(null, null, null, expression); + forceUpdate(); + } + switch (operatorOption.valueMode) { case ValueMode.NONE: @@ -152,9 +231,87 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC case ValueMode.SINGLE: return makeTextField(); case ValueMode.SINGLE_DATE: - return makeTextField(); + return + { + criteria.expression == null && makeTextField() + } + { + criteria.expression != null && makeDateTimeExpressionTextField(criteria.expression.toString()) + } + + + event_upcoming + + + setExpressionNowWithOffset("MINUS", 1, "DAYS")}>1 day ago + setExpressionThisOrLastPeriod("THIS", "DAYS")}>today + setExpressionThisOrLastPeriod("LAST", "DAYS")}>yesterday + setExpressionNowWithOffset("MINUS", 7, "DAYS")}>7 days ago + setExpressionThisOrLastPeriod("THIS", "WEEKS")}>start of this week + setExpressionThisOrLastPeriod("LAST", "WEEKS")}>start of last week + setExpressionNowWithOffset("MINUS", 14, "DAYS")}>14 days ago + setExpressionNowWithOffset("MINUS", 30, "DAYS")}>30 days ago + setExpressionThisOrLastPeriod("THIS", "MONTHS")}>start of this month + setExpressionThisOrLastPeriod("LAST", "MONTHS")}>start of last month + setExpressionNowWithOffset("MINUS", 90, "DAYS")}>90 days ago + setExpressionNowWithOffset("MINUS", 180, "DAYS")}>180 days ago + setExpressionNowWithOffset("MINUS", 1, "YEARS")}>1 year ago + setExpressionThisOrLastPeriod("THIS", "YEARS")}>start of this year + setExpressionThisOrLastPeriod("LAST", "YEARS")}>start of last year + + + + saveNewDateTimeExpression(expression)} /> + + ; case ValueMode.SINGLE_DATE_TIME: - return makeTextField(); + return + { + criteria.expression == null && makeTextField() + } + { + criteria.expression != null && makeDateTimeExpressionTextField(criteria.expression.toString()) + } + + + event_upcoming + + + setExpressionNowWithOffset("MINUS", 1, "HOURS")}>1 hour ago + setExpressionThisOrLastPeriod("THIS", "HOURS")}>start of this hour + setExpressionThisOrLastPeriod("LAST", "HOURS")}>start of last hour + setExpressionNowWithOffset("MINUS", 12, "HOURS")}>12 hours ago + setExpressionNowWithOffset("MINUS", 24, "HOURS")}>24 hours ago + setExpressionThisOrLastPeriod("THIS", "DAYS")}>start of today + setExpressionThisOrLastPeriod("LAST", "DAYS")}>start of yesterday + setExpressionNowWithOffset("MINUS", 7, "DAYS")}>7 days ago + setExpressionThisOrLastPeriod("THIS", "WEEKS")}>start of this week + setExpressionThisOrLastPeriod("LAST", "WEEKS")}>start of last week + setExpressionNowWithOffset("MINUS", 14, "DAYS")}>14 days ago + setExpressionNowWithOffset("MINUS", 30, "DAYS")}>30 days ago + setExpressionThisOrLastPeriod("THIS", "MONTHS")}>start of this month + setExpressionThisOrLastPeriod("LAST", "MONTHS")}>start of last month + setExpressionNowWithOffset("MINUS", 90, "DAYS")}>90 days ago + setExpressionNowWithOffset("MINUS", 180, "DAYS")}>180 days ago + setExpressionNowWithOffset("MINUS", 1, "YEARS")}>1 year ago + setExpressionThisOrLastPeriod("THIS", "YEARS")}>start of this year + setExpressionThisOrLastPeriod("LAST", "YEARS")}>start of last year + + + + saveNewDateTimeExpression(expression)} /> + + ; case ValueMode.DOUBLE: return diff --git a/src/qqq/styles/qqq-override-styles.css b/src/qqq/styles/qqq-override-styles.css index 0b791d4..73d6aa5 100644 --- a/src/qqq/styles/qqq-override-styles.css +++ b/src/qqq/styles/qqq-override-styles.css @@ -446,22 +446,34 @@ input[type="search"]::-webkit-search-results-decoration { display: none; } font-size: 14px !important; } -/* fix something in AND/OR dropdown in filters */ -.customFilterPanel .booleanOperatorColumn .MuiSvgIcon-root +/* make down-arrow appear in select boxes */ +.customFilterPanel .booleanOperatorColumn .MuiSvgIcon-root, +.AdvancedDateTimeFilterValues .MuiSvgIcon-root { display: inline-block !important; } -/* adjust bottom of AND/OR dropdown in filters */ -.customFilterPanel .booleanOperatorColumn .MuiInputBase-formControl +/* adjust vertical padding in filter selects */ +.customFilterPanel .booleanOperatorColumn .MuiInputBase-formControl .MuiSelect-select, +.AdvancedDateTimeFilterValues .MuiInputBase-formControl .MuiSelect-select { - padding-bottom: calc(0.25rem + 1px); + padding-bottom: calc(0.25rem + 1px) !important; + padding-top: calc(0.25rem + 1px) !important; } -/* adjust down-arrow in AND/OR dropdown in filters */ -.customFilterPanel .booleanOperatorColumn .MuiSelect-iconStandard +.customFilterPanel .filterValuesColumn .MuiInputBase-inputAdornedEnd { - top: calc(50% - 0.75rem); + padding-right: 0 !important; +} + +.customFilterPanel .filterValuesColumn .MuiInputAdornment-positionEnd button +{ + padding-left: 0; +} + +.customFilterPanel .filterValuesColumn .MuiSelect-iconStandard +{ + display: inline; } /* change tags in any-of value fields to not be black bg with white text */ diff --git a/src/qqq/utils/qqq/FilterUtils.ts b/src/qqq/utils/qqq/FilterUtils.ts index a428c2c..2c5b0dd 100644 --- a/src/qqq/utils/qqq/FilterUtils.ts +++ b/src/qqq/utils/qqq/FilterUtils.ts @@ -23,10 +23,12 @@ 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 {NowWithOffsetExpression} from "@kingsrook/qqq-frontend-core/lib/model/query/NowWithOffsetExpression"; import {QCriteriaOperator} from "@kingsrook/qqq-frontend-core/lib/model/query/QCriteriaOperator"; import {QFilterCriteria} from "@kingsrook/qqq-frontend-core/lib/model/query/QFilterCriteria"; import {QFilterOrderBy} from "@kingsrook/qqq-frontend-core/lib/model/query/QFilterOrderBy"; import {QQueryFilter} from "@kingsrook/qqq-frontend-core/lib/model/query/QQueryFilter"; +import {ThisOrLastPeriodExpression} from "@kingsrook/qqq-frontend-core/lib/model/query/ThisOrLastPeriodExpression"; import {GridFilterItem, GridFilterModel, GridLinkOperator, GridSortItem} from "@mui/x-data-grid-pro"; import ValueUtils from "qqq/utils/qqq/ValueUtils"; @@ -285,6 +287,11 @@ class FilterUtils return (param); } + if (FilterUtils.gridCriteriaValueToExpression(param)) + { + return (null); + } + let rs = []; for (let i = 0; i < param.length; i++) { @@ -330,8 +337,13 @@ class FilterUtils ** Convert a filter field's value from the style that qqq uses, to the style that ** the grid uses. *******************************************************************************/ - public static qqqCriteriaValuesToGrid = (operator: QCriteriaOperator, values: any[], field: QFieldMetaData): any | any[] => + public static qqqCriteriaValuesToGrid = (operator: QCriteriaOperator, values: any[], expression: any, field: QFieldMetaData): any | any[] => { + if(expression) + { + return (expression); + } + const fieldType = field.type; if (operator === QCriteriaOperator.IS_BLANK || operator === QCriteriaOperator.IS_NOT_BLANK) { @@ -342,7 +354,7 @@ class FilterUtils return (values); } - if (values.length > 0) + if (values && values.length > 0) { //////////////////////////////////////////////////////////////////////////////////////////////// // make sure dates are formatted for the grid the way it expects - not the way we pass it in. // @@ -353,7 +365,7 @@ class FilterUtils } } - return (values[0]); + return (values ? values[0] : ""); }; @@ -432,6 +444,7 @@ class FilterUtils } } + // todo - use expressions here!! if (field && field.type == "DATE_TIME" && !values) { try @@ -538,7 +551,7 @@ class FilterUtils defaultFilter.items.push({ columnField: criteria.fieldName, operatorValue: FilterUtils.qqqCriteriaOperatorToGrid(criteria.operator, field, values), - value: FilterUtils.qqqCriteriaValuesToGrid(criteria.operator, values, field), + value: FilterUtils.qqqCriteriaValuesToGrid(criteria.operator, values, criteria.expression, field), id: id++, // not sure what this id is!! }); } @@ -595,6 +608,18 @@ class FilterUtils } } + if(defaultFilter && defaultFilter.items && defaultFilter.items.length) + { + defaultFilter.items.forEach((item) => + { + const expression = this.gridCriteriaValueToExpression(item.value) + if(expression) + { + item.value = expression; + } + }); + } + return ({filter: defaultFilter, sort: defaultSort}); } @@ -612,7 +637,7 @@ class FilterUtils const [field, fieldTable] = FilterUtils.getField(tableMetaData, criteria.fieldName); if (field) { - gridItems.push({columnField: criteria.fieldName, id: i, operatorValue: FilterUtils.qqqCriteriaOperatorToGrid(criteria.operator, field, criteria.values), value: FilterUtils.qqqCriteriaValuesToGrid(criteria.operator, criteria.values, field)}); + gridItems.push({columnField: criteria.fieldName, id: i, operatorValue: FilterUtils.qqqCriteriaOperatorToGrid(criteria.operator, field, criteria.values), value: FilterUtils.qqqCriteriaValuesToGrid(criteria.operator, criteria.values, criteria.expression, field)}); } } @@ -711,7 +736,13 @@ class FilterUtils const fieldMetadata = tableMetaData?.fields.get(item.columnField); const operator = FilterUtils.gridCriteriaOperatorToQQQ(item.operatorValue); const values = FilterUtils.gridCriteriaValueToQQQ(operator, item.value, item.operatorValue, fieldMetadata); - qFilter.addCriteria(new QFilterCriteria(item.columnField, operator, values)); + let criteria = new QFilterCriteria(item.columnField, operator, values); + const expression = FilterUtils.gridCriteriaValueToExpression(item.value); + if(expression) + { + criteria.expression = expression; + } + qFilter.addCriteria(criteria); foundFilter = true; }); @@ -729,6 +760,29 @@ class FilterUtils }; + /******************************************************************************* + ** + *******************************************************************************/ + private static gridCriteriaValueToExpression(value: any) + { + if (value.length) + { + value = value[0]; + } + + if (value.type && value.type == "NowWithOffset") + { + return (new NowWithOffsetExpression(value)); + } + else if (value.type && value.type == "ThisOrLastPeriod") + { + return (new ThisOrLastPeriodExpression(value)); + } + + return (null); + } + + /******************************************************************************* ** edit the input filter object, replacing any values which have {id,label} attributes ** to instead just have the id part.