From cd16d92f0ecf8daa2b8f692ac0fea57ee42b0f72 Mon Sep 17 00:00:00 2001 From: "Kalle (Sendou)" <38327916+Sendouc@users.noreply.github.com> Date: Tue, 9 Mar 2021 11:57:42 +0200 Subject: [PATCH] xrank in app --- .../xrank/components}/Top500Table.tsx | 0 app/xrank/components/TrendTier.tsx | 90 +++++++ .../xrank/components}/WeaponLineChart.tsx | 0 app/xrank/components/XSearchPage.tsx | 85 +++++++ app/xrank/components/XTrendsPage.tsx | 135 +++++++++++ .../getXTrends.ts => app/xrank/service.ts | 57 ++++- pages/xsearch/[[...slug]].tsx | 104 +------- pages/xtrends.tsx | 227 +----------------- prisma/queries/getTop500PlacementsByMonth.ts | 41 ---- 9 files changed, 379 insertions(+), 360 deletions(-) rename {components/top500 => app/xrank/components}/Top500Table.tsx (100%) create mode 100644 app/xrank/components/TrendTier.tsx rename {components/top500 => app/xrank/components}/WeaponLineChart.tsx (100%) create mode 100644 app/xrank/components/XSearchPage.tsx create mode 100644 app/xrank/components/XTrendsPage.tsx rename prisma/queries/getXTrends.ts => app/xrank/service.ts (52%) delete mode 100644 prisma/queries/getTop500PlacementsByMonth.ts diff --git a/components/top500/Top500Table.tsx b/app/xrank/components/Top500Table.tsx similarity index 100% rename from components/top500/Top500Table.tsx rename to app/xrank/components/Top500Table.tsx diff --git a/app/xrank/components/TrendTier.tsx b/app/xrank/components/TrendTier.tsx new file mode 100644 index 000000000..32be626e3 --- /dev/null +++ b/app/xrank/components/TrendTier.tsx @@ -0,0 +1,90 @@ +import { + Box, + Flex, + Popover, + PopoverArrow, + PopoverContent, + PopoverTrigger, +} from "@chakra-ui/react"; +import { useLingui } from "@lingui/react"; +import WeaponLineChart from "app/xrank/components/WeaponLineChart"; +import SubText from "components/common/SubText"; +import WeaponImage from "components/common/WeaponImage"; +import { useMyTheme } from "hooks/common"; + +const TrendTier = ({ + tier, + weapons, + getDataForChart, + mode, +}: { + tier: { label: string; criteria: number; color: string }; + weapons: { + name: string; + count: number; + xPowerAverage: number; + }[]; + mode: string; + getDataForChart: (weapon: string) => { count: number }[]; +}) => { + const { i18n } = useLingui(); + const { gray, secondaryBgColor } = useMyTheme(); + + if (!weapons.length) return null; + + return ( + + + + {tier.label} + + + {tier.criteria === 0.002 ? ">0%" : `${tier.criteria}%`} + + + + {weapons.map((weapon) => ( + + + + + + {weapon.count} / {weapon.xPowerAverage.toFixed(1)} + + + + + + + + {i18n._(weapon.name)} + {i18n._(mode)} + + getDataForChart(weapon.name)} + /> + + + + ))} + + + ); +}; + +export default TrendTier; diff --git a/components/top500/WeaponLineChart.tsx b/app/xrank/components/WeaponLineChart.tsx similarity index 100% rename from components/top500/WeaponLineChart.tsx rename to app/xrank/components/WeaponLineChart.tsx diff --git a/app/xrank/components/XSearchPage.tsx b/app/xrank/components/XSearchPage.tsx new file mode 100644 index 000000000..3b48465ca --- /dev/null +++ b/app/xrank/components/XSearchPage.tsx @@ -0,0 +1,85 @@ +import { Radio, RadioGroup, Select, Stack } from "@chakra-ui/react"; +import { t } from "@lingui/macro"; +import { RankedMode } from "@prisma/client"; +import Top500Table from "app/xrank/components/Top500Table"; +import HeaderBanner from "components/layout/HeaderBanner"; +import { useRouter } from "next/router"; +import { useEffect, useState } from "react"; +import { Top500PlacementsByMonth } from "../service"; + +export interface XSearchPageProps { + placements: Top500PlacementsByMonth; + monthOptions: { label: string; value: string }[]; +} + +const XSearchPage = ({ placements, monthOptions }: XSearchPageProps) => { + const [variables, setVariables] = useState<{ + month: number; + year: number; + mode: RankedMode; + }>({ + month: Number(monthOptions[0].value.split(",")[0]), + year: Number(monthOptions[0].value.split(",")[1]), + mode: "SZ" as RankedMode, + }); + + const router = useRouter(); + + useEffect(() => { + router.replace( + `/xsearch/${variables.year}/${variables.month}/${variables.mode}` + ); + }, [variables]); + + //TODO: layout can be persistent between route changes + return ( + <> + + + setVariables({ ...variables, mode: value as RankedMode }) + } + mt={4} + mb={8} + > + + {t`SZ`} + {t`TC`} + {t`RM`} + {t`CB`} + + + + + ); +}; + +XSearchPage.header = ( + +); + +export default XSearchPage; diff --git a/app/xrank/components/XTrendsPage.tsx b/app/xrank/components/XTrendsPage.tsx new file mode 100644 index 000000000..a05f6bab4 --- /dev/null +++ b/app/xrank/components/XTrendsPage.tsx @@ -0,0 +1,135 @@ +import { Box, Select } from "@chakra-ui/react"; +import { Trans } from "@lingui/macro"; +import ModeSelector from "components/common/ModeSelector"; +import HeaderBanner from "components/layout/HeaderBanner"; +import { useMyTheme } from "hooks/common"; +import { useXTrends } from "hooks/xtrends"; +import { XTrends } from "../service"; +import TrendTier from "./TrendTier"; + +const tiers = [ + { + label: "X", + criteria: 6, + color: "purple.700", + }, + { + label: "S+", + criteria: 5, + color: "red.700", + }, + { + label: "S", + criteria: 4, + color: "red.700", + }, + { + label: "A+", + criteria: 3, + color: "orange.700", + }, + { + label: "A", + criteria: 2, + color: "orange.700", + }, + { + label: "B+", + criteria: 1.5, + color: "yellow.700", + }, + { + label: "B", + criteria: 1, + color: "yellow.700", + }, + { + label: "C+", + criteria: 0.4, + color: "green.700", + }, + { + label: "C", + criteria: 0.002, //1 in 500 + color: "green.700", + }, +] as const; + +export interface XTrendsPageProps { + trends: XTrends; +} + +const XTrendsPage = ({ trends }: XTrendsPageProps) => { + const { gray } = useMyTheme(); + const { + state, + dispatch, + weaponData, + getDataForChart, + monthOptions, + } = useXTrends(trends); + + return ( + <> + + + Here you can find X Rank Top 500 usage tier lists. For example for a + weapon to be in the X tier it needs at least 30 placements in that + mode that month. Below the weapon count and X Power average are shown. + + + + dispatch({ type: "SET_MODE", mode })} + /> + {tiers.map((tier, i) => ( + { + const targetCount = 500 * (tier.criteria / 100); + const previousTargetCount = + i === 0 ? Infinity : 500 * (tiers[i - 1].criteria / 100); + + return ( + weapon.count >= targetCount && weapon.count < previousTargetCount + ); + })} + getDataForChart={getDataForChart} + mode={state.mode} + /> + ))} + + ); +}; + +XTrendsPage.header = ( + +); + +export default XTrendsPage; diff --git a/prisma/queries/getXTrends.ts b/app/xrank/service.ts similarity index 52% rename from prisma/queries/getXTrends.ts rename to app/xrank/service.ts index 6d115a45d..a454e0bea 100644 --- a/prisma/queries/getXTrends.ts +++ b/app/xrank/service.ts @@ -1,9 +1,9 @@ -import { Prisma } from "@prisma/client"; +import { Prisma, RankedMode } from "@prisma/client"; import prisma from "prisma/client"; -export type GetXTrendsData = Prisma.PromiseReturnType; +export type XTrends = Prisma.PromiseReturnType; -export const getXTrends = async () => { +const getXTrends = async () => { const placements = await prisma.xRankPlacement.findMany({ select: { mode: true, month: true, year: true, weapon: true, xPower: true }, }); @@ -49,3 +49,54 @@ export const getXTrends = async () => { return result; }; + +export type Top500PlacementsByMonth = Prisma.PromiseReturnType< + typeof getTop500PlacementsByMonth +>; + +const getTop500PlacementsByMonth = async ({ + month, + year, + mode, +}: { + month: number; + year: number; + mode: RankedMode; +}) => { + return prisma.xRankPlacement.findMany({ + where: { month, year, mode }, + orderBy: { ranking: "asc" }, + select: { + playerName: true, + xPower: true, + ranking: true, + switchAccountId: true, + weapon: true, + player: { + include: { + user: { + select: { + discordId: true, + discordAvatar: true, + discriminator: true, + username: true, + profile: { select: { customUrlPath: true } }, + }, + }, + }, + }, + }, + }); +}; + +export type MostRecentResult = Prisma.PromiseReturnType< + typeof getMostRecentResult +>; + +const getMostRecentResult = () => { + return prisma.xRankPlacement.findFirst({ + orderBy: [{ year: "desc" }, { month: "desc" }], + }); +}; + +export default { getXTrends, getTop500PlacementsByMonth, getMostRecentResult }; diff --git a/pages/xsearch/[[...slug]].tsx b/pages/xsearch/[[...slug]].tsx index 6f8013319..9e4c13f59 100644 --- a/pages/xsearch/[[...slug]].tsx +++ b/pages/xsearch/[[...slug]].tsx @@ -1,89 +1,13 @@ -import { Radio, RadioGroup, Select, Stack } from "@chakra-ui/react"; -import { t } from "@lingui/macro"; import { RankedMode } from "@prisma/client"; -import HeaderBanner from "components/layout/HeaderBanner"; -import Top500Table from "components/top500/Top500Table"; +import XSearchPage, { + XSearchPageProps, +} from "app/xrank/components/XSearchPage"; +import xRankService from "app/xrank/service"; import { GetStaticPaths, GetStaticProps } from "next"; -import { useRouter } from "next/router"; -import prisma from "prisma/client"; -import { - getTop500PlacementsByMonth, - GetTop500PlacementsByMonthData, -} from "prisma/queries/getTop500PlacementsByMonth"; -import { useEffect, useState } from "react"; import { getLocalizedMonthYearString } from "utils/strings"; -interface Props { - placements: GetTop500PlacementsByMonthData; - monthOptions: { label: string; value: string }[]; -} - -const XSearchPage = ({ placements, monthOptions }: Props) => { - const [variables, setVariables] = useState<{ - month: number; - year: number; - mode: RankedMode; - }>({ - month: Number(monthOptions[0].value.split(",")[0]), - year: Number(monthOptions[0].value.split(",")[1]), - mode: "SZ" as RankedMode, - }); - - const router = useRouter(); - - useEffect(() => { - router.replace( - `/xsearch/${variables.year}/${variables.month}/${variables.mode}` - ); - }, [variables]); - - //TODO: layout can be persistent between route changes - return ( - <> - - - setVariables({ ...variables, mode: value as RankedMode }) - } - mt={4} - mb={8} - > - - {t`SZ`} - {t`TC`} - {t`RM`} - {t`CB`} - - - - - ); -}; - export const getStaticPaths: GetStaticPaths = async () => { - const mostRecentResult = await prisma.xRankPlacement.findFirst({ - orderBy: [{ year: "desc" }, { month: "desc" }], - }); + const mostRecentResult = await xRankService.getMostRecentResult(); if (!mostRecentResult) return { paths: [], fallback: false }; @@ -120,10 +44,10 @@ export const getStaticPaths: GetStaticPaths = async () => { }; }; -export const getStaticProps: GetStaticProps = async ({ params }) => { - const mostRecentResult = await prisma.xRankPlacement.findFirst({ - orderBy: [{ year: "desc" }, { month: "desc" }], - }); +export const getStaticProps: GetStaticProps = async ({ + params, +}) => { + const mostRecentResult = await xRankService.getMostRecentResult(); if (!mostRecentResult) throw Error("No X Rank Placements"); @@ -138,7 +62,7 @@ export const getStaticProps: GetStaticProps = async ({ params }) => { if (isNaN(month) || isNaN(year)) return { notFound: true }; - const placements = await getTop500PlacementsByMonth({ + const placements = await xRankService.getTop500PlacementsByMonth({ month, year, mode: slug[2] as RankedMode, @@ -185,12 +109,4 @@ export function getMonthOptions(latestMonth: number, latestYear: number) { return monthChoices; } -XSearchPage.header = ( - -); - export default XSearchPage; diff --git a/pages/xtrends.tsx b/pages/xtrends.tsx index ebc7ea85e..c6a4263cb 100644 --- a/pages/xtrends.tsx +++ b/pages/xtrends.tsx @@ -1,228 +1,11 @@ -import { - Box, - Flex, - Popover, - PopoverArrow, - PopoverContent, - PopoverTrigger, - Select, -} from "@chakra-ui/react"; -import { Trans } from "@lingui/macro"; -import { useLingui } from "@lingui/react"; -import ModeSelector from "components/common/ModeSelector"; -import SubText from "components/common/SubText"; -import WeaponImage from "components/common/WeaponImage"; -import HeaderBanner from "components/layout/HeaderBanner"; -import WeaponLineChart from "components/top500/WeaponLineChart"; -import { useMyTheme } from "hooks/common"; -import { useXTrends } from "hooks/xtrends"; +import { XTrendsPageProps } from "app/xrank/components/XTrendsPage"; +import xRankService from "app/xrank/service"; import { GetStaticProps } from "next"; -import { getXTrends, GetXTrendsData } from "prisma/queries/getXTrends"; -const tiers = [ - { - label: "X", - criteria: 6, - color: "purple.700", - }, - { - label: "S+", - criteria: 5, - color: "red.700", - }, - { - label: "S", - criteria: 4, - color: "red.700", - }, - { - label: "A+", - criteria: 3, - color: "orange.700", - }, - { - label: "A", - criteria: 2, - color: "orange.700", - }, - { - label: "B+", - criteria: 1.5, - color: "yellow.700", - }, - { - label: "B", - criteria: 1, - color: "yellow.700", - }, - { - label: "C+", - criteria: 0.4, - color: "green.700", - }, - { - label: "C", - criteria: 0.002, //1 in 500 - color: "green.700", - }, -] as const; +export { default as default } from "app/xrank/components/XTrendsPage"; -interface Props { - trends: GetXTrendsData; -} - -const XTrendsPage = ({ trends }: { trends: GetXTrendsData }) => { - const { gray } = useMyTheme(); - const { - state, - dispatch, - weaponData, - getDataForChart, - monthOptions, - } = useXTrends(trends); - - return ( - <> - - - Here you can find X Rank Top 500 usage tier lists. For example for a - weapon to be in the X tier it needs at least 30 placements in that - mode that month. Below the weapon count and X Power average are shown. - - - - dispatch({ type: "SET_MODE", mode })} - /> - {tiers.map((tier, i) => ( - { - const targetCount = 500 * (tier.criteria / 100); - const previousTargetCount = - i === 0 ? Infinity : 500 * (tiers[i - 1].criteria / 100); - - return ( - weapon.count >= targetCount && weapon.count < previousTargetCount - ); - })} - getDataForChart={getDataForChart} - mode={state.mode} - /> - ))} - - ); -}; - -function Tier({ - tier, - weapons, - getDataForChart, - mode, -}: { - tier: { label: string; criteria: number; color: string }; - weapons: { - name: string; - count: number; - xPowerAverage: number; - }[]; - mode: string; - getDataForChart: (weapon: string) => { count: number }[]; -}) { - const { i18n } = useLingui(); - const { gray, secondaryBgColor } = useMyTheme(); - - if (!weapons.length) return null; - - return ( - - - - {tier.label} - - - {tier.criteria === 0.002 ? ">0%" : `${tier.criteria}%`} - - - - {weapons.map((weapon) => ( - - - - - - {weapon.count} / {weapon.xPowerAverage.toFixed(1)} - - - - - - - - {i18n._(weapon.name)} - {i18n._(mode)} - - getDataForChart(weapon.name)} - /> - - - - ))} - - - ); -} - -export const getStaticProps: GetStaticProps = async () => { - const trends = await getXTrends(); +export const getStaticProps: GetStaticProps = async () => { + const trends = await xRankService.getXTrends(); return { props: { trends } }; }; - -XTrendsPage.header = ( - -); - -export default XTrendsPage; diff --git a/prisma/queries/getTop500PlacementsByMonth.ts b/prisma/queries/getTop500PlacementsByMonth.ts deleted file mode 100644 index 18b41044c..000000000 --- a/prisma/queries/getTop500PlacementsByMonth.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Prisma, RankedMode } from "@prisma/client"; -import prisma from "prisma/client"; - -export type GetTop500PlacementsByMonthData = Prisma.PromiseReturnType< - typeof getTop500PlacementsByMonth ->; - -export const getTop500PlacementsByMonth = async ({ - month, - year, - mode, -}: { - month: number; - year: number; - mode: RankedMode; -}) => { - return prisma.xRankPlacement.findMany({ - where: { month, year, mode }, - orderBy: { ranking: "asc" }, - select: { - playerName: true, - xPower: true, - ranking: true, - switchAccountId: true, - weapon: true, - player: { - include: { - user: { - select: { - discordId: true, - discordAvatar: true, - discriminator: true, - username: true, - profile: { select: { customUrlPath: true } }, - }, - }, - }, - }, - }, - }); -};