From e77a9ee8b535e3ff2185f9f2e6364afd7a74efde Mon Sep 17 00:00:00 2001 From: Aurora Lahtela <24460436+AuroraLS3@users.noreply.github.com> Date: Sat, 21 May 2022 13:43:49 +0300 Subject: [PATCH] Implemented rest of the graphs on Online Activity Overview --- .../plan/settings/locale/lang/HtmlLang.java | 1 + .../src/components/calendar/ServerCalendar.js | 28 ++++++ .../cards/server/OnlineActivityGraphsCard.js | 85 +++++++++++++++++++ .../src/components/graphs/LineGraph.js | 42 +++++++++ .../src/components/graphs/PingGraph.js | 29 ++----- .../components/graphs/PlayersOnlineGraph.js | 34 ++------ .../src/components/graphs/TimeByTimeGraph.js | 33 +++++++ .../src/components/navigation/Sidebar.js | 6 +- .../dashboard/src/hooks/dataFetchHook.js | 9 +- .../dashboard/src/hooks/navigationHook.js | 1 + .../dashboard/src/service/serverService.js | 4 +- .../src/views/ServerOnlineActivity.js | 58 +------------ 12 files changed, 218 insertions(+), 112 deletions(-) create mode 100644 Plan/react/dashboard/src/components/calendar/ServerCalendar.js create mode 100644 Plan/react/dashboard/src/components/cards/server/OnlineActivityGraphsCard.js create mode 100644 Plan/react/dashboard/src/components/graphs/LineGraph.js create mode 100644 Plan/react/dashboard/src/components/graphs/TimeByTimeGraph.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 371932da7..ec6690c87 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 @@ -37,6 +37,7 @@ public enum HtmlLang implements Lang { SIDE_PERFORMANCE("html.label.performance", "Performance"), QUERY_MAKE("html.label.query", "Make a query"), UNIT_NO_DATA("generic.noData", "No Data"), // Generic + GRAPH_NO_DATA("html.label.noDataToDisplay", "No Data to Display"), // Modals TITLE_THEME_SELECT("html.label.themeSelect", "Theme Select"), LINK_NIGHT_MODE("html.button.nightMode", "Night Mode"), diff --git a/Plan/react/dashboard/src/components/calendar/ServerCalendar.js b/Plan/react/dashboard/src/components/calendar/ServerCalendar.js new file mode 100644 index 000000000..fa0bf34a4 --- /dev/null +++ b/Plan/react/dashboard/src/components/calendar/ServerCalendar.js @@ -0,0 +1,28 @@ +import React from "react"; +import FullCalendar from '@fullcalendar/react' +import dayGridPlugin from '@fullcalendar/daygrid' + +const ServerCalendar = ({series, firstDay}) => { + return ( + successCallback(series)} + /> + ) +} + +export default ServerCalendar \ No newline at end of file diff --git a/Plan/react/dashboard/src/components/cards/server/OnlineActivityGraphsCard.js b/Plan/react/dashboard/src/components/cards/server/OnlineActivityGraphsCard.js new file mode 100644 index 000000000..fb43d4b95 --- /dev/null +++ b/Plan/react/dashboard/src/components/cards/server/OnlineActivityGraphsCard.js @@ -0,0 +1,85 @@ +import {useParams} from "react-router-dom"; +import {useDataRequest} from "../../../hooks/dataFetchHook"; +import { + fetchDayByDayGraph, + fetchHourByHourGraph, + fetchPunchCardGraph, + fetchServerCalendarGraph +} from "../../../service/serverService"; +import {ErrorViewBody} from "../../../views/ErrorView"; +import PunchCard from "../../graphs/PunchCard"; +import {useTranslation} from "react-i18next"; +import {Card} from "react-bootstrap-v5"; +import CardTabs from "../../CardTabs"; +import {faBraille, faChartArea} from "@fortawesome/free-solid-svg-icons"; +import {faCalendar} from "@fortawesome/free-regular-svg-icons"; +import React from "react"; +import TimeByTimeGraph from "../../graphs/TimeByTimeGraph"; +import ServerCalendar from "../../calendar/ServerCalendar"; + +const DayByDayTab = () => { + const {identifier} = useParams(); + + const {data, loadingError} = useDataRequest(fetchDayByDayGraph, [identifier]) + + if (loadingError) return + if (!data) return <>; + + return +} + +const HourByHourTab = () => { + const {identifier} = useParams(); + + const {data, loadingError} = useDataRequest(fetchHourByHourGraph, [identifier]) + + if (loadingError) return + if (!data) return <>; + + return +} + +const ServerCalendarTab = () => { + const {identifier} = useParams(); + + const {data, loadingError} = useDataRequest(fetchServerCalendarGraph, [identifier]) + + if (loadingError) return + if (!data) return <>; + + return +} + +const PunchCardTab = () => { + const {identifier} = useParams(); + + const {data, loadingError} = useDataRequest(fetchPunchCardGraph, [identifier]) + + if (loadingError) return + if (!data) return <>; + + return +} + +const OnlineActivityGraphsCard = () => { + const {t} = useTranslation(); + return + + }, { + name: t('html.label.hourByHour'), icon: faChartArea, color: 'blue', href: 'hour-by-hour', + element: + }, { + name: t('html.label.serverCalendar'), icon: faCalendar, color: 'teal', href: 'server-calendar', + element: + }, { + name: t('html.label.punchcard30days'), icon: faBraille, color: 'black', href: 'punchcard', + element: + }, + ]}/> + +} + +export default OnlineActivityGraphsCard; \ No newline at end of file diff --git a/Plan/react/dashboard/src/components/graphs/LineGraph.js b/Plan/react/dashboard/src/components/graphs/LineGraph.js new file mode 100644 index 000000000..045b9c1b5 --- /dev/null +++ b/Plan/react/dashboard/src/components/graphs/LineGraph.js @@ -0,0 +1,42 @@ +import {useTheme} from "../../hooks/themeHook"; +import React, {useEffect} from "react"; +import {linegraphButtons} from "../../util/graphs"; +import Highcharts from "highcharts/highstock"; +import NoDataDisplay from "highcharts/modules/no-data-to-display" +import {useTranslation} from "react-i18next"; + +const LineGraph = ({id, series}) => { + const {t} = useTranslation() + const {graphTheming} = useTheme(); + + useEffect(() => { + NoDataDisplay(Highcharts); + Highcharts.setOptions({lang: {noData: t('html.labels.noDataToDisplay')}}) + Highcharts.setOptions(graphTheming); + Highcharts.stockChart(id, { + rangeSelector: { + selected: 2, + buttons: linegraphButtons + }, + yAxis: { + softMax: 2, + softMin: 0 + }, + title: {text: ''}, + plotOptions: { + areaspline: { + fillOpacity: 0.4 + } + }, + series: series + }) + }, [series, graphTheming]) + + return ( +
+ +
+ ) +} + +export default LineGraph \ No newline at end of file diff --git a/Plan/react/dashboard/src/components/graphs/PingGraph.js b/Plan/react/dashboard/src/components/graphs/PingGraph.js index 668a6653d..fab7da72a 100644 --- a/Plan/react/dashboard/src/components/graphs/PingGraph.js +++ b/Plan/react/dashboard/src/components/graphs/PingGraph.js @@ -1,12 +1,11 @@ -import React, {useEffect} from "react"; +import React, {useEffect, useState} from "react"; import {useTranslation} from "react-i18next"; -import {useTheme} from "../../hooks/themeHook"; -import Highcharts from "highcharts/highstock"; -import {linegraphButtons, tooltip} from "../../util/graphs"; +import {tooltip} from "../../util/graphs"; +import LineGraph from "./LineGraph"; const PingGraph = ({data}) => { const {t} = useTranslation(); - const {graphTheming} = useTheme(); + const [series, setSeries] = useState([]); useEffect(() => { const avgPingSeries = { @@ -30,25 +29,11 @@ const PingGraph = ({data}) => { data: data.min_ping_series, color: data.colors.min } - Highcharts.setOptions(graphTheming); - Highcharts.stockChart("ping-graph", { - rangeSelector: { - selected: 2, - buttons: linegraphButtons - }, - yAxis: { - softMax: 2, - softMin: 0 - }, - title: {text: ''}, - series: [avgPingSeries, maxPingSeries, minPingSeries] - }) - }, [data, graphTheming, t]) + setSeries([avgPingSeries, maxPingSeries, minPingSeries]); + }, [data, t]) return ( -
- -
+ ) } diff --git a/Plan/react/dashboard/src/components/graphs/PlayersOnlineGraph.js b/Plan/react/dashboard/src/components/graphs/PlayersOnlineGraph.js index 85b44d6a4..3f84f9f41 100644 --- a/Plan/react/dashboard/src/components/graphs/PlayersOnlineGraph.js +++ b/Plan/react/dashboard/src/components/graphs/PlayersOnlineGraph.js @@ -1,12 +1,11 @@ -import React, {useEffect} from "react"; +import React, {useEffect, useState} from "react"; import {useTranslation} from "react-i18next"; -import {useTheme} from "../../hooks/themeHook"; -import Highcharts from "highcharts/highstock"; -import {linegraphButtons, tooltip} from "../../util/graphs"; +import {tooltip} from "../../util/graphs"; +import LineGraph from "./LineGraph"; const PlayersOnlineGraph = ({data}) => { const {t} = useTranslation(); - const {graphTheming} = useTheme(); + const [series, setSeries] = useState([]); useEffect(() => { const playersOnlineSeries = { @@ -17,30 +16,11 @@ const PlayersOnlineGraph = ({data}) => { color: data.color, yAxis: 0 } - Highcharts.setOptions(graphTheming); - Highcharts.stockChart("online-activity-graph", { - rangeSelector: { - selected: 2, - buttons: linegraphButtons - }, - yAxis: { - softMax: 2, - softMin: 0 - }, - title: {text: ''}, - plotOptions: { - areaspline: { - fillOpacity: 0.4 - } - }, - series: [playersOnlineSeries] - }) - }, [data, graphTheming, t]) + setSeries([playersOnlineSeries]); + }, [data, t]) return ( -
- -
+ ) } diff --git a/Plan/react/dashboard/src/components/graphs/TimeByTimeGraph.js b/Plan/react/dashboard/src/components/graphs/TimeByTimeGraph.js new file mode 100644 index 000000000..fca0889a4 --- /dev/null +++ b/Plan/react/dashboard/src/components/graphs/TimeByTimeGraph.js @@ -0,0 +1,33 @@ +import {useTranslation} from "react-i18next"; +import React, {useEffect, useState} from "react"; +import {tooltip} from "../../util/graphs"; +import LineGraph from "./LineGraph"; + +const TimeByTimeGraph = ({data}) => { + const {t} = useTranslation(); + const [series, setSeries] = useState([]); + + useEffect(() => { + const uniquePlayers = { + name: t('html.label.uniquePlayers'), + type: 'spline', + tooltip: tooltip.zeroDecimals, + data: data.uniquePlayers, + color: data.colors.playersOnline + }; + const newPlayers = { + name: t('html.label.newPlayers'), + type: 'spline', + tooltip: tooltip.zeroDecimals, + data: data.newPlayers, + color: data.colors.newPlayers + }; + setSeries([uniquePlayers, newPlayers]); + }, [data, t]) + + return ( + + ) +} + +export default TimeByTimeGraph \ No newline at end of file diff --git a/Plan/react/dashboard/src/components/navigation/Sidebar.js b/Plan/react/dashboard/src/components/navigation/Sidebar.js index 3ab1c1722..38e8d53ee 100644 --- a/Plan/react/dashboard/src/components/navigation/Sidebar.js +++ b/Plan/react/dashboard/src/components/navigation/Sidebar.js @@ -172,7 +172,7 @@ const SidebarCollapse = ({item, open, setOpen}) => { ) } -const renderItem = (item, i, openCollapse, setOpenCollapse) => { +const renderItem = (item, i, openCollapse, setOpenCollapse, t) => { if (item.contents) { return { } if (item.name) { - return
{item.name}
+ return
{t(item.name)}
} return
@@ -227,7 +227,7 @@ const Sidebar = ({items, showBackButton}) => { : ''} - {items.map((item, i) => renderItem(item, i, openCollapse, toggleCollapse))} + {items.map((item, i) => renderItem(item, i, openCollapse, toggleCollapse, t))} } diff --git a/Plan/react/dashboard/src/hooks/dataFetchHook.js b/Plan/react/dashboard/src/hooks/dataFetchHook.js index a55136932..cf3c534c5 100644 --- a/Plan/react/dashboard/src/hooks/dataFetchHook.js +++ b/Plan/react/dashboard/src/hooks/dataFetchHook.js @@ -1,19 +1,22 @@ import {useEffect, useState} from "react"; +import {useNavigation} from "./navigationHook"; export const useDataRequest = (fetchMethod, parameters) => { const [data, setData] = useState(undefined); const [loadingError, setLoadingError] = useState(undefined); + const {updateRequested, finishUpdate} = useNavigation(); /*eslint-disable react-hooks/exhaustive-deps */ useEffect(() => { - fetchMethod(...parameters).then(({data: json, error}) => { + fetchMethod(...parameters, updateRequested).then(({data: json, error}) => { if (json) { - setData(json) + setData(json); + finishUpdate(json.timestamp, json.timestamp_f); } else if (error) { setLoadingError(error); } }); - }, [fetchMethod, ...parameters]) + }, [fetchMethod, ...parameters, updateRequested]) /* eslint-enable react-hooks/exhaustive-deps */ return {data, loadingError}; diff --git a/Plan/react/dashboard/src/hooks/navigationHook.js b/Plan/react/dashboard/src/hooks/navigationHook.js index 3d662bcf6..5f84f88be 100644 --- a/Plan/react/dashboard/src/hooks/navigationHook.js +++ b/Plan/react/dashboard/src/hooks/navigationHook.js @@ -18,6 +18,7 @@ export const NavigationContextProvider = ({children}) => { }, [updating, setUpdateRequested, setUpdating]); const finishUpdate = useCallback((date, formatted) => { + // TODO Logic to retry if received data is too old setLastUpdate({date, formatted}); setUpdating(false); }, [setLastUpdate, setUpdating]); diff --git a/Plan/react/dashboard/src/service/serverService.js b/Plan/react/dashboard/src/service/serverService.js index f996a05b6..5f1130699 100644 --- a/Plan/react/dashboard/src/service/serverService.js +++ b/Plan/react/dashboard/src/service/serverService.js @@ -14,13 +14,13 @@ export const fetchPlayersOnlineGraph = async (identifier) => { export const fetchDayByDayGraph = async (identifier) => { const timestamp = Date.now(); - const url = `/v1/graph?type=dayByDay&server=${identifier}×tamp=${timestamp}`; + const url = `/v1/graph?type=uniqueAndNew&server=${identifier}×tamp=${timestamp}`; return doGetRequest(url); } export const fetchHourByHourGraph = async (identifier) => { const timestamp = Date.now(); - const url = `/v1/graph?type=hourByHour&server=${identifier}×tamp=${timestamp}`; + const url = `/v1/graph?type=hourlyUniqueAndNew&server=${identifier}×tamp=${timestamp}`; return doGetRequest(url); } diff --git a/Plan/react/dashboard/src/views/ServerOnlineActivity.js b/Plan/react/dashboard/src/views/ServerOnlineActivity.js index 7cc086c15..125a756c7 100644 --- a/Plan/react/dashboard/src/views/ServerOnlineActivity.js +++ b/Plan/react/dashboard/src/views/ServerOnlineActivity.js @@ -1,65 +1,13 @@ -import {useParams} from "react-router-dom"; import React from "react"; -import {fetchPunchCardGraph} from "../service/serverService"; -import {Card, Col, Row} from "react-bootstrap-v5"; -import CardTabs from "../components/CardTabs"; -import {useTranslation} from "react-i18next"; -import {faBraille, faChartArea} from "@fortawesome/free-solid-svg-icons"; -import PunchCard from "../components/graphs/PunchCard"; -import {faCalendar} from "@fortawesome/free-regular-svg-icons"; -import {useDataRequest} from "../hooks/dataFetchHook"; -import {ErrorViewBody} from "./ErrorView"; - -const DayByDayGraph = () => { - return <> -} - -const HourByHourGraph = () => { - return <> -} - -const ServerCalendar = () => { - return <> -} - -const ServerPunchCard = () => { - const {identifier} = useParams(); - - const {data, loadingError} = useDataRequest(fetchPunchCardGraph, [identifier]) - - if (loadingError) return - if (!data) return <>; - - return -} - -const GraphsTabbedCard = () => { - const {t} = useTranslation(); - return - }, { - name: t('html.label.hourByHour'), icon: faChartArea, color: 'blue', href: 'hour-by-hour', - element: - }, { - name: t('html.label.serverCalendar'), icon: faCalendar, color: 'teal', href: 'server-calendar', - element: - }, { - name: t('html.label.punchcard30days'), icon: faBraille, color: 'black', href: 'punchcard', - element: - }, - ]} - /> -} +import {Col, Row} from "react-bootstrap-v5"; +import OnlineActivityGraphsCard from "../components/cards/server/OnlineActivityGraphsCard"; const ServerOnlineActivity = () => { return (
- +