From ef673a88b7c1bdf294ed5f3e4ff049d567dc03a1 Mon Sep 17 00:00:00 2001 From: himaniraghav3 Date: Fri, 17 Apr 2026 15:57:54 +0530 Subject: [PATCH 1/7] OM-4886 Update default state of talent search --- .../src/lib/services/talentSearch.service.ts | 7 +- .../TalentSearchPage/TalentSearchPage.tsx | 144 ++++++++++++------ .../TalentResultCard.module.scss | 4 + .../TalentResultCard/TalentResultCard.tsx | 52 ++++--- 4 files changed, 137 insertions(+), 70 deletions(-) diff --git a/src/apps/customer-portal/src/lib/services/talentSearch.service.ts b/src/apps/customer-portal/src/lib/services/talentSearch.service.ts index a8fc905ed..02c8fb750 100644 --- a/src/apps/customer-portal/src/lib/services/talentSearch.service.ts +++ b/src/apps/customer-portal/src/lib/services/talentSearch.service.ts @@ -21,16 +21,17 @@ export type SearchTalent = { } export type MemberSearchPayload = { + country?: string limit: number - openToWork: boolean + openToWork?: boolean page: number - recentlyActive: boolean + recentlyActive?: boolean skillSearchType: 'OR' skills: Array<{ id: string wins: number }> - verifiedProfile: boolean + verifiedProfile?: boolean } export type MemberSearchResponse = { diff --git a/src/apps/customer-portal/src/pages/talent-search/TalentSearchPage/TalentSearchPage.tsx b/src/apps/customer-portal/src/pages/talent-search/TalentSearchPage/TalentSearchPage.tsx index fa88968ff..fac94dae1 100644 --- a/src/apps/customer-portal/src/pages/talent-search/TalentSearchPage/TalentSearchPage.tsx +++ b/src/apps/customer-portal/src/pages/talent-search/TalentSearchPage/TalentSearchPage.tsx @@ -28,18 +28,22 @@ import personSearchImage from '../../../lib/assets/person-search.png' import styles from './TalentSearchPage.module.scss' +type TalentSearchSortOption = 'alphabetical' | 'matching-index' + export const TalentSearchPage: FC = () => { const skipNextAutoSearchRef = useRef(false) const countryLookup: CountryLookup[] | undefined = useCountryLookup() const [jobDescription, setJobDescription] = useState('') const [isExtractingSkills, setIsExtractingSkills] = useState(false) const [errorMessage, setErrorMessage] = useState('') - const [hasSearched, setHasSearched] = useState(false) + const [hasSearched, setHasSearched] = useState(true) const [skillOptionsLoading, setSkillOptionsLoading] = useState(false) const [selectedSkills, setSelectedSkills] = useState([]) + const [sortBy, setSortBy] = useState('alphabetical') const [selectedCountry, setSelectedCountry] = useState('all') - const [onlyOpenToWork, setOnlyOpenToWork] = useState(true) - const [onlyActive, setOnlyActive] = useState(true) + const [onlyProfileComplete, setOnlyProfileComplete] = useState(true) + const [onlyOpenToWork, setOnlyOpenToWork] = useState(false) + const [onlyActive, setOnlyActive] = useState(false) const [isSearchingMembers, setIsSearchingMembers] = useState(false) const [isLoadingMore, setIsLoadingMore] = useState(false) const [results, setResults] = useState([]) @@ -65,28 +69,40 @@ export const TalentSearchPage: FC = () => { [countryLookup], ) + const hasSkillSearch = selectedSkills.length > 0 + const activeSort: TalentSearchSortOption = hasSkillSearch ? 'matching-index' : sortBy + const sortOptions = useMemo( + (): InputSelectOption[] => (hasSkillSearch + ? [{ label: 'Matching Index', value: 'matching-index' }] + : [{ label: 'Alphabetical', value: 'alphabetical' }]), + [hasSkillSearch], + ) + const filteredResults = useMemo(() => results.filter(talent => { - if (selectedCountry !== 'all') { - const selectedCountryOption = countryOptions.find(option => option.value === selectedCountry) - const selectedCountryName = typeof selectedCountryOption?.label === 'string' - ? selectedCountryOption.label - : '' - const normalizedLocation = talent.location.toLowerCase() - - if (!selectedCountryName || !normalizedLocation.includes(selectedCountryName.toLowerCase())) { - return false - } + if (onlyActive && !talent.isRecentlyActive) { + return false } - if (onlyActive && !talent.isRecentlyActive) { + if (onlyOpenToWork && !talent.openToWork) { return false } return true - }), [countryOptions, onlyActive, results, selectedCountry]) - const foundMembersCount = selectedCountry === 'all' - ? (totalResults || filteredResults.length) - : filteredResults.length + }), [onlyActive, onlyOpenToWork, results]) + + const displayedResults = useMemo(() => { + const sorted = [...filteredResults] + if (activeSort === 'matching-index') { + sorted.sort((a, b) => b.matchIndex - a.matchIndex) + return sorted + } + + sorted.sort((a, b) => String(a.handle || '') + .localeCompare(String(b.handle || ''), undefined, { sensitivity: 'base' })) + return sorted + }, [activeSort, filteredResults]) + + const foundMembersCount = totalResults || displayedResults.length const hasMoreResults = results.length < totalResults const loadSkillOptions = useCallback(async (query: string): Promise => { @@ -104,21 +120,23 @@ export const TalentSearchPage: FC = () => { skillsToSearch: InputMultiselectOption[], overrides?: { append?: boolean + country?: string openToWork?: boolean page?: number recentlyActive?: boolean + verifiedProfile?: boolean }, ): Promise => { const append = overrides?.append === true + const country = overrides?.country ?? selectedCountry const openToWork = overrides?.openToWork ?? onlyOpenToWork const page = overrides?.page ?? 1 const recentlyActive = overrides?.recentlyActive ?? onlyActive + const verifiedProfile = overrides?.verifiedProfile ?? onlyProfileComplete const payload: MemberSearchPayload = { limit: MEMBER_SEARCH_LIMIT, - openToWork, page, - recentlyActive, skills: skillsToSearch .map(skill => String(skill.value || '') .trim()) @@ -128,7 +146,22 @@ export const TalentSearchPage: FC = () => { wins: 1, })), skillSearchType: 'OR', - verifiedProfile: true, + } + + if (country !== 'all') { + payload.country = country + } + + if (openToWork) { + payload.openToWork = true + } + + if (recentlyActive) { + payload.recentlyActive = true + } + + if (verifiedProfile) { + payload.verifiedProfile = true } if (append) { @@ -176,19 +209,25 @@ export const TalentSearchPage: FC = () => { setIsSearchingMembers(false) } } - }, [onlyActive, onlyOpenToWork]) + }, [onlyActive, onlyOpenToWork, onlyProfileComplete, selectedCountry]) const clearAllFilters = useCallback((): void => { setSelectedCountry('all') - setOnlyOpenToWork(true) - setOnlyActive(true) + setOnlyProfileComplete(true) + setOnlyOpenToWork(false) + setOnlyActive(false) + setSortBy('alphabetical') setSelectedSkills([]) - setHasSearched(false) - setResults([]) - setTotalResults(0) - setCurrentPage(1) + setHasSearched(true) setErrorMessage('') - }, []) + skipNextAutoSearchRef.current = true + runMemberSearch([], { + openToWork: false, + page: 1, + recentlyActive: false, + verifiedProfile: true, + }) + }, [runMemberSearch]) const handleAiSearch = useCallback(async (): Promise => { const normalizedDescription = jobDescription.trim() @@ -230,12 +269,12 @@ export const TalentSearchPage: FC = () => { setTotalResults(0) setHasSearched(true) setErrorMessage('No skills were extracted from the job description.') + skipNextAutoSearchRef.current = true return } setHasSearched(true) - skipNextAutoSearchRef.current = true - await runMemberSearch(extractedOptions, { page: 1 }) + // Let the normal effect-driven search run with populated skills. } catch { // Prevent stale auto-search when extraction fails and loading flips to false. skipNextAutoSearchRef.current = true @@ -244,10 +283,10 @@ export const TalentSearchPage: FC = () => { } finally { setIsExtractingSkills(false) } - }, [isExtractingSkills, jobDescription, runMemberSearch]) + }, [isExtractingSkills, jobDescription]) useEffect(() => { - if (!hasSearched || isExtractingSkills || selectedSkills.length === 0) { + if (!hasSearched || isExtractingSkills) { return } @@ -256,18 +295,20 @@ export const TalentSearchPage: FC = () => { return } - runMemberSearch(selectedSkills) + runMemberSearch(selectedSkills, { page: 1 }) }, [ hasSearched, isExtractingSkills, onlyActive, + onlyProfileComplete, onlyOpenToWork, runMemberSearch, + selectedCountry, selectedSkills, ]) const handleLoadMore = useCallback((): void => { - if (isLoadingMore || isSearchingMembers || !hasMoreResults || selectedSkills.length === 0) { + if (isLoadingMore || isSearchingMembers || !hasMoreResults) { return } @@ -344,7 +385,7 @@ export const TalentSearchPage: FC = () => { onChange={(event: ChangeEvent) => { const value = (event.target.value || []) as InputMultiselectOption[] setSelectedSkills(value) - setHasSearched(value.length > 0) + setHasSearched(true) }} /> @@ -360,6 +401,18 @@ export const TalentSearchPage: FC = () => { placeholder='Select country' /> +