New design for ResetPassword view and NewPassword view (#232)

* newdesign for resetpassword view and newpassword view

* bought password token back

* added params.reset_password_token

* no themify
This commit is contained in:
Yining Shi 2016-12-19 16:49:37 -05:00 committed by Cassie Tarakajian
parent acad9538cc
commit 0f17633f79
17 changed files with 246 additions and 228 deletions

View file

@ -1,74 +0,0 @@
import React, { PropTypes } from 'react';
import { reduxForm } from 'redux-form';
import NewPasswordForm from './NewPasswordForm';
import * as UserActions from '../../User/actions';
import { bindActionCreators } from 'redux';
import classNames from 'classnames';
import { Link } from 'react-router';
class NewPasswordView extends React.Component {
componentDidMount() {
this.refs.newPassword.focus();
// need to check if this is a valid token
this.props.validateResetPasswordToken(this.props.token);
}
render() {
const newPasswordClass = classNames({
'new-password': true,
'new-password--invalid': this.props.user.resetPasswordInvalid
});
return (
<div className={newPasswordClass} ref="newPassword" tabIndex="0">
<h1>Set a New Password</h1>
<NewPasswordForm {...this.props} />
<p className="new-password__invalid">
The password reset token is invalid or has expired.
</p>
<Link className="form__cancel-button" to="/">Close</Link>
</div>
);
}
}
NewPasswordView.propTypes = {
token: PropTypes.string.isRequired,
validateResetPasswordToken: PropTypes.func.isRequired,
user: PropTypes.shape({
resetPasswordInvalid: PropTypes.bool
})
};
function validate(formProps) {
const errors = {};
if (!formProps.password) {
errors.password = 'Please enter a password';
}
if (!formProps.confirmPassword) {
errors.confirmPassword = 'Please enter a password confirmation';
}
if (formProps.password !== formProps.confirmPassword) {
errors.password = 'Passwords must match';
}
return errors;
}
function mapStateToProps(state, ownProps) {
return {
user: state.user,
token: ownProps.token
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(UserActions, dispatch);
}
export default reduxForm({
form: 'new-password',
fields: ['password', 'confirmPassword'],
validate
}, mapStateToProps, mapDispatchToProps)(NewPasswordView);

View file

@ -1,71 +0,0 @@
import React, { PropTypes } from 'react';
import { Link } from 'react-router';
import * as UserActions from '../../User/actions';
import { bindActionCreators } from 'redux';
import { reduxForm } from 'redux-form';
import ResetPasswordForm from './ResetPasswordForm';
import classNames from 'classnames';
class ResetPasswordView extends React.Component {
componentWillMount() {
this.props.resetPasswordReset();
}
componentDidMount() {
this.refs.resetPassword.focus();
}
render() {
const resetPasswordClass = classNames({
'reset-password': true,
'reset-password--submitted': this.props.user.resetPasswordInitiate
});
return (
<div className={resetPasswordClass} ref="resetPassword" tabIndex="0">
<h1>Reset Your Password</h1>
<ResetPasswordForm {...this.props} />
<p className="reset-password__submitted">
Your password reset email should arrive shortly. If you don't see it, check
in your spam folder as sometimes it can end up there.
</p>
<p className="form__navigation-options">
<Link className="form__login-button" to="/login">Login</Link>
&nbsp;or&nbsp;
<Link className="form__signup-button" to="/signup">Sign up</Link>
</p>
<Link className="form__cancel-button" to="/">Cancel</Link>
</div>
);
}
}
ResetPasswordView.propTypes = {
resetPasswordReset: PropTypes.func.isRequired,
user: PropTypes.shape({
resetPasswordInitiate: PropTypes.bool
}).isRequired,
};
function mapStateToProps(state) {
return {
user: state.user
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(UserActions, dispatch);
}
function validate(formProps) {
const errors = {};
if (!formProps.email) {
errors.email = 'Please enter an email';
}
return errors;
}
export default reduxForm({
form: 'reset-password',
fields: ['email'],
validate
}, mapStateToProps, mapDispatchToProps)(ResetPasswordView);

View file

@ -28,8 +28,6 @@ import SplitPane from 'react-split-pane';
import Overlay from '../../App/components/Overlay';
import SketchList from '../components/SketchList';
import About from '../components/About';
import ResetPasswordView from '../components/ResetPasswordView';
import NewPasswordView from '../components/NewPasswordView';
class IDEView extends React.Component {
constructor(props) {
@ -432,26 +430,6 @@ class IDEView extends React.Component {
);
}
})()}
{(() => { // eslint-disable-line
if (this.props.location.pathname === '/reset-password') {
return (
<Overlay>
<ResetPasswordView />
</Overlay>
);
}
})()}
{(() => { // eslint-disable-line
if (this.props.location.pathname.match(/\/reset-password\/[a-fA-F0-9]+/)) {
return (
<Overlay>
<NewPasswordView
token={this.props.params.reset_password_token}
/>
</Overlay>
);
}
})()}
{(() => { // eslint-disable-line
if (this.props.ide.forceAuthenticationVisible) {
return (

View file

@ -4,7 +4,7 @@ import { domOnlyProps } from '../../../utils/reduxFormUtils';
function LoginForm(props) {
const { fields: { email, password }, handleSubmit, submitting, pristine } = props;
return (
<form className="login-form" onSubmit={handleSubmit(props.validateAndLoginUser.bind(this, props.previousPath))}>
<form className="form" onSubmit={handleSubmit(props.validateAndLoginUser.bind(this, props.previousPath))}>
<p className="form__field">
<label htmlFor="email" className="form__label">Email</label>
<input

View file

@ -4,23 +4,25 @@ import { domOnlyProps } from '../../../utils/reduxFormUtils';
function NewPasswordForm(props) {
const { fields: { password, confirmPassword }, handleSubmit, submitting, invalid, pristine } = props;
return (
<form className="new-password-form" onSubmit={handleSubmit(props.updatePassword.bind(this, props.token))}>
<p className="new-password-form__field">
<form className="form" onSubmit={handleSubmit(props.updatePassword.bind(this, props.params.reset_password_token))}>
<p className="form__field">
<label htmlFor="password" className="form__label">Password</label>
<input
className="new-password-form__password-input"
className="form__input"
aria-label="password"
type="password"
placeholder="Password"
id="Password"
{...domOnlyProps(password)}
/>
{password.touched && password.error && <span className="form-error">{password.error}</span>}
</p>
<p className="new-password-form__field">
<p className="form__field">
<label htmlFor="confirm password" className="form__label">Confirm Password</label>
<input
className="new-password-form__confirm-password-input"
className="form__input"
type="password"
placeholder="Confirm Password"
aria-label="confirm password"
id="confirm password"
{...domOnlyProps(confirmPassword)}
/>
{confirmPassword.touched && confirmPassword.error && <span className="form-error">{confirmPassword.error}</span>}
@ -40,7 +42,9 @@ NewPasswordForm.propTypes = {
submitting: PropTypes.bool,
invalid: PropTypes.bool,
pristine: PropTypes.bool,
token: PropTypes.string.isRequired
params: PropTypes.shape({
reset_password_token: PropTypes.string,
}),
};
export default NewPasswordForm;

View file

@ -4,17 +4,18 @@ import { domOnlyProps } from '../../../utils/reduxFormUtils';
function ResetPasswordForm(props) {
const { fields: { email }, handleSubmit, submitting, invalid, pristine } = props;
return (
<form className="reset-password-form" onSubmit={handleSubmit(props.initiateResetPassword.bind(this))}>
<p className="reset-password-form__field">
<form className="form" onSubmit={handleSubmit(props.initiateResetPassword.bind(this))}>
<p className="form__field">
<label htmlFor="email" className="form__label">Email used for registration</label>
<input
className="reset-password-form__email-input"
className="form__input"
aria-label="email"
type="text"
placeholder="Email used for registration"
id="email"
{...domOnlyProps(email)}
/>
</p>
<input type="submit" disabled={submitting || invalid || pristine || props.user.resetPasswordInitiate} value="Send password reset email" aria-label="Send email to reset password" />
<input type="submit" disabled={submitting || invalid || pristine || props.user.resetPasswordInitiate} value="Send Password Reset Email" aria-label="Send email to reset password" />
</form>
);
}

View file

@ -4,7 +4,7 @@ import { domOnlyProps } from '../../../utils/reduxFormUtils';
function SignupForm(props) {
const { fields: { username, email, password, confirmPassword }, handleSubmit, submitting, invalid, pristine } = props;
return (
<form className="signup-form" onSubmit={handleSubmit(props.signUpUser.bind(this, props.previousPath))}>
<form className="form" onSubmit={handleSubmit(props.signUpUser.bind(this, props.previousPath))}>
<p className="form__field">
<label htmlFor="username" className="form__label">User Name</label>
<input

View file

@ -0,0 +1,96 @@
import React, { PropTypes } from 'react';
import { reduxForm } from 'redux-form';
import NewPasswordForm from '../components/NewPasswordForm';
import * as UserActions from '../actions';
import { bindActionCreators } from 'redux';
import classNames from 'classnames';
import { browserHistory } from 'react-router';
import InlineSVG from 'react-inlinesvg';
const exitUrl = require('../../../images/exit.svg');
const logoUrl = require('../../../images/p5js-logo.svg');
class NewPasswordView extends React.Component {
constructor(props) {
super(props);
this.gotoHomePage = this.gotoHomePage.bind(this);
}
componentDidMount() {
// need to check if this is a valid token
this.props.validateResetPasswordToken(this.props.params.reset_password_token);
}
gotoHomePage() {
browserHistory.push('/');
}
render() {
const newPasswordClass = classNames({
'new-password': true,
'new-password--invalid': this.props.user.resetPasswordInvalid,
'form-container': true
});
return (
<div className={newPasswordClass}>
<div className="form-container__header">
<button className="form-container__logo-button" onClick={this.gotoHomePage}>
<InlineSVG src={logoUrl} alt="p5js Logo" />
</button>
<button className="form-container__exit-button" onClick={this.gotoHomePage}>
<InlineSVG src={exitUrl} alt="Close NewPassword Page" />
</button>
</div>
<div className="form-container__content">
<h2 className="form-container__title">Set a New Password</h2>
<NewPasswordForm {...this.props} />
<p className="new-password__invalid">
The password reset token is invalid or has expired.
</p>
</div>
</div>
);
}
}
NewPasswordView.propTypes = {
params: PropTypes.shape({
reset_password_token: PropTypes.string,
}),
validateResetPasswordToken: PropTypes.func.isRequired,
user: PropTypes.shape({
resetPasswordInvalid: PropTypes.bool
})
};
function validate(formProps) {
const errors = {};
if (!formProps.password) {
errors.password = 'Please enter a password';
}
if (!formProps.confirmPassword) {
errors.confirmPassword = 'Please enter a password confirmation';
}
if (formProps.password !== formProps.confirmPassword) {
errors.password = 'Passwords must match';
}
return errors;
}
function mapStateToProps(state) {
return {
user: state.user
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(UserActions, dispatch);
}
export default reduxForm({
form: 'new-password',
fields: ['password', 'confirmPassword'],
validate
}, mapStateToProps, mapDispatchToProps)(NewPasswordView);

View file

@ -0,0 +1,89 @@
import React, { PropTypes } from 'react';
import { Link, browserHistory } from 'react-router';
import * as UserActions from '../actions';
import { bindActionCreators } from 'redux';
import { reduxForm } from 'redux-form';
import ResetPasswordForm from '../components/ResetPasswordForm';
import classNames from 'classnames';
import InlineSVG from 'react-inlinesvg';
const exitUrl = require('../../../images/exit.svg');
const logoUrl = require('../../../images/p5js-logo.svg');
class ResetPasswordView extends React.Component {
constructor(props) {
super(props);
this.gotoHomePage = this.gotoHomePage.bind(this);
}
componentWillMount() {
this.props.resetPasswordReset();
}
gotoHomePage() {
browserHistory.push('/');
}
render() {
const resetPasswordClass = classNames({
'reset-password': true,
'reset-password--submitted': this.props.user.resetPasswordInitiate,
'form-container': true
});
return (
<div className={resetPasswordClass}>
<div className="form-container__header">
<button className="form-container__logo-button" onClick={this.gotoHomePage}>
<InlineSVG src={logoUrl} alt="p5js Logo" />
</button>
<button className="form-container__exit-button" onClick={this.gotoHomePage}>
<InlineSVG src={exitUrl} alt="Close ResetPassword Page" />
</button>
</div>
<div className="form-container__content">
<h2 className="form-container__title">Reset Your Password</h2>
<ResetPasswordForm {...this.props} />
<p className="reset-password__submitted">
Your password reset email should arrive shortly. If you don't see it, check
in your spam folder as sometimes it can end up there.
</p>
<p className="form__navigation-options">
<Link className="form__login-button" to="/login">Log In</Link>
&nbsp;or&nbsp;
<Link className="form__signup-button" to="/signup">Sign Up</Link>
</p>
</div>
</div>
);
}
}
ResetPasswordView.propTypes = {
resetPasswordReset: PropTypes.func.isRequired,
user: PropTypes.shape({
resetPasswordInitiate: PropTypes.bool
}).isRequired,
};
function mapStateToProps(state) {
return {
user: state.user
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(UserActions, dispatch);
}
function validate(formProps) {
const errors = {};
if (!formProps.email) {
errors.email = 'Please enter an email';
}
return errors;
}
export default reduxForm({
form: 'reset-password',
fields: ['email'],
validate
}, mapStateToProps, mapDispatchToProps)(ResetPasswordView);

View file

@ -5,6 +5,8 @@ import IDEView from './modules/IDE/pages/IDEView';
import FullView from './modules/IDE/pages/FullView';
import LoginView from './modules/User/pages/LoginView';
import SignupView from './modules/User/pages/SignupView';
import ResetPasswordView from './modules/User/pages/ResetPasswordView';
import NewPasswordView from './modules/User/pages/NewPasswordView';
// import SketchListView from './modules/Sketch/pages/SketchListView';
import { getUser } from './modules/User/actions';
@ -18,8 +20,8 @@ const routes = (store) =>
<IndexRoute component={IDEView} onEnter={checkAuth(store)} />
<Route path="/login" component={LoginView} />
<Route path="/signup" component={SignupView} />
<Route path="/reset-password" component={IDEView} />
<Route path="/reset-password/:reset_password_token" component={IDEView} />
<Route path="/reset-password" component={ResetPasswordView} />
<Route path="/reset-password/:reset_password_token" component={NewPasswordView} />
<Route path="/projects/:project_id" component={IDEView} />
<Route path="/full/:project_id" component={FullView} />
<Route path="/sketches" component={IDEView} />

View file

@ -58,6 +58,24 @@
padding: 0;
}
%none-themify-icon-with-hover {
color: $icon-color;
& g {
fill: $icon-color;
}
&:hover {
color: $icon-hover-color;
& g {
opacity: 1;
fill: $icon-hover-color;
}
}
background-color: transparent;
border: none;
cursor: pointer;
padding: 0;
}
%button {
@include themify() {
background-color: getThemifyVariable('button-background-color');

View file

@ -120,3 +120,6 @@ $form-button-background-active-color: #f10046;
$form-button-hover-color: $white;
$form-button-active-color: $white;
$form-navigation-options-color: #999999;
$icon-color: #8b8b8b;
$icon-hover-color: #333;

View file

@ -47,7 +47,7 @@ input {
input[type="submit"] {
@include themify() {
@extend %forms-button;
@extend %button;
}
}

View file

@ -34,7 +34,5 @@
}
.form-container__exit-button {
@include themify() {
@extend %icon;
}
@extend %none-themify-icon-with-hover;
}

View file

@ -23,10 +23,16 @@
font-size: #{12 / $base-font-size}rem;
margin-top: #{25 / $base-font-size}rem;
margin-bottom: #{7 / $base-font-size}rem;
display:block;
display: block;
}
.form__input {
width: #{360 / $base-font-size}rem;
height: #{40 / $base-font-size}rem;
color: $icon-hover-color;
border-color: $secondary-form-title-color;
}
.form input[type="submit"] {
@extend %forms-button;
}

View file

@ -1,22 +1,3 @@
.new-password {
@extend %modal;
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
padding: #{20 / $base-font-size}rem;
align-items: center;
}
.new-password-form__password-input,
.new-password-form__confirm-password-input {
width: #{300 / $base-font-size}rem;
}
.new-password-form__field {
margin: #{20 / $base-font-size}rem 0;
}
.new-password-form {
.new-password--invalid & {
display: none;
@ -27,5 +8,8 @@
display: none;
.new-password--invalid & {
display: block;
margin-top: #{40 / $base-font-size}rem;
margin-bottom: #{80 / $base-font-size}rem;
color: $form-navigation-options-color;
}
}

View file

@ -1,29 +1,13 @@
.reset-password {
@extend %modal;
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
padding: #{20 / $base-font-size}rem;
align-items: center;
}
.reset-password-form__email-input {
width: #{300 / $base-font-size}rem;
}
.reset-password-form__field {
margin: #{20 / $base-font-size}rem 0;
}
.reset-password__submitted {
width: #{300 / $base-font-size}rem;
width: #{360 / $base-font-size}rem;
display: none;
text-align: left;
font-size: #{12 / $base-font-size}rem;
margin-top: #{10 / $base-font-size}rem;
color: $form-navigation-options-color;
padding-right: #{30 / $base-font-size}rem;
padding-left: #{39 / $base-font-size}rem;
.reset-password--submitted & {
display: block;
}
}