From 5e8180af257d8664ee5ba29b16fac87fca8d70e1 Mon Sep 17 00:00:00 2001 From: juhomakkonen Date: Thu, 29 Feb 2024 13:56:51 +0200 Subject: [PATCH 01/15] Add optional chaining & update min & max date values --- .../AirMonitoringContent/AirMonitoringContent.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/MobilityPlatform/EnvironmentObservations/AirMonitoring/components/AirMonitoringContent/AirMonitoringContent.js b/src/components/MobilityPlatform/EnvironmentObservations/AirMonitoring/components/AirMonitoringContent/AirMonitoringContent.js index 27c7f5bd1..b140983cb 100644 --- a/src/components/MobilityPlatform/EnvironmentObservations/AirMonitoring/components/AirMonitoringContent/AirMonitoringContent.js +++ b/src/components/MobilityPlatform/EnvironmentObservations/AirMonitoring/components/AirMonitoringContent/AirMonitoringContent.js @@ -10,7 +10,7 @@ import { } from '@mui/material'; import styled from '@emotion/styled'; import { - getMonth, getWeek, getYear, format, startOfMonth, + getMonth, getWeek, getYear, format, startOfMonth, subDays, } from 'date-fns'; import { enGB, fi, sv } from 'date-fns/locale'; import DatePicker, { registerLocale } from 'react-datepicker'; @@ -50,6 +50,8 @@ const AirMonitoringContent = ({ intl, station }) => { const selectedMonth = getMonth(selectedDate) + 1; const selectedYear = getYear(selectedDate); + const yesterday = subDays(new Date(), 1); + const changeDate = newDate => { setSelectedDate(newDate); }; @@ -352,7 +354,7 @@ const AirMonitoringContent = ({ intl, station }) => { const renderData = () => { const data = setRenderData(); - return data.map(item => ( + return data?.map(item => ( @@ -361,7 +363,7 @@ const AirMonitoringContent = ({ intl, station }) => {
- {item.measurements.map(measurement => ( + {item?.measurements?.map(measurement => (
{measurement.parameter === 'AQINDEX_PT1H_avg' ? renderAirQuality(measurement) @@ -386,8 +388,8 @@ const AirMonitoringContent = ({ intl, station }) => { dateFormat="P" showYearDropdown dropdownMode="select" - minDate={new Date('2015-01-01')} - maxDate={new Date()} + minDate={new Date('2011-01-01')} + maxDate={yesterday} customInput={} /> @@ -396,7 +398,7 @@ const AirMonitoringContent = ({ intl, station }) => { {renderData()}
- {buttonSteps.map((timing, i) => ( + {buttonSteps?.map((timing, i) => ( Date: Thu, 29 Feb 2024 15:30:55 +0200 Subject: [PATCH 02/15] Update info texts & test --- src/i18n/en.js | 8 ++++---- src/i18n/fi.js | 2 +- src/i18n/sv.js | 8 ++++---- .../__snapshots__/AirMonitoringInfo.test.js.snap | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/i18n/en.js b/src/i18n/en.js index 426b25841..c78fbaf4b 100644 --- a/src/i18n/en.js +++ b/src/i18n/en.js @@ -961,10 +961,10 @@ const translations = { 'mobilityPlatform.info.publicBenches': 'The map shows public benches that are located in the city of Turku.', 'mobilityPlatform.info.roadworks': 'The map shows road construction sites in the Turku area, which cause exceptional situations for traffic and mobility. Reduced speed limits apply in marked areas and, if necessary, traffic can be diverted. The data comes from the interface provided by Digitraffic.', '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.', - 'mobilityPlatform.info.airMonitoring.paragraph.1': 'The air quality data is displayed for each measurement station as an hourly calculated index, covering all pollutants measured at the respective station. Air quality is classified into five categories: green = good, yellow = satisfactory, orange = moderate, red = poor, violet = very poor.', - 'mobilityPlatform.info.airMonitoring.paragraph.2': 'The calculation takes into account sulfur dioxide (SO2), nitrogen dioxide (NO2), inhalable particles (PM10), fine particles (PM2.5), and ozone (O3). When air quality is poor or very poor, health effects are possible, especially for sensitive individuals. Factors contributing to poor air quality include street dust, traffic emissions, wood burning, energy production, and occasional long-range transport.', - 'mobilityPlatform.info.airMonitoring.paragraph.3': 'The air quality information on the Service map is obtained from the air protection collaboration group of Turku.', - 'mobilityPlatform.info.airMonitoring.link': 'Additional information', + 'mobilityPlatform.info.airMonitoring.paragraph.1': 'Air pollution data is displayed for each measuring station as an index calculated for each hour and covers all pollutants measured at the respective measuring station. The air quality is classified into five categories: green=good, yellow=satisfactory, orange=fair, red=poor, purple=very poor.', + 'mobilityPlatform.info.airMonitoring.paragraph.2': 'The calculation takes into account sulfur dioxide (SO2), nitrogen dioxide (NO2), respirable particles (PM10), fine particles (PM2.5), and ozone (O3). When the air quality is poor or very poor, health effects may occur in sensitive individuals. Air quality is worsened by street dust, traffic emissions, small-scale wood combustion, energy production, and occasionally by long range transport.', + 'mobilityPlatform.info.airMonitoring.paragraph.3': 'Air quality data for the service map is obtained from the Turku region air protection co-operative group.', + 'mobilityPlatform.info.airMonitoring.link': 'For more information visit https://en.ilmatieteenlaitos.fi/air-quality', // 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 6aac190ff..f0dd4de7b 100644 --- a/src/i18n/fi.js +++ b/src/i18n/fi.js @@ -959,7 +959,7 @@ const translations = { 'mobilityPlatform.info.airMonitoring.paragraph.1': 'Ilmansaastetiedot näytetään kullekin mittausasemalle tunnin välein laskettavana indeksinä, ja se kattaa kaikki ks. mittausasemalla mitatut saasteet. Ilmanlaatu luokitellaan viiteen luokkaan: vihreä=hyvä, keltainen=tyydyttävä, oranssi=välttävä, punainen=huono, violetti=erittäin huono.', 'mobilityPlatform.info.airMonitoring.paragraph.2': 'Laskennassa otetaan huomioon rikkidioksidi (SO2), typpidioksidi (NO2), hengitettävät hiukkaset (PM10), pienhiukkaset (PM2,5) sekä otsoni (O3). Kun ilmanlaatu on huono tai erittäin huono, terveysvaikutukset ovat mahdollisia herkillä ihmisillä. Ilmanlaatua heikentävät katupöly, liikenteen päästöt, puun pienpoltto, energiantuotanto sekä ajoittaiset kaukokulkeumat.', 'mobilityPlatform.info.airMonitoring.paragraph.3': 'Palvelukartan ilmanlaatutiedot saadaan Turun seudun ilmansuojelun yhteistyöryhmältä.', - 'mobilityPlatform.info.airMonitoring.link': 'Lisätietoja', + 'mobilityPlatform.info.airMonitoring.link': 'Lisätietoja saa osoitteesta https://www.ilmatieteenlaitos.fi/ilmanlaatu', // 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 a26e31484..48fe4e1e5 100644 --- a/src/i18n/sv.js +++ b/src/i18n/sv.js @@ -965,10 +965,10 @@ const translations = { 'mobilityPlatform.info.publicBenches': 'Kartan visar allmänna bänkar i Åbo.', 'mobilityPlatform.info.roadworks': 'Kartan visar vägbyggen i Åboområdet som orsakar exceptionella situationer för trafiken och mobiliteten. Sänkta hastighetsbegränsningar gäller på markerade områden och vid behov kan trafiken ledas om. Uppgifterna kommer från Digitraffics gränssnitt.', '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.', - 'mobilityPlatform.info.airMonitoring.paragraph.1': 'Luftkvalitetsdata visas för varje mätstation som en timme beräknad index och omfattar alla föroreningar som mäts vid respektive station. Luftkvaliteten klassificeras i fem kategorier: grön = bra, gul = tillfredsställande, orange = måttlig, röd = dålig, violett = mycket dålig.', - 'mobilityPlatform.info.airMonitoring.paragraph.2': 'Beräkningen tar hänsyn till svaveldioxid (SO2), kvävedioxid (NO2), inandningsbara partiklar (PM10), fina partiklar (PM2,5) och ozon (O3). När luftkvaliteten är dålig eller mycket dålig kan hälsopåverkan vara möjlig, särskilt för känsliga personer. Faktorer som försämrar luftkvaliteten inkluderar gatudamm, trafikutsläpp, vedeldning, energiproduktion och sporadiska fjärrtransporter.', - 'mobilityPlatform.info.airMonitoring.paragraph.3': 'Luftkvalitetsinformationen på Tjänstekartan erhålls från samarbetsgruppen för luftskydd i Åbo-regionen.', - 'mobilityPlatform.info.airMonitoring.link': 'Ytterligare info', + 'mobilityPlatform.info.airMonitoring.paragraph.1': 'Luftföroreningsdata visas för varje mätningsstation som ett index beräknat för varje timme och omfattar alla föroreningar som mäts vid resp. mätningsstation. Luftkvaliteten klassificeras i fem kategorier: grön=bra, gul=tillfredsställande, orange=nöjaktig, röd=dålig, lila=mycket dålig.', + 'mobilityPlatform.info.airMonitoring.paragraph.2': 'Vid beräkningen beaktas svaveldioxid (SO2), kvävedioxid (NO2), inandningsbara partiklar (PM10), småpartiklar (PM2,5) samt ozon (O3). När luftkvaliteten är dålig eller mycket dålig kan hälsopåverkan förekomma hos känsliga personer. Luftkvaliteten försämras av gatudamm, trafikutsläpp, småskalig vedeldning, energiproduktion samt av sporadiskt förekommande långväga transport.', + 'mobilityPlatform.info.airMonitoring.paragraph.3': 'Luftkvalitetsdata för Servicekartan erhålls från Åboregionens samarbetsgrupp för luftskydd.', + 'mobilityPlatform.info.airMonitoring.link': 'För mer information besök: https://sv.ilmatieteenlaitos.fi/luftkvalitet', // 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/MobilitySettingsView/components/AirMonitoringInfo/__tests__/__snapshots__/AirMonitoringInfo.test.js.snap b/src/views/MobilitySettingsView/components/AirMonitoringInfo/__tests__/__snapshots__/AirMonitoringInfo.test.js.snap index 43a83ab4a..d065c9197 100644 --- a/src/views/MobilitySettingsView/components/AirMonitoringInfo/__tests__/__snapshots__/AirMonitoringInfo.test.js.snap +++ b/src/views/MobilitySettingsView/components/AirMonitoringInfo/__tests__/__snapshots__/AirMonitoringInfo.test.js.snap @@ -28,7 +28,7 @@ exports[` should match snapshot 1`] = `

- Lisätietoja + Lisätietoja saa osoitteesta https://www.ilmatieteenlaitos.fi/ilmanlaatu

From a3e38a811823d69c80c56c6b782914e8dc81c5a4 Mon Sep 17 00:00:00 2001 From: juhomakkonen <69158965+juhomakkonen@users.noreply.github.com> Date: Thu, 7 Mar 2024 09:16:12 +0200 Subject: [PATCH 03/15] Fix/counter values error check (#226) * Add optional chaining into counter variables * Formatting changes --- .../EcoCounterContent/EcoCounterContent.js | 50 +++++++++---------- .../LamCounterContent/LamCounterContent.js | 48 +++++++++--------- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/src/components/EcoCounter/TrafficCounters/EcoCounterContent/EcoCounterContent.js b/src/components/EcoCounter/TrafficCounters/EcoCounterContent/EcoCounterContent.js index 3f2342e15..b2e0f547c 100644 --- a/src/components/EcoCounter/TrafficCounters/EcoCounterContent/EcoCounterContent.js +++ b/src/components/EcoCounter/TrafficCounters/EcoCounterContent/EcoCounterContent.js @@ -54,18 +54,18 @@ const EcoCounterContent = ({ classes, intl, station }) => { const [activeStep, setActiveStep] = useState(0); const [selectedDate, setSelectedDate] = useState(subDays(new Date(), 1)); - const locale = useSelector((state) => state.user.locale); + const locale = useSelector(state => state.user.locale); const inputRef = useRef(null); const useMobileStatus = () => useMediaQuery('(max-width:768px)'); const isNarrow = useMobileStatus(); - const stationId = station.id; - const stationName = station.name; - const stationSource = station.csv_data_source; - const dataFrom = station.data_from_date; - const dataUntil = station.data_until_date; - const isActiveStation = station.is_active['30']; + const stationId = station?.id; + const stationName = station?.name; + const stationSource = station?.csv_data_source; + const dataFrom = station?.data_from_date; + const dataUntil = station?.data_until_date; + const isActiveStation = station?.is_active?.['30']; /** When all 3 user types are rendered, a reverse order is required where 'at' is placed last */ const reverseUserTypes = () => { @@ -142,7 +142,7 @@ const EcoCounterContent = ({ classes, intl, station }) => { * @param {string} translationId * @returns JSX element */ - const userTypeText = (translationId) => ( + const userTypeText = translationId => (
{intl.formatMessage({ id: translationId })} @@ -155,7 +155,7 @@ const EcoCounterContent = ({ classes, intl, station }) => { * @param {string} userType * @returns JSX element */ - const renderUserTypeText = (userType) => { + const renderUserTypeText = userType => { if (userType === 'at') { return userTypeText('ecocounter.car'); } @@ -206,7 +206,7 @@ const EcoCounterContent = ({ classes, intl, station }) => { return null; }; - const changeDate = (newDate) => { + const changeDate = newDate => { setSelectedDate(newDate); }; @@ -225,7 +225,7 @@ const EcoCounterContent = ({ classes, intl, station }) => { * @param {*date} dateValue * @returns {*number} */ - const checkWeekNumber = (dateValue) => { + const checkWeekNumber = dateValue => { const start = getWeek(startOfMonth(dateValue)); const end = getWeek(endOfMonth(dateValue)); if (start > end) { @@ -302,7 +302,7 @@ const EcoCounterContent = ({ classes, intl, station }) => { * @param {date} weekValue * @returns {*string} */ - const formatWeeks = (weekValue) => { + const formatWeeks = weekValue => { const startOfSelectedWeek = startOfWeek(new Date(selectedYear, 0, 1), { weekStartsOn: 1 }); const targetWeekStartDate = addWeeks(startOfSelectedWeek, weekValue - 1); return format(targetWeekStartDate, 'dd.MM', { weekStartsOn: 1 }); @@ -325,9 +325,9 @@ const EcoCounterContent = ({ classes, intl, station }) => { * @param {*object} newValue3 */ const setAllChannelCounts = (newValue1, newValue2, newValue3) => { - setChannel1Counts((channel1Counts) => [...channel1Counts, newValue1]); - setChannel2Counts((channel2Counts) => [...channel2Counts, newValue2]); - setChannelTotals((channelTotals) => [...channelTotals, newValue3]); + setChannel1Counts(channel1Counts => [...channel1Counts, newValue1]); + setChannel2Counts(channel2Counts => [...channel2Counts, newValue2]); + setChannelTotals(channelTotals => [...channelTotals, newValue3]); }; /** @@ -335,7 +335,7 @@ const EcoCounterContent = ({ classes, intl, station }) => { * @param {object} el * @returns {*Array} */ - const getUserTypedata = (el) => { + const getUserTypedata = el => { switch (currentType) { case 'walking': return [el.value_jk, el.value_jp, el.value_jt]; @@ -354,11 +354,11 @@ const EcoCounterContent = ({ classes, intl, station }) => { * @param {function} labelFormatter */ const processData = (data, labelFormatter) => { - data.forEach((el) => { + data.forEach(el => { if (el.station === stationId) { const countsArr = getUserTypedata(el); setAllChannelCounts(countsArr[0], countsArr[1], countsArr[2]); - setEcoCounterLabels((lamCounterLabels) => [...lamCounterLabels, labelFormatter(el)]); + setEcoCounterLabels(lamCounterLabels => [...lamCounterLabels, labelFormatter(el)]); } }); }; @@ -393,13 +393,13 @@ const EcoCounterContent = ({ classes, intl, station }) => { if (currentTime === 'hour') { processHourData(); } else if (currentTime === 'day') { - processData(ecoCounterDay, (el) => formatDates(el.day_info.date)); + processData(ecoCounterDay, el => formatDates(el.day_info.date)); } else if (currentTime === 'week') { - processData(ecoCounterWeek, (el) => formatWeeks(el.week_info.week_number)); + processData(ecoCounterWeek, el => formatWeeks(el.week_info.week_number)); } else if (currentTime === 'month') { - processData(ecoCounterMonth, (el) => formatMonths(el.month_info.month_number, intl)); + processData(ecoCounterMonth, el => formatMonths(el.month_info.month_number, intl)); } else if (currentTime === 'year') { - processData(ecoCounterMultipleYears, (el) => el.year_info.year_number); + processData(ecoCounterMultipleYears, el => el.year_info.year_number); } }; @@ -490,7 +490,7 @@ const EcoCounterContent = ({ classes, intl, station }) => { * @param {*string} input * @returns {*string} */ - const renderStationName = (input) => { + const renderStationName = input => { if (input === 'Teatteri ranta') { return 'Teatteriranta'; } @@ -529,7 +529,7 @@ const EcoCounterContent = ({ classes, intl, station }) => {
changeDate(newDate)} + onChange={newDate => changeDate(newDate)} locale={locale} dateFormat="P" showYearDropdown={stationSource !== 'TR'} @@ -569,7 +569,7 @@ const EcoCounterContent = ({ classes, intl, station }) => {
{buttonSteps - .filter((item) => item.step.visible) + .filter(item => item.step.visible) .map((timing, i) => ( { const [activeStep, setActiveStep] = useState(0); const [selectedDate, setSelectedDate] = useState(startOfMonth(subMonths(new Date(), 1))); - const locale = useSelector((state) => state.user.locale); + const locale = useSelector(state => state.user.locale); const inputRef = useRef(null); const useMobileStatus = () => useMediaQuery('(max-width:768px)'); const isNarrow = useMobileStatus(); - const stationId = station.id; - const stationName = station.name; - const stationSource = station.csv_data_source; - const userTypes = station.sensor_types; - const dataFrom = station.data_from_date; - const dataUntil = station.data_until_date; + const stationId = station?.id; + const stationName = station?.name; + const stationSource = station?.csv_data_source; + const userTypes = station?.sensor_types; + const dataFrom = station?.data_from_date; + const dataUntil = station?.data_until_date; // steps that determine which data is shown on the chart const buttonSteps = [ @@ -98,7 +98,7 @@ const LamCounterContent = ({ classes, intl, station }) => { }, ]; - const renderUserTypeText = (userType) => { + const renderUserTypeText = userType => { if (userType === 'at') { return (
@@ -111,7 +111,7 @@ const LamCounterContent = ({ classes, intl, station }) => { return null; }; - const renderUserTypeIcon = (userType) => { + const renderUserTypeIcon = userType => { if (userType === 'at') { return (
@@ -122,7 +122,7 @@ const LamCounterContent = ({ classes, intl, station }) => { return null; }; - const changeDate = (newDate) => { + const changeDate = newDate => { setSelectedDate(newDate); }; @@ -141,7 +141,7 @@ const LamCounterContent = ({ classes, intl, station }) => { * @param {*date} dateValue * @returns {*number} */ - const checkWeekNumber = (dateValue) => { + const checkWeekNumber = dateValue => { const start = getWeek(startOfMonth(dateValue)); const end = getWeek(endOfMonth(dateValue)); if (start > end) { @@ -218,7 +218,7 @@ const LamCounterContent = ({ classes, intl, station }) => { * @param {date} weekValue * @returns {*string} */ - const formatWeeks = (weekValue) => { + const formatWeeks = weekValue => { const startOfSelectedWeek = startOfWeek(new Date(selectedYear, 0, 1), { weekStartsOn: 1 }); const targetWeekStartDate = addWeeks(startOfSelectedWeek, weekValue - 1); return format(targetWeekStartDate, 'dd.MM', { weekStartsOn: 1 }); @@ -234,9 +234,9 @@ const LamCounterContent = ({ classes, intl, station }) => { // Channel data is set inside this function to avoid duplicate code const setAllChannelCounts = (newValue1, newValue2, newValue3) => { - setChannel1Counts((channel1Counts) => [...channel1Counts, newValue1]); - setChannel2Counts((channel2Counts) => [...channel2Counts, newValue2]); - setChannelTotals((channelTotals) => [...channelTotals, newValue3]); + setChannel1Counts(channel1Counts => [...channel1Counts, newValue1]); + setChannel2Counts(channel2Counts => [...channel2Counts, newValue2]); + setChannelTotals(channelTotals => [...channelTotals, newValue3]); }; /** @@ -245,11 +245,11 @@ const LamCounterContent = ({ classes, intl, station }) => { * @param {function} labelFormatter */ const processData = (data, labelFormatter) => { - data.forEach((el) => { + data.forEach(el => { if (el.station === stationId) { const countsArr = [el.value_ak, el.value_ap, el.value_at]; setAllChannelCounts(countsArr[0], countsArr[1], countsArr[2]); - setLamCounterLabels((lamCounterLabels) => [...lamCounterLabels, labelFormatter(el)]); + setLamCounterLabels(lamCounterLabels => [...lamCounterLabels, labelFormatter(el)]); } }); }; @@ -274,13 +274,13 @@ const LamCounterContent = ({ classes, intl, station }) => { if (currentTime === 'hour') { processHourData(); } else if (currentTime === 'day') { - processData(lamCounterDay, (el) => formatDates(el.day_info.date)); + processData(lamCounterDay, el => formatDates(el.day_info.date)); } else if (currentTime === 'week') { - processData(lamCounterWeek, (el) => formatWeeks(el.week_info.week_number)); + processData(lamCounterWeek, el => formatWeeks(el.week_info.week_number)); } else if (currentTime === 'month') { - processData(lamCounterMonth, (el) => formatMonths(el.month_info.month_number, intl)); + processData(lamCounterMonth, el => formatMonths(el.month_info.month_number, intl)); } else if (currentTime === 'year') { - processData(lamCounterMultipleYears, (el) => el.year_info.year_number); + processData(lamCounterMultipleYears, el => el.year_info.year_number); } }; @@ -378,7 +378,7 @@ const LamCounterContent = ({ classes, intl, station }) => { * @param {string} name for example vt1_Kupittaa * @returns {string} for example Kupittaa */ - const formatCounterName = (name) => name?.split('_').splice(1).join(' '); + const formatCounterName = name => name?.split('_').splice(1).join(' '); return ( <> @@ -389,7 +389,7 @@ const LamCounterContent = ({ classes, intl, station }) => {
changeDate(newDate)} + onChange={newDate => changeDate(newDate)} locale={locale} dateFormat="P" showYearDropdown @@ -402,7 +402,7 @@ const LamCounterContent = ({ classes, intl, station }) => {
- {userTypes?.map((userType) => ( + {userTypes?.map(userType => (
{renderUserTypeIcon(userType)} {renderUserTypeText(userType)} From f20af59bb381e7b21a52f01941de7594dbefbcb7 Mon Sep 17 00:00:00 2001 From: juhomakkonen Date: Fri, 8 Mar 2024 15:25:37 +0200 Subject: [PATCH 04/15] Open element if roadworks have been selected --- src/views/MobilitySettingsView/MobilitySettingsView.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/views/MobilitySettingsView/MobilitySettingsView.js b/src/views/MobilitySettingsView/MobilitySettingsView.js index 365525a3e..581a845cb 100644 --- a/src/views/MobilitySettingsView/MobilitySettingsView.js +++ b/src/views/MobilitySettingsView/MobilitySettingsView.js @@ -473,6 +473,10 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { checkVisibilityValues(showAirMonitoringStations, setOpenAirMonitoringSettings); }, [showAirMonitoringStations]); + useEffect(() => { + checkVisibilityValues(showRoadworks, setOpenRoadworkSettings); + }, [showRoadworks]); + const nameKeys = { fi: 'name', en: 'name_en', From 26d47bb769abc8470ad75cd8d2ced3a87a1d2a85 Mon Sep 17 00:00:00 2001 From: juhomakkonen Date: Mon, 18 Mar 2024 11:55:16 +0200 Subject: [PATCH 05/15] Update selectedDate value --- .../TrafficCounters/LamCounterContent/LamCounterContent.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/EcoCounter/TrafficCounters/LamCounterContent/LamCounterContent.js b/src/components/EcoCounter/TrafficCounters/LamCounterContent/LamCounterContent.js index b67f6dfbc..3c82aa541 100644 --- a/src/components/EcoCounter/TrafficCounters/LamCounterContent/LamCounterContent.js +++ b/src/components/EcoCounter/TrafficCounters/LamCounterContent/LamCounterContent.js @@ -49,7 +49,6 @@ const LamCounterContent = ({ classes, intl, station }) => { const [lamCounterLabels, setLamCounterLabels] = useState([]); const [currentTime, setCurrentTime] = useState('hour'); const [activeStep, setActiveStep] = useState(0); - const [selectedDate, setSelectedDate] = useState(startOfMonth(subMonths(new Date(), 1))); const locale = useSelector(state => state.user.locale); const inputRef = useRef(null); @@ -64,6 +63,8 @@ const LamCounterContent = ({ classes, intl, station }) => { const dataFrom = station?.data_from_date; const dataUntil = station?.data_until_date; + const [selectedDate, setSelectedDate] = useState(new Date(dataUntil)); + // steps that determine which data is shown on the chart const buttonSteps = [ { @@ -172,7 +173,7 @@ const LamCounterContent = ({ classes, intl, station }) => { // Reset selectedDate value when the new popup is opened. useEffect(() => { - setSelectedDate(startOfMonth(subMonths(currentDate, 1))); + setSelectedDate(new Date(dataUntil)); }, [stationId]); // This will show full year if available From 5de2ed129ccff2995d357184a1936d605089d4e4 Mon Sep 17 00:00:00 2001 From: juhomakkonen Date: Mon, 18 Mar 2024 13:10:17 +0200 Subject: [PATCH 06/15] Subtract 1 day from date until to show the last full day of counts --- .../TrafficCounters/LamCounterContent/LamCounterContent.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/EcoCounter/TrafficCounters/LamCounterContent/LamCounterContent.js b/src/components/EcoCounter/TrafficCounters/LamCounterContent/LamCounterContent.js index 3c82aa541..a852004d4 100644 --- a/src/components/EcoCounter/TrafficCounters/LamCounterContent/LamCounterContent.js +++ b/src/components/EcoCounter/TrafficCounters/LamCounterContent/LamCounterContent.js @@ -18,6 +18,7 @@ import { endOfWeek, subMonths, addWeeks, + subDays, } from 'date-fns'; import { enGB, fi, sv } from 'date-fns/locale'; import { ReactSVG } from 'react-svg'; @@ -63,7 +64,7 @@ const LamCounterContent = ({ classes, intl, station }) => { const dataFrom = station?.data_from_date; const dataUntil = station?.data_until_date; - const [selectedDate, setSelectedDate] = useState(new Date(dataUntil)); + const [selectedDate, setSelectedDate] = useState(subDays(new Date(dataUntil), 1)); // steps that determine which data is shown on the chart const buttonSteps = [ @@ -173,7 +174,7 @@ const LamCounterContent = ({ classes, intl, station }) => { // Reset selectedDate value when the new popup is opened. useEffect(() => { - setSelectedDate(new Date(dataUntil)); + setSelectedDate(subDays(new Date(dataUntil), 1)); }, [stationId]); // This will show full year if available From cd5eabe0e57f8d838c386a6b8c00a50513eea678 Mon Sep 17 00:00:00 2001 From: juhomakkonen Date: Mon, 18 Mar 2024 15:41:18 +0200 Subject: [PATCH 07/15] Update initial date values --- .../LamCounterContent/LamCounterContent.js | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/components/EcoCounter/TrafficCounters/LamCounterContent/LamCounterContent.js b/src/components/EcoCounter/TrafficCounters/LamCounterContent/LamCounterContent.js index a852004d4..f07f0412b 100644 --- a/src/components/EcoCounter/TrafficCounters/LamCounterContent/LamCounterContent.js +++ b/src/components/EcoCounter/TrafficCounters/LamCounterContent/LamCounterContent.js @@ -16,7 +16,6 @@ import { getYear, startOfWeek, endOfWeek, - subMonths, addWeeks, subDays, } from 'date-fns'; @@ -153,15 +152,14 @@ const LamCounterContent = ({ classes, intl, station }) => { }; // Initial values that are used to fetch data - const currentDate = new Date(); - const lastMonth = subMonths(currentDate, 1); - const lastMonthFormat = format(lastMonth, 'yyyy-MM-dd'); - const initialDateStart = format(startOfWeek(lastMonth), 'yyyy-MM-dd'); - const initialDateEnd = format(endOfWeek(lastMonth), 'yyyy-MM-dd'); - const initialWeekStart = checkWeekNumber(lastMonth); - const initialWeekEnd = getWeek(endOfMonth(lastMonth)); - const initialMonth = getMonth(lastMonth); - const initialYear = getYear(lastMonth); + const initialDate = new Date(dataUntil); + const initialDateFormat = format(initialDate, 'yyyy-MM-dd'); + const initialDateStart = format(startOfWeek(initialDate), 'yyyy-MM-dd'); + const initialDateEnd = format(endOfWeek(initialDate), 'yyyy-MM-dd'); + const initialWeekStart = checkWeekNumber(initialDate); + const initialWeekEnd = getWeek(endOfMonth(initialDate)); + const initialMonth = getMonth(initialDate); + const initialYear = getYear(initialDate); // Values that change based on the datepicker value const selectedDateFormat = format(selectedDate, 'yyyy-MM-dd'); @@ -169,7 +167,7 @@ const LamCounterContent = ({ classes, intl, station }) => { const selectedDateEnd = format(endOfWeek(selectedDate, 1), 'yyyy-MM-dd'); const selectedWeekStart = checkWeekNumber(selectedDate); const selectedWeekEnd = getWeek(endOfMonth(selectedDate)); - let selectedMonth = getMonth(currentDate); + let selectedMonth = getMonth(initialDate); const selectedYear = getYear(selectedDate); // Reset selectedDate value when the new popup is opened. @@ -179,7 +177,7 @@ const LamCounterContent = ({ classes, intl, station }) => { // This will show full year if available const checkYear = () => { - if (getYear(selectedDate) < getYear(currentDate)) { + if (getYear(selectedDate) < getYear(initialDate)) { selectedMonth = 12; } }; @@ -318,7 +316,7 @@ const LamCounterContent = ({ classes, intl, station }) => { // Fetch initial data based on the default date useEffect(() => { setLamCounterLabels(labelsHour); - fetchInitialHourData(lastMonthFormat, stationId, setLamCounterHour); + fetchInitialHourData(initialDateFormat, stationId, setLamCounterHour); }, [stationId]); useEffect(() => { From 1842c204b9152ccec183e66b6acae1d5b2c10384 Mon Sep 17 00:00:00 2001 From: juhomakkonen Date: Tue, 19 Mar 2024 12:06:05 +0200 Subject: [PATCH 08/15] Update initial date & date-picker maxDate values --- .../EcoCounterContent/EcoCounterContent.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/components/EcoCounter/TrafficCounters/EcoCounterContent/EcoCounterContent.js b/src/components/EcoCounter/TrafficCounters/EcoCounterContent/EcoCounterContent.js index b2e0f547c..bd85ce4c1 100644 --- a/src/components/EcoCounter/TrafficCounters/EcoCounterContent/EcoCounterContent.js +++ b/src/components/EcoCounter/TrafficCounters/EcoCounterContent/EcoCounterContent.js @@ -52,7 +52,6 @@ const EcoCounterContent = ({ classes, intl, station }) => { const [currentType, setCurrentType] = useState('bicycle'); const [currentTime, setCurrentTime] = useState('hour'); const [activeStep, setActiveStep] = useState(0); - const [selectedDate, setSelectedDate] = useState(subDays(new Date(), 1)); const locale = useSelector(state => state.user.locale); const inputRef = useRef(null); @@ -67,6 +66,8 @@ const EcoCounterContent = ({ classes, intl, station }) => { const dataUntil = station?.data_until_date; const isActiveStation = station?.is_active?.['30']; + const [selectedDate, setSelectedDate] = useState(subDays(new Date(dataUntil), 1)); + /** When all 3 user types are rendered, a reverse order is required where 'at' is placed last */ const reverseUserTypes = () => { if (station.sensor_types.includes('at')) { @@ -235,8 +236,8 @@ const EcoCounterContent = ({ classes, intl, station }) => { }; // Initial values that are used to fetch data - const currentDate = new Date(); - const yesterDay = subDays(currentDate, 1); + const initialDate = new Date(dataUntil); + const yesterDay = subDays(initialDate, 1); const yesterDayFormat = format(yesterDay, 'yyyy-MM-dd'); const initialDateStart = format(startOfWeek(yesterDay, 1), 'yyyy-MM-dd'); const initialDateEnd = format(endOfWeek(yesterDay, 1), 'yyyy-MM-dd'); @@ -251,19 +252,19 @@ const EcoCounterContent = ({ classes, intl, station }) => { const selectedDateEnd = format(endOfWeek(selectedDate, 1), 'yyyy-MM-dd'); const selectedWeekStart = checkWeekNumber(selectedDate); const selectedWeekEnd = getWeek(endOfMonth(selectedDate)); - let selectedMonth = getMonth(currentDate); + let selectedMonth = getMonth(initialDate); const selectedYear = getYear(selectedDate); // This will show full year if available const checkYear = () => { - if (getYear(selectedDate) < getYear(currentDate)) { + if (getYear(selectedDate) < getYear(initialDate)) { selectedMonth = 12; } }; // Reset selectedDate value when the new popup is opened. useEffect(() => { - setSelectedDate(subDays(currentDate, 1)); + setSelectedDate(subDays(new Date(dataUntil), 1)); }, [stationId]); useEffect(() => { @@ -535,7 +536,7 @@ const EcoCounterContent = ({ classes, intl, station }) => { showYearDropdown={stationSource !== 'TR'} dropdownMode="select" minDate={new Date(dataFrom)} - maxDate={stationSource === 'TR' ? new Date(dataUntil) : new Date()} + maxDate={new Date(dataUntil)} customInput={} />
From 1caea2b906573c00507e4415563e6de22f477999 Mon Sep 17 00:00:00 2001 From: juhomakkonen <69158965+juhomakkonen@users.noreply.github.com> Date: Wed, 20 Mar 2024 12:22:30 +0200 Subject: [PATCH 09/15] Feature/counters render period text (#229) * Render text that shows from & until dates of counter data * Render period text for remaining counters * Add new tests * Update English & Swedish translations --- .../CounterActiveText/CounterActiveText.js | 46 +++++++++++++++++++ .../__tests__/CounterActiveText.test.js | 26 +++++++++++ .../CounterActiveText.test.js.snap | 15 ++++++ .../CounterActiveText/index.js | 3 ++ .../EcoCounterContent/EcoCounterContent.js | 29 ++---------- .../LamCounterContent/LamCounterContent.js | 2 + src/i18n/en.js | 2 +- src/i18n/fi.js | 2 +- src/i18n/sv.js | 2 +- 9 files changed, 98 insertions(+), 29 deletions(-) create mode 100644 src/components/EcoCounter/TrafficCounters/CounterActiveText/CounterActiveText.js create mode 100644 src/components/EcoCounter/TrafficCounters/CounterActiveText/__tests__/CounterActiveText.test.js create mode 100644 src/components/EcoCounter/TrafficCounters/CounterActiveText/__tests__/__snapshots__/CounterActiveText.test.js.snap create mode 100644 src/components/EcoCounter/TrafficCounters/CounterActiveText/index.js diff --git a/src/components/EcoCounter/TrafficCounters/CounterActiveText/CounterActiveText.js b/src/components/EcoCounter/TrafficCounters/CounterActiveText/CounterActiveText.js new file mode 100644 index 000000000..098a7d0d6 --- /dev/null +++ b/src/components/EcoCounter/TrafficCounters/CounterActiveText/CounterActiveText.js @@ -0,0 +1,46 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { useIntl } from 'react-intl'; +import { Typography } from '@mui/material'; +import styled from '@emotion/styled'; +import { formatFullDates } from '../../utils'; + +/** + * Render text that shows from and until dates. + * @prop {string} dataFrom + * @prop {string} dataUntil + * @returns JSX eement + */ +const CounterActiveText = ({ dataFrom, dataUntil }) => { + const intl = useIntl(); + const dataFromFormat = formatFullDates(dataFrom); + const dataUntilFormat = formatFullDates(dataUntil); + + return ( + + + {intl.formatMessage( + { id: 'ecocounter.station.counts.period' }, + { value1: dataFromFormat, value2: dataUntilFormat }, + )} + + + ); +}; + +const StyledTextContainer = styled.div(({ theme }) => ({ + textAlign: 'center', + margin: `${theme.spacing(1)} 0`, +})); + +CounterActiveText.propTypes = { + dataFrom: PropTypes.string, + dataUntil: PropTypes.string, +}; + +CounterActiveText.defaultProps = { + dataFrom: '', + dataUntil: '', +}; + +export default CounterActiveText; diff --git a/src/components/EcoCounter/TrafficCounters/CounterActiveText/__tests__/CounterActiveText.test.js b/src/components/EcoCounter/TrafficCounters/CounterActiveText/__tests__/CounterActiveText.test.js new file mode 100644 index 000000000..1cab8f670 --- /dev/null +++ b/src/components/EcoCounter/TrafficCounters/CounterActiveText/__tests__/CounterActiveText.test.js @@ -0,0 +1,26 @@ +/* eslint-disable react/jsx-props-no-spreading */ +// Link.react.test.js +import React from 'react'; +import CounterActiveText from '../index'; +import { getRenderWithProviders } from '../../../../../../jestUtils'; + +const mockProps = { + dataFrom: '2020-01-01', + dataUntil: '2023-10-10', +}; + +const renderWithProviders = getRenderWithProviders({}); + +describe('', () => { + it('should work', () => { + const { container } = renderWithProviders(); + expect(container).toMatchSnapshot(); + }); + + it('does show text correctly', () => { + const { container } = renderWithProviders(); + + const p = container.querySelectorAll('p'); + expect(p[0].textContent).toEqual('Laskentatiedot ovat väliltä 01.01.2020 - 10.10.2023'); + }); +}); diff --git a/src/components/EcoCounter/TrafficCounters/CounterActiveText/__tests__/__snapshots__/CounterActiveText.test.js.snap b/src/components/EcoCounter/TrafficCounters/CounterActiveText/__tests__/__snapshots__/CounterActiveText.test.js.snap new file mode 100644 index 000000000..cbcc286b9 --- /dev/null +++ b/src/components/EcoCounter/TrafficCounters/CounterActiveText/__tests__/__snapshots__/CounterActiveText.test.js.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should work 1`] = ` +
+
+

+ Laskentatiedot ovat väliltä 01.01.2020 - 10.10.2023 +

+
+
+`; diff --git a/src/components/EcoCounter/TrafficCounters/CounterActiveText/index.js b/src/components/EcoCounter/TrafficCounters/CounterActiveText/index.js new file mode 100644 index 000000000..dc3de64a2 --- /dev/null +++ b/src/components/EcoCounter/TrafficCounters/CounterActiveText/index.js @@ -0,0 +1,3 @@ +import CounterActiveText from './CounterActiveText'; + +export default CounterActiveText; diff --git a/src/components/EcoCounter/TrafficCounters/EcoCounterContent/EcoCounterContent.js b/src/components/EcoCounter/TrafficCounters/EcoCounterContent/EcoCounterContent.js index bd85ce4c1..62551e386 100644 --- a/src/components/EcoCounter/TrafficCounters/EcoCounterContent/EcoCounterContent.js +++ b/src/components/EcoCounter/TrafficCounters/EcoCounterContent/EcoCounterContent.js @@ -33,9 +33,10 @@ import { fetchInitialWeekDatas, fetchSelectedYearData, } from '../../EcoCounterRequests/ecoCounterRequests'; -import { formatDates, formatFullDates, formatMonths } from '../../utils'; +import { formatDates, formatMonths } from '../../utils'; import LineChart from '../../LineChart'; import InputDate from '../../InputDate'; +import CounterActiveText from '../CounterActiveText'; const CustomInput = forwardRef((props, ref) => ); @@ -64,7 +65,6 @@ const EcoCounterContent = ({ classes, intl, station }) => { const stationSource = station?.csv_data_source; const dataFrom = station?.data_from_date; const dataUntil = station?.data_until_date; - const isActiveStation = station?.is_active?.['30']; const [selectedDate, setSelectedDate] = useState(subDays(new Date(dataUntil), 1)); @@ -498,29 +498,6 @@ const EcoCounterContent = ({ classes, intl, station }) => { return input; }; - /** - * Render text on 2 Telraam stations that are currently inactive and do not collect new data. - * @returns JSX element - */ - const renderOldStationText = () => { - const dataFromFormat = formatDates(dataFrom); - const dataUntilFormat = formatFullDates(dataUntil); - - if (!isActiveStation) { - return ( -
- - {intl.formatMessage( - { id: 'ecocounter.station.active.period' }, - { value1: dataFromFormat, value2: dataUntilFormat }, - )} - -
- ); - } - return null; - }; - return ( <>
@@ -550,7 +527,7 @@ const EcoCounterContent = ({ classes, intl, station }) => {
))}
- {stationSource === 'TR' ? renderOldStationText() : null} +
); @@ -416,6 +417,7 @@ const LamCounterContent = ({ classes, intl, station }) => {
) : null} +
Date: Tue, 26 Mar 2024 15:04:27 +0200 Subject: [PATCH 10/15] Update app description for open graph --- src/App.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/App.js b/src/App.js index ccd892c7d..10bc0f633 100755 --- a/src/App.js +++ b/src/App.js @@ -38,11 +38,14 @@ import LocaleUtility from './utils/locale'; // General meta tags for app const MetaTags = () => { const intl = useIntl(); + // If external theme (by Turku) is true, then can be used to select which app description to render. + const externalTheme = config.themePKG; + const isExternalTheme = !externalTheme || externalTheme === 'undefined' ? null : externalTheme; return ( {isClient() && } - + { +const mapStateToProps = state => { const locale = getLocale(state); return { locale, From 680cffdf188077da3af285c13e8ce3e40f3d385a Mon Sep 17 00:00:00 2001 From: juhomakkonen Date: Wed, 3 Apr 2024 10:10:22 +0300 Subject: [PATCH 11/15] Update app description for home page --- src/layouts/components/PageHandler/PageHandler.js | 7 ++++++- src/layouts/components/PageHandler/pageDescriptions.js | 7 +++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/layouts/components/PageHandler/PageHandler.js b/src/layouts/components/PageHandler/PageHandler.js index e88a0b73a..55e9c3f73 100644 --- a/src/layouts/components/PageHandler/PageHandler.js +++ b/src/layouts/components/PageHandler/PageHandler.js @@ -5,6 +5,7 @@ import { uppercaseFirst } from '../../../utils'; import useLocaleText from '../../../utils/useLocaleText'; import getPageDescriptions from './pageDescriptions'; import { isEmbed } from '../../../utils/path'; +import config from '../../../../config'; const PageHandler = (props) => { const { @@ -14,6 +15,10 @@ const PageHandler = (props) => { const getLocaleText = useLocaleText(); const embed = isEmbed(); + // If external theme (by Turku) is true, then can be used to select which app description to render. + const externalTheme = config.themePKG; + const isExternalTheme = !externalTheme || externalTheme === 'undefined' ? null : externalTheme; + useEffect(() => { // Save current page to redux setCurrentPage(page); @@ -22,7 +27,7 @@ const PageHandler = (props) => { // Modify html head const message = messageId ? intl.formatMessage({ id: messageId }) : ''; let pageMessage = ''; - const pageDescription = getPageDescriptions(page, unit, address, getLocaleText, intl); + const pageDescription = getPageDescriptions(page, unit, address, getLocaleText, intl, isExternalTheme); // Add unit or service name to title if needed if ((page === 'unit' || page === 'eventList') && unit && unit.name) { diff --git a/src/layouts/components/PageHandler/pageDescriptions.js b/src/layouts/components/PageHandler/pageDescriptions.js index 2a93b1f91..32b6e97bd 100644 --- a/src/layouts/components/PageHandler/pageDescriptions.js +++ b/src/layouts/components/PageHandler/pageDescriptions.js @@ -1,8 +1,8 @@ /* eslint-disable camelcase */ -const getPageDescriptions = (page, unit, address, getLocaleText, intl) => { +const getPageDescriptions = (page, unit, address, getLocaleText, intl, isExternalTheme) => { switch (page) { case 'home': { - return intl.formatMessage({ id: 'app.description' }); + return intl.formatMessage({ id: isExternalTheme ? 'app.description.tku' : 'app.description' }); } case 'area': { @@ -58,9 +58,8 @@ const getPageDescriptions = (page, unit, address, getLocaleText, intl) => { } default: - return intl.formatMessage({ id: 'app.description' }); + return intl.formatMessage({ id: isExternalTheme ? 'app.description.tku' : 'app.description' }); } }; - export default getPageDescriptions; From 546c8101dcd08b2127182bbf35fb0e9fd1b9e806 Mon Sep 17 00:00:00 2001 From: juhomakkonen <69158965+juhomakkonen@users.noreply.github.com> Date: Mon, 8 Apr 2024 08:48:37 +0300 Subject: [PATCH 12/15] Feature/park and ride stops (#231) * Show park and ride stops for bicycles * Show info about park and ride stops * Update TextComponent & snapshots * Use correct icons * Render info text for park & ride stops --- .../BikeServiceStationContent.test.js.snap | 6 +- .../ChargerStationContent.test.js.snap | 6 +- .../LoadingPlacesContent.test.js.snap | 10 +-- .../OutdoorGymDevicesContent.test.js.snap | 6 +- .../ParkAndRideBikes/ParkAndRideBikes.js | 57 ++++++++++++ .../ParkAndRideBikesContent.js | 90 +++++++++++++++++++ .../__tests__/ParkAndRideBikesContent.test.js | 40 +++++++++ .../ParkAndRideBikesContent.test.js.snap | 49 ++++++++++ .../ParkAndRideBikesContent/index.js | 3 + .../ParkAndRideBikes/index.js | 3 + .../PublicParkingContent.test.js.snap | 8 +- .../RentalCarParkingContent.test.js.snap | 6 +- .../ParkingMachinesContent.test.js.snap | 6 +- .../TextComponent/TextComponent.js | 17 ++-- .../__snapshots__/TextComponent.test.js.snap | 2 +- .../MobilityPlatform/TextComponent/index.js | 5 +- .../MobilityPlatform/TextComponent/styles.js | 5 -- src/context/MobilityPlatformContext.js | 3 + src/i18n/en.js | 3 + src/i18n/fi.js | 3 + src/i18n/sv.js | 5 +- .../MobilityPlatformMapView.js | 2 + .../MobilitySettingsView.js | 20 ++++- 23 files changed, 313 insertions(+), 42 deletions(-) create mode 100644 src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/ParkAndRideBikes.js create mode 100644 src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/ParkAndRideBikesContent.js create mode 100644 src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/__tests__/ParkAndRideBikesContent.test.js create mode 100644 src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/__tests__/__snapshots__/ParkAndRideBikesContent.test.js.snap create mode 100644 src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/index.js create mode 100644 src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/index.js delete mode 100644 src/components/MobilityPlatform/TextComponent/styles.js diff --git a/src/components/MobilityPlatform/BikeServiceStations/components/BikeServiceStationContent/__tests__/__snapshots__/BikeServiceStationContent.test.js.snap b/src/components/MobilityPlatform/BikeServiceStations/components/BikeServiceStationContent/__tests__/__snapshots__/BikeServiceStationContent.test.js.snap index b827a86e9..a3a279cc9 100644 --- a/src/components/MobilityPlatform/BikeServiceStations/components/BikeServiceStationContent/__tests__/__snapshots__/BikeServiceStationContent.test.js.snap +++ b/src/components/MobilityPlatform/BikeServiceStations/components/BikeServiceStationContent/__tests__/__snapshots__/BikeServiceStationContent.test.js.snap @@ -9,7 +9,7 @@ exports[` should match snapshot 1`] = ` class="BikeServiceStationContent-headerContainer-2" >

should match snapshot 1`] = ` class="BikeServiceStationContent-textContainer-3" >

should match snapshot 1`] = `

should match snapshot 1`] = ` class="injectIntl(ChargerStationContent)-headerContainer-2" >

should match snapshot 1`] = ` class="injectIntl(ChargerStationContent)-textContainer-3" >

should match snapshot 1`] = `

should match snapshot 1`] = ` class="LoadingPlacesContent-headerContainer-2" >

should match snapshot 1`] = ` class="LoadingPlacesContent-textContainer-3" >

should match snapshot 1`] = `

should match snapshot 1`] = `

should match snapshot 1`] = `

should match snapshot 1`] = ` class="OutdoorGymDevicesContent-headerContainer-2" >

should match snapshot 1`] = ` class="OutdoorGymDevicesContent-textContainer-3" >

should match snapshot 1`] = `

{ + const [parkAndRideBikesData, setParkAndRideBikesData] = useState([]); + + const { showParkAndRideBikes } = useMobilityPlatformContext(); + + const { icon } = global.L; + + const useContrast = useSelector(useAccessibleMap); + + const customIcon = icon(createIcon(useContrast ? parkAndRideIconBw : parkAndRideIcon)); + + useEffect(() => { + const options = { + type_name: 'FoliParkAndRideBikesStop', + page_size: 100, + }; + if (showParkAndRideBikes) { + fetchMobilityMapData(options, setParkAndRideBikesData); + } + }, [showParkAndRideBikes]); + + const map = useMap(); + + const renderData = isDataValid(showParkAndRideBikes, parkAndRideBikesData); + + useEffect(() => { + fitToMapBounds(renderData, parkAndRideBikesData, map); + }, [showParkAndRideBikes, parkAndRideBikesData]); + + return ( + renderData + ? parkAndRideBikesData.map(item => ( + + + + )) + : null + ); +}; + +export default ParkAndRideBikes; diff --git a/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/ParkAndRideBikesContent.js b/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/ParkAndRideBikesContent.js new file mode 100644 index 000000000..1f8c0e682 --- /dev/null +++ b/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/ParkAndRideBikesContent.js @@ -0,0 +1,90 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import styled from '@emotion/styled'; +import { Typography } from '@mui/material'; +import { useIntl } from 'react-intl'; +import TextComponent from '../../../../TextComponent'; + +const ParkAndRideBikesContent = ({ item }) => { + const intl = useIntl(); + const itemName = { + fi: item.name_fi, + en: item.name_en, + sv: item.name_sv, + }; + + /** + * Take string (like kaarina) and return the same string with first character in uppercase. + * @param {*string} str + * @returns string + */ + const toSentenceCase = str => { + if (str) { + return str.charAt(0).toUpperCase() + str.slice(1); + } + return ''; + }; + + const itemAddress = { + fi: `${item.address_fi}, ${item.address_zip} ${toSentenceCase(item.municipality)}`, + en: `${item.address_en}, ${item.address_zip} ${toSentenceCase(item.municipality)}`, + sv: `${item.address_sv}, ${item.address_zip} ${toSentenceCase(item.municipality)}`, + }; + + const parkAndRideInfo = ( + + + + + + + + {intl.formatMessage({ id: 'mobilityPlatform.parkAndRide.content.subtitle' })} + + + {item.address_fi !== '' ? ( + + ) : null} + + + ); + + return {parkAndRideInfo}; +}; + +const StyledContainer = styled.div(({ theme }) => ({ + margin: theme.spacing(1), +})); + +const StyledHeaderContainer = styled.div(({ theme }) => ({ + width: '85%', + borderBottom: '1px solid #000', + paddingBottom: theme.spacing(0.5), +})); + +const StyledTextContainer = styled.div(({ theme }) => ({ + marginTop: theme.spacing(0.5), +})); + +const StyledMargin = styled.div(({ theme }) => ({ + margin: theme.spacing(0.4), +})); + +ParkAndRideBikesContent.propTypes = { + item: PropTypes.shape({ + name_fi: PropTypes.string, + name_en: PropTypes.string, + name_sv: PropTypes.string, + address_fi: PropTypes.string, + address_en: PropTypes.string, + address_sv: PropTypes.string, + address_zip: PropTypes.string, + municipality: PropTypes.string, + }), +}; + +ParkAndRideBikesContent.defaultProps = { + item: {}, +}; + +export default ParkAndRideBikesContent; diff --git a/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/__tests__/ParkAndRideBikesContent.test.js b/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/__tests__/ParkAndRideBikesContent.test.js new file mode 100644 index 000000000..39c1ec092 --- /dev/null +++ b/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/__tests__/ParkAndRideBikesContent.test.js @@ -0,0 +1,40 @@ +/* eslint-disable react/jsx-props-no-spreading */ +// Link.react.test.js +import React from 'react'; +import { getRenderWithProviders } from '../../../../../../../../jestUtils'; +import { initialState } from '../../../../../../../redux/reducers/user'; +import ParkAndRideBikesContent from '../index'; +import finnishTranslations from '../../../../../../../i18n/fi'; + +const mockProps = { + item: { + name_fi: 'Testinimi', + name_en: 'Test name', + name_sv: 'Test namn', + address_fi: 'Testiosoite', + address_en: 'Test address', + address_sv: 'Test adress', + address_zip: '20200', + municipality: 'turku', + }, +}; + +const renderWithProviders = getRenderWithProviders({ + user: initialState, +}); + +describe('', () => { + it('should work', () => { + const { container } = renderWithProviders(); + expect(container).toMatchSnapshot(); + }); + + it('does show text correctly', () => { + const { container } = renderWithProviders(); + + const p = container.querySelectorAll('p'); + expect(p[0].textContent).toEqual('Testinimi'); + expect(p[1].textContent).toContain(finnishTranslations['mobilityPlatform.parkAndRide.content.subtitle']); + expect(p[2].textContent).toEqual('Osoite: Testiosoite, 20200 Turku'); + }); +}); diff --git a/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/__tests__/__snapshots__/ParkAndRideBikesContent.test.js.snap b/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/__tests__/__snapshots__/ParkAndRideBikesContent.test.js.snap new file mode 100644 index 000000000..2cfb04334 --- /dev/null +++ b/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/__tests__/__snapshots__/ParkAndRideBikesContent.test.js.snap @@ -0,0 +1,49 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should work 1`] = ` +

+
+
+
+
+

+ Testinimi +

+
+
+
+
+

+ Liityntäpysäkki pyörille +

+
+
+

+ Osoite: Testiosoite, 20200 Turku +

+
+
+
+
+
+`; diff --git a/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/index.js b/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/index.js new file mode 100644 index 000000000..84b76227a --- /dev/null +++ b/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/components/ParkAndRideBikesContent/index.js @@ -0,0 +1,3 @@ +import ParkAndRideBikesContent from './ParkAndRideBikesContent'; + +export default ParkAndRideBikesContent; diff --git a/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/index.js b/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/index.js new file mode 100644 index 000000000..341af6ac0 --- /dev/null +++ b/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/index.js @@ -0,0 +1,3 @@ +import ParkAndRideBikes from './ParkAndRideBikes'; + +export default ParkAndRideBikes; diff --git a/src/components/MobilityPlatform/Parking/PublicParking/components/PublicParkingContent/__tests__/__snapshots__/PublicParkingContent.test.js.snap b/src/components/MobilityPlatform/Parking/PublicParking/components/PublicParkingContent/__tests__/__snapshots__/PublicParkingContent.test.js.snap index fd0a9b568..4577ed7d5 100644 --- a/src/components/MobilityPlatform/Parking/PublicParking/components/PublicParkingContent/__tests__/__snapshots__/PublicParkingContent.test.js.snap +++ b/src/components/MobilityPlatform/Parking/PublicParking/components/PublicParkingContent/__tests__/__snapshots__/PublicParkingContent.test.js.snap @@ -9,7 +9,7 @@ exports[` does match snapshot 1`] = ` class="injectIntl(PublicParkingContent)-headerContainer-2" >

does match snapshot 1`] = ` class="injectIntl(PublicParkingContent)-textContainer-3" >

does match snapshot 1`] = `

does match snapshot 1`] = `

does match snapshot 1`] = ` class="injectIntl(RentalCarParkingContent)-headerContainer-2" >

does match snapshot 1`] = ` class="injectIntl(RentalCarParkingContent)-textContainer-3" >

does match snapshot 1`] = `

should match snapshot 1`] = ` class="injectIntl(ParkingMachinesContent)-textContainer-3" >

should match snapshot 1`] = `

should match snapshot 1`] = `

{ + const intl = useIntl(); const getLocaleText = useLocaleText(); const wrapper = prop => (messageId ? intl.formatMessage({ id: messageId }, { value: prop }) : prop); return ( -

+ {wrapper(getLocaleText(textObj))} -
+ ); }; +const StyledContainer = styled.div(({ theme }) => ({ + margin: theme.spacing(0.4), +})); + TextComponent.propTypes = { - classes: PropTypes.objectOf(PropTypes.any).isRequired, - intl: PropTypes.objectOf(PropTypes.any).isRequired, - textObj: PropTypes.objectOf(PropTypes.any), + textObj: PropTypes.objectOf(PropTypes.string), isTitle: PropTypes.bool, messageId: PropTypes.string, }; diff --git a/src/components/MobilityPlatform/TextComponent/__tests__/__snapshots__/TextComponent.test.js.snap b/src/components/MobilityPlatform/TextComponent/__tests__/__snapshots__/TextComponent.test.js.snap index 4c278d2ee..b3e11906f 100644 --- a/src/components/MobilityPlatform/TextComponent/__tests__/__snapshots__/TextComponent.test.js.snap +++ b/src/components/MobilityPlatform/TextComponent/__tests__/__snapshots__/TextComponent.test.js.snap @@ -3,7 +3,7 @@ exports[` should match snapshot 1`] = `

({ - margin: { - margin: theme.spacing(0.4), - }, -}); diff --git a/src/context/MobilityPlatformContext.js b/src/context/MobilityPlatformContext.js index 72efe0894..aa9de8073 100644 --- a/src/context/MobilityPlatformContext.js +++ b/src/context/MobilityPlatformContext.js @@ -40,6 +40,7 @@ const MobilityPlatformContextProvider = ({ children }) => { const [showBikeServiceStations, setShowBikeServiceStations] = useState(false); const [showCityBikes, setShowCityBikes] = useState(false); const [showCargoBikes, setShowCargoBikes] = useState(false); + const [showParkAndRideBikes, setShowParkAndRideBikes] = useState(false); // culture routes const [showCultureRoutes, setShowCultureRoutes] = useState(false); @@ -117,6 +118,7 @@ const MobilityPlatformContextProvider = ({ children }) => { showBikeServiceStations, showCityBikes, showCargoBikes, + showParkAndRideBikes, // culture routes showCultureRoutes, cultureRouteId, @@ -186,6 +188,7 @@ const MobilityPlatformContextProvider = ({ children }) => { setShowBikeServiceStations, setShowCityBikes, setShowCargoBikes, + setShowParkAndRideBikes, // culture routes setShowCultureRoutes, setCultureRouteId, diff --git a/src/i18n/en.js b/src/i18n/en.js index c6ba1773a..4717311c5 100644 --- a/src/i18n/en.js +++ b/src/i18n/en.js @@ -801,6 +801,7 @@ const translations = { 'mobilityPlatform.menu.show.roadworks': 'Roadworks', 'mobilityPlatform.menu.show.railwayStations': 'Railway stations', 'mobilityPlatform.menu.show.airMonitoring': 'Air quality stations', + 'mobilityPlatform.menu.show.parkAndRideBikes': 'Park and ride stops for bicycles', // Content 'mobilityPlatform.content.general.provider': 'Service provider: {value}', @@ -906,6 +907,7 @@ const translations = { 'mobilityPlatform.content.arrivingTrains.title': 'Incoming trains', 'mobilityPlatform.content.departingTrains.empty': 'No departing trains', 'mobilityPlatform.content.arrivingTrains.empty': 'No incoming trains', + 'mobilityPlatform.parkAndRide.content.subtitle': 'Park and ride stop for bicycles', // Info text 'mobilityPlatform.info.description.title': 'Route description', @@ -965,6 +967,7 @@ const translations = { 'mobilityPlatform.info.airMonitoring.paragraph.2': 'The calculation takes into account sulfur dioxide (SO2), nitrogen dioxide (NO2), respirable particles (PM10), fine particles (PM2.5), and ozone (O3). When the air quality is poor or very poor, health effects may occur in sensitive individuals. Air quality is worsened by street dust, traffic emissions, small-scale wood combustion, energy production, and occasionally by long range transport.', 'mobilityPlatform.info.airMonitoring.paragraph.3': 'Air quality data for the service map is obtained from the Turku region air protection co-operative group.', 'mobilityPlatform.info.airMonitoring.link': 'For more information visit https://en.ilmatieteenlaitos.fi/air-quality', + 'mobilityPlatform.info.parkAndRideBicycles': 'Park-and-ride arrangements provide the opportunity to leave your bicycle parked safely and hop on a bus to continue your journey. The Föli area boasts many park-and-ride sites for bicycles. Park-and-ride parking is free and intended for those using public transport for connections.', // 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 6d6025855..83a5c7a15 100644 --- a/src/i18n/fi.js +++ b/src/i18n/fi.js @@ -806,6 +806,7 @@ const translations = { 'mobilityPlatform.menu.show.roadworks': 'Tietyömaat', 'mobilityPlatform.menu.show.railwayStations': 'Rautatieasemat', 'mobilityPlatform.menu.show.airMonitoring': 'Ilmanlaadun mittauspisteet', + 'mobilityPlatform.menu.show.parkAndRideBikes': 'Liityntäpysäkit pyörille', // Content 'mobilityPlatform.content.general.provider': 'Palveluntarjoaja: {value}', @@ -901,6 +902,7 @@ const translations = { 'mobilityPlatform.content.arrivingTrains.title': 'Saapuvat junat', 'mobilityPlatform.content.departingTrains.empty': 'Ei lähteviä junia', 'mobilityPlatform.content.arrivingTrains.empty': 'Ei saapuvia junia', + 'mobilityPlatform.parkAndRide.content.subtitle': 'Liityntäpysäkki pyörille', // Info text 'mobilityPlatform.info.description.title': 'Tietoja reitistä', @@ -960,6 +962,7 @@ const translations = { 'mobilityPlatform.info.airMonitoring.paragraph.2': 'Laskennassa otetaan huomioon rikkidioksidi (SO2), typpidioksidi (NO2), hengitettävät hiukkaset (PM10), pienhiukkaset (PM2,5) sekä otsoni (O3). Kun ilmanlaatu on huono tai erittäin huono, terveysvaikutukset ovat mahdollisia herkillä ihmisillä. Ilmanlaatua heikentävät katupöly, liikenteen päästöt, puun pienpoltto, energiantuotanto sekä ajoittaiset kaukokulkeumat.', 'mobilityPlatform.info.airMonitoring.paragraph.3': 'Palvelukartan ilmanlaatutiedot saadaan Turun seudun ilmansuojelun yhteistyöryhmältä.', 'mobilityPlatform.info.airMonitoring.link': 'Lisätietoja saa osoitteesta https://www.ilmatieteenlaitos.fi/ilmanlaatu', + 'mobilityPlatform.info.parkAndRideBicycles': 'Liityntäpysäköinti tarjoaa mahdollisuuden jättää oma pyörä parkkiin ja jatkaa matkaa bussilla. Föli-alueella on useita liityntäpysäköintipaikkoja pyörille. Liityntäpysäköinti on maksutonta ja tarkoitettu vain joukkoliikennettä vaihtoyhteytenä käyttäville.', // 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 b6bf77dda..eb942f088 100644 --- a/src/i18n/sv.js +++ b/src/i18n/sv.js @@ -802,9 +802,10 @@ 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.airMonitoring': 'Stationer för luftkvalitet', // TODO verify + 'mobilityPlatform.menu.show.airMonitoring': 'Stationer för luftkvalitet', 'mobilityPlatform.menu.show.roadworks': 'Vägarbeten', 'mobilityPlatform.menu.show.railwayStations': 'Järnvägsstationer', + 'mobilityPlatform.menu.show.parkAndRideBikes': 'Infartsparkering för cyklar', // Content 'mobilityPlatform.content.general.provider': 'Tjänsteleverantör: {value}', @@ -910,6 +911,7 @@ const translations = { 'mobilityPlatform.content.arrivingTrains.title': 'Inkommande tåg', 'mobilityPlatform.content.departingTrains.empty': 'Inga avgående tåg', 'mobilityPlatform.content.arrivingTrains.empty': 'Inga inkommande tåg', + 'mobilityPlatform.parkAndRide.content.subtitle': 'Infartspark for cyklarna', // Info text 'mobilityPlatform.info.description.title': 'Beskrivning av rutten', @@ -969,6 +971,7 @@ const translations = { 'mobilityPlatform.info.airMonitoring.paragraph.2': 'Vid beräkningen beaktas svaveldioxid (SO2), kvävedioxid (NO2), inandningsbara partiklar (PM10), småpartiklar (PM2,5) samt ozon (O3). När luftkvaliteten är dålig eller mycket dålig kan hälsopåverkan förekomma hos känsliga personer. Luftkvaliteten försämras av gatudamm, trafikutsläpp, småskalig vedeldning, energiproduktion samt av sporadiskt förekommande långväga transport.', 'mobilityPlatform.info.airMonitoring.paragraph.3': 'Luftkvalitetsdata för Servicekartan erhålls från Åboregionens samarbetsgrupp för luftskydd.', 'mobilityPlatform.info.airMonitoring.link': 'För mer information besök: https://sv.ilmatieteenlaitos.fi/luftkvalitet', + 'mobilityPlatform.info.parkAndRideBicycles': 'Infartsparkeringen erbjuder möjlighet att lämna din egen cykel på parkeringsplatsen och fortsätta resan med buss. Inom Föli-området finns flera infartsparkeringsplatser för cyklar. Infartsparkeringen är gratis och endast avsedd för dem som fortsätter sin resa med kollektivtrafik.', // 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 b2f66218b..918e8c499 100644 --- a/src/views/MobilityPlatformMapView/MobilityPlatformMapView.js +++ b/src/views/MobilityPlatformMapView/MobilityPlatformMapView.js @@ -37,6 +37,7 @@ import PublicBenches from '../../components/MobilityPlatform/PublicBenches'; import Roadworks from '../../components/MobilityPlatform/Roadworks'; import RailwayStations from '../../components/MobilityPlatform/RailwayStations'; import AirMonitoring from '../../components/MobilityPlatform/EnvironmentObservations/AirMonitoring'; +import ParkAndRideBikes from '../../components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes'; const MobilityPlatformMapView = ({ mapObject }) => ( <> @@ -77,6 +78,7 @@ const MobilityPlatformMapView = ({ mapObject }) => ( + ); diff --git a/src/views/MobilitySettingsView/MobilitySettingsView.js b/src/views/MobilitySettingsView/MobilitySettingsView.js index 581a845cb..aea79297d 100644 --- a/src/views/MobilitySettingsView/MobilitySettingsView.js +++ b/src/views/MobilitySettingsView/MobilitySettingsView.js @@ -172,6 +172,8 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { setShowRailwayStations, showAirMonitoringStations, setShowAirMonitoringStations, + showParkAndRideBikes, + setShowParkAndRideBikes, } = useMobilityPlatformContext(); const locale = useSelector(state => state.user.locale); @@ -377,7 +379,8 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { checkVisibilityValues(showBikeServiceStations, setOpenBicycleSettings); checkVisibilityValues(showCityBikes, setOpenBicycleSettings); checkVisibilityValues(showCargoBikes, setOpenBicycleSettings); - }, [showBicycleStands, showHullLockableStands, showBikeServiceStations, showCityBikes, showCargoBikes]); + checkVisibilityValues(showParkAndRideBikes, setOpenBicycleSettings); + }, [showBicycleStands, showHullLockableStands, showBikeServiceStations, showCityBikes, showCargoBikes, showParkAndRideBikes]); useEffect(() => { checkVisibilityValues(showBicycleRoutes, setOpenBicycleSettings); @@ -848,6 +851,10 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { setShowRoadworks(current => !current); }; + const parkAndRideBikesToggle = () => { + setShowParkAndRideBikes(current => !current); + }; + const cultureRouteListToggle = () => { setOpenCultureRouteList(current => !current); if (cultureRouteId) { @@ -1244,6 +1251,12 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { checkedValue: showBikeServiceStations, onChangeValue: bikeServiceStationsToggle, }, + { + type: 'parkAndRideBikeStops', + msgId: 'mobilityPlatform.menu.show.parkAndRideBikes', + checkedValue: showParkAndRideBikes, + onChangeValue: parkAndRideBikesToggle, + }, { type: 'brushSandedRoute', msgId: 'mobilityPlatform.menu.show.brushSandedRoute', @@ -1632,6 +1645,11 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { type: 'cargoBikesInfo', component: , }, + { + visible: showParkAndRideBikes, + type: 'parkAndRideBicyclesInfo', + component: , + }, { visible: showBrushSaltedRoute || showBrushSandedRoute, type: 'brushedRoutes', From 45c66b0331d81eb6ad5cacc88fdc1ace968cd4d7 Mon Sep 17 00:00:00 2001 From: juhomakkonen <69158965+juhomakkonen@users.noreply.github.com> Date: Mon, 8 Apr 2024 15:43:09 +0300 Subject: [PATCH 13/15] Filter point data from polygons (#232) --- .../components/NoParking/NoParking.js | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/src/components/MobilityPlatform/Scooters/components/NoParking/NoParking.js b/src/components/MobilityPlatform/Scooters/components/NoParking/NoParking.js index 9bab0ffe4..49d03b0a9 100644 --- a/src/components/MobilityPlatform/Scooters/components/NoParking/NoParking.js +++ b/src/components/MobilityPlatform/Scooters/components/NoParking/NoParking.js @@ -42,10 +42,20 @@ const NoParking = () => { const map = useMap(); + /** + * Filter point data from polygons. Polygons are an array and points are an object. + */ + const noParkingFiltered = noParkingData.reduce((acc, curr) => { + if (Array.isArray(curr.geometry_coords)) { + acc.push(curr); + } + return acc; + }, []); + useEffect(() => { if (renderData) { const bounds = []; - noParkingData.forEach((item) => { + noParkingFiltered.forEach(item => { bounds.push(item.geometry_coords); }); map.fitBounds(bounds); @@ -53,23 +63,21 @@ const NoParking = () => { }, [showScooterNoParking, noParkingData, map]); return ( - <> - {renderData - ? noParkingData.map(item => ( - - - - )) - : null} - + renderData + ? noParkingFiltered.map(item => ( + + + + )) + : null ); }; From 150a170f2339508f8c0e22f080f3140b876a5ef1 Mon Sep 17 00:00:00 2001 From: juhomakkonen <69158965+juhomakkonen@users.noreply.github.com> Date: Tue, 9 Apr 2024 08:43:27 +0300 Subject: [PATCH 14/15] Update/fetch custom hook (#233) * Add custom hook that fetches mobility map data * Use custom hook to fetch data in more components * More components now use the custom hook * Update more components to use custom hook * Update over & underpasses to use new hook * Culrure routes & brushed cycling routes now use custom hook * Update trails to use custom hook * Park & ride stops now use custom hook --- .../BicycleStands/BicycleStands.js | 23 ++++--- .../BikeServiceStations.js | 41 +++++-------- .../Boating/BoatParking/BoatParking.js | 58 ++++++++---------- .../Boating/GuestHarbour/GuestHarbour.js | 58 ++++++++---------- .../Boating/Marinas/Marinas.js | 53 +++++++--------- .../ChargerStationMarkers.js | 30 ++++------ .../CultureRoutes/CultureRoutes.js | 41 +++++-------- .../GasFillingStationMarkers.js | 43 ++++++------- .../LoadingPlaces/LoadingPlaces.js | 58 ++++++++---------- .../OutdoorGymDevices/OutdoorGymDevices.js | 48 +++++++-------- .../MobilityPlatform/Overpasses/Overpasses.js | 41 +++++-------- .../ParkAndRideBikes/ParkAndRideBikes.js | 28 ++++----- .../DisabledParking/DisabledParking.js | 53 +++++++--------- .../Parking/PublicParking/PublicParking.js | 48 ++++++--------- .../RentalCarParking/RentalCarParking.js | 48 +++++++-------- .../ParkingMachines/ParkingMachines.js | 54 +++++++---------- .../PublicToilets/PublicToilets.js | 45 ++++++-------- .../components/NoParking/NoParking.js | 32 ++++------ .../components/ParkingAreas/ParkingAreas.js | 59 ++++++++---------- .../SpeedLimitAreas/SpeedLimitAreas.js | 60 ++++++++----------- .../BrushedBicycleRoads.js | 36 ++++------- .../utils/useMobilityDataFetch.js | 17 ++++++ .../MobilitySettingsView.js | 53 +++++++--------- 23 files changed, 423 insertions(+), 604 deletions(-) create mode 100644 src/components/MobilityPlatform/utils/useMobilityDataFetch.js diff --git a/src/components/MobilityPlatform/BicycleStands/BicycleStands.js b/src/components/MobilityPlatform/BicycleStands/BicycleStands.js index b13b291ea..97c31c837 100644 --- a/src/components/MobilityPlatform/BicycleStands/BicycleStands.js +++ b/src/components/MobilityPlatform/BicycleStands/BicycleStands.js @@ -8,7 +8,7 @@ import bicycleStandIcon from 'servicemap-ui-turku/assets/icons/icons-icon_bicycl import circleIcon from 'servicemap-ui-turku/assets/icons/icons-icon_circle_border.svg'; import { useMobilityPlatformContext } from '../../../context/MobilityPlatformContext'; import { useAccessibleMap } from '../../../redux/selectors/settings'; -import { fetchMobilityMapData } from '../mobilityPlatformRequests/mobilityPlatformRequests'; +import useMobilityDataFetch from '../utils/useMobilityDataFetch'; import { isDataValid, fitToMapBounds, setRender, checkMapType, } from '../utils/utils'; @@ -17,7 +17,11 @@ import MarkerComponent from '../MarkerComponent'; import BicycleStandContent from './components/BicycleStandContent'; const BicycleStands = () => { - const [bicycleStands, setBicycleStands] = useState([]); + const options = { + type_name: 'BicycleStand', + page_size: 500, + }; + const [zoomLevel, setZoomLevel] = useState(13); const { showBicycleStands, showHullLockableStands } = useMobilityPlatformContext(); @@ -39,15 +43,8 @@ const BicycleStands = () => { iconSize: zoomLevel < 14 ? [20, 20] : [45, 45], }); - useEffect(() => { - const options = { - type_name: 'BicycleStand', - page_size: 500, - }; - if (showBicycleStands || showHullLockableStands || embedded) { - fetchMobilityMapData(options, setBicycleStands); - } - }, [showBicycleStands, showHullLockableStands, embedded]); + const getData = showBicycleStands || showHullLockableStands; + const { data } = useMobilityDataFetch(options, getData, embedded); const mapEvent = useMapEvents({ zoomend() { @@ -58,7 +55,7 @@ const BicycleStands = () => { const otherBicycleStands = []; /** Separate bicycle stands that are frame/hull lockable from those that are not */ - const hullLockableBicycleStands = bicycleStands.reduce((acc, curr) => { + const hullLockableBicycleStands = data?.reduce((acc, curr) => { if (curr.extra.hull_lockable) { acc.push(curr); } else { @@ -86,7 +83,7 @@ const BicycleStands = () => { }, [showHullLockableStands]); const renderBicycleStands = (isValid, data) => (isValid ? ( - data.map(item => ( + data?.map(item => ( { - const [bikeServiceStations, setBikeServiceStations] = useState([]); + const options = { + type_name: 'BikeServiceStation', + }; const { showBikeServiceStations } = useMobilityPlatformContext(); @@ -21,34 +23,23 @@ const BikeServiceStations = () => { const useContrast = useSelector(useAccessibleMap); const { icon } = global.L; - const customIcon = icon(createIcon(useContrast ? bikeServiceIconBw : bikeServiceIcon)); - useEffect(() => { - const options = { - type_name: 'BikeServiceStation', - }; - if (showBikeServiceStations) { - fetchMobilityMapData(options, setBikeServiceStations); - } - }, [showBikeServiceStations]); - - const renderData = isDataValid(showBikeServiceStations, bikeServiceStations); + const { data } = useMobilityDataFetch(options, showBikeServiceStations); + const renderData = isDataValid(showBikeServiceStations, data); useEffect(() => { - fitToMapBounds(renderData, bikeServiceStations, map); - }, [showBikeServiceStations, bikeServiceStations]); + fitToMapBounds(renderData, data, map); + }, [showBikeServiceStations, data]); return ( - <> - {renderData - ? bikeServiceStations.map(item => ( - - - - )) - : null} - + renderData + ? data.map(item => ( + + + + )) + : null ); }; diff --git a/src/components/MobilityPlatform/Boating/BoatParking/BoatParking.js b/src/components/MobilityPlatform/Boating/BoatParking/BoatParking.js index 6bb2b14a3..cc904507e 100644 --- a/src/components/MobilityPlatform/Boating/BoatParking/BoatParking.js +++ b/src/components/MobilityPlatform/Boating/BoatParking/BoatParking.js @@ -1,5 +1,5 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useSelector } from 'react-redux'; import { useMap } from 'react-leaflet'; import { useMobilityPlatformContext } from '../../../../context/MobilityPlatformContext'; @@ -7,7 +7,7 @@ import { useAccessibleMap } from '../../../../redux/selectors/settings'; import { isDataValid, fitPolygonsToBounds, blueOptionsBase, whiteOptionsBase, } from '../../utils/utils'; -import { fetchMobilityMapData } from '../../mobilityPlatformRequests/mobilityPlatformRequests'; +import useMobilityDataFetch from '../../utils/useMobilityDataFetch'; import PolygonComponent from '../../PolygonComponent'; import TextContent from '../../TextContent'; @@ -16,22 +16,15 @@ import TextContent from '../../TextContent'; */ const BoatParking = () => { - const [boatParkingData, setBoatParkingData] = useState([]); + const options = { + type_name: 'BoatParking', + latlon: true, + }; const { showBoatParking } = useMobilityPlatformContext(); const useContrast = useSelector(useAccessibleMap); - useEffect(() => { - const options = { - type_name: 'BoatParking', - latlon: true, - }; - if (showBoatParking) { - fetchMobilityMapData(options, setBoatParkingData); - } - }, [showBoatParking]); - const blueOptions = blueOptionsBase({ weight: 5 }); const whiteOptions = whiteOptionsBase({ fillOpacity: 0.3, @@ -42,30 +35,29 @@ const BoatParking = () => { const map = useMap(); - const renderData = isDataValid(showBoatParking, boatParkingData); + const { data } = useMobilityDataFetch(options, showBoatParking); + const renderData = isDataValid(showBoatParking, data); useEffect(() => { - fitPolygonsToBounds(renderData, boatParkingData, map); - }, [showBoatParking, boatParkingData]); + fitPolygonsToBounds(renderData, data, map); + }, [showBoatParking, data]); return ( - <> - {renderData - ? boatParkingData.map(item => ( - - - - )) - : null} - + renderData + ? data.map(item => ( + + + + )) + : null ); }; diff --git a/src/components/MobilityPlatform/Boating/GuestHarbour/GuestHarbour.js b/src/components/MobilityPlatform/Boating/GuestHarbour/GuestHarbour.js index e45181332..7911f27e0 100644 --- a/src/components/MobilityPlatform/Boating/GuestHarbour/GuestHarbour.js +++ b/src/components/MobilityPlatform/Boating/GuestHarbour/GuestHarbour.js @@ -1,10 +1,10 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useMap } from 'react-leaflet'; import { useSelector } from 'react-redux'; import { useMobilityPlatformContext } from '../../../../context/MobilityPlatformContext'; import { useAccessibleMap } from '../../../../redux/selectors/settings'; -import { fetchMobilityMapData } from '../../mobilityPlatformRequests/mobilityPlatformRequests'; +import useMobilityDataFetch from '../../utils/useMobilityDataFetch'; import { fitPolygonsToBounds, isDataValid, blueOptionsBase, whiteOptionsBase, } from '../../utils/utils'; @@ -16,22 +16,15 @@ import TextContent from '../../TextContent'; */ const GuestHarbour = () => { - const [guestHarbourData, setGuestHarbourData] = useState([]); + const options = { + type_name: 'GuestMarina', + latlon: true, + }; const { showGuestHarbour } = useMobilityPlatformContext(); const useContrast = useSelector(useAccessibleMap); - useEffect(() => { - const options = { - type_name: 'GuestMarina', - latlon: true, - }; - if (showGuestHarbour) { - fetchMobilityMapData(options, setGuestHarbourData); - } - }, [showGuestHarbour]); - const blueOptions = blueOptionsBase({ weight: 5 }); const whiteOptions = whiteOptionsBase({ fillOpacity: 0.3, @@ -42,30 +35,29 @@ const GuestHarbour = () => { const map = useMap(); - const renderData = isDataValid(showGuestHarbour, guestHarbourData); + const { data } = useMobilityDataFetch(options, showGuestHarbour); + const renderData = isDataValid(showGuestHarbour, data); useEffect(() => { - fitPolygonsToBounds(renderData, guestHarbourData, map); - }, [showGuestHarbour, guestHarbourData]); + fitPolygonsToBounds(renderData, data, map); + }, [showGuestHarbour, data]); return ( - <> - {renderData - ? guestHarbourData.map(item => ( - - - - )) - : null} - + renderData + ? data.map(item => ( + + + + )) + : null ); }; diff --git a/src/components/MobilityPlatform/Boating/Marinas/Marinas.js b/src/components/MobilityPlatform/Boating/Marinas/Marinas.js index 48d64a8fb..27152aa4b 100644 --- a/src/components/MobilityPlatform/Boating/Marinas/Marinas.js +++ b/src/components/MobilityPlatform/Boating/Marinas/Marinas.js @@ -1,10 +1,10 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useMap } from 'react-leaflet'; import { useSelector } from 'react-redux'; import { useMobilityPlatformContext } from '../../../../context/MobilityPlatformContext'; import { useAccessibleMap } from '../../../../redux/selectors/settings'; -import { fetchMobilityMapData } from '../../mobilityPlatformRequests/mobilityPlatformRequests'; +import useMobilityDataFetch from '../../utils/useMobilityDataFetch'; import { isDataValid, fitPolygonsToBounds, blueOptionsBase, whiteOptionsBase, } from '../../utils/utils'; @@ -16,22 +16,14 @@ import MarinasContent from './components/MarinasContent'; */ const Marinas = () => { - const [marinasData, setMarinasData] = useState([]); - + const options = { + type_name: 'Marina', + latlon: true, + }; const { showMarinas } = useMobilityPlatformContext(); const useContrast = useSelector(useAccessibleMap); - useEffect(() => { - const options = { - type_name: 'Marina', - latlon: true, - }; - if (showMarinas) { - fetchMobilityMapData(options, setMarinasData); - } - }, [showMarinas]); - const blueOptions = blueOptionsBase({ weight: 5 }); const whiteOptions = whiteOptionsBase({ fillOpacity: 0.3, @@ -42,27 +34,26 @@ const Marinas = () => { const map = useMap(); - const renderData = isDataValid(showMarinas, marinasData); + const { data } = useMobilityDataFetch(options, showMarinas); + const renderData = isDataValid(showMarinas, data); useEffect(() => { - fitPolygonsToBounds(renderData, marinasData, map); - }, [showMarinas, marinasData]); + fitPolygonsToBounds(renderData, data, map); + }, [showMarinas, data]); return ( - <> - {renderData - ? marinasData.map(item => ( - - - - )) - : null} - + renderData + ? data.map(item => ( + + + + )) + : null ); }; diff --git a/src/components/MobilityPlatform/ChargerStationMarkers/ChargerStationMarkers.js b/src/components/MobilityPlatform/ChargerStationMarkers/ChargerStationMarkers.js index 0cf7d11ca..dd00003d3 100644 --- a/src/components/MobilityPlatform/ChargerStationMarkers/ChargerStationMarkers.js +++ b/src/components/MobilityPlatform/ChargerStationMarkers/ChargerStationMarkers.js @@ -1,21 +1,24 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useSelector } from 'react-redux'; import { useMap } from 'react-leaflet'; import chargerIcon from 'servicemap-ui-turku/assets/icons/icons-icon_charging_station.svg'; import chargerIconBw from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_charging_station-bw.svg'; import { useMobilityPlatformContext } from '../../../context/MobilityPlatformContext'; import { useAccessibleMap } from '../../../redux/selectors/settings'; -import { fetchMobilityMapData } from '../mobilityPlatformRequests/mobilityPlatformRequests'; import { createIcon, isDataValid, fitToMapBounds, setRender, checkMapType, } from '../utils/utils'; import { isEmbed } from '../../../utils/path'; +import useMobilityDataFetch from '../utils/useMobilityDataFetch'; import MarkerComponent from '../MarkerComponent'; import ChargerStationContent from './components/ChargerStationContent'; const ChargerStationMarkers = () => { - const [chargerStations, setChargerStations] = useState([]); + const options = { + type_name: 'ChargingStation', + page_size: 600, + }; const { showChargingStations } = useMobilityPlatformContext(); @@ -24,34 +27,25 @@ const ChargerStationMarkers = () => { const { icon } = global.L; const useContrast = useSelector(useAccessibleMap); - const url = new URL(window.location); const embedded = isEmbed({ url: url.toString() }); - const chargerStationIcon = icon(createIcon(checkMapType(embedded, useContrast, url) ? chargerIconBw : chargerIcon)); + const { data } = useMobilityDataFetch(options, showChargingStations, embedded); - useEffect(() => { - const options = { - type_name: 'ChargingStation', - page_size: 600, - }; - if (showChargingStations || embedded) { - fetchMobilityMapData(options, setChargerStations); - } - }, [showChargingStations, embedded]); + const chargerStationIcon = icon(createIcon(checkMapType(embedded, useContrast, url) ? chargerIconBw : chargerIcon)); const paramValue = url.searchParams.get('charging_station') === '1'; - const renderData = setRender(paramValue, embedded, showChargingStations, chargerStations, isDataValid); + const renderData = setRender(paramValue, embedded, showChargingStations, data, isDataValid); useEffect(() => { if (!embedded) { - fitToMapBounds(renderData, chargerStations, map); + fitToMapBounds(renderData, data, map); } - }, [showChargingStations, chargerStations, embedded]); + }, [showChargingStations, data, embedded]); return ( renderData - ? chargerStations.map(item => ( + ? data.map(item => ( diff --git a/src/components/MobilityPlatform/CultureRoutes/CultureRoutes.js b/src/components/MobilityPlatform/CultureRoutes/CultureRoutes.js index da4382cfb..6dbbf266b 100644 --- a/src/components/MobilityPlatform/CultureRoutes/CultureRoutes.js +++ b/src/components/MobilityPlatform/CultureRoutes/CultureRoutes.js @@ -1,18 +1,24 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useMap } from 'react-leaflet'; import { useSelector } from 'react-redux'; import { useMobilityPlatformContext } from '../../../context/MobilityPlatformContext'; import { useAccessibleMap } from '../../../redux/selectors/settings'; -import { fetchMobilityMapData } from '../mobilityPlatformRequests/mobilityPlatformRequests'; +import useMobilityDataFetch from '../utils/useMobilityDataFetch'; import { isDataValid, blueOptionsBase, whiteOptionsBase, blackOptionsBase, } from '../utils/utils'; import CultureRouteUnits from './components/CultureRouteUnits'; const CultureRoutes = () => { - const [cultureRoutesGeometry, setCultureRoutesGeometry] = useState([]); - const [cultureRouteUnits, setCultureRouteUnits] = useState([]); + const optionsGeometry = { + type_name: 'CultureRouteGeometry', + page_size: 50, + }; + const optionsUnit = { + type_name: 'CultureRouteUnit', + page_size: 200, + }; const { openMobilityPlatform, showCultureRoutes, cultureRouteId } = useMobilityPlatformContext(); @@ -24,27 +30,10 @@ const CultureRoutes = () => { const whiteOptions = whiteOptionsBase({ dashArray: !useContrast ? '1, 8' : null }); const blackOptions = blackOptionsBase({ dashArray: '2 10 10 10' }); - useEffect(() => { - const options = { - type_name: 'CultureRouteGeometry', - page_size: 50, - }; - if (openMobilityPlatform) { - fetchMobilityMapData(options, setCultureRoutesGeometry); - } - }, [openMobilityPlatform, setCultureRoutesGeometry]); - - useEffect(() => { - const options = { - type_name: 'CultureRouteUnit', - page_size: 200, - }; - if (openMobilityPlatform) { - fetchMobilityMapData(options, setCultureRouteUnits); - } - }, [openMobilityPlatform, setCultureRouteUnits]); + const { data: cultureRoutesGeometry } = useMobilityDataFetch(optionsGeometry, openMobilityPlatform); + const { data: cultureRouteUnits } = useMobilityDataFetch(optionsUnit, openMobilityPlatform); - const filterRoutes = (data) => { + const filterRoutes = data => { if (data && data.length > 0) { return data.filter(item => item.mobile_unit_group.id === cultureRouteId); } @@ -53,7 +42,7 @@ const CultureRoutes = () => { const activeCultureRoute = filterRoutes(cultureRoutesGeometry); - const swapCoords = (inputData) => { + const swapCoords = inputData => { if (inputData && inputData.length > 0) { return inputData.map(item => [item[1], item[0]]); } @@ -67,7 +56,7 @@ const CultureRoutes = () => { useEffect(() => { if (renderData) { const bounds = []; - activeCultureRoute.forEach((item) => { + activeCultureRoute.forEach(item => { bounds.push(swapCoords(item.geometry_coords)); }); map.fitBounds([bounds]); diff --git a/src/components/MobilityPlatform/GasFillingStationMarkers/GasFillingStationMarkers.js b/src/components/MobilityPlatform/GasFillingStationMarkers/GasFillingStationMarkers.js index 28f9898eb..0d3313d77 100644 --- a/src/components/MobilityPlatform/GasFillingStationMarkers/GasFillingStationMarkers.js +++ b/src/components/MobilityPlatform/GasFillingStationMarkers/GasFillingStationMarkers.js @@ -1,54 +1,45 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useSelector } from 'react-redux'; import { useMap } from 'react-leaflet'; import gasFillingIcon from 'servicemap-ui-turku/assets/icons/icons-icon_gas_station.svg'; import gasFillingIconBw from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_gas_station-bw.svg'; import { useMobilityPlatformContext } from '../../../context/MobilityPlatformContext'; import { useAccessibleMap } from '../../../redux/selectors/settings'; -import { fetchMobilityMapData } from '../mobilityPlatformRequests/mobilityPlatformRequests'; import { createIcon, isDataValid, fitToMapBounds } from '../utils/utils'; +import useMobilityDataFetch from '../utils/useMobilityDataFetch'; import MarkerComponent from '../MarkerComponent'; import GasFillingStationContent from './components/GasFillingStationContent'; const GasFillingStationMarkers = () => { - const [gasFillingStations, setGasFillingStations] = useState([]); + const options = { + type_name: 'GasFillingStation', + }; const { showGasFillingStations } = useMobilityPlatformContext(); - const { icon } = global.L; - const useContrast = useSelector(useAccessibleMap); + const { icon } = global.L; const gasStationIcon = icon(createIcon(useContrast ? gasFillingIconBw : gasFillingIcon)); - useEffect(() => { - const options = { - type_name: 'GasFillingStation', - }; - if (showGasFillingStations) { - fetchMobilityMapData(options, setGasFillingStations); - } - }, [showGasFillingStations]); - - const renderData = isDataValid(showGasFillingStations, gasFillingStations); + const { data } = useMobilityDataFetch(options, showGasFillingStations); + const renderData = isDataValid(showGasFillingStations, data); const map = useMap(); useEffect(() => { - fitToMapBounds(renderData, gasFillingStations, map); - }, [showGasFillingStations, gasFillingStations]); + fitToMapBounds(renderData, data, map); + }, [showGasFillingStations, data]); return ( - <> - {renderData - ? gasFillingStations.map(item => ( - - - - )) - : null} - + renderData + ? data.map(item => ( + + + + )) + : null ); }; diff --git a/src/components/MobilityPlatform/LoadingPlaces/LoadingPlaces.js b/src/components/MobilityPlatform/LoadingPlaces/LoadingPlaces.js index 27aeeab21..6d196544e 100644 --- a/src/components/MobilityPlatform/LoadingPlaces/LoadingPlaces.js +++ b/src/components/MobilityPlatform/LoadingPlaces/LoadingPlaces.js @@ -1,17 +1,21 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useMap } from 'react-leaflet'; import { useSelector } from 'react-redux'; import loadingPlaceIcon from 'servicemap-ui-turku/assets/icons/icons-icon_loading_place.svg'; import loadingPlaceIconBw from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_loading_place-bw.svg'; import { useMobilityPlatformContext } from '../../../context/MobilityPlatformContext'; import { useAccessibleMap } from '../../../redux/selectors/settings'; -import { fetchMobilityMapData } from '../mobilityPlatformRequests/mobilityPlatformRequests'; +import useMobilityDataFetch from '../utils/useMobilityDataFetch'; import { isDataValid, fitPolygonsToBounds, createIcon } from '../utils/utils'; import LoadingPlacesContent from './components/LoadingPlacesContent'; const LoadingPlaces = () => { - const [loadingPlaces, setLoadingPlaces] = useState([]); + const options = { + type_name: 'LoadingUnloadingPlace', + page_size: 300, + latlon: true, + }; const { showLoadingPlaces } = useMobilityPlatformContext(); @@ -24,43 +28,29 @@ const LoadingPlaces = () => { const customIcon = icon(createIcon(useContrast ? loadingPlaceIconBw : loadingPlaceIcon)); - useEffect(() => { - const options = { - type_name: 'LoadingUnloadingPlace', - page_size: 300, - latlon: true, - }; - if (showLoadingPlaces) { - fetchMobilityMapData(options, setLoadingPlaces); - } - }, [showLoadingPlaces]); - - const renderData = isDataValid(showLoadingPlaces, loadingPlaces); + const { data } = useMobilityDataFetch(options, showLoadingPlaces); + const renderData = isDataValid(showLoadingPlaces, data); useEffect(() => { - fitPolygonsToBounds(renderData, loadingPlaces, map); - }, [showLoadingPlaces, loadingPlaces]); + fitPolygonsToBounds(renderData, data, map); + }, [showLoadingPlaces, data]); const getSingleCoordinates = data => data[0][0]; return ( - <> - {renderData - ? loadingPlaces.map(item => ( - - <> - - - - - - )) - : null} - + renderData + ? data.map(item => ( + + + + + + )) + : null ); }; diff --git a/src/components/MobilityPlatform/OutdoorGymDevices/OutdoorGymDevices.js b/src/components/MobilityPlatform/OutdoorGymDevices/OutdoorGymDevices.js index 102cc8b58..58069cb44 100644 --- a/src/components/MobilityPlatform/OutdoorGymDevices/OutdoorGymDevices.js +++ b/src/components/MobilityPlatform/OutdoorGymDevices/OutdoorGymDevices.js @@ -1,18 +1,20 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useMap } from 'react-leaflet'; import { useSelector } from 'react-redux'; import sportIconContrast from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_outdoor_gym-bw.svg'; import sportIcon from 'servicemap-ui-turku/assets/icons/icons-icon_outdoor_gym.svg'; import { useMobilityPlatformContext } from '../../../context/MobilityPlatformContext'; import { useAccessibleMap } from '../../../redux/selectors/settings'; -import { fetchMobilityMapData } from '../mobilityPlatformRequests/mobilityPlatformRequests'; import { isDataValid, fitToMapBounds, createIcon } from '../utils/utils'; +import useMobilityDataFetch from '../utils/useMobilityDataFetch'; import MarkerComponent from '../MarkerComponent'; import OutdoorGymDevicesContent from './components/OutdoorGymDevicesContent'; const OutdoorGymDevices = () => { - const [outdoorGymDevices, setOutdoorGymDevices] = useState([]); + const options = { + type_name: 'OutdoorGymDevice', + }; const { showOutdoorGymDevices } = useMobilityPlatformContext(); @@ -24,35 +26,25 @@ const OutdoorGymDevices = () => { const customIcon = icon(createIcon(useContrast ? sportIconContrast : sportIcon)); - useEffect(() => { - const options = { - type_name: 'OutdoorGymDevice', - }; - if (showOutdoorGymDevices) { - fetchMobilityMapData(options, setOutdoorGymDevices); - } - }, [showOutdoorGymDevices]); - - const renderData = isDataValid(showOutdoorGymDevices, outdoorGymDevices); + const { data } = useMobilityDataFetch(options, showOutdoorGymDevices); + const renderData = isDataValid(showOutdoorGymDevices, data); useEffect(() => { - fitToMapBounds(renderData, outdoorGymDevices, map); - }, [showOutdoorGymDevices, outdoorGymDevices]); + fitToMapBounds(renderData, data, map); + }, [showOutdoorGymDevices, data]); return ( - <> - {renderData ? ( - outdoorGymDevices.map(item => ( - - - - )) - ) : null} - + renderData ? ( + data.map(item => ( + + + + )) + ) : null ); }; diff --git a/src/components/MobilityPlatform/Overpasses/Overpasses.js b/src/components/MobilityPlatform/Overpasses/Overpasses.js index cab83bcbb..146339160 100644 --- a/src/components/MobilityPlatform/Overpasses/Overpasses.js +++ b/src/components/MobilityPlatform/Overpasses/Overpasses.js @@ -1,10 +1,10 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useMap } from 'react-leaflet'; import { useSelector } from 'react-redux'; import { useMobilityPlatformContext } from '../../../context/MobilityPlatformContext'; import { useAccessibleMap } from '../../../redux/selectors/settings'; -import { fetchMobilityMapData } from '../mobilityPlatformRequests/mobilityPlatformRequests'; +import useMobilityDataFetch from '../utils/useMobilityDataFetch'; import { isDataValid, fitPolygonsToBounds, blueOptionsBase, whiteOptionsBase, setRender, } from '../utils/utils'; @@ -15,8 +15,17 @@ import { isEmbed } from '../../../utils/path'; */ const Overpasses = () => { - const [overpassData, setOverpassData] = useState([]); - const [underpassData, setUnderpassData] = useState([]); + const optionsOverpass = { + type_name: 'Overpass', + page_size: 200, + latlon: true, + }; + + const optionsUnderpass = { + type_name: 'Underpass', + page_size: 200, + latlon: true, + }; const { showOverpasses, showUnderpasses } = useMobilityPlatformContext(); @@ -27,28 +36,6 @@ const Overpasses = () => { const url = new URL(window.location); const embedded = isEmbed({ url: url.toString() }); - useEffect(() => { - const options = { - type_name: 'Overpass', - page_size: 200, - latlon: true, - }; - if (showOverpasses || embedded) { - fetchMobilityMapData(options, setOverpassData); - } - }, [showOverpasses, embedded]); - - useEffect(() => { - const options = { - type_name: 'Underpass', - page_size: 200, - latlon: true, - }; - if (showUnderpasses || embedded) { - fetchMobilityMapData(options, setUnderpassData); - } - }, [showUnderpasses, embedded]); - const blueOptions = blueOptionsBase({ weight: 9 }); const greenOptions = { color: 'rgba(13, 145, 31, 255)', weight: 9 }; const whiteOptions = whiteOptionsBase({ @@ -59,6 +46,8 @@ const Overpasses = () => { const map = useMap(); + const { data: overpassData } = useMobilityDataFetch(optionsOverpass, showOverpasses, embedded); + const { data: underpassData } = useMobilityDataFetch(optionsUnderpass, showUnderpasses, embedded); const paramOverpass = url.searchParams.get('overpass') === '1'; const paramUnderpass = url.searchParams.get('underpass') === '1'; const renderOverpassData = setRender(paramOverpass, embedded, showOverpasses, overpassData, isDataValid); diff --git a/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/ParkAndRideBikes.js b/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/ParkAndRideBikes.js index 3d1f2d920..73883d5a9 100644 --- a/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/ParkAndRideBikes.js +++ b/src/components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes/ParkAndRideBikes.js @@ -1,12 +1,12 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useMap } from 'react-leaflet'; import { useSelector } from 'react-redux'; import parkAndRideIcon from 'servicemap-ui-turku/assets/icons/icons-icon_park_and_ride_bicycle.svg'; import parkAndRideIconBw from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_park_and_ride_bicycle-bw.svg'; import { useMobilityPlatformContext } from '../../../../context/MobilityPlatformContext'; import { useAccessibleMap } from '../../../../redux/selectors/settings'; -import { fetchMobilityMapData } from '../../mobilityPlatformRequests/mobilityPlatformRequests'; +import useMobilityDataFetch from '../../utils/useMobilityDataFetch'; import { createIcon, isDataValid, fitToMapBounds } from '../../utils/utils'; import MarkerComponent from '../../MarkerComponent'; import ParkAndRideBikesContent from './components/ParkAndRideBikesContent'; @@ -15,7 +15,10 @@ import ParkAndRideBikesContent from './components/ParkAndRideBikesContent'; * Displays park and ride stops for bikes on the map in marker format. */ const ParkAndRideBikes = () => { - const [parkAndRideBikesData, setParkAndRideBikesData] = useState([]); + const options = { + type_name: 'FoliParkAndRideBikesStop', + page_size: 100, + }; const { showParkAndRideBikes } = useMobilityPlatformContext(); @@ -25,27 +28,18 @@ const ParkAndRideBikes = () => { const customIcon = icon(createIcon(useContrast ? parkAndRideIconBw : parkAndRideIcon)); - useEffect(() => { - const options = { - type_name: 'FoliParkAndRideBikesStop', - page_size: 100, - }; - if (showParkAndRideBikes) { - fetchMobilityMapData(options, setParkAndRideBikesData); - } - }, [showParkAndRideBikes]); - const map = useMap(); - const renderData = isDataValid(showParkAndRideBikes, parkAndRideBikesData); + const { data } = useMobilityDataFetch(options, showParkAndRideBikes); + const renderData = isDataValid(showParkAndRideBikes, data); useEffect(() => { - fitToMapBounds(renderData, parkAndRideBikesData, map); - }, [showParkAndRideBikes, parkAndRideBikesData]); + fitToMapBounds(renderData, data, map); + }, [showParkAndRideBikes, data]); return ( renderData - ? parkAndRideBikesData.map(item => ( + ? data.map(item => ( diff --git a/src/components/MobilityPlatform/Parking/DisabledParking/DisabledParking.js b/src/components/MobilityPlatform/Parking/DisabledParking/DisabledParking.js index 2d3b5270b..d3a31d32b 100644 --- a/src/components/MobilityPlatform/Parking/DisabledParking/DisabledParking.js +++ b/src/components/MobilityPlatform/Parking/DisabledParking/DisabledParking.js @@ -1,12 +1,12 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useMap } from 'react-leaflet'; import { useSelector } from 'react-redux'; import disabledParkingIcon from 'servicemap-ui-turku/assets/icons/icons-icon_disabled_parking.svg'; import disabledParkingIconBw from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_disabled_parking-bw.svg'; import { useMobilityPlatformContext } from '../../../../context/MobilityPlatformContext'; import { useAccessibleMap } from '../../../../redux/selectors/settings'; -import { fetchMobilityMapData } from '../../mobilityPlatformRequests/mobilityPlatformRequests'; +import useMobilityDataFetch from '../../utils/useMobilityDataFetch'; import { createIcon, isDataValid, fitPolygonsToBounds } from '../../utils/utils'; import DisabledParkingContent from './components/DisabledParkingContent'; @@ -15,8 +15,11 @@ import DisabledParkingContent from './components/DisabledParkingContent'; */ const DisabledParking = () => { - const [disabledParkingData, setDisabledParkingData] = useState([]); - + const options = { + type_name: 'DisabledParking', + page_size: 1000, + latlon: true, + }; const { showDisabledParking } = useMobilityPlatformContext(); const { Marker, Popup } = global.rL; @@ -26,41 +29,29 @@ const DisabledParking = () => { const customIcon = icon(createIcon(useContrast ? disabledParkingIconBw : disabledParkingIcon)); - useEffect(() => { - const options = { - type_name: 'DisabledParking', - page_size: 1000, - latlon: true, - }; - if (showDisabledParking) { - fetchMobilityMapData(options, setDisabledParkingData); - } - }, [showDisabledParking]); - const map = useMap(); - const renderData = isDataValid(showDisabledParking, disabledParkingData); + const { data } = useMobilityDataFetch(options, showDisabledParking); + const renderData = isDataValid(showDisabledParking, data); useEffect(() => { - fitPolygonsToBounds(renderData, disabledParkingData, map); - }, [showDisabledParking, disabledParkingData]); + fitPolygonsToBounds(renderData, data, map); + }, [showDisabledParking, data]); const getSingleCoordinates = data => data[0][0]; return ( - <> - {renderData ? ( - disabledParkingData.map(item => ( -

- - - - - -
- )) - ) : null} - + renderData ? ( + data.map(item => ( +
+ + + + + +
+ )) + ) : null ); }; diff --git a/src/components/MobilityPlatform/Parking/PublicParking/PublicParking.js b/src/components/MobilityPlatform/Parking/PublicParking/PublicParking.js index 1bbaab2dd..62614963c 100644 --- a/src/components/MobilityPlatform/Parking/PublicParking/PublicParking.js +++ b/src/components/MobilityPlatform/Parking/PublicParking/PublicParking.js @@ -1,10 +1,10 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useMap } from 'react-leaflet'; import { useSelector } from 'react-redux'; import { useAccessibleMap } from '../../../../redux/selectors/settings'; import { useMobilityPlatformContext } from '../../../../context/MobilityPlatformContext'; -import { fetchMobilityMapData } from '../../mobilityPlatformRequests/mobilityPlatformRequests'; +import useMobilityDataFetch from '../../utils/useMobilityDataFetch'; import { isDataValid, blueOptionsBase, whiteOptionsBase, fitPolygonsToBounds, } from '../../utils/utils'; @@ -16,49 +16,35 @@ import PublicParkingContent from './components/PublicParkingContent'; */ const PublicParking = () => { - const [publicParkingData, setPublicParkingData] = useState([]); - + const options = { + type_name: 'NoStaffParking', + page_size: 1000, + latlon: true, + }; const { showPublicParking } = useMobilityPlatformContext(); const useContrast = useSelector(useAccessibleMap); - useEffect(() => { - const options = { - type_name: 'NoStaffParking', - page_size: 1000, - latlon: true, - }; - if (showPublicParking) { - fetchMobilityMapData(options, setPublicParkingData); - } - }, [showPublicParking]); - const blueOptions = blueOptionsBase({ weight: 5 }); const whiteOptions = whiteOptionsBase({ fillOpacity: 0.3, weight: 5, dashArray: '2 4 6' }); const pathOptions = useContrast ? whiteOptions : blueOptions; const map = useMap(); - const renderData = isDataValid(showPublicParking, publicParkingData); + const { data } = useMobilityDataFetch(options, showPublicParking); + const renderData = isDataValid(showPublicParking, data); useEffect(() => { - fitPolygonsToBounds(renderData, publicParkingData, map); - }, [showPublicParking, publicParkingData, map]); + fitPolygonsToBounds(renderData, data, map); + }, [showPublicParking, data, map]); return ( - <> - {renderData - && publicParkingData.map(item => ( - - - - ))} - + renderData + && data.map(item => ( + + + + )) ); }; diff --git a/src/components/MobilityPlatform/Parking/RentalCarParking/RentalCarParking.js b/src/components/MobilityPlatform/Parking/RentalCarParking/RentalCarParking.js index 722776bc7..734582c0c 100644 --- a/src/components/MobilityPlatform/Parking/RentalCarParking/RentalCarParking.js +++ b/src/components/MobilityPlatform/Parking/RentalCarParking/RentalCarParking.js @@ -1,12 +1,12 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useMap } from 'react-leaflet'; import { useSelector } from 'react-redux'; import rentalCarParkingIcon from 'servicemap-ui-turku/assets/icons/icons-icon_rental_car_parking.svg'; import rentalCarParkingIconBw from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_rental_car_parking-bw.svg'; import { useAccessibleMap } from '../../../../redux/selectors/settings'; import { useMobilityPlatformContext } from '../../../../context/MobilityPlatformContext'; -import { fetchMobilityMapData } from '../../mobilityPlatformRequests/mobilityPlatformRequests'; +import useMobilityDataFetch from '../../utils/useMobilityDataFetch'; import { isDataValid, fitPolygonsToBounds, createIcon } from '../../utils/utils'; import RentalCarParkingContent from './components/RentalCarParkingContent'; @@ -15,7 +15,11 @@ import RentalCarParkingContent from './components/RentalCarParkingContent'; */ const RentalCarParking = () => { - const [rentalCarParkingData, setRentalCarParkingData] = useState([]); + const options = { + type_name: 'ShareCarParkingPlace', + page_size: 100, + latlon: true, + }; const { showRentalCarParking } = useMobilityPlatformContext(); @@ -24,41 +28,29 @@ const RentalCarParking = () => { const { Marker, Popup } = global.rL; const { icon } = global.L; - useEffect(() => { - const options = { - type_name: 'ShareCarParkingPlace', - page_size: 100, - latlon: true, - }; - if (showRentalCarParking) { - fetchMobilityMapData(options, setRentalCarParkingData); - } - }, [showRentalCarParking]); - const customIcon = icon(createIcon(useContrast ? rentalCarParkingIconBw : rentalCarParkingIcon)); const map = useMap(); - const renderData = isDataValid(showRentalCarParking, rentalCarParkingData); + const { data } = useMobilityDataFetch(options, showRentalCarParking); + const renderData = isDataValid(showRentalCarParking, data); useEffect(() => { - fitPolygonsToBounds(renderData, rentalCarParkingData, map); - }, [showRentalCarParking, rentalCarParkingData, map]); + fitPolygonsToBounds(renderData, data, map); + }, [showRentalCarParking, data, map]); const getSingleCoordinates = data => data[0][0]; return ( - <> - {renderData - ? rentalCarParkingData.map(item => ( - - - - - - )) - : null} - + renderData + ? data.map(item => ( + + + + + + )) + : null ); }; diff --git a/src/components/MobilityPlatform/ParkingMachines/ParkingMachines.js b/src/components/MobilityPlatform/ParkingMachines/ParkingMachines.js index bdfe0c165..b7d7bc246 100644 --- a/src/components/MobilityPlatform/ParkingMachines/ParkingMachines.js +++ b/src/components/MobilityPlatform/ParkingMachines/ParkingMachines.js @@ -1,59 +1,49 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useSelector } from 'react-redux'; import { useMap } from 'react-leaflet'; import parkingMachineIcon from 'servicemap-ui-turku/assets/icons/icons-icon_parking_machine.svg'; import parkingMachineIconContrast from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_parking_machine-bw.svg'; import { useMobilityPlatformContext } from '../../../context/MobilityPlatformContext'; import { useAccessibleMap } from '../../../redux/selectors/settings'; -import { fetchMobilityMapData } from '../mobilityPlatformRequests/mobilityPlatformRequests'; +import useMobilityDataFetch from '../utils/useMobilityDataFetch'; import { createIcon, isDataValid, fitToMapBounds } from '../utils/utils'; import MarkerComponent from '../MarkerComponent'; import ParkingMachinesContent from './components/ParkingMachinesContent'; const ParkingMachines = () => { - const [parkingMachinesData, setParkingMachinesData] = useState([]); + const options = { + type_name: 'ParkingMachine', + page_size: 500, + }; const { showParkingMachines } = useMobilityPlatformContext(); const map = useMap(); - - const { icon } = global.L; - const useContrast = useSelector(useAccessibleMap); + const { icon } = global.L; const customIcon = icon(createIcon(useContrast ? parkingMachineIconContrast : parkingMachineIcon)); - useEffect(() => { - const options = { - type_name: 'ParkingMachine', - page_size: 500, - }; - if (showParkingMachines) { - fetchMobilityMapData(options, setParkingMachinesData); - } - }, [showParkingMachines]); - - const renderData = isDataValid(showParkingMachines, parkingMachinesData); + const { data } = useMobilityDataFetch(options, showParkingMachines); + const renderData = isDataValid(showParkingMachines, data); useEffect(() => { - fitToMapBounds(renderData, parkingMachinesData, map); - }, [showParkingMachines, parkingMachinesData]); + fitToMapBounds(renderData, data, map); + }, [showParkingMachines, data]); return ( - <> - {renderData ? ( - parkingMachinesData.map(item => ( - - - - )) - ) : null} - + renderData ? ( + data.map(item => ( + + + + )) + ) : null ); }; diff --git a/src/components/MobilityPlatform/PublicToilets/PublicToilets.js b/src/components/MobilityPlatform/PublicToilets/PublicToilets.js index c72fb2744..0025a9cfa 100644 --- a/src/components/MobilityPlatform/PublicToilets/PublicToilets.js +++ b/src/components/MobilityPlatform/PublicToilets/PublicToilets.js @@ -1,55 +1,46 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useMap } from 'react-leaflet'; import { useSelector } from 'react-redux'; import publicToiletIcon from 'servicemap-ui-turku/assets/icons/icons-icon_toilet.svg'; import publicToiletIconBw from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_toilet-bw.svg'; import { useMobilityPlatformContext } from '../../../context/MobilityPlatformContext'; -import { fetchMobilityMapData } from '../mobilityPlatformRequests/mobilityPlatformRequests'; +import useMobilityDataFetch from '../utils/useMobilityDataFetch'; import { createIcon, isDataValid, fitToMapBounds } from '../utils/utils'; import { useAccessibleMap } from '../../../redux/selectors/settings'; import MarkerComponent from '../MarkerComponent'; import PublicToiletsContent from './components/PublicToiletsContent'; const PublicToilets = () => { - const [publicToiletsData, setPublicToiletsData] = useState([]); + const options = { + type_name: 'PublicToilet', + page_size: 50, + }; const { showPublicToilets } = useMobilityPlatformContext(); - const { icon } = global.L; - const useContrast = useSelector(useAccessibleMap); + const { icon } = global.L; const customIcon = icon(createIcon(useContrast ? publicToiletIconBw : publicToiletIcon)); - useEffect(() => { - const options = { - type_name: 'PublicToilet', - page_size: 50, - }; - if (showPublicToilets) { - fetchMobilityMapData(options, setPublicToiletsData); - } - }, [showPublicToilets]); - - const renderData = isDataValid(showPublicToilets, publicToiletsData); + const { data } = useMobilityDataFetch(options, showPublicToilets); + const renderData = isDataValid(showPublicToilets, data); const map = useMap(); useEffect(() => { - fitToMapBounds(renderData, publicToiletsData, map); - }, [showPublicToilets, publicToiletsData]); + fitToMapBounds(renderData, data, map); + }, [showPublicToilets, data]); return ( - <> - {renderData - ? publicToiletsData.map(item => ( - - - - )) - : null} - + renderData + ? data.map(item => ( + + + + )) + : null ); }; diff --git a/src/components/MobilityPlatform/Scooters/components/NoParking/NoParking.js b/src/components/MobilityPlatform/Scooters/components/NoParking/NoParking.js index 49d03b0a9..496dc2bd0 100644 --- a/src/components/MobilityPlatform/Scooters/components/NoParking/NoParking.js +++ b/src/components/MobilityPlatform/Scooters/components/NoParking/NoParking.js @@ -1,9 +1,9 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useMap } from 'react-leaflet'; import { useSelector } from 'react-redux'; import { useMobilityPlatformContext } from '../../../../../context/MobilityPlatformContext'; -import { fetchMobilityMapData } from '../../../mobilityPlatformRequests/mobilityPlatformRequests'; +import useMobilityDataFetch from '../../../utils/useMobilityDataFetch'; import { isDataValid, whiteOptionsBase, redOptionsBase } from '../../../utils/utils'; import { useAccessibleMap } from '../../../../../redux/selectors/settings'; import PolygonComponent from '../../../PolygonComponent'; @@ -12,22 +12,13 @@ import TextContent from '../../../TextContent'; /** * Displays no parking zones of scooters on the map in polygon format. */ - const NoParking = () => { - const [noParkingData, setNoParkingData] = useState([]); + const options = { + type_name: 'ScooterNoParkingArea', + latlon: true, + }; const { showScooterNoParking } = useMobilityPlatformContext(); - - useEffect(() => { - const options = { - type_name: 'ScooterNoParkingArea', - latlon: true, - }; - if (showScooterNoParking) { - fetchMobilityMapData(options, setNoParkingData); - } - }, [showScooterNoParking]); - const useContrast = useSelector(useAccessibleMap); const redOptions = redOptionsBase({ weight: 5 }); @@ -37,21 +28,22 @@ const NoParking = () => { dashArray: '11 2 11', }); const pathOptions = useContrast ? whiteOptions : redOptions; - - const renderData = isDataValid(showScooterNoParking, noParkingData); - const map = useMap(); + const { data } = useMobilityDataFetch(options, showScooterNoParking); + /** * Filter point data from polygons. Polygons are an array and points are an object. */ - const noParkingFiltered = noParkingData.reduce((acc, curr) => { + const noParkingFiltered = data.reduce((acc, curr) => { if (Array.isArray(curr.geometry_coords)) { acc.push(curr); } return acc; }, []); + const renderData = isDataValid(showScooterNoParking, noParkingFiltered); + useEffect(() => { if (renderData) { const bounds = []; @@ -60,7 +52,7 @@ const NoParking = () => { }); map.fitBounds(bounds); } - }, [showScooterNoParking, noParkingData, map]); + }, [showScooterNoParking, noParkingFiltered, map]); return ( renderData diff --git a/src/components/MobilityPlatform/Scooters/components/ParkingAreas/ParkingAreas.js b/src/components/MobilityPlatform/Scooters/components/ParkingAreas/ParkingAreas.js index d2de2b093..bb447aeae 100644 --- a/src/components/MobilityPlatform/Scooters/components/ParkingAreas/ParkingAreas.js +++ b/src/components/MobilityPlatform/Scooters/components/ParkingAreas/ParkingAreas.js @@ -1,18 +1,19 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useMap } from 'react-leaflet'; import { useSelector } from 'react-redux'; import scooterParkingIcon from 'servicemap-ui-turku/assets/icons/icons-icon_scooter_parking.svg'; import scooterParkingIconBw from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_scooter_parking-bw.svg'; import { useMobilityPlatformContext } from '../../../../../context/MobilityPlatformContext'; import { useAccessibleMap } from '../../../../../redux/selectors/settings'; -import { fetchMobilityMapData } from '../../../mobilityPlatformRequests/mobilityPlatformRequests'; +import useMobilityDataFetch from '../../../utils/useMobilityDataFetch'; import { createIcon, isDataValid, fitToMapBounds } from '../../../utils/utils'; import TextContent from '../../../TextContent'; const ParkingAreas = () => { - const [parkingAreas, setParkingAreas] = useState([]); - + const options = { + type_name: 'ScooterParkingArea', + }; const { showScooterParkingAreas } = useMobilityPlatformContext(); const map = useMap(); @@ -24,40 +25,30 @@ const ParkingAreas = () => { const customIcon = icon(createIcon(useContrast ? scooterParkingIconBw : scooterParkingIcon)); - useEffect(() => { - const options = { - type_name: 'ScooterParkingArea', - }; - if (showScooterParkingAreas) { - fetchMobilityMapData(options, setParkingAreas); - } - }, [showScooterParkingAreas]); - - const renderData = isDataValid(showScooterParkingAreas, parkingAreas); + const { data } = useMobilityDataFetch(options, showScooterParkingAreas); + const renderData = isDataValid(showScooterParkingAreas, data); useEffect(() => { - fitToMapBounds(renderData, parkingAreas, map); - }, [showScooterParkingAreas, parkingAreas]); + fitToMapBounds(renderData, data, map); + }, [showScooterParkingAreas, data]); return ( - <> - {renderData ? ( - parkingAreas.map(item => ( - - - - - - )) - ) : null} - + renderData ? ( + data.map(item => ( + + + + + + )) + ) : null ); }; diff --git a/src/components/MobilityPlatform/Scooters/components/SpeedLimitAreas/SpeedLimitAreas.js b/src/components/MobilityPlatform/Scooters/components/SpeedLimitAreas/SpeedLimitAreas.js index 74775bdd5..45b327fca 100644 --- a/src/components/MobilityPlatform/Scooters/components/SpeedLimitAreas/SpeedLimitAreas.js +++ b/src/components/MobilityPlatform/Scooters/components/SpeedLimitAreas/SpeedLimitAreas.js @@ -1,10 +1,10 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useSelector } from 'react-redux'; import { useMap } from 'react-leaflet'; import { useMobilityPlatformContext } from '../../../../../context/MobilityPlatformContext'; import { useAccessibleMap } from '../../../../../redux/selectors/settings'; -import { fetchMobilityMapData } from '../../../mobilityPlatformRequests/mobilityPlatformRequests'; +import useMobilityDataFetch from '../../../utils/useMobilityDataFetch'; import { isDataValid, fitPolygonsToBounds, blueOptionsBase, whiteOptionsBase, } from '../../../utils/utils'; @@ -16,53 +16,45 @@ import TextContent from '../../../TextContent'; */ const SpeedLimitAreas = () => { - const [speedLimitAreas, setSpeedLimitAreas] = useState([]); + const options = { + type_name: 'ScooterSpeedLimitArea', + page_size: 100, + latlon: true, + }; const { showScooterSpeedLimitAreas } = useMobilityPlatformContext(); - useEffect(() => { - const options = { - type_name: 'ScooterSpeedLimitArea', - page_size: 100, - latlon: true, - }; - if (showScooterSpeedLimitAreas) { - fetchMobilityMapData(options, setSpeedLimitAreas); - } - }, [showScooterSpeedLimitAreas]); - const useContrast = useSelector(useAccessibleMap); const blueOptions = blueOptionsBase(); const whiteOptions = whiteOptionsBase({ fillOpacity: 0.3, dashArray: '10 2 10' }); const pathOptions = useContrast ? whiteOptions : blueOptions; - const renderData = isDataValid(showScooterSpeedLimitAreas, speedLimitAreas); + const { data } = useMobilityDataFetch(options, showScooterSpeedLimitAreas); + const renderData = isDataValid(showScooterSpeedLimitAreas, data); const map = useMap(); useEffect(() => { - fitPolygonsToBounds(renderData, speedLimitAreas, map); - }, [showScooterSpeedLimitAreas, speedLimitAreas]); + fitPolygonsToBounds(renderData, data, map); + }, [showScooterSpeedLimitAreas, data]); return ( - <> - {renderData - ? speedLimitAreas.map(item => ( - - - - )) - : null} - + renderData + ? data.map(item => ( + + + + )) + : null ); }; diff --git a/src/components/MobilityPlatform/SnowPlows/components/BrushedBicycleRoads/BrushedBicycleRoads.js b/src/components/MobilityPlatform/SnowPlows/components/BrushedBicycleRoads/BrushedBicycleRoads.js index 6b385fee4..ce89780f6 100644 --- a/src/components/MobilityPlatform/SnowPlows/components/BrushedBicycleRoads/BrushedBicycleRoads.js +++ b/src/components/MobilityPlatform/SnowPlows/components/BrushedBicycleRoads/BrushedBicycleRoads.js @@ -1,17 +1,23 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { useMap } from 'react-leaflet'; import { useSelector } from 'react-redux'; import { useMobilityPlatformContext } from '../../../../../context/MobilityPlatformContext'; import { useAccessibleMap } from '../../../../../redux/selectors/settings'; -import { fetchMobilityMapData } from '../../../mobilityPlatformRequests/mobilityPlatformRequests'; +import useMobilityDataFetch from '../../../utils/useMobilityDataFetch'; import { isDataValid, fitPolygonsToBounds } from '../../../utils/utils'; /* Display brush sanded and brush salted bicycle roads */ const BrushedBicycleRoads = () => { - const [brushSandedRoutes, setBrushSandedRoutes] = useState([]); - const [brushSaltedRoutes, setBrushSaltedRoutes] = useState([]); + const optionsBrushSanded = { + type_name: 'BrushSandedBicycleNetwork', + latlon: true, + }; + const optionsBrushSalted = { + type_name: 'BrushSaltedBicycleNetwork', + latlon: true, + }; const { showBrushSandedRoute, showBrushSaltedRoute } = useMobilityPlatformContext(); @@ -48,28 +54,10 @@ const BrushedBicycleRoads = () => { weight: 4, }; - useEffect(() => { - const options = { - type_name: 'BrushSandedBicycleNetwork', - latlon: true, - }; - if (showBrushSandedRoute) { - fetchMobilityMapData(options, setBrushSandedRoutes); - } - }, [showBrushSandedRoute]); - - useEffect(() => { - const options = { - type_name: 'BrushSaltedBicycleNetwork', - latlon: true, - }; - if (showBrushSaltedRoute) { - fetchMobilityMapData(options, setBrushSaltedRoutes); - } - }, [showBrushSaltedRoute]); - const map = useMap(); + const { data: brushSaltedRoutes } = useMobilityDataFetch(optionsBrushSalted, showBrushSaltedRoute); + const { data: brushSandedRoutes } = useMobilityDataFetch(optionsBrushSanded, showBrushSandedRoute); const renderBrushSandedData = isDataValid(showBrushSandedRoute, brushSandedRoutes); const renderBrushSaltedData = isDataValid(showBrushSaltedRoute, brushSaltedRoutes); diff --git a/src/components/MobilityPlatform/utils/useMobilityDataFetch.js b/src/components/MobilityPlatform/utils/useMobilityDataFetch.js new file mode 100644 index 000000000..9c4e5f3bc --- /dev/null +++ b/src/components/MobilityPlatform/utils/useMobilityDataFetch.js @@ -0,0 +1,17 @@ +import { useState, useEffect } from 'react'; +import { fetchMobilityMapData } from '../mobilityPlatformRequests/mobilityPlatformRequests'; + +const useMobilityDataFetch = (options, showData, embedded) => { + const [data, setData] = useState([]); + + useEffect(() => { + if (showData || embedded) { + fetchMobilityMapData(options, setData); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [showData, embedded]); + + return { data }; +}; + +export default useMobilityDataFetch; diff --git a/src/views/MobilitySettingsView/MobilitySettingsView.js b/src/views/MobilitySettingsView/MobilitySettingsView.js index aea79297d..5046655d1 100644 --- a/src/views/MobilitySettingsView/MobilitySettingsView.js +++ b/src/views/MobilitySettingsView/MobilitySettingsView.js @@ -23,9 +23,10 @@ import { fetchCultureRouteNames, fetchMobilityMapData, } from '../../components/MobilityPlatform/mobilityPlatformRequests/mobilityPlatformRequests'; +import useMobilityDataFetch from '../../components/MobilityPlatform/utils/useMobilityDataFetch'; import useLocaleText from '../../utils/useLocaleText'; -import TitleBar from '../../components/TitleBar'; import { useMobilityPlatformContext } from '../../context/MobilityPlatformContext'; +import TitleBar from '../../components/TitleBar'; import CityBikeInfo from './components/CityBikeInfo'; import EmptyRouteList from './components/EmptyRouteList'; import ExtendedInfo from './components/ExtendedInfo'; @@ -59,11 +60,8 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { const [openScooterProviderList, setOpenScooterProviderList] = useState(false); const [openStreetMaintenanceSelectionList, setOpenStreetMaintenanceSelectionList] = useState(false); const [openMarkedTrailsList, setOpenMarkedTrailsList] = useState(false); - const [markedTrailsList, setMarkedTrailsList] = useState([]); const [openNatureTrailsList, setOpenNatureTrailsList] = useState(false); - const [natureTrailsList, setNatureTrailsList] = useState([]); const [openFitnessTrailsList, setOpenFitnessTrailsList] = useState(false); - const [fitnessTrailsList, setFitnessTrailsList] = useState([]); const { setOpenMobilityPlatform, @@ -284,37 +282,26 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { } }, [openCarSettings, setParkingChargeZones]); - useEffect(() => { - const options = { - type_name: 'PaavonPolku', - latlon: true, - }; - if (openWalkSettings) { - fetchMobilityMapData(options, setMarkedTrailsList); - } - }, [openWalkSettings]); + const optionsPaavoTrails = { + type_name: 'PaavonPolku', + latlon: true, + }; - useEffect(() => { - const options = { - type_name: 'NatureTrail', - page_size: 200, - latlon: true, - }; - if (openWalkSettings) { - fetchMobilityMapData(options, setNatureTrailsList); - } - }, [openWalkSettings]); + const optionsNaturetTrails = { + type_name: 'NatureTrail', + page_size: 200, + latlon: true, + }; - useEffect(() => { - const options = { - type_name: 'FitnessTrail', - page_size: 200, - latlon: true, - }; - if (openWalkSettings) { - fetchMobilityMapData(options, setFitnessTrailsList); - } - }, [openWalkSettings]); + const optionsFitnessTrails = { + type_name: 'FitnessTrail', + page_size: 200, + latlon: true, + }; + + const { data: markedTrailsList } = useMobilityDataFetch(optionsPaavoTrails, openWalkSettings); + const { data: natureTrailsList } = useMobilityDataFetch(optionsNaturetTrails, openWalkSettings); + const { data: fitnessTrailsList } = useMobilityDataFetch(optionsFitnessTrails, openWalkSettings); /** If direct link is used to navigate, open correct content view * @param {string} pathname From 30736be332cbc2b583f1ca2de62c18df2b2b4830 Mon Sep 17 00:00:00 2001 From: juhomakkonen <69158965+juhomakkonen@users.noreply.github.com> Date: Thu, 11 Apr 2024 14:08:33 +0300 Subject: [PATCH 15/15] Feature/barbecue places (#234) * Show barbecue places on the map * Use correct icons * Render info about barbecue places * Use new hook to fetch data of barbecue places * Add info text & update translations --- .../BarbecuePlaces/BarbecuePlaces.js | 46 ++++++++++++++++ .../BarbecuePlacesContent.js | 52 +++++++++++++++++++ .../components/BarbecuePlacesContent/index.js | 3 ++ .../MobilityPlatform/BarbecuePlaces/index.js | 3 ++ src/context/MobilityPlatformContext.js | 3 ++ src/i18n/en.js | 3 ++ src/i18n/fi.js | 3 ++ src/i18n/sv.js | 5 +- .../MobilityPlatformMapView.js | 2 + .../MobilitySettingsView.js | 28 +++++++++- 10 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 src/components/MobilityPlatform/BarbecuePlaces/BarbecuePlaces.js create mode 100644 src/components/MobilityPlatform/BarbecuePlaces/components/BarbecuePlacesContent/BarbecuePlacesContent.js create mode 100644 src/components/MobilityPlatform/BarbecuePlaces/components/BarbecuePlacesContent/index.js create mode 100644 src/components/MobilityPlatform/BarbecuePlaces/index.js diff --git a/src/components/MobilityPlatform/BarbecuePlaces/BarbecuePlaces.js b/src/components/MobilityPlatform/BarbecuePlaces/BarbecuePlaces.js new file mode 100644 index 000000000..5ee035823 --- /dev/null +++ b/src/components/MobilityPlatform/BarbecuePlaces/BarbecuePlaces.js @@ -0,0 +1,46 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import React, { useEffect } from 'react'; +import { useMap } from 'react-leaflet'; +import { useSelector } from 'react-redux'; +import barbecuePlaceIconBw from 'servicemap-ui-turku/assets/icons/contrast/icons-icon_barbecue_place-bw.svg'; +import barbecuePlaceIcon from 'servicemap-ui-turku/assets/icons/icons-icon_barbecue_place.svg'; +import { useMobilityPlatformContext } from '../../../context/MobilityPlatformContext'; +import { useAccessibleMap } from '../../../redux/selectors/settings'; +import useMobilityDataFetch from '../utils/useMobilityDataFetch'; +import { createIcon, isDataValid, fitToMapBounds } from '../utils/utils'; +import MarkerComponent from '../MarkerComponent'; +import BarbecuePlacesContent from './components/BarbecuePlacesContent'; + +const BarbecuePlaces = () => { + const options = { + type_name: 'BarbecuePlace', + }; + + const { showBarbecuePlaces } = useMobilityPlatformContext(); + + const map = useMap(); + + const useContrast = useSelector(useAccessibleMap); + + const { icon } = global.L; + + const customIcon = icon(createIcon(useContrast ? barbecuePlaceIconBw : barbecuePlaceIcon)); + + const { data } = useMobilityDataFetch(options, showBarbecuePlaces); + const renderData = isDataValid(showBarbecuePlaces, data); + + useEffect(() => { + fitToMapBounds(renderData, data, map); + }, [showBarbecuePlaces, data]); + + return (renderData + ? data.map(item => ( + + + + )) + : null + ); +}; + +export default BarbecuePlaces; diff --git a/src/components/MobilityPlatform/BarbecuePlaces/components/BarbecuePlacesContent/BarbecuePlacesContent.js b/src/components/MobilityPlatform/BarbecuePlaces/components/BarbecuePlacesContent/BarbecuePlacesContent.js new file mode 100644 index 000000000..e71f1d8d6 --- /dev/null +++ b/src/components/MobilityPlatform/BarbecuePlaces/components/BarbecuePlacesContent/BarbecuePlacesContent.js @@ -0,0 +1,52 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import { Typography } from '@mui/material'; +import { useIntl } from 'react-intl'; +import styled from '@emotion/styled'; + +const BarbecuePlacesContent = ({ item }) => { + const intl = useIntl(); + return ( + + + + {intl.formatMessage({ id: 'mobilityPlatform.content.barbecuePlace.title' })} + + + + + {`${item.extra.malli.trim()} (${item.extra.valmistaja})`} + + + + ); +}; + +const StyledContainer = styled.div(({ theme }) => ({ + margin: theme.spacing(1), +})); + +const StyledHeader = styled.div(({ theme }) => ({ + width: '85%', + borderBottom: '1px solid #000', + paddingBottom: theme.spacing(0.5), +})); + +const StyledText = styled.div(({ theme }) => ({ + marginTop: theme.spacing(0.5), +})); + +BarbecuePlacesContent.propTypes = { + item: PropTypes.shape({ + extra: PropTypes.shape({ + malli: PropTypes.string, + valmistaja: PropTypes.string, + }), + }), +}; + +BarbecuePlacesContent.defaultProps = { + item: {}, +}; + +export default BarbecuePlacesContent; diff --git a/src/components/MobilityPlatform/BarbecuePlaces/components/BarbecuePlacesContent/index.js b/src/components/MobilityPlatform/BarbecuePlaces/components/BarbecuePlacesContent/index.js new file mode 100644 index 000000000..93d6a21f8 --- /dev/null +++ b/src/components/MobilityPlatform/BarbecuePlaces/components/BarbecuePlacesContent/index.js @@ -0,0 +1,3 @@ +import BarbecuePlacesContent from './BarbecuePlacesContent'; + +export default BarbecuePlacesContent; diff --git a/src/components/MobilityPlatform/BarbecuePlaces/index.js b/src/components/MobilityPlatform/BarbecuePlaces/index.js new file mode 100644 index 000000000..ac19a0091 --- /dev/null +++ b/src/components/MobilityPlatform/BarbecuePlaces/index.js @@ -0,0 +1,3 @@ +import BarbecuePlaces from './BarbecuePlaces'; + +export default BarbecuePlaces; diff --git a/src/context/MobilityPlatformContext.js b/src/context/MobilityPlatformContext.js index aa9de8073..2c1713146 100644 --- a/src/context/MobilityPlatformContext.js +++ b/src/context/MobilityPlatformContext.js @@ -103,6 +103,7 @@ const MobilityPlatformContextProvider = ({ children }) => { const [showUnderpasses, setShowUnderpasses] = useState(false); const [showPublicBenches, setShowPublicBenches] = useState(false); const [showRoadworks, setShowRoadworks] = useState(false); + const [showBarbecuePlaces, setShowBarbecuePlaces] = useState(false); const getters = { openMobilityPlatform, @@ -172,6 +173,7 @@ const MobilityPlatformContextProvider = ({ children }) => { showUnderpasses, showPublicBenches, showRoadworks, + showBarbecuePlaces, }; const setters = { @@ -242,6 +244,7 @@ const MobilityPlatformContextProvider = ({ children }) => { setShowOverpasses, setShowPublicBenches, setShowRoadworks, + setShowBarbecuePlaces, }; const contextValues = { ...getters, ...setters }; diff --git a/src/i18n/en.js b/src/i18n/en.js index 4717311c5..3efcd5543 100644 --- a/src/i18n/en.js +++ b/src/i18n/en.js @@ -802,6 +802,7 @@ const translations = { 'mobilityPlatform.menu.show.railwayStations': 'Railway stations', 'mobilityPlatform.menu.show.airMonitoring': 'Air quality stations', 'mobilityPlatform.menu.show.parkAndRideBikes': 'Park and ride stops for bicycles', + 'mobilityPlatform.menu.show.barbecuePlaces': 'Sites for barbequing & making fire', // Content 'mobilityPlatform.content.general.provider': 'Service provider: {value}', @@ -908,6 +909,7 @@ const translations = { 'mobilityPlatform.content.departingTrains.empty': 'No departing trains', 'mobilityPlatform.content.arrivingTrains.empty': 'No incoming trains', 'mobilityPlatform.parkAndRide.content.subtitle': 'Park and ride stop for bicycles', + 'mobilityPlatform.content.barbecuePlace.title': 'Site for barbequing or making fire', // Info text 'mobilityPlatform.info.description.title': 'Route description', @@ -968,6 +970,7 @@ const translations = { 'mobilityPlatform.info.airMonitoring.paragraph.3': 'Air quality data for the service map is obtained from the Turku region air protection co-operative group.', 'mobilityPlatform.info.airMonitoring.link': 'For more information visit https://en.ilmatieteenlaitos.fi/air-quality', 'mobilityPlatform.info.parkAndRideBicycles': 'Park-and-ride arrangements provide the opportunity to leave your bicycle parked safely and hop on a bus to continue your journey. The Föli area boasts many park-and-ride sites for bicycles. Park-and-ride parking is free and intended for those using public transport for connections.', + 'mobilityPlatform.info.barbecuePlaces': 'The map shows official sites for barbequing or making fire. Making a fire on the land administered by Turku City is allowed only on places designated for making an open flame. Making a fire on any other place than the official campfire and barbeque sites is always forbidden.', // 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 83a5c7a15..c80337b96 100644 --- a/src/i18n/fi.js +++ b/src/i18n/fi.js @@ -807,6 +807,7 @@ const translations = { 'mobilityPlatform.menu.show.railwayStations': 'Rautatieasemat', 'mobilityPlatform.menu.show.airMonitoring': 'Ilmanlaadun mittauspisteet', 'mobilityPlatform.menu.show.parkAndRideBikes': 'Liityntäpysäkit pyörille', + 'mobilityPlatform.menu.show.barbecuePlaces': 'Grillaus- ja tulentekopaikat', // Content 'mobilityPlatform.content.general.provider': 'Palveluntarjoaja: {value}', @@ -903,6 +904,7 @@ const translations = { 'mobilityPlatform.content.departingTrains.empty': 'Ei lähteviä junia', 'mobilityPlatform.content.arrivingTrains.empty': 'Ei saapuvia junia', 'mobilityPlatform.parkAndRide.content.subtitle': 'Liityntäpysäkki pyörille', + 'mobilityPlatform.content.barbecuePlace.title': 'Grillaus- ja tulentekopaikka', // Info text 'mobilityPlatform.info.description.title': 'Tietoja reitistä', @@ -963,6 +965,7 @@ const translations = { 'mobilityPlatform.info.airMonitoring.paragraph.3': 'Palvelukartan ilmanlaatutiedot saadaan Turun seudun ilmansuojelun yhteistyöryhmältä.', 'mobilityPlatform.info.airMonitoring.link': 'Lisätietoja saa osoitteesta https://www.ilmatieteenlaitos.fi/ilmanlaatu', 'mobilityPlatform.info.parkAndRideBicycles': 'Liityntäpysäköinti tarjoaa mahdollisuuden jättää oma pyörä parkkiin ja jatkaa matkaa bussilla. Föli-alueella on useita liityntäpysäköintipaikkoja pyörille. Liityntäpysäköinti on maksutonta ja tarkoitettu vain joukkoliikennettä vaihtoyhteytenä käyttäville.', + 'mobilityPlatform.info.barbecuePlaces': 'Kartalla näkyvät Turun viralliset tulenteko- ja grillauspaikat. Turun kaupungin hallinnoimilla mailla tulenteko on sallittu ainoastaan avotulen tekoon tarkoitetuilla paikoilla. Muilla kuin virallisilla nuotio- ja grillauspaikoilla avotulen teko on aina kielletty.', // 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 eb942f088..44f034757 100644 --- a/src/i18n/sv.js +++ b/src/i18n/sv.js @@ -806,6 +806,7 @@ const translations = { 'mobilityPlatform.menu.show.roadworks': 'Vägarbeten', 'mobilityPlatform.menu.show.railwayStations': 'Järnvägsstationer', 'mobilityPlatform.menu.show.parkAndRideBikes': 'Infartsparkering för cyklar', + 'mobilityPlatform.menu.show.barbecuePlaces': 'Grill- och eldningsplatser', // Content 'mobilityPlatform.content.general.provider': 'Tjänsteleverantör: {value}', @@ -912,6 +913,7 @@ const translations = { 'mobilityPlatform.content.departingTrains.empty': 'Inga avgående tåg', 'mobilityPlatform.content.arrivingTrains.empty': 'Inga inkommande tåg', 'mobilityPlatform.parkAndRide.content.subtitle': 'Infartspark for cyklarna', + 'mobilityPlatform.content.barbecuePlace.title': 'Grill- och eldningplats', // Info text 'mobilityPlatform.info.description.title': 'Beskrivning av rutten', @@ -959,7 +961,7 @@ const translations = { 'mobilityPlatform.info.parkingMachines': 'Kartan visar de parkeringsautomater som ägs av Åbo stad. Bankkort och kontaktlös betalning används som betalningsmetoder i alla automater. Du kan se mer information om maskinen genom att trycka på ikonen på kartan.', 'mobilityPlatform.info.publicParkingSpaces': 'Allmänna parkeringsplatser visas på kartan. Det finns dock ingen information om deras utnyttjandegrad. Mer detaljerad information om det valda parkeringsområdet kan läsas genom att trycka på området på kartan.', 'mobilityPlatform.info.outdoorGymDevices': 'Åbo stad upprätthåller utomhusgym. De erbjuder ett roligt sätt att träna medan du njuter av den friska luften. På de utomhusgym som finns runt om i staden kan du träna hela kroppen. Overhead-remskivor, benpressar, armhävningar, surf- och promenadutrustning passar alla. Fler utmaningar erbjuds av armhävningsräcket, dip, magplanka och ryggbänk.', - 'mobilityPlatform.info.crosswalks': 'Kartan visar placeringen av övergångsställen inne i Åbo stad. Zooma in på kartan för att se övergångsställen.', + 'mobilityPlatform.info.crosswalks': 'Kartan visar placeringarna av övergångsställen inne i Åbo stad. Zooma in på kartan för att se övergångsställen.', 'mobilityPlatform.info.short.crosswalks': 'Kartan visar övergångsställen inne i Åbo.', 'mobilityPlatform.info.busStops': 'Kartan visar Åboregionens kollektivtrafiks, Fölis, busshållplatser. Om du kilckar på ikonen kan du se nästa buss som går från hållplatsen. Zooma in på kartan för att se hållplatserna. Datan kommer från gränssnittet som underhålls av Digitransit.', 'mobilityPlatform.info.underAndOverpasses': 'Kartan visar gångtunnlar och gångbroar som ligger i Åbo stadsområdet.', @@ -972,6 +974,7 @@ const translations = { 'mobilityPlatform.info.airMonitoring.paragraph.3': 'Luftkvalitetsdata för Servicekartan erhålls från Åboregionens samarbetsgrupp för luftskydd.', 'mobilityPlatform.info.airMonitoring.link': 'För mer information besök: https://sv.ilmatieteenlaitos.fi/luftkvalitet', 'mobilityPlatform.info.parkAndRideBicycles': 'Infartsparkeringen erbjuder möjlighet att lämna din egen cykel på parkeringsplatsen och fortsätta resan med buss. Inom Föli-området finns flera infartsparkeringsplatser för cyklar. Infartsparkeringen är gratis och endast avsedd för dem som fortsätter sin resa med kollektivtrafik.', + 'mobilityPlatform.info.barbecuePlaces': 'Kartan visar eldnings- och grillplatser i Åbo. Det är tillåtet att göra upp öppen eld endast vid särskilt avsedda eldningsplatser. Att göra upp eld på andra platser är förbjudet.', // 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 918e8c499..d445d8948 100644 --- a/src/views/MobilityPlatformMapView/MobilityPlatformMapView.js +++ b/src/views/MobilityPlatformMapView/MobilityPlatformMapView.js @@ -38,6 +38,7 @@ import Roadworks from '../../components/MobilityPlatform/Roadworks'; import RailwayStations from '../../components/MobilityPlatform/RailwayStations'; import AirMonitoring from '../../components/MobilityPlatform/EnvironmentObservations/AirMonitoring'; import ParkAndRideBikes from '../../components/MobilityPlatform/ParkAndRideStops/ParkAndRideBikes'; +import BarbecuePlaces from '../../components/MobilityPlatform/BarbecuePlaces'; const MobilityPlatformMapView = ({ mapObject }) => ( <> @@ -79,6 +80,7 @@ const MobilityPlatformMapView = ({ mapObject }) => ( + ); diff --git a/src/views/MobilitySettingsView/MobilitySettingsView.js b/src/views/MobilitySettingsView/MobilitySettingsView.js index 5046655d1..1965cc769 100644 --- a/src/views/MobilitySettingsView/MobilitySettingsView.js +++ b/src/views/MobilitySettingsView/MobilitySettingsView.js @@ -172,6 +172,8 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { setShowAirMonitoringStations, showParkAndRideBikes, setShowParkAndRideBikes, + showBarbecuePlaces, + setShowBarbecuePlaces, } = useMobilityPlatformContext(); const locale = useSelector(state => state.user.locale); @@ -346,7 +348,16 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { checkVisibilityValues(showUnderpasses, setOpenWalkSettings); checkVisibilityValues(showOverpasses, setOpenWalkSettings); checkVisibilityValues(showPublicBenches, setOpenWalkSettings); - }, [showPublicToilets, showOutdoorGymDevices, showCrossWalks, showUnderpasses, showOverpasses, showPublicBenches]); + checkVisibilityValues(showBarbecuePlaces, setOpenWalkSettings); + }, [ + showPublicToilets, + showOutdoorGymDevices, + showCrossWalks, + showUnderpasses, + showOverpasses, + showPublicBenches, + showBarbecuePlaces, + ]); useEffect(() => { checkVisibilityValues(showTrafficCounter.walking, setOpenWalkSettings); @@ -842,6 +853,10 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { setShowParkAndRideBikes(current => !current); }; + const barbecuePlacesToggle = () => { + setShowBarbecuePlaces(current => !current); + }; + const cultureRouteListToggle = () => { setOpenCultureRouteList(current => !current); if (cultureRouteId) { @@ -1175,6 +1190,12 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { checkedValue: showPublicBenches, onChangeValue: publicBenchesToggle, }, + { + type: 'barbecuePlaces', + msgId: 'mobilityPlatform.menu.show.barbecuePlaces', + checkedValue: showBarbecuePlaces, + onChangeValue: barbecuePlacesToggle, + }, { type: 'cultureRoutes', msgId: 'mobilityPlatform.menu.showCultureRoutes', @@ -1594,6 +1615,11 @@ const MobilitySettingsView = ({ classes, intl, navigator }) => { type: 'publicBenchesInfo', component: , }, + { + visible: showBarbecuePlaces, + type: 'barbecuePlacesInfo', + component: , + }, { visible: openMarkedTrailsList, type: 'markedTrailsListInfo',