diff --git a/.env.example b/.env.example index 91a449e6f..8411ae06a 100644 --- a/.env.example +++ b/.env.example @@ -51,6 +51,7 @@ REITTIOPAS_URL="https://reittiopas.foli.fi/reitti/" # New values for Eco-counter and mobility view. THEME_PKG=1 MOBILITY_PLATFORM_API="https://palvelukartta-api.turku.fi" +RAILWAYS_API="https://rata.digitraffic.fi/api/v1" # API URLs to fetch parking spaces data PARKING_SPACES_URL="https://parkkiopas.turku.fi/public/v1/parking_area/" diff --git a/config/default.js b/config/default.js index c92a97d94..ec0efe667 100644 --- a/config/default.js +++ b/config/default.js @@ -282,4 +282,5 @@ export default { "mobilityPlatformAPI": settings.MOBILITY_PLATFORM_API, "parkingSpacesURL": settings.PARKING_SPACES_URL, "parkingStatisticsURL": settings.PARKING_STATISTICS_URL, + "railwaysAPI": settings.RAILWAYS_API, } diff --git a/server/server.js b/server/server.js index 330b8b09b..95fc2d9ff 100644 --- a/server/server.js +++ b/server/server.js @@ -270,6 +270,7 @@ const htmlTemplate = (req, reactDom, preloadedState, css, cssString, emotionCss, window.nodeEnvSettings.MOBILITY_PLATFORM_API = "${process.env.MOBILITY_PLATFORM_API}"; window.nodeEnvSettings.PARKING_SPACES_URL = "${process.env.PARKING_SPACES_URL}"; window.nodeEnvSettings.PARKING_STATISTICS_URL = "${process.env.PARKING_STATISTICS_URL}"; + window.nodeEnvSettings.RAILWAYS_API = "${process.env.RAILWAYS_API}"; window.nodeEnvSettings.FEATURE_SERVICEMAP_PAGE_TRACKING = "${process.env.FEATURE_SERVICEMAP_PAGE_TRACKING}"; window.appVersion = {}; diff --git a/src/.eslintrc b/src/.eslintrc index 2d7d7e963..550d17109 100644 --- a/src/.eslintrc +++ b/src/.eslintrc @@ -5,7 +5,7 @@ "no-shadow": 0, "no-console": ["error", { "allow": ["warn"] }], "no-param-reassign": ["error", { "props": false }], - "max-len": ["error", { "code": 150 }], + "max-len": ["error", { "code": 165 }], "arrow-parens": ["warn", "as-needed"], "react/function-component-definition": [ "error", diff --git a/src/components/MobilityPlatform/ChargerStationMarkers/ChargerStationMarkers.js b/src/components/MobilityPlatform/ChargerStationMarkers/ChargerStationMarkers.js index a6d32022f..0cf7d11ca 100644 --- a/src/components/MobilityPlatform/ChargerStationMarkers/ChargerStationMarkers.js +++ b/src/components/MobilityPlatform/ChargerStationMarkers/ChargerStationMarkers.js @@ -33,7 +33,7 @@ const ChargerStationMarkers = () => { useEffect(() => { const options = { type_name: 'ChargingStation', - page_size: 200, + page_size: 600, }; if (showChargingStations || embedded) { fetchMobilityMapData(options, setChargerStations); @@ -50,15 +50,13 @@ const ChargerStationMarkers = () => { }, [showChargingStations, chargerStations, embedded]); return ( - <> - {renderData - ? chargerStations.map(item => ( - - - - )) - : null} - + renderData + ? chargerStations.map(item => ( + + + + )) + : null ); }; diff --git a/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/CityBikesContent.js b/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/CityBikesContent.js index 7bf8fac14..3e238fc14 100644 --- a/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/CityBikesContent.js +++ b/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/CityBikesContent.js @@ -9,7 +9,7 @@ const CityBikesContent = ({ const isCargoBike = getStationType(); - const getStation = (data) => { + const getStation = data => { if (data && data.length > 0) { return data.find(item => item.station_id === bikeStation.station_id); } @@ -20,14 +20,12 @@ const CityBikesContent = ({ const renderText = (translationId, value) => (
- - {intl.formatMessage({ id: translationId }, { value })} - + {intl.formatMessage({ id: translationId }, { value })}
); /** Remove 'eCargo bikes' from station name before render */ - const formatStationName = (name) => { + const formatStationName = name => { const split = name.split(':'); return split[1]; }; @@ -53,6 +51,8 @@ const CityBikesContent = ({ return null; }; + const getNumberOfVacantPlaces = (capacity, numberOfBikes) => capacity - numberOfBikes; + return (
@@ -66,7 +66,12 @@ const CityBikesContent = ({ ? renderText('mobilityPlatform.content.cityBikes.name', formatStationName(bikeStation.name)) : renderText('mobilityPlatform.content.cityBikes.name', bikeStation.name)} {renderStationType(bikeStation.is_virtual_station, 'mobilityPlatform.content.cityBikes.virtualStation')} - {!isCargoBike ? renderText('mobilityPlatform.content.cityBikes.capacity', bikeStation.capacity) : null} + {!isCargoBike + ? renderText( + 'mobilityPlatform.content.cityBikes.vacantPlaces', + getNumberOfVacantPlaces(bikeStation.capacity, stationItem?.num_bikes_available), + ) + : null}
{!isCargoBike ? renderText('mobilityPlatform.content.cityBikes.bikes.available', stationItem?.num_bikes_available) @@ -94,10 +99,25 @@ const CityBikesContent = ({ }; CityBikesContent.propTypes = { - intl: PropTypes.objectOf(PropTypes.any).isRequired, - classes: PropTypes.objectOf(PropTypes.any).isRequired, - bikeStation: PropTypes.objectOf(PropTypes.any), - cityBikeStatistics: PropTypes.arrayOf(PropTypes.any), + intl: PropTypes.shape({ + formatMessage: PropTypes.func, + }).isRequired, + classes: PropTypes.objectOf(PropTypes.string).isRequired, + bikeStation: PropTypes.shape({ + station_id: PropTypes.string, + name: PropTypes.string, + is_virtual_station: PropTypes.bool, + capacity: PropTypes.number, + rental_uris: PropTypes.shape({ + android: PropTypes.string, + ios: PropTypes.string, + }), + }), + cityBikeStatistics: PropTypes.arrayOf( + PropTypes.shape({ + station_id: PropTypes.string, + }), + ), }; CityBikesContent.defaultProps = { diff --git a/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/__tests__/CityBikesContent.test.js b/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/__tests__/CityBikesContent.test.js index 0e1c982e4..efaf7550b 100644 --- a/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/__tests__/CityBikesContent.test.js +++ b/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/__tests__/CityBikesContent.test.js @@ -44,7 +44,7 @@ describe('', () => { expect(h3.textContent).toContain(finnishTranslations['mobilityPlatform.content.cityBikes.title']); expect(p[0].textContent).toContain(`Asema: ${mockProps.bikeStation.name}`); expect(p[1].textContent).toContain(finnishTranslations['mobilityPlatform.content.cityBikes.virtualStation']); - expect(p[2].textContent).toContain(`Kapasiteetti: ${mockProps.bikeStation.capacity}`); + expect(p[2].textContent).toContain('Vapaita paikkoja: 10'); expect(p[3].textContent).toContain('Pyöriä vapaana: 10'); expect(p[4].textContent).toContain(finnishTranslations['mobilityPlatform.content.general.rentalUris']); expect(link[0].textContent).toEqual('Android'); diff --git a/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/__tests__/__snapshots__/CityBikesContent.test.js.snap b/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/__tests__/__snapshots__/CityBikesContent.test.js.snap index 24ee927a2..3dd153c4c 100644 --- a/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/__tests__/__snapshots__/CityBikesContent.test.js.snap +++ b/src/components/MobilityPlatform/CityBikes/components/CityBikesContent/__tests__/__snapshots__/CityBikesContent.test.js.snap @@ -38,7 +38,7 @@ exports[` should work 1`] = `

- Kapasiteetti: 20 + Vapaita paikkoja: 10

diff --git a/src/components/MobilityPlatform/InfoTextBox/__tests__/__snapshots__/InfoTextBox.test.js.snap b/src/components/MobilityPlatform/InfoTextBox/__tests__/__snapshots__/InfoTextBox.test.js.snap index 51f71d1c5..d29f9467f 100644 --- a/src/components/MobilityPlatform/InfoTextBox/__tests__/__snapshots__/InfoTextBox.test.js.snap +++ b/src/components/MobilityPlatform/InfoTextBox/__tests__/__snapshots__/InfoTextBox.test.js.snap @@ -6,10 +6,10 @@ exports[` should work 1`] = ` class="injectIntl(InfoTextBox)-container-1 injectIntl(InfoTextBox)-padding-3" >

- Turun alueen julkiset autojen sähkölatauspisteet. Latauspistetiedot perustuvat 05/2022 tehtyyn kartoitukseen. + Turun alueen julkiset autojen sähkölatauspisteet. Latauspistetiedot perustuvat 12/2023 tehtyyn kartoitukseen.

diff --git a/src/components/MobilityPlatform/RailwayStations/RailwayStations.js b/src/components/MobilityPlatform/RailwayStations/RailwayStations.js new file mode 100644 index 000000000..ba28978d7 --- /dev/null +++ b/src/components/MobilityPlatform/RailwayStations/RailwayStations.js @@ -0,0 +1,60 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import React, { useEffect, useState } from 'react'; +import { useSelector } from 'react-redux'; +import { useMap } from 'react-leaflet'; +import railwayIcon from 'servicemap-ui-turku/assets/icons/icons-icon_railway_station.svg'; +import railwayIconBw from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_railway_station-bw.svg'; +import { useMobilityPlatformContext } from '../../../context/MobilityPlatformContext'; +import { useAccessibleMap } from '../../../redux/selectors/settings'; +import { fetchRailwaysData } from '../mobilityPlatformRequests/mobilityPlatformRequests'; +import { createIcon, isDataValid } from '../utils/utils'; +import RailwayStationsContent from './components/RailwayStationsContent'; + +const RailwayStations = () => { + const [railwayStations, setRailwayStations] = useState([]); + + const { showRailwayStations } = useMobilityPlatformContext(); + + const map = useMap(); + + const { Marker, Popup } = global.rL; + const { icon } = global.L; + + const useContrast = useSelector(useAccessibleMap); + + const customIcon = icon(createIcon(useContrast ? railwayIconBw : railwayIcon)); + + useEffect(() => { + if (showRailwayStations && !railwayStations.length) { + fetchRailwaysData('metadata/stations', setRailwayStations); + } + }, [showRailwayStations]); + + /** Separate railway stations of Turku, eg. Turku station and Kupittaa */ + const turkuStationCodes = ['TKU', 'KUT', 'TUS']; + const railwayStationsTku = railwayStations.filter(curr => turkuStationCodes.includes(curr.stationShortCode)); + + const renderData = isDataValid(showRailwayStations, railwayStationsTku); + + useEffect(() => { + if (renderData) { + const bounds = []; + railwayStationsTku.forEach(item => { + bounds.push([item.latitude, item.longitude]); + }); + map.fitBounds(bounds); + } + }, [showRailwayStations, railwayStationsTku]); + + return renderData + ? railwayStationsTku.map(item => ( + + + + + + )) + : null; +}; + +export default RailwayStations; diff --git a/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/RailwayStationsContent.js b/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/RailwayStationsContent.js new file mode 100644 index 000000000..c36a5c1e7 --- /dev/null +++ b/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/RailwayStationsContent.js @@ -0,0 +1,194 @@ +import React, { useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import { Typography } from '@mui/material'; +import styled from '@emotion/styled'; +import { format } from 'date-fns'; +import { fetchRailwaysData } from '../../../mobilityPlatformRequests/mobilityPlatformRequests'; + +const RailwayStationsContent = ({ intl, item, stationsData }) => { + const [stationTrainsData, setStationTrainsData] = useState([]); + + const formatDateTime = timeValue => format(new Date(timeValue), 'HH:mm'); + + const optionsToParams = options => { + const params = new URLSearchParams(); + Object.entries({ ...options }).forEach(([key, value]) => { + params.set(key, value); + }); + return params.toString(); + }; + + useEffect(() => { + const options = { + minutes_before_departure: 180, + minutes_after_departure: 15, + minutes_before_arrival: 180, + minutes_after_arrival: 15, + train_categories: 'Long-distance', + }; + const endpoint = `live-trains/station/${item.stationShortCode}`; + const params = optionsToParams(options); + const query = `${endpoint}?${params}`; + fetchRailwaysData(query, setStationTrainsData); + }, [item.stationShortCode]); + + const filterArrivals = data => data.filter(train => { + const lastRow = train.timeTableRows.slice(-1)[0]; + return lastRow.stationShortCode === item.stationShortCode && lastRow.type === 'ARRIVAL'; + }); + + const filterDeparting = data => data.filter(train => { + const firstRow = train.timeTableRows[0]; + return firstRow.stationShortCode === item.stationShortCode && firstRow.type === 'DEPARTURE'; + }); + + const arrivingTrains = filterArrivals(stationTrainsData); + const departingTrains = filterDeparting(stationTrainsData); + + const findStation = (stationsArr, codeValue) => stationsArr.find(station => station.stationShortCode === codeValue); + + const renderDestination = data => { + const lastIdx = data.slice(-1)[0]; + const arrivalStation = findStation(stationsData, lastIdx.stationShortCode); + return ( + + {arrivalStation.stationName} + + ); + }; + + const renderTrainInfo = train => ( + {`${train.trainType} ${train.trainNumber}`} + ); + + const renderTimeValues = elem => ( + + {elem.liveEstimateTime && elem.differenceInMinutes > 1 + ? `${formatDateTime(elem.liveEstimateTime)} (${formatDateTime(elem.scheduledTime)})` + : `${formatDateTime(elem.scheduledTime)}`} + + ); + + return ( + + + + {item?.stationName} + + +
+
+ + + {intl.formatMessage({ id: 'mobilityPlatform.content.departingTrains.title' })} + + {!departingTrains?.length ? ( + + {intl.formatMessage({ id: 'mobilityPlatform.content.departingTrains.empty' })} + + ) : null} + + {departingTrains?.map(train => ( + + + {renderTrainInfo(train)} + {renderDestination(train.timeTableRows)} + {train.timeTableRows + .filter(elem => elem.stationShortCode === item.stationShortCode && elem.type === 'DEPARTURE') + .map(elem => renderTimeValues(elem))} + + ))} +
+
+ + + {intl.formatMessage({ id: 'mobilityPlatform.content.arrivingTrains.title' })} + + {!arrivingTrains?.length ? ( + + {intl.formatMessage({ id: 'mobilityPlatform.content.arrivingTrains.empty' })} + + ) : null} + + {arrivingTrains?.map(train => ( + + + {renderTrainInfo(train)} + {renderDestination(train.timeTableRows)} + {train.timeTableRows + .filter(elem => elem.stationShortCode === item.stationShortCode && elem.type === 'ARRIVAL') + .map(elem => renderTimeValues(elem))} + + ))} +
+
+
+ ); +}; + +const TrainIcon = styled.span(({ color }) => ({ + fontSize: 20, + width: 20, + height: 20, + lineHeight: '21px', + marginLeft: 6, + marginRight: 4, + color, +})); + +const StyledText = styled(Typography)(({ theme }) => ({ + marginBottom: theme.spacing(0.5), +})); + +const StyledTextContainer = styled.div(({ theme }) => ({ + marginBottom: theme.spacing(0.75), +})); + +const StyledFlexContainer = styled.div(({ theme }) => ({ + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + marginBottom: theme.spacing(0.75), + width: '90%', +})); + +const StyledPopupInner = styled.div(({ theme }) => ({ + borderRadius: '3px', + marginBottom: theme.spacing(1), + marginLeft: theme.spacing(1.2), + lineHeight: 1.2, + overflowX: 'hidden', +})); + +const StyledHeader = styled.div(({ theme }) => ({ + display: 'flex', + flexDirection: 'row', + marginTop: theme.spacing(0.5), + marginBottom: theme.spacing(1), + alignItems: 'flex-end', + borderBottom: '2px solid gray', + justifyContent: 'space-between', + width: '80%', +})); + +RailwayStationsContent.propTypes = { + intl: PropTypes.shape({ + formatMessage: PropTypes.func, + }).isRequired, + item: PropTypes.shape({ + stationShortCode: PropTypes.string, + stationName: PropTypes.string, + }), + stationsData: PropTypes.arrayOf( + PropTypes.shape({ + stationShortCode: PropTypes.string, + }), + ), +}; + +RailwayStationsContent.defaultProps = { + item: {}, + stationsData: [], +}; + +export default RailwayStationsContent; diff --git a/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/__tests__/RailwayStationsContent.test.js b/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/__tests__/RailwayStationsContent.test.js new file mode 100644 index 000000000..079405fe5 --- /dev/null +++ b/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/__tests__/RailwayStationsContent.test.js @@ -0,0 +1,55 @@ +// Link.react.test.js +import React from 'react'; +import { getRenderWithProviders } from '../../../../../../../jestUtils'; +import finnishTranslations from '../../../../../../i18n/fi'; +import RailwayStationsContent from '../index'; + +const mockProps = { + item: { + stationName: 'Kupittaa', + stationShortCode: 'KUT', + }, + stationsData: [ + { + stationName: 'Kupittaa', + stationShortCode: 'KUT', + }, + { + stationName: 'Turku asema', + stationShortCode: 'TKU', + }, + { + stationName: 'Turku Satama', + stationShortCode: 'TUS', + }, + { + stationName: 'Helsinki asema', + stationShortCode: 'HKI', + }, + { + stationName: 'Tampere asema', + stationShortCode: 'TRE', + }, + ], +}; + +const renderWithProviders = getRenderWithProviders({}); + +describe('', () => { + it('should match snapshot', () => { + const { container } = renderWithProviders(); + expect(container).toMatchSnapshot(); + }); + + it('does show text', () => { + const { container } = renderWithProviders(); + const p = container.querySelectorAll('p'); + const h4 = container.querySelector('h4'); + const h5 = container.querySelectorAll('h5'); + expect(h4.textContent).toContain(mockProps.item.stationName); + expect(h5[0].textContent).toContain(finnishTranslations['mobilityPlatform.content.departingTrains.title']); + expect(h5[1].textContent).toContain(finnishTranslations['mobilityPlatform.content.arrivingTrains.title']); + expect(p[0]).toBeInTheDocument(); + expect(p[1]).toBeInTheDocument(); + }); +}); diff --git a/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/__tests__/__snapshots__/RailwayStationsContent.test.js.snap b/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/__tests__/__snapshots__/RailwayStationsContent.test.js.snap new file mode 100644 index 000000000..07eb20b81 --- /dev/null +++ b/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/__tests__/__snapshots__/RailwayStationsContent.test.js.snap @@ -0,0 +1,53 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should match snapshot 1`] = ` +
+
+
+

+ Kupittaa +

+
+
+
+
+
+ Lähtevät junat +
+

+ Ei lähteviä junia +

+
+
+
+
+
+ Saapuvat junat +
+

+ Ei saapuvia junia +

+
+
+
+
+
+`; diff --git a/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/index.js b/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/index.js new file mode 100644 index 000000000..40c76cd7f --- /dev/null +++ b/src/components/MobilityPlatform/RailwayStations/components/RailwayStationsContent/index.js @@ -0,0 +1,4 @@ +import { injectIntl } from 'react-intl'; +import RailwayStationsContent from './RailwayStationsContent'; + +export default injectIntl(RailwayStationsContent); diff --git a/src/components/MobilityPlatform/RailwayStations/index.js b/src/components/MobilityPlatform/RailwayStations/index.js new file mode 100644 index 000000000..2224114d8 --- /dev/null +++ b/src/components/MobilityPlatform/RailwayStations/index.js @@ -0,0 +1,3 @@ +import RailwayStations from './RailwayStations'; + +export default RailwayStations; diff --git a/src/components/MobilityPlatform/mobilityPlatformRequests/mobilityPlatformRequests.js b/src/components/MobilityPlatform/mobilityPlatformRequests/mobilityPlatformRequests.js index 7fad0f36e..72a31ee32 100644 --- a/src/components/MobilityPlatform/mobilityPlatformRequests/mobilityPlatformRequests.js +++ b/src/components/MobilityPlatform/mobilityPlatformRequests/mobilityPlatformRequests.js @@ -5,6 +5,9 @@ import config from '../../../../config'; const apiUrl = config.mobilityPlatformAPI; const isApiUrl = !apiUrl || apiUrl === 'undefined' ? null : apiUrl; +const railwaysApiUrl = config.railwaysAPI; +const isRailwaysApiUrl = !railwaysApiUrl || railwaysApiUrl === 'undefined' ? null : railwaysApiUrl; + /** * Returns query options as a search params for URLs * @param {Object} options @@ -12,7 +15,7 @@ const isApiUrl = !apiUrl || apiUrl === 'undefined' ? null : apiUrl; * @returns {string} */ -const optionsToParams = (options) => { +const optionsToParams = options => { const defaultOptions = { page_size: 100, srid: 4326, @@ -35,7 +38,7 @@ const fetchMobilityMapData = async (options, setData) => { } }; -const fetchCultureRouteNames = async (setData) => { +const fetchCultureRouteNames = async setData => { try { const response = await fetch(`${isApiUrl}/mobility_data/mobile_unit_groups/`); const jsonData = await response.json(); @@ -45,7 +48,7 @@ const fetchCultureRouteNames = async (setData) => { } }; -const fetchBicycleRouteNames = async (setData) => { +const fetchBicycleRouteNames = async setData => { try { const response = await fetch(`${isApiUrl}/bicycle_network/bicycle_networks/`); const jsonData = await response.json(); @@ -55,7 +58,7 @@ const fetchBicycleRouteNames = async (setData) => { } }; -const fetchBicycleRoutesGeometry = async (setData) => { +const fetchBicycleRoutesGeometry = async setData => { try { const response = await fetch(`${isApiUrl}/bicycle_network/bicycle_networkparts/?page_size=1000&latlon=true`); const jsonData = await response.json(); @@ -117,6 +120,16 @@ const fetchParkingAreaStats = async (endpoint, setData, setError) => { } }; +const fetchRailwaysData = async (endpoint, setData) => { + try { + const response = await fetch(`${isRailwaysApiUrl}/${endpoint}`); + const jsonData = await response.json(); + setData(jsonData); + } catch (err) { + console.warn(err.message); + } +}; + export { fetchMobilityMapData, fetchCultureRouteNames, @@ -127,4 +140,5 @@ export { fetchStreetMaintenanceData, fetchParkingAreaGeometries, fetchParkingAreaStats, + fetchRailwaysData, }; diff --git a/src/context/MobilityPlatformContext.js b/src/context/MobilityPlatformContext.js index 1d9e6aeda..ac597f7c3 100644 --- a/src/context/MobilityPlatformContext.js +++ b/src/context/MobilityPlatformContext.js @@ -86,12 +86,15 @@ const MobilityPlatformContextProvider = ({ children }) => { const [showFitnessTrails, setShowFitnessTrails] = useState(false); const [fitnessTrailsObj, setFitnessTrailsObj] = useState({}); + // public transport + const [showBusStops, setShowBusStops] = useState(false); + const [showRailwayStations, setShowRailwayStations] = useState(false); + // other const [showOutdoorGymDevices, setShowOutdoorGymDevices] = useState(false); const [showLoadingPlaces, setShowLoadingPlaces] = useState(false); const [showPublicToilets, setShowPublicToilets] = useState(false); const [showCrossWalks, setShowCrossWalks] = useState(false); - const [showBusStops, setShowBusStops] = useState(false); const [showOverpasses, setShowOverpasses] = useState(false); const [showUnderpasses, setShowUnderpasses] = useState(false); const [showPublicBenches, setShowPublicBenches] = useState(false); @@ -149,12 +152,14 @@ const MobilityPlatformContextProvider = ({ children }) => { natureTrailsObj, showFitnessTrails, fitnessTrailsObj, + // public transport + showBusStops, + showRailwayStations, // other showOutdoorGymDevices, showPublicToilets, showLoadingPlaces, showCrossWalks, - showBusStops, showOverpasses, showUnderpasses, showPublicBenches, @@ -213,12 +218,14 @@ const MobilityPlatformContextProvider = ({ children }) => { setNatureTrailsObj, setShowFitnessTrails, setFitnessTrailsObj, + // public transport + setShowBusStops, + setShowRailwayStations, // other setShowOutdoorGymDevices, setShowPublicToilets, setShowLoadingPlaces, setShowCrossWalks, - setShowBusStops, setShowUnderpasses, setShowOverpasses, setShowPublicBenches, diff --git a/src/i18n/en.js b/src/i18n/en.js index c06a40520..3a1bd4265 100644 --- a/src/i18n/en.js +++ b/src/i18n/en.js @@ -796,6 +796,7 @@ const translations = { 'mobilityPlatform.menu.show.rentalCarParking': 'Parking places for shared use cars', 'mobilityPlatform.menu.show.publicBenches': 'Public benches (zoom in on the map)', 'mobilityPlatform.embedded.label.publicBenches': 'Public benches (zoom in on the map to see benches)', + 'mobilityPlatform.menu.show.railwayStations': 'Railway stations', // Content 'mobilityPlatform.content.general.provider': 'Service provider: {value}', @@ -853,9 +854,8 @@ const translations = { 'mobilityPlatform.content.description.notAvailable': 'Description text is not available.', 'mobilityPlatform.content.cityBikes.title': 'City bike station', 'mobilityPlatform.content.cityBikes.name': 'Station: {value}', - 'mobilityPlatform.content.cityBikes.capacity': 'Capacity: {value}', + 'mobilityPlatform.content.cityBikes.vacantPlaces': 'Number of vacant places: {value}', 'mobilityPlatform.content.cityBikes.bikes.available': 'Number of vacant bikes: {value}', - 'mobilityPlatform.content.cityBikes.docks.available': 'Number of vacant docks: {value}', 'mobilityPlatform.content.cityBikes.virtualStation': 'Virtual station', 'mobilityPlatform.content.cargoBikes.title': 'Cargo bike station', 'mobilityPlatform.content.cargoBikes.available': 'Number of available cargo bikes: {value}', @@ -897,6 +897,11 @@ const translations = { 'mobilityPlatform.content.parkingMachine.paymentTypes': 'Payment methods: {value}', 'mobilityPlatform.content.parkingMachine.otherInfo': 'Additional info: {value}', 'mobilityPlatform.content.crosswalks.title': 'Crosswalk', + 'mobilityPlatform.content.railways.train': 'Train: {value1} {value2}', + 'mobilityPlatform.content.departingTrains.title': 'Departing trains', + 'mobilityPlatform.content.arrivingTrains.title': 'Incoming trains', + 'mobilityPlatform.content.departingTrains.empty': 'No departing trains', + 'mobilityPlatform.content.arrivingTrains.empty': 'No incoming trains', // Info text 'mobilityPlatform.info.description.title': 'Route description', @@ -905,7 +910,7 @@ const translations = { 'mobilityPlatform.info.gasFillingStations': 'Public gas filling stations in the Turku area. The information on the gas stations is based on information from the traffic situation website maintained by Fintraffic, https://liikennetilanne.fintraffic.fi', 'mobilityPlatform.info.rentalCars': 'The car share vehicles are rental cars. The available cars are visible on the map. Information about the cars is provided by 24Rent.', 'mobilityPlatform.info.parkingSpaces': 'The map shows public parking areas of Turku. The red colour next to a parking area indicates that it is almost full. To see the exact number of available spaces, click on the parking area on the map. The information on parking areas comes from parking hub of Turku.', - 'mobilityPlatform.info.chargingStations': 'Turku area public car e-charging points. The charging point information is based on a mappig carried out 05/2022.', + 'mobilityPlatform.info.chargingStations': 'Turku area public car e-charging points. The charging point information is based on a mappig carried out 12/2023.', 'mobilityPlatform.info.parkingChargeZones.paragraph.1': 'There are three different zones with different hourly rates in the City of Turku.', 'mobilityPlatform.info.parkingChargeZones.paragraph.2': 'Zone 3 applies to the area between the boundaries of Zone 2 and the city limits.', 'mobilityPlatform.info.parkingChargeZones.paragraph.3': 'However, the charge is always determined by the traffic signs in force and applies to the streets of Turku and the city’s own areas, such as the market hall and the City Hall.', @@ -950,6 +955,7 @@ const translations = { 'mobilityPlatform.info.underAndOverpasses': 'The map shows underpasses and overpasses located within the city of Turku.', 'mobilityPlatform.info.rentalCarParking': 'There are total of five parking places in Turku intended only for shared use cars.', 'mobilityPlatform.info.publicBenches': 'The map shows public benches that are located in the city of Turku.', + 'mobilityPlatform.info.railwayStations': 'The map shows Turku railway stations and train timetables for the next 3 hours. Train traffic is divided into trains departing from and arriving at the station. If the train is delayed, the estimated time of arrival and the old scheduled time of arrival are also shown in parentheses. The data comes from the interface provided by Digitraffic.', // Bicycle routes 'mobilityPlatform.menu.bicycleRoutes.euroVelo': 'The EuroVelo 10, is the European cycle route that stretches along the Finnish costal line. The distance between Helsinki and Turku has roadside directions for the route.', diff --git a/src/i18n/fi.js b/src/i18n/fi.js index 5ceb5e79c..a8b021af4 100644 --- a/src/i18n/fi.js +++ b/src/i18n/fi.js @@ -801,6 +801,7 @@ const translations = { 'mobilityPlatform.menu.show.rentalCarParking': 'Yhteiskäyttöautojen pysäköintipaikat', 'mobilityPlatform.menu.show.publicBenches': 'Yleiset penkit (tarkenna karttaa)', 'mobilityPlatform.embedded.label.publicBenches': 'Yleiset penkit (tarkenna karttaa lähietäisyydelle, jotta penkit näkyvät)', + 'mobilityPlatform.menu.show.railwayStations': 'Rautatieasemat', // Content 'mobilityPlatform.content.general.provider': 'Palveluntarjoaja: {value}', @@ -848,9 +849,8 @@ const translations = { 'mobilityPlatform.content.description.notAvailable': 'Kuvaustekstiä ei ole saatavilla.', 'mobilityPlatform.content.cityBikes.title': 'Kaupunkipyöräasema', 'mobilityPlatform.content.cityBikes.name': 'Asema: {value}', - 'mobilityPlatform.content.cityBikes.capacity': 'Kapasiteetti: {value}', + 'mobilityPlatform.content.cityBikes.vacantPlaces': 'Vapaita paikkoja: {value}', 'mobilityPlatform.content.cityBikes.bikes.available': 'Pyöriä vapaana: {value}', - 'mobilityPlatform.content.cityBikes.docks.available': 'Telineitä vapaana: {value}', 'mobilityPlatform.content.cityBikes.virtualStation': 'Virtuaaliasema', 'mobilityPlatform.content.cargoBikes.title': 'Tavarapyöräasema', 'mobilityPlatform.content.cargoBikes.available': 'Tavarapyöriä on vapaana: {value}', @@ -892,6 +892,11 @@ const translations = { 'mobilityPlatform.content.parkingMachine.paymentTypes': 'Maksutavat: {value}', 'mobilityPlatform.content.parkingMachine.otherInfo': 'Lisätietoja: {value}', 'mobilityPlatform.content.crosswalks.title': 'Suojatie', + 'mobilityPlatform.content.railways.train': 'Juna: {value1} {value2}', + 'mobilityPlatform.content.departingTrains.title': 'Lähtevät junat', + 'mobilityPlatform.content.arrivingTrains.title': 'Saapuvat junat', + 'mobilityPlatform.content.departingTrains.empty': 'Ei lähteviä junia', + 'mobilityPlatform.content.arrivingTrains.empty': 'Ei saapuvia junia', // Info text 'mobilityPlatform.info.description.title': 'Tietoja reitistä', @@ -900,7 +905,7 @@ const translations = { 'mobilityPlatform.info.gasFillingStations': 'Turun alueen julkiset kaasutankkausasemat. Tankkausasemien tiedot perustuvat Fintrafficin ylläpitämän liikennetilanne (https://liikennetilanne.fintraffic.fi) -sivuston tietoihin.', 'mobilityPlatform.info.rentalCars': 'Yhteiskäyttöautot ovat vapaasti vuokrattavia autoja. Kartalla näytetään tällä hetkellä vapaana olevat autot. Tiedot niistä tulevat 24Rent:lta.', 'mobilityPlatform.info.parkingSpaces': 'Kartalla näkyvät Turun julkiset pysäköintialueet. Punainen väri parkkialueen kohdalla tarkoittaa, että se on jo lähes täynnä. Tarkat lukumäärät vapaista paikoista näet klikkaamalla parkkialuetta kartalla. Tiedot pysäköintialueista tulevat Turun parkkihubista.', - 'mobilityPlatform.info.chargingStations': 'Turun alueen julkiset autojen sähkölatauspisteet. Latauspistetiedot perustuvat 05/2022 tehtyyn kartoitukseen.', + 'mobilityPlatform.info.chargingStations': 'Turun alueen julkiset autojen sähkölatauspisteet. Latauspistetiedot perustuvat 12/2023 tehtyyn kartoitukseen.', 'mobilityPlatform.info.parkingChargeZones.paragraph.1': 'Turussa on käytössä kolme eri vyöhykettä, joilla on eri tuntimaksut.', 'mobilityPlatform.info.parkingChargeZones.paragraph.2': '3. vyöhyke on voimassa 2. vyöhykkeen rajojen, sekä kaupungin rajojen välisellä alueella.', 'mobilityPlatform.info.parkingChargeZones.paragraph.3': 'Maksullisuus määräytyy kuitenkin aina voimassa olevien liikennemerkkien mukaisesti ja koskee Turun kaupungin katutilaa ja kaupungin omia alueita, kuten kauppahallia ja kaupungintaloa.', @@ -945,6 +950,7 @@ const translations = { 'mobilityPlatform.info.underAndOverpasses': 'Kartalla näkyvät Turun alueella sijaitsevat alikulkutunnelit ja ylikulkukuväylät.', 'mobilityPlatform.info.rentalCarParking': 'Turussa on yhteensä viisi vain yhteiskäyttöautoille tarkoitettua pysäköintipaikkaa.', 'mobilityPlatform.info.publicBenches': 'Kartalla näkyvät Turussa sijaitsevat yleiset penkit.', + 'mobilityPlatform.info.railwayStations': 'Kartalla näkyvät Turun rautatieasemat ja juna-aikataulut seuraavan 3 tunnin ajalta. Junavuorot on jaettu asemalta lähteviin ja sinne saapuviin juniin. Jos juna on myöhässä, näytetään myös arvioitu saapumisaika ja vanha suunniteltu saapumisaika näkyy suluissa. Tiedot tulevat Digitrafficin rajapinnasta.', // Bicycle routes 'mobilityPlatform.menu.bicycleRoutes.euroVelo': 'EuroVelo 10 on eurooppalainen Suomen rannikkoa seuraava polkupyöräreitti. Helsingin ja Turun välisellä matkalla reitti on merkitty opastein.', diff --git a/src/i18n/sv.js b/src/i18n/sv.js index 2768329f8..38a03c393 100644 --- a/src/i18n/sv.js +++ b/src/i18n/sv.js @@ -800,6 +800,7 @@ const translations = { 'mobilityPlatform.menu.show.rentalCarParking': 'Bilpool bilars parkeringsplatser', 'mobilityPlatform.menu.show.publicBenches': 'Allmänna bänkar (zooma in på kartan)', 'mobilityPlatform.embedded.label.publicBenches': 'Allmänna bänkar (zooma in på kartan för att se bänkar)', + 'mobilityPlatform.menu.show.railwayStations': 'Järnvägsstationer', // Content 'mobilityPlatform.content.general.provider': 'Tjänsteleverantör: {value}', @@ -857,9 +858,8 @@ const translations = { 'mobilityPlatform.content.description.notAvailable': 'Beskrivningstext är inte tillgänglig.', 'mobilityPlatform.content.cityBikes.title': 'Stadscykelstation', 'mobilityPlatform.content.cityBikes.name': 'Station: {value}', - 'mobilityPlatform.content.cityBikes.capacity': 'Kapasitet: {value}', + 'mobilityPlatform.content.cityBikes.vacantPlaces': 'Antal lediga utrymmen: {value}', 'mobilityPlatform.content.cityBikes.bikes.available': 'Antal lediga cycklar kvar: {value}', - 'mobilityPlatform.content.cityBikes.docks.available': 'Antal lediga cykelställ kvar: {value}', 'mobilityPlatform.content.cityBikes.virtualStation': 'Virtual station', 'mobilityPlatform.content.cargoBikes.title': 'Lastcyklarstation', 'mobilityPlatform.content.cargoBikes.available': 'Antal lediga lastcyklar: {value}', @@ -901,6 +901,11 @@ const translations = { 'mobilityPlatform.content.parkingMachine.paymentTypes': 'Betalningstyper: {value}', 'mobilityPlatform.content.parkingMachine.otherInfo': 'Ytterligare info: {value}', 'mobilityPlatform.content.crosswalks.title': 'Övergångställe', + 'mobilityPlatform.content.railways.train': 'Tåg: {value1} {value2}', + 'mobilityPlatform.content.departingTrains.title': 'Avgående tåg', + 'mobilityPlatform.content.arrivingTrains.title': 'Inkommande tåg', + 'mobilityPlatform.content.departingTrains.empty': 'Inga avgående tåg', + 'mobilityPlatform.content.arrivingTrains.empty': 'Inga inkommande tåg', // Info text 'mobilityPlatform.info.description.title': 'Beskrivning av rutten', @@ -909,7 +914,7 @@ const translations = { 'mobilityPlatform.info.gasFillingStations': 'Offentliga gastankstationer i Åboområdet. Informationen om gastankstationerna baseras på information som hämtas från webbplatsen om trafikläget https://liikennetilanne.fintraffic.fi, som underhålls av Fintraffic.', 'mobilityPlatform.info.rentalCars': 'Bilpool bilarna är hyrbilar. Kartan visar lediga bilar. Informationen om bilarna kommer från 24Rent.', 'mobilityPlatform.info.parkingSpaces': 'Kartan visar Åbos allmänna parkeringsområden. Den röda färgen bredvid en parkeringsplats betyder att den är nästan full. För att se det exakta antalet lediga platser, klicka på parkeringsområdet på kartan. Informationen om parkeringsområden kommer från Åbo parkeringscentral.', - 'mobilityPlatform.info.chargingStations': 'Åboområdets allmänna billaddningsplatser. Informationen om laddningsplatser baserar sig på en kartläggning som gjordes 05/2022.', + 'mobilityPlatform.info.chargingStations': 'Åboområdets allmänna billaddningsplatser. Informationen om laddningsplatser baserar sig på en kartläggning som gjordes 12/2023.', 'mobilityPlatform.info.parkingChargeZones.paragraph.1': 'I Åbo finns tre olika zoner, som har olika timavgift för parkering.', 'mobilityPlatform.info.parkingChargeZones.paragraph.2': 'Zon 3 omfattar området mellan gränserna för zon 2 och stadens gränser.', 'mobilityPlatform.info.parkingChargeZones.paragraph.3': 'Avgiften bestäms dock alltid av de gällande trafikskyltarna och gäller Åbo gator och stadens egna områden, t.ex. marknadshuset och stadshuset.', @@ -954,6 +959,7 @@ const translations = { 'mobilityPlatform.info.underAndOverpasses': 'Kartan visar gångtunnlar och gångbroar som ligger i Åbo stadsområdet.', 'mobilityPlatform.info.rentalCarParking': 'Det finns totalt fem parkeringsplatser i Åbo som endast är avsedda för bilpool bilar.', 'mobilityPlatform.info.publicBenches': 'Kartan visar allmänna bänkar i Åbo.', + 'mobilityPlatform.info.railwayStations': 'Kartan visar Åbo järnvägsstationer och tågtidtabeller för de kommande 3 timmarna. Tågtrafiken är uppdelad i tåg som avgår från och ankommer till stationen. Om tåget är försenat visas även den beräknade ankomsttiden och den gamla planerade ankomsttiden inom parentes. Uppgifterna kommer från Digitraffics gränssnitt.', // Bicycle routes 'mobilityPlatform.menu.bicycleRoutes.euroVelo': 'EuroVelo 10 är en europeisk cykelrutt som följer den finländska kusten. Sträckan mellan Helsingfors och Åbo är skyltad.', diff --git a/src/views/MobilityPlatformMapView/MobilityPlatformMapView.js b/src/views/MobilityPlatformMapView/MobilityPlatformMapView.js index 5b6d77b13..fa0079282 100644 --- a/src/views/MobilityPlatformMapView/MobilityPlatformMapView.js +++ b/src/views/MobilityPlatformMapView/MobilityPlatformMapView.js @@ -34,6 +34,7 @@ import CrossWalks from '../../components/MobilityPlatform/CrossWalks'; import Overpasses from '../../components/MobilityPlatform/Overpasses'; import RentalCarParking from '../../components/MobilityPlatform/Parking/RentalCarParking'; import PublicBenches from '../../components/MobilityPlatform/PublicBenches'; +import RailwayStations from '../../components/MobilityPlatform/RailwayStations'; const MobilityPlatformMapView = ({ mapObject }) => ( <> @@ -71,6 +72,7 @@ const MobilityPlatformMapView = ({ mapObject }) => ( + ); diff --git a/src/views/MobilitySettingsView/MobilitySettingsView.js b/src/views/MobilitySettingsView/MobilitySettingsView.js index 8d1704b13..660b17d9d 100644 --- a/src/views/MobilitySettingsView/MobilitySettingsView.js +++ b/src/views/MobilitySettingsView/MobilitySettingsView.js @@ -162,9 +162,11 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { setShowRentalCarParking, showPublicBenches, setShowPublicBenches, + showRailwayStations, + setShowRailwayStations, } = useMobilityPlatformContext(); - const locale = useSelector((state) => state.user.locale); + const locale = useSelector(state => state.user.locale); const location = useLocation(); const getLocaleText = useLocaleText(); @@ -440,7 +442,8 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { useEffect(() => { checkVisibilityValues(showBusStops, setOpenPublicTransportSettings); - }, [showBusStops]); + checkVisibilityValues(showRailwayStations, setOpenPublicTransportSettings); + }, [showBusStops, showRailwayStations]); const nameKeys = { fi: 'name', @@ -455,7 +458,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { */ useEffect(() => { if (cultureRouteList && cultureRouteList.length > 0) { - setLocalizedCultureRoutes(cultureRouteList.filter((item) => item[nameKeys[locale]])); + setLocalizedCultureRoutes(cultureRouteList.filter(item => item[nameKeys[locale]])); } }, [cultureRouteList, locale]); @@ -474,7 +477,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { } }, [cultureRouteList, localizedCultureRoutes, locale]); - const sortMarkedTrails = (data) => { + const sortMarkedTrails = data => { if (data && data.length > 0) { return data.sort((a, b) => a[nameKeys[locale]].split(': ').slice(-1)[0].localeCompare(b[nameKeys[locale]].split(': ').slice(-1)[0])); } @@ -505,7 +508,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { } }, [bicycleRouteList, locale]); - const sortTrails = (data) => { + const sortTrails = data => { if (data && data.length > 0) { return data.sort((a, b) => a.name.localeCompare(b.name)); } @@ -518,7 +521,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { * @function reduce * @returns {Array} */ - const getLocalTrails = (data) => data.reduce((acc, curr) => { + const getLocalTrails = data => data.reduce((acc, curr) => { if (curr.municipality === 'turku') { acc.push(curr); } @@ -537,7 +540,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { * @returns {Boolean} */ const walkSettingsToggle = () => { - setOpenWalkSettings((current) => !current); + setOpenWalkSettings(current => !current); if (!openWalkSettings) { navigator.push('mobilityPlatform', 'walking'); setPageTitle(intl.formatMessage({ id: 'mobilityPlatform.menu.title.walk' })); @@ -545,7 +548,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { }; const bicycleSettingsToggle = () => { - setOpenBicycleSettings((current) => !current); + setOpenBicycleSettings(current => !current); if (!openBicycleSettings) { navigator.push('mobilityPlatform', 'cycling'); setPageTitle(intl.formatMessage({ id: 'mobilityPlatform.menu.title.bicycle' })); @@ -553,7 +556,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { }; const carSettingsToggle = () => { - setOpenCarSettings((current) => !current); + setOpenCarSettings(current => !current); if (!openCarSettings) { navigator.push('mobilityPlatform', 'driving'); setPageTitle(intl.formatMessage({ id: 'mobilityPlatform.menu.title.car' })); @@ -561,7 +564,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { }; const publicTransportSettingsToggle = () => { - setOpenPublicTransportSettings((current) => !current); + setOpenPublicTransportSettings(current => !current); if (!openPublicTransportSettings) { navigator.push('mobilityPlatform', 'transport'); setPageTitle(intl.formatMessage({ id: 'mobilityPlatform.menu.title.public.transport' })); @@ -569,7 +572,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { }; const boatingSettingsToggle = () => { - setOpenBoatingSettings((current) => !current); + setOpenBoatingSettings(current => !current); if (!openBoatingSettings) { navigator.push('mobilityPlatform', 'boating'); setPageTitle(intl.formatMessage({ id: 'mobilityPlatform.menu.title.boating' })); @@ -577,7 +580,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { }; const scooterSettingsToggle = () => { - setOpenScooterSettings((current) => !current); + setOpenScooterSettings(current => !current); if (!openScooterSettings) { navigator.push('mobilityPlatform', 'scooters'); setPageTitle(intl.formatMessage({ id: 'mobilityPlatform.menu.title.scooter' })); @@ -585,7 +588,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { }; const streetMaintenanceSettingsToggle = () => { - setOpenStreetMaintenanceSettings((current) => !current); + setOpenStreetMaintenanceSettings(current => !current); if (!openStreetMaintenanceSettings) { navigator.push('mobilityPlatform', 'snowplows'); setPageTitle(intl.formatMessage({ id: 'mobilityPlatform.menu.title.streetMaintenance' })); @@ -624,7 +627,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { * @param {*function} setState */ const toggleObjectValue = (key, state, setState) => { - setState((prevState) => ({ + setState(prevState => ({ ...prevState, [key]: !prevState[key], })); @@ -663,126 +666,130 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { * @returns {boolean} */ const bicycleStandsToggle = () => { - setShowBicycleStands((current) => !current); + setShowBicycleStands(current => !current); }; const hullLockableStandsToggle = () => { - setShowHullLockableStands((current) => !current); + setShowHullLockableStands(current => !current); }; const parkingSpacesToggle = () => { - setShowParkingSpaces((current) => !current); + setShowParkingSpaces(current => !current); }; const rentalCarsToggle = () => { - setShowRentalCars((current) => !current); + setShowRentalCars(current => !current); }; const gasFillingStationsToggle = () => { - setShowGasFillingStations((current) => !current); + setShowGasFillingStations(current => !current); }; const chargingStationsToggle = () => { - setShowChargingStations((current) => !current); + setShowChargingStations(current => !current); }; const bikeServiceStationsToggle = () => { - setShowBikeServiceStations((current) => !current); + setShowBikeServiceStations(current => !current); }; const cityBikesToggle = () => { - setShowCityBikes((current) => !current); + setShowCityBikes(current => !current); }; const cargoBikesToggle = () => { - setShowCargoBikes((current) => !current); + setShowCargoBikes(current => !current); }; const marinasToggle = () => { - setShowMarinas((current) => !current); + setShowMarinas(current => !current); }; const boatParkingToggle = () => { - setShowBoatParking((current) => !current); + setShowBoatParking(current => !current); }; const guestHarbourToggle = () => { - setShowGuestHarbour((current) => !current); + setShowGuestHarbour(current => !current); }; const publicToiletsToggle = () => { - setShowPublicToilets((current) => !current); + setShowPublicToilets(current => !current); }; const publicBenchesToggle = () => { - setShowPublicBenches((current) => !current); + setShowPublicBenches(current => !current); }; const noParkingToggle = () => { - setShowScooterNoParking((current) => !current); + setShowScooterNoParking(current => !current); }; const parkingAreasToggle = () => { - setShowScooterParkingAreas((current) => !current); + setShowScooterParkingAreas(current => !current); }; const parkingMachinesToggle = () => { - setShowParkingMachines((current) => !current); + setShowParkingMachines(current => !current); }; const loadingPlacesToggle = () => { - setShowLoadingPlaces((current) => !current); + setShowLoadingPlaces(current => !current); }; const outdoorGymDevicesToggle = () => { - setShowOutdoorGymDevices((current) => !current); + setShowOutdoorGymDevices(current => !current); }; const crossWalksToggle = () => { - setShowCrossWalks((current) => !current); + setShowCrossWalks(current => !current); }; const overPassesToggle = () => { - setShowOverpasses((current) => !current); + setShowOverpasses(current => !current); }; const underPassesToggle = () => { - setShowUnderpasses((current) => !current); + setShowUnderpasses(current => !current); }; const scooterSpeedLimitAreasToggle = () => { - setShowScooterSpeedLimitAreas((current) => !current); + setShowScooterSpeedLimitAreas(current => !current); }; const scooterListToggle = () => { - setOpenScooterProviderList((current) => !current); + setOpenScooterProviderList(current => !current); if (showScootersRyde) { setShowScootersRyde(false); } }; const scootersRydeToggle = () => { - setShowScootersRyde((current) => !current); + setShowScootersRyde(current => !current); }; const disabledParkingToggle = () => { - setShowDisabledParking((current) => !current); + setShowDisabledParking(current => !current); }; const publicParkingToggle = () => { - setShowPublicParking((current) => !current); + setShowPublicParking(current => !current); }; const rentalCarParkingToggle = () => { - setShowRentalCarParking((current) => !current); + setShowRentalCarParking(current => !current); }; const busStopsToggle = () => { - setShowBusStops((current) => !current); + setShowBusStops(current => !current); + }; + + const railwayStationsToggle = () => { + setShowRailwayStations(current => !current); }; const cultureRouteListToggle = () => { - setOpenCultureRouteList((current) => !current); + setOpenCultureRouteList(current => !current); if (cultureRouteId) { setCultureRouteId(null); } @@ -792,7 +799,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { }; const bicycleRouteListToggle = () => { - setOpenBicycleRouteList((current) => !current); + setOpenBicycleRouteList(current => !current); if (bicycleRouteName) { setBicycleRouteName(null); } @@ -802,7 +809,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { }; const markedTrailListToggle = () => { - setOpenMarkedTrailsList((current) => !current); + setOpenMarkedTrailsList(current => !current); if (markedTrailsObj) { setMarkedTrailsObj({}); } @@ -812,7 +819,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { }; const natureTrailListToggle = () => { - setOpenNatureTrailsList((current) => !current); + setOpenNatureTrailsList(current => !current); if (natureTrailsObj) { setNatureTrailsObj({}); } @@ -822,7 +829,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { }; const fitnessTrailListToggle = () => { - setOpenFitnessTrailsList((current) => !current); + setOpenFitnessTrailsList(current => !current); if (fitnessTrailsObj) { setFitnessTrailsObj({}); } @@ -832,7 +839,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { }; const streetMaintenanceListToggle = () => { - setOpenStreetMaintenanceSelectionList((current) => !current); + setOpenStreetMaintenanceSelectionList(current => !current); if (streetMaintenancePeriod) { setStreetMaintenancePeriod(null); } @@ -842,11 +849,11 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { }; const brushSandedRouteToggle = () => { - setShowBrushSandedRoute((current) => !current); + setShowBrushSandedRoute(current => !current); }; const brushSaltedRouteToggle = () => { - setShowBrushSaltedRoute((current) => !current); + setShowBrushSaltedRoute(current => !current); }; /** @@ -863,7 +870,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { * Otherwise new values are set * @param {string} itemId */ - const setCultureRouteState = (itemId) => { + const setCultureRouteState = itemId => { setCultureRouteId(itemId); setShowCultureRoutes(true); if (itemId === prevCultureRouteIdRef.current) { @@ -888,7 +895,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { /** * @param {string} routeName */ - const setBicycleRouteState = (routeName) => { + const setBicycleRouteState = routeName => { setBicycleRouteName(routeName); setShowBicycleRoutes(true); if (routeName === prevBicycleRouteNameRef.current) { @@ -913,7 +920,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { /** * @param {obj} */ - const setMarkedTrailState = (obj) => { + const setMarkedTrailState = obj => { setMarkedTrailsObj(obj); setShowMarkedTrails(true); if (obj === prevMarkedTrailObjRef.current) { @@ -935,7 +942,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { /** * @param {obj} */ - const setNatureTrailState = (obj) => { + const setNatureTrailState = obj => { setNatureTrailsObj(obj); setShowNatureTrails(true); if (obj === prevNatureTrailObjRef.current) { @@ -960,7 +967,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { /** * @param {obj} */ - const setFitnessTrailState = (obj) => { + const setFitnessTrailState = obj => { setFitnessTrailsObj(obj); setShowFitnessTrails(true); if (obj === prevFitnessTrailObjRef.current) { @@ -970,22 +977,22 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { }; const speedLimitZonesToggle = () => { - setOpenSpeedLimitList((current) => !current); - setShowSpeedLimitZones((current) => !current); + setOpenSpeedLimitList(current => !current); + setShowSpeedLimitZones(current => !current); if (speedLimitSelections && speedLimitSelections.length > 0) { setSpeedLimitSelections([]); } }; - const setSpeedLimitState = (limitItem) => { + const setSpeedLimitState = limitItem => { if (!speedLimitSelections.includes(limitItem)) { - setSpeedLimitSelections((speedLimitSelections) => [...speedLimitSelections, limitItem]); + setSpeedLimitSelections(speedLimitSelections => [...speedLimitSelections, limitItem]); setShowSpeedLimitZones(true); - } else setSpeedLimitSelections(speedLimitSelections.filter((item) => item !== limitItem)); + } else setSpeedLimitSelections(speedLimitSelections.filter(item => item !== limitItem)); }; const parkingChargeZonesListToggle = () => { - setOpenParkingChargeZoneList((current) => !current); + setOpenParkingChargeZoneList(current => !current); if (showParkingChargeZones) { setShowParkingChargeZones(false); } @@ -1008,7 +1015,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { * Otherwise new values are set * @param {string} id */ - const selectParkingChargeZone = (id) => { + const selectParkingChargeZone = id => { setParkingChargeZoneId(id); setShowParkingChargeZones(true); if (id === prevParkingChargeZoneIdRef.current) { @@ -1026,7 +1033,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { prevStreetMaintenancePeriodRef.current = streetMaintenancePeriod; }, [streetMaintenancePeriod]); - const setStreetMaintenancePeriodSelection = (periodType) => { + const setStreetMaintenancePeriodSelection = periodType => { setStreetMaintenancePeriod(periodType); setShowStreetMaintenance(true); if (periodType === prevStreetMaintenancePeriodRef.current) { @@ -1279,6 +1286,12 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { checkedValue: showBusStops, onChangeValue: busStopsToggle, }, + { + type: 'railwayStations', + msgId: 'mobilityPlatform.menu.show.railwayStations', + checkedValue: showRailwayStations, + onChangeValue: railwayStationsToggle, + }, ]; const boatingControlTypes = [ @@ -1351,7 +1364,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { * @param {Array} inputData * @return {JSX Element} */ - const renderBicycleRoutes = (inputData) => ( + const renderBicycleRoutes = inputData => ( { * @param {Array} inputData * @return {JSX Element} */ - const renderCultureRoutes = (inputData) => ( + const renderCultureRoutes = inputData => ( { */ const renderSettings = (settingVisibility, settingsData) => { if (settingVisibility) { - return settingsData.map((item) => ( + return settingsData.map(item => (
{ // Create array of speed limit values from data and remove duplicates const speedLimitList = useMemo( - () => [...new Set(speedLimitZones.map((item) => item.extra.speed_limit))], + () => [...new Set(speedLimitZones.map(item => item.extra.speed_limit))], [speedLimitZones], ); @@ -1437,19 +1450,12 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { > {intl.formatMessage({ id: 'mobilityPlatform.menu.streetMaintenance.info' })} -
- {streetMaintenanceInfo(classes.blue, 'mobilityPlatform.menu.streetMaintenance.info.snowplow')} - {streetMaintenanceInfo(classes.purple, 'mobilityPlatform.menu.streetMaintenance.info.deicing')} - {streetMaintenanceInfo(classes.burgundy, 'mobilityPlatform.menu.streetMaintenance.info.sandRemoval')} - {streetMaintenanceInfo(classes.green, 'mobilityPlatform.menu.streetMaintenance.info.sanitation')} -
{!isActiveStreetMaintenance && streetMaintenancePeriod ? ( ) : null}
- {streetMaintenanceSelections - && streetMaintenanceSelections.length > 0 - && streetMaintenanceSelections.map((item) => ( + {streetMaintenanceSelections?.length > 0 + && streetMaintenanceSelections.map(item => (
{ />
))} +
+
+ {streetMaintenanceInfo(classes.blue, 'mobilityPlatform.menu.streetMaintenance.info.snowplow')} + {streetMaintenanceInfo(classes.purple, 'mobilityPlatform.menu.streetMaintenance.info.deicing')} + {streetMaintenanceInfo(classes.burgundy, 'mobilityPlatform.menu.streetMaintenance.info.sandRemoval')} + {streetMaintenanceInfo(classes.green, 'mobilityPlatform.menu.streetMaintenance.info.sanitation')} +
+
) : null); @@ -1678,13 +1692,18 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { type: 'busStopsInfo', component: , }, + { + visible: showRailwayStations, + type: 'railwayStationsInfo', + component: , + }, ]; /** Render infotext(s) if visible value is true * @param {Array} textData * @return {Element} */ - const renderInfoTexts = (textData) => textData.reduce((acc, curr) => { + const renderInfoTexts = textData => textData.reduce((acc, curr) => { if (curr.visible) { acc.push({curr.component}); } @@ -1870,7 +1889,7 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => {
- {categories.map((category) => ( + {categories.map(category => (