Created
May 20, 2024 11:48
-
-
Save OlegLustenko/c5b1a970dcd0933b2a4bfb966811e413 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// infrastructure/api/search | |
const baseURL = '/api/v1/projects' | |
const fetchSearchAPI = async ( | |
{ | |
projectSlug, | |
params, | |
}: { | |
projectSlug: string; | |
params: QueryParams; | |
}, | |
options: PassedOptions = {}, | |
): Response<SearchResponse> => { | |
const url = `${baseURL}/${projectSlug}/search`; | |
try { | |
return await fetcher(url, { | |
...options, | |
params, | |
}); | |
} catch (error) { | |
return handleError({ error, origin: 'fetchSearchAPI', url }); | |
} | |
}; | |
// infrastructure/api/remoteAPI.ts | |
import * as searchAPI from './search/search.api'; | |
export const remoteAPI = { | |
search: searchAPI, | |
}; | |
// infrastructure/feature/Search/@core/search.server-actions.ts | |
export const getSearchList = async ( | |
projectSlug: string, | |
searchParams: SearchListParams, | |
) => { | |
const { | |
tab: searchParamTab, | |
filters: searchParamFilters, | |
page, | |
search, | |
skills, | |
cities, | |
industries, | |
areas, | |
} = searchParams; | |
const tab = TAB_MAP[searchParamTab]; | |
const filters = searchParamFilters && FILTERS_MAP[searchParamFilters]; | |
const searchListItems = | |
await remoteAPI.search.fetchSearchAPI({ | |
projectSlug, | |
params: { | |
tab, | |
search, | |
filters, | |
page, | |
skills, | |
cities, | |
industries, | |
areas, | |
}, | |
}); | |
assertErrorInResponse(searchListItems, 'getSearchList'); | |
const { items, aggregations, total, pagination } = searchListItems.data; | |
return { | |
items: getMappedItems(items), | |
counters: getApplicantsFiltersCounters(aggregations), | |
total, | |
pagination, | |
}; | |
}; | |
// infrastructure/feature/Search/@core/search.hooks.ts | |
export const useApplicantsSearchParams = () => { | |
const [isPending, startTransition] = useTransition(); | |
const searchParams = useSearchParams(); | |
const router = useRouter(); | |
const tabStateRef = useRef<TabStateRef>(tabStateRefInitialState); | |
const replaceParams = useReplaceParams(); | |
const tab = searchParams!.get('tab') as ApplicantsTabs; | |
const filters = searchParams!.get('filters') as ApplicantsFilters; | |
const page = searchParams!.get('page') as string; | |
const search = searchParams!.get('search') as string; | |
const cities = searchParams.getAll('cities'); | |
const industries = searchParams.getAll('industries'); | |
const skills = searchParams.getAll('skills'); | |
const areas = searchParams.getAll('areas'); | |
const getNewUrl = ({ | |
newTab = tab, | |
newFilter = filters, | |
newPage = page, | |
newSearch = search, | |
newCities = cities.length ? cities : [], | |
newAreas = areas.length ? areas : [], | |
newSkills = skills.length ? skills : [], | |
newIndustries = industries.length ? industries : [], | |
}: ApplicantsUrlParams) => { | |
return replaceParams({ | |
tab: newTab, | |
filters: newFilter, | |
page: newPage, | |
search: newSearch, | |
cities: newCities, | |
areas: newAreas, | |
industries: newIndustries, | |
skills: newSkills, | |
}); | |
}; | |
const updateApplicantsParams = (params: ApplicantsUrlParams) => { | |
const newUrl = getNewUrl(params); | |
startTransition(() => { | |
router.push(newUrl); | |
}); | |
}; | |
const getFilterUrl = (passedFilter: ApplicantsFilters | null) => { | |
const newFilter = passedFilter === filters ? null : passedFilter; | |
return getNewUrl({ newFilter }); | |
}; | |
const setPage = (passedPage: string | null) => { | |
if (passedPage === page) { | |
return; | |
} | |
let newPage = passedPage; | |
if (passedPage && page > passedPage && passedPage === '1') { | |
newPage = null; | |
} | |
updateApplicantsParams({ newPage }); | |
}; | |
const setSearch = (passedSearch?: string | null) => { | |
if (passedSearch === search) { | |
return; | |
} | |
const newSearch = !passedSearch ? null : passedSearch; | |
updateApplicantsParams({ newSearch, newPage: null }); | |
}; | |
// On tab switch we set tab and clear out all filters. | |
const getTabUrl = (newTab: ApplicantsTabs) => { | |
const data = { | |
newTab, | |
newFilter: filters, | |
newSearch: search, | |
newPage: null, | |
} as ApplicantsUrlParams; | |
const tabsState = tabStateRef.current; | |
if (tab !== newTab) { | |
data.newFilter = tabsState[newTab].filters; | |
data.newSearch = tabsState[newTab].search; | |
} | |
if (tab === newTab) { | |
tabsState[newTab] = { | |
filters: data.newFilter, | |
search: data.newSearch, | |
}; | |
} | |
return getNewUrl(data); | |
}; | |
const getCityUrl = (passedCities: string[]) => { | |
// ... | |
}; | |
const getAreaUrl = (passedArea: string[]) => { | |
const newAreas = isEqual(passedArea, areas) ? null : passedArea; | |
return getNewUrl({ newAreas }); | |
}; | |
const setSkills = (passedSkills: string[]) => { | |
if (isEqual(passedSkills, skills)) { | |
return; | |
} | |
return updateApplicantsParams({ newSkills: passedSkills }); | |
}; | |
const setIndustries = (passedIndustries: string[]) => { | |
if (isEqual(passedIndustries, industries)) { | |
return; | |
} | |
return updateApplicantsParams({ newIndustries: passedIndustries }); | |
}; | |
const getSkillUrl = (passedSkill: string, selected: boolean) => { | |
// .. | |
}; | |
const getIndustryUrl = (passedIndustry: string, selected: boolean) => { | |
if (industries.includes(passedIndustry)) { | |
const newIndustries = industries.filter((industry) => { | |
if (passedIndustry === industry) { | |
return !selected; | |
} | |
return true; | |
}); | |
return getNewUrl({ newIndustries }); | |
} | |
return getNewUrl({ newIndustries: [...industries, passedIndustry] }); | |
}; | |
return { | |
isPending, | |
tab, | |
filters, | |
page, | |
search, | |
skills, | |
areas, | |
cities, | |
getFilterUrl, | |
getNewUrl, | |
getTabUrl, | |
getCityUrl, | |
getAreaUrl, | |
getIndustryUrl, | |
setIndustries, | |
setSkills, | |
getSkillUrl, | |
setPage, | |
setSearch, | |
}; | |
}; | |
// infrastructure/feature/Search/@core/index.ts | |
import * as aggregations from './search.aggregations'; | |
import * as constants from './search.constants'; | |
import * as domain from './search.domain'; | |
import * as serverActions from './search.server-actions'; | |
export const searchFeature = { | |
aggregations, | |
constants, | |
serverActions, | |
domain, | |
}; | |
// infrastructure/features/Search/SearchList.tsx | |
type SearchListProps = { | |
searchParams: SearchListParams; | |
params: { projectId: string }; | |
}; | |
export const SearchList = async ({ | |
searchParams, | |
params, | |
}: SearchListProps) => { | |
const searchResults = | |
await searchFeature.serverActions.getSearchList( | |
params.projectId, | |
searchParams, | |
); | |
const activeTabCounter = searchResults.counters; | |
const hasTabApplicants = Boolean(activeTabCounter.total); | |
const noApplicantsUnderThisTab = | |
!activeTabCounter.total && !searchParams.search; | |
const noApplicantsAfterSearch = | |
!activeTabCounter.total && searchParams.search; | |
const shouldSearchBeDisplayed = hasTabApplicants || noApplicantsAfterSearch; | |
return ( | |
<Page.Body> | |
<> | |
{noApplicantsUnderThisTab && ( | |
<ApplicantsBannerTab tab={searchParams.tab} /> | |
)} | |
{shouldSearchBeDisplayed && ( | |
<> | |
<ApplicantsSearchV2 /> | |
<div className="md:hidden"> | |
<ApplicantsFilterModalButton> | |
<ApplicantsFiltersV2 | |
searchParams={searchParams} | |
params={params} | |
/> | |
</ApplicantsFilterModalButton> | |
</div> | |
</> | |
)} | |
<PageContentWithSide> | |
<PageContentWithSideLeft> | |
<div className="subheading">Filter by:</div> | |
<ApplicantsFiltersV2 | |
searchParams={searchParams} | |
params={params} | |
/> | |
</PageContentWithSideLeft> | |
<PageContentWithSideMain> | |
{noApplicantsAfterSearch && ( | |
<ApplicantsBannerSearch search={searchParams.search} /> | |
)} | |
{hasTabApplicants && ( | |
<Suspense | |
fallback={<Page.ContentLoader single />} | |
key={JSON.stringify(searchParams)} | |
> | |
<ErrorBoundary fallbackRender={Error}> | |
<ApplicantsList | |
isApplicantFilteringEnabled | |
params={params} | |
searchParams={searchParams} | |
/> | |
</ErrorBoundary> | |
</Suspense> | |
)} | |
</PageContentWithSideMain> | |
</PageContentWithSide> | |
</> | |
</Page.Body> | |
); | |
}; | |
// infrastructure/features/Search/ | |
export const SearchFilterSkills = ({ skills, skillsOptions }: Props) => { | |
const { getSkillUrl, setSkills } = useApplicantsSearchParams(); | |
return ( | |
<SearchFilterCard title="Skills"> | |
<SearchFilterAutocomplete | |
getUrl={getSkillUrl} | |
options={skillsOptions} | |
values={skills} | |
setValues={setSkills} | |
placeholder="Search or select skills" | |
/> | |
</SearchFilterCard> | |
); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment