mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-23 19:46:28 -05:00
result form
This commit is contained in:
30
frontend-react/package-lock.json
generated
30
frontend-react/package-lock.json
generated
@@ -1207,9 +1207,9 @@
|
||||
"integrity": "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA=="
|
||||
},
|
||||
"@hapi/hoek": {
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.5.0.tgz",
|
||||
"integrity": "sha512-7XYT10CZfPsH7j9F1Jmg1+d0ezOux2oM2GfArAzLwWe4mE2Dr3hVjsAL6+TFY49RRJlCdJDMw3nJsLFroTc8Kw=="
|
||||
"version": "8.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.5.1.tgz",
|
||||
"integrity": "sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow=="
|
||||
},
|
||||
"@hapi/joi": {
|
||||
"version": "15.1.1",
|
||||
@@ -1969,9 +1969,9 @@
|
||||
}
|
||||
},
|
||||
"@types/history": {
|
||||
"version": "4.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.4.tgz",
|
||||
"integrity": "sha512-+o2igcuZA3xtOoFH56s+MCZVidwlJNcJID57DSCyawS2i910yG9vkwehCjJNZ6ImhCR5S9DbvIJKyYHcMyOfMw=="
|
||||
"version": "4.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.5.tgz",
|
||||
"integrity": "sha512-wLD/Aq2VggCJXSjxEwrMafIP51Z+13H78nXIX0ABEuIGhmB5sNGbR113MOKo+yfw+RDo1ZU3DM6yfnnRF/+ouw=="
|
||||
},
|
||||
"@types/istanbul-lib-coverage": {
|
||||
"version": "2.0.1",
|
||||
@@ -2112,9 +2112,9 @@
|
||||
"integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA=="
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "13.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.0.tgz",
|
||||
"integrity": "sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ=="
|
||||
"version": "13.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.1.tgz",
|
||||
"integrity": "sha512-Zq8gcQGmn4txQEJeiXo/KiLpon8TzAl0kmKH4zdWctPj05nWwp1ClMdAVEloqrQKfaC48PNLdgN/aVaLqUrluA=="
|
||||
},
|
||||
"@types/parse-json": {
|
||||
"version": "4.0.0",
|
||||
@@ -2132,9 +2132,9 @@
|
||||
"integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw=="
|
||||
},
|
||||
"@types/reach__router": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.2.6.tgz",
|
||||
"integrity": "sha512-Oh5DAVr/L2svBvubw6QEFpXGu295Y406BPs4i9t1n2pp7M+q3pmCmhzb9oZV5wncR41KCD3NHl1Yhi7uKnTPsA==",
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.0.tgz",
|
||||
"integrity": "sha512-0aL79bFPJzJOJOOMZm2301ErQVaveBdpW88uuavXymUlcYIAOCmI1ujJ2XLH6Mzn76O94eQCHIl1FDzNNKJCYA==",
|
||||
"requires": {
|
||||
"@types/history": "*",
|
||||
"@types/react": "*"
|
||||
@@ -11786,9 +11786,9 @@
|
||||
}
|
||||
},
|
||||
"react-datepicker": {
|
||||
"version": "2.12.0",
|
||||
"resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-2.12.0.tgz",
|
||||
"integrity": "sha512-LlFK36MyQXLSMGB+DiZvPmVw+P1IeCvn2eX3bU1P32T6i8o7+b8LrUVYV9o7slXZMhqOjgTq2vYiXvdRpN/dlQ==",
|
||||
"version": "2.12.1",
|
||||
"resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-2.12.1.tgz",
|
||||
"integrity": "sha512-Y5xIim/tzHZ0M+bo6EAZZX5KZ7ov+N6VceSM981bS9dNDoD+ezUAT1/DpeSRXxmFBrNnd5dW4BhlgE36ZzkfLQ==",
|
||||
"requires": {
|
||||
"classnames": "^2.2.6",
|
||||
"date-fns": "^2.0.1",
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
"@testing-library/react": "^9.4.0",
|
||||
"@testing-library/user-event": "^8.1.0",
|
||||
"@types/jest": "^25.1.2",
|
||||
"@types/node": "^13.7.0",
|
||||
"@types/reach__router": "^1.2.6",
|
||||
"@types/node": "^13.7.1",
|
||||
"@types/reach__router": "^1.3.0",
|
||||
"@types/react": "^16.9.19",
|
||||
"@types/react-datepicker": "^2.11.0",
|
||||
"@types/react-dom": "^16.9.5",
|
||||
@@ -26,7 +26,7 @@
|
||||
"jstz": "^2.1.1",
|
||||
"node-sass": "^4.13.1",
|
||||
"react": "^16.12.0",
|
||||
"react-datepicker": "^2.12.0",
|
||||
"react-datepicker": "^2.12.1",
|
||||
"react-dom": "^16.12.0",
|
||||
"react-draggable": "^4.2.0",
|
||||
"react-helmet-async": "^1.0.4",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from "react"
|
||||
import React, { forwardRef } from "react"
|
||||
import HackerOneDatePicker from "react-datepicker"
|
||||
import Button from "./Button"
|
||||
import "react-datepicker/src/stylesheets/datepicker.scss"
|
||||
@@ -13,15 +13,20 @@ interface CustomInputProps {
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
const CustomInput: React.FC<CustomInputProps> = ({ value, onClick }) => (
|
||||
<Button onClick={onClick as () => void}>{value as string}</Button>
|
||||
)
|
||||
|
||||
const DatePicker: React.FC<DatePickerProps> = ({ date, setDate }) => {
|
||||
const CustomInput: React.FC<CustomInputProps> = forwardRef(
|
||||
({ onClick, value }, ref) => (
|
||||
<Button onClick={onClick as () => void}>{value as string}</Button>
|
||||
)
|
||||
)
|
||||
|
||||
return (
|
||||
<HackerOneDatePicker
|
||||
selected={date}
|
||||
onChange={date => setDate(date)}
|
||||
onChange={date => {
|
||||
date?.setUTCHours(0, 0, 0, 0)
|
||||
setDate(date)
|
||||
}}
|
||||
customInput={<CustomInput />}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from "react"
|
||||
import React, { useState, useEffect } from "react"
|
||||
import Modal from "../elements/Modal"
|
||||
import Box from "../elements/Box"
|
||||
import { TournamentResult } from "../../types"
|
||||
@@ -7,22 +7,27 @@ import { useMutation } from "@apollo/react-hooks"
|
||||
import { useToast } from "@chakra-ui/core"
|
||||
import { ADD_RESULT } from "../../graphql/mutations/addResult"
|
||||
import TweetEmbed from "react-tweet-embed"
|
||||
import { useContext } from "react"
|
||||
import MyThemeContext from "../../themeContext"
|
||||
import DatePicker from "../elements/DatePicker"
|
||||
import Label from "../elements/Label"
|
||||
import PlacementInput from "./PlacementInput"
|
||||
import Button from "../elements/Button"
|
||||
|
||||
interface AddResultModalProps {
|
||||
closeModal: () => void
|
||||
}
|
||||
|
||||
const AddResultModal: React.FC<AddResultModalProps> = ({ closeModal }) => {
|
||||
const { colorMode } = useContext(MyThemeContext)
|
||||
const [result, setResult] = useState<Partial<TournamentResult>>({})
|
||||
const [result, setResult] = useState<Partial<TournamentResult>>({
|
||||
placement: 1,
|
||||
date: (new Date() as unknown) as string,
|
||||
})
|
||||
const toast = useToast()
|
||||
|
||||
const [addResult] = useMutation<boolean, TournamentResult>(ADD_RESULT, {
|
||||
variables: result as TournamentResult,
|
||||
variables: {
|
||||
...(result as TournamentResult),
|
||||
date: result.date?.toString() as string,
|
||||
},
|
||||
onCompleted: () => {
|
||||
closeModal()
|
||||
toast({
|
||||
@@ -37,15 +42,30 @@ const AddResultModal: React.FC<AddResultModalProps> = ({ closeModal }) => {
|
||||
title: "An error occurred",
|
||||
description: error.message,
|
||||
position: "top-right",
|
||||
status: "success",
|
||||
status: "error",
|
||||
duration: 10000,
|
||||
})
|
||||
},
|
||||
refetchQueries: ["searchForUser"],
|
||||
refetchQueries: ["searchForTeam"],
|
||||
})
|
||||
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!result.tournament_name) {
|
||||
setError("Tournament name is a required field")
|
||||
} else if (result.tournament_name.length > 100) {
|
||||
setError("Tournament name has to be 100 or less characters")
|
||||
} else if (result.tweet_id && isNaN(result.tweet_id as any)) {
|
||||
setError("Invalid Tweet ID")
|
||||
} else if (result.placement && result.placement > 500) {
|
||||
setError("Placement has to be between 1 and 500.")
|
||||
} else {
|
||||
setError(null)
|
||||
}
|
||||
}, [result])
|
||||
|
||||
const handleChange = (newValueObject: Partial<TournamentResult>) => {
|
||||
console.log("newV", newValueObject)
|
||||
setResult({ ...result, ...newValueObject })
|
||||
}
|
||||
|
||||
@@ -83,6 +103,27 @@ const AddResultModal: React.FC<AddResultModalProps> = ({ closeModal }) => {
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
<Box mt="1em">
|
||||
<PlacementInput
|
||||
value={result.placement}
|
||||
onChange={value => handleChange({ placement: value })}
|
||||
/>
|
||||
</Box>
|
||||
<Box mt="1em">
|
||||
<Button disabled={!!error} onClick={() => addResult()}>
|
||||
Submit
|
||||
</Button>
|
||||
<Box as="span" ml="0.5em">
|
||||
<Button outlined onClick={() => closeModal()}>
|
||||
Cancel
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
{error && (
|
||||
<Box mt="0.5em" color="red.500">
|
||||
{error}
|
||||
</Box>
|
||||
)}
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
33
frontend-react/src/components/team/PlacementInput.tsx
Normal file
33
frontend-react/src/components/team/PlacementInput.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import React, { useContext } from "react"
|
||||
import Label from "../elements/Label"
|
||||
import {
|
||||
NumberInput,
|
||||
NumberInputField,
|
||||
NumberInputStepper,
|
||||
NumberIncrementStepper,
|
||||
NumberDecrementStepper,
|
||||
} from "@chakra-ui/core"
|
||||
import MyThemeContext from "../../themeContext"
|
||||
|
||||
interface PlacementInputProps {
|
||||
value?: number
|
||||
onChange: (value: number) => void
|
||||
}
|
||||
|
||||
const PlacementInput: React.FC<PlacementInputProps> = ({ value, onChange }) => {
|
||||
const { themeColorHex } = useContext(MyThemeContext)
|
||||
return (
|
||||
<>
|
||||
<Label>Placement</Label>
|
||||
<NumberInput size="lg" defaultValue={1} min={1} max={500}>
|
||||
<NumberInputField focusBorderColor={themeColorHex} />
|
||||
<NumberInputStepper>
|
||||
<NumberIncrementStepper />
|
||||
<NumberDecrementStepper />
|
||||
</NumberInputStepper>
|
||||
</NumberInput>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default PlacementInput
|
||||
@@ -128,7 +128,7 @@ const BuildFormModal: React.FC<BuildFormModalProps> = ({
|
||||
title: "An error occurred",
|
||||
description: error.message,
|
||||
position: "top-right",
|
||||
status: "success",
|
||||
status: "error",
|
||||
duration: 10000,
|
||||
})
|
||||
},
|
||||
|
||||
@@ -79,7 +79,7 @@ const ProfileModal: React.FC<ProfileModalProps> = ({
|
||||
title: "An error occurred",
|
||||
description: error.message,
|
||||
position: "top-right",
|
||||
status: "success",
|
||||
status: "error",
|
||||
duration: 10000,
|
||||
})
|
||||
},
|
||||
|
||||
@@ -7,7 +7,7 @@ export const ADD_RESULT: DocumentNode = gql`
|
||||
$tournament_name: String!
|
||||
$placement: Int!
|
||||
) {
|
||||
updateUser(
|
||||
addResult(
|
||||
date: $date
|
||||
tweet_id: $tweet_id
|
||||
tournament_name: $tournament_name
|
||||
|
||||
@@ -87,12 +87,11 @@ const resolvers = {
|
||||
throw new UserInputError("Must be logged in")
|
||||
}
|
||||
|
||||
if (user.team.captain_discord_id !== user.discord_id) {
|
||||
//??
|
||||
throw new UserInputError("Must be a captain to add a result")
|
||||
}
|
||||
const team = await Team.findById(user.team)
|
||||
|
||||
if (user.team.tournament_results.length >= 100) {
|
||||
if (!team) throw new UserInputError("Not a team captain")
|
||||
|
||||
if (team.tournament_results.length > 100) {
|
||||
throw new UserInputError("Can't have more than 100 tournament results")
|
||||
}
|
||||
|
||||
@@ -100,7 +99,7 @@ const resolvers = {
|
||||
throw new UserInputError("Invalid date")
|
||||
}
|
||||
|
||||
if (args.tweet_id && !isNaN(args.tweet_id)) {
|
||||
if (args.tweet_id && isNaN(args.tweet_id)) {
|
||||
throw new UserInputError("Tweet ID can only contain numbers")
|
||||
}
|
||||
|
||||
@@ -117,8 +116,7 @@ const resolvers = {
|
||||
throw new UserInputError("Placement has to be between 1 and 500")
|
||||
}
|
||||
|
||||
const team = await Team.findById(user.team)
|
||||
team.push({
|
||||
team.tournament_results.push({
|
||||
date: args.date,
|
||||
tournament_name: args.tournament_name,
|
||||
placement: args.placement,
|
||||
|
||||
@@ -80,6 +80,7 @@ const resolvers = {
|
||||
if (process.env.LOGGED_IN) {
|
||||
return {
|
||||
id: "5cee8f73d1120d4315c55011",
|
||||
team: "5e3ecc0ea7dd3830303eaaa7",
|
||||
discord_id: "79237403620945920",
|
||||
__v: 0,
|
||||
avatar: "2e292c1b5d1366c24a9e4b6c1cffc700",
|
||||
|
||||
1
index.js
1
index.js
@@ -91,6 +91,7 @@ const server = new ApolloServer({
|
||||
return {
|
||||
user: {
|
||||
_id: "5cee8f73d1120d4315c55011",
|
||||
team: "5e3ecc0ea7dd3830303eaaa7",
|
||||
discord_id: "79237403620945920",
|
||||
__v: 0,
|
||||
avatar: "2e292c1b5d1366c24a9e4b6c1cffc700",
|
||||
|
||||
6
package-lock.json
generated
6
package-lock.json
generated
@@ -1656,9 +1656,9 @@
|
||||
}
|
||||
},
|
||||
"mongoose": {
|
||||
"version": "5.8.11",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.8.11.tgz",
|
||||
"integrity": "sha512-Yz0leNEJsAtNtMTxTDEadacLWt58gaVeBVL3c1Z3vaBoc159aJqlf+T8jaL9mAdBxKndF5YWhh6Q719xac7cjA==",
|
||||
"version": "5.8.12",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.8.12.tgz",
|
||||
"integrity": "sha512-PM6Ks4kIcsLiHcqskVaLj5vDE9l2PoW5foecIuUb5vIG22Gf2yCps31+U3I7Rw8UPnrnbmZvvrRgkHmZR2/Kbw==",
|
||||
"requires": {
|
||||
"bson": "~1.1.1",
|
||||
"kareem": "2.3.1",
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"express-session": "^1.17.0",
|
||||
"graphql": "^14.6.0",
|
||||
"lodash": "^4.17.15",
|
||||
"mongoose": "^5.8.11",
|
||||
"mongoose": "^5.8.12",
|
||||
"mongoose-unique-validator": "^2.0.3",
|
||||
"node-fetch": "^2.6.0",
|
||||
"passport": "^0.4.1",
|
||||
|
||||
Reference in New Issue
Block a user