diff --git a/esp/src/src-react/components/Metrics.tsx b/esp/src/src-react/components/Metrics.tsx index 03f1e4d3a72..798dd207711 100644 --- a/esp/src/src-react/components/Metrics.tsx +++ b/esp/src/src-react/components/Metrics.tsx @@ -1,9 +1,9 @@ import * as React from "react"; -import { CommandBar, ContextualMenuItemType, ICommandBarItemProps, IIconProps, SearchBox } from "@fluentui/react"; -import { Label, Spinner } from "@fluentui/react-components"; +import { CommandBar, ContextualMenuItemType, ICommandBarItemProps, IIconProps, SearchBox, Stack } from "@fluentui/react"; +import { Label, Spinner, ToggleButton } from "@fluentui/react-components"; import { typographyStyles } from "@fluentui/react-theme"; import { useConst } from "@fluentui/react-hooks"; -import { bundleIcon, Folder20Filled, Folder20Regular, FolderOpen20Filled, FolderOpen20Regular, } from "@fluentui/react-icons"; +import { bundleIcon, Folder20Filled, Folder20Regular, FolderOpen20Filled, FolderOpen20Regular, TextCaseTitleRegular } from "@fluentui/react-icons"; import { Database } from "@hpcc-js/common"; import { WorkunitsServiceEx, IScope, splitMetric } from "@hpcc-js/comms"; import { DBStore, Table } from "@hpcc-js/dgrid"; @@ -55,7 +55,7 @@ class TableEx extends Table { this._store = new DBStoreEx(this, this._db); } - scopeFilterFunc(row: object, scopeFilter: string): boolean { + scopeFilterFunc(row: object, scopeFilter: string, matchCase: boolean): boolean { const filter = scopeFilter.trim(); if (filter) { let field = ""; @@ -64,12 +64,14 @@ class TableEx extends Table { field = filter.substring(0, colonIdx); } if (field) { - return row[field]?.indexOf && row[field]?.indexOf(filter.substring(colonIdx + 1)) >= 0; + const value: string = !matchCase ? row[field]?.toString().toLowerCase() : row[field]?.toString(); + const filterValue: string = !matchCase ? filter.toLowerCase() : filter; + return value?.indexOf(filterValue.substring(colonIdx + 1)) >= 0 ?? false; } - for (const key in row) { - if (row[key]?.indexOf && row[key]?.indexOf(filter) >= 0) { - return true; - } + for (const field in row) { + const value: string = !matchCase ? row[field].toString().toLowerCase() : row[field].toString(); + const filterValue: string = !matchCase ? filter.toLowerCase() : filter; + return value?.indexOf(filterValue) >= 0 ?? false; } return false; } @@ -77,12 +79,12 @@ class TableEx extends Table { } _rawDataMap: { [id: number]: string } = {}; - metrics(metrics: any[], options: MetricsOptionsT, timelineFilter: string, scopeFilter: string): this { + metrics(metrics: any[], options: MetricsOptionsT, timelineFilter: string, scopeFilter: string, matchCase: boolean): this { this .columns(["##"]) // Reset hash to force recalculation of default widths .columns(["##", nlsHPCC.Type, nlsHPCC.Scope, ...options.properties]) .data(metrics - .filter(m => this.scopeFilterFunc(m, scopeFilter)) + .filter(m => this.scopeFilterFunc(m, scopeFilter, matchCase)) .filter(row => { return (timelineFilter === "" || row.name?.indexOf(timelineFilter) === 0) && (options.scopeTypes.indexOf(row.type) >= 0); @@ -170,6 +172,7 @@ export const Metrics: React.FunctionComponent = ({ const [isLayoutComplete, setIsLayoutComplete] = React.useState(false); const [isRenderComplete, setIsRenderComplete] = React.useState(false); const [dot, setDot] = React.useState(""); + const [matchCase, setMatchCase] = React.useState(false); React.useEffect(() => { const service = new WorkunitsServiceEx({ baseUrl: "" }); @@ -258,7 +261,7 @@ export const Metrics: React.FunctionComponent = ({ const scopesTable = useConst(() => new TableEx() .multiSelect(true) - .metrics([], options, timelineFilter, scopeFilter) + .metrics([], options, timelineFilter, scopeFilter, matchCase) .sortable(true) .on("click", debounce((row, col, sel) => { if (sel) { @@ -269,10 +272,10 @@ export const Metrics: React.FunctionComponent = ({ React.useEffect(() => { scopesTable - .metrics(metrics, options, timelineFilter, scopeFilter) + .metrics(metrics, options, timelineFilter, scopeFilter, matchCase) .render() ; - }, [metrics, options, scopeFilter, scopesTable, timelineFilter]); + }, [matchCase, metrics, options, scopeFilter, scopesTable, timelineFilter]); const updateScopesTable = React.useCallback((selection: IScope[]) => { if (scopesTable?.renderCount() > 0) { @@ -447,13 +450,13 @@ export const Metrics: React.FunctionComponent = ({ }, [lineage, selectedLineage]); // Props Table --- - const propsTable2 = useConst(() => new Table() + const crossTabTable = useConst(() => new Table() .columns([nlsHPCC.Property, nlsHPCC.Value]) .columnWidth("auto") .sortable(true) ); - const updatePropsTable2 = React.useCallback((selection: IScope[]) => { + const updateCrossTabTable = React.useCallback((selection: IScope[]) => { const columns = []; const props = []; selection.forEach(item => { @@ -470,12 +473,13 @@ export const Metrics: React.FunctionComponent = ({ }); props.push(row); }); - propsTable2 - ?.columns(columns) - ?.data(props) - ?.lazyRender() + crossTabTable + .columns([]) + .columns(columns) + .data(props) + .lazyRender() ; - }, [propsTable2]); + }, [crossTabTable]); React.useEffect(() => { const dot = metricGraph.graphTpl(selectedLineage ? [selectedLineage] : [], options); @@ -503,7 +507,7 @@ export const Metrics: React.FunctionComponent = ({ React.useEffect(() => { if (selectedMetrics) { updateScopesTable(selectedMetrics); - updatePropsTable2(selectedMetrics); + updateCrossTabTable(selectedMetrics); updateLineage(selectedMetrics); } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -607,13 +611,13 @@ export const Metrics: React.FunctionComponent = ({ const setShowMetricOptionsHook = React.useCallback((show: boolean) => { setShowMetricOptions(show); scopesTable - .metrics(metrics, options, timelineFilter, scopeFilter) + .metrics(metrics, options, timelineFilter, scopeFilter, matchCase) .render(() => { updateScopesTable(selectedMetrics); }) ; - }, [metrics, options, scopeFilter, scopesTable, selectedMetrics, timelineFilter, updateScopesTable]); + }, [matchCase, metrics, options, scopeFilter, scopesTable, selectedMetrics, timelineFilter, updateScopesTable]); return @@ -625,7 +629,12 @@ export const Metrics: React.FunctionComponent = ({ } + header={ + + + + } title={nlsHPCC.MatchCase} checked={matchCase} onClick={() => { setMatchCase(!matchCase); }} /> + } main={} /> @@ -655,7 +664,7 @@ export const Metrics: React.FunctionComponent = ({ - + diff --git a/esp/src/src-react/components/MetricsPropertiesTables.tsx b/esp/src/src-react/components/MetricsPropertiesTables.tsx index 83d8a894cff..9763fa04654 100644 --- a/esp/src/src-react/components/MetricsPropertiesTables.tsx +++ b/esp/src/src-react/components/MetricsPropertiesTables.tsx @@ -22,7 +22,6 @@ export const MetricsPropertiesTables: React.FunctionComponent new Table() .columns([nlsHPCC.Property, nlsHPCC.Value, "Avg", "Min", "Max", "Delta", "StdDev", "SkewMin", "SkewMax", "NodeMin", "NodeMax"]) - .columnWidth("auto") .sortable(true) ); @@ -53,8 +52,10 @@ export const MetricsPropertiesTables: React.FunctionComponent