From 6ef73583df4cf5c11a61ea35e63a442e5d884d36 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Wed, 30 Nov 2022 05:20:22 -0800 Subject: [PATCH] Implement job skill search --- components/CharacterGrid/index.tsx | 2 +- components/JobSection/index.tsx | 58 ++++- components/JobSkillItem/index.scss | 4 +- components/JobSkillItem/index.tsx | 66 ++++-- components/JobSkillModal/index.scss | 16 -- components/JobSkillModal/index.tsx | 203 ------------------ components/JobSkillSearchFilterBar/index.scss | 3 + components/JobSkillSearchFilterBar/index.tsx | 71 ++++++ components/SearchModal/index.tsx | 55 +++-- 9 files changed, 219 insertions(+), 259 deletions(-) delete mode 100644 components/JobSkillModal/index.scss delete mode 100644 components/JobSkillModal/index.tsx create mode 100644 components/JobSkillSearchFilterBar/index.scss create mode 100644 components/JobSkillSearchFilterBar/index.tsx diff --git a/components/CharacterGrid/index.tsx b/components/CharacterGrid/index.tsx index af9b4721..6f7f7c8a 100644 --- a/components/CharacterGrid/index.tsx +++ b/components/CharacterGrid/index.tsx @@ -254,7 +254,7 @@ const CharacterGrid = (props: Props) => { return (
- + { + const { t } = useTranslation("common") + const [job, setJob] = useState() const [imageUrl, setImageUrl] = useState("") @@ -20,9 +28,11 @@ const JobSection = (props: Props) => { const [numSkills, setNumSkills] = useState(4) const [skills, setSkills] = useState([]) + const [skillRefs, setSkillRefs] = useState[]>([]) + useEffect(() => { // Set current job based on ID - setJob(party.job) + if (party.job) setJob(party.job) }, []) useEffect(() => { @@ -33,6 +43,14 @@ const JobSection = (props: Props) => { if (job) appState.party.job = job }, [job]) + useEffect(() => { + setSkillRefs(Array(numSkills).fill(React.createRef())) + }, [numSkills]) + + useEffect(() => { + console.log(skillRefs) + }, [skillRefs]) + function receiveJob(job?: Job) { console.log(`Receiving job! Row ${job?.row}: ${job?.name.en}`) if (job) { @@ -62,6 +80,34 @@ const JobSection = (props: Props) => { setImageUrl(imgSrc) } + const skillItem = (index: number, editable: boolean) => { + return ( + + ) + } + + const editableSkillItem = (index: number) => { + return ( + + {skillItem(index, true)} + + ) + } + + function updateObject(object: SearchableObject, position: number) {} + // Render: JSX components return (
@@ -76,8 +122,10 @@ const JobSection = (props: Props) => { />
    {[...Array(numSkills)].map((e, i) => ( -
  • - +
  • + {job && job.id != "-1" && !skills[i]?.main && props.editable + ? editableSkillItem(i) + : skillItem(i, false)}
  • ))}
diff --git a/components/JobSkillItem/index.scss b/components/JobSkillItem/index.scss index 55cd1ea5..539beb60 100644 --- a/components/JobSkillItem/index.scss +++ b/components/JobSkillItem/index.scss @@ -6,8 +6,8 @@ &.editable:hover { cursor: pointer; - & > img, - & > div.placeholder { + & > img.editable, + & > div.placeholder.editable { border: $hover-stroke; box-shadow: $hover-shadow; cursor: pointer; diff --git a/components/JobSkillItem/index.tsx b/components/JobSkillItem/index.tsx index b1238863..02f2db1f 100644 --- a/components/JobSkillItem/index.tsx +++ b/components/JobSkillItem/index.tsx @@ -12,9 +12,10 @@ import "./index.scss" interface Props { skill?: JobSkill editable: boolean + hasJob: boolean } -const JobSkillItem = (props: Props) => { +const JobSkillItem = React.forwardRef((props, ref) => { const router = useRouter() const { t } = useTranslation("common") const locale = @@ -25,28 +26,57 @@ const JobSkillItem = (props: Props) => { editable: props.editable, }) - return ( -
- {props.skill ? ( + const imageClasses = classNames({ + placeholder: !props.skill, + editable: props.editable && props.hasJob, + }) + + const skillImage = () => { + let jsx: React.ReactNode + + if (props.skill) { + jsx = ( {props.skill.name[locale]} - ) : ( -
- + ) + } else { + jsx = ( +
+ {props.editable && props.hasJob ? : ""}
- )} -
- {/* {props.skill ?
Grouping
: ""} */} - {props.skill ? ( -

{props.skill.name[locale]}

- ) : ( -

Select a skill

- )} + ) + } + + return jsx + } + + const label = () => { + let jsx: React.ReactNode + + if (props.skill) { + jsx =

{props.skill.name[locale]}

+ } else if (props.editable && props.hasJob) { + jsx =

Select a skill

+ } else { + jsx =

No skill

+ } + + return jsx + } + + const skillItem = () => { + return ( +
+ {skillImage()} + {label()}
-
- ) -} + ) + } + + return skillItem() +}) export default JobSkillItem diff --git a/components/JobSkillModal/index.scss b/components/JobSkillModal/index.scss deleted file mode 100644 index 136f26b8..00000000 --- a/components/JobSkillModal/index.scss +++ /dev/null @@ -1,16 +0,0 @@ -#Header #Bar select { - background-color: $grey-90; -} -#Header > label { - margin: 0 $unit * 3; - - .Input { - border: 1px solid $grey-80; - border-radius: calc($unit / 1.5); - box-sizing: border-box; - font-size: $font-regular; - padding: $unit * 1.5; - text-align: left; - width: 100%; - } -} diff --git a/components/JobSkillModal/index.tsx b/components/JobSkillModal/index.tsx deleted file mode 100644 index 415ff74f..00000000 --- a/components/JobSkillModal/index.tsx +++ /dev/null @@ -1,203 +0,0 @@ -import React, { useEffect, useState } from "react" -import { getCookie, setCookie } from "cookies-next" -import { useRouter } from "next/router" -import { useSnapshot } from "valtio" -import { useTranslation } from "react-i18next" -import InfiniteScroll from "react-infinite-scroll-component" - -import { appState } from "~utils/appState" -import { skillGroups } from "~utils/skillGroups" - -import * as Dialog from "@radix-ui/react-dialog" -import JobSkillResult from "~components/JobSkillResult" - -import CrossIcon from "~public/icons/Cross.svg" - -import "./index.scss" - -interface Props { - send: (skill: JobSkill, position: number) => any - job?: Job - fromPosition: number - children: React.ReactNode -} - -const JobSkillModal = (props: Props) => { - // Set up router - const router = useRouter() - const locale = router.locale - - // Set up translation - const { t } = useTranslation("common") - - let searchInput = React.createRef() - let scrollContainer = React.createRef() - - const [currentGroup, setCurrentGroup] = useState(-1) - const [currentGroupName, setCurrentGroupName] = useState("") - const [open, setOpen] = useState(false) - const [query, setQuery] = useState("") - const [results, setResults] = useState([]) - - // Pagination states - const [recordCount, setRecordCount] = useState(0) - const [currentPage, setCurrentPage] = useState(1) - const [totalPages, setTotalPages] = useState(1) - - useEffect(() => { - setResults(appState.jobSkills.filter((skill) => skill.main === false)) - setRecordCount( - appState.jobSkills.filter((skill) => skill.main === false).length - ) - }, [appState, setResults]) - - useEffect(() => { - if (searchInput.current) searchInput.current.focus() - }, [searchInput]) - - useEffect(() => { - setRecordCount(results.length) - }, [results]) - - useEffect(() => { - const name = skillGroups - .find((skill) => skill.id === currentGroup) - ?.name["en"].toLowerCase() - setCurrentGroupName(name ? name : "") - }, [currentGroup]) - - function onChange(event: React.ChangeEvent) { - const newValue = parseInt(event.target.value) - setCurrentGroup(newValue) - - if (newValue >= 0) { - setResults( - appState.jobSkills.filter((skill, i) => { - if (newValue === 4) { - return skill.emp && !skill.main - } else if (newValue === 5) { - return skill.base && !skill.main - } else { - return skill.color === newValue && !skill.main - } - }) - ) - } else { - setResults(appState.jobSkills.filter((skill) => skill.main === false)) - } - } - - function inputChanged(event: React.ChangeEvent) { - const text = event.target.value - if (text.length) { - setQuery(text) - } else { - setQuery("") - } - } - - function openChange() { - if (open) { - setQuery("") - // setFirstLoad(true) - setResults([]) - setRecordCount(0) - setCurrentPage(1) - setOpen(false) - } else { - setOpen(true) - } - } - - function onBlur() {} - - function render() { - const rows = results.map((result: JobSkill, i: number) => { - return ( - {}} /> - ) - }) - return ( - 0 ? results.length : 0} - next={() => setCurrentPage(currentPage + 1)} - hasMore={totalPages > currentPage} - scrollableTarget="Results" - loader={
Loading...
} - > - {rows} -
- ) - } - return ( - - {props.children} - - - - -
-
- {t("search.result_count", { record_count: recordCount })} -
- {open ? render() : ""} -
-
- -
-
- ) -} - -export default JobSkillModal diff --git a/components/JobSkillSearchFilterBar/index.scss b/components/JobSkillSearchFilterBar/index.scss new file mode 100644 index 00000000..6ccb5e2e --- /dev/null +++ b/components/JobSkillSearchFilterBar/index.scss @@ -0,0 +1,3 @@ +.SearchFilterBar select { + background-color: $grey-90; +} diff --git a/components/JobSkillSearchFilterBar/index.tsx b/components/JobSkillSearchFilterBar/index.tsx new file mode 100644 index 00000000..1de78add --- /dev/null +++ b/components/JobSkillSearchFilterBar/index.tsx @@ -0,0 +1,71 @@ +import React, { useEffect, useState } from "react" +import { useRouter } from "next/router" +import { useTranslation } from "react-i18next" + +import { skillGroups } from "~utils/skillGroups" + +import "./index.scss" + +interface Props { + sendFilters: (filters: { [key: string]: number }) => void +} + +const JobSkillSearchFilterBar = (props: Props) => { + // Set up translation + const { t } = useTranslation("common") + + const [currentGroup, setCurrentGroup] = useState(-1) + + function onChange(event: React.ChangeEvent) { + setCurrentGroup(parseInt(event.target.value)) + } + + function onBlur(event: React.ChangeEvent) {} + + function sendFilters() { + const filters = { + group: currentGroup, + } + + props.sendFilters(filters) + } + + useEffect(() => { + sendFilters() + }, [currentGroup]) + + return ( +
+ +
+ ) +} + +export default JobSkillSearchFilterBar diff --git a/components/SearchModal/index.tsx b/components/SearchModal/index.tsx index e049a33e..317ab5eb 100644 --- a/components/SearchModal/index.tsx +++ b/components/SearchModal/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useState } from "react" +import React, { useEffect, useState } from "react" import { getCookie, setCookie } from "cookies-next" import { useRouter } from "next/router" import { useSnapshot } from "valtio" @@ -13,10 +13,13 @@ import * as Dialog from "@radix-ui/react-dialog" import CharacterSearchFilterBar from "~components/CharacterSearchFilterBar" import WeaponSearchFilterBar from "~components/WeaponSearchFilterBar" import SummonSearchFilterBar from "~components/SummonSearchFilterBar" +import JobSkillSearchFilterBar from "~components/JobSkillSearchFilterBar" import CharacterResult from "~components/CharacterResult" import WeaponResult from "~components/WeaponResult" import SummonResult from "~components/SummonResult" +import JobSkillResult from "~components/JobSkillResult" + import type { SearchableObject, SearchableObjectArray } from "~types" import "./index.scss" @@ -27,14 +30,12 @@ interface Props { send: (object: SearchableObject, position: number) => any placeholderText: string fromPosition: number - object: "weapons" | "characters" | "summons" + job?: Job + object: "weapons" | "characters" | "summons" | "job_skills" children: React.ReactNode } const SearchModal = (props: Props) => { - // Set up snapshot of app state - let { grid, search } = useSnapshot(appState) - // Set up router const router = useRouter() const locale = router.locale @@ -46,10 +47,7 @@ const SearchModal = (props: Props) => { let scrollContainer = React.createRef() const [firstLoad, setFirstLoad] = useState(true) - const [objects, setObjects] = useState<{ - [id: number]: GridCharacter | GridWeapon | GridSummon | undefined - }>() - const [filters, setFilters] = useState<{ [key: string]: number[] }>() + const [filters, setFilters] = useState<{ [key: string]: any }>() const [open, setOpen] = useState(false) const [query, setQuery] = useState("") const [results, setResults] = useState([]) @@ -59,10 +57,6 @@ const SearchModal = (props: Props) => { const [currentPage, setCurrentPage] = useState(1) const [totalPages, setTotalPages] = useState(1) - useEffect(() => { - setObjects(grid[props.object]) - }, [grid, props.object]) - useEffect(() => { if (searchInput.current) searchInput.current.focus() }, [searchInput]) @@ -77,15 +71,19 @@ const SearchModal = (props: Props) => { } function fetchResults({ replace = false }: { replace?: boolean }) { + console.log("Fetch results!!!") api .search({ object: props.object, query: query, + job: props.job?.id, filters: filters, locale: locale, page: currentPage, }) .then((response) => { + console.log("resp") + console.log(response) setTotalPages(response.data.total_pages) setRecordCount(response.data.count) @@ -152,7 +150,7 @@ const SearchModal = (props: Props) => { openChange() } - function receiveFilters(filters: { [key: string]: number[] }) { + function receiveFilters(filters: { [key: string]: any }) { setCurrentPage(1) setResults([]) setFilters(filters) @@ -208,6 +206,9 @@ const SearchModal = (props: Props) => { case "characters": jsx = renderCharacterSearchResults(results) break + case "job_skills": + jsx = renderJobSkillSearchResults(results) + break } return ( @@ -286,6 +287,27 @@ const SearchModal = (props: Props) => { return jsx } + function renderJobSkillSearchResults(results: { [key: string]: any }) { + let jsx: React.ReactNode + + const castResults: JobSkill[] = results as JobSkill[] + if (castResults && Object.keys(castResults).length > 0) { + jsx = castResults.map((result: JobSkill) => { + return ( + { + storeRecentResult(result) + }} + /> + ) + }) + } + + return jsx + } + function openChange() { if (open) { setQuery("") @@ -338,6 +360,11 @@ const SearchModal = (props: Props) => { ) : ( "" )} + {props.object === "job_skills" ? ( + + ) : ( + "" + )}