+
+ {renderReviewButton()}
+
+
+
+ Reviews
+
+
{detailsQuery.data.additionalInfo && (
diff --git a/apps/portal/src/pages/resumes/index.tsx b/apps/portal/src/pages/resumes/index.tsx
index 46bb9476..28e69114 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,6 +24,9 @@ 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 ResumeLocationTypeahead from '~/components/resumes/shared/ResumeLocationTypeahead';
+import ResumeRoleTypeahead from '~/components/resumes/shared/ResumeRoleTypeahead';
import ResumeSignInButton from '~/components/resumes/shared/ResumeSignInButton';
import loginPageHref from '~/components/shared/loginPageHref';
@@ -32,6 +36,7 @@ import type {
FilterLabel,
Shortcut,
} from '~/utils/resumes/resumeFilters';
+import type { FilterState, SortOrder } from '~/utils/resumes/resumeFilters';
import {
BROWSE_TABS_VALUES,
EXPERIENCES,
@@ -47,8 +52,6 @@ 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';
-
const STALE_TIME = 5 * 60 * 1000;
const DEBOUNCE_DELAY = 800;
const PAGE_LIMIT = 10;
@@ -200,10 +203,10 @@ export default function ResumeHomePage() {
[
'resumes.resume.findAll',
{
- experienceFilters: userFilters.experience,
+ experienceFilters: userFilters.experience.map(({ value }) => value),
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 +222,10 @@ export default function ResumeHomePage() {
[
'resumes.resume.user.findUserStarred',
{
- experienceFilters: userFilters.experience,
+ experienceFilters: userFilters.experience.map(({ value }) => value),
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 +242,10 @@ export default function ResumeHomePage() {
[
'resumes.resume.user.findUserCreated',
{
- experienceFilters: userFilters.experience,
+ experienceFilters: userFilters.experience.map(({ value }) => value),
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 +267,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,6 +332,52 @@ export default function ResumeHomePage() {
return getTabQueryData()?.filterCounts;
};
+ 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 (
+
+ );
+ case 'location':
+ return (
+
+ );
+ case 'role':
+ return (
+
+ );
+ default:
+ return null;
+ }
+ };
+
const getFilterCount = (filter: FilterLabel, value: string) => {
const filterCountsData = getTabFilterCounts();
if (!filterCountsData) {
@@ -461,21 +485,24 @@ export default function ResumeHomePage() {
- {filter.options.map((option) => (
+ {getFilterTypeahead(filter.id)}
+ {userFilters[filter.id].map((option) => (
- onFilterCheckboxChange(
- isChecked,
- filter.id,
- option.value,
- )
+ value={true}
+ onChange={() =>
+ setUserFilters({
+ ...userFilters,
+ [filter.id]: userFilters[
+ filter.id
+ ].filter(
+ ({ value }) =>
+ value !== option.value,
+ ),
+ })
}
/>
@@ -570,26 +597,28 @@ export default function ResumeHomePage() {
+ {getFilterTypeahead(filter.id)}
- {filter.options.map((option) => (
+ {userFilters[filter.id].map((option) => (
- onFilterCheckboxChange(
- isChecked,
- filter.id,
- option.value,
- )
+ value={true}
+ onChange={() =>
+ setUserFilters({
+ ...userFilters,
+ [filter.id]: userFilters[
+ filter.id
+ ].filter(
+ ({ value }) => value !== option.value,
+ ),
+ })
}
/>
@@ -660,7 +689,7 @@ export default function ResumeHomePage() {
+ label={getFilterLabel('sort', sortOrder)}>
{SORT_OPTIONS.map(({ label, value }) => (
= {
label: string;
value: T;
@@ -31,10 +17,11 @@ export type FilterOption = {
export type Filter = {
id: FilterId;
label: FilterLabel;
- options: Array>;
+ options: Array;
};
-export type FilterState = CustomFilter & Record>;
+export type FilterState = CustomFilter &
+ Record>;
export type SortOrder = 'latest' | 'mostComments' | 'popular';
@@ -57,45 +44,54 @@ export const SORT_OPTIONS: Array> = [
{ label: 'Most Comments', value: 'mostComments' },
];
-export const ROLES: Array> = [
+export const ROLES: Array = [
+ {
+ id: 'software-engineer',
+ label: 'Software Engineer',
+ value: 'software-engineer',
+ },
+ {
+ id: 'back-end-engineer',
+ label: 'Back End Engineer',
+ value: 'back-end-engineer',
+ },
{
- label: 'Full-Stack Engineer',
- value: 'Full-Stack Engineer',
+ id: 'front-end-engineer',
+ label: 'Front End Engineer',
+ value: 'front-end-engineer',
},
- { label: 'Frontend Engineer', value: 'Frontend Engineer' },
- { label: 'Backend Engineer', value: 'Backend Engineer' },
- { label: 'DevOps Engineer', value: 'DevOps Engineer' },
- { label: 'iOS Engineer', value: 'iOS Engineer' },
- { label: 'Android Engineer', value: 'Android Engineer' },
];
-export const EXPERIENCES: Array> = [
- { label: 'Internship', value: 'Internship' },
+export const EXPERIENCES: Array = [
{
+ id: 'Entry Level (0 - 2 years)',
label: 'Entry Level (0 - 2 years)',
value: 'Entry Level (0 - 2 years)',
},
{
+ id: 'Internship',
+ label: 'Internship',
+ value: 'Internship',
+ },
+ {
+ id: 'Mid Level (3 - 5 years)',
label: 'Mid Level (3 - 5 years)',
value: 'Mid Level (3 - 5 years)',
},
{
+ id: 'Senior Level (5+ years)',
label: 'Senior Level (5+ years)',
value: 'Senior Level (5+ years)',
},
];
-export const LOCATIONS: Array> = [
- { label: 'Singapore', value: 'Singapore' },
- { label: 'United States', value: 'United States' },
- { label: 'India', value: 'India' },
-];
+export const LOCATIONS: Array = [];
export const INITIAL_FILTER_STATE: FilterState = {
- experience: Object.values(EXPERIENCES).map(({ value }) => value),
+ experience: EXPERIENCES,
isUnreviewed: true,
- location: Object.values(LOCATIONS).map(({ value }) => value),
- role: Object.values(ROLES).map(({ value }) => value),
+ location: [],
+ role: ROLES,
};
export const SHORTCUTS: Array = [
@@ -118,7 +114,7 @@ export const SHORTCUTS: Array = [
{
filters: {
...INITIAL_FILTER_STATE,
- experience: ['Entry Level (0 - 2 years)'],
+ experience: [],
isUnreviewed: false,
},
name: 'Fresh Grad',
@@ -136,7 +132,7 @@ export const SHORTCUTS: Array = [
filters: {
...INITIAL_FILTER_STATE,
isUnreviewed: false,
- location: ['United States'],
+ location: [],
},
name: 'US Only',
sortOrder: 'latest',
@@ -154,8 +150,29 @@ export const isInitialFilterState = (filters: FilterState) =>
});
export const getFilterLabel = (
- filters: Array<
- FilterOption
- >,
- filterValue: ExperienceFilter | LocationFilter | RoleFilter | SortOrder,
-) => filters.find(({ value }) => value === filterValue)?.label ?? filterValue;
+ filterId: FilterId | 'sort',
+ filterValue: SortOrder | string,
+) => {
+ let filters: Array = [];
+
+ switch (filterId) {
+ case 'experience':
+ filters = EXPERIENCES;
+ break;
+ case 'location':
+ break;
+ case 'role':
+ filters = Object.entries(JobTitleLabels).map(([slug, label]) => ({
+ id: slug,
+ label,
+ value: slug,
+ }));
+ break;
+ case 'sort':
+ return SORT_OPTIONS.find(({ value }) => value === filterValue)?.label;
+ default:
+ break;
+ }
+
+ return filters.find(({ value }) => value === filterValue)?.label;
+};