mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-07-30 23:56:50 -05:00
editing and deleting builds
This commit is contained in:
parent
6a479146f4
commit
09c99a4cbf
6
frontend-react/package-lock.json
generated
6
frontend-react/package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
import { gql } from 'apollo-boost'
|
||||
|
||||
export const deleteBuild = gql`
|
||||
mutation deleteBuild ($id: ID!) {
|
||||
deleteBuild(id: $id)
|
||||
}
|
||||
`
|
||||
7
frontend-react/src/graphql/mutations/deleteBuild.ts
Normal file
7
frontend-react/src/graphql/mutations/deleteBuild.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { gql, DocumentNode } from "apollo-boost"
|
||||
|
||||
export const DELETE_BUILD: DocumentNode = gql`
|
||||
mutation deleteBuild($id: ID!) {
|
||||
deleteBuild(id: $id)
|
||||
}
|
||||
`
|
||||
|
|
@ -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!
|
||||
Loading…
Reference in New Issue
Block a user