diff --git a/src/qqq/components/forms/DynamicForm.tsx b/src/qqq/components/forms/DynamicForm.tsx index 663d210..f95d42d 100644 --- a/src/qqq/components/forms/DynamicForm.tsx +++ b/src/qqq/components/forms/DynamicForm.tsx @@ -174,7 +174,8 @@ function QDynamicForm({formData, formLabel, bulkEditMode, bulkEditSwitchChangeHa { // console.log(`doing a search with ${searchTerm}`); - const results: QPossibleValue[] = await qController.possibleValues(tableName, processName, fieldName ?? possibleValueSourceName, searchTerm ?? "", null, otherValues); + const results: QPossibleValue[] = await qController.possibleValues(tableName, processName, possibleValueSourceName ?? fieldName, searchTerm ?? "", null, otherValues); if(tableMetaData == null && tableName) { @@ -231,7 +227,7 @@ function DynamicSelect({tableName, processName, fieldName, possibleValueSourceNa setLoading(true); setOptions([]); console.log("Refreshing possible values..."); - const results: QPossibleValue[] = await qController.possibleValues(tableName, processName, fieldName ?? possibleValueSourceName, searchTerm ?? "", null, otherValues); + const results: QPossibleValue[] = await qController.possibleValues(tableName, processName, possibleValueSourceName ?? fieldName, searchTerm ?? "", null, otherValues); setLoading(false); setOptions([ ...results ]); setOtherValuesWhenResultsWereLoaded(JSON.stringify(Object.fromEntries(otherValues))); diff --git a/src/qqq/components/query/AssignFilterVariable.tsx b/src/qqq/components/query/AssignFilterVariable.tsx index 642f157..0be134f 100644 --- a/src/qqq/components/query/AssignFilterVariable.tsx +++ b/src/qqq/components/query/AssignFilterVariable.tsx @@ -57,7 +57,7 @@ export default function AssignFilterVariable({valueIndex, field, valueChangeHand return - + functions diff --git a/src/qqq/components/query/CriteriaDateField.tsx b/src/qqq/components/query/CriteriaDateField.tsx index 1c61f5d..5782a5c 100644 --- a/src/qqq/components/query/CriteriaDateField.tsx +++ b/src/qqq/components/query/CriteriaDateField.tsx @@ -196,84 +196,120 @@ export default function CriteriaDateField({valueIndex, label, idPrefix, field, c 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 ? makeDateTimeExpressionTextField(criteria.values[valueIndex], valueIndex, label, idPrefix) + 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) } - - - 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 - + { + (!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, "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} /> - + : + + + {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} /> + + ) + } ; } diff --git a/src/qqq/components/query/CustomFilterPanel.tsx b/src/qqq/components/query/CustomFilterPanel.tsx index 03dfee3..10e48f2 100644 --- a/src/qqq/components/query/CustomFilterPanel.tsx +++ b/src/qqq/components/query/CustomFilterPanel.tsx @@ -28,8 +28,8 @@ import Box from "@mui/material/Box"; import Button from "@mui/material/Button/Button"; import Icon from "@mui/material/Icon/Icon"; import {GridFilterPanelProps, GridSlotsComponentsProps} from "@mui/x-data-grid-pro"; -import React, {forwardRef, useReducer} from "react"; import {FilterCriteriaRow, getDefaultCriteriaValue} from "qqq/components/query/FilterCriteriaRow"; +import React, {forwardRef, useReducer} from "react"; declare module "@mui/x-data-grid" @@ -49,7 +49,7 @@ declare module "@mui/x-data-grid" export class QFilterCriteriaWithId extends QFilterCriteria { - id: number + id: number; } @@ -62,6 +62,7 @@ export const CustomFilterPanel = forwardRef( const [, forceUpdate] = useReducer((x) => x + 1, 0); const queryFilter = props.queryFilter; + // console.log(`CustomFilterPanel: filter: ${JSON.stringify(queryFilter)}`); function focusLastField() @@ -124,7 +125,7 @@ export const CustomFilterPanel = forwardRef( } } - if(queryFilter.criteria.length == 1 && !queryFilter.criteria[0].fieldName) + if (queryFilter.criteria.length == 1 && !queryFilter.criteria[0].fieldName) { focusLastField(); } @@ -142,7 +143,7 @@ export const CustomFilterPanel = forwardRef( { queryFilter.criteria[index] = newCriteria; - clearTimeout(debounceTimeout) + clearTimeout(debounceTimeout); debounceTimeout = setTimeout(() => props.updateFilter(queryFilter), needDebounce ? 500 : 1); forceUpdate(); @@ -178,6 +179,7 @@ export const CustomFilterPanel = forwardRef( updateCriteria={(newCriteria, needDebounce) => updateCriteria(newCriteria, index, needDebounce)} removeCriteria={() => removeCriteria(index)} updateBooleanOperator={(newValue) => updateBooleanOperator(newValue)} + queryScreenUsage={props.queryScreenUsage} /> {/*JSON.stringify(criteria)*/} diff --git a/src/qqq/components/query/FilterCriteriaRow.tsx b/src/qqq/components/query/FilterCriteriaRow.tsx index 2f82085..2b4ad63 100644 --- a/src/qqq/components/query/FilterCriteriaRow.tsx +++ b/src/qqq/components/query/FilterCriteriaRow.tsx @@ -35,6 +35,7 @@ import TextField from "@mui/material/TextField"; import Tooltip from "@mui/material/Tooltip"; import FieldAutoComplete from "qqq/components/misc/FieldAutoComplete"; import FilterCriteriaRowValues from "qqq/components/query/FilterCriteriaRowValues"; +import {QueryScreenUsage} from "qqq/pages/records/query/RecordQuery"; import FilterUtils from "qqq/utils/qqq/FilterUtils"; import React, {ReactNode, SyntheticEvent, useState} from "react"; @@ -197,6 +198,7 @@ interface FilterCriteriaRowProps updateCriteria: (newCriteria: QFilterCriteria, needDebounce: boolean) => void; removeCriteria: () => void; updateBooleanOperator: (newValue: string) => void; + queryScreenUsage?: QueryScreenUsage; } FilterCriteriaRow.defaultProps = @@ -265,7 +267,7 @@ export function validateCriteria(criteria: QFilterCriteria, operatorSelectedValu return {criteriaIsValid, criteriaStatusTooltip}; } -export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, booleanOperator, updateCriteria, removeCriteria, updateBooleanOperator}: FilterCriteriaRowProps): JSX.Element +export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, booleanOperator, updateCriteria, removeCriteria, updateBooleanOperator, queryScreenUsage}: FilterCriteriaRowProps): JSX.Element { // console.log(`FilterCriteriaRow: criteria: ${JSON.stringify(criteria)}`); const [operatorSelectedValue, setOperatorSelectedValue] = useState(null as OperatorOption); @@ -513,6 +515,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, field={field} table={fieldTable} valueChangeHandler={(event, valueIndex, newValue) => handleValueChange(event, valueIndex, newValue)} + queryScreenUsage={queryScreenUsage} /> diff --git a/src/qqq/components/query/FilterCriteriaRowValues.tsx b/src/qqq/components/query/FilterCriteriaRowValues.tsx index 5e5a653..2588cd4 100644 --- a/src/qqq/components/query/FilterCriteriaRowValues.tsx +++ b/src/qqq/components/query/FilterCriteriaRowValues.tsx @@ -220,6 +220,8 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC forceUpdate(); } + const isExpression = criteria.values && criteria.values[0] && criteria.values[0].type; + switch (operatorOption.valueMode) { case ValueMode.NONE: @@ -227,18 +229,18 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC case ValueMode.SINGLE: return makeTextField(field, criteria, valueChangeHandler, 0, undefined, undefined, allowVariables); case ValueMode.SINGLE_DATE: - return ; + return ; case ValueMode.DOUBLE_DATE: return - - + + ; case ValueMode.SINGLE_DATE_TIME: return ; case ValueMode.DOUBLE_DATE_TIME: return - - + + ; case ValueMode.DOUBLE: return @@ -279,19 +281,30 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC { selectedPossibleValue = criteria.values[0]; } - return - valueChangeHandler(null, 0, value)} - variant="standard" - /> + return + { + isExpression ? ( + makeTextField(field, criteria, valueChangeHandler, 0, undefined, undefined, allowVariables) + ) : ( + + valueChangeHandler(null, 0, value)} + variant="standard" + /> + + ) + } + { + allowVariables && !isExpression && + } ; case ValueMode.PVS_MULTI: console.log("Doing pvs multi: " + criteria.values); @@ -307,20 +320,31 @@ function FilterCriteriaRowValues({operatorOption, criteria, field, table, valueC initialValues = criteria.values; } } - return - valueChangeHandler(null, "all", value)} - variant="standard" - /> + return + { + isExpression ? ( + makeTextField(field, criteria, valueChangeHandler, 0, undefined, undefined, allowVariables) + ) : ( + + valueChangeHandler(null, "all", value)} + variant="standard" + /> + + ) + } + { + allowVariables && !isExpression && + } ; } diff --git a/src/qqq/components/widgets/misc/DynamicFormWidget.tsx b/src/qqq/components/widgets/misc/DynamicFormWidget.tsx index e06e31a..c0f1a3c 100644 --- a/src/qqq/components/widgets/misc/DynamicFormWidget.tsx +++ b/src/qqq/components/widgets/misc/DynamicFormWidget.tsx @@ -99,9 +99,10 @@ export default function DynamicFormWidget({isEditable, widgetMetaData, widgetDat if(newFields.length > 0) { + const recordOfFieldValues = widgetData.recordOfFieldValues ? new QRecord(widgetData.recordOfFieldValues) : null; const {dynamicFormFields: newDynamicFormFields, formValidations: newFormValidations} = DynamicFormUtils.getFormData(newFields); const defaultDisplayValues = new Map(); // todo - seems not right? - DynamicFormUtils.addPossibleValueProps(newDynamicFormFields, newFields, recordValues.tableName, null, record ? record.displayValues : defaultDisplayValues); + DynamicFormUtils.addPossibleValueProps(newDynamicFormFields, newFields, recordValues.tableName, null, recordOfFieldValues ? recordOfFieldValues.displayValues : defaultDisplayValues); setDynamicFormFields(newDynamicFormFields) setFormValidations(newFormValidations) } @@ -226,7 +227,7 @@ export default function DynamicFormWidget({isEditable, widgetMetaData, widgetDat { const fieldNames: string[] = []; const fieldMap: {[name: string]: QFieldMetaData} = {}; - const fakeRecord = new QRecord({}); + const fakeRecord = new QRecord(widgetData.recordOfFieldValues ?? {}); const mergedDynamicFormValuesIntoFieldName = widgetData.mergedDynamicFormValuesIntoFieldName; diff --git a/src/qqq/pages/records/query/RecordQuery.tsx b/src/qqq/pages/records/query/RecordQuery.tsx index 25609ec..2b57895 100644 --- a/src/qqq/pages/records/query/RecordQuery.tsx +++ b/src/qqq/pages/records/query/RecordQuery.tsx @@ -900,6 +900,26 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init return; } + ///////////////////////////////////////////////////////////////////////////////////////////////// + // if any values in the query are of type "FilterVariableExpression", display an error showing // + // that a backend query cannot be made because of missing values for that expression // + ///////////////////////////////////////////////////////////////////////////////////////////////// + setWarningAlert(null); + for (var i = 0; i < queryFilter?.criteria.length; i++) + { + for (var j = 0; j < queryFilter?.criteria[i].values.length; j++) + { + const value = queryFilter.criteria[i].values[j]; + if (value?.type == "FilterVariableExpression") + { + setWarningAlert("Cannot perform query because of a missing value for a variable."); + setLoading(false); + setRows([]); + return; + } + } + } + recordAnalytics({category: "tableEvents", action: "query", label: tableMetaData.label}); console.log(`In updateTable for ${reason} ${JSON.stringify(queryFilter)}`); @@ -2888,6 +2908,7 @@ const RecordQuery = forwardRef(({table, usage, isModal, initialQueryFilter, init filterPanel: { tableMetaData: tableMetaData, + queryScreenUsage: usage, metaData: metaData, queryFilter: queryFilter, updateFilter: doSetQueryFilter, diff --git a/src/qqq/styles/qqq-override-styles.css b/src/qqq/styles/qqq-override-styles.css index 8a16e82..d786c36 100644 --- a/src/qqq/styles/qqq-override-styles.css +++ b/src/qqq/styles/qqq-override-styles.css @@ -693,6 +693,11 @@ input[type="search"]::-webkit-search-results-decoration padding: 24px; } +.entityForm .widget +{ + padding: 24px; +} + .recordView .widget .recordGridWidget { margin: -8px;