mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2026-04-04 03:18:05 +08:00
[portal][feat] add ranking to job titles typeahead
This commit is contained in:
@@ -3,7 +3,7 @@ import { useFormContext, useWatch } from 'react-hook-form';
|
||||
|
||||
import type { JobTitleType } 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<
|
||||
ComponentProps<typeof JobTitlesTypeahead>,
|
||||
@@ -21,11 +21,15 @@ export default function FormJobTitlesTypeahead({ name, ...props }: Props) {
|
||||
return (
|
||||
<JobTitlesTypeahead
|
||||
{...props}
|
||||
value={{
|
||||
id: watchJobTitle,
|
||||
label: getLabelForJobTitleType(watchJobTitle as JobTitleType),
|
||||
value: watchJobTitle,
|
||||
}}
|
||||
value={
|
||||
watchJobTitle
|
||||
? {
|
||||
id: watchJobTitle,
|
||||
label: getLabelForJobTitleType(watchJobTitle as JobTitleType),
|
||||
value: watchJobTitle,
|
||||
}
|
||||
: null
|
||||
}
|
||||
onSelect={(option) => {
|
||||
setValue(name, option?.value);
|
||||
}}
|
||||
|
||||
@@ -12,7 +12,7 @@ export type RoleTypeaheadProps = Omit<
|
||||
>;
|
||||
|
||||
const ROLES: FilterChoices = Object.entries(JobTitleLabels).map(
|
||||
([slug, label]) => ({
|
||||
([slug, { label }]) => ({
|
||||
id: slug,
|
||||
label,
|
||||
value: slug,
|
||||
|
||||
@@ -30,7 +30,7 @@ export default function ResumeRoleTypeahead({
|
||||
}: Props) {
|
||||
const [query, setQuery] = useState('');
|
||||
const options = Object.entries(JobTitleLabels)
|
||||
.map(([slug, label]) => ({
|
||||
.map(([slug, { label }]) => ({
|
||||
id: slug,
|
||||
label,
|
||||
value: slug,
|
||||
|
||||
@@ -1,56 +1,82 @@
|
||||
export const JobTitleLabels = {
|
||||
'ai-engineer': 'Artificial Intelligence (AI) Engineer',
|
||||
'algorithms-engineer': 'Algorithms Engineer',
|
||||
'android-engineer': 'Android Software Engineer',
|
||||
'applications-engineer': 'Applications Engineer',
|
||||
'back-end-engineer': 'Back End Engineer',
|
||||
'business-analyst': 'Business Analyst',
|
||||
'business-engineer': 'Business Engineer',
|
||||
'capacity-engineer': 'Capacity Engineer',
|
||||
'customer-engineer': 'Customer Engineer',
|
||||
'data-analyst': 'Data Analyst',
|
||||
'data-engineer': 'Data Engineer',
|
||||
'data-scientist': 'Data Scientist',
|
||||
'devops-engineer': 'DevOps Engineer',
|
||||
'engineering-director': 'Engineering Director',
|
||||
'engineering-manager': 'Engineering Manager',
|
||||
'enterprise-engineer': 'Enterprise Engineer',
|
||||
'forward-deployed-engineer': 'Forward Deployed Engineer',
|
||||
'front-end-engineer': 'Front End Engineer',
|
||||
'full-stack-engineer': 'Full Stack Engineer',
|
||||
'gameplay-engineer': 'Gameplay Engineer',
|
||||
'hardware-engineer': 'Hardware Engineer',
|
||||
'infrastructure-engineer': 'Infrastructure Engineer',
|
||||
'ios-engineer': 'iOS Software Engineer',
|
||||
'machine-learning-engineer': 'Machine Learning (ML) Engineer',
|
||||
'machine-learning-researcher': 'Machine Learning (ML) Researcher',
|
||||
'mobile-engineer': 'Mobile Software Engineer (iOS + Android)',
|
||||
'networks-engineer': 'Networks Engineer',
|
||||
'partner-engineer': 'Partner Engineer',
|
||||
'product-engineer': 'Product Engineer',
|
||||
'product-manager': 'Product Manager',
|
||||
'production-engineer': 'Production Engineer',
|
||||
'project-manager': 'Project Manager',
|
||||
'release-engineer': 'Release Engineer',
|
||||
'research-engineer': 'Research Engineer',
|
||||
'research-scientist': 'Research Scientist',
|
||||
'rotational-engineer': 'Rotational Engineer',
|
||||
'sales-engineer': 'Sales Engineer',
|
||||
'security-engineer': 'Security Engineer',
|
||||
'site-reliability-engineer': 'Site Reliability Engineer (SRE)',
|
||||
'software-engineer': 'Software Engineer',
|
||||
'solutions-architect': 'Solutions Architect',
|
||||
'solutions-engineer': 'Solutions Engineer',
|
||||
'systems-analyst': 'Systems Analyst',
|
||||
'systems-engineer': 'Systems Engineer',
|
||||
'tech-ops-engineer': 'Tech Ops Engineer',
|
||||
'technical-program-manager': 'Technical Program Manager',
|
||||
'test-engineer': 'QA/Test Engineer (SDET)',
|
||||
'ux-engineer': 'User Experience (UX) Engineer',
|
||||
type JobTitleData = Record<
|
||||
string,
|
||||
Readonly<{
|
||||
label: string;
|
||||
ranking: number;
|
||||
}>
|
||||
>;
|
||||
|
||||
export const JobTitleLabels: JobTitleData = {
|
||||
'ai-engineer': { label: 'Artificial Intelligence (AI) Engineer', ranking: 5 },
|
||||
'algorithms-engineer': { label: 'Algorithms Engineer', ranking: 0 },
|
||||
'android-engineer': { label: 'Android Software Engineer', ranking: 8 },
|
||||
'applications-engineer': { label: 'Applications Engineer', ranking: 0 },
|
||||
'back-end-engineer': { label: 'Back End Engineer', ranking: 9 },
|
||||
'business-analyst': { label: 'Business Analyst', ranking: 0 },
|
||||
'business-engineer': { label: 'Business Engineer', ranking: 5 },
|
||||
'capacity-engineer': { label: 'Capacity Engineer', ranking: 0 },
|
||||
'customer-engineer': { label: 'Customer Engineer', ranking: 0 },
|
||||
'data-analyst': { label: 'Data Analyst', ranking: 0 },
|
||||
'data-engineer': { label: 'Data Engineer', ranking: 0 },
|
||||
'data-scientist': { label: 'Data Scientist', ranking: 5 },
|
||||
'devops-engineer': { label: 'DevOps Engineer', ranking: 0 },
|
||||
'engineering-director': { label: 'Engineering Director', ranking: 0 },
|
||||
'engineering-manager': { label: 'Engineering Manager', ranking: 0 },
|
||||
'enterprise-engineer': { label: 'Enterprise Engineer', ranking: 0 },
|
||||
'forward-deployed-engineer': {
|
||||
label: 'Forward Deployed Engineer (FDE)',
|
||||
ranking: 0,
|
||||
},
|
||||
'front-end-engineer': { label: 'Front End Engineer', ranking: 9 },
|
||||
'full-stack-engineer': { label: 'Full Stack Engineer', ranking: 9 },
|
||||
'gameplay-engineer': { label: 'Gameplay Engineer', ranking: 0 },
|
||||
'hardware-engineer': { label: 'Hardware Engineer', ranking: 0 },
|
||||
'infrastructure-engineer': { label: 'Infrastructure Engineer', ranking: 0 },
|
||||
'ios-engineer': { label: 'iOS Software Engineer', ranking: 0 },
|
||||
'machine-learning-engineer': {
|
||||
label: 'Machine Learning (ML) Engineer',
|
||||
ranking: 5,
|
||||
},
|
||||
'machine-learning-researcher': {
|
||||
label: 'Machine Learning (ML) Researcher',
|
||||
ranking: 0,
|
||||
},
|
||||
'mobile-engineer': {
|
||||
label: 'Mobile Software Engineer (iOS + Android)',
|
||||
ranking: 8,
|
||||
},
|
||||
'networks-engineer': { label: 'Networks Engineer', ranking: 0 },
|
||||
'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 function getLabelForJobTitleType(jobTitle: JobTitleType): string {
|
||||
return JobTitleLabels[jobTitle];
|
||||
return JobTitleLabels[jobTitle].label;
|
||||
}
|
||||
|
||||
@@ -28,11 +28,13 @@ export default function JobTitlesTypeahead({
|
||||
}: Props) {
|
||||
const [query, setQuery] = useState('');
|
||||
const options = Object.entries(JobTitleLabels)
|
||||
.map(([slug, label]) => ({
|
||||
.map(([slug, { label, ranking }]) => ({
|
||||
id: slug,
|
||||
label,
|
||||
ranking,
|
||||
value: slug,
|
||||
}))
|
||||
.sort((a, b) => b.ranking - a.ranking)
|
||||
.filter(
|
||||
({ label }) =>
|
||||
label.toLocaleLowerCase().indexOf(query.toLocaleLowerCase()) > -1,
|
||||
@@ -9,8 +9,8 @@ import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead';
|
||||
import Container from '~/components/shared/Container';
|
||||
import CountriesTypeahead from '~/components/shared/CountriesTypeahead';
|
||||
import type { JobTitleType } from '~/components/shared/JobTitles';
|
||||
import { JobTitleLabels } from '~/components/shared/JobTitles';
|
||||
import JobTitlesTypeahead from '~/components/shared/JobTitlesTypahead';
|
||||
import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
|
||||
import JobTitlesTypeahead from '~/components/shared/JobTitlesTypeahead';
|
||||
|
||||
import { useSearchParamSingle } from '~/utils/offers/useSearchParam';
|
||||
|
||||
@@ -79,7 +79,9 @@ export default function OffersHomePage() {
|
||||
selectedJobTitleId
|
||||
? {
|
||||
id: selectedJobTitleId,
|
||||
label: JobTitleLabels[selectedJobTitleId as JobTitleType],
|
||||
label: getLabelForJobTitleType(
|
||||
selectedJobTitleId as JobTitleType,
|
||||
),
|
||||
value: selectedJobTitleId,
|
||||
}
|
||||
: null
|
||||
@@ -139,4 +141,4 @@ export default function OffersHomePage() {
|
||||
</Container>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@ import QuestionSearchBar from '~/components/questions/QuestionSearchBar';
|
||||
import CompanyTypeahead from '~/components/questions/typeahead/CompanyTypeahead';
|
||||
import LocationTypeahead from '~/components/questions/typeahead/LocationTypeahead';
|
||||
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 { QUESTION_SORT_TYPES } from '~/utils/questions/constants';
|
||||
@@ -316,7 +317,7 @@ export default function QuestionsBrowsePage() {
|
||||
return selectedRoles.map((role) => ({
|
||||
checked: true,
|
||||
id: role,
|
||||
label: JobTitleLabels[role as keyof typeof JobTitleLabels],
|
||||
label: getLabelForJobTitleType(role as JobTitleType),
|
||||
value: role,
|
||||
}));
|
||||
}, [selectedRoles]);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { HorizontalDivider } from '@tih/ui';
|
||||
import CitiesTypeahead from '~/components/shared/CitiesTypeahead';
|
||||
import CompaniesTypeahead from '~/components/shared/CompaniesTypeahead';
|
||||
import CountriesTypeahead from '~/components/shared/CountriesTypeahead';
|
||||
import JobTitlesTypeahead from '~/components/shared/JobTitlesTypahead';
|
||||
import JobTitlesTypeahead from '~/components/shared/JobTitlesTypeahead';
|
||||
import type {
|
||||
Month,
|
||||
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';
|
||||
|
||||
@@ -8,7 +9,7 @@ export default function relabelQuestionAggregates({
|
||||
}: AggregatedQuestionEncounter) {
|
||||
const newRoleCounts = Object.fromEntries(
|
||||
Object.entries(roleCounts).map(([roleId, count]) => [
|
||||
JobTitleLabels[roleId as keyof typeof JobTitleLabels],
|
||||
getLabelForJobTitleType(roleId as JobTitleType),
|
||||
count,
|
||||
]),
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { TypeaheadOption } from '@tih/ui';
|
||||
|
||||
import type { JobTitleType } from '~/components/shared/JobTitles';
|
||||
import { getLabelForJobTitleType } from '~/components/shared/JobTitles';
|
||||
import { JobTitleLabels } from '~/components/shared/JobTitles';
|
||||
|
||||
export type FilterId = 'experience' | 'location' | 'role';
|
||||
@@ -42,7 +43,7 @@ export const getTypeaheadOption = (
|
||||
case 'role':
|
||||
return {
|
||||
id: filterValue,
|
||||
label: JobTitleLabels[filterValue as keyof typeof JobTitleLabels],
|
||||
label: getLabelForJobTitleType(filterValue as JobTitleType),
|
||||
value: filterValue,
|
||||
};
|
||||
case 'location':
|
||||
@@ -222,7 +223,7 @@ export const getFilterLabel = (
|
||||
filters = EXPERIENCES;
|
||||
break;
|
||||
case 'role':
|
||||
filters = Object.entries(JobTitleLabels).map(([slug, label]) => ({
|
||||
filters = Object.entries(JobTitleLabels).map(([slug, { label }]) => ({
|
||||
id: slug,
|
||||
label,
|
||||
value: slug,
|
||||
|
||||
Reference in New Issue
Block a user