Client: refactor (wip)

This commit is contained in:
Sam 2018-06-12 11:47:01 +02:00
parent 5d749f3f47
commit b54f25357f
18 changed files with 291 additions and 421 deletions

View File

@ -4,8 +4,8 @@
[![Python Version](https://img.shields.io/badge/python-3.6-brightgreen.svg)](https://python.org) [![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/) [![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/) [![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/Grade/290a285f22e94132904dc13b4dd19d1d)](https://www.codacy.com/app/SamR1/FitTrackee)
[![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)<sup><sup>1</sup></sup> [![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) [![Build Status](https://travis-ci.org/SamR1/FitTrackee.svg?branch=master)](https://travis-ci.org/SamR1/FitTrackee)
--- ---

View File

@ -12,7 +12,7 @@ from .utils_format import convert_timedelta_to_integer
stats_blueprint = Blueprint('stats', __name__) stats_blueprint = Blueprint('stats', __name__)
def get_activities(user_id, type): def get_activities(user_id, filter_type):
try: try:
user = User.query.filter_by(id=user_id).first() user = User.query.filter_by(id=user_id).first()
if not user: if not user:
@ -35,7 +35,7 @@ def get_activities(user_id, type):
sport_id = params.get('sport_id') sport_id = params.get('sport_id')
time = params.get('time') time = params.get('time')
if type == 'by_sport': if filter_type == 'by_sport':
sport_id = params.get('sport_id') sport_id = params.get('sport_id')
if sport_id: if sport_id:
sport = Sport.query.filter_by(id=sport_id).first() sport = Sport.query.filter_by(id=sport_id).first()
@ -59,7 +59,7 @@ def get_activities(user_id, type):
activities_list = {} activities_list = {}
for activity in activities: for activity in activities:
if type == 'by_sport': if filter_type == 'by_sport':
sport_id = activity.sport_id sport_id = activity.sport_id
if sport_id not in activities_list: if sport_id not in activities_list:
activities_list[sport_id] = { activities_list[sport_id] = {

View File

@ -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="username"]', username)
.typeText('input[name="email"]', email) .typeText('input[name="email"]', email)
.typeText('input[name="password"]', password) .typeText('input[name="password"]', password)
.typeText('input[name="passwordConf"]', password) .typeText('input[name="password_conf"]', password)
.click(Selector('input[type="submit"]')) .click(Selector('input[type="submit"]'))
await t await t

View File

@ -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="username"]', username)
.typeText('input[name="email"]', email) .typeText('input[name="email"]', email)
.typeText('input[name="password"]', password) .typeText('input[name="password"]', password)
.typeText('input[name="passwordConf"]', password) .typeText('input[name="password_conf"]', password)
.click(Selector('input[type="submit"]')) .click(Selector('input[type="submit"]'))
await t await t

View File

@ -20,7 +20,7 @@ test('should display the registration form', async t => {
.expect(Selector('input[name="username"]').exists).notOk() .expect(Selector('input[name="username"]').exists).notOk()
.expect(Selector('input[name="email"]').exists).ok() .expect(Selector('input[name="email"]').exists).ok()
.expect(Selector('input[name="password"]').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 => { 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="username"]', username)
.typeText('input[name="email"]', email) .typeText('input[name="email"]', email)
.typeText('input[name="password"]', password) .typeText('input[name="password"]', password)
.typeText('input[name="passwordConf"]', password) .typeText('input[name="password_conf"]', password)
.click(Selector('input[type="submit"]')) .click(Selector('input[type="submit"]'))
await t await t

View File

@ -19,7 +19,7 @@ test('should be able to access his profile page', async t => {
.typeText('input[name="username"]', username) .typeText('input[name="username"]', username)
.typeText('input[name="email"]', email) .typeText('input[name="email"]', email)
.typeText('input[name="password"]', password) .typeText('input[name="password"]', password)
.typeText('input[name="passwordConf"]', password) .typeText('input[name="password_conf"]', password)
.click(Selector('input[type="submit"]')) .click(Selector('input[type="submit"]'))
await t await t

View File

@ -20,7 +20,7 @@ test('should display the registration form', async t => {
.expect(Selector('input[name="username"]').exists).ok() .expect(Selector('input[name="username"]').exists).ok()
.expect(Selector('input[name="email"]').exists).ok() .expect(Selector('input[name="email"]').exists).ok()
.expect(Selector('input[name="password"]').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 => { 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="username"]', username)
.typeText('input[name="email"]', email) .typeText('input[name="email"]', email)
.typeText('input[name="password"]', password) .typeText('input[name="password"]', password)
.typeText('input[name="passwordConf"]', password) .typeText('input[name="password_conf"]', password)
.click(Selector('input[type="submit"]')) .click(Selector('input[type="submit"]'))
// assert user is redirected to '/' // 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="username"]', username)
.typeText('input[name="email"]', `${email}2`) .typeText('input[name="email"]', `${email}2`)
.typeText('input[name="password"]', password) .typeText('input[name="password"]', password)
.typeText('input[name="passwordConf"]', password) .typeText('input[name="password_conf"]', password)
.click(Selector('input[type="submit"]')) .click(Selector('input[type="submit"]'))
// assert user registration failed // 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="username"]', `${username}2`)
.typeText('input[name="email"]', email) .typeText('input[name="email"]', email)
.typeText('input[name="password"]', password) .typeText('input[name="password"]', password)
.typeText('input[name="passwordConf"]', password) .typeText('input[name="password_conf"]', password)
.click(Selector('input[type="submit"]')) .click(Selector('input[type="submit"]'))
// assert user registration failed // 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="username"]', `${shortUsername}`)
.typeText('input[name="email"]', email) .typeText('input[name="email"]', email)
.typeText('input[name="password"]', password) .typeText('input[name="password"]', password)
.typeText('input[name="passwordConf"]', password) .typeText('input[name="password_conf"]', password)
.click(Selector('input[type="submit"]')) .click(Selector('input[type="submit"]'))
// assert user registration failed // 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="username"]', `${longUsername}`)
.typeText('input[name="email"]', email) .typeText('input[name="email"]', email)
.typeText('input[name="password"]', password) .typeText('input[name="password"]', password)
.typeText('input[name="passwordConf"]', password) .typeText('input[name="password_conf"]', password)
.click(Selector('input[type="submit"]')) .click(Selector('input[type="submit"]'))
// assert user registration failed // 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="username"]', username)
.typeText('input[name="email"]', invalidEmail) .typeText('input[name="email"]', invalidEmail)
.typeText('input[name="password"]', password) .typeText('input[name="password"]', password)
.typeText('input[name="passwordConf"]', password) .typeText('input[name="password_conf"]', password)
.click(Selector('input[type="submit"]')) .click(Selector('input[type="submit"]'))
// assert user registration failed // 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="username"]', username)
.typeText('input[name="email"]', email) .typeText('input[name="email"]', email)
.typeText('input[name="password"]', password) .typeText('input[name="password"]', password)
.typeText('input[name="passwordConf"]', `${password}2`) .typeText('input[name="password_conf"]', `${password}2`)
.click(Selector('input[type="submit"]')) .click(Selector('input[type="submit"]'))
// assert user registration failed // 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="username"]', username)
.typeText('input[name="email"]', email) .typeText('input[name="email"]', email)
.typeText('input[name="password"]', invalidPassword) .typeText('input[name="password"]', invalidPassword)
.typeText('input[name="passwordConf"]', invalidPassword) .typeText('input[name="password_conf"]', invalidPassword)
.click(Selector('input[type="submit"]')) .click(Selector('input[type="submit"]'))
// assert user registration failed // assert user registration failed

View File

@ -10,7 +10,7 @@ const AuthErrors = messages => ({ type: 'AUTH_ERRORS', messages })
const PictureError = message => ({ type: 'PICTURE_ERROR', message }) 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 }) const ProfileError = message => ({ type: 'PROFILE_ERROR', message })
@ -18,30 +18,13 @@ const ProfileUpdateError = message => ({
type: 'PROFILE_UPDATE_ERROR', 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 export const getProfile = () => dispatch => FitTrackeeApi
.getProfile() .getProfile()
.then(ret => { .then(ret => {
if (ret.status === 'success') { if (ret.status === 'success') {
dispatch(getData('sports')) dispatch(getData('sports'))
return dispatch(ProfileSuccess(ret)) ret.data.isAuthenticated = true
return dispatch(ProfileSuccess(ret.data))
} }
return dispatch(ProfileError(ret.message)) return dispatch(ProfileError(ret.message))
}) })
@ -51,11 +34,7 @@ export const getProfile = () => dispatch => FitTrackeeApi
export const register = formData => dispatch => FitTrackeeApi export const register = formData => dispatch => FitTrackeeApi
.register( .register(formData)
formData.username,
formData.email,
formData.password,
formData.passwordConf)
.then(ret => { .then(ret => {
if (ret.status === 'success') { if (ret.status === 'success') {
window.localStorage.setItem('authToken', ret.auth_token) window.localStorage.setItem('authToken', ret.auth_token)
@ -69,7 +48,7 @@ export const register = formData => dispatch => FitTrackeeApi
export const login = formData => dispatch => FitTrackeeApi export const login = formData => dispatch => FitTrackeeApi
.login(formData.email, formData.password) .login(formData)
.then(ret => { .then(ret => {
if (ret.status === 'success') { if (ret.status === 'success') {
window.localStorage.setItem('authToken', ret.auth_token) window.localStorage.setItem('authToken', ret.auth_token)
@ -95,7 +74,7 @@ const RegisterFormControl = formData => {
if (formData.username.length < 3 || formData.username.length > 12) { if (formData.username.length < 3 || formData.username.length > 12) {
errMsg.push('Username: 3 to 12 characters required.') 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.') errMsg.push('Password and password confirmation don\'t match.')
} }
if (formData.password.length < 8) { if (formData.password.length < 8) {
@ -104,41 +83,26 @@ const RegisterFormControl = formData => {
return errMsg return errMsg
} }
export const handleUserFormSubmit = (event, formType) => ( export const handleUserFormSubmit = (formData, formType) => dispatch => {
dispatch,
getState
) => {
event.preventDefault()
const state = getState()
const { formData } = state.formData
formData.formData = state.formData.formData
if (formType === 'Login') { if (formType === 'Login') {
return dispatch(login(formData.formData)) return dispatch(login(formData))
} }
// formType === 'Register' // formType === 'Register'
const ret = RegisterFormControl(formData.formData) const ret = RegisterFormControl(formData)
if (ret.length === 0) { if (ret.length === 0) {
return dispatch(register(formData.formData)) return dispatch(register(formData))
} }
return dispatch(AuthErrors(generateIds(ret))) return dispatch(AuthErrors(generateIds(ret)))
} }
export const initProfileForm = () => (dispatch, getState) => { export const handleProfileFormSubmit = formData => dispatch => {
const state = getState() if (!formData.password === formData.password_conf) {
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) {
return dispatch(ProfileUpdateError( return dispatch(ProfileUpdateError(
'Password and password confirmation don\'t match.' 'Password and password confirmation don\'t match.'
)) ))
} }
return FitTrackeeApi return FitTrackeeApi
.updateProfile(state.formProfile.formProfile) .updateProfile(formData)
.then(ret => { .then(ret => {
if (ret.status === 'success') { if (ret.status === 'success') {
dispatch(getProfile()) dispatch(getProfile())

View File

@ -19,7 +19,7 @@ function Admin (props) {
<title>FitTrackee - Admin</title> <title>FitTrackee - Admin</title>
</Helmet> </Helmet>
{isLoggedIn() ? ( {isLoggedIn() ? (
user.isAdmin ? ( user.admin ? (
<Switch> <Switch>
<Route exact path="/admin" component={AdminMenu} /> <Route exact path="/admin" component={AdminMenu} />
<Route exact path="/admin/sports" component={AdminSports} /> <Route exact path="/admin/sports" component={AdminSports} />

View File

@ -3,12 +3,12 @@ import React from 'react'
export default function UserStatistics (props) { export default function UserStatistics (props) {
const { user } = props const { user } = props
const days = user.totalDuration.match(/day/g) const days = user.total_duration.match(/day/g)
? `${user.totalDuration.split(',')[0]},` ? `${user.total_duration.split(',')[0]},`
: '0 days,' : '0 days,'
let duration = user.totalDuration.match(/day/g) let duration = user.total_duration.match(/day/g)
? user.totalDuration.split(', ')[1] ? user.total_duration.split(', ')[1]
: user.totalDuration : user.total_duration
duration = `${duration.split(':')[0]}h ${duration.split(':')[1]}min` duration = `${duration.split(':')[0]}h ${duration.split(':')[1]}min`
return ( return (
<div className="row"> <div className="row">
@ -20,7 +20,7 @@ export default function UserStatistics (props) {
</div> </div>
<div className="col-9 text-right"> <div className="col-9 text-right">
<div className="huge">{user.nbActivities}</div> <div className="huge">{user.nbActivities}</div>
<div>{`workout${user.nbActivities === 1 ? '' : 's'}`}</div> <div>{`workout${user.nb_activities === 1 ? '' : 's'}`}</div>
</div> </div>
</div> </div>
</div> </div>
@ -33,7 +33,7 @@ export default function UserStatistics (props) {
</div> </div>
<div className="col-9 text-right"> <div className="col-9 text-right">
<div className="huge"> <div className="huge">
{Number(user.totalDistance).toFixed(2)} {Number(user.total_distance).toFixed(2)}
</div> </div>
<div>km</div> <div>km</div>
</div> </div>
@ -60,8 +60,8 @@ export default function UserStatistics (props) {
<i className="fa fa-tags fa-3x fa-color" /> <i className="fa fa-tags fa-3x fa-color" />
</div> </div>
<div className="col-9 text-right"> <div className="col-9 text-right">
<div className="huge">{user.nbSports}</div> <div className="huge">{user.nb_sports}</div>
<div>{`sport${user.nbSports === 1 ? '' : 's'}`}</div> <div>{`sport${user.nb_sports === 1 ? '' : 's'}`}</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -58,7 +58,7 @@ function NavBar(props) {
</Link> </Link>
</li> </li>
)} )}
{props.user.isAdmin && ( {props.user.admin && (
<li className="nav-item"> <li className="nav-item">
<Link <Link
className="nav-link" className="nav-link"

View File

@ -55,11 +55,11 @@ export default function Form (props) {
<div className="form-group"> <div className="form-group">
<input <input
className="form-control input-lg" className="form-control input-lg"
name="passwordConf" name="password_conf"
placeholder="Enter the password confirmation" placeholder="Enter the password confirmation"
required required
type="password" type="password"
value={props.userForm.passwordConf} value={props.userForm.password_conf}
onChange={props.onHandleFormChange} onChange={props.onHandleFormChange}
/> />
</div> </div>

View File

@ -11,7 +11,7 @@ function Profile ({ message, onDeletePicture, onUploadPicture, user }) {
return ( return (
<div> <div>
<Helmet> <Helmet>
<title>FitTrackee - {user.username} - Profile</title> <title>FitTrackee - Profile</title>
</Helmet> </Helmet>
{ message !== '' && ( { message !== '' && (
<code>{message}</code> <code>{message}</code>
@ -36,11 +36,16 @@ function Profile ({ message, onDeletePicture, onUploadPicture, user }) {
<div className="col-md-8"> <div className="col-md-8">
<p>Email: {user.email}</p> <p>Email: {user.email}</p>
<p>Registration Date: { <p>Registration Date: {
format(new Date(user.createdAt), 'DD/MM/YYYY HH:mm') format(new Date(user.created_at), 'DD/MM/YYYY HH:mm')
}</p> }
<p>First Name: {user.firstName}</p> </p>
<p>Last Name: {user.lastName}</p> <p>First Name: {user.first_name}</p>
<p>Birth Date: {user.birthDate}</p> <p>Last Name: {user.last_name}</p>
<p>Birth Date: {user.birth_date
? format(new Date(user.birth_date), 'DD/MM/YYYY')
: ''
}
</p>
<p>Location: {user.location}</p> <p>Location: {user.location}</p>
<p>Bio: {user.bio}</p> <p>Bio: {user.bio}</p>
<p>Time zone: {user.timezone}</p> <p>Time zone: {user.timezone}</p>

View File

@ -1,198 +1,223 @@
import { format } from 'date-fns'
import React from 'react' import React from 'react'
import { Helmet } from 'react-helmet' import { Helmet } from 'react-helmet'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import TimezonePicker from 'react-timezone' import TimezonePicker from 'react-timezone'
import { import { handleProfileFormSubmit } from '../../actions/user'
initProfileForm,
updateProfileFormData,
handleProfileFormSubmit
} from '../../actions/user'
import { history } from '../../index' import { history } from '../../index'
class ProfileEdit extends React.Component { class ProfileEdit extends React.Component {
componentDidMount() { constructor(props, context) {
this.props.initForm(this.props.user) 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 () { render () {
const { formProfile, const { onHandleProfileFormSubmit, message, user } = this.props
onHandleFormChange, const { formData } = this.state
onHandleProfileFormSubmit,
message,
user
} = this.props
return ( return (
<div> <div>
<Helmet> <Helmet>
<title>FitTrackee - {user.username} - Edit Profile</title> <title>FitTrackee - Edit Profile</title>
</Helmet> </Helmet>
{ message !== '' && ( { message !== '' && (
<code>{message}</code> <code>{message}</code>
)} )}
<div className="container"> {formData.isAuthenticated && (
<h1 className="page-title">Profile Edition</h1> <div className="container">
<div className="row"> <h1 className="page-title">Profile Edition</h1>
<div className="col-md-2" /> <div className="row">
<div className="col-md-8"> <div className="col-md-2" />
<div className="card"> <div className="col-md-8">
<div className="card-header"> <div className="card">
{user.username} <div className="card-header">
</div> {user.username}
<div className="card-body"> </div>
<div className="row"> <div className="card-body">
<div className="col-md-12"> <div className="row">
<form onSubmit={event => <div className="col-md-12">
onHandleProfileFormSubmit(event)} <form onSubmit={event => {
> event.preventDefault()
<div className="form-group"> onHandleProfileFormSubmit(formData)
<label>Email: }}
<input >
name="email"
className="form-control input-lg"
type="text"
value={user.email}
readOnly
/>
</label>
</div>
<div className="form-group">
<label>
Registration Date:
<input
name="createdAt"
className="form-control input-lg"
type="text"
value={user.createdAt}
readOnly
/>
</label>
</div>
<div className="form-group">
<label>
Password:
<input
name="password"
className="form-control input-lg"
type="password"
onChange={onHandleFormChange}
/>
</label>
</div>
<div className="form-group"> <div className="form-group">
<label> <label>Email:
Password Confirmation: <input
<input name="email"
name="passwordConf"
className="form-control input-lg"
type="password"
onChange={onHandleFormChange}
/>
</label>
</div>
<hr />
<div className="form-group">
<label>
First Name:
<input
name="firstName"
className="form-control input-lg" className="form-control input-lg"
type="text" type="text"
value={formProfile.firstName} value={formData.email}
onChange={onHandleFormChange} readOnly
/> />
</label> </label>
</div> </div>
<div className="form-group"> <div className="form-group">
<label> <label>
Last Name: Registration Date:
<input <input
name="lastName" name="createdAt"
className="form-control input-lg" className="form-control input-lg"
type="text" type="text"
value={formProfile.lastName} value={formData.created_at}
onChange={onHandleFormChange} disabled
/> />
</label> </label>
</div> </div>
<div className="form-group"> <div className="form-group">
<label> <label>
Birth Date Password:
<input <input
name="birthDate" name="password"
className="form-control input-lg" className="form-control input-lg"
type="date" type="password"
value={formProfile.birthDate} onChange={e => this.handleFormChange(e)}
onChange={onHandleFormChange} />
/> </label>
</label> </div>
</div> <div className="form-group">
<div className="form-group"> <label>
<label> Password Confirmation:
Location: <input
<input name="password_conf"
name="location" className="form-control input-lg"
className="form-control input-lg" type="password"
type="text" onChange={e => this.handleFormChange(e)}
value={formProfile.location} />
onChange={onHandleFormChange} </label>
/> </div>
</label> <hr />
</div> <div className="form-group">
<div className="form-group"> <label>
<label> First Name:
Bio: <input
<textarea name="first_name"
name="bio" className="form-control input-lg"
className="form-control input-lg" type="text"
maxLength="200" value={formData.first_name}
type="text" onChange={e => this.handleFormChange(e)}
value={formProfile.bio} />
onChange={onHandleFormChange} </label>
/> </div>
</label> <div className="form-group">
</div> <label>
<div className="form-group"> Last Name:
<label> <input
Timezone: name="last_name"
<TimezonePicker className="form-control input-lg"
className="form-control" type="text"
onChange={tz => { value={formData.last_name}
const e = { target: onChange={e => this.handleFormChange(e)}
{ />
name: 'timezone', </label>
value: tz ? tz : 'Europe/Paris' </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"
type="text"
value={formData.bio}
onChange={e => this.handleFormChange(e)}
/>
</label>
</div>
<div className="form-group">
<label>
Timezone:
<TimezonePicker
className="form-control"
onChange={tz => {
const e = { target:
{
name: 'timezone',
value: tz ? tz : 'Europe/Paris'
}
} }
} this.handleFormChange(e)
onHandleFormChange(e) }}
}} value={formData.timezone}
value={formProfile.timezone />
? formProfile.timezone </label>
: 'Europe/Paris' </div>
} <input
/> type="submit"
</label> 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>
<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> </div>
</div> </div>
</div> </div>
<div className="col-md-2" />
</div> </div>
<div className="col-md-2" />
</div> </div>
</div> )}
</div> </div>
) )
} }
@ -201,19 +226,13 @@ class ProfileEdit extends React.Component {
export default connect( export default connect(
state => ({ state => ({
formProfile: state.formProfile.formProfile, location: state.router.location,
message: state.message, message: state.message,
user: state.user, user: state.user,
}), }),
dispatch => ({ dispatch => ({
initForm: () => { onHandleProfileFormSubmit: formData => {
dispatch(initProfileForm()) dispatch(handleProfileFormSubmit(formData))
},
onHandleFormChange: event => {
dispatch(updateProfileFormData(event.target.name, event.target.value))
},
onHandleProfileFormSubmit: event => {
dispatch(handleProfileFormSubmit(event))
}, },
}) })
)(ProfileEdit) )(ProfileEdit)

View File

@ -3,32 +3,48 @@ import { connect } from 'react-redux'
import { Redirect } from 'react-router-dom' import { Redirect } from 'react-router-dom'
import Form from './Form' import Form from './Form'
import { import { handleUserFormSubmit } from '../../actions/user'
emptyForm,
handleFormChange,
handleUserFormSubmit
} from '../../actions/user'
import { isLoggedIn } from '../../utils' import { isLoggedIn } from '../../utils'
class UserForm extends React.Component { class UserForm extends React.Component {
constructor(props, context) {
super(props, context)
this.state = {
formData: {
username: '',
email: '',
password: '',
password_conf: '',
}
}
}
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
if ( if (prevProps.location.pathname !== this.props.location.pathname) {
(prevProps.location.pathname !== this.props.location.pathname) this.emptyForm()
) {
this.props.onEmptyForm()
} }
} }
emptyForm() {
const { formData } = this.state
Object.keys(formData).map(k => formData[k] = '')
this.setState(formData)
}
onHandleFormChange(e) {
const { formData } = this.state
formData[e.target.name] = e.target.value
this.setState(formData)
}
render() { render() {
const { const {
formData,
formType, formType,
message, message,
messages, messages,
onHandleFormChange,
onHandleUserFormSubmit onHandleUserFormSubmit
} = this.props } = this.props
const { formData } = this.state
return ( return (
<div> <div>
{isLoggedIn() ? ( {isLoggedIn() ? (
@ -52,10 +68,11 @@ class UserForm extends React.Component {
<Form <Form
formType={formType} formType={formType}
userForm={formData} userForm={formData}
onHandleFormChange={event => onHandleFormChange(event)} onHandleFormChange={event => this.onHandleFormChange(event)}
handleUserFormSubmit={event => handleUserFormSubmit={event => {
onHandleUserFormSubmit(event, formType) event.preventDefault()
} onHandleUserFormSubmit(formData, formType)
}}
/> />
</div> </div>
)} )}
@ -65,20 +82,13 @@ class UserForm extends React.Component {
} }
export default connect( export default connect(
state => ({ state => ({
formData: state.formData.formData,
location: state.router.location, location: state.router.location,
message: state.message, message: state.message,
messages: state.messages, messages: state.messages,
}), }),
dispatch => ({ dispatch => ({
onEmptyForm: () => { onHandleUserFormSubmit: (formData, formType) => {
dispatch(emptyForm()) dispatch(handleUserFormSubmit(formData, formType))
},
onHandleFormChange: event => {
dispatch(handleFormChange(event.target.name, event.target.value))
},
onHandleUserFormSubmit: (event, formType) => {
dispatch(handleUserFormSubmit(event, formType))
}, },
}) })
)(UserForm) )(UserForm)

View File

@ -2,31 +2,23 @@ import { apiUrl, createRequest } from '../utils'
export default class FitTrackeeApi { export default class FitTrackeeApi {
static login(email, password) { static login(data) {
const params = { const params = {
url: `${apiUrl}auth/login`, url: `${apiUrl}auth/login`,
method: 'POST', method: 'POST',
noAuthorization: true, noAuthorization: true,
body: { body: data,
email: email,
password: password,
},
type: 'application/json', type: 'application/json',
} }
return createRequest(params) return createRequest(params)
} }
static register(username, email, password, passwordConf) { static register(data) {
const params = { const params = {
url: `${apiUrl}auth/register`, url: `${apiUrl}auth/register`,
method: 'POST', method: 'POST',
noAuthorization: true, noAuthorization: true,
body: { body: data,
username: username,
email: email,
password: password,
password_conf: passwordConf,
},
type: 'application/json', type: 'application/json',
} }
return createRequest(params) return createRequest(params)
@ -41,20 +33,11 @@ export default class FitTrackeeApi {
return createRequest(params) return createRequest(params)
} }
static updateProfile(form) { static updateProfile(data) {
const params = { const params = {
url: `${apiUrl}auth/profile/edit`, url: `${apiUrl}auth/profile/edit`,
method: 'POST', method: 'POST',
body: { body: data,
first_name: form.firstName,
last_name: form.lastName,
bio: form.bio,
location: form.location,
birth_date: form.birthDate,
password: form.password,
password_conf: form.passwordConf,
timezone: form.timezone,
},
type: 'application/json', type: 'application/json',
} }
return createRequest(params) return createRequest(params)

View File

@ -1,4 +1,3 @@
import { format } from 'date-fns'
import { routerReducer } from 'react-router-redux' import { routerReducer } from 'react-router-redux'
import { combineReducers } from 'redux' import { combineReducers } from 'redux'
@ -52,51 +51,6 @@ const chartData = (state = initial.chartData, action) => {
} }
} }
const formData = (state = initial.formData, action) => {
switch (action.type) {
case 'UPDATE_USER_FORMDATA':
return {
formData: {
...state.formData,
[action.target]: action.value
},
}
case 'PROFILE_SUCCESS':
case 'EMPTY_USER_FORMDATA':
return initial.formData
default:
return state
}
}
const formProfile = (state = initial.formProfile, action) => {
switch (action.type) {
case 'UPDATE_PROFILE_FORMDATA':
return {
formProfile: {
...state.formProfile,
[action.target]: action.value
},
}
case 'INIT_PROFILE_FORM':
return {
formProfile: {
...state.formProfile,
firstName: action.user.firstName,
lastName: action.user.lastName,
birthDate: action.user.birthDate,
location: action.user.location,
bio: action.user.bio,
timezone: action.user.timezone,
},
}
case 'PROFILE_SUCCESS':
return initial.formProfile
default:
return state
}
}
const gpx = (state = initial.gpx, action) => { const gpx = (state = initial.gpx, action) => {
switch (action.type) { switch (action.type) {
case 'SET_GPX': case 'SET_GPX':
@ -160,40 +114,7 @@ const user = (state = initial.user, action) => {
window.localStorage.removeItem('authToken') window.localStorage.removeItem('authToken')
return initial.user return initial.user
case 'PROFILE_SUCCESS': case 'PROFILE_SUCCESS':
return { return action.profil
id: action.message.data.id,
username: action.message.data.username,
email: action.message.data.email,
isAdmin: action.message.data.admin,
createdAt: action.message.data.created_at,
isAuthenticated: true,
firstName: action.message.data.first_name
? action.message.data.first_name
: '',
lastName: action.message.data.last_name
? action.message.data.last_name
: '',
bio: action.message.data.bio
? action.message.data.bio
: '',
location: action.message.data.location
? action.message.data.location
: '',
birthDate: action.message.data.birth_date
? format(new Date(action.message.data.birth_date),
'DD/MM/YYYY')
: '',
picture: action.message.data.picture === true
? action.message.data.picture
: false,
timezone: action.message.data.timezone
? action.message.data.timezone
: '',
nbActivities: action.message.data.nb_activities,
nbSports: action.message.data.nb_sports,
totalDistance: action.message.data.total_distance,
totalDuration: action.message.data.total_duration,
}
default: default:
return state return state
} }
@ -206,8 +127,6 @@ const reducers = combineReducers({
activities, activities,
calendarActivities, calendarActivities,
chartData, chartData,
formData,
formProfile,
gpx, gpx,
loading, loading,
message, message,

View File

@ -6,37 +6,7 @@ export default {
message: '', message: '',
messages: [], messages: [],
user: { user: {
id: '',
username: '',
email: '',
createdAt: '',
isAdmin: false,
isAuthenticated: false, isAuthenticated: false,
firstName: '',
lastName: '',
bio: '',
location: '',
birthDate: '',
picture: false
},
formData: {
formData: {
username: '',
email: '',
password: '',
passwordConf: '',
}
},
formProfile: {
formProfile: {
firstName: '',
lastName: '',
bio: '',
location: '',
birthDate: '',
password: '',
passwordConf: '',
}
}, },
activities: { activities: {
...emptyData, ...emptyData,