diff --git a/mpwo_api/mpwo_api/activities/activities.py b/mpwo_api/mpwo_api/activities/activities.py
index 37e194f8..b41ae104 100644
--- a/mpwo_api/mpwo_api/activities/activities.py
+++ b/mpwo_api/mpwo_api/activities/activities.py
@@ -26,6 +26,7 @@ def get_activities(auth_user_id):
page = 1 if 'page' not in params.keys() else int(params.get('page'))
date_from = params.get('from')
date_to = params.get('to')
+ order = params.get('order')
activities = Activity.query.filter(
Activity.user_id == auth_user_id,
Activity.activity_date >= datetime.strptime(date_from, '%Y-%m-%d')
@@ -33,7 +34,9 @@ def get_activities(auth_user_id):
Activity.activity_date <= datetime.strptime(date_to, '%Y-%m-%d')
if date_to else True,
).order_by(
- Activity.activity_date.desc()
+ Activity.activity_date.asc()
+ if order == 'asc'
+ else Activity.activity_date.desc()
).paginate(
page, 5, False
).items
diff --git a/mpwo_api/mpwo_api/tests/test_activities_api_0_get.py b/mpwo_api/mpwo_api/tests/test_activities_api_0_get.py
index bcc6cc2e..18eaff0f 100644
--- a/mpwo_api/mpwo_api/tests/test_activities_api_0_get.py
+++ b/mpwo_api/mpwo_api/tests/test_activities_api_0_get.py
@@ -344,6 +344,64 @@ def test_get_activities_date_filter_paginate(
assert 'Mon, 20 Mar 2017 00:00:00 GMT' == data['data']['activities'][1]['activity_date'] # noqa
+def test_get_activities_order(
+ app, user_1, sport_1_cycling, seven_activities_user_1
+):
+ client = app.test_client()
+ resp_login = client.post(
+ '/api/auth/login',
+ data=json.dumps(dict(
+ email='test@test.com',
+ password='12345678'
+ )),
+ content_type='application/json'
+ )
+ response = client.get(
+ '/api/activities?order=asc',
+ headers=dict(
+ Authorization='Bearer ' + json.loads(
+ resp_login.data.decode()
+ )['auth_token']
+ )
+ )
+ data = json.loads(response.data.decode())
+
+ assert response.status_code == 200
+ assert 'success' in data['status']
+ assert len(data['data']['activities']) == 5
+ assert 'Mon, 20 Mar 2017 00:00:00 GMT' == data['data']['activities'][0]['activity_date'] # noqa
+ assert 'Fri, 23 Feb 2018 00:00:00 GMT' == data['data']['activities'][4]['activity_date'] # noqa
+
+
+def test_get_activities_date_filter_paginate_order(
+ app, user_1, sport_1_cycling, seven_activities_user_1
+):
+ client = app.test_client()
+ resp_login = client.post(
+ '/api/auth/login',
+ data=json.dumps(dict(
+ email='test@test.com',
+ password='12345678'
+ )),
+ content_type='application/json'
+ )
+ response = client.get(
+ '/api/activities?from=2017-01-01&page=2&order=asc',
+ headers=dict(
+ Authorization='Bearer ' + json.loads(
+ resp_login.data.decode()
+ )['auth_token']
+ )
+ )
+ data = json.loads(response.data.decode())
+
+ assert response.status_code == 200
+ assert 'success' in data['status']
+ assert len(data['data']['activities']) == 2
+ assert 'Sun, 01 Apr 2018 00:00:00 GMT' == data['data']['activities'][0]['activity_date'] # noqa
+ assert 'Wed, 09 May 2018 00:00:00 GMT' == data['data']['activities'][1]['activity_date'] # noqa
+
+
def test_get_an_activity(
app, user_1, sport_1_cycling, activity_cycling_user_1
):
diff --git a/mpwo_client/src/actions/activities.js b/mpwo_client/src/actions/activities.js
index 18ebc5ff..3a334fe4 100644
--- a/mpwo_client/src/actions/activities.js
+++ b/mpwo_client/src/actions/activities.js
@@ -1,3 +1,5 @@
+import { parse } from 'date-fns'
+
import mpwoGenericApi from '../mwpoApi'
import mpwoApi from '../mwpoApi/activities'
import { history } from '../index'
@@ -9,6 +11,11 @@ export const pushActivities = activities => ({
activities,
})
+export const updateCalendar = activities => ({
+ type: 'UPDATE_CALENDAR',
+ activities,
+})
+
export const setGpx = gpxContent => ({
type: 'SET_GPX',
gpxContent,
@@ -121,3 +128,21 @@ export const getMoreActivities = page => dispatch => mpwoGenericApi
}
})
.catch(error => dispatch(setError(`activities: ${error}`)))
+
+export const getMonthActivities = (start, end) => dispatch => mpwoGenericApi
+ .getData('activities', null, null, start, end, 'asc')
+ .then(ret => {
+ if (ret.status === 'success') {
+ if (ret.data.activities.length > 0) {
+ for (let i = 0; i < ret.data.activities.length; i++) {
+ ret.data.activities[i].activity_date = parse(
+ ret.data.activities[i].activity_date
+ )
+ }
+ dispatch(updateCalendar(ret.data.activities))
+ }
+ } else {
+ dispatch(setError(`activities: ${ret.message}`))
+ }
+ })
+ .catch(error => dispatch(setError(`activities: ${error}`)))
diff --git a/mpwo_client/src/components/App.css b/mpwo_client/src/components/App.css
index 89d6a016..23ddef0d 100644
--- a/mpwo_client/src/components/App.css
+++ b/mpwo_client/src/components/App.css
@@ -71,6 +71,12 @@ input, textarea {
margin-top: 20px;
}
+.activity-sport {
+ margin-right: 1px;
+ max-width: 20px;
+ max-height: 20px;
+}
+
.add-activity {
margin-top: 50px;
}
diff --git a/mpwo_client/src/components/Dashboard/Records.jsx b/mpwo_client/src/components/Dashboard/Records.jsx
index 1f16c187..90d2e5d7 100644
--- a/mpwo_client/src/components/Dashboard/Records.jsx
+++ b/mpwo_client/src/components/Dashboard/Records.jsx
@@ -23,42 +23,45 @@ export default function RecordsCard (props) {
Personal records
- {Object.keys(recordsBySport).map(sportLabel => (
-
-
-
-
-
- {sportLabel}
- |
-
-
-
- {recordsBySport[sportLabel].records.map(rec => (
-
-
- {rec.record_type}
- |
-
- {rec.value}
- |
-
-
- {rec.activity_date}
-
- |
+ {Object.keys(recordsBySport).length === 0
+ ? 'No records.'
+ : (Object.keys(recordsBySport).map(sportLabel => (
+
+
+
+
+
+ {sportLabel}
+ |
- ))}
-
-
- ))}
+
+
+ {recordsBySport[sportLabel].records.map(rec => (
+
+
+ {rec.record_type}
+ |
+
+ {rec.value}
+ |
+
+
+ {rec.activity_date}
+
+ |
+
+ ))}
+
+
))
+ )
+ }
)
diff --git a/mpwo_client/src/components/Dashboard/index.jsx b/mpwo_client/src/components/Dashboard/index.jsx
index 4229637e..80e06468 100644
--- a/mpwo_client/src/components/Dashboard/index.jsx
+++ b/mpwo_client/src/components/Dashboard/index.jsx
@@ -37,22 +37,25 @@ class DashBoard extends React.Component {
{message ? (
{message}
) : (
- (activities.length > 0 && sports.length > 0) ? (
+ (activities && sports.length > 0) && (
-
- {activities.map(activity => (
+
+ {activities.length > 0 ? (
+ activities.map(activity => (
- ))}
+ />)
+ )) : (
+ 'No activities. Upload one !'
+ )}
{!paginationEnd &&
- ) : (
- 'No activities for now'
- ))}
+ )
+ )}
)
}
diff --git a/mpwo_client/src/components/Others/Calendar.jsx b/mpwo_client/src/components/Others/Calendar.jsx
index 1426cd44..7d7caa0d 100644
--- a/mpwo_client/src/components/Others/Calendar.jsx
+++ b/mpwo_client/src/components/Others/Calendar.jsx
@@ -2,15 +2,32 @@
// source: https://blog.flowandform.agency/create-a-custom-calendar-in-react-3df1bfd0b728
import dateFns from 'date-fns'
import React from 'react'
+import { connect } from 'react-redux'
+import { Link } from 'react-router-dom'
-export default class Calendar extends React.Component {
+import { getMonthActivities } from '../../actions/activities'
+
+const getStartAndEndMonth = date => ({
+ start: dateFns.startOfMonth(date),
+ end: dateFns.endOfMonth(date),
+})
+
+
+class Calendar extends React.Component {
constructor(props, context) {
super(props, context)
+ const calendarDate = new Date()
this.state = {
- currentMonth: new Date(),
+ currentMonth: calendarDate,
+ monthStart: getStartAndEndMonth(calendarDate).start,
+ monthEnd: getStartAndEndMonth(calendarDate).end,
}
}
+ componentDidMount() {
+ this.props.loadMonthActivities(this.state.monthStart, this.state.monthEnd)
+ }
+
renderHeader() {
const dateFormat = 'MMM YYYY'
return (
@@ -51,10 +68,15 @@ export default class Calendar extends React.Component {
return {days}
}
+ filterActivities(day) {
+ const { activities } = this.props
+ return activities
+ .filter(act => dateFns.isSameDay(act.activity_date, day))
+ }
+
renderCells() {
- const { currentMonth, selectedDate } = this.state
- const monthStart = dateFns.startOfMonth(currentMonth)
- const monthEnd = dateFns.endOfMonth(monthStart)
+ const { monthStart, monthEnd } = this.state
+ const { sports } = this.props
const startDate = dateFns.startOfWeek(monthStart)
const endDate = dateFns.endOfWeek(monthEnd)
@@ -68,16 +90,21 @@ export default class Calendar extends React.Component {
while (day <= endDate) {
for (let i = 0; i < 7; i++) {
formattedDate = dateFns.format(day, dateFormat)
+ const dayActivities = this.filterActivities(day)
days.push(
-
+
{formattedDate}
+ {dayActivities.map(act => (
+
+
s.id === act.sport_id)
+ .map(s => s.img)}
+ alt="activity sport logo"
+ />
+
+ ))}
)
day = dateFns.addDays(day, 1)
@@ -92,16 +119,24 @@ export default class Calendar extends React.Component {
return
{rows}
}
- handleNextMonth () {
+ updateStateDate (calendarDate) {
+ const { start, end } = getStartAndEndMonth(calendarDate)
this.setState({
- currentMonth: dateFns.addMonths(this.state.currentMonth, 1)
+ currentMonth: calendarDate,
+ monthStart: start,
+ monthEnd: end,
})
+ this.props.loadMonthActivities(start, end)
+ }
+
+ handleNextMonth () {
+ const calendarDate = dateFns.addMonths(this.state.currentMonth, 1)
+ this.updateStateDate(calendarDate)
}
handlePrevMonth () {
- this.setState({
- currentMonth: dateFns.subMonths(this.state.currentMonth, 1)
- })
+ const calendarDate = dateFns.subMonths(this.state.currentMonth, 1)
+ this.updateStateDate(calendarDate)
}
render() {
@@ -116,3 +151,19 @@ export default class Calendar extends React.Component {
)
}
}
+
+export default connect(
+ state => ({
+ activities: state.calendarActivities.data,
+ sports: state.sports.data,
+ }),
+ dispatch => ({
+ loadMonthActivities: (start, end) => {
+ const dateFormat = 'YYYY-MM-DD'
+ dispatch(getMonthActivities(
+ dateFns.format(start, dateFormat),
+ dateFns.format(end, dateFormat),
+ ))
+ },
+ })
+)(Calendar)
diff --git a/mpwo_client/src/mwpoApi/index.js b/mpwo_client/src/mwpoApi/index.js
index e0f15506..a7c1c7a2 100644
--- a/mpwo_client/src/mwpoApi/index.js
+++ b/mpwo_client/src/mwpoApi/index.js
@@ -2,13 +2,30 @@ import { apiUrl, createRequest } from '../utils'
export default class MpwoApi {
- static getData(target, id = null, page = null) {
+ static getData(target,
+ id = null,
+ page = null,
+ start = null,
+ end = null,
+ order = null) {
let url = `${apiUrl}${target}`
if (id) {
url = `${url}/${id}`
} else if (page) {
url = `${url}?page=${page}`
}
+ if (start || end) {
+ url = `${url}${
+ page ? '' : '?'
+ }${
+ start && `&from=${start}`
+ }${
+ end && `&to=${end}`
+ }`
+ }
+ if (order) {
+ url = `${url}${(page || start || end) ? '' : '?'}${`&order=${order}`}`
+ }
const params = {
url: url,
method: 'GET',
diff --git a/mpwo_client/src/reducers/index.js b/mpwo_client/src/reducers/index.js
index 7aa56998..08c1aa51 100644
--- a/mpwo_client/src/reducers/index.js
+++ b/mpwo_client/src/reducers/index.js
@@ -31,6 +31,18 @@ const activities = (state = initial.activities, action) => {
}
}
+const calendarActivities = (state = initial.calendarActivities, action) => {
+ switch (action.type) {
+ case 'UPDATE_CALENDAR':
+ return {
+ ...state,
+ data: action.activities,
+ }
+ default:
+ return handleDataAndError(state, 'calendarActivities', action)
+ }
+}
+
const chartData = (state = initial.chartData, action) => {
switch (action.type) {
case 'SET_CHART_DATA':
@@ -181,6 +193,7 @@ const user = (state = initial.user, action) => {
const reducers = combineReducers({
activities,
+ calendarActivities,
chartData,
formData,
formProfile,
diff --git a/mpwo_client/src/reducers/initial.js b/mpwo_client/src/reducers/initial.js
index d8a5004c..b1771c4d 100644
--- a/mpwo_client/src/reducers/initial.js
+++ b/mpwo_client/src/reducers/initial.js
@@ -41,6 +41,9 @@ export default {
activities: {
...emptyData,
},
+ calendarActivities: {
+ ...emptyData,
+ },
chartData: [],
// check if storing gpx content is OK
gpx: null,