API & Client: refactor (rename mpwo to fittrackee)

This commit is contained in:
Sam
2018-06-07 14:45:43 +02:00
parent 1f36de74ba
commit f65d636f85
81 changed files with 99 additions and 98 deletions

View File

@ -0,0 +1,48 @@
import { format } from 'date-fns'
import React from 'react'
import { Link } from 'react-router-dom'
import { apiUrl } from '../../utils'
export default function ActivityCard (props) {
const { activity, sports } = props
return (
<div className="card activity-card text-center">
<div className="card-header">
<Link to={`/activities/${activity.id}`}>
{sports.filter(sport => sport.id === activity.sport_id)
.map(sport => sport.label)} -{' '}
{format(activity.activity_date, 'DD/MM/YYYY HH:mm')}
</Link>
</div>
<div className="card-body">
<div className="row">
{activity.map && (
<div className="col">
<img
alt="Map"
src={`${apiUrl}activities/map/${activity.map}` +
`?${Date.now()}`}
className="img-fluid"
/>
</div>
)}
<div className="col">
<p>
<i className="fa fa-clock-o" aria-hidden="true" />{' '}
Duration: {activity.duration}
{activity.map ? (
<span><br /><br /></span>
) : (
' - '
)}
<i className="fa fa-road" aria-hidden="true" />{' '}
Distance: {activity.distance} km
</p>
</div>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,69 @@
import React from 'react'
import { Link } from 'react-router-dom'
import { formatRecord } from '../../utils'
export default function RecordsCard (props) {
const { records, sports } = props
const recordsBySport = records.reduce((sportList, record) => {
const sport = sports.find(s => s.id === record.sport_id)
if (sportList[sport.label] === void 0) {
sportList[sport.label] = {
img: sport.img,
records: [],
}
}
sportList[sport.label].records.push(formatRecord(record))
return sportList
}, {})
return (
<div className="card activity-card">
<div className="card-header">
Personal records
</div>
<div className="card-body">
{Object.keys(recordsBySport).length === 0
? 'No records'
: (Object.keys(recordsBySport).map(sportLabel => (
<table
className="table table-borderless record-table"
key={sportLabel}
>
<thead>
<tr>
<th colSpan="3">
<img
alt={`${sportLabel} logo`}
className="record-logo"
src={recordsBySport[sportLabel].img}
/>
{sportLabel}
</th>
</tr>
</thead>
<tbody>
{recordsBySport[sportLabel].records.map(rec => (
<tr key={rec.id}>
<td>
{rec.record_type}
</td>
<td>
{rec.value}
</td>
<td>
<Link to={`/activities/${rec.activity_id}`}>
{rec.activity_date}
</Link>
</td>
</tr>
))}
</tbody>
</table>))
)
}
</div>
</div>
)
}

View File

@ -0,0 +1,138 @@
import { endOfMonth, format, startOfMonth } from 'date-fns'
import React from 'react'
import { connect } from 'react-redux'
import {
Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis
} from 'recharts'
import { getStats } from '../../actions/stats'
import { activityColors, formatStats } from '../../utils'
class Statistics extends React.Component {
constructor(props, context) {
super(props, context)
const date = new Date()
this.state = {
start: startOfMonth(date),
end: endOfMonth(date),
displayedData: 'distance'
}
}
componentDidMount() {
this.props.loadMonthActivities(
this.props.user.id,
this.state.start,
this.state.end,
)
}
handleRadioChange (changeEvent) {
this.setState({
displayedData: changeEvent.target.name
})
}
render() {
const { sports, statistics } = this.props
const { displayedData, end, start } = this.state
const stats = formatStats(statistics, sports, start, end)
return (
<div className="card activity-card">
<div className="card-header">
This month
</div>
<div className="card-body">
{Object.keys(statistics).length === 0 ? (
'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: 15 }}
>
<XAxis
dataKey="date"
interval={0} // to force to display all ticks
/>
<YAxis
tickFormatter={value => displayedData === 'distance'
? `${value} km`
: displayedData === 'duration'
? format(new Date(value * 1000), 'HH:mm')
: value
}
/>
<Tooltip />
{sports.map((s, i) => (
<Bar
key={s.id}
dataKey={s.label}
formatter={value => displayedData === 'duration'
? format(new Date(value * 1000), 'HH:mm')
: value
}
stackId="a"
fill={activityColors[i]}
unit={displayedData === 'distance' ? ' km' : ''}
/>
))}
</BarChart>
</ResponsiveContainer>
</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 = {
start: format(start, dateFormat),
end: format(end, dateFormat),
time: 'week'
}
dispatch(getStats(userId, 'by_time', params))
},
})
)(Statistics)

View File

@ -0,0 +1,64 @@
import React from 'react'
export default function UserStatistics (props) {
const { user } = props
return (
<div className="row">
<div className="col">
<div className="card activity-card">
<div className="card-body row">
<div className="col-3">
<i className="fa fa-calendar fa-3x fa-color" />
</div>
<div className="col-9 text-right">
<div className="huge">{user.nbActivities}</div>
<div>{`workout${user.nbActivities === 1 ? '' : 's'}`}</div>
</div>
</div>
</div>
</div>
<div className="col">
<div className="card activity-card">
<div className="card-body row">
<div className="col-3">
<i className="fa fa-road fa-3x fa-color" />
</div>
<div className="col-9 text-right">
<div className="huge">
{Math.round(user.totalDistance * 100) / 100}
</div>
<div>km</div>
</div>
</div>
</div>
</div>
<div className="col">
<div className="card activity-card">
<div className="card-body row">
<div className="col-3">
<i className="fa fa-clock-o fa-3x fa-color" />
</div>
<div className="col-9 text-right">
<div className="huge">{user.totalDuration}</div>
<div>total duration</div>
</div>
</div>
</div>
</div>
<div className="col">
<div className="card activity-card">
<div className="card-body row">
<div className="col-3">
<i className="fa fa-tags fa-3x fa-color" />
</div>
<div className="col-9 text-right">
<div className="huge">{user.nbSports}</div>
<div>{`sport${user.nbSports === 1 ? '' : 's'}`}</div>
</div>
</div>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,107 @@
import React from 'react'
import { Helmet } from 'react-helmet'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'
import ActivityCard from './ActivityCard'
import Calendar from './../Others/Calendar'
import Records from './Records'
import Statistics from './Statistics'
import UserStatistics from './UserStatistics'
import { getData } from '../../actions'
import { getMoreActivities } from '../../actions/activities'
class DashBoard extends React.Component {
constructor(props, context) {
super(props, context)
this.state = {
page: 1,
}
}
componentDidMount() {
this.props.loadActivities()
}
render() {
const {
activities, loadMoreActivities, message, records, sports, user
} = this.props
const paginationEnd = activities.length > 0
? activities[activities.length - 1].previous_activity === null
: true
const { page } = this.state
return (
<div>
<Helmet>
<title>FitTrackee - Dashboard</title>
</Helmet>
{message ? (
<code>{message}</code>
) : (
(activities && sports.length > 0) && (
<div className="container dashboard">
<UserStatistics user={user} />
<div className="row">
<div className="col-md-4">
<Statistics />
<Records records={records} sports={sports} />
</div>
<div className="col-md-8">
<Calendar />
{activities.length > 0 ? (
activities.map(activity => (
<ActivityCard
activity={activity}
key={activity.id}
sports={sports}
/>)
)) : (
<div className="card text-center">
<div className="card-body">
No workouts. {' '}
<Link to={{ pathname: '/activities/add' }}>
Upload one !
</Link>
</div>
</div>
)}
{!paginationEnd &&
<input
type="submit"
className="btn btn-default btn-md btn-block"
value="Load more activities"
onClick={() => {
loadMoreActivities(page + 1)
this.setState({ page: page + 1 })
}}
/>
}
</div>
</div>
</div>
)
)}
</div>
)
}
}
export default connect(
state => ({
activities: state.activities.data,
message: state.message,
records: state.records.data,
sports: state.sports.data,
user: state.user,
}),
dispatch => ({
loadActivities: () => {
dispatch(getData('activities', { page: 1 }))
dispatch(getData('records'))
},
loadMoreActivities: page => {
dispatch(getMoreActivities(page))
},
})
)(DashBoard)