mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-17 21:00:45 +00:00
CE-793 - Rename as .tsx; remove functions that worked with GridFilter; add some functions for human-strings & JSON processing;
This commit is contained in:
@ -1,849 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. 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 {QController} from "@kingsrook/qqq-frontend-core/lib/controllers/QController";
|
||||
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 {NowExpression} from "@kingsrook/qqq-frontend-core/lib/model/query/NowExpression";
|
||||
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 TableUtils from "qqq/utils/qqq/TableUtils";
|
||||
import ValueUtils from "qqq/utils/qqq/ValueUtils";
|
||||
|
||||
const CURRENT_SAVED_FILTER_ID_LOCAL_STORAGE_KEY_ROOT = "qqq.currentSavedFilterId";
|
||||
|
||||
/*******************************************************************************
|
||||
** Utility class for working with QQQ Filters
|
||||
**
|
||||
*******************************************************************************/
|
||||
class FilterUtils
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Convert a grid operator to a QQQ Criteria Operator.
|
||||
*******************************************************************************/
|
||||
public static gridCriteriaOperatorToQQQ = (operator: string): QCriteriaOperator =>
|
||||
{
|
||||
switch (operator)
|
||||
{
|
||||
case "contains":
|
||||
return QCriteriaOperator.CONTAINS;
|
||||
case "notContains":
|
||||
return QCriteriaOperator.NOT_CONTAINS;
|
||||
case "startsWith":
|
||||
return QCriteriaOperator.STARTS_WITH;
|
||||
case "notStartsWith":
|
||||
return QCriteriaOperator.NOT_STARTS_WITH;
|
||||
case "endsWith":
|
||||
return QCriteriaOperator.ENDS_WITH;
|
||||
case "notEndsWith":
|
||||
return QCriteriaOperator.NOT_ENDS_WITH;
|
||||
case "is":
|
||||
case "equals":
|
||||
case "=":
|
||||
case "isTrue":
|
||||
case "isFalse":
|
||||
return QCriteriaOperator.EQUALS;
|
||||
case "isNot":
|
||||
case "!=":
|
||||
return QCriteriaOperator.NOT_EQUALS_OR_IS_NULL;
|
||||
case "after":
|
||||
case ">":
|
||||
return QCriteriaOperator.GREATER_THAN;
|
||||
case "onOrAfter":
|
||||
case ">=":
|
||||
return QCriteriaOperator.GREATER_THAN_OR_EQUALS;
|
||||
case "before":
|
||||
case "<":
|
||||
return QCriteriaOperator.LESS_THAN;
|
||||
case "onOrBefore":
|
||||
case "<=":
|
||||
return QCriteriaOperator.LESS_THAN_OR_EQUALS;
|
||||
case "isEmpty":
|
||||
return QCriteriaOperator.IS_BLANK;
|
||||
case "isNotEmpty":
|
||||
return QCriteriaOperator.IS_NOT_BLANK;
|
||||
case "isAnyOf":
|
||||
return QCriteriaOperator.IN;
|
||||
case "isNone":
|
||||
return QCriteriaOperator.NOT_IN;
|
||||
case "between":
|
||||
return QCriteriaOperator.BETWEEN;
|
||||
case "notBetween":
|
||||
return QCriteriaOperator.NOT_BETWEEN;
|
||||
default:
|
||||
return QCriteriaOperator.EQUALS;
|
||||
}
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
** Convert a qqq criteria operator to one expected by the grid.
|
||||
*******************************************************************************/
|
||||
public static qqqCriteriaOperatorToGrid = (operator: QCriteriaOperator, field: QFieldMetaData, criteriaValues: any[]): string =>
|
||||
{
|
||||
const fieldType = field.type;
|
||||
switch (operator)
|
||||
{
|
||||
case QCriteriaOperator.EQUALS:
|
||||
|
||||
if (field.possibleValueSourceName)
|
||||
{
|
||||
return ("is");
|
||||
}
|
||||
|
||||
switch (fieldType)
|
||||
{
|
||||
case QFieldType.INTEGER:
|
||||
case QFieldType.DECIMAL:
|
||||
return ("=");
|
||||
case QFieldType.DATE:
|
||||
case QFieldType.TIME:
|
||||
case QFieldType.DATE_TIME:
|
||||
case QFieldType.STRING:
|
||||
case QFieldType.TEXT:
|
||||
case QFieldType.HTML:
|
||||
case QFieldType.PASSWORD:
|
||||
case QFieldType.BLOB:
|
||||
return ("equals");
|
||||
case QFieldType.BOOLEAN:
|
||||
if (criteriaValues && criteriaValues[0] === true)
|
||||
{
|
||||
return ("isTrue");
|
||||
}
|
||||
else if (criteriaValues && criteriaValues[0] === false)
|
||||
{
|
||||
return ("isFalse");
|
||||
}
|
||||
return ("is");
|
||||
default:
|
||||
return ("is");
|
||||
}
|
||||
case QCriteriaOperator.NOT_EQUALS:
|
||||
case QCriteriaOperator.NOT_EQUALS_OR_IS_NULL:
|
||||
|
||||
if (field.possibleValueSourceName)
|
||||
{
|
||||
return ("isNot");
|
||||
}
|
||||
|
||||
switch (fieldType)
|
||||
{
|
||||
case QFieldType.INTEGER:
|
||||
case QFieldType.DECIMAL:
|
||||
return ("!=");
|
||||
case QFieldType.DATE:
|
||||
case QFieldType.TIME:
|
||||
case QFieldType.DATE_TIME:
|
||||
case QFieldType.BOOLEAN:
|
||||
case QFieldType.STRING:
|
||||
case QFieldType.TEXT:
|
||||
case QFieldType.HTML:
|
||||
case QFieldType.PASSWORD:
|
||||
case QFieldType.BLOB:
|
||||
default:
|
||||
return ("isNot");
|
||||
}
|
||||
case QCriteriaOperator.IN:
|
||||
return ("isAnyOf");
|
||||
case QCriteriaOperator.NOT_IN:
|
||||
return ("isNone");
|
||||
case QCriteriaOperator.STARTS_WITH:
|
||||
return ("startsWith");
|
||||
case QCriteriaOperator.ENDS_WITH:
|
||||
return ("endsWith");
|
||||
case QCriteriaOperator.CONTAINS:
|
||||
return ("contains");
|
||||
case QCriteriaOperator.NOT_STARTS_WITH:
|
||||
return ("notStartsWith");
|
||||
case QCriteriaOperator.NOT_ENDS_WITH:
|
||||
return ("notEndsWith");
|
||||
case QCriteriaOperator.NOT_CONTAINS:
|
||||
return ("notContains");
|
||||
case QCriteriaOperator.LESS_THAN:
|
||||
switch (fieldType)
|
||||
{
|
||||
case QFieldType.DATE:
|
||||
case QFieldType.TIME:
|
||||
case QFieldType.DATE_TIME:
|
||||
return ("before");
|
||||
default:
|
||||
return ("<");
|
||||
}
|
||||
case QCriteriaOperator.LESS_THAN_OR_EQUALS:
|
||||
switch (fieldType)
|
||||
{
|
||||
case QFieldType.DATE:
|
||||
case QFieldType.TIME:
|
||||
case QFieldType.DATE_TIME:
|
||||
return ("onOrBefore");
|
||||
default:
|
||||
return ("<=");
|
||||
}
|
||||
case QCriteriaOperator.GREATER_THAN:
|
||||
switch (fieldType)
|
||||
{
|
||||
case QFieldType.DATE:
|
||||
case QFieldType.TIME:
|
||||
case QFieldType.DATE_TIME:
|
||||
return ("after");
|
||||
default:
|
||||
return (">");
|
||||
}
|
||||
case QCriteriaOperator.GREATER_THAN_OR_EQUALS:
|
||||
switch (fieldType)
|
||||
{
|
||||
case QFieldType.DATE:
|
||||
case QFieldType.TIME:
|
||||
case QFieldType.DATE_TIME:
|
||||
return ("onOrAfter");
|
||||
default:
|
||||
return (">=");
|
||||
}
|
||||
case QCriteriaOperator.IS_BLANK:
|
||||
return ("isEmpty");
|
||||
case QCriteriaOperator.IS_NOT_BLANK:
|
||||
return ("isNotEmpty");
|
||||
case QCriteriaOperator.BETWEEN:
|
||||
return ("between");
|
||||
case QCriteriaOperator.NOT_BETWEEN:
|
||||
return ("notBetween");
|
||||
default:
|
||||
console.warn(`Unhandled criteria operator: ${operator}`);
|
||||
return ("=");
|
||||
}
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
** the values object needs handled differently based on cardinality of the operator.
|
||||
** that is - qqq always wants a list, but the grid provides it differently per-operator.
|
||||
** for single-values (the default), we must wrap it in an array.
|
||||
** for non-values (e.g., blank), set it to null.
|
||||
** for list-values, it's already in an array, so don't wrap it.
|
||||
*******************************************************************************/
|
||||
public static gridCriteriaValueToQQQ = (operator: QCriteriaOperator, value: any, gridOperatorValue: string, fieldMetaData: QFieldMetaData): any[] =>
|
||||
{
|
||||
if (gridOperatorValue === "isTrue")
|
||||
{
|
||||
return [true];
|
||||
}
|
||||
else if (gridOperatorValue === "isFalse")
|
||||
{
|
||||
return [false];
|
||||
}
|
||||
|
||||
if (operator === QCriteriaOperator.IS_BLANK || operator === QCriteriaOperator.IS_NOT_BLANK)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
else if (operator === QCriteriaOperator.IN || operator === QCriteriaOperator.NOT_IN || operator === QCriteriaOperator.BETWEEN || operator === QCriteriaOperator.NOT_BETWEEN)
|
||||
{
|
||||
if ((value == null || value.length < 2) && (operator === QCriteriaOperator.BETWEEN || operator === QCriteriaOperator.NOT_BETWEEN))
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if we send back null, we get a 500 - bad look every time you try to set up a BETWEEN filter //
|
||||
// but array of 2 nulls? comes up sunshine. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
return ([null, null]);
|
||||
}
|
||||
return (FilterUtils.cleanseCriteriaValueForQQQ(value, fieldMetaData));
|
||||
}
|
||||
|
||||
return (FilterUtils.cleanseCriteriaValueForQQQ([value], fieldMetaData));
|
||||
};
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Helper method - take a list of values, which may be possible values, and
|
||||
** either return the original list, or a new list that is just the ids of the
|
||||
** possible values (if it was a list of possible values).
|
||||
**
|
||||
** Or, if the values are date-times, convert them to UTC.
|
||||
*******************************************************************************/
|
||||
private static cleanseCriteriaValueForQQQ = (param: any[], fieldMetaData: QFieldMetaData): number[] | string[] =>
|
||||
{
|
||||
if (param === null || param === undefined)
|
||||
{
|
||||
return (param);
|
||||
}
|
||||
|
||||
if (FilterUtils.gridCriteriaValueToExpression(param))
|
||||
{
|
||||
return (param);
|
||||
}
|
||||
|
||||
let rs = [];
|
||||
for (let i = 0; i < param.length; i++)
|
||||
{
|
||||
console.log(param[i]);
|
||||
if (param[i] && param[i].id && param[i].label)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if the param looks like a possible value, return its id //
|
||||
// during build of new custom filter panel, this ended up causing us //
|
||||
// problems (because we wanted the full PV object in the filter model for the frontend) //
|
||||
// so, we can keep the PV as-is here, and see calls to convertFilterPossibleValuesToIds //
|
||||
// to do what this used to do. //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// rs.push(param[i].id);
|
||||
rs.push(param[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fieldMetaData?.type == QFieldType.DATE_TIME)
|
||||
{
|
||||
try
|
||||
{
|
||||
let toPush = ValueUtils.frontendLocalZoneDateTimeStringToUTCStringForBackend(param[i]);
|
||||
rs.push(toPush);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
console.log("Error converting date-time to UTC: ", e);
|
||||
rs.push(param[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rs.push(param[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (rs);
|
||||
};
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** 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[] =>
|
||||
{
|
||||
const fieldType = field.type;
|
||||
if (operator === QCriteriaOperator.IS_BLANK || operator === QCriteriaOperator.IS_NOT_BLANK)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (operator === QCriteriaOperator.IN || operator === QCriteriaOperator.NOT_IN || operator === QCriteriaOperator.BETWEEN || operator === QCriteriaOperator.NOT_BETWEEN)
|
||||
{
|
||||
return (values);
|
||||
}
|
||||
|
||||
if (values && values.length > 0)
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// make sure dates are formatted for the grid the way it expects - not the way we pass it in. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if (fieldType === QFieldType.DATE_TIME)
|
||||
{
|
||||
for (let i = 0; i < values.length; i++)
|
||||
{
|
||||
if (!values[i].type)
|
||||
{
|
||||
values[i] = ValueUtils.formatDateTimeValueForForm(values[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (values ? values[0] : "");
|
||||
};
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Get the default filter to use on the page - either from given filter string, query string param, or
|
||||
** local storage, or a default (empty).
|
||||
*******************************************************************************/
|
||||
public static async determineFilterAndSortModels(qController: QController, tableMetaData: QTableMetaData, filterString: string, searchParams: URLSearchParams, filterLocalStorageKey: string, sortLocalStorageKey: string): Promise<{ filter: GridFilterModel, sort: GridSortItem[], warning: string }>
|
||||
{
|
||||
let defaultFilter = {items: []} as GridFilterModel;
|
||||
let defaultSort = [] as GridSortItem[];
|
||||
let warningParts = [] as string[];
|
||||
|
||||
if (tableMetaData && tableMetaData.fields !== undefined)
|
||||
{
|
||||
if (filterString != null || (searchParams && searchParams.has("filter")))
|
||||
{
|
||||
try
|
||||
{
|
||||
const filterJSON = (filterString !== null) ? JSON.parse(filterString) : JSON.parse(searchParams.get("filter"));
|
||||
const qQueryFilter = filterJSON as QQueryFilter;
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// translate from a qqq-style filter to one that the grid wants //
|
||||
//////////////////////////////////////////////////////////////////
|
||||
let id = 1;
|
||||
for (let i = 0; i < qQueryFilter?.criteria?.length; i++)
|
||||
{
|
||||
const criteria = qQueryFilter.criteria[i];
|
||||
let [field, fieldTable] = TableUtils.getFieldAndTable(tableMetaData, criteria.fieldName);
|
||||
if (field == null)
|
||||
{
|
||||
console.log("Couldn't find field for filter: " + criteria.fieldName);
|
||||
warningParts.push("Your filter contained an unrecognized field name: " + criteria.fieldName);
|
||||
continue;
|
||||
}
|
||||
|
||||
let values = criteria.values;
|
||||
if (field.possibleValueSourceName)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// possible-values in query-string are expected to only be their id values. //
|
||||
// e.g., ...values=[1]... //
|
||||
// but we need them to be possibleValue objects (w/ id & label) so the label //
|
||||
// can be shown in the filter dropdown. So, make backend call to look them up. //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
if (values && values.length > 0)
|
||||
{
|
||||
values = await qController.possibleValues(fieldTable.name, null, field.name, "", values);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////
|
||||
// log message if no values were returned //
|
||||
////////////////////////////////////////////
|
||||
if (!values || values.length === 0)
|
||||
{
|
||||
console.warn("WARNING: No possible values were returned for [" + field.possibleValueSourceName + "] for values [" + criteria.values + "].");
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// replace objects that look like expressions with expression instances //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
if (values && values.length)
|
||||
{
|
||||
for (let i = 0; i < values.length; i++)
|
||||
{
|
||||
const expression = this.gridCriteriaValueToExpression(values[i]);
|
||||
if (expression)
|
||||
{
|
||||
values[i] = expression;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defaultFilter.items.push({
|
||||
columnField: criteria.fieldName,
|
||||
operatorValue: FilterUtils.qqqCriteriaOperatorToGrid(criteria.operator, field, values),
|
||||
value: FilterUtils.qqqCriteriaValuesToGrid(criteria.operator, values, field),
|
||||
id: id++
|
||||
});
|
||||
}
|
||||
|
||||
defaultFilter.linkOperator = GridLinkOperator.And;
|
||||
if (qQueryFilter.booleanOperator === "OR")
|
||||
{
|
||||
defaultFilter.linkOperator = GridLinkOperator.Or;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// translate from qqq-style orderBy to one that the grid wants //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
if (qQueryFilter.orderBys && qQueryFilter.orderBys.length > 0)
|
||||
{
|
||||
for (let i = 0; i < qQueryFilter.orderBys.length; i++)
|
||||
{
|
||||
const orderBy = qQueryFilter.orderBys[i];
|
||||
defaultSort.push({
|
||||
field: orderBy.fieldName,
|
||||
sort: orderBy.isAscending ? "asc" : "desc"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (searchParams && searchParams.has("filter"))
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if we're setting the filter based on a filter query-string param, then make sure we don't have a currentSavedFilter in local storage. //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
localStorage.removeItem(`${CURRENT_SAVED_FILTER_ID_LOCAL_STORAGE_KEY_ROOT}.${tableMetaData.name}`);
|
||||
localStorage.setItem(filterLocalStorageKey, JSON.stringify(defaultFilter));
|
||||
localStorage.setItem(sortLocalStorageKey, JSON.stringify(defaultSort));
|
||||
}
|
||||
|
||||
return ({filter: defaultFilter, sort: defaultSort, warning: warningParts.length > 0 ? "Warning: " + warningParts.join("; ") : ""});
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
console.warn("Error parsing filter from query string", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (localStorage.getItem(filterLocalStorageKey))
|
||||
{
|
||||
defaultFilter = JSON.parse(localStorage.getItem(filterLocalStorageKey));
|
||||
console.log(`Got default from LS: ${JSON.stringify(defaultFilter)}`);
|
||||
}
|
||||
|
||||
if (localStorage.getItem(sortLocalStorageKey))
|
||||
{
|
||||
defaultSort = JSON.parse(localStorage.getItem(sortLocalStorageKey));
|
||||
console.log(`Got default from LS: ${JSON.stringify(defaultSort)}`);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// if any values in the items are objects, but should be expression instances, //
|
||||
// then convert & replace them. //
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
if (defaultFilter && defaultFilter.items && defaultFilter.items.length)
|
||||
{
|
||||
defaultFilter.items.forEach((item) =>
|
||||
{
|
||||
if (item.value && item.value.length)
|
||||
{
|
||||
for (let i = 0; i < item.value.length; i++)
|
||||
{
|
||||
const expression = this.gridCriteriaValueToExpression(item.value[i]);
|
||||
if (expression)
|
||||
{
|
||||
item.value[i] = expression;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const expression = this.gridCriteriaValueToExpression(item.value);
|
||||
if (expression)
|
||||
{
|
||||
item.value = expression;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return ({filter: defaultFilter, sort: defaultSort, warning: warningParts.length > 0 ? "Warning: " + warningParts.join("; ") : ""});
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** build a grid filter from a qqq filter
|
||||
*******************************************************************************/
|
||||
public static buildGridFilterFromQFilter(tableMetaData: QTableMetaData, queryFilter: QQueryFilter): GridFilterModel
|
||||
{
|
||||
const gridItems: GridFilterItem[] = [];
|
||||
|
||||
for (let i = 0; i < queryFilter.criteria.length; i++)
|
||||
{
|
||||
const criteria = queryFilter.criteria[i];
|
||||
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)});
|
||||
}
|
||||
}
|
||||
|
||||
const gridFilter: GridFilterModel = {items: gridItems, linkOperator: queryFilter.booleanOperator == "AND" ? GridLinkOperator.And : GridLinkOperator.Or};
|
||||
return (gridFilter);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static getField(tableMetaData: QTableMetaData, fieldName: string): [QFieldMetaData, QTableMetaData]
|
||||
{
|
||||
if (fieldName == null)
|
||||
{
|
||||
return ([null, null]);
|
||||
}
|
||||
|
||||
if (fieldName.indexOf(".") > -1)
|
||||
{
|
||||
let parts = fieldName.split(".", 2);
|
||||
if (tableMetaData.exposedJoins && tableMetaData.exposedJoins.length)
|
||||
{
|
||||
for (let i = 0; i < tableMetaData.exposedJoins.length; i++)
|
||||
{
|
||||
const joinTable = tableMetaData.exposedJoins[i].joinTable;
|
||||
if (joinTable.name == parts[0])
|
||||
{
|
||||
return ([joinTable.fields.get(parts[1]), joinTable]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Failed to find join field: ${fieldName}`);
|
||||
return ([null, null]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ([tableMetaData.fields.get(fieldName), tableMetaData]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** build a qqq filter from a grid and column sort model
|
||||
*******************************************************************************/
|
||||
public static buildQFilterFromGridFilter(tableMetaData: QTableMetaData, filterModel: GridFilterModel, columnSortModel: GridSortItem[], limit?: number, allowIncompleteCriteria = false): QQueryFilter
|
||||
{
|
||||
console.log("Building q filter with model:");
|
||||
console.log(filterModel);
|
||||
|
||||
const qFilter = new QQueryFilter();
|
||||
if (columnSortModel)
|
||||
{
|
||||
columnSortModel.forEach((gridSortItem) =>
|
||||
{
|
||||
qFilter.addOrderBy(new QFilterOrderBy(gridSortItem.field, gridSortItem.sort === "asc"));
|
||||
});
|
||||
}
|
||||
|
||||
if (limit)
|
||||
{
|
||||
console.log("Setting limit to: " + limit);
|
||||
qFilter.limit = limit;
|
||||
}
|
||||
|
||||
if (filterModel)
|
||||
{
|
||||
let foundFilter = false;
|
||||
filterModel.items.forEach((item) =>
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// set the values for these operators that otherwise don't have values //
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
if (item.operatorValue === "isTrue")
|
||||
{
|
||||
item.value = [true];
|
||||
}
|
||||
else if (item.operatorValue === "isFalse")
|
||||
{
|
||||
item.value = [false];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// if no value set and not 'empty' or 'not empty' operators, skip this filter //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
let incomplete = false;
|
||||
if (item.operatorValue === "between" || item.operatorValue === "notBetween")
|
||||
{
|
||||
if (!item.value || !item.value.length || item.value.length < 2 || this.isUnset(item.value[0]) || this.isUnset(item.value[1]))
|
||||
{
|
||||
incomplete = true;
|
||||
}
|
||||
}
|
||||
else if ((!item.value || item.value.length == 0 || (item.value.length == 1 && this.isUnset(item.value[0]))) && item.operatorValue !== "isEmpty" && item.operatorValue !== "isNotEmpty")
|
||||
{
|
||||
incomplete = true;
|
||||
}
|
||||
|
||||
if (incomplete && !allowIncompleteCriteria)
|
||||
{
|
||||
console.log(`Discarding incomplete filter criteria: ${JSON.stringify(item)}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const fieldMetadata = tableMetaData?.fields.get(item.columnField);
|
||||
const operator = FilterUtils.gridCriteriaOperatorToQQQ(item.operatorValue);
|
||||
const values = FilterUtils.gridCriteriaValueToQQQ(operator, item.value, item.operatorValue, fieldMetadata);
|
||||
let criteria = new QFilterCriteria(item.columnField, operator, values);
|
||||
qFilter.addCriteria(criteria);
|
||||
foundFilter = true;
|
||||
});
|
||||
|
||||
qFilter.booleanOperator = "AND";
|
||||
if (filterModel.linkOperator == "or")
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// by default qFilter uses AND - so only if we see linkOperator=or do we need to set it //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
qFilter.booleanOperator = "OR";
|
||||
}
|
||||
}
|
||||
|
||||
return qFilter;
|
||||
};
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static isUnset(value: any)
|
||||
{
|
||||
return value === "" || value === undefined;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static gridCriteriaValueToExpression(value: any)
|
||||
{
|
||||
if (value && value.length)
|
||||
{
|
||||
value = value[0];
|
||||
}
|
||||
|
||||
if (value && value.type)
|
||||
{
|
||||
if (value.type == "NowWithOffset")
|
||||
{
|
||||
return (new NowWithOffsetExpression(value));
|
||||
}
|
||||
else if (value.type == "Now")
|
||||
{
|
||||
return (new NowExpression(value));
|
||||
}
|
||||
else if (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.
|
||||
*******************************************************************************/
|
||||
public static convertFilterPossibleValuesToIds(inputFilter: QQueryFilter): QQueryFilter
|
||||
{
|
||||
const filter = Object.assign({}, inputFilter);
|
||||
|
||||
if (filter.criteria)
|
||||
{
|
||||
for (let i = 0; i < filter.criteria.length; i++)
|
||||
{
|
||||
const criteria = filter.criteria[i];
|
||||
if (criteria.values)
|
||||
{
|
||||
for (let j = 0; j < criteria.values.length; j++)
|
||||
{
|
||||
let value = criteria.values[j];
|
||||
if (value && value.id && value.label)
|
||||
{
|
||||
criteria.values[j] = value.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (filter);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static canFilterWorkAsBasic(tableMetaData: QTableMetaData, filter: QQueryFilter): { canFilterWorkAsBasic: boolean; reasonsWhyItCannot?: string[] }
|
||||
{
|
||||
const reasonsWhyItCannot: string[] = [];
|
||||
|
||||
if(filter == null)
|
||||
{
|
||||
return ({canFilterWorkAsBasic: true});
|
||||
}
|
||||
|
||||
if(filter.booleanOperator == "OR")
|
||||
{
|
||||
reasonsWhyItCannot.push("Filter uses the 'OR' operator.")
|
||||
}
|
||||
|
||||
if(filter.criteria)
|
||||
{
|
||||
const usedFields: {[name: string]: boolean} = {};
|
||||
const warnedFields: {[name: string]: boolean} = {};
|
||||
for (let i = 0; i < filter.criteria.length; i++)
|
||||
{
|
||||
const criteriaName = filter.criteria[i].fieldName;
|
||||
if(!criteriaName)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(usedFields[criteriaName])
|
||||
{
|
||||
if(!warnedFields[criteriaName])
|
||||
{
|
||||
const [field, tableForField] = TableUtils.getFieldAndTable(tableMetaData, criteriaName);
|
||||
let fieldLabel = field.label;
|
||||
if(tableForField.name != tableMetaData.name)
|
||||
{
|
||||
let fieldLabel = `${tableForField.label}: ${field.label}`;
|
||||
}
|
||||
reasonsWhyItCannot.push(`Filter contains more than 1 condition for the field: ${fieldLabel}`);
|
||||
warnedFields[criteriaName] = true;
|
||||
}
|
||||
}
|
||||
usedFields[criteriaName] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(reasonsWhyItCannot.length == 0)
|
||||
{
|
||||
return ({canFilterWorkAsBasic: true});
|
||||
}
|
||||
else
|
||||
{
|
||||
return ({canFilterWorkAsBasic: false, reasonsWhyItCannot: reasonsWhyItCannot});
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** get the values associated with a criteria as a string, e.g., for showing
|
||||
** in a tooltip.
|
||||
*******************************************************************************/
|
||||
public static getValuesString(fieldMetaData: QFieldMetaData, criteria: QFilterCriteria, maxValuesToShow: number = 3): string
|
||||
{
|
||||
let valuesString = "";
|
||||
if (criteria.values && criteria.values.length && fieldMetaData.type !== QFieldType.BOOLEAN)
|
||||
{
|
||||
let labels = [] as string[];
|
||||
|
||||
let maxLoops = criteria.values.length;
|
||||
if (maxLoops > (maxValuesToShow + 2))
|
||||
{
|
||||
maxLoops = maxValuesToShow;
|
||||
}
|
||||
|
||||
for (let i = 0; i < maxLoops; i++)
|
||||
{
|
||||
if (criteria.values[i] && criteria.values[i].label)
|
||||
{
|
||||
labels.push(criteria.values[i].label);
|
||||
}
|
||||
else
|
||||
{
|
||||
labels.push(criteria.values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxLoops < criteria.values.length)
|
||||
{
|
||||
labels.push(" and " + (criteria.values.length - maxLoops) + " other values.");
|
||||
}
|
||||
|
||||
valuesString = (labels.join(", "));
|
||||
}
|
||||
return valuesString;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default FilterUtils;
|
485
src/qqq/utils/qqq/FilterUtils.tsx
Normal file
485
src/qqq/utils/qqq/FilterUtils.tsx
Normal file
@ -0,0 +1,485 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. 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 {QController} from "@kingsrook/qqq-frontend-core/lib/controllers/QController";
|
||||
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 {NowExpression} from "@kingsrook/qqq-frontend-core/lib/model/query/NowExpression";
|
||||
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 {GridSortModel} from "@mui/x-data-grid-pro";
|
||||
import TableUtils from "qqq/utils/qqq/TableUtils";
|
||||
import ValueUtils from "qqq/utils/qqq/ValueUtils";
|
||||
|
||||
/*******************************************************************************
|
||||
** Utility class for working with QQQ Filters
|
||||
**
|
||||
*******************************************************************************/
|
||||
class FilterUtils
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** Helper method - take a list of values, which may be possible values, and
|
||||
** either return the original list, or a new list that is just the ids of the
|
||||
** possible values (if it was a list of possible values).
|
||||
**
|
||||
** Or, if the values are date-times, convert them to UTC.
|
||||
*******************************************************************************/
|
||||
public static cleanseCriteriaValueForQQQ = (param: any[], fieldMetaData: QFieldMetaData): number[] | string[] =>
|
||||
{
|
||||
if (param === null || param === undefined)
|
||||
{
|
||||
return (param);
|
||||
}
|
||||
|
||||
if (FilterUtils.gridCriteriaValueToExpression(param))
|
||||
{
|
||||
return (param);
|
||||
}
|
||||
|
||||
let rs = [];
|
||||
for (let i = 0; i < param.length; i++)
|
||||
{
|
||||
console.log(param[i]);
|
||||
if (param[i] && param[i].id && param[i].label)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if the param looks like a possible value, return its id //
|
||||
// during build of new custom filter panel, this ended up causing us //
|
||||
// problems (because we wanted the full PV object in the filter model for the frontend) //
|
||||
// so, we can keep the PV as-is here, and see calls to convertFilterPossibleValuesToIds //
|
||||
// to do what this used to do. //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// rs.push(param[i].id);
|
||||
rs.push(param[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fieldMetaData?.type == QFieldType.DATE_TIME)
|
||||
{
|
||||
try
|
||||
{
|
||||
let toPush = ValueUtils.frontendLocalZoneDateTimeStringToUTCStringForBackend(param[i]);
|
||||
rs.push(toPush);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
console.log("Error converting date-time to UTC: ", e);
|
||||
rs.push(param[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rs.push(param[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (rs);
|
||||
};
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static async cleanupValuesInFilerFromQueryString(qController: QController, tableMetaData: QTableMetaData, queryFilter: QQueryFilter)
|
||||
{
|
||||
for (let i = 0; i < queryFilter?.criteria?.length; i++)
|
||||
{
|
||||
const criteria = queryFilter.criteria[i];
|
||||
let [field, fieldTable] = TableUtils.getFieldAndTable(tableMetaData, criteria.fieldName);
|
||||
|
||||
let values = criteria.values;
|
||||
if (field.possibleValueSourceName)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// possible-values in query-string are expected to only be their id values. //
|
||||
// e.g., ...values=[1]... //
|
||||
// but we need them to be possibleValue objects (w/ id & label) so the label //
|
||||
// can be shown in the filter dropdown. So, make backend call to look them up. //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
if (values && values.length > 0)
|
||||
{
|
||||
values = await qController.possibleValues(fieldTable.name, null, field.name, "", values);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////
|
||||
// log message if no values were returned //
|
||||
////////////////////////////////////////////
|
||||
if (!values || values.length === 0)
|
||||
{
|
||||
console.warn("WARNING: No possible values were returned for [" + field.possibleValueSourceName + "] for values [" + criteria.values + "].");
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// replace objects that look like expressions with expression instances //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
if (values && values.length)
|
||||
{
|
||||
for (let i = 0; i < values.length; i++)
|
||||
{
|
||||
const expression = this.gridCriteriaValueToExpression(values[i]);
|
||||
if (expression)
|
||||
{
|
||||
values[i] = expression;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
criteria.values = values;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** given a table, and a field name (which may be prefixed with an exposed-join
|
||||
** table name (from the table) - return the corresponding field-meta-data, and
|
||||
** the table that the field is from (e.g., may be a join table!)
|
||||
*******************************************************************************/
|
||||
public static getField(tableMetaData: QTableMetaData, fieldName: string): [QFieldMetaData, QTableMetaData]
|
||||
{
|
||||
if (fieldName == null)
|
||||
{
|
||||
return ([null, null]);
|
||||
}
|
||||
|
||||
if (fieldName.indexOf(".") > -1)
|
||||
{
|
||||
let parts = fieldName.split(".", 2);
|
||||
if (tableMetaData.exposedJoins && tableMetaData.exposedJoins.length)
|
||||
{
|
||||
for (let i = 0; i < tableMetaData.exposedJoins.length; i++)
|
||||
{
|
||||
const joinTable = tableMetaData.exposedJoins[i].joinTable;
|
||||
if (joinTable.name == parts[0])
|
||||
{
|
||||
return ([joinTable.fields.get(parts[1]), joinTable]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Failed to find join field: ${fieldName}`);
|
||||
return ([null, null]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ([tableMetaData.fields.get(fieldName), tableMetaData]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static gridCriteriaValueToExpression(value: any)
|
||||
{
|
||||
if (value && value.length)
|
||||
{
|
||||
value = value[0];
|
||||
}
|
||||
|
||||
if (value && value.type)
|
||||
{
|
||||
if (value.type == "NowWithOffset")
|
||||
{
|
||||
return (new NowWithOffsetExpression(value));
|
||||
}
|
||||
else if (value.type == "Now")
|
||||
{
|
||||
return (new NowExpression(value));
|
||||
}
|
||||
else if (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.
|
||||
*******************************************************************************/
|
||||
public static convertFilterPossibleValuesToIds(inputFilter: QQueryFilter): QQueryFilter
|
||||
{
|
||||
const filter = Object.assign({}, inputFilter);
|
||||
|
||||
if (filter.criteria)
|
||||
{
|
||||
for (let i = 0; i < filter.criteria.length; i++)
|
||||
{
|
||||
const criteria = filter.criteria[i];
|
||||
if (criteria.values)
|
||||
{
|
||||
for (let j = 0; j < criteria.values.length; j++)
|
||||
{
|
||||
let value = criteria.values[j];
|
||||
if (value && value.id && value.label)
|
||||
{
|
||||
criteria.values[j] = value.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (filter);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static canFilterWorkAsBasic(tableMetaData: QTableMetaData, filter: QQueryFilter): { canFilterWorkAsBasic: boolean; reasonsWhyItCannot?: string[] }
|
||||
{
|
||||
const reasonsWhyItCannot: string[] = [];
|
||||
|
||||
if(filter == null)
|
||||
{
|
||||
return ({canFilterWorkAsBasic: true});
|
||||
}
|
||||
|
||||
if(filter.booleanOperator == "OR")
|
||||
{
|
||||
reasonsWhyItCannot.push("Filter uses the 'OR' operator.")
|
||||
}
|
||||
|
||||
if(filter.criteria)
|
||||
{
|
||||
const usedFields: {[name: string]: boolean} = {};
|
||||
const warnedFields: {[name: string]: boolean} = {};
|
||||
for (let i = 0; i < filter.criteria.length; i++)
|
||||
{
|
||||
const criteriaName = filter.criteria[i].fieldName;
|
||||
if(!criteriaName)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(usedFields[criteriaName])
|
||||
{
|
||||
if(!warnedFields[criteriaName])
|
||||
{
|
||||
const [field, tableForField] = TableUtils.getFieldAndTable(tableMetaData, criteriaName);
|
||||
let fieldLabel = field.label;
|
||||
if(tableForField.name != tableMetaData.name)
|
||||
{
|
||||
let fieldLabel = `${tableForField.label}: ${field.label}`;
|
||||
}
|
||||
reasonsWhyItCannot.push(`Filter contains more than 1 condition for the field: ${fieldLabel}`);
|
||||
warnedFields[criteriaName] = true;
|
||||
}
|
||||
}
|
||||
usedFields[criteriaName] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(reasonsWhyItCannot.length == 0)
|
||||
{
|
||||
return ({canFilterWorkAsBasic: true});
|
||||
}
|
||||
else
|
||||
{
|
||||
return ({canFilterWorkAsBasic: false, reasonsWhyItCannot: reasonsWhyItCannot});
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** get the values associated with a criteria as a string, e.g., for showing
|
||||
** in a tooltip.
|
||||
*******************************************************************************/
|
||||
public static getValuesString(fieldMetaData: QFieldMetaData, criteria: QFilterCriteria, maxValuesToShow: number = 3): string
|
||||
{
|
||||
let valuesString = "";
|
||||
if (criteria.values && criteria.values.length && fieldMetaData.type !== QFieldType.BOOLEAN)
|
||||
{
|
||||
let labels = [] as string[];
|
||||
|
||||
let maxLoops = criteria.values.length;
|
||||
if (maxLoops > (maxValuesToShow + 2))
|
||||
{
|
||||
maxLoops = maxValuesToShow;
|
||||
}
|
||||
|
||||
for (let i = 0; i < maxLoops; i++)
|
||||
{
|
||||
if (criteria.values[i] && criteria.values[i].label)
|
||||
{
|
||||
labels.push(criteria.values[i].label);
|
||||
}
|
||||
else
|
||||
{
|
||||
labels.push(criteria.values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxLoops < criteria.values.length)
|
||||
{
|
||||
labels.push(" and " + (criteria.values.length - maxLoops) + " other values.");
|
||||
}
|
||||
|
||||
valuesString = (labels.join(", "));
|
||||
}
|
||||
return valuesString;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static buildQFilterFromJSONObject(object: any): QQueryFilter
|
||||
{
|
||||
const queryFilter = new QQueryFilter();
|
||||
|
||||
queryFilter.criteria = [];
|
||||
for (let i = 0; i < object.criteria?.length; i++)
|
||||
{
|
||||
const criteriaObject = object.criteria[i];
|
||||
queryFilter.criteria.push(new QFilterCriteria(criteriaObject.fieldName, criteriaObject.operator, criteriaObject.values));
|
||||
}
|
||||
|
||||
queryFilter.orderBys = [];
|
||||
for (let i = 0; i < object.orderBys?.length; i++)
|
||||
{
|
||||
const orderByObject = object.orderBys[i];
|
||||
queryFilter.orderBys.push(new QFilterOrderBy(orderByObject.fieldName, orderByObject.isAscending));
|
||||
}
|
||||
|
||||
queryFilter.booleanOperator = object.booleanOperator;
|
||||
queryFilter.skip = object.skip;
|
||||
queryFilter.limit = object.limit;
|
||||
|
||||
return (queryFilter);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static getGridSortFromQueryFilter(queryFilter: QQueryFilter): GridSortModel
|
||||
{
|
||||
const gridSortModel: GridSortModel = [];
|
||||
for (let i = 0; i < queryFilter?.orderBys?.length; i++)
|
||||
{
|
||||
const orderBy = queryFilter.orderBys[i];
|
||||
gridSortModel.push({field: orderBy.fieldName, sort: orderBy.isAscending ? "asc" : "desc"})
|
||||
}
|
||||
return (gridSortModel);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static operatorToHumanString(criteria: QFilterCriteria): string
|
||||
{
|
||||
if(criteria == null || criteria.operator == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
switch(criteria.operator)
|
||||
{
|
||||
case QCriteriaOperator.EQUALS:
|
||||
return ("equals");
|
||||
case QCriteriaOperator.NOT_EQUALS:
|
||||
case QCriteriaOperator.NOT_EQUALS_OR_IS_NULL:
|
||||
return ("does not equal");
|
||||
case QCriteriaOperator.IN:
|
||||
return ("is any of");
|
||||
case QCriteriaOperator.NOT_IN:
|
||||
return ("is none of");
|
||||
case QCriteriaOperator.STARTS_WITH:
|
||||
return ("starts with");
|
||||
case QCriteriaOperator.ENDS_WITH:
|
||||
return ("ends with");
|
||||
case QCriteriaOperator.CONTAINS:
|
||||
return ("contains");
|
||||
case QCriteriaOperator.NOT_STARTS_WITH:
|
||||
return ("does not start with");
|
||||
case QCriteriaOperator.NOT_ENDS_WITH:
|
||||
return ("does not end with");
|
||||
case QCriteriaOperator.NOT_CONTAINS:
|
||||
return ("does not contain");
|
||||
case QCriteriaOperator.LESS_THAN:
|
||||
return ("less than");
|
||||
case QCriteriaOperator.LESS_THAN_OR_EQUALS:
|
||||
return ("less than or equals");
|
||||
case QCriteriaOperator.GREATER_THAN:
|
||||
return ("greater than or equals");
|
||||
case QCriteriaOperator.GREATER_THAN_OR_EQUALS:
|
||||
return ("greater than or equals");
|
||||
case QCriteriaOperator.IS_BLANK:
|
||||
return ("is blank");
|
||||
case QCriteriaOperator.IS_NOT_BLANK:
|
||||
return ("is not blank");
|
||||
case QCriteriaOperator.BETWEEN:
|
||||
return ("is between");
|
||||
case QCriteriaOperator.NOT_BETWEEN:
|
||||
return ("is not between");
|
||||
}
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
console.log(`Error getting operator human string for ${JSON.stringify(criteria)}: ${e}`);
|
||||
return criteria?.operator
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static criteriaToHumanString(table: QTableMetaData, criteria: QFilterCriteria, styled = false): string | JSX.Element
|
||||
{
|
||||
if(criteria == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
const [field, fieldTable] = TableUtils.getFieldAndTable(table, criteria.fieldName);
|
||||
const fieldLabel = TableUtils.getFieldFullLabel(table, criteria.fieldName);
|
||||
const valuesString = FilterUtils.getValuesString(field, criteria);
|
||||
|
||||
if(styled)
|
||||
{
|
||||
return (<>
|
||||
<b>{fieldLabel}</b> {FilterUtils.operatorToHumanString(criteria)} <span style={{color: "#0062FF"}}>{valuesString}</span>
|
||||
</>);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (`${fieldLabel} ${FilterUtils.operatorToHumanString(criteria)} ${valuesString}`);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default FilterUtils;
|
Reference in New Issue
Block a user