editing and deleting builds

This commit is contained in:
Sendou 2020-02-05 21:34:09 +02:00
parent 6a479146f4
commit 09c99a4cbf
12 changed files with 149 additions and 58 deletions

View File

@ -1455,9 +1455,9 @@
"integrity": "sha512-a1USH7L3bEfDdPN4iNZGvMEFuBfkdG+QNybeyDv8RloVFgZYRoM+KGXyy2KOfEnTUM8QWDRSROwaL3+ts5Angg=="
},
"@reach/router": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@reach/router/-/router-1.3.0.tgz",
"integrity": "sha512-LLuwmgR1vW+nHdvZfJjp/3Dk3xP0H+GkH6XQWWpYrYwLDnEArZ9TR9YTkWPTLN8Ua5ZAkaTSRqACvRqplMNA8w==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@reach/router/-/router-1.3.1.tgz",
"integrity": "sha512-Ov1j1J+pSgXliJHFL7XWhjyREwc6GxeWfgBTa5MMH5eRmYtHbPhaovba4xKo7aTVCg8fxkt2yDMNSpvwfUP+pA==",
"requires": {
"create-react-context": "0.3.0",
"invariant": "^2.2.3",

View File

@ -7,7 +7,7 @@
"@chakra-ui/core": "^0.5.2",
"@emotion/core": "^10.0.27",
"@emotion/styled": "^10.0.27",
"@reach/router": "^1.3.0",
"@reach/router": "^1.3.1",
"@sendou/react-sketch": "^0.5.2",
"@testing-library/jest-dom": "^5.1.1",
"@testing-library/react": "^9.4.0",

View File

@ -10,7 +10,7 @@ import {
BoxProps,
} from "@chakra-ui/core"
import { Link } from "@reach/router"
import { FaInfo } from "react-icons/fa"
import { FaInfo, FaEdit } from "react-icons/fa"
import WeaponImage from "../common/WeaponImage"
import { top500 } from "../../assets/imageImports"
@ -25,12 +25,16 @@ interface BuildCardProps {
build: Build
defaultToAPView: boolean
showUser?: boolean
canModify?: boolean
setBuildBeingEdited?: (build: Build) => void
}
const BuildCard: React.FC<BuildCardProps & BoxProps> = ({
build,
defaultToAPView,
showUser = false,
canModify,
setBuildBeingEdited,
showUser,
...props
}) => {
const [apView, setApView] = useState(defaultToAPView)
@ -45,14 +49,14 @@ const BuildCard: React.FC<BuildCardProps & BoxProps> = ({
return (
<Box
as="fieldset"
display="block"
borderWidth="1px"
border={borderStyle}
w="300px"
rounded="lg"
overflow="hidden"
pt="2"
pb="6"
px="6"
//pb={showUser && build.discord_user ? "20px" : "15px"}
p="15px"
{...props}
>
{showUser && build.discord_user && (
@ -130,7 +134,20 @@ const BuildCard: React.FC<BuildCardProps & BoxProps> = ({
</PopoverContent>
</Popover>
) : (
<Box />
<Box w="24px" />
)}
{canModify && (
<IconButton
variant="ghost"
isRound
variantColor={themeColor}
onClick={
setBuildBeingEdited && (() => setBuildBeingEdited(build))
}
aria-label="Show description"
fontSize="20px"
icon={FaEdit}
/>
)}
<Button
variant="ghost"

View File

@ -10,7 +10,6 @@ import {
SearchForBuildsVars,
Build,
} from "../../types"
import useBreakPoints from "../../hooks/useBreakPoints"
import { Box, Flex, Heading, FormLabel, Switch, Button } from "@chakra-ui/core"
import { useContext } from "react"
import MyThemeContext from "../../themeContext"

View File

@ -4,14 +4,16 @@ import { IconType } from "react-icons/lib/cjs"
interface IconButtonProps {
icon: IconType
onClick: () => void
display?: string
onClick?: () => void
}
const IconButton: React.FC<IconButtonProps> = ({ icon, onClick }) => {
const IconButton: React.FC<IconButtonProps> = ({ icon, onClick, display }) => {
return (
<ChakraIconButton
aria-label=""
icon={icon}
display={display}
variant="ghost"
onClick={onClick}
size="lg"

View File

@ -1,12 +1,5 @@
import React, { useContext } from "react"
import ReactSelect, {
components,
OptionsType,
GroupedOptionsType,
ValueType,
OptionTypeBase,
createFilter,
} from "react-select"
import ReactSelect, { OptionsType, GroupedOptionsType } from "react-select"
import MyThemeContext from "../../themeContext"
import { SelectComponents } from "react-select/src/components"
import Box from "./Box"
@ -34,6 +27,7 @@ interface SelectProps {
}>
>
clearable?: boolean
initialValue?: string
}
const Select: React.FC<SelectProps> = ({
@ -44,6 +38,7 @@ const Select: React.FC<SelectProps> = ({
autoFocus,
label,
required,
initialValue,
width = "300px",
}) => {
const {

View File

@ -1,11 +1,11 @@
import React, { useState, useContext } from "react"
import { Box, Flex, IconButton, Link, Image } from "@chakra-ui/core"
import { Link, Image } from "@chakra-ui/core"
import { footerSquid, footerOcto } from "../../assets/imageImports"
import MyThemeContext from "../../themeContext"
import { FaTwitter, FaGithub, FaDiscord, FaTwitch } from "react-icons/fa"
import useBreakPoints from "../../hooks/useBreakPoints"
const ICON_SIZE = "lg"
import Box from "../elements/Box"
import IconButton from "../elements/IconButton"
const Footer: React.FC = () => {
const [footerBojoing] = useState(0.5 ? footerSquid : footerOcto)
@ -14,40 +14,32 @@ const Footer: React.FC = () => {
return (
<Box mt="2em">
<Flex alignItems="flex-end">
<Box display="flex" alignItems="flex-end">
<Link href="https://discord.gg/J6NqUvt" mx="1em">
<IconButton
variant="ghost"
icon={FaDiscord}
aria-label="Link to Discord"
size={ICON_SIZE}
display={isSmall ? "none" : undefined}
/>
</Link>
<Link href="https://twitter.com/sendouc" mx="1em">
<IconButton
variant="ghost"
icon={FaTwitter}
aria-label="Link to Twitter"
size={ICON_SIZE}
display={isSmall ? "none" : undefined}
/>
</Link>
<Link href="https://www.twitch.tv/sendou" mx="1em">
<IconButton
variant="ghost"
icon={FaTwitch}
aria-label="Link to Twitch"
size={ICON_SIZE}
display={isSmall ? "none" : undefined}
/>
</Link>
<Link href="https://github.com/Sendouc/sendou-ink" mx="1em">
<IconButton
variant="ghost"
icon={FaGithub}
aria-label="Link to Github"
size={ICON_SIZE}
display={isSmall ? "none" : undefined}
/>
</Link>
@ -60,8 +52,9 @@ const Footer: React.FC = () => {
mr="50px"
userSelect="none"
/>
</Flex>
<Flex
</Box>
<Box
display="flex"
bg={themeColorWithShade}
p="25px"
flexShrink={0}
@ -73,7 +66,8 @@ const Footer: React.FC = () => {
justifyContent={isSmall ? "center" : undefined}
>
{isSmall && (
<Flex
<Box
display="flex"
alignItems="center"
flexWrap="wrap"
justifyContent="center"
@ -91,7 +85,7 @@ const Footer: React.FC = () => {
<Link href="https://github.com/Sendouc/sendou-ink" m="1em">
<Box as={FaGithub} size="50px" />
</Link>
</Flex>
</Box>
)}
<Link
mx="2em"
@ -114,7 +108,7 @@ const Footer: React.FC = () => {
>
External links
</Link>
</Flex>
</Box>
</Box>
)
}

View File

@ -33,10 +33,13 @@ import Button from "../elements/Button"
import { useMutation } from "@apollo/react-hooks"
import { ADD_BUILD } from "../../graphql/mutations/addBuild"
import { useToast } from "@chakra-ui/core"
import { UPDATE_BUILD } from "../../graphql/mutations/updateBuild"
import { DELETE_BUILD } from "../../graphql/mutations/deleteBuild"
interface BuildFormModalProps {
existingGear: ExistingGearObject
closeModal: () => void
buildBeingEdited?: Build | null
}
type ExistingGearObject = Record<
@ -47,13 +50,18 @@ type ExistingGearObject = Record<
const BuildFormModal: React.FC<BuildFormModalProps> = ({
existingGear,
closeModal,
buildBeingEdited,
}) => {
const toast = useToast()
const [build, setBuild] = useState<Partial<Build>>({
headgear: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
clothing: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
shoes: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
})
const [build, setBuild] = useState<Partial<Build>>(
buildBeingEdited
? buildBeingEdited
: {
headgear: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
clothing: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
shoes: ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"],
}
)
const [addBuild] = useMutation<{ addBuild: Build }, Build>(ADD_BUILD, {
variables: { ...(build as Build) },
@ -64,7 +72,6 @@ const BuildFormModal: React.FC<BuildFormModalProps> = ({
position: "top-right",
status: "success",
duration: 10000,
isClosable: true,
})
},
onError: error => {
@ -74,7 +81,55 @@ const BuildFormModal: React.FC<BuildFormModalProps> = ({
position: "top-right",
status: "success",
duration: 10000,
isClosable: true,
})
},
refetchQueries: ["searchForBuilds"],
})
const [updateBuild] = useMutation<{ updateBuild: Build }, Build>(
UPDATE_BUILD,
{
variables: { ...(build as Build) },
onCompleted: () => {
closeModal()
toast({
description: "Build updated",
position: "top-right",
status: "success",
duration: 10000,
})
},
onError: error => {
toast({
title: "An error occurred",
description: error.message,
position: "top-right",
status: "success",
duration: 10000,
})
},
refetchQueries: ["searchForBuilds"],
}
)
const [deleteBuild] = useMutation<boolean, { id: string }>(DELETE_BUILD, {
variables: { id: build.id as string },
onCompleted: () => {
closeModal()
toast({
description: "Build deleted",
position: "top-right",
status: "success",
duration: 10000,
})
},
onError: error => {
toast({
title: "An error occurred",
description: error.message,
position: "top-right",
status: "success",
duration: 10000,
})
},
refetchQueries: ["searchForBuilds"],
@ -186,7 +241,21 @@ const BuildFormModal: React.FC<BuildFormModalProps> = ({
}
return (
<Modal title="Adding a new build" closeModal={() => closeModal()}>
<Modal
title={buildBeingEdited ? "Editing existing build" : "Adding a new build"}
closeModal={() => closeModal()}
>
{buildBeingEdited && (
<Box
color="red.500"
mb="1em"
textDecoration="underline"
cursor="pointer"
onClick={() => deleteBuild()}
>
Delete build
</Box>
)}
<WeaponSelector
required
label="Weapon"
@ -300,7 +369,10 @@ const BuildFormModal: React.FC<BuildFormModalProps> = ({
/>
</Box>
<Box mt="1em">
<Button disabled={!buildCanBeSubmitted()} onClick={() => addBuild()}>
<Button
disabled={!buildCanBeSubmitted()}
onClick={buildBeingEdited ? () => updateBuild() : () => addBuild()}
>
Submit
</Button>
<Box as="span" ml="0.5em">

View File

@ -8,6 +8,8 @@ import Button from "../elements/Button"
import Alert from "../elements/Alert"
import Box from "../elements/Box"
import BuildCard from "../builds/BuildCard"
import IconButton from "../elements/IconButton"
import { FaTrashAlt, FaEdit } from "react-icons/fa"
interface BuildTabProps {
builds: Build[]
@ -35,6 +37,7 @@ const buildsReducer = (acc: ExistingGearObject, cur: Build) => {
const BuildTab: React.FC<BuildTabProps> = ({ builds, canModifyBuilds }) => {
const [APView] = useLocalStorage<boolean>("prefersAPView")
const [formOpen, setFormOpen] = useState(false)
const [buildBeingEdited, setBuildBeingEdited] = useState<Build | null>(null)
const existingGear = builds
? builds.reduce(buildsReducer, {} as ExistingGearObject)
@ -45,7 +48,11 @@ const BuildTab: React.FC<BuildTabProps> = ({ builds, canModifyBuilds }) => {
{formOpen && (
<BuildFormModal
existingGear={existingGear}
closeModal={() => setFormOpen(false)}
closeModal={() => {
setFormOpen(false)
setBuildBeingEdited(null)
}}
buildBeingEdited={buildBeingEdited}
/>
)}
{canModifyBuilds && builds.length < 100 && (
@ -56,9 +63,14 @@ const BuildTab: React.FC<BuildTabProps> = ({ builds, canModifyBuilds }) => {
You already have 100 builds. Please delete one before adding a new one
</Alert>
)}
<Box asFlex flexWrap="wrap" justifyContent="center" mt="1em">
<Box display="flex" flexWrap="wrap" justifyContent="center" mt="1em">
{builds.map(build => (
<BuildCard
canModify={canModifyBuilds}
setBuildBeingEdited={(build: Build) => {
setBuildBeingEdited(build)
setFormOpen(true)
}}
key={build.id}
build={build}
defaultToAPView={APView !== null ? APView : false}

View File

@ -1,7 +0,0 @@
import { gql } from 'apollo-boost'
export const deleteBuild = gql`
mutation deleteBuild ($id: ID!) {
deleteBuild(id: $id)
}
`

View File

@ -0,0 +1,7 @@
import { gql, DocumentNode } from "apollo-boost"
export const DELETE_BUILD: DocumentNode = gql`
mutation deleteBuild($id: ID!) {
deleteBuild(id: $id)
}
`

View File

@ -1,6 +1,6 @@
import { gql } from "apollo-boost"
import { gql, DocumentNode } from "apollo-boost"
export const updateBuild = gql`
export const UPDATE_BUILD: DocumentNode = gql`
mutation updateBuild(
$id: ID!
$weapon: String!