mirror of
https://github.com/hibiken/asynqmon.git
synced 2025-01-19 03:05:53 +08:00
Show daily stats with line chart
This commit is contained in:
parent
d0b6dee896
commit
db0749892b
71
ui/src/components/DailyStatsChart.tsx
Normal file
71
ui/src/components/DailyStatsChart.tsx
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
LineChart,
|
||||||
|
Line,
|
||||||
|
XAxis,
|
||||||
|
YAxis,
|
||||||
|
CartesianGrid,
|
||||||
|
Tooltip,
|
||||||
|
Legend,
|
||||||
|
ResponsiveContainer,
|
||||||
|
} from "recharts";
|
||||||
|
import { useTheme, Theme } from "@material-ui/core/styles";
|
||||||
|
import { DailyStat } from "../api";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: { [qname: string]: DailyStat[] };
|
||||||
|
numDays: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChartData {
|
||||||
|
succeeded: number;
|
||||||
|
failed: number;
|
||||||
|
date: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DailyStatsChart(props: Props) {
|
||||||
|
const data = makeChartData(props.data, props.numDays);
|
||||||
|
const theme = useTheme<Theme>();
|
||||||
|
return (
|
||||||
|
<ResponsiveContainer>
|
||||||
|
<LineChart data={data}>
|
||||||
|
<CartesianGrid strokeDasharray="3 3" />
|
||||||
|
<XAxis dataKey="date" minTickGap={10} />
|
||||||
|
<YAxis />
|
||||||
|
<Tooltip />
|
||||||
|
<Legend />
|
||||||
|
<Line
|
||||||
|
type="monotone"
|
||||||
|
dataKey="succeeded"
|
||||||
|
stroke={theme.palette.success.main}
|
||||||
|
/>
|
||||||
|
<Line
|
||||||
|
type="monotone"
|
||||||
|
dataKey="failed"
|
||||||
|
stroke={theme.palette.error.main}
|
||||||
|
/>
|
||||||
|
</LineChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeChartData(
|
||||||
|
queueStats: { [qname: string]: DailyStat[] },
|
||||||
|
numDays: number
|
||||||
|
): ChartData[] {
|
||||||
|
const dataByDate: { [date: string]: ChartData } = {};
|
||||||
|
for (const qname in queueStats) {
|
||||||
|
for (const stat of queueStats[qname]) {
|
||||||
|
if (!dataByDate.hasOwnProperty(stat.date)) {
|
||||||
|
dataByDate[stat.date] = { succeeded: 0, failed: 0, date: stat.date };
|
||||||
|
}
|
||||||
|
dataByDate[stat.date].succeeded += stat.processed - stat.failed;
|
||||||
|
dataByDate[stat.date].failed += stat.failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Object.values(dataByDate).sort(sortByDate).slice(-numDays);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortByDate(x: ChartData, y: ChartData): number {
|
||||||
|
return Date.parse(x.date) - Date.parse(y.date);
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { connect, ConnectedProps } from "react-redux";
|
import { connect, ConnectedProps } from "react-redux";
|
||||||
import Container from "@material-ui/core/Container";
|
import Container from "@material-ui/core/Container";
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
@ -19,8 +19,8 @@ import ProcessedTasksChart from "../components/ProcessedTasksChart";
|
|||||||
import QueuesOverviewTable from "../components/QueuesOverviewTable";
|
import QueuesOverviewTable from "../components/QueuesOverviewTable";
|
||||||
import Tooltip from "../components/Tooltip";
|
import Tooltip from "../components/Tooltip";
|
||||||
import SplitButton from "../components/SplitButton";
|
import SplitButton from "../components/SplitButton";
|
||||||
import { getCurrentUTCDate } from "../utils";
|
|
||||||
import { usePolling } from "../hooks";
|
import { usePolling } from "../hooks";
|
||||||
|
import DailyStatsChart from "../components/DailyStatsChart";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
container: {
|
container: {
|
||||||
@ -68,6 +68,7 @@ function mapStateToProps(state: AppState) {
|
|||||||
requestPending: q.requestPending,
|
requestPending: q.requestPending,
|
||||||
})),
|
})),
|
||||||
pollInterval: state.settings.pollInterval,
|
pollInterval: state.settings.pollInterval,
|
||||||
|
queueStats: state.queueStats.data,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,9 +84,15 @@ const connector = connect(mapStateToProps, mapDispatchToProps);
|
|||||||
|
|
||||||
type Props = ConnectedProps<typeof connector>;
|
type Props = ConnectedProps<typeof connector>;
|
||||||
|
|
||||||
|
type DailyStatsKey = "today" | "last-7d" | "last-30d" | "last-90d";
|
||||||
|
const initialStatsKey = "last-7d";
|
||||||
|
|
||||||
function DashboardView(props: Props) {
|
function DashboardView(props: Props) {
|
||||||
const { pollInterval, listQueuesAsync, queues, listQueueStatsAsync } = props;
|
const { pollInterval, listQueuesAsync, queues, listQueueStatsAsync } = props;
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
const [dailyStatsKey, setDailyStatsKey] = useState<DailyStatsKey>(
|
||||||
|
initialStatsKey
|
||||||
|
);
|
||||||
|
|
||||||
usePolling(listQueuesAsync, pollInterval);
|
usePolling(listQueuesAsync, pollInterval);
|
||||||
|
|
||||||
@ -161,16 +168,15 @@ function DashboardView(props: Props) {
|
|||||||
title={
|
title={
|
||||||
<div>
|
<div>
|
||||||
<div className={classes.tooltipSection}>
|
<div className={classes.tooltipSection}>
|
||||||
Total number of tasks processed today (
|
Total number of tasks processed in a given day (UTC)
|
||||||
{getCurrentUTCDate()} UTC)
|
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.tooltipSection}>
|
<div className={classes.tooltipSection}>
|
||||||
<strong>Succeeded</strong>: number of tasks successfully
|
<strong>Succeeded</strong>: number of tasks successfully
|
||||||
processed from the queue
|
processed
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<strong>Failed</strong>: number of tasks failed to be
|
<strong>Failed</strong>: number of tasks failed to be
|
||||||
processed from the queue
|
processed
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@ -186,13 +192,24 @@ function DashboardView(props: Props) {
|
|||||||
{ label: "Last 30d", key: "last-30d" },
|
{ label: "Last 30d", key: "last-30d" },
|
||||||
{ label: "Last 90d", key: "last-90d" },
|
{ label: "Last 90d", key: "last-90d" },
|
||||||
]}
|
]}
|
||||||
initialSelectedKey="today"
|
initialSelectedKey={initialStatsKey}
|
||||||
onSelect={(key) => console.log("option selected:", key)}
|
onSelect={(key) => setDailyStatsKey(key as DailyStatsKey)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={classes.chartContainer}>
|
<div className={classes.chartContainer}>
|
||||||
|
{dailyStatsKey === "today" && (
|
||||||
<ProcessedTasksChart data={processedStats} />
|
<ProcessedTasksChart data={processedStats} />
|
||||||
|
)}
|
||||||
|
{dailyStatsKey === "last-7d" && (
|
||||||
|
<DailyStatsChart data={props.queueStats} numDays={7} />
|
||||||
|
)}
|
||||||
|
{dailyStatsKey === "last-30d" && (
|
||||||
|
<DailyStatsChart data={props.queueStats} numDays={30} />
|
||||||
|
)}
|
||||||
|
{dailyStatsKey === "last-90d" && (
|
||||||
|
<DailyStatsChart data={props.queueStats} numDays={90} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
Loading…
Reference in New Issue
Block a user