diff --git a/Plan/react/dashboard/package.json b/Plan/react/dashboard/package.json index 5421870d7..e9f6cf121 100644 --- a/Plan/react/dashboard/package.json +++ b/Plan/react/dashboard/package.json @@ -4,6 +4,7 @@ "private": true, "proxy": "https://localhost:8804", "dependencies": { + "@fortawesome/fontawesome-free": "^6.1.1", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-brands-svg-icons": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1", @@ -17,6 +18,9 @@ "@testing-library/user-event": "^13.2.1", "axios": "^0.26.1", "bootstrap": "^5.1.3", + "datatables.net": "^1.12.1", + "datatables.net-bs5": "^1.12.1", + "datatables.net-responsive-bs5": "^2.3.0", "highcharts": "^9.3.2", "i18next": "^21.6.14", "i18next-chained-backend": "^3.0.2", diff --git a/Plan/react/dashboard/src/App.js b/Plan/react/dashboard/src/App.js index 748928d6a..ba1cb8bd1 100644 --- a/Plan/react/dashboard/src/App.js +++ b/Plan/react/dashboard/src/App.js @@ -24,6 +24,7 @@ import OnlineActivity from "./views/server/OnlineActivity"; import ServerSessions from "./views/server/ServerSessions"; import ServerPvpPve from "./views/server/ServerPvpPve"; import PlayerbaseOverview from "./views/server/PlayerbaseOverview"; +import ServerPlayers from "./views/server/ServerPlayers"; const OverviewRedirect = () => { return () @@ -72,7 +73,7 @@ function App() { }/> }/> }/> - }/> + }/> }/> }/> }/> diff --git a/Plan/react/dashboard/src/components/cards/common/PlayerListCard.js b/Plan/react/dashboard/src/components/cards/common/PlayerListCard.js new file mode 100644 index 000000000..0a47b313e --- /dev/null +++ b/Plan/react/dashboard/src/components/cards/common/PlayerListCard.js @@ -0,0 +1,41 @@ +import {useTranslation} from "react-i18next"; +import {Card} from "react-bootstrap-v5"; +import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome"; +import React, {useEffect, useState} from "react"; +import {faUsers} from "@fortawesome/free-solid-svg-icons"; +import DataTablesTable from "../../table/DataTablesTable"; +import {CardLoader} from "../../navigation/Loader"; + +const PlayerListCard = ({data}) => { + const {t} = useTranslation(); + const [options, setOptions] = useState(undefined); + + useEffect(() => { + for (const row of data.data) { + row.name = row.name.replace('../player/', '../../player/'); + } + + setOptions({ + responsive: true, + deferRender: true, + columns: data.columns, + data: data?.data, + order: [[5, "desc"]] + }); + }, [data]) + + if (!options) return + + return ( + + +
+ {t('html.label.playerList')} +
+
+ +
+ ) +} + +export default PlayerListCard; \ No newline at end of file diff --git a/Plan/react/dashboard/src/components/cards/server/insights/SessionInsightsCard.js b/Plan/react/dashboard/src/components/cards/server/insights/SessionInsightsCard.js index 9bbb98f4b..3b17b5df4 100644 --- a/Plan/react/dashboard/src/components/cards/server/insights/SessionInsightsCard.js +++ b/Plan/react/dashboard/src/components/cards/server/insights/SessionInsightsCard.js @@ -10,8 +10,8 @@ import {faGamepad, faUsers} from "@fortawesome/free-solid-svg-icons"; import {faClock} from "@fortawesome/free-regular-svg-icons"; const SessionInsightsCard = () => { - const {identifier} = useParams(); const {t} = useTranslation(); + const {identifier} = useParams(); const {data, loadingError} = useDataRequest(fetchSessionOverview, [identifier]); diff --git a/Plan/react/dashboard/src/components/navigation/Sidebar.js b/Plan/react/dashboard/src/components/navigation/Sidebar.js index 38e8d53ee..06c26459c 100644 --- a/Plan/react/dashboard/src/components/navigation/Sidebar.js +++ b/Plan/react/dashboard/src/components/navigation/Sidebar.js @@ -1,4 +1,4 @@ -import React, {useEffect, useState} from "react"; +import React, {useCallback, useEffect, useState} from "react"; import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome"; import logo from '../../Flaticon_circle.png'; import {faArrowLeft, faDoorOpen, faDownload, faPalette, faQuestionCircle} from "@fortawesome/free-solid-svg-icons"; @@ -208,11 +208,11 @@ const Sidebar = ({items, showBackButton}) => { } const [windowWidth, setWindowWidth] = useState(window.innerWidth); - const updateWidth = () => setWindowWidth(window.innerWidth); + const updateWidth = useCallback(() => setWindowWidth(window.innerWidth), []); useEffect(() => { window.addEventListener('resize', updateWidth); return () => window.removeEventListener('resize', updateWidth); - }, []); + }, [updateWidth]); const collapseSidebar = () => setSidebarExpanded(windowWidth > 1350); useEffect(collapseSidebar, [windowWidth, currentTab, setSidebarExpanded]); diff --git a/Plan/react/dashboard/src/components/table/DataTablesTable.js b/Plan/react/dashboard/src/components/table/DataTablesTable.js new file mode 100644 index 000000000..0a97e0e20 --- /dev/null +++ b/Plan/react/dashboard/src/components/table/DataTablesTable.js @@ -0,0 +1,33 @@ +import React, {useEffect, useState} from 'react'; +import DataTable from 'datatables.net' +import 'datatables.net-bs5' +import 'datatables.net-responsive-bs5' +import 'datatables.net-bs5/css/dataTables.bootstrap5.min.css'; +import 'datatables.net-responsive-bs5/css/responsive.bootstrap5.min.css'; + +const DataTablesTable = ({id, options}) => { + const [dataTable, setDataTable] = useState(null); + + useEffect(() => { + if (dataTable) { + dataTable.destroy(); + setDataTable(null); + } + + const createdDataTable = new DataTable(`#${id}`, options); + setDataTable(createdDataTable); + + return () => { + if (dataTable) { + dataTable.clear(); + dataTable.destroy(); + } + }; + }, [id, options, dataTable]); + + return ( + + ) +}; + +export default DataTablesTable \ No newline at end of file diff --git a/Plan/react/dashboard/src/index.js b/Plan/react/dashboard/src/index.js index 594efe9f8..7eb1d25d3 100644 --- a/Plan/react/dashboard/src/index.js +++ b/Plan/react/dashboard/src/index.js @@ -2,7 +2,8 @@ import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; -import reportWebVitals from './reportWebVitals'; + +import '@fortawesome/fontawesome-free/css/all.min.css' import {library} from '@fortawesome/fontawesome-svg-core'; import {fas} from '@fortawesome/free-solid-svg-icons'; @@ -20,16 +21,4 @@ localeService.init().then(() => ReactDOM.render( , document.getElementById('root') -)); - -// ReactDOM.render( -// -// -// , -// document.getElementById('root') -// ); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); +)); \ No newline at end of file diff --git a/Plan/react/dashboard/src/service/localeService.js b/Plan/react/dashboard/src/service/localeService.js index 3683922ab..583384ffa 100644 --- a/Plan/react/dashboard/src/service/localeService.js +++ b/Plan/react/dashboard/src/service/localeService.js @@ -44,7 +44,7 @@ export const localeService = { this.languageVersions = data.languageVersions; } else { this.defaultLanguage = 'en' - this.availableLanguages = []; + this.availableLanguages = {}; this.languageVersions = []; } diff --git a/Plan/react/dashboard/src/service/serverService.js b/Plan/react/dashboard/src/service/serverService.js index cde917433..ac566d90f 100644 --- a/Plan/react/dashboard/src/service/serverService.js +++ b/Plan/react/dashboard/src/service/serverService.js @@ -42,6 +42,12 @@ export const fetchKills = async (identifier) => { return doGetRequest(url); } +export const fetchPlayers = async (identifier) => { + const timestamp = Date.now(); + const url = `/v1/players?server=${identifier}×tamp=${timestamp}`; + return doGetRequest(url); +} + export const fetchPlayersOnlineGraph = async (identifier) => { const timestamp = Date.now(); const url = `/v1/graph?type=playersOnline&server=${identifier}×tamp=${timestamp}`; diff --git a/Plan/react/dashboard/src/views/server/ServerPlayers.js b/Plan/react/dashboard/src/views/server/ServerPlayers.js new file mode 100644 index 000000000..c5d1b5990 --- /dev/null +++ b/Plan/react/dashboard/src/views/server/ServerPlayers.js @@ -0,0 +1,26 @@ +import React from 'react'; +import {useDataRequest} from "../../hooks/dataFetchHook"; +import {useParams} from "react-router-dom"; +import {fetchPlayers} from "../../service/serverService"; +import ErrorView from "../ErrorView"; +import {Col, Row} from "react-bootstrap-v5"; +import PlayerListCard from "../../components/cards/common/PlayerListCard"; + +const ServerPlayers = () => { + const {identifier} = useParams(); + + const {data, loadingError} = useDataRequest(fetchPlayers, [identifier]); + + if (!data) return <>; + if (loadingError) return + + return ( + + + + + + ) +}; + +export default ServerPlayers \ No newline at end of file diff --git a/Plan/react/dashboard/yarn.lock b/Plan/react/dashboard/yarn.lock index e109cad1a..0715024c5 100644 --- a/Plan/react/dashboard/yarn.lock +++ b/Plan/react/dashboard/yarn.lock @@ -1153,6 +1153,11 @@ resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.1.1.tgz#7dc996042d21fc1ae850e3173b5c67b0549f9105" integrity sha512-wVn5WJPirFTnzN6tR95abCx+ocH+3IFLXAgyavnf9hUmN0CfWoDjPT/BAWsUVwSlYYVBeCLJxaqi7ZGe4uSjBA== +"@fortawesome/fontawesome-free@^6.1.1": + version "6.1.1" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-6.1.1.tgz#bf5d45611ab74890be386712a0e5d998c65ee2a1" + integrity sha512-J/3yg2AIXc9wznaVqpHVX3Wa5jwKovVF0AMYSnbmcXTiL3PpRPfF58pzWucCwEiCJBp+hCNRLWClTomD8SseKg== + "@fortawesome/fontawesome-svg-core@^6.1.1": version "6.1.1" resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.1.1.tgz#3424ec6182515951816be9b11665d67efdce5b5f" @@ -3465,6 +3470,38 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +datatables.net-bs5@>=1.11.3, datatables.net-bs5@^1.12.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/datatables.net-bs5/-/datatables.net-bs5-1.12.1.tgz#b05d4bcaed77f6b77df0b50eaf1d5ec8e5d476bd" + integrity sha512-CcQCImfmH4YZk7I0aC0kTiNPyfHJ2ueGgOh/kFB9KqsZD8bNJy2A88gC6hn9A7TbmmenOL+K3Q1ti7G8yqi8SQ== + dependencies: + datatables.net ">=1.11.3" + jquery ">=1.7" + +datatables.net-responsive-bs5@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/datatables.net-responsive-bs5/-/datatables.net-responsive-bs5-2.3.0.tgz#f92b79b7d5995c8c6cdc31ab0a725c240bcec448" + integrity sha512-OVe2y+WGbZNqwu0clpzjPGg85Y+8KW8pVXVZJEKKyHW0GYAU3Ewhk0f3ZdS0B6Al9Q4v3Gt6OzupVwuY5Ck1pA== + dependencies: + datatables.net-bs5 ">=1.11.3" + datatables.net-responsive ">=2.2.9" + jquery ">=1.7" + +datatables.net-responsive@>=2.2.9: + version "2.3.0" + resolved "https://registry.yarnpkg.com/datatables.net-responsive/-/datatables.net-responsive-2.3.0.tgz#2b8d894af5456f8c8a320eeac5e837a24d84e033" + integrity sha512-QA5QsD1sJQRQ7/IFi3rSd33O84f/Augz2KnaehjfuEANtK4KeC9Lbkut5tPuuMcK4jOpQPOOPYTbmfrt+tfh9w== + dependencies: + datatables.net ">=1.11.3" + jquery ">=1.7" + +datatables.net@>=1.11.3, datatables.net@^1.12.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-1.12.1.tgz#3e625e49a3341f605b0efb519fae94e37b278f24" + integrity sha512-e6XAMUoV41JdQPS/r9YRfRcmTPcCVvyZbWI+xog1Zg+kjVliMQbEkvWK5XFItmi64Cvwg+IqsZbTUJ1KSY3umA== + dependencies: + jquery ">=1.7" + debug@2.6.9, debug@^2.6.0, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -5634,6 +5671,11 @@ jest@^27.4.3: import-local "^3.0.2" jest-cli "^27.5.1" +jquery@>=1.7: + version "3.6.0" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470" + integrity sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"