diff --git a/.env.example b/.env.example
index 1de37a7a9..c2af37afb 100644
--- a/.env.example
+++ b/.env.example
@@ -9,7 +9,11 @@ ACCESSIBILITY_SENTENCE_API="https://www.hel.fi/palvelukarttaws/rest/v4"
SERVICEMAP_API="https://api.hel.fi/servicemap/"
SERVICEMAP_API_VERSION="v2"
EVENTS_API="https://api.hel.fi/linkedevents/v1"
-RESERVATIONS_API="https://api.hel.fi/respa/v1"
+
+RESERVATIONS_API="https://tilavaraus.hel.fi" # For production
+# RESERVATIONS_API="https://tilavaraus.test.hel.ninja" # For test environment
+# RESERVATIONS_API="https://tilavaraus.dev.hel.ninja" # For local development
+
FEEDBACK_URL="https://api.hel.fi/servicemap/open311/"
DIGITRANSIT_API="https://api.digitransit.fi/routing/v1/routers/hsl/index/graphql"
# This is key for DIGITRANSIT API. There are separate urls and keys for prod (api.digitransit.fi) and dev (dev-api.digitransit.fi) digitransit apis. Developer should generate own
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b4cfc426a..1ee1da059 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -21,7 +21,7 @@ jobs:
SERVICEMAP_API: https://api.hel.fi/servicemap/
SERVICEMAP_API_VERSION: "v2"
EVENTS_API: https://api.hel.fi/linkedevents/v1
- RESERVATIONS_API: https://api.hel.fi/respa/v1
+ RESERVATIONS_API: https://tilavaraus.hel.fi
PRODUCTION_PREFIX: SM
DIGITRANSIT_API: https://api.digitransit.fi/routing/v1/routers/hsl/index/graphql
DIGITRANSIT_API_KEY: a9219bfb875d4cc79b5f69123b57d0db
diff --git a/server/dataFetcher.js b/server/dataFetcher.js
index fd7e47f85..ab817fd01 100644
--- a/server/dataFetcher.js
+++ b/server/dataFetcher.js
@@ -180,7 +180,7 @@ export const fetchSelectedUnitData = (req, res, next) => {
store.dispatch(fetchSuccess(data.results));
response();
}
- reservationsFetch({ unit: `tprek:${id}` }, null, reservationFetchEnd, fetchOnError, null, null, controller)
+ reservationsFetch(null, null, reservationFetchEnd, fetchOnError, null, id, controller);
} catch(e) {
console.log('Error in fetchSelectedUnitData', e.message);
diff --git a/src/components/ListItems/ReservationItem/ReservationItem.js b/src/components/ListItems/ReservationItem/ReservationItem.js
index e688ac60a..3fdc53838 100644
--- a/src/components/ListItems/ReservationItem/ReservationItem.js
+++ b/src/components/ListItems/ReservationItem/ReservationItem.js
@@ -2,20 +2,37 @@ import React from 'react';
import PropTypes from 'prop-types';
import { EventAvailable } from '@mui/icons-material';
import SimpleListItem from '../SimpleListItem';
-import useLocaleText from '../../../utils/useLocaleText';
+import config from '../../../../config';
+import { useSelector } from 'react-redux';
+import { getLocale } from '../../../redux/selectors/user';
+
+const getLocalizedText = (reservation, locale) => {
+ switch (locale) {
+ case 'fi':
+ return reservation.name_fi;
+ case 'en':
+ return reservation.name_en;
+ case 'sv':
+ return reservation.name_sv;
+ default:
+ return reservation.name_fi; // Fallback to Finnish
+ }
+};
const ReservationItem = ({ reservation, intl, divider }) => {
- const getLocaleText = useLocaleText();
+ const locale = useSelector(getLocale);
+ const localizedText = getLocalizedText(reservation, locale);
+
return (
}
link
- text={`${getLocaleText(reservation.name)} ${intl.formatMessage({ id: 'opens.new.tab' })}`}
+ text={`${localizedText} ${intl.formatMessage({ id: 'opens.new.tab' })}`}
divider={divider}
handleItemClick={() => {
- window.open(`https://varaamo.hel.fi/resources/${reservation.id}`);
+ window.open(`${config.reservationsAPI.root}/${locale}/reservation-unit/${reservation.pk}`);
}}
/>
);
@@ -24,8 +41,10 @@ const ReservationItem = ({ reservation, intl, divider }) => {
ReservationItem.propTypes = {
intl: PropTypes.objectOf(PropTypes.any).isRequired,
reservation: PropTypes.shape({
- id: PropTypes.string,
- name: PropTypes.objectOf(PropTypes.any),
+ pk: PropTypes.number,
+ name_fi: PropTypes.string,
+ name_en: PropTypes.string,
+ name_sv: PropTypes.string,
}).isRequired,
divider: PropTypes.bool,
};
diff --git a/src/i18n/en.js b/src/i18n/en.js
index b3f5218a3..7d96737b7 100644
--- a/src/i18n/en.js
+++ b/src/i18n/en.js
@@ -74,6 +74,7 @@ export default {
'area.neighborhood.title': 'Choose neighbourhood',
'area.postcode_area.title': 'Choose postal code',
'area.major_district.title': 'Choose major district',
+ 'area.service.filter': 'Filtering of services for areas',
'area.statisticalDistrict.info': 'First, select a population data area, and then you can browse the area\'s services',
'area.statisticalDistrict.title': 'Select a population data area',
'area.statisticalDistrict.section': 'Cropping: {text}',
diff --git a/src/i18n/fi.js b/src/i18n/fi.js
index dc7f9ba66..c7a73f670 100644
--- a/src/i18n/fi.js
+++ b/src/i18n/fi.js
@@ -74,6 +74,7 @@ export default {
'area.neighborhood.title': 'Valitse kaupunginosa',
'area.postcode_area.title': 'Valitse postinumero',
'area.major_district.title': 'Valitse suurpiiri',
+ 'area.service.filter': 'Palvelujen suodatus',
'area.statisticalDistrict.info': 'Valitse ensin väestötietoalue, jonka jälkeen voit selata alueen palveluita',
'area.statisticalDistrict.label': '{count} henkilöä, {percent}% alueen koko väestöstä',
'area.statisticalDistrict.label.total': '{count} henkilöä',
diff --git a/src/i18n/sv.js b/src/i18n/sv.js
index 8507bd36a..ab5979e52 100644
--- a/src/i18n/sv.js
+++ b/src/i18n/sv.js
@@ -74,6 +74,7 @@ export default {
'area.neighborhood.title': 'Välj stadsdel',
'area.postcode_area.title': 'Välj postnummer',
'area.major_district.title': 'Välj stordistrikt',
+ 'area.service.filter': 'Filtrering av geografiska tjänster',
'area.statisticalDistrict.info': 'Välj först befolkningsdataområdet, varefter du kan bläddra bland regionens tjänster',
'area.statisticalDistrict.title': 'Välj befolkningsdataområde',
'area.statisticalDistrict.section': 'Beskärning: {text}',
diff --git a/src/redux/actions/selectedUnitReservations.js b/src/redux/actions/selectedUnitReservations.js
index 35e97ae86..a45067814 100644
--- a/src/redux/actions/selectedUnitReservations.js
+++ b/src/redux/actions/selectedUnitReservations.js
@@ -2,48 +2,31 @@ import { reservations } from './fetchDataActions';
import { reservationsFetch } from '../../utils/fetch';
const {
- isFetching, fetchSuccess, fetchMoreSuccess, fetchError, fetchProgressUpdate,
+ isFetching, fetchSuccess, fetchError
} = reservations;
-export const fetchReservations = (id, pageSize, all = false) => async (dispatch, getState) => {
- const { selectedUnit } = getState();
- const { reservations } = selectedUnit;
- const previousFetch = reservations.previousSearch;
- if (previousFetch) {
- const parts = previousFetch.split('-');
- if (parts[0] === id && parts[1] === 'all') {
- return;
- }
- }
+export const fetchReservations = (id) => async (dispatch, getState) => {
+
const onStart = () => {
- dispatch(isFetching(`${id}-${all ? 'all' : 'partial'}`));
+ dispatch(isFetching(id));
};
+
const onSuccess = (data) => {
if (data && data.length) {
dispatch(fetchSuccess(data));
return;
}
- dispatch(fetchProgressUpdate(data.results.length, data.count, data.next));
dispatch(fetchSuccess(data.results, { count: data.count, next: data.next }));
};
- const onError = e => dispatch(fetchError(e.message));
- const onNext = all ? (resultTotal, response) => {
- dispatch(fetchProgressUpdate(resultTotal.length, response.count));
- } : null;
- // Fetch data
- reservationsFetch({ unit: `tprek:${id}`, page_size: pageSize || 5 }, onStart, onSuccess, onError, onNext);
-};
-
-
-export const fetchAdditionalReservations = next => async (dispatch) => {
- // fetch additional data that is added to previous data
- const onStart = () => dispatch(isFetching());
- const onSuccess = (data) => {
- dispatch(fetchMoreSuccess(data.results, { count: data.count, next: data.next }));
+ const onError = e => {
+ console.error(e); // Log the error
+ dispatch(fetchError(e.message));
};
- const onError = e => dispatch(fetchError(e.message));
- // Fetch data
- reservationsFetch(null, onStart, onSuccess, onError, null, null, null, next);
+ try {
+ await reservationsFetch(null, onStart, onSuccess, onError, null, id, null, null, null);
+ } catch (e) {
+ onError(e);
+ }
};
diff --git a/src/utils/fetch/constants.js b/src/utils/fetch/constants.js
index c8b993569..907ef6a86 100644
--- a/src/utils/fetch/constants.js
+++ b/src/utils/fetch/constants.js
@@ -26,11 +26,9 @@ export const APIHandlers = {
envName: config.serviceMapAPI.id,
},
reservations: {
- url: `${config.reservationsAPI.root}/resource/`,
- options: {
- page_size: 5,
- },
- envName: config.serviceMapAPI.id,
+ url: id => `${config.reservationsAPI.root}/v1/palvelukartta/reservation-units/${id}`,
+ options: {},
+ envName: config.reservationsAPI.id,
},
search: {
url: `${config.serviceMapAPI.root}${config.serviceMapAPI.version}/search/`,
diff --git a/src/views/AreaView/components/GeographicalUnitList/GeographicalUnitList.js b/src/views/AreaView/components/GeographicalUnitList/GeographicalUnitList.js
index 98afeefa6..2571c8008 100644
--- a/src/views/AreaView/components/GeographicalUnitList/GeographicalUnitList.js
+++ b/src/views/AreaView/components/GeographicalUnitList/GeographicalUnitList.js
@@ -1,7 +1,10 @@
import { Checkbox, List, Typography } from '@mui/material';
import PropTypes from 'prop-types';
-import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import React, {
+ useCallback, useEffect, useMemo, useRef, useState,
+} from 'react';
import { useDispatch, useSelector } from 'react-redux';
+import { useIntl } from 'react-intl';
import { UnitItem } from '../../../../components';
import {
addSelectedDistrictService,
@@ -13,7 +16,7 @@ import {
selectSelectedDistrictServices,
} from '../../../../redux/selectors/district';
import { getLocale } from '../../../../redux/selectors/user';
-import { uppercaseFirst } from '../../../../utils';
+import { keyboardHandler, uppercaseFirst } from '../../../../utils';
import { orderUnits } from '../../../../utils/orderUnits';
import useLocaleText from '../../../../utils/useLocaleText';
import {
@@ -24,6 +27,7 @@ import {
StyledUnitList,
StyledUnitListArea,
} from '../styled/styled';
+import ServiceFilterContainer from '../ServiceFilterContainer/ServiceFilterContainer';
// Custom uncontrolled checkbox that allows default value
const UnitCheckbox = ({
@@ -56,7 +60,16 @@ const GeographicalUnitList = ({ initialOpenItems }) => {
const locale = useSelector(getLocale);
const [serviceList, setServiceList] = useState([]);
const [initialCheckedItems] = useState(selectedServices);
-
+ const inputRef = useRef();
+ const { formatMessage } = useIntl();
+ const [filterValue, setFilterValue] = useState('');
+ const title = formatMessage({ id: 'area.service.filter' });
+
+ const handlefilterButtonClick = () => {
+ if (inputRef) {
+ setFilterValue(inputRef.current.value);
+ }
+ };
const handleUnitCheckboxChange = useCallback((event, id) => {
if (event.target.checked) {
@@ -112,18 +125,37 @@ const GeographicalUnitList = ({ initialOpenItems }) => {
if (emptyCategories.length) {
dispatch(removeSelectedDistrictService(emptyCategories));
}
+
+ // Use filter
+ if (filterValue) {
+ const filteredServiceList = serviceList.filter((category) => {
+ const name = getLocaleText(category.name);
+ return name.toLowerCase().includes(filterValue.toLowerCase());
+ });
+ setServiceList(filteredServiceList);
+ return;
+ }
+
setServiceList(serviceList);
};
useEffect(() => {
createServiceCategories();
- }, [filteredSubdistrictUnits]);
-
+ }, [filteredSubdistrictUnits, filterValue]);
// Render list of units for neighborhood and postcode-area subdistricts
const renderUnitList = useMemo(() => (
+
{serviceList.map(category => (
{
+ const theme = useTheme();
+ const {
+ serviceFilterInputClass,
+ serviceFilterButtonLabelClass,
+ serviceFilterButtonFocusClass,
+ } = createServiceFilterStyles(theme);
+
+ return (
+
+ {typeof title === 'string' && (
+
+ {title}
+
+ )}
+
+ handlefilterButtonClick(), ['enter'])}
+ endAdornment={
+ filterValue ? (
+ {
+ inputRef.current.value = '';
+ setFilterValue('');
+ }}
+ >
+
+
+ ) : null
+ }
+ />
+
+
+
+
+
+
+
+ );
+};
+
+export default ServiceFilterContainer;
diff --git a/src/views/AreaView/components/ServiceFilterContainer/index.js b/src/views/AreaView/components/ServiceFilterContainer/index.js
new file mode 100644
index 000000000..0fb8aead0
--- /dev/null
+++ b/src/views/AreaView/components/ServiceFilterContainer/index.js
@@ -0,0 +1,3 @@
+import ServiceFilterContainer from './ServiceFilterContainer';
+
+export default ServiceFilterContainer;
diff --git a/src/views/AreaView/components/StatisticalDistrictUnitList/StatisticalDistrictUnitListComponent.js b/src/views/AreaView/components/StatisticalDistrictUnitList/StatisticalDistrictUnitListComponent.js
index 47de90329..edd985313 100644
--- a/src/views/AreaView/components/StatisticalDistrictUnitList/StatisticalDistrictUnitListComponent.js
+++ b/src/views/AreaView/components/StatisticalDistrictUnitList/StatisticalDistrictUnitListComponent.js
@@ -1,14 +1,10 @@
-import { css } from '@emotion/css';
-import { Clear } from '@mui/icons-material';
import {
- Button, Checkbox, IconButton, InputBase, List, Typography,
+ Checkbox, List, Typography,
} from '@mui/material';
-import { styled } from '@mui/material/styles';
-import { useTheme } from '@mui/styles';
import { visuallyHidden } from '@mui/utils';
import PropTypes from 'prop-types';
import React, { useMemo, useRef, useState } from 'react';
-import { FormattedMessage, useIntl } from 'react-intl';
+import { useIntl } from 'react-intl';
import { useSelector } from 'react-redux';
import { UnitItem } from '../../../../components';
import {
@@ -22,6 +18,7 @@ import {
StyledUnitList,
StyledUnitListArea,
} from '../styled/styled';
+import ServiceFilterContainer from '../ServiceFilterContainer/ServiceFilterContainer';
// Custom uncontrolled checkbox that allows default value
const UnitCheckbox = ({
@@ -53,7 +50,6 @@ const StatisticalDistrictUnitListComponent = ({
const inputRef = useRef();
const { formatMessage } = useIntl();
const getLocaleText = useLocaleText();
- const theme = useTheme();
const statisticalDistrictUnits = useSelector(getServiceFilteredStatisticalDistrictUnits);
const [initialCheckedItems] = useState(selectedServices || []);
const [filterValue, setFilterValue] = useState('');
@@ -69,67 +65,19 @@ const StatisticalDistrictUnitListComponent = ({
setFilterValue(inputRef.current.value);
}
};
- const serviceFilterInputClass = css({
- margin: theme.spacing(1),
- });
- const serviceFilterButtonLabelClass = css({
- flexDirection: 'column',
- });
- const serviceFilterButtonFocusClass = css({
- boxShadow: `0 0 0 4px ${theme.palette.focusBorder.main} !important`,
- });
// Render list of units for neighborhood and postcode-area subdistricts
const renderServiceList = useMemo(() => (
-
- {
- typeof title === 'string' && (
- {title}
- )
- }
-
- handlefilterButtonClick(), ['enter'])}
- endAdornment={
- filterValue
- ? (
- {
- inputRef.current.value = '';
- setFilterValue('');
- }}
- >
-
-
- )
- : null
- }
- />
-
-
-
-
-
+
{
filterValue && filterValue !== ''
@@ -204,37 +152,3 @@ UnitCheckbox.defaultProps = {
};
export default StatisticalDistrictUnitListComponent;
-
-const StyledRowContainer = styled('div')`
- display: flex;
- flex-direction: row;
- justify-content: space-between;
-`;
-const StyledServiceFilterContainer = styled('div')(({ theme }) => ({
- padding: theme.spacing(2),
- paddingLeft: 72,
- display: 'flex',
- flexDirection: 'column',
-}));
-const StyledServiceFilterText = styled(Typography)(({ theme }) => ({
- paddingBottom: theme.spacing(1),
- fontWeight: 'bold',
-}));
-const StyledServiceFilter = styled(InputBase)(({ theme }) => ({
- backgroundColor: theme.palette.white.main,
- flex: '1 0 auto',
-}));
-const StyledServiceFilterButton = styled(Button)(({ theme }) => ({
- flex: '0 0 auto',
- borderRadius: 0,
- borderTopRightRadius: 4,
- borderBottomRightRadius: 4,
- boxShadow: 'none',
- padding: theme.spacing(1, 2),
- textTransform: 'none',
- '& svg': {
- fontSize: 20,
- marginBottom: theme.spacing(0.5),
- },
- flexDirection: 'column',
-}));
diff --git a/src/views/AreaView/components/styled/styled.js b/src/views/AreaView/components/styled/styled.js
index 3994cbcd4..f8bb763e2 100644
--- a/src/views/AreaView/components/styled/styled.js
+++ b/src/views/AreaView/components/styled/styled.js
@@ -1,6 +1,6 @@
import styled from '@emotion/styled';
import {
- Divider, List, ListItem, Typography,
+ Divider, List, ListItem, Typography, InputBase, Button,
} from '@mui/material';
import { SMAccordion } from '../../../../components';
@@ -113,6 +113,40 @@ const StyledUnitList = styled(List)(({ theme }) => ({
paddingBottom: theme.spacing(1),
}));
+const StyledRowContainer = styled('div')`
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+`;
+const StyledServiceFilterContainer = styled('div')(({ theme }) => ({
+ padding: theme.spacing(2),
+ paddingLeft: 72,
+ display: 'flex',
+ flexDirection: 'column',
+}));
+const StyledServiceFilterText = styled(Typography)(({ theme }) => ({
+ paddingBottom: theme.spacing(1),
+ fontWeight: 'bold',
+}));
+const StyledServiceFilter = styled(InputBase)(({ theme }) => ({
+ backgroundColor: theme.palette.white.main,
+ flex: '1 0 auto',
+}));
+const StyledServiceFilterButton = styled(Button)(({ theme }) => ({
+ flex: '0 0 auto',
+ borderRadius: 0,
+ borderTopRightRadius: 4,
+ borderBottomRightRadius: 4,
+ boxShadow: 'none',
+ padding: theme.spacing(1, 2),
+ textTransform: 'none',
+ '& svg': {
+ fontSize: 20,
+ marginBottom: theme.spacing(0.5),
+ },
+ flexDirection: 'column',
+}));
+
export {
StyledDivider,
StyledDistrictServiceList,
@@ -134,4 +168,9 @@ export {
StyledUnitListArea,
StyledAccordionServiceTitle,
StyledUnitList,
+ StyledRowContainer,
+ StyledServiceFilterContainer,
+ StyledServiceFilterText,
+ StyledServiceFilter,
+ StyledServiceFilterButton,
};
diff --git a/src/views/AreaView/serviceFilterStyles.js b/src/views/AreaView/serviceFilterStyles.js
new file mode 100644
index 000000000..32599b038
--- /dev/null
+++ b/src/views/AreaView/serviceFilterStyles.js
@@ -0,0 +1,13 @@
+import { css } from '@emotion/css';
+
+export const createServiceFilterStyles = (theme) => ({
+ serviceFilterInputClass: css({
+ margin: theme.spacing(1),
+ }),
+ serviceFilterButtonLabelClass: css({
+ flexDirection: 'column',
+ }),
+ serviceFilterButtonFocusClass: css({
+ boxShadow: `0 0 0 4px ${theme.palette.focusBorder.main} !important`,
+ }),
+});
diff --git a/src/views/UnitView/components/ExtendedData/ExtendedData.js b/src/views/UnitView/components/ExtendedData/ExtendedData.js
index a241b707f..a0c637f09 100644
--- a/src/views/UnitView/components/ExtendedData/ExtendedData.js
+++ b/src/views/UnitView/components/ExtendedData/ExtendedData.js
@@ -38,7 +38,7 @@ const ExtendedData = ({
fetchUnitEvents(unit, 50, true);
break;
case 'reservations':
- fetchReservations(unit, 20, true);
+ fetchReservations(unit, true);
break;
default:
}
@@ -163,7 +163,7 @@ const ExtendedData = ({
id="reservations"
data={data || []}
customComponent={item => (
-
+
)}
srTitle={srTitle}
title={titleText}
diff --git a/src/views/UnitView/components/UnitDataList/UnitDataList.js b/src/views/UnitView/components/UnitDataList/UnitDataList.js
index dd67a7819..70cf4f6cb 100644
--- a/src/views/UnitView/components/UnitDataList/UnitDataList.js
+++ b/src/views/UnitView/components/UnitDataList/UnitDataList.js
@@ -22,7 +22,7 @@ const UnitDataList = ({
const dataItems = data.data;
let fullDataLength;
- if (type === 'educationServices') {
+ if (type === 'educationServices' || type === 'reservations') {
fullDataLength = dataItems?.length;
} else {
fullDataLength = data.max;
@@ -57,7 +57,7 @@ const UnitDataList = ({
} if (type === 'reservations') {
return (
shownData.map((item, i) => (
-
+
))
);
}