mirror of
https://github.com/opensupports/opensupports.git
synced 2025-07-27 15:54:23 +02:00
Enhances password-recovery to show a logo if required, and adds password recovery to admin panel
This commit is contained in:
parent
949334fa71
commit
876fd8d52f
@ -2,24 +2,32 @@ import React from 'react';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import i18n from 'lib-app/i18n';
|
import i18n from 'lib-app/i18n';
|
||||||
|
import API from 'lib-app/api-call';
|
||||||
|
|
||||||
import Form from 'core-components/form';
|
import Form from 'core-components/form';
|
||||||
import FormField from 'core-components/form-field';
|
import FormField from 'core-components/form-field';
|
||||||
import Widget from 'core-components/widget';
|
import Widget from 'core-components/widget';
|
||||||
import Button from 'core-components/button';
|
import Button from 'core-components/button';
|
||||||
import SubmitButton from 'core-components/submit-button';
|
import SubmitButton from 'core-components/submit-button';
|
||||||
|
import Message from 'core-components/message';
|
||||||
|
|
||||||
class PasswordRecovery extends React.Component {
|
class PasswordRecovery extends React.Component {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
recoverSent: React.PropTypes.bool,
|
recoverSent: React.PropTypes.bool,
|
||||||
formProps: React.PropTypes.object,
|
formProps: React.PropTypes.object,
|
||||||
onBackToLoginClick: React.PropTypes.func
|
onBackToLoginClick: React.PropTypes.func,
|
||||||
|
renderLogo: React.PropTypes.bool
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
renderLogo: false
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Widget {...this.props} title={i18n('RECOVER_PASSWORD')}>
|
<Widget {...this.props} className="password-recovery__content">
|
||||||
|
{this.renderLogo()}
|
||||||
<Form {...this.props.formProps}>
|
<Form {...this.props.formProps}>
|
||||||
<div className="password-recovery__inputs">
|
<div className="password-recovery__inputs">
|
||||||
<FormField placeholder={i18n('EMAIL_LOWERCASE')} name="email" className="password-recovery__input" validation="EMAIL" required/>
|
<FormField placeholder={i18n('EMAIL_LOWERCASE')} name="email" className="password-recovery__input" validation="EMAIL" required/>
|
||||||
@ -36,6 +44,16 @@ class PasswordRecovery extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderLogo() {
|
||||||
|
let logo = null;
|
||||||
|
|
||||||
|
if (this.props.renderLogo) {
|
||||||
|
logo = (<div className="password-recovery__image"><img width="100%" src={API.getURL() + '/images/logo.png'} alt="OpenSupports Login Panel"/></div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
return logo;
|
||||||
|
}
|
||||||
|
|
||||||
renderRecoverStatus() {
|
renderRecoverStatus() {
|
||||||
let status = null;
|
let status = null;
|
||||||
|
|
||||||
|
@ -6,6 +6,11 @@
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
&__forgot-password {
|
&__forgot-password {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
@ -13,4 +18,9 @@
|
|||||||
&__message {
|
&__message {
|
||||||
margin-top: 18px;
|
margin-top: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__image {
|
||||||
|
width: 365px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import classNames from 'classnames';
|
||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
|
|
||||||
import i18n from 'lib-app/i18n';
|
import i18n from 'lib-app/i18n';
|
||||||
import API from 'lib-app/api-call';
|
import API from 'lib-app/api-call';
|
||||||
import SessionActions from 'actions/session-actions';
|
import SessionActions from 'actions/session-actions';
|
||||||
|
|
||||||
|
import PasswordRecovery from 'app-components/password-recovery.js';
|
||||||
import Button from 'core-components/button';
|
import Button from 'core-components/button';
|
||||||
import Form from 'core-components/form';
|
import Form from 'core-components/form';
|
||||||
import FormField from 'core-components/form-field';
|
import FormField from 'core-components/form-field';
|
||||||
@ -17,31 +19,45 @@ import WidgetTransition from 'core-components/widget-transition';
|
|||||||
class AdminLoginPage extends React.Component {
|
class AdminLoginPage extends React.Component {
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
sideToShow: 'front'
|
sideToShow: 'front',
|
||||||
|
loginFormErrors: {},
|
||||||
|
recoverFormErrors: {},
|
||||||
|
recoverSent: false,
|
||||||
|
loadingLogin: false,
|
||||||
|
loadingRecover: false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
if (!prevProps.session.failed && this.props.session.failed) {
|
||||||
|
this.refs.loginForm.refs.password.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<WidgetTransition sideToShow={this.state.sideToShow}>
|
<div className="admin-login-page">
|
||||||
{this.renderLogin()}
|
<WidgetTransition sideToShow={this.state.sideToShow} className={classNames('admin-login-page__container', this.props.className)}>
|
||||||
{this.renderPasswordRecovery()}
|
{this.renderLogin()}
|
||||||
</WidgetTransition>
|
{this.renderPasswordRecovery()}
|
||||||
)
|
</WidgetTransition>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLogin() {
|
renderLogin() {
|
||||||
return (
|
return (
|
||||||
<div className="admin-login-page">
|
<div>
|
||||||
<Widget className="admin-login-page__content">
|
<Widget className="admin-login-page__content">
|
||||||
<div className="admin-login-page__image"><img width="100%" src={API.getURL() + '/images/logo.png'} alt="OpenSupports Admin Panel"/></div>
|
<div className="admin-login-page__image"><img width="100%" src={API.getURL() + '/images/logo.png'} alt="OpenSupports Admin Panel"/></div>
|
||||||
<div className="admin-login-page__login-form">
|
<div className="admin-login-page__login-form">
|
||||||
<Form onSubmit={this.onSubmit.bind(this)} loading={this.props.session.pending}>
|
<Form onSubmit={this.onLoginFormSubmit.bind(this)} loading={this.props.session.pending}>
|
||||||
<FormField name="email" label={i18n('EMAIL')} field="input" validation="EMAIL" fieldProps={{size:'large'}} required />
|
<FormField name="email" label={i18n('EMAIL')} field="input" validation="EMAIL" fieldProps={{size:'large'}} required />
|
||||||
<FormField name="password" label={i18n('PASSWORD')} field="input" fieldProps={{password:true, size:'large'}} />
|
<FormField name="password" label={i18n('PASSWORD')} field="input" fieldProps={{password:true, size:'large'}} />
|
||||||
<SubmitButton>{i18n('LOG_IN')}</SubmitButton>
|
<SubmitButton>{i18n('LOG_IN')}</SubmitButton>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
{this.renderMessage()}
|
{this.renderRecoverStatus()}
|
||||||
|
{this.renderErrorStatus()}
|
||||||
<Button className="login-widget__forgot-password" type="link" onClick={this.onForgotPasswordClick.bind(this)} onMouseDown={(event) => {event.preventDefault()}}>
|
<Button className="login-widget__forgot-password" type="link" onClick={this.onForgotPasswordClick.bind(this)} onMouseDown={(event) => {event.preventDefault()}}>
|
||||||
{i18n('FORGOT_PASSWORD')}
|
{i18n('FORGOT_PASSWORD')}
|
||||||
</Button>
|
</Button>
|
||||||
@ -49,48 +65,139 @@ class AdminLoginPage extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPasswordRecovery() {
|
renderPasswordRecovery() {
|
||||||
return (
|
return (
|
||||||
<div className="admin-login-page">
|
<div>
|
||||||
<Widget className="admin-login-page__content">
|
<PasswordRecovery recoverSent={this.state.recoverSent} formProps={this.getRecoverFormProps()} onBackToLoginClick={this.onBackToLoginClick.bind(this)} renderLogo={true}/>
|
||||||
<div className="admin-login-page__image"><img width="100%" src={API.getURL() + '/images/logo.png'} alt="OpenSupports Admin Panel"/></div>
|
|
||||||
<div className="admin-login-page__login-form">
|
|
||||||
<Form onSubmit={this.onSubmit.bind(this)} loading={this.props.session.pending}>
|
|
||||||
<FormField name="email" label={i18n('EMAIL')} field="input" validation="EMAIL" fieldProps={{size:'large'}} required />
|
|
||||||
<FormField name="password" label={i18n('PASSWORD')} field="input" fieldProps={{password:true, size:'large'}} />
|
|
||||||
<SubmitButton>{i18n('LOG_IN')}</SubmitButton>
|
|
||||||
</Form>
|
|
||||||
</div>
|
|
||||||
{this.renderMessage()}
|
|
||||||
</Widget>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onForgotPasswordClick() {
|
renderRecoverStatus() {
|
||||||
this.setState({sideToShow: 'back'});
|
let status = null;
|
||||||
|
|
||||||
|
if (this.state.recoverSent) {
|
||||||
|
status = (
|
||||||
|
<Message className="admin-login-page__message" type="info" leftAligned>
|
||||||
|
{i18n('RECOVER_SENT')}
|
||||||
|
</Message>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderMessage() {
|
renderErrorStatus() {
|
||||||
let message = null;
|
let status = null;
|
||||||
|
|
||||||
if(this.props.session.failed) {
|
if (this.props.session.failed) {
|
||||||
message = (
|
status = (
|
||||||
<Message className="admin-login-page__error" type="error">
|
<Message className="admin-login-page__error" type="error">
|
||||||
{i18n('EMAIL_OR_PASSWORD')}
|
{i18n('EMAIL_OR_PASSWORD')}
|
||||||
</Message>
|
</Message>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return message;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmit(formState) {
|
getLoginFormProps() {
|
||||||
|
return {
|
||||||
|
loading: this.props.session.pending,
|
||||||
|
className: 'admin-login-page__form',
|
||||||
|
ref: 'loginForm',
|
||||||
|
onSubmit: this.onLoginFormSubmit.bind(this),
|
||||||
|
errors: this.getLoginFormErrors(),
|
||||||
|
onValidateErrors: this.onLoginFormErrorsValidation.bind(this)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getRecoverFormProps() {
|
||||||
|
return {
|
||||||
|
loading: this.state.loadingRecover,
|
||||||
|
className: 'admin-login-page__form',
|
||||||
|
ref: 'recoverForm',
|
||||||
|
onSubmit: this.onForgotPasswordSubmit.bind(this),
|
||||||
|
errors: this.state.recoverFormErrors,
|
||||||
|
onValidateErrors: this.onRecoverFormErrorsValidation.bind(this)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getLoginFormErrors() {
|
||||||
|
let errors = _.extend({}, this.state.loginFormErrors);
|
||||||
|
|
||||||
|
if (this.props.session.failed) {
|
||||||
|
if (this.props.session.failMessage === 'INVALID_CREDENTIALS') {
|
||||||
|
errors.password = i18n('ERROR_PASSWORD');
|
||||||
|
} else if (this.props.session.failMessage === 'UNVERIFIED_USER') {
|
||||||
|
errors.email = i18n('UNVERIFIED_EMAIL');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoginFormSubmit(formState) {
|
||||||
this.props.dispatch(SessionActions.login(_.extend({}, formState, {
|
this.props.dispatch(SessionActions.login(_.extend({}, formState, {
|
||||||
staff: true
|
staff: true
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onForgotPasswordSubmit(formState) {
|
||||||
|
this.setState({
|
||||||
|
loadingRecover: true,
|
||||||
|
recoverSent: false
|
||||||
|
});
|
||||||
|
|
||||||
|
API.call({
|
||||||
|
path: '/user/send-recover-password',
|
||||||
|
data: _.extend({}, formState, {staff: true})
|
||||||
|
}).then(this.onRecoverPasswordSent.bind(this)).catch(this.onRecoverPasswordFail.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoginFormErrorsValidation(errors) {
|
||||||
|
this.setState({
|
||||||
|
loginFormErrors: errors
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecoverFormErrorsValidation(errors) {
|
||||||
|
this.setState({
|
||||||
|
recoverFormErrors: errors
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onForgotPasswordClick() {
|
||||||
|
this.setState({
|
||||||
|
sideToShow: 'back'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onBackToLoginClick() {
|
||||||
|
this.setState({
|
||||||
|
sideToShow: 'front',
|
||||||
|
recoverSent: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecoverPasswordSent() {
|
||||||
|
this.setState({
|
||||||
|
loadingRecover: false,
|
||||||
|
recoverSent: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecoverPasswordFail() {
|
||||||
|
this.setState({
|
||||||
|
loadingRecover: false,
|
||||||
|
recoverFormErrors: {
|
||||||
|
email: i18n('EMAIL_NOT_EXIST')
|
||||||
|
}
|
||||||
|
}, function () {
|
||||||
|
this.refs.recoverForm.refs.email.focus();
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect((store) => {
|
export default connect((store) => {
|
||||||
|
@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
.admin-login-page {
|
.admin-login-page {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 446px;
|
||||||
|
|
||||||
|
&__container {
|
||||||
|
height: 361px;
|
||||||
|
}
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
@ -21,4 +27,4 @@
|
|||||||
&__error {
|
&__error {
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ class WidgetTransition extends React.Component {
|
|||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
if (prevProps.sideToShow != this.props.sideToShow && this.primaryWidget && this.secondaryWidget) {
|
if (prevProps.sideToShow != this.props.sideToShow && this.primaryWidget && this.secondaryWidget) {
|
||||||
console.log("The component was updated!");
|
|
||||||
this.moveFocusToCurrentSide();
|
this.moveFocusToCurrentSide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user