Client - reformat js files w/ prettier
This commit is contained in:
		@@ -29,155 +29,154 @@ export const setChartData = chartData => ({
 | 
			
		||||
  chartData,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export const addActivity = form => dispatch => FitTrackeeGenericApi
 | 
			
		||||
  .addDataWithFile('activities', form)
 | 
			
		||||
  .then(ret => {
 | 
			
		||||
    if (ret.status === 'created') {
 | 
			
		||||
      if (ret.data.activities.length === 0) {
 | 
			
		||||
export const addActivity = form => dispatch =>
 | 
			
		||||
  FitTrackeeGenericApi.addDataWithFile('activities', form)
 | 
			
		||||
    .then(ret => {
 | 
			
		||||
      if (ret.status === 'created') {
 | 
			
		||||
        if (ret.data.activities.length === 0) {
 | 
			
		||||
          dispatch(setError('activities: no correct file'))
 | 
			
		||||
      } else if (ret.data.activities.length === 1) {
 | 
			
		||||
        } else if (ret.data.activities.length === 1) {
 | 
			
		||||
          dispatch(loadProfile())
 | 
			
		||||
          history.push(`/activities/${ret.data.activities[0].id}`)
 | 
			
		||||
      } else { // ret.data.activities.length > 1
 | 
			
		||||
        } else {
 | 
			
		||||
          // ret.data.activities.length > 1
 | 
			
		||||
          dispatch(loadProfile())
 | 
			
		||||
          history.push('/')
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        dispatch(setError(`activities: ${ret.message}`))
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      dispatch(setError(`activities: ${ret.message}`))
 | 
			
		||||
    }
 | 
			
		||||
    dispatch(setLoading(false))
 | 
			
		||||
  })
 | 
			
		||||
  .catch(error => dispatch(setError(`activities: ${error}`)))
 | 
			
		||||
      dispatch(setLoading(false))
 | 
			
		||||
    })
 | 
			
		||||
    .catch(error => dispatch(setError(`activities: ${error}`)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export const addActivityWithoutGpx = form => dispatch => FitTrackeeGenericApi
 | 
			
		||||
  .addData('activities/no_gpx', form)
 | 
			
		||||
  .then(ret => {
 | 
			
		||||
    if (ret.status === 'created') {
 | 
			
		||||
      dispatch(loadProfile())
 | 
			
		||||
      history.push(`/activities/${ret.data.activities[0].id}`)
 | 
			
		||||
    } else {
 | 
			
		||||
      dispatch(setError(`activities: ${ret.message}`))
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
  .catch(error => dispatch(setError(`activities: ${error}`)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export const getActivityGpx = activityId => dispatch => {
 | 
			
		||||
  if (activityId) {
 | 
			
		||||
    return FitTrackeeGenericApi
 | 
			
		||||
    .getData(`activities/${activityId}/gpx`)
 | 
			
		||||
export const addActivityWithoutGpx = form => dispatch =>
 | 
			
		||||
  FitTrackeeGenericApi.addData('activities/no_gpx', form)
 | 
			
		||||
    .then(ret => {
 | 
			
		||||
      if (ret.status === 'success') {
 | 
			
		||||
         dispatch(setGpx(ret.data.gpx))
 | 
			
		||||
      if (ret.status === 'created') {
 | 
			
		||||
        dispatch(loadProfile())
 | 
			
		||||
        history.push(`/activities/${ret.data.activities[0].id}`)
 | 
			
		||||
      } else {
 | 
			
		||||
        dispatch(setError(`activities: ${ret.message}`))
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    .catch(error => dispatch(setError(`activities: ${error}`)))
 | 
			
		||||
 | 
			
		||||
export const getActivityGpx = activityId => dispatch => {
 | 
			
		||||
  if (activityId) {
 | 
			
		||||
    return FitTrackeeGenericApi.getData(`activities/${activityId}/gpx`)
 | 
			
		||||
      .then(ret => {
 | 
			
		||||
        if (ret.status === 'success') {
 | 
			
		||||
          dispatch(setGpx(ret.data.gpx))
 | 
			
		||||
        } else {
 | 
			
		||||
          dispatch(setError(`activities: ${ret.message}`))
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
      .catch(error => dispatch(setError(`activities: ${error}`)))
 | 
			
		||||
  }
 | 
			
		||||
  dispatch(setGpx(null))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getSegmentGpx = (activityId, segmentId) => dispatch => {
 | 
			
		||||
  if (activityId) {
 | 
			
		||||
    return FitTrackeeGenericApi
 | 
			
		||||
    .getData(`activities/${activityId}/gpx/segment/${segmentId}`)
 | 
			
		||||
    .then(ret => {
 | 
			
		||||
      if (ret.status === 'success') {
 | 
			
		||||
         dispatch(setGpx(ret.data.gpx))
 | 
			
		||||
      } else {
 | 
			
		||||
        dispatch(setError(`activities: ${ret.message}`))
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    .catch(error => dispatch(setError(`activities: ${error}`)))
 | 
			
		||||
    return FitTrackeeGenericApi.getData(
 | 
			
		||||
      `activities/${activityId}/gpx/segment/${segmentId}`
 | 
			
		||||
    )
 | 
			
		||||
      .then(ret => {
 | 
			
		||||
        if (ret.status === 'success') {
 | 
			
		||||
          dispatch(setGpx(ret.data.gpx))
 | 
			
		||||
        } else {
 | 
			
		||||
          dispatch(setError(`activities: ${ret.message}`))
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
      .catch(error => dispatch(setError(`activities: ${error}`)))
 | 
			
		||||
  }
 | 
			
		||||
  dispatch(setGpx(null))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export const getActivityChartData = activityId => dispatch => {
 | 
			
		||||
  if (activityId) {
 | 
			
		||||
    return FitTrackeeGenericApi
 | 
			
		||||
    .getData(`activities/${activityId}/chart_data`)
 | 
			
		||||
    .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}`)))
 | 
			
		||||
    return FitTrackeeGenericApi.getData(`activities/${activityId}/chart_data`)
 | 
			
		||||
      .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 getSegmentChartData = (activityId, segmentId) => dispatch => {
 | 
			
		||||
  if (activityId) {
 | 
			
		||||
    return FitTrackeeGenericApi
 | 
			
		||||
    .getData(`activities/${activityId}/chart_data/segment/${segmentId}`)
 | 
			
		||||
    return FitTrackeeGenericApi.getData(
 | 
			
		||||
      `activities/${activityId}/chart_data/segment/${segmentId}`
 | 
			
		||||
    )
 | 
			
		||||
      .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 =>
 | 
			
		||||
  FitTrackeeGenericApi.deleteData('activities', id)
 | 
			
		||||
    .then(ret => {
 | 
			
		||||
      if (ret.status === 204) {
 | 
			
		||||
        Promise.resolve(dispatch(removeActivity(id)))
 | 
			
		||||
          .then(() => dispatch(loadProfile()))
 | 
			
		||||
          .then(() => history.push('/'))
 | 
			
		||||
      } else {
 | 
			
		||||
        dispatch(setError(`activities: ${ret.status}`))
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    .catch(error => dispatch(setError(`activities: ${error}`)))
 | 
			
		||||
 | 
			
		||||
export const editActivity = form => dispatch =>
 | 
			
		||||
  FitTrackeeGenericApi.updateData('activities', form)
 | 
			
		||||
    .then(ret => {
 | 
			
		||||
      if (ret.status === 'success') {
 | 
			
		||||
         dispatch(setChartData(formatChartData(ret.data.chart_data)))
 | 
			
		||||
        dispatch(loadProfile())
 | 
			
		||||
        history.push(`/activities/${ret.data.activities[0].id}`)
 | 
			
		||||
      } else {
 | 
			
		||||
        dispatch(setError(`activities: ${ret.message}`))
 | 
			
		||||
      }
 | 
			
		||||
      dispatch(setLoading(false))
 | 
			
		||||
    })
 | 
			
		||||
    .catch(error => dispatch(setError(`activities: ${error}`)))
 | 
			
		||||
 | 
			
		||||
export const getMoreActivities = params => dispatch =>
 | 
			
		||||
  FitTrackeeGenericApi.getData('activities', params)
 | 
			
		||||
    .then(ret => {
 | 
			
		||||
      if (ret.status === 'success') {
 | 
			
		||||
        if (ret.data.activities.length > 0) {
 | 
			
		||||
          dispatch(pushActivities(ret.data.activities))
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        dispatch(setError(`activities: ${ret.message}`))
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    .catch(error => dispatch(setError(`activities: ${error}`)))
 | 
			
		||||
  }
 | 
			
		||||
  dispatch(setChartData(null))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export const deleteActivity = id => dispatch => FitTrackeeGenericApi
 | 
			
		||||
  .deleteData('activities', id)
 | 
			
		||||
  .then(ret => {
 | 
			
		||||
    if (ret.status === 204) {
 | 
			
		||||
      Promise.resolve(dispatch(removeActivity(id))).then(() =>
 | 
			
		||||
        dispatch(loadProfile())
 | 
			
		||||
      ).then(() => history.push('/'))
 | 
			
		||||
    } else {
 | 
			
		||||
      dispatch(setError(`activities: ${ret.status}`))
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
  .catch(error => dispatch(setError(`activities: ${error}`)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export const editActivity = form => dispatch => FitTrackeeGenericApi
 | 
			
		||||
  .updateData('activities', form)
 | 
			
		||||
  .then(ret => {
 | 
			
		||||
    if (ret.status === 'success') {
 | 
			
		||||
      dispatch(loadProfile())
 | 
			
		||||
      history.push(`/activities/${ret.data.activities[0].id}`)
 | 
			
		||||
    } else {
 | 
			
		||||
      dispatch(setError(`activities: ${ret.message}`))
 | 
			
		||||
    }
 | 
			
		||||
    dispatch(setLoading(false))
 | 
			
		||||
  })
 | 
			
		||||
  .catch(error => dispatch(setError(`activities: ${error}`)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export const getMoreActivities = params => dispatch => FitTrackeeGenericApi
 | 
			
		||||
  .getData('activities', params)
 | 
			
		||||
  .then(ret => {
 | 
			
		||||
    if (ret.status === 'success') {
 | 
			
		||||
      if (ret.data.activities.length > 0) {
 | 
			
		||||
        dispatch(pushActivities(ret.data.activities))
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      dispatch(setError(`activities: ${ret.message}`))
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
  .catch(error => dispatch(setError(`activities: ${error}`)))
 | 
			
		||||
 | 
			
		||||
export const getMonthActivities = (from, to) => dispatch =>
 | 
			
		||||
  FitTrackeeGenericApi
 | 
			
		||||
  .getData('activities', { from, to, order: 'asc', per_page: 100 })
 | 
			
		||||
  .then(ret => {
 | 
			
		||||
    if (ret.status === 'success') {
 | 
			
		||||
      dispatch(updateCalendar(ret.data.activities))
 | 
			
		||||
    } else {
 | 
			
		||||
      dispatch(setError(`activities: ${ret.message}`))
 | 
			
		||||
    }
 | 
			
		||||
  FitTrackeeGenericApi.getData('activities', {
 | 
			
		||||
    from,
 | 
			
		||||
    to,
 | 
			
		||||
    order: 'asc',
 | 
			
		||||
    per_page: 100,
 | 
			
		||||
  })
 | 
			
		||||
  .catch(error => dispatch(setError(`activities: ${error}`)))
 | 
			
		||||
    .then(ret => {
 | 
			
		||||
      if (ret.status === 'success') {
 | 
			
		||||
        dispatch(updateCalendar(ret.data.activities))
 | 
			
		||||
      } else {
 | 
			
		||||
        dispatch(setError(`activities: ${ret.message}`))
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    .catch(error => dispatch(setError(`activities: ${error}`)))
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
import FitTrackeeApi from '../fitTrackeeApi/index'
 | 
			
		||||
import { history } from '../index'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export const setData = (target, data) => ({
 | 
			
		||||
  type: 'SET_DATA',
 | 
			
		||||
  data,
 | 
			
		||||
@@ -15,7 +14,7 @@ export const setError = message => ({
 | 
			
		||||
 | 
			
		||||
export const setLoading = loading => ({
 | 
			
		||||
  type: 'SET_LOADING',
 | 
			
		||||
  loading
 | 
			
		||||
  loading,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export const getOrUpdateData = (action, target, data) => dispatch => {
 | 
			
		||||
@@ -23,39 +22,38 @@ export const getOrUpdateData = (action, target, data) => dispatch => {
 | 
			
		||||
    return dispatch(setError(target, `${target}: Incorrect id`))
 | 
			
		||||
  }
 | 
			
		||||
  return FitTrackeeApi[action](target, data)
 | 
			
		||||
  .then(ret => {
 | 
			
		||||
    if (ret.status === 'success') {
 | 
			
		||||
      dispatch(setData(target, ret.data))
 | 
			
		||||
    } else {
 | 
			
		||||
      dispatch(setError(`${target}: ${ret.message || ret.status}`))
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
  .catch(error => dispatch(setError(`${target}: ${error}`)))
 | 
			
		||||
    .then(ret => {
 | 
			
		||||
      if (ret.status === 'success') {
 | 
			
		||||
        dispatch(setData(target, ret.data))
 | 
			
		||||
      } else {
 | 
			
		||||
        dispatch(setError(`${target}: ${ret.message || ret.status}`))
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    .catch(error => dispatch(setError(`${target}: ${error}`)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const addData = (target, data) => dispatch => FitTrackeeApi
 | 
			
		||||
  .addData(target, data)
 | 
			
		||||
  .then(ret => {
 | 
			
		||||
    if (ret.status === 'created') {
 | 
			
		||||
      history.push(`/admin/${target}`)
 | 
			
		||||
    } else {
 | 
			
		||||
      dispatch(setError(`${target}: ${ret.status}`))
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
  .catch(error => dispatch(setError(`${target}: ${error}`)))
 | 
			
		||||
export const addData = (target, data) => dispatch =>
 | 
			
		||||
  FitTrackeeApi.addData(target, data)
 | 
			
		||||
    .then(ret => {
 | 
			
		||||
      if (ret.status === 'created') {
 | 
			
		||||
        history.push(`/admin/${target}`)
 | 
			
		||||
      } else {
 | 
			
		||||
        dispatch(setError(`${target}: ${ret.status}`))
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    .catch(error => dispatch(setError(`${target}: ${error}`)))
 | 
			
		||||
 | 
			
		||||
export const deleteData = (target, id) => dispatch => {
 | 
			
		||||
  if (isNaN(id)) {
 | 
			
		||||
    return dispatch(setError(target, `${target}: Incorrect id`))
 | 
			
		||||
  }
 | 
			
		||||
  return FitTrackeeApi
 | 
			
		||||
  .deleteData(target, id)
 | 
			
		||||
  .then(ret => {
 | 
			
		||||
    if (ret.status === 204) {
 | 
			
		||||
      history.push(`/admin/${target}`)
 | 
			
		||||
    } else {
 | 
			
		||||
      dispatch(setError(`${target}: ${ret.message || ret.status}`))
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
  .catch(error => dispatch(setError(`${target}: ${error}`)))
 | 
			
		||||
  return FitTrackeeApi.deleteData(target, id)
 | 
			
		||||
    .then(ret => {
 | 
			
		||||
      if (ret.status === 204) {
 | 
			
		||||
        history.push(`/admin/${target}`)
 | 
			
		||||
      } else {
 | 
			
		||||
        dispatch(setError(`${target}: ${ret.message || ret.status}`))
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    .catch(error => dispatch(setError(`${target}: ${error}`)))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
import FitTrackeeGenericApi from '../fitTrackeeApi'
 | 
			
		||||
import { setData, setError } from './index'
 | 
			
		||||
 | 
			
		||||
export const getStats = (userId, type, data) => dispatch => FitTrackeeGenericApi
 | 
			
		||||
    .getData(`stats/${userId}/${type}`, data)
 | 
			
		||||
export const getStats = (userId, type, data) => dispatch =>
 | 
			
		||||
  FitTrackeeGenericApi.getData(`stats/${userId}/${type}`, data)
 | 
			
		||||
    .then(ret => {
 | 
			
		||||
      if (ret.status === 'success') {
 | 
			
		||||
         dispatch(setData('statistics', ret.data))
 | 
			
		||||
        dispatch(setData('statistics', ret.data))
 | 
			
		||||
      } else {
 | 
			
		||||
        dispatch(setError(`statistics: ${ret.message}`))
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@ import { history } from '../index'
 | 
			
		||||
import { generateIds } from '../utils'
 | 
			
		||||
import { getOrUpdateData } from './index'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const AuthError = message => ({ type: 'AUTH_ERROR', message })
 | 
			
		||||
 | 
			
		||||
const AuthErrors = messages => ({ type: 'AUTH_ERRORS', messages })
 | 
			
		||||
@@ -16,7 +15,8 @@ const ProfileSuccess = profil => ({ type: 'PROFILE_SUCCESS', profil })
 | 
			
		||||
const ProfileError = message => ({ type: 'PROFILE_ERROR', message })
 | 
			
		||||
 | 
			
		||||
const ProfileUpdateError = message => ({
 | 
			
		||||
  type: 'PROFILE_UPDATE_ERROR', message
 | 
			
		||||
  type: 'PROFILE_UPDATE_ERROR',
 | 
			
		||||
  message,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export const logout = () => ({ type: 'LOGOUT' })
 | 
			
		||||
@@ -28,32 +28,32 @@ export const loadProfile = () => dispatch => {
 | 
			
		||||
  return { type: 'LOGOUT' }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getProfile = () => dispatch => FitTrackeeGenericApi
 | 
			
		||||
  .getData('auth/profile')
 | 
			
		||||
  .then(ret => {
 | 
			
		||||
    if (ret.status === 'success') {
 | 
			
		||||
      dispatch(getOrUpdateData('getData', 'sports'))
 | 
			
		||||
      ret.data.isAuthenticated = true
 | 
			
		||||
      return dispatch(ProfileSuccess(ret.data))
 | 
			
		||||
    }
 | 
			
		||||
    return dispatch(ProfileError(ret.message))
 | 
			
		||||
  })
 | 
			
		||||
  .catch(error => {
 | 
			
		||||
    throw error
 | 
			
		||||
  })
 | 
			
		||||
export const getProfile = () => dispatch =>
 | 
			
		||||
  FitTrackeeGenericApi.getData('auth/profile')
 | 
			
		||||
    .then(ret => {
 | 
			
		||||
      if (ret.status === 'success') {
 | 
			
		||||
        dispatch(getOrUpdateData('getData', 'sports'))
 | 
			
		||||
        ret.data.isAuthenticated = true
 | 
			
		||||
        return dispatch(ProfileSuccess(ret.data))
 | 
			
		||||
      }
 | 
			
		||||
      return dispatch(ProfileError(ret.message))
 | 
			
		||||
    })
 | 
			
		||||
    .catch(error => {
 | 
			
		||||
      throw error
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
export const loginOrRegister = (target, formData) => dispatch => FitTrackeeApi
 | 
			
		||||
  .loginOrRegister(target, formData)
 | 
			
		||||
  .then(ret => {
 | 
			
		||||
    if (ret.status === 'success') {
 | 
			
		||||
      window.localStorage.setItem('authToken', ret.auth_token)
 | 
			
		||||
      return dispatch(getProfile())
 | 
			
		||||
    }
 | 
			
		||||
    return dispatch(AuthError(ret.message))
 | 
			
		||||
  })
 | 
			
		||||
  .catch(error => {
 | 
			
		||||
    throw error
 | 
			
		||||
  })
 | 
			
		||||
export const loginOrRegister = (target, formData) => dispatch =>
 | 
			
		||||
  FitTrackeeApi.loginOrRegister(target, formData)
 | 
			
		||||
    .then(ret => {
 | 
			
		||||
      if (ret.status === 'success') {
 | 
			
		||||
        window.localStorage.setItem('authToken', ret.auth_token)
 | 
			
		||||
        return dispatch(getProfile())
 | 
			
		||||
      }
 | 
			
		||||
      return dispatch(AuthError(ret.message))
 | 
			
		||||
    })
 | 
			
		||||
    .catch(error => {
 | 
			
		||||
      throw error
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
const RegisterFormControl = formData => {
 | 
			
		||||
  const errMsg = []
 | 
			
		||||
@@ -61,7 +61,7 @@ const RegisterFormControl = formData => {
 | 
			
		||||
    errMsg.push('Username: 3 to 12 characters required.')
 | 
			
		||||
  }
 | 
			
		||||
  if (formData.password !== formData.password_conf) {
 | 
			
		||||
    errMsg.push('Password and password confirmation don\'t match.')
 | 
			
		||||
    errMsg.push("Password and password confirmation don't match.")
 | 
			
		||||
  }
 | 
			
		||||
  if (formData.password.length < 8) {
 | 
			
		||||
    errMsg.push('Password: 8 characters required.')
 | 
			
		||||
@@ -81,13 +81,12 @@ export const handleUserFormSubmit = (formData, formType) => dispatch => {
 | 
			
		||||
 | 
			
		||||
export const handleProfileFormSubmit = formData => dispatch => {
 | 
			
		||||
  if (!formData.password === formData.password_conf) {
 | 
			
		||||
    return dispatch(ProfileUpdateError(
 | 
			
		||||
      'Password and password confirmation don\'t match.'
 | 
			
		||||
    ))
 | 
			
		||||
    return dispatch(
 | 
			
		||||
      ProfileUpdateError("Password and password confirmation don't match.")
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
  delete formData.id
 | 
			
		||||
  return FitTrackeeGenericApi
 | 
			
		||||
    .postData('auth/profile/edit', formData)
 | 
			
		||||
  return FitTrackeeGenericApi.postData('auth/profile/edit', formData)
 | 
			
		||||
    .then(ret => {
 | 
			
		||||
      if (ret.status === 'success') {
 | 
			
		||||
        dispatch(getProfile())
 | 
			
		||||
@@ -105,8 +104,7 @@ export const uploadPicture = event => dispatch => {
 | 
			
		||||
  const form = new FormData()
 | 
			
		||||
  form.append('file', event.target.picture.files[0])
 | 
			
		||||
  event.target.reset()
 | 
			
		||||
  return FitTrackeeGenericApi
 | 
			
		||||
    .addDataWithFile('auth/picture', form)
 | 
			
		||||
  return FitTrackeeGenericApi.addDataWithFile('auth/picture', form)
 | 
			
		||||
    .then(ret => {
 | 
			
		||||
      if (ret.status === 'success') {
 | 
			
		||||
        return dispatch(getProfile())
 | 
			
		||||
@@ -118,14 +116,14 @@ export const uploadPicture = event => dispatch => {
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const deletePicture = () => dispatch => FitTrackeeApi
 | 
			
		||||
  .deletePicture()
 | 
			
		||||
  .then(ret => {
 | 
			
		||||
    if (ret.status === 204) {
 | 
			
		||||
      return dispatch(getProfile())
 | 
			
		||||
    }
 | 
			
		||||
    return dispatch(PictureError(ret.message))
 | 
			
		||||
  })
 | 
			
		||||
  .catch(error => {
 | 
			
		||||
    throw error
 | 
			
		||||
  })
 | 
			
		||||
export const deletePicture = () => dispatch =>
 | 
			
		||||
  FitTrackeeApi.deletePicture()
 | 
			
		||||
    .then(ret => {
 | 
			
		||||
      if (ret.status === 204) {
 | 
			
		||||
        return dispatch(getProfile())
 | 
			
		||||
      }
 | 
			
		||||
      return dispatch(PictureError(ret.message))
 | 
			
		||||
    })
 | 
			
		||||
    .catch(error => {
 | 
			
		||||
      throw error
 | 
			
		||||
    })
 | 
			
		||||
 
 | 
			
		||||
@@ -12,48 +12,49 @@ export default class ActivitiesList extends React.PureComponent {
 | 
			
		||||
        <div className="card-body">
 | 
			
		||||
          <table className="table">
 | 
			
		||||
            <thead>
 | 
			
		||||
            <tr>
 | 
			
		||||
              <th scope="col" />
 | 
			
		||||
              <th scope="col">Workout</th>
 | 
			
		||||
              <th scope="col">Date</th>
 | 
			
		||||
              <th scope="col">Distance</th>
 | 
			
		||||
              <th scope="col">Duration</th>
 | 
			
		||||
              <th scope="col">Ave. speed</th>
 | 
			
		||||
              <th scope="col">Max. speed</th>
 | 
			
		||||
            </tr>
 | 
			
		||||
              <tr>
 | 
			
		||||
                <th scope="col" />
 | 
			
		||||
                <th scope="col">Workout</th>
 | 
			
		||||
                <th scope="col">Date</th>
 | 
			
		||||
                <th scope="col">Distance</th>
 | 
			
		||||
                <th scope="col">Duration</th>
 | 
			
		||||
                <th scope="col">Ave. speed</th>
 | 
			
		||||
                <th scope="col">Max. speed</th>
 | 
			
		||||
              </tr>
 | 
			
		||||
            </thead>
 | 
			
		||||
            <tbody>
 | 
			
		||||
            {sports && activities.map((activity, idx) => (
 | 
			
		||||
              // eslint-disable-next-line react/no-array-index-key
 | 
			
		||||
              <tr key={idx}>
 | 
			
		||||
                <td>
 | 
			
		||||
                  <img
 | 
			
		||||
                    className="activity-sport"
 | 
			
		||||
                    src={sports
 | 
			
		||||
                      .filter(s => s.id === activity.sport_id)
 | 
			
		||||
                      .map(s => s.img)}
 | 
			
		||||
                    alt="activity sport logo"
 | 
			
		||||
                  />
 | 
			
		||||
                </td>
 | 
			
		||||
                <td>
 | 
			
		||||
                  <Link to={`/activities/${activity.id}`}>
 | 
			
		||||
                    {activity.title}
 | 
			
		||||
                  </Link>
 | 
			
		||||
                </td>
 | 
			
		||||
                <td>
 | 
			
		||||
                  {format(
 | 
			
		||||
                    getDateWithTZ(activity.activity_date, user.timezone),
 | 
			
		||||
                    'dd/MM/yyyy HH:mm'
 | 
			
		||||
                  )}
 | 
			
		||||
                </td>
 | 
			
		||||
                <td className="text-right">
 | 
			
		||||
                  {Number(activity.distance).toFixed(2)} km
 | 
			
		||||
                </td>
 | 
			
		||||
                <td className="text-right">{activity.moving}</td>
 | 
			
		||||
                <td className="text-right">{activity.ave_speed} km/h</td>
 | 
			
		||||
                <td className="text-right">{activity.max_speed} km/h</td>
 | 
			
		||||
              </tr>
 | 
			
		||||
            ))}
 | 
			
		||||
              {sports &&
 | 
			
		||||
                activities.map((activity, idx) => (
 | 
			
		||||
                  // eslint-disable-next-line react/no-array-index-key
 | 
			
		||||
                  <tr key={idx}>
 | 
			
		||||
                    <td>
 | 
			
		||||
                      <img
 | 
			
		||||
                        className="activity-sport"
 | 
			
		||||
                        src={sports
 | 
			
		||||
                          .filter(s => s.id === activity.sport_id)
 | 
			
		||||
                          .map(s => s.img)}
 | 
			
		||||
                        alt="activity sport logo"
 | 
			
		||||
                      />
 | 
			
		||||
                    </td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                      <Link to={`/activities/${activity.id}`}>
 | 
			
		||||
                        {activity.title}
 | 
			
		||||
                      </Link>
 | 
			
		||||
                    </td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                      {format(
 | 
			
		||||
                        getDateWithTZ(activity.activity_date, user.timezone),
 | 
			
		||||
                        'dd/MM/yyyy HH:mm'
 | 
			
		||||
                      )}
 | 
			
		||||
                    </td>
 | 
			
		||||
                    <td className="text-right">
 | 
			
		||||
                      {Number(activity.distance).toFixed(2)} km
 | 
			
		||||
                    </td>
 | 
			
		||||
                    <td className="text-right">{activity.moving}</td>
 | 
			
		||||
                    <td className="text-right">{activity.ave_speed} km/h</td>
 | 
			
		||||
                    <td className="text-right">{activity.max_speed} km/h</td>
 | 
			
		||||
                  </tr>
 | 
			
		||||
                ))}
 | 
			
		||||
            </tbody>
 | 
			
		||||
          </table>
 | 
			
		||||
        </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,6 @@ import ActivitiesList from './ActivitiesList'
 | 
			
		||||
import { getOrUpdateData } from '../../actions'
 | 
			
		||||
import { getMoreActivities } from '../../actions/activities'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Activities extends React.Component {
 | 
			
		||||
  constructor(props, context) {
 | 
			
		||||
    super(props, context)
 | 
			
		||||
@@ -36,12 +35,18 @@ class Activities extends React.Component {
 | 
			
		||||
  }
 | 
			
		||||
  render() {
 | 
			
		||||
    const {
 | 
			
		||||
      activities, loadActivities, loadMoreActivities, message, sports, user
 | 
			
		||||
      activities,
 | 
			
		||||
      loadActivities,
 | 
			
		||||
      loadMoreActivities,
 | 
			
		||||
      message,
 | 
			
		||||
      sports,
 | 
			
		||||
      user,
 | 
			
		||||
    } = this.props
 | 
			
		||||
    const { params } = this.state
 | 
			
		||||
    const paginationEnd = activities.length > 0
 | 
			
		||||
      ? activities[activities.length - 1].previous_activity === null
 | 
			
		||||
      : true
 | 
			
		||||
    const paginationEnd =
 | 
			
		||||
      activities.length > 0
 | 
			
		||||
        ? activities[activities.length - 1].previous_activity === null
 | 
			
		||||
        : true
 | 
			
		||||
    return (
 | 
			
		||||
      <div>
 | 
			
		||||
        <Helmet>
 | 
			
		||||
@@ -65,7 +70,7 @@ class Activities extends React.Component {
 | 
			
		||||
                  sports={sports}
 | 
			
		||||
                  user={user}
 | 
			
		||||
                />
 | 
			
		||||
                {!paginationEnd &&
 | 
			
		||||
                {!paginationEnd && (
 | 
			
		||||
                  <input
 | 
			
		||||
                    type="submit"
 | 
			
		||||
                    className="btn btn-default btn-md btn-block"
 | 
			
		||||
@@ -76,11 +81,11 @@ class Activities extends React.Component {
 | 
			
		||||
                      this.setState(params)
 | 
			
		||||
                    }}
 | 
			
		||||
                  />
 | 
			
		||||
                }
 | 
			
		||||
                )}
 | 
			
		||||
                {activities.length === 0 && (
 | 
			
		||||
                  <div className="card text-center">
 | 
			
		||||
                    <div className="card-body">
 | 
			
		||||
                      No workouts. {' '}
 | 
			
		||||
                      No workouts.{' '}
 | 
			
		||||
                      <Link to={{ pathname: '/activities/add' }}>
 | 
			
		||||
                        Upload one !
 | 
			
		||||
                      </Link>
 | 
			
		||||
 
 | 
			
		||||
@@ -3,24 +3,17 @@ import { connect } from 'react-redux'
 | 
			
		||||
 | 
			
		||||
import ActivityAddOrEdit from './ActivityAddOrEdit'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function ActivityAdd (props) {
 | 
			
		||||
function ActivityAdd(props) {
 | 
			
		||||
  const { message, sports } = props
 | 
			
		||||
  return (
 | 
			
		||||
    <div>
 | 
			
		||||
      <ActivityAddOrEdit
 | 
			
		||||
        activity={null}
 | 
			
		||||
        message={message}
 | 
			
		||||
        sports={sports}
 | 
			
		||||
      />
 | 
			
		||||
      <ActivityAddOrEdit activity={null} message={message} sports={sports} />
 | 
			
		||||
    </div>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default connect(
 | 
			
		||||
  state => ({
 | 
			
		||||
    message: state.message,
 | 
			
		||||
    sports: state.sports.data,
 | 
			
		||||
    user: state.user,
 | 
			
		||||
  }),
 | 
			
		||||
)(ActivityAdd)
 | 
			
		||||
export default connect(state => ({
 | 
			
		||||
  message: state.message,
 | 
			
		||||
  sports: state.sports.data,
 | 
			
		||||
  user: state.user,
 | 
			
		||||
}))(ActivityAdd)
 | 
			
		||||
 
 | 
			
		||||
@@ -13,11 +13,12 @@ class ActivityAddEdit extends React.Component {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleRadioChange (changeEvent) {
 | 
			
		||||
  handleRadioChange(changeEvent) {
 | 
			
		||||
    this.setState({
 | 
			
		||||
      withGpx:
 | 
			
		||||
        changeEvent.target.name === 'withGpx'
 | 
			
		||||
          ? changeEvent.target.value : !changeEvent.target.value
 | 
			
		||||
          ? changeEvent.target.value
 | 
			
		||||
          : !changeEvent.target.value,
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -27,15 +28,13 @@ class ActivityAddEdit extends React.Component {
 | 
			
		||||
    return (
 | 
			
		||||
      <div>
 | 
			
		||||
        <Helmet>
 | 
			
		||||
          <title>FitTrackee - {activity
 | 
			
		||||
            ? 'Edit a workout'
 | 
			
		||||
            : 'Add a workout'}
 | 
			
		||||
            </title>
 | 
			
		||||
          <title>
 | 
			
		||||
            FitTrackee - {activity ? 'Edit a workout' : 'Add a workout'}
 | 
			
		||||
          </title>
 | 
			
		||||
        </Helmet>
 | 
			
		||||
        <br /><br />
 | 
			
		||||
        {message && (
 | 
			
		||||
          <code>{message}</code>
 | 
			
		||||
        )}
 | 
			
		||||
        <br />
 | 
			
		||||
        <br />
 | 
			
		||||
        {message && <code>{message}</code>}
 | 
			
		||||
        <div className="container">
 | 
			
		||||
          <div className="row">
 | 
			
		||||
            <div className="col-md-2" />
 | 
			
		||||
@@ -57,27 +56,31 @@ class ActivityAddEdit extends React.Component {
 | 
			
		||||
                        <div className="form-group row">
 | 
			
		||||
                          <div className="col">
 | 
			
		||||
                            <label className="radioLabel">
 | 
			
		||||
                            <input
 | 
			
		||||
                              className="add-activity-radio"
 | 
			
		||||
                              type="radio"
 | 
			
		||||
                              name="withGpx"
 | 
			
		||||
                              disabled={loading}
 | 
			
		||||
                              checked={withGpx}
 | 
			
		||||
                              onChange={event => this.handleRadioChange(event)}
 | 
			
		||||
                            />
 | 
			
		||||
                              <input
 | 
			
		||||
                                className="add-activity-radio"
 | 
			
		||||
                                type="radio"
 | 
			
		||||
                                name="withGpx"
 | 
			
		||||
                                disabled={loading}
 | 
			
		||||
                                checked={withGpx}
 | 
			
		||||
                                onChange={event =>
 | 
			
		||||
                                  this.handleRadioChange(event)
 | 
			
		||||
                                }
 | 
			
		||||
                              />
 | 
			
		||||
                              with gpx file
 | 
			
		||||
                            </label>
 | 
			
		||||
                          </div>
 | 
			
		||||
                          <div className="col">
 | 
			
		||||
                            <label className="radioLabel">
 | 
			
		||||
                            <input
 | 
			
		||||
                              className="add-activity-radio"
 | 
			
		||||
                              type="radio"
 | 
			
		||||
                              name="withoutGpx"
 | 
			
		||||
                              disabled={loading}
 | 
			
		||||
                              checked={!withGpx}
 | 
			
		||||
                              onChange={event => this.handleRadioChange(event)}
 | 
			
		||||
                            />
 | 
			
		||||
                              <input
 | 
			
		||||
                                className="add-activity-radio"
 | 
			
		||||
                                type="radio"
 | 
			
		||||
                                name="withoutGpx"
 | 
			
		||||
                                disabled={loading}
 | 
			
		||||
                                checked={!withGpx}
 | 
			
		||||
                                onChange={event =>
 | 
			
		||||
                                  this.handleRadioChange(event)
 | 
			
		||||
                                }
 | 
			
		||||
                              />
 | 
			
		||||
                              without gpx file
 | 
			
		||||
                            </label>
 | 
			
		||||
                          </div>
 | 
			
		||||
@@ -101,8 +104,6 @@ class ActivityAddEdit extends React.Component {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default connect(
 | 
			
		||||
  state => ({
 | 
			
		||||
    loading: state.loading
 | 
			
		||||
  }),
 | 
			
		||||
)(ActivityAddEdit)
 | 
			
		||||
export default connect(state => ({
 | 
			
		||||
  loading: state.loading,
 | 
			
		||||
}))(ActivityAddEdit)
 | 
			
		||||
 
 | 
			
		||||
@@ -4,26 +4,30 @@ import { Link } from 'react-router-dom'
 | 
			
		||||
import { getDateWithTZ } from '../../../utils'
 | 
			
		||||
import { formatActivityDate } from '../../../utils/activities'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export default function ActivityCardHeader(props) {
 | 
			
		||||
  const {
 | 
			
		||||
    activity, dataType, displayModal, segmentId, sport, title, user
 | 
			
		||||
    activity,
 | 
			
		||||
    dataType,
 | 
			
		||||
    displayModal,
 | 
			
		||||
    segmentId,
 | 
			
		||||
    sport,
 | 
			
		||||
    title,
 | 
			
		||||
    user,
 | 
			
		||||
  } = props
 | 
			
		||||
  const activityDate = activity
 | 
			
		||||
    ? formatActivityDate(
 | 
			
		||||
        getDateWithTZ(activity.activity_date, user.timezone)
 | 
			
		||||
      )
 | 
			
		||||
    ? formatActivityDate(getDateWithTZ(activity.activity_date, user.timezone))
 | 
			
		||||
    : null
 | 
			
		||||
 | 
			
		||||
  const previousUrl = dataType === 'segment' && segmentId !== 1
 | 
			
		||||
    ? `/activities/${activity.id}/segment/${segmentId - 1}`
 | 
			
		||||
    : dataType === 'activity' && activity.previous_activity
 | 
			
		||||
  const previousUrl =
 | 
			
		||||
    dataType === 'segment' && segmentId !== 1
 | 
			
		||||
      ? `/activities/${activity.id}/segment/${segmentId - 1}`
 | 
			
		||||
      : dataType === 'activity' && activity.previous_activity
 | 
			
		||||
      ? `/activities/${activity.previous_activity}`
 | 
			
		||||
      : null
 | 
			
		||||
    const nextUrl =
 | 
			
		||||
      dataType === 'segment' && segmentId < activity.segments.length
 | 
			
		||||
    ? `/activities/${activity.id}/segment/${segmentId + 1}`
 | 
			
		||||
    : dataType === 'activity' && activity.next_activity
 | 
			
		||||
  const nextUrl =
 | 
			
		||||
    dataType === 'segment' && segmentId < activity.segments.length
 | 
			
		||||
      ? `/activities/${activity.id}/segment/${segmentId + 1}`
 | 
			
		||||
      : dataType === 'activity' && activity.next_activity
 | 
			
		||||
      ? `/activities/${activity.next_activity}`
 | 
			
		||||
      : null
 | 
			
		||||
 | 
			
		||||
@@ -32,10 +36,7 @@ export default function ActivityCardHeader(props) {
 | 
			
		||||
      <div className="row">
 | 
			
		||||
        <div className="col-auto">
 | 
			
		||||
          {previousUrl ? (
 | 
			
		||||
            <Link
 | 
			
		||||
              className="unlink"
 | 
			
		||||
              to={previousUrl}
 | 
			
		||||
            >
 | 
			
		||||
            <Link className="unlink" to={previousUrl}>
 | 
			
		||||
              <i
 | 
			
		||||
                className="fa fa-chevron-left"
 | 
			
		||||
                aria-hidden="true"
 | 
			
		||||
@@ -51,35 +52,29 @@ export default function ActivityCardHeader(props) {
 | 
			
		||||
          )}
 | 
			
		||||
        </div>
 | 
			
		||||
        <div className="col-auto col-activity-logo">
 | 
			
		||||
          <img
 | 
			
		||||
            className="sport-img-medium"
 | 
			
		||||
            src={sport.img}
 | 
			
		||||
            alt="sport logo"
 | 
			
		||||
          />
 | 
			
		||||
          <img className="sport-img-medium" src={sport.img} alt="sport logo" />
 | 
			
		||||
        </div>
 | 
			
		||||
        <div className="col">
 | 
			
		||||
          {dataType === 'activity' ? (
 | 
			
		||||
          <>
 | 
			
		||||
            {title}{' '}
 | 
			
		||||
            <Link
 | 
			
		||||
              className="unlink"
 | 
			
		||||
              to={`/activities/${activity.id}/edit`}
 | 
			
		||||
            >
 | 
			
		||||
            <>
 | 
			
		||||
              {title}{' '}
 | 
			
		||||
              <Link className="unlink" to={`/activities/${activity.id}/edit`}>
 | 
			
		||||
                <i
 | 
			
		||||
                  className="fa fa-edit custom-fa"
 | 
			
		||||
                  aria-hidden="true"
 | 
			
		||||
                  title="Edit activity"
 | 
			
		||||
                />
 | 
			
		||||
              </Link>
 | 
			
		||||
              <i
 | 
			
		||||
                className="fa fa-edit custom-fa"
 | 
			
		||||
                className="fa fa-trash custom-fa"
 | 
			
		||||
                aria-hidden="true"
 | 
			
		||||
                title="Edit activity"
 | 
			
		||||
                onClick={() => displayModal(true)}
 | 
			
		||||
                title="Delete activity"
 | 
			
		||||
              />
 | 
			
		||||
            </Link>
 | 
			
		||||
            <i
 | 
			
		||||
              className="fa fa-trash custom-fa"
 | 
			
		||||
              aria-hidden="true"
 | 
			
		||||
              onClick={() => displayModal(true)}
 | 
			
		||||
              title="Delete activity"
 | 
			
		||||
            />
 | 
			
		||||
          </>
 | 
			
		||||
            </>
 | 
			
		||||
          ) : (
 | 
			
		||||
            <>
 | 
			
		||||
              {/* prettier-ignore */}
 | 
			
		||||
              <Link
 | 
			
		||||
                to={`/activities/${activity.id}`}
 | 
			
		||||
              >
 | 
			
		||||
@@ -91,16 +86,13 @@ export default function ActivityCardHeader(props) {
 | 
			
		||||
          <br />
 | 
			
		||||
          {activityDate && (
 | 
			
		||||
            <span className="activity-date">
 | 
			
		||||
          {`${activityDate.activity_date} - ${activityDate.activity_time}`}
 | 
			
		||||
        </span>
 | 
			
		||||
              {`${activityDate.activity_date} - ${activityDate.activity_time}`}
 | 
			
		||||
            </span>
 | 
			
		||||
          )}
 | 
			
		||||
        </div>
 | 
			
		||||
        <div className="col-auto">
 | 
			
		||||
          {nextUrl ? (
 | 
			
		||||
            <Link
 | 
			
		||||
              className="unlink"
 | 
			
		||||
              to={nextUrl}
 | 
			
		||||
            >
 | 
			
		||||
            <Link className="unlink" to={nextUrl}>
 | 
			
		||||
              <i
 | 
			
		||||
                className="fa fa-chevron-right"
 | 
			
		||||
                aria-hidden="true"
 | 
			
		||||
 
 | 
			
		||||
@@ -2,20 +2,26 @@ import { format } from 'date-fns'
 | 
			
		||||
import React from 'react'
 | 
			
		||||
import { connect } from 'react-redux'
 | 
			
		||||
import {
 | 
			
		||||
   Area, ComposedChart, Line, ResponsiveContainer, Tooltip, XAxis, YAxis
 | 
			
		||||
  Area,
 | 
			
		||||
  ComposedChart,
 | 
			
		||||
  Line,
 | 
			
		||||
  ResponsiveContainer,
 | 
			
		||||
  Tooltip,
 | 
			
		||||
  XAxis,
 | 
			
		||||
  YAxis,
 | 
			
		||||
} from 'recharts'
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  getActivityChartData, getSegmentChartData
 | 
			
		||||
  getActivityChartData,
 | 
			
		||||
  getSegmentChartData,
 | 
			
		||||
} from '../../../actions/activities'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ActivityCharts extends React.Component {
 | 
			
		||||
  constructor(props, context) {
 | 
			
		||||
    super(props, context)
 | 
			
		||||
    this.state = {
 | 
			
		||||
      displayDistance: true,
 | 
			
		||||
      dataToHide: []
 | 
			
		||||
      dataToHide: [],
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -28,13 +34,15 @@ class ActivityCharts extends React.Component {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  componentDidUpdate(prevProps) {
 | 
			
		||||
    if (this.props.dataType === 'activity' && (
 | 
			
		||||
      prevProps.activity.id !== this.props.activity.id)
 | 
			
		||||
    if (
 | 
			
		||||
      this.props.dataType === 'activity' &&
 | 
			
		||||
      prevProps.activity.id !== this.props.activity.id
 | 
			
		||||
    ) {
 | 
			
		||||
        this.props.loadActivityData(this.props.activity.id)
 | 
			
		||||
      this.props.loadActivityData(this.props.activity.id)
 | 
			
		||||
    }
 | 
			
		||||
    if (this.props.dataType === 'segment' && (
 | 
			
		||||
      prevProps.segmentId !== this.props.segmentId)
 | 
			
		||||
    if (
 | 
			
		||||
      this.props.dataType === 'segment' &&
 | 
			
		||||
      prevProps.segmentId !== this.props.segmentId
 | 
			
		||||
    ) {
 | 
			
		||||
      this.props.loadSegmentData(this.props.activity.id, this.props.segmentId)
 | 
			
		||||
    }
 | 
			
		||||
@@ -44,16 +52,16 @@ class ActivityCharts extends React.Component {
 | 
			
		||||
    this.props.loadActivityData(null)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleRadioChange (changeEvent) {
 | 
			
		||||
  handleRadioChange(changeEvent) {
 | 
			
		||||
    this.setState({
 | 
			
		||||
      displayDistance:
 | 
			
		||||
        changeEvent.target.name === 'distance'
 | 
			
		||||
          ? changeEvent.target.value
 | 
			
		||||
          : !changeEvent.target.value
 | 
			
		||||
          : !changeEvent.target.value,
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleLegendChange (e) {
 | 
			
		||||
  handleLegendChange(e) {
 | 
			
		||||
    const { dataToHide } = this.state
 | 
			
		||||
    const name = e.target.name // eslint-disable-line prefer-destructuring
 | 
			
		||||
    if (dataToHide.find(d => d === name)) {
 | 
			
		||||
@@ -64,7 +72,7 @@ class ActivityCharts extends React.Component {
 | 
			
		||||
    this.setState({ dataToHide })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  displayData (name) {
 | 
			
		||||
  displayData(name) {
 | 
			
		||||
    const { dataToHide } = this.state
 | 
			
		||||
    return !dataToHide.find(d => d === name)
 | 
			
		||||
  }
 | 
			
		||||
@@ -141,22 +149,27 @@ class ActivityCharts extends React.Component {
 | 
			
		||||
                    label={{ value: xDataKey, offset: 0, position: 'bottom' }}
 | 
			
		||||
                    scale={xScale}
 | 
			
		||||
                    interval={xInterval}
 | 
			
		||||
                    tickFormatter={value => displayDistance
 | 
			
		||||
                                            ? value
 | 
			
		||||
                                            : format(value, 'HH:mm:ss')}
 | 
			
		||||
                    tickFormatter={value =>
 | 
			
		||||
                      displayDistance ? value : format(value, 'HH:mm:ss')
 | 
			
		||||
                    }
 | 
			
		||||
                    type="number"
 | 
			
		||||
                  />
 | 
			
		||||
                  <YAxis
 | 
			
		||||
                    label={{
 | 
			
		||||
                      value: 'speed (km/h)', angle: -90, position: 'left'
 | 
			
		||||
                      value: 'speed (km/h)',
 | 
			
		||||
                      angle: -90,
 | 
			
		||||
                      position: 'left',
 | 
			
		||||
                    }}
 | 
			
		||||
                    yAxisId="left"
 | 
			
		||||
                  />
 | 
			
		||||
                  <YAxis
 | 
			
		||||
                    label={{
 | 
			
		||||
                      value: 'altitude (m)', angle: -90, position: 'right'
 | 
			
		||||
                      value: 'altitude (m)',
 | 
			
		||||
                      angle: -90,
 | 
			
		||||
                      position: 'right',
 | 
			
		||||
                    }}
 | 
			
		||||
                    yAxisId="right" orientation="right"
 | 
			
		||||
                    yAxisId="right"
 | 
			
		||||
                    orientation="right"
 | 
			
		||||
                  />
 | 
			
		||||
                  {this.displayData('elevation') && (
 | 
			
		||||
                    <Area
 | 
			
		||||
@@ -181,9 +194,11 @@ class ActivityCharts extends React.Component {
 | 
			
		||||
                    />
 | 
			
		||||
                  )}
 | 
			
		||||
                  <Tooltip
 | 
			
		||||
                    labelFormatter={value => displayDistance
 | 
			
		||||
                                    ? `distance: ${value} km`
 | 
			
		||||
                                    : `duration: ${format(value, 'HH:mm:ss')}`}
 | 
			
		||||
                    labelFormatter={value =>
 | 
			
		||||
                      displayDistance
 | 
			
		||||
                        ? `distance: ${value} km`
 | 
			
		||||
                        : `duration: ${format(value, 'HH:mm:ss')}`
 | 
			
		||||
                    }
 | 
			
		||||
                  />
 | 
			
		||||
                </ComposedChart>
 | 
			
		||||
              </ResponsiveContainer>
 | 
			
		||||
@@ -202,7 +217,7 @@ class ActivityCharts extends React.Component {
 | 
			
		||||
 | 
			
		||||
export default connect(
 | 
			
		||||
  state => ({
 | 
			
		||||
    chartData: state.chartData
 | 
			
		||||
    chartData: state.chartData,
 | 
			
		||||
  }),
 | 
			
		||||
  dispatch => ({
 | 
			
		||||
    loadActivityData: activityId => {
 | 
			
		||||
 
 | 
			
		||||
@@ -8,20 +8,14 @@ export default function ActivityDetails(props) {
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="activity-details">
 | 
			
		||||
      <p>
 | 
			
		||||
        <i
 | 
			
		||||
          className="fa fa-clock-o custom-fa"
 | 
			
		||||
          aria-hidden="true"
 | 
			
		||||
        />
 | 
			
		||||
        <i className="fa fa-clock-o custom-fa" aria-hidden="true" />
 | 
			
		||||
        Duration: {activity.moving}
 | 
			
		||||
        {activity.records && activity.records.find(r => r.record_type === 'LD'
 | 
			
		||||
        ) && (
 | 
			
		||||
          <sup>
 | 
			
		||||
            <i
 | 
			
		||||
              className="fa fa-trophy custom-fa"
 | 
			
		||||
              aria-hidden="true"
 | 
			
		||||
            />
 | 
			
		||||
          </sup>
 | 
			
		||||
        )}
 | 
			
		||||
        {activity.records &&
 | 
			
		||||
          activity.records.find(r => r.record_type === 'LD') && (
 | 
			
		||||
            <sup>
 | 
			
		||||
              <i className="fa fa-trophy custom-fa" aria-hidden="true" />
 | 
			
		||||
            </sup>
 | 
			
		||||
          )}
 | 
			
		||||
        {withPauses && (
 | 
			
		||||
          <span>
 | 
			
		||||
            <br />
 | 
			
		||||
@@ -30,47 +24,32 @@ export default function ActivityDetails(props) {
 | 
			
		||||
        )}
 | 
			
		||||
      </p>
 | 
			
		||||
      <p>
 | 
			
		||||
        <i
 | 
			
		||||
          className="fa fa-road custom-fa"
 | 
			
		||||
          aria-hidden="true"
 | 
			
		||||
        />
 | 
			
		||||
        <i className="fa fa-road custom-fa" aria-hidden="true" />
 | 
			
		||||
        Distance: {activity.distance} km
 | 
			
		||||
        {activity.records && activity.records.find(r => r.record_type === 'FD'
 | 
			
		||||
        ) && (
 | 
			
		||||
          <sup>
 | 
			
		||||
            <i
 | 
			
		||||
              className="fa fa-trophy custom-fa"
 | 
			
		||||
              aria-hidden="true"
 | 
			
		||||
            />
 | 
			
		||||
          </sup>
 | 
			
		||||
        )}
 | 
			
		||||
        {activity.records &&
 | 
			
		||||
          activity.records.find(r => r.record_type === 'FD') && (
 | 
			
		||||
            <sup>
 | 
			
		||||
              <i className="fa fa-trophy custom-fa" aria-hidden="true" />
 | 
			
		||||
            </sup>
 | 
			
		||||
          )}
 | 
			
		||||
      </p>
 | 
			
		||||
      <p>
 | 
			
		||||
        <i
 | 
			
		||||
          className="fa fa-tachometer custom-fa"
 | 
			
		||||
          aria-hidden="true"
 | 
			
		||||
        />
 | 
			
		||||
        <i className="fa fa-tachometer custom-fa" aria-hidden="true" />
 | 
			
		||||
        Average speed: {activity.ave_speed} km/h
 | 
			
		||||
        {activity.records && activity.records.find(r => r.record_type === 'AS'
 | 
			
		||||
        ) && (
 | 
			
		||||
          <sup>
 | 
			
		||||
            <i
 | 
			
		||||
              className="fa fa-trophy custom-fa"
 | 
			
		||||
              aria-hidden="true"
 | 
			
		||||
            />
 | 
			
		||||
          </sup>
 | 
			
		||||
        )}
 | 
			
		||||
        {activity.records &&
 | 
			
		||||
          activity.records.find(r => r.record_type === 'AS') && (
 | 
			
		||||
            <sup>
 | 
			
		||||
              <i className="fa fa-trophy custom-fa" aria-hidden="true" />
 | 
			
		||||
            </sup>
 | 
			
		||||
          )}
 | 
			
		||||
        <br />
 | 
			
		||||
        Max speed : {activity.max_speed} km/h
 | 
			
		||||
        {activity.records && activity.records.find(r => r.record_type === 'MS'
 | 
			
		||||
        ) && (
 | 
			
		||||
          <sup>
 | 
			
		||||
            <i
 | 
			
		||||
              className="fa fa-trophy custom-fa"
 | 
			
		||||
              aria-hidden="true"
 | 
			
		||||
            />
 | 
			
		||||
          </sup>
 | 
			
		||||
        )}
 | 
			
		||||
        {activity.records &&
 | 
			
		||||
          activity.records.find(r => r.record_type === 'MS') && (
 | 
			
		||||
            <sup>
 | 
			
		||||
              <i className="fa fa-trophy custom-fa" aria-hidden="true" />
 | 
			
		||||
            </sup>
 | 
			
		||||
          )}
 | 
			
		||||
      </p>
 | 
			
		||||
      {activity.min_alt && activity.max_alt && (
 | 
			
		||||
        <p>
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,6 @@ import { thunderforestApiKey } from '../../../utils'
 | 
			
		||||
import { getGeoJson } from '../../../utils/activities'
 | 
			
		||||
 | 
			
		||||
class ActivityMap extends React.Component {
 | 
			
		||||
 | 
			
		||||
  constructor(props, context) {
 | 
			
		||||
    super(props, context)
 | 
			
		||||
    this.state = {
 | 
			
		||||
@@ -25,13 +24,15 @@ class ActivityMap extends React.Component {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  componentDidUpdate(prevProps) {
 | 
			
		||||
    if (this.props.dataType === 'activity' && (
 | 
			
		||||
      prevProps.activity.id !== this.props.activity.id)
 | 
			
		||||
    if (
 | 
			
		||||
      this.props.dataType === 'activity' &&
 | 
			
		||||
      prevProps.activity.id !== this.props.activity.id
 | 
			
		||||
    ) {
 | 
			
		||||
        this.props.loadActivityGpx(this.props.activity.id)
 | 
			
		||||
      this.props.loadActivityGpx(this.props.activity.id)
 | 
			
		||||
    }
 | 
			
		||||
    if (this.props.dataType === 'segment' && (
 | 
			
		||||
      prevProps.segmentId !== this.props.segmentId)
 | 
			
		||||
    if (
 | 
			
		||||
      this.props.dataType === 'segment' &&
 | 
			
		||||
      prevProps.segmentId !== this.props.segmentId
 | 
			
		||||
    ) {
 | 
			
		||||
      this.props.loadSegmentGpx(this.props.activity.id, this.props.segmentId)
 | 
			
		||||
    }
 | 
			
		||||
@@ -42,13 +43,11 @@ class ActivityMap extends React.Component {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const {
 | 
			
		||||
      activity, coordinates, gpxContent
 | 
			
		||||
    } = this.props
 | 
			
		||||
    const { activity, coordinates, gpxContent } = this.props
 | 
			
		||||
    const { jsonData } = getGeoJson(gpxContent)
 | 
			
		||||
    const bounds = [
 | 
			
		||||
      [activity.bounds[0], activity.bounds[1]],
 | 
			
		||||
      [activity.bounds[2], activity.bounds[3]]
 | 
			
		||||
      [activity.bounds[2], activity.bounds[3]],
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
@@ -78,14 +77,13 @@ class ActivityMap extends React.Component {
 | 
			
		||||
          </Map>
 | 
			
		||||
        )}
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default connect(
 | 
			
		||||
  state => ({
 | 
			
		||||
    gpxContent: state.gpx
 | 
			
		||||
    gpxContent: state.gpx,
 | 
			
		||||
  }),
 | 
			
		||||
  dispatch => ({
 | 
			
		||||
    loadActivityGpx: activityId => {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,5 @@
 | 
			
		||||
import React from 'react'
 | 
			
		||||
 | 
			
		||||
export default function ActivityNoMap() {
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="activity-no-map text-center">
 | 
			
		||||
      No Map
 | 
			
		||||
    </div>
 | 
			
		||||
  )
 | 
			
		||||
  return <div className="activity-no-map text-center">No Map</div>
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,9 +8,7 @@ export default function ActivityNotes(props) {
 | 
			
		||||
        <div className="card activity-card">
 | 
			
		||||
          <div className="card-body">
 | 
			
		||||
            Notes
 | 
			
		||||
            <div className="activity-notes">
 | 
			
		||||
              {notes ? notes : 'No notes'}
 | 
			
		||||
            </div>
 | 
			
		||||
            <div className="activity-notes">{notes ? notes : 'No notes'}</div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -18,8 +18,8 @@ export default function ActivitySegments(props) {
 | 
			
		||||
                    key={`segment-${index}`}
 | 
			
		||||
                  >
 | 
			
		||||
                    <Link
 | 
			
		||||
                      to={`/activities/${
 | 
			
		||||
                        segment.activity_id}/segment/${index + 1}`}
 | 
			
		||||
                      to={`/activities/${segment.activity_id}/segment/${index +
 | 
			
		||||
                        1}`}
 | 
			
		||||
                    >
 | 
			
		||||
                      segment {index + 1}
 | 
			
		||||
                    </Link>{' '}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,9 +5,7 @@ export default function ActivityWeather(props) {
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="container">
 | 
			
		||||
      {activity.weather_start && activity.weather_end && (
 | 
			
		||||
        <table
 | 
			
		||||
          className="table table-borderless weather-table text-center"
 | 
			
		||||
        >
 | 
			
		||||
        <table className="table table-borderless weather-table text-center">
 | 
			
		||||
          <thead>
 | 
			
		||||
            <tr>
 | 
			
		||||
              <th />
 | 
			
		||||
@@ -42,12 +40,8 @@ export default function ActivityWeather(props) {
 | 
			
		||||
                  alt="Temperatures"
 | 
			
		||||
                />
 | 
			
		||||
              </td>
 | 
			
		||||
              <td>
 | 
			
		||||
                {Number(activity.weather_start.temperature).toFixed(1)}°C
 | 
			
		||||
              </td>
 | 
			
		||||
              <td>
 | 
			
		||||
                {Number(activity.weather_end.temperature).toFixed(1)}°C
 | 
			
		||||
              </td>
 | 
			
		||||
              <td>{Number(activity.weather_start.temperature).toFixed(1)}°C</td>
 | 
			
		||||
              <td>{Number(activity.weather_end.temperature).toFixed(1)}°C</td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            <tr>
 | 
			
		||||
              <td>
 | 
			
		||||
@@ -60,9 +54,7 @@ export default function ActivityWeather(props) {
 | 
			
		||||
              <td>
 | 
			
		||||
                {Number(activity.weather_start.humidity * 100).toFixed(1)}%
 | 
			
		||||
              </td>
 | 
			
		||||
              <td>
 | 
			
		||||
                {Number(activity.weather_end.humidity * 100).toFixed(1)}%
 | 
			
		||||
              </td>
 | 
			
		||||
              <td>{Number(activity.weather_end.humidity * 100).toFixed(1)}%</td>
 | 
			
		||||
            </tr>
 | 
			
		||||
            <tr>
 | 
			
		||||
              <td>
 | 
			
		||||
@@ -72,12 +64,8 @@ export default function ActivityWeather(props) {
 | 
			
		||||
                  alt="Temperatures"
 | 
			
		||||
                />
 | 
			
		||||
              </td>
 | 
			
		||||
              <td>
 | 
			
		||||
                {Number(activity.weather_start.wind).toFixed(1)}m/s
 | 
			
		||||
              </td>
 | 
			
		||||
              <td>
 | 
			
		||||
                {Number(activity.weather_end.wind).toFixed(1)}m/s
 | 
			
		||||
              </td>
 | 
			
		||||
              <td>{Number(activity.weather_start.wind).toFixed(1)}m/s</td>
 | 
			
		||||
              <td>{Number(activity.weather_end.wind).toFixed(1)}m/s</td>
 | 
			
		||||
            </tr>
 | 
			
		||||
          </tbody>
 | 
			
		||||
        </table>
 | 
			
		||||
 
 | 
			
		||||
@@ -30,8 +30,9 @@ class ActivityDisplay extends React.Component {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  componentDidUpdate(prevProps) {
 | 
			
		||||
    if (prevProps.match.params.activityId !==
 | 
			
		||||
      this.props.match.params.activityId) {
 | 
			
		||||
    if (
 | 
			
		||||
      prevProps.match.params.activityId !== this.props.match.params.activityId
 | 
			
		||||
    ) {
 | 
			
		||||
      this.props.loadActivity(this.props.match.params.activityId)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@@ -39,23 +40,24 @@ class ActivityDisplay extends React.Component {
 | 
			
		||||
  displayModal(value) {
 | 
			
		||||
    this.setState(prevState => ({
 | 
			
		||||
      ...prevState,
 | 
			
		||||
      displayModal: value
 | 
			
		||||
      displayModal: value,
 | 
			
		||||
    }))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  updateCoordinates(activePayload) {
 | 
			
		||||
    const coordinates = (activePayload && activePayload.length > 0)
 | 
			
		||||
      ? {
 | 
			
		||||
          latitude: activePayload[0].payload.latitude,
 | 
			
		||||
          longitude: activePayload[0].payload.longitude,
 | 
			
		||||
        }
 | 
			
		||||
      : {
 | 
			
		||||
          latitude: null,
 | 
			
		||||
          longitude: null,
 | 
			
		||||
        }
 | 
			
		||||
    const coordinates =
 | 
			
		||||
      activePayload && activePayload.length > 0
 | 
			
		||||
        ? {
 | 
			
		||||
            latitude: activePayload[0].payload.latitude,
 | 
			
		||||
            longitude: activePayload[0].payload.longitude,
 | 
			
		||||
          }
 | 
			
		||||
        : {
 | 
			
		||||
            latitude: null,
 | 
			
		||||
            longitude: null,
 | 
			
		||||
          }
 | 
			
		||||
    this.setState(prevState => ({
 | 
			
		||||
      ...prevState,
 | 
			
		||||
      coordinates
 | 
			
		||||
      coordinates,
 | 
			
		||||
    }))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -68,9 +70,7 @@ class ActivityDisplay extends React.Component {
 | 
			
		||||
      ? sports.filter(s => s.id === activity.sport_id)
 | 
			
		||||
      : []
 | 
			
		||||
    const segmentId = parseInt(this.props.match.params.segmentId)
 | 
			
		||||
    const dataType = segmentId >= 0
 | 
			
		||||
      ? 'segment'
 | 
			
		||||
      : 'activity'
 | 
			
		||||
    const dataType = segmentId >= 0 ? 'segment' : 'activity'
 | 
			
		||||
    return (
 | 
			
		||||
      <div className="activity-page">
 | 
			
		||||
        <Helmet>
 | 
			
		||||
@@ -80,16 +80,17 @@ class ActivityDisplay extends React.Component {
 | 
			
		||||
          <code>{message}</code>
 | 
			
		||||
        ) : (
 | 
			
		||||
          <div className="container">
 | 
			
		||||
            {displayModal &&
 | 
			
		||||
            <CustomModal
 | 
			
		||||
              title="Confirmation"
 | 
			
		||||
              text="Are you sure you want to delete this activity?"
 | 
			
		||||
              confirm={() => {
 | 
			
		||||
                onDeleteActivity(activity.id)
 | 
			
		||||
                this.displayModal(false)
 | 
			
		||||
              }}
 | 
			
		||||
              close={() => this.displayModal(false)}
 | 
			
		||||
            />}
 | 
			
		||||
            {displayModal && (
 | 
			
		||||
              <CustomModal
 | 
			
		||||
                title="Confirmation"
 | 
			
		||||
                text="Are you sure you want to delete this activity?"
 | 
			
		||||
                confirm={() => {
 | 
			
		||||
                  onDeleteActivity(activity.id)
 | 
			
		||||
                  this.displayModal(false)
 | 
			
		||||
                }}
 | 
			
		||||
                close={() => this.displayModal(false)}
 | 
			
		||||
              />
 | 
			
		||||
            )}
 | 
			
		||||
            {activity && sport && activities.length === 1 && (
 | 
			
		||||
              <div>
 | 
			
		||||
                <div className="row">
 | 
			
		||||
@@ -122,9 +123,11 @@ class ActivityDisplay extends React.Component {
 | 
			
		||||
                          </div>
 | 
			
		||||
                          <div className="col">
 | 
			
		||||
                            <ActivityDetails
 | 
			
		||||
                              activity={dataType === 'activity'
 | 
			
		||||
                                ? activity
 | 
			
		||||
                                : activity.segments[segmentId - 1]}
 | 
			
		||||
                              activity={
 | 
			
		||||
                                dataType === 'activity'
 | 
			
		||||
                                  ? activity
 | 
			
		||||
                                  : activity.segments[segmentId - 1]
 | 
			
		||||
                              }
 | 
			
		||||
                            />
 | 
			
		||||
                          </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
@@ -144,8 +147,8 @@ class ActivityDisplay extends React.Component {
 | 
			
		||||
                                activity={activity}
 | 
			
		||||
                                dataType={dataType}
 | 
			
		||||
                                segmentId={segmentId}
 | 
			
		||||
                                updateCoordinates={
 | 
			
		||||
                                  e => this.updateCoordinates(e)
 | 
			
		||||
                                updateCoordinates={e =>
 | 
			
		||||
                                  this.updateCoordinates(e)
 | 
			
		||||
                                }
 | 
			
		||||
                              />
 | 
			
		||||
                            </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -4,12 +4,9 @@ import { connect } from 'react-redux'
 | 
			
		||||
import ActivityAddOrEdit from './ActivityAddOrEdit'
 | 
			
		||||
import { getOrUpdateData } from '../../actions'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ActivityEdit extends React.Component {
 | 
			
		||||
  componentDidMount() {
 | 
			
		||||
    this.props.loadActivity(
 | 
			
		||||
      this.props.match.params.activityId
 | 
			
		||||
    )
 | 
			
		||||
    this.props.loadActivity(this.props.match.params.activityId)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,11 +6,8 @@ import { addActivity, editActivity } from '../../../actions/activities'
 | 
			
		||||
import { history } from '../../../index'
 | 
			
		||||
import { gpxLimit } from '../../../utils'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function FormWithGpx (props) {
 | 
			
		||||
  const {
 | 
			
		||||
    activity, loading, onAddActivity, onEditActivity, sports
 | 
			
		||||
  } = props
 | 
			
		||||
function FormWithGpx(props) {
 | 
			
		||||
  const { activity, loading, onAddActivity, onEditActivity, sports } = props
 | 
			
		||||
  const sportId = activity ? activity.sport_id : ''
 | 
			
		||||
  return (
 | 
			
		||||
    <form
 | 
			
		||||
@@ -52,9 +49,10 @@ function FormWithGpx (props) {
 | 
			
		||||
      ) : (
 | 
			
		||||
        <div className="form-group">
 | 
			
		||||
          <label>
 | 
			
		||||
            <strong>gpx</strong> file or <strong>zip</strong>{' '}
 | 
			
		||||
            file containing <strong>gpx</strong> (no folder inside, {
 | 
			
		||||
            gpxLimit} files max):
 | 
			
		||||
            {/* prettier-ignore */}
 | 
			
		||||
            <strong>gpx</strong> file or <strong>zip</strong> file containing
 | 
			
		||||
            {/* prettier-ignore */}
 | 
			
		||||
            <strong> gpx</strong> (no folder inside, {gpxLimit} files max):
 | 
			
		||||
            <input
 | 
			
		||||
              accept=".gpx, .zip"
 | 
			
		||||
              className="form-control form-control-file gpx-file"
 | 
			
		||||
@@ -85,10 +83,8 @@ function FormWithGpx (props) {
 | 
			
		||||
          <input
 | 
			
		||||
            type="submit"
 | 
			
		||||
            className="btn btn-primary btn-lg btn-block"
 | 
			
		||||
            onClick={
 | 
			
		||||
              event => activity
 | 
			
		||||
                ? onEditActivity(event, activity)
 | 
			
		||||
                : onAddActivity(event)
 | 
			
		||||
            onClick={event =>
 | 
			
		||||
              activity ? onEditActivity(event, activity) : onAddActivity(event)
 | 
			
		||||
            }
 | 
			
		||||
            value="Submit"
 | 
			
		||||
          />
 | 
			
		||||
@@ -106,13 +102,14 @@ function FormWithGpx (props) {
 | 
			
		||||
 | 
			
		||||
export default connect(
 | 
			
		||||
  state => ({
 | 
			
		||||
    loading: state.loading
 | 
			
		||||
    loading: state.loading,
 | 
			
		||||
  }),
 | 
			
		||||
  dispatch => ({
 | 
			
		||||
    onAddActivity: e => {
 | 
			
		||||
      dispatch(setLoading(true))
 | 
			
		||||
      const form = new FormData()
 | 
			
		||||
      form.append('file', e.target.form.gpxFile.files[0])
 | 
			
		||||
      /* prettier-ignore */
 | 
			
		||||
      form.append(
 | 
			
		||||
        'data',
 | 
			
		||||
        `{"sport_id": ${e.target.form.sport.value
 | 
			
		||||
@@ -121,12 +118,14 @@ export default connect(
 | 
			
		||||
      dispatch(addActivity(form))
 | 
			
		||||
    },
 | 
			
		||||
    onEditActivity: (e, activity) => {
 | 
			
		||||
      dispatch(editActivity({
 | 
			
		||||
        id: activity.id,
 | 
			
		||||
        notes: e.target.form.notes.value,
 | 
			
		||||
        sport_id: +e.target.form.sport.value,
 | 
			
		||||
        title: e.target.form.title.value,
 | 
			
		||||
      }))
 | 
			
		||||
      dispatch(
 | 
			
		||||
        editActivity({
 | 
			
		||||
          id: activity.id,
 | 
			
		||||
          notes: e.target.form.notes.value,
 | 
			
		||||
          sport_id: +e.target.form.sport.value,
 | 
			
		||||
          title: e.target.form.title.value,
 | 
			
		||||
        })
 | 
			
		||||
      )
 | 
			
		||||
    },
 | 
			
		||||
  })
 | 
			
		||||
)(FormWithGpx)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,14 +2,17 @@ import React from 'react'
 | 
			
		||||
import { connect } from 'react-redux'
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  addActivityWithoutGpx, editActivity
 | 
			
		||||
  addActivityWithoutGpx,
 | 
			
		||||
  editActivity,
 | 
			
		||||
} from '../../../actions/activities'
 | 
			
		||||
import { history } from '../../../index'
 | 
			
		||||
import { formatActivityDate } from '../../../utils/activities'
 | 
			
		||||
 | 
			
		||||
function FormWithoutGpx (props) {
 | 
			
		||||
function FormWithoutGpx(props) {
 | 
			
		||||
  const { activity, onAddOrEdit, sports } = props
 | 
			
		||||
  let activityDate, activityTime, sportId = ''
 | 
			
		||||
  let activityDate,
 | 
			
		||||
    activityTime,
 | 
			
		||||
    sportId = ''
 | 
			
		||||
  if (activity) {
 | 
			
		||||
    const activityDateTime = formatActivityDate(
 | 
			
		||||
      activity.activity_date,
 | 
			
		||||
@@ -21,9 +24,7 @@ function FormWithoutGpx (props) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <form
 | 
			
		||||
      onSubmit={event => event.preventDefault()}
 | 
			
		||||
    >
 | 
			
		||||
    <form onSubmit={event => event.preventDefault()}>
 | 
			
		||||
      <div className="form-group">
 | 
			
		||||
        <label>
 | 
			
		||||
          Title:
 | 
			
		||||
@@ -78,15 +79,15 @@ function FormWithoutGpx (props) {
 | 
			
		||||
      <div className="form-group">
 | 
			
		||||
        <label>
 | 
			
		||||
          Duration:
 | 
			
		||||
            <input
 | 
			
		||||
              name="duration"
 | 
			
		||||
              defaultValue={activity ? activity.duration : ''}
 | 
			
		||||
              className="form-control col-xs-4"
 | 
			
		||||
              pattern="^([0-9]*[0-9]):([0-5][0-9]):([0-5][0-9])$"
 | 
			
		||||
              placeholder="hh:mm:ss"
 | 
			
		||||
              required
 | 
			
		||||
              type="text"
 | 
			
		||||
            />
 | 
			
		||||
          <input
 | 
			
		||||
            name="duration"
 | 
			
		||||
            defaultValue={activity ? activity.duration : ''}
 | 
			
		||||
            className="form-control col-xs-4"
 | 
			
		||||
            pattern="^([0-9]*[0-9]):([0-5][0-9]):([0-5][0-9])$"
 | 
			
		||||
            placeholder="hh:mm:ss"
 | 
			
		||||
            required
 | 
			
		||||
            type="text"
 | 
			
		||||
          />
 | 
			
		||||
        </label>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="form-group">
 | 
			
		||||
@@ -131,12 +132,13 @@ function FormWithoutGpx (props) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default connect(
 | 
			
		||||
  () => ({ }),
 | 
			
		||||
  () => ({}),
 | 
			
		||||
  dispatch => ({
 | 
			
		||||
    onAddOrEdit: (e, activity) => {
 | 
			
		||||
      const d = e.target.form.duration.value.split(':')
 | 
			
		||||
      const duration = +d[0] * 60 * 60 + +d[1] * 60 + +d[2]
 | 
			
		||||
 | 
			
		||||
      /* prettier-ignore */
 | 
			
		||||
      const activityDate = `${e.target.form.activity_date.value
 | 
			
		||||
        } ${ e.target.form.activity_time.value}`
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ import ActivityEdit from './ActivityEdit'
 | 
			
		||||
import NotFound from './../Others/NotFound'
 | 
			
		||||
import { isLoggedIn } from '../../utils'
 | 
			
		||||
 | 
			
		||||
function Activity () {
 | 
			
		||||
function Activity() {
 | 
			
		||||
  return (
 | 
			
		||||
    <div>
 | 
			
		||||
      <Helmet>
 | 
			
		||||
@@ -19,11 +19,13 @@ function Activity () {
 | 
			
		||||
        <Switch>
 | 
			
		||||
          <Route exact path="/activities/add" component={ActivityAdd} />
 | 
			
		||||
          <Route
 | 
			
		||||
            exact path="/activities/:activityId"
 | 
			
		||||
            exact
 | 
			
		||||
            path="/activities/:activityId"
 | 
			
		||||
            component={ActivityDisplay}
 | 
			
		||||
          />
 | 
			
		||||
          <Route
 | 
			
		||||
            exact path="/activities/:activityId/edit"
 | 
			
		||||
            exact
 | 
			
		||||
            path="/activities/:activityId/edit"
 | 
			
		||||
            component={ActivityEdit}
 | 
			
		||||
          />
 | 
			
		||||
          <Route
 | 
			
		||||
@@ -32,13 +34,13 @@ function Activity () {
 | 
			
		||||
          />
 | 
			
		||||
          <Route component={NotFound} />
 | 
			
		||||
        </Switch>
 | 
			
		||||
      ) : (<Redirect to="/login" />)}
 | 
			
		||||
      ) : (
 | 
			
		||||
        <Redirect to="/login" />
 | 
			
		||||
      )}
 | 
			
		||||
    </div>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default connect(
 | 
			
		||||
  state => ({
 | 
			
		||||
    user: state.user,
 | 
			
		||||
  })
 | 
			
		||||
)(Activity)
 | 
			
		||||
export default connect(state => ({
 | 
			
		||||
  user: state.user,
 | 
			
		||||
}))(Activity)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ import React from 'react'
 | 
			
		||||
import { Helmet } from 'react-helmet'
 | 
			
		||||
import { Link } from 'react-router-dom'
 | 
			
		||||
 | 
			
		||||
export default function AdminMenu () {
 | 
			
		||||
export default function AdminMenu() {
 | 
			
		||||
  return (
 | 
			
		||||
    <div>
 | 
			
		||||
      <Helmet>
 | 
			
		||||
 
 | 
			
		||||
@@ -17,10 +17,7 @@ class AdminSports extends React.Component {
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <div>
 | 
			
		||||
        <AdminDetail
 | 
			
		||||
          results={sports}
 | 
			
		||||
          target="sports"
 | 
			
		||||
        />
 | 
			
		||||
        <AdminDetail results={sports} target="sports" />
 | 
			
		||||
      </div>
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -6,17 +6,14 @@ import AdminPage from '../generic/AdminPage'
 | 
			
		||||
 | 
			
		||||
class AdminSports extends React.Component {
 | 
			
		||||
  componentDidMount() {
 | 
			
		||||
      this.props.loadSports()
 | 
			
		||||
    this.props.loadSports()
 | 
			
		||||
  }
 | 
			
		||||
  render() {
 | 
			
		||||
    const { sports } = this.props
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <div>
 | 
			
		||||
        <AdminPage
 | 
			
		||||
          data={sports}
 | 
			
		||||
          target="sports"
 | 
			
		||||
        />
 | 
			
		||||
        <AdminPage data={sports} target="sports" />
 | 
			
		||||
      </div>
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ import { addData } from '../../../actions/index'
 | 
			
		||||
import { history } from '../../../index'
 | 
			
		||||
 | 
			
		||||
class AdminSportsAdd extends React.Component {
 | 
			
		||||
  componentDidMount() { }
 | 
			
		||||
  componentDidMount() {}
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const { message, onAddSport } = this.props
 | 
			
		||||
@@ -16,25 +16,17 @@ class AdminSportsAdd extends React.Component {
 | 
			
		||||
        <Helmet>
 | 
			
		||||
          <title>FitTrackee - Admin - Add Sport</title>
 | 
			
		||||
        </Helmet>
 | 
			
		||||
        <h1 className="page-title">
 | 
			
		||||
          Administration - Sport
 | 
			
		||||
        </h1>
 | 
			
		||||
        {message && (
 | 
			
		||||
          <code>{message}</code>
 | 
			
		||||
        )}
 | 
			
		||||
        <h1 className="page-title">Administration - Sport</h1>
 | 
			
		||||
        {message && <code>{message}</code>}
 | 
			
		||||
 | 
			
		||||
        <div className="container">
 | 
			
		||||
          <div className="row">
 | 
			
		||||
            <div className="col-md-2" />
 | 
			
		||||
            <div className="col-md-8">
 | 
			
		||||
              <div className="card">
 | 
			
		||||
                <div className="card-header">
 | 
			
		||||
                  Add a sport
 | 
			
		||||
                </div>
 | 
			
		||||
                <div className="card-header">Add a sport</div>
 | 
			
		||||
                <div className="card-body">
 | 
			
		||||
                  <form onSubmit={event =>
 | 
			
		||||
                    event.preventDefault()}
 | 
			
		||||
                  >
 | 
			
		||||
                  <form onSubmit={event => event.preventDefault()}>
 | 
			
		||||
                    <div className="form-group">
 | 
			
		||||
                      <label>
 | 
			
		||||
                        Label:
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@ import { deleteData, getOrUpdateData } from '../../../actions/index'
 | 
			
		||||
import { history } from '../../../index'
 | 
			
		||||
 | 
			
		||||
class AdminDetail extends React.Component {
 | 
			
		||||
 | 
			
		||||
  constructor(props, context) {
 | 
			
		||||
    super(props, context)
 | 
			
		||||
    this.state = {
 | 
			
		||||
@@ -15,13 +14,7 @@ class AdminDetail extends React.Component {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const {
 | 
			
		||||
      message,
 | 
			
		||||
      onDataUpdate,
 | 
			
		||||
      onDataDelete,
 | 
			
		||||
      results,
 | 
			
		||||
      target,
 | 
			
		||||
    } = this.props
 | 
			
		||||
    const { message, onDataUpdate, onDataDelete, results, target } = this.props
 | 
			
		||||
    const { isInEdition } = this.state
 | 
			
		||||
    const title = target.charAt(0).toUpperCase() + target.slice(1)
 | 
			
		||||
 | 
			
		||||
@@ -30,100 +23,98 @@ class AdminDetail extends React.Component {
 | 
			
		||||
        <Helmet>
 | 
			
		||||
          <title>FitTrackee - Admin</title>
 | 
			
		||||
        </Helmet>
 | 
			
		||||
        <h1 className="page-title">
 | 
			
		||||
            Administration - {title}
 | 
			
		||||
        </h1>
 | 
			
		||||
        <h1 className="page-title">Administration - {title}</h1>
 | 
			
		||||
        {message ? (
 | 
			
		||||
          <code>{message}</code>
 | 
			
		||||
        ) : (
 | 
			
		||||
          results.length === 1 && (
 | 
			
		||||
          <div className="container">
 | 
			
		||||
            <div className="row">
 | 
			
		||||
              <div className="col-md-2" />
 | 
			
		||||
              <div className="col-md-8 card">
 | 
			
		||||
                <div className="card-body">
 | 
			
		||||
                  <form onSubmit={event =>
 | 
			
		||||
                   event.preventDefault()}
 | 
			
		||||
                  >
 | 
			
		||||
                    { Object.keys(results[0])
 | 
			
		||||
            <div className="container">
 | 
			
		||||
              <div className="row">
 | 
			
		||||
                <div className="col-md-2" />
 | 
			
		||||
                <div className="col-md-8 card">
 | 
			
		||||
                  <div className="card-body">
 | 
			
		||||
                    <form onSubmit={event => event.preventDefault()}>
 | 
			
		||||
                      {Object.keys(results[0])
 | 
			
		||||
                        .filter(key => key.charAt(0) !== '_')
 | 
			
		||||
                        .map(key => (
 | 
			
		||||
                        <div className="form-group" key={key}>
 | 
			
		||||
                          <label>
 | 
			
		||||
                            {key}:
 | 
			
		||||
                            {key === 'img' ? (
 | 
			
		||||
                              <img
 | 
			
		||||
                                src={results[0][key]
 | 
			
		||||
                                  ? results[0][key]
 | 
			
		||||
                                  : '/img/photo.png'}
 | 
			
		||||
                                alt="property"
 | 
			
		||||
                              />
 | 
			
		||||
                            ) : (
 | 
			
		||||
                             <input
 | 
			
		||||
                              className="form-control input-lg"
 | 
			
		||||
                              name={key}
 | 
			
		||||
                              readOnly={key === 'id' || !isInEdition}
 | 
			
		||||
                              defaultValue={results[0][key]}
 | 
			
		||||
                             />
 | 
			
		||||
                            )}
 | 
			
		||||
                          </label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                      ))
 | 
			
		||||
                    }
 | 
			
		||||
                    {isInEdition ? (
 | 
			
		||||
                      <div>
 | 
			
		||||
                        <input
 | 
			
		||||
                          type="submit"
 | 
			
		||||
                          className="btn btn-primary btn-lg btn-block"
 | 
			
		||||
                          onClick={event => {
 | 
			
		||||
                          <div className="form-group" key={key}>
 | 
			
		||||
                            <label>
 | 
			
		||||
                              {key}:
 | 
			
		||||
                              {key === 'img' ? (
 | 
			
		||||
                                <img
 | 
			
		||||
                                  src={
 | 
			
		||||
                                    results[0][key]
 | 
			
		||||
                                      ? results[0][key]
 | 
			
		||||
                                      : '/img/photo.png'
 | 
			
		||||
                                  }
 | 
			
		||||
                                  alt="property"
 | 
			
		||||
                                />
 | 
			
		||||
                              ) : (
 | 
			
		||||
                                <input
 | 
			
		||||
                                  className="form-control input-lg"
 | 
			
		||||
                                  name={key}
 | 
			
		||||
                                  readOnly={key === 'id' || !isInEdition}
 | 
			
		||||
                                  defaultValue={results[0][key]}
 | 
			
		||||
                                />
 | 
			
		||||
                              )}
 | 
			
		||||
                            </label>
 | 
			
		||||
                          </div>
 | 
			
		||||
                        ))}
 | 
			
		||||
                      {isInEdition ? (
 | 
			
		||||
                        <div>
 | 
			
		||||
                          <input
 | 
			
		||||
                            type="submit"
 | 
			
		||||
                            className="btn btn-primary btn-lg btn-block"
 | 
			
		||||
                            onClick={event => {
 | 
			
		||||
                              onDataUpdate(event, target)
 | 
			
		||||
                              this.setState({ isInEdition: false })
 | 
			
		||||
                            }}
 | 
			
		||||
                            value="Submit"
 | 
			
		||||
                          />
 | 
			
		||||
                          <input
 | 
			
		||||
                            type="submit"
 | 
			
		||||
                            className="btn btn-secondary btn-lg btn-block"
 | 
			
		||||
                            onClick={event => {
 | 
			
		||||
                              event.target.form.reset()
 | 
			
		||||
                              this.setState({ isInEdition: false })
 | 
			
		||||
                            }}
 | 
			
		||||
                            value="Cancel"
 | 
			
		||||
                          />
 | 
			
		||||
                        </div>
 | 
			
		||||
                      ) : (
 | 
			
		||||
                        <div>
 | 
			
		||||
                          <input
 | 
			
		||||
                            type="submit"
 | 
			
		||||
                            className="btn btn-primary btn-lg btn-block"
 | 
			
		||||
                            onClick={() => this.setState({ isInEdition: true })}
 | 
			
		||||
                            value="Edit"
 | 
			
		||||
                          />
 | 
			
		||||
                          <input
 | 
			
		||||
                            type="submit"
 | 
			
		||||
                            className="btn btn-danger btn-lg btn-block"
 | 
			
		||||
                            disabled={!results[0]._can_be_deleted}
 | 
			
		||||
                            onClick={event => onDataDelete(event, target)}
 | 
			
		||||
                            title={
 | 
			
		||||
                              results[0]._can_be_deleted
 | 
			
		||||
                                ? ''
 | 
			
		||||
                                : "Can't be deleted, associated data exist"
 | 
			
		||||
                            }
 | 
			
		||||
                          }
 | 
			
		||||
                          value="Submit"
 | 
			
		||||
                        />
 | 
			
		||||
                        <input
 | 
			
		||||
                          type="submit"
 | 
			
		||||
                          className="btn btn-secondary btn-lg btn-block"
 | 
			
		||||
                          onClick={event => {
 | 
			
		||||
                            event.target.form.reset()
 | 
			
		||||
                            this.setState({ isInEdition: false })
 | 
			
		||||
                          }}
 | 
			
		||||
                          value="Cancel"
 | 
			
		||||
                        />
 | 
			
		||||
                      </div>
 | 
			
		||||
                    ) : (
 | 
			
		||||
                      <div>
 | 
			
		||||
                        <input
 | 
			
		||||
                          type="submit"
 | 
			
		||||
                          className="btn btn-primary btn-lg btn-block"
 | 
			
		||||
                          onClick={() => this.setState({ isInEdition: true })}
 | 
			
		||||
                          value="Edit"
 | 
			
		||||
                        />
 | 
			
		||||
                        <input
 | 
			
		||||
                          type="submit"
 | 
			
		||||
                          className="btn btn-danger btn-lg btn-block"
 | 
			
		||||
                          disabled={!results[0]._can_be_deleted}
 | 
			
		||||
                          onClick={event => onDataDelete(event, target)}
 | 
			
		||||
                          title={results[0]._can_be_deleted
 | 
			
		||||
                            ? ''
 | 
			
		||||
                            : 'Can\'t be deleted, associated data exist'}
 | 
			
		||||
                          value="Delete"
 | 
			
		||||
                        />
 | 
			
		||||
                        <input
 | 
			
		||||
                          type="submit"
 | 
			
		||||
                          className="btn btn-secondary btn-lg btn-block"
 | 
			
		||||
                          onClick={() => history.push(`/admin/${target}`)}
 | 
			
		||||
                          value="Back to the list"
 | 
			
		||||
                        />
 | 
			
		||||
                      </div>
 | 
			
		||||
                    )}
 | 
			
		||||
                  </form>
 | 
			
		||||
                            value="Delete"
 | 
			
		||||
                          />
 | 
			
		||||
                          <input
 | 
			
		||||
                            type="submit"
 | 
			
		||||
                            className="btn btn-secondary btn-lg btn-block"
 | 
			
		||||
                            onClick={() => history.push(`/admin/${target}`)}
 | 
			
		||||
                            value="Back to the list"
 | 
			
		||||
                          />
 | 
			
		||||
                        </div>
 | 
			
		||||
                      )}
 | 
			
		||||
                    </form>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div className="col-md-2" />
 | 
			
		||||
              </div>
 | 
			
		||||
              <div className="col-md-2" />
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          )
 | 
			
		||||
        )}
 | 
			
		||||
      </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ import { Link } from 'react-router-dom'
 | 
			
		||||
import { history } from '../../../index'
 | 
			
		||||
 | 
			
		||||
export default function AdminPage(props) {
 | 
			
		||||
 | 
			
		||||
  const { data, target } = props
 | 
			
		||||
  const { error } = data
 | 
			
		||||
  const results = data.data
 | 
			
		||||
@@ -22,9 +21,7 @@ export default function AdminPage(props) {
 | 
			
		||||
      <Helmet>
 | 
			
		||||
        <title>FitTrackee - Admin</title>
 | 
			
		||||
      </Helmet>
 | 
			
		||||
      <h1 className="page-title">
 | 
			
		||||
          Administration - {title}
 | 
			
		||||
      </h1>
 | 
			
		||||
      <h1 className="page-title">Administration - {title}</h1>
 | 
			
		||||
      {error ? (
 | 
			
		||||
        <code>{error}</code>
 | 
			
		||||
      ) : (
 | 
			
		||||
@@ -36,40 +33,45 @@ export default function AdminPage(props) {
 | 
			
		||||
                <table className="table">
 | 
			
		||||
                  <thead>
 | 
			
		||||
                    <tr>
 | 
			
		||||
                      {tbKeys.map(
 | 
			
		||||
                        tbKey => <th key={tbKey} scope="col">{tbKey}</th>
 | 
			
		||||
                      )}
 | 
			
		||||
                      {tbKeys.map(tbKey => (
 | 
			
		||||
                        <th key={tbKey} scope="col">
 | 
			
		||||
                          {tbKey}
 | 
			
		||||
                        </th>
 | 
			
		||||
                      ))}
 | 
			
		||||
                    </tr>
 | 
			
		||||
                  </thead>
 | 
			
		||||
                  <tbody>
 | 
			
		||||
                    { results.map((result, idx) => (
 | 
			
		||||
                    {results.map((result, idx) => (
 | 
			
		||||
                      // eslint-disable-next-line react/no-array-index-key
 | 
			
		||||
                      <tr key={idx}>
 | 
			
		||||
                        { Object.keys(result)
 | 
			
		||||
                        {Object.keys(result)
 | 
			
		||||
                          .filter(key => key.charAt(0) !== '_')
 | 
			
		||||
                          .map(key => {
 | 
			
		||||
                          if (key === 'id') {
 | 
			
		||||
                            return (
 | 
			
		||||
                              <th key={key} scope="row">
 | 
			
		||||
                              <Link to={`/admin/${target}/${result[key]}`}>
 | 
			
		||||
                                {result[key]}
 | 
			
		||||
                              </Link>
 | 
			
		||||
                              </th>
 | 
			
		||||
                            )
 | 
			
		||||
                          } else if (key === 'img') {
 | 
			
		||||
                            return (<td key={key}>
 | 
			
		||||
                              <img
 | 
			
		||||
                                className="admin-img"
 | 
			
		||||
                                src={result[key]
 | 
			
		||||
                                  ? result[key]
 | 
			
		||||
                                  : '/img/photo.png'}
 | 
			
		||||
                                alt="logo"
 | 
			
		||||
                              />
 | 
			
		||||
                            </td>)
 | 
			
		||||
                          }
 | 
			
		||||
                          return <td key={key}>{result[key]}</td>
 | 
			
		||||
                          })
 | 
			
		||||
                        }
 | 
			
		||||
                            if (key === 'id') {
 | 
			
		||||
                              return (
 | 
			
		||||
                                <th key={key} scope="row">
 | 
			
		||||
                                  <Link to={`/admin/${target}/${result[key]}`}>
 | 
			
		||||
                                    {result[key]}
 | 
			
		||||
                                  </Link>
 | 
			
		||||
                                </th>
 | 
			
		||||
                              )
 | 
			
		||||
                            } else if (key === 'img') {
 | 
			
		||||
                              return (
 | 
			
		||||
                                <td key={key}>
 | 
			
		||||
                                  <img
 | 
			
		||||
                                    className="admin-img"
 | 
			
		||||
                                    src={
 | 
			
		||||
                                      result[key]
 | 
			
		||||
                                        ? result[key]
 | 
			
		||||
                                        : '/img/photo.png'
 | 
			
		||||
                                    }
 | 
			
		||||
                                    alt="logo"
 | 
			
		||||
                                  />
 | 
			
		||||
                                </td>
 | 
			
		||||
                              )
 | 
			
		||||
                            }
 | 
			
		||||
                            return <td key={key}>{result[key]}</td>
 | 
			
		||||
                          })}
 | 
			
		||||
                      </tr>
 | 
			
		||||
                    ))}
 | 
			
		||||
                  </tbody>
 | 
			
		||||
@@ -88,9 +90,9 @@ export default function AdminPage(props) {
 | 
			
		||||
                />
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          <div className="col-md-2" />
 | 
			
		||||
            <div className="col-md-2" />
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      )}
 | 
			
		||||
    </div>
 | 
			
		||||
  )
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ import AccessDenied from './../Others/AccessDenied'
 | 
			
		||||
import NotFound from './../Others/NotFound'
 | 
			
		||||
import { isLoggedIn } from '../../utils'
 | 
			
		||||
 | 
			
		||||
function Admin (props) {
 | 
			
		||||
function Admin(props) {
 | 
			
		||||
  const { user } = props
 | 
			
		||||
  return (
 | 
			
		||||
    <div>
 | 
			
		||||
@@ -23,26 +23,20 @@ function Admin (props) {
 | 
			
		||||
          <Switch>
 | 
			
		||||
            <Route exact path="/admin" component={AdminMenu} />
 | 
			
		||||
            <Route exact path="/admin/sports" component={AdminSports} />
 | 
			
		||||
            <Route
 | 
			
		||||
              exact path="/admin/sports/add"
 | 
			
		||||
              component={AdminSportsAdd}
 | 
			
		||||
            />
 | 
			
		||||
            <Route
 | 
			
		||||
              exact path="/admin/sports/:sportId"
 | 
			
		||||
              component={AdminSport}
 | 
			
		||||
            />
 | 
			
		||||
            <Route exact path="/admin/sports/add" component={AdminSportsAdd} />
 | 
			
		||||
            <Route exact path="/admin/sports/:sportId" component={AdminSport} />
 | 
			
		||||
            <Route component={NotFound} />
 | 
			
		||||
          </Switch>
 | 
			
		||||
        ) : (
 | 
			
		||||
          <AccessDenied />
 | 
			
		||||
        )
 | 
			
		||||
      ) : (<Redirect to="/login" />)}
 | 
			
		||||
      ) : (
 | 
			
		||||
        <Redirect to="/login" />
 | 
			
		||||
      )}
 | 
			
		||||
    </div>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default connect(
 | 
			
		||||
  state => ({
 | 
			
		||||
    user: state.user,
 | 
			
		||||
  })
 | 
			
		||||
)(Admin)
 | 
			
		||||
export default connect(state => ({
 | 
			
		||||
  user: state.user,
 | 
			
		||||
}))(Admin)
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,6 @@ import UserForm from './User/UserForm'
 | 
			
		||||
import { isLoggedIn } from '../utils'
 | 
			
		||||
 | 
			
		||||
export default class App extends React.Component {
 | 
			
		||||
 | 
			
		||||
  constructor(props) {
 | 
			
		||||
    super(props)
 | 
			
		||||
    this.props = props
 | 
			
		||||
@@ -29,78 +28,57 @@ export default class App extends React.Component {
 | 
			
		||||
        <NavBar />
 | 
			
		||||
        <Switch>
 | 
			
		||||
          <Route
 | 
			
		||||
            exact path="/"
 | 
			
		||||
            render={() => (
 | 
			
		||||
              isLoggedIn() ? (
 | 
			
		||||
                <Dashboard />
 | 
			
		||||
              ) : (
 | 
			
		||||
                <Redirect to="/login" />
 | 
			
		||||
              )
 | 
			
		||||
            )}
 | 
			
		||||
            exact
 | 
			
		||||
            path="/"
 | 
			
		||||
            render={() =>
 | 
			
		||||
              isLoggedIn() ? <Dashboard /> : <Redirect to="/login" />
 | 
			
		||||
            }
 | 
			
		||||
          />
 | 
			
		||||
          <Route
 | 
			
		||||
            exact path="/register"
 | 
			
		||||
            render={() => (
 | 
			
		||||
            exact
 | 
			
		||||
            path="/register"
 | 
			
		||||
            render={() =>
 | 
			
		||||
              isLoggedIn() ? (
 | 
			
		||||
                <Redirect to="/" />
 | 
			
		||||
              ) : (
 | 
			
		||||
                <UserForm
 | 
			
		||||
                  formType={'register'}
 | 
			
		||||
                />
 | 
			
		||||
                <UserForm formType={'register'} />
 | 
			
		||||
              )
 | 
			
		||||
            )}
 | 
			
		||||
            }
 | 
			
		||||
          />
 | 
			
		||||
          <Route
 | 
			
		||||
            exact path="/login"
 | 
			
		||||
            render={() => (
 | 
			
		||||
            exact
 | 
			
		||||
            path="/login"
 | 
			
		||||
            render={() =>
 | 
			
		||||
              isLoggedIn() ? (
 | 
			
		||||
                <Redirect to="/" />
 | 
			
		||||
              ) : (
 | 
			
		||||
                <UserForm
 | 
			
		||||
                  formType={'login'}
 | 
			
		||||
                />
 | 
			
		||||
                <UserForm formType={'login'} />
 | 
			
		||||
              )
 | 
			
		||||
            )}
 | 
			
		||||
            }
 | 
			
		||||
          />
 | 
			
		||||
          <Route exact path="/logout" component={Logout} />
 | 
			
		||||
          <Route
 | 
			
		||||
            exact path="/profile/edit"
 | 
			
		||||
            render={() => (
 | 
			
		||||
              isLoggedIn() ? (
 | 
			
		||||
                <ProfileEdit />
 | 
			
		||||
              ) : (
 | 
			
		||||
                <UserForm
 | 
			
		||||
                  formType={'login'}
 | 
			
		||||
                />
 | 
			
		||||
              )
 | 
			
		||||
            )}
 | 
			
		||||
            exact
 | 
			
		||||
            path="/profile/edit"
 | 
			
		||||
            render={() =>
 | 
			
		||||
              isLoggedIn() ? <ProfileEdit /> : <UserForm formType={'login'} />
 | 
			
		||||
            }
 | 
			
		||||
          />
 | 
			
		||||
          <Route
 | 
			
		||||
            exact path="/profile"
 | 
			
		||||
            render={() => (
 | 
			
		||||
              isLoggedIn() ? (
 | 
			
		||||
                <Profile />
 | 
			
		||||
              ) : (
 | 
			
		||||
                <UserForm
 | 
			
		||||
                  formType={'login'}
 | 
			
		||||
                />
 | 
			
		||||
              )
 | 
			
		||||
            )}
 | 
			
		||||
          />
 | 
			
		||||
          <Route
 | 
			
		||||
            exact path="/activities/history"
 | 
			
		||||
            component={Activities}
 | 
			
		||||
          />
 | 
			
		||||
          <Route
 | 
			
		||||
            exact path="/activities/statistics"
 | 
			
		||||
            component={Statistics}
 | 
			
		||||
            exact
 | 
			
		||||
            path="/profile"
 | 
			
		||||
            render={() =>
 | 
			
		||||
              isLoggedIn() ? <Profile /> : <UserForm formType={'login'} />
 | 
			
		||||
            }
 | 
			
		||||
          />
 | 
			
		||||
          <Route exact path="/activities/history" component={Activities} />
 | 
			
		||||
          <Route exact path="/activities/statistics" component={Statistics} />
 | 
			
		||||
          <Route path="/activities" component={Activity} />
 | 
			
		||||
          {/* <Route path="/admin" component={Admin} /> */}
 | 
			
		||||
          <Route component={NotFound} />
 | 
			
		||||
        </Switch>
 | 
			
		||||
        <Footer />
 | 
			
		||||
     </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ import { formatValue } from '../../../utils/stats'
 | 
			
		||||
/**
 | 
			
		||||
 * @return {null}
 | 
			
		||||
 */
 | 
			
		||||
export default function CustomLabel (props) {
 | 
			
		||||
export default function CustomLabel(props) {
 | 
			
		||||
  const { displayedData, x, y, width, value } = props
 | 
			
		||||
  if (!value) {
 | 
			
		||||
    return null
 | 
			
		||||
 
 | 
			
		||||
@@ -2,34 +2,32 @@ import React from 'react'
 | 
			
		||||
 | 
			
		||||
import { formatDuration } from '../../../utils/stats'
 | 
			
		||||
 | 
			
		||||
const formatValue = (displayedData, value) => displayedData === 'duration'
 | 
			
		||||
  ? formatDuration(value, true)
 | 
			
		||||
  : displayedData === 'distance'
 | 
			
		||||
const formatValue = (displayedData, value) =>
 | 
			
		||||
  displayedData === 'duration'
 | 
			
		||||
    ? formatDuration(value, true)
 | 
			
		||||
    : displayedData === 'distance'
 | 
			
		||||
    ? value.toFixed(2)
 | 
			
		||||
    : value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @return {null}
 | 
			
		||||
 */
 | 
			
		||||
export default function CustomTooltip (props) {
 | 
			
		||||
export default function CustomTooltip(props) {
 | 
			
		||||
  const { active } = props
 | 
			
		||||
  if (active) {
 | 
			
		||||
    const { displayedData, payload, label } = props
 | 
			
		||||
    let total = 0
 | 
			
		||||
    payload.map(p => total += p.value)
 | 
			
		||||
    payload.map(p => (total += p.value))
 | 
			
		||||
    return (
 | 
			
		||||
      <div className="custom-tooltip">
 | 
			
		||||
        <p className="custom-tooltip-label">{label}</p>
 | 
			
		||||
        {payload.map(p => (
 | 
			
		||||
          <p key={p.name} style={{ color: p.fill }}>
 | 
			
		||||
            {p.name}: {formatValue(displayedData, p.value)} {p.unit}
 | 
			
		||||
          </p>))
 | 
			
		||||
        }
 | 
			
		||||
        {payload.length > 0 && (
 | 
			
		||||
          <p>
 | 
			
		||||
            Total: {formatValue(displayedData, total)}
 | 
			
		||||
          </p>
 | 
			
		||||
        ))}
 | 
			
		||||
        {payload.length > 0 && (
 | 
			
		||||
          <p>Total: {formatValue(displayedData, total)}</p>
 | 
			
		||||
        )}
 | 
			
		||||
      </div>
 | 
			
		||||
    )
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,11 @@
 | 
			
		||||
import React from 'react'
 | 
			
		||||
import {
 | 
			
		||||
   Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis
 | 
			
		||||
  Bar,
 | 
			
		||||
  BarChart,
 | 
			
		||||
  ResponsiveContainer,
 | 
			
		||||
  Tooltip,
 | 
			
		||||
  XAxis,
 | 
			
		||||
  YAxis,
 | 
			
		||||
} from 'recharts'
 | 
			
		||||
 | 
			
		||||
import { activityColors } from '../../../utils/activities'
 | 
			
		||||
@@ -8,17 +13,16 @@ import { formatValue } from '../../../utils/stats'
 | 
			
		||||
import CustomTooltip from './CustomTooltip'
 | 
			
		||||
import CustomLabel from './CustomLabel'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export default class StatsCharts extends React.PureComponent {
 | 
			
		||||
  constructor(props, context) {
 | 
			
		||||
    super(props, context)
 | 
			
		||||
    this.state = {
 | 
			
		||||
      displayedData: 'distance'
 | 
			
		||||
      displayedData: 'distance',
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  handleRadioChange (changeEvent) {
 | 
			
		||||
  handleRadioChange(changeEvent) {
 | 
			
		||||
    this.setState({
 | 
			
		||||
      displayedData: changeEvent.target.name
 | 
			
		||||
      displayedData: changeEvent.target.name,
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -60,22 +64,14 @@ export default class StatsCharts extends React.PureComponent {
 | 
			
		||||
          </label>
 | 
			
		||||
        </div>
 | 
			
		||||
        <ResponsiveContainer height={300}>
 | 
			
		||||
          <BarChart
 | 
			
		||||
            data={stats[displayedData]}
 | 
			
		||||
            margin={{ top: 15, bottom: 0 }}
 | 
			
		||||
          >
 | 
			
		||||
          <BarChart data={stats[displayedData]} margin={{ top: 15, bottom: 0 }}>
 | 
			
		||||
            <XAxis
 | 
			
		||||
              dataKey="date"
 | 
			
		||||
              interval={0} // to force to display all ticks
 | 
			
		||||
            />
 | 
			
		||||
            <YAxis
 | 
			
		||||
              tickFormatter={value => formatValue(displayedData, value)}
 | 
			
		||||
            />
 | 
			
		||||
            <Tooltip content={
 | 
			
		||||
              <CustomTooltip
 | 
			
		||||
                displayedData={displayedData}
 | 
			
		||||
              />
 | 
			
		||||
            }
 | 
			
		||||
            <YAxis tickFormatter={value => formatValue(displayedData, value)} />
 | 
			
		||||
            <Tooltip
 | 
			
		||||
              content={<CustomTooltip displayedData={displayedData} />}
 | 
			
		||||
            />
 | 
			
		||||
            {sports.map((s, i) => (
 | 
			
		||||
              <Bar
 | 
			
		||||
@@ -86,9 +82,12 @@ export default class StatsCharts extends React.PureComponent {
 | 
			
		||||
                dataKey={s.label}
 | 
			
		||||
                stackId="a"
 | 
			
		||||
                fill={activityColors[i]}
 | 
			
		||||
                label={i === sports.length - 1
 | 
			
		||||
                  ? <CustomLabel displayedData={displayedData} />
 | 
			
		||||
                  : ''
 | 
			
		||||
                label={
 | 
			
		||||
                  i === sports.length - 1 ? (
 | 
			
		||||
                    <CustomLabel displayedData={displayedData} />
 | 
			
		||||
                  ) : (
 | 
			
		||||
                    ''
 | 
			
		||||
                  )
 | 
			
		||||
                }
 | 
			
		||||
              />
 | 
			
		||||
            ))}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,14 +6,14 @@ import { getStats } from '../../../actions/stats'
 | 
			
		||||
import { formatStats } from '../../../utils/stats'
 | 
			
		||||
import StatsChart from './StatsChart'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Statistics extends React.PureComponent {
 | 
			
		||||
  componentDidMount() {
 | 
			
		||||
    this.updateData()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  componentDidUpdate(prevProps) {
 | 
			
		||||
    if ((this.props.user.id && (this.props.user.id !== prevProps.user.id)) ||
 | 
			
		||||
    if (
 | 
			
		||||
      (this.props.user.id && this.props.user.id !== prevProps.user.id) ||
 | 
			
		||||
      this.props.statsParams !== prevProps.statsParams
 | 
			
		||||
    ) {
 | 
			
		||||
      this.updateData()
 | 
			
		||||
@@ -22,22 +22,23 @@ class Statistics extends React.PureComponent {
 | 
			
		||||
 | 
			
		||||
  updateData() {
 | 
			
		||||
    if (this.props.user.id) {
 | 
			
		||||
      this.props.loadActivities(
 | 
			
		||||
        this.props.user.id,
 | 
			
		||||
        this.props.statsParams,
 | 
			
		||||
      )
 | 
			
		||||
      this.props.loadActivities(this.props.user.id, this.props.statsParams)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const {
 | 
			
		||||
      displayedSports, sports, statistics, statsParams, displayEmpty
 | 
			
		||||
      displayedSports,
 | 
			
		||||
      sports,
 | 
			
		||||
      statistics,
 | 
			
		||||
      statsParams,
 | 
			
		||||
      displayEmpty,
 | 
			
		||||
    } = this.props
 | 
			
		||||
    if (!displayEmpty && Object.keys(statistics).length === 0) {
 | 
			
		||||
      return 'No workouts'
 | 
			
		||||
    }
 | 
			
		||||
    const stats = formatStats(statistics, sports, statsParams, displayedSports)
 | 
			
		||||
    return (<StatsChart sports={sports} stats={stats} />)
 | 
			
		||||
    return <StatsChart sports={sports} stats={stats} />
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -53,7 +54,7 @@ export default connect(
 | 
			
		||||
      const params = {
 | 
			
		||||
        from: format(data.start, dateFormat),
 | 
			
		||||
        to: format(data.end, dateFormat),
 | 
			
		||||
        time: data.duration
 | 
			
		||||
        time: data.duration,
 | 
			
		||||
      }
 | 
			
		||||
      dispatch(getStats(userId, data.type, params))
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
@@ -4,19 +4,21 @@ import { Link } from 'react-router-dom'
 | 
			
		||||
 | 
			
		||||
import { apiUrl, getDateWithTZ } from '../../utils'
 | 
			
		||||
 | 
			
		||||
export default function ActivityCard (props) {
 | 
			
		||||
export default function ActivityCard(props) {
 | 
			
		||||
  const { activity, sports, user } = 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(
 | 
			
		||||
          getDateWithTZ(activity.activity_date, user.timezone),
 | 
			
		||||
          'dd/MM/yyyy HH:mm'
 | 
			
		||||
        )}
 | 
			
		||||
          {sports
 | 
			
		||||
            .filter(sport => sport.id === activity.sport_id)
 | 
			
		||||
            .map(sport => sport.label)}{' '}
 | 
			
		||||
          -{' '}
 | 
			
		||||
          {format(
 | 
			
		||||
            getDateWithTZ(activity.activity_date, user.timezone),
 | 
			
		||||
            'dd/MM/yyyy HH:mm'
 | 
			
		||||
          )}
 | 
			
		||||
        </Link>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="card-body">
 | 
			
		||||
@@ -25,15 +27,14 @@ export default function ActivityCard (props) {
 | 
			
		||||
            <div className="col">
 | 
			
		||||
              <img
 | 
			
		||||
                alt="Map"
 | 
			
		||||
                src={`${apiUrl}activities/map/${activity.map}` +
 | 
			
		||||
                     `?${Date.now()}`}
 | 
			
		||||
                src={
 | 
			
		||||
                  `${apiUrl}activities/map/${activity.map}` + `?${Date.now()}`
 | 
			
		||||
                }
 | 
			
		||||
                className="img-fluid"
 | 
			
		||||
              />
 | 
			
		||||
              <div className="map-attribution text-right">
 | 
			
		||||
                <div>
 | 
			
		||||
                  <span className="map-attribution-text">
 | 
			
		||||
                    ©
 | 
			
		||||
                  </span>
 | 
			
		||||
                  <span className="map-attribution-text">©</span>
 | 
			
		||||
                  <a
 | 
			
		||||
                    className="map-attribution-text"
 | 
			
		||||
                    href="http://www.openstreetmap.org/copyright"
 | 
			
		||||
@@ -48,15 +49,18 @@ export default function ActivityCard (props) {
 | 
			
		||||
          )}
 | 
			
		||||
          <div className="col">
 | 
			
		||||
            <p>
 | 
			
		||||
              <i className="fa fa-clock-o" aria-hidden="true" />{' '}
 | 
			
		||||
              Duration: {activity.moving}
 | 
			
		||||
              <i className="fa fa-clock-o" aria-hidden="true" /> Duration:{' '}
 | 
			
		||||
              {activity.moving}
 | 
			
		||||
              {activity.map ? (
 | 
			
		||||
                <span><br /><br /></span>
 | 
			
		||||
                <span>
 | 
			
		||||
                  <br />
 | 
			
		||||
                  <br />
 | 
			
		||||
                </span>
 | 
			
		||||
              ) : (
 | 
			
		||||
                ' - '
 | 
			
		||||
              )}
 | 
			
		||||
              <i className="fa fa-road" aria-hidden="true" />{' '}
 | 
			
		||||
              Distance: {activity.distance} km
 | 
			
		||||
              <i className="fa fa-road" aria-hidden="true" /> Distance:{' '}
 | 
			
		||||
              {activity.distance} km
 | 
			
		||||
            </p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,16 @@
 | 
			
		||||
// eslint-disable-next-line max-len
 | 
			
		||||
// source: https://blog.flowandform.agency/create-a-custom-calendar-in-react-3df1bfd0b728
 | 
			
		||||
import {
 | 
			
		||||
  addDays, addMonths, endOfMonth, endOfWeek, format, isSameDay, isSameMonth,
 | 
			
		||||
  startOfMonth, startOfWeek, subMonths
 | 
			
		||||
  addDays,
 | 
			
		||||
  addMonths,
 | 
			
		||||
  endOfMonth,
 | 
			
		||||
  endOfWeek,
 | 
			
		||||
  format,
 | 
			
		||||
  isSameDay,
 | 
			
		||||
  isSameMonth,
 | 
			
		||||
  startOfMonth,
 | 
			
		||||
  startOfWeek,
 | 
			
		||||
  subMonths,
 | 
			
		||||
} from 'date-fns'
 | 
			
		||||
import React, { Fragment } from 'react'
 | 
			
		||||
import { connect } from 'react-redux'
 | 
			
		||||
@@ -21,7 +29,6 @@ const getStartAndEndMonth = date => {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Calendar extends React.Component {
 | 
			
		||||
  constructor(props, context) {
 | 
			
		||||
    super(props, context)
 | 
			
		||||
@@ -42,21 +49,13 @@ class Calendar extends React.Component {
 | 
			
		||||
    return (
 | 
			
		||||
      <div className="header row flex-middle">
 | 
			
		||||
        <div className="col col-start" onClick={() => this.handlePrevMonth()}>
 | 
			
		||||
          <i
 | 
			
		||||
            className="fa fa-chevron-left"
 | 
			
		||||
            aria-hidden="true"
 | 
			
		||||
          />
 | 
			
		||||
          <i className="fa fa-chevron-left" aria-hidden="true" />
 | 
			
		||||
        </div>
 | 
			
		||||
        <div className="col col-center">
 | 
			
		||||
          <span>
 | 
			
		||||
            {format(this.state.currentMonth, dateFormat)}
 | 
			
		||||
          </span>
 | 
			
		||||
          <span>{format(this.state.currentMonth, dateFormat)}</span>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div className="col col-end" onClick={() => this.handleNextMonth()}>
 | 
			
		||||
          <i
 | 
			
		||||
            className="fa fa-chevron-right"
 | 
			
		||||
            aria-hidden="true"
 | 
			
		||||
          />
 | 
			
		||||
          <i className="fa fa-chevron-right" aria-hidden="true" />
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    )
 | 
			
		||||
@@ -80,11 +79,9 @@ class Calendar extends React.Component {
 | 
			
		||||
  filterActivities(day) {
 | 
			
		||||
    const { activities, user } = this.props
 | 
			
		||||
    if (activities) {
 | 
			
		||||
      return activities
 | 
			
		||||
        .filter(act => isSameDay(
 | 
			
		||||
          getDateWithTZ(act.activity_date, user.timezone),
 | 
			
		||||
          day
 | 
			
		||||
        ))
 | 
			
		||||
      return activities.filter(act =>
 | 
			
		||||
        isSameDay(getDateWithTZ(act.activity_date, user.timezone), day)
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
    return []
 | 
			
		||||
  }
 | 
			
		||||
@@ -104,14 +101,9 @@ class Calendar extends React.Component {
 | 
			
		||||
      for (let i = 0; i < 7; i++) {
 | 
			
		||||
        formattedDate = format(day, dateFormat)
 | 
			
		||||
        const dayActivities = this.filterActivities(day)
 | 
			
		||||
        const isDisabled = isSameMonth(day, currentMonth)
 | 
			
		||||
          ? ''
 | 
			
		||||
          : 'disabled'
 | 
			
		||||
        const isDisabled = isSameMonth(day, currentMonth) ? '' : 'disabled'
 | 
			
		||||
        days.push(
 | 
			
		||||
          <div
 | 
			
		||||
            className={`col cell img-${isDisabled}`}
 | 
			
		||||
            key={day}
 | 
			
		||||
          >
 | 
			
		||||
          <div className={`col cell img-${isDisabled}`} key={day}>
 | 
			
		||||
            <span className="number">{formattedDate}</span>
 | 
			
		||||
            {dayActivities.map(act => (
 | 
			
		||||
              <Link key={act.id} to={`/activities/${act.id}`}>
 | 
			
		||||
@@ -129,11 +121,14 @@ class Calendar extends React.Component {
 | 
			
		||||
                      <i
 | 
			
		||||
                        className="fa fa-trophy custom-fa-small"
 | 
			
		||||
                        aria-hidden="true"
 | 
			
		||||
                        title={act.records.map(rec => ` ${
 | 
			
		||||
                          recordsLabels.filter(
 | 
			
		||||
                            r => r.record_type === rec.record_type
 | 
			
		||||
                          )[0].label
 | 
			
		||||
                          }`)}
 | 
			
		||||
                        title={act.records.map(
 | 
			
		||||
                          rec =>
 | 
			
		||||
                            ` ${
 | 
			
		||||
                              recordsLabels.filter(
 | 
			
		||||
                                r => r.record_type === rec.record_type
 | 
			
		||||
                              )[0].label
 | 
			
		||||
                            }`
 | 
			
		||||
                        )}
 | 
			
		||||
                      />
 | 
			
		||||
                    </sup>
 | 
			
		||||
                  )}
 | 
			
		||||
@@ -196,10 +191,9 @@ export default connect(
 | 
			
		||||
  dispatch => ({
 | 
			
		||||
    loadMonthActivities: (start, end) => {
 | 
			
		||||
      const dateFormat = 'yyyy-MM-dd'
 | 
			
		||||
      dispatch(getMonthActivities(
 | 
			
		||||
        format(start, dateFormat),
 | 
			
		||||
        format(end, dateFormat),
 | 
			
		||||
      ))
 | 
			
		||||
      dispatch(
 | 
			
		||||
        getMonthActivities(format(start, dateFormat), format(end, dateFormat))
 | 
			
		||||
      )
 | 
			
		||||
    },
 | 
			
		||||
  })
 | 
			
		||||
)(Calendar)
 | 
			
		||||
 
 | 
			
		||||
@@ -3,67 +3,59 @@ import { Link } from 'react-router-dom'
 | 
			
		||||
 | 
			
		||||
import { formatRecord } from '../../utils/activities'
 | 
			
		||||
 | 
			
		||||
export default function RecordsCard (props) {
 | 
			
		||||
export default function RecordsCard(props) {
 | 
			
		||||
  const { records, sports, user } = 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: [],
 | 
			
		||||
        }
 | 
			
		||||
    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, user.timezone))
 | 
			
		||||
      return sportList
 | 
			
		||||
    }
 | 
			
		||||
    sportList[sport.label].records.push(formatRecord(record, user.timezone))
 | 
			
		||||
    return sportList
 | 
			
		||||
  }, {})
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="card activity-card">
 | 
			
		||||
      <div className="card-header">
 | 
			
		||||
        Personal records
 | 
			
		||||
      </div>
 | 
			
		||||
      <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 table-sm 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 className="text-right">
 | 
			
		||||
                     {rec.value}
 | 
			
		||||
                    </td>
 | 
			
		||||
                    <td className="text-right">
 | 
			
		||||
                      <Link to={`/activities/${rec.activity_id}`}>
 | 
			
		||||
                        {rec.activity_date}
 | 
			
		||||
                      </Link>
 | 
			
		||||
                    </td>
 | 
			
		||||
          : Object.keys(recordsBySport).map(sportLabel => (
 | 
			
		||||
              <table
 | 
			
		||||
                className="table table-borderless table-sm record-table"
 | 
			
		||||
                key={sportLabel}
 | 
			
		||||
              >
 | 
			
		||||
                <thead>
 | 
			
		||||
                  <tr>
 | 
			
		||||
                    <th colSpan="3">
 | 
			
		||||
                      <img
 | 
			
		||||
                        alt={`${sportLabel} logo`}
 | 
			
		||||
                        className="record-logo"
 | 
			
		||||
                        src={recordsBySport[sportLabel].img}
 | 
			
		||||
                      />
 | 
			
		||||
                      {sportLabel}
 | 
			
		||||
                    </th>
 | 
			
		||||
                  </tr>
 | 
			
		||||
                ))}
 | 
			
		||||
              </tbody>
 | 
			
		||||
            </table>))
 | 
			
		||||
          )
 | 
			
		||||
        }
 | 
			
		||||
                </thead>
 | 
			
		||||
                <tbody>
 | 
			
		||||
                  {recordsBySport[sportLabel].records.map(rec => (
 | 
			
		||||
                    <tr key={rec.id}>
 | 
			
		||||
                      <td>{rec.record_type}</td>
 | 
			
		||||
                      <td className="text-right">{rec.value}</td>
 | 
			
		||||
                      <td className="text-right">
 | 
			
		||||
                        <Link to={`/activities/${rec.activity_id}`}>
 | 
			
		||||
                          {rec.activity_date}
 | 
			
		||||
                        </Link>
 | 
			
		||||
                      </td>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                  ))}
 | 
			
		||||
                </tbody>
 | 
			
		||||
              </table>
 | 
			
		||||
            ))}
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,6 @@ import React from 'react'
 | 
			
		||||
 | 
			
		||||
import Stats from '../Common/Stats'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export default class Statistics extends React.Component {
 | 
			
		||||
  constructor(props, context) {
 | 
			
		||||
    super(props, context)
 | 
			
		||||
@@ -19,9 +18,7 @@ export default class Statistics extends React.Component {
 | 
			
		||||
  render() {
 | 
			
		||||
    return (
 | 
			
		||||
      <div className="card activity-card">
 | 
			
		||||
        <div className="card-header">
 | 
			
		||||
          This month
 | 
			
		||||
        </div>
 | 
			
		||||
        <div className="card-header">This month</div>
 | 
			
		||||
        <div className="card-body">
 | 
			
		||||
          <Stats displayEmpty={false} statsParams={this.state} />
 | 
			
		||||
        </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
import React from 'react'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export default function UserStatistics (props) {
 | 
			
		||||
export default function UserStatistics(props) {
 | 
			
		||||
  const { user } = props
 | 
			
		||||
  const days = user.total_duration.match(/day/g)
 | 
			
		||||
    ? `${user.total_duration.split(',')[0]},`
 | 
			
		||||
 
 | 
			
		||||
@@ -25,11 +25,17 @@ class DashBoard extends React.Component {
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const {
 | 
			
		||||
      activities, loadMoreActivities, message, records, sports, user
 | 
			
		||||
      activities,
 | 
			
		||||
      loadMoreActivities,
 | 
			
		||||
      message,
 | 
			
		||||
      records,
 | 
			
		||||
      sports,
 | 
			
		||||
      user,
 | 
			
		||||
    } = this.props
 | 
			
		||||
    const paginationEnd = activities.length > 0
 | 
			
		||||
      ? activities[activities.length - 1].previous_activity === null
 | 
			
		||||
      : true
 | 
			
		||||
    const paginationEnd =
 | 
			
		||||
      activities.length > 0
 | 
			
		||||
        ? activities[activities.length - 1].previous_activity === null
 | 
			
		||||
        : true
 | 
			
		||||
    const { page } = this.state
 | 
			
		||||
    return (
 | 
			
		||||
      <div>
 | 
			
		||||
@@ -39,7 +45,8 @@ class DashBoard extends React.Component {
 | 
			
		||||
        {message ? (
 | 
			
		||||
          <code>{message}</code>
 | 
			
		||||
        ) : (
 | 
			
		||||
          (activities && sports.length > 0) && (
 | 
			
		||||
          activities &&
 | 
			
		||||
          sports.length > 0 && (
 | 
			
		||||
            <div className="container dashboard">
 | 
			
		||||
              <UserStatistics user={user} />
 | 
			
		||||
              <div className="row">
 | 
			
		||||
@@ -51,23 +58,24 @@ class DashBoard extends React.Component {
 | 
			
		||||
                  <Calendar />
 | 
			
		||||
                  {activities.length > 0 ? (
 | 
			
		||||
                    activities.map(activity => (
 | 
			
		||||
                    <ActivityCard
 | 
			
		||||
                      activity={activity}
 | 
			
		||||
                      key={activity.id}
 | 
			
		||||
                      sports={sports}
 | 
			
		||||
                      user={user}
 | 
			
		||||
                    />)
 | 
			
		||||
                  )) : (
 | 
			
		||||
                      <ActivityCard
 | 
			
		||||
                        activity={activity}
 | 
			
		||||
                        key={activity.id}
 | 
			
		||||
                        sports={sports}
 | 
			
		||||
                        user={user}
 | 
			
		||||
                      />
 | 
			
		||||
                    ))
 | 
			
		||||
                  ) : (
 | 
			
		||||
                    <div className="card text-center">
 | 
			
		||||
                      <div className="card-body">
 | 
			
		||||
                        No workouts. {' '}
 | 
			
		||||
                        No workouts.{' '}
 | 
			
		||||
                        <Link to={{ pathname: '/activities/add' }}>
 | 
			
		||||
                          Upload one !
 | 
			
		||||
                        </Link>
 | 
			
		||||
                      </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                  )}
 | 
			
		||||
                  {!paginationEnd &&
 | 
			
		||||
                  {!paginationEnd && (
 | 
			
		||||
                    <input
 | 
			
		||||
                      type="submit"
 | 
			
		||||
                      className="btn btn-default btn-md btn-block"
 | 
			
		||||
@@ -77,7 +85,7 @@ class DashBoard extends React.Component {
 | 
			
		||||
                        this.setState({ page: page + 1 })
 | 
			
		||||
                      }}
 | 
			
		||||
                    />
 | 
			
		||||
                  }
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,8 @@ export default function Footer() {
 | 
			
		||||
          rel="noopener noreferrer"
 | 
			
		||||
        >
 | 
			
		||||
          source code
 | 
			
		||||
        </a> under{' '}
 | 
			
		||||
        </a>{' '}
 | 
			
		||||
        under{' '}
 | 
			
		||||
        <a
 | 
			
		||||
          href="https://choosealicense.com/licenses/gpl-3.0/"
 | 
			
		||||
          target="_blank"
 | 
			
		||||
@@ -21,7 +22,7 @@ export default function Footer() {
 | 
			
		||||
        >
 | 
			
		||||
          GPLv3
 | 
			
		||||
        </a>{' '}
 | 
			
		||||
         license -{' '}
 | 
			
		||||
        license -{' '}
 | 
			
		||||
        <a
 | 
			
		||||
          href="https://samr1.github.io/FitTrackee/"
 | 
			
		||||
          target="_blank"
 | 
			
		||||
@@ -33,4 +34,3 @@ export default function Footer() {
 | 
			
		||||
    </footer>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -75,16 +75,16 @@ class NavBar extends React.PureComponent {
 | 
			
		||||
                  </li>
 | 
			
		||||
                )}
 | 
			
		||||
                {/* {user.admin && ( */}
 | 
			
		||||
                  {/* <li className="nav-item"> */}
 | 
			
		||||
                    {/* <Link */}
 | 
			
		||||
                      {/* className="nav-link" */}
 | 
			
		||||
                      {/* to={{ */}
 | 
			
		||||
                        {/* pathname: '/admin', */}
 | 
			
		||||
                      {/* }} */}
 | 
			
		||||
                    {/* > */}
 | 
			
		||||
                      {/* Admin */}
 | 
			
		||||
                    {/* </Link> */}
 | 
			
		||||
                  {/* </li> */}
 | 
			
		||||
                {/* <li className="nav-item"> */}
 | 
			
		||||
                {/* <Link */}
 | 
			
		||||
                {/* className="nav-link" */}
 | 
			
		||||
                {/* to={{ */}
 | 
			
		||||
                {/* pathname: '/admin', */}
 | 
			
		||||
                {/* }} */}
 | 
			
		||||
                {/* > */}
 | 
			
		||||
                {/* Admin */}
 | 
			
		||||
                {/* </Link> */}
 | 
			
		||||
                {/* </li> */}
 | 
			
		||||
                {/* )} */}
 | 
			
		||||
              </ul>
 | 
			
		||||
              <ul className="navbar-nav flex-row ml-md-auto d-none d-md-flex">
 | 
			
		||||
@@ -115,8 +115,7 @@ class NavBar extends React.PureComponent {
 | 
			
		||||
                {picture === true && (
 | 
			
		||||
                  <img
 | 
			
		||||
                    alt="Avatar"
 | 
			
		||||
                    src={`${apiUrl}users/${id}/picture` +
 | 
			
		||||
                    `?${Date.now()}`}
 | 
			
		||||
                    src={`${apiUrl}users/${id}/picture` + `?${Date.now()}`}
 | 
			
		||||
                    className="img-fluid App-nav-profile-img"
 | 
			
		||||
                  />
 | 
			
		||||
                )}
 | 
			
		||||
@@ -153,11 +152,9 @@ class NavBar extends React.PureComponent {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default connect(
 | 
			
		||||
  ({ user }) => ({
 | 
			
		||||
    id: user.id,
 | 
			
		||||
    isAuthenticated: user.isAuthenticated,
 | 
			
		||||
    picture: user.picture,
 | 
			
		||||
    username: user.username,
 | 
			
		||||
  })
 | 
			
		||||
)(NavBar)
 | 
			
		||||
export default connect(({ user }) => ({
 | 
			
		||||
  id: user.id,
 | 
			
		||||
  isAuthenticated: user.isAuthenticated,
 | 
			
		||||
  picture: user.picture,
 | 
			
		||||
  username: user.username,
 | 
			
		||||
}))(NavBar)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import React from 'react'
 | 
			
		||||
import { Helmet } from 'react-helmet'
 | 
			
		||||
 | 
			
		||||
export default function AccessDenied () {
 | 
			
		||||
export default function AccessDenied() {
 | 
			
		||||
  return (
 | 
			
		||||
    <div>
 | 
			
		||||
      <Helmet>
 | 
			
		||||
@@ -9,7 +9,7 @@ export default function AccessDenied () {
 | 
			
		||||
      </Helmet>
 | 
			
		||||
      <h1 className="page-title">Access denied</h1>
 | 
			
		||||
      <p className="App-center">
 | 
			
		||||
        {'You don\'t have permissions to access this page.'}
 | 
			
		||||
        {"You don't have permissions to access this page."}
 | 
			
		||||
      </p>
 | 
			
		||||
    </div>
 | 
			
		||||
  )
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,13 @@
 | 
			
		||||
import React from 'react'
 | 
			
		||||
import { Helmet } from 'react-helmet'
 | 
			
		||||
 | 
			
		||||
export default function NotFound () {
 | 
			
		||||
export default function NotFound() {
 | 
			
		||||
  return (
 | 
			
		||||
    <div>
 | 
			
		||||
      <Helmet>
 | 
			
		||||
        <title>fittrackee - 404</title>
 | 
			
		||||
      </Helmet>
 | 
			
		||||
        <h1 className="page-title">Page not found</h1>
 | 
			
		||||
      <h1 className="page-title">Page not found</h1>
 | 
			
		||||
    </div>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ import {
 | 
			
		||||
  addYears,
 | 
			
		||||
  subMonths,
 | 
			
		||||
  subWeeks,
 | 
			
		||||
  subYears
 | 
			
		||||
  subYears,
 | 
			
		||||
} from 'date-fns'
 | 
			
		||||
import React from 'react'
 | 
			
		||||
import { Helmet } from 'react-helmet'
 | 
			
		||||
@@ -32,7 +32,7 @@ class Statistics extends React.Component {
 | 
			
		||||
        end: endOfMonth(date),
 | 
			
		||||
        duration: 'month',
 | 
			
		||||
        type: 'by_time',
 | 
			
		||||
      }
 | 
			
		||||
      },
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -51,26 +51,26 @@ class Statistics extends React.Component {
 | 
			
		||||
    const duration = e.target.name
 | 
			
		||||
 | 
			
		||||
    const date = new Date()
 | 
			
		||||
    const start = duration === 'year'
 | 
			
		||||
      ? startOfYear(subYears(date, 9))
 | 
			
		||||
      : duration === 'week'
 | 
			
		||||
    const start =
 | 
			
		||||
      duration === 'year'
 | 
			
		||||
        ? startOfYear(subYears(date, 9))
 | 
			
		||||
        : duration === 'week'
 | 
			
		||||
        ? startOfMonth(subMonths(date, 2))
 | 
			
		||||
        : startOfMonth(subMonths(date, 11))
 | 
			
		||||
    const end = duration === 'year'
 | 
			
		||||
      ? endOfYear(date)
 | 
			
		||||
      : duration === 'week'
 | 
			
		||||
    const end =
 | 
			
		||||
      duration === 'year'
 | 
			
		||||
        ? endOfYear(date)
 | 
			
		||||
        : duration === 'week'
 | 
			
		||||
        ? endOfWeek(date)
 | 
			
		||||
        : endOfMonth(date)
 | 
			
		||||
    this.setState({ statsParams:
 | 
			
		||||
      { duration, end, start, type: 'by_time' }
 | 
			
		||||
    })
 | 
			
		||||
    this.setState({ statsParams: { duration, end, start, type: 'by_time' } })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleOnChangeSports(sportId) {
 | 
			
		||||
    const { displayedSports } = this.state
 | 
			
		||||
    if (displayedSports.includes(sportId)) {
 | 
			
		||||
      this.setState({
 | 
			
		||||
        displayedSports: displayedSports.filter(s => s !== sportId)
 | 
			
		||||
        displayedSports: displayedSports.filter(s => s !== sportId),
 | 
			
		||||
      })
 | 
			
		||||
    } else {
 | 
			
		||||
      this.setState({ displayedSports: displayedSports.concat([sportId]) })
 | 
			
		||||
@@ -81,30 +81,34 @@ class Statistics extends React.Component {
 | 
			
		||||
    const { start, end, duration } = this.state.statsParams
 | 
			
		||||
    let newStart, newEnd
 | 
			
		||||
    if (forward) {
 | 
			
		||||
      newStart = duration === 'year'
 | 
			
		||||
        ? startOfYear(subYears(start, 1))
 | 
			
		||||
        : duration === 'week'
 | 
			
		||||
      newStart =
 | 
			
		||||
        duration === 'year'
 | 
			
		||||
          ? startOfYear(subYears(start, 1))
 | 
			
		||||
          : duration === 'week'
 | 
			
		||||
          ? startOfWeek(subWeeks(start, 1))
 | 
			
		||||
          : startOfMonth(subMonths(start, 1))
 | 
			
		||||
      newEnd = duration === 'year'
 | 
			
		||||
        ? endOfYear(subYears(end, 1))
 | 
			
		||||
        : duration === 'week'
 | 
			
		||||
      newEnd =
 | 
			
		||||
        duration === 'year'
 | 
			
		||||
          ? endOfYear(subYears(end, 1))
 | 
			
		||||
          : duration === 'week'
 | 
			
		||||
          ? endOfWeek(subWeeks(end, 1))
 | 
			
		||||
          : endOfMonth(subMonths(end, 1))
 | 
			
		||||
    } else {
 | 
			
		||||
      newStart = duration === 'year'
 | 
			
		||||
        ? startOfYear(addYears(start, 1))
 | 
			
		||||
        : duration === 'week'
 | 
			
		||||
      newStart =
 | 
			
		||||
        duration === 'year'
 | 
			
		||||
          ? startOfYear(addYears(start, 1))
 | 
			
		||||
          : duration === 'week'
 | 
			
		||||
          ? startOfWeek(addWeeks(start, 1))
 | 
			
		||||
          : startOfMonth(addMonths(start, 1))
 | 
			
		||||
      newEnd = duration === 'year'
 | 
			
		||||
        ? endOfYear(addYears(end, 1))
 | 
			
		||||
        : duration === 'week'
 | 
			
		||||
      newEnd =
 | 
			
		||||
        duration === 'year'
 | 
			
		||||
          ? endOfYear(addYears(end, 1))
 | 
			
		||||
          : duration === 'week'
 | 
			
		||||
          ? endOfWeek(addWeeks(end, 1))
 | 
			
		||||
          : endOfMonth(addMonths(end, 1))
 | 
			
		||||
    }
 | 
			
		||||
    this.setState({ statsParams:
 | 
			
		||||
      { duration, end: newEnd, start: newStart, type: 'by_time' }
 | 
			
		||||
    this.setState({
 | 
			
		||||
      statsParams: { duration, end: newEnd, start: newStart, type: 'by_time' },
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -118,9 +122,7 @@ class Statistics extends React.Component {
 | 
			
		||||
        </Helmet>
 | 
			
		||||
        <div className="container dashboard">
 | 
			
		||||
          <div className="card activity-card">
 | 
			
		||||
            <div className="card-header">
 | 
			
		||||
              Statistics
 | 
			
		||||
            </div>
 | 
			
		||||
            <div className="card-header">Statistics</div>
 | 
			
		||||
            <div className="card-body">
 | 
			
		||||
              <div className="chart-filters row">
 | 
			
		||||
                <div className="col chart-arrows">
 | 
			
		||||
@@ -135,7 +137,7 @@ class Statistics extends React.Component {
 | 
			
		||||
                <div className="col-md-3 time-frames justify-content-around">
 | 
			
		||||
                  {durations.map(d => (
 | 
			
		||||
                    <div className="time-frame" key={d}>
 | 
			
		||||
                      <label >
 | 
			
		||||
                      <label>
 | 
			
		||||
                        <input
 | 
			
		||||
                          type="radio"
 | 
			
		||||
                          id={d}
 | 
			
		||||
@@ -143,7 +145,7 @@ class Statistics extends React.Component {
 | 
			
		||||
                          checked={d === statsParams.duration}
 | 
			
		||||
                          onChange={e => this.handleOnChangeDuration(e)}
 | 
			
		||||
                        />
 | 
			
		||||
                        <span>{ d }</span>
 | 
			
		||||
                        <span>{d}</span>
 | 
			
		||||
                      </label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                  ))}
 | 
			
		||||
@@ -186,8 +188,6 @@ class Statistics extends React.Component {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default connect(
 | 
			
		||||
  state => ({
 | 
			
		||||
    sports: state.sports.data,
 | 
			
		||||
  })
 | 
			
		||||
)(Statistics)
 | 
			
		||||
export default connect(state => ({
 | 
			
		||||
  sports: state.sports.data,
 | 
			
		||||
}))(Statistics)
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ import { Helmet } from 'react-helmet'
 | 
			
		||||
import { history } from '../../index'
 | 
			
		||||
import { isRegistrationAllowed } from '../../utils'
 | 
			
		||||
 | 
			
		||||
export default function Form (props) {
 | 
			
		||||
export default function Form(props) {
 | 
			
		||||
  return (
 | 
			
		||||
    <div>
 | 
			
		||||
      <Helmet>
 | 
			
		||||
@@ -17,7 +17,8 @@ export default function Form (props) {
 | 
			
		||||
        <div className="row">
 | 
			
		||||
          <div className="col-md-3" />
 | 
			
		||||
          <div className="col-md-6">
 | 
			
		||||
            <hr /><br />
 | 
			
		||||
            <hr />
 | 
			
		||||
            <br />
 | 
			
		||||
            {props.formType === 'register' && !isRegistrationAllowed ? (
 | 
			
		||||
              <div className="card">
 | 
			
		||||
                <div className="card-body">Registration is disabled.</div>
 | 
			
		||||
@@ -26,27 +27,30 @@ export default function Form (props) {
 | 
			
		||||
                    type="submit"
 | 
			
		||||
                    className="btn btn-secondary btn-lg btn-block"
 | 
			
		||||
                    onClick={() => history.go(-1)}
 | 
			
		||||
                  >Back
 | 
			
		||||
                  >
 | 
			
		||||
                    Back
 | 
			
		||||
                  </button>
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
            ) : (
 | 
			
		||||
              <form onSubmit={event =>
 | 
			
		||||
                props.handleUserFormSubmit(event, props.formType)}
 | 
			
		||||
              >
 | 
			
		||||
                {props.formType === 'register' &&
 | 
			
		||||
                <div className="form-group">
 | 
			
		||||
                  <input
 | 
			
		||||
                    className="form-control input-lg"
 | 
			
		||||
                    name="username"
 | 
			
		||||
                    placeholder="Enter a username"
 | 
			
		||||
                    required
 | 
			
		||||
                    type="text"
 | 
			
		||||
                    value={props.userForm.username}
 | 
			
		||||
                    onChange={props.onHandleFormChange}
 | 
			
		||||
                  />
 | 
			
		||||
                </div>
 | 
			
		||||
              <form
 | 
			
		||||
                onSubmit={event =>
 | 
			
		||||
                  props.handleUserFormSubmit(event, props.formType)
 | 
			
		||||
                }
 | 
			
		||||
              >
 | 
			
		||||
                {props.formType === 'register' && (
 | 
			
		||||
                  <div className="form-group">
 | 
			
		||||
                    <input
 | 
			
		||||
                      className="form-control input-lg"
 | 
			
		||||
                      name="username"
 | 
			
		||||
                      placeholder="Enter a username"
 | 
			
		||||
                      required
 | 
			
		||||
                      type="text"
 | 
			
		||||
                      value={props.userForm.username}
 | 
			
		||||
                      onChange={props.onHandleFormChange}
 | 
			
		||||
                    />
 | 
			
		||||
                  </div>
 | 
			
		||||
                )}
 | 
			
		||||
                <div className="form-group">
 | 
			
		||||
                  <input
 | 
			
		||||
                    className="form-control input-lg"
 | 
			
		||||
@@ -69,19 +73,19 @@ export default function Form (props) {
 | 
			
		||||
                    onChange={props.onHandleFormChange}
 | 
			
		||||
                  />
 | 
			
		||||
                </div>
 | 
			
		||||
                {props.formType === 'register' &&
 | 
			
		||||
                <div className="form-group">
 | 
			
		||||
                  <input
 | 
			
		||||
                    className="form-control input-lg"
 | 
			
		||||
                    name="password_conf"
 | 
			
		||||
                    placeholder="Enter the password confirmation"
 | 
			
		||||
                    required
 | 
			
		||||
                    type="password"
 | 
			
		||||
                    value={props.userForm.password_conf}
 | 
			
		||||
                    onChange={props.onHandleFormChange}
 | 
			
		||||
                  />
 | 
			
		||||
                </div>
 | 
			
		||||
                }
 | 
			
		||||
                {props.formType === 'register' && (
 | 
			
		||||
                  <div className="form-group">
 | 
			
		||||
                    <input
 | 
			
		||||
                      className="form-control input-lg"
 | 
			
		||||
                      name="password_conf"
 | 
			
		||||
                      placeholder="Enter the password confirmation"
 | 
			
		||||
                      required
 | 
			
		||||
                      type="password"
 | 
			
		||||
                      value={props.userForm.password_conf}
 | 
			
		||||
                      onChange={props.onHandleFormChange}
 | 
			
		||||
                    />
 | 
			
		||||
                  </div>
 | 
			
		||||
                )}
 | 
			
		||||
                <input
 | 
			
		||||
                  type="submit"
 | 
			
		||||
                  className="btn btn-primary btn-lg btn-block"
 | 
			
		||||
 
 | 
			
		||||
@@ -16,8 +16,8 @@ class Logout extends React.Component {
 | 
			
		||||
          <div className="card col-8">
 | 
			
		||||
            <div className="card-body">
 | 
			
		||||
              <div className="text-center">
 | 
			
		||||
                You are now logged out.
 | 
			
		||||
                Click <Link to="/login">here</Link> to log back in.
 | 
			
		||||
                You are now logged out. Click <Link to="/login">here</Link> to
 | 
			
		||||
                log back in.
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
@@ -35,6 +35,6 @@ export default connect(
 | 
			
		||||
  dispatch => ({
 | 
			
		||||
    UserLogout: () => {
 | 
			
		||||
      dispatch(logout())
 | 
			
		||||
    }
 | 
			
		||||
    },
 | 
			
		||||
  })
 | 
			
		||||
)(Logout)
 | 
			
		||||
 
 | 
			
		||||
@@ -7,22 +7,20 @@ import { Link } from 'react-router-dom'
 | 
			
		||||
import { deletePicture, uploadPicture } from '../../actions/user'
 | 
			
		||||
import { apiUrl } from '../../utils'
 | 
			
		||||
 | 
			
		||||
function Profile ({ message, onDeletePicture, onUploadPicture, user }) {
 | 
			
		||||
function Profile({ message, onDeletePicture, onUploadPicture, user }) {
 | 
			
		||||
  return (
 | 
			
		||||
    <div>
 | 
			
		||||
      <Helmet>
 | 
			
		||||
        <title>FitTrackee - Profile</title>
 | 
			
		||||
      </Helmet>
 | 
			
		||||
      { message !== '' && (
 | 
			
		||||
        <code>{message}</code>
 | 
			
		||||
      )}
 | 
			
		||||
      {message !== '' && <code>{message}</code>}
 | 
			
		||||
      <div className="container">
 | 
			
		||||
        <h1 className="page-title">Profile</h1>
 | 
			
		||||
        <div className="row">
 | 
			
		||||
          <div className="col-md-12">
 | 
			
		||||
            <div className="card">
 | 
			
		||||
              <div className="card-header userName">
 | 
			
		||||
                {user.username} {' '}
 | 
			
		||||
                {user.username}{' '}
 | 
			
		||||
                <Link
 | 
			
		||||
                  to={{
 | 
			
		||||
                    pathname: '/profile/edit',
 | 
			
		||||
@@ -35,48 +33,49 @@ function Profile ({ message, onDeletePicture, onUploadPicture, user }) {
 | 
			
		||||
                <div className="row">
 | 
			
		||||
                  <div className="col-md-8">
 | 
			
		||||
                    <p>Email: {user.email}</p>
 | 
			
		||||
                    <p>Registration Date: {
 | 
			
		||||
                         format(new Date(user.created_at), 'dd/MM/yyyy HH:mm')
 | 
			
		||||
                       }
 | 
			
		||||
                    <p>
 | 
			
		||||
                      Registration Date:{' '}
 | 
			
		||||
                      {format(new Date(user.created_at), 'dd/MM/yyyy HH:mm')}
 | 
			
		||||
                    </p>
 | 
			
		||||
                    <p>First Name: {user.first_name}</p>
 | 
			
		||||
                    <p>Last Name: {user.last_name}</p>
 | 
			
		||||
                    <p>Birth Date: {user.birth_date
 | 
			
		||||
                    <p>
 | 
			
		||||
                      Birth Date:{' '}
 | 
			
		||||
                      {user.birth_date
 | 
			
		||||
                        ? format(new Date(user.birth_date), 'dd/MM/yyyy')
 | 
			
		||||
                        : ''
 | 
			
		||||
                      }
 | 
			
		||||
                        : ''}
 | 
			
		||||
                    </p>
 | 
			
		||||
                    <p>Location: {user.location}</p>
 | 
			
		||||
                    <p>Bio: {user.bio}</p>
 | 
			
		||||
                    <p>Time zone: {user.timezone}</p>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div className="col-md-4">
 | 
			
		||||
                    { user.picture === true && (
 | 
			
		||||
                    <div>
 | 
			
		||||
                      <img
 | 
			
		||||
                        alt="Profile"
 | 
			
		||||
                        src={`${apiUrl}users/${user.id}/picture` +
 | 
			
		||||
                             `?${Date.now()}`}
 | 
			
		||||
                        className="img-fluid App-profile-img-small"
 | 
			
		||||
                      />
 | 
			
		||||
                      <br />
 | 
			
		||||
                      <button
 | 
			
		||||
                        type="submit"
 | 
			
		||||
                        onClick={() => onDeletePicture()}
 | 
			
		||||
                      >
 | 
			
		||||
                        Delete picture
 | 
			
		||||
                      </button>
 | 
			
		||||
                      <br /><br />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    {user.picture === true && (
 | 
			
		||||
                      <div>
 | 
			
		||||
                        <img
 | 
			
		||||
                          alt="Profile"
 | 
			
		||||
                          src={
 | 
			
		||||
                            `${apiUrl}users/${user.id}/picture` +
 | 
			
		||||
                            `?${Date.now()}`
 | 
			
		||||
                          }
 | 
			
		||||
                          className="img-fluid App-profile-img-small"
 | 
			
		||||
                        />
 | 
			
		||||
                        <br />
 | 
			
		||||
                        <button type="submit" onClick={() => onDeletePicture()}>
 | 
			
		||||
                          Delete picture
 | 
			
		||||
                        </button>
 | 
			
		||||
                        <br />
 | 
			
		||||
                        <br />
 | 
			
		||||
                      </div>
 | 
			
		||||
                    )}
 | 
			
		||||
                    <form
 | 
			
		||||
                      encType="multipart/form-data"
 | 
			
		||||
                      onSubmit={event => onUploadPicture(event)}
 | 
			
		||||
                    >
 | 
			
		||||
                      <input
 | 
			
		||||
                          type="file"
 | 
			
		||||
                          name="picture"
 | 
			
		||||
                          accept=".png,.jpg,.gif"
 | 
			
		||||
                        type="file"
 | 
			
		||||
                        name="picture"
 | 
			
		||||
                        accept=".png,.jpg,.gif"
 | 
			
		||||
                      />
 | 
			
		||||
                      <br />
 | 
			
		||||
                      <button type="submit">Send</button>
 | 
			
		||||
 
 | 
			
		||||
@@ -7,12 +7,11 @@ import TimezonePicker from 'react-timezone'
 | 
			
		||||
import { handleProfileFormSubmit } from '../../actions/user'
 | 
			
		||||
import { history } from '../../index'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProfileEdit extends React.Component {
 | 
			
		||||
  constructor(props, context) {
 | 
			
		||||
    super(props, context)
 | 
			
		||||
    this.state = {
 | 
			
		||||
      formData: {}
 | 
			
		||||
      formData: {},
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -29,11 +28,13 @@ class ProfileEdit extends React.Component {
 | 
			
		||||
  initForm() {
 | 
			
		||||
    const { user } = this.props
 | 
			
		||||
    const formData = {}
 | 
			
		||||
    Object.keys(user).map(k => user[k] === null
 | 
			
		||||
      ? formData[k] = ''
 | 
			
		||||
      : k === 'birth_date'
 | 
			
		||||
        ? formData[k] = format(new Date(user[k]), 'yyyy-MM-DD')
 | 
			
		||||
        : formData[k] = user[k])
 | 
			
		||||
    Object.keys(user).map(k =>
 | 
			
		||||
      user[k] === null
 | 
			
		||||
        ? (formData[k] = '')
 | 
			
		||||
        : k === 'birth_date'
 | 
			
		||||
        ? (formData[k] = format(new Date(user[k]), 'yyyy-MM-DD'))
 | 
			
		||||
        : (formData[k] = user[k])
 | 
			
		||||
    )
 | 
			
		||||
    this.setState({ formData })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -43,7 +44,7 @@ class ProfileEdit extends React.Component {
 | 
			
		||||
    this.setState(formData)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render () {
 | 
			
		||||
  render() {
 | 
			
		||||
    const { onHandleProfileFormSubmit, message, user } = this.props
 | 
			
		||||
    const { formData } = this.state
 | 
			
		||||
    return (
 | 
			
		||||
@@ -51,9 +52,7 @@ class ProfileEdit extends React.Component {
 | 
			
		||||
        <Helmet>
 | 
			
		||||
          <title>FitTrackee - Edit Profile</title>
 | 
			
		||||
        </Helmet>
 | 
			
		||||
        { message !== '' && (
 | 
			
		||||
            <code>{message}</code>
 | 
			
		||||
          )}
 | 
			
		||||
        {message !== '' && <code>{message}</code>}
 | 
			
		||||
        {formData.isAuthenticated && (
 | 
			
		||||
          <div className="container">
 | 
			
		||||
            <h1 className="page-title">Profile Edition</h1>
 | 
			
		||||
@@ -61,153 +60,153 @@ class ProfileEdit extends React.Component {
 | 
			
		||||
              <div className="col-md-2" />
 | 
			
		||||
              <div className="col-md-8">
 | 
			
		||||
                <div className="card">
 | 
			
		||||
                  <div className="card-header">
 | 
			
		||||
                    {user.username}
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div className="card-header">{user.username}</div>
 | 
			
		||||
                  <div className="card-body">
 | 
			
		||||
                    <div className="row">
 | 
			
		||||
                      <div className="col-md-12">
 | 
			
		||||
                      <form onSubmit={event => {
 | 
			
		||||
                        event.preventDefault()
 | 
			
		||||
                        onHandleProfileFormSubmit(formData)
 | 
			
		||||
                      }}
 | 
			
		||||
                      >
 | 
			
		||||
                        <div className="form-group">
 | 
			
		||||
                          <label>Email:
 | 
			
		||||
                            <input
 | 
			
		||||
                            name="email"
 | 
			
		||||
                            className="form-control input-lg"
 | 
			
		||||
                            type="text"
 | 
			
		||||
                            value={formData.email}
 | 
			
		||||
                            readOnly
 | 
			
		||||
                            />
 | 
			
		||||
                          </label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div className="form-group">
 | 
			
		||||
                          <label>
 | 
			
		||||
                            Registration Date:
 | 
			
		||||
                            <input
 | 
			
		||||
                              name="createdAt"
 | 
			
		||||
                              className="form-control input-lg"
 | 
			
		||||
                              type="text"
 | 
			
		||||
                              value={formData.created_at}
 | 
			
		||||
                              disabled
 | 
			
		||||
                            />
 | 
			
		||||
                          </label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div className="form-group">
 | 
			
		||||
                          <label>
 | 
			
		||||
                            Password:
 | 
			
		||||
                            <input
 | 
			
		||||
                              name="password"
 | 
			
		||||
                              className="form-control input-lg"
 | 
			
		||||
                              type="password"
 | 
			
		||||
                              onChange={e => this.handleFormChange(e)}
 | 
			
		||||
                            />
 | 
			
		||||
                          </label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <form
 | 
			
		||||
                          onSubmit={event => {
 | 
			
		||||
                            event.preventDefault()
 | 
			
		||||
                            onHandleProfileFormSubmit(formData)
 | 
			
		||||
                          }}
 | 
			
		||||
                        >
 | 
			
		||||
                          <div className="form-group">
 | 
			
		||||
                          <label>
 | 
			
		||||
                            Password Confirmation:
 | 
			
		||||
                            <input
 | 
			
		||||
                              name="password_conf"
 | 
			
		||||
                              className="form-control input-lg"
 | 
			
		||||
                              type="password"
 | 
			
		||||
                              onChange={e => this.handleFormChange(e)}
 | 
			
		||||
                            />
 | 
			
		||||
                          </label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <hr />
 | 
			
		||||
                        <div className="form-group">
 | 
			
		||||
                          <label>
 | 
			
		||||
                            First Name:
 | 
			
		||||
                            <input
 | 
			
		||||
                              name="first_name"
 | 
			
		||||
                              className="form-control input-lg"
 | 
			
		||||
                              type="text"
 | 
			
		||||
                              value={formData.first_name}
 | 
			
		||||
                              onChange={e => this.handleFormChange(e)}
 | 
			
		||||
                            />
 | 
			
		||||
                          </label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div className="form-group">
 | 
			
		||||
                          <label>
 | 
			
		||||
                            Last Name:
 | 
			
		||||
                            <input
 | 
			
		||||
                              name="last_name"
 | 
			
		||||
                              className="form-control input-lg"
 | 
			
		||||
                              type="text"
 | 
			
		||||
                              value={formData.last_name}
 | 
			
		||||
                              onChange={e => this.handleFormChange(e)}
 | 
			
		||||
                            />
 | 
			
		||||
                          </label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div className="form-group">
 | 
			
		||||
                          <label>
 | 
			
		||||
                            Birth Date
 | 
			
		||||
                            <input
 | 
			
		||||
                              name="birth_date"
 | 
			
		||||
                              className="form-control input-lg"
 | 
			
		||||
                              type="date"
 | 
			
		||||
                              value={formData.birth_date}
 | 
			
		||||
                              onChange={e => this.handleFormChange(e)}
 | 
			
		||||
                            />
 | 
			
		||||
                          </label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div className="form-group">
 | 
			
		||||
                          <label>
 | 
			
		||||
                            Location:
 | 
			
		||||
                            <input
 | 
			
		||||
                              name="location"
 | 
			
		||||
                              className="form-control input-lg"
 | 
			
		||||
                              type="text"
 | 
			
		||||
                              value={formData.location}
 | 
			
		||||
                              onChange={e => this.handleFormChange(e)}
 | 
			
		||||
                            />
 | 
			
		||||
                          </label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div className="form-group">
 | 
			
		||||
                          <label>
 | 
			
		||||
                            Bio:
 | 
			
		||||
                            <textarea
 | 
			
		||||
                              name="bio"
 | 
			
		||||
                              className="form-control input-lg"
 | 
			
		||||
                              maxLength="200"
 | 
			
		||||
                              value={formData.bio}
 | 
			
		||||
                              onChange={e => this.handleFormChange(e)}
 | 
			
		||||
                            />
 | 
			
		||||
                          </label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div className="form-group">
 | 
			
		||||
                          <label>
 | 
			
		||||
                            Timezone:
 | 
			
		||||
                            <TimezonePicker
 | 
			
		||||
                              className="form-control timezone-custom-height"
 | 
			
		||||
                              onChange={tz => {
 | 
			
		||||
                                const e = { target:
 | 
			
		||||
                                  {
 | 
			
		||||
                                    name: 'timezone',
 | 
			
		||||
                                    value: tz ? tz : 'Europe/Paris'
 | 
			
		||||
                            <label>
 | 
			
		||||
                              Email:
 | 
			
		||||
                              <input
 | 
			
		||||
                                name="email"
 | 
			
		||||
                                className="form-control input-lg"
 | 
			
		||||
                                type="text"
 | 
			
		||||
                                value={formData.email}
 | 
			
		||||
                                readOnly
 | 
			
		||||
                              />
 | 
			
		||||
                            </label>
 | 
			
		||||
                          </div>
 | 
			
		||||
                          <div className="form-group">
 | 
			
		||||
                            <label>
 | 
			
		||||
                              Registration Date:
 | 
			
		||||
                              <input
 | 
			
		||||
                                name="createdAt"
 | 
			
		||||
                                className="form-control input-lg"
 | 
			
		||||
                                type="text"
 | 
			
		||||
                                value={formData.created_at}
 | 
			
		||||
                                disabled
 | 
			
		||||
                              />
 | 
			
		||||
                            </label>
 | 
			
		||||
                          </div>
 | 
			
		||||
                          <div className="form-group">
 | 
			
		||||
                            <label>
 | 
			
		||||
                              Password:
 | 
			
		||||
                              <input
 | 
			
		||||
                                name="password"
 | 
			
		||||
                                className="form-control input-lg"
 | 
			
		||||
                                type="password"
 | 
			
		||||
                                onChange={e => this.handleFormChange(e)}
 | 
			
		||||
                              />
 | 
			
		||||
                            </label>
 | 
			
		||||
                          </div>
 | 
			
		||||
                          <div className="form-group">
 | 
			
		||||
                            <label>
 | 
			
		||||
                              Password Confirmation:
 | 
			
		||||
                              <input
 | 
			
		||||
                                name="password_conf"
 | 
			
		||||
                                className="form-control input-lg"
 | 
			
		||||
                                type="password"
 | 
			
		||||
                                onChange={e => this.handleFormChange(e)}
 | 
			
		||||
                              />
 | 
			
		||||
                            </label>
 | 
			
		||||
                          </div>
 | 
			
		||||
                          <hr />
 | 
			
		||||
                          <div className="form-group">
 | 
			
		||||
                            <label>
 | 
			
		||||
                              First Name:
 | 
			
		||||
                              <input
 | 
			
		||||
                                name="first_name"
 | 
			
		||||
                                className="form-control input-lg"
 | 
			
		||||
                                type="text"
 | 
			
		||||
                                value={formData.first_name}
 | 
			
		||||
                                onChange={e => this.handleFormChange(e)}
 | 
			
		||||
                              />
 | 
			
		||||
                            </label>
 | 
			
		||||
                          </div>
 | 
			
		||||
                          <div className="form-group">
 | 
			
		||||
                            <label>
 | 
			
		||||
                              Last Name:
 | 
			
		||||
                              <input
 | 
			
		||||
                                name="last_name"
 | 
			
		||||
                                className="form-control input-lg"
 | 
			
		||||
                                type="text"
 | 
			
		||||
                                value={formData.last_name}
 | 
			
		||||
                                onChange={e => this.handleFormChange(e)}
 | 
			
		||||
                              />
 | 
			
		||||
                            </label>
 | 
			
		||||
                          </div>
 | 
			
		||||
                          <div className="form-group">
 | 
			
		||||
                            <label>
 | 
			
		||||
                              Birth Date
 | 
			
		||||
                              <input
 | 
			
		||||
                                name="birth_date"
 | 
			
		||||
                                className="form-control input-lg"
 | 
			
		||||
                                type="date"
 | 
			
		||||
                                value={formData.birth_date}
 | 
			
		||||
                                onChange={e => this.handleFormChange(e)}
 | 
			
		||||
                              />
 | 
			
		||||
                            </label>
 | 
			
		||||
                          </div>
 | 
			
		||||
                          <div className="form-group">
 | 
			
		||||
                            <label>
 | 
			
		||||
                              Location:
 | 
			
		||||
                              <input
 | 
			
		||||
                                name="location"
 | 
			
		||||
                                className="form-control input-lg"
 | 
			
		||||
                                type="text"
 | 
			
		||||
                                value={formData.location}
 | 
			
		||||
                                onChange={e => this.handleFormChange(e)}
 | 
			
		||||
                              />
 | 
			
		||||
                            </label>
 | 
			
		||||
                          </div>
 | 
			
		||||
                          <div className="form-group">
 | 
			
		||||
                            <label>
 | 
			
		||||
                              Bio:
 | 
			
		||||
                              <textarea
 | 
			
		||||
                                name="bio"
 | 
			
		||||
                                className="form-control input-lg"
 | 
			
		||||
                                maxLength="200"
 | 
			
		||||
                                value={formData.bio}
 | 
			
		||||
                                onChange={e => this.handleFormChange(e)}
 | 
			
		||||
                              />
 | 
			
		||||
                            </label>
 | 
			
		||||
                          </div>
 | 
			
		||||
                          <div className="form-group">
 | 
			
		||||
                            <label>
 | 
			
		||||
                              Timezone:
 | 
			
		||||
                              <TimezonePicker
 | 
			
		||||
                                className="form-control timezone-custom-height"
 | 
			
		||||
                                onChange={tz => {
 | 
			
		||||
                                  const e = {
 | 
			
		||||
                                    target: {
 | 
			
		||||
                                      name: 'timezone',
 | 
			
		||||
                                      value: tz ? tz : 'Europe/Paris',
 | 
			
		||||
                                    },
 | 
			
		||||
                                  }
 | 
			
		||||
                                }
 | 
			
		||||
                                this.handleFormChange(e)
 | 
			
		||||
                              }}
 | 
			
		||||
                              value={formData.timezone}
 | 
			
		||||
                            />
 | 
			
		||||
                          </label>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <input
 | 
			
		||||
                          type="submit"
 | 
			
		||||
                          className="btn btn-primary btn-lg btn-block"
 | 
			
		||||
                          value="Submit"
 | 
			
		||||
                        />
 | 
			
		||||
                        <input
 | 
			
		||||
                          type="submit"
 | 
			
		||||
                          className="btn btn-secondary btn-lg btn-block"
 | 
			
		||||
                          onClick={() => history.push('/profile')}
 | 
			
		||||
                          value="Cancel"
 | 
			
		||||
                        />
 | 
			
		||||
                      </form>
 | 
			
		||||
                                  this.handleFormChange(e)
 | 
			
		||||
                                }}
 | 
			
		||||
                                value={formData.timezone}
 | 
			
		||||
                              />
 | 
			
		||||
                            </label>
 | 
			
		||||
                          </div>
 | 
			
		||||
                          <input
 | 
			
		||||
                            type="submit"
 | 
			
		||||
                            className="btn btn-primary btn-lg btn-block"
 | 
			
		||||
                            value="Submit"
 | 
			
		||||
                          />
 | 
			
		||||
                          <input
 | 
			
		||||
                            type="submit"
 | 
			
		||||
                            className="btn btn-secondary btn-lg btn-block"
 | 
			
		||||
                            onClick={() => history.push('/profile')}
 | 
			
		||||
                            value="Cancel"
 | 
			
		||||
                          />
 | 
			
		||||
                        </form>
 | 
			
		||||
                      </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                  </div>
 | 
			
		||||
@@ -220,7 +219,6 @@ class ProfileEdit extends React.Component {
 | 
			
		||||
      </div>
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default connect(
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ class UserForm extends React.Component {
 | 
			
		||||
        email: '',
 | 
			
		||||
        password: '',
 | 
			
		||||
        password_conf: '',
 | 
			
		||||
      }
 | 
			
		||||
      },
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -27,7 +27,7 @@ class UserForm extends React.Component {
 | 
			
		||||
 | 
			
		||||
  emptyForm() {
 | 
			
		||||
    const { formData } = this.state
 | 
			
		||||
    Object.keys(formData).map(k => formData[k] = '')
 | 
			
		||||
    Object.keys(formData).map(k => (formData[k] = ''))
 | 
			
		||||
    this.setState(formData)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -38,12 +38,7 @@ class UserForm extends React.Component {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const {
 | 
			
		||||
      formType,
 | 
			
		||||
      message,
 | 
			
		||||
      messages,
 | 
			
		||||
      onHandleUserFormSubmit
 | 
			
		||||
    } = this.props
 | 
			
		||||
    const { formType, message, messages, onHandleUserFormSubmit } = this.props
 | 
			
		||||
    const { formData } = this.state
 | 
			
		||||
    return (
 | 
			
		||||
      <div>
 | 
			
		||||
@@ -51,16 +46,12 @@ class UserForm extends React.Component {
 | 
			
		||||
          <Redirect to="/" />
 | 
			
		||||
        ) : (
 | 
			
		||||
          <div>
 | 
			
		||||
            {message !== '' && (
 | 
			
		||||
              <code>{message}</code>
 | 
			
		||||
            )}
 | 
			
		||||
            {message !== '' && <code>{message}</code>}
 | 
			
		||||
            {messages.length > 0 && (
 | 
			
		||||
              <code>
 | 
			
		||||
                <ul>
 | 
			
		||||
                  {messages.map(msg => (
 | 
			
		||||
                    <li key={msg.id}>
 | 
			
		||||
                      {msg.value}
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li key={msg.id}>{msg.value}</li>
 | 
			
		||||
                  ))}
 | 
			
		||||
                </ul>
 | 
			
		||||
              </code>
 | 
			
		||||
@@ -70,8 +61,8 @@ class UserForm extends React.Component {
 | 
			
		||||
              userForm={formData}
 | 
			
		||||
              onHandleFormChange={event => this.onHandleFormChange(event)}
 | 
			
		||||
              handleUserFormSubmit={event => {
 | 
			
		||||
                  event.preventDefault()
 | 
			
		||||
                  onHandleUserFormSubmit(formData, formType)
 | 
			
		||||
                event.preventDefault()
 | 
			
		||||
                onHandleUserFormSubmit(formData, formType)
 | 
			
		||||
              }}
 | 
			
		||||
            />
 | 
			
		||||
          </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,13 @@
 | 
			
		||||
import { createApiRequest } from '../utils'
 | 
			
		||||
 | 
			
		||||
export default class FitTrackeeApi {
 | 
			
		||||
 | 
			
		||||
  static getData(target, data = {}) {
 | 
			
		||||
    let url = target
 | 
			
		||||
    if (data.id) {
 | 
			
		||||
      url = `${url}/${data.id}`
 | 
			
		||||
    } else if (Object.keys(data).length > 0) {
 | 
			
		||||
      url += '?'
 | 
			
		||||
      Object.keys(data).map(key => url += `&${key}=${data[key]}`)
 | 
			
		||||
      Object.keys(data).map(key => (url += `&${key}=${data[key]}`))
 | 
			
		||||
    }
 | 
			
		||||
    const params = {
 | 
			
		||||
      url: url,
 | 
			
		||||
@@ -39,7 +38,7 @@ export default class FitTrackeeApi {
 | 
			
		||||
 | 
			
		||||
  static postData(target, data) {
 | 
			
		||||
    const params = {
 | 
			
		||||
      url: `${target}${data.id ? `/${data.id}` : '' }`,
 | 
			
		||||
      url: `${target}${data.id ? `/${data.id}` : ''}`,
 | 
			
		||||
      method: 'POST',
 | 
			
		||||
      body: data,
 | 
			
		||||
      type: 'application/json',
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
import { createApiRequest } from '../utils'
 | 
			
		||||
 | 
			
		||||
export default class FitTrackeeApi {
 | 
			
		||||
 | 
			
		||||
  static loginOrRegister(target, data) {
 | 
			
		||||
    const params = {
 | 
			
		||||
      url: `auth/${target}`,
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ import { loadProfile } from './actions/user'
 | 
			
		||||
export const history = createBrowserHistory()
 | 
			
		||||
 | 
			
		||||
history.listen(() => {
 | 
			
		||||
    window.scrollTo(0, 0)
 | 
			
		||||
  window.scrollTo(0, 0)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export const rootNode = document.getElementById('root')
 | 
			
		||||
 
 | 
			
		||||
@@ -128,17 +128,18 @@ const user = (state = initial.user, action) => {
 | 
			
		||||
const statistics = (state = initial.statistics, action) =>
 | 
			
		||||
  handleDataAndError(state, 'statistics', action)
 | 
			
		||||
 | 
			
		||||
export default history => combineReducers({
 | 
			
		||||
  activities,
 | 
			
		||||
  calendarActivities,
 | 
			
		||||
  chartData,
 | 
			
		||||
  gpx,
 | 
			
		||||
  loading,
 | 
			
		||||
  message,
 | 
			
		||||
  messages,
 | 
			
		||||
  records,
 | 
			
		||||
  router: connectRouter(history),
 | 
			
		||||
  sports,
 | 
			
		||||
  statistics,
 | 
			
		||||
  user,
 | 
			
		||||
})
 | 
			
		||||
export default history =>
 | 
			
		||||
  combineReducers({
 | 
			
		||||
    activities,
 | 
			
		||||
    calendarActivities,
 | 
			
		||||
    chartData,
 | 
			
		||||
    gpx,
 | 
			
		||||
    loading,
 | 
			
		||||
    message,
 | 
			
		||||
    messages,
 | 
			
		||||
    records,
 | 
			
		||||
    router: connectRouter(history),
 | 
			
		||||
    sports,
 | 
			
		||||
    statistics,
 | 
			
		||||
    user,
 | 
			
		||||
  })
 | 
			
		||||
 
 | 
			
		||||
@@ -58,13 +58,13 @@ function registerValidSW(swUrl) {
 | 
			
		||||
              // the fresh content will have been added to the cache.
 | 
			
		||||
              // It's the perfect time to display a "New content is
 | 
			
		||||
              // available; please refresh." message in your web app.
 | 
			
		||||
// eslint-disable-next-line no-console
 | 
			
		||||
              // eslint-disable-next-line no-console
 | 
			
		||||
              console.log('New content is available; please refresh.')
 | 
			
		||||
            } else {
 | 
			
		||||
              // At this point, everything has been precached.
 | 
			
		||||
              // It's the perfect time to display a
 | 
			
		||||
              // "Content is cached for offline use." message.
 | 
			
		||||
// eslint-disable-next-line no-console
 | 
			
		||||
              // eslint-disable-next-line no-console
 | 
			
		||||
              console.log('Content is cached for offline use.')
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
@@ -97,7 +97,7 @@ function checkValidServiceWorker(swUrl) {
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    .catch(() => {
 | 
			
		||||
// eslint-disable-next-line no-console
 | 
			
		||||
      // eslint-disable-next-line no-console
 | 
			
		||||
      console.log(
 | 
			
		||||
        'No internet connection found. App is running in offline mode.'
 | 
			
		||||
      )
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@ export const getGeoJson = gpxContent => {
 | 
			
		||||
export const formatActivityDate = (
 | 
			
		||||
  dateTime,
 | 
			
		||||
  dateFormat = null,
 | 
			
		||||
  timeFormat = null,
 | 
			
		||||
  timeFormat = null
 | 
			
		||||
) => {
 | 
			
		||||
  if (!dateFormat) {
 | 
			
		||||
    dateFormat = 'yyyy/MM/dd'
 | 
			
		||||
@@ -70,16 +70,16 @@ export const formatRecord = (record, tz) => {
 | 
			
		||||
    case 'FD':
 | 
			
		||||
      value = `${record.value} km`
 | 
			
		||||
      break
 | 
			
		||||
    default: // 'LD'
 | 
			
		||||
    default:
 | 
			
		||||
      // 'LD'
 | 
			
		||||
      value = record.value // eslint-disable-line prefer-destructuring
 | 
			
		||||
  }
 | 
			
		||||
  const [recordType] = recordsLabels.filter(
 | 
			
		||||
    r => r.record_type === record.record_type
 | 
			
		||||
  )
 | 
			
		||||
  return {
 | 
			
		||||
    activity_date: formatActivityDate(
 | 
			
		||||
      getDateWithTZ(record.activity_date, tz)
 | 
			
		||||
    ).activity_date,
 | 
			
		||||
    activity_date: formatActivityDate(getDateWithTZ(record.activity_date, tz))
 | 
			
		||||
      .activity_date,
 | 
			
		||||
    activity_id: record.activity_id,
 | 
			
		||||
    id: record.id,
 | 
			
		||||
    record_type: recordType.label,
 | 
			
		||||
 
 | 
			
		||||
@@ -3,9 +3,10 @@ import { DateTime } from 'luxon'
 | 
			
		||||
 | 
			
		||||
export const version = '0.2.1-beta' // version stored in 'utils' for now
 | 
			
		||||
export const apiUrl = `${process.env.REACT_APP_API_URL}/api/`
 | 
			
		||||
/* prettier-ignore */
 | 
			
		||||
export const thunderforestApiKey = `${
 | 
			
		||||
  process.env.REACT_APP_THUNDERFOREST_API_KEY
 | 
			
		||||
  }`
 | 
			
		||||
}`
 | 
			
		||||
export const gpxLimit = `${process.env.REACT_APP_GPX_LIMIT_IMPORT}`
 | 
			
		||||
export const isRegistrationAllowed =
 | 
			
		||||
  process.env.REACT_APP_ALLOW_REGISTRATION !== 'false'
 | 
			
		||||
@@ -21,12 +22,10 @@ export const generateIds = arr => {
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export const createApiRequest = params => {
 | 
			
		||||
  const headers = {}
 | 
			
		||||
  if (!params.noAuthorization) {
 | 
			
		||||
    headers.Authorization = `Bearer ${
 | 
			
		||||
      window.localStorage.getItem('authToken')}`
 | 
			
		||||
    headers.Authorization = `Bearer ${window.localStorage.getItem('authToken')}`
 | 
			
		||||
  }
 | 
			
		||||
  if (params.type) {
 | 
			
		||||
    headers['Content-Type'] = params.type
 | 
			
		||||
@@ -42,22 +41,23 @@ export const createApiRequest = params => {
 | 
			
		||||
  }
 | 
			
		||||
  const request = new Request(`${apiUrl}${params.url}`, requestParams)
 | 
			
		||||
  return fetch(request)
 | 
			
		||||
    .then(response => params.method === 'DELETE'
 | 
			
		||||
      ? response
 | 
			
		||||
      : response.json())
 | 
			
		||||
    .then(response => (params.method === 'DELETE' ? response : response.json()))
 | 
			
		||||
    .catch(error => {
 | 
			
		||||
      console.error(error)
 | 
			
		||||
      return new Error('An error occurred. Please contact the administrator.')
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export const getDateWithTZ = (date, tz) => {
 | 
			
		||||
  if (!date) {
 | 
			
		||||
    return ''
 | 
			
		||||
  }
 | 
			
		||||
  const dt = DateTime.fromISO(
 | 
			
		||||
    format(new Date(date), "yyyy-MM-dd'T'HH:mm:ss.SSSxxx")).setZone(tz)
 | 
			
		||||
    format(new Date(date), "yyyy-MM-dd'T'HH:mm:ss.SSSxxx")
 | 
			
		||||
  ).setZone(tz)
 | 
			
		||||
  return parse(
 | 
			
		||||
    dt.toFormat('yyyy-MM-dd HH:mm:ss'), 'yyyy-MM-dd HH:mm:ss', new Date())
 | 
			
		||||
    dt.toFormat('yyyy-MM-dd HH:mm:ss'),
 | 
			
		||||
    'yyyy-MM-dd HH:mm:ss',
 | 
			
		||||
    new Date()
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,13 @@
 | 
			
		||||
import {
 | 
			
		||||
  addDays,
 | 
			
		||||
  addMonths,
 | 
			
		||||
  addYears, format, startOfMonth,
 | 
			
		||||
  addYears,
 | 
			
		||||
  format,
 | 
			
		||||
  startOfMonth,
 | 
			
		||||
  startOfWeek,
 | 
			
		||||
  startOfYear
 | 
			
		||||
  startOfYear,
 | 
			
		||||
} from 'date-fns'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const xAxisFormats = [
 | 
			
		||||
  { duration: 'week', dateFormat: 'yyyy-MM-dd', xAxis: 'dd/MM' },
 | 
			
		||||
  { duration: 'month', dateFormat: 'yyyy-MM', xAxis: 'MM/yyyy' },
 | 
			
		||||
@@ -24,22 +25,21 @@ export const formatDuration = (totalSeconds, formatWithDay = false) => {
 | 
			
		||||
  const minutes = String(Math.floor(totalSeconds / 60)).padStart(2, '0')
 | 
			
		||||
  const seconds = String(totalSeconds % 60).padStart(2, '0')
 | 
			
		||||
  if (formatWithDay) {
 | 
			
		||||
    return `${
 | 
			
		||||
      days === '0' ? '' : `${days}d:`
 | 
			
		||||
    }${
 | 
			
		||||
    return `${days === '0' ? '' : `${days}d:`}${
 | 
			
		||||
      hours === '00' ? '' : `${hours}h:`
 | 
			
		||||
    }${minutes}m:${seconds}s`
 | 
			
		||||
  }
 | 
			
		||||
  return `${hours === '00' ? '' : `${hours}:`}${minutes}:${seconds}`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const formatValue = (displayedData, value) => value === 0
 | 
			
		||||
export const formatValue = (displayedData, value) =>
 | 
			
		||||
  value === 0
 | 
			
		||||
    ? ''
 | 
			
		||||
    : displayedData === 'distance'
 | 
			
		||||
      ? `${value.toFixed(2)} km`
 | 
			
		||||
      : displayedData === 'duration'
 | 
			
		||||
        ? formatDuration(value)
 | 
			
		||||
        : value
 | 
			
		||||
    ? `${value.toFixed(2)} km`
 | 
			
		||||
    : displayedData === 'duration'
 | 
			
		||||
    ? formatDuration(value)
 | 
			
		||||
    : value
 | 
			
		||||
 | 
			
		||||
const dateIncrement = (duration, day) => {
 | 
			
		||||
  switch (duration) {
 | 
			
		||||
@@ -65,16 +65,15 @@ const startDate = (duration, day) => {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const formatStats = (
 | 
			
		||||
  stats, sports, params, displayedSports
 | 
			
		||||
) => {
 | 
			
		||||
export const formatStats = (stats, sports, params, displayedSports) => {
 | 
			
		||||
  const nbActivitiesStats = []
 | 
			
		||||
  const distanceStats = []
 | 
			
		||||
  const durationStats = []
 | 
			
		||||
 | 
			
		||||
  for (let day = startDate(params.duration, params.start);
 | 
			
		||||
       day <= params.end;
 | 
			
		||||
       day = dateIncrement(params.duration, day)
 | 
			
		||||
  for (
 | 
			
		||||
    let day = startDate(params.duration, params.start);
 | 
			
		||||
    day <= params.end;
 | 
			
		||||
    day = dateIncrement(params.duration, day)
 | 
			
		||||
  ) {
 | 
			
		||||
    const [xAxisFormat] = xAxisFormats.filter(
 | 
			
		||||
      x => x.duration === params.duration
 | 
			
		||||
@@ -86,15 +85,17 @@ export const formatStats = (
 | 
			
		||||
    const dataDuration = { date: xAxis }
 | 
			
		||||
 | 
			
		||||
    if (stats[date]) {
 | 
			
		||||
      Object.keys(stats[date]).filter(
 | 
			
		||||
        sportId => displayedSports ? displayedSports.includes(+sportId) : true
 | 
			
		||||
      ).map(sportId => {
 | 
			
		||||
        const sportLabel = sports.filter(s => s.id === +sportId)[0].label
 | 
			
		||||
        dataNbActivities[sportLabel] = stats[date][sportId].nb_activities
 | 
			
		||||
        dataDistance[sportLabel] = stats[date][sportId].total_distance
 | 
			
		||||
        dataDuration[sportLabel] = stats[date][sportId].total_duration
 | 
			
		||||
        return null
 | 
			
		||||
      })
 | 
			
		||||
      Object.keys(stats[date])
 | 
			
		||||
        .filter(sportId =>
 | 
			
		||||
          displayedSports ? displayedSports.includes(+sportId) : true
 | 
			
		||||
        )
 | 
			
		||||
        .map(sportId => {
 | 
			
		||||
          const sportLabel = sports.filter(s => s.id === +sportId)[0].label
 | 
			
		||||
          dataNbActivities[sportLabel] = stats[date][sportId].nb_activities
 | 
			
		||||
          dataDistance[sportLabel] = stats[date][sportId].total_distance
 | 
			
		||||
          dataDuration[sportLabel] = stats[date][sportId].total_duration
 | 
			
		||||
          return null
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    nbActivitiesStats.push(dataNbActivities)
 | 
			
		||||
    distanceStats.push(dataDistance)
 | 
			
		||||
@@ -104,6 +105,6 @@ export const formatStats = (
 | 
			
		||||
  return {
 | 
			
		||||
    activities: nbActivitiesStats,
 | 
			
		||||
    distance: distanceStats,
 | 
			
		||||
    duration: durationStats
 | 
			
		||||
    duration: durationStats,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user