Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UHF-5253: Job search location filter #864

Merged
merged 10 commits into from
Dec 27, 2023
2 changes: 1 addition & 1 deletion dist/js/job-search.min.js

Large diffs are not rendered by default.

29 changes: 28 additions & 1 deletion src/js/react/apps/job-search/containers/FormContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import SearchComponents from '../enum/SearchComponents';
import { getInitialLanguage } from '../helpers/Language';
import transformDropdownsValues from '../helpers/Params';
import {
areaFilterAtom,
areaFilterSelectionAtom,
configurationsAtom,
continuousAtom,
employmentAtom,
Expand All @@ -25,6 +27,7 @@ import {
} from '../store';
import type OptionType from '../types/OptionType';
import SelectionsContainer from './SelectionsContainer';
import { getInitialAreaFilter } from '../helpers/Areas';

const FormContainer = () => {
const formAction = drupalSettings?.helfi_rekry_job_search?.results_page_path || '';
Expand All @@ -43,10 +46,13 @@ const FormContainer = () => {
const [languageSelection, setLanguageFilter] = useAtom(languageSelectionAtom);
const { employmentSearchIds } = useAtomValue(configurationsAtom);
const employmentSearchIdMap = bucketToMap(employmentSearchIds);
const areaFilterOptions = useAtomValue(areaFilterAtom);
const [areaFilterSelection, setAreaFilter] = useAtom(areaFilterSelectionAtom);

// Set form control values from url parameters on load
useEffect(() => {
setKeyword(urlParams?.keyword?.toString() || '');
setAreaFilter(getInitialAreaFilter(urlParams?.area_filter, areaFilterOptions));
setTaskAreaFilter(transformDropdownsValues(urlParams?.task_areas, taskAreasOptions));
setEmploymentFilter(transformDropdownsValues(urlParams?.employment, employmentOptions));
setContinuous(!!urlParams?.continuous);
Expand All @@ -63,6 +69,7 @@ const FormContainer = () => {

event.preventDefault();
setUrlParams({
area_filter: areaFilterSelection?.value,
employment: employmentSelection.reduce((acc: any, curr: any) => {
const target = curr.additionalValue
? [curr.additionalValue.toString(), curr.value.toString()]
Expand Down Expand Up @@ -94,6 +101,7 @@ const FormContainer = () => {
employmentSearchIdMap.get(CustomIds.YOUTH_SUMMER_JOBS) || employmentSearchIdMap.get(CustomIds.COOL_SUMMER_PROJECT);
const showCheckboxes = showContinuous || showInternships || showSummerJobs || showYouthSummerJobs;

const areaFilterLabel: string = Drupal.t('Job location', {}, { context: 'Job search: Job location label'});
const taskAreasLabel: string = Drupal.t('Task area', {}, { context: 'Task areas filter label' });
const employmentRelationshipLabel: string = Drupal.t('Type of employment relationship', {}, { context: 'Employment filter label' });
const languageLabel: string = Drupal.t('Language', {}, { context: 'Language filter label' });
Expand Down Expand Up @@ -189,7 +197,7 @@ const FormContainer = () => {
</div>
{isFullSearch && (
<div className='job-search-form__dropdowns'>
<div className='job-search-form__dropdowns__lower'>
<div className='job-search-form__dropdowns__upper'>
<div className='job-search-form__filter job-search-form__dropdown--upper'>
<Select
clearButtonAriaLabel={Drupal.t('Clear @label selection', {'@label': languageLabel}, { context: 'React search clear selection label' })}
Expand All @@ -209,6 +217,25 @@ const FormContainer = () => {
onChange={setLanguageFilter}
/>
</div>
<div className='job-search-form__filter job-search-form__dropdown--upper'>
<Select
clearButtonAriaLabel={Drupal.t('Clear @label selection', {'@label': areaFilterLabel}, { context: 'React search clear selection label' })}
className='job-search-form__dropdown'
clearable
selectedItemRemoveButtonAriaLabel={Drupal.t(
'Remove item',
{},
{ context: 'Job search remove item aria label' }
)}
placeholder={Drupal.t('All areas', {}, { context: 'Location placeholder' })}
label={areaFilterLabel}
// @ts-ignore
options={areaFilterOptions}
value={areaFilterSelection}
id={SearchComponents.AREA_FILTER}
onChange={setAreaFilter}
/>
</div>
</div>
</div>
)}
Expand Down
16 changes: 12 additions & 4 deletions src/js/react/apps/job-search/containers/SelectionsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import SearchComponents from '../enum/SearchComponents';
import { getLanguageLabel } from '../helpers/Language';
import transformDropdownsValues from '../helpers/Params';
import {
areaFilterSelectionAtom,
continuousAtom,
employmentAtom,
employmentSelectionAtom,
Expand All @@ -20,6 +21,7 @@ import {
youthSummerJobsAtom,
} from '../store';
import OptionType from '../types/OptionType';
import { getAreaInfo } from '../helpers/Areas';

const SelectionsContainer = () => {
const urlParams = useAtomValue(urlAtom);
Expand All @@ -30,6 +32,7 @@ const SelectionsContainer = () => {
const updateEmploymentOptions = useSetAtom(employmentSelectionAtom);

const showClearButton =
urlParams?.area_filter ||
urlParams?.task_areas?.length ||
urlParams?.continuous ||
urlParams?.internship ||
Expand Down Expand Up @@ -63,6 +66,13 @@ const SelectionsContainer = () => {
atom={languageSelectionAtom}
valueKey={SearchComponents.LANGUAGE}
/>
)}
{urlParams.area_filter && (
<SingleFilter
label={getAreaInfo.find(area => area.key === urlParams.area_filter?.toString())?.label}
atom={areaFilterSelectionAtom}
valueKey={SearchComponents.AREA_FILTER}
/>
)}
{urlParams.continuous && (
<CheckboxFilterPill
Expand Down Expand Up @@ -156,21 +166,19 @@ const CheckboxFilterPill = ({ atom, valueKey, label }: CheckboxFilterPillProps)

type SingleFilterProps = {
atom: any;
valueKey: string;
label: string;
valueKey: string;
};
const SingleFilter = ({ atom, valueKey, label }: SingleFilterProps) => {
const setValue = useSetAtom(atom);
const urlParams = useAtomValue(urlAtom);
const setUrlParams = useSetAtom(urlUpdateAtom);

const { language, ...updatedParams } = urlParams;

return (
<FilterButton
value={label}
clearSelection={() => {
setUrlParams(updatedParams);
setUrlParams({ ...urlParams, [valueKey]: undefined });
setValue(null);
}}
/>
Expand Down
1 change: 1 addition & 0 deletions src/js/react/apps/job-search/enum/IndexFields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const IndexFields = {
ORGANIZATION: 'field_organization',
ORGANIZATION_NAME: 'field_organization_name',
POSTAL_AREA: 'field_postal_area',
POSTAL_CODE: 'field_postal_code',
PROMOTED: 'field_promoted',
PUBLICATION_STARTS: 'field_publication_starts',
RECRUITMENT_ID: 'field_recruitment_id',
Expand Down
107 changes: 107 additions & 0 deletions src/js/react/apps/job-search/enum/PostalCodes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
const PostalCodes = {
'eastern': [
'00880',
'00890',
'00900',
'00910',
'00920',
'00930',
'00940',
'00950',
'00960',
'00970',
'00980',
'00990',
],
'central': [
'00230',
'00240',
'00500',
'00510',
'00520',
'00530',
'00540',
'00550',
'00560',
'00580',
'00600',
'00610',
],
'southern': [
'00100',
'00120',
'00130',
'00140',
'00150',
'00160',
'00170',
'00180',
'00190',
'00200',
'00210',
'00220',
'00250',
'00260',
],
'southeastern': [
'00570',
'00590',
'00800',
'00810',
'00820',
'00830',
'00840',
'00850',
'00860',
'00870',
],
'western': [
'00270',
'00280',
'00290',
'00300',
'00310',
'00320',
'00330',
'00340',
'00350',
'00360',
'00370',
'00380',
'00390',
'00400',
'00440',
'00410',
'00420',
'00430',
'08100',
'10900',
],
'northern': [
'00620',
'00630',
'00640',
'00650',
'00660',
'00670',
'00680',
'00690',
'01510',
'05950',
],
'northeast': [
'00700',
'00710',
'00720',
'00730',
'00740',
'00750',
'00760',
'00760',
'00770',
'00780',
'00790',
]
};

export default PostalCodes;
1 change: 1 addition & 0 deletions src/js/react/apps/job-search/enum/SearchComponents.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const SearchComponents = {
AREA_FILTER: 'area_filter',
EMPLOYMENT: 'employment',
KEYWORD: 'keyword',
TASK_AREAS: 'task_areas',
Expand Down
44 changes: 44 additions & 0 deletions src/js/react/apps/job-search/helpers/Areas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import PostalCodes from '../enum/PostalCodes';
import OptionType from '../types/OptionType';

export const getInitialAreaFilter = (key: string[] | string = '', options: OptionType[] = []) => options.find((option: OptionType) => option?.value === key.toString()) || null;

export const getAreaInfo = [
{
key: 'eastern',
label: Drupal.t('Eastern area', {}, {context: 'Search filter option: Eastern area'}),
postalCodes: PostalCodes.eastern,
},
{
key: 'central',
label: Drupal.t('Central area', {}, {context: 'Search filter option: Central area'}),
postalCodes: PostalCodes.central,
},
{
key: 'southern',
label: Drupal.t('Southern area', {}, {context: 'Search filter option: Southern area'}),
postalCodes: PostalCodes.southern,
},
{
key: 'southeastern',
label: Drupal.t('South-Eastern area', {}, {context: 'Search filter option: South-Eastern area'}),
postalCodes: PostalCodes.southeastern,
},
{
key: 'western',
label: Drupal.t('Western area', {}, {context: 'Search filter option: Western area'}),
postalCodes: PostalCodes.western,
},
{
key: 'northern',
label: Drupal.t('Northern area', {}, {context: 'Search filter option: Northern area'}),
PostalCodes: PostalCodes.northern,
},
{
key: 'northeast',
label: Drupal.t('North-Eastern area', {}, {context: 'Search filter option: North-Eastern area'}),
PostalCodes: PostalCodes.northeast,
},
];

export default getAreaInfo;
9 changes: 9 additions & 0 deletions src/js/react/apps/job-search/hooks/useQueryString.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import IndexFields from '../enum/IndexFields';
import { nodeFilter } from '../query/queries';
import URLParams from '../types/URLParams';
import { configurationsAtom } from '../store';
import { getAreaInfo } from '../helpers/Areas';

const useQueryString = (urlParams: URLParams): string => {
const { size: globalSize, sortOptions } = Global;
Expand Down Expand Up @@ -135,6 +136,14 @@ const useQueryString = (urlParams: URLParams): string => {
});
}

if (urlParams.area_filter) {
query.bool.filter.push({
terms: {
[IndexFields.POSTAL_CODE]: getAreaInfo.find(area => area.key === urlParams.area_filter?.toString())?.postalCodes,
}
});
}

if (Object.keys(must).length) {
query.bool.must = must;
}
Expand Down
13 changes: 13 additions & 0 deletions src/js/react/apps/job-search/store.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type OptionType from './types/OptionType';
import type Term from './types/Term';
import type URLParams from './types/URLParams';
import AggregationItem from './types/AggregationItem';
import { getAreaInfo } from './helpers/Areas';

// Make maps out of bucket responses
const bucketToMap = (bucket: AggregationItem[]) => {
Expand Down Expand Up @@ -289,6 +290,7 @@ export const summerJobsAtom = atom<boolean>(false);
export const youthSummerJobsAtom = atom<boolean>(false);

export const resetFormAtom = atom(null, (get, set) => {
set(areaFilterSelectionAtom, null);
set(taskAreasSelectionAtom, []);
set(keywordAtom, '');
set(continuousAtom, false);
Expand All @@ -299,3 +301,14 @@ export const resetFormAtom = atom(null, (get, set) => {
set(urlUpdateAtom, {});
set(languageSelectionAtom, null);
});

export const areaFilterAtom = atom(
getAreaInfo.map((item: any) => (
{
label: item.label,
value: item.key,
}
)
));

export const areaFilterSelectionAtom = atom<OptionType | null>(null);
1 change: 1 addition & 0 deletions src/js/react/apps/job-search/types/Job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type Job = {
field_organization_name: string[];
field_original_language: string[];
field_postal_area: string[];
field_postal_code: string[];
field_publication_starts: number[];
field_recruitment_id: string[];
field_recruitment_type: string[];
Expand Down
1 change: 1 addition & 0 deletions src/js/react/apps/job-search/types/URLParams.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
type URLParams = {
area_filter?: string;
continuous?: boolean;
employment?: string[];
internship?: boolean;
Expand Down
Loading