diff --git a/frontend-react/src/calendar/CalendarPage.tsx b/frontend-react/src/calendar/CalendarPage.tsx index eaafc4656..ceb0bc83a 100644 --- a/frontend-react/src/calendar/CalendarPage.tsx +++ b/frontend-react/src/calendar/CalendarPage.tsx @@ -1,7 +1,6 @@ import React, { useState, useEffect, useRef } from "react" import jstz from "jstz" import { - Button, Popover, PopoverTrigger, PopoverContent, @@ -17,6 +16,7 @@ import { useContext } from "react" import MyThemeContext from "../themeContext" import PageHeader from "../components/common/PageHeader" import { Helmet } from "react-helmet-async" +import Button from "../components/elements/Button" const CalendarPage: React.FC = () => { const { @@ -46,14 +46,11 @@ const CalendarPage: React.FC = () => { - + + + diff --git a/frontend-react/src/components/builds/AbilitySelector.tsx b/frontend-react/src/components/builds/AbilitySelector.tsx index dce0edda3..5fd7b293e 100644 --- a/frontend-react/src/components/builds/AbilitySelector.tsx +++ b/frontend-react/src/components/builds/AbilitySelector.tsx @@ -1,11 +1,12 @@ import React, { useState } from "react" import FieldsetWithLegend from "../common/FieldsetWithLegend" -import { Flex, Box, Button } from "@chakra-ui/core" +import { Flex, Box } from "@chakra-ui/core" import { abilitiesGameOrder } from "../../utils/lists" import AbilityIcon from "./AbilityIcon" import { Ability } from "../../types" import { useContext } from "react" import MyThemeContext from "../../themeContext" +import Button from "../elements/Button" interface AbilitySelectorProps { abilities: Ability[] @@ -21,7 +22,6 @@ const AbilitySelector: React.FC = ({ return show ? ( <> + ) } diff --git a/frontend-react/src/components/builds/BuildsPage.tsx b/frontend-react/src/components/builds/BuildsPage.tsx index efe7e0485..e1d7ec95e 100644 --- a/frontend-react/src/components/builds/BuildsPage.tsx +++ b/frontend-react/src/components/builds/BuildsPage.tsx @@ -11,16 +11,7 @@ import { Build, } from "../../types" import useBreakPoints from "../../hooks/useBreakPoints" -import { - Box, - Flex, - Heading, - FormLabel, - Switch, - Button, - Alert, - AlertIcon, -} from "@chakra-ui/core" +import { Box, Flex, Heading, FormLabel, Switch, Button } from "@chakra-ui/core" import { useContext } from "react" import MyThemeContext from "../../themeContext" import useLocalStorage from "@rehooks/local-storage" @@ -32,6 +23,7 @@ import BuildCard from "./BuildCard" import InfiniteScroll from "react-infinite-scroller" import PageHeader from "../common/PageHeader" import AbilitySelector from "./AbilitySelector" +import Alert from "../elements/Alert" const BuildsPage: React.FC = () => { const { themeColor } = useContext(MyThemeContext) @@ -126,10 +118,7 @@ const BuildsPage: React.FC = () => { )} {weapon && buildsFilterByAbilities.length === 0 && ( - - - No builds found with the current selection. Please adjust it above. - + No builds found with the current filter )} ) diff --git a/frontend-react/src/components/common/Error.tsx b/frontend-react/src/components/common/Error.tsx index f982a63f3..b13a24de0 100644 --- a/frontend-react/src/components/common/Error.tsx +++ b/frontend-react/src/components/common/Error.tsx @@ -1,17 +1,12 @@ import React from "react" -import { Alert, AlertIcon } from "@chakra-ui/core" +import Alert from "../elements/Alert" interface ErrorProps { errorMessage: string } const Error: React.FC = ({ errorMessage }) => { - return ( - - - {errorMessage} - - ) + return {errorMessage} } export default Error diff --git a/frontend-react/src/components/common/WeaponSelector.tsx b/frontend-react/src/components/common/WeaponSelector.tsx index 430a64852..ec15403a8 100644 --- a/frontend-react/src/components/common/WeaponSelector.tsx +++ b/frontend-react/src/components/common/WeaponSelector.tsx @@ -7,7 +7,9 @@ import Select from "../elements/Select" interface WeaponSelectorProps { setValue: (value: string) => void + label?: string autoFocus?: boolean + clearable?: boolean } const singleOption = (props: any) => ( @@ -23,19 +25,29 @@ const singleOption = (props: any) => ( const WeaponSelector: React.FC = ({ setValue, - autoFocus = false, + label, + clearable, + autoFocus, }) => { return ( - null, + Option: singleOption, + }} + autoFocus={autoFocus} + /> + ) } diff --git a/frontend-react/src/components/elements/Alert.tsx b/frontend-react/src/components/elements/Alert.tsx new file mode 100644 index 000000000..23e6f8ed1 --- /dev/null +++ b/frontend-react/src/components/elements/Alert.tsx @@ -0,0 +1,18 @@ +import React from "react" +import { Alert as ChakraAlert, AlertIcon } from "@chakra-ui/core" + +interface AlertProps { + children: string | string[] + status: "error" | "success" | "warning" | "info" +} + +const Alert: React.FC = ({ children, status }) => { + return ( + + + {children} + + ) +} + +export default Alert diff --git a/frontend-react/src/components/elements/Box.tsx b/frontend-react/src/components/elements/Box.tsx new file mode 100644 index 000000000..73431cd66 --- /dev/null +++ b/frontend-react/src/components/elements/Box.tsx @@ -0,0 +1,12 @@ +import React from "react" +import { Box as ChakraBox, BoxProps } from "@chakra-ui/core" + +interface MyBoxProps { + children: JSX.Element | JSX.Element[] +} + +const Box: React.FC = ({ children, ...props }) => { + return {children} +} + +export default Box diff --git a/frontend-react/src/components/elements/Button.tsx b/frontend-react/src/components/elements/Button.tsx new file mode 100644 index 000000000..a2bc895d2 --- /dev/null +++ b/frontend-react/src/components/elements/Button.tsx @@ -0,0 +1,39 @@ +import React from "react" +import { Button as ChakraButton } from "@chakra-ui/core" +import { useContext } from "react" +import MyThemeContext from "../../themeContext" +import { IconType } from "react-icons/lib/cjs" + +interface ButtonProps { + children: string | string[] + onClick: () => void + size?: "xs" | "sm" | "lg" | "md" + icon?: IconType + outlined?: boolean + disabled?: boolean +} + +const Button: React.FC = ({ + children, + onClick, + icon, + size, + disabled, + outlined = false, +}) => { + const { themeColor } = useContext(MyThemeContext) + return ( + + {children} + + ) +} + +export default Button diff --git a/frontend-react/src/components/elements/RadioGroup.tsx b/frontend-react/src/components/elements/RadioGroup.tsx new file mode 100644 index 000000000..ec26c3733 --- /dev/null +++ b/frontend-react/src/components/elements/RadioGroup.tsx @@ -0,0 +1,48 @@ +import React, { useContext } from "react" +import { RadioGroup as ChakraRadioGroup, Radio } from "@chakra-ui/core" +import MyThemeContext from "../../themeContext" +import Box from "./Box" + +interface RadioGroupProps { + options: string[] + value: string + label?: string + setValue: (value: any) => void +} + +const RadioGroup: React.FC = ({ + value, + setValue, + options, + label, +}) => { + const { themeColor } = useContext(MyThemeContext) + return ( + <> + {label && ( + + {label} + + )} + setValue(e.target.value)} + value={value} + > + {options.map(option => ( + + {option} + + ))} + + + ) +} + +export default RadioGroup diff --git a/frontend-react/src/components/elements/Select.tsx b/frontend-react/src/components/elements/Select.tsx index 4e6d3b6b4..741a51310 100644 --- a/frontend-react/src/components/elements/Select.tsx +++ b/frontend-react/src/components/elements/Select.tsx @@ -29,6 +29,7 @@ interface SelectProps { value: string }> > + clearable?: boolean } const Select: React.FC = ({ @@ -36,7 +37,8 @@ const Select: React.FC = ({ components, placeholder, setValue, - autoFocus = false, + clearable, + autoFocus, }) => { const { colorMode, @@ -46,7 +48,7 @@ const Select: React.FC = ({ } = useContext(MyThemeContext) const handleChange = (selectedOption: any) => { - setValue(selectedOption.value) + setValue(selectedOption?.value) } return ( @@ -56,6 +58,7 @@ const Select: React.FC = ({ onChange={handleChange} placeholder={placeholder} isSearchable + isClearable={clearable} options={options} components={{ IndicatorSeparator: () => null, diff --git a/frontend-react/src/components/freeagents/FreeAgentsPage.tsx b/frontend-react/src/components/freeagents/FreeAgentsPage.tsx index 727b0f092..829f32971 100644 --- a/frontend-react/src/components/freeagents/FreeAgentsPage.tsx +++ b/frontend-react/src/components/freeagents/FreeAgentsPage.tsx @@ -1,7 +1,12 @@ import React, { useState } from "react" import { useQuery } from "@apollo/react-hooks" import { USER } from "../../graphql/queries/user" -import { UserData, FreeAgentPostsData, Weapon } from "../../types" +import { + UserData, + FreeAgentPostsData, + Weapon, + FreeAgentPost, +} from "../../types" import { FREE_AGENT_POSTS } from "../../graphql/queries/freeAgentPosts" import Loading from "../common/Loading" import Error from "../common/Error" @@ -10,9 +15,27 @@ import PostsAccordion from "./PostsAccordion" import PageHeader from "../common/PageHeader" import { Helmet } from "react-helmet-async" import WeaponSelector from "../common/WeaponSelector" +import RadioGroup from "../elements/RadioGroup" +import Box from "../elements/Box" +import { continents } from "../../utils/lists" + +/*can_vc: "YES" | "USUALLY" | "SOMETIMES" | "NO" + playstyles: ("FRONTLINE" | "MIDLINE" | "BACKLINE")[]*/ + +const playstyleToEnum = { + "Frontline/Slayer": "FRONTLINE", + "Midline/Support": "MIDLINE", + "Backline/Anchor": "BACKLINE", +} as const const FreeAgentsPage: React.FC = () => { const [weapon, setWeapon] = useState(null) + const [playstyle, setPlaystyle] = useState< + "Any" | "Frontline/Slayer" | "Midline/Support" | "Backline/Anchor" + >("Any") + const [region, setRegion] = useState< + "Any" | "Europe" | "The Americas" | "Oceania" | "Other" + >("Any") const { data, error, loading } = useQuery( FREE_AGENT_POSTS ) @@ -28,17 +51,83 @@ const FreeAgentsPage: React.FC = () => { const faPosts = data.freeAgentPosts + const postsFilter = (post: FreeAgentPost) => { + if (post.hidden) return false + + if (weapon && post.discord_user.weapons.indexOf(weapon) === -1) { + return false + } + + if (playstyle !== "Any") { + if (post.playstyles.indexOf(playstyleToEnum[playstyle]) === -1) + return false + } + + if (region !== "Any") { + if (!post.discord_user.country) { + if (region === "Other") return true + + return false + } + + const continentCode = continents[post.discord_user.country] + + if (region === "Europe" && continentCode !== "EU") return false + else if ( + region === "The Americas" && + continentCode !== "NA" && + continentCode !== "SA" + ) + return false + else if (region === "Oceania" && continentCode !== "OC") return false + else if ( + region === "Other" && + continentCode !== "AF" && + continentCode !== "AN" && + continentCode !== "AS" && + continentCode !== "OC" + ) + return false + } + + return true + } + return ( <> Free Agents | sendou.ink - setWeapon(weapon as Weapon)} - autoFocus - /> - + + + + + + + + setWeapon(weapon as Weapon)} + clearable + /> + + ) } diff --git a/frontend-react/src/components/freeagents/PostsAccordion.tsx b/frontend-react/src/components/freeagents/PostsAccordion.tsx index 62ba530bc..5494ae917 100644 --- a/frontend-react/src/components/freeagents/PostsAccordion.tsx +++ b/frontend-react/src/components/freeagents/PostsAccordion.tsx @@ -21,6 +21,7 @@ import { FaTwitter } from "react-icons/fa" import WeaponImage from "../common/WeaponImage" import RoleIcons from "./RoleIcons" import VCIcon from "./VCIcon" +import Alert from "../elements/Alert" interface PostsAccordionProps { posts: FreeAgentPost[] @@ -37,125 +38,126 @@ const hasExtraInfo = (post: FreeAgentPost) => { const PostsAccordion: React.FC = ({ posts }) => { const { darkerBgColor } = useContext(MyThemeContext) + + if (posts.length === 0) { + return ( + No free agents found with the current filter + ) + } return ( - {posts - .filter(post => !post.hidden) - .map(post => { - const { discord_user } = post - const canBeExpanded = hasExtraInfo(post) - return ( - - - {canBeExpanded ? ( - - ) : ( - - )} - - - - - - {discord_user.username}#{discord_user.discriminator} - - - - - {discord_user.country && ( - <> - - { - countries.find( - obj => obj.code === discord_user.country - )?.name - } - - )} - - - {discord_user.twitter_name && ( - <> - - - {discord_user.twitter_name} - - - )} - - - {new Date(parseInt(post.createdAt)).toLocaleDateString()} - - - {discord_user?.weapons && - discord_user.weapons.map(wpn => ( - - - - ))} - - - - - - {canBeExpanded && ( - - {post.activity && ( - - Activity - {post.activity} - - )} - {post.looking_for && ( - - Looking for - {post.looking_for} - - )} - {post.past_experience && ( - - Past experience - {post.past_experience} - - )} - {post.description && ( - - Description - {post.description} - - )} - + {posts.map(post => { + const { discord_user } = post + const canBeExpanded = hasExtraInfo(post) + return ( + + + {canBeExpanded ? ( + + ) : ( + )} - - ) - })} + + + + + + {discord_user.username}#{discord_user.discriminator} + + + + + {discord_user.country && ( + <> + + { + countries.find(obj => obj.code === discord_user.country) + ?.name + } + + )} + + + {discord_user.twitter_name && ( + <> + + + {discord_user.twitter_name} + + + )} + + + {new Date(parseInt(post.createdAt)).toLocaleDateString()} + + + {discord_user?.weapons && + discord_user.weapons.map(wpn => ( + + + + ))} + + + + + + {canBeExpanded && ( + + {post.activity && ( + + Activity + {post.activity} + + )} + {post.looking_for && ( + + Looking for + {post.looking_for} + + )} + {post.past_experience && ( + + Past experience + {post.past_experience} + + )} + {post.description && ( + + Description + {post.description} + + )} + + )} + + ) + })} ) } diff --git a/frontend-react/src/components/home/HomePage.tsx b/frontend-react/src/components/home/HomePage.tsx index 032454be7..7990d27fa 100644 --- a/frontend-react/src/components/home/HomePage.tsx +++ b/frontend-react/src/components/home/HomePage.tsx @@ -6,6 +6,7 @@ import { useContext } from "react" import MyThemeContext from "../../themeContext" import "./HomePage.css" import { Helmet } from "react-helmet-async" +import Alert from "../elements/Alert" const HomePage: React.FC = () => { const { colorMode, grayWithShade } = useContext(MyThemeContext) diff --git a/frontend-react/src/components/plans/MapPlannerPage.tsx b/frontend-react/src/components/plans/MapPlannerPage.tsx index 0afa44803..5fde51205 100644 --- a/frontend-react/src/components/plans/MapPlannerPage.tsx +++ b/frontend-react/src/components/plans/MapPlannerPage.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useRef, useContext } from "react" +import React, { useState, useEffect, useRef } from "react" import { SketchField, Tools } from "@sendou/react-sketch" import { CirclePicker } from "react-color" import weaponDict from "../../utils/english_internal.json" @@ -9,20 +9,19 @@ import { Helmet } from "react-helmet-async" import { Weapon } from "../../types" import Error from "../common/Error" import { - Button, Flex, Box, InputGroup, Input, InputRightElement, } from "@chakra-ui/core" -import MyThemeContext from "../../themeContext" import { FaFileDownload, FaFileUpload, FaFileImage } from "react-icons/fa" import reef from "../../assets/plannerMaps/reef-sz.png" import MapSelect from "./MapSelect" import { RouteComponentProps } from "@reach/router" import PageHeader from "../common/PageHeader" import DraggableWeaponSelector from "./DraggableWeaponSelector" +import Button from "../elements/Button" const MapPlannerPage: React.FC = () => { let sketch: any = null @@ -53,7 +52,6 @@ const MapPlannerPage: React.FC = () => { enableCopyPaste: false, } const fileInput = useRef(null) - const { themeColor } = useContext(MyThemeContext) const [tool, setTool] = useState(Tools.Pencil) const [color, setColor] = useState("#f44336") const [canUndo, setCanUndo] = useState(false) @@ -202,9 +200,8 @@ const MapPlannerPage: React.FC = () => { @@ -216,18 +213,12 @@ const MapPlannerPage: React.FC = () => { "json" ) } - leftIcon={FaFileDownload} - variantColor={themeColor} - variant="outline" + icon={FaFileDownload} + outlined > Download as .json - @@ -244,10 +235,9 @@ const MapPlannerPage: React.FC = () => { /> diff --git a/frontend-react/src/types.ts b/frontend-react/src/types.ts index fd1ece2ee..1090a86e4 100644 --- a/frontend-react/src/types.ts +++ b/frontend-react/src/types.ts @@ -72,7 +72,7 @@ export interface User { twitch_name?: string twitter_name?: string country?: CountryCode - weapons?: Weapon[] + weapons: Weapon[] top500: boolean custom_url?: string sens?: { @@ -143,7 +143,7 @@ export interface FreeAgentPost { discord_id: string twitter_name?: string country?: CountryCode - weapons?: Weapon[] + weapons: Weapon[] top500: boolean } } diff --git a/graphql-schemas/user.js b/graphql-schemas/user.js index 6dfbdc984..c40df402b 100644 --- a/graphql-schemas/user.js +++ b/graphql-schemas/user.js @@ -42,7 +42,7 @@ const typeDef = gql` twitter_name: String country: String sens: Sens - weapons: [String]! + weapons: [String!]! custom_url: String top500: Boolean! }