Checkpoint - functional between, expressions working as values, etc

This commit is contained in:
2023-07-14 09:36:33 -05:00
parent 8458ff6b2a
commit 28c48cc2ef
5 changed files with 453 additions and 327 deletions

View File

@ -19,7 +19,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType";
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 {FormControl, FormControlLabel, Radio, RadioGroup, Select} from "@mui/material"; import {FormControl, FormControlLabel, Radio, RadioGroup, Select} from "@mui/material";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Card from "@mui/material/Card"; import Card from "@mui/material/Card";
@ -37,33 +40,57 @@ import {QCancelButton, QSaveButton} from "qqq/components/buttons/DefaultButtons"
interface Props interface Props
{ {
type: "date" | "datetime"; type: QFieldType
expression: any; expression: any;
onSave: (expression: any) => void; onSave: (expression: any) => void;
} }
AdvancedDateTimeFilterValues.defaultProps = {}; AdvancedDateTimeFilterValues.defaultProps = {};
const extractExpressionType = (expression: any) => expression?.type ?? "NowWithOffset";
const extractNowWithOffsetAmount = (expression: any) => expression?.type == "NowWithOffset" ? (expression?.amount ?? 1) : 1;
const extractNowWithOffsetTimeUnit = (expression: any) => expression?.type == "NowWithOffset" ? (expression?.timeUnit ?? "DAYS") : "DAYS" as NowWithOffsetUnit;
const extractNowWithOffsetOperator = (expression: any) => expression?.type == "NowWithOffset" ? (expression?.operator ?? "MINUS") : "MINUS" as NowWithOffsetOperator;
const extractThisOrLastPeriodTimeUnit = (expression: any) => expression?.type == "ThisOrLastPeriod" ? (expression?.timeUnit ?? "DAYS") : "DAYS" as ThisOrLastPeriodUnit;
const extractThisOrLastPeriodOperator = (expression: any) => expression?.type == "ThisOrLastPeriod" ? (expression?.operator ?? "THIS") : "THIS" as ThisOrLastPeriodOperator;
function AdvancedDateTimeFilterValues({type, expression, onSave}: Props): JSX.Element function AdvancedDateTimeFilterValues({type, expression, onSave}: Props): JSX.Element
{ {
const [originalExpression, setOriginalExpression] = useState(JSON.stringify(expression)); const [originalExpression, setOriginalExpression] = useState(JSON.stringify(expression));
const [expressionType, setExpressionType] = useState(expression?.type ?? "NowWithOffset") const [expressionType, setExpressionType] = useState(extractExpressionType(expression))
const [nowWithOffsetAmount, setNowWithOffsetAmount] = useState(extractNowWithOffsetAmount(expression));
const [nowWithOffsetTimeUnit, setNowWithOffsetTimeUnit] = useState(extractNowWithOffsetTimeUnit(expression));
const [nowWithOffsetOperator, setNowWithOffsetOperator] = useState(extractNowWithOffsetOperator(expression));
const [thisOrLastPeriodTimeUnit, setThisOrLastPeriodTimeUnit] = useState(extractThisOrLastPeriodTimeUnit(expression));
const [thisOrLastPeriodOperator, setThisOrLastPeriodOperator] = useState(extractThisOrLastPeriodOperator(expression));
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) const [isOpen, setIsOpen] = useState(false)
const setStateToExpression = (activeExpression: any) =>
{
setExpressionType(extractExpressionType(activeExpression))
setNowWithOffsetAmount(extractNowWithOffsetAmount(activeExpression))
setNowWithOffsetTimeUnit(extractNowWithOffsetTimeUnit(activeExpression))
setNowWithOffsetOperator(extractNowWithOffsetOperator(activeExpression))
setThisOrLastPeriodTimeUnit(extractThisOrLastPeriodTimeUnit(activeExpression))
setThisOrLastPeriodOperator(extractThisOrLastPeriodOperator(activeExpression))
}
////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////
// if the expression (prop) has changed, re-set the state variables based on it // // if the expression (prop) has changed, re-set the state variables based on it //
////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////
if(JSON.stringify(expression) !== originalExpression) if(JSON.stringify(expression) !== originalExpression)
{ {
setExpressionType(expression?.type ?? "NowWithOffset") ///////////////////////////////////////////////////////////
setAmount(expression?.amount ?? 1) // update all state vars based on the current expression //
setTimeUnit(expression?.timeUnit ?? "DAYS") ///////////////////////////////////////////////////////////
setOperator(expression?.operator ?? "MINUS") setStateToExpression(expression);
setOriginalExpression(JSON.stringify(expression)) setOriginalExpression(JSON.stringify(expression))
} }
@ -72,17 +99,47 @@ function AdvancedDateTimeFilterValues({type, expression, onSave}: Props): JSX.El
setIsOpen(true); setIsOpen(true);
} }
const handleCancelClicked = () =>
{
///////////////////////////////////////////////////////////
// update all state vars back to the original expression //
///////////////////////////////////////////////////////////
const restoreExpression = JSON.parse(originalExpression)
setStateToExpression(restoreExpression);
close();
}
const handleSaveClicked = () => const handleSaveClicked = () =>
{ {
switch(expressionType) switch(expressionType)
{ {
case "Now":
{
const expression = new NowExpression();
onSave(expression);
break;
}
case "NowWithOffset": case "NowWithOffset":
{ {
const expression = new NowWithOffsetExpression() const expression = new NowWithOffsetExpression()
expression.operator = operator; expression.operator = nowWithOffsetOperator;
expression.amount = amount; expression.amount = nowWithOffsetAmount;
expression.timeUnit = timeUnit; expression.timeUnit = nowWithOffsetTimeUnit;
onSave(expression); onSave(expression);
break;
}
case "ThisOrLastPeriod":
{
const expression = new ThisOrLastPeriodExpression()
expression.operator = thisOrLastPeriodOperator;
expression.timeUnit = thisOrLastPeriodTimeUnit;
onSave(expression);
break;
}
default:
{
console.log(`Unmapped expression type in handleSAveClicked: ${expressionType}`);
} }
} }
@ -99,35 +156,47 @@ function AdvancedDateTimeFilterValues({type, expression, onSave}: Props): JSX.El
setExpressionType(e.target.value); setExpressionType(e.target.value);
} }
function handleAmountChange(event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) function handleNowWithOffsetAmountChange(event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>)
{ {
setAmount(parseInt(event.target.value)); setNowWithOffsetAmount(parseInt(event.target.value));
} }
function handleTimeUnitChange(event: SelectChangeEvent<NowWithOffsetUnit>, child: ReactNode) function handleNowWithOffsetTimeUnitChange(event: SelectChangeEvent<NowWithOffsetUnit>, child: ReactNode)
{ {
// @ts-ignore // @ts-ignore
setTimeUnit(event.target.value) setNowWithOffsetTimeUnit(event.target.value)
} }
function handleOperatorChange(event: SelectChangeEvent<NowWithOffsetOperator>, child: ReactNode) function handleNowWithOffsetOperatorChange(event: SelectChangeEvent<NowWithOffsetOperator>, child: ReactNode)
{ {
// @ts-ignore // @ts-ignore
setOperator(event.target.value) setNowWithOffsetOperator(event.target.value)
} }
function handleThisOrLastPeriodTimeUnitChange(event: SelectChangeEvent<ThisOrLastPeriodUnit>, child: ReactNode)
{
// @ts-ignore
setThisOrLastPeriodTimeUnit(event.target.value)
}
function handleThisOrLastPeriodOperatorChange(event: SelectChangeEvent<ThisOrLastPeriodOperator>, child: ReactNode)
{
// @ts-ignore
setThisOrLastPeriodOperator(event.target.value)
}
const mainCardStyles: any = {}; const mainCardStyles: any = {};
mainCardStyles.width = "600px"; mainCardStyles.width = "600px";
///////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////
// for the time units, have them end in an 's' if the amount is plural // // for the time units, have them end in an 's' if the amount is plural //
// name here means "time unit 's'" //
///////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////
const tuS = (amount == 1 ? "" : "s"); const nwoTUs = (nowWithOffsetAmount == 1 ? "" : "s");
return ( return (
<Box> <Box>
<Tooltip title={`Define a more advanced ${type == "date" ? "date" : "date-time"} condition`}> <Tooltip title={`Define a more advanced ${type == QFieldType.DATE ? "date" : "date-time"} condition`}>
<Icon onClick={openDialog} fontSize="small" color="info" sx={{mx: 0.25, cursor: "pointer"}}>settings</Icon> <Icon onClick={openDialog} fontSize="small" color="info" sx={{mx: 0.25, cursor: "pointer"}}>settings</Icon>
</Tooltip> </Tooltip>
{ {
@ -150,6 +219,10 @@ function AdvancedDateTimeFilterValues({type, expression, onSave}: Props): JSX.El
</Box> </Box>
<RadioGroup name="expressionType" value={expressionType} onChange={handleExpressionTypeChange}> <RadioGroup name="expressionType" value={expressionType} onChange={handleExpressionTypeChange}>
<Box px={4} pb={4}>
<FormControlLabel value={"Now"} control={<Radio size="small" />} label="Now" />
</Box>
<Box px={4} pb={4}> <Box px={4} pb={4}>
<FormControlLabel value={"NowWithOffset"} control={<Radio size="small" />} label="Relative Expression" /> <FormControlLabel value={"NowWithOffset"} control={<Radio size="small" />} label="Relative Expression" />
<Box pl={4}> <Box pl={4}>
@ -159,26 +232,26 @@ function AdvancedDateTimeFilterValues({type, expression, onSave}: Props): JSX.El
type="number" type="number"
inputProps={{min: 0}} inputProps={{min: 0}}
autoComplete="off" autoComplete="off"
value={amount} value={nowWithOffsetAmount}
onChange={(event) => handleAmountChange(event)} onChange={(event) => handleNowWithOffsetAmountChange(event)}
fullWidth fullWidth
/> />
</FormControl> </FormControl>
<FormControl variant="standard" sx={{verticalAlign: "bottom", width: "30%"}}> <FormControl variant="standard" sx={{verticalAlign: "bottom", width: "30%"}}>
<Select value={timeUnit} disabled={false} onChange={handleTimeUnitChange} label="Unit"> <Select value={nowWithOffsetTimeUnit} disabled={false} onChange={handleNowWithOffsetTimeUnitChange} label="Unit">
{type == "datetime" && <MenuItem value="SECONDS">Second{tuS}</MenuItem>} {type == QFieldType.DATE_TIME && <MenuItem value="SECONDS">Second{nwoTUs}</MenuItem>}
{type == "datetime" && <MenuItem value="MINUTES">Minute{tuS}</MenuItem>} {type == QFieldType.DATE_TIME && <MenuItem value="MINUTES">Minute{nwoTUs}</MenuItem>}
{type == "datetime" && <MenuItem value="HOURS">Hour{tuS}</MenuItem>} {type == QFieldType.DATE_TIME && <MenuItem value="HOURS">Hour{nwoTUs}</MenuItem>}
<MenuItem value="DAYS">Day{tuS}</MenuItem> <MenuItem value="DAYS">Day{nwoTUs}</MenuItem>
<MenuItem value="WEEKS">Week{tuS}</MenuItem> <MenuItem value="WEEKS">Week{nwoTUs}</MenuItem>
<MenuItem value="MONTHS">Month{tuS}</MenuItem> <MenuItem value="MONTHS">Month{nwoTUs}</MenuItem>
<MenuItem value="YEARS">Year{tuS}</MenuItem> <MenuItem value="YEARS">Year{nwoTUs}</MenuItem>
</Select> </Select>
</FormControl> </FormControl>
<FormControl variant="standard" sx={{verticalAlign: "bottom", width: "40%"}}> <FormControl variant="standard" sx={{verticalAlign: "bottom", width: "40%"}}>
<Select value={operator} disabled={false} onChange={handleOperatorChange}> <Select value={nowWithOffsetOperator} disabled={false} onChange={handleNowWithOffsetOperatorChange}>
<MenuItem value="MINUS">Ago (in the past)</MenuItem> <MenuItem value="MINUS">Ago (in the past)</MenuItem>
<MenuItem value="PLUS">From now (in the future)</MenuItem> <MenuItem value="PLUS">From now (in the future)</MenuItem>
</Select> </Select>
@ -186,10 +259,34 @@ function AdvancedDateTimeFilterValues({type, expression, onSave}: Props): JSX.El
</Box> </Box>
</Box> </Box>
<Box px={4} pb={4}>
<FormControlLabel value={"ThisOrLastPeriod"} control={<Radio size="small" />} label={`${type == QFieldType.DATE_TIME ? "Start of " : ""}This or Last...`} />
<Box pl={4}>
<FormControl variant="standard" sx={{verticalAlign: "bottom", width: "30%"}}>
<Select value={thisOrLastPeriodOperator} disabled={false} onChange={handleThisOrLastPeriodOperatorChange}>
<MenuItem value="THIS">This</MenuItem>
<MenuItem value="LAST">Last</MenuItem>
</Select>
</FormControl>
<FormControl variant="standard" sx={{verticalAlign: "bottom", width: "30%"}}>
<Select value={thisOrLastPeriodTimeUnit} disabled={false} onChange={handleThisOrLastPeriodTimeUnitChange} label="Unit">
{type == QFieldType.DATE_TIME && <MenuItem value="HOURS">Hour</MenuItem>}
<MenuItem value="DAYS">Day</MenuItem>
<MenuItem value="WEEKS">Week</MenuItem>
<MenuItem value="MONTHS">Month</MenuItem>
<MenuItem value="YEARS">Year</MenuItem>
</Select>
</FormControl>
</Box>
</Box>
</RadioGroup> </RadioGroup>
<Box p={3} pt={0}> <Box p={3} pt={0}>
<Grid container pl={1} pr={1} justifyContent="right" alignItems="stretch" sx={{display: "flex-inline "}}> <Grid container pl={1} pr={1} justifyContent="right" alignItems="stretch" sx={{display: "flex-inline "}}>
<QCancelButton onClickHandler={close} iconName="cancel" disabled={false} /> <QCancelButton onClickHandler={handleCancelClicked} iconName="cancel" disabled={false} />
<QSaveButton onClickHandler={handleSaveClicked} label="Apply" disabled={false} /> <QSaveButton onClickHandler={handleSaveClicked} label="Apply" disabled={false} />
</Grid> </Grid>
</Box> </Box>

View File

@ -0,0 +1,221 @@
/*
* 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 {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType";
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";
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 TextField from "@mui/material/TextField";
import Tooltip from "@mui/material/Tooltip";
import React, {SyntheticEvent, useReducer, useState} from "react";
import AdvancedDateTimeFilterValues from "qqq/components/query/AdvancedDateTimeFilterValues";
import {QFilterCriteriaWithId} from "qqq/components/query/CustomFilterPanel";
import {makeTextField} from "qqq/components/query/FilterCriteriaRowValues";
interface CriteriaDateFieldProps
{
valueIndex: number;
label: string;
idPrefix: string;
field: QFieldMetaData;
criteria: QFilterCriteriaWithId;
valueChangeHandler: (event: React.ChangeEvent | SyntheticEvent, valueIndex?: number | "all", newValue?: any) => void;
}
CriteriaDateField.defaultProps = {
valueIndex: 0,
label: "Value",
idPrefix: "value-"
};
export default function CriteriaDateField({valueIndex, label, idPrefix, field, criteria, valueChangeHandler}: CriteriaDateFieldProps): JSX.Element
{
const [relativeDateTimeMenuAnchorElement, setRelativeDateTimeMenuAnchorElement] = useState(null);
const [, forceUpdate] = useReducer((x) => x + 1, 0);
const openRelativeDateTimeMenu = (event: React.MouseEvent<HTMLElement>) =>
{
setRelativeDateTimeMenuAnchorElement(event.currentTarget);
};
const closeRelativeDateTimeMenu = () =>
{
setRelativeDateTimeMenuAnchorElement(null);
};
const setExpressionNow = (valueIndex: number) =>
{
const expression = new NowExpression()
saveNewDateTimeExpression(valueIndex, expression);
closeRelativeDateTimeMenu();
};
const setExpressionNowWithOffset = (valueIndex: number, operator: NowWithOffsetOperator, amount: number, timeUnit: NowWithOffsetUnit) =>
{
const expression = new NowWithOffsetExpression()
expression.operator = operator;
expression.amount = amount;
expression.timeUnit = timeUnit;
saveNewDateTimeExpression(valueIndex, expression);
closeRelativeDateTimeMenu();
};
const setExpressionThisOrLastPeriod = (valueIndex: number, operator: ThisOrLastPeriodOperator, timeUnit: ThisOrLastPeriodUnit) =>
{
const expression = new ThisOrLastPeriodExpression()
expression.operator = operator;
expression.timeUnit = timeUnit;
saveNewDateTimeExpression(valueIndex, expression);
closeRelativeDateTimeMenu();
};
function saveNewDateTimeExpression(valueIndex: number, expression: any)
{
valueChangeHandler(null, valueIndex, expression);
forceUpdate();
}
const makeDateTimeExpressionTextField = (expression: any, valueIndex: number = 0, label = "Value", idPrefix = "value-") =>
{
const clearValue = (event: React.MouseEvent<HTMLAnchorElement> | React.MouseEvent<HTMLButtonElement>, index: number) =>
{
valueChangeHandler(event, index, "");
forceUpdate()
document.getElementById(`${idPrefix}${criteria.id}`).focus();
};
const inputProps: any = {};
inputProps.endAdornment = (
<InputAdornment position="end">
<IconButton sx={{visibility: expression ? "visible" : "hidden"}} onClick={(event) => clearValue(event, valueIndex)}>
<Icon>close</Icon>
</IconButton>
</InputAdornment>
);
let displayValue = expression.toString();
if (expression?.type == "ThisOrLastPeriod")
{
if(field.type == QFieldType.DATE_TIME || (field.type == QFieldType.DATE && expression.timeUnit != "DAYS"))
{
displayValue = "start of " + displayValue;
}
}
return <TextField
id={`${idPrefix}${criteria.id}`}
label={label}
variant="standard"
autoComplete="off"
InputProps={{readOnly: true, unselectable: "off", ...inputProps}}
InputLabelProps={{shrink: true}}
value={displayValue}
fullWidth
/>;
}
const isExpression = criteria.values && criteria.values[valueIndex] && criteria.values[valueIndex].type;
const currentExpression = isExpression ? criteria.values[valueIndex] : null;
return <Box display="flex" alignItems="flex-end">
{
isExpression ? makeDateTimeExpressionTextField(criteria.values[valueIndex], valueIndex, label, idPrefix)
: makeTextField(field, criteria, valueChangeHandler, valueIndex, label, idPrefix)
}
<Box>
<Tooltip title={`Choose a common relative ${field.type == QFieldType.DATE ? "date" : "date-time"} expression`} placement="top">
<Icon fontSize="small" color="info" sx={{mx: 0.25, cursor: "pointer"}} onClick={openRelativeDateTimeMenu}>date_range</Icon>
</Tooltip>
<Menu
open={relativeDateTimeMenuAnchorElement}
anchorEl={relativeDateTimeMenuAnchorElement}
transformOrigin={{horizontal: "left", vertical: "top"}}
onClose={closeRelativeDateTimeMenu}
>
{
field.type == QFieldType.DATE ?
<Box display="flex">
<Box>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 7, "DAYS")}>7 days ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 14, "DAYS")}>14 days ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 30, "DAYS")}>30 days ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 90, "DAYS")}>90 days ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 180, "DAYS")}>180 days ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 1, "YEARS")}>1 year ago</MenuItem>
</Box>
<Box>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "THIS", "DAYS")}>today</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "LAST", "DAYS")}>yesterday</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "THIS", "WEEKS")}>start of this week</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "LAST", "WEEKS")}>start of last week</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "THIS", "MONTHS")}>start of this month</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "LAST", "MONTHS")}>start of last month</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "THIS", "YEARS")}>start of this year</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "LAST", "YEARS")}>start of last year</MenuItem>
</Box>
</Box>
:
<Box display="flex">
<Box>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 1, "HOURS")}>1 hour ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 12, "HOURS")}>12 hours ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 24, "HOURS")}>24 hours ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 7, "DAYS")}>7 days ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 14, "DAYS")}>14 days ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 30, "DAYS")}>30 days ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 90, "DAYS")}>90 days ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 180, "DAYS")}>180 days ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 1, "YEARS")}>1 year ago</MenuItem>
</Box>
<Box>
<MenuItem onClick={() => setExpressionNow(valueIndex)}>now</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "THIS", "HOURS")}>start of this hour</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "LAST", "HOURS")}>start of last hour</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "THIS", "DAYS")}>start of today</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "LAST", "DAYS")}>start of yesterday</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "THIS", "WEEKS")}>start of this week</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "LAST", "WEEKS")}>start of last week</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "THIS", "MONTHS")}>start of this month</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "LAST", "MONTHS")}>start of last month</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "THIS", "YEARS")}>start of this year</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "LAST", "YEARS")}>start of last year</MenuItem>
</Box>
</Box>
}
</Menu>
</Box>
<Box>
<AdvancedDateTimeFilterValues type={field.type} expression={currentExpression} onSave={(expression: any) => saveNewDateTimeExpression(valueIndex, expression)} />
</Box>
</Box>;
}

View File

@ -343,24 +343,8 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria,
////////////////////////////////////////////////// //////////////////////////////////////////////////
// event handler for value field (of all types) // // event handler for value field (of all types) //
////////////////////////////////////////////////// //////////////////////////////////////////////////
const handleValueChange = (event: React.ChangeEvent | SyntheticEvent, valueIndex: number | "all" = 0, newValue?: any, newExpression?: any) => const handleValueChange = (event: React.ChangeEvent | SyntheticEvent, valueIndex: number | "all" = 0, newValue?: 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 // @ts-ignore
const value = newValue !== undefined ? newValue : event ? event.target.value : null; const value = newValue !== undefined ? newValue : event ? event.target.value : null;
@ -471,15 +455,9 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria,
// don't need to look at values // // don't need to look at values //
////////////////////////////////// //////////////////////////////////
} }
else if (criteria.expression) else if(operatorSelectedValue.valueMode == ValueMode.DOUBLE || operatorSelectedValue.valueMode == ValueMode.DOUBLE_DATE || operatorSelectedValue.valueMode == ValueMode.DOUBLE_DATE_TIME)
{ {
//////////////////////////////////////////////////////// if(criteria.values.length < 2 || isNotSet(criteria.values[0]) || isNotSet(criteria.values[1]))
// if there's an expression - let's assume it's valid //
////////////////////////////////////////////////////////
}
else if(operatorSelectedValue.valueMode == ValueMode.DOUBLE)
{
if(criteria.values.length < 2)
{ {
criteriaIsValid = false; criteriaIsValid = false;
criteriaStatusTooltip = "You must enter two values to complete the definition of this condition."; criteriaStatusTooltip = "You must enter two values to complete the definition of this condition.";
@ -563,7 +541,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria,
criteria={{id: id, ...criteria}} criteria={{id: id, ...criteria}}
field={field} field={field}
table={fieldTable} table={fieldTable}
valueChangeHandler={(event, valueIndex, newValue, newExpression) => handleValueChange(event, valueIndex, newValue, newExpression)} valueChangeHandler={(event, valueIndex, newValue) => handleValueChange(event, valueIndex, newValue)}
/> />
</Box> </Box>
<Box display="inline-block" pl={0.5} pr={1}> <Box display="inline-block" pl={0.5} pr={1}>

View File

@ -23,21 +23,15 @@
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 {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 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 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 TextField from "@mui/material/TextField";
import Tooltip from "@mui/material/Tooltip"; import React, {SyntheticEvent, useReducer} from "react";
import React, {ReactNode, SyntheticEvent, useReducer, useState} from "react";
import DynamicSelect from "qqq/components/forms/DynamicSelect"; import DynamicSelect from "qqq/components/forms/DynamicSelect";
import AdvancedDateTimeFilterValues from "qqq/components/query/AdvancedDateTimeFilterValues"; import CriteriaDateField from "qqq/components/query/CriteriaDateField";
import {QFilterCriteriaWithId} from "qqq/components/query/CustomFilterPanel"; import {QFilterCriteriaWithId} from "qqq/components/query/CustomFilterPanel";
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";
@ -49,25 +43,13 @@ interface Props
criteria: QFilterCriteriaWithId; criteria: QFilterCriteriaWithId;
field: QFieldMetaData; field: QFieldMetaData;
table: QTableMetaData; table: QTableMetaData;
valueChangeHandler: (event: React.ChangeEvent | SyntheticEvent, valueIndex?: number | "all", newValue?: any, newExpression?: any) => void; valueChangeHandler: (event: React.ChangeEvent | SyntheticEvent, valueIndex?: number | "all", newValue?: any) => void;
} }
FilterCriteriaRowValues.defaultProps = { FilterCriteriaRowValues.defaultProps = {};
};
function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueChangeHandler}: Props): JSX.Element export const getTypeForTextField = (field: QFieldMetaData): string =>
{ {
const [relativeDateTimeMenuAnchorElement, setRelativeDateTimeMenuAnchorElement] = useState(null);
const [, forceUpdate] = useReducer((x) => x + 1, 0);
if (!operatorOption)
{
return <br />;
}
const getTypeForTextField = (): string =>
{
let type = "search"; let type = "search";
if (field.type == QFieldType.INTEGER) if (field.type == QFieldType.INTEGER)
@ -84,11 +66,11 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
} }
return (type); return (type);
}; };
const makeTextField = (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-") =>
{ {
let type = getTypeForTextField(); let type = getTypeForTextField(field);
const inputLabelProps: any = {}; const inputLabelProps: any = {};
if (field.type == QFieldType.DATE || field.type == QFieldType.DATE_TIME) if (field.type == QFieldType.DATE || field.type == QFieldType.DATE_TIME)
@ -129,132 +111,15 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
InputProps={inputProps} InputProps={inputProps}
fullWidth fullWidth
/>; />;
}; };
const makeDateTimeExpressionTextField = (expression: any, valueIndex: number = 0, label = "Value", idPrefix = "value-") => function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueChangeHandler}: Props): JSX.Element
{ {
const clearValue = (event: React.MouseEvent<HTMLAnchorElement> | React.MouseEvent<HTMLButtonElement>, index: number) => const [, forceUpdate] = useReducer((x) => x + 1, 0);
{
valueChangeHandler(event, index, "");
forceUpdate()
document.getElementById(`${idPrefix}${criteria.id}`).focus();
};
const inputProps: any = {}; if (!operatorOption)
inputProps.endAdornment = (
<InputAdornment position="end">
<IconButton sx={{visibility: expression ? "visible" : "hidden"}} onClick={(event) => clearValue(event, valueIndex)}>
<Icon>close</Icon>
</IconButton>
</InputAdornment>
);
let displayValue = expression.toString();
if (expression?.type == "ThisOrLastPeriod")
{ {
if(field.type == QFieldType.DATE_TIME || (field.type == QFieldType.DATE && expression.timeUnit != "DAYS")) return <br />;
{
displayValue = "start of " + displayValue;
}
}
return <TextField
id={`${idPrefix}${criteria.id}`}
label={label}
variant="standard"
autoComplete="off"
InputProps={{readOnly: true, unselectable: "off", ...inputProps}}
value={displayValue}
fullWidth
/>;
}
const makeDateField = (valueIndex: number = 0, label = "Value", idPrefix = "value-") =>
{
return <Box display="flex" alignItems="flex-end">
{
criteria.expression == null && makeTextField(valueIndex, label, idPrefix)
}
{
criteria.expression != null && makeDateTimeExpressionTextField(criteria.expression, valueIndex, label, idPrefix)
}
<Box>
<Tooltip title="Choose a common relative date expression" placement="top">
<Icon fontSize="small" color="info" sx={{mx: 0.25, cursor: "pointer"}} onClick={openRelativeDateTimeMenu}>date_range</Icon>
</Tooltip>
<Menu
open={relativeDateTimeMenuAnchorElement}
anchorEl={relativeDateTimeMenuAnchorElement}
transformOrigin={{horizontal: "center", vertical: "top"}}
onClose={closeRelativeDateTimeMenu}
>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 1, "DAYS")}>1 day ago</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "THIS", "DAYS")}>today</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "LAST", "DAYS")}>yesterday</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 7, "DAYS")}>7 days ago</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "THIS", "WEEKS")}>start of this week</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "LAST", "WEEKS")}>start of last week</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 14, "DAYS")}>14 days ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 30, "DAYS")}>30 days ago</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "THIS", "MONTHS")}>start of this month</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "LAST", "MONTHS")}>start of last month</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 90, "DAYS")}>90 days ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 180, "DAYS")}>180 days ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 1, "YEARS")}>1 year ago</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "THIS", "YEARS")}>start of this year</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "LAST", "YEARS")}>start of last year</MenuItem>
</Menu>
</Box>
<Box>
<AdvancedDateTimeFilterValues type={"date"} expression={criteria.expression} onSave={(expression: any) => saveNewDateTimeExpression(valueIndex, expression)} />
</Box>
</Box>;
}
const makeDateTimeField = (valueIndex: number = 0, label = "Value", idPrefix = "value-") =>
{
return <Box display="flex" alignItems="flex-end">
{
criteria.expression == null && makeTextField(valueIndex, label, idPrefix)
}
{
criteria.expression != null && makeDateTimeExpressionTextField(criteria.expression, valueIndex, label, idPrefix)
}
<Box>
<Tooltip title="Choose a common relative date-time expression" placement="top">
<Icon fontSize="small" color="info" sx={{mx: 0.25, cursor: "pointer"}} onClick={openRelativeDateTimeMenu}>date_range</Icon>
</Tooltip>
<Menu
open={relativeDateTimeMenuAnchorElement}
anchorEl={relativeDateTimeMenuAnchorElement}
transformOrigin={{horizontal: "center", vertical: "top"}}
onClose={closeRelativeDateTimeMenu}
>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 1, "HOURS")}>1 hour ago</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "THIS", "HOURS")}>start of this hour</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "LAST", "HOURS")}>start of last hour</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 12, "HOURS")}>12 hours ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 24, "HOURS")}>24 hours ago</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "THIS", "DAYS")}>start of today</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "LAST", "DAYS")}>start of yesterday</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 7, "DAYS")}>7 days ago</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "THIS", "WEEKS")}>start of this week</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "LAST", "WEEKS")}>start of last week</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 14, "DAYS")}>14 days ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 30, "DAYS")}>30 days ago</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "THIS", "MONTHS")}>start of this month</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "LAST", "MONTHS")}>start of last month</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 90, "DAYS")}>90 days ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 180, "DAYS")}>180 days ago</MenuItem>
<MenuItem onClick={() => setExpressionNowWithOffset(valueIndex, "MINUS", 1, "YEARS")}>1 year ago</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "THIS", "YEARS")}>start of this year</MenuItem>
<MenuItem onClick={() => setExpressionThisOrLastPeriod(valueIndex, "LAST", "YEARS")}>start of last year</MenuItem>
</Menu>
</Box>
<Box>
<AdvancedDateTimeFilterValues type={"datetime"} expression={criteria.expression} onSave={(expression: any) => saveNewDateTimeExpression(valueIndex, expression)} />
</Box>
</Box>;
} }
function saveNewPasterValues(newValues: any[]) function saveNewPasterValues(newValues: any[])
@ -280,69 +145,33 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
forceUpdate(); forceUpdate();
} }
const openRelativeDateTimeMenu = (event: React.MouseEvent<HTMLElement>) =>
{
setRelativeDateTimeMenuAnchorElement(event.currentTarget);
};
const closeRelativeDateTimeMenu = () =>
{
setRelativeDateTimeMenuAnchorElement(null);
};
const setExpressionNowWithOffset = (valueIndex: number, operator: NowWithOffsetOperator, amount: number, timeUnit: NowWithOffsetUnit) =>
{
const expression = new NowWithOffsetExpression()
expression.operator = operator;
expression.amount = amount;
expression.timeUnit = timeUnit;
saveNewDateTimeExpression(valueIndex, expression);
closeRelativeDateTimeMenu();
};
const setExpressionThisOrLastPeriod = (valueIndex: number, operator: ThisOrLastPeriodOperator, timeUnit: ThisOrLastPeriodUnit) =>
{
const expression = new ThisOrLastPeriodExpression()
expression.operator = operator;
expression.timeUnit = timeUnit;
saveNewDateTimeExpression(valueIndex, expression);
closeRelativeDateTimeMenu();
};
function saveNewDateTimeExpression(valueIndex: number, expression: any)
{
criteria.expression = expression;
criteria.values = null;
valueChangeHandler(null, valueIndex, null, expression);
forceUpdate();
}
switch (operatorOption.valueMode) switch (operatorOption.valueMode)
{ {
case ValueMode.NONE: case ValueMode.NONE:
return <br />; return <br />;
case ValueMode.SINGLE: case ValueMode.SINGLE:
return makeTextField(); return makeTextField(field, criteria, valueChangeHandler);
case ValueMode.SINGLE_DATE: case ValueMode.SINGLE_DATE:
return makeDateField(); return <CriteriaDateField field={field} valueChangeHandler={valueChangeHandler} criteria={criteria} />;
case ValueMode.DOUBLE_DATE: case ValueMode.DOUBLE_DATE:
return <Box> return <Box>
{makeDateField(0, "From", "from-")} <CriteriaDateField field={field} valueChangeHandler={valueChangeHandler} criteria={criteria} valueIndex={0} label="From" idPrefix="from-" />
{makeDateField(1, "To", "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 makeDateTimeField(); return <CriteriaDateField field={field} valueChangeHandler={valueChangeHandler} criteria={criteria} />;
case ValueMode.DOUBLE_DATE_TIME:
return <Box>
<CriteriaDateField field={field} valueChangeHandler={valueChangeHandler} criteria={criteria} valueIndex={0} label="From" idPrefix="from-" />
<CriteriaDateField field={field} valueChangeHandler={valueChangeHandler} criteria={criteria} valueIndex={1} label="To" idPrefix="to-" />
</Box>;
case ValueMode.DOUBLE: case ValueMode.DOUBLE:
return <Box> return <Box>
<Box width="50%" display="inline-block"> <Box width="50%" display="inline-block">
{ makeTextField(0, "From", "from-") } {makeTextField(field, criteria, valueChangeHandler, 0, "From", "from-")}
</Box> </Box>
<Box width="50%" display="inline-block"> <Box width="50%" display="inline-block">
{makeTextField(1, "To", "to-")} {makeTextField(field, criteria, valueChangeHandler, 1, "To", "to-")}
</Box> </Box>
</Box>; </Box>;
case ValueMode.MULTI: case ValueMode.MULTI:
@ -365,7 +194,7 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
onChange={(event, value) => valueChangeHandler(event, "all", value)} onChange={(event, value) => valueChangeHandler(event, "all", value)}
/> />
<Box> <Box>
<FilterCriteriaPaster type={getTypeForTextField()} onSave={(newValues: any[]) => saveNewPasterValues(newValues)} /> <FilterCriteriaPaster type={getTypeForTextField(field)} onSave={(newValues: any[]) => saveNewPasterValues(newValues)} />
</Box> </Box>
</Box>; </Box>;
case ValueMode.PVS_SINGLE: case ValueMode.PVS_SINGLE:
@ -414,7 +243,7 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC
inForm={false} inForm={false}
onChange={(value: any) => valueChangeHandler(null, "all", value)} onChange={(value: any) => valueChangeHandler(null, "all", value)}
/> />
</Box> </Box>;
} }
return (<br />); return (<br />);

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 {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";
import {QFilterCriteria} from "@kingsrook/qqq-frontend-core/lib/model/query/QFilterCriteria"; import {QFilterCriteria} from "@kingsrook/qqq-frontend-core/lib/model/query/QFilterCriteria";
@ -289,7 +290,7 @@ class FilterUtils
if (FilterUtils.gridCriteriaValueToExpression(param)) if (FilterUtils.gridCriteriaValueToExpression(param))
{ {
return (null); return (param);
} }
let rs = []; let rs = [];
@ -337,13 +338,8 @@ class FilterUtils
** Convert a filter field's value from the style that qqq uses, to the style that ** Convert a filter field's value from the style that qqq uses, to the style that
** the grid uses. ** the grid uses.
*******************************************************************************/ *******************************************************************************/
public static qqqCriteriaValuesToGrid = (operator: QCriteriaOperator, values: any[], expression: any, field: QFieldMetaData): any | any[] => public static qqqCriteriaValuesToGrid = (operator: QCriteriaOperator, values: any[], field: QFieldMetaData): any | any[] =>
{ {
if(expression)
{
return (expression);
}
const fieldType = field.type; const fieldType = field.type;
if (operator === QCriteriaOperator.IS_BLANK || operator === QCriteriaOperator.IS_NOT_BLANK) if (operator === QCriteriaOperator.IS_BLANK || operator === QCriteriaOperator.IS_NOT_BLANK)
{ {
@ -361,7 +357,13 @@ class FilterUtils
//////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////
if (fieldType === QFieldType.DATE_TIME) if (fieldType === QFieldType.DATE_TIME)
{ {
values[0] = ValueUtils.formatDateTimeValueForForm(values[0]); for(let i = 0; i<values.length; i++)
{
if(!values[i].type)
{
values[i] = ValueUtils.formatDateTimeValueForForm(values[i]);
}
}
} }
} }
@ -551,7 +553,7 @@ class FilterUtils
defaultFilter.items.push({ defaultFilter.items.push({
columnField: criteria.fieldName, columnField: criteria.fieldName,
operatorValue: FilterUtils.qqqCriteriaOperatorToGrid(criteria.operator, field, values), operatorValue: FilterUtils.qqqCriteriaOperatorToGrid(criteria.operator, field, values),
value: FilterUtils.qqqCriteriaValuesToGrid(criteria.operator, values, criteria.expression, field), value: FilterUtils.qqqCriteriaValuesToGrid(criteria.operator, values, field),
id: id++, // not sure what this id is!! id: id++, // not sure what this id is!!
}); });
} }
@ -637,7 +639,7 @@ class FilterUtils
const [field, fieldTable] = FilterUtils.getField(tableMetaData, criteria.fieldName); const [field, fieldTable] = FilterUtils.getField(tableMetaData, criteria.fieldName);
if (field) if (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)}); gridItems.push({columnField: criteria.fieldName, id: i, operatorValue: FilterUtils.qqqCriteriaOperatorToGrid(criteria.operator, field, criteria.values), value: FilterUtils.qqqCriteriaValuesToGrid(criteria.operator, criteria.values, field)});
} }
} }
@ -737,11 +739,6 @@ class FilterUtils
const operator = FilterUtils.gridCriteriaOperatorToQQQ(item.operatorValue); const operator = FilterUtils.gridCriteriaOperatorToQQQ(item.operatorValue);
const values = FilterUtils.gridCriteriaValueToQQQ(operator, item.value, item.operatorValue, fieldMetadata); const values = FilterUtils.gridCriteriaValueToQQQ(operator, item.value, item.operatorValue, fieldMetadata);
let criteria = 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); qFilter.addCriteria(criteria);
foundFilter = true; foundFilter = true;
}); });
@ -774,6 +771,10 @@ class FilterUtils
{ {
return (new NowWithOffsetExpression(value)); return (new NowWithOffsetExpression(value));
} }
else if (value.type && value.type == "Now")
{
return (new NowExpression(value));
}
else if (value.type && value.type == "ThisOrLastPeriod") else if (value.type && value.type == "ThisOrLastPeriod")
{ {
return (new ThisOrLastPeriodExpression(value)); return (new ThisOrLastPeriodExpression(value));