/*
* 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}>
;
};
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
saveNewDateTimeExpression(valueIndex, expression)} forcedOpen={forceAdvancedDateTimeDialogOpen} />
>
)
}
;
}