build page stats

This commit is contained in:
Kalle (Sendou)
2021-01-30 16:24:26 +02:00
parent 1cd728de84
commit a7e05319be
3 changed files with 179 additions and 9 deletions

View File

@@ -0,0 +1,100 @@
import { Flex } from "@chakra-ui/react";
import { Trans } from "@lingui/macro";
import AbilityIcon from "components/common/AbilityIcon";
import SubText from "components/common/SubText";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "components/common/Table";
import { mainOnlyAbilities } from "lib/lists/abilities";
interface Props {
stats: {
code: string;
average: number;
counts: number[][];
}[];
}
const APStats: React.FC<Props> = ({ stats }) => {
if (Number.isNaN(stats[0].average)) return null;
return (
<Flex justify="space-evenly" flexWrap="wrap" mt={6}>
<Table maxW={64}>
<TableHead>
<TableRow>
<TableHeader>
<Trans>Ability</Trans>
</TableHeader>
<TableHeader>
<Trans>Average AP</Trans>
</TableHeader>
<TableHeader>
<Trans>Popular values</Trans>
</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{stats
.filter((stat) => !mainOnlyAbilities.includes(stat.code as any))
.map((stat) => {
return (
<TableRow>
<TableCell>
<AbilityIcon ability={stat.code} size="TINY" />
</TableCell>
<TableCell fontWeight="bold">
{stat.average.toFixed(2)}
</TableCell>
<TableCell>
{stat.counts.map((count) => {
return (
<Flex align="center">
{count[0]} <SubText ml={1}>{count[1]}</SubText>
</Flex>
);
})}
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
<Table maxW={64}>
<TableHead>
<TableRow>
<TableHeader>
<Trans>Ability</Trans>
</TableHeader>
<TableHeader>
<Trans>Appearance %</Trans>
</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{stats
.filter((stat) => mainOnlyAbilities.includes(stat.code as any))
.map((stat) => {
return (
<TableRow>
<TableCell>
<AbilityIcon ability={stat.code} size="TINY" />
</TableCell>
<TableCell fontWeight="bold">
{(stat.average * 10).toFixed(2)}%
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</Flex>
);
};
export default APStats;

View File

@@ -205,11 +205,55 @@ export function useBuildsByWeapon() {
[]
);
const stats = Object.entries(
buildsToShow.reduce(
(acc: Record<Ability, Record<number, number>>, buildArr) => {
buildArr.forEach(({ abilityPoints }) => {
abilities.forEach(({ code }) => {
// @ts-ignore
const buildAPCount = (abilityPoints[code] as number) ?? 0;
let existingApRecord = acc[code][buildAPCount] ?? 0;
acc[code][buildAPCount] = ++existingApRecord;
});
});
return acc;
},
getStatsInitialValue()
)
)
.map(([abilityCode, apCounts]) => {
return {
code: abilityCode,
average:
Object.entries(apCounts).reduce(
(acc, cur) => Number(cur[0]) * cur[1] + acc,
0
) / Object.values(apCounts).reduce((acc, cur) => acc + cur, 0),
counts: Object.entries(apCounts)
.map(([ap, count]) => [Number(ap), count])
.sort((a, b) => b[1] - a[1])
.slice(0, 3),
};
})
.sort((a, b) => b.average - a.average);
return {
data: buildsToShow,
isLoading: state.weapon && !data,
stats,
state,
dispatch,
hiddenBuildCount: buildArrays.length - buildsToShow.length,
};
}
function getStatsInitialValue() {
const result: Partial<Record<Ability, Record<number, number>>> = {};
abilities.forEach(({ code }) => {
result[code] = {};
});
return result as Record<Ability, Record<number, number>>;
}

View File

@@ -1,5 +1,6 @@
import { Box, Button, Flex } from "@chakra-ui/react";
import { t, Trans } from "@lingui/macro";
import APStats from "components/builds/APStats";
import BuildCard from "components/builds/BuildCard";
import BuildFilters from "components/builds/BuildFilters";
import BuildsSkeleton from "components/builds/BuildsSkeleton";
@@ -10,11 +11,13 @@ import WeaponImage from "components/common/WeaponImage";
import WeaponSelector from "components/common/WeaponSelector";
import { useBuildsByWeapon } from "hooks/builds";
import { useMyTheme, useUser } from "hooks/common";
import { RiTShirtLine } from "react-icons/ri";
import { useState } from "react";
import { RiBarChart2Fill, RiTShirtLine } from "react-icons/ri";
const BuildsPage = () => {
const {
data,
stats,
isLoading,
state,
dispatch,
@@ -22,6 +25,8 @@ const BuildsPage = () => {
} = useBuildsByWeapon();
const [user] = useUser();
const { secondaryBgColor, themeColorShade } = useMyTheme();
const [showStats, setShowStats] = useState(true);
return (
<>
<Breadcrumbs pages={[{ name: t`Builds` }]} />
@@ -36,20 +41,39 @@ const BuildsPage = () => {
</Box>
{state.weapon && (
<>
{user && (
<MyLink href={`/u/${user.discordId}?build=${state.weapon}`}>
<Button size="sm" variant="outline" leftIcon={<RiTShirtLine />}>
<Trans>Add build</Trans>
</Button>
</MyLink>
)}
<Flex justify="space-between" mt={6}>
<Button
size="sm"
variant="outline"
leftIcon={<RiBarChart2Fill />}
onClick={() => setShowStats(!showStats)}
>
{showStats ? (
<Trans>Hide Stats</Trans>
) : (
<Trans>Show Stats</Trans>
)}
</Button>
{user && (
<MyLink href={`/u/${user.discordId}?build=${state.weapon}`}>
<Button
size="sm"
variant="outline"
leftIcon={<RiTShirtLine />}
onClick={() => setShowStats(false)}
>
<Trans>Add build</Trans>
</Button>
</MyLink>
)}
</Flex>
<Box mt={4} pr={3} mb="-5rem">
<WeaponImage name={state.weapon} size={128} />
</Box>
<Flex
justifyContent="flex-end"
p={2}
mb={8}
mb={2}
w="100%"
bg={secondaryBgColor}
rounded="lg"
@@ -89,6 +113,8 @@ const BuildsPage = () => {
<BuildFilters filters={state.filters} dispatch={dispatch} />
)}
{showStats && <APStats stats={stats} />}
{isLoading && <BuildsSkeleton />}
<MyInfiniteScroller>