/* * QQQ - Low-code Application Framework for Engineers. * Copyright (C) 2021-2023. Kingsrook, LLC * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States * contact@kingsrook.com * https://github.com/Kingsrook/ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData"; import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType"; import {FilterVariableExpression} from "@kingsrook/qqq-frontend-core/lib/model/query/FilterVariableExpression"; import {NowExpression} from "@kingsrook/qqq-frontend-core/lib/model/query/NowExpression"; import {NowWithOffsetExpression, NowWithOffsetOperator, NowWithOffsetUnit} from "@kingsrook/qqq-frontend-core/lib/model/query/NowWithOffsetExpression"; import {ThisOrLastPeriodExpression, ThisOrLastPeriodOperator, ThisOrLastPeriodUnit} from "@kingsrook/qqq-frontend-core/lib/model/query/ThisOrLastPeriodExpression"; import Box from "@mui/material/Box"; import Divider from "@mui/material/Divider"; 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 {styled} from "@mui/material/styles"; import TextField from "@mui/material/TextField"; import Tooltip, {tooltipClasses, TooltipProps} from "@mui/material/Tooltip"; import AdvancedDateTimeFilterValues from "qqq/components/query/AdvancedDateTimeFilterValues"; import {QFilterCriteriaWithId} from "qqq/components/query/CustomFilterPanel"; import {EvaluatedExpression} from "qqq/components/query/EvaluatedExpression"; import {makeTextField} from "qqq/components/query/FilterCriteriaRowValues"; import React, {SyntheticEvent, useReducer, useState} from "react"; export type Expression = NowWithOffsetExpression | ThisOrLastPeriodExpression | NowExpression | FilterVariableExpression; interface CriteriaDateFieldProps { valueIndex: number; label: string; idPrefix: string; field: QFieldMetaData; criteria: QFilterCriteriaWithId; valueChangeHandler: (event: React.ChangeEvent | SyntheticEvent, valueIndex?: number | "all", newValue?: any) => void; allowVariables?: boolean; } CriteriaDateField.defaultProps = { valueIndex: 0, label: "Value", idPrefix: "value-" }; export const NoWrapTooltip = styled(({className, children, ...props}: TooltipProps) => ( {children} ))({ [`& .${tooltipClasses.tooltip}`]: { whiteSpace: "nowrap" }, }); export default function CriteriaDateField({valueIndex, label, idPrefix, field, criteria, valueChangeHandler, allowVariables}: CriteriaDateFieldProps): JSX.Element { const [relativeDateTimeOpen, setRelativeDateTimeOpen] = useState(false); const [relativeDateTimeMenuAnchorElement, setRelativeDateTimeMenuAnchorElement] = useState(null); const [forceAdvancedDateTimeDialogOpen, setForceAdvancedDateTimeDialogOpen] = useState(false); const [, forceUpdate] = useReducer((x) => x + 1, 0); const openRelativeDateTimeMenu = (event: React.MouseEvent) => { setRelativeDateTimeOpen(true); setRelativeDateTimeMenuAnchorElement(event.currentTarget); }; const closeRelativeDateTimeMenu = () => { setRelativeDateTimeOpen(false); setRelativeDateTimeMenuAnchorElement(null); }; const setExpression = (valueIndex: number, expression: Expression) => { 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 | React.MouseEvent, index: number) => { valueChangeHandler(event, index, ""); forceUpdate(); document.getElementById(`${idPrefix}${criteria.id}`).focus(); }; const inputProps: any = {}; inputProps.endAdornment = ( clearValue(event, valueIndex)}> close ); 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; } } if (expression?.type == "Now") { if (field.type == QFieldType.DATE) { displayValue = "today"; } } return } placement="bottom" enterDelay={1000} sx={{marginLeft: "-75px !important", marginTop: "-8px !important"}}>; }; const isExpression = criteria.values && criteria.values[valueIndex] && criteria.values[valueIndex].type; const currentExpression = isExpression ? criteria.values[valueIndex] : null; const tooltipMenuItemFromExpression = (valueIndex: number, tooltipPlacement: "left" | "right", expression: Expression) => { let startOfPrefix = ""; if (expression.type == "ThisOrLastPeriod") { if (field.type == QFieldType.DATE_TIME || expression.timeUnit != "DAYS") { startOfPrefix = "start of "; } } return } placement={tooltipPlacement}> setExpression(valueIndex, expression)}>{startOfPrefix}{expression.toString()} ; }; const newNowExpression = (): NowExpression => { const expression = new NowExpression(); return (expression); }; const newNowWithOffsetExpression = (operator: NowWithOffsetOperator, amount: number, timeUnit: NowWithOffsetUnit): NowWithOffsetExpression => { const expression = new NowWithOffsetExpression(); expression.operator = operator; expression.amount = amount; expression.timeUnit = timeUnit; return (expression); }; const newThisOrLastPeriodExpression = (operator: ThisOrLastPeriodOperator, timeUnit: ThisOrLastPeriodUnit): ThisOrLastPeriodExpression => { const expression = new ThisOrLastPeriodExpression(); expression.operator = operator; expression.timeUnit = timeUnit; return (expression); }; function doForceAdvancedDateTimeDialogOpen() { setForceAdvancedDateTimeDialogOpen(true); closeRelativeDateTimeMenu(); setTimeout(() => setForceAdvancedDateTimeDialogOpen(false), 100); } const makeFilterVariableTextField = (expression: FilterVariableExpression, valueIndex: number = 0, label = "Value", idPrefix = "value-") => { const clearValue = (event: React.MouseEvent | React.MouseEvent, index: number) => { valueChangeHandler(event, index, ""); document.getElementById(`${idPrefix}${criteria.id}`).focus(); }; const inputProps2: any = {}; inputProps2.endAdornment = ( clearValue(event, valueIndex)}> closer ); return } placement="bottom" enterDelay={1000} sx={{marginLeft: "-75px !important", marginTop: "-8px !important"}}>; }; return { isExpression ? currentExpression?.type == "FilterVariableExpression" ? ( makeFilterVariableTextField(criteria.values[valueIndex], valueIndex, label, idPrefix) ) : ( makeDateTimeExpressionTextField(criteria.values[valueIndex], valueIndex, label, idPrefix) ) : makeTextField(field, criteria, valueChangeHandler, valueIndex, label, idPrefix, allowVariables) } { (!isExpression || currentExpression?.type != "FilterVariableExpression") && ( <> date_range {field.type == QFieldType.DATE ? {tooltipMenuItemFromExpression(valueIndex, "left", newNowWithOffsetExpression("MINUS", 7, "DAYS"))} {tooltipMenuItemFromExpression(valueIndex, "left", newNowWithOffsetExpression("MINUS", 14, "DAYS"))} {tooltipMenuItemFromExpression(valueIndex, "left", newNowWithOffsetExpression("MINUS", 30, "DAYS"))} {tooltipMenuItemFromExpression(valueIndex, "left", newNowWithOffsetExpression("MINUS", 90, "DAYS"))} {tooltipMenuItemFromExpression(valueIndex, "left", newNowWithOffsetExpression("MINUS", 180, "DAYS"))} {tooltipMenuItemFromExpression(valueIndex, "left", newNowWithOffsetExpression("MINUS", 1, "YEARS"))} Custom {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("THIS", "DAYS"))} {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("LAST", "DAYS"))} {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("THIS", "WEEKS"))} {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("LAST", "WEEKS"))} {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("THIS", "MONTHS"))} {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("LAST", "MONTHS"))} {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("THIS", "YEARS"))} {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("LAST", "YEARS"))} : {tooltipMenuItemFromExpression(valueIndex, "left", newNowWithOffsetExpression("MINUS", 1, "HOURS"))} {tooltipMenuItemFromExpression(valueIndex, "left", newNowWithOffsetExpression("MINUS", 12, "HOURS"))} {tooltipMenuItemFromExpression(valueIndex, "left", newNowWithOffsetExpression("MINUS", 24, "HOURS"))} {tooltipMenuItemFromExpression(valueIndex, "left", newNowWithOffsetExpression("MINUS", 7, "DAYS"))} {tooltipMenuItemFromExpression(valueIndex, "left", newNowWithOffsetExpression("MINUS", 14, "DAYS"))} {tooltipMenuItemFromExpression(valueIndex, "left", newNowWithOffsetExpression("MINUS", 30, "DAYS"))} {tooltipMenuItemFromExpression(valueIndex, "left", newNowWithOffsetExpression("MINUS", 90, "DAYS"))} {tooltipMenuItemFromExpression(valueIndex, "left", newNowWithOffsetExpression("MINUS", 180, "DAYS"))} {tooltipMenuItemFromExpression(valueIndex, "left", newNowWithOffsetExpression("MINUS", 1, "YEARS"))} Custom {tooltipMenuItemFromExpression(valueIndex, "right", newNowExpression())} {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("THIS", "HOURS"))} {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("LAST", "HOURS"))} {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("THIS", "DAYS"))} {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("LAST", "DAYS"))} {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("THIS", "WEEKS"))} {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("LAST", "WEEKS"))} {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("THIS", "MONTHS"))} {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("LAST", "MONTHS"))} {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("THIS", "YEARS"))} {tooltipMenuItemFromExpression(valueIndex, "right", newThisOrLastPeriodExpression("LAST", "YEARS"))} } saveNewDateTimeExpression(valueIndex, expression)} forcedOpen={forceAdvancedDateTimeDialogOpen} /> ) } ; }