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-9193: maternity and child health clinic search #865

Merged
merged 8 commits into from
Dec 22, 2023
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
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