0

Componentize a bit and consolidate some data

This commit is contained in:
Gabe Kangas 2020-10-28 18:36:25 -07:00
parent 87d69e1665
commit df14a55429
7 changed files with 165 additions and 166 deletions

View File

@ -17,17 +17,13 @@ interface ChartProps {
}
function CustomizedTooltip(props: ToolTipProps) {
const { active, payload } = props;
const { active, payload, unit } = props;
if (active && payload && payload[0]) {
const time = payload[0].payload
? timeFormat("%I:%M")(new Date(payload[0].payload.time), {
nearestTo: 1,
})
: "";
const time = payload[0].payload ? timeFormat("%I:%M")(new Date(payload[0].payload.time)) : "";
return (
<div className="custom-tooltip">
<p className="label">
<strong>{time}</strong> {payload[0].payload.value} %
<strong>{time}</strong> {payload[0].payload.value} {unit}
</p>
</div>
);
@ -38,9 +34,7 @@ CustomizedTooltip.defaultProps = defaultProps;
export default function Chart({ data, color, unit }: ChartProps) {
const timeFormatter = (tick: string) => {
return timeFormat("%I:%M")(new Date(tick), {
nearestTo: 1,
});
return timeFormat("%I:%M")(new Date(tick));
};
return (
@ -60,7 +54,7 @@ export default function Chart({ data, color, unit }: ChartProps) {
unit={unit}
domain={["dataMin", "dataMax"]}
/>
<Tooltip content={<CustomizedTooltip />} />
<Tooltip content={<CustomizedTooltip unit={unit} />} />
<Legend />
<Line
type="monotone"

View File

@ -67,15 +67,9 @@ export default function MainLayout(props) {
</Menu.Item>
<SubMenu key="current-stream-menu" icon={<LineChartOutlined />} title="Stream Details">
<Menu.Item key="broadcast-info">
<Link href="/broadcast-info">Broadcaster Info</Link>
</Menu.Item>
<Menu.Item key="viewer-info">
<Link href="/viewer-info">Viewers</Link>
</Menu.Item>
<Menu.Item key="connected-clients">
<Link href="/connected-clients">Connected Clients</Link>
</Menu.Item>
<Menu.Item key="hardware-info">
<Link href="/hardware-info">Hardware</Link>
</Menu.Item>

View File

@ -0,0 +1,25 @@
import { Statistic, Card, Col} from "antd";
interface ItemProps {
title: string,
value: string,
prefix: JSX.Element,
};
export default function StatisticItem(props: ItemProps) {
const { title, value, prefix } = props;
const valueStyle = { color: "#334", fontSize: "1.8rem" };
return (
<Col span={8}>
<Card>
<Statistic
title={title}
value={value}
valueStyle={valueStyle}
prefix={prefix}
/>
</Card>
</Col>
);
}

View File

@ -1,93 +0,0 @@
import React, { useState, useEffect, useContext } from 'react';
import { Table } from 'antd';
import { formatDistanceToNow } from "date-fns";
import { BroadcastStatusContext } from './utils/broadcast-status-context';
import { CONNECTED_CLIENTS, fetchData, FETCH_INTERVAL } from './utils/apis';
/*
geo data looks like this
"geo": {
"countryCode": "US",
"regionName": "California",
"timeZone": "America/Los_Angeles"
}
*/
export default function ConnectedClients() {
const context = useContext(BroadcastStatusContext);
const { broadcastActive } = context || {};
const [clients, setClients] = useState([]);
const getInfo = async () => {
try {
const result = await fetchData(CONNECTED_CLIENTS);
console.log("result",result)
setClients(result);
} catch (error) {
console.log("==== error", error)
}
};
useEffect(() => {
let getStatusIntervalId = null;
getInfo();
if (broadcastActive) {
getStatusIntervalId = setInterval(getInfo, FETCH_INTERVAL);
// returned function will be called on component unmount
return () => {
clearInterval(getStatusIntervalId);
}
}
return () => [];
}, []);
if (!clients.length) {
return "no clients";
}
// todo - check to see if broadcast active has changed. if so, start polling.
const columns = [
{
title: "User name",
dataIndex: "username",
key: "username",
render: (username) => username || "-",
sorter: (a, b) => a.username - b.username,
sortDirections: ["descend", "ascend"],
},
{
title: "Messages sent",
dataIndex: "messageCount",
key: "messageCount",
sorter: (a, b) => a.messageCount - b.messageCount,
sortDirections: ["descend", "ascend"],
},
{
title: "Connected Time",
dataIndex: "connectedAt",
key: "connectedAt",
render: (time) => formatDistanceToNow(new Date(time)),
},
{
title: "User Agent",
dataIndex: "userAgent",
key: "userAgent",
},
{
title: "Location",
dataIndex: "geo",
key: "geo",
render: (geo) => geo && `${geo.regionName}, ${geo.countryCode}`,
},
];
return (
<div>
<h2>Connected Clients</h2>
<Table dataSource={clients} columns={columns} />;
</div>
);
}

View File

@ -1,6 +1,9 @@
import React, { useState, useEffect } from 'react';
import { Row, Skeleton, Empty, Typography } from "antd";
import {LaptopOutlined, BulbOutlined, SaveOutlined} from "@ant-design/icons"
import { HARDWARE_STATS, fetchData, FETCH_INTERVAL } from './utils/apis';
import Chart from './components/chart';
import StatisticItem from "./components/statistic";
export default function HardwareInfo() {
const [hardwareStatus, setHardwareStatus] = useState({
@ -37,10 +40,33 @@ export default function HardwareInfo() {
return null;
}
const currentCPUUsage = hardwareStatus.cpu[hardwareStatus.cpu.length - 1]?.value;
const currentRamUsage =
hardwareStatus.memory[hardwareStatus.memory.length - 1]?.value;
const currentDiskUsage =
hardwareStatus.disk[hardwareStatus.disk.length - 1]?.value;
return (
<div>
<div>
<h2>Hardware Info</h2>
<Row gutter={[16, 16]}>
<StatisticItem
title="CPU used"
value={`${currentCPUUsage} %`}
prefix={<LaptopOutlined />}
/>
<StatisticItem
title="Memory used"
value={`${currentRamUsage} %`}
prefix={<BulbOutlined />}
/>
<StatisticItem
title="Disk used"
value={`${currentDiskUsage} %`}
prefix={<SaveOutlined />}
/>
</Row>
<div className="chart-container">
<h3>CPU</h3>
<Chart data={hardwareStatus.cpu} color="#FF7700" unit="%" />

View File

@ -8,10 +8,11 @@ TODO: Link each overview value to the sub-page that focuses on it.
*/
import React, { useState, useEffect, useContext } from "react";
import { Statistic, Card, Row, Col, Skeleton, Empty, Typography } from "antd";
import { Row, Skeleton, Empty, Typography } from "antd";
import { UserOutlined, ClockCircleOutlined } from "@ant-design/icons";
import { formatDistanceToNow, formatRelative } from "date-fns";
import { BroadcastStatusContext } from "./utils/broadcast-status-context";
import StatisticItem from "./components/statistic"
import {
STREAM_STATUS,
SERVER_CONFIG,
@ -22,30 +23,6 @@ import { formatIPAddress, isEmptyObject } from "./utils/format";
const { Title } = Typography;
interface ItemProps {
title: string,
value: string,
prefix: JSX.Element,
};
function Item(props: ItemProps) {
const { title, value, prefix } = props;
const valueStyle = { color: "#334", fontSize: "1.8rem" };
return (
<Col span={8}>
<Card>
<Statistic
title={title}
value={value}
valueStyle={valueStyle}
prefix={prefix}
/>
</Card>
</Col>
);
}
function Offline() {
return (
<div>
@ -116,17 +93,17 @@ export default function Stats() {
return (
<Row gutter={[16, 16]} key={index}>
<Item
<StatisticItem
title="Output"
value={`Video variant ${index}`}
prefix={null}
/>
<Item
<StatisticItem
title="Outbound Video Stream"
value={`${setting.videoBitrate} kbps ${setting.framerate} fps`}
prefix={null}
/>
<Item
<StatisticItem
title="Outbound Audio Stream"
value={audioSetting}
prefix={null}
@ -143,7 +120,7 @@ export default function Stats() {
<div>
<Title>Server Overview</Title>
<Row gutter={[16, 16]}>
<Item
<StatisticItem
title={`Stream started ${formatRelative(
new Date(lastConnectTime),
new Date()
@ -151,12 +128,12 @@ export default function Stats() {
value={formatDistanceToNow(new Date(lastConnectTime))}
prefix={<ClockCircleOutlined />}
/>
<Item
<StatisticItem
title="Viewers"
value={viewerCount}
prefix={<UserOutlined />}
/>
<Item
<StatisticItem
title="Peak viewer count"
value={sessionMaxViewerCount}
prefix={<UserOutlined />}
@ -164,17 +141,17 @@ export default function Stats() {
</Row>
<Row gutter={[16, 16]}>
<Item
<StatisticItem
title="Input"
value={formatIPAddress(remoteAddr)}
prefix={null}
/>
<Item
<StatisticItem
title="Inbound Video Stream"
value={streamVideoDetailString}
prefix={null}
/>
<Item
<StatisticItem
title="Inbound Audio Stream"
value={streamAudioDetailString}
prefix={null}

View File

@ -1,9 +1,18 @@
import React, { useState, useEffect, useContext } from 'react';
import { timeFormat } from "d3-time-format";
import { Table, Row } from "antd";
import { formatDistanceToNow } from "date-fns";
import { UserOutlined} from "@ant-design/icons";
import Chart from "./components/chart";
import StatisticItem from "./components/statistic";
import { BroadcastStatusContext } from './utils/broadcast-status-context';
import { VIEWERS_OVER_TIME, fetchData } from './utils/apis';
import {
CONNECTED_CLIENTS,
STREAM_STATUS, VIEWERS_OVER_TIME,
fetchData,
} from "./utils/apis";
const FETCH_INTERVAL = 5 * 60 * 1000; // 5 mins
@ -12,14 +21,32 @@ export default function ViewersOverTime() {
const { broadcastActive } = context || {};
const [viewerInfo, setViewerInfo] = useState([]);
const [clients, setClients] = useState([]);
const [stats, setStats] = useState(null);
const getInfo = async () => {
try {
const result = await fetchData(VIEWERS_OVER_TIME);
setViewerInfo(result);
} catch (error) {
console.log("==== error", error)
console.log("==== error", error);
}
try {
const result = await fetchData(CONNECTED_CLIENTS);
console.log("result", result);
setClients(result);
} catch (error) {
console.log("==== error", error);
}
try {
const result = await fetchData(STREAM_STATUS);
setStats(result);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
@ -28,23 +55,60 @@ export default function ViewersOverTime() {
getInfo();
if (broadcastActive) {
getStatusIntervalId = setInterval(getInfo, FETCH_INTERVAL);
// returned function will be called on component unmount
// returned function will be called on component unmount
return () => {
clearInterval(getStatusIntervalId);
}
};
}
return () => [];
}, []);
// todo - check to see if broadcast active has changed. if so, start polling.
return () => [];
}, []);
// todo - check to see if broadcast active has changed. if so, start polling.
if (!viewerInfo.length) {
return "no info";
}
const timeFormatter = (tick) => { return timeFormat('%H:%M')(new Date(tick));};
const columns = [
{
title: "User name",
dataIndex: "username",
key: "username",
render: (username) => username || "-",
sorter: (a, b) => a.username - b.username,
sortDirections: ["descend", "ascend"],
},
{
title: "Messages sent",
dataIndex: "messageCount",
key: "messageCount",
sorter: (a, b) => a.messageCount - b.messageCount,
sortDirections: ["descend", "ascend"],
},
{
title: "Connected Time",
dataIndex: "connectedAt",
key: "connectedAt",
render: (time) => formatDistanceToNow(new Date(time)),
},
{
title: "User Agent",
dataIndex: "userAgent",
key: "userAgent",
},
{
title: "Location",
dataIndex: "geo",
key: "geo",
render: (geo) => geo && `${geo.regionName}, ${geo.countryCode}`,
},
];
const timeFormatter = (tick) => {
return timeFormat("%H:%M")(new Date(tick));
};
const CustomizedTooltip = (props) => {
const { active, payload, label } = props;
@ -61,24 +125,36 @@ export default function ViewersOverTime() {
return null;
};
/*
geo data looks like this
"geo": {
"countryCode": "US",
"regionName": "California",
"timeZone": "America/Los_Angeles"
}
*/
return (
<div>
<h2>Current Viewers</h2>
<Row gutter={[16, 16]}>
<StatisticItem
title="Current viewers"
value={stats?.viewerCount ?? ""}
prefix={<UserOutlined />}
/>
<StatisticItem
title="Peak viewers this session"
value={stats?.sessionMaxViewerCount ?? ""}
prefix={<UserOutlined />}
/>
</Row>
<div className="chart-container">
<Chart data={viewerInfo} color="#ff84d8" unit="" />
{/* <LineChart width={800} height={400} data={viewerInfo}>
<XAxis dataKey="time" tickFormatter={timeFormatter} />
<YAxis dataKey="value" />
<Tooltip content={<CustomizedTooltip />} />
<Line
type="monotone"
dataKey="value"
stroke="#ff84d8"
dot={{ stroke: "red", strokeWidth: 2 }}
/>
</LineChart> */}
<div>
<Table dataSource={clients} columns={columns} />;
</div>
</div>
</div>
);