From 9f24e0bcca2f386479b5966832c2ccd920a9a6cc Mon Sep 17 00:00:00 2001 From: Wu Peirong Date: Sun, 16 Oct 2022 17:15:33 +0800 Subject: [PATCH] [resumes][feat] implement filter shortcuts --- .../resumes/browse/resumeConstants.ts | 96 ------------ .../resumes/browse/resumeFilters.ts | 146 ++++++++++++++++++ apps/portal/src/pages/resumes/browse.tsx | 75 ++++----- apps/portal/src/pages/resumes/submit.tsx | 25 ++- 4 files changed, 194 insertions(+), 148 deletions(-) delete mode 100644 apps/portal/src/components/resumes/browse/resumeConstants.ts create mode 100644 apps/portal/src/components/resumes/browse/resumeFilters.ts diff --git a/apps/portal/src/components/resumes/browse/resumeConstants.ts b/apps/portal/src/components/resumes/browse/resumeConstants.ts deleted file mode 100644 index 9f0a2058..00000000 --- a/apps/portal/src/components/resumes/browse/resumeConstants.ts +++ /dev/null @@ -1,96 +0,0 @@ -export const BROWSE_TABS_VALUES = { - ALL: 'all', - MY: 'my', - STARRED: 'starred', -}; - -export type SortOrder = 'latest' | 'popular' | 'topComments'; -type SortOption = { - name: string; - value: SortOrder; -}; - -export const SORT_OPTIONS: Array = [ - { name: 'Latest', value: 'latest' }, - { name: 'Popular', value: 'popular' }, - { name: 'Top Comments', value: 'topComments' }, -]; - -export const TOP_HITS = [ - { href: '#', name: 'Unreviewed' }, - { href: '#', name: 'Fresh Grad' }, - { href: '#', name: 'GOATs' }, - { href: '#', name: 'US Only' }, -]; - -export type FilterOption = { - label: string; - value: string; -}; - -export const ROLE: Array = [ - { - label: 'Full-Stack Engineer', - value: 'Full-Stack 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 EXPERIENCE: Array = [ - { label: 'Freshman', value: 'Freshman' }, - { label: 'Sophomore', value: 'Sophomore' }, - { label: 'Junior', value: 'Junior' }, - { label: 'Senior', value: 'Senior' }, - { - label: 'Entry Level (0 - 2 years)', - value: 'Entry Level (0 - 2 years)', - }, - { - label: 'Mid Level (3 - 5 years)', - value: 'Mid Level (3 - 5 years)', - }, - { - label: 'Senior Level (5+ years)', - value: 'Senior Level (5+ years)', - }, -]; - -export const LOCATION: Array = [ - { label: 'Singapore', value: 'Singapore' }, - { label: 'United States', value: 'United States' }, - { label: 'India', value: 'India' }, -]; - -export const TEST_RESUMES = [ - { - createdAt: new Date(), - experience: 'Fresh Grad (0-1 years)', - numComments: 9, - numStars: 1, - role: 'Backend Engineer', - title: 'Rejected from multiple companies, please help...:(', - user: 'Git Ji Ra', - }, - { - createdAt: new Date(), - experience: 'Fresh Grad (0-1 years)', - numComments: 9, - numStars: 1, - role: 'Backend Engineer', - title: 'Rejected from multiple companies, please help...:(', - user: 'Git Ji Ra', - }, - { - createdAt: new Date(), - experience: 'Fresh Grad (0-1 years)', - numComments: 9, - numStars: 1, - role: 'Backend Engineer', - title: 'Rejected from multiple companies, please help...:(', - user: 'Git Ji Ra', - }, -]; diff --git a/apps/portal/src/components/resumes/browse/resumeFilters.ts b/apps/portal/src/components/resumes/browse/resumeFilters.ts new file mode 100644 index 00000000..ac36d456 --- /dev/null +++ b/apps/portal/src/components/resumes/browse/resumeFilters.ts @@ -0,0 +1,146 @@ +export type FilterId = 'experience' | 'location' | 'role'; + +export type CustomFilter = { + numComments: number; +}; + +type RoleFilter = + | 'Android Engineer' + | 'Backend Engineer' + | 'DevOps Engineer' + | 'Frontend Engineer' + | 'Full-Stack Engineer' + | 'iOS Engineer'; + +type ExperienceFilter = + | 'Entry Level (0 - 2 years)' + | 'Freshman' + | 'Junior' + | 'Mid Level (3 - 5 years)' + | 'Senior Level (5+ years)' + | 'Senior' + | 'Sophomore'; + +type LocationFilter = 'India' | 'Singapore' | 'United States'; + +export type FilterValue = ExperienceFilter | LocationFilter | RoleFilter; + +export type FilterOption = { + label: string; + value: T; +}; + +export type Filter = { + id: FilterId; + label: string; + options: Array>; +}; + +export type FilterState = Partial & + Record>; + +export type SortOrder = 'latest' | 'popular' | 'topComments'; + +type SortOption = { + name: string; + value: SortOrder; +}; + +export type Shortcut = { + customFilters?: CustomFilter; + filters: FilterState; + name: string; + sortOrder: SortOrder; +}; + +export const BROWSE_TABS_VALUES = { + ALL: 'all', + MY: 'my', + STARRED: 'starred', +}; + +export const SORT_OPTIONS: Array = [ + { name: 'Latest', value: 'latest' }, + { name: 'Popular', value: 'popular' }, + { name: 'Top Comments', value: 'topComments' }, +]; + +export const ROLE: Array> = [ + { + label: 'Full-Stack Engineer', + value: 'Full-Stack 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 EXPERIENCE: Array> = [ + { label: 'Freshman', value: 'Freshman' }, + { label: 'Sophomore', value: 'Sophomore' }, + { label: 'Junior', value: 'Junior' }, + { label: 'Senior', value: 'Senior' }, + { + label: 'Entry Level (0 - 2 years)', + value: 'Entry Level (0 - 2 years)', + }, + { + label: 'Mid Level (3 - 5 years)', + value: 'Mid Level (3 - 5 years)', + }, + { + label: 'Senior Level (5+ years)', + value: 'Senior Level (5+ years)', + }, +]; + +export const LOCATION: Array> = [ + { label: 'Singapore', value: 'Singapore' }, + { label: 'United States', value: 'United States' }, + { label: 'India', value: 'India' }, +]; + +export const INITIAL_FILTER_STATE: FilterState = { + experience: Object.values(EXPERIENCE).map(({ value }) => value), + location: Object.values(LOCATION).map(({ value }) => value), + role: Object.values(ROLE).map(({ value }) => value), +}; + +export const SHORTCUTS: Array = [ + { + filters: INITIAL_FILTER_STATE, + name: 'All', + sortOrder: 'latest', + }, + { + filters: { + ...INITIAL_FILTER_STATE, + numComments: 0, + }, + name: 'Unreviewed', + sortOrder: 'latest', + }, + { + filters: { + ...INITIAL_FILTER_STATE, + experience: ['Entry Level (0 - 2 years)'], + }, + name: 'Fresh Grad', + sortOrder: 'latest', + }, + { + filters: INITIAL_FILTER_STATE, + name: 'GOATs', + sortOrder: 'popular', + }, + { + filters: { + ...INITIAL_FILTER_STATE, + location: ['United States'], + }, + name: 'US Only', + sortOrder: 'latest', + }, +]; diff --git a/apps/portal/src/pages/resumes/browse.tsx b/apps/portal/src/pages/resumes/browse.tsx index 7b08ba02..1ddd5f05 100644 --- a/apps/portal/src/pages/resumes/browse.tsx +++ b/apps/portal/src/pages/resumes/browse.tsx @@ -14,19 +14,24 @@ import { TextInput, } from '@tih/ui'; +import ResumeFilterPill from '~/components/resumes/browse/ResumeFilterPill'; import type { - FilterOption, + Filter, + FilterId, + FilterState, + FilterValue, + Shortcut, SortOrder, -} from '~/components/resumes/browse/resumeConstants'; +} from '~/components/resumes/browse/resumeFilters'; import { BROWSE_TABS_VALUES, EXPERIENCE, + INITIAL_FILTER_STATE, LOCATION, ROLE, + SHORTCUTS, SORT_OPTIONS, - TOP_HITS, -} from '~/components/resumes/browse/resumeConstants'; -import ResumeFilterPill from '~/components/resumes/browse/ResumeFilterPill'; +} from '~/components/resumes/browse/resumeFilters'; import ResumeListItems from '~/components/resumes/browse/ResumeListItems'; import ResumeReviewsTitle from '~/components/resumes/ResumeReviewsTitle'; import ResumeSignInButton from '~/components/resumes/shared/ResumeSignInButton'; @@ -35,38 +40,24 @@ import { trpc } from '~/utils/trpc'; import type { Resume } from '~/types/resume'; -type FilterId = 'experience' | 'location' | 'role'; -type Filter = { - id: FilterId; - name: string; - options: Array; -}; -type FilterState = Record>; - const filters: Array = [ { id: 'role', - name: 'Role', + label: 'Role', options: ROLE, }, { id: 'experience', - name: 'Experience', + label: 'Experience', options: EXPERIENCE, }, { id: 'location', - name: 'Location', + label: 'Location', options: LOCATION, }, ]; -const INITIAL_FILTER_STATE: FilterState = { - experience: Object.values(EXPERIENCE).map(({ value }) => value), - location: Object.values(LOCATION).map(({ value }) => value), - role: Object.values(ROLE).map(({ value }) => value), -}; - const filterResumes = ( resumes: Array, searchValue: string, @@ -78,9 +69,14 @@ const filterResumes = ( ) .filter( ({ experience, location, role }) => - userFilters.role.includes(role) && - userFilters.experience.includes(experience) && - userFilters.location.includes(location), + userFilters.role.includes(role as FilterValue) && + userFilters.experience.includes(experience as FilterValue) && + userFilters.location.includes(location as FilterValue), + ) + .filter( + ({ numComments }) => + userFilters.numComments === undefined || + numComments === userFilters.numComments, ); const sortComparators: Record< @@ -172,6 +168,14 @@ export default function ResumeHomePage() { } }; + const onShortcutChange = ({ + sortOrder: shortcutSortOrder, + filters: shortcutFilters, + }: Shortcut) => { + setSortOrder(shortcutSortOrder); + setUserFilters(shortcutFilters); + }; + return ( <> @@ -258,12 +262,11 @@ export default function ResumeHomePage() {
    - {TOP_HITS.map((category) => ( -
  • - {/* TODO: Replace onClick with filtering function */} + {SHORTCUTS.map((shortcut) => ( +
  • true} + title={shortcut.name} + onClick={() => onShortcutChange(shortcut)} />
  • ))} @@ -271,9 +274,9 @@ export default function ResumeHomePage() {

    Explore these filters:

    - {filters.map((section) => ( + {filters.map((filter) => ( {({ open }) => ( @@ -281,7 +284,7 @@ export default function ResumeHomePage() {

    - {section.name} + {filter.label} {open ? ( @@ -304,19 +307,19 @@ export default function ResumeHomePage() { isLabelHidden={true} label="" orientation="vertical"> - {section.options.map((option) => ( + {filter.options.map((option) => (
    onFilterCheckboxChange( isChecked, - section.id, + filter.id, option.value, ) } diff --git a/apps/portal/src/pages/resumes/submit.tsx b/apps/portal/src/pages/resumes/submit.tsx index 471cd511..b6ee74d8 100644 --- a/apps/portal/src/pages/resumes/submit.tsx +++ b/apps/portal/src/pages/resumes/submit.tsx @@ -18,12 +18,12 @@ import { TextInput, } from '@tih/ui'; -import type { FilterOption } from '~/components/resumes/browse/resumeConstants'; +import type { Filter } from '~/components/resumes/browse/resumeFilters'; import { EXPERIENCE, LOCATION, ROLE, -} from '~/components/resumes/browse/resumeConstants'; +} from '~/components/resumes/browse/resumeFilters'; import SubmissionGuidelines from '~/components/resumes/submit-form/SubmissionGuidelines'; import { RESUME_STORAGE_KEY } from '~/constants/file-storage-keys'; @@ -47,17 +47,10 @@ type IFormInput = { title: string; }; -type SelectorType = 'experience' | 'location' | 'role'; -type SelectorOptions = { - key: SelectorType; - label: string; - options: Array; -}; - -const selectors: Array = [ - { key: 'role', label: 'Role', options: ROLE }, - { key: 'experience', label: 'Experience Level', options: EXPERIENCE }, - { key: 'location', label: 'Location', options: LOCATION }, +const selectors: Array = [ + { id: 'role', label: 'Role', options: ROLE }, + { id: 'experience', label: 'Experience Level', options: EXPERIENCE }, + { id: 'location', label: 'Location', options: LOCATION }, ]; type InitFormDetails = { @@ -309,14 +302,14 @@ export default function SubmitResumeForm({
    {/* Selectors */} {selectors.map((item) => ( -
    +