From eb5ccbf724fc491c1feef84ce55a99b1bee54e07 Mon Sep 17 00:00:00 2001 From: "Kalle (Sendou)" <38327916+Sendouc@users.noreply.github.com> Date: Mon, 2 Nov 2020 18:01:36 +0200 Subject: [PATCH] top 500 page initial --- components/LoadingBoundary.tsx | 34 +++++ components/ModeImage.tsx | 2 +- components/PageHeader.tsx | 26 ++++ generated/graphql.tsx | 83 ++++++----- graphql/schema/xRankPlacement.ts | 32 ++--- lib/getLocalizedMontYearString.ts | 16 +++ lib/getRankingString.ts | 13 ++ nexus-typegen.ts | 26 ++-- pages/top500.tsx | 134 ++++++++++++++++++ pages/xsearch.tsx | 35 ----- prisma/seed.ts | 14 +- scenes/XSearch/index.tsx | 78 ++++------ .../XSearch/queries/XRankPlacements.graphql | 16 ++- schema.graphql | 13 +- 14 files changed, 336 insertions(+), 186 deletions(-) create mode 100644 components/LoadingBoundary.tsx create mode 100644 components/PageHeader.tsx create mode 100644 lib/getLocalizedMontYearString.ts create mode 100644 lib/getRankingString.ts create mode 100644 pages/top500.tsx delete mode 100644 pages/xsearch.tsx diff --git a/components/LoadingBoundary.tsx b/components/LoadingBoundary.tsx new file mode 100644 index 000000000..030bc413d --- /dev/null +++ b/components/LoadingBoundary.tsx @@ -0,0 +1,34 @@ +import { ApolloError } from "@apollo/client"; +import { Alert, AlertDescription, AlertIcon, Spinner } from "@chakra-ui/core"; +import { useEffect, useState } from "react"; + +interface Props { + children: React.ReactNode; + loading: boolean; + error?: ApolloError; +} + +const LoadingBoundary: React.FC = ({ children, loading, error }) => { + const [showSpinner, setShowSpinner] = useState(false); + + useEffect(() => { + const timer = setTimeout(() => setShowSpinner(true), 1000); + + return () => clearTimeout(timer); + }, []); + + if (loading) return showSpinner ? : null; + + if (error) { + return ( + + + {error.message} + + ); + } + + return <>{children}; +}; + +export default LoadingBoundary; diff --git a/components/ModeImage.tsx b/components/ModeImage.tsx index c2fb0e062..12739ce02 100644 --- a/components/ModeImage.tsx +++ b/components/ModeImage.tsx @@ -8,7 +8,7 @@ const ModeImage: React.FC = ({ mode, ...props }) => { - return ; + return ; }; export default ModeImage; diff --git a/components/PageHeader.tsx b/components/PageHeader.tsx new file mode 100644 index 000000000..113371fa7 --- /dev/null +++ b/components/PageHeader.tsx @@ -0,0 +1,26 @@ +import { Heading } from "@chakra-ui/core"; +import { useMyTheme } from "lib/useMyTheme"; + +interface PageHeaderProps { + title: string; +} + +const PageHeader: React.FC = ({ title }) => { + const { themeColor } = useMyTheme(); + return ( + <> + + {title} + + + ); +}; + +export default PageHeader; diff --git a/generated/graphql.tsx b/generated/graphql.tsx index 17073084f..c8f4f1b01 100644 --- a/generated/graphql.tsx +++ b/generated/graphql.tsx @@ -37,7 +37,7 @@ export type Profile = { export type Query = { __typename?: 'Query'; getUserByIdentifier?: Maybe; - xRankPlacements: Array; + getXRankPlacements: Array; }; @@ -46,12 +46,10 @@ export type QueryGetUserByIdentifierArgs = { }; -export type QueryXRankPlacementsArgs = { - orderBy?: Maybe>; - first?: Maybe; - last?: Maybe; - before?: Maybe; - after?: Maybe; +export type QueryGetXRankPlacementsArgs = { + year: Scalars['Int']; + month: Scalars['Int']; + mode: RankedMode; }; export type UpdateUserProfileInput = { @@ -119,12 +117,6 @@ export type XRankPlacementWhereUniqueInput = { playerId_mode_month_year?: Maybe; }; -export type QueryXRankPlacementsOrderByInput = { - ranking?: Maybe; - month?: Maybe; - year?: Maybe; -}; - export type PlayerIdModeMonthYearCompoundUniqueInput = { playerId: Scalars['String']; mode: RankedMode; @@ -132,11 +124,6 @@ export type PlayerIdModeMonthYearCompoundUniqueInput = { year: Scalars['Int']; }; -export enum SortOrder { - Asc = 'asc', - Desc = 'desc' -} - export type UpdateUserProfileMutationVariables = Exact<{ profile: UpdateUserProfileInput; }>; @@ -164,14 +151,25 @@ export type GetUserByIdentifierQuery = ( )> } ); -export type XRankPlacementsQueryVariables = Exact<{ [key: string]: never; }>; +export type GetXRankPlacementsQueryVariables = Exact<{ + month: Scalars['Int']; + year: Scalars['Int']; + mode: RankedMode; +}>; -export type XRankPlacementsQuery = ( +export type GetXRankPlacementsQuery = ( { __typename?: 'Query' } - & { xRankPlacements: Array<( + & { getXRankPlacements: Array<( { __typename?: 'XRankPlacement' } - & Pick + & Pick + & { player: ( + { __typename?: 'Player' } + & { user?: Maybe<( + { __typename?: 'User' } + & Pick + )> } + ) } )> } ); @@ -252,43 +250,50 @@ export function useGetUserByIdentifierLazyQuery(baseOptions?: Apollo.LazyQueryHo export type GetUserByIdentifierQueryHookResult = ReturnType; export type GetUserByIdentifierLazyQueryHookResult = ReturnType; export type GetUserByIdentifierQueryResult = Apollo.QueryResult; -export const XRankPlacementsDocument = gql` - query XRankPlacements { - xRankPlacements(orderBy: [{ranking: asc}, {month: desc}, {year: desc}]) { - id +export const GetXRankPlacementsDocument = gql` + query getXRankPlacements($month: Int!, $year: Int!, $mode: RankedMode!) { + getXRankPlacements(month: $month, year: $year, mode: $mode) { playerId playerName ranking xPower weapon mode - month - year + player { + user { + avatarUrl + fullUsername + profilePath + } + } } } `; /** - * __useXRankPlacementsQuery__ + * __useGetXRankPlacementsQuery__ * - * To run a query within a React component, call `useXRankPlacementsQuery` and pass it any options that fit your needs. - * When your component renders, `useXRankPlacementsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * To run a query within a React component, call `useGetXRankPlacementsQuery` and pass it any options that fit your needs. + * When your component renders, `useGetXRankPlacementsQuery` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example - * const { data, loading, error } = useXRankPlacementsQuery({ + * const { data, loading, error } = useGetXRankPlacementsQuery({ * variables: { + * month: // value for 'month' + * year: // value for 'year' + * mode: // value for 'mode' * }, * }); */ -export function useXRankPlacementsQuery(baseOptions?: Apollo.QueryHookOptions) { - return Apollo.useQuery(XRankPlacementsDocument, baseOptions); +export function useGetXRankPlacementsQuery(baseOptions?: Apollo.QueryHookOptions) { + return Apollo.useQuery(GetXRankPlacementsDocument, baseOptions); } -export function useXRankPlacementsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { - return Apollo.useLazyQuery(XRankPlacementsDocument, baseOptions); +export function useGetXRankPlacementsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + return Apollo.useLazyQuery(GetXRankPlacementsDocument, baseOptions); } -export type XRankPlacementsQueryHookResult = ReturnType; -export type XRankPlacementsLazyQueryHookResult = ReturnType; -export type XRankPlacementsQueryResult = Apollo.QueryResult; \ No newline at end of file +export type GetXRankPlacementsQueryHookResult = ReturnType; +export type GetXRankPlacementsLazyQueryHookResult = ReturnType; +export type GetXRankPlacementsQueryResult = Apollo.QueryResult; \ No newline at end of file diff --git a/graphql/schema/xRankPlacement.ts b/graphql/schema/xRankPlacement.ts index b390ecaf2..24551c3c9 100644 --- a/graphql/schema/xRankPlacement.ts +++ b/graphql/schema/xRankPlacement.ts @@ -1,4 +1,4 @@ -import { extendType, objectType } from "@nexus/schema"; +import { arg, extendType, intArg, objectType } from "@nexus/schema"; export const XRankPlacmement = objectType({ name: "XRankPlacement", @@ -33,22 +33,20 @@ export const Player = objectType({ export const Query = extendType({ type: "Query", definition(t) { - // TODO: limit so that you can only get max 100 results max and 50 by default - t.crud.xRankPlacements({ - ordering: { ranking: true, month: true, year: true }, - async resolve( - root: any, - args: any, - ctx: any, - info: any, - originalResolve: any - ) { - console.log({ args: args.orderBy }); - if (!args.first && !args.last) args.first = 25; - - args.first = Math.min(args.first, 100); - args.last = Math.min(args.last, 100); - return originalResolve(root, args, ctx, info); + t.field("getXRankPlacements", { + type: "XRankPlacement", + nullable: false, + list: [true], + args: { + year: intArg({ nullable: false }), + month: intArg({ nullable: false }), + mode: arg({ type: "RankedMode", nullable: false }), + }, + resolve: (_root, args, ctx) => { + return ctx.prisma.xRankPlacement.findMany({ + where: { month: args.month, year: args.year, mode: args.mode }, + orderBy: [{ ranking: "asc" }, { month: "desc" }, { year: "desc" }], + }); }, }); }, diff --git a/lib/getLocalizedMontYearString.ts b/lib/getLocalizedMontYearString.ts new file mode 100644 index 000000000..32fa29e84 --- /dev/null +++ b/lib/getLocalizedMontYearString.ts @@ -0,0 +1,16 @@ +export const getLocalizedMonthYearString = ( + month: number, + year: number, + locale: string +) => { + const dateForLocalization = new Date(); + dateForLocalization.setDate(1); + dateForLocalization.setMonth(month - 1); + dateForLocalization.setFullYear(year); + const localizedString = dateForLocalization.toLocaleString(locale, { + month: "long", + year: "numeric", + }); + + return localizedString.charAt(0).toUpperCase() + localizedString.slice(1); +}; diff --git a/lib/getRankingString.ts b/lib/getRankingString.ts new file mode 100644 index 000000000..6449958dd --- /dev/null +++ b/lib/getRankingString.ts @@ -0,0 +1,13 @@ +export const getRankingString = (ranking: number) => { + switch (ranking) { + case 1: + return "🥇"; + case 2: + return "🥈"; + case 3: + return "🥉"; + + default: + return `${ranking}`; + } +}; diff --git a/nexus-typegen.ts b/nexus-typegen.ts index 0b152273d..826a1efd6 100644 --- a/nexus-typegen.ts +++ b/nexus-typegen.ts @@ -26,11 +26,6 @@ export interface NexusGenInputs { playerId: string; // String! year: number; // Int! } - QueryXRankPlacementsOrderByInput: { // input type - month?: NexusGenEnums['SortOrder'] | null; // SortOrder - ranking?: NexusGenEnums['SortOrder'] | null; // SortOrder - year?: NexusGenEnums['SortOrder'] | null; // SortOrder - } UpdateUserProfileInput: { // input type bio?: string | null; // String country?: string | null; // String @@ -50,7 +45,6 @@ export interface NexusGenInputs { export interface NexusGenEnums { RankedMode: Prisma.RankedMode - SortOrder: Prisma.SortOrder } export interface NexusGenScalars { @@ -72,11 +66,9 @@ export interface NexusGenRootTypes { export interface NexusGenAllTypes extends NexusGenRootTypes { PlayerIdModeMonthYearCompoundUniqueInput: NexusGenInputs['PlayerIdModeMonthYearCompoundUniqueInput']; - QueryXRankPlacementsOrderByInput: NexusGenInputs['QueryXRankPlacementsOrderByInput']; UpdateUserProfileInput: NexusGenInputs['UpdateUserProfileInput']; XRankPlacementWhereUniqueInput: NexusGenInputs['XRankPlacementWhereUniqueInput']; RankedMode: NexusGenEnums['RankedMode']; - SortOrder: NexusGenEnums['SortOrder']; String: NexusGenScalars['String']; Int: NexusGenScalars['Int']; Float: NexusGenScalars['Float']; @@ -107,7 +99,7 @@ export interface NexusGenFieldTypes { } Query: { // field return type getUserByIdentifier: NexusGenRootTypes['User'] | null; // User - xRankPlacements: NexusGenRootTypes['XRankPlacement'][]; // [XRankPlacement!]! + getXRankPlacements: NexusGenRootTypes['XRankPlacement'][]; // [XRankPlacement!]! } User: { // field return type avatarUrl: string | null; // String @@ -154,7 +146,7 @@ export interface NexusGenFieldTypeNames { } Query: { // field return type name getUserByIdentifier: 'User' - xRankPlacements: 'XRankPlacement' + getXRankPlacements: 'XRankPlacement' } User: { // field return type name avatarUrl: 'String' @@ -196,12 +188,10 @@ export interface NexusGenArgTypes { getUserByIdentifier: { // args identifier: string; // String! } - xRankPlacements: { // args - after?: NexusGenInputs['XRankPlacementWhereUniqueInput'] | null; // XRankPlacementWhereUniqueInput - before?: NexusGenInputs['XRankPlacementWhereUniqueInput'] | null; // XRankPlacementWhereUniqueInput - first?: number | null; // Int - last?: number | null; // Int - orderBy?: NexusGenInputs['QueryXRankPlacementsOrderByInput'][] | null; // [QueryXRankPlacementsOrderByInput!] + getXRankPlacements: { // args + mode: NexusGenEnums['RankedMode']; // RankedMode! + month: number; // Int! + year: number; // Int! } } } @@ -213,9 +203,9 @@ export interface NexusGenInheritedFields {} export type NexusGenObjectNames = "Mutation" | "Player" | "Profile" | "Query" | "User" | "XRankPlacement"; -export type NexusGenInputNames = "PlayerIdModeMonthYearCompoundUniqueInput" | "QueryXRankPlacementsOrderByInput" | "UpdateUserProfileInput" | "XRankPlacementWhereUniqueInput"; +export type NexusGenInputNames = "PlayerIdModeMonthYearCompoundUniqueInput" | "UpdateUserProfileInput" | "XRankPlacementWhereUniqueInput"; -export type NexusGenEnumNames = "RankedMode" | "SortOrder"; +export type NexusGenEnumNames = "RankedMode"; export type NexusGenInterfaceNames = never; diff --git a/pages/top500.tsx b/pages/top500.tsx new file mode 100644 index 000000000..1d11226ba --- /dev/null +++ b/pages/top500.tsx @@ -0,0 +1,134 @@ +import { Radio, RadioGroup, Select, Stack } from "@chakra-ui/core"; +import { PrismaClient } from "@prisma/client"; +import LoadingBoundary from "components/LoadingBoundary"; +import PageHeader from "components/PageHeader"; +import { + GetXRankPlacementsDocument, + GetXRankPlacementsQueryVariables, + RankedMode, + useGetXRankPlacementsQuery, +} from "generated/graphql"; +import { initializeApollo } from "lib/apollo"; +import { getLocalizedMonthYearString } from "lib/getLocalizedMontYearString"; +import { useTranslation } from "lib/useMockT"; +import { GetStaticProps } from "next"; +import { useState } from "react"; +import XSearch from "scenes/XSearch"; + +const prisma = new PrismaClient(); + +const getMonthOptions = (latestMonth: number, latestYear: number) => { + const monthChoices = []; + let month = 5; + let year = 2018; + + while (true) { + const monthString = getLocalizedMonthYearString(month, year, "en"); + monthChoices.push({ label: monthString, value: `${month},${year}` }); + + if (month === latestMonth && year === latestYear) break; + + month++; + if (month === 13) { + month = 1; + year++; + } + } + + monthChoices.reverse(); + + return monthChoices; +}; + +export const getStaticProps: GetStaticProps = async () => { + const apolloClient = initializeApollo(null, { prisma }); + + const mostRecentResult = await prisma.xRankPlacement.findFirst({ + orderBy: [{ month: "desc" }, { year: "desc" }], + }); + + if (!mostRecentResult) throw Error("No X Rank Placements"); + + await apolloClient.query({ + query: GetXRankPlacementsDocument, + variables: { + month: mostRecentResult.month, + year: mostRecentResult.year, + mode: "SZ", + }, + }); + + return { + props: { + initialApolloState: apolloClient.cache.extract(), + monthOptions: getMonthOptions( + mostRecentResult.month, + mostRecentResult.year + ), + }, + }; +}; + +const XSearchPage = ({ + monthOptions, +}: { + monthOptions: { label: string; value: string }[]; +}) => { + const { t } = useTranslation(); + + const [variables, setVariables] = useState({ + month: Number(monthOptions[0].value.split(",")[0]), + year: Number(monthOptions[0].value.split(",")[1]), + mode: "SZ" as RankedMode, + }); + const { data, loading, error } = useGetXRankPlacementsQuery({ + variables, + }); + + return ( + <> + + + + setVariables({ ...variables, mode: value as RankedMode }) + } + mt={4} + mb={8} + > + + {t("plans;splatZonesShort")} + {t("plans;towerControlShort")} + {t("plans;rainMakerShort")} + {t("plans;clamBlitzShort")} + + + + + + + ); +}; + +export default XSearchPage; diff --git a/pages/xsearch.tsx b/pages/xsearch.tsx deleted file mode 100644 index f8015addd..000000000 --- a/pages/xsearch.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { PrismaClient } from "@prisma/client"; -import { - useXRankPlacementsQuery, - XRankPlacementsDocument, -} from "generated/graphql"; -import { initializeApollo } from "lib/apollo"; -import { GetStaticProps } from "next"; -import { useRouter } from "next/router"; -import XSearch from "scenes/XSearch"; - -export const getStaticProps: GetStaticProps = async () => { - const apolloClient = initializeApollo(null, { prisma: new PrismaClient() }); - - await apolloClient.query({ - query: XRankPlacementsDocument, - }); - - return { - props: { - initialApolloState: apolloClient.cache.extract(), - }, - }; -}; - -const XSearchPage = () => { - const { data } = useXRankPlacementsQuery(); - const router = useRouter(); - - // FIXME: handle fallback - const getUserByIdentifier = data!.xRankPlacements; - - return ; -}; - -export default XSearchPage; diff --git a/prisma/seed.ts b/prisma/seed.ts index 153ec5aff..61736eef0 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -1,5 +1,5 @@ -const { PrismaClient } = require("@prisma/client"); -//import { PrismaClient } from "@prisma/client"; +//const { PrismaClient } = require("@prisma/client"); +import { PrismaClient } from "@prisma/client"; const fs = require("fs"); const path = require("path"); @@ -26,7 +26,7 @@ const main = async () => { await prisma.profile.deleteMany({}); await prisma.user.deleteMany({}); - await prisma.user.create({ + const testUser = await prisma.user.create({ data: { username: "N-ZAP", discriminator: "6227", @@ -86,6 +86,14 @@ const main = async () => { create: { playerId: "" + i, names: [playerName], + user: + i === 0 + ? { + connect: { + id: testUser.id, + }, + } + : undefined, }, }, }, diff --git a/scenes/XSearch/index.tsx b/scenes/XSearch/index.tsx index b2b52538e..d7511e9da 100644 --- a/scenes/XSearch/index.tsx +++ b/scenes/XSearch/index.tsx @@ -1,4 +1,4 @@ -import { Button, Text } from "@chakra-ui/core"; +import { Avatar, Text } from "@chakra-ui/core"; import MyHead from "components/MyHead"; import { Table, @@ -9,19 +9,20 @@ import { TableRow, } from "components/Table"; import WeaponImage from "components/WeaponImage"; -import { XRankPlacementsQuery } from "generated/graphql"; +import { GetXRankPlacementsQuery } from "generated/graphql"; +import { getRankingString } from "lib/getRankingString"; import { useTranslation } from "lib/useMockT"; import { useMyTheme } from "lib/useMyTheme"; -import Image from "next/image"; +import Link from "next/link"; interface Props { - placements: NonNullable; + placements: NonNullable; } const XSearch: React.FC = ({ placements }) => { const { t } = useTranslation(); - const { gray, themeColor } = useMyTheme(); - console.log({ placements }); + const { gray } = useMyTheme(); + return ( <> @@ -36,71 +37,40 @@ const XSearch: React.FC = ({ placements }) => { + + {t("xsearch;Name")} - {t("freeagents;Weapon")} {t("xsearch;X Power")} - {t("xsearch;Placement")} - {t("xsearch;Mode")} - {t("xsearch;Month")} - + {t("freeagents;Weapon")} {placements.map((record) => { return ( - + + + {getRankingString(record.ranking)} + - {/* {record.user ? ( - - + {record.player.user && ( + + - - {record.playerName} - - ) : ( - <>{record.playerName} - )} */} - {record.playerName} - - - + + + )} + {record.playerName} {record.xPower} - {record.ranking} - - - - {record.month}/{record.year} - - - + ); diff --git a/scenes/XSearch/queries/XRankPlacements.graphql b/scenes/XSearch/queries/XRankPlacements.graphql index 0cc48a22f..209fe1305 100644 --- a/scenes/XSearch/queries/XRankPlacements.graphql +++ b/scenes/XSearch/queries/XRankPlacements.graphql @@ -1,15 +1,17 @@ -query XRankPlacements { - xRankPlacements( - orderBy: [{ ranking: asc }, { month: desc }, { year: desc }] - ) { - id +query getXRankPlacements($month: Int!, $year: Int!, $mode: RankedMode!) { + getXRankPlacements(month: $month, year: $year, mode: $mode) { playerId playerName ranking xPower weapon mode - month - year + player { + user { + avatarUrl + fullUsername + profilePath + } + } } } diff --git a/schema.graphql b/schema.graphql index b430a30a7..7016cbb2f 100644 --- a/schema.graphql +++ b/schema.graphql @@ -37,13 +37,7 @@ type Profile { type Query { getUserByIdentifier(identifier: String!): User - xRankPlacements(after: XRankPlacementWhereUniqueInput, before: XRankPlacementWhereUniqueInput, first: Int, last: Int, orderBy: [QueryXRankPlacementsOrderByInput!]): [XRankPlacement!]! -} - -input QueryXRankPlacementsOrderByInput { - month: SortOrder - ranking: SortOrder - year: SortOrder + getXRankPlacements(mode: RankedMode!, month: Int!, year: Int!): [XRankPlacement!]! } enum RankedMode { @@ -53,11 +47,6 @@ enum RankedMode { TC } -enum SortOrder { - asc - desc -} - input UpdateUserProfileInput { bio: String country: String