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 5254: Employment dropdown #150

Merged
merged 6 commits into from
Dec 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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