diff --git a/README.md b/README.md index 3f8d615e..eac381c4 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ [![Python Version](https://img.shields.io/badge/python-3.6-brightgreen.svg)](https://python.org) [![Flask Version](https://img.shields.io/badge/flask-1.0-brightgreen.svg)](http://flask.pocoo.org/) [![React Version](https://img.shields.io/badge/react-16.4-brightgreen.svg)](https://reactjs.org/) -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/45d64b31e37e4890a239b8298e66a011)](https://www.codacy.com/app/SamR1/FitTrackee?utm_source=github.com&utm_medium=referral&utm_content=SamR1/mpwo&utm_campaign=badger) -[![Codacy Badge](https://api.codacy.com/project/badge/Coverage/45d64b31e37e4890a239b8298e66a011)](https://www.codacy.com/app/SamR1/FitTrackee?utm_source=github.com&utm_medium=referral&utm_content=SamR1/mpwo&utm_campaign=Badge_Coverage)1 +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/290a285f22e94132904dc13b4dd19d1d)](https://www.codacy.com/app/SamR1/FitTrackee) +[![Codacy Coverage Badge](https://api.codacy.com/project/badge/Coverage/290a285f22e94132904dc13b4dd19d1d)](https://www.codacy.com/app/SamR1/FitTrackee) [![Build Status](https://travis-ci.org/SamR1/FitTrackee.svg?branch=master)](https://travis-ci.org/SamR1/FitTrackee) --- diff --git a/fittrackee_api/fittrackee_api/activities/stats.py b/fittrackee_api/fittrackee_api/activities/stats.py index d305f643..8df8e9f3 100644 --- a/fittrackee_api/fittrackee_api/activities/stats.py +++ b/fittrackee_api/fittrackee_api/activities/stats.py @@ -12,7 +12,7 @@ from .utils_format import convert_timedelta_to_integer stats_blueprint = Blueprint('stats', __name__) -def get_activities(user_id, type): +def get_activities(user_id, filter_type): try: user = User.query.filter_by(id=user_id).first() if not user: @@ -35,7 +35,7 @@ def get_activities(user_id, type): sport_id = params.get('sport_id') time = params.get('time') - if type == 'by_sport': + if filter_type == 'by_sport': sport_id = params.get('sport_id') if sport_id: sport = Sport.query.filter_by(id=sport_id).first() @@ -59,7 +59,7 @@ def get_activities(user_id, type): activities_list = {} for activity in activities: - if type == 'by_sport': + if filter_type == 'by_sport': sport_id = activity.sport_id if sport_id not in activities_list: activities_list[sport_id] = { diff --git a/fittrackee_client/e2e/activities.test.js b/fittrackee_client/e2e/activities.test.js index 43b092a4..17289f93 100644 --- a/fittrackee_client/e2e/activities.test.js +++ b/fittrackee_client/e2e/activities.test.js @@ -18,7 +18,7 @@ test('standard user should be able to add a workout (w/o gpx)', async t => { .typeText('input[name="username"]', username) .typeText('input[name="email"]', email) .typeText('input[name="password"]', password) - .typeText('input[name="passwordConf"]', password) + .typeText('input[name="password_conf"]', password) .click(Selector('input[type="submit"]')) await t diff --git a/fittrackee_client/e2e/admin.test.js b/fittrackee_client/e2e/admin.test.js index 7f841626..516b919d 100644 --- a/fittrackee_client/e2e/admin.test.js +++ b/fittrackee_client/e2e/admin.test.js @@ -23,7 +23,7 @@ test('standard user should not be able to access admin page', async t => { .typeText('input[name="username"]', username) .typeText('input[name="email"]', email) .typeText('input[name="password"]', password) - .typeText('input[name="passwordConf"]', password) + .typeText('input[name="password_conf"]', password) .click(Selector('input[type="submit"]')) await t diff --git a/fittrackee_client/e2e/login.test.js b/fittrackee_client/e2e/login.test.js index 065b2d2b..55fe8259 100644 --- a/fittrackee_client/e2e/login.test.js +++ b/fittrackee_client/e2e/login.test.js @@ -20,7 +20,7 @@ test('should display the registration form', async t => { .expect(Selector('input[name="username"]').exists).notOk() .expect(Selector('input[name="email"]').exists).ok() .expect(Selector('input[name="password"]').exists).ok() - .expect(Selector('input[name="passwordConf"]').exists).notOk() + .expect(Selector('input[name="password_conf"]').exists).notOk() }) test('should throw an error if the user is not registered', async t => { @@ -47,7 +47,7 @@ test('should allow a user to login', async t => { .typeText('input[name="username"]', username) .typeText('input[name="email"]', email) .typeText('input[name="password"]', password) - .typeText('input[name="passwordConf"]', password) + .typeText('input[name="password_conf"]', password) .click(Selector('input[type="submit"]')) await t diff --git a/fittrackee_client/e2e/profile.test.js b/fittrackee_client/e2e/profile.test.js index b4429503..1f378de6 100644 --- a/fittrackee_client/e2e/profile.test.js +++ b/fittrackee_client/e2e/profile.test.js @@ -19,7 +19,7 @@ test('should be able to access his profile page', async t => { .typeText('input[name="username"]', username) .typeText('input[name="email"]', email) .typeText('input[name="password"]', password) - .typeText('input[name="passwordConf"]', password) + .typeText('input[name="password_conf"]', password) .click(Selector('input[type="submit"]')) await t diff --git a/fittrackee_client/e2e/register.test.js b/fittrackee_client/e2e/register.test.js index 306211c3..f0c051e6 100644 --- a/fittrackee_client/e2e/register.test.js +++ b/fittrackee_client/e2e/register.test.js @@ -20,7 +20,7 @@ test('should display the registration form', async t => { .expect(Selector('input[name="username"]').exists).ok() .expect(Selector('input[name="email"]').exists).ok() .expect(Selector('input[name="password"]').exists).ok() - .expect(Selector('input[name="passwordConf"]').exists).ok() + .expect(Selector('input[name="password_conf"]').exists).ok() }) test('should allow a user to register', async t => { @@ -32,7 +32,7 @@ test('should allow a user to register', async t => { .typeText('input[name="username"]', username) .typeText('input[name="email"]', email) .typeText('input[name="password"]', password) - .typeText('input[name="passwordConf"]', password) + .typeText('input[name="password_conf"]', password) .click(Selector('input[type="submit"]')) // assert user is redirected to '/' @@ -48,7 +48,7 @@ test('should throw an error if the username is taken', async t => { .typeText('input[name="username"]', username) .typeText('input[name="email"]', `${email}2`) .typeText('input[name="password"]', password) - .typeText('input[name="passwordConf"]', password) + .typeText('input[name="password_conf"]', password) .click(Selector('input[type="submit"]')) // assert user registration failed @@ -68,7 +68,7 @@ test('should throw an error if the email is taken', async t => { .typeText('input[name="username"]', `${username}2`) .typeText('input[name="email"]', email) .typeText('input[name="password"]', password) - .typeText('input[name="passwordConf"]', password) + .typeText('input[name="password_conf"]', password) .click(Selector('input[type="submit"]')) // assert user registration failed @@ -88,7 +88,7 @@ test('should throw an error if the username is too short', async t => { .typeText('input[name="username"]', `${shortUsername}`) .typeText('input[name="email"]', email) .typeText('input[name="password"]', password) - .typeText('input[name="passwordConf"]', password) + .typeText('input[name="password_conf"]', password) .click(Selector('input[type="submit"]')) // assert user registration failed @@ -108,7 +108,7 @@ test('should throw an error if the user is too long', async t => { .typeText('input[name="username"]', `${longUsername}`) .typeText('input[name="email"]', email) .typeText('input[name="password"]', password) - .typeText('input[name="passwordConf"]', password) + .typeText('input[name="password_conf"]', password) .click(Selector('input[type="submit"]')) // assert user registration failed @@ -128,7 +128,7 @@ test('should throw an error if the email is invalid', async t => { .typeText('input[name="username"]', username) .typeText('input[name="email"]', invalidEmail) .typeText('input[name="password"]', password) - .typeText('input[name="passwordConf"]', password) + .typeText('input[name="password_conf"]', password) .click(Selector('input[type="submit"]')) // assert user registration failed @@ -146,7 +146,7 @@ test('should throw an error if passwords don\'t match', async t => { .typeText('input[name="username"]', username) .typeText('input[name="email"]', email) .typeText('input[name="password"]', password) - .typeText('input[name="passwordConf"]', `${password}2`) + .typeText('input[name="password_conf"]', `${password}2`) .click(Selector('input[type="submit"]')) // assert user registration failed @@ -166,7 +166,7 @@ test('should throw an error if the password is too short', async t => { .typeText('input[name="username"]', username) .typeText('input[name="email"]', email) .typeText('input[name="password"]', invalidPassword) - .typeText('input[name="passwordConf"]', invalidPassword) + .typeText('input[name="password_conf"]', invalidPassword) .click(Selector('input[type="submit"]')) // assert user registration failed diff --git a/fittrackee_client/src/actions/user.js b/fittrackee_client/src/actions/user.js index f7907447..d90ca41d 100644 --- a/fittrackee_client/src/actions/user.js +++ b/fittrackee_client/src/actions/user.js @@ -10,7 +10,7 @@ const AuthErrors = messages => ({ type: 'AUTH_ERRORS', messages }) const PictureError = message => ({ type: 'PICTURE_ERROR', message }) -const ProfileSuccess = message => ({ type: 'PROFILE_SUCCESS', message }) +const ProfileSuccess = profil => ({ type: 'PROFILE_SUCCESS', profil }) const ProfileError = message => ({ type: 'PROFILE_ERROR', message }) @@ -18,30 +18,13 @@ const ProfileUpdateError = message => ({ type: 'PROFILE_UPDATE_ERROR', message }) -const initProfileFormData = user => ({ type: 'INIT_PROFILE_FORM', user }) - -export const emptyForm = () => ({ - type: 'EMPTY_USER_FORMDATA' -}) - -export const handleFormChange = (target, value) => ({ - type: 'UPDATE_USER_FORMDATA', - target, - value, -}) - -export const updateProfileFormData = (target, value) => ({ - type: 'UPDATE_PROFILE_FORMDATA', - target, - value, -}) - export const getProfile = () => dispatch => FitTrackeeApi .getProfile() .then(ret => { if (ret.status === 'success') { dispatch(getData('sports')) - return dispatch(ProfileSuccess(ret)) + ret.data.isAuthenticated = true + return dispatch(ProfileSuccess(ret.data)) } return dispatch(ProfileError(ret.message)) }) @@ -51,11 +34,7 @@ export const getProfile = () => dispatch => FitTrackeeApi export const register = formData => dispatch => FitTrackeeApi - .register( - formData.username, - formData.email, - formData.password, - formData.passwordConf) + .register(formData) .then(ret => { if (ret.status === 'success') { window.localStorage.setItem('authToken', ret.auth_token) @@ -69,7 +48,7 @@ export const register = formData => dispatch => FitTrackeeApi export const login = formData => dispatch => FitTrackeeApi - .login(formData.email, formData.password) + .login(formData) .then(ret => { if (ret.status === 'success') { window.localStorage.setItem('authToken', ret.auth_token) @@ -95,7 +74,7 @@ const RegisterFormControl = formData => { if (formData.username.length < 3 || formData.username.length > 12) { errMsg.push('Username: 3 to 12 characters required.') } - if (formData.password !== formData.passwordConf) { + if (formData.password !== formData.password_conf) { errMsg.push('Password and password confirmation don\'t match.') } if (formData.password.length < 8) { @@ -104,41 +83,26 @@ const RegisterFormControl = formData => { return errMsg } -export const handleUserFormSubmit = (event, formType) => ( - dispatch, - getState -) => { - event.preventDefault() - const state = getState() - const { formData } = state.formData - formData.formData = state.formData.formData +export const handleUserFormSubmit = (formData, formType) => dispatch => { if (formType === 'Login') { - return dispatch(login(formData.formData)) + return dispatch(login(formData)) } // formType === 'Register' - const ret = RegisterFormControl(formData.formData) + const ret = RegisterFormControl(formData) if (ret.length === 0) { - return dispatch(register(formData.formData)) + return dispatch(register(formData)) } return dispatch(AuthErrors(generateIds(ret))) } -export const initProfileForm = () => (dispatch, getState) => { - const state = getState() - return dispatch(initProfileFormData(state.user)) -} - -export const handleProfileFormSubmit = event => (dispatch, getState) => { - event.preventDefault() - const state = getState() - if (!state.formProfile.formProfile.password === - state.formProfile.formProfile.passwordConf) { +export const handleProfileFormSubmit = formData => dispatch => { + if (!formData.password === formData.password_conf) { return dispatch(ProfileUpdateError( 'Password and password confirmation don\'t match.' )) } return FitTrackeeApi - .updateProfile(state.formProfile.formProfile) + .updateProfile(formData) .then(ret => { if (ret.status === 'success') { dispatch(getProfile()) diff --git a/fittrackee_client/src/components/Admin/index.jsx b/fittrackee_client/src/components/Admin/index.jsx index 1e9b8016..0bd5e21f 100644 --- a/fittrackee_client/src/components/Admin/index.jsx +++ b/fittrackee_client/src/components/Admin/index.jsx @@ -19,7 +19,7 @@ function Admin (props) { FitTrackee - Admin {isLoggedIn() ? ( - user.isAdmin ? ( + user.admin ? ( diff --git a/fittrackee_client/src/components/Dashboard/UserStatistics.jsx b/fittrackee_client/src/components/Dashboard/UserStatistics.jsx index 68edd8ef..a7ee2a74 100644 --- a/fittrackee_client/src/components/Dashboard/UserStatistics.jsx +++ b/fittrackee_client/src/components/Dashboard/UserStatistics.jsx @@ -3,12 +3,12 @@ import React from 'react' export default function UserStatistics (props) { const { user } = props - const days = user.totalDuration.match(/day/g) - ? `${user.totalDuration.split(',')[0]},` + const days = user.total_duration.match(/day/g) + ? `${user.total_duration.split(',')[0]},` : '0 days,' - let duration = user.totalDuration.match(/day/g) - ? user.totalDuration.split(', ')[1] - : user.totalDuration + let duration = user.total_duration.match(/day/g) + ? user.total_duration.split(', ')[1] + : user.total_duration duration = `${duration.split(':')[0]}h ${duration.split(':')[1]}min` return (
@@ -20,7 +20,7 @@ export default function UserStatistics (props) {
{user.nbActivities}
-
{`workout${user.nbActivities === 1 ? '' : 's'}`}
+
{`workout${user.nb_activities === 1 ? '' : 's'}`}
@@ -33,7 +33,7 @@ export default function UserStatistics (props) {
- {Number(user.totalDistance).toFixed(2)} + {Number(user.total_distance).toFixed(2)}
km
@@ -60,8 +60,8 @@ export default function UserStatistics (props) {
-
{user.nbSports}
-
{`sport${user.nbSports === 1 ? '' : 's'}`}
+
{user.nb_sports}
+
{`sport${user.nb_sports === 1 ? '' : 's'}`}
diff --git a/fittrackee_client/src/components/NavBar/index.jsx b/fittrackee_client/src/components/NavBar/index.jsx index 47ea79de..f2912b10 100644 --- a/fittrackee_client/src/components/NavBar/index.jsx +++ b/fittrackee_client/src/components/NavBar/index.jsx @@ -58,7 +58,7 @@ function NavBar(props) { )} - {props.user.isAdmin && ( + {props.user.admin && (
  • diff --git a/fittrackee_client/src/components/User/Profile.jsx b/fittrackee_client/src/components/User/Profile.jsx index 2f945a92..9d3c9733 100644 --- a/fittrackee_client/src/components/User/Profile.jsx +++ b/fittrackee_client/src/components/User/Profile.jsx @@ -11,7 +11,7 @@ function Profile ({ message, onDeletePicture, onUploadPicture, user }) { return (
    - FitTrackee - {user.username} - Profile + FitTrackee - Profile { message !== '' && ( {message} @@ -36,11 +36,16 @@ function Profile ({ message, onDeletePicture, onUploadPicture, user }) {

    Email: {user.email}

    Registration Date: { - format(new Date(user.createdAt), 'DD/MM/YYYY HH:mm') - }

    -

    First Name: {user.firstName}

    -

    Last Name: {user.lastName}

    -

    Birth Date: {user.birthDate}

    + format(new Date(user.created_at), 'DD/MM/YYYY HH:mm') + } +

    +

    First Name: {user.first_name}

    +

    Last Name: {user.last_name}

    +

    Birth Date: {user.birth_date + ? format(new Date(user.birth_date), 'DD/MM/YYYY') + : '' + } +

    Location: {user.location}

    Bio: {user.bio}

    Time zone: {user.timezone}

    diff --git a/fittrackee_client/src/components/User/ProfileEdit.jsx b/fittrackee_client/src/components/User/ProfileEdit.jsx index 3b0e2d41..9741d369 100644 --- a/fittrackee_client/src/components/User/ProfileEdit.jsx +++ b/fittrackee_client/src/components/User/ProfileEdit.jsx @@ -1,198 +1,223 @@ +import { format } from 'date-fns' import React from 'react' import { Helmet } from 'react-helmet' import { connect } from 'react-redux' import TimezonePicker from 'react-timezone' -import { - initProfileForm, - updateProfileFormData, - handleProfileFormSubmit -} from '../../actions/user' +import { handleProfileFormSubmit } from '../../actions/user' import { history } from '../../index' class ProfileEdit extends React.Component { - componentDidMount() { - this.props.initForm(this.props.user) + constructor(props, context) { + super(props, context) + this.state = { + formData: {} + } } + + componentDidMount() { + this.initForm() + } + + componentDidUpdate(prevProps) { + if (prevProps.user !== this.props.user) { + this.initForm() + } + } + + 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]) + this.setState({ formData }) + } + + handleFormChange(e) { + const { formData } = this.state + formData[e.target.name] = e.target.value + this.setState(formData) + } + render () { - const { formProfile, - onHandleFormChange, - onHandleProfileFormSubmit, - message, - user - } = this.props + const { onHandleProfileFormSubmit, message, user } = this.props + const { formData } = this.state return (
    - FitTrackee - {user.username} - Edit Profile + FitTrackee - Edit Profile { message !== '' && ( {message} )} -
    -

    Profile Edition

    -
    -
    -
    -
    -
    - {user.username} -
    -
    -
    -
    -
    - onHandleProfileFormSubmit(event)} - > -
    - -
    -
    - -
    -
    - -
    + {formData.isAuthenticated && ( +
    +

    Profile Edition

    +
    +
    +
    +
    +
    + {user.username} +
    +
    +
    +
    + { + event.preventDefault() + onHandleProfileFormSubmit(formData) + }} + >
    - -
    -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    -