Client: display elevation and speed chart for an activity

This commit is contained in:
Sam
2018-05-28 15:33:56 +02:00
parent 8b186c2e41
commit 681ea6643d
9 changed files with 290 additions and 24 deletions

View File

@ -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 => {

View File

@ -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)

View File

@ -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>

View File

@ -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)
}
}

View File

@ -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,

View File

@ -41,6 +41,7 @@ export default {
activities: {
...emptyData,
},
chartData: [],
// check if storing gpx content is OK
gpx: null,
records: {

View File

@ -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
}