PlayerStatsDrawer.tsx
This commit is contained in:
22
src/App.tsx
22
src/App.tsx
@ -16,8 +16,9 @@ import {
|
|||||||
} from "@mui/joy";
|
} from "@mui/joy";
|
||||||
import GameAccordionItem from "./components/GameAccordionItem.tsx";
|
import GameAccordionItem from "./components/GameAccordionItem.tsx";
|
||||||
import PlayerAccordionItem from "./components/PlayerAccordionItem.tsx";
|
import PlayerAccordionItem from "./components/PlayerAccordionItem.tsx";
|
||||||
import StatsDrawer from "./components/StatsDrawer.tsx";
|
import PlayerGameStatsDrawer from "./components/PlayerGameStatsDrawer.tsx";
|
||||||
import {Cancel, CheckCircleOutline} from '@mui/icons-material';
|
import {Cancel, CheckCircleOutline} from '@mui/icons-material';
|
||||||
|
import {PlayerStatsDrawer} from "./components/PlayerStatsDrawer.tsx";
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const [apiUri, setApiUri] = useState<string>("http://127.0.0.1:5239");
|
const [apiUri, setApiUri] = useState<string>("http://127.0.0.1:5239");
|
||||||
@ -29,12 +30,18 @@ export default function App() {
|
|||||||
|
|
||||||
const [selectedPlayer, setSelectedPlayer] = useState<IPlayer | null>(null);
|
const [selectedPlayer, setSelectedPlayer] = useState<IPlayer | null>(null);
|
||||||
const [selectedGame, setSelectedGame] = useState<IGame | null>(null);
|
const [selectedGame, setSelectedGame] = useState<IGame | null>(null);
|
||||||
const [open, setOpen] = useState<boolean>(false);
|
const [openPlayerGameStats, setOpenPlayerGameStats] = useState<boolean>(false);
|
||||||
|
const [openPlayerStats, setOpenPlayerStats] = useState<boolean>(false);
|
||||||
|
|
||||||
const OpenDrawer = (player: IPlayer, game: IGame) => {
|
const OpenPlayerGameStatsDrawer = (player: IPlayer, game: IGame) => {
|
||||||
setSelectedPlayer(player);
|
setSelectedPlayer(player);
|
||||||
setSelectedGame(game);
|
setSelectedGame(game);
|
||||||
setOpen(true);
|
setOpenPlayerGameStats(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const OpenPlayerStatsDrawer = (player: IPlayer) => {
|
||||||
|
setSelectedPlayer(player);
|
||||||
|
setOpenPlayerStats(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -92,18 +99,19 @@ export default function App() {
|
|||||||
<Box sx={{width:'50%'}}>
|
<Box sx={{width:'50%'}}>
|
||||||
<Typography level={"h2"}>Players</Typography>
|
<Typography level={"h2"}>Players</Typography>
|
||||||
<AccordionGroup>
|
<AccordionGroup>
|
||||||
{players?.map((player) => <PlayerAccordionItem key={player.steamId} player={player} OpenDrawer={OpenDrawer} />)}
|
{players?.map((player) => <PlayerAccordionItem key={player.steamId} player={player} OpenPlayerGameStatsDrawer={OpenPlayerGameStatsDrawer} OpenPlayerStatsDrawer={OpenPlayerStatsDrawer} />)}
|
||||||
</AccordionGroup>
|
</AccordionGroup>
|
||||||
</Box>
|
</Box>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Box sx={{width:'50%'}}>
|
<Box sx={{width:'50%'}}>
|
||||||
<Typography level={"h2"}>Games</Typography>
|
<Typography level={"h2"}>Games</Typography>
|
||||||
<AccordionGroup>
|
<AccordionGroup>
|
||||||
{games?.map((game) => <GameAccordionItem key={game.appId} game={game} OpenDrawer={OpenDrawer} />)}
|
{games?.map((game) => <GameAccordionItem key={game.appId} game={game} OpenPlayerGameStatsDrawer={OpenPlayerGameStatsDrawer} />)}
|
||||||
</AccordionGroup>
|
</AccordionGroup>
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
<StatsDrawer player={selectedPlayer} game={selectedGame} open={open} setOpen={setOpen} />
|
<PlayerGameStatsDrawer player={selectedPlayer} game={selectedGame} open={openPlayerGameStats} setOpen={setOpenPlayerGameStats} />
|
||||||
|
<PlayerStatsDrawer player={selectedPlayer} open={openPlayerStats} setOpen={setOpenPlayerStats} />
|
||||||
</GamesContext>
|
</GamesContext>
|
||||||
</PlayersContext.Provider>
|
</PlayersContext.Provider>
|
||||||
</ApiUriContext>
|
</ApiUriContext>
|
||||||
|
@ -2,7 +2,7 @@ import {getData} from "../fetchApi.tsx";
|
|||||||
import type ITrackedTime from "../types/ITrackedTime.ts";
|
import type ITrackedTime from "../types/ITrackedTime.ts";
|
||||||
|
|
||||||
export function GetTimelines(apiUri: string, steamId: bigint){
|
export function GetTimelines(apiUri: string, steamId: bigint){
|
||||||
return getData(`${apiUri}/TimeTrack/${steamId}`) as Promise<Map<bigint, ITrackedTime[]>>;
|
return getData(`${apiUri}/TimeTrack/${steamId}`) as Promise<{key: bigint, value: ITrackedTime[]}[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GetTimelineGame(apiUri: string, steamId: bigint, appId: bigint){
|
export function GetTimelineGame(apiUri: string, steamId: bigint, appId: bigint){
|
||||||
@ -14,5 +14,5 @@ export function GetTotal(apiUri: string, steamId: bigint){
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function GetTotalPerGame(apiUri: string, steamId: bigint){
|
export function GetTotalPerGame(apiUri: string, steamId: bigint){
|
||||||
return getData(`${apiUri}/TimeTrack/${steamId}/Total/PerGame`) as Promise<Map<bigint, bigint>>;
|
return getData(`${apiUri}/TimeTrack/${steamId}/Total/PerGame`) as Promise<{key: bigint, value: bigint}[]>;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import {GetPlayersOfGame} from "../api/endpoints/Data.tsx";
|
|||||||
import {ApiUriContext} from "../api/fetchApi.tsx";
|
import {ApiUriContext} from "../api/fetchApi.tsx";
|
||||||
import PlayerCard from "./PlayerCard.tsx";
|
import PlayerCard from "./PlayerCard.tsx";
|
||||||
|
|
||||||
export default function GameAccordionItem({game, OpenDrawer} : {game: IGame, OpenDrawer : (player: IPlayer, game: IGame) => void}) {
|
export default function GameAccordionItem({game, OpenPlayerGameStatsDrawer} : {game: IGame, OpenPlayerGameStatsDrawer : (player: IPlayer, game: IGame) => void}) {
|
||||||
const apiUri = useContext(ApiUriContext);
|
const apiUri = useContext(ApiUriContext);
|
||||||
|
|
||||||
const [players, setPlayers] = useState<IPlayer[]>([]);
|
const [players, setPlayers] = useState<IPlayer[]>([]);
|
||||||
@ -39,7 +39,7 @@ export default function GameAccordionItem({game, OpenDrawer} : {game: IGame, Ope
|
|||||||
<Stack flexWrap={"wrap"} direction={"row"} spacing={1} useFlexGap>
|
<Stack flexWrap={"wrap"} direction={"row"} spacing={1} useFlexGap>
|
||||||
{loading
|
{loading
|
||||||
? <CircularProgress />
|
? <CircularProgress />
|
||||||
: players?.map((player) => <PlayerCard player={player} onClick={() => OpenDrawer(player, game)} />)}
|
: players?.map((player) => <PlayerCard key={player.steamId} player={player} onClick={() => OpenPlayerGameStatsDrawer(player, game)} />)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
@ -12,7 +12,7 @@ import {GetGamesOfPlayer} from "../api/endpoints/Data.tsx";
|
|||||||
import GameCard from "./GameCard.tsx";
|
import GameCard from "./GameCard.tsx";
|
||||||
import {GetTotal} from "../api/endpoints/TimeTrack.tsx";
|
import {GetTotal} from "../api/endpoints/TimeTrack.tsx";
|
||||||
|
|
||||||
export default function PlayerAccordionItem({player, OpenDrawer} : {player: IPlayer, OpenDrawer : (player: IPlayer, game: IGame) => void}) {
|
export default function PlayerAccordionItem({player, OpenPlayerGameStatsDrawer, OpenPlayerStatsDrawer} : {player: IPlayer, OpenPlayerGameStatsDrawer : (player: IPlayer, game: IGame) => void, OpenPlayerStatsDrawer : (player: IPlayer) => void}) {
|
||||||
const apiUri = useContext(ApiUriContext);
|
const apiUri = useContext(ApiUriContext);
|
||||||
|
|
||||||
const [games, setGames] = useState<IGame[]>([]);
|
const [games, setGames] = useState<IGame[]>([]);
|
||||||
@ -41,13 +41,13 @@ export default function PlayerAccordionItem({player, OpenDrawer} : {player: IPla
|
|||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
<AccordionDetails>
|
<AccordionDetails>
|
||||||
<Stack flexWrap={"nowrap"} direction={"row"} alignContent={"center"} spacing={2} useFlexGap marginBottom={"10px"}>
|
<Stack flexWrap={"nowrap"} direction={"row"} alignContent={"center"} spacing={2} useFlexGap marginBottom={"10px"}>
|
||||||
<Button>All Games</Button>
|
<Button onClick={() => OpenPlayerStatsDrawer(player)}>All Games</Button>
|
||||||
<Typography level={"h4"} >Total Time: {totalTime}</Typography>
|
<Typography level={"h4"} >Total Time: {totalTime}</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack flexWrap={"wrap"} direction={"row"} spacing={1} useFlexGap>
|
<Stack flexWrap={"wrap"} direction={"row"} spacing={1} useFlexGap>
|
||||||
{loadingGames
|
{loadingGames
|
||||||
? <CircularProgress />
|
? <CircularProgress />
|
||||||
: games?.map((game) => <GameCard game={game} onClick={() => OpenDrawer(player, game)} />)}
|
: games?.map((game) => <GameCard key={game.appId} game={game} onClick={() => OpenPlayerGameStatsDrawer(player, game)} />)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
@ -9,7 +9,7 @@ import {LineChart} from "@mui/x-charts";
|
|||||||
import PlayerCard from "./PlayerCard.tsx";
|
import PlayerCard from "./PlayerCard.tsx";
|
||||||
import GameCard from "./GameCard.tsx";
|
import GameCard from "./GameCard.tsx";
|
||||||
|
|
||||||
export default function StatsDrawer({player, game, open, setOpen} : {player: IPlayer | null, game: IGame | null, open: boolean, setOpen: Dispatch<boolean>}) {
|
export default function PlayerGameStatsDrawer({player, game, open, setOpen} : {player: IPlayer | null, game: IGame | null, open: boolean, setOpen: Dispatch<boolean>}) {
|
||||||
|
|
||||||
const apiUri = useContext(ApiUriContext);
|
const apiUri = useContext(ApiUriContext);
|
||||||
|
|
88
src/components/PlayerStatsDrawer.tsx
Normal file
88
src/components/PlayerStatsDrawer.tsx
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import type IPlayer from "../api/types/IPlayer.ts";
|
||||||
|
import {Autocomplete, DialogContent, DialogTitle, Drawer, ModalClose, Stack} from "@mui/joy";
|
||||||
|
import {type Dispatch, useContext, useEffect, useState} from "react";
|
||||||
|
import {ApiUriContext} from "../api/fetchApi.tsx";
|
||||||
|
import {LineChart, type LineSeriesType} from "@mui/x-charts";
|
||||||
|
import PlayerCard from "./PlayerCard.tsx";
|
||||||
|
import {GamesContext} from "../api/contexts/GamesContext.tsx";
|
||||||
|
import {GetTimelines} from "../api/endpoints/TimeTrack.tsx";
|
||||||
|
import type {DatasetElementType} from "@mui/x-charts/internals";
|
||||||
|
|
||||||
|
export function PlayerStatsDrawer({player, open, setOpen}: {
|
||||||
|
player: IPlayer | null,
|
||||||
|
open: boolean,
|
||||||
|
setOpen: Dispatch<boolean>
|
||||||
|
}) {
|
||||||
|
|
||||||
|
const apiUri = useContext(ApiUriContext);
|
||||||
|
const games = useContext(GamesContext);
|
||||||
|
|
||||||
|
const [trackedTimes, setTrackedTimes] = useState<DatasetElementType<string | number | Date | null | undefined>[]>([]);
|
||||||
|
const [availableSeries, setAvailableSeries] = useState<LineSeriesType[]>([]);
|
||||||
|
const [selectedSeries, setSelectedSeries] = useState<LineSeriesType[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open || !player)
|
||||||
|
return;
|
||||||
|
GetTimelines(apiUri, player.steamId)
|
||||||
|
.then((tt) => {
|
||||||
|
const times: DatasetElementType<string | number | Date | null | undefined>[] = [];
|
||||||
|
const g: Set<bigint> = new Set<bigint>();
|
||||||
|
tt.forEach((app) => {
|
||||||
|
app.value.forEach((time) => {
|
||||||
|
let o = {date: new Date(time.timeStamp)};
|
||||||
|
// @ts-ignore
|
||||||
|
o[`${app.key.toString()}`] = Number(time.timePlayed / 60)
|
||||||
|
times.push(o);
|
||||||
|
g.add(app.key);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const seriesTypes: LineSeriesType[] = [];
|
||||||
|
const iterator = g.keys();
|
||||||
|
let value = iterator.next();
|
||||||
|
while (!value.done) {
|
||||||
|
const appId = value.value;
|
||||||
|
seriesTypes.push({
|
||||||
|
type: "line",
|
||||||
|
dataKey: appId.toString(),
|
||||||
|
connectNulls: true,
|
||||||
|
label: games.games.find(g => g.appId == appId)?.name ?? appId?.toString() ?? ""
|
||||||
|
})
|
||||||
|
value = iterator.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
setAvailableSeries(seriesTypes);
|
||||||
|
setTrackedTimes(times);
|
||||||
|
});
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Drawer anchor={"bottom"} size={"lg"} open={open} onClose={() => setOpen(false)}>
|
||||||
|
<ModalClose/>
|
||||||
|
<DialogTitle>
|
||||||
|
<PlayerCard player={player}/>
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<Stack direction={"column"} height={"100%"}>
|
||||||
|
<Autocomplete multiple
|
||||||
|
options={availableSeries.sort((a, b) => (a.label as string).localeCompare(b.label as string))}
|
||||||
|
placeholder={"Games"}
|
||||||
|
getOptionLabel={(opt) => opt.label as string}
|
||||||
|
isOptionEqualToValue={(a, b) => a.dataKey == b.dataKey}
|
||||||
|
onChange={(_, value, reason) => {
|
||||||
|
if(reason == "selectOption" || reason == "removeOption" || reason == "clear")
|
||||||
|
setSelectedSeries(value);
|
||||||
|
console.log(value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<LineChart dataset={trackedTimes}
|
||||||
|
xAxis={[{dataKey: "date", scaleType: "utc"}]}
|
||||||
|
yAxis={[{dataKey: "time", scaleType: "linear"}]}
|
||||||
|
series={selectedSeries}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</DialogContent>
|
||||||
|
</Drawer>
|
||||||
|
);
|
||||||
|
}
|
Reference in New Issue
Block a user