Implement job skill search
This commit is contained in:
parent
ee7dc0bc4a
commit
6ef73583df
9 changed files with 219 additions and 259 deletions
|
|
@ -254,7 +254,7 @@ const CharacterGrid = (props: Props) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div id="CharacterGrid">
|
<div id="CharacterGrid">
|
||||||
<JobSection />
|
<JobSection editable={party.editable} />
|
||||||
<CharacterConflictModal
|
<CharacterConflictModal
|
||||||
open={modalOpen}
|
open={modalOpen}
|
||||||
incomingCharacter={incoming}
|
incomingCharacter={incoming}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,25 @@
|
||||||
import React, { useEffect, useState } from "react"
|
import React, { ForwardedRef, useEffect, useState } from "react"
|
||||||
|
import { useTranslation } from "next-i18next"
|
||||||
import { useSnapshot } from "valtio"
|
import { useSnapshot } from "valtio"
|
||||||
|
|
||||||
import JobDropdown from "~components/JobDropdown"
|
import JobDropdown from "~components/JobDropdown"
|
||||||
import JobSkillItem from "~components/JobSkillItem"
|
import JobSkillItem from "~components/JobSkillItem"
|
||||||
|
import SearchModal from "~components/SearchModal"
|
||||||
|
|
||||||
import { appState } from "~utils/appState"
|
import { appState } from "~utils/appState"
|
||||||
|
|
||||||
|
import type { SearchableObject } from "~types"
|
||||||
|
|
||||||
import "./index.scss"
|
import "./index.scss"
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
interface Props {}
|
interface Props {
|
||||||
|
editable: boolean
|
||||||
|
}
|
||||||
|
|
||||||
const JobSection = (props: Props) => {
|
const JobSection = (props: Props) => {
|
||||||
|
const { t } = useTranslation("common")
|
||||||
|
|
||||||
const [job, setJob] = useState<Job>()
|
const [job, setJob] = useState<Job>()
|
||||||
const [imageUrl, setImageUrl] = useState("")
|
const [imageUrl, setImageUrl] = useState("")
|
||||||
|
|
||||||
|
|
@ -20,9 +28,11 @@ const JobSection = (props: Props) => {
|
||||||
const [numSkills, setNumSkills] = useState(4)
|
const [numSkills, setNumSkills] = useState(4)
|
||||||
const [skills, setSkills] = useState<JobSkill[]>([])
|
const [skills, setSkills] = useState<JobSkill[]>([])
|
||||||
|
|
||||||
|
const [skillRefs, setSkillRefs] = useState<ForwardedRef<HTMLDivElement>[]>([])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Set current job based on ID
|
// Set current job based on ID
|
||||||
setJob(party.job)
|
if (party.job) setJob(party.job)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -33,6 +43,14 @@ const JobSection = (props: Props) => {
|
||||||
if (job) appState.party.job = job
|
if (job) appState.party.job = job
|
||||||
}, [job])
|
}, [job])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSkillRefs(Array(numSkills).fill(React.createRef<HTMLDivElement>()))
|
||||||
|
}, [numSkills])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log(skillRefs)
|
||||||
|
}, [skillRefs])
|
||||||
|
|
||||||
function receiveJob(job?: Job) {
|
function receiveJob(job?: Job) {
|
||||||
console.log(`Receiving job! Row ${job?.row}: ${job?.name.en}`)
|
console.log(`Receiving job! Row ${job?.row}: ${job?.name.en}`)
|
||||||
if (job) {
|
if (job) {
|
||||||
|
|
@ -62,6 +80,34 @@ const JobSection = (props: Props) => {
|
||||||
setImageUrl(imgSrc)
|
setImageUrl(imgSrc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const skillItem = (index: number, editable: boolean) => {
|
||||||
|
return (
|
||||||
|
<JobSkillItem
|
||||||
|
skill={skills[index]}
|
||||||
|
editable={!skills[index]?.main && editable}
|
||||||
|
key={`skill-${index}`}
|
||||||
|
hasJob={job != undefined && job.id != "-1"}
|
||||||
|
ref={skillRefs[index]}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const editableSkillItem = (index: number) => {
|
||||||
|
return (
|
||||||
|
<SearchModal
|
||||||
|
placeholderText={t("search.placeholders.job_skill")}
|
||||||
|
fromPosition={index}
|
||||||
|
object="job_skills"
|
||||||
|
job={job}
|
||||||
|
send={updateObject}
|
||||||
|
>
|
||||||
|
{skillItem(index, true)}
|
||||||
|
</SearchModal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateObject(object: SearchableObject, position: number) {}
|
||||||
|
|
||||||
// Render: JSX components
|
// Render: JSX components
|
||||||
return (
|
return (
|
||||||
<section id="Job">
|
<section id="Job">
|
||||||
|
|
@ -76,8 +122,10 @@ const JobSection = (props: Props) => {
|
||||||
/>
|
/>
|
||||||
<ul className="JobSkills">
|
<ul className="JobSkills">
|
||||||
{[...Array(numSkills)].map((e, i) => (
|
{[...Array(numSkills)].map((e, i) => (
|
||||||
<li>
|
<li key={`job-${i}`}>
|
||||||
<JobSkillItem skill={skills[i]} editable={!skills[i]?.main} />
|
{job && job.id != "-1" && !skills[i]?.main && props.editable
|
||||||
|
? editableSkillItem(i)
|
||||||
|
: skillItem(i, false)}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
&.editable:hover {
|
&.editable:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
& > img,
|
& > img.editable,
|
||||||
& > div.placeholder {
|
& > div.placeholder.editable {
|
||||||
border: $hover-stroke;
|
border: $hover-stroke;
|
||||||
box-shadow: $hover-shadow;
|
box-shadow: $hover-shadow;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,10 @@ import "./index.scss"
|
||||||
interface Props {
|
interface Props {
|
||||||
skill?: JobSkill
|
skill?: JobSkill
|
||||||
editable: boolean
|
editable: boolean
|
||||||
|
hasJob: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const JobSkillItem = (props: Props) => {
|
const JobSkillItem = React.forwardRef<HTMLDivElement, Props>((props, ref) => {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { t } = useTranslation("common")
|
const { t } = useTranslation("common")
|
||||||
const locale =
|
const locale =
|
||||||
|
|
@ -25,28 +26,57 @@ const JobSkillItem = (props: Props) => {
|
||||||
editable: props.editable,
|
editable: props.editable,
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
const imageClasses = classNames({
|
||||||
<div className={classes}>
|
placeholder: !props.skill,
|
||||||
{props.skill ? (
|
editable: props.editable && props.hasJob,
|
||||||
|
})
|
||||||
|
|
||||||
|
const skillImage = () => {
|
||||||
|
let jsx: React.ReactNode
|
||||||
|
|
||||||
|
if (props.skill) {
|
||||||
|
jsx = (
|
||||||
<img
|
<img
|
||||||
alt={props.skill.name[locale]}
|
alt={props.skill.name[locale]}
|
||||||
|
className={imageClasses}
|
||||||
src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}job-skills/${props.skill.slug}.png`}
|
src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}job-skills/${props.skill.slug}.png`}
|
||||||
/>
|
/>
|
||||||
) : (
|
)
|
||||||
<div className="placeholder">
|
} else {
|
||||||
<PlusIcon />
|
jsx = (
|
||||||
|
<div className={imageClasses}>
|
||||||
|
{props.editable && props.hasJob ? <PlusIcon /> : ""}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)
|
||||||
<div className="info">
|
}
|
||||||
{/* {props.skill ? <div className="skill pill">Grouping</div> : ""} */}
|
|
||||||
{props.skill ? (
|
return jsx
|
||||||
<p>{props.skill.name[locale]}</p>
|
}
|
||||||
) : (
|
|
||||||
<p className="placeholder">Select a skill</p>
|
const label = () => {
|
||||||
)}
|
let jsx: React.ReactNode
|
||||||
|
|
||||||
|
if (props.skill) {
|
||||||
|
jsx = <p>{props.skill.name[locale]}</p>
|
||||||
|
} else if (props.editable && props.hasJob) {
|
||||||
|
jsx = <p className="placeholder">Select a skill</p>
|
||||||
|
} else {
|
||||||
|
jsx = <p className="placeholder">No skill</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsx
|
||||||
|
}
|
||||||
|
|
||||||
|
const skillItem = () => {
|
||||||
|
return (
|
||||||
|
<div className={classes} ref={ref}>
|
||||||
|
{skillImage()}
|
||||||
|
{label()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
|
return skillItem()
|
||||||
|
})
|
||||||
|
|
||||||
export default JobSkillItem
|
export default JobSkillItem
|
||||||
|
|
|
||||||
|
|
@ -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%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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<HTMLInputElement>()
|
|
||||||
let scrollContainer = React.createRef<HTMLDivElement>()
|
|
||||||
|
|
||||||
const [currentGroup, setCurrentGroup] = useState(-1)
|
|
||||||
const [currentGroupName, setCurrentGroupName] = useState("")
|
|
||||||
const [open, setOpen] = useState(false)
|
|
||||||
const [query, setQuery] = useState("")
|
|
||||||
const [results, setResults] = useState<JobSkill[]>([])
|
|
||||||
|
|
||||||
// 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<HTMLSelectElement>) {
|
|
||||||
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<HTMLInputElement>) {
|
|
||||||
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 (
|
|
||||||
<JobSkillResult data={result} key={`skill-${i}`} onClick={() => {}} />
|
|
||||||
)
|
|
||||||
})
|
|
||||||
return (
|
|
||||||
<InfiniteScroll
|
|
||||||
dataLength={results && results.length > 0 ? results.length : 0}
|
|
||||||
next={() => setCurrentPage(currentPage + 1)}
|
|
||||||
hasMore={totalPages > currentPage}
|
|
||||||
scrollableTarget="Results"
|
|
||||||
loader={<div className="footer">Loading...</div>}
|
|
||||||
>
|
|
||||||
{rows}
|
|
||||||
</InfiniteScroll>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Dialog.Root open={open} onOpenChange={openChange}>
|
|
||||||
<Dialog.Trigger asChild>{props.children}</Dialog.Trigger>
|
|
||||||
<Dialog.Portal>
|
|
||||||
<Dialog.Content className="Search Dialog">
|
|
||||||
<div id="Header">
|
|
||||||
<div id="Bar">
|
|
||||||
<select
|
|
||||||
key="job-skill-groups"
|
|
||||||
value={currentGroup}
|
|
||||||
onBlur={onBlur}
|
|
||||||
onChange={onChange}
|
|
||||||
>
|
|
||||||
<option key="all" value={-1}>
|
|
||||||
All skills
|
|
||||||
</option>
|
|
||||||
<option key="damaging" value={2}>
|
|
||||||
Damaging
|
|
||||||
</option>
|
|
||||||
<option key="buffing" value={0}>
|
|
||||||
Buffing
|
|
||||||
</option>
|
|
||||||
<option key="debuffing" value={1}>
|
|
||||||
Debuffing
|
|
||||||
</option>
|
|
||||||
<option key="healing" value={3}>
|
|
||||||
Healing
|
|
||||||
</option>
|
|
||||||
<option key="emp" value={4}>
|
|
||||||
Extended Mastery
|
|
||||||
</option>
|
|
||||||
<option key="base" value={5}>
|
|
||||||
Base Skills
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
<Dialog.Close className="DialogClose" onClick={openChange}>
|
|
||||||
<CrossIcon />
|
|
||||||
</Dialog.Close>
|
|
||||||
</div>
|
|
||||||
<label className="search_label" htmlFor="search_input">
|
|
||||||
<input
|
|
||||||
autoComplete="off"
|
|
||||||
type="text"
|
|
||||||
name="query"
|
|
||||||
className="Input"
|
|
||||||
id="search_input"
|
|
||||||
ref={searchInput}
|
|
||||||
value={query}
|
|
||||||
placeholder={
|
|
||||||
currentGroupName
|
|
||||||
? `Search for ${currentGroupName} skills...`
|
|
||||||
: `Search all skills...`
|
|
||||||
}
|
|
||||||
onChange={inputChanged}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="Results" ref={scrollContainer}>
|
|
||||||
<h5 className="total">
|
|
||||||
{t("search.result_count", { record_count: recordCount })}
|
|
||||||
</h5>
|
|
||||||
{open ? render() : ""}
|
|
||||||
</div>
|
|
||||||
</Dialog.Content>
|
|
||||||
<Dialog.Overlay className="Overlay" />
|
|
||||||
</Dialog.Portal>
|
|
||||||
</Dialog.Root>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default JobSkillModal
|
|
||||||
3
components/JobSkillSearchFilterBar/index.scss
Normal file
3
components/JobSkillSearchFilterBar/index.scss
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
.SearchFilterBar select {
|
||||||
|
background-color: $grey-90;
|
||||||
|
}
|
||||||
71
components/JobSkillSearchFilterBar/index.tsx
Normal file
71
components/JobSkillSearchFilterBar/index.tsx
Normal file
|
|
@ -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<HTMLSelectElement>) {
|
||||||
|
setCurrentGroup(parseInt(event.target.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
function onBlur(event: React.ChangeEvent<HTMLSelectElement>) {}
|
||||||
|
|
||||||
|
function sendFilters() {
|
||||||
|
const filters = {
|
||||||
|
group: currentGroup,
|
||||||
|
}
|
||||||
|
|
||||||
|
props.sendFilters(filters)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
sendFilters()
|
||||||
|
}, [currentGroup])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="SearchFilterBar">
|
||||||
|
<select
|
||||||
|
key="job-skill-groups"
|
||||||
|
value={currentGroup}
|
||||||
|
onBlur={onBlur}
|
||||||
|
onChange={onChange}
|
||||||
|
>
|
||||||
|
<option key="all" value={-1}>
|
||||||
|
{t(`job_skills.all`)}
|
||||||
|
</option>
|
||||||
|
<option key="damaging" value={2}>
|
||||||
|
{t(`job_skills.damaging`)}
|
||||||
|
</option>
|
||||||
|
<option key="buffing" value={0}>
|
||||||
|
{t(`job_skills.buffing`)}
|
||||||
|
</option>
|
||||||
|
<option key="debuffing" value={1}>
|
||||||
|
{t(`job_skills.debuffing`)}
|
||||||
|
</option>
|
||||||
|
<option key="healing" value={3}>
|
||||||
|
{t(`job_skills.healing`)}
|
||||||
|
</option>
|
||||||
|
<option key="emp" value={4}>
|
||||||
|
{t(`job_skills.emp`)}
|
||||||
|
</option>
|
||||||
|
<option key="base" value={5}>
|
||||||
|
{t(`job_skills.base`)}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default JobSkillSearchFilterBar
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useEffect, useRef, useState } from "react"
|
import React, { useEffect, useState } from "react"
|
||||||
import { getCookie, setCookie } from "cookies-next"
|
import { getCookie, setCookie } from "cookies-next"
|
||||||
import { useRouter } from "next/router"
|
import { useRouter } from "next/router"
|
||||||
import { useSnapshot } from "valtio"
|
import { useSnapshot } from "valtio"
|
||||||
|
|
@ -13,10 +13,13 @@ import * as Dialog from "@radix-ui/react-dialog"
|
||||||
import CharacterSearchFilterBar from "~components/CharacterSearchFilterBar"
|
import CharacterSearchFilterBar from "~components/CharacterSearchFilterBar"
|
||||||
import WeaponSearchFilterBar from "~components/WeaponSearchFilterBar"
|
import WeaponSearchFilterBar from "~components/WeaponSearchFilterBar"
|
||||||
import SummonSearchFilterBar from "~components/SummonSearchFilterBar"
|
import SummonSearchFilterBar from "~components/SummonSearchFilterBar"
|
||||||
|
import JobSkillSearchFilterBar from "~components/JobSkillSearchFilterBar"
|
||||||
|
|
||||||
import CharacterResult from "~components/CharacterResult"
|
import CharacterResult from "~components/CharacterResult"
|
||||||
import WeaponResult from "~components/WeaponResult"
|
import WeaponResult from "~components/WeaponResult"
|
||||||
import SummonResult from "~components/SummonResult"
|
import SummonResult from "~components/SummonResult"
|
||||||
|
import JobSkillResult from "~components/JobSkillResult"
|
||||||
|
|
||||||
import type { SearchableObject, SearchableObjectArray } from "~types"
|
import type { SearchableObject, SearchableObjectArray } from "~types"
|
||||||
|
|
||||||
import "./index.scss"
|
import "./index.scss"
|
||||||
|
|
@ -27,14 +30,12 @@ interface Props {
|
||||||
send: (object: SearchableObject, position: number) => any
|
send: (object: SearchableObject, position: number) => any
|
||||||
placeholderText: string
|
placeholderText: string
|
||||||
fromPosition: number
|
fromPosition: number
|
||||||
object: "weapons" | "characters" | "summons"
|
job?: Job
|
||||||
|
object: "weapons" | "characters" | "summons" | "job_skills"
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
const SearchModal = (props: Props) => {
|
const SearchModal = (props: Props) => {
|
||||||
// Set up snapshot of app state
|
|
||||||
let { grid, search } = useSnapshot(appState)
|
|
||||||
|
|
||||||
// Set up router
|
// Set up router
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const locale = router.locale
|
const locale = router.locale
|
||||||
|
|
@ -46,10 +47,7 @@ const SearchModal = (props: Props) => {
|
||||||
let scrollContainer = React.createRef<HTMLDivElement>()
|
let scrollContainer = React.createRef<HTMLDivElement>()
|
||||||
|
|
||||||
const [firstLoad, setFirstLoad] = useState(true)
|
const [firstLoad, setFirstLoad] = useState(true)
|
||||||
const [objects, setObjects] = useState<{
|
const [filters, setFilters] = useState<{ [key: string]: any }>()
|
||||||
[id: number]: GridCharacter | GridWeapon | GridSummon | undefined
|
|
||||||
}>()
|
|
||||||
const [filters, setFilters] = useState<{ [key: string]: number[] }>()
|
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const [query, setQuery] = useState("")
|
const [query, setQuery] = useState("")
|
||||||
const [results, setResults] = useState<SearchableObjectArray>([])
|
const [results, setResults] = useState<SearchableObjectArray>([])
|
||||||
|
|
@ -59,10 +57,6 @@ const SearchModal = (props: Props) => {
|
||||||
const [currentPage, setCurrentPage] = useState(1)
|
const [currentPage, setCurrentPage] = useState(1)
|
||||||
const [totalPages, setTotalPages] = useState(1)
|
const [totalPages, setTotalPages] = useState(1)
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setObjects(grid[props.object])
|
|
||||||
}, [grid, props.object])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (searchInput.current) searchInput.current.focus()
|
if (searchInput.current) searchInput.current.focus()
|
||||||
}, [searchInput])
|
}, [searchInput])
|
||||||
|
|
@ -77,15 +71,19 @@ const SearchModal = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchResults({ replace = false }: { replace?: boolean }) {
|
function fetchResults({ replace = false }: { replace?: boolean }) {
|
||||||
|
console.log("Fetch results!!!")
|
||||||
api
|
api
|
||||||
.search({
|
.search({
|
||||||
object: props.object,
|
object: props.object,
|
||||||
query: query,
|
query: query,
|
||||||
|
job: props.job?.id,
|
||||||
filters: filters,
|
filters: filters,
|
||||||
locale: locale,
|
locale: locale,
|
||||||
page: currentPage,
|
page: currentPage,
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
console.log("resp")
|
||||||
|
console.log(response)
|
||||||
setTotalPages(response.data.total_pages)
|
setTotalPages(response.data.total_pages)
|
||||||
setRecordCount(response.data.count)
|
setRecordCount(response.data.count)
|
||||||
|
|
||||||
|
|
@ -152,7 +150,7 @@ const SearchModal = (props: Props) => {
|
||||||
openChange()
|
openChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
function receiveFilters(filters: { [key: string]: number[] }) {
|
function receiveFilters(filters: { [key: string]: any }) {
|
||||||
setCurrentPage(1)
|
setCurrentPage(1)
|
||||||
setResults([])
|
setResults([])
|
||||||
setFilters(filters)
|
setFilters(filters)
|
||||||
|
|
@ -208,6 +206,9 @@ const SearchModal = (props: Props) => {
|
||||||
case "characters":
|
case "characters":
|
||||||
jsx = renderCharacterSearchResults(results)
|
jsx = renderCharacterSearchResults(results)
|
||||||
break
|
break
|
||||||
|
case "job_skills":
|
||||||
|
jsx = renderJobSkillSearchResults(results)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -286,6 +287,27 @@ const SearchModal = (props: Props) => {
|
||||||
return jsx
|
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 (
|
||||||
|
<JobSkillResult
|
||||||
|
key={result.id}
|
||||||
|
data={result}
|
||||||
|
onClick={() => {
|
||||||
|
storeRecentResult(result)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsx
|
||||||
|
}
|
||||||
|
|
||||||
function openChange() {
|
function openChange() {
|
||||||
if (open) {
|
if (open) {
|
||||||
setQuery("")
|
setQuery("")
|
||||||
|
|
@ -338,6 +360,11 @@ const SearchModal = (props: Props) => {
|
||||||
) : (
|
) : (
|
||||||
""
|
""
|
||||||
)}
|
)}
|
||||||
|
{props.object === "job_skills" ? (
|
||||||
|
<JobSkillSearchFilterBar sendFilters={receiveFilters} />
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="Results" ref={scrollContainer}>
|
<div id="Results" ref={scrollContainer}>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue