mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-01-20 07:19:17 +01:00
vmui: add set up series custom limits (#3368)
* feat: add set up series custom limits * feat: add button for show series without limits * fix: resolve merge conflicts
This commit is contained in:
parent
e45022c344
commit
a59c7a0dd7
@ -1,9 +1,10 @@
|
||||
@use "src/styles/variables" as *;
|
||||
$chart-tooltip-width: 300px;
|
||||
$chart-tooltip-icon-width: 25px;
|
||||
$chart-tooltip-half-icon: calc($chart-tooltip-icon-width/2);
|
||||
$chart-tooltip-date-width: $chart-tooltip-width - (2*$chart-tooltip-icon-width) - (2*$padding-global) - $padding-small;
|
||||
$chart-tooltip-x: -1 * ($padding-small + $padding-global + $chart-tooltip-date-width + ($chart-tooltip-icon-width/2));
|
||||
$chart-tooltip-y: -1 * ($padding-small + ($chart-tooltip-icon-width/2));
|
||||
$chart-tooltip-x: -1 * ($padding-small + $padding-global + $chart-tooltip-date-width + $chart-tooltip-half-icon);
|
||||
$chart-tooltip-y: -1 * ($padding-small + $chart-tooltip-half-icon);
|
||||
|
||||
.vm-chart-tooltip {
|
||||
position: absolute;
|
||||
@ -54,9 +55,12 @@ $chart-tooltip-y: -1 * ($padding-small + ($chart-tooltip-icon-width/2));
|
||||
}
|
||||
|
||||
&-data {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: $padding-small;
|
||||
align-items: flex-start;
|
||||
word-break: break-all;
|
||||
line-height: 12px;
|
||||
|
||||
&__value {
|
||||
padding: 4px;
|
||||
@ -66,7 +70,6 @@ $chart-tooltip-y: -1 * ($padding-small + ($chart-tooltip-icon-width/2));
|
||||
&__marker {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: $padding-small;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,28 +6,33 @@ import Button from "../../Main/Button/Button";
|
||||
import Modal from "../../Main/Modal/Modal";
|
||||
import "./style.scss";
|
||||
import Tooltip from "../../Main/Tooltip/Tooltip";
|
||||
import LimitsConfigurator from "./LimitsConfigurator/LimitsConfigurator";
|
||||
import { SeriesLimits } from "../../../types";
|
||||
import { useCustomPanelDispatch, useCustomPanelState } from "../../../state/customPanel/CustomPanelStateContext";
|
||||
|
||||
const title = "Setting Server URL";
|
||||
const title = "Settings";
|
||||
|
||||
const GlobalSettings: FC = () => {
|
||||
|
||||
const { serverUrl } = useAppState();
|
||||
const { serverUrl: stateServerUrl } = useAppState();
|
||||
const { seriesLimits } = useCustomPanelState();
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const [changedServerUrl, setChangedServerUrl] = useState(serverUrl);
|
||||
const customPanelDispatch = useCustomPanelDispatch();
|
||||
|
||||
const setServer = (url?: string) => {
|
||||
dispatch({ type: "SET_SERVER", payload: url || changedServerUrl });
|
||||
handleClose();
|
||||
};
|
||||
|
||||
const createSetServer = () => () => {
|
||||
setServer();
|
||||
};
|
||||
const [serverUrl, setServerUrl] = useState(stateServerUrl);
|
||||
const [limits, setLimits] = useState<SeriesLimits>(seriesLimits);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const handleOpen = () => setOpen(true);
|
||||
const handleClose = () => setOpen(false);
|
||||
|
||||
const handlerApply = () => {
|
||||
dispatch({ type: "SET_SERVER", payload: serverUrl });
|
||||
customPanelDispatch({ type: "SET_SERIES_LIMITS", payload: limits });
|
||||
handleClose();
|
||||
};
|
||||
|
||||
return <>
|
||||
<Tooltip title={title}>
|
||||
<Button
|
||||
@ -46,8 +51,16 @@ const GlobalSettings: FC = () => {
|
||||
<div className="vm-server-configurator">
|
||||
<div className="vm-server-configurator__input">
|
||||
<ServerConfigurator
|
||||
setServer={setChangedServerUrl}
|
||||
onEnter={setServer}
|
||||
serverUrl={serverUrl}
|
||||
onChange={setServerUrl}
|
||||
onEnter={handlerApply}
|
||||
/>
|
||||
</div>
|
||||
<div className="vm-server-configurator__input">
|
||||
<LimitsConfigurator
|
||||
limits={limits}
|
||||
onChange={setLimits}
|
||||
onEnter={handlerApply}
|
||||
/>
|
||||
</div>
|
||||
<div className="vm-server-configurator__footer">
|
||||
@ -60,7 +73,7 @@ const GlobalSettings: FC = () => {
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={createSetServer()}
|
||||
onClick={handlerApply}
|
||||
>
|
||||
apply
|
||||
</Button>
|
||||
|
@ -0,0 +1,88 @@
|
||||
import React, { FC, useState } from "preact/compat";
|
||||
import { DisplayType, ErrorTypes, SeriesLimits } from "../../../../types";
|
||||
import TextField from "../../../Main/TextField/TextField";
|
||||
import Tooltip from "../../../Main/Tooltip/Tooltip";
|
||||
import { InfoIcon, RestartIcon } from "../../../Main/Icons";
|
||||
import Button from "../../../Main/Button/Button";
|
||||
import { DEFAULT_MAX_SERIES } from "../../../../constants/graph";
|
||||
import "./style.scss";
|
||||
|
||||
export interface ServerConfiguratorProps {
|
||||
limits: SeriesLimits
|
||||
onChange: (limits: SeriesLimits) => void
|
||||
onEnter: () => void
|
||||
}
|
||||
|
||||
const fields: {label: string, type: DisplayType}[] = [
|
||||
{ label: "Graph", type: "chart" },
|
||||
{ label: "JSON", type: "code" },
|
||||
{ label: "Table", type: "table" }
|
||||
];
|
||||
|
||||
const LimitsConfigurator: FC<ServerConfiguratorProps> = ({ limits, onChange , onEnter }) => {
|
||||
|
||||
const [error, setError] = useState({
|
||||
table: "",
|
||||
chart: "",
|
||||
code: ""
|
||||
});
|
||||
|
||||
const handleChange = (val: string, type: DisplayType) => {
|
||||
const value = val || "";
|
||||
setError(prev => ({ ...prev, [type]: +value < 0 ? ErrorTypes.positiveNumber : "" }));
|
||||
onChange({
|
||||
...limits,
|
||||
[type]: !value ? Infinity : value
|
||||
});
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
onChange(DEFAULT_MAX_SERIES);
|
||||
};
|
||||
|
||||
const createChangeHandler = (type: DisplayType) => (val: string) => {
|
||||
handleChange(val, type);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="vm-limits-configurator">
|
||||
<div className="vm-limits-configurator-title">
|
||||
Series limits by tabs
|
||||
<Tooltip title="To disable limits set to 0">
|
||||
<Button
|
||||
variant="text"
|
||||
color="primary"
|
||||
size="small"
|
||||
startIcon={<InfoIcon/>}
|
||||
/>
|
||||
</Tooltip>
|
||||
<div className="vm-limits-configurator-title__reset">
|
||||
<Button
|
||||
variant="text"
|
||||
color="primary"
|
||||
size="small"
|
||||
startIcon={<RestartIcon/>}
|
||||
onClick={handleReset}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="vm-limits-configurator__inputs">
|
||||
{fields.map(f => (
|
||||
<TextField
|
||||
key={f.type}
|
||||
label={f.label}
|
||||
value={limits[f.type]}
|
||||
error={error[f.type]}
|
||||
onChange={createChangeHandler(f.type)}
|
||||
onEnter={onEnter}
|
||||
type="number"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LimitsConfigurator;
|
@ -0,0 +1,28 @@
|
||||
@use "src/styles/variables" as *;
|
||||
|
||||
.vm-limits-configurator {
|
||||
|
||||
&-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
font-size: $font-size;
|
||||
font-weight: bold;
|
||||
margin-bottom: $padding-global;
|
||||
|
||||
&__reset {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&__inputs {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: $padding-global;
|
||||
}
|
||||
}
|
@ -1,41 +1,34 @@
|
||||
import React, { FC, useState } from "preact/compat";
|
||||
import { useAppState } from "../../../../state/common/StateContext";
|
||||
import { ErrorTypes } from "../../../../types";
|
||||
import TextField from "../../../Main/TextField/TextField";
|
||||
import { isValidHttpUrl } from "../../../../utils/url";
|
||||
|
||||
export interface ServerConfiguratorProps {
|
||||
setServer: (url: string) => void
|
||||
onEnter: (url: string) => void
|
||||
serverUrl: string
|
||||
onChange: (url: string) => void
|
||||
onEnter: () => void
|
||||
}
|
||||
|
||||
const ServerConfigurator: FC<ServerConfiguratorProps> = ({ setServer , onEnter }) => {
|
||||
const ServerConfigurator: FC<ServerConfiguratorProps> = ({ serverUrl, onChange , onEnter }) => {
|
||||
|
||||
const { serverUrl } = useAppState();
|
||||
const [error, setError] = useState("");
|
||||
const [changedServerUrl, setChangedServerUrl] = useState(serverUrl);
|
||||
|
||||
const onChangeServer = (val: string) => {
|
||||
const value = val || "";
|
||||
setChangedServerUrl(value);
|
||||
setServer(value);
|
||||
onChange(value);
|
||||
setError("");
|
||||
if (!value) setError(ErrorTypes.emptyServer);
|
||||
if (!isValidHttpUrl(value)) setError(ErrorTypes.validServer);
|
||||
};
|
||||
|
||||
const handleEnter = () => {
|
||||
onEnter(changedServerUrl);
|
||||
};
|
||||
|
||||
return (
|
||||
<TextField
|
||||
autofocus
|
||||
label="Server URL"
|
||||
value={changedServerUrl}
|
||||
value={serverUrl}
|
||||
error={error}
|
||||
onChange={onChangeServer}
|
||||
onEnter={handleEnter}
|
||||
onEnter={onEnter}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -3,7 +3,7 @@
|
||||
.vm-server-configurator {
|
||||
display: grid;
|
||||
align-items: center;
|
||||
gap: $padding-global;
|
||||
gap: $padding-medium;
|
||||
width: 600px;
|
||||
|
||||
&__input {
|
||||
|
@ -113,11 +113,11 @@ const QueryEditor: FC<QueryEditorProps> = ({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!autocomplete || !foundOptions.length) return;
|
||||
if (!autocomplete) return;
|
||||
setFocusOption(-1);
|
||||
const words = (value.match(/[a-zA-Z_:.][a-zA-Z0-9_:.]*/gm) || []).length;
|
||||
setOpenAutocomplete(autocomplete && value.length > 2 && words <= 1);
|
||||
}, [autocomplete, value, foundOptions]);
|
||||
}, [autocomplete, value]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!wrapperEl.current) return;
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React, { FC } from "preact/compat";
|
||||
import { ReactNode } from "react";
|
||||
import classNames from "classnames";
|
||||
import "./style.scss";
|
||||
import { ErrorIcon, InfoIcon, SuccessIcon, WarningIcon } from "../Icons";
|
||||
import "./style.scss";
|
||||
|
||||
interface AlertProps {
|
||||
variant?: "success" | "error" | "info" | "warning"
|
||||
|
@ -4,7 +4,7 @@
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: 20px 1fr;
|
||||
align-items: flex-start;
|
||||
align-items: center;
|
||||
gap: $padding-small;
|
||||
padding: $padding-global;
|
||||
background-color: $color-background-block;
|
||||
|
@ -5,7 +5,7 @@ import "./style.scss";
|
||||
|
||||
interface ButtonProps {
|
||||
variant?: "contained" | "outlined" | "text"
|
||||
color?: "primary" | "secondary" | "success" | "error" | "gray"
|
||||
color?: "primary" | "secondary" | "success" | "error" | "gray" | "warning"
|
||||
size?: "small" | "medium" | "large"
|
||||
endIcon?: ReactNode
|
||||
startIcon?: ReactNode
|
||||
|
@ -143,6 +143,15 @@ $button-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&_contained_warning {
|
||||
color: $color-warning;
|
||||
|
||||
&:before {
|
||||
background-color: $color-warning;
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* variant TEXT */
|
||||
&_text_primary {
|
||||
@ -165,6 +174,10 @@ $button-radius: 6px;
|
||||
color: $color-text-secondary;
|
||||
}
|
||||
|
||||
&_text_warning {
|
||||
color: $color-warning;
|
||||
}
|
||||
|
||||
|
||||
/* variant OUTLINED */
|
||||
&_outlined_primary {
|
||||
@ -191,4 +204,9 @@ $button-radius: 6px;
|
||||
border: 1px solid $color-text-secondary;
|
||||
color: $color-text-secondary;
|
||||
}
|
||||
|
||||
&_outlined_warning {
|
||||
border: 1px solid $color-warning;
|
||||
color: $color-warning;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
export const MAX_QUERY_FIELDS = 4;
|
||||
export const MAX_SERIES = {
|
||||
export const DEFAULT_MAX_SERIES = {
|
||||
table: 100,
|
||||
chart: 20,
|
||||
code: Infinity,
|
||||
code: 1000,
|
||||
};
|
||||
|
@ -3,11 +3,10 @@ import { getQueryRangeUrl, getQueryUrl } from "../api/query-range";
|
||||
import { useAppState } from "../state/common/StateContext";
|
||||
import { InstantMetricResult, MetricBase, MetricResult } from "../api/types";
|
||||
import { isValidHttpUrl } from "../utils/url";
|
||||
import { ErrorTypes } from "../types";
|
||||
import { ErrorTypes, SeriesLimits } from "../types";
|
||||
import debounce from "lodash.debounce";
|
||||
import { DisplayType } from "../pages/CustomPanel/DisplayTypeSwitch";
|
||||
import Trace from "../components/TraceQuery/Trace";
|
||||
import { MAX_SERIES } from "../constants/graph";
|
||||
import { useQueryState } from "../state/query/QueryStateContext";
|
||||
import { useTimeState } from "../state/time/TimeStateContext";
|
||||
import { useCustomPanelState } from "../state/customPanel/CustomPanelStateContext";
|
||||
@ -18,9 +17,10 @@ interface FetchQueryParams {
|
||||
display?: DisplayType,
|
||||
customStep: number,
|
||||
hideQuery?: number[]
|
||||
showAllSeries?: boolean
|
||||
}
|
||||
|
||||
export const useFetchQuery = ({ predefinedQuery, visible, display, customStep, hideQuery }: FetchQueryParams): {
|
||||
interface FetchQueryReturn {
|
||||
fetchUrl?: string[],
|
||||
isLoading: boolean,
|
||||
graphData?: MetricResult[],
|
||||
@ -28,10 +28,28 @@ export const useFetchQuery = ({ predefinedQuery, visible, display, customStep, h
|
||||
error?: ErrorTypes | string,
|
||||
warning?: string,
|
||||
traces?: Trace[],
|
||||
} => {
|
||||
}
|
||||
|
||||
interface FetchDataParams {
|
||||
fetchUrl: string[],
|
||||
fetchQueue: AbortController[],
|
||||
displayType: DisplayType,
|
||||
query: string[],
|
||||
stateSeriesLimits: SeriesLimits,
|
||||
showAllSeries?: boolean,
|
||||
}
|
||||
|
||||
export const useFetchQuery = ({
|
||||
predefinedQuery,
|
||||
visible,
|
||||
display,
|
||||
customStep,
|
||||
hideQuery,
|
||||
showAllSeries
|
||||
}: FetchQueryParams): FetchQueryReturn => {
|
||||
const { query } = useQueryState();
|
||||
const { period } = useTimeState();
|
||||
const { displayType, nocache, isTracingEnabled } = useCustomPanelState();
|
||||
const { displayType, nocache, isTracingEnabled, seriesLimits: stateSeriesLimits } = useCustomPanelState();
|
||||
const { serverUrl } = useAppState();
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@ -50,12 +68,19 @@ export const useFetchQuery = ({ predefinedQuery, visible, display, customStep, h
|
||||
}
|
||||
}, [error]);
|
||||
|
||||
const fetchData = async (fetchUrl: string[], fetchQueue: AbortController[], displayType: DisplayType, query: string[]) => {
|
||||
const fetchData = async ({
|
||||
fetchUrl,
|
||||
fetchQueue,
|
||||
displayType,
|
||||
query,
|
||||
stateSeriesLimits,
|
||||
showAllSeries,
|
||||
}: FetchDataParams) => {
|
||||
const controller = new AbortController();
|
||||
setFetchQueue([...fetchQueue, controller]);
|
||||
try {
|
||||
const isDisplayChart = displayType === "chart";
|
||||
const seriesLimit = MAX_SERIES[displayType];
|
||||
const seriesLimit = showAllSeries ? Infinity : stateSeriesLimits[displayType];
|
||||
const tempData: MetricBase[] = [];
|
||||
const tempTraces: Trace[] = [];
|
||||
let counter = 1;
|
||||
@ -131,8 +156,15 @@ export const useFetchQuery = ({ predefinedQuery, visible, display, customStep, h
|
||||
if (!visible || !fetchUrl?.length) return;
|
||||
setIsLoading(true);
|
||||
const expr = predefinedQuery ?? query;
|
||||
throttledFetchData(fetchUrl, fetchQueue, (display || displayType), expr);
|
||||
}, [fetchUrl, visible]);
|
||||
throttledFetchData({
|
||||
fetchUrl,
|
||||
fetchQueue,
|
||||
displayType: display || displayType,
|
||||
query: expr,
|
||||
stateSeriesLimits,
|
||||
showAllSeries,
|
||||
});
|
||||
}, [fetchUrl, visible, stateSeriesLimits, showAllSeries]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchPast = fetchQueue.slice(0, -1);
|
||||
|
@ -19,6 +19,7 @@ import { useSetQueryParams } from "./hooks/useSetQueryParams";
|
||||
import "./style.scss";
|
||||
import Alert from "../../components/Main/Alert/Alert";
|
||||
import TableView from "../../components/Views/TableView/TableView";
|
||||
import Button from "../../components/Main/Button/Button";
|
||||
|
||||
const CustomPanel: FC = () => {
|
||||
const { displayType, isTracingEnabled } = useCustomPanelState();
|
||||
@ -30,6 +31,7 @@ const CustomPanel: FC = () => {
|
||||
const [displayColumns, setDisplayColumns] = useState<string[]>();
|
||||
const [tracesState, setTracesState] = useState<Trace[]>([]);
|
||||
const [hideQuery, setHideQuery] = useState<number[]>([]);
|
||||
const [showAllSeries, setShowAllSeries] = useState(false);
|
||||
|
||||
const { customStep, yaxis } = useGraphState();
|
||||
const graphDispatch = useGraphDispatch();
|
||||
@ -38,7 +40,8 @@ const CustomPanel: FC = () => {
|
||||
const { isLoading, liveData, graphData, error, warning, traces } = useFetchQuery({
|
||||
visible: true,
|
||||
customStep,
|
||||
hideQuery
|
||||
hideQuery,
|
||||
showAllSeries
|
||||
});
|
||||
|
||||
const setYaxisLimits = (limits: AxisRange) => {
|
||||
@ -53,6 +56,10 @@ const CustomPanel: FC = () => {
|
||||
timeDispatch({ type: "SET_PERIOD", payload: { from, to } });
|
||||
};
|
||||
|
||||
const handleShowAll = () => {
|
||||
setShowAllSeries(true);
|
||||
};
|
||||
|
||||
const handleTraceDelete = (trace: Trace) => {
|
||||
const updatedTraces = tracesState.filter((data) => data.idValue !== trace.idValue);
|
||||
setTracesState([...updatedTraces]);
|
||||
@ -72,6 +79,10 @@ const CustomPanel: FC = () => {
|
||||
setTracesState([]);
|
||||
}, [displayType]);
|
||||
|
||||
useEffect(() => {
|
||||
setShowAllSeries(false);
|
||||
}, [query]);
|
||||
|
||||
return (
|
||||
<div className="vm-custom-panel">
|
||||
<QueryConfigurator
|
||||
@ -89,7 +100,18 @@ const CustomPanel: FC = () => {
|
||||
)}
|
||||
{isLoading && <Spinner />}
|
||||
{error && <Alert variant="error">{error}</Alert>}
|
||||
{warning && <Alert variant="warning">{warning}</Alert>}
|
||||
{warning && <Alert variant="warning">
|
||||
<div className="vm-custom-panel__warning">
|
||||
<p>{warning}</p>
|
||||
<Button
|
||||
color="warning"
|
||||
variant="outlined"
|
||||
onClick={handleShowAll}
|
||||
>
|
||||
Show all
|
||||
</Button>
|
||||
</div>
|
||||
</Alert>}
|
||||
<div className="vm-custom-panel-body vm-block">
|
||||
<div className="vm-custom-panel-body-header">
|
||||
<DisplayTypeSwitch/>
|
||||
|
@ -6,6 +6,13 @@
|
||||
gap: $padding-medium;
|
||||
height: 100%;
|
||||
|
||||
&__warning {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&__trace {
|
||||
}
|
||||
|
||||
|
@ -1,24 +1,31 @@
|
||||
import { DisplayType, displayTypeTabs } from "../../pages/CustomPanel/DisplayTypeSwitch";
|
||||
import { getFromStorage, saveToStorage } from "../../utils/storage";
|
||||
import { getQueryStringValue } from "../../utils/query-string";
|
||||
import { SeriesLimits } from "../../types";
|
||||
import { DEFAULT_MAX_SERIES } from "../../constants/graph";
|
||||
|
||||
export interface CustomPanelState {
|
||||
displayType: DisplayType;
|
||||
nocache: boolean;
|
||||
isTracingEnabled: boolean;
|
||||
seriesLimits: SeriesLimits
|
||||
}
|
||||
|
||||
export type CustomPanelAction =
|
||||
| { type: "SET_DISPLAY_TYPE", payload: DisplayType }
|
||||
| { type: "SET_SERIES_LIMITS", payload: SeriesLimits }
|
||||
| { type: "TOGGLE_NO_CACHE"}
|
||||
| { type: "TOGGLE_QUERY_TRACING" }
|
||||
|
||||
const queryTab = getQueryStringValue("g0.tab", 0) as string;
|
||||
const displayType = displayTypeTabs.find(t => t.prometheusCode === +queryTab || t.value === queryTab);
|
||||
const limitsStorage = getFromStorage("SERIES_LIMITS") as string;
|
||||
|
||||
export const initialCustomPanelState: CustomPanelState = {
|
||||
displayType: (displayType?.value || "chart") as DisplayType,
|
||||
nocache: false,
|
||||
isTracingEnabled: false,
|
||||
seriesLimits: limitsStorage ? JSON.parse(getFromStorage("SERIES_LIMITS") as string) : DEFAULT_MAX_SERIES
|
||||
};
|
||||
|
||||
export function reducer(state: CustomPanelState, action: CustomPanelAction): CustomPanelState {
|
||||
@ -28,6 +35,12 @@ export function reducer(state: CustomPanelState, action: CustomPanelAction): Cus
|
||||
...state,
|
||||
displayType: action.payload
|
||||
};
|
||||
case "SET_SERIES_LIMITS":
|
||||
saveToStorage("SERIES_LIMITS", JSON.stringify(action.payload));
|
||||
return {
|
||||
...state,
|
||||
seriesLimits: action.payload
|
||||
};
|
||||
case "TOGGLE_QUERY_TRACING":
|
||||
return {
|
||||
...state,
|
||||
|
@ -44,6 +44,7 @@ export enum ErrorTypes {
|
||||
validQuery = "Please enter a valid Query and execute it",
|
||||
traceNotFound = "Not found the tracing information",
|
||||
emptyTitle = "Please enter title",
|
||||
positiveNumber = "Please enter positive number"
|
||||
}
|
||||
|
||||
export interface PanelSettings {
|
||||
@ -98,3 +99,9 @@ export interface TopQueriesData extends TopQueryStats{
|
||||
topByCount: TopQuery[]
|
||||
topBySumDuration: TopQuery[]
|
||||
}
|
||||
|
||||
export interface SeriesLimits {
|
||||
table: number,
|
||||
chart: number,
|
||||
code: number,
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ export type StorageKeys = "BASIC_AUTH_DATA"
|
||||
| "AUTOCOMPLETE"
|
||||
| "NO_CACHE"
|
||||
| "QUERY_TRACING"
|
||||
| "SERIES_LIMITS"
|
||||
|
||||
export const saveToStorage = (key: StorageKeys, value: string | boolean | Record<string, unknown>): void => {
|
||||
if (value) {
|
||||
|
@ -26,6 +26,7 @@ The following tip changes can be tested by building VictoriaMetrics components f
|
||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add the ability to hide results of a particular query by clicking the `eye` icon. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3359).
|
||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add copy button to row on Table view. The button copies row in MetricQL format. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2815).
|
||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add the ability to "stick" a tooltip on the chart by clicking on a data point. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3321) and [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3376)
|
||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add the ability to set up series custom limits. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3297).
|
||||
* FEATURE: [vmalert](https://docs.victoriametrics.com/vmalert.html): add default alert list for vmalert's metrics. See [alerts-vmalert.yml](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/deployment/docker/alerts-vmalert.yml).
|
||||
* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): expose `vmagent_relabel_config_*`, `vm_relabel_config_*` and `vm_promscrape_config_*` metrics for tracking relabel and scrape configuration hot-reloads. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3345).
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user