1
0
mirror of https://github.com/plan-player-analytics/Plan.git synced 2025-03-28 14:36:05 +01:00
This commit is contained in:
AuroraLS3 2022-12-08 14:55:31 +00:00
parent 8008131ad7
commit f4bf770b8c
15 changed files with 107 additions and 74 deletions

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta content="AuroraLS3" name="author">
<meta content="noindex, nofollow" name="robots">
<title>Plan | Player Analytics</title>
<script>window.location.href = `/?redirect=${encodeURIComponent(window.location.pathname + window.location.hash + window.location.search)}`</script>
</head>
<body>
<noscript>Please enable javascript.</noscript>
</body>
</html>

View File

@ -23,11 +23,11 @@
"datatables.net": "^1.13.1",
"datatables.net-bs5": "^1.12.1",
"datatables.net-responsive-bs5": "^2.4.0",
"highcharts": "^10.3.1",
"highcharts": "^10.3.2",
"i18next": "^22.0.6",
"i18next-chained-backend": "^4.0.0",
"i18next-http-backend": "^2.0.1",
"i18next-localstorage-backend": "^4.0.0",
"i18next-chained-backend": "^4.0.1",
"i18next-http-backend": "^2.0.2",
"i18next-localstorage-backend": "^4.0.1",
"masonry-layout": "^4.2.2",
"react": "^17.0.2",
"react-bootstrap-v5": "^1.4.0",

View File

@ -3,8 +3,7 @@
<head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<meta content="Player Analytics, player page that displays more insights about a specific player"
name="description">
<meta content="Player Analytics" name="description">
<meta content="AuroraLS3" name="author">
<meta content="noindex, nofollow" name="robots">

View File

@ -22,28 +22,28 @@ const PlayerbaseTrendsCard = ({data}) => {
<ComparisonTable comparisonHeader={t('html.text.comparing30daysAgo')}
headers={[t('html.label.thirtyDaysAgo'), t('html.label.now'), t('html.label.trend')]}>
<TableRow icon={faUsers} color="black" text={t('html.label.totalPlayers')}
values={[data.total_players_now, data.total_players_then,
values={[data.total_players_then, data.total_players_now,
<BigTrend trend={data.total_players_trend}/>]}/>
<TableRow icon={faUsers} color="lime" text={t('html.label.regularPlayers')}
values={[data.regular_players_now, data.regular_players_then,
values={[data.regular_players_then, data.regular_players_now,
<BigTrend trend={data.regular_players_trend}/>]}/>
<TableRow icon={faClock} color="green"
text={t('html.label.averagePlaytime') + ' ' + t('html.label.perPlayer')}
values={[data.playtime_avg_now, data.playtime_avg_then,
values={[data.playtime_avg_then, data.playtime_avg_now,
<BigTrend trend={data.playtime_avg_trend}/>]}/>
<TableRow icon={faClock} color="gray" text={t('html.label.afk') + ' ' + t('html.label.perPlayer')}
values={[data.afk_now, data.afk_then, <BigTrend trend={data.afk_trend}/>]}/>
values={[data.afk_then, data.afk_now, <BigTrend trend={data.afk_trend}/>]}/>
<TableRow icon={faClock} color="green"
text={t('html.label.averagePlaytime') + ' ' + t('html.label.perRegularPlayer')}
values={[data.regular_playtime_avg_now, data.regular_playtime_avg_then,
values={[data.regular_playtime_avg_then, data.regular_playtime_avg_now,
<BigTrend trend={data.regular_playtime_avg_trend}/>]}/>
<TableRow icon={faClock} color="teal"
text={t('html.label.averageSessionLength') + ' ' + t('html.label.perRegularPlayer')}
values={[data.regular_session_avg_now, data.regular_session_avg_then,
values={[data.regular_session_avg_then, data.regular_session_avg_now,
<BigTrend trend={data.regular_session_avg_trend}/>]}/>
<TableRow icon={faClock} color="gray"
text={t('html.label.afk') + ' ' + t('html.label.perRegularPlayer')}
values={[data.regular_afk_avg_now, data.regular_afk_avg_then,
values={[data.regular_afk_avg_then, data.regular_afk_avg_now,
<BigTrend trend={data.regular_afk_avg_trend}/>]}/>
</ComparisonTable>
</Card>

View File

@ -1,7 +1,7 @@
import {useMetadata} from "../../hooks/metadataHook";
import {useAuth} from "../../hooks/authenticationHook";
import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome";
import {faBars, faCog, faDoorOpen, faPalette, faSyncAlt} from "@fortawesome/free-solid-svg-icons";
import {faBars, faClockRotateLeft, faCog, faDoorOpen, faPalette, faSyncAlt} from "@fortawesome/free-solid-svg-icons";
import DropdownMenu from "react-bootstrap-v5/lib/esm/DropdownMenu";
import DropdownItem from "react-bootstrap-v5/lib/esm/DropdownItem";
import {useTheme} from "../../hooks/themeHook";
@ -58,6 +58,7 @@ const Header = ({page, tab, hideUpdater}) => {
{!staticSite && <button onClick={requestUpdate}>
<Fa icon={faSyncAlt} spin={Boolean(updating)}/>
</button>}
{staticSite && <Fa icon={faClockRotateLeft} title={t('html.label.exported')}/>}
{' '}
<span className="refresh-time">{lastUpdate.formatted}</span>
</div>

View File

@ -2,6 +2,7 @@ import {useAuth} from "../../hooks/authenticationHook";
import {useMetadata} from "../../hooks/metadataHook";
import {Navigate} from "react-router-dom";
import React, {useEffect, useState} from "react";
import {staticSite} from "../../service/backendConfiguration";
const RedirectPlaceholder = () => {
const [redirectStart] = useState(Date.now())
@ -49,14 +50,19 @@ const MainPageRedirect = () => {
const {authLoaded, authRequired, loggedIn, user} = useAuth();
const {isProxy, serverName} = useMetadata();
if (staticSite) {
const urlParams = new URLSearchParams(window.location.search);
const redirect = urlParams.get('redirect');
if (redirect) {
return (<Navigate to={redirect} replace={true}/>)
}
}
if (!authLoaded || !serverName) {
return <RedirectPlaceholder/>
}
if (authRequired && !loggedIn) {
return (<Navigate to="/login" replace={true}/>)
} else if (authRequired && loggedIn) {
const redirectBasedOnPermissions = () => {
if (isProxy && user.permissions.includes('page.network')) {
return (<Navigate to={"/network/overview"} replace={true}/>)
} else if (user.permissions.includes('page.server')) {
@ -66,6 +72,12 @@ const MainPageRedirect = () => {
} else if (user.permissions.includes('page.player.self')) {
return (<Navigate to={"/player/" + user.linkedToUuid} replace={true}/>)
}
};
if (authRequired && !loggedIn) {
return (<Navigate to="/login" replace={true}/>)
} else if (authRequired && loggedIn) {
return redirectBasedOnPermissions();
} else {
return (<Navigate to={isProxy ? "/network/overview" : "/server/" + encodeURIComponent(serverName) + "/overview"}
replace={true}/>)

View File

@ -23,12 +23,12 @@ const Divider = ({showMargin}) => (
<hr className={"sidebar-divider" + (showMargin ? '' : " my-0")}/>
)
const InnerItem = ({href, icon, name, nameShort, color}) => {
const InnerItem = ({href, icon, name, nameShort, color, external}) => {
if (!href) {
return (<hr className={"nav-servers dropdown-divider mx-3 my-2"}/>)
}
if (href.startsWith('/')) {
if (external) {
return (
<a href={href} className="collapse-item nav-button">
<Fa icon={icon} className={color ? "col-" + color : undefined}/>
@ -56,10 +56,11 @@ const Item = ({item, inner}) => {
}, [pathname, href, setCurrentTab, name, external])
if (inner) {
return (<InnerItem href={href} icon={icon} name={t(name)} nameShort={t(nameShort)} color={color}/>)
return (<InnerItem href={href} icon={icon} name={t(name)} nameShort={t(nameShort)} color={color}
external={external}/>)
}
if (href.startsWith('/')) {
if (external) {
return (
<li className={"nav-item nav-button"}>
<a href={baseAddress + href} className="nav-link">
@ -165,12 +166,14 @@ const SidebarCollapse = ({item, open, setOpen}) => {
<Collapse in={open}>
<div id={item.name + "-collapse"}>
<div className="bg-white py-2 collapse-inner rounded">
{item.contents.map((content, i) =>
<Item key={i}
inner
active={false}
item={content}
/>)}
{item.contents
.filter(content => content !== undefined)
.map((content, i) =>
<Item key={i}
inner
active={false}
item={content}
/>)}
</div>
</div>
</Collapse>

View File

@ -15,9 +15,11 @@ export const NavigationContextProvider = ({children}) => {
const pathname = window.location.href;
setItems(items);
for (const item of items) {
if (!item) continue;
if ('/' !== item.href && pathname.includes(item.href)) setCurrentTab(item.name);
if (item.contents) {
for (const subItem of item.contents) {
if (!subItem) continue;
if ('/' !== subItem.href && pathname.includes(subItem.href)) setCurrentTab(subItem.name);
}
}

View File

@ -2,7 +2,7 @@ import {doGetRequest, doSomePostRequest, standard200option, staticSite} from "./
export const fetchWhoAmI = async () => {
if (staticSite) {
return {authRequired: false, loggedIn: false}
return {data: {authRequired: false, loggedIn: false}, error: null};
}
const url = '/v1/whoami';
return doGetRequest(url);

View File

@ -12,7 +12,6 @@ import drawSine from "../../util/loginSineRenderer";
import {fetchLogin} from "../../service/authenticationService";
import ForgotPasswordModal from "../../components/modal/ForgotPasswordModal";
import {useAuth} from "../../hooks/authenticationHook";
import {baseAddress} from "../../service/backendConfiguration";
const Logo = () => {
return (
@ -170,9 +169,9 @@ const LoginPage = () => {
} else if (data && data.success) {
await updateLoginDetails();
if (redirectTo && !redirectTo.startsWith('http') && !redirectTo.startsWith('file') && !redirectTo.startsWith('javascript')) {
navigate(baseAddress + redirectTo.substring(redirectTo.indexOf('/')) + (window.location.hash ? window.location.hash : ''));
navigate(redirectTo.substring(redirectTo.indexOf('/')) + (window.location.hash ? window.location.hash : ''));
} else {
navigate(baseAddress + '/');
navigate('/');
}
} else {
setFailMessage(t('html.login.failed') + data ? data.error : t('generic.noData'));

View File

@ -95,11 +95,13 @@ const NetworkSidebar = () => {
}).forEach(item => items.push(item))
}
items.push(
{},
{name: 'html.label.links'},
{name: 'html.label.query', icon: faSearch, href: "/query"}
);
if (!staticSite) {
items.push(
{},
{name: 'html.label.links'},
{name: 'html.label.query', icon: faSearch, href: "/query"}
);
}
setSidebarItems(items);
window.document.title = `Plan | Network`;

View File

@ -9,6 +9,7 @@ import Header from "../../components/navigation/Header";
import ColorSelectorModal from "../../components/modal/ColorSelectorModal";
import {useMetadata} from "../../hooks/metadataHook";
import ErrorPage from "./ErrorPage";
import {staticSite} from "../../service/backendConfiguration";
const PlayersPage = () => {
const {t, i18n} = useTranslation();
@ -20,7 +21,7 @@ const PlayersPage = () => {
const {currentTab, setCurrentTab} = useNavigation();
useEffect(() => {
const items = [
const items = staticSite ? [] : [
{name: 'html.label.links'},
{name: 'html.label.query', icon: faSearch, href: "/query"},
]

View File

@ -11,7 +11,6 @@ import ColorSelectorModal from "../../components/modal/ColorSelectorModal";
import {useAuth} from "../../hooks/authenticationHook";
import FinalizeRegistrationModal from "../../components/modal/FinalizeRegistrationModal";
import {fetchRegisterCheck, postRegister} from "../../service/authenticationService";
import {baseAddress} from "../../service/backendConfiguration";
const Logo = () => {
return (
@ -132,7 +131,7 @@ const RegisterPage = () => {
if (error) {
setFailMessage(t('html.register.error.checkFailed') + error)
} else if (data && data.success) {
navigate(baseAddress + '/login?registerSuccess=true');
navigate('/login?registerSuccess=true');
} else {
setTimeout(() => checkRegistration(code), 5000);
}

View File

@ -29,6 +29,7 @@ import {useDataRequest} from "../../hooks/dataFetchHook";
import {fetchServerIdentity} from "../../service/serverService";
import {ServerExtensionContextProvider, useServerExtensionContext} from "../../hooks/serverExtensionDataContext";
import {iconTypeToFontAwesomeClass} from "../../util/icons";
import {staticSite} from "../../service/backendConfiguration";
const ServerSidebar = () => {
const {t, i18n} = useTranslation();
@ -93,11 +94,13 @@ const ServerSidebar = () => {
}).forEach(item => items.push(item))
}
items.push(
{},
{name: 'html.label.links'},
{name: 'html.label.query', icon: faSearch, href: "/query"}
);
if (!staticSite) {
items.push(
{},
{name: 'html.label.links'},
{name: 'html.label.query', icon: faSearch, href: "/query"}
);
}
setSidebarItems(items);
window.document.title = `Plan | Server Analysis`;

View File

@ -1525,10 +1525,10 @@
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.5.tgz#db5a11bf66bdab39569719555b0f76e138d7bd64"
integrity sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==
"@remix-run/router@1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.0.3.tgz#953b88c20ea00d0eddaffdc1b115c08474aa295d"
integrity sha512-ceuyTSs7PZ/tQqi19YZNBc5X7kj1f8p+4DIyrcIYFY9h+hd1OKm4RqtiWldR9eGEvIiJfsqwM4BsuCtRIuEw6Q==
"@remix-run/router@1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.0.4.tgz#cbfbec6735711e7c2fc93b9b40adf70ef5a39990"
integrity sha512-gTL8H5USTAKOyVA4xczzDJnC3HMssdFa3tRlwBicXynx9XfiXwneHnYQogwSKpdCkjXISrEKSTtX62rLpNEVQg==
"@restart/context@^2.1.4":
version "2.1.4"
@ -4907,10 +4907,10 @@ he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
highcharts@^10.3.1:
version "10.3.1"
resolved "https://registry.yarnpkg.com/highcharts/-/highcharts-10.3.1.tgz#d06d7e7543823c80eea24f44e1c3c2142cf1ccda"
integrity sha512-8UgVcLmgpiYwnsII0Ht76O+GRutfbrLslZFH3c53fXgl3aZ6NRB4mW5qsIyfsUExMny/n9JqYO/BFNejOKC6AA==
highcharts@^10.3.2:
version "10.3.2"
resolved "https://registry.yarnpkg.com/highcharts/-/highcharts-10.3.2.tgz#cb74091b12c1a0112532857eb751044072aa34a8"
integrity sha512-uDSuAOTKXOA90WUE38KKJawzMPjeIX/4UH3fMxZdoypbGVjsbuHO6jJSdeGf1LaDpu5wRg/xjO4QXzJqXfIrkA==
highlight.js@^10.4.1, highlight.js@~10.7.0:
version "10.7.3"
@ -5070,24 +5070,24 @@ human-signals@^2.1.0:
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
i18next-chained-backend@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/i18next-chained-backend/-/i18next-chained-backend-4.0.0.tgz#97679ee4b6e04e1ad96e49b3c4ab755ff62238eb"
integrity sha512-gOfkl2tvRDSMKQ2vaYbP+n5fsHeYM/836/Co8/NVP8LplRE8Ck7IrKWswp4vKw4D5Ji7cEdzA4drrG4ssgsXIg==
i18next-chained-backend@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/i18next-chained-backend/-/i18next-chained-backend-4.0.1.tgz#2a0476155678152d3463d03b96981d3e2078591e"
integrity sha512-HCRBCN0IT7+M9FdbtBQRBH0E6htYIG8AjE3VC7WyP1WZq/qabG0SKXTYbHzfO78fcFWo1g2iFztV+Lfbre2HMA==
dependencies:
"@babel/runtime" "^7.19.4"
i18next-http-backend@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/i18next-http-backend/-/i18next-http-backend-2.0.1.tgz#ef744dd76f36399b9bc169c4e4221db9bbaf6fe5"
integrity sha512-kzvSkOT3yhVijumDlp8/TgD1v07lYdFXsf5YYbB7Yu+K2S6PO0lKgZ4c/fyFcAKWiAiTjt9uVecBAbnJjKzhOw==
i18next-http-backend@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/i18next-http-backend/-/i18next-http-backend-2.0.2.tgz#83fa92c12b0b6e90f1969d904b81855d57b1db1c"
integrity sha512-TFiIqitZEc8+jyca31EW5ef5PjUYtUGGfL8c8FJwiiHguq5OQTqoR3mxpKqaCPiikg+cxSgXtNA2gZPCu0aryQ==
dependencies:
cross-fetch "3.1.5"
i18next-localstorage-backend@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/i18next-localstorage-backend/-/i18next-localstorage-backend-4.0.0.tgz#bd1b4318fe0f97baa1121dbb31c0c57e61e45a5d"
integrity sha512-XErjf0Zvciw3fo9/vzU1hWQfwHViq8l31ahKEvf6lgtqysPCtCBxNlIdrSjVZWEe76LD/thox1ixmO9PmlsL/w==
i18next-localstorage-backend@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/i18next-localstorage-backend/-/i18next-localstorage-backend-4.0.1.tgz#ddd1bf02f2a41258ced6d10734badf042aea9f6c"
integrity sha512-hicBcvYOqoKmfbTHcabKFaFT1DWw/rJOw5zBmpuSJUAnJSlV4dI/Y2LRXXhNLM5fZL6Kb/iY5DeWucgy6q3Utw==
dependencies:
"@babel/runtime" "^7.19.4"
@ -7805,19 +7805,19 @@ react-refresh@^0.11.0:
integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==
react-router-dom@6:
version "6.4.3"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.4.3.tgz#70093b5f65f85f1df9e5d4182eb7ff3a08299275"
integrity sha512-MiaYQU8CwVCaOfJdYvt84KQNjT78VF0TJrA17SIQgNHRvLnXDJO6qsFqq8F/zzB1BWZjCFIrQpu4QxcshitziQ==
version "6.4.4"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.4.4.tgz#4271ec66333c440d1754477e4e6a3a5acb5487f8"
integrity sha512-0Axverhw5d+4SBhLqLpzPhNkmv7gahUwlUVIOrRLGJ4/uwt30JVajVJXqv2Qr/LCwyvHhQc7YyK1Do8a9Jj7qA==
dependencies:
"@remix-run/router" "1.0.3"
react-router "6.4.3"
"@remix-run/router" "1.0.4"
react-router "6.4.4"
react-router@6.4.3:
version "6.4.3"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.4.3.tgz#9ed3ee4d6e95889e9b075a5d63e29acc7def0d49"
integrity sha512-BT6DoGn6aV1FVP5yfODMOiieakp3z46P1Fk0RNzJMACzE7C339sFuHebfvWtnB4pzBvXXkHP2vscJzWRuUjTtA==
react-router@6.4.4:
version "6.4.4"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.4.4.tgz#8e7794f55ccc7050cb03937c87ff3720ce9f8b60"
integrity sha512-SA6tSrUCRfuLWeYsTJDuriRqfFIsrSvuH7SqAJHegx9ZgxadE119rU8oOX/rG5FYEthpdEaEljdjDlnBxvfr+Q==
dependencies:
"@remix-run/router" "1.0.3"
"@remix-run/router" "1.0.4"
react-scripts@5.0.1:
version "5.0.1"