+
+
+
+
{lists?.[selectedListIndex] && (
{lists[selectedListIndex].questionEntries.map(
diff --git a/apps/portal/src/pages/resumes/[resumeId].tsx b/apps/portal/src/pages/resumes/[resumeId].tsx
index ba7d9f42..fc547bb4 100644
--- a/apps/portal/src/pages/resumes/[resumeId].tsx
+++ b/apps/portal/src/pages/resumes/[resumeId].tsx
@@ -24,23 +24,17 @@ import ResumePdf from '~/components/resumes/ResumePdf';
import ResumeExpandableText from '~/components/resumes/shared/ResumeExpandableText';
import loginPageHref from '~/components/shared/loginPageHref';
-import type {
- ExperienceFilter,
- FilterOption,
- LocationFilter,
- RoleFilter,
-} from '~/utils/resumes/resumeFilters';
import {
BROWSE_TABS_VALUES,
- EXPERIENCES,
getFilterLabel,
+ getTypeaheadOption,
INITIAL_FILTER_STATE,
- LOCATIONS,
- ROLES,
} from '~/utils/resumes/resumeFilters';
import { trpc } from '~/utils/trpc';
import SubmitResumeForm from './submit';
+import type { JobTitleType } from '../../components/shared/JobTitles';
+import { getLabelForJobTitleType } from '../../components/shared/JobTitles';
export default function ResumeReviewPage() {
const ErrorPage = (
@@ -124,29 +118,24 @@ export default function ResumeReviewPage() {
};
const onInfoTagClick = ({
- locationLabel,
- experienceLabel,
- roleLabel,
+ locationName,
+ locationValue,
+ experienceValue,
+ roleValue,
}: {
- experienceLabel?: string;
- locationLabel?: string;
- roleLabel?: string;
+ experienceValue?: string;
+ locationName?: string;
+ locationValue?: string;
+ roleValue?: string;
}) => {
- const getFilterValue = (
- label: string,
- filterOptions: Array<
- FilterOption
- >,
- ) => filterOptions.find((option) => option.label === label)?.value;
-
router.push({
pathname: '/resumes',
query: {
currentPage: JSON.stringify(1),
isFiltersOpen: JSON.stringify({
- experience: experienceLabel !== undefined,
- location: locationLabel !== undefined,
- role: roleLabel !== undefined,
+ experience: experienceValue !== undefined,
+ location: locationValue !== undefined,
+ role: roleValue !== undefined,
}),
searchValue: JSON.stringify(''),
shortcutSelected: JSON.stringify('all'),
@@ -154,14 +143,16 @@ export default function ResumeReviewPage() {
tabsValue: JSON.stringify(BROWSE_TABS_VALUES.ALL),
userFilters: JSON.stringify({
...INITIAL_FILTER_STATE,
- ...(locationLabel && {
- location: [getFilterValue(locationLabel, LOCATIONS)],
+ ...(locationValue && {
+ location: [
+ getTypeaheadOption('location', locationValue, locationName),
+ ],
}),
- ...(roleLabel && {
- role: [getFilterValue(roleLabel, ROLES)],
+ ...(roleValue && {
+ role: [getTypeaheadOption('role', roleValue)],
}),
- ...(experienceLabel && {
- experience: [getFilterValue(experienceLabel, EXPERIENCES)],
+ ...(experienceValue && {
+ experience: [getTypeaheadOption('experience', experienceValue)],
}),
}),
},
@@ -207,9 +198,19 @@ export default function ResumeReviewPage() {
initFormDetails={{
additionalInfo: detailsQuery.data.additionalInfo ?? '',
experience: detailsQuery.data.experience,
- location: detailsQuery.data.location,
+ location: {
+ id: detailsQuery.data.locationId,
+ label: detailsQuery.data.location.name,
+ value: detailsQuery.data.locationId,
+ },
resumeId: resumeId as string,
- role: detailsQuery.data.role,
+ role: {
+ id: detailsQuery.data.role,
+ label: getLabelForJobTitleType(
+ detailsQuery.data.role as JobTitleType,
+ ),
+ value: detailsQuery.data.role,
+ },
title: detailsQuery.data.title,
url: detailsQuery.data.url,
}}
@@ -325,13 +326,10 @@ export default function ResumeReviewPage() {
type="button"
onClick={() =>
onInfoTagClick({
- roleLabel: detailsQuery.data?.role,
+ roleValue: detailsQuery.data?.role,
})
}>
- {getFilterLabel(
- ROLES,
- detailsQuery.data.role as RoleFilter,
- )}
+ {getFilterLabel('role', detailsQuery.data.role)}
@@ -344,13 +342,11 @@ export default function ResumeReviewPage() {
type="button"
onClick={() =>
onInfoTagClick({
- locationLabel: detailsQuery.data?.location,
+ locationName: detailsQuery.data?.location.name,
+ locationValue: detailsQuery.data?.locationId,
})
}>
- {getFilterLabel(
- LOCATIONS,
- detailsQuery.data.location as LocationFilter,
- )}
+ {detailsQuery.data?.location.name}
@@ -363,12 +359,12 @@ export default function ResumeReviewPage() {
type="button"
onClick={() =>
onInfoTagClick({
- experienceLabel: detailsQuery.data?.experience,
+ experienceValue: detailsQuery.data?.experience,
})
}>
{getFilterLabel(
- EXPERIENCES,
- detailsQuery.data.experience as ExperienceFilter,
+ 'experience',
+ detailsQuery.data.experience,
)}
diff --git a/apps/portal/src/pages/resumes/index.tsx b/apps/portal/src/pages/resumes/index.tsx
index 46bb9476..142dd3c0 100644
--- a/apps/portal/src/pages/resumes/index.tsx
+++ b/apps/portal/src/pages/resumes/index.tsx
@@ -9,6 +9,7 @@ import {
NewspaperIcon,
XMarkIcon,
} from '@heroicons/react/24/outline';
+import type { TypeaheadOption } from '@tih/ui';
import {
Button,
CheckboxInput,
@@ -23,23 +24,17 @@ import {
import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics';
import ResumeFilterPill from '~/components/resumes/browse/ResumeFilterPill';
import ResumeListItems from '~/components/resumes/browse/ResumeListItems';
+import ResumeExperienceTypeahead from '~/components/resumes/shared/ResumeExperienceTypeahead';
import ResumeSignInButton from '~/components/resumes/shared/ResumeSignInButton';
+import CountriesTypeahead from '~/components/shared/CountriesTypeahead';
import loginPageHref from '~/components/shared/loginPageHref';
-import type {
- Filter,
- FilterId,
- FilterLabel,
- Shortcut,
-} from '~/utils/resumes/resumeFilters';
+import type { Filter, FilterId, Shortcut } from '~/utils/resumes/resumeFilters';
+import type { SortOrder } from '~/utils/resumes/resumeFilters';
import {
BROWSE_TABS_VALUES,
- EXPERIENCES,
getFilterLabel,
INITIAL_FILTER_STATE,
- isInitialFilterState,
- LOCATIONS,
- ROLES,
SHORTCUTS,
SORT_OPTIONS,
} from '~/utils/resumes/resumeFilters';
@@ -47,7 +42,7 @@ import useDebounceValue from '~/utils/resumes/useDebounceValue';
import useSearchParams from '~/utils/resumes/useSearchParams';
import { trpc } from '~/utils/trpc';
-import type { FilterState, SortOrder } from '../../utils/resumes/resumeFilters';
+import JobTitlesTypeahead from '../../components/shared/JobTitlesTypeahead';
const STALE_TIME = 5 * 60 * 1000;
const DEBOUNCE_DELAY = 800;
@@ -56,17 +51,14 @@ const filters: Array
= [
{
id: 'role',
label: 'Role',
- options: ROLES,
},
{
id: 'experience',
label: 'Experience',
- options: EXPERIENCES,
},
{
id: 'location',
label: 'Location',
- options: LOCATIONS,
},
];
@@ -81,20 +73,14 @@ const getLoggedOutText = (tabsValue: string) => {
}
};
-const getEmptyDataText = (
- tabsValue: string,
- searchValue: string,
- userFilters: FilterState,
-) => {
+const getEmptyDataText = (tabsValue: string, searchValue: string) => {
if (searchValue.length > 0) {
return 'Try tweaking your search text to see more resumes.';
}
- if (!isInitialFilterState(userFilters)) {
- return 'Try tweaking your filters to see more resumes.';
- }
+
switch (tabsValue) {
case BROWSE_TABS_VALUES.ALL:
- return "There's nothing to see here...";
+ return 'Oops, there is no resumes to see here. Maybe try tweaking your filters to see more.';
case BROWSE_TABS_VALUES.STARRED:
return 'You have not starred any resumes. Star one to see it here!';
case BROWSE_TABS_VALUES.MY:
@@ -200,10 +186,11 @@ export default function ResumeHomePage() {
[
'resumes.resume.findAll',
{
- experienceFilters: userFilters.experience,
+ experienceFilters: userFilters.experience.map(({ value }) => value),
+ isTop10: userFilters.isTop10,
isUnreviewed: userFilters.isUnreviewed,
- locationFilters: userFilters.location,
- roleFilters: userFilters.role,
+ locationFilters: userFilters.location.map(({ value }) => value),
+ roleFilters: userFilters.role.map(({ value }) => value),
searchValue: useDebounceValue(searchValue, DEBOUNCE_DELAY),
skip,
sortOrder,
@@ -219,10 +206,11 @@ export default function ResumeHomePage() {
[
'resumes.resume.user.findUserStarred',
{
- experienceFilters: userFilters.experience,
+ experienceFilters: userFilters.experience.map(({ value }) => value),
+ isTop10: userFilters.isTop10,
isUnreviewed: userFilters.isUnreviewed,
- locationFilters: userFilters.location,
- roleFilters: userFilters.role,
+ locationFilters: userFilters.location.map(({ value }) => value),
+ roleFilters: userFilters.role.map(({ value }) => value),
searchValue: useDebounceValue(searchValue, DEBOUNCE_DELAY),
skip,
sortOrder,
@@ -239,10 +227,11 @@ export default function ResumeHomePage() {
[
'resumes.resume.user.findUserCreated',
{
- experienceFilters: userFilters.experience,
+ experienceFilters: userFilters.experience.map(({ value }) => value),
+ isTop10: userFilters.isTop10,
isUnreviewed: userFilters.isUnreviewed,
- locationFilters: userFilters.location,
- roleFilters: userFilters.role,
+ locationFilters: userFilters.location.map(({ value }) => value),
+ roleFilters: userFilters.role.map(({ value }) => value),
searchValue: useDebounceValue(searchValue, DEBOUNCE_DELAY),
skip,
sortOrder,
@@ -264,31 +253,6 @@ export default function ResumeHomePage() {
}
};
- const onFilterCheckboxChange = (
- isChecked: boolean,
- filterSection: FilterId,
- filterValue: string,
- ) => {
- if (isChecked) {
- setUserFilters({
- ...userFilters,
- [filterSection]: [...userFilters[filterSection], filterValue],
- });
- } else {
- setUserFilters({
- ...userFilters,
- [filterSection]: userFilters[filterSection].filter(
- (value) => value !== filterValue,
- ),
- });
- }
- gaEvent({
- action: 'resumes.filter_checkbox_click',
- category: 'engagement',
- label: 'Select Filter',
- });
- };
-
const onClearFilterClick = (filterSection: FilterId) => {
setUserFilters({
...userFilters,
@@ -354,12 +318,74 @@ export default function ResumeHomePage() {
return getTabQueryData()?.filterCounts;
};
- const getFilterCount = (filter: FilterLabel, value: string) => {
+ const getFilterTypeahead = (filterId: FilterId) => {
+ const onSelect = (option: TypeaheadOption | null) => {
+ if (option === null) {
+ return;
+ }
+ setUserFilters({
+ ...userFilters,
+ [filterId]: [...userFilters[filterId], option],
+ });
+ gaEvent({
+ action: 'resumes.filter_typeahead_click',
+ category: 'engagement',
+ label: 'Select Filter',
+ });
+ };
+
+ switch (filterId) {
+ case 'experience':
+ return (
+ value))
+ }
+ onSelect={onSelect}
+ />
+ );
+ case 'location':
+ return (
+ value))
+ }
+ isLabelHidden={true}
+ label="Location"
+ placeholder="Select countries"
+ onSelect={onSelect}
+ />
+ );
+ case 'role':
+ return (
+ value))
+ }
+ isLabelHidden={true}
+ label="Role"
+ noResultsMessage="No available roles."
+ placeholder="Select roles"
+ onSelect={onSelect}
+ />
+ );
+ default:
+ return null;
+ }
+ };
+
+ const getFilterCount = (filterId: FilterId, value: string) => {
const filterCountsData = getTabFilterCounts();
- if (!filterCountsData) {
+ if (
+ filterCountsData === undefined ||
+ filterCountsData[filterId] === undefined ||
+ filterCountsData[filterId][value] === undefined
+ ) {
return 0;
}
- return filterCountsData[filter][value];
+ return filterCountsData[filterId][value];
};
return (
@@ -461,29 +487,28 @@ export default function ResumeHomePage() {
- {filter.options.map((option) => (
+ {getFilterTypeahead(filter.id)}
+ {userFilters[filter.id].map((option) => (
+ className="flex items-center px-1 text-sm">
- onFilterCheckboxChange(
- isChecked,
- filter.id,
- option.value,
- )
+ value={true}
+ onChange={() =>
+ setUserFilters({
+ ...userFilters,
+ [filter.id]: userFilters[
+ filter.id
+ ].filter(
+ ({ value }) =>
+ value !== option.value,
+ ),
+ })
}
/>
- (
- {getFilterCount(
- filter.label,
- option.label,
- )}
+ ({getFilterCount(filter.id, option.value)}
)
@@ -570,32 +595,32 @@ export default function ResumeHomePage() {
+ {getFilterTypeahead(filter.id)}
- {filter.options.map((option) => (
+ {userFilters[filter.id].map((option) => (
+ className="flex items-center px-1 text-sm">
- onFilterCheckboxChange(
- isChecked,
- filter.id,
- option.value,
- )
+ value={true}
+ onChange={() =>
+ setUserFilters({
+ ...userFilters,
+ [filter.id]: userFilters[
+ filter.id
+ ].filter(
+ ({ value }) => value !== option.value,
+ ),
+ })
}
/>
- (
- {getFilterCount(filter.label, option.label)}
- )
+ ({getFilterCount(filter.id, option.value)})
))}
@@ -660,7 +685,7 @@ export default function ResumeHomePage() {
+ label={getFilterLabel('sort', sortOrder)}>
{SORT_OPTIONS.map(({ label, value }) => (
- {getEmptyDataText(tabsValue, searchValue, userFilters)}
+ {getEmptyDataText(tabsValue, searchValue)}