stats components refactor
This commit is contained in:
parent
0e6b9030f8
commit
af5b0e1889
@ -128,10 +128,6 @@ label {
|
|||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart-month {
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-radio {
|
.chart-radio {
|
||||||
display: flex;
|
display: flex;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
@ -145,6 +141,10 @@ label {
|
|||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chart-stats {
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
.chart-title {
|
.chart-title {
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
@ -1,140 +1,31 @@
|
|||||||
import { endOfMonth, format, startOfMonth } from 'date-fns'
|
import { endOfMonth, startOfMonth } from 'date-fns'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { connect } from 'react-redux'
|
|
||||||
import {
|
|
||||||
Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis
|
|
||||||
} from 'recharts'
|
|
||||||
|
|
||||||
import { getStats } from '../../actions/stats'
|
import Stats from '../Others/Stats'
|
||||||
import { activityColors, formatDuration, formatStats } from '../../utils'
|
|
||||||
import CustomTooltip from '../Others/CustomTooltip'
|
|
||||||
|
|
||||||
|
|
||||||
class Statistics extends React.Component {
|
export default class Statistics extends React.Component {
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props, context)
|
super(props, context)
|
||||||
const date = new Date()
|
const date = new Date()
|
||||||
this.state = {
|
this.state = {
|
||||||
start: startOfMonth(date),
|
start: startOfMonth(date),
|
||||||
end: endOfMonth(date),
|
end: endOfMonth(date),
|
||||||
displayedData: 'distance'
|
duration: 'week',
|
||||||
|
type: 'by_time',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.props.loadMonthActivities(
|
|
||||||
this.props.user.id,
|
|
||||||
this.state.start,
|
|
||||||
this.state.end,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
handleRadioChange (changeEvent) {
|
|
||||||
this.setState({
|
|
||||||
displayedData: changeEvent.target.name
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { sports, statistics } = this.props
|
|
||||||
const { displayedData, end, start } = this.state
|
|
||||||
const stats = formatStats(statistics, sports, start, end)
|
|
||||||
return (
|
return (
|
||||||
<div className="card activity-card">
|
<div className="card activity-card">
|
||||||
<div className="card-header">
|
<div className="card-header">
|
||||||
This month
|
This month
|
||||||
</div>
|
</div>
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
{Object.keys(statistics).length === 0 ? (
|
<Stats statsParams={this.state} />
|
||||||
'No workouts'
|
|
||||||
) : (
|
|
||||||
<div className="chart-month">
|
|
||||||
<div className="row chart-radio">
|
|
||||||
<label className="radioLabel col">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="distance"
|
|
||||||
checked={displayedData === 'distance'}
|
|
||||||
onChange={e => this.handleRadioChange(e)}
|
|
||||||
/>
|
|
||||||
distance
|
|
||||||
</label>
|
|
||||||
<label className="radioLabel col">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="duration"
|
|
||||||
checked={displayedData === 'duration'}
|
|
||||||
onChange={e => this.handleRadioChange(e)}
|
|
||||||
/>
|
|
||||||
duration
|
|
||||||
</label>
|
|
||||||
<label className="radioLabel col">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="activities"
|
|
||||||
checked={displayedData === 'activities'}
|
|
||||||
onChange={e => this.handleRadioChange(e)}
|
|
||||||
/>
|
|
||||||
activities
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<ResponsiveContainer height={300}>
|
|
||||||
<BarChart
|
|
||||||
data={stats[displayedData]}
|
|
||||||
margin={{ top: 15, bottom: 0 }}
|
|
||||||
>
|
|
||||||
<XAxis
|
|
||||||
dataKey="date"
|
|
||||||
interval={0} // to force to display all ticks
|
|
||||||
/>
|
|
||||||
<YAxis
|
|
||||||
tickFormatter={value => displayedData === 'distance'
|
|
||||||
? `${value} km`
|
|
||||||
: displayedData === 'duration'
|
|
||||||
? formatDuration(value)
|
|
||||||
: value
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Tooltip content={
|
|
||||||
<CustomTooltip
|
|
||||||
displayedData={displayedData}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{sports.map((s, i) => (
|
|
||||||
<Bar
|
|
||||||
key={s.id}
|
|
||||||
dataKey={s.label}
|
|
||||||
stackId="a"
|
|
||||||
fill={activityColors[i]}
|
|
||||||
unit={displayedData === 'distance' ? ' km' : ''}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</BarChart>
|
|
||||||
</ResponsiveContainer>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(
|
|
||||||
state => ({
|
|
||||||
sports: state.sports.data,
|
|
||||||
statistics: state.statistics.data,
|
|
||||||
user: state.user,
|
|
||||||
}),
|
|
||||||
dispatch => ({
|
|
||||||
loadMonthActivities: (userId, start, end) => {
|
|
||||||
const dateFormat = 'YYYY-MM-DD'
|
|
||||||
const params = {
|
|
||||||
from: format(start, dateFormat),
|
|
||||||
to: format(end, dateFormat),
|
|
||||||
time: 'week'
|
|
||||||
}
|
|
||||||
dispatch(getStats(userId, 'by_time', params))
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)(Statistics)
|
|
||||||
|
97
fittrackee_client/src/components/Others/Stats/StatsChart.jsx
Normal file
97
fittrackee_client/src/components/Others/Stats/StatsChart.jsx
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis
|
||||||
|
} from 'recharts'
|
||||||
|
|
||||||
|
import { activityColors, formatDuration } from '../../../utils'
|
||||||
|
import CustomTooltip from '../CustomTooltip'
|
||||||
|
|
||||||
|
|
||||||
|
export default class StatsCharts extends React.PureComponent {
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context)
|
||||||
|
this.state = {
|
||||||
|
displayedData: 'distance'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleRadioChange (changeEvent) {
|
||||||
|
this.setState({
|
||||||
|
displayedData: changeEvent.target.name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { displayedData } = this.state
|
||||||
|
const { sports, stats } = this.props
|
||||||
|
if (Object.keys(stats).length === 0) {
|
||||||
|
return 'No workouts'
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="chart-stats">
|
||||||
|
<div className="row chart-radio">
|
||||||
|
<label className="radioLabel col">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="distance"
|
||||||
|
checked={displayedData === 'distance'}
|
||||||
|
onChange={e => this.handleRadioChange(e)}
|
||||||
|
/>
|
||||||
|
distance
|
||||||
|
</label>
|
||||||
|
<label className="radioLabel col">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="duration"
|
||||||
|
checked={displayedData === 'duration'}
|
||||||
|
onChange={e => this.handleRadioChange(e)}
|
||||||
|
/>
|
||||||
|
duration
|
||||||
|
</label>
|
||||||
|
<label className="radioLabel col">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="activities"
|
||||||
|
checked={displayedData === 'activities'}
|
||||||
|
onChange={e => this.handleRadioChange(e)}
|
||||||
|
/>
|
||||||
|
activities
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<ResponsiveContainer height={300}>
|
||||||
|
<BarChart
|
||||||
|
data={stats[displayedData]}
|
||||||
|
margin={{ top: 15, bottom: 0 }}
|
||||||
|
>
|
||||||
|
<XAxis
|
||||||
|
dataKey="date"
|
||||||
|
interval={0} // to force to display all ticks
|
||||||
|
/>
|
||||||
|
<YAxis
|
||||||
|
tickFormatter={value => displayedData === 'distance'
|
||||||
|
? `${value} km`
|
||||||
|
: displayedData === 'duration'
|
||||||
|
? formatDuration(value)
|
||||||
|
: value
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Tooltip content={
|
||||||
|
<CustomTooltip
|
||||||
|
displayedData={displayedData}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{sports.map((s, i) => (
|
||||||
|
<Bar
|
||||||
|
key={s.id}
|
||||||
|
dataKey={s.label}
|
||||||
|
stackId="a"
|
||||||
|
fill={activityColors[i]}
|
||||||
|
unit={displayedData === 'distance' ? ' km' : ''}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</BarChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
50
fittrackee_client/src/components/Others/Stats/index.jsx
Normal file
50
fittrackee_client/src/components/Others/Stats/index.jsx
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { format } from 'date-fns'
|
||||||
|
import React from 'react'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
|
||||||
|
import { getStats } from '../../../actions/stats'
|
||||||
|
import { formatStats } from '../../../utils'
|
||||||
|
import StatsChart from './StatsChart'
|
||||||
|
|
||||||
|
|
||||||
|
class Statistics extends React.PureComponent {
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.loadMonthActivities(
|
||||||
|
this.props.user.id,
|
||||||
|
this.props.statsParams,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { sports, statistics, statsParams } = this.props
|
||||||
|
const stats = formatStats(statistics, sports, statsParams)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{Object.keys(statistics).length === 0 ? (
|
||||||
|
'No workouts'
|
||||||
|
) : (
|
||||||
|
<StatsChart sports={sports} stats={stats} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
state => ({
|
||||||
|
sports: state.sports.data,
|
||||||
|
statistics: state.statistics.data,
|
||||||
|
user: state.user,
|
||||||
|
}),
|
||||||
|
dispatch => ({
|
||||||
|
loadMonthActivities: (userId, data) => {
|
||||||
|
const dateFormat = 'YYYY-MM-DD'
|
||||||
|
const params = {
|
||||||
|
from: format(data.start, dateFormat),
|
||||||
|
to: format(data.end, dateFormat),
|
||||||
|
time: data.duration
|
||||||
|
}
|
||||||
|
dispatch(getStats(userId, data.type, params))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)(Statistics)
|
@ -1,44 +1,22 @@
|
|||||||
import { endOfMonth, format, startOfMonth, subMonths } from 'date-fns'
|
import { endOfMonth, startOfMonth, subMonths } from 'date-fns'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { connect } from 'react-redux'
|
|
||||||
import {
|
|
||||||
Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis
|
|
||||||
} from 'recharts'
|
|
||||||
|
|
||||||
import { getStats } from '../../actions/stats'
|
import Stats from '../Others/Stats'
|
||||||
import { activityColors, formatDuration, formatStats } from '../../utils'
|
|
||||||
import CustomTooltip from '../Others/CustomTooltip'
|
|
||||||
|
|
||||||
|
|
||||||
class Statistics extends React.Component {
|
export default class Statistics extends React.Component {
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props, context)
|
super(props, context)
|
||||||
const date = new Date()
|
const date = new Date()
|
||||||
this.state = {
|
this.state = {
|
||||||
start: startOfMonth(subMonths(date, 12)),
|
start: startOfMonth(subMonths(date, 12)),
|
||||||
end: endOfMonth(date),
|
end: endOfMonth(date),
|
||||||
displayedData: 'distance'
|
duration: 'month',
|
||||||
|
type: 'by_time',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.props.loadMonthActivities(
|
|
||||||
this.props.user.id,
|
|
||||||
this.state.start,
|
|
||||||
this.state.end,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
handleRadioChange (changeEvent) {
|
|
||||||
this.setState({
|
|
||||||
displayedData: changeEvent.target.name
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { sports, statistics } = this.props
|
|
||||||
const { displayedData, end, start } = this.state
|
|
||||||
const stats = formatStats(statistics, sports, start, end, 'month')
|
|
||||||
return (
|
return (
|
||||||
<div className="container dashboard">
|
<div className="container dashboard">
|
||||||
<div className="card activity-card">
|
<div className="card activity-card">
|
||||||
@ -46,97 +24,10 @@ class Statistics extends React.Component {
|
|||||||
Statistics
|
Statistics
|
||||||
</div>
|
</div>
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
{Object.keys(statistics).length === 0 ? (
|
<Stats statsParams={this.state} />
|
||||||
'No workouts'
|
|
||||||
) : (
|
|
||||||
<div className="chart-month">
|
|
||||||
<div className="row chart-radio">
|
|
||||||
<label className="radioLabel col">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="distance"
|
|
||||||
checked={displayedData === 'distance'}
|
|
||||||
onChange={e => this.handleRadioChange(e)}
|
|
||||||
/>
|
|
||||||
distance
|
|
||||||
</label>
|
|
||||||
<label className="radioLabel col">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="duration"
|
|
||||||
checked={displayedData === 'duration'}
|
|
||||||
onChange={e => this.handleRadioChange(e)}
|
|
||||||
/>
|
|
||||||
duration
|
|
||||||
</label>
|
|
||||||
<label className="radioLabel col">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="activities"
|
|
||||||
checked={displayedData === 'activities'}
|
|
||||||
onChange={e => this.handleRadioChange(e)}
|
|
||||||
/>
|
|
||||||
activities
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<ResponsiveContainer height={300}>
|
|
||||||
<BarChart
|
|
||||||
data={stats[displayedData]}
|
|
||||||
margin={{ top: 15, bottom: 0 }}
|
|
||||||
>
|
|
||||||
<XAxis
|
|
||||||
dataKey="date"
|
|
||||||
interval={0} // to force to display all ticks
|
|
||||||
/>
|
|
||||||
<YAxis
|
|
||||||
tickFormatter={value => displayedData === 'distance'
|
|
||||||
? `${value} km`
|
|
||||||
: displayedData === 'duration'
|
|
||||||
? formatDuration(value)
|
|
||||||
: value
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Tooltip content={
|
|
||||||
<CustomTooltip
|
|
||||||
displayedData={displayedData}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{sports.map((s, i) => (
|
|
||||||
<Bar
|
|
||||||
key={s.id}
|
|
||||||
dataKey={s.label}
|
|
||||||
stackId="a"
|
|
||||||
fill={activityColors[i]}
|
|
||||||
unit={displayedData === 'distance' ? ' km' : ''}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</BarChart>
|
|
||||||
</ResponsiveContainer>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(
|
|
||||||
state => ({
|
|
||||||
sports: state.sports.data,
|
|
||||||
statistics: state.statistics.data,
|
|
||||||
user: state.user,
|
|
||||||
}),
|
|
||||||
dispatch => ({
|
|
||||||
loadMonthActivities: (userId, start, end) => {
|
|
||||||
const dateFormat = 'YYYY-MM-DD'
|
|
||||||
const params = {
|
|
||||||
from: format(start, dateFormat),
|
|
||||||
to: format(end, dateFormat),
|
|
||||||
time: 'month'
|
|
||||||
}
|
|
||||||
dispatch(getStats(userId, 'by_time', params))
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)(Statistics)
|
|
||||||
|
@ -172,17 +172,19 @@ const dateIncrement = (duration, day) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const formatStats = (
|
export const formatStats = (
|
||||||
stats, sports, startDate, endDate, duration = 'week'
|
stats, sports, params
|
||||||
) => {
|
) => {
|
||||||
const nbActivitiesStats = []
|
const nbActivitiesStats = []
|
||||||
const distanceStats = []
|
const distanceStats = []
|
||||||
const durationStats = []
|
const durationStats = []
|
||||||
|
|
||||||
for (let day = startOfWeek(startDate);
|
for (let day = startOfWeek(params.start);
|
||||||
day <= endDate;
|
day <= params.end;
|
||||||
day = dateIncrement(duration, day)
|
day = dateIncrement(params.duration, day)
|
||||||
) {
|
) {
|
||||||
const [xAxisFormat] = xAxisFormats.filter(x => x.duration === duration)
|
const [xAxisFormat] = xAxisFormats.filter(
|
||||||
|
x => x.duration === params.duration
|
||||||
|
)
|
||||||
const date = format(day, xAxisFormat.dateFormat)
|
const date = format(day, xAxisFormat.dateFormat)
|
||||||
const xAxis = format(day, xAxisFormat.xAxis)
|
const xAxis = format(day, xAxisFormat.xAxis)
|
||||||
const dataNbActivities = { date: xAxis }
|
const dataNbActivities = { date: xAxis }
|
||||||
|
Loading…
Reference in New Issue
Block a user