Client: display elevation and speed chart for an activity
This commit is contained in:
parent
8b186c2e41
commit
681ea6643d
@ -1,6 +1,7 @@
|
||||
import mpwoGenericApi from '../mwpoApi'
|
||||
import mpwoApi from '../mwpoApi/activities'
|
||||
import { history } from '../index'
|
||||
import { formatChartData } from '../utils'
|
||||
import { setError } from './index'
|
||||
|
||||
export const pushActivities = activities => ({
|
||||
@ -13,6 +14,11 @@ export const setGpx = gpxContent => ({
|
||||
gpxContent,
|
||||
})
|
||||
|
||||
export const setChartData = chartData => ({
|
||||
type: 'SET_CHART_DATA',
|
||||
chartData,
|
||||
})
|
||||
|
||||
export const addActivity = form => dispatch => mpwoApi
|
||||
.addActivity(form)
|
||||
.then(ret => {
|
||||
@ -54,6 +60,23 @@ export const getActivityGpx = activityId => dispatch => {
|
||||
}
|
||||
|
||||
|
||||
export const getActivityChartData = activityId => dispatch => {
|
||||
if (activityId) {
|
||||
return mpwoApi
|
||||
.getActivityChartData(activityId)
|
||||
.then(ret => {
|
||||
if (ret.status === 'success') {
|
||||
dispatch(setChartData(formatChartData(ret.data.chart_data)))
|
||||
} else {
|
||||
dispatch(setError(`activities: ${ret.message}`))
|
||||
}
|
||||
})
|
||||
.catch(error => dispatch(setError(`activities: ${error}`)))
|
||||
}
|
||||
dispatch(setChartData(null))
|
||||
}
|
||||
|
||||
|
||||
export const deleteActivity = id => dispatch => mpwoGenericApi
|
||||
.deleteData('activities', id)
|
||||
.then(ret => {
|
||||
|
@ -0,0 +1,84 @@
|
||||
import { format } from 'date-fns'
|
||||
import React from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
import {
|
||||
Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis
|
||||
} from 'recharts'
|
||||
|
||||
import { getActivityChartData } from '../../../actions/activities'
|
||||
|
||||
|
||||
class ActivityCharts extends React.Component {
|
||||
componentDidMount() {
|
||||
this.props.loadActivityData(this.props.activity.id)
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.activity.id !==
|
||||
this.props.activity.id) {
|
||||
this.props.loadActivityData(this.props.activity.id)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.loadActivityData(null)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { chartData } = this.props
|
||||
return (
|
||||
<div>
|
||||
<ResponsiveContainer height={300}>
|
||||
<LineChart
|
||||
data={chartData}
|
||||
margin={{ top: 15, right: 30, left: 20, bottom: 15 }}
|
||||
>
|
||||
<XAxis
|
||||
dataKey="duration"
|
||||
label={{ value: 'duration', offset: 0, position: 'bottom' }}
|
||||
scale="time"
|
||||
tickFormatter={time => format(time, 'HH:mm:ss')}
|
||||
type="number"
|
||||
/>
|
||||
<YAxis
|
||||
label={{ value: 'speed (km/h)', angle: -90, position: 'left' }}
|
||||
yAxisId="left"
|
||||
/>
|
||||
<YAxis
|
||||
label={{ value: 'altitude (m)', angle: -90, position: 'right' }}
|
||||
yAxisId="right" orientation="right"
|
||||
/>
|
||||
<Line
|
||||
yAxisId="left"
|
||||
type="linear"
|
||||
dataKey="speed"
|
||||
stroke="#8884d8"
|
||||
dot={false}
|
||||
/>
|
||||
<Line
|
||||
yAxisId="right"
|
||||
type="linear"
|
||||
dataKey="elevation"
|
||||
stroke="#808080"
|
||||
dot={false}
|
||||
/>
|
||||
<Tooltip
|
||||
labelFormatter={time => format(time, 'HH:mm:ss')}
|
||||
/>
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
state => ({
|
||||
chartData: state.chartData
|
||||
}),
|
||||
dispatch => ({
|
||||
loadActivityData: activityId => {
|
||||
dispatch(getActivityChartData(activityId))
|
||||
},
|
||||
})
|
||||
)(ActivityCharts)
|
@ -3,6 +3,7 @@ import { Helmet } from 'react-helmet'
|
||||
import { connect } from 'react-redux'
|
||||
|
||||
import ActivityCardHeader from './ActivityCardHeader'
|
||||
import ActivityCharts from './ActivityCharts'
|
||||
import ActivityDetails from './ActivityDetails'
|
||||
import ActivityMap from './ActivityMap'
|
||||
import CustomModal from './../../Others/CustomModal'
|
||||
@ -61,26 +62,41 @@ class ActivityDisplay extends React.Component {
|
||||
close={() => this.displayModal(false)}
|
||||
/>}
|
||||
{activity && sport && activities.length === 1 && (
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<div className="card">
|
||||
<div className="card-header">
|
||||
<ActivityCardHeader
|
||||
activity={activity}
|
||||
sport={sport}
|
||||
title={title}
|
||||
displayModal={() => this.displayModal(true)}
|
||||
/>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
<div className="row">
|
||||
{activity.with_gpx && (
|
||||
<div className="col-8">
|
||||
<ActivityMap activity={activity} />
|
||||
<div>
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<div className="card">
|
||||
<div className="card-header">
|
||||
<ActivityCardHeader
|
||||
activity={activity}
|
||||
sport={sport}
|
||||
title={title}
|
||||
displayModal={() => this.displayModal(true)}
|
||||
/>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
<div className="row">
|
||||
{activity.with_gpx && (
|
||||
<div className="col-8">
|
||||
<ActivityMap activity={activity} />
|
||||
</div>
|
||||
)}
|
||||
<div className="col">
|
||||
<ActivityDetails activity={activity} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<div className="card">
|
||||
<div className="card-body">
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<ActivityCharts activity={activity} />
|
||||
</div>
|
||||
)}
|
||||
<div className="col">
|
||||
<ActivityDetails activity={activity} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -29,4 +29,12 @@ export default class MpwoApi {
|
||||
return createRequest(params)
|
||||
}
|
||||
|
||||
static getActivityChartData(activityId) {
|
||||
const params = {
|
||||
url: `${apiUrl}activities/${activityId}/chart_data`,
|
||||
method: 'GET',
|
||||
}
|
||||
return createRequest(params)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -31,6 +31,15 @@ const activities = (state = initial.activities, action) => {
|
||||
}
|
||||
}
|
||||
|
||||
const chartData = (state = initial.chartData, action) => {
|
||||
switch (action.type) {
|
||||
case 'SET_CHART_DATA':
|
||||
return action.chartData
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
const formData = (state = initial.formData, action) => {
|
||||
switch (action.type) {
|
||||
case 'UPDATE_USER_FORMDATA':
|
||||
@ -163,6 +172,7 @@ const user = (state = initial.user, action) => {
|
||||
|
||||
const reducers = combineReducers({
|
||||
activities,
|
||||
chartData,
|
||||
formData,
|
||||
formProfile,
|
||||
gpx,
|
||||
|
@ -41,6 +41,7 @@ export default {
|
||||
activities: {
|
||||
...emptyData,
|
||||
},
|
||||
chartData: [],
|
||||
// check if storing gpx content is OK
|
||||
gpx: null,
|
||||
records: {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import togeojson from '@mapbox/togeojson'
|
||||
import { format, parse } from 'date-fns'
|
||||
import { format, parse, subHours } from 'date-fns'
|
||||
|
||||
export const apiUrl = `${process.env.REACT_APP_API_URL}/api/`
|
||||
export const thunderforestApiKey = `${
|
||||
@ -90,3 +90,17 @@ export const formatRecord = record => {
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
const formatDuration = seconds => {
|
||||
let newDate = new Date(0)
|
||||
newDate = subHours(newDate.setSeconds(seconds), 1)
|
||||
return newDate.getTime()
|
||||
}
|
||||
|
||||
export const formatChartData = chartData => {
|
||||
for (let i = 0; i < chartData.length; i++) {
|
||||
chartData[i].time = new Date(chartData[i].time).getTime()
|
||||
chartData[i].duration = formatDuration(chartData[i].duration)
|
||||
}
|
||||
return chartData
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
"react-router-dom": "^4.2.2",
|
||||
"react-router-redux": "^5.0.0-alpha.9",
|
||||
"react-scripts": "1.1.4",
|
||||
"recharts": "^1.0.0-beta.10",
|
||||
"redux": "4.0.0",
|
||||
"redux-thunk": "^2.2.0"
|
||||
},
|
||||
|
117
yarn.lock
117
yarn.lock
@ -1870,6 +1870,10 @@ class-utils@^0.3.5:
|
||||
isobject "^3.0.0"
|
||||
static-extend "^0.1.1"
|
||||
|
||||
classnames@2.2.5:
|
||||
version "2.2.5"
|
||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d"
|
||||
|
||||
clean-css@4.1.x:
|
||||
version "4.1.11"
|
||||
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.11.tgz#2ecdf145aba38f54740f26cefd0ff3e03e125d6a"
|
||||
@ -2128,6 +2132,10 @@ copy-descriptor@^0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
|
||||
|
||||
core-js@2.5.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b"
|
||||
|
||||
core-js@^1.0.0:
|
||||
version "1.2.7"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
|
||||
@ -2342,6 +2350,60 @@ currently-unhandled@^0.4.1:
|
||||
dependencies:
|
||||
array-find-index "^1.0.1"
|
||||
|
||||
d3-array@^1.2.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.1.tgz#d1ca33de2f6ac31efadb8e050a021d7e2396d5dc"
|
||||
|
||||
d3-collection@1:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.4.tgz#342dfd12837c90974f33f1cc0a785aea570dcdc2"
|
||||
|
||||
d3-color@1:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.2.0.tgz#d1ea19db5859c86854586276ec892cf93148459a"
|
||||
|
||||
d3-format@1:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.3.0.tgz#a3ac44269a2011cdb87c7b5693040c18cddfff11"
|
||||
|
||||
d3-interpolate@1, d3-interpolate@^1.1.5:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.2.0.tgz#40d81bd8e959ff021c5ea7545bc79b8d22331c41"
|
||||
dependencies:
|
||||
d3-color "1"
|
||||
|
||||
d3-path@1:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.5.tgz#241eb1849bd9e9e8021c0d0a799f8a0e8e441764"
|
||||
|
||||
d3-scale@1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-1.0.6.tgz#bce19da80d3a0cf422c9543ae3322086220b34ed"
|
||||
dependencies:
|
||||
d3-array "^1.2.0"
|
||||
d3-collection "1"
|
||||
d3-color "1"
|
||||
d3-format "1"
|
||||
d3-interpolate "1"
|
||||
d3-time "1"
|
||||
d3-time-format "2"
|
||||
|
||||
d3-shape@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.2.0.tgz#45d01538f064bafd05ea3d6d2cb748fd8c41f777"
|
||||
dependencies:
|
||||
d3-path "1"
|
||||
|
||||
d3-time-format@2:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.1.1.tgz#85b7cdfbc9ffca187f14d3c456ffda268081bb31"
|
||||
dependencies:
|
||||
d3-time "1"
|
||||
|
||||
d3-time@1:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.8.tgz#dbd2d6007bf416fe67a76d17947b784bffea1e84"
|
||||
|
||||
d@1:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f"
|
||||
@ -2654,6 +2716,10 @@ dom-converter@~0.1:
|
||||
dependencies:
|
||||
utila "~0.3"
|
||||
|
||||
dom-helpers@^3.3.1:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.3.1.tgz#fc1a4e15ffdf60ddde03a480a9c0fece821dd4a6"
|
||||
|
||||
dom-serializer@0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
|
||||
@ -5536,7 +5602,7 @@ lodash@4.16.4:
|
||||
version "4.16.4"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.16.4.tgz#01ce306b9bad1319f2a5528674f88297aeb70127"
|
||||
|
||||
"lodash@4.6.1 || ^4.16.1", "lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0:
|
||||
"lodash@4.6.1 || ^4.16.1", "lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0, lodash@~4.17.4:
|
||||
version "4.17.10"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
|
||||
|
||||
@ -6887,7 +6953,7 @@ promisify-event@^1.0.0:
|
||||
dependencies:
|
||||
pinkie-promise "^2.0.0"
|
||||
|
||||
prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.6.0:
|
||||
prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.6.0, prop-types@^15.6.1:
|
||||
version "15.6.1"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca"
|
||||
dependencies:
|
||||
@ -6985,7 +7051,7 @@ querystringify@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.0.0.tgz#fa3ed6e68eb15159457c89b37bc6472833195755"
|
||||
|
||||
raf@3.4.0:
|
||||
raf@3.4.0, raf@^3.2.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.0.tgz#a28876881b4bc2ca9117d4138163ddb80f781575"
|
||||
dependencies:
|
||||
@ -7103,6 +7169,12 @@ react-redux@^5.0.7:
|
||||
loose-envify "^1.1.0"
|
||||
prop-types "^15.6.0"
|
||||
|
||||
react-resize-detector@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-resize-detector/-/react-resize-detector-1.1.0.tgz#4a9831fa3caad32230478dd0185cbd2aa91a5ebf"
|
||||
dependencies:
|
||||
prop-types "^15.5.10"
|
||||
|
||||
react-router-dom@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.2.2.tgz#c8a81df3adc58bba8a76782e946cbd4eae649b8d"
|
||||
@ -7186,6 +7258,23 @@ react-side-effect@^1.1.0:
|
||||
exenv "^1.2.1"
|
||||
shallowequal "^1.0.1"
|
||||
|
||||
react-smooth@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-1.0.0.tgz#b29dbebddddb06d21b5b08962167fb9eac1897d8"
|
||||
dependencies:
|
||||
lodash "~4.17.4"
|
||||
prop-types "^15.6.0"
|
||||
raf "^3.2.0"
|
||||
react-transition-group "^2.2.1"
|
||||
|
||||
react-transition-group@^2.2.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.3.1.tgz#31d611b33e143a5e0f2d94c348e026a0f3b474b6"
|
||||
dependencies:
|
||||
dom-helpers "^3.3.1"
|
||||
loose-envify "^1.3.1"
|
||||
prop-types "^15.6.1"
|
||||
|
||||
react@^16.4.0:
|
||||
version "16.4.0"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-16.4.0.tgz#402c2db83335336fba1962c08b98c6272617d585"
|
||||
@ -7307,6 +7396,26 @@ recast@^0.11.17:
|
||||
private "~0.1.5"
|
||||
source-map "~0.5.0"
|
||||
|
||||
recharts-scale@0.3.2:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/recharts-scale/-/recharts-scale-0.3.2.tgz#dac7621714a4765d152cb2adbc30c73b831208c9"
|
||||
|
||||
recharts@^1.0.0-beta.10:
|
||||
version "1.0.0-beta.10"
|
||||
resolved "https://registry.yarnpkg.com/recharts/-/recharts-1.0.0-beta.10.tgz#d3cd15df6b7879d5968da3c942b5fcdaf2504fe1"
|
||||
dependencies:
|
||||
classnames "2.2.5"
|
||||
core-js "2.5.1"
|
||||
d3-interpolate "^1.1.5"
|
||||
d3-scale "1.0.6"
|
||||
d3-shape "1.2.0"
|
||||
lodash "~4.17.4"
|
||||
prop-types "^15.6.0"
|
||||
react-resize-detector "1.1.0"
|
||||
react-smooth "1.0.0"
|
||||
recharts-scale "0.3.2"
|
||||
reduce-css-calc "1.3.0"
|
||||
|
||||
recursive-readdir@2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.1.tgz#90ef231d0778c5ce093c9a48d74e5c5422d13a99"
|
||||
@ -7320,7 +7429,7 @@ redent@^1.0.0:
|
||||
indent-string "^2.1.0"
|
||||
strip-indent "^1.0.1"
|
||||
|
||||
reduce-css-calc@^1.2.6:
|
||||
reduce-css-calc@1.3.0, reduce-css-calc@^1.2.6:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716"
|
||||
dependencies:
|
||||
|
Loading…
Reference in New Issue
Block a user