mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-23 12:31:07 +01:00
vmui: refactor Cardinality panel (#2726)
* vmui: refactor Cardinality panel * vmui: change width of the search panel * vmui: code cleanup * vmui: code cleanup * vmui: fixed vulnerability (npm audit fix) * wip Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
This commit is contained in:
parent
7b3c9c50a8
commit
af3dc91a51
@ -1,12 +1,12 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "./static/css/main.7e6d0c89.css",
|
||||
"main.js": "./static/js/main.42cb1c78.js",
|
||||
"main.js": "./static/js/main.a6bca65f.js",
|
||||
"static/js/27.939f971b.chunk.js": "./static/js/27.939f971b.chunk.js",
|
||||
"index.html": "./index.html"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.7e6d0c89.css",
|
||||
"static/js/main.42cb1c78.js"
|
||||
"static/js/main.a6bca65f.js"
|
||||
]
|
||||
}
|
@ -1 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><script src="./dashboards/index.js" type="module"></script><script defer="defer" src="./static/js/main.42cb1c78.js"></script><link href="./static/css/main.7e6d0c89.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><script src="./dashboards/index.js" type="module"></script><script defer="defer" src="./static/js/main.a6bca65f.js"></script><link href="./static/css/main.7e6d0c89.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
File diff suppressed because one or more lines are too long
22
app/vmui/packages/vmui/package-lock.json
generated
22
app/vmui/packages/vmui/package-lock.json
generated
@ -17808,16 +17808,6 @@
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/svgo/node_modules/nth-check": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
|
||||
"integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"boolbase": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/symbol-tree": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
||||
@ -32702,7 +32692,7 @@
|
||||
"boolbase": "^1.0.0",
|
||||
"css-what": "^3.2.1",
|
||||
"domutils": "^1.7.0",
|
||||
"nth-check": "^1.0.2"
|
||||
"nth-check": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"css-what": {
|
||||
@ -32753,16 +32743,6 @@
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"nth-check": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
|
||||
"integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"boolbase": "~1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -9,7 +9,7 @@ const BarChart: FC<BarChartProps> = ({
|
||||
configs}) => {
|
||||
|
||||
const uPlotRef = useRef<HTMLDivElement>(null);
|
||||
const [isPanning, setPanning] = useState(false);
|
||||
const [isPanning] = useState(false);
|
||||
const [uPlotInst, setUPlotInst] = useState<uPlot>();
|
||||
const layoutSize = useResize(container);
|
||||
|
||||
|
@ -20,6 +20,10 @@ export interface CardinalityConfiguratorProps {
|
||||
query: string;
|
||||
topN: number;
|
||||
error?: ErrorTypes | string;
|
||||
totalSeries: number;
|
||||
totalLabelValuePairs: number;
|
||||
date: string | null;
|
||||
match: string | null;
|
||||
}
|
||||
|
||||
const CardinalityConfigurator: FC<CardinalityConfiguratorProps> = ({
|
||||
@ -29,7 +33,12 @@ const CardinalityConfigurator: FC<CardinalityConfiguratorProps> = ({
|
||||
onSetHistory,
|
||||
onRunQuery,
|
||||
onSetQuery,
|
||||
onTopNChange }) => {
|
||||
onTopNChange,
|
||||
totalSeries,
|
||||
totalLabelValuePairs,
|
||||
date,
|
||||
match
|
||||
}) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const {queryControls: {autocomplete}} = useAppState();
|
||||
const {queryOptions} = useFetchQueryOptions();
|
||||
@ -41,36 +50,40 @@ const CardinalityConfigurator: FC<CardinalityConfiguratorProps> = ({
|
||||
|
||||
return <Box boxShadow="rgba(99, 99, 99, 0.2) 0px 2px 8px 0px;" p={4} pb={2} mb={2}>
|
||||
<Box>
|
||||
<Box display="grid" gridTemplateColumns="1fr auto auto" gap="4px" width="100%" mb={0}>
|
||||
<Box display="grid" gridTemplateColumns="1fr auto auto" gap="4px" width="50%" mb={4}>
|
||||
<QueryEditor
|
||||
query={query} index={0} autocomplete={autocomplete} queryOptions={queryOptions}
|
||||
error={error} setHistoryIndex={onSetHistory} runQuery={onRunQuery} setQuery={onSetQuery}
|
||||
label={"Arbitrary time series selector"}
|
||||
/>
|
||||
<Tooltip title="Execute Query">
|
||||
<IconButton onClick={onRunQuery} sx={{height: "49px", width: "49px"}}>
|
||||
<PlayCircleOutlineIcon/>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Box display="flex" alignItems="center">
|
||||
<Box ml={2}>
|
||||
<TextField
|
||||
label="Number of top entries"
|
||||
type="number"
|
||||
size="small"
|
||||
variant="outlined"
|
||||
value={topN}
|
||||
error={topN < 1}
|
||||
helperText={topN < 1 ? "Number must be bigger than zero" : " "}
|
||||
onChange={onTopNChange}/>
|
||||
</Box>
|
||||
<Tooltip title="Execute Query">
|
||||
<IconButton onClick={onRunQuery} sx={{height: "49px", width: "49px"}}>
|
||||
<PlayCircleOutlineIcon/>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Box>
|
||||
<FormControlLabel label="Enable autocomplete"
|
||||
control={<BasicSwitch checked={autocomplete} onChange={onChangeAutocomplete}/>}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box display="flex" alignItems="center" mt={3} mr={"53px"}>
|
||||
<Box>
|
||||
<FormControlLabel label="Enable autocomplete"
|
||||
control={<BasicSwitch checked={autocomplete} onChange={onChangeAutocomplete}/>}
|
||||
/>
|
||||
</Box>
|
||||
<Box ml={2}>
|
||||
<TextField
|
||||
label="Number of top entries"
|
||||
type="number"
|
||||
size="small"
|
||||
variant="outlined"
|
||||
value={topN}
|
||||
error={topN < 1}
|
||||
helperText={topN < 1 ? "Number must be bigger than zero" : " "}
|
||||
onChange={onTopNChange}/>
|
||||
</Box>
|
||||
<Box>
|
||||
Analyzed <b>{totalSeries}</b> series and <b>{totalLabelValuePairs}</b> label=value pairs
|
||||
at <b>{date}</b> {match && <span>for series selector <b>{match}</b></span>}. Show top {topN} entries per table.
|
||||
</Box>
|
||||
</Box>;
|
||||
};
|
||||
|
@ -1,26 +1,22 @@
|
||||
import React, {ChangeEvent, FC, useState} from "react";
|
||||
import {SyntheticEvent} from "react";
|
||||
import {Typography, Grid, Alert, Box, Tabs, Tab, Tooltip} from "@mui/material";
|
||||
import TableChartIcon from "@mui/icons-material/TableChart";
|
||||
import ShowChartIcon from "@mui/icons-material/ShowChart";
|
||||
import {Alert} from "@mui/material";
|
||||
import {useFetchQuery} from "../../hooks/useCardinalityFetch";
|
||||
import EnhancedTable from "../Table/Table";
|
||||
import {TSDBStatus, TopHeapEntry, DefaultState, Tabs as TabsType, Containers} from "./types";
|
||||
import {
|
||||
defaultHeadCells,
|
||||
headCellsWithProgress,
|
||||
LABEL_VALUE_PAIR_CONTENT_TITLE,
|
||||
LABEL_VALUE_PAIRS_TABLE_HEADERS,
|
||||
LABEL_WITH_UNIQUE_VALUES_TABLE_HEADERS,
|
||||
LABELS_CONTENT_TITLE, METRICS_TABLE_HEADERS,
|
||||
SERIES_CONTENT_TITLE,
|
||||
SPINNER_TITLE,
|
||||
spinnerContainerStyles
|
||||
} from "./consts";
|
||||
import {defaultProperties, progressCount, queryUpdater, tableTitles} from "./helpers";
|
||||
import {defaultProperties, queryUpdater} from "./helpers";
|
||||
import {Data} from "../Table/types";
|
||||
import BarChart from "../BarChart/BarChart";
|
||||
import CardinalityConfigurator from "./CardinalityConfigurator/CardinalityConfigurator";
|
||||
import {barOptions} from "../BarChart/consts";
|
||||
import Spinner from "../common/Spinner";
|
||||
import TabPanel from "../TabPanel/TabPanel";
|
||||
import {useCardinalityDispatch, useCardinalityState} from "../../state/cardinality/CardinalityStateContext";
|
||||
import {tableCells} from "./TableCells/TableCells";
|
||||
import MetricsContent from "./MetricsContent/MetricsContent";
|
||||
|
||||
const CardinalityPanel: FC = () => {
|
||||
const cardinalityDispatch = useCardinalityDispatch();
|
||||
@ -84,72 +80,45 @@ const CardinalityPanel: FC = () => {
|
||||
</Alert>}
|
||||
/>}
|
||||
<CardinalityConfigurator error={configError} query={query} onRunQuery={onRunQuery} onSetQuery={onSetQuery}
|
||||
onSetHistory={onSetHistory} onTopNChange={onTopNChange} topN={topN} />
|
||||
onSetHistory={onSetHistory} onTopNChange={onTopNChange} topN={topN} date={date} match={match}
|
||||
totalSeries={tsdbStatus.totalSeries} totalLabelValuePairs={tsdbStatus.totalLabelValuePairs}/>
|
||||
{error && <Alert color="error" severity="error" sx={{whiteSpace: "pre-wrap", mt: 2}}>{error}</Alert>}
|
||||
{<Box m={2}>
|
||||
Analyzed <b>{tsdbStatus.totalSeries}</b> series and <b>{tsdbStatus.totalLabelValuePairs}</b> label=value pairs
|
||||
at <b>{date}</b> {match && <span>for series selector <b>{match}</b></span>}. Show top {topN} entries per table.
|
||||
</Box>}
|
||||
{Object.keys(tsdbStatus).map((key ) => {
|
||||
if (key == "totalSeries" || key == "totalLabelValuePairs") return null;
|
||||
const tableTitle = tableTitles[key];
|
||||
const rows = tsdbStatus[key as keyof TSDBStatus] as unknown as Data[];
|
||||
rows.forEach((row) => {
|
||||
progressCount(tsdbStatus.totalSeries, key, row);
|
||||
row.actions = "0";
|
||||
});
|
||||
const headerCells = (key == "seriesCountByMetricName" || key == "seriesCountByLabelValuePair") ? headCellsWithProgress : defaultHeadCells;
|
||||
return (
|
||||
<>
|
||||
<Grid container spacing={2} sx={{px: 2}}>
|
||||
<Grid item xs={12} md={12} lg={12} key={key}>
|
||||
<Typography gutterBottom variant="h5" component="h5">
|
||||
{tableTitle}
|
||||
</Typography>
|
||||
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
|
||||
<Tabs
|
||||
value={stateTabs[key as keyof DefaultState]}
|
||||
onChange={handleTabChange} aria-label="basic tabs example">
|
||||
{defaultProps.tabs[key as keyof TabsType].map((title: string, i: number) =>
|
||||
<Tab
|
||||
key={title}
|
||||
label={title}
|
||||
aria-controls={`tabpanel-${i}`}
|
||||
id={key}
|
||||
iconPosition={"start"}
|
||||
icon={ i === 0 ? <TableChartIcon /> : <ShowChartIcon /> } />
|
||||
)}
|
||||
</Tabs>
|
||||
</Box>
|
||||
{defaultProps.tabs[key as keyof TabsType].map((_,idx) =>
|
||||
<div
|
||||
ref={defaultProps.containerRefs[key as keyof Containers<HTMLDivElement>]}
|
||||
style={{width: "100%", paddingRight: idx !== 0 ? "40px" : 0 }} key={`${key}-${idx}`}>
|
||||
<TabPanel value={stateTabs[key as keyof DefaultState]} index={idx}>
|
||||
{stateTabs[key as keyof DefaultState] === 0 ? <EnhancedTable
|
||||
rows={rows}
|
||||
headerCells={headerCells}
|
||||
defaultSortColumn={"value"}
|
||||
tableCells={(row) => tableCells(row,date,handleFilterClick(key))}
|
||||
/>: <BarChart
|
||||
data={[
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
rows.map((v) => v.name),
|
||||
rows.map((v) => v.value),
|
||||
rows.map((_, i) => i % 12 == 0 ? 1 : i % 10 == 0 ? 2 : 0),
|
||||
]}
|
||||
container={defaultProps.containerRefs[key as keyof Containers<HTMLDivElement>]?.current}
|
||||
configs={barOptions}
|
||||
/>}
|
||||
</TabPanel>
|
||||
</div>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
})}
|
||||
<MetricsContent
|
||||
activeTab={stateTabs.seriesCountByMetricName}
|
||||
rows={tsdbStatus.seriesCountByMetricName as unknown as Data[]}
|
||||
onChange={handleTabChange}
|
||||
onActionClick={handleFilterClick("seriesCountByMetricName")}
|
||||
tabs={defaultProps.tabs.seriesCountByMetricName}
|
||||
chartContainer={defaultProps.containerRefs.seriesCountByMetricName}
|
||||
totalSeries={tsdbStatus.totalSeries}
|
||||
tabId={"seriesCountByMetricName"}
|
||||
sectionTitle={SERIES_CONTENT_TITLE}
|
||||
tableHeaderCells={METRICS_TABLE_HEADERS}
|
||||
/>
|
||||
<MetricsContent
|
||||
activeTab={stateTabs.seriesCountByLabelValuePair}
|
||||
rows={tsdbStatus.seriesCountByLabelValuePair as unknown as Data[]}
|
||||
onChange={handleTabChange}
|
||||
onActionClick={handleFilterClick("seriesCountByLabelValuePair")}
|
||||
tabs={defaultProps.tabs.seriesCountByLabelValuePair}
|
||||
chartContainer={defaultProps.containerRefs.seriesCountByLabelValuePair}
|
||||
totalSeries={tsdbStatus.totalSeries}
|
||||
tabId={"seriesCountByLabelValuePair"}
|
||||
sectionTitle={LABEL_VALUE_PAIR_CONTENT_TITLE}
|
||||
tableHeaderCells={LABEL_VALUE_PAIRS_TABLE_HEADERS}
|
||||
/>
|
||||
<MetricsContent
|
||||
activeTab={stateTabs.labelValueCountByLabelName}
|
||||
rows={tsdbStatus.labelValueCountByLabelName as unknown as Data[]}
|
||||
onChange={handleTabChange}
|
||||
onActionClick={handleFilterClick("labelValueCountByLabelName")}
|
||||
tabs={defaultProps.tabs.labelValueCountByLabelName}
|
||||
chartContainer={defaultProps.containerRefs.labelValueCountByLabelName}
|
||||
totalSeries={-1}
|
||||
tabId={"labelValueCountByLabelName"}
|
||||
sectionTitle={LABELS_CONTENT_TITLE}
|
||||
tableHeaderCells={LABEL_WITH_UNIQUE_VALUES_TABLE_HEADERS}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,96 @@
|
||||
import {FC} from "react";
|
||||
import {Box, Grid, Tab, Tabs, Typography} from "@mui/material";
|
||||
import TableChartIcon from "@mui/icons-material/TableChart";
|
||||
import ShowChartIcon from "@mui/icons-material/ShowChart";
|
||||
import TabPanel from "../../TabPanel/TabPanel";
|
||||
import EnhancedTable from "../../Table/Table";
|
||||
import TableCells from "../TableCells/TableCells";
|
||||
import BarChart from "../../BarChart/BarChart";
|
||||
import {barOptions} from "../../BarChart/consts";
|
||||
import React, {SyntheticEvent} from "react";
|
||||
import {Data, HeadCell} from "../../Table/types";
|
||||
import {MutableRef} from "preact/hooks";
|
||||
|
||||
interface MetricsProperties {
|
||||
rows: Data[];
|
||||
activeTab: number;
|
||||
onChange: (e: SyntheticEvent, newValue: number) => void;
|
||||
onActionClick: (e: SyntheticEvent) => void;
|
||||
tabs: string[];
|
||||
chartContainer: MutableRef<HTMLDivElement> | undefined;
|
||||
totalSeries: number,
|
||||
tabId: string;
|
||||
sectionTitle: string;
|
||||
tableHeaderCells: HeadCell[];
|
||||
}
|
||||
|
||||
const MetricsContent: FC<MetricsProperties> = ({
|
||||
rows,
|
||||
activeTab,
|
||||
onChange,
|
||||
tabs,
|
||||
chartContainer,
|
||||
totalSeries,
|
||||
tabId,
|
||||
onActionClick,
|
||||
sectionTitle,
|
||||
tableHeaderCells
|
||||
}) => {
|
||||
const tableCells = (row: Data) => (
|
||||
<TableCells
|
||||
row={row}
|
||||
totalSeries={totalSeries}
|
||||
onActionClick={onActionClick}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Grid container spacing={2} sx={{px: 2}}>
|
||||
<Grid item xs={12} md={12} lg={12}>
|
||||
<Typography gutterBottom variant="h5" component="h5">{sectionTitle}</Typography>
|
||||
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onChange={onChange} aria-label="basic tabs example">
|
||||
{tabs.map((title: string, i: number) =>
|
||||
<Tab
|
||||
key={title}
|
||||
label={title}
|
||||
aria-controls={`tabpanel-${i}`}
|
||||
id={tabId}
|
||||
iconPosition={"start"}
|
||||
icon={ i === 0 ? <TableChartIcon /> : <ShowChartIcon /> } />
|
||||
)}
|
||||
</Tabs>
|
||||
</Box>
|
||||
{tabs.map((_,idx) =>
|
||||
<div
|
||||
ref={chartContainer}
|
||||
style={{width: "100%", paddingRight: idx !== 0 ? "40px" : 0 }} key={`chart-${idx}`}>
|
||||
<TabPanel value={activeTab} index={idx}>
|
||||
{activeTab === 0 ? <EnhancedTable
|
||||
rows={rows}
|
||||
headerCells={tableHeaderCells}
|
||||
defaultSortColumn={"value"}
|
||||
tableCells={tableCells}
|
||||
/>: <BarChart
|
||||
data={[
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
rows.map((v) => v.name),
|
||||
rows.map((v) => v.value),
|
||||
rows.map((_, i) => i % 12 == 0 ? 1 : i % 10 == 0 ? 2 : 0),
|
||||
]}
|
||||
container={chartContainer?.current || null}
|
||||
configs={barOptions}
|
||||
/>}
|
||||
</TabPanel>
|
||||
</div>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MetricsContent;
|
@ -1,50 +1,39 @@
|
||||
import {SyntheticEvent} from "react";
|
||||
import React, {FC} from "preact/compat";
|
||||
import {TableCell, ButtonGroup} from "@mui/material";
|
||||
import {Data} from "../../Table/types";
|
||||
import {BorderLinearProgressWithLabel} from "../../BorderLineProgress/BorderLinearProgress";
|
||||
import React from "preact/compat";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import PlayCircleOutlineIcon from "@mui/icons-material/PlayCircleOutline";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import {SyntheticEvent} from "react";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export const tableCells = (
|
||||
interface CardinalityTableCells {
|
||||
row: Data,
|
||||
date: string | null,
|
||||
onFilterClick: (e: SyntheticEvent) => void) => {
|
||||
const pathname = window.location.pathname;
|
||||
const withday = dayjs(date).add(1, "day").toDate();
|
||||
return Object.keys(row).map((key, idx) => {
|
||||
if (idx === 0) {
|
||||
return (<TableCell component="th" scope="row" key={key}>
|
||||
{row[key as keyof Data]}
|
||||
</TableCell>);
|
||||
}
|
||||
if (key === "progressValue") {
|
||||
return (
|
||||
<TableCell key={key}>
|
||||
<BorderLinearProgressWithLabel
|
||||
variant="determinate"
|
||||
value={row[key as keyof Data] as number}
|
||||
/>
|
||||
</TableCell>
|
||||
);
|
||||
}
|
||||
if (key === "actions") {
|
||||
const title = `Filter by ${row.name}`;
|
||||
return (<TableCell key={key}>
|
||||
<ButtonGroup variant="contained">
|
||||
<Tooltip title={title}>
|
||||
<IconButton
|
||||
id={row.name}
|
||||
onClick={onFilterClick}
|
||||
sx={{height: "20px", width: "20px"}}>
|
||||
<PlayCircleOutlineIcon/>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
</TableCell>);
|
||||
}
|
||||
return (<TableCell key={key}>{row[key as keyof Data]}</TableCell>);
|
||||
});
|
||||
totalSeries: number;
|
||||
onActionClick: (e: SyntheticEvent) => void;
|
||||
}
|
||||
|
||||
const TableCells: FC<CardinalityTableCells> = ({ row, totalSeries, onActionClick }) => {
|
||||
const progress = totalSeries > 0 ? row.value / totalSeries * 100 : -1;
|
||||
return <>
|
||||
<TableCell key={row.name}>{row.name}</TableCell>
|
||||
<TableCell key={row.value}>{row.value}</TableCell>
|
||||
{progress > 0 ? <TableCell key={row.progressValue}>
|
||||
<BorderLinearProgressWithLabel variant="determinate" value={progress} />
|
||||
</TableCell> : null}
|
||||
<TableCell key={"action"}>
|
||||
<ButtonGroup variant="contained">
|
||||
<Tooltip title={`Filter by ${row.name}`}>
|
||||
<IconButton
|
||||
id={row.name}
|
||||
onClick={onActionClick}
|
||||
sx={{height: "20px", width: "20px"}}>
|
||||
<PlayCircleOutlineIcon/>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
</TableCell>
|
||||
</>;
|
||||
};
|
||||
|
||||
export default TableCells;
|
||||
|
@ -1,16 +1,16 @@
|
||||
import {HeadCell} from "../Table/types";
|
||||
|
||||
export const headCellsWithProgress = [
|
||||
export const METRICS_TABLE_HEADERS = [
|
||||
{
|
||||
disablePadding: false,
|
||||
id: "name",
|
||||
label: "Name",
|
||||
label: "Metrics name",
|
||||
numeric: false,
|
||||
},
|
||||
{
|
||||
disablePadding: false,
|
||||
id: "value",
|
||||
label: "Value",
|
||||
label: "Number of series",
|
||||
numeric: false,
|
||||
},
|
||||
{
|
||||
@ -25,9 +25,55 @@ export const headCellsWithProgress = [
|
||||
label: "Action",
|
||||
numeric: false,
|
||||
}
|
||||
] as HeadCell[];
|
||||
]as HeadCell[];
|
||||
|
||||
export const defaultHeadCells = headCellsWithProgress.filter((head) => head.id!=="percentage");
|
||||
export const LABEL_VALUE_PAIRS_TABLE_HEADERS = [
|
||||
{
|
||||
disablePadding: false,
|
||||
id: "name",
|
||||
label: "Lable=value pair",
|
||||
numeric: false,
|
||||
},
|
||||
{
|
||||
disablePadding: false,
|
||||
id: "value",
|
||||
label: "Number of series",
|
||||
numeric: false,
|
||||
},
|
||||
{
|
||||
disablePadding: false,
|
||||
id: "percentage",
|
||||
label: "Percent of total label value pairs",
|
||||
numeric: false,
|
||||
},
|
||||
{
|
||||
disablePadding: false,
|
||||
id: "action",
|
||||
label: "Action",
|
||||
numeric: false,
|
||||
}
|
||||
]as HeadCell[];
|
||||
|
||||
export const LABEL_WITH_UNIQUE_VALUES_TABLE_HEADERS = [
|
||||
{
|
||||
disablePadding: false,
|
||||
id: "name",
|
||||
label: "Label name",
|
||||
numeric: false,
|
||||
},
|
||||
{
|
||||
disablePadding: false,
|
||||
id: "value",
|
||||
label: "Number of unique values",
|
||||
numeric: false,
|
||||
},
|
||||
{
|
||||
disablePadding: false,
|
||||
id: "action",
|
||||
label: "Action",
|
||||
numeric: false,
|
||||
}
|
||||
] as HeadCell[];
|
||||
|
||||
export const spinnerContainerStyles = (height: string) => {
|
||||
return {
|
||||
@ -41,4 +87,8 @@ export const spinnerContainerStyles = (height: string) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const SPINNER_TITLE = "Please wait while cardinality stats is calculated. This may take some time if the db contains big number of time series";
|
||||
export const SPINNER_TITLE = "Please wait while cardinality stats is calculated. " +
|
||||
"This may take some time if the db contains big number of time series";
|
||||
export const SERIES_CONTENT_TITLE = "Metric names with the highest number of series";
|
||||
export const LABEL_VALUE_PAIR_CONTENT_TITLE = "Label=value pairs with the highest number of series";
|
||||
export const LABELS_CONTENT_TITLE = "Labels with the highest number of unique values";
|
||||
|
@ -1,13 +1,6 @@
|
||||
import {Containers, DefaultState, QueryUpdater, Tabs, TSDBStatus, TypographyFunctions} from "./types";
|
||||
import {Data} from "../Table/types";
|
||||
import {Containers, DefaultState, QueryUpdater, Tabs, TSDBStatus} from "./types";
|
||||
import {useRef} from "preact/compat";
|
||||
|
||||
export const tableTitles: {[key: string]: string} = {
|
||||
"seriesCountByMetricName": "Metric names with the highest number of series",
|
||||
"seriesCountByLabelValuePair": "Label=value pairs with the highest number of series",
|
||||
"labelValueCountByLabelName": "Labels with the highest number of unique values",
|
||||
};
|
||||
|
||||
export const queryUpdater: QueryUpdater = {
|
||||
labelValueCountByLabelName: (query: string): string => `{${query}!=""}`,
|
||||
seriesCountByLabelValuePair: (query: string): string => {
|
||||
@ -25,14 +18,6 @@ const getSeriesSelector = (label: string, value: string): string => {
|
||||
return "{" + label + "=" + JSON.stringify(value) + "}";
|
||||
};
|
||||
|
||||
export const progressCount = (totalSeries: number, key: string, row: Data): Data => {
|
||||
if (key === "seriesCountByMetricName" || key === "seriesCountByLabelValuePair") {
|
||||
row.progressValue = row.value / totalSeries * 100;
|
||||
return row;
|
||||
}
|
||||
return row;
|
||||
};
|
||||
|
||||
export const defaultProperties = (tsdbStatus: TSDBStatus) => {
|
||||
return Object.keys(tsdbStatus).reduce((acc, key) => {
|
||||
if (key === "totalSeries" || key === "totalLabelValuePairs") return acc;
|
||||
|
@ -13,10 +13,6 @@ export interface TopHeapEntry {
|
||||
count: number;
|
||||
}
|
||||
|
||||
export type TypographyFunctions = {
|
||||
[key: string]: (value: number) => string,
|
||||
}
|
||||
|
||||
export type QueryUpdater = {
|
||||
[key: string]: (query: string) => string,
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {Box, Paper, Table, TableBody, TableCell, TableContainer, TablePagination, TableRow,} from "@mui/material";
|
||||
import React, {FC, useState} from "preact/compat";
|
||||
import {ChangeEvent, MouseEvent, SyntheticEvent} from "react";
|
||||
import {ChangeEvent, MouseEvent} from "react";
|
||||
import {Data, Order, TableProps,} from "./types";
|
||||
import {EnhancedTableHead} from "./TableHead";
|
||||
import {getComparator, stableSort} from "./helpers";
|
||||
@ -37,7 +37,7 @@ const EnhancedTable: FC<TableProps> = ({
|
||||
setSelected([]);
|
||||
};
|
||||
|
||||
const handleClick = (event: SyntheticEvent, name: string) => {
|
||||
const handleClick = (name: string) => () => {
|
||||
const selectedIndex = selected.indexOf(name);
|
||||
let newSelected: readonly string[] = [];
|
||||
|
||||
@ -101,7 +101,7 @@ const EnhancedTable: FC<TableProps> = ({
|
||||
return (
|
||||
<TableRow
|
||||
hover
|
||||
onClick={(event) => handleClick(event, row.name)}
|
||||
onClick={handleClick(row.name)}
|
||||
role="checkbox"
|
||||
aria-checked={isItemSelected}
|
||||
tabIndex={-1}
|
||||
|
@ -10,7 +10,7 @@ export function descendingComparator<T>(a: T, b: T, orderBy: keyof T) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function getComparator<Key extends keyof any>(
|
||||
export function getComparator<Key extends (string | number | symbol)>(
|
||||
order: Order,
|
||||
orderBy: Key,
|
||||
): (
|
||||
|
@ -23,7 +23,7 @@ export interface TableProps {
|
||||
rows: Data[];
|
||||
headerCells: HeadCell[],
|
||||
defaultSortColumn: keyof Data,
|
||||
tableCells: (row: Data) => ReactNode[],
|
||||
tableCells: (row: Data) => ReactNode,
|
||||
isPagingEnabled?: boolean,
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user