Skip to content

Commit

Permalink
Merge pull request #865 from City-of-Helsinki/UHF-9193_maternity_clin…
Browse files Browse the repository at this point in the history
…ic_search

UHF-9193: maternity and child health clinic search
  • Loading branch information
j-mys authored Dec 22, 2023
2 parents d7e0aa8 + feee010 commit ba0fad9
Show file tree
Hide file tree
Showing 28 changed files with 642 additions and 35 deletions.
2 changes: 1 addition & 1 deletion dist/js/health-station-search.min.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions dist/js/maternity-and-child-health-clinic-search.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/js/school-search.min.js

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions hdbt.libraries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,16 @@ health-station-search:
- core/drupalSettings
- core/drupal

maternity-and-child-health-clinic-search:
version: 1.0
js:
dist/js/maternity-and-child-health-clinic-search.min.js: {
preprocess: false
}
dependencies:
- core/drupalSettings
- core/drupal

ploughing-schedule:
version: 1.0
js:
Expand Down
14 changes: 14 additions & 0 deletions hdbt.theme
Original file line number Diff line number Diff line change
Expand Up @@ -1668,6 +1668,20 @@ function hdbt_preprocess_paragraph__health_station_search(array &$variables): vo
}
}

/**
* Implements hook_preprocess_HOOK().
*/
function hdbt_preprocess_paragraph__maternity_and_child_health_clini(array &$variables): void {
$variables['#attached']['library'][] = 'hdbt/maternity-and-child-health-clinic-search';

$privacyUrl = helfi_eu_cookie_compliance_get_privacy_policy_url();

if ($privacyUrl instanceof Url) {
$privacyUrl->setAbsolute();
$variables['#attached']['drupalSettings']['helfi_react_search']['cookie_privacy_url'] = $privacyUrl->toString();
}
}

/**
* Implements hook_preprocess_HOOK().
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const ResultCard = ({ address, name, name_override, picture_url, provided_langua
cardUrl={url?.[0] || ''}
location={address?.[0]}
locationLabel={Drupal.t('Address', {}, {context: 'React search: location label'})}
cardCategoryTag={provided_languages.includes('sv') ? {'tag': Drupal.t('Service in Swedish', {}, {'context': 'Health station search: Service in Swedish tag'})} : undefined}
cardCategoryTag={provided_languages.includes('sv') ? {'tag': Drupal.t('Service in Swedish', {}, {'context': 'React search: Service in Swedish tag'})} : undefined}
/>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ const ProximityFormContainer = () => {
<form className='hdbt-search--react__form-container' onSubmit={onSubmit}>
<TextInput
className='hdbt-search__filter hdbt-search--react__text-field'
helperText={Drupal.t('Enter the street name and house number', {}, { context: 'Health station search: input helper'})}
placeholder={Drupal.t('For example, Kotikatu 1', {}, { context: 'Health station search: input placeholder'})}
helperText={Drupal.t('Enter the street name and house number', {}, { context: 'React search: street input helper'})}
placeholder={Drupal.t('For example, Kotikatu 1', {}, { context: 'React search: street input helper placeholder'})}
id='keyword'
label={Drupal.t('Home address', {}, { context: 'Health station search: input label'})}
label={Drupal.t('Home address', {}, { context: 'React search: home address'})}
type='search'
/>
<div className='react-search__checkbox-filter-container'>
Expand All @@ -46,7 +46,7 @@ const ProximityFormContainer = () => {
name='sv_only'
value='sv_only'
onClick={() => setStagedParams({...stagedParams, sv_only: !stagedParams?.sv_only})}
label={Drupal.t('Show the nearest service location where service is available in Swedish.', {}, { context: 'Health station search: checkbox label'})}
label={Drupal.t('Show the nearest service location where service is available in Swedish.', {}, { context: 'React search: checkbox label swedish'})}
/>
</fieldset>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
type ImageOverride = {
picture_url_override: {
alt: string,
photographer: string,
title: string,
url: string
}
};
import { ImageOverride } from '@/types/ImageOverride';

export type HealthStation = {
_language: string,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import CardItem from '@/react/common/Card';
import CardImage from '@/react/common/CardImage';
import { MaternityAndChildHealthClinic } from '../types/MaternityAndChildHealthClinic';

const ResultCard = ({ address, name, name_override, picture_url, provided_languages, media_as_objects, url }: MaternityAndChildHealthClinic) => {
const title = name_override?.[0] || name?.[0];
const imageOverride = media_as_objects?.[0].picture_url_override;

if (!title) {
return null;
}

let cardImage;

if (imageOverride) {
cardImage = <CardImage
alt={imageOverride.alt}
photographer={imageOverride.photographer}
src={imageOverride.url}
title={imageOverride.title}
/>;
}
else if (picture_url?.[0]) {
cardImage = <CardImage src={picture_url?.[0]} />;
}

return (
<CardItem
cardImage={cardImage}
cardModifierClass=''
cardTitle={title}
cardUrl={url?.[0] || ''}
location={address?.[0]}
locationLabel={Drupal.t('Address', {}, {context: 'React search: location label'})}
cardCategoryTag={provided_languages.includes('sv') ? {'tag': Drupal.t('Service in Swedish', {}, {'context': 'React search: Service in Swedish tag'})} : undefined}
/>
);
};

export default ResultCard;
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { SyntheticEvent, createRef, useState } from 'react';
import { useAtomValue } from 'jotai';

import Result from '@/types/Result';
import Pagination from '@/react/common/Pagination';
import useScrollToResults from '@/react/common/hooks/useScrollToResults';
import LoadingOverlay from '@/react/common/LoadingOverlay';
import ResultsError from '@/react/common/ResultsError';
import ResultsMap from '@/react/common/ResultsMap';
import AppSettings from '../enum/AppSettings';
import { MaternityAndChildHealthClinic } from '../types/MaternityAndChildHealthClinic';
import ResultCard from './ResultCard';
import { paramsAtom } from '../store';

type ResultsListProps = {
data: any;
error: string|Error;
isLoading: boolean;
isValidating: boolean;
page?: number;
updatePage: Function
}

const ResultsList = ({ data, error, isLoading, isValidating, page, updatePage }: ResultsListProps) => {
const [useMap, setUseMap] = useState<boolean>(false);
const { size } = AppSettings;
const params = useAtomValue(paramsAtom);
const scrollTarget = createRef<HTMLDivElement>();
const { sv_only, keyword } = params;
const choices = Boolean(Object.keys(params).length);
useScrollToResults(scrollTarget, choices);

if (isLoading || isValidating) {
return (
<div className='hdbt__loading-wrapper'>
<LoadingOverlay />
</div>
);
}

if (error) {
return (
<ResultsError
error={error}
ref={scrollTarget}
/>
);
}

if (!data?.hits?.hits.length) {
return (
<div ref={scrollTarget}>
{Drupal.t('No results were found for the criteria you entered. Try changing your search criteria.', {}, { context: 'React search: no search results' })}
</div>
);
}

const results = data.hits.hits;
const total = keyword && sv_only ? data.hits.hits.length : data.hits.total.value;
const pages = Math.floor(total / size);
const addLastPage = total > size && total % size;
const showPagination = !useMap && (pages > 1 || addLastPage);
const sv_id = results?.[0]?._source?.id?.[0];
const mapIds = keyword && sv_only && sv_id ? data?.aggregations?.ids?.buckets?.filter((item: any) => item.key === sv_id) : data?.aggregations?.ids?.buckets;

return (
<div className='react-search__results'>
<div className='hdbt-search--react__result-top-area'>
{!Number.isNaN(total) &&
<h3 className='hdbt-search--react__results--title' ref={scrollTarget}>
{ total > 1 ?
Drupal.t('@clinics clinics', { '@clinics': total }, { context: 'React search: Maternity and child health clinic result count'})
:
Drupal.t('@clinics clinic', { '@clinics': total }, { context: 'React search: Maternity and child health clinic result count'})
}
</h3>
}
<div className='hdbt-search--react__results--tablist' role='tablist'>
<button type='button' className='tablist-tab' role='tab' aria-selected={!useMap} aria-controls='hdbt-search--react__results--tabpanel' onClick={() => setUseMap(false)}>
{ Drupal.t('View as a list', {}, {context: 'React search: result display'}) }
</button>
<button type='button' className='tablist-tab' role='tab' aria-selected={useMap} aria-controls='hdbt-search--react__results--tabpanel' onClick={() => setUseMap(true)}>
{ Drupal.t('View in a map', {}, {context: 'React search: result display'}) }
</button>
</div>
</div>
<div id='hdbt-search--react__results--tabpanel' role="tabpanel">
{
useMap ?
<ResultsMap ids={mapIds} />
:
<>
{results.map((hit: Result<MaternityAndChildHealthClinic>) => (
<ResultCard key={hit._id} {...hit._source} />
))}
</>
}
{
showPagination &&
<Pagination
currentPage={page || 1}
pages={5}
totalPages={addLastPage ? pages + 1 : pages}
updatePage={(e: SyntheticEvent, nextPage: number) => {
e.preventDefault();
updatePage(nextPage);
}}
/>
}
</div>
</div>
);
};

export default ResultsList;
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Button, Checkbox, TextInput } from 'hds-react';
import { useAtomValue, useSetAtom } from 'jotai';

import { paramsAtom, stagedParamsAtom } from '../store';
import SearchParams from '../types/SearchParams';

type SubmitFormType = HTMLFormElement & {
keyword: HTMLInputElement;
};

const ProximityFormContainer = () => {
const stagedParams = useAtomValue(stagedParamsAtom);
const setParams = useSetAtom(paramsAtom);
const setStagedParams = useSetAtom(stagedParamsAtom);

const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const { keyword, sv_only } = event.target as SubmitFormType;
const params: SearchParams = {};

if (keyword.value && keyword.value.length) {
params.keyword = keyword.value;
};

params.sv_only = sv_only.checked;

setParams(params);
};

return (
<form className='hdbt-search--react__form-container' onSubmit={onSubmit}>
<TextInput
className='hdbt-search__filter hdbt-search--react__text-field'
helperText={Drupal.t('Enter the street name and house number', {}, { context: 'React search: street input helper'})}
placeholder={Drupal.t('For example, Kotikatu 1', {}, { context: 'React search: street input helper placeholder'})}
id='keyword'
label={Drupal.t('Home address', {}, { context: 'React search: home address'})}
type='search'
/>
<div className='react-search__checkbox-filter-container'>
<fieldset className='hdbt-search--react__fieldset'>
<Checkbox
className='react-search__checkbox'
checked={stagedParams?.sv_only || false}
id='sv_only'
name='sv_only'
value='sv_only'
onClick={() => setStagedParams({...stagedParams, sv_only: !stagedParams?.sv_only})}
label={Drupal.t('Show the nearest service location where service is available in Swedish.', {}, { context: 'React search: checkbox label swedish'})}
/>
</fieldset>
</div>
<Button className='hdbt-search--react__submit-button' type='submit'>{Drupal.t('Search')}</Button>
</form>
);
};

export default ProximityFormContainer;
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useAtomValue, useSetAtom } from 'jotai';

import { paramsAtom, updateParamsAtom } from '../store';
import UseProximityQuery from '../hooks/UseProximityQuery';
import ResultsList from '../components/ResultsList';

const ProximityResultsContainer = () => {
const params = useAtomValue(paramsAtom);
const setParams = useSetAtom(updateParamsAtom);
const updatePage = (page: number) => {
setParams({
...params,
page
});
};
const { data, error, isLoading, isValidating } = UseProximityQuery(params);
const { page } = params;

return (
<ResultsList {...{ data, error, isLoading, isValidating, page, updatePage }} />
);
};

export default ProximityResultsContainer;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Suspense } from 'react';

import LoadingOverlay from '@/react/common/LoadingOverlay';
import FormContainer from './FormContainer';
import ResultsContainer from './ResultsContainer';

const SearchContainer = () => (
<Suspense fallback={
<div className='hdbt__loading-wrapper'>
<LoadingOverlay />
</div>
}>
<div className='hdbt-search--react'>
<FormContainer />
<ResultsContainer />
</div>
</Suspense>
);

export default SearchContainer;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const AppSettings = {
index: 'maternity_and_child_health_clinic',
locationsBaseUrl: 'https://api.hel.fi/servicemap/v2/administrative_division/?municipality=helsinki&type=maternity_clinic_district&unit_include=id',
size: 10
};

export default AppSettings;
Loading

0 comments on commit ba0fad9

Please sign in to comment.