tournaments progress

This commit is contained in:
Sendou 2020-03-07 17:37:12 +02:00
parent 4ba688cf03
commit 671287be89
13 changed files with 267 additions and 62 deletions

View File

@ -2100,9 +2100,9 @@
}
},
"@types/jest": {
"version": "25.1.3",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-25.1.3.tgz",
"integrity": "sha512-jqargqzyJWgWAJCXX96LBGR/Ei7wQcZBvRv0PLEu9ZByMfcs23keUJrKv9FMR6YZf9YCbfqDqgmY+JUBsnqhrg==",
"version": "25.1.4",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-25.1.4.tgz",
"integrity": "sha512-QDDY2uNAhCV7TMCITrxz+MRk1EizcsevzfeS6LykIlq2V1E5oO4wXG8V2ZEd9w7Snxeeagk46YbMgZ8ESHx3sw==",
"requires": {
"jest-diff": "^25.1.0",
"pretty-format": "^25.1.0"
@ -2120,9 +2120,9 @@
}
},
"@types/yargs": {
"version": "15.0.3",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.3.tgz",
"integrity": "sha512-XCMQRK6kfpNBixHLyHUsGmXrpEmFFxzMrcnSXFMziHd8CoNJo8l16FkHyQq4x+xbM7E2XL83/O78OD8u+iZTdQ==",
"version": "15.0.4",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz",
"integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==",
"requires": {
"@types/yargs-parser": "*"
}
@ -2216,9 +2216,9 @@
"integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA=="
},
"@types/node": {
"version": "13.7.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.7.tgz",
"integrity": "sha512-Uo4chgKbnPNlxQwoFmYIwctkQVkMMmsAoGGU4JKwLuvBefF0pCq4FybNSnfkfRCpC7ZW7kttcC/TrRtAJsvGtg=="
"version": "13.9.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.0.tgz",
"integrity": "sha512-0ARSQootUG1RljH2HncpsY2TJBfGQIKOOi7kxzUY6z54ePu/ZD+wJA8zI2Q6v8rol2qpG/rvqsReco8zNMPvhQ=="
},
"@types/parse-json": {
"version": "4.0.0",
@ -2624,9 +2624,9 @@
}
},
"acorn": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz",
"integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ=="
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
"integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg=="
},
"acorn-globals": {
"version": "4.3.4",

View File

@ -13,8 +13,8 @@
"@testing-library/jest-dom": "^5.1.1",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^10.0.0",
"@types/jest": "^25.1.3",
"@types/node": "^13.7.7",
"@types/jest": "^25.1.4",
"@types/node": "^13.9.0",
"@types/reach__router": "^1.3.0",
"@types/react": "^16.9.23",
"@types/react-color": "^3.0.1",

View File

@ -35,7 +35,7 @@ const Input: React.FC<InputProps> = ({
setValue(event.target.value)
return (
<>
<Box>
{label && <Label required={required}>{label}</Label>}
<InputGroup>
{textLeft && <InputLeftAddon>{textLeft}</InputLeftAddon>}
@ -57,7 +57,7 @@ const Input: React.FC<InputProps> = ({
{(value ?? "").length}/{limit}
</Box>
)}
</>
</Box>
)
}

View File

@ -90,7 +90,7 @@ const Select: React.FC<SelectProps> = ({
}
return (
<Box w={width}>
<Box>
{label && <Label required={required}>{label}</Label>}
<ReactSelect
className="basic-single"

View File

@ -9,7 +9,7 @@ import {
import Error from "../common/Error"
import Loading from "../common/Loading"
import TournamentCard from "./TournamentCard"
import { Box, Flex, Image, Avatar, Icon, Grid } from "@chakra-ui/core"
import { Box, Flex, Avatar, Icon, Grid } from "@chakra-ui/core"
import Button from "../elements/Button"
import { Helmet } from "react-helmet-async"
import { FaLongArrowAltLeft } from "react-icons/fa"

View File

@ -1,10 +1,36 @@
import React, { useState } from "react"
import React, { useState, useContext } from "react"
import Button from "../elements/Button"
import { Collapse } from "@chakra-ui/core"
import { Collapse, Grid, Flex, Box, RadioGroup, Radio } from "@chakra-ui/core"
import Input from "../elements/Input"
import WeaponSelector from "../common/WeaponSelector"
import Select from "../elements/Select"
import Label from "../elements/Label"
import MyThemeContext from "../../themeContext"
import { Weapon } from "../../types"
import { maps } from "../../utils/lists"
interface TournamentFiltersProps {}
interface TournamentFiltersProps {
forms: {
tournament_name?: string
region?: string
player_name?: string
team_name?: string
comp?: string[]
mode?: string
stage?: string
}
handleChange: (value: Object) => void
handleClear: () => void
onSubmit: () => void
}
const TournamentFilters: React.FC<TournamentFiltersProps> = ({}) => {
const TournamentFilters: React.FC<TournamentFiltersProps> = ({
forms,
handleChange,
handleClear,
onSubmit,
}) => {
const { themeColor } = useContext(MyThemeContext)
const [show, setShow] = useState(true)
return (
<>
@ -12,9 +38,84 @@ const TournamentFilters: React.FC<TournamentFiltersProps> = ({}) => {
{show ? "Hide filters" : "Show filters"}
</Button>
<Collapse mt={4} isOpen={show}>
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus
terry richardson ad squid. Nihil anim keffiyeh helvetica, craft beer
labore wes anderson cred nesciunt sapiente ea proident.
<Grid maxW="500px" gridRowGap="1em" gridTemplateColumns="1fr">
<Input
label="Tournament"
value={forms.tournament_name ?? ""}
setValue={(value: string) =>
handleChange({ tournament_name: value })
}
/>
<Input
label="Team"
value={forms.team_name ?? ""}
setValue={(value: string) => handleChange({ team_name: value })}
/>
<Input
label="Player"
value={forms.player_name ?? ""}
setValue={(value: string) => handleChange({ player_name: value })}
/>
<WeaponSelector
label="Comp"
value={(forms.comp as Weapon[]) ?? []}
setValue={(value: Weapon[]) => handleChange({ comp: value })}
isMulti
/>
<Select
label="Map & mode"
isSearchable
value={
forms.stage && forms.mode ? `${forms.stage} (${forms.mode})` : ""
}
setValue={(value: string) => {
const partsArray = value.split(" (")
handleChange({
stage: partsArray[0],
mode: partsArray[1].substring(0, partsArray[1].length - 1),
})
}}
options={maps.reduce(
(acc: { label: string; value: string }[], cur: string) => [
...acc,
{ label: `${cur} (TW)`, value: `${cur} (TW)` },
{ label: `${cur} (SZ)`, value: `${cur} (SZ)` },
{ label: `${cur} (TC)`, value: `${cur} (TC)` },
{ label: `${cur} (RM)`, value: `${cur} (RM)` },
{ label: `${cur} (CB)`, value: `${cur} (CB)` },
],
[]
)}
/>
<Box mt="0.5em">
<Label>Mode</Label>
<RadioGroup
value={forms.region ?? "all"}
defaultValue="0"
spacing={5}
isInline
onChange={(e, value: any) => handleChange({ region: value })}
>
<Radio variantColor={themeColor} value="all">
All
</Radio>
<Radio variantColor={themeColor} value="western">
Western only
</Radio>
<Radio variantColor={themeColor} value="jpn">
Japanese only
</Radio>
</RadioGroup>
</Box>
<Flex mt="1em">
<Button onClick={onSubmit}>Apply</Button>
<Box mx="1em">
<Button outlined onClick={handleClear}>
Clear filters
</Button>
</Box>
</Flex>
</Grid>
</Collapse>
</>
)

View File

@ -7,14 +7,17 @@ import {
NumberParam,
StringParam,
ArrayParam,
encodeQueryParams,
} from "use-query-params"
import { useQuery } from "@apollo/react-hooks"
import { SEARCH_FOR_TOURNAMENTS } from "../../graphql/queries/searchForTournaments"
import Loading from "../common/Loading"
import Error from "../common/Error"
import TournamentCard from "./TournamentCard"
import { Box, Grid } from "@chakra-ui/core"
import { Box, Grid, Alert, AlertIcon } from "@chakra-ui/core"
import TournamentFilters from "./TournamentFilters"
import Pagination from "../common/Pagination"
import { stringify } from "querystring"
interface SearchForTournamentsData {
searchForTournaments: {
@ -37,21 +40,26 @@ interface SearchForTournamentsVars {
tournament_name?: string
region?: string
player_name?: string
unique_id?: string
team_name?: string
comp?: string[]
page?: number
mode?: string
stage?: string
}
const TournamentsPage: React.FC<RouteComponentProps> = ({}) => {
const [query, setQuery] = useQueryParams({
page: NumberParam,
tournament_name: StringParam,
region: StringParam,
team_name: StringParam,
player_name: StringParam,
comp: ArrayParam,
})
const queryMap = {
page: NumberParam,
tournament_name: StringParam,
region: StringParam,
team_name: StringParam,
player_name: StringParam,
comp: ArrayParam,
mode: StringParam,
stage: StringParam,
}
const TournamentsPage: React.FC<RouteComponentProps> = () => {
const [query, setQuery] = useQueryParams(queryMap)
const [forms, setForms] = useState<SearchForTournamentsVars>({
page: query.page,
tournament_name: query.tournament_name,
@ -59,9 +67,11 @@ const TournamentsPage: React.FC<RouteComponentProps> = ({}) => {
team_name: query.team_name,
player_name: query.player_name,
comp: query.comp,
mode: query.mode,
stage: query.stage,
})
const { data, error, loading } = useQuery<
const { data, error } = useQuery<
SearchForTournamentsData,
SearchForTournamentsVars
>(SEARCH_FOR_TOURNAMENTS, {
@ -69,29 +79,79 @@ const TournamentsPage: React.FC<RouteComponentProps> = ({}) => {
})
if (error) return <Error errorMessage={error.message} />
if (loading || !data) return <Loading />
const { tournaments } = data.searchForTournaments
const handleFormChange = (value: Object) => {
setForms({ ...forms, ...value })
}
const handleClear = () => {
setForms({})
setQuery({}, "replace")
}
const encodedQuery = encodeQueryParams(queryMap, query)
const linkSuffix = `?${stringify(encodedQuery)}`
return (
<>
<Helmet>
<title>Tournaments | sendou.ink</title>
</Helmet>
<PageHeader title="Tournaments" />
<TournamentFilters />
<Grid
gridGap="1em"
gridTemplateColumns="repeat(auto-fit, minmax(260px, 1fr))"
mt="1em"
>
{tournaments.map(tournament => (
<Box key={tournament.id}>
<Link to={`/tournaments/${tournament.id}`}>
<TournamentCard tournament={tournament} styledOnHover />
</Link>
<TournamentFilters
forms={forms}
handleChange={handleFormChange}
handleClear={handleClear}
onSubmit={() => setQuery(forms)}
/>
{data && data.searchForTournaments.tournaments.length > 0 ? (
<>
<Box mt="1em">
<Pagination
currentPage={forms.page ?? 1}
pageCount={data?.searchForTournaments.pageCount ?? 999}
onChange={page => {
setForms({ ...forms, page })
setQuery({ ...query, page })
}}
/>
</Box>
))}
</Grid>
{data && data.searchForTournaments ? (
<>
<Grid
gridGap="1em"
gridTemplateColumns="repeat(auto-fit, minmax(260px, 1fr))"
mt="1em"
>
{data.searchForTournaments.tournaments.map(tournament => (
<Box key={tournament.id}>
<Link to={`/tournaments/${tournament.id}${linkSuffix}`}>
<TournamentCard tournament={tournament} styledOnHover />
</Link>
</Box>
))}
</Grid>
<Box mt="1em">
<Pagination
currentPage={forms.page ?? 1}
pageCount={data.searchForTournaments.pageCount}
onChange={page => {
setForms({ ...forms, page })
setQuery({ ...query, page })
}}
/>
</Box>
</>
) : (
<Loading />
)}
</>
) : (
<Alert status="info" mt="2em">
<AlertIcon />
Your doesn't match any tournaments. Try another filter!
</Alert>
)}
</>
)
}

View File

@ -84,11 +84,7 @@ const Top500Forms: React.FC<Top500FormsProps> = ({ forms, handleChange }) => {
isInline
onChange={(e, value: any) => handleChange({ mode: parseInt(value) })}
>
<Radio
variantColor={themeColor}
value="0"
isChecked={forms.mode === 0 || !forms.mode}
>
<Radio variantColor={themeColor} value="0">
All modes
</Radio>
<Radio variantColor={themeColor} value="1">

View File

@ -8,6 +8,8 @@ export const SEARCH_FOR_TOURNAMENTS: DocumentNode = gql`
$team_name: String
$player_name: String
$comp: [String]
$mode: Mode
$stage: String
) {
searchForTournaments(
page: $page
@ -16,6 +18,8 @@ export const SEARCH_FOR_TOURNAMENTS: DocumentNode = gql`
team_name: $team_name
player_name: $player_name
comp: $comp
mode: $mode
stage: $stage
) {
tournaments {
id

View File

@ -789,6 +789,32 @@ export const continents = {
zw: "AF",
} as const
export const maps = [
"The Reef",
"Musselforge Fitness",
"Starfish Mainstage",
"Humpback Pump Track",
"Inkblot Art Academy",
"Sturgeon Shipyard",
"Moray Towers",
"Port Mackerel",
"Manta Maria",
"Kelp Dome",
"Snapper Canal",
"Blackbelly Skatepark",
"MakoMart",
"Walleye Warehouse",
"Shellendorf Institute",
"Arowana Mall",
"Goby Arena",
"Piranha Pit",
"Camp Triggerfish",
"Wahoo World",
"New Albacore Hotel",
"Ancho-V Games",
"Skipper Pavilion",
]
export const modes = [
"",
"Splat Zones",

View File

@ -12,6 +12,8 @@ const typeDef = gql`
unique_id: String
team_name: String
comp: [String]
stage: String
mode: Mode
page: Int
): TournamentCollection!
}
@ -166,6 +168,14 @@ const resolvers = {
})
}
if (args.mode) {
roundSearchCriteria.mode = args.mode
}
if (args.stage) {
roundSearchCriteria.stage = args.stage
}
// if criteria were presented that we have to search
// from the Round collection
let tournament_ids = null
@ -176,7 +186,15 @@ const resolvers = {
if (args.region && args.region === "jpn")
tournamentSearchCriteria.jpn = true
if (roundSearchCriteria.$or.length !== 0) {
if (roundSearchCriteria.$or.length === 0) {
delete roundSearchCriteria.$or
}
if (
roundSearchCriteria.$or ||
roundSearchCriteria.stage ||
roundSearchCriteria.mode
) {
tournament_ids = await Round.find(roundSearchCriteria).distinct(
"tournament_id"
)

6
package-lock.json generated
View File

@ -828,9 +828,9 @@
}
},
"cross-env": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.1.tgz",
"integrity": "sha512-1+DmLosu38kC4s1H4HzNkcolwdANifu9+5bE6uKQCV4L6jvVdV9qdRAk8vV3GoWRe0x4z+K2fFhgoDMqwNsPqQ==",
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.2.tgz",
"integrity": "sha512-KZP/bMEOJEDCkDQAyRhu3RL2ZO/SUVrxQVI0G3YEQ+OLbRA3c6zgixe8Mq8a/z7+HKlNEjo8oiLUs8iRijY2Rw==",
"dev": true,
"requires": {
"cross-spawn": "^7.0.1"

View File

@ -33,7 +33,7 @@
"webhook-discord": "^3.5.1"
},
"devDependencies": {
"cross-env": "^7.0.1",
"cross-env": "^7.0.2",
"nodemon": "^2.0.2"
}
}