From a43a2ae581d12a734f74d03e4e8cd4ec44e55bd3 Mon Sep 17 00:00:00 2001 From: Aurora Lahtela <24460436+AuroraLS3@users.noreply.github.com> Date: Sat, 20 Aug 2022 15:53:34 +0300 Subject: [PATCH] Implement rest of Performance tab (graphs) in React Adds all graphs and their respective tabs. Extra: - Zero baselined charts where it made sense - Increased performance graph card height to improve readability Affects issues: - Close #2270 --- .../plan/settings/locale/lang/HtmlLang.java | 2 + .../server/graphs/PerformanceGraphsCard.js | 67 +++++-- .../{PingGraph.js => PlayerPingGraph.js} | 4 +- .../graphs/performance/AllPerformanceGraph.js | 182 ++++++++++++++++++ .../performance/CpuRamPerformanceGraph.js | 94 +++++++++ .../performance/DiskPerformanceGraph.js | 75 ++++++++ .../graphs/performance/PingGraph.js | 72 +++++++ .../graphs/performance/TpsPerformanceGraph.js | 91 +++++++++ .../performance/WorldPerformanceGraph.js | 93 +++++++++ .../src/components/navigation/Loader.js | 4 +- .../dashboard/src/hooks/navigationHook.js | 6 +- .../dashboard/src/service/serverService.js | 6 + .../src/views/player/PlayerServers.js | 4 +- 13 files changed, 679 insertions(+), 21 deletions(-) rename Plan/react/dashboard/src/components/graphs/{PingGraph.js => PlayerPingGraph.js} (94%) create mode 100644 Plan/react/dashboard/src/components/graphs/performance/AllPerformanceGraph.js create mode 100644 Plan/react/dashboard/src/components/graphs/performance/CpuRamPerformanceGraph.js create mode 100644 Plan/react/dashboard/src/components/graphs/performance/DiskPerformanceGraph.js create mode 100644 Plan/react/dashboard/src/components/graphs/performance/PingGraph.js create mode 100644 Plan/react/dashboard/src/components/graphs/performance/TpsPerformanceGraph.js create mode 100644 Plan/react/dashboard/src/components/graphs/performance/WorldPerformanceGraph.js diff --git a/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/HtmlLang.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/HtmlLang.java index b052ea863..694268ac9 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/HtmlLang.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/HtmlLang.java @@ -94,7 +94,9 @@ public enum HtmlLang implements Lang { TITLE_AS_NUMBERS("html.label.asNumbers", "as Numbers"), LABEL_AVG_TPS("html.label.averageTps", "Average TPS"), LABEL_AVG_PLAYERS("html.label.averagePlayers", "Average Players"), + LABEL_CPU("html.label.cpuUsage", "CPU Usage"), LABEL_AVG_CPU("html.label.averageCpuUsage", "Average CPU Usage"), + LABEL_RAM("html.label.ramUsage", "RAM Usage"), LABEL_AVG_RAM("html.label.averageRamUsage", "Average RAM Usage"), LABEL_AVG_ENTITIES("html.label.averageEntities", "Average Entities"), LABEL_AVG_CHUNKS("html.label.averageChunks", "Average Chunks"), diff --git a/Plan/react/dashboard/src/components/cards/server/graphs/PerformanceGraphsCard.js b/Plan/react/dashboard/src/components/cards/server/graphs/PerformanceGraphsCard.js index 4933173a1..baa0e6ed2 100644 --- a/Plan/react/dashboard/src/components/cards/server/graphs/PerformanceGraphsCard.js +++ b/Plan/react/dashboard/src/components/cards/server/graphs/PerformanceGraphsCard.js @@ -1,6 +1,6 @@ import {useParams} from "react-router-dom"; import {useDataRequest} from "../../../../hooks/dataFetchHook"; -import {fetchOptimizedPerformance} from "../../../../service/serverService"; +import {fetchOptimizedPerformance, fetchPingGraph} from "../../../../service/serverService"; import {ErrorViewBody} from "../../../../views/ErrorView"; import {useTranslation} from "react-i18next"; import {Card} from "react-bootstrap-v5"; @@ -8,13 +8,54 @@ import CardTabs from "../../../CardTabs"; import {faGears, faHdd, faMap, faMicrochip, faSignal, faTachometerAlt} from "@fortawesome/free-solid-svg-icons"; import React, {useEffect, useState} from "react"; import {ChartLoader} from "../../../navigation/Loader"; +import AllPerformanceGraph from "../../../graphs/performance/AllPerformanceGraph"; +import TpsPerformanceGraph from "../../../graphs/performance/TpsPerformanceGraph"; +import CpuRamPerformanceGraph from "../../../graphs/performance/CpuRamPerformanceGraph"; +import WorldPerformanceGraph from "../../../graphs/performance/WorldPerformanceGraph"; +import DiskPerformanceGraph from "../../../graphs/performance/DiskPerformanceGraph"; +import PingGraph from "../../../graphs/performance/PingGraph"; -const PunchCardTab = ({data, loadingError}) => { - +const AllGraphTab = ({data, dataSeries, loadingError}) => { if (loadingError) return - if (!data) return ; + if (!dataSeries) return ; - return + return +} + +const TpsGraphTab = ({data, dataSeries, loadingError}) => { + if (loadingError) return + if (!dataSeries) return ; + + return +} + +const CpuRamGraphTab = ({data, dataSeries, loadingError}) => { + if (loadingError) return + if (!dataSeries) return ; + + return +} + +const WorldGraphTab = ({data, dataSeries, loadingError}) => { + if (loadingError) return + if (!dataSeries) return ; + + return +} + +const DiskGraphTab = ({data, dataSeries, loadingError}) => { + if (loadingError) return + if (!dataSeries) return ; + + return +} + +const PingGraphTab = ({identifier}) => { + const {data, loadingError} = useDataRequest(fetchPingGraph, [identifier]); + if (loadingError) return + if (!data) return ; + + return ; } function mapToDataSeries(performanceData) { @@ -63,30 +104,30 @@ const PerformanceGraphsCard = () => { useEffect(() => { if (data) { - mapToDataSeries(data).then(parsed => setParsedData(parsed)) + mapToDataSeries(data.values).then(parsed => setParsedData(parsed)) } - }, [data]); + }, [data, setParsedData]); return + element: }, { name: t('html.label.tps'), icon: faTachometerAlt, color: 'red', href: 'tps', - element: + element: }, { name: t('html.label.cpuRam'), icon: faMicrochip, color: 'light-green', href: 'cpu-ram', - element: + element: }, { name: t('html.label.world'), icon: faMap, color: 'purple', href: 'world-load', - element: + element: }, { name: t('html.label.ping'), icon: faSignal, color: 'amber', href: 'ping', - element: + element: }, { name: t('html.label.diskSpace'), icon: faHdd, color: 'green', href: 'disk', - element: + element: }, ]}/> diff --git a/Plan/react/dashboard/src/components/graphs/PingGraph.js b/Plan/react/dashboard/src/components/graphs/PlayerPingGraph.js similarity index 94% rename from Plan/react/dashboard/src/components/graphs/PingGraph.js rename to Plan/react/dashboard/src/components/graphs/PlayerPingGraph.js index fab7da72a..689458744 100644 --- a/Plan/react/dashboard/src/components/graphs/PingGraph.js +++ b/Plan/react/dashboard/src/components/graphs/PlayerPingGraph.js @@ -3,7 +3,7 @@ import {useTranslation} from "react-i18next"; import {tooltip} from "../../util/graphs"; import LineGraph from "./LineGraph"; -const PingGraph = ({data}) => { +const PlayerPingGraph = ({data}) => { const {t} = useTranslation(); const [series, setSeries] = useState([]); @@ -37,4 +37,4 @@ const PingGraph = ({data}) => { ) } -export default PingGraph \ No newline at end of file +export default PlayerPingGraph \ No newline at end of file diff --git a/Plan/react/dashboard/src/components/graphs/performance/AllPerformanceGraph.js b/Plan/react/dashboard/src/components/graphs/performance/AllPerformanceGraph.js new file mode 100644 index 000000000..e0a89bdfe --- /dev/null +++ b/Plan/react/dashboard/src/components/graphs/performance/AllPerformanceGraph.js @@ -0,0 +1,182 @@ +import React, {useCallback, useEffect} from 'react'; + +import {linegraphButtons, tooltip} from "../../../util/graphs"; +import Highcharts from "highcharts/highstock"; +import NoDataDisplay from "highcharts/modules/no-data-to-display" +import {useTranslation} from "react-i18next"; +import {useTheme} from "../../../hooks/themeHook"; +import {withReducedSaturation} from "../../../util/colors"; + +const yAxis = [ + { + labels: { + formatter: function () { + return this.value + ' P'; + } + }, + softMin: 0, + softMax: 2 + }, { + opposite: true, + labels: { + formatter: function () { + return this.value + ' TPS'; + } + }, + softMin: 0, + softMax: 20 + }, { + opposite: true, + labels: { + formatter: function () { + return this.value + '%'; + } + }, + softMin: 0, + softMax: 100 + }, { + labels: { + formatter: function () { + return this.value + ' MB'; + } + }, + softMin: 0 + }, { + opposite: true, + labels: { + formatter: function () { + return this.value + ' E'; + } + }, + softMin: 0, + softMax: 2 + }, { + labels: { + formatter: function () { + return this.value + ' C'; + } + }, + softMin: 0 + } +] + +const AllPerformanceGraph = ({id, data, dataSeries}) => { + const {t} = useTranslation(); + const {graphTheming, nightModeEnabled} = useTheme(); + + const onResize = useCallback(() => { + let chartElement = document.getElementById(id); + let chartId = chartElement?.getAttribute('data-highcharts-chart'); + const chart = chartId !== undefined ? Highcharts.charts[chartId] : undefined; + + if (chart && chart.yAxis && chart.yAxis.length) { + const newWidth = window.innerWidth + chart.yAxis[0].update({labels: {enabled: newWidth >= 900}}); + chart.yAxis[1].update({labels: {enabled: newWidth >= 900}}); + chart.yAxis[2].update({labels: {enabled: newWidth >= 1000}}); + chart.yAxis[3].update({labels: {enabled: newWidth >= 1000}}); + chart.yAxis[4].update({labels: {enabled: newWidth >= 1400}}); + chart.yAxis[5].update({labels: {enabled: newWidth >= 1400}}); + } + }, [id]) + + useEffect(() => { + window.addEventListener("resize", onResize); + return () => { + window.removeEventListener("resize", onResize); + } + }, [onResize]) + + useEffect(() => { + const zones = { + tps: [{ + value: data.zones.tpsThresholdMed, + color: nightModeEnabled ? withReducedSaturation(data.colors.low) : data.colors.low + }, { + value: data.zones.tpsThresholdHigh, + color: nightModeEnabled ? withReducedSaturation(data.colors.med) : data.colors.med + }, { + value: 30, + color: nightModeEnabled ? withReducedSaturation(data.colors.high) : data.colors.high + }] + }; + + const spline = 'spline' + + const series = { + playersOnline: { + name: t('html.label.playersOnline'), + type: 'areaspline', + tooltip: tooltip.zeroDecimals, + data: dataSeries.playersOnline, + color: nightModeEnabled ? withReducedSaturation(data.colors.playersOnline) : data.colors.playersOnline, + yAxis: 0 + }, tps: { + name: t('html.label.tps'), + type: spline, + color: nightModeEnabled ? withReducedSaturation(data.colors.high) : data.colors.high, + zones: zones.tps, + tooltip: tooltip.twoDecimals, + data: dataSeries.tps, + yAxis: 1 + }, cpu: { + name: t('html.label.cpu'), + type: spline, + tooltip: tooltip.twoDecimals, + data: dataSeries.cpu, + color: nightModeEnabled ? withReducedSaturation(data.colors.cpu) : data.colors.cpu, + yAxis: 2 + }, ram: { + name: t('html.label.ram'), + type: spline, + tooltip: tooltip.zeroDecimals, + data: dataSeries.ram, + color: nightModeEnabled ? withReducedSaturation(data.colors.ram) : data.colors.ram, + yAxis: 3 + }, entities: { + name: t('html.label.loadedEntities'), + type: spline, + tooltip: tooltip.zeroDecimals, + data: dataSeries.entities, + color: nightModeEnabled ? withReducedSaturation(data.colors.entities) : data.colors.entities, + yAxis: 4 + }, chunks: { + name: t('html.label.loadedChunks'), + type: spline, + tooltip: tooltip.zeroDecimals, + data: dataSeries.chunks, + color: nightModeEnabled ? withReducedSaturation(data.colors.chunks) : data.colors.chunks, + yAxis: 5 + } + }; + + NoDataDisplay(Highcharts); + Highcharts.setOptions({lang: {noData: t('html.label.noDataToDisplay')}}) + Highcharts.setOptions(graphTheming); + Highcharts.stockChart(id, { + rangeSelector: { + selected: 2, + buttons: linegraphButtons + }, + yAxis, + title: {text: ''}, + plotOptions: { + areaspline: { + fillOpacity: 0.4 + } + }, + legend: { + enabled: true + }, + series: [series.playersOnline, series.tps, series.cpu, series.ram, series.entities, series.chunks] + }); + }, [data, dataSeries, graphTheming, nightModeEnabled, id, t]) + + return ( +
+ +
+ ) +}; + +export default AllPerformanceGraph \ No newline at end of file diff --git a/Plan/react/dashboard/src/components/graphs/performance/CpuRamPerformanceGraph.js b/Plan/react/dashboard/src/components/graphs/performance/CpuRamPerformanceGraph.js new file mode 100644 index 000000000..c211e79ba --- /dev/null +++ b/Plan/react/dashboard/src/components/graphs/performance/CpuRamPerformanceGraph.js @@ -0,0 +1,94 @@ +import React, {useEffect} from 'react'; + +import {linegraphButtons, tooltip} from "../../../util/graphs"; +import Highcharts from "highcharts/highstock"; +import NoDataDisplay from "highcharts/modules/no-data-to-display" +import {useTranslation} from "react-i18next"; +import {useTheme} from "../../../hooks/themeHook"; +import {withReducedSaturation} from "../../../util/colors"; + +const CpuRamPerformanceGraph = ({id, data, dataSeries}) => { + const {t} = useTranslation(); + const {graphTheming, nightModeEnabled} = useTheme(); + + useEffect(() => { + const spline = 'spline' + + const series = { + playersOnline: { + name: t('html.label.playersOnline'), + type: 'areaspline', + tooltip: tooltip.zeroDecimals, + data: dataSeries.playersOnline, + color: nightModeEnabled ? withReducedSaturation(data.colors.playersOnline) : data.colors.playersOnline, + yAxis: 0 + }, cpu: { + name: t('html.label.cpu'), + type: spline, + tooltip: tooltip.twoDecimals, + data: dataSeries.cpu, + color: nightModeEnabled ? withReducedSaturation(data.colors.cpu) : data.colors.cpu, + yAxis: 1 + }, ram: { + name: t('html.label.ram'), + type: spline, + tooltip: tooltip.zeroDecimals, + data: dataSeries.ram, + color: nightModeEnabled ? withReducedSaturation(data.colors.ram) : data.colors.ram, + yAxis: 2 + } + }; + + NoDataDisplay(Highcharts); + Highcharts.setOptions({lang: {noData: t('html.label.noDataToDisplay')}}) + Highcharts.setOptions(graphTheming); + Highcharts.stockChart(id, { + rangeSelector: { + selected: 1, // TODO Sync range selectors state + buttons: linegraphButtons + }, + yAxis: [{ + labels: { + formatter: function () { + return this.value + ' ' + t('html.unit.players') + } + }, + softMin: 0, + softMax: 2 + }, { + labels: { + formatter: function () { + return this.value + ' %' + } + }, + softMin: 0, + softMax: 100 + }, { + labels: { + formatter: function () { + return this.value + ' MB' + } + }, + softMin: 0 + }], + title: {text: ''}, + plotOptions: { + areaspline: { + fillOpacity: 0.4 + } + }, + legend: { + enabled: true + }, + series: [series.playersOnline, series.cpu, series.ram] + }); + }, [data, dataSeries, graphTheming, nightModeEnabled, id, t]) + + return ( +
+ +
+ ) +}; + +export default CpuRamPerformanceGraph \ No newline at end of file diff --git a/Plan/react/dashboard/src/components/graphs/performance/DiskPerformanceGraph.js b/Plan/react/dashboard/src/components/graphs/performance/DiskPerformanceGraph.js new file mode 100644 index 000000000..8febe51a5 --- /dev/null +++ b/Plan/react/dashboard/src/components/graphs/performance/DiskPerformanceGraph.js @@ -0,0 +1,75 @@ +import React, {useEffect} from 'react'; + +import {linegraphButtons, tooltip} from "../../../util/graphs"; +import Highcharts from "highcharts/highstock"; +import NoDataDisplay from "highcharts/modules/no-data-to-display" +import {useTranslation} from "react-i18next"; +import {useTheme} from "../../../hooks/themeHook"; +import {withReducedSaturation} from "../../../util/colors"; + +const DiskPerformanceGraph = ({id, data, dataSeries}) => { + const {t} = useTranslation(); + const {graphTheming, nightModeEnabled} = useTheme(); + + useEffect(() => { + const zones = { + disk: [{ + value: data.zones.diskThresholdMed, + color: nightModeEnabled ? withReducedSaturation(data.colors.low) : data.colors.low + }, { + value: data.zones.diskThresholdHigh, + color: nightModeEnabled ? withReducedSaturation(data.colors.med) : data.colors.med + }, { + value: Number.MAX_VALUE, + color: nightModeEnabled ? withReducedSaturation(data.colors.high) : data.colors.high + }] + }; + + const series = { + disk: { + name: t('html.label.disk'), + type: 'areaspline', + color: nightModeEnabled ? withReducedSaturation(data.colors.high) : data.colors.high, + zones: zones.disk, + tooltip: tooltip.zeroDecimals, + data: dataSeries.disk + } + }; + + NoDataDisplay(Highcharts); + Highcharts.setOptions({lang: {noData: t('html.label.noDataToDisplay')}}) + Highcharts.setOptions(graphTheming); + Highcharts.stockChart(id, { + rangeSelector: { + selected: 2, + buttons: linegraphButtons + }, + yAxis: { + labels: { + formatter: function () { + return this.value + ' MB'; + } + }, + softMin: 0 + }, + title: {text: ''}, + plotOptions: { + areaspline: { + fillOpacity: 0.4 + } + }, + legend: { + enabled: true + }, + series: [series.disk] + }); + }, [data, dataSeries, graphTheming, nightModeEnabled, id, t]) + + return ( +
+ +
+ ) +}; + +export default DiskPerformanceGraph \ No newline at end of file diff --git a/Plan/react/dashboard/src/components/graphs/performance/PingGraph.js b/Plan/react/dashboard/src/components/graphs/performance/PingGraph.js new file mode 100644 index 000000000..77118b2bf --- /dev/null +++ b/Plan/react/dashboard/src/components/graphs/performance/PingGraph.js @@ -0,0 +1,72 @@ +import React, {useEffect} from 'react'; + +import {linegraphButtons, tooltip} from "../../../util/graphs"; +import Highcharts from "highcharts/highstock"; +import NoDataDisplay from "highcharts/modules/no-data-to-display" +import {useTranslation} from "react-i18next"; +import {useTheme} from "../../../hooks/themeHook"; +import {withReducedSaturation} from "../../../util/colors"; + +const PingGraph = ({id, data}) => { + const {t} = useTranslation(); + const {graphTheming, nightModeEnabled} = useTheme(); + + useEffect(() => { + const spline = 'spline' + + const series = { + avgPing: { + name: t('html.label.averagePing'), + type: spline, + tooltip: tooltip.twoDecimals, + data: data.avg_ping_series, + color: nightModeEnabled ? withReducedSaturation(data.colors.avg) : data.colors.avg, + }, + maxPing: { + name: t('html.label.worstPing'), + type: spline, + tooltip: tooltip.zeroDecimals, + data: data.max_ping_series, + color: nightModeEnabled ? withReducedSaturation(data.colors.max) : data.colors.max, + }, + minPing: { + name: t('html.label.bestPing'), + type: spline, + tooltip: tooltip.zeroDecimals, + data: data.min_ping_series, + color: nightModeEnabled ? withReducedSaturation(data.colors.min) : data.colors.min, + } + }; + + NoDataDisplay(Highcharts); + Highcharts.setOptions({lang: {noData: t('html.label.noDataToDisplay')}}) + Highcharts.setOptions(graphTheming); + Highcharts.stockChart(id, { + rangeSelector: { + selected: 2, + buttons: linegraphButtons + }, + yAxis: { + labels: { + formatter: function () { + return this.value + ' ms' + } + }, + softMin: 0 + }, + title: {text: ''}, + legend: { + enabled: true + }, + series: [series.avgPing, series.maxPing, series.minPing] + }); + }, [data, graphTheming, nightModeEnabled, id, t]) + + return ( +
+ +
+ ) +}; + +export default PingGraph \ No newline at end of file diff --git a/Plan/react/dashboard/src/components/graphs/performance/TpsPerformanceGraph.js b/Plan/react/dashboard/src/components/graphs/performance/TpsPerformanceGraph.js new file mode 100644 index 000000000..929f18d17 --- /dev/null +++ b/Plan/react/dashboard/src/components/graphs/performance/TpsPerformanceGraph.js @@ -0,0 +1,91 @@ +import React, {useEffect} from 'react'; + +import {linegraphButtons, tooltip} from "../../../util/graphs"; +import Highcharts from "highcharts/highstock"; +import NoDataDisplay from "highcharts/modules/no-data-to-display" +import {useTranslation} from "react-i18next"; +import {useTheme} from "../../../hooks/themeHook"; +import {withReducedSaturation} from "../../../util/colors"; + +const TpsPerformanceGraph = ({id, data, dataSeries}) => { + const {t} = useTranslation(); + const {graphTheming, nightModeEnabled} = useTheme(); + + useEffect(() => { + const zones = { + tps: [{ + value: data.zones.tpsThresholdMed, + color: nightModeEnabled ? withReducedSaturation(data.colors.low) : data.colors.low + }, { + value: data.zones.tpsThresholdHigh, + color: nightModeEnabled ? withReducedSaturation(data.colors.med) : data.colors.med + }, { + value: 30, + color: nightModeEnabled ? withReducedSaturation(data.colors.high) : data.colors.high + }] + }; + + const spline = 'spline' + const series = { + playersOnline: { + name: t('html.label.playersOnline'), + type: 'areaspline', + tooltip: tooltip.zeroDecimals, + data: dataSeries.playersOnline, + color: nightModeEnabled ? withReducedSaturation(data.colors.playersOnline) : data.colors.playersOnline, + yAxis: 0 + }, tps: { + name: t('html.label.tps'), + type: spline, + color: nightModeEnabled ? withReducedSaturation(data.colors.high) : data.colors.high, + zones: zones.tps, + tooltip: tooltip.twoDecimals, + data: dataSeries.tps, + yAxis: 1 + } + }; + + NoDataDisplay(Highcharts); + Highcharts.setOptions({lang: {noData: t('html.label.noDataToDisplay')}}) + Highcharts.setOptions(graphTheming); + Highcharts.stockChart(id, { + rangeSelector: { + selected: 1, + buttons: linegraphButtons + }, + yAxis: [{ + labels: { + formatter: function () { + return this.value + ' ' + t('html.unit.players') + } + }, + softMin: 0, + softMax: 2 + }, { + labels: { + formatter: function () { + return this.value + ' ' + t('html.label.tps') + } + } + }], + title: {text: ''}, + plotOptions: { + areaspline: { + fillOpacity: 0.4 + } + }, + legend: { + enabled: true + }, + series: [series.playersOnline, series.tps] + }); + }, [data, dataSeries, graphTheming, nightModeEnabled, id, t]) + + return ( +
+ +
+ ) +}; + +export default TpsPerformanceGraph \ No newline at end of file diff --git a/Plan/react/dashboard/src/components/graphs/performance/WorldPerformanceGraph.js b/Plan/react/dashboard/src/components/graphs/performance/WorldPerformanceGraph.js new file mode 100644 index 000000000..f87054567 --- /dev/null +++ b/Plan/react/dashboard/src/components/graphs/performance/WorldPerformanceGraph.js @@ -0,0 +1,93 @@ +import React, {useEffect} from 'react'; + +import {linegraphButtons, tooltip} from "../../../util/graphs"; +import Highcharts from "highcharts/highstock"; +import NoDataDisplay from "highcharts/modules/no-data-to-display" +import {useTranslation} from "react-i18next"; +import {useTheme} from "../../../hooks/themeHook"; +import {withReducedSaturation} from "../../../util/colors"; + +const WorldPerformanceGraph = ({id, data, dataSeries}) => { + const {t} = useTranslation(); + const {graphTheming, nightModeEnabled} = useTheme(); + + useEffect(() => { + const spline = 'spline' + + const series = { + playersOnline: { + name: t('html.label.playersOnline'), + type: 'areaspline', + tooltip: tooltip.zeroDecimals, + data: dataSeries.playersOnline, + color: nightModeEnabled ? withReducedSaturation(data.colors.playersOnline) : data.colors.playersOnline, + yAxis: 0 + }, entities: { + name: t('html.label.loadedEntities'), + type: spline, + tooltip: tooltip.zeroDecimals, + data: dataSeries.entities, + color: nightModeEnabled ? withReducedSaturation(data.colors.entities) : data.colors.entities, + yAxis: 1 + }, chunks: { + name: t('html.label.loadedChunks'), + type: spline, + tooltip: tooltip.zeroDecimals, + data: dataSeries.chunks, + color: nightModeEnabled ? withReducedSaturation(data.colors.chunks) : data.colors.chunks, + yAxis: 2 + } + }; + + NoDataDisplay(Highcharts); + Highcharts.setOptions({lang: {noData: t('html.label.noDataToDisplay')}}) + Highcharts.setOptions(graphTheming); + Highcharts.stockChart(id, { + rangeSelector: { + selected: 2, + buttons: linegraphButtons + }, + yAxis: [{ + labels: { + formatter: function () { + return this.value + ' ' + t('html.unit.players') + } + }, + softMin: 0, + softMax: 2 + }, { + labels: { + formatter: function () { + return this.value + ' ' + t('html.label.entities') + } + }, + softMin: 0, + }, { + labels: { + formatter: function () { + return this.value + ' ' + t('html.unit.chunks') + } + }, + softMin: 0, + }], + title: {text: ''}, + plotOptions: { + areaspline: { + fillOpacity: 0.4 + } + }, + legend: { + enabled: true + }, + series: [series.playersOnline, series.entities, series.chunks] + }); + }, [data, dataSeries, graphTheming, nightModeEnabled, id, t]) + + return ( +
+ +
+ ) +}; + +export default WorldPerformanceGraph \ No newline at end of file diff --git a/Plan/react/dashboard/src/components/navigation/Loader.js b/Plan/react/dashboard/src/components/navigation/Loader.js index b806fafec..b7ddd2528 100644 --- a/Plan/react/dashboard/src/components/navigation/Loader.js +++ b/Plan/react/dashboard/src/components/navigation/Loader.js @@ -14,8 +14,8 @@ export const CardLoader = () => { ) } -export const ChartLoader = () => { - return
+export const ChartLoader = ({style}) => { + return
} diff --git a/Plan/react/dashboard/src/hooks/navigationHook.js b/Plan/react/dashboard/src/hooks/navigationHook.js index 5f84f88be..34b01908b 100644 --- a/Plan/react/dashboard/src/hooks/navigationHook.js +++ b/Plan/react/dashboard/src/hooks/navigationHook.js @@ -19,8 +19,10 @@ export const NavigationContextProvider = ({children}) => { const finishUpdate = useCallback((date, formatted) => { // TODO Logic to retry if received data is too old - setLastUpdate({date, formatted}); - setUpdating(false); + if (date) { + setLastUpdate({date, formatted}); + setUpdating(false); + } }, [setLastUpdate, setUpdating]); const toggleSidebar = useCallback(() => { diff --git a/Plan/react/dashboard/src/service/serverService.js b/Plan/react/dashboard/src/service/serverService.js index 2593f0b90..33ae69b00 100644 --- a/Plan/react/dashboard/src/service/serverService.js +++ b/Plan/react/dashboard/src/service/serverService.js @@ -119,3 +119,9 @@ export const fetchOptimizedPerformance = async (identifier) => { const url = `/v1/graph?type=optimizedPerformance&server=${identifier}×tamp=${timestamp}`; return doGetRequest(url); } + +export const fetchPingGraph = async (identifier) => { + const timestamp = Date.now(); + const url = `/v1/graph?type=aggregatedPing&server=${identifier}×tamp=${timestamp}`; + return doGetRequest(url); +} diff --git a/Plan/react/dashboard/src/views/player/PlayerServers.js b/Plan/react/dashboard/src/views/player/PlayerServers.js index 8b9936452..95b5e8c0e 100644 --- a/Plan/react/dashboard/src/views/player/PlayerServers.js +++ b/Plan/react/dashboard/src/views/player/PlayerServers.js @@ -8,7 +8,7 @@ import ServerPie from "../../components/graphs/ServerPie"; import ServerAccordion from "../../components/accordion/ServerAccordion"; import {usePlayer} from "../layout/PlayerPage"; import {useTranslation} from "react-i18next"; -import PingGraph from "../../components/graphs/PingGraph"; +import PlayerPingGraph from "../../components/graphs/PlayerPingGraph"; import LoadIn from "../../components/animation/LoadIn"; const PingGraphCard = ({player}) => { @@ -23,7 +23,7 @@ const PingGraphCard = ({player}) => { {t('html.label.ping')} - {hasPingData && } + {hasPingData && } {!hasPingData &&

{t('generic.noData')}

} )