Compare commits

..

10 Commits

Author SHA1 Message Date
39a7aadd3f CE-989 add option to (not) includeTableCountsOn(app)HomeScreen 2024-03-07 20:30:14 -06:00
167af989d5 Add tooltips from metaData/helpContent to widget blocks. 2024-03-05 14:36:54 -06:00
ad7ea994a8 CE-889 - try to fix NPE's on localeCompares 2024-03-04 15:09:22 -06:00
e925310173 Merge pull request #44 from Kingsrook/feature/CE-878-make-the-operations-dashboard
CE-878: updated to allow sublabel to be displayed under label
2024-03-04 14:47:52 -06:00
8ebc2415fe Merged feature/CE-878-make-the-operations-dashboard into main 2024-02-29 09:39:08 -06:00
88a4c17bbc CE-798 follow-up - in cleanupValuesInFilerFromQueryString, don't try to translate [null] to a list of possible values (which fetches all of them)... 2024-02-27 13:57:04 -06:00
2900cd8593 CE-798 follow-up - Prevent tab in date/date-time filter value input boxes from closing a quick-filter menu (via an onKeyDown handler) 2024-02-27 13:35:37 -06:00
8ab0f5f549 CE-798 follow-up - increase width of date-time boxes (was too small to see all the fields!) 2024-02-27 11:56:54 -06:00
8cffbbcac4 Update to cimg/openjdk:17.0.9 2024-02-22 20:30:51 -06:00
37eb280d79 Revert to previous check for disabling export-menu items - that is - totalRecords === 0, instead of !totalRecords. makes tables w/o count allowed to do exports again. 2024-02-22 12:16:27 -06:00
16 changed files with 205 additions and 64 deletions

View File

@ -7,7 +7,7 @@ orbs:
executors:
java17:
docker:
- image: 'cimg/openjdk:17.0'
- image: 'cimg/openjdk:17.0.9'
commands:
install_java17:

View File

@ -36,7 +36,7 @@ import Icon from "@mui/material/Icon";
import Typography from "@mui/material/Typography";
import {makeStyles} from "@mui/styles";
import {Command} from "cmdk";
import React, {useContext, useEffect, useRef, useState} from "react";
import React, {useContext, useEffect, useRef} from "react";
import {useNavigate} from "react-router-dom";
import QContext from "QContext";
import HistoryUtils, {QHistoryEntry} from "qqq/utils/HistoryUtils";
@ -174,7 +174,9 @@ const CommandMenu = ({metaData}: Props) =>
})
tableNames = tableNames.sort((a: string, b:string) =>
{
return (metaData.tables.get(a).label.localeCompare(metaData.tables.get(b).label));
const labelA = metaData.tables.get(a).label ?? "";
const labelB = metaData.tables.get(b).label ?? "";
return (labelA.localeCompare(labelB));
})
const path = location.pathname;
@ -222,7 +224,9 @@ const CommandMenu = ({metaData}: Props) =>
})
tableNames = tableNames.sort((a: string, b:string) =>
{
return (metaData.tables.get(a).label.localeCompare(metaData.tables.get(b).label));
const labelA = metaData.tables.get(a).label ?? "";
const labelB = metaData.tables.get(b).label ?? "";
return (labelA.localeCompare(labelB));
})
return(
<Command.Group heading="Tables">
@ -252,7 +256,9 @@ const CommandMenu = ({metaData}: Props) =>
appNames = appNames.sort((a: string, b:string) =>
{
return (getFullAppLabel(metaData.appTree, a, 1, "").localeCompare(getFullAppLabel(metaData.appTree, b, 1, "")));
const labelA = getFullAppLabel(metaData.appTree, a, 1, "") ?? "";
const labelB = getFullAppLabel(metaData.appTree, b, 1, "") ?? "";
return (labelA.localeCompare(labelB));
})
return(
@ -286,7 +292,9 @@ const CommandMenu = ({metaData}: Props) =>
appNames = appNames.sort((a: string, b:string) =>
{
return (metaData.apps.get(a).label.localeCompare(metaData.apps.get(b).label));
const labelA = metaData.apps.get(a).label ?? "";
const labelB = metaData.apps.get(b).label ?? "";
return (labelA.localeCompare(labelB));
})
const entryMap = new Map<string, boolean>();

View File

@ -22,7 +22,9 @@
package com.kingsrook.qqq.frontend.materialdashboard.model.metadata;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QSupplementalAppMetaData;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
/*******************************************************************************
@ -30,7 +32,39 @@ import com.kingsrook.qqq.backend.core.model.metadata.layout.QSupplementalAppMeta
*******************************************************************************/
public class MaterialDashboardAppMetaData extends QSupplementalAppMetaData
{
public static final String TYPE_NAME = "materialDashboard";
private Boolean showAppLabelOnHomeScreen = true;
private Boolean includeTableCountsOnHomeScreen = true;
/*******************************************************************************
**
*******************************************************************************/
public static MaterialDashboardAppMetaData of(QAppMetaData app)
{
return ((MaterialDashboardAppMetaData) CollectionUtils.nonNullMap(app.getSupplementalMetaData()).get(TYPE_NAME));
}
/*******************************************************************************
** either get the supplemental meta dat attached to an app - or create a new one
** and attach it to the app, and return that.
*******************************************************************************/
public static MaterialDashboardAppMetaData ofOrWithNew(QAppMetaData app)
{
MaterialDashboardAppMetaData materialDashboardAppMetaData = of(app);
if(materialDashboardAppMetaData == null)
{
materialDashboardAppMetaData = new MaterialDashboardAppMetaData();
app.withSupplementalMetaData(materialDashboardAppMetaData);
}
return (materialDashboardAppMetaData);
}
@ -51,7 +85,7 @@ public class MaterialDashboardAppMetaData extends QSupplementalAppMetaData
@Override
public String getType()
{
return ("materialDashboard");
return TYPE_NAME;
}
@ -85,4 +119,35 @@ public class MaterialDashboardAppMetaData extends QSupplementalAppMetaData
return (this);
}
/*******************************************************************************
** Getter for includeTableCountsOnHomeScreen
*******************************************************************************/
public Boolean getIncludeTableCountsOnHomeScreen()
{
return (this.includeTableCountsOnHomeScreen);
}
/*******************************************************************************
** Setter for includeTableCountsOnHomeScreen
*******************************************************************************/
public void setIncludeTableCountsOnHomeScreen(Boolean includeTableCountsOnHomeScreen)
{
this.includeTableCountsOnHomeScreen = includeTableCountsOnHomeScreen;
}
/*******************************************************************************
** Fluent setter for includeTableCountsOnHomeScreen
*******************************************************************************/
public MaterialDashboardAppMetaData withIncludeTableCountsOnHomeScreen(Boolean includeTableCountsOnHomeScreen)
{
this.includeTableCountsOnHomeScreen = includeTableCountsOnHomeScreen;
return (this);
}
}

View File

@ -45,7 +45,7 @@ export default function ExportMenuItem(props: QExportMenuItemProps)
return (
<MenuItem
disabled={!totalRecords}
disabled={totalRecords === 0}
onClick={() =>
{
///////////////////////////////////////////////////////////////////////////////

View File

@ -94,6 +94,24 @@ export const makeTextField = (field: QFieldMetaData, criteria: QFilterCriteriaWi
document.getElementById(`${idPrefix}${criteria.id}`).focus();
};
/*******************************************************************************
** Event handler for key-down events - specifically added here, to stop pressing
** 'tab' in a date or date-time from closing the quick-filter...
*******************************************************************************/
const handleKeyDown = (e: any) =>
{
if (field.type == QFieldType.DATE || field.type == QFieldType.DATE_TIME)
{
if(e.code == "Tab")
{
console.log("Tab on date or date-time - don't close me, just move to the next sub-field!...");
e.stopPropagation();
}
}
};
const inputProps: any = {};
inputProps.endAdornment = (
<InputAdornment position="end">
@ -110,6 +128,7 @@ export const makeTextField = (field: QFieldMetaData, criteria: QFilterCriteriaWi
autoComplete="off"
type={type}
onChange={(event) => valueChangeHandler(event, valueIndex)}
onKeyDown={handleKeyDown}
value={value}
InputLabelProps={inputLabelProps}
InputProps={inputProps}

View File

@ -504,7 +504,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData
//////////////////////////////
// return the button & menu //
//////////////////////////////
const widthAndMaxWidth = 250
const widthAndMaxWidth = fieldMetaData?.type == QFieldType.DATE_TIME ? 275 : 250
return (
<>
{button}

View File

@ -21,8 +21,6 @@
import BlockElementWrapper from "qqq/components/widgets/blocks/BlockElementWrapper";
import {StandardBlockComponentProps} from "qqq/components/widgets/blocks/BlockModels";
import UpOrDownNumberBlock from "qqq/components/widgets/blocks/UpOrDownNumberBlock";
/*******************************************************************************
@ -40,7 +38,7 @@ export default function BigNumberBlock({widgetMetaData, data}: StandardBlockComp
<div style={{width: data.styles.width ?? "auto"}}>
<div style={{fontWeight: "700", fontSize: "0.875rem", color: "#3D3D3D", marginBottom: "-0.5rem"}}>
<BlockElementWrapper data={data} slot="heading">
<BlockElementWrapper metaData={widgetMetaData} data={data} slot="heading">
<span>{data.values.heading}</span>
</BlockElementWrapper>
</div>
@ -49,14 +47,14 @@ export default function BigNumberBlock({widgetMetaData, data}: StandardBlockComp
<div style={{display: "flex", alignItems: "baseline"}}>
<div style={{fontWeight: "700", fontSize: "2rem", marginRight: "0.25rem"}}>
<BlockElementWrapper data={data} slot="number">
<BlockElementWrapper metaData={widgetMetaData} data={data} slot="number">
<span style={{color: data.styles.numberColor}}>{data.values.number}</span>
</BlockElementWrapper>
</div>
{
data.values.context &&
<div style={{fontWeight: "500", fontSize: "0.875rem", color: "#7b809a"}}>
<BlockElementWrapper data={data} slot="context">
<BlockElementWrapper metaData={widgetMetaData} data={data} slot="context">
<span>{data.values.context}</span>
</BlockElementWrapper>
</div>

View File

@ -20,14 +20,18 @@
*/
import {QWidgetMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QWidgetMetaData";
import {Tooltip} from "@mui/material";
import React, {ReactElement} from "react";
import React, {ReactElement, useContext} from "react";
import {Link} from "react-router-dom";
import QContext from "QContext";
import HelpContent, {hasHelpContent} from "qqq/components/misc/HelpContent";
import {BlockData, BlockLink, BlockTooltip} from "qqq/components/widgets/blocks/BlockModels";
interface BlockElementWrapperProps
{
data: BlockData;
metaData: QWidgetMetaData;
slot: string
linkProps?: any;
children: ReactElement;
@ -36,8 +40,10 @@ interface BlockElementWrapperProps
/*******************************************************************************
** For Blocks - wrap their "slot" elements with an optional tooltip and/or link
*******************************************************************************/
export default function BlockElementWrapper({data, slot, linkProps, children}: BlockElementWrapperProps): JSX.Element
export default function BlockElementWrapper({data, metaData, slot, linkProps, children}: BlockElementWrapperProps): JSX.Element
{
const {helpHelpActive} = useContext(QContext);
let link: BlockLink;
let tooltip: BlockTooltip;
@ -61,6 +67,26 @@ export default function BlockElementWrapper({data, slot, linkProps, children}: B
tooltip = data.tooltip;
}
if(!tooltip)
{
const helpRoles = ["ALL_SCREENS"]
///////////////////////////////////////////////////////////////////////////////////////////////
// the full keys in the helpContent table will look like: //
// widget:MyCoolWidget;slot=myBlockId,label (if the block has a blockId in data) //
// widget:MyCoolWidget;slot=label (no blockId; note, label is slot name here) //
// in the widget metaData, the map of helpContent will just have the "slot" portion as a key //
///////////////////////////////////////////////////////////////////////////////////////////////
const key = data.blockId ? `${data.blockId},${slot}` : slot;
const showHelp = helpHelpActive || hasHelpContent(metaData?.helpContent?.get(key), helpRoles);
if(showHelp)
{
const formattedHelpContent = <HelpContent helpContents={metaData?.helpContent?.get(key)} roles={helpRoles} helpContentKey={`widget:${metaData?.name};slot:${key}`} />;
tooltip = {title: formattedHelpContent, placement: "bottom"}
}
}
let rs = children;
if(link)

View File

@ -24,6 +24,7 @@ import {QWidgetMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/Q
export interface BlockData
{
blockId?: string;
blockTypeName: string;
tooltip?: BlockTooltip;
@ -38,7 +39,7 @@ export interface BlockData
export interface BlockTooltip
{
title: string;
title: string | JSX.Element;
placement: string;
}

View File

@ -28,19 +28,19 @@ import {StandardBlockComponentProps} from "qqq/components/widgets/blocks/BlockMo
**
** ${number} ${icon}
*******************************************************************************/
export default function NumberIconBadgeBlock({data}: StandardBlockComponentProps): JSX.Element
export default function NumberIconBadgeBlock({widgetMetaData, data}: StandardBlockComponentProps): JSX.Element
{
return (
<div style={{display: "inline-block", whiteSpace: "nowrap", color: data.styles.color}}>
{
data.values.number &&
<BlockElementWrapper data={data} slot="number">
<BlockElementWrapper metaData={widgetMetaData} data={data} slot="number">
<span style={{color: data.styles.color, fontSize: "0.875rem"}}>{data.values.number}</span>
</BlockElementWrapper>
}
{
data.values.iconName &&
<BlockElementWrapper data={data} slot="icon">
<BlockElementWrapper metaData={widgetMetaData} data={data} slot="icon">
<Icon style={{color: data.styles.color, fontSize: "1rem", position: "relative", top: "3px"}}>{data.values.iconName}</Icon>
</BlockElementWrapper>
}

View File

@ -35,14 +35,14 @@ import {StandardBlockComponentProps} from "qqq/components/widgets/blocks/BlockMo
** ${heading}
** ${bar} ${value}
*******************************************************************************/
export default function ProgressBarBlock({data}: StandardBlockComponentProps): JSX.Element
export default function ProgressBarBlock({widgetMetaData, data}: StandardBlockComponentProps): JSX.Element
{
return (
<Typography component="div" variant="button" color="text" fontWeight="light" sx={{textTransform: "none"}}>
{
data.values.heading &&
<div style={{marginBottom: "0.25rem", fontWeight: 500, color: "#3D3D3D"}}>
<BlockElementWrapper data={data} slot="heading">
<BlockElementWrapper metaData={widgetMetaData} data={data} slot="heading">
<span>{data.values.heading}</span>
</BlockElementWrapper>
</div>
@ -50,7 +50,7 @@ export default function ProgressBarBlock({data}: StandardBlockComponentProps): J
<div style={{display: "flex", alignItems: "center", marginBottom: "0.75rem"}}>
<BlockElementWrapper data={data} slot="bar" linkProps={{style: {width: "100%"}}}>
<BlockElementWrapper metaData={widgetMetaData} data={data} slot="bar" linkProps={{style: {width: "100%"}}}>
<div style={{background: "#E0E0E0", width: "100%", borderRadius: "0.5rem", height: "1rem"}}>
{
data.values.percent > 0 ? <div style={{background: data.styles.barColor ?? "#0062ff", minWidth: "1rem", width: `${data.values.percent}%`, borderRadius: "0.5rem", height: "1rem"}}></div> : <></>
@ -59,7 +59,7 @@ export default function ProgressBarBlock({data}: StandardBlockComponentProps): J
</BlockElementWrapper>
<div style={{width: "60px", textAlign: "right", fontWeight: 600, color: "#3D3D3D"}}>
<BlockElementWrapper data={data} slot="value">
<BlockElementWrapper metaData={widgetMetaData} data={data} slot="value">
<span>{data.values.value ?? `${(data.values.percent as number).toFixed(1)}%`}</span>
</BlockElementWrapper>
</div>

View File

@ -29,7 +29,7 @@ import {StandardBlockComponentProps} from "qqq/components/widgets/blocks/BlockMo
**
** ${label} ${value}
*******************************************************************************/
export default function TableSubRowDetailRowBlock({data}: StandardBlockComponentProps): JSX.Element
export default function TableSubRowDetailRowBlock({widgetMetaData, data}: StandardBlockComponentProps): JSX.Element
{
return (
<div style={{display: "flex", maxWidth: "calc(100% - 24px)", justifyContent: "space-between"}}>
@ -37,7 +37,7 @@ export default function TableSubRowDetailRowBlock({data}: StandardBlockComponent
{
data.values.label &&
<div style={{overflow: "hidden", whiteSpace: "nowrap", textOverflow: "ellipsis"}}>
<BlockElementWrapper data={data} slot="label">
<BlockElementWrapper metaData={widgetMetaData} data={data} slot="label">
<span style={{color: data.styles.labelColor}}>{data.values.label}</span>
</BlockElementWrapper>
</div>
@ -45,7 +45,7 @@ export default function TableSubRowDetailRowBlock({data}: StandardBlockComponent
{
data.values.value &&
<BlockElementWrapper data={data} slot="value">
<BlockElementWrapper metaData={widgetMetaData} data={data} slot="value">
<span style={{color: data.styles.valueColor}}>{data.values.value}</span>
</BlockElementWrapper>
}

View File

@ -27,10 +27,10 @@ import {StandardBlockComponentProps} from "qqq/components/widgets/blocks/BlockMo
**
** ${text}
*******************************************************************************/
export default function TextBlock({data}: StandardBlockComponentProps): JSX.Element
export default function TextBlock({widgetMetaData, data}: StandardBlockComponentProps): JSX.Element
{
return (
<BlockElementWrapper data={data} slot="">
<BlockElementWrapper metaData={widgetMetaData} data={data} slot="">
<span>{data.values.text}</span>
</BlockElementWrapper>
);

View File

@ -35,7 +35,7 @@ import {StandardBlockComponentProps} from "qqq/components/widgets/blocks/BlockMo
** ${icon} ${number}
** ${context}
*******************************************************************************/
export default function UpOrDownNumberBlock({data}: StandardBlockComponentProps): JSX.Element
export default function UpOrDownNumberBlock({widgetMetaData, data}: StandardBlockComponentProps): JSX.Element
{
if (!data.styles)
{
@ -61,7 +61,7 @@ export default function UpOrDownNumberBlock({data}: StandardBlockComponentProps)
<div style={{display: "flex", flexDirection: data.styles.isStacked ? "column" : "row", alignItems: data.styles.isStacked ? "flex-end" : "baseline"}}>
<div style={{display: "flex", alignItems: "baseline", fontWeight: 700, fontSize: ".875rem"}}>
<BlockElementWrapper data={data} slot="number">
<BlockElementWrapper metaData={widgetMetaData} data={data} slot="number">
<>
<Icon sx={{color: goodOrBadColor, alignSelf: "flex-end", fontSize: "2.25rem !important", lineHeight: "0.875rem", height: "1rem", width: "2rem",}}>{iconName}</Icon>
<span style={{color: goodOrBadColor}}>{data.values.number}</span>
@ -70,7 +70,7 @@ export default function UpOrDownNumberBlock({data}: StandardBlockComponentProps)
</div>
<div style={{fontWeight: 500, fontSize: "0.875rem", color: "#7b809a", marginLeft: "0.25rem"}}>
<BlockElementWrapper data={data} slot="context">
<BlockElementWrapper metaData={widgetMetaData} data={data} slot="context">
<span>{data.values.context}</span>
</BlockElementWrapper>
</div>

View File

@ -77,9 +77,11 @@ function AppHome({app}: Props): JSX.Element
const mdbMetaData = app?.supplementalAppMetaData?.get("materialDashboard");
let showAppLabelOnHomeScreen = true;
let includeTableCountsOnHomeScreen = true;
if(mdbMetaData)
{
showAppLabelOnHomeScreen = mdbMetaData.showAppLabelOnHomeScreen;
includeTableCountsOnHomeScreen = mdbMetaData.includeTableCountsOnHomeScreen;
}
useEffect(() =>
@ -129,48 +131,60 @@ function AppHome({app}: Props): JSX.Element
const tableCountTexts = new Map<string, string>();
newTables.forEach((table) =>
{
tableCounts.set(table.name, {isLoading: true, value: null});
setTimeout(async () =>
if(includeTableCountsOnHomeScreen)
{
const tableMetaData = await qController.loadTableMetaData(table.name);
let countResult = null;
if(tableMetaData.capabilities.has(Capability.TABLE_COUNT) && tableMetaData.readPermission)
tableCounts.set(table.name, {isLoading: true, value: null});
setTimeout(async () =>
{
try
const tableMetaData = await qController.loadTableMetaData(table.name);
let countResult = null;
if (tableMetaData.capabilities.has(Capability.TABLE_COUNT) && tableMetaData.readPermission)
{
[countResult] = await qController.count(table.name);
try
{
[countResult] = await qController.count(table.name);
if (countResult !== null && countResult !== undefined)
{
tableCountNumbers.set(table.name, countResult.toLocaleString());
tableCountTexts.set(table.name, countResult === 1 ? "total record" : "total records");
if (countResult !== null && countResult !== undefined)
{
tableCountNumbers.set(table.name, countResult.toLocaleString());
tableCountTexts.set(table.name, countResult === 1 ? "total record" : "total records");
}
else
{
tableCountNumbers.set(table.name, "");
tableCountTexts.set(table.name, " ");
}
}
else
catch (e)
{
console.log("Caught: " + e);
tableCountNumbers.set(table.name, "");
tableCountTexts.set(table.name, " ");
}
}
catch(e)
else
{
console.log("Caught: " + e);
tableCountNumbers.set(table.name, "");
tableCountTexts.set(table.name, " ");
}
}
else
{
tableCountNumbers.set(table.name, "");
tableCountTexts.set(table.name, " ");
}
tableCounts.set(table.name, {isLoading: false, value: countResult});
tableCounts.set(table.name, {isLoading: false, value: countResult});
setTableCounts(tableCounts);
setTableCountNumbers(tableCountNumbers);
setTableCountTexts(tableCountTexts);
setUpdatedTableCounts(new Date());
}, 1);
}
else
{
tableCounts.set(table.name, {isLoading: false, value: null});
tableCountNumbers.set(table.name, " ");
tableCountTexts.set(table.name, " ");
setTableCounts(tableCounts);
setTableCountNumbers(tableCountNumbers);
setTableCountTexts(tableCountTexts);
setUpdatedTableCounts(new Date());
}, 1);
}
});
setTableCounts(tableCounts);
@ -299,7 +313,7 @@ function AppHome({app}: Props): JSX.Element
</Box>
{
section.processes ? (
<Box p={3} pl={5} pt={0} pb={1}>
<Box p={3} pl={3} pt={0} pb={1}>
<MDTypography variant="h6">Actions</MDTypography>
</Box>
) : null
@ -340,7 +354,7 @@ function AppHome({app}: Props): JSX.Element
}
{
section.reports ? (
<Box p={3} pl={5} pt={0} pb={1}>
<Box p={3} pl={3} pt={0} pb={1}>
<MDTypography variant="h6">Reports</MDTypography>
</Box>
) : null
@ -383,7 +397,7 @@ function AppHome({app}: Props): JSX.Element
}
{
section.tables ? (
<Box p={3} pl={5} pb={1} pt={0}>
<Box p={3} pl={3} pb={1} pt={0}>
<MDTypography variant="h6">Data</MDTypography>
</Box>
) : null
@ -395,6 +409,13 @@ function AppHome({app}: Props): JSX.Element
section.tables.map((tableName) =>
{
let table = app.childMap.get(tableName);
let count = "";
let percentage = "";
if(includeTableCountsOnHomeScreen)
{
count = !tableCounts.has(table.name) || tableCounts.get(table.name).isLoading ? "..." : (tableCountNumbers.get(table.name));
percentage = !tableCounts.has(table.name) || tableCounts.get(table.name).isLoading ? "" : (tableCountTexts.get(table.name));
}
return (
<Grid key={table.name} item xs={12} md={12} lg={tileSizeLg}>
{hasTablePermission(tableName) ?
@ -402,8 +423,8 @@ function AppHome({app}: Props): JSX.Element
<Box className="big-icon" mb={3}>
<MiniStatisticsCard
title={{fontWeight: "bold", text: table.label}}
count={!tableCounts.has(table.name) || tableCounts.get(table.name).isLoading ? "..." : (tableCountNumbers.get(table.name))}
percentage={{color: "info", text: (!tableCounts.has(table.name) || tableCounts.get(table.name).isLoading ? "" : (tableCountTexts.get(table.name)))}}
count={count}
percentage={{color: "info", text: percentage}}
icon={{color: "info", component: <Icon>{table.iconName || app.iconName}</Icon>}}
/>
</Box>
@ -411,8 +432,8 @@ function AppHome({app}: Props): JSX.Element
<Box mb={3} title="You do not have permission to access this table">
<MiniStatisticsCard
title={{fontWeight: "bold", text: table.label}}
count={!tableCounts.has(table.name) || tableCounts.get(table.name).isLoading ? "..." : (tableCountNumbers.get(table.name))}
percentage={{color: "info", text: (!tableCounts.has(table.name) || tableCounts.get(table.name).isLoading ? "" : (tableCountTexts.get(table.name)))}}
count={count}
percentage={{color: "info", text: percentage}}
icon={{color: "info", component: <Icon>{table.iconName || app.iconName}</Icon>}}
isDisabled={true}
/>

View File

@ -114,8 +114,11 @@ class FilterUtils
// 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. //
// also, there are cases where we can get a null or "" as the only value in the //
// values array - avoid sending that to the backend, as it comes back w/ all //
// possible values, and a general "bad time" //
//////////////////////////////////////////////////////////////////////////////////
if (values && values.length > 0)
if (values && values.length > 0 && values[0] !== null && values[0] !== undefined && values[0] !== "")
{
values = await qController.possibleValues(fieldTable.name, null, field.name, "", values);
}