Skip to content

Commit

Permalink
Merge pull request #150 from City-of-Helsinki/UHF-5254-employment-dro…
Browse files Browse the repository at this point in the history
…pdown

Uhf 5254: Employment dropdown
  • Loading branch information
jeremysteerio authored Dec 30, 2022
2 parents 6077349 + a192066 commit 95332f2
Show file tree
Hide file tree
Showing 23 changed files with 403 additions and 216 deletions.
54 changes: 54 additions & 0 deletions conf/cmi/search_api.index.job_listings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,41 @@ dependencies:
- node
- scheduler
- search_api
- helfi_rekry_job_search
id: job_listings
name: 'Job listings'
description: ''
read_only: false
field_settings:
employment:
label: Employment
datasource_id: 'entity:node'
property_path: employment
type: object
employment_id:
label: 'Employment » Luokittelutermi » Termin ID'
datasource_id: 'entity:node'
property_path: 'field_employment:entity:tid'
type: integer
dependencies:
config:
- field.storage.node.field_employment
module:
- taxonomy
employment_type_id:
label: 'Employment type » Luokittelutermi » Termin ID'
datasource_id: 'entity:node'
property_path: 'field_employment_type:entity:tid'
type: integer
dependencies:
config:
- field.storage.node.field_employment_type
module:
- taxonomy
entity_type:
label: 'Entity type'
property_path: search_api_entity_type
type: string
field_copied:
label: 'Copied from another language'
datasource_id: 'entity:node'
Expand Down Expand Up @@ -138,6 +168,14 @@ field_settings:
dependencies:
module:
- node
name:
label: Name
datasource_id: 'entity:taxonomy_term'
property_path: name
type: string
dependencies:
module:
- taxonomy
status:
label: Published
datasource_id: 'entity:node'
Expand All @@ -146,6 +184,14 @@ field_settings:
dependencies:
module:
- node
tid:
label: 'Term ID'
datasource_id: 'entity:taxonomy_term'
property_path: tid
type: integer
dependencies:
module:
- taxonomy
title:
label: Title
datasource_id: 'entity:node'
Expand Down Expand Up @@ -189,6 +235,13 @@ datasource_settings:
- en
- fi
- sv
'entity:taxonomy_term':
bundles:
default: true
selected: { }
languages:
default: true
selected: { }
processor_settings:
add_url: { }
aggregated_field: { }
Expand All @@ -210,6 +263,7 @@ processor_settings:
preprocess_index: 0
boosts: { }
rendered_item: { }
reverse_entity_references: { }
tokenizer:
weights:
preprocess_index: -6
Expand Down

This file was deleted.

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions public/modules/custom/helfi_rekry_job_search/assets/main.js

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { Button, Checkbox, Select, TextInput } from 'hds-react';
import { useAtom, useAtomValue } from 'jotai';
import { useUpdateAtom } from 'jotai/utils';
import React, { useEffect } from 'react';
import React, { Fragment, useEffect } from 'react';

import SearchComponents from '../enum/SearchComponents';
import { transformDropdownsValues } from '../helpers/Params';
import {
continuousAtom,
employmentAtom,
employmentSelectionAtom,
internshipAtom,
keywordAtom,
summerJobsAtom,
Expand All @@ -30,28 +33,14 @@ const FormContainer = () => {
const setUrlParams = useUpdateAtom(urlUpdateAtom);
const [taskAreaSelection, setTaskAreaFilter] = useAtom(taskAreasSelectionAtom);
const taskAreasOptions = useAtomValue(taskAreasAtom);

const transformTaskAreas = (taskAreas: string[] | undefined = []) => {
const transformedOptions: OptionType[] = [];

taskAreas.forEach((taskArea: string) => {
const matchedOption = taskAreasOptions.find((option: OptionType) => option.value === taskArea);

if (matchedOption) {
transformedOptions.push({
label: matchedOption.label,
value: matchedOption.value,
});
}
});

return transformedOptions;
};
const employmentOptions = useAtomValue(employmentAtom);
const [employmentSelection, setEmploymentFilter] = useAtom(employmentSelectionAtom);

// Set form control values from url parameters on load
useEffect(() => {
setKeyword(urlParams?.keyword?.toString() || '');
setTaskAreaFilter(transformTaskAreas(urlParams?.task_areas));
setTaskAreaFilter(transformDropdownsValues(urlParams?.task_areas, taskAreasOptions));
setEmploymentFilter(transformDropdownsValues(urlParams?.employment, employmentOptions));
setContinuous(!!urlParams?.continuous);
setInternship(!!urlParams?.internship);
setSummerJobs(!!urlParams?.summer_jobs);
Expand All @@ -65,6 +54,7 @@ const FormContainer = () => {

event.preventDefault();
setUrlParams({
employment: employmentSelection.map((selection: OptionType) => selection.value),
keyword,
continuous,
internship,
Expand All @@ -79,6 +69,9 @@ const FormContainer = () => {
const handleTaskAreasChange = (option: OptionType[]) => setTaskAreaFilter(option);
const taskAreaInputValue = taskAreaSelection.map((option: OptionType) => option.value);

const handleEmploymentChange = (option: OptionType[]) => setEmploymentFilter(option);
const employmentInputValue = employmentSelection.map((option: OptionType) => option.value);

const isFullSearch = !drupalSettings?.helfi_rekry_job_search?.results_page_path;

return (
Expand All @@ -93,21 +86,41 @@ const FormContainer = () => {
placeholder={Drupal.t('Eg. title, office, department', { context: 'Search keyword placeholder' })}
/>
<div className='job-search-form__dropdowns'>
<div className='job-search-form__filter job-search-form__dropdown--upper'>
<Select
clearButtonAriaLabel=''
className='job-search-form__dropdown'
selectedItemRemoveButtonAriaLabel=''
placeholder={Drupal.t('All task areas', { context: 'Task areas filter placeholder' })}
multiselect
label={Drupal.t('Task area', { context: 'Task areas filter label' })}
// @ts-ignore
options={taskAreasOptions}
value={taskAreaSelection}
id={SearchComponents.TASK_AREAS}
onChange={handleTaskAreasChange}
/>
{formAction && (
<div className='job-search-form__dropdowns__upper'>
<div className='job-search-form__filter job-search-form__dropdown--upper'>
<Select
clearButtonAriaLabel=''
className='job-search-form__dropdown'
selectedItemRemoveButtonAriaLabel=''
placeholder={Drupal.t('All task areas', { context: 'Task areas filter placeholder' })}
multiselect
label={Drupal.t('Task area', { context: 'Task areas filter label' })}
// @ts-ignore
options={taskAreasOptions}
value={taskAreaSelection}
id={SearchComponents.TASK_AREAS}
onChange={handleTaskAreasChange}
/>
</div>
<div className='job-search-form__filter job-search-form__dropdown--upper'>
<Select
clearButtonAriaLabel=''
className='job-search-form__dropdown'
selectedItemRemoveButtonAriaLabel=''
placeholder={Drupal.t('All employment type options', { context: 'Employment filter placeholder' })}
multiselect
label={Drupal.t('Employment type', { context: 'Employment filter label' })}
// @ts-ignore
options={employmentOptions}
value={employmentSelection}
id={SearchComponents.TASK_AREAS}
onChange={handleEmploymentChange}
/>
</div>
</div>
{/** Hidden select elements to enable native form functions */}
{formAction && (
<Fragment>
<select
aria-hidden
multiple
Expand All @@ -119,8 +132,19 @@ const FormContainer = () => {
<option key={value} value={value} selected />
))}
</select>
)}
</div>
<select
aria-hidden
multiple
value={employmentInputValue}
name={SearchComponents.EMPLOYMENT}
style={{ display: 'none' }}
>
{employmentInputValue.map((value: string) => (
<option key={value} value={value} selected />
))}
</select>
</Fragment>
)}
</div>
{isFullSearch && (
<fieldset className='job-search-form__checkboxes'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,109 +5,16 @@ import useSWR from 'swr';

import Pagination from '../components/results/Pagination';
import ResultCard from '../components/results/ResultCard';
import Global from '../enum/Global';
import IndexFields from '../enum/IndexFields';
import { FILTER } from '../query/queries';
import useQueryString from '../hooks/useQueryString';
import { urlAtom } from '../store';
import type URLParams from '../types/URLParams';

const SIZE = 10;

const getQueryParamString = (urlParams: URLParams): string => {
const page = Number.isNaN(Number(urlParams.page)) ? 1 : Number(urlParams.page);
const must = [];
const should = [];

if (urlParams.keyword && urlParams.keyword.length > 0) {
must.push({
bool: {
should: [
{
combined_fields: {
query: urlParams.keyword.toString(),
fields: [`${IndexFields.TITLE}^2`, IndexFields.EMPLOYMENT, IndexFields.ORGANIZATION_NAME],
},
},
{
wildcard: {
[`${IndexFields.TITLE}.keyword`]: `*${urlParams.keyword}*`,
},
},
],
},
});
}

if (urlParams?.task_areas?.length) {
must.push({
terms: {
[`${IndexFields.TASK_AREA}.keyword`]: urlParams.task_areas,
},
});
}

if (urlParams.continuous) {
should.push({
term: {
[IndexFields.CONTINUOUS]: true,
},
});
}

if (urlParams.internship) {
should.push({
term: {
[IndexFields.INTERNSHIP]: true,
},
});
}

if (urlParams.summer_jobs) {
should.push({
term: {
[IndexFields.SUMMER_JOB]: true,
},
});
}

if (urlParams.youth_summer_jobs) {
should.push({
term: {
[IndexFields.YOUTH_SUMMER_JOB]: true,
},
});
}

const query: any = {
bool: {
...FILTER,
},
};

if (Object.keys(must).length) {
query.bool.must = must;
}

if (should.length) {
query.bool.should = should;
query.bool.minimum_should_match = 1;
}

return JSON.stringify({
aggs: {
[IndexFields.NUMBER_OF_JOBS]: {
sum: {
field: IndexFields.NUMBER_OF_JOBS,
},
},
},
size: SIZE,
from: SIZE * (page - 1),
query: query,
});
};

const ResultsContainer = () => {
const { size } = Global;
const urlParams: URLParams = useAtomValue(urlAtom);
const queryString = useQueryString(urlParams);
const fetcher = () => {
const proxyUrl = drupalSettings?.helfi_rekry_job_search?.elastic_proxy_url;
const url: string | undefined = proxyUrl || process.env.REACT_APP_ELASTIC_URL;
Expand All @@ -117,17 +24,16 @@ const ResultsContainer = () => {
headers: {
'Content-Type': 'application/json',
},
body: getQueryParamString(urlParams),
body: queryString,
}).then((res) => res.json());
};

const { data, error } = useSWR(JSON.stringify(urlParams), fetcher);
const { data, error } = useSWR(queryString, fetcher);

if (!data && !error) {
return <LoadingSpinner />;
}

// @todo add no results message.
if (!data?.hits?.hits.length) {
return (
<div className='job-search__no-results'>
Expand All @@ -139,8 +45,8 @@ const ResultsContainer = () => {

const results = data.hits.hits;
const total = data.hits.total.value;
const pages = Math.floor(total / SIZE);
const addLastPage = total > SIZE && total % SIZE;
const pages = Math.floor(total / size);
const addLastPage = total > size && total % size;

if (error) {
console.warn('Error loading data');
Expand Down
Loading

0 comments on commit 95332f2

Please sign in to comment.