[portal][feat] add ranking to job titles typeahead

pull/519/head
Yangshun Tay 2 years ago
parent ac215dcbff
commit 79500b8a35

@ -3,7 +3,7 @@ import { useFormContext, useWatch } from 'react-hook-form';
import type { JobTitleType } from '~/components/shared/JobTitles'; import type { JobTitleType } from '~/components/shared/JobTitles';
import { getLabelForJobTitleType } from '~/components/shared/JobTitles'; import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
import JobTitlesTypeahead from '~/components/shared/JobTitlesTypahead'; import JobTitlesTypeahead from '~/components/shared/JobTitlesTypeahead';
type Props = Omit< type Props = Omit<
ComponentProps<typeof JobTitlesTypeahead>, ComponentProps<typeof JobTitlesTypeahead>,
@ -21,11 +21,15 @@ export default function FormJobTitlesTypeahead({ name, ...props }: Props) {
return ( return (
<JobTitlesTypeahead <JobTitlesTypeahead
{...props} {...props}
value={{ value={
watchJobTitle
? {
id: watchJobTitle, id: watchJobTitle,
label: getLabelForJobTitleType(watchJobTitle as JobTitleType), label: getLabelForJobTitleType(watchJobTitle as JobTitleType),
value: watchJobTitle, value: watchJobTitle,
}} }
: null
}
onSelect={(option) => { onSelect={(option) => {
setValue(name, option?.value); setValue(name, option?.value);
}} }}

@ -12,7 +12,7 @@ export type RoleTypeaheadProps = Omit<
>; >;
const ROLES: FilterChoices = Object.entries(JobTitleLabels).map( const ROLES: FilterChoices = Object.entries(JobTitleLabels).map(
([slug, label]) => ({ ([slug, { label }]) => ({
id: slug, id: slug,
label, label,
value: slug, value: slug,

@ -30,7 +30,7 @@ export default function ResumeRoleTypeahead({
}: Props) { }: Props) {
const [query, setQuery] = useState(''); const [query, setQuery] = useState('');
const options = Object.entries(JobTitleLabels) const options = Object.entries(JobTitleLabels)
.map(([slug, label]) => ({ .map(([slug, { label }]) => ({
id: slug, id: slug,
label, label,
value: slug, value: slug,

@ -1,56 +1,82 @@
export const JobTitleLabels = { type JobTitleData = Record<
'ai-engineer': 'Artificial Intelligence (AI) Engineer', string,
'algorithms-engineer': 'Algorithms Engineer', Readonly<{
'android-engineer': 'Android Software Engineer', label: string;
'applications-engineer': 'Applications Engineer', ranking: number;
'back-end-engineer': 'Back End Engineer', }>
'business-analyst': 'Business Analyst', >;
'business-engineer': 'Business Engineer',
'capacity-engineer': 'Capacity Engineer', export const JobTitleLabels: JobTitleData = {
'customer-engineer': 'Customer Engineer', 'ai-engineer': { label: 'Artificial Intelligence (AI) Engineer', ranking: 5 },
'data-analyst': 'Data Analyst', 'algorithms-engineer': { label: 'Algorithms Engineer', ranking: 0 },
'data-engineer': 'Data Engineer', 'android-engineer': { label: 'Android Software Engineer', ranking: 8 },
'data-scientist': 'Data Scientist', 'applications-engineer': { label: 'Applications Engineer', ranking: 0 },
'devops-engineer': 'DevOps Engineer', 'back-end-engineer': { label: 'Back End Engineer', ranking: 9 },
'engineering-director': 'Engineering Director', 'business-analyst': { label: 'Business Analyst', ranking: 0 },
'engineering-manager': 'Engineering Manager', 'business-engineer': { label: 'Business Engineer', ranking: 5 },
'enterprise-engineer': 'Enterprise Engineer', 'capacity-engineer': { label: 'Capacity Engineer', ranking: 0 },
'forward-deployed-engineer': 'Forward Deployed Engineer', 'customer-engineer': { label: 'Customer Engineer', ranking: 0 },
'front-end-engineer': 'Front End Engineer', 'data-analyst': { label: 'Data Analyst', ranking: 0 },
'full-stack-engineer': 'Full Stack Engineer', 'data-engineer': { label: 'Data Engineer', ranking: 0 },
'gameplay-engineer': 'Gameplay Engineer', 'data-scientist': { label: 'Data Scientist', ranking: 5 },
'hardware-engineer': 'Hardware Engineer', 'devops-engineer': { label: 'DevOps Engineer', ranking: 0 },
'infrastructure-engineer': 'Infrastructure Engineer', 'engineering-director': { label: 'Engineering Director', ranking: 0 },
'ios-engineer': 'iOS Software Engineer', 'engineering-manager': { label: 'Engineering Manager', ranking: 0 },
'machine-learning-engineer': 'Machine Learning (ML) Engineer', 'enterprise-engineer': { label: 'Enterprise Engineer', ranking: 0 },
'machine-learning-researcher': 'Machine Learning (ML) Researcher', 'forward-deployed-engineer': {
'mobile-engineer': 'Mobile Software Engineer (iOS + Android)', label: 'Forward Deployed Engineer (FDE)',
'networks-engineer': 'Networks Engineer', ranking: 0,
'partner-engineer': 'Partner Engineer', },
'product-engineer': 'Product Engineer', 'front-end-engineer': { label: 'Front End Engineer', ranking: 9 },
'product-manager': 'Product Manager', 'full-stack-engineer': { label: 'Full Stack Engineer', ranking: 9 },
'production-engineer': 'Production Engineer', 'gameplay-engineer': { label: 'Gameplay Engineer', ranking: 0 },
'project-manager': 'Project Manager', 'hardware-engineer': { label: 'Hardware Engineer', ranking: 0 },
'release-engineer': 'Release Engineer', 'infrastructure-engineer': { label: 'Infrastructure Engineer', ranking: 0 },
'research-engineer': 'Research Engineer', 'ios-engineer': { label: 'iOS Software Engineer', ranking: 0 },
'research-scientist': 'Research Scientist', 'machine-learning-engineer': {
'rotational-engineer': 'Rotational Engineer', label: 'Machine Learning (ML) Engineer',
'sales-engineer': 'Sales Engineer', ranking: 5,
'security-engineer': 'Security Engineer', },
'site-reliability-engineer': 'Site Reliability Engineer (SRE)', 'machine-learning-researcher': {
'software-engineer': 'Software Engineer', label: 'Machine Learning (ML) Researcher',
'solutions-architect': 'Solutions Architect', ranking: 0,
'solutions-engineer': 'Solutions Engineer', },
'systems-analyst': 'Systems Analyst', 'mobile-engineer': {
'systems-engineer': 'Systems Engineer', label: 'Mobile Software Engineer (iOS + Android)',
'tech-ops-engineer': 'Tech Ops Engineer', ranking: 8,
'technical-program-manager': 'Technical Program Manager', },
'test-engineer': 'QA/Test Engineer (SDET)', 'networks-engineer': { label: 'Networks Engineer', ranking: 0 },
'ux-engineer': 'User Experience (UX) Engineer', 'partner-engineer': { label: 'Partner Engineer', ranking: 0 },
'product-engineer': { label: 'Product Engineer', ranking: 7 },
'product-manager': { label: 'Product Manager', ranking: 0 },
'production-engineer': { label: 'Production Engineer', ranking: 8 },
'project-manager': { label: 'Project Manager', ranking: 0 },
'release-engineer': { label: 'Release Engineer', ranking: 0 },
'research-engineer': { label: 'Research Engineer', ranking: 6 },
'research-scientist': { label: 'Research Scientist', ranking: 7 },
'rotational-engineer': { label: 'Rotational Engineer', ranking: 0 },
'sales-engineer': { label: 'Sales Engineer', ranking: 0 },
'security-engineer': { label: 'Security Engineer', ranking: 7 },
'site-reliability-engineer': {
label: 'Site Reliability Engineer (SRE)',
ranking: 8,
},
'software-engineer': { label: 'Software Engineer', ranking: 10 },
'solutions-architect': { label: 'Solutions Architect', ranking: 0 },
'solutions-engineer': { label: 'Solutions Engineer', ranking: 0 },
'systems-analyst': { label: 'Systems Analyst', ranking: 0 },
'systems-engineer': { label: 'Systems Engineer', ranking: 0 },
'tech-ops-engineer': { label: 'Tech Ops Engineer', ranking: 0 },
'technical-program-manager': {
label: 'Technical Program Manager',
ranking: 0,
},
'test-engineer': { label: 'QA/Test Engineer (SDET)', ranking: 6 },
'ux-engineer': { label: 'User Experience (UX) Engineer', ranking: 0 },
}; };
export type JobTitleType = keyof typeof JobTitleLabels; export type JobTitleType = keyof typeof JobTitleLabels;
export function getLabelForJobTitleType(jobTitle: JobTitleType): string { export function getLabelForJobTitleType(jobTitle: JobTitleType): string {
return JobTitleLabels[jobTitle]; return JobTitleLabels[jobTitle].label;
} }

@ -28,11 +28,13 @@ export default function JobTitlesTypeahead({
}: Props) { }: Props) {
const [query, setQuery] = useState(''); const [query, setQuery] = useState('');
const options = Object.entries(JobTitleLabels) const options = Object.entries(JobTitleLabels)
.map(([slug, label]) => ({ .map(([slug, { label, ranking }]) => ({
id: slug, id: slug,
label, label,
ranking,
value: slug, value: slug,
})) }))
.sort((a, b) => b.ranking - a.ranking)
.filter( .filter(
({ label }) => ({ label }) =>
label.toLocaleLowerCase().indexOf(query.toLocaleLowerCase()) > -1, label.toLocaleLowerCase().indexOf(query.toLocaleLowerCase()) > -1,

@ -9,8 +9,8 @@ import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead';
import Container from '~/components/shared/Container'; import Container from '~/components/shared/Container';
import CountriesTypeahead from '~/components/shared/CountriesTypeahead'; import CountriesTypeahead from '~/components/shared/CountriesTypeahead';
import type { JobTitleType } from '~/components/shared/JobTitles'; import type { JobTitleType } from '~/components/shared/JobTitles';
import { JobTitleLabels } from '~/components/shared/JobTitles'; import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
import JobTitlesTypeahead from '~/components/shared/JobTitlesTypahead'; import JobTitlesTypeahead from '~/components/shared/JobTitlesTypeahead';
import { useSearchParamSingle } from '~/utils/offers/useSearchParam'; import { useSearchParamSingle } from '~/utils/offers/useSearchParam';
@ -79,7 +79,9 @@ export default function OffersHomePage() {
selectedJobTitleId selectedJobTitleId
? { ? {
id: selectedJobTitleId, id: selectedJobTitleId,
label: JobTitleLabels[selectedJobTitleId as JobTitleType], label: getLabelForJobTitleType(
selectedJobTitleId as JobTitleType,
),
value: selectedJobTitleId, value: selectedJobTitleId,
} }
: null : null

@ -17,7 +17,8 @@ import QuestionSearchBar from '~/components/questions/QuestionSearchBar';
import CompanyTypeahead from '~/components/questions/typeahead/CompanyTypeahead'; import CompanyTypeahead from '~/components/questions/typeahead/CompanyTypeahead';
import LocationTypeahead from '~/components/questions/typeahead/LocationTypeahead'; import LocationTypeahead from '~/components/questions/typeahead/LocationTypeahead';
import RoleTypeahead from '~/components/questions/typeahead/RoleTypeahead'; import RoleTypeahead from '~/components/questions/typeahead/RoleTypeahead';
import { JobTitleLabels } from '~/components/shared/JobTitles'; import type { JobTitleType } from '~/components/shared/JobTitles';
import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
import type { QuestionAge } from '~/utils/questions/constants'; import type { QuestionAge } from '~/utils/questions/constants';
import { QUESTION_SORT_TYPES } from '~/utils/questions/constants'; import { QUESTION_SORT_TYPES } from '~/utils/questions/constants';
@ -316,7 +317,7 @@ export default function QuestionsBrowsePage() {
return selectedRoles.map((role) => ({ return selectedRoles.map((role) => ({
checked: true, checked: true,
id: role, id: role,
label: JobTitleLabels[role as keyof typeof JobTitleLabels], label: getLabelForJobTitleType(role as JobTitleType),
value: role, value: role,
})); }));
}, [selectedRoles]); }, [selectedRoles]);

@ -7,7 +7,7 @@ import { HorizontalDivider } from '@tih/ui';
import CitiesTypeahead from '~/components/shared/CitiesTypeahead'; import CitiesTypeahead from '~/components/shared/CitiesTypeahead';
import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead'; import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead';
import CountriesTypeahead from '~/components/shared/CountriesTypeahead'; import CountriesTypeahead from '~/components/shared/CountriesTypeahead';
import JobTitlesTypeahead from '~/components/shared/JobTitlesTypahead'; import JobTitlesTypeahead from '~/components/shared/JobTitlesTypeahead';
import type { import type {
Month, Month,
MonthYearOptional, MonthYearOptional,

@ -1,4 +1,5 @@
import { JobTitleLabels } from '~/components/shared/JobTitles'; import type { JobTitleType } from '~/components/shared/JobTitles';
import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
import type { AggregatedQuestionEncounter } from '~/types/questions'; import type { AggregatedQuestionEncounter } from '~/types/questions';
@ -8,7 +9,7 @@ export default function relabelQuestionAggregates({
}: AggregatedQuestionEncounter) { }: AggregatedQuestionEncounter) {
const newRoleCounts = Object.fromEntries( const newRoleCounts = Object.fromEntries(
Object.entries(roleCounts).map(([roleId, count]) => [ Object.entries(roleCounts).map(([roleId, count]) => [
JobTitleLabels[roleId as keyof typeof JobTitleLabels], getLabelForJobTitleType(roleId as JobTitleType),
count, count,
]), ]),
); );

@ -1,6 +1,7 @@
import type { TypeaheadOption } from '@tih/ui'; import type { TypeaheadOption } from '@tih/ui';
import type { JobTitleType } from '~/components/shared/JobTitles'; import type { JobTitleType } from '~/components/shared/JobTitles';
import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
import { JobTitleLabels } from '~/components/shared/JobTitles'; import { JobTitleLabels } from '~/components/shared/JobTitles';
export type FilterId = 'experience' | 'location' | 'role'; export type FilterId = 'experience' | 'location' | 'role';
@ -42,7 +43,7 @@ export const getTypeaheadOption = (
case 'role': case 'role':
return { return {
id: filterValue, id: filterValue,
label: JobTitleLabels[filterValue as keyof typeof JobTitleLabels], label: getLabelForJobTitleType(filterValue as JobTitleType),
value: filterValue, value: filterValue,
}; };
case 'location': case 'location':
@ -222,7 +223,7 @@ export const getFilterLabel = (
filters = EXPERIENCES; filters = EXPERIENCES;
break; break;
case 'role': case 'role':
filters = Object.entries(JobTitleLabels).map(([slug, label]) => ({ filters = Object.entries(JobTitleLabels).map(([slug, { label }]) => ({
id: slug, id: slug,
label, label,
value: slug, value: slug,

Loading…
Cancel
Save