From a03bd8ff8d8c2847aab12d4618acf232c14615d7 Mon Sep 17 00:00:00 2001 From: Kevin Belisle Date: Mon, 12 Jul 2021 17:47:27 -0400 Subject: [PATCH] Add aggregate statistics showcase to search view --- spa/src/AggregatesShowcase.js | 98 +++++++++++++++++++++++++++++++++++ spa/src/Search.js | 37 +++++++------ spa/src/StackedBar.js | 76 +++++++++++++++++++++++++++ spa/src/useHover.js | 25 +++++++++ 4 files changed, 221 insertions(+), 15 deletions(-) create mode 100644 spa/src/AggregatesShowcase.js create mode 100644 spa/src/StackedBar.js create mode 100644 spa/src/useHover.js diff --git a/spa/src/AggregatesShowcase.js b/spa/src/AggregatesShowcase.js new file mode 100644 index 0000000..891954b --- /dev/null +++ b/spa/src/AggregatesShowcase.js @@ -0,0 +1,98 @@ +import React from "react"; +import { useQuery } from "react-query"; +import axios from "axios"; + +import { + AspectRatio, + Box, + Flex, + Heading, + HStack, + Icon, + SimpleGrid, + Stat, + StatLabel, + StatNumber, + StatHelpText, + StatArrow, + StatGroup, + Tooltip, +} from "@chakra-ui/react"; +import { FaUserCircle, FaArrowAltCircleRight } from "react-icons/fa"; +import { Link } from "react-router-dom"; +import { AutoSizer, WindowScroller, List } from "react-virtualized"; +import prettifyStatisticName from "./PrettifyStatisticName"; +import StackedBar from "./StackedBar"; + +const AggregatesShowcase = () => { + const aggregates = useQuery( + `aggregates`, + async () => { + const { data } = await axios.get(`http://localhost:7000/api/aggregates`); + return data; + }, + { + placeholderData: [], + } + ); + + const killedAggregates = aggregates.data + .filter((x) => x.type === "minecraft:killed") + .sort((a, b) => b.value - a.value); + const killedTotal = killedAggregates.reduce((acc, val) => acc + val.value, 0); + + const killedByAggregates = aggregates.data + .filter((x) => x.type === "minecraft:killed_by") + .sort((a, b) => b.value - a.value); + const killedByTotal = killedByAggregates.reduce( + (acc, val) => acc + val.value, + 0 + ); + + const travelAggregates = aggregates.data + .filter((x) => x.type === "minecraft:custom" && x.name.endsWith("one_cm")) + .sort((a, b) => b.value - a.value); + const travelTotal = travelAggregates.reduce((acc, val) => acc + val.value, 0); + + return aggregates.isFetched ? ( + <> + + + + + + + + {aggregates.data + .filter( + (x) => + x.type !== "minecraft:killed" && + x.type !== "minecraft:killed_by" && + !x.name.endsWith("one_cm") + ) + .map((x, i) => { + var value = x.value; + if (x.name === "minecraft:play_one_minute") { + value = x.value / 20 / 60; + } + + return ( + + {prettifyStatisticName(x.type, x.name)} + {`${x.value + .toString() + .replace( + /\B(? + + ); + })} + + + ) : ( + <> + ); +}; + +export default AggregatesShowcase; diff --git a/spa/src/Search.js b/spa/src/Search.js index d9aa71b..3fcdf54 100644 --- a/spa/src/Search.js +++ b/spa/src/Search.js @@ -6,6 +6,7 @@ import { FaUserCircle, FaArrowAltCircleRight } from "react-icons/fa"; import { Link } from "react-router-dom"; import { AutoSizer, WindowScroller, List } from "react-virtualized"; import prettifyStatisticName from "./PrettifyStatisticName"; +import AggregatesShowcase from "./AggregatesShowcase"; const Search = ({ statistics, players }) => { const [searchTerm, setSearchTerm] = useState(""); @@ -100,23 +101,29 @@ const Search = ({ statistics, players }) => { variant="filled" value={searchTerm} onChange={(event) => setSearchTerm(event.target.value)} + mb="8" /> + {searchResults.length === 0 ? : <>}
- - {({ width }) => ( - - )} - + {searchResults.length > 0 ? ( + + {({ width }) => ( + + )} + + ) : ( + <> + )}
)} diff --git a/spa/src/StackedBar.js b/spa/src/StackedBar.js new file mode 100644 index 0000000..de5dbe1 --- /dev/null +++ b/spa/src/StackedBar.js @@ -0,0 +1,76 @@ +import React, { useMemo } from "react"; +import useHover from "./useHover"; +import { + AspectRatio, + Box, + Button, + Heading, + HStack, + Icon, + Input, + Stat, + StatLabel, + StatNumber, + StatHelpText, + StatArrow, + StatGroup, + Tooltip, +} from "@chakra-ui/react"; +import prettifyStatisticName from "./PrettifyStatisticName"; + +const randomColor = () => + `hsl(${Math.floor(Math.random() * 359)}, ${ + 50 + Math.floor(Math.random() * 50) + }%, ${25 + Math.floor(Math.random() * 50)}%)`; + +const StackedBarSegment = ({ aggregate, total }) => { + const color = useMemo(randomColor, []); + + const [hoverRef, isHovered] = useHover(); + + return ( + + + + ); +}; + +const StackedBar = ({ heading, aggregates }) => { + const total = aggregates.reduce((acc, val) => acc + val.value, 0); + + return ( + <> + + {heading} + + + {aggregates.map((x, i) => ( + + ))} + + + ); +}; + +export default StackedBar; diff --git a/spa/src/useHover.js b/spa/src/useHover.js new file mode 100644 index 0000000..04a3bac --- /dev/null +++ b/spa/src/useHover.js @@ -0,0 +1,25 @@ +import React, { useEffect, useRef, useState } from "react"; + +function useHover() { + const [value, setValue] = useState(false); + const ref = useRef(null); + const handleMouseOver = () => setValue(true); + const handleMouseOut = () => setValue(false); + useEffect( + () => { + const node = ref.current; + if (node) { + node.addEventListener("mouseover", handleMouseOver); + node.addEventListener("mouseout", handleMouseOut); + return () => { + node.removeEventListener("mouseover", handleMouseOver); + node.removeEventListener("mouseout", handleMouseOut); + }; + } + }, + [ref.current] // Recall only if ref changes + ); + return [ref, value]; +} + +export default useHover;