mirror of
https://github.com/yangshun/tech-interview-handbook.git
synced 2025-07-20 05:42:06 +08:00
[resumes][refactor] use JobTitlesTypeahead instead
This commit is contained in:
@ -1,56 +0,0 @@
|
|||||||
import type { ComponentProps } from 'react';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import type { TypeaheadOption } from '@tih/ui';
|
|
||||||
import { Typeahead } from '@tih/ui';
|
|
||||||
|
|
||||||
import { JobTitleLabels } from '~/components/shared/JobTitles';
|
|
||||||
|
|
||||||
type BaseProps = Pick<
|
|
||||||
ComponentProps<typeof Typeahead>,
|
|
||||||
| 'disabled'
|
|
||||||
| 'errorMessage'
|
|
||||||
| 'isLabelHidden'
|
|
||||||
| 'placeholder'
|
|
||||||
| 'required'
|
|
||||||
| 'textSize'
|
|
||||||
>;
|
|
||||||
|
|
||||||
type Props = BaseProps &
|
|
||||||
Readonly<{
|
|
||||||
onSelect: (option: TypeaheadOption | null) => void;
|
|
||||||
selectedValues?: Set<string>;
|
|
||||||
value?: TypeaheadOption | null;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
export default function ResumeRoleTypeahead({
|
|
||||||
onSelect,
|
|
||||||
selectedValues = new Set(),
|
|
||||||
value,
|
|
||||||
...props
|
|
||||||
}: Props) {
|
|
||||||
const [query, setQuery] = useState('');
|
|
||||||
const options = Object.entries(JobTitleLabels)
|
|
||||||
.map(([slug, { label }]) => ({
|
|
||||||
id: slug,
|
|
||||||
label,
|
|
||||||
value: slug,
|
|
||||||
}))
|
|
||||||
.filter((option) => !selectedValues.has(option.value))
|
|
||||||
.filter(
|
|
||||||
({ label }) =>
|
|
||||||
label.toLocaleLowerCase().indexOf(query.toLocaleLowerCase()) > -1,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Typeahead
|
|
||||||
label="Role"
|
|
||||||
noResultsMessage="No available roles."
|
|
||||||
nullable={true}
|
|
||||||
options={options}
|
|
||||||
value={value}
|
|
||||||
onQueryChange={setQuery}
|
|
||||||
onSelect={onSelect}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
@ -17,11 +17,17 @@ type BaseProps = Pick<
|
|||||||
|
|
||||||
type Props = BaseProps &
|
type Props = BaseProps &
|
||||||
Readonly<{
|
Readonly<{
|
||||||
|
excludedValues?: Set<string>;
|
||||||
|
label?: string;
|
||||||
|
noResultsMessage?: string;
|
||||||
onSelect: (option: TypeaheadOption | null) => void;
|
onSelect: (option: TypeaheadOption | null) => void;
|
||||||
value?: TypeaheadOption | null;
|
value?: TypeaheadOption | null;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export default function JobTitlesTypeahead({
|
export default function JobTitlesTypeahead({
|
||||||
|
excludedValues,
|
||||||
|
label: labelProp = 'Job Title',
|
||||||
|
noResultsMessage = 'No available job titles.',
|
||||||
onSelect,
|
onSelect,
|
||||||
value,
|
value,
|
||||||
...props
|
...props
|
||||||
@ -34,15 +40,16 @@ export default function JobTitlesTypeahead({
|
|||||||
ranking,
|
ranking,
|
||||||
value: slug,
|
value: slug,
|
||||||
}))
|
}))
|
||||||
.sort((a, b) => b.ranking - a.ranking)
|
|
||||||
.filter(({ label }) =>
|
.filter(({ label }) =>
|
||||||
label.toLocaleLowerCase().includes(query.trim().toLocaleLowerCase()),
|
label.toLocaleLowerCase().includes(query.trim().toLocaleLowerCase()),
|
||||||
);
|
)
|
||||||
|
.filter((option) => !excludedValues?.has(option.value))
|
||||||
|
.sort((a, b) => b.ranking - a.ranking);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Typeahead
|
<Typeahead
|
||||||
label="Job Title"
|
label={labelProp}
|
||||||
noResultsMessage="No available job titles."
|
noResultsMessage={noResultsMessage}
|
||||||
nullable={true}
|
nullable={true}
|
||||||
options={options}
|
options={options}
|
||||||
value={value}
|
value={value}
|
||||||
|
@ -25,7 +25,6 @@ import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics';
|
|||||||
import ResumeFilterPill from '~/components/resumes/browse/ResumeFilterPill';
|
import ResumeFilterPill from '~/components/resumes/browse/ResumeFilterPill';
|
||||||
import ResumeListItems from '~/components/resumes/browse/ResumeListItems';
|
import ResumeListItems from '~/components/resumes/browse/ResumeListItems';
|
||||||
import ResumeExperienceTypeahead from '~/components/resumes/shared/ResumeExperienceTypeahead';
|
import ResumeExperienceTypeahead from '~/components/resumes/shared/ResumeExperienceTypeahead';
|
||||||
import ResumeRoleTypeahead from '~/components/resumes/shared/ResumeRoleTypeahead';
|
|
||||||
import ResumeSignInButton from '~/components/resumes/shared/ResumeSignInButton';
|
import ResumeSignInButton from '~/components/resumes/shared/ResumeSignInButton';
|
||||||
import CountriesTypeahead from '~/components/shared/CountriesTypeahead';
|
import CountriesTypeahead from '~/components/shared/CountriesTypeahead';
|
||||||
import loginPageHref from '~/components/shared/loginPageHref';
|
import loginPageHref from '~/components/shared/loginPageHref';
|
||||||
@ -43,6 +42,8 @@ import useDebounceValue from '~/utils/resumes/useDebounceValue';
|
|||||||
import useSearchParams from '~/utils/resumes/useSearchParams';
|
import useSearchParams from '~/utils/resumes/useSearchParams';
|
||||||
import { trpc } from '~/utils/trpc';
|
import { trpc } from '~/utils/trpc';
|
||||||
|
|
||||||
|
import JobTitlesTypeahead from '../../components/shared/JobTitlesTypeahead';
|
||||||
|
|
||||||
const STALE_TIME = 5 * 60 * 1000;
|
const STALE_TIME = 5 * 60 * 1000;
|
||||||
const DEBOUNCE_DELAY = 800;
|
const DEBOUNCE_DELAY = 800;
|
||||||
const PAGE_LIMIT = 10;
|
const PAGE_LIMIT = 10;
|
||||||
@ -356,12 +357,14 @@ export default function ResumeHomePage() {
|
|||||||
);
|
);
|
||||||
case 'role':
|
case 'role':
|
||||||
return (
|
return (
|
||||||
<ResumeRoleTypeahead
|
<JobTitlesTypeahead
|
||||||
isLabelHidden={true}
|
excludedValues={
|
||||||
placeholder="Select roles"
|
|
||||||
selectedValues={
|
|
||||||
new Set(userFilters[filterId].map(({ value }) => value))
|
new Set(userFilters[filterId].map(({ value }) => value))
|
||||||
}
|
}
|
||||||
|
isLabelHidden={true}
|
||||||
|
label="Role"
|
||||||
|
noResultsMessage="No available roles."
|
||||||
|
placeholder="Select roles"
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -21,10 +21,10 @@ import {
|
|||||||
} from '@tih/ui';
|
} from '@tih/ui';
|
||||||
|
|
||||||
import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics';
|
import { useGoogleAnalytics } from '~/components/global/GoogleAnalytics';
|
||||||
import ResumeRoleTypeahead from '~/components/resumes/shared/ResumeRoleTypeahead';
|
|
||||||
import ResumeSubmissionGuidelines from '~/components/resumes/submit-form/ResumeSubmissionGuidelines';
|
import ResumeSubmissionGuidelines from '~/components/resumes/submit-form/ResumeSubmissionGuidelines';
|
||||||
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 JobTitlesTypeahead from '~/components/shared/JobTitlesTypeahead';
|
||||||
import loginPageHref from '~/components/shared/loginPageHref';
|
import loginPageHref from '~/components/shared/loginPageHref';
|
||||||
|
|
||||||
import { RESUME_STORAGE_KEY } from '~/constants/file-storage-keys';
|
import { RESUME_STORAGE_KEY } from '~/constants/file-storage-keys';
|
||||||
@ -333,8 +333,10 @@ export default function SubmitResumeForm({
|
|||||||
control={control}
|
control={control}
|
||||||
name="role"
|
name="role"
|
||||||
render={({ field: { value } }) => (
|
render={({ field: { value } }) => (
|
||||||
<ResumeRoleTypeahead
|
<JobTitlesTypeahead
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
|
label="Role"
|
||||||
|
noResultsMessage="No available roles."
|
||||||
placeholder="Select a role"
|
placeholder="Select a role"
|
||||||
required={true}
|
required={true}
|
||||||
value={value}
|
value={value}
|
||||||
|
Reference in New Issue
Block a user