diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/ServerOverviewJSONCreator.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/ServerOverviewJSONCreator.java index 9970cdb22..e8ec4cedc 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/ServerOverviewJSONCreator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/ServerOverviewJSONCreator.java @@ -16,7 +16,6 @@ */ package com.djrapitops.plan.delivery.rendering.json; -import com.djrapitops.plan.delivery.domain.DateHolder; import com.djrapitops.plan.delivery.domain.DateObj; import com.djrapitops.plan.delivery.domain.mutators.TPSMutator; import com.djrapitops.plan.delivery.formatting.Formatter; @@ -55,17 +54,14 @@ import java.util.concurrent.TimeUnit; @Singleton public class ServerOverviewJSONCreator implements ServerTabJSONCreator> { - private final Formatter day; private final PlanConfig config; private final DBSystem dbSystem; private final ServerInfo serverInfo; private final ServerSensor serverSensor; - private final Formatter timeAmount; private final Formatter decimals; private final Formatter percentage; private final ServerUptimeCalculator serverUptimeCalculator; - private final Formatter year; @Inject public ServerOverviewJSONCreator( @@ -82,9 +78,6 @@ public class ServerOverviewJSONCreator implements ServerTabJSONCreator> lastPeak = db.query(TPSQueries.fetchPeakPlayerCount(serverUUID, twoDaysAgo)); Optional> allTimePeak = db.query(TPSQueries.fetchAllTimePeakPlayerCount(serverUUID)); - numbers.put("last_peak_date", lastPeak.map(year).orElse("-")); + numbers.put("last_peak_date", lastPeak.map(DateObj::getDate).map(Object.class::cast).orElse("-")); numbers.put("last_peak_players", lastPeak.map(dateObj -> dateObj.getValue().toString()).orElse("-")); - numbers.put("best_peak_date", allTimePeak.map(year).orElse("-")); + numbers.put("best_peak_date", allTimePeak.map(DateObj::getDate).map(Object.class::cast).orElse("-")); numbers.put("best_peak_players", allTimePeak.map(dateObj -> dateObj.getValue().toString()).orElse("-")); Long totalPlaytime = db.query(SessionQueries.playtime(0L, now, serverUUID)); - numbers.put("playtime", timeAmount.apply(totalPlaytime)); - numbers.put("player_playtime", userCount != 0 ? timeAmount.apply(totalPlaytime / userCount) : "-"); + numbers.put("playtime", totalPlaytime); + numbers.put("player_playtime", userCount != 0 ? totalPlaytime / userCount : "-"); numbers.put("sessions", db.query(SessionQueries.sessionCount(0L, now, serverUUID))); numbers.put("player_kills", db.query(KillQueries.playerKillCount(0L, now, serverUUID))); numbers.put("mob_kills", db.query(KillQueries.mobKillCount(0L, now, serverUUID))); numbers.put("deaths", db.query(KillQueries.deathCount(0L, now, serverUUID))); - numbers.put("current_uptime", serverUptimeCalculator.getServerUptimeMillis(serverUUID).map(timeAmount) + numbers.put("current_uptime", serverUptimeCalculator.getServerUptimeMillis(serverUUID) + .map(Object.class::cast) .orElse(GenericLang.UNAVAILABLE.getKey())); return numbers; @@ -171,9 +165,9 @@ public class ServerOverviewJSONCreator implements ServerTabJSONCreator weeks = new HashMap<>(); - weeks.put("start", day.apply(twoWeeksAgo)); - weeks.put("midpoint", day.apply(oneWeekAgo)); - weeks.put("end", day.apply(now)); + weeks.put("start", twoWeeksAgo); + weeks.put("midpoint", oneWeekAgo); + weeks.put("end", now); Integer uniqueBefore = db.query(PlayerCountQueries.uniquePlayerCount(twoWeeksAgo, oneWeekAgo, serverUUID)); Integer uniqueAfter = db.query(PlayerCountQueries.uniquePlayerCount(oneWeekAgo, now, serverUUID)); @@ -199,9 +193,9 @@ public class ServerOverviewJSONCreator implements ServerTabJSONCreator> { - private final Formatter day; private final PlanConfig config; private final DBSystem dbSystem; private final ServerInfo serverInfo; private final ServerSensor serverSensor; - private final Formatter timeAmount; private final ServerUptimeCalculator serverUptimeCalculator; - private final Formatter year; @Inject public NetworkOverviewJSONCreator( @@ -73,10 +68,6 @@ public class NetworkOverviewJSONCreator implements NetworkTabJSONCreator createJSONAsMap() { @@ -122,17 +113,18 @@ public class NetworkOverviewJSONCreator implements NetworkTabJSONCreator> lastPeak = db.query(TPSQueries.fetchPeakPlayerCount(serverUUID, twoDaysAgo)); Optional> allTimePeak = db.query(TPSQueries.fetchAllTimePeakPlayerCount(serverUUID)); - numbers.put("last_peak_date", lastPeak.map(year).orElse("-")); + numbers.put("last_peak_date", lastPeak.map(DateObj::getDate).map(Object.class::cast).orElse("-")); numbers.put("last_peak_players", lastPeak.map(dateObj -> dateObj.getValue().toString()).orElse("-")); - numbers.put("best_peak_date", allTimePeak.map(year).orElse("-")); + numbers.put("best_peak_date", allTimePeak.map(DateObj::getDate).map(Object.class::cast).orElse("-")); numbers.put("best_peak_players", allTimePeak.map(dateObj -> dateObj.getValue().toString()).orElse("-")); Long totalPlaytime = db.query(SessionQueries.playtime(0L, now)); - numbers.put("playtime", timeAmount.apply(totalPlaytime)); - numbers.put("player_playtime", userCount != 0 ? timeAmount.apply(totalPlaytime / userCount) : "-"); + numbers.put("playtime", totalPlaytime); + numbers.put("player_playtime", userCount != 0 ? totalPlaytime / userCount : "-"); Long sessionCount = db.query(SessionQueries.sessionCount(0L, now)); numbers.put("sessions", sessionCount); - numbers.put("session_length_avg", sessionCount != 0 ? timeAmount.apply(totalPlaytime / sessionCount) : "-"); - numbers.put("current_uptime", serverUptimeCalculator.getServerUptimeMillis(serverUUID).map(timeAmount) + numbers.put("session_length_avg", sessionCount != 0 ? totalPlaytime / sessionCount : "-"); + numbers.put("current_uptime", serverUptimeCalculator.getServerUptimeMillis(serverUUID) + .map(Object.class::cast) .orElse(GenericLang.UNAVAILABLE.getKey())); return numbers; @@ -147,9 +139,9 @@ public class NetworkOverviewJSONCreator implements NetworkTabJSONCreator weeks = new HashMap<>(); - weeks.put("start", day.apply(twoWeeksAgo)); - weeks.put("midpoint", day.apply(oneWeekAgo)); - weeks.put("end", day.apply(now)); + weeks.put("start", twoWeeksAgo); + weeks.put("midpoint", oneWeekAgo); + weeks.put("end", now); Integer uniqueBefore = db.query(PlayerCountQueries.uniquePlayerCount(twoWeeksAgo, oneWeekAgo)); Integer uniqueAfter = db.query(PlayerCountQueries.uniquePlayerCount(oneWeekAgo, now)); @@ -175,9 +167,9 @@ public class NetworkOverviewJSONCreator implements NetworkTabJSONCreator { const {t} = useTranslation(); @@ -20,11 +22,18 @@ const ServerWeekComparisonCard = ({data}) => { - - , + <> - , + t('html.label.trend')]}> + ]}/> + trend={data.unique_trend}/> + ]}/> { trend={data.regular_trend}/>]}/> , + , ]}/> - ]}/> + trend={data.average_playtime_trend} + format={formatTimeFunction}/>]}/> + {data.session_length_average_before !== undefined && , + , + ]}/>} { const {t} = useTranslation(); @@ -40,23 +42,23 @@ const ServerAsNumbersCard = ({data}) => { value={data.online_players} bold/> {showPeaks && <>
- {t('html.label.lastPeak')} ()} color={'blue'} icon={faChartLine} value={data.last_peak_players} valueLabel={t('html.unit.players')} bold/> - {t('html.label.bestPeak')} ()} color={'light-green'} icon={faChartLine} value={data.best_peak_players} valueLabel={t('html.unit.players')} bold/> }
+ value={}/> + value={}/> + value={}/> diff --git a/Plan/react/dashboard/src/components/datapoint/CurrentUptime.jsx b/Plan/react/dashboard/src/components/datapoint/CurrentUptime.jsx index 0c69693e8..1e3e575de 100644 --- a/Plan/react/dashboard/src/components/datapoint/CurrentUptime.jsx +++ b/Plan/react/dashboard/src/components/datapoint/CurrentUptime.jsx @@ -4,6 +4,8 @@ import {faPowerOff} from "@fortawesome/free-solid-svg-icons"; import {faQuestionCircle} from "@fortawesome/free-regular-svg-icons"; import Datapoint from "../Datapoint"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import FormattedTime from "../text/FormattedTime.jsx"; +import {isNumber} from "../../util/isNumber.js"; const CurrentUptime = ({uptime}) => { const {t} = useTranslation(); @@ -15,7 +17,7 @@ const CurrentUptime = ({uptime}) => { return ( + value={isNumber(uptime) && || uptime} valueLabel={infoBubble}/> ) }; diff --git a/Plan/react/dashboard/src/components/text/FormattedDate.jsx b/Plan/react/dashboard/src/components/text/FormattedDate.jsx index af5be08a0..d19a8d405 100644 --- a/Plan/react/dashboard/src/components/text/FormattedDate.jsx +++ b/Plan/react/dashboard/src/components/text/FormattedDate.jsx @@ -3,13 +3,16 @@ import {usePreferences} from "../../hooks/preferencesHook"; import {SimpleDateFormat} from "../../util/format/SimpleDateFormat"; import {useMetadata} from "../../hooks/metadataHook"; import {useTranslation} from "react-i18next"; +import {isNumber} from "../../util/isNumber.js"; const FormattedDate = ({date}) => { + console.log(date); const {t} = useTranslation(); const {timeZoneOffsetHours} = useMetadata(); const {preferencesLoaded, dateFormatNoSeconds, recentDaysInDateFormat} = usePreferences(); - if (!preferencesLoaded || date === undefined || date === null) return <> + if (!preferencesLoaded || date === undefined || date === null) return <>; + if (!isNumber(date)) return date; const pattern = dateFormatNoSeconds; const recentDays = recentDaysInDateFormat; diff --git a/Plan/react/dashboard/src/components/text/FormattedDay.jsx b/Plan/react/dashboard/src/components/text/FormattedDay.jsx new file mode 100644 index 000000000..ba7a86892 --- /dev/null +++ b/Plan/react/dashboard/src/components/text/FormattedDay.jsx @@ -0,0 +1,24 @@ +import React from 'react'; +import {SimpleDateFormat} from "../../util/format/SimpleDateFormat"; +import {useMetadata} from "../../hooks/metadataHook"; +import {isNumber} from "../../util/isNumber.js"; + +const FormattedDay = ({date}) => { + const {timeZoneOffsetHours} = useMetadata(); + + if (date === undefined || date === null) return <>; + if (!isNumber(date)) return date; + + const pattern = "MMMMM d"; + + const offset = timeZoneOffsetHours * 60 * 60 * 1000; + const timestamp = date - offset; + + const formatted = date !== 0 ? new SimpleDateFormat(pattern).format(timestamp) : '-'; + + return ( + <>{formatted} + ) +}; + +export default FormattedDay \ No newline at end of file diff --git a/Plan/react/dashboard/src/components/text/FormattedTime.jsx b/Plan/react/dashboard/src/components/text/FormattedTime.jsx index f401c82ba..dae04c83f 100644 --- a/Plan/react/dashboard/src/components/text/FormattedTime.jsx +++ b/Plan/react/dashboard/src/components/text/FormattedTime.jsx @@ -1,11 +1,13 @@ import React from 'react'; import {usePreferences} from "../../hooks/preferencesHook"; import {formatTimeAmount} from "../../util/format/TimeAmountFormat"; +import {isNumber} from "../../util/isNumber.js"; const FormattedTime = ({timeMs}) => { const {preferencesLoaded, timeFormat} = usePreferences(); - if (!preferencesLoaded) return <> + if (!preferencesLoaded) return <>; + if (!isNumber(timeMs)) return timeMs; const options = { YEAR: timeFormat.year, @@ -26,4 +28,10 @@ const FormattedTime = ({timeMs}) => { ) }; +export const formatTimeFunction = time => { + return ( + + ); +} + export default FormattedTime \ No newline at end of file diff --git a/Plan/react/dashboard/src/components/trend/BigTrend.jsx b/Plan/react/dashboard/src/components/trend/BigTrend.jsx index 45419a97f..0e29b0f56 100644 --- a/Plan/react/dashboard/src/components/trend/BigTrend.jsx +++ b/Plan/react/dashboard/src/components/trend/BigTrend.jsx @@ -9,17 +9,20 @@ const TrendDownGood = ({value}) => {value}; -const BigTrend = ({trend}) => { +const BigTrend = ({trend, format}) => { if (!trend) { return ; } + + const value = format ? format(trend.text) : trend.text; + switch (trend.direction) { case '+': - return (trend.reversed ? : ); + return (trend.reversed ? : ); case '-': - return (trend.reversed ? : ); + return (trend.reversed ? : ); default: - return ; + return ; } } diff --git a/Plan/react/dashboard/src/style/style.css b/Plan/react/dashboard/src/style/style.css index 8d1e64c33..8e3af46bf 100644 --- a/Plan/react/dashboard/src/style/style.css +++ b/Plan/react/dashboard/src/style/style.css @@ -1505,4 +1505,14 @@ ul.filters { .nav-item.disabled { opacity: 30%; -} \ No newline at end of file +} + +.link { + color: var(--bs-link-color); + opacity: var(--bs-link-opacity); + text-decoration: underline; +} + +.link:hover { + color: var(--bs-link-hover-color) +} diff --git a/Plan/react/dashboard/src/util/isNumber.js b/Plan/react/dashboard/src/util/isNumber.js new file mode 100644 index 000000000..68c3e26b3 --- /dev/null +++ b/Plan/react/dashboard/src/util/isNumber.js @@ -0,0 +1,4 @@ +// https://stackoverflow.com/a/1421988/20825073 +export function isNumber(n) { + return !isNaN(parseFloat(n)) && !isNaN(n - 0) +} \ No newline at end of file diff --git a/Plan/react/dashboard/src/views/server/ServerOverview.jsx b/Plan/react/dashboard/src/views/server/ServerOverview.jsx index 0fea3a70a..dbf7e0dfa 100644 --- a/Plan/react/dashboard/src/views/server/ServerOverview.jsx +++ b/Plan/react/dashboard/src/views/server/ServerOverview.jsx @@ -23,6 +23,7 @@ import {CardLoader} from "../../components/navigation/Loader"; import ExtendableRow from "../../components/layout/extension/ExtendableRow"; import {useAuth} from "../../hooks/authenticationHook"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import FormattedTime from "../../components/text/FormattedTime.jsx"; const Last7DaysCard = ({data}) => { const {t} = useTranslation(); @@ -64,7 +65,7 @@ const Last7DaysCard = ({data}) => { value={data.low_tps_spikes} bold/> + value={}/> )