(ui): Add custom duration field

This commit is contained in:
Ken Hibino
2021-12-02 06:53:37 -08:00
parent c8cbc49122
commit 7692f6bb85
2 changed files with 94 additions and 6 deletions

View File

@@ -123,3 +123,24 @@ export function prettifyPayload(p: string) {
export function currentUnixtime(): number { export function currentUnixtime(): number {
return Math.floor(Date.now() / 1000); 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");
}
}

View File

@@ -11,10 +11,11 @@ import RadioGroup from "@material-ui/core/RadioGroup";
import FormControlLabel from "@material-ui/core/FormControlLabel"; import FormControlLabel from "@material-ui/core/FormControlLabel";
import FormControl from "@material-ui/core/FormControl"; import FormControl from "@material-ui/core/FormControl";
import FormLabel from "@material-ui/core/FormLabel"; import FormLabel from "@material-ui/core/FormLabel";
import TextField from "@material-ui/core/TextField";
import { getMetricsAsync } from "../actions/metricsActions"; import { getMetricsAsync } from "../actions/metricsActions";
import { AppState } from "../store"; import { AppState } from "../store";
import QueueSizeMetricsChart from "../components/QueueSizeMetricsChart"; import QueueSizeMetricsChart from "../components/QueueSizeMetricsChart";
import { currentUnixtime } from "../utils"; import { currentUnixtime, parseDuration } from "../utils";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
container: { container: {
@@ -28,7 +29,7 @@ const useStyles = makeStyles((theme) => ({
controls: {}, controls: {},
controlSelectorBox: { controlSelectorBox: {
display: "flex", display: "flex",
minWidth: 400, minWidth: 490,
padding: theme.spacing(2), padding: theme.spacing(2),
}, },
controlEndTimeSelector: { controlEndTimeSelector: {
@@ -36,6 +37,7 @@ const useStyles = makeStyles((theme) => ({
}, },
controlDurationSelector: { controlDurationSelector: {
width: "50%", width: "50%",
marginLeft: theme.spacing(2),
}, },
radioButtonRoot: { radioButtonRoot: {
paddingTop: theme.spacing(0.5), paddingTop: theme.spacing(0.5),
@@ -49,6 +51,9 @@ const useStyles = makeStyles((theme) => ({
buttonLabel: { buttonLabel: {
textTransform: "none", textTransform: "none",
}, },
formControlRoot: {
width: "100%",
},
})); }));
function mapStateToProps(state: AppState) { function mapStateToProps(state: AppState) {
@@ -74,6 +79,8 @@ function MetricsView(props: Props) {
const [endTimeSec, setEndTimeSec] = React.useState(currentUnixtime()); const [endTimeSec, setEndTimeSec] = React.useState(currentUnixtime());
const [durationOption, setDurationOption] = React.useState("1h"); const [durationOption, setDurationOption] = React.useState("1h");
const [durationSec, setDurationSec] = React.useState(60 * 60); // 1h const [durationSec, setDurationSec] = React.useState(60 * 60); // 1h
const [customDuration, setCustomDuration] = React.useState("");
const [customDurationError, setCustomDurationError] = React.useState("");
const handleEndTimeOptionChange = ( const handleEndTimeOptionChange = (
event: React.ChangeEvent<HTMLInputElement> event: React.ChangeEvent<HTMLInputElement>
@@ -100,21 +107,52 @@ function MetricsView(props: Props) {
switch (selected) { switch (selected) {
case "1h": case "1h":
setDurationSec(60 * 60); setDurationSec(60 * 60);
setCustomDuration("");
setCustomDurationError("");
break; break;
case "6h": case "6h":
setDurationSec(6 * 60 * 60); setDurationSec(6 * 60 * 60);
setCustomDuration("");
setCustomDurationError("");
break; break;
case "1d": case "1d":
setDurationSec(24 * 60 * 60); setDurationSec(24 * 60 * 60);
setCustomDuration("");
setCustomDurationError("");
break; break;
case "8d": case "8d":
setDurationSec(8 * 24 * 60 * 60); setDurationSec(8 * 24 * 60 * 60);
setCustomDuration("");
setCustomDurationError("");
break; break;
case "30d": case "30d":
setDurationSec(30 * 24 * 60 * 60); setDurationSec(30 * 24 * 60 * 60);
setCustomDuration("");
setCustomDurationError("");
break; break;
case "custom": case "custom":
// TODO // No-op
}
};
const handleCustomDurationChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
setCustomDuration(event.target.value);
};
const handleCustomDurationKeyDown = (
event: React.KeyboardEvent<HTMLInputElement>
) => {
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"}:{" "} {endTimeOption === "real_time" ? "Realtime" : "Historical"}:{" "}
{durationOption} {durationOption === "custom" ? customDuration : durationOption}
</Button> </Button>
<Popover <Popover
id={id} id={id}
@@ -177,7 +215,11 @@ function MetricsView(props: Props) {
> >
<div className={classes.controlSelectorBox}> <div className={classes.controlSelectorBox}>
<div className={classes.controlEndTimeSelector}> <div className={classes.controlEndTimeSelector}>
<FormControl component="fieldset" margin="dense"> <FormControl
component="fieldset"
margin="dense"
classes={{ root: classes.formControlRoot }}
>
<FormLabel component="legend">End Time</FormLabel> <FormLabel component="legend">End Time</FormLabel>
<RadioGroup <RadioGroup
aria-label="end_time" aria-label="end_time"
@@ -225,10 +267,22 @@ function MetricsView(props: Props) {
label="Custom End Time" label="Custom End Time"
/> />
</RadioGroup> </RadioGroup>
<div>
<TextField
id="custom-endtime"
label="yyyy-mm-dd hh:mm:ssz"
variant="outlined"
size="small"
/>
</div>
</FormControl> </FormControl>
</div> </div>
<div className={classes.controlDurationSelector}> <div className={classes.controlDurationSelector}>
<FormControl component="fieldset" margin="dense"> <FormControl
component="fieldset"
margin="dense"
classes={{ root: classes.formControlRoot }}
>
<FormLabel component="legend">Duration</FormLabel> <FormLabel component="legend">Duration</FormLabel>
<RadioGroup <RadioGroup
aria-label="duration" aria-label="duration"
@@ -315,6 +369,19 @@ function MetricsView(props: Props) {
label="Custom Duration" label="Custom Duration"
/> />
</RadioGroup> </RadioGroup>
<div>
<TextField
id="custom-duration"
label="duration"
variant="outlined"
size="small"
onChange={handleCustomDurationChange}
value={customDuration}
onKeyDown={handleCustomDurationKeyDown}
error={customDurationError !== ""}
helperText={customDurationError}
/>
</div>
</FormControl> </FormControl>
</div> </div>
</div> </div>