Ivan - Update i18n, add recover-password path logic [skip ci]

This commit is contained in:
ivan 2016-08-01 11:34:08 -03:00
parent 783f13cf59
commit 3dd27fbb3e
19 changed files with 245 additions and 53 deletions

View File

@ -1,9 +1,10 @@
import Reflux from 'reflux';
let UserActions = Reflux.createActions([
const UserActions = Reflux.createActions([
'checkLoginStatus',
'login',
'logout'
'logout',
'recoverPassword'
]);
export default UserActions;

View File

@ -3,10 +3,11 @@ import Reflux from 'reflux';
import CommonStore from 'stores/common-store';
let App = React.createClass({
const App = React.createClass({
contextTypes: {
router: React.PropTypes.object
router: React.PropTypes.object,
location: React.PropTypes.object
},
mixins: [Reflux.listenTo(CommonStore, 'onCommonStoreChanged')],
@ -21,7 +22,7 @@ let App = React.createClass({
onCommonStoreChanged(change) {
let handle = {
'i18n': () => {this.forceUpdate()},
'i18n': () => {this.context.router.push(this.context.location.pathname)},
'logged': () => {this.context.router.push('/app/dashboard')},
'loggedOut': () => {this.context.router.push('/app')}
};

View File

@ -7,6 +7,7 @@ const DemoPage = require('app/demo/components-demo-page');
const MainLayout = require('app/main/main-layout');
const MainHomePage = require('app/main/main-home/main-home-page');
const MainSignUpPage = require('app/main/main-signup/main-signup-page');
const MainRecoverPasswordPage = require('app/main/main-recover-password/main-recover-password-page');
const DashboardLayout = require('app/main/dashboard/dashboard-layout');
@ -25,6 +26,7 @@ export default (
<Route path='/app' component={MainLayout}>
<IndexRoute component={MainHomePage} />
<Route path='signup' component={MainSignUpPage}/>
<Route path='recover-password' component={MainRecoverPasswordPage}/>
<Route path='dashboard' component={DashboardLayout}>
<IndexRoute component={DashboardListTicketsPage} />
<Route path='articles' component={DashboardListArticlesPage}/>

View File

@ -1,19 +1,19 @@
const React = require('react');
const ReactDOM = require('react-dom');
const Reflux = require('reflux');
const _ = require('lodash');
const classNames = require('classnames');
import React from 'react';
import ReactDOM from 'react-dom';
import Reflux from 'reflux';
import classNames from 'classnames';
const UserActions = require('actions/user-actions');
const UserStore = require('stores/user-store');
const focus = require('lib-core/focus');
import UserActions from 'actions/user-actions';
import UserStore from 'stores/user-store';
import focus from 'lib-core/focus';
import i18n from 'lib-app/i18n';
const Button = require('core-components/button');
const Form = require('core-components/form');
const Input = require('core-components/input');
const Checkbox = require('core-components/checkbox');
const Widget = require('core-components/widget');
const WidgetTransition = require('core-components/widget-transition');
import Button from 'core-components/button';
import Form from 'core-components/form';
import Input from 'core-components/input';
import Checkbox from 'core-components/checkbox';
import Widget from 'core-components/widget';
import WidgetTransition from 'core-components/widget-transition';
let MainHomePageLoginWidget = React.createClass({
@ -49,7 +49,7 @@ let MainHomePageLoginWidget = React.createClass({
</div>
</Form>
<Button className="login-widget--forgot-password" type="link" onClick={this.handleForgotPasswordClick} onMouseDown={(event) => {event.preventDefault()}}>
{'Forgot your password?'}
{i18n('FORGOT_PASSWORD')}
</Button>
</Widget>
);
@ -57,7 +57,7 @@ let MainHomePageLoginWidget = React.createClass({
renderPasswordRecovery() {
return (
<Widget className="main-home-page--widget main-home-page--password-widget" title="Password Recovery" ref="recoverWidget">
<Widget className="main-home-page--widget main-home-page--password-widget" title={i18n('RECOVER_PASSWORD')} ref="recoverWidget">
<Form className="login-widget--form" onSubmit={this.handleForgotPasswordSubmit}>
<div className="login-widget--inputs">
<Input placeholder="email" name="email" className="login-widget--input" validation="EMAIL"/>
@ -103,7 +103,7 @@ let MainHomePageLoginWidget = React.createClass({
if (event === 'LOGIN_FAIL') {
this.setState({
loginFormErrors: {
password: 'Password does not match'
password: i18n('ERROR_PASSWORD')
}
}, function () {
this.refs.loginForm.refs.password.focus()

View File

@ -1,10 +1,10 @@
const React = require( 'react');
import React from 'react';
const MainHomePageLoginWidget = require('app/main/main-home/main-home-page-login-widget');
const MainHomePagePortal = require('app/main/main-home/main-home-page-portal');
import MainHomePageLoginWidget from 'app/main/main-home/main-home-page-login-widget';
import MainHomePagePortal from 'app/main/main-home/main-home-page-portal';
const CommonActions = require('actions/common-actions');
const UserStore = require('stores/user-store');
import CommonActions from 'actions/common-actions';
import UserStore from 'stores/user-store';
const MainHomePage = React.createClass({

View File

@ -7,14 +7,13 @@ import UserStore from 'stores/user-store';
import Button from 'core-components/button';
import DropDown from 'core-components/drop-down';
import Icon from 'core-components/icon';
let languageList = ['English', 'Spanish', 'Portuguese', 'German', 'Turkish', 'Indian'];
let codeLanguages = {
'English': 'us',
'Spanish': 'es',
'Portuguese': 'pt',
'German': 'de',
'French': 'fr',
'Chinese': 'cn',
'Turkish': 'tr',
'Indian': 'in'
};
@ -52,7 +51,7 @@ let MainLayoutHeader = React.createClass({
},
getLanguageList() {
return languageList.map((language) => {
return Object.keys(codeLanguages).map((language) => {
return {
content: language,
icon: codeLanguages[language]
@ -61,7 +60,7 @@ let MainLayoutHeader = React.createClass({
},
changeLanguage(event) {
let language = languageList[event.index];
let language = Object.keys(codeLanguages)[event.index];
CommonActions.changeLanguage(codeLanguages[language]);
},

View File

@ -0,0 +1,91 @@
import React from 'react';
import Reflux from 'reflux';
import _ from 'lodash';
import CommonActions from 'actions/common-actions';
import UserActions from 'actions/user-actions';
import UserStore from 'stores/user-store';
import i18n from 'lib-app/i18n';
import Widget from 'core-components/widget';
import Form from 'core-components/form';
import Input from 'core-components/input';
import Button from 'core-components/button';
const MainRecoverPasswordPage = React.createClass({
mixins: [Reflux.listenTo(UserStore, 'onUserStoreChanged')],
propTypes: {
location: React.PropTypes.object,
router: React.PropTypes.object
},
componentWillMount() {
if (UserStore.isLoggedIn()) {
CommonActions.logged();
}
if (!this.props.location.query.token || !this.props.location.query.email) {
CommonActions.loggedOut();
}
},
getInitialState() {
return {
recoverStatus: 'waiting'
};
},
render() {
return (
<div className="main-recover-password-page">
<Widget title={i18n('RECOVER_PASSWORD')} className="col-md-4 col-md-offset-4">
<Form className="recover-password__form" onSubmit={this.handleRecoverPasswordSubmit}>
<div className="recover-password__inputs">
<Input placeholder={i18n('NEW_PASSWORD')} name="password" className="recover-password__input" validation="PASSWORD" password required/>
<Input placeholder={i18n('REPEAT_NEW_PASSWORD')} name="password-repeat" className="recover-password__input" validation="REPEAT_PASSWORD" password required/>
</div>
<div className="recover-password__submit-button">
<Button type="primary">{i18n('SUBMIT')}</Button>
</div>
{this.renderRecoverStatus()}
</Form>
</Widget>
</div>
);
},
renderRecoverStatus() {
switch (this.state.recoverStatus) {
case 'valid':
return <div className="recover-password__text_valid">{i18n('VALID_RECOVER')}</div>;
case 'invalid':
return <div className="recover-password__text_invalid">{i18n('INVALID_RECOVER')}</div>;
case 'waiting':
return null;
}
},
handleRecoverPasswordSubmit(formState) {
let recoverData = _.clone(formState);
recoverData.token = this.props.location.query.token;
recoverData.email = this.props.location.query.email;
UserActions.recoverPassword(formState);
},
onUserStoreChanged(event) {
if (event === 'VALID_RECOVER') {
this.setState({
recoverStatus: 'valid'
});
} else {
this.setState({
recoverStatus: 'invalid'
});
}
}
});
export default MainRecoverPasswordPage;

View File

@ -0,0 +1,19 @@
.recover-password {
&__inputs {
display: inline-block;
margin: 0 auto 20px;
text-align: left;
}
&__input {
margin: 10px 0 0 0;
}
&__text_valid {
color: green;
}
&__text_invalid {
color: red;
}
}

View File

@ -63,7 +63,8 @@ const DropDown = React.createClass({
let menuProps = {
items: this.props.items,
onItemClick: this.handleItemClick,
onMouseDown: this.handleListMouseDown
onMouseDown: this.handleListMouseDown,
selectedIndex: this.state.selectedIndex
};
return (

View File

@ -46,5 +46,24 @@ module.exports = [
}
};
}
},
{
path: 'user/recover-password',
time: 100,
response: function (data) {
if (data.password.length > 6) {
return {
status: 'success',
data: {}
};
} else {
return {
status: 'fail',
message: 'Invalid token',
data: {}
};
}
}
}
];

View File

@ -1,9 +1,19 @@
const englishLanguage = require('data/languages/en');
const spanishLanguage = require('data/languages/es');
import englishLanguage from 'data/languages/en';
import spanishLanguage from 'data/languages/en';
import germanLanguage from 'data/languages/en';
import frenchLanguage from 'data/languages/en';
import chineseLanguage from 'data/languages/en';
import turkishLanguage from 'data/languages/en';
import indianLanguage from 'data/languages/en';
const languages = {
'us': englishLanguage,
'es': spanishLanguage
'es': spanishLanguage,
'de': germanLanguage,
'fr': frenchLanguage,
'cn': chineseLanguage,
'tr': turkishLanguage,
'in': indianLanguage
};
const i18nData = function (key, lang) {

View File

@ -2,6 +2,16 @@ export default {
'SUBMIT': 'Submit',
'LOG_IN': 'Log in',
'SIGN_UP': 'Sign up',
'FORGOT_PASSWORD': 'Forgot your password?',
'RECOVER_PASSWORD': 'Recover Password',
'NEW_PASSWORD': 'New password',
'REPEAT_NEW_PASSWORD': 'Repeat new password',
'VALID_RECOVER': 'Password recovered successfully',
'INVALID_RECOVER': 'Invalid recover data',
//ERRORS
'ERROR_EMPTY': 'Invalid value',
'ERROR_EMAIL': 'Invalid email'
'ERROR_PASSWORD': 'Invalid password',
'ERROR_EMAIL': 'Invalid email',
'PASSWORD_NOT_MATCH': 'Password does not match'
};

View File

@ -1,7 +0,0 @@
export default {
'SUBMIT': 'Enviar',
'LOG_IN': 'Ingresar',
'SIGN_UP': 'Registrarse',
'ERROR_EMPTY': 'Valor invalido',
'ERROR_EMAIL': 'Email invalido'
};

View File

@ -1,4 +1,4 @@
const Validator = require('lib-app/validations/validator');
import Validator from 'lib-app/validations/validator';
class EmailValidator extends Validator {

View File

@ -0,0 +1,16 @@
import Validator from 'lib-app/validations/validator';
class LengthValidator extends Validator {
constructor(length, errorKey = 'INVALID_VALUE') {
super();
this.minlength = length;
this.errorKey = errorKey;
}
validate(value, form) {
if (value.length < this.minlength) return this.getError(this.errorKey);
}
}
export default LengthValidator;

View File

@ -0,0 +1,10 @@
import Validator from 'lib-app/validations/validator';
class RepeatPasswordValidator extends Validator {
validate(value, form) {
if (value !== form.password) return this.getError('PASSWORD_NOT_MATCH');
}
}
export default RepeatPasswordValidator;

View File

@ -1,9 +1,13 @@
const Validator = require('lib-app/validations/validator');
const EmailValidator = require('lib-app/validations/email-validator');
import Validator from 'lib-app/validations/validator';
import EmailValidator from 'lib-app/validations/email-validator';
import RepeatPasswordValidator from 'lib-app/validations/repeat-password-validator';
import LengthValidator from 'lib-app/validations/length-validator';
let validators = {
'DEFAULT': new Validator(),
'EMAIL': new EmailValidator()
'EMAIL': new EmailValidator(),
'PASSWORD': new LengthValidator(6, 'ERROR_PASSWORD'),
'REPEAT_PASSWORD': new RepeatPasswordValidator()
};
class ValidatorFactory {

View File

@ -11,11 +11,14 @@ let reactDFS = function (children, visitFunction) {
let element = stack.pop();
let tempChilds = [];
if(element.props && element.props.children) {
React.Children.forEach(element.props.children, child => tempChilds.push(child));
if (element) {
if (element.props && element.props.children) {
React.Children.forEach(element.props.children, child => tempChilds.push(child));
}
visitFunction(element);
}
visitFunction(element);
stack = stack.concat(tempChilds.reverse());
}
};
@ -26,12 +29,12 @@ let renderChildrenWithProps = function(children, mapFunction) {
}
return React.Children.map(children, function (child) {
let props = mapFunction(child);
if (typeof child !== 'object' || child === null) {
return child;
}
let props = mapFunction(child);
if (!_.isEmpty(props)) {
return React.cloneElement(child, props, child.props && child.props.children);
} else {

View File

@ -13,6 +13,7 @@ const UserStore = Reflux.createStore({
this.listenTo(UserActions.checkLoginStatus, this.checkLoginStatus);
this.listenTo(UserActions.login, this.loginUser);
this.listenTo(UserActions.logout, this.logoutUser);
this.listenTo(UserActions.recoverPassword, this.recoverPassword);
},
initSession() {
@ -53,6 +54,18 @@ const UserStore = Reflux.createStore({
});
},
recoverPassword(recoverData) {
return API.call({
path: 'user/recover-password',
data: recoverData
}).then(() => {
this.trigger('VALID_RECOVER');
setTimeout(CommonActions.loggedOut, 2000);
}, () => {
this.trigger('INVALID_RECOVER')
});
},
isLoggedIn() {
return sessionStore.isLoggedIn();
},