From 6b79a8a44b02807fb380a82fdf5f9733aa2bd26b Mon Sep 17 00:00:00 2001 From: JF-Cozy Date: Wed, 2 Nov 2022 11:47:47 +0100 Subject: [PATCH 01/16] fix: Help tooltip placement when editing a recurrence --- .../RecurrenceEditDialog/RecurrenceEditDialogTitle.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/EditDialogs/RecurrenceEditDialog/RecurrenceEditDialogTitle.jsx b/src/components/EditDialogs/RecurrenceEditDialog/RecurrenceEditDialogTitle.jsx index eb6cf603..da6d9907 100644 --- a/src/components/EditDialogs/RecurrenceEditDialog/RecurrenceEditDialogTitle.jsx +++ b/src/components/EditDialogs/RecurrenceEditDialog/RecurrenceEditDialogTitle.jsx @@ -63,7 +63,7 @@ const RecurrenceEditDialogTitle = () => { return (
{t('recurring.title')} - + From af43fdb3513a3a961cb09c030bcd64b9678a5f7c Mon Sep 17 00:00:00 2001 From: JF-Cozy Date: Wed, 2 Nov 2022 16:44:30 +0100 Subject: [PATCH 02/16] chore: Add ts and tsx file to jest config and lint --- jest.config.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jest.config.js b/jest.config.js index 08353ff8..b8c6371f 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,7 +2,7 @@ process.env.TZ = 'UTC' module.exports = { testURL: 'http://localhost/', - moduleFileExtensions: ['js', 'jsx', 'json', 'styl'], + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'styl'], setupFiles: ['/test/jestLib/setup.js'], setupFilesAfterEnv: ['/test/jestLib/setupAfterEnv.js'], moduleDirectories: ['src', 'node_modules'], diff --git a/package.json b/package.json index 20f05498..c5e7ccb0 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "scripts": { "analyze": "COZY_SCRIPTS_ANALYZER=true yarn build", "lint": "yarn lint:js && yarn lint:styles", - "lint:js": "cs lint '{src,test}/**/*.{js,jsx}'", + "lint:js": "cs lint '{src,test}/**/*.{js,jsx,ts,tsx}'", "lint:styles": "stylint src/styles --config ./.stylintrc", "prebuild": "yarn lint", "build": "yarn build:browser", From 8f5f88686133ed5febde009a19b8ad47063075a7 Mon Sep 17 00:00:00 2001 From: JF-Cozy Date: Wed, 2 Nov 2022 15:40:26 +0100 Subject: [PATCH 03/16] fix(Queries): Use correct bike mode in request to get commute timeseries --- src/queries/queries.js | 4 ++-- src/queries/queries.spec.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/queries/queries.js b/src/queries/queries.js index 53a6492b..152a20df 100644 --- a/src/queries/queries.js +++ b/src/queries/queries.js @@ -11,7 +11,7 @@ import { ACCOUNTS_DOCTYPE, SETTINGS_DOCTYPE } from 'src/doctypes' -import { TRIPS_DISTANCE_SIMILARITY_RATIO } from 'src/constants' +import { TRIPS_DISTANCE_SIMILARITY_RATIO, BICYCLING_MODE } from 'src/constants' const older30s = 30 * 1000 const neverReload = 100000 * 1000 @@ -194,7 +194,7 @@ export const buildOneYearBikeCommuteTimeseriesQueryByDateAndAccountId = ( 'aggregation.purpose': 'COMMUTE', 'aggregation.modes': { $elemMatch: { - $eq: 'BIKE' + $eq: BICYCLING_MODE } } }) diff --git a/src/queries/queries.spec.js b/src/queries/queries.spec.js index f519ab58..448486d4 100644 --- a/src/queries/queries.spec.js +++ b/src/queries/queries.spec.js @@ -145,7 +145,7 @@ describe('buildOneYearBikeCommuteTimeseriesQueryByDateAndAccountId', () => { selector: { 'aggregation.modes': { $elemMatch: { - $eq: 'BIKE' + $eq: 'BICYCLING' } }, 'aggregation.purpose': 'COMMUTE', From 3708301d5a07be132b82668e84f40bc019948f55 Mon Sep 17 00:00:00 2001 From: JF-Cozy Date: Thu, 3 Nov 2022 10:26:05 +0100 Subject: [PATCH 04/16] feat(Queries): Change approach to get bike commute timeseries Because of the way couch works, using elemMatch means that we don't rely on the index and so we browse the whole database. This is suboptimal. But it is a necessary discriminant. Another point, we delete the date selector here. This made sense if we had optimal queries, and then execute a query by year, when the year changes on the front. Finally, to know the dates to select, we have to go through all the trips anyway. With Paul we decided to make only one query and then to sort the data on the front. --- src/queries/queries.js | 32 ++++++++------------------------ src/queries/queries.spec.js | 24 +++++------------------- 2 files changed, 13 insertions(+), 43 deletions(-) diff --git a/src/queries/queries.js b/src/queries/queries.js index 152a20df..60913bd3 100644 --- a/src/queries/queries.js +++ b/src/queries/queries.js @@ -1,8 +1,6 @@ import endOfMonth from 'date-fns/endOfMonth' import startOfMonth from 'date-fns/startOfMonth' import subYears from 'date-fns/subYears' -import startOfYear from 'date-fns/startOfYear' -import endOfYear from 'date-fns/endOfYear' import CozyClient, { Q } from 'cozy-client' @@ -176,31 +174,22 @@ export const buildOneYearOldTimeseriesWithAggregationByAccountId = } } -export const buildOneYearBikeCommuteTimeseriesQueryByDateAndAccountId = ( - { date, accountId }, +export const buildBikeCommuteTimeseriesQueryByAccountId = ( + { accountId }, enabled ) => { - const startYearDate = startOfYear(date) - const endYearDate = endOfYear(date) - return { definition: Q('io.cozy.timeseries.geojson') .where({ 'cozyMetadata.sourceAccount': accountId, - startDate: { - $gte: startYearDate.toISOString(), - $lte: endYearDate.toISOString() - }, + startDate: { $gt: null }, 'aggregation.purpose': 'COMMUTE', - 'aggregation.modes': { - $elemMatch: { - $eq: BICYCLING_MODE - } - } + 'aggregation.modes': { $elemMatch: { $eq: BICYCLING_MODE } } }) .select([ 'startDate', 'endDate', + 'aggregation', 'aggregation.modes', 'aggregation.purpose', 'cozyMetadata.sourceAccount' @@ -211,15 +200,10 @@ export const buildOneYearBikeCommuteTimeseriesQueryByDateAndAccountId = ( 'aggregation.purpose', 'aggregation.modes' ]) - .sortBy([ - { 'cozyMetadata.sourceAccount': 'desc' }, - { startDate: 'desc' }, - { 'aggregation.purpose': 'desc' }, - { 'aggregation.modes': 'desc' } - ]) - .limitBy(50), + .sortBy([{ 'cozyMetadata.sourceAccount': 'desc' }, { startDate: 'desc' }]) + .limitBy(1000), options: { - as: `${GEOJSON_DOCTYPE}/sourceAccount/${accountId}/OneYearBikeCommute/year/${startYearDate.getFullYear()}`, + as: `${GEOJSON_DOCTYPE}/sourceAccount/${accountId}/BikeCommute/`, fetchPolicy: CozyClient.fetchPolicies.olderThan(neverReload), enabled } diff --git a/src/queries/queries.spec.js b/src/queries/queries.spec.js index 448486d4..06852d54 100644 --- a/src/queries/queries.spec.js +++ b/src/queries/queries.spec.js @@ -4,7 +4,7 @@ import { buildTimeseriesQueryByDateAndAccountId, buildOneYearOldTimeseriesWithAggregationByAccountId, builTimeserieQueryByAccountIdAndStartPlaceAndEndPlaceAndStartDateAndDistance, - buildOneYearBikeCommuteTimeseriesQueryByDateAndAccountId + buildBikeCommuteTimeseriesQueryByAccountId } from './queries' describe('buildTimeseriesQueryByDateAndAccountId', () => { @@ -122,21 +122,10 @@ describe('builTimeserieQueryByAccountIdAndStartPlaceAndEndPlaceAndStartDateAndDi }) }) -describe('buildOneYearBikeCommuteTimeseriesQueryByDateAndAccountId', () => { - beforeEach(() => { - MockDate.set('2020-01-01') - }) - - afterEach(() => { - MockDate.reset() - }) - +describe('buildBikeCommuteTimeseriesQueryByAccountId', () => { it('should use a well formated selector', () => { - const query = buildOneYearBikeCommuteTimeseriesQueryByDateAndAccountId( - { - date: new Date(), - accountId: 'accountId' - }, + const query = buildBikeCommuteTimeseriesQueryByAccountId( + { accountId: 'accountId' }, false ) @@ -150,10 +139,7 @@ describe('buildOneYearBikeCommuteTimeseriesQueryByDateAndAccountId', () => { }, 'aggregation.purpose': 'COMMUTE', 'cozyMetadata.sourceAccount': 'accountId', - startDate: { - $gte: '2020-01-01T00:00:00.000Z', - $lte: '2020-12-31T23:59:59.999Z' - } + startDate: { $gt: null } } } }) From bb4d08220e112f3a057ac94173ecac98184a1fdc Mon Sep 17 00:00:00 2001 From: JF-Cozy Date: Thu, 3 Nov 2022 10:17:26 +0100 Subject: [PATCH 05/16] feat(BikeGoalDateSelector): Relies on year in url, not on date from provider --- .../BikeGoal/DateSelector/ActionButton.jsx | 2 +- .../DateSelector/BikeGoalDateSelector.jsx | 32 +++++++------------ .../BikeGoal/DateSelector/helpers.spec.js | 15 +++++++++ .../Goals/BikeGoal/DateSelector/helpers.ts | 27 ++++++++++++++++ 4 files changed, 54 insertions(+), 22 deletions(-) create mode 100644 src/components/Goals/BikeGoal/DateSelector/helpers.spec.js create mode 100644 src/components/Goals/BikeGoal/DateSelector/helpers.ts diff --git a/src/components/Goals/BikeGoal/DateSelector/ActionButton.jsx b/src/components/Goals/BikeGoal/DateSelector/ActionButton.jsx index 29bc7637..525535d3 100644 --- a/src/components/Goals/BikeGoal/DateSelector/ActionButton.jsx +++ b/src/components/Goals/BikeGoal/DateSelector/ActionButton.jsx @@ -31,7 +31,7 @@ const ActionButton = forwardRef(({ label, onClick }, ref) => { ActionButton.displayName = 'ActionButton' ActionButton.propTypes = { - label: PropTypes.number, + label: PropTypes.string, onClick: PropTypes.func } diff --git a/src/components/Goals/BikeGoal/DateSelector/BikeGoalDateSelector.jsx b/src/components/Goals/BikeGoal/DateSelector/BikeGoalDateSelector.jsx index 0abd80c0..7b21646c 100644 --- a/src/components/Goals/BikeGoal/DateSelector/BikeGoalDateSelector.jsx +++ b/src/components/Goals/BikeGoal/DateSelector/BikeGoalDateSelector.jsx @@ -1,38 +1,25 @@ import React, { useRef, useState, useMemo } from 'react' import PropTypes from 'prop-types' -import differenceInCalendarYears from 'date-fns/differenceInCalendarYears' -import subYears from 'date-fns/subYears' +import { useNavigate, useParams } from 'react-router-dom' import ActionMenu, { ActionMenuItem } from 'cozy-ui/transpiled/react/ActionMenu' -import { getEarliestTimeserie } from 'src/lib/timeseries' -import { useBikeGoalDateContext } from 'src/components/Providers/BikeGoalDateProvider' import ActionButton from 'src/components/Goals/BikeGoal/DateSelector/ActionButton' +import { makeMenuDates } from 'src/components/Goals/BikeGoal/DateSelector/helpers' const BikeGoalDateSelector = ({ timeseries }) => { - const { date, setDate } = useBikeGoalDateContext() + const { year } = useParams() + const navigate = useNavigate() const actionMenuAnchorRef = useRef() const [showMenu, setShowMenu] = useState(false) - const earliestDate = useMemo( - () => new Date(getEarliestTimeserie(timeseries).startDate), - [timeseries] - ) - const latestDate = useMemo(() => new Date(), []) - const numberOfYears = differenceInCalendarYears(latestDate, earliestDate) - const menuDates = useMemo( - () => - Array.from({ length: numberOfYears + 1 }, (_, index) => { - return subYears(latestDate, index) - }), - [latestDate, numberOfYears] - ) + const menuDates = useMemo(() => makeMenuDates(timeseries), [timeseries]) return ( <> setShowMenu(v => !v)} /> {showMenu && ( @@ -43,8 +30,11 @@ const BikeGoalDateSelector = ({ timeseries }) => { popperOptions={{ placement: 'bottom-end' }} > {menuDates.map((menuDate, index) => ( - setDate(menuDate)}> - {menuDate.getFullYear()} + navigate(`/bikegoal/${menuDate}/trips`)} + > + {menuDate} ))} diff --git a/src/components/Goals/BikeGoal/DateSelector/helpers.spec.js b/src/components/Goals/BikeGoal/DateSelector/helpers.spec.js new file mode 100644 index 00000000..3137feb1 --- /dev/null +++ b/src/components/Goals/BikeGoal/DateSelector/helpers.spec.js @@ -0,0 +1,15 @@ +import { makeMenuDates } from './helpers' + +describe('makeMenuDates', () => { + it('should return an array of years', () => { + const timeseries = [ + { startDate: '2022-01-01T00:00:00', endDate: '2022-01-01T00:00:00' }, + { startDate: '2020-01-01T00:00:00', endDate: '2020-01-01T00:00:00' }, + { startDate: '2021-01-01T00:00:00', endDate: '2021-01-01T00:00:00' } + ] + + const res = makeMenuDates(timeseries) + + expect(res).toStrictEqual(['2022', '2021', '2020']) + }) +}) diff --git a/src/components/Goals/BikeGoal/DateSelector/helpers.ts b/src/components/Goals/BikeGoal/DateSelector/helpers.ts new file mode 100644 index 00000000..649305ab --- /dev/null +++ b/src/components/Goals/BikeGoal/DateSelector/helpers.ts @@ -0,0 +1,27 @@ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ + +// TODO: remove eslint-disable by convert 'src/lib/timeseries' in TS +// or at least getEarliestTimeserie + +import type { TimeSeries } from 'cozy-client/types/models/timeseries' + +import differenceInCalendarYears from 'date-fns/differenceInCalendarYears' +import subYears from 'date-fns/subYears' + +import { getEarliestTimeserie } from 'src/lib/timeseries' + +export const makeMenuDates = (timeseries: Array): Array => { + const latestDate = new Date() + if (!timeseries || timeseries.length === 0) { + return [latestDate.getFullYear().toString()] + } + + const earliestDate = new Date(getEarliestTimeserie(timeseries).startDate) + const numberOfYears = differenceInCalendarYears(latestDate, earliestDate) + + return Array.from({ length: numberOfYears + 1 }, (_, index) => { + return subYears(latestDate, index).getFullYear().toString() + }) +} From 5e02c959a85670b1f219e740da81adfb3a16a045 Mon Sep 17 00:00:00 2001 From: JF-Cozy Date: Thu, 3 Nov 2022 12:44:31 +0100 Subject: [PATCH 06/16] feat(BikeGoal): Remove year in certificate generation route since we already have it in the parent route we also relies on year url param and not on date provider --- src/components/AppRouter.jsx | 2 +- src/components/Goals/BikeGoal/BikeGoalAchievement.jsx | 5 ++--- src/components/Goals/BikeGoal/BikeGoalAlertSuccess.jsx | 5 ++--- .../BikeGoal/Certificate/CertificateGenerationContent.jsx | 6 +++--- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/components/AppRouter.jsx b/src/components/AppRouter.jsx index 6dfac1bb..2e6bf1d1 100644 --- a/src/components/AppRouter.jsx +++ b/src/components/AppRouter.jsx @@ -83,7 +83,7 @@ const AppRouter = () => { } /> } /> } /> diff --git a/src/components/Goals/BikeGoal/BikeGoalAchievement.jsx b/src/components/Goals/BikeGoal/BikeGoalAchievement.jsx index bdeedadc..61cf72e7 100644 --- a/src/components/Goals/BikeGoal/BikeGoalAchievement.jsx +++ b/src/components/Goals/BikeGoal/BikeGoalAchievement.jsx @@ -1,5 +1,5 @@ import React from 'react' -import { useNavigate, useParams } from 'react-router-dom' +import { useNavigate } from 'react-router-dom' import PropTypes from 'prop-types' import { useI18n } from 'cozy-ui/transpiled/react/I18n' @@ -19,11 +19,10 @@ import { const BikeGoalAchievement = ({ className, timeseries }) => { const { t } = useI18n() const { isMobile } = useBreakpoints() - const { year } = useParams() const navigate = useNavigate() const handleClickAchievement = () => { - navigate(`certificate/generate/${year}`) + navigate('certificate/generate') } return ( diff --git a/src/components/Goals/BikeGoal/BikeGoalAlertSuccess.jsx b/src/components/Goals/BikeGoal/BikeGoalAlertSuccess.jsx index 7490062c..89d285d4 100644 --- a/src/components/Goals/BikeGoal/BikeGoalAlertSuccess.jsx +++ b/src/components/Goals/BikeGoal/BikeGoalAlertSuccess.jsx @@ -6,18 +6,17 @@ import Alert from 'cozy-ui/transpiled/react/Alert' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { getBountyAmount } from 'src/components/Goals/BikeGoal/helpers' -import { useBikeGoalDateContext } from 'src/components/Providers/BikeGoalDateProvider' import useSettings from 'src/hooks/useSettings' const BikeGoalAlertSuccess = () => { const { t } = useI18n() const navigate = useNavigate() - const { date } = useBikeGoalDateContext() const { save: setShowAlertSuccess } = useSettings('bikeGoal.showAlertSuccess') const handleConfirm = () => { setShowAlertSuccess(false) - navigate(`certificate/generate/${date.getFullYear()}`) + const currentYear = new Date().getFullYear().toString() + navigate(`/bikegoal/${currentYear}/trips/certificate/generate`) } return ( diff --git a/src/components/Goals/BikeGoal/Certificate/CertificateGenerationContent.jsx b/src/components/Goals/BikeGoal/Certificate/CertificateGenerationContent.jsx index 03da6da3..78203020 100644 --- a/src/components/Goals/BikeGoal/Certificate/CertificateGenerationContent.jsx +++ b/src/components/Goals/BikeGoal/Certificate/CertificateGenerationContent.jsx @@ -1,4 +1,5 @@ import React from 'react' +import { useParams } from 'react-router-dom' import Typography from 'cozy-ui/transpiled/react/Typography' import { useI18n } from 'cozy-ui/transpiled/react/I18n' @@ -14,14 +15,13 @@ import FileTypePdfIcon from 'cozy-ui/transpiled/react/Icons/FileTypePdf' import BikeGoalChart from 'src/components/Goals/BikeGoal/BikeGoalChart' import { getBountyAmount } from 'src/components/Goals/BikeGoal/helpers' -import { useBikeGoalDateContext } from 'src/components/Providers/BikeGoalDateProvider' import styles from 'src/components/Goals/BikeGoal/Certificate/CertificateGeneration.styl' const CertificateGenerationContent = () => { const { t } = useI18n() const { isMobile } = useBreakpoints() - const { date } = useBikeGoalDateContext() + const { year } = useParams() return (
@@ -32,7 +32,7 @@ const CertificateGenerationContent = () => { {t('bikeGoal.certificateGeneration.content', { bountyAmount: getBountyAmount(), - year: date.getFullYear() + year })} From a9d44856649623e2556096844404846de74ca5d2 Mon Sep 17 00:00:00 2001 From: JF-Cozy Date: Thu, 3 Nov 2022 10:25:01 +0100 Subject: [PATCH 07/16] feat(BikeGoal): Remove useless Date provider we now rely on url to get the year --- src/components/AppProviders.jsx | 13 +++---- .../Providers/BikeGoalDateProvider.jsx | 34 ------------------- 2 files changed, 5 insertions(+), 42 deletions(-) delete mode 100644 src/components/Providers/BikeGoalDateProvider.jsx diff --git a/src/components/AppProviders.jsx b/src/components/AppProviders.jsx index 9287ea1d..4945ed2c 100644 --- a/src/components/AppProviders.jsx +++ b/src/components/AppProviders.jsx @@ -13,7 +13,6 @@ import { BreakpointsProvider } from 'cozy-ui/transpiled/react/hooks/useBreakpoin import { GEOJSON_DOCTYPE, SETTINGS_DOCTYPE } from 'src/doctypes' import AccountProvider from 'src/components/Providers/AccountProvider' import SelectDatesProvider from 'src/components/Providers/SelectDatesProvider' -import BikeGoalDateProvider from 'src/components/Providers/BikeGoalDateProvider' /* With MUI V4, it is possible to generate deterministic class names. @@ -35,13 +34,11 @@ const AppProviders = ({ client, lang, polyglot, children }) => { - - - - {children} - - - + + + {children} + + diff --git a/src/components/Providers/BikeGoalDateProvider.jsx b/src/components/Providers/BikeGoalDateProvider.jsx deleted file mode 100644 index 9a7a704b..00000000 --- a/src/components/Providers/BikeGoalDateProvider.jsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, { createContext, useState, useContext, useMemo } from 'react' - -export const BikeGoalDateContext = createContext() - -export const useBikeGoalDateContext = () => { - const context = useContext(BikeGoalDateContext) - - if (!context) { - throw new Error( - 'useBikeGoalDateContext must be used within a BikeGoalDateProvider' - ) - } - return context -} - -export const BikeGoalDateProvider = ({ children }) => { - const [date, setDate] = useState(() => new Date()) - - const value = useMemo( - () => ({ - date, - setDate - }), - [date] - ) - - return ( - - {children} - - ) -} - -export default BikeGoalDateProvider From ce369795dfa6925881b80597f078b1b88ee47282 Mon Sep 17 00:00:00 2001 From: JF-Cozy Date: Thu, 3 Nov 2022 11:15:28 +0100 Subject: [PATCH 08/16] fix(AppRouter): Redirection must be children of path="/" route to work --- src/components/AppRouter.jsx | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/components/AppRouter.jsx b/src/components/AppRouter.jsx index 2e6bf1d1..4d528e07 100644 --- a/src/components/AppRouter.jsx +++ b/src/components/AppRouter.jsx @@ -89,21 +89,24 @@ const AppRouter = () => { )} - - {/* // redirection */} - } - /> - {flag('coachco2.bikegoal.enabled') && ( + {/* redirection */} } + path="analysis" + element={} /> - )} - } /> - } /> + {flag('coachco2.bikegoal.enabled') && ( + + } + /> + )} + } /> + } /> + {/* redirection - end */} + ) From 4af060a78cc0612dc12d4a661e36d70c1071ac59 Mon Sep 17 00:00:00 2001 From: JF-Cozy Date: Thu, 3 Nov 2022 11:16:05 +0100 Subject: [PATCH 09/16] refactor: Don't rely on redirection and use real route to navigate to home --- src/components/Goals/BikeGoal/BikeGoalDialogMobile.jsx | 2 +- src/components/Goals/BikeGoal/BikeGoalViewDesktop.jsx | 2 +- .../Goals/BikeGoal/OptionSelector/DeactivationModal.jsx | 2 +- src/components/Views/BikeGoalEdit.jsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Goals/BikeGoal/BikeGoalDialogMobile.jsx b/src/components/Goals/BikeGoal/BikeGoalDialogMobile.jsx index 0f0a682c..9b3c4080 100644 --- a/src/components/Goals/BikeGoal/BikeGoalDialogMobile.jsx +++ b/src/components/Goals/BikeGoal/BikeGoalDialogMobile.jsx @@ -15,7 +15,7 @@ const BikeGoalDialogMobile = ({ timeseries, timeseriesQueryLeft }) => { return ( navigate('/')} + onBack={() => navigate('/trips')} disableGutters content={ <> diff --git a/src/components/Goals/BikeGoal/BikeGoalViewDesktop.jsx b/src/components/Goals/BikeGoal/BikeGoalViewDesktop.jsx index b23da134..b530cfb7 100644 --- a/src/components/Goals/BikeGoal/BikeGoalViewDesktop.jsx +++ b/src/components/Goals/BikeGoal/BikeGoalViewDesktop.jsx @@ -19,7 +19,7 @@ const BikeGoalViewDesktop = ({ timeseries, timeseriesQueryLeft }) => { } - onBack={() => navigate('/')} + onBack={() => navigate('/trips')} /> { const handleDeactivation = () => { setIsBusy(true) save(false) - navigate('/') + navigate('/trips') } return ( diff --git a/src/components/Views/BikeGoalEdit.jsx b/src/components/Views/BikeGoalEdit.jsx index 0223e3bb..867de42f 100644 --- a/src/components/Views/BikeGoalEdit.jsx +++ b/src/components/Views/BikeGoalEdit.jsx @@ -17,7 +17,7 @@ const BikeGoalEdit = () => { title={t('bikeGoal.edit.title')} content={} onBack={() => navigate('..')} - onClose={() => navigate('/')} + onClose={() => navigate('/trips')} /> ) } From 351b91b2cadc6328031af01affd5f653391ff6ae Mon Sep 17 00:00:00 2001 From: JF-Cozy Date: Thu, 3 Nov 2022 12:22:50 +0100 Subject: [PATCH 10/16] fix(BikeGoal): Deal with null or empty timeseries in some helpers isGoalCompleted and countDaysOrDaysToReach --- src/components/Goals/BikeGoal/helpers.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/Goals/BikeGoal/helpers.js b/src/components/Goals/BikeGoal/helpers.js index 178361b5..a586c156 100644 --- a/src/components/Goals/BikeGoal/helpers.js +++ b/src/components/Goals/BikeGoal/helpers.js @@ -18,10 +18,14 @@ export const getBountyAmount = () => { } export const isGoalCompleted = timeseries => { + if (!timeseries || timeseries.length === 0) return false + return countDays(timeseries) >= getDaysToReach() } export const countDaysOrDaysToReach = timeseries => { + if (!timeseries || timeseries.length === 0) return 0 + const days = countDays(timeseries) const daysToReach = getDaysToReach() From ed5a6d87a8ca42ebf9a51eefd5be59d5d88d864d Mon Sep 17 00:00:00 2001 From: JF-Cozy Date: Thu, 3 Nov 2022 12:28:27 +0100 Subject: [PATCH 11/16] feat(BikeGoal): Use correct query to get bike goal trips on bike goal page --- src/components/Views/BikeGoal.jsx | 40 +++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/components/Views/BikeGoal.jsx b/src/components/Views/BikeGoal.jsx index b17ba003..4854f07b 100644 --- a/src/components/Views/BikeGoal.jsx +++ b/src/components/Views/BikeGoal.jsx @@ -1,35 +1,35 @@ import React from 'react' +import { isQueryLoading, useQueryAll } from 'cozy-client' import useBreakpoints from 'cozy-ui/transpiled/react/hooks/useBreakpoints' import { useAccountContext } from 'src/components/Providers/AccountProvider' import SpinnerOrEmptyContent from 'src/components/SpinnerOrEmptyContent' import BikeGoalDialogMobile from 'src/components/Goals/BikeGoal/BikeGoalDialogMobile' import BikeGoalViewDesktop from 'src/components/Goals/BikeGoal/BikeGoalViewDesktop' -import { useTemporaryQueryForBikeGoal } from 'src/components/Goals/useTemporaryQueryForBikeGoal' +import { buildBikeCommuteTimeseriesQueryByAccountId } from 'src/queries/queries' const Bikegoal = () => { const { account, isAccountLoading } = useAccountContext() const { isMobile } = useBreakpoints() - // TODO: uncomment this when the request return something - // - // const { date } = useBikeGoalDateContext() - // const timeseriesQuery = - // buildOneYearBikeCommuteTimeseriesQueryByDateAndAccountId( - // { - // date, - // accountId: account?._id - // }, - // Boolean(account) - // ) - - // TODO: remove this hooks when the above request will work - const { - timeseries, - timeseriesQueryLeft, - isLoadingTimeseriesQuery, - isLoadingOrEmpty - } = useTemporaryQueryForBikeGoal() + + const timeseriesQuery = buildBikeCommuteTimeseriesQueryByAccountId( + { accountId: account?._id }, + Boolean(account) + ) + const { data: timeseries, ...timeseriesQueryLeft } = useQueryAll( + timeseriesQuery.definition, + timeseriesQuery.options + ) + + const isLoadingTimeseriesQuery = + isQueryLoading(timeseriesQueryLeft) || timeseriesQueryLeft.hasMore + + const isLoadingOrEmpty = + isAccountLoading || + !account || + isLoadingTimeseriesQuery || + timeseries.length === 0 if (isLoadingOrEmpty) { return ( From 90c75fb4995e4fe2221a2265d721d867623561d6 Mon Sep 17 00:00:00 2001 From: JF-Cozy Date: Thu, 3 Nov 2022 12:29:15 +0100 Subject: [PATCH 12/16] feat(BikeGoal): Filter bike goal trips according to the year selected --- .../Goals/BikeGoal/BikeGoalDialogMobile.jsx | 12 ++++++---- .../Goals/BikeGoal/BikeGoalViewDesktop.jsx | 12 ++++++---- src/lib/timeseries.js | 6 +++++ src/lib/timeseries.spec.js | 22 ++++++++++++++++++- 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/src/components/Goals/BikeGoal/BikeGoalDialogMobile.jsx b/src/components/Goals/BikeGoal/BikeGoalDialogMobile.jsx index 9b3c4080..0199db14 100644 --- a/src/components/Goals/BikeGoal/BikeGoalDialogMobile.jsx +++ b/src/components/Goals/BikeGoal/BikeGoalDialogMobile.jsx @@ -1,6 +1,6 @@ import React from 'react' import PropTypes from 'prop-types' -import { useNavigate } from 'react-router-dom' +import { useNavigate, useParams } from 'react-router-dom' import { IllustrationDialog } from 'cozy-ui/transpiled/react/CozyDialogs' @@ -8,9 +8,13 @@ import BikeGoalList from 'src/components/Goals/BikeGoal/BikeGoalList' import BikeGoalAchievement from 'src/components/Goals/BikeGoal/BikeGoalAchievement' import BikeGoalChart from 'src/components/Goals/BikeGoal/BikeGoalChart' import BikeGoalActions from 'src/components/Goals/BikeGoal/BikeGoalActions' +import { filterTimeseriesByYear } from 'src/lib/timeseries' const BikeGoalDialogMobile = ({ timeseries, timeseriesQueryLeft }) => { const navigate = useNavigate() + const { year } = useParams() + + const timeseriesByYear = filterTimeseriesByYear(timeseries, year) return ( { display="flex" paddingTop="1.5rem" marginTop="3rem" - timeseries={timeseries} + timeseries={timeseriesByYear} /> diff --git a/src/components/Goals/BikeGoal/BikeGoalViewDesktop.jsx b/src/components/Goals/BikeGoal/BikeGoalViewDesktop.jsx index b530cfb7..812410c8 100644 --- a/src/components/Goals/BikeGoal/BikeGoalViewDesktop.jsx +++ b/src/components/Goals/BikeGoal/BikeGoalViewDesktop.jsx @@ -1,6 +1,6 @@ import React from 'react' import PropTypes from 'prop-types' -import { useNavigate } from 'react-router-dom' +import { useNavigate, useParams } from 'react-router-dom' import { useI18n } from 'cozy-ui/transpiled/react/I18n' @@ -9,16 +9,20 @@ import BikeGoalList from 'src/components/Goals/BikeGoal/BikeGoalList' import BikeGoalAchievement from 'src/components/Goals/BikeGoal/BikeGoalAchievement' import BikeGoalChart from 'src/components/Goals/BikeGoal/BikeGoalChart' import BikeGoalActions from 'src/components/Goals/BikeGoal/BikeGoalActions' +import { filterTimeseriesByYear } from 'src/lib/timeseries' const BikeGoalViewDesktop = ({ timeseries, timeseriesQueryLeft }) => { const { t } = useI18n() const navigate = useNavigate() + const { year } = useParams() + + const timeseriesByYear = filterTimeseriesByYear(timeseries, year) return ( <> } + subtitle={} onBack={() => navigate('/trips')} /> @@ -27,11 +31,11 @@ const BikeGoalViewDesktop = ({ timeseries, timeseriesQueryLeft }) => { position="absolute" top="2rem" right="2rem" - timeseries={timeseries} + timeseries={timeseriesByYear} /> diff --git a/src/lib/timeseries.js b/src/lib/timeseries.js index 4f21c7a3..09af7521 100644 --- a/src/lib/timeseries.js +++ b/src/lib/timeseries.js @@ -492,3 +492,9 @@ export const getEarliestTimeserie = timeseries => { return prev.startDate < current.startDate ? prev : current }) } + +export const filterTimeseriesByYear = (timeseries, year) => { + return timeseries.filter( + timeserie => new Date(timeserie.startDate).getFullYear().toString() === year + ) +} diff --git a/src/lib/timeseries.spec.js b/src/lib/timeseries.spec.js index 693b64cb..4d84f1a7 100644 --- a/src/lib/timeseries.spec.js +++ b/src/lib/timeseries.spec.js @@ -45,7 +45,8 @@ import { setManualPurpose, computeFirstAndLastDay, countDays, - getEarliestTimeserie + getEarliestTimeserie, + filterTimeseriesByYear } from 'src/lib/timeseries' describe('transformSerieToTrip', () => { @@ -816,3 +817,22 @@ describe('getEarliestTimeserie', () => { }) }) }) + +describe('filterTimeseriesByYear', () => { + it('should return only timeseries that match the year', () => { + const timeseries = [ + { startDate: '2022-01-01T00:00:00', endDate: '2022-01-01T00:00:00' }, + { startDate: '2020-01-01T00:00:00', endDate: '2020-01-01T00:00:00' }, + { startDate: '2021-01-01T00:00:00', endDate: '2021-01-01T00:00:00' } + ] + + const res = filterTimeseriesByYear(timeseries, '2021') + + expect(res).toStrictEqual([ + { + startDate: '2021-01-01T00:00:00', + endDate: '2021-01-01T00:00:00' + } + ]) + }) +}) From 477d46d8f24879d790048dc2097786f1d2818dda Mon Sep 17 00:00:00 2001 From: JF-Cozy Date: Thu, 3 Nov 2022 12:43:05 +0100 Subject: [PATCH 13/16] feat(BikeGoal): Use correct query for BikeGoalSummary --- .../Goals/BikeGoal/BikeGoalSummary.jsx | 92 +++++++++++-------- 1 file changed, 52 insertions(+), 40 deletions(-) diff --git a/src/components/Goals/BikeGoal/BikeGoalSummary.jsx b/src/components/Goals/BikeGoal/BikeGoalSummary.jsx index 4e20fe1b..f4cee4ab 100644 --- a/src/components/Goals/BikeGoal/BikeGoalSummary.jsx +++ b/src/components/Goals/BikeGoal/BikeGoalSummary.jsx @@ -1,5 +1,6 @@ import React from 'react' +import { isQueryLoading, useQueryAll } from 'cozy-client' import Typography from 'cozy-ui/transpiled/react/Typography' import Paper from 'cozy-ui/transpiled/react/Paper' import { useI18n } from 'cozy-ui/transpiled/react/I18n' @@ -11,24 +12,41 @@ import { isGoalCompleted, countDaysOrDaysToReach } from 'src/components/Goals/BikeGoal/helpers' -import { useTemporaryQueryForBikeGoal } from 'src/components/Goals/useTemporaryQueryForBikeGoal' +import { buildBikeCommuteTimeseriesQueryByAccountId } from 'src/queries/queries' +import { useAccountContext } from 'src/components/Providers/AccountProvider' +import { filterTimeseriesByYear } from 'src/lib/timeseries' const BikeGoalSummary = () => { const { t } = useI18n() + const { account, isAccountLoading } = useAccountContext() - // TODO: uncomment this when the request return something - // const timeseriesQuery = - // buildOneYearBikeCommuteTimeseriesQueryByDateAndAccountId( - // { - // date: new Date(), - // accountId: account?._id - // }, - // Boolean(account) - // ) + const timeseriesQuery = buildBikeCommuteTimeseriesQueryByAccountId( + { accountId: account?._id }, + Boolean(account) + ) + const { data: timeseries, ...timeseriesQueryLeft } = useQueryAll( + timeseriesQuery.definition, + timeseriesQuery.options + ) + + const isLoadingTimeseriesQuery = + isQueryLoading(timeseriesQueryLeft) || timeseriesQueryLeft.hasMore + + const isLoading = isAccountLoading || isLoadingTimeseriesQuery + + if (isLoading) { + return ( + + + + ) + } - // TODO: remove this hooks when the above request will work - const { timeseries, isLoadingTimeseriesQuery } = - useTemporaryQueryForBikeGoal() + const currentYear = new Date().getFullYear().toString() + const timeseriesByYear = filterTimeseriesByYear(timeseries, currentYear) return ( <> @@ -36,33 +54,27 @@ const BikeGoalSummary = () => { elevation={2} className="u-flex u-flex-items-center u-mh-1-s u-mh-2 u-mb-1 u-flex-items-start" > - {isLoadingTimeseriesQuery ? ( - - ) : ( - <> - -
- {t('bikeGoal.title')} - - {t('bikeGoal.goal_progression', { - days: countDaysOrDaysToReach(timeseries), - daysToReach: getDaysToReach() - })} - -
- - )} + +
+ {t('bikeGoal.title')} + + {t('bikeGoal.goal_progression', { + days: countDaysOrDaysToReach(timeseriesByYear), + daysToReach: getDaysToReach() + })} + +
) From b63ff0b643afdba67f8ca0ecd8decffb136a19ad Mon Sep 17 00:00:00 2001 From: JF-Cozy Date: Thu, 3 Nov 2022 12:43:31 +0100 Subject: [PATCH 14/16] feat(BikeGoal): Remove useless useTemporaryQueryForBikeGoal --- .../Goals/useTemporaryQueryForBikeGoal.jsx | 35 ------------------- 1 file changed, 35 deletions(-) delete mode 100644 src/components/Goals/useTemporaryQueryForBikeGoal.jsx diff --git a/src/components/Goals/useTemporaryQueryForBikeGoal.jsx b/src/components/Goals/useTemporaryQueryForBikeGoal.jsx deleted file mode 100644 index 8e02fd33..00000000 --- a/src/components/Goals/useTemporaryQueryForBikeGoal.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import { hasQueryBeenLoaded, isQueryLoading, useQuery } from 'cozy-client' - -import { buildAggregatedTimeseriesQueryByAccountId } from 'src/queries/queries' -import { useAccountContext } from 'src/components/Providers/AccountProvider' - -export const useTemporaryQueryForBikeGoal = () => { - const { account, isAccountLoading } = useAccountContext() - - const timeseriesQuery = buildAggregatedTimeseriesQueryByAccountId({ - accountId: account?._id, - limitBy: 50 - }) - - const { data: timeseries, ...timeseriesQueryLeft } = useQuery( - timeseriesQuery.definition, - timeseriesQuery.options - ) - - const isLoadingTimeseriesQuery = - isQueryLoading(timeseriesQueryLeft) && - !hasQueryBeenLoaded(timeseriesQueryLeft) - - const isLoadingOrEmpty = - isAccountLoading || - !account || - isLoadingTimeseriesQuery || - timeseries.length === 0 - - return { - isLoadingTimeseriesQuery, - timeseriesQueryLeft, - isLoadingOrEmpty, - timeseries - } -} From 1d598fbb0bc9c19040d05ffa4f2f295d54962035 Mon Sep 17 00:00:00 2001 From: JF-Cozy Date: Thu, 3 Nov 2022 12:43:46 +0100 Subject: [PATCH 15/16] feat(BikeGoal): Change some admin wording --- src/locales/fr.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/locales/fr.json b/src/locales/fr.json index 004daf8d..0ed1a42e 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -210,9 +210,9 @@ }, "settings": { "participation": "Participer à l'objectif “Aller au travail à vélo”", - "showAlerter": "Afficher l'alerter de l'objectif vélo", - "hideOnboarding": "Masquer l'embarquement d'objectif vélo", - "showAlerterSuccess": "Afficher l'alerte de succès de l'objectif vélo" + "showAlerter": "Objectif vélo : afficher l'alerter", + "hideOnboarding": "Objectif vélo : Masquer l'onboarding", + "showAlerterSuccess": "Objectif vélo : Afficher l'alerte de succès" }, "onboarding": { "title": "Forfait \"mobilités durables\"", From 58e4777b45ecaca5536c3d23c84f6b7a84617e8c Mon Sep 17 00:00:00 2001 From: JF-Cozy Date: Thu, 3 Nov 2022 12:48:02 +0100 Subject: [PATCH 16/16] feat(BikeGoal): Remove back arrow on desktop for goal editing --- src/components/Views/BikeGoalEdit.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/Views/BikeGoalEdit.jsx b/src/components/Views/BikeGoalEdit.jsx index 867de42f..9846b42a 100644 --- a/src/components/Views/BikeGoalEdit.jsx +++ b/src/components/Views/BikeGoalEdit.jsx @@ -16,8 +16,7 @@ const BikeGoalEdit = () => { disableGutters title={t('bikeGoal.edit.title')} content={} - onBack={() => navigate('..')} - onClose={() => navigate('/trips')} + onClose={() => navigate('..')} /> ) }