Booleans on query; getProcessSummaryListItemForTableRecordLink; better location-change on entity-view; better singular/plural words in processes

This commit is contained in:
2022-10-04 10:30:54 -05:00
parent 017111d183
commit 2ba9085883
7 changed files with 121 additions and 13 deletions

View File

@ -35,12 +35,13 @@ import LinearProgress from "@mui/material/LinearProgress";
import Menu from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem";
import {
DataGridPro,
DataGridPro, getGridDateOperators, getGridNumericOperators, getGridStringOperators,
GridCallbackDetails,
GridColDef,
GridColumnOrderChangeParams,
GridColumnVisibilityModel,
GridExportMenuItemProps,
GridFilterItem,
GridFilterModel,
GridRowId,
GridRowParams,
@ -55,6 +56,7 @@ import {
GridToolbarFilterButton,
MuiEvent
} from "@mui/x-data-grid-pro";
import {GridFilterOperator} from "@mui/x-data-grid/models/gridFilterOperator";
import React, {useCallback, useEffect, useReducer, useRef, useState} from "react";
import {Link, useNavigate, useParams, useSearchParams} from "react-router-dom";
import DashboardLayout from "qqq/components/DashboardLayout";
@ -216,7 +218,7 @@ function EntityList({table}: Props): JSX.Element
filterModel.items.forEach((item) =>
{
const operator = QFilterUtils.gridCriteriaOperatorToQQQ(item.operatorValue);
const values = QFilterUtils.gridCriteriaValueToQQQ(operator, item.value);
const values = QFilterUtils.gridCriteriaValueToQQQ(operator, item.value, item.operatorValue);
qFilter.addCriteria(new QFilterCriteria(item.columnField, operator, values));
});
}
@ -324,6 +326,35 @@ function EntityList({table}: Props): JSX.Element
delete countResults[latestQueryId];
}, [receivedCountTimestamp]);
const booleanTrueOperator: GridFilterOperator = {
label: "is yes",
value: "isTrue",
getApplyFilterFn: (filterItem: GridFilterItem, column: GridColDef) => null
};
const booleanFalseOperator: GridFilterOperator = {
label: "is no",
value: "isFalse",
getApplyFilterFn: (filterItem: GridFilterItem, column: GridColDef) => null
};
const booleanEmptyOperator: GridFilterOperator = {
label: "is empty",
value: "isEmpty",
getApplyFilterFn: (filterItem: GridFilterItem, column: GridColDef) => null
};
const booleanNotEmptyOperator: GridFilterOperator = {
label: "is not empty",
value: "isNotEmpty",
getApplyFilterFn: (filterItem: GridFilterItem, column: GridColDef) => null
};
const getCustomGridBooleanOperators = (): GridFilterOperator[] =>
{
return [booleanTrueOperator, booleanFalseOperator, booleanEmptyOperator, booleanNotEmptyOperator];
}
///////////////////////////
// display query results //
///////////////////////////
@ -380,8 +411,13 @@ function EntityList({table}: Props): JSX.Element
let columnType = "string";
let columnWidth = 200;
let filterOperators: GridFilterOperator<any>[] = getGridStringOperators();
if (!field.possibleValueSourceName)
if (field.possibleValueSourceName)
{
filterOperators = getGridNumericOperators();
}
else
{
switch (field.type)
{
@ -395,18 +431,23 @@ function EntityList({table}: Props): JSX.Element
columnWidth = 75;
}
// @ts-ignore
filterOperators = getGridNumericOperators();
break;
case QFieldType.DATE:
columnType = "date";
columnWidth = 100;
filterOperators = getGridDateOperators();
break;
case QFieldType.DATE_TIME:
columnType = "dateTime";
columnWidth = 200;
filterOperators = getGridDateOperators(true);
break;
case QFieldType.BOOLEAN:
columnType = "boolean";
columnType = "string"; // using boolean gives an odd 'no' for nulls.
columnWidth = 75;
filterOperators = getCustomGridBooleanOperators();
break;
default:
// noop - leave as string
@ -452,6 +493,7 @@ function EntityList({table}: Props): JSX.Element
headerName: field.label,
width: columnWidth,
renderCell: null as any,
filterOperators: filterOperators,
};
if(columnsToRender[field.name])
@ -835,7 +877,7 @@ function EntityList({table}: Props): JSX.Element
records on this page are selected.
<Button onClick={() => setSelectFullFilterState("filter")}>
Select all
{` ${totalRecords.toLocaleString()} `}
{` ${totalRecords ? totalRecords.toLocaleString() : ""} `}
records matching this query
</Button>
</div>
@ -845,7 +887,7 @@ function EntityList({table}: Props): JSX.Element
selectFullFilterState === "filter" && (
<div className="selectionTool">
All
<strong>{` ${totalRecords.toLocaleString()} `}</strong>
<strong>{` ${totalRecords ? totalRecords.toLocaleString() : "All"} `}</strong>
records matching this query are selected.
<Button onClick={() => setSelectFullFilterState("checked")}>
Select the

View File

@ -88,6 +88,13 @@ function ViewContents({id, table}: Props): JSX.Element
useEffect(() =>
{
setAsyncLoadInited(false);
setTableMetaData(null)
setRecord(null)
setT1SectionElement(null)
setNonT1TableSections([])
setTableProcesses([])
setTableSections(null)
setWidgets(null)
}, [location]);
if (!asyncLoadInited)

View File

@ -70,8 +70,7 @@ function QProcessSummaryResults({
{QValueUtils.getFormattedNumber(processValues.recordCount)}
{" "}
{sourceTableMetaData.label}
{" "}
records were processed.
{processValues.recordCount === 1 ? " record was" : " records were"} processed.
</ListItemText>
</ListItem>
)

View File

@ -189,8 +189,7 @@ function QValidationReview({
<ListItem sx={{my: 2}}>
<ListItemText primaryTypographyProps={{fontSize: 16}}>
Validation complete on
{` ${QValueUtils.getFormattedNumber(processValues.recordCount)} ${sourceTableMetaData?.label} `}
records.
{` ${QValueUtils.getFormattedNumber(processValues.recordCount)} ${sourceTableMetaData?.label} ${processValues.recordCount === 1 ? "record." : "records."}`}
</ListItemText>
</ListItem>
)

View File

@ -44,20 +44,58 @@ export class ProcessSummaryLine
status: "OK" | "INFO" | "WARNING" | "ERROR";
count: number;
message: string;
primaryKeys: any[];
tableName: string;
recordId: any;
linkPreText: string;
linkText: string;
linkPostText: string;
constructor(processSummaryLine: any)
{
this.status = processSummaryLine.status;
this.count = processSummaryLine.count;
this.message = processSummaryLine.message;
this.primaryKeys = processSummaryLine.primaryKeys;
this.tableName = processSummaryLine.tableName;
this.recordId = processSummaryLine.recordId;
this.linkPreText = processSummaryLine.linkPreText;
this.linkText = processSummaryLine.linkText;
this.linkPostText = processSummaryLine.linkPostText;
}
getProcessSummaryListItem(i: number, table: QTableMetaData, qInstance: QInstance, isResultScreen: boolean = false): JSX.Element
{
if (this.tableName != undefined && this.recordId != undefined)
{
return (this.getProcessSummaryListItemForTableRecordLink(i, table, qInstance, isResultScreen));
}
return (this.getProcessSummaryListItemForCountAndMessage(i, table, qInstance, isResultScreen));
}
private getProcessSummaryListItemForTableRecordLink(i: number, table: QTableMetaData, qInstance: QInstance, isResultScreen: boolean = false): JSX.Element
{
const tablePath = qInstance.getTablePathByName(this.tableName);
return (
<ListItem key={i} sx={{pl: 4, my: 2}}>
<MDBox display="flex" alignItems="top">
<Icon fontSize="medium" sx={{mr: 1}} color={this.getColor()}>{this.getIcon(isResultScreen)}</Icon>
<ListItemText primaryTypographyProps={{fontSize: 16}}>
{this.linkPreText ?? ""}
<Link to={`${tablePath}/${this.recordId}`}>{this.linkText}</Link>
{this.linkPostText ?? ""}
</ListItemText>
</MDBox>
</ListItem>
);
}
private getProcessSummaryListItemForCountAndMessage(i: number, table: QTableMetaData, qInstance: QInstance, isResultScreen: boolean = false): JSX.Element
{
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// split up the message into words - then we'll display the last word by itself with a non-breaking space, no-wrap-glued to the button. //

View File

@ -117,3 +117,15 @@
display: flex;
align-items: flex-start;
}
/* let long field names in filter dropdown wrap instead of get cut off */
.MuiDataGrid-filterForm .MuiDataGrid-filterFormColumnInput .MuiNativeSelect-select.MuiNativeSelect-standard
{
white-space: normal;
height: auto;
}
.MuiDataGrid-filterForm
{
align-items: flex-end;
}

View File

@ -44,6 +44,8 @@ class QFilterUtils
case "is":
case "equals":
case "=":
case "isTrue":
case "isFalse":
return QCriteriaOperator.EQUALS;
case "isNot":
case "!=":
@ -194,8 +196,17 @@ class QFilterUtils
** 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): any[] =>
public static gridCriteriaValueToQQQ = (operator: QCriteriaOperator, value: any, gridOperatorValue: string): any[] =>
{
if(gridOperatorValue === "isTrue")
{
return [true];
}
else if(gridOperatorValue === "isFalse")
{
return [false];
}
if (operator === QCriteriaOperator.IS_BLANK || operator === QCriteriaOperator.IS_NOT_BLANK)
{
return (null);