mirror of
https://github.com/hibiken/asynqmon.git
synced 2025-10-21 05:46:11 +08:00
(ui): Add custom duration field
This commit is contained in:
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -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>
|
||||||
|
Reference in New Issue
Block a user