mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-03-11 15:34:56 +00:00
vmui: make the step input field global across all the tabs and views (#3644)
* feat: make the step input field global * fix: correct get step from url * fix: set minimumSignificantDigits to 1 * app/vmselect/vmui: `make vmui-update` Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
This commit is contained in:
parent
556484a52f
commit
060780af69
23 changed files with 220 additions and 126 deletions
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"files": {
|
"files": {
|
||||||
"main.css": "./static/css/main.7672c15c.css",
|
"main.css": "./static/css/main.01144815.css",
|
||||||
"main.js": "./static/js/main.84759f8d.js",
|
"main.js": "./static/js/main.f9c0c050.js",
|
||||||
"static/js/27.c1ccfd29.chunk.js": "./static/js/27.c1ccfd29.chunk.js",
|
"static/js/27.c1ccfd29.chunk.js": "./static/js/27.c1ccfd29.chunk.js",
|
||||||
"index.html": "./index.html"
|
"index.html": "./index.html"
|
||||||
},
|
},
|
||||||
"entrypoints": [
|
"entrypoints": [
|
||||||
"static/css/main.7672c15c.css",
|
"static/css/main.01144815.css",
|
||||||
"static/js/main.84759f8d.js"
|
"static/js/main.f9c0c050.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="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono&family=Lato:wght@300;400;700&display=swap" rel="stylesheet"><script src="./dashboards/index.js" type="module"></script><script defer="defer" src="./static/js/main.84759f8d.js"></script><link href="./static/css/main.7672c15c.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="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono&family=Lato:wght@300;400;700&display=swap" rel="stylesheet"><script src="./dashboards/index.js" type="module"></script><script defer="defer" src="./static/js/main.f9c0c050.js"></script><link href="./static/css/main.01144815.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
1
app/vmselect/vmui/static/css/main.01144815.css
Normal file
1
app/vmselect/vmui/static/css/main.01144815.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
app/vmselect/vmui/static/js/main.f9c0c050.js
Normal file
2
app/vmselect/vmui/static/js/main.f9c0c050.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -17,6 +17,17 @@
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React Router DOM v6.4.5
|
||||||
|
*
|
||||||
|
* Copyright (c) Remix Software Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE.md file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* React Router v6.4.5
|
* React Router v6.4.5
|
||||||
*
|
*
|
|
@ -1,20 +1,13 @@
|
||||||
import React, { FC, useEffect } from "preact/compat";
|
import React, { FC } from "preact/compat";
|
||||||
import StepConfigurator from "../StepConfigurator/StepConfigurator";
|
|
||||||
import { useGraphDispatch, useGraphState } from "../../../state/graph/GraphStateContext";
|
|
||||||
import { getAppModeParams } from "../../../utils/app-mode";
|
import { getAppModeParams } from "../../../utils/app-mode";
|
||||||
import TenantsConfiguration from "../TenantsConfiguration/TenantsConfiguration";
|
import TenantsConfiguration from "../TenantsConfiguration/TenantsConfiguration";
|
||||||
import { useCustomPanelDispatch, useCustomPanelState } from "../../../state/customPanel/CustomPanelStateContext";
|
import { useCustomPanelDispatch, useCustomPanelState } from "../../../state/customPanel/CustomPanelStateContext";
|
||||||
import { useTimeState } from "../../../state/time/TimeStateContext";
|
|
||||||
import { useQueryDispatch, useQueryState } from "../../../state/query/QueryStateContext";
|
import { useQueryDispatch, useQueryState } from "../../../state/query/QueryStateContext";
|
||||||
import "./style.scss";
|
import "./style.scss";
|
||||||
import Switch from "../../Main/Switch/Switch";
|
import Switch from "../../Main/Switch/Switch";
|
||||||
import usePrevious from "../../../hooks/usePrevious";
|
|
||||||
|
|
||||||
const AdditionalSettings: FC = () => {
|
const AdditionalSettings: FC = () => {
|
||||||
|
|
||||||
const { customStep } = useGraphState();
|
|
||||||
const graphDispatch = useGraphDispatch();
|
|
||||||
|
|
||||||
const { inputTenantID } = getAppModeParams();
|
const { inputTenantID } = getAppModeParams();
|
||||||
|
|
||||||
const { autocomplete } = useQueryState();
|
const { autocomplete } = useQueryState();
|
||||||
|
@ -23,9 +16,6 @@ const AdditionalSettings: FC = () => {
|
||||||
const { nocache, isTracingEnabled } = useCustomPanelState();
|
const { nocache, isTracingEnabled } = useCustomPanelState();
|
||||||
const customPanelDispatch = useCustomPanelDispatch();
|
const customPanelDispatch = useCustomPanelDispatch();
|
||||||
|
|
||||||
const { period: { step }, duration } = useTimeState();
|
|
||||||
const prevDuration = usePrevious(duration);
|
|
||||||
|
|
||||||
const onChangeCache = () => {
|
const onChangeCache = () => {
|
||||||
customPanelDispatch({ type: "TOGGLE_NO_CACHE" });
|
customPanelDispatch({ type: "TOGGLE_NO_CACHE" });
|
||||||
};
|
};
|
||||||
|
@ -38,19 +28,6 @@ const AdditionalSettings: FC = () => {
|
||||||
queryDispatch({ type: "TOGGLE_AUTOCOMPLETE" });
|
queryDispatch({ type: "TOGGLE_AUTOCOMPLETE" });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onChangeStep = (value: string) => {
|
|
||||||
graphDispatch({ type: "SET_CUSTOM_STEP", payload: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!customStep && step) onChangeStep(step);
|
|
||||||
}, [step]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (duration === prevDuration || !prevDuration) return;
|
|
||||||
if (step) onChangeStep(step);
|
|
||||||
}, [duration, prevDuration]);
|
|
||||||
|
|
||||||
return <div className="vm-additional-settings">
|
return <div className="vm-additional-settings">
|
||||||
<Switch
|
<Switch
|
||||||
label={"Autocomplete"}
|
label={"Autocomplete"}
|
||||||
|
@ -67,13 +44,6 @@ const AdditionalSettings: FC = () => {
|
||||||
value={isTracingEnabled}
|
value={isTracingEnabled}
|
||||||
onChange={onChangeQueryTracing}
|
onChange={onChangeQueryTracing}
|
||||||
/>
|
/>
|
||||||
<div className="vm-additional-settings__input">
|
|
||||||
<StepConfigurator
|
|
||||||
defaultStep={step}
|
|
||||||
setStep={onChangeStep}
|
|
||||||
value={customStep}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{!!inputTenantID && (
|
{!!inputTenantID && (
|
||||||
<div className="vm-additional-settings__input">
|
<div className="vm-additional-settings__input">
|
||||||
<TenantsConfiguration/>
|
<TenantsConfiguration/>
|
||||||
|
|
|
@ -1,26 +1,59 @@
|
||||||
import React, { FC, useEffect, useState } from "preact/compat";
|
import React, { FC, useEffect, useRef, useState } from "preact/compat";
|
||||||
import { RestartIcon } from "../../Main/Icons";
|
import { RestartIcon, TimelineIcon } from "../../Main/Icons";
|
||||||
import TextField from "../../Main/TextField/TextField";
|
import TextField from "../../Main/TextField/TextField";
|
||||||
import Button from "../../Main/Button/Button";
|
import Button from "../../Main/Button/Button";
|
||||||
import Tooltip from "../../Main/Tooltip/Tooltip";
|
import Tooltip from "../../Main/Tooltip/Tooltip";
|
||||||
import { ErrorTypes } from "../../../types";
|
import { ErrorTypes } from "../../../types";
|
||||||
import { supportedDurations } from "../../../utils/time";
|
import { supportedDurations } from "../../../utils/time";
|
||||||
|
import { useTimeState } from "../../../state/time/TimeStateContext";
|
||||||
|
import { useGraphDispatch, useGraphState } from "../../../state/graph/GraphStateContext";
|
||||||
|
import usePrevious from "../../../hooks/usePrevious";
|
||||||
|
import "./style.scss";
|
||||||
|
import { getAppModeEnable } from "../../../utils/app-mode";
|
||||||
|
import Popper from "../../Main/Popper/Popper";
|
||||||
|
|
||||||
interface StepConfiguratorProps {
|
const StepConfigurator: FC = () => {
|
||||||
defaultStep?: string,
|
const appModeEnable = getAppModeEnable();
|
||||||
value?: string,
|
|
||||||
setStep: (step: string) => void,
|
|
||||||
}
|
|
||||||
|
|
||||||
const StepConfigurator: FC<StepConfiguratorProps> = ({ value, defaultStep, setStep }) => {
|
const { customStep: value } = useGraphState();
|
||||||
|
const { period: { step: defaultStep } } = useTimeState();
|
||||||
|
const graphDispatch = useGraphDispatch();
|
||||||
|
|
||||||
|
const { period: duration } = useTimeState();
|
||||||
|
const prevDuration = usePrevious(duration.end - duration.start);
|
||||||
|
|
||||||
|
const [openOptions, setOpenOptions] = useState(false);
|
||||||
const [customStep, setCustomStep] = useState(value || defaultStep);
|
const [customStep, setCustomStep] = useState(value || defaultStep);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
|
|
||||||
|
const buttonRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const toggleOpenOptions = () => {
|
||||||
|
setOpenOptions(prev => !prev);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseOptions = () => {
|
||||||
|
setOpenOptions(false);
|
||||||
|
};
|
||||||
|
|
||||||
const handleApply = (value?: string) => {
|
const handleApply = (value?: string) => {
|
||||||
const step = value || customStep || defaultStep || "1s";
|
const step = value || customStep || defaultStep || "1s";
|
||||||
const durations = step.match(/[a-zA-Z]+/g) || [];
|
const durations = step.match(/[a-zA-Z]+/g) || [];
|
||||||
setStep(!durations.length ? `${step}s` : step);
|
const stepDur = !durations.length ? `${step}s` : step;
|
||||||
|
graphDispatch({ type: "SET_CUSTOM_STEP", payload: stepDur });
|
||||||
|
setCustomStep(stepDur);
|
||||||
|
setError("");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFocus = () => {
|
||||||
|
if (document.activeElement instanceof HTMLInputElement) {
|
||||||
|
document.activeElement.select();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEnter = () => {
|
||||||
|
handleApply();
|
||||||
|
handleCloseOptions();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChangeStep = (value: string) => {
|
const handleChangeStep = (value: string) => {
|
||||||
|
@ -46,28 +79,94 @@ const StepConfigurator: FC<StepConfiguratorProps> = ({ value, defaultStep, setSt
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (value) handleChangeStep(value);
|
if (value) {
|
||||||
|
handleApply(value);
|
||||||
|
}
|
||||||
}, [value]);
|
}, [value]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!value && defaultStep) {
|
||||||
|
handleApply(defaultStep);
|
||||||
|
}
|
||||||
|
}, [defaultStep]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const dur = duration.end - duration.start;
|
||||||
|
if (dur === prevDuration || !prevDuration) return;
|
||||||
|
if (defaultStep) {
|
||||||
|
handleApply(defaultStep);
|
||||||
|
}
|
||||||
|
}, [duration, prevDuration, defaultStep]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TextField
|
<div
|
||||||
label="Step value"
|
className="vm-step-control"
|
||||||
value={customStep}
|
ref={buttonRef}
|
||||||
error={error}
|
>
|
||||||
onChange={handleChangeStep}
|
<Tooltip title="Query resolution step width">
|
||||||
onEnter={handleApply}
|
<Button
|
||||||
onBlur={handleApply}
|
className={appModeEnable ? "" : "vm-header-button"}
|
||||||
endIcon={(
|
variant="contained"
|
||||||
<Tooltip title="Reset step to default">
|
color="primary"
|
||||||
<Button
|
startIcon={<TimelineIcon/>}
|
||||||
variant={"text"}
|
onClick={toggleOpenOptions}
|
||||||
size={"small"}
|
>
|
||||||
startIcon={<RestartIcon/>}
|
STEP {customStep}
|
||||||
onClick={handleReset}
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
<Popper
|
||||||
|
open={openOptions}
|
||||||
|
placement="bottom-right"
|
||||||
|
onClose={handleCloseOptions}
|
||||||
|
buttonRef={buttonRef}
|
||||||
|
>
|
||||||
|
<div className="vm-step-control-popper">
|
||||||
|
<TextField
|
||||||
|
autofocus
|
||||||
|
label="Step value"
|
||||||
|
value={customStep}
|
||||||
|
error={error}
|
||||||
|
onChange={handleChangeStep}
|
||||||
|
onEnter={handleEnter}
|
||||||
|
onFocus={handleFocus}
|
||||||
|
onBlur={handleApply}
|
||||||
|
endIcon={(
|
||||||
|
<Tooltip title={`Set default step value: ${defaultStep}`}>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
variant="text"
|
||||||
|
color="primary"
|
||||||
|
startIcon={<RestartIcon/>}
|
||||||
|
onClick={handleReset}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
<div className="vm-step-control-popper-info">
|
||||||
)}
|
<code>step</code> - the <a
|
||||||
/>
|
className="vm-link vm-link_colored"
|
||||||
|
href="https://prometheus.io/docs/prometheus/latest/querying/basics/#time-durations"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
interval
|
||||||
|
</a>
|
||||||
|
between datapoints, which must be returned from the range query.
|
||||||
|
The <code>query</code> is executed at
|
||||||
|
<code>start</code>, <code>start+step</code>, <code>start+2*step</code>, …, <code>end</code> timestamps.
|
||||||
|
<a
|
||||||
|
className="vm-link vm-link_colored"
|
||||||
|
href="https://docs.victoriametrics.com/keyConcepts.html#range-query"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
Read more about Range query
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Popper>
|
||||||
|
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
@use "src/styles/variables" as *;
|
||||||
|
@use "src/components/Main/Button/style" as *;
|
||||||
|
|
||||||
|
.vm-step-control {
|
||||||
|
display: inline-flex;
|
||||||
|
|
||||||
|
button {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-popper {
|
||||||
|
display: grid;
|
||||||
|
gap: $padding-small;
|
||||||
|
max-width: 300px;
|
||||||
|
max-height: 208px;
|
||||||
|
overflow: auto;
|
||||||
|
padding: $padding-global;
|
||||||
|
font-size: $font-size;
|
||||||
|
|
||||||
|
&-info {
|
||||||
|
font-size: $font-size-small;
|
||||||
|
line-height: 1.6;
|
||||||
|
|
||||||
|
a {
|
||||||
|
margin: 0 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
padding: 0.2em 0.4em;
|
||||||
|
margin: 0 0.2em;
|
||||||
|
font-size: 85%;
|
||||||
|
background-color: rgba($color-black, 0.05);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,6 @@
|
||||||
import React, { FC, useEffect, useMemo } from "preact/compat";
|
import React, { FC, useMemo } from "preact/compat";
|
||||||
import Select from "../../Main/Select/Select";
|
import Select from "../../Main/Select/Select";
|
||||||
import StepConfigurator from "../../Configurators/StepConfigurator/StepConfigurator";
|
|
||||||
import "./style.scss";
|
import "./style.scss";
|
||||||
import { useTimeState } from "../../../state/time/TimeStateContext";
|
|
||||||
import { useGraphDispatch, useGraphState } from "../../../state/graph/GraphStateContext";
|
|
||||||
import usePrevious from "../../../hooks/usePrevious";
|
|
||||||
import { GRAPH_SIZES } from "../../../constants/graph";
|
import { GRAPH_SIZES } from "../../../constants/graph";
|
||||||
|
|
||||||
interface ExploreMetricsHeaderProps {
|
interface ExploreMetricsHeaderProps {
|
||||||
|
@ -36,28 +32,9 @@ const ExploreMetricsHeader: FC<ExploreMetricsHeaderProps> = ({
|
||||||
onToggleMetric,
|
onToggleMetric,
|
||||||
onChangeSize
|
onChangeSize
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
const { period: { step }, duration } = useTimeState();
|
|
||||||
const { customStep } = useGraphState();
|
|
||||||
const graphDispatch = useGraphDispatch();
|
|
||||||
const prevDuration = usePrevious(duration);
|
|
||||||
|
|
||||||
const noInstanceText = useMemo(() => job ? "" : "No instances. Please select job", [job]);
|
const noInstanceText = useMemo(() => job ? "" : "No instances. Please select job", [job]);
|
||||||
const noMetricsText = useMemo(() => job ? "" : "No metric names. Please select job", [job]);
|
const noMetricsText = useMemo(() => job ? "" : "No metric names. Please select job", [job]);
|
||||||
|
|
||||||
const handleChangeStep = (value: string) => {
|
|
||||||
graphDispatch({ type: "SET_CUSTOM_STEP", payload: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (duration === prevDuration || !prevDuration) return;
|
|
||||||
if (customStep) handleChangeStep(step || "1s");
|
|
||||||
}, [duration, prevDuration]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!customStep && step) handleChangeStep(step);
|
|
||||||
}, [step]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="vm-explore-metrics-header vm-block">
|
<div className="vm-explore-metrics-header vm-block">
|
||||||
<div className="vm-explore-metrics-header__job">
|
<div className="vm-explore-metrics-header__job">
|
||||||
|
@ -81,13 +58,6 @@ const ExploreMetricsHeader: FC<ExploreMetricsHeaderProps> = ({
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="vm-explore-metrics-header__step">
|
|
||||||
<StepConfigurator
|
|
||||||
defaultStep={step}
|
|
||||||
setStep={handleChangeStep}
|
|
||||||
value={customStep}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="vm-explore-metrics-header__size">
|
<div className="vm-explore-metrics-header__size">
|
||||||
<Select
|
<Select
|
||||||
label="Size graphs"
|
label="Size graphs"
|
||||||
|
|
|
@ -8,7 +8,7 @@ const Footer: FC = () => {
|
||||||
|
|
||||||
return <footer className="vm-footer">
|
return <footer className="vm-footer">
|
||||||
<a
|
<a
|
||||||
className="vm__link vm-footer__website"
|
className="vm-link vm-footer__website"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href="https://victoriametrics.com/"
|
href="https://victoriametrics.com/"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
|
@ -17,7 +17,7 @@ const Footer: FC = () => {
|
||||||
victoriametrics.com
|
victoriametrics.com
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
className="vm__link"
|
className="vm-link"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href="https://github.com/VictoriaMetrics/VictoriaMetrics/issues/new/choose"
|
href="https://github.com/VictoriaMetrics/VictoriaMetrics/issues/new/choose"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
|
|
|
@ -15,6 +15,7 @@ import Tabs from "../../Main/Tabs/Tabs";
|
||||||
import "./style.scss";
|
import "./style.scss";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { useDashboardsState } from "../../../state/dashboards/DashboardsStateContext";
|
import { useDashboardsState } from "../../../state/dashboards/DashboardsStateContext";
|
||||||
|
import StepConfigurator from "../../Configurators/StepConfigurator/StepConfigurator";
|
||||||
|
|
||||||
const Header: FC = () => {
|
const Header: FC = () => {
|
||||||
const primaryColor = getCssVariable("color-primary");
|
const primaryColor = getCssVariable("color-primary");
|
||||||
|
@ -101,6 +102,7 @@ const Header: FC = () => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="vm-header__settings">
|
<div className="vm-header__settings">
|
||||||
|
{headerSetup?.stepControl && <StepConfigurator/>}
|
||||||
{headerSetup?.timeSelector && <TimeSelector/>}
|
{headerSetup?.timeSelector && <TimeSelector/>}
|
||||||
{headerSetup?.cardinalityDatePicker && <CardinalityDatePicker/>}
|
{headerSetup?.cardinalityDatePicker && <CardinalityDatePicker/>}
|
||||||
{headerSetup?.executionControls && <ExecutionControls/>}
|
{headerSetup?.executionControls && <ExecutionControls/>}
|
||||||
|
|
|
@ -333,3 +333,14 @@ export const ResizeIcon = () => (
|
||||||
<path d="M21 11V3h-8l3.29 3.29-10 10L3 13v8h8l-3.29-3.29 10-10z"></path>
|
<path d="M21 11V3h-8l3.29 3.29-10 10L3 13v8h8l-3.29-3.29 10-10z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const TimelineIcon = () => (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M23 8c0 1.1-.9 2-2 2-.18 0-.35-.02-.51-.07l-3.56 3.55c.05.16.07.34.07.52 0 1.1-.9 2-2 2s-2-.9-2-2c0-.18.02-.36.07-.52l-2.55-2.55c-.16.05-.34.07-.52.07s-.36-.02-.52-.07l-4.55 4.56c.05.16.07.33.07.51 0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2c.18 0 .35.02.51.07l4.56-4.55C8.02 9.36 8 9.18 8 9c0-1.1.9-2 2-2s2 .9 2 2c0 .18-.02.36-.07.52l2.55 2.55c.16-.05.34-.07.52-.07s.36.02.52.07l3.55-3.56C19.02 8.35 19 8.18 19 8c0-1.1.9-2 2-2s2 .9 2 2z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { useEffect } from "react";
|
||||||
import { compactObject } from "../../../utils/object";
|
import { compactObject } from "../../../utils/object";
|
||||||
import { useTimeState } from "../../../state/time/TimeStateContext";
|
import { useTimeState } from "../../../state/time/TimeStateContext";
|
||||||
import { setQueryStringWithoutPageReload } from "../../../utils/query-string";
|
import { setQueryStringWithoutPageReload } from "../../../utils/query-string";
|
||||||
|
import { useGraphState } from "../../../state/graph/GraphStateContext";
|
||||||
|
|
||||||
interface queryProps {
|
interface queryProps {
|
||||||
job: string
|
job: string
|
||||||
|
@ -11,13 +12,14 @@ interface queryProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useSetQueryParams = ({ job, instance, metrics, size }: queryProps) => {
|
export const useSetQueryParams = ({ job, instance, metrics, size }: queryProps) => {
|
||||||
const { duration, relativeTime, period: { date, step } } = useTimeState();
|
const { duration, relativeTime, period: { date } } = useTimeState();
|
||||||
|
const { customStep } = useGraphState();
|
||||||
|
|
||||||
const setSearchParamsFromState = () => {
|
const setSearchParamsFromState = () => {
|
||||||
const params = compactObject({
|
const params = compactObject({
|
||||||
["g0.range_input"]: duration,
|
["g0.range_input"]: duration,
|
||||||
["g0.end_input"]: date,
|
["g0.end_input"]: date,
|
||||||
["g0.step_input"]: step,
|
["g0.step_input"]: customStep,
|
||||||
["g0.relative_time"]: relativeTime,
|
["g0.relative_time"]: relativeTime,
|
||||||
size,
|
size,
|
||||||
job,
|
job,
|
||||||
|
@ -28,6 +30,6 @@ export const useSetQueryParams = ({ job, instance, metrics, size }: queryProps)
|
||||||
setQueryStringWithoutPageReload(params);
|
setQueryStringWithoutPageReload(params);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(setSearchParamsFromState, [duration, relativeTime, date, step, job, instance, metrics, size]);
|
useEffect(setSearchParamsFromState, [duration, relativeTime, date, customStep, job, instance, metrics, size]);
|
||||||
useEffect(setSearchParamsFromState, []);
|
useEffect(setSearchParamsFromState, []);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { AxisRange, YaxisState } from "../../../state/graph/reducer";
|
||||||
import GraphView from "../../../components/Views/GraphView/GraphView";
|
import GraphView from "../../../components/Views/GraphView/GraphView";
|
||||||
import { useFetchQuery } from "../../../hooks/useFetchQuery";
|
import { useFetchQuery } from "../../../hooks/useFetchQuery";
|
||||||
import Spinner from "../../../components/Main/Spinner/Spinner";
|
import Spinner from "../../../components/Main/Spinner/Spinner";
|
||||||
import StepConfigurator from "../../../components/Configurators/StepConfigurator/StepConfigurator";
|
|
||||||
import GraphSettings from "../../../components/Configurators/GraphSettings/GraphSettings";
|
import GraphSettings from "../../../components/Configurators/GraphSettings/GraphSettings";
|
||||||
import { marked } from "marked";
|
import { marked } from "marked";
|
||||||
import { useTimeDispatch, useTimeState } from "../../../state/time/TimeStateContext";
|
import { useTimeDispatch, useTimeState } from "../../../state/time/TimeStateContext";
|
||||||
|
@ -12,7 +11,7 @@ import { InfoIcon } from "../../../components/Main/Icons";
|
||||||
import "./style.scss";
|
import "./style.scss";
|
||||||
import Alert from "../../../components/Main/Alert/Alert";
|
import Alert from "../../../components/Main/Alert/Alert";
|
||||||
import Tooltip from "../../../components/Main/Tooltip/Tooltip";
|
import Tooltip from "../../../components/Main/Tooltip/Tooltip";
|
||||||
import usePrevious from "../../../hooks/usePrevious";
|
import { useGraphState } from "../../../state/graph/GraphStateContext";
|
||||||
|
|
||||||
export interface PredefinedPanelsProps extends PanelSettings {
|
export interface PredefinedPanelsProps extends PanelSettings {
|
||||||
filename: string;
|
filename: string;
|
||||||
|
@ -28,13 +27,12 @@ const PredefinedPanel: FC<PredefinedPanelsProps> = ({
|
||||||
alias
|
alias
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
const { period, duration } = useTimeState();
|
const { period } = useTimeState();
|
||||||
|
const { customStep } = useGraphState();
|
||||||
const dispatch = useTimeDispatch();
|
const dispatch = useTimeDispatch();
|
||||||
const prevDuration = usePrevious(duration);
|
|
||||||
|
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const [visible, setVisible] = useState(true);
|
const [visible, setVisible] = useState(true);
|
||||||
const [customStep, setCustomStep] = useState(period.step || "1s");
|
|
||||||
const [yaxis, setYaxis] = useState<YaxisState>({
|
const [yaxis, setYaxis] = useState<YaxisState>({
|
||||||
limits: {
|
limits: {
|
||||||
enable: false,
|
enable: false,
|
||||||
|
@ -77,11 +75,6 @@ const PredefinedPanel: FC<PredefinedPanelsProps> = ({
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (duration === prevDuration || !prevDuration) return;
|
|
||||||
if (customStep) setCustomStep(period.step || "1s");
|
|
||||||
}, [duration, prevDuration]);
|
|
||||||
|
|
||||||
if (!validExpr) return (
|
if (!validExpr) return (
|
||||||
<Alert variant="error">
|
<Alert variant="error">
|
||||||
<code>"expr"</code> not found. Check the configuration file <b>{filename}</b>.
|
<code>"expr"</code> not found. Check the configuration file <b>{filename}</b>.
|
||||||
|
@ -123,13 +116,6 @@ const PredefinedPanel: FC<PredefinedPanelsProps> = ({
|
||||||
<h3 className="vm-predefined-panel-header__title">
|
<h3 className="vm-predefined-panel-header__title">
|
||||||
{title || ""}
|
{title || ""}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="vm-predefined-panel-header__step">
|
|
||||||
<StepConfigurator
|
|
||||||
defaultStep={period.step}
|
|
||||||
value={customStep}
|
|
||||||
setStep={setCustomStep}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<GraphSettings
|
<GraphSettings
|
||||||
yaxis={yaxis}
|
yaxis={yaxis}
|
||||||
setYaxisLimits={setYaxisLimits}
|
setYaxisLimits={setYaxisLimits}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
&-header {
|
&-header {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto 1fr 160px auto;
|
grid-template-columns: auto 1fr auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
gap: $padding-small;
|
gap: $padding-small;
|
||||||
|
|
|
@ -2,21 +2,23 @@ import { useEffect } from "react";
|
||||||
import { compactObject } from "../../../utils/object";
|
import { compactObject } from "../../../utils/object";
|
||||||
import { useTimeState } from "../../../state/time/TimeStateContext";
|
import { useTimeState } from "../../../state/time/TimeStateContext";
|
||||||
import { setQueryStringWithoutPageReload } from "../../../utils/query-string";
|
import { setQueryStringWithoutPageReload } from "../../../utils/query-string";
|
||||||
|
import { useGraphState } from "../../../state/graph/GraphStateContext";
|
||||||
|
|
||||||
export const useSetQueryParams = () => {
|
export const useSetQueryParams = () => {
|
||||||
const { duration, relativeTime, period: { date, step } } = useTimeState();
|
const { duration, relativeTime, period: { date } } = useTimeState();
|
||||||
|
const { customStep } = useGraphState();
|
||||||
|
|
||||||
const setSearchParamsFromState = () => {
|
const setSearchParamsFromState = () => {
|
||||||
const params = compactObject({
|
const params = compactObject({
|
||||||
["g0.range_input"]: duration,
|
["g0.range_input"]: duration,
|
||||||
["g0.end_input"]: date,
|
["g0.end_input"]: date,
|
||||||
["g0.step_input"]: step,
|
["g0.step_input"]: customStep,
|
||||||
["g0.relative_time"]: relativeTime
|
["g0.relative_time"]: relativeTime
|
||||||
});
|
});
|
||||||
|
|
||||||
setQueryStringWithoutPageReload(params);
|
setQueryStringWithoutPageReload(params);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(setSearchParamsFromState, [duration, relativeTime, date, step]);
|
useEffect(setSearchParamsFromState, [duration, relativeTime, date, customStep]);
|
||||||
useEffect(setSearchParamsFromState, []);
|
useEffect(setSearchParamsFromState, []);
|
||||||
};
|
};
|
||||||
|
|
|
@ -40,7 +40,7 @@ const Index: FC = () => {
|
||||||
const getQueryStatsTitle = (key: keyof TopQueryStats) => {
|
const getQueryStatsTitle = (key: keyof TopQueryStats) => {
|
||||||
if (!data) return key;
|
if (!data) return key;
|
||||||
const value = data[key];
|
const value = data[key];
|
||||||
if (typeof value === "number") return formatPrettyNumber(value);
|
if (typeof value === "number") return formatPrettyNumber(value, 0, value);
|
||||||
return value || key;
|
return value || key;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -148,7 +148,7 @@ const TracePage: FC = () => {
|
||||||
{"\n"}
|
{"\n"}
|
||||||
In order to use tracing please refer to the doc:
|
In order to use tracing please refer to the doc:
|
||||||
<a
|
<a
|
||||||
className="vm__link vm__link_colored"
|
className="vm-link vm-link_colored"
|
||||||
href="https://docs.victoriametrics.com/#query-tracing"
|
href="https://docs.victoriametrics.com/#query-tracing"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
|
|
|
@ -11,6 +11,7 @@ const router = {
|
||||||
export interface RouterOptions {
|
export interface RouterOptions {
|
||||||
title?: string,
|
title?: string,
|
||||||
header: {
|
header: {
|
||||||
|
stepControl?: boolean,
|
||||||
timeSelector?: boolean,
|
timeSelector?: boolean,
|
||||||
executionControls?: boolean,
|
executionControls?: boolean,
|
||||||
globalSettings?: boolean,
|
globalSettings?: boolean,
|
||||||
|
@ -20,6 +21,7 @@ export interface RouterOptions {
|
||||||
|
|
||||||
const routerOptionsDefault = {
|
const routerOptionsDefault = {
|
||||||
header: {
|
header: {
|
||||||
|
stepControl: true,
|
||||||
timeSelector: true,
|
timeSelector: true,
|
||||||
executionControls: true,
|
executionControls: true,
|
||||||
}
|
}
|
||||||
|
@ -33,6 +35,7 @@ export const routerOptions: {[key: string]: RouterOptions} = {
|
||||||
[router.metrics]: {
|
[router.metrics]: {
|
||||||
title: "Explore metrics",
|
title: "Explore metrics",
|
||||||
header: {
|
header: {
|
||||||
|
stepControl: true,
|
||||||
timeSelector: true,
|
timeSelector: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
@use "src/styles/variables" as *;
|
@use "src/styles/variables" as *;
|
||||||
|
|
||||||
.vm__link {
|
.vm-link {
|
||||||
transition: color 200ms ease;
|
transition: color 200ms ease;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ The following tip changes can be tested by building VictoriaMetrics components f
|
||||||
## tip
|
## tip
|
||||||
|
|
||||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add ability to show custom dashboards at vmui by specifying a path to a directory with dashboard config files via `-vmui.customDashboardsPath` command-line flag. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3322) and [these docs](https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/app/vmui/packages/vmui/public/dashboards).
|
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add ability to show custom dashboards at vmui by specifying a path to a directory with dashboard config files via `-vmui.customDashboardsPath` command-line flag. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3322) and [these docs](https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/app/vmui/packages/vmui/public/dashboards).
|
||||||
|
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): apply the `step` globally to all the displayed graphs. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3574).
|
||||||
|
|
||||||
* BUGFIX: reduce the increased CPU usage at `vmselect` to v1.85.3 level when processing heavy queries. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3641).
|
* BUGFIX: reduce the increased CPU usage at `vmselect` to v1.85.3 level when processing heavy queries. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3641).
|
||||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): [dockerswarm_sd_configs](https://docs.victoriametrics.com/sd_configs.html#dockerswarm_sd_configs): apply `filters` only to objects of the specified `role`. Previously filters were applied to all the objects, which could cause errors when different types of objects were used with filters that were not compatible with them. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3579).
|
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): [dockerswarm_sd_configs](https://docs.victoriametrics.com/sd_configs.html#dockerswarm_sd_configs): apply `filters` only to objects of the specified `role`. Previously filters were applied to all the objects, which could cause errors when different types of objects were used with filters that were not compatible with them. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3579).
|
||||||
|
|
Loading…
Reference in a new issue