From 7692f6bb852f2e0a5d3b46577f43ee2d370bc215 Mon Sep 17 00:00:00 2001 From: Ken Hibino Date: Thu, 2 Dec 2021 06:53:37 -0800 Subject: [PATCH] (ui): Add custom duration field --- ui/src/utils.ts | 21 ++++++++++ ui/src/views/MetricsView.tsx | 79 +++++++++++++++++++++++++++++++++--- 2 files changed, 94 insertions(+), 6 deletions(-) diff --git a/ui/src/utils.ts b/ui/src/utils.ts index ad27d6c..9caeef5 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -123,3 +123,24 @@ export function prettifyPayload(p: string) { export function currentUnixtime(): number { return Math.floor(Date.now() / 1000); } + +const durationRegex = /[1-9]([0-9]*)[s|m|h]/; +// Parses the given string and returns the number of seconds if the input is valid. +// Otherwise, it throws an error +// Supported time units are "s", "m", "h" +export function parseDuration(s: string): number { + if (!durationRegex.test(s)) { + throw new Error("invalid duration"); + } + const val = parseInt(s.slice(0, -1)); + switch (s.slice(-1)) { + case "s": + return val; + case "m": + return val * 60; + case "h": + return val * 6 * 60; + default: + throw new Error("invalid duration unit"); + } +} diff --git a/ui/src/views/MetricsView.tsx b/ui/src/views/MetricsView.tsx index 5977752..d9863f6 100644 --- a/ui/src/views/MetricsView.tsx +++ b/ui/src/views/MetricsView.tsx @@ -11,10 +11,11 @@ import RadioGroup from "@material-ui/core/RadioGroup"; import FormControlLabel from "@material-ui/core/FormControlLabel"; import FormControl from "@material-ui/core/FormControl"; import FormLabel from "@material-ui/core/FormLabel"; +import TextField from "@material-ui/core/TextField"; import { getMetricsAsync } from "../actions/metricsActions"; import { AppState } from "../store"; import QueueSizeMetricsChart from "../components/QueueSizeMetricsChart"; -import { currentUnixtime } from "../utils"; +import { currentUnixtime, parseDuration } from "../utils"; const useStyles = makeStyles((theme) => ({ container: { @@ -28,7 +29,7 @@ const useStyles = makeStyles((theme) => ({ controls: {}, controlSelectorBox: { display: "flex", - minWidth: 400, + minWidth: 490, padding: theme.spacing(2), }, controlEndTimeSelector: { @@ -36,6 +37,7 @@ const useStyles = makeStyles((theme) => ({ }, controlDurationSelector: { width: "50%", + marginLeft: theme.spacing(2), }, radioButtonRoot: { paddingTop: theme.spacing(0.5), @@ -49,6 +51,9 @@ const useStyles = makeStyles((theme) => ({ buttonLabel: { textTransform: "none", }, + formControlRoot: { + width: "100%", + }, })); function mapStateToProps(state: AppState) { @@ -74,6 +79,8 @@ function MetricsView(props: Props) { const [endTimeSec, setEndTimeSec] = React.useState(currentUnixtime()); const [durationOption, setDurationOption] = React.useState("1h"); const [durationSec, setDurationSec] = React.useState(60 * 60); // 1h + const [customDuration, setCustomDuration] = React.useState(""); + const [customDurationError, setCustomDurationError] = React.useState(""); const handleEndTimeOptionChange = ( event: React.ChangeEvent @@ -100,21 +107,52 @@ function MetricsView(props: Props) { switch (selected) { case "1h": setDurationSec(60 * 60); + setCustomDuration(""); + setCustomDurationError(""); break; case "6h": setDurationSec(6 * 60 * 60); + setCustomDuration(""); + setCustomDurationError(""); break; case "1d": setDurationSec(24 * 60 * 60); + setCustomDuration(""); + setCustomDurationError(""); break; case "8d": setDurationSec(8 * 24 * 60 * 60); + setCustomDuration(""); + setCustomDurationError(""); break; case "30d": setDurationSec(30 * 24 * 60 * 60); + setCustomDuration(""); + setCustomDurationError(""); break; case "custom": - // TODO + // No-op + } + }; + + const handleCustomDurationChange = ( + event: React.ChangeEvent + ) => { + setCustomDuration(event.target.value); + }; + + const handleCustomDurationKeyDown = ( + event: React.KeyboardEvent + ) => { + if (event.key === "Enter") { + try { + const d = parseDuration(customDuration); + setDurationOption("custom"); + setDurationSec(d); + setCustomDurationError(""); + } catch (error) { + setCustomDurationError("Duration invalid"); + } } }; @@ -159,7 +197,7 @@ function MetricsView(props: Props) { }} > {endTimeOption === "real_time" ? "Realtime" : "Historical"}:{" "} - {durationOption} + {durationOption === "custom" ? customDuration : durationOption}
- + End Time +
+ +
- + Duration +
+ +