mirror of
https://github.com/ascribe/onion.git
synced 2024-12-22 17:33:14 +01:00
signup/login
This commit is contained in:
parent
e1dbc0e183
commit
23190f6c48
@ -7,7 +7,8 @@ import UserFetcher from '../fetchers/user_fetcher';
|
||||
class UserActions {
|
||||
constructor() {
|
||||
this.generateActions(
|
||||
'updateCurrentUser'
|
||||
'updateCurrentUser',
|
||||
'deleteCurrentUser'
|
||||
);
|
||||
}
|
||||
|
||||
@ -20,6 +21,15 @@ class UserActions {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
logoutCurrentUser() {
|
||||
UserFetcher.logout()
|
||||
.then(() => {
|
||||
this.actions.deleteCurrentUser();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default alt.createActions(UserActions);
|
||||
|
@ -85,7 +85,7 @@ let Form = React.createClass({
|
||||
},
|
||||
clearErrors(){
|
||||
for (var ref in this.refs){
|
||||
if ('clearError' in this.refs[ref]){
|
||||
if ('clearErrors' in this.refs[ref]){
|
||||
this.refs[ref].clearErrors();
|
||||
}
|
||||
}
|
||||
@ -123,10 +123,9 @@ let Form = React.createClass({
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<form role="form" onSubmit={this.submit}>
|
||||
<form role="form" className="ascribe-form" onSubmit={this.submit} autoComplete="on">
|
||||
{this.getErrors()}
|
||||
{this.renderChildren()}
|
||||
{this.getButtons()}
|
||||
</form>
|
||||
|
||||
);
|
||||
|
@ -3,6 +3,9 @@
|
||||
import React from 'react';
|
||||
import ReactAddons from 'react/addons';
|
||||
|
||||
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
|
||||
import Tooltip from 'react-bootstrap/lib/Tooltip';
|
||||
|
||||
let Property = React.createClass({
|
||||
propTypes: {
|
||||
editable: React.PropTypes.bool,
|
||||
@ -49,6 +52,11 @@ let Property = React.createClass({
|
||||
isFocused: true
|
||||
});
|
||||
},
|
||||
handleBlur() {
|
||||
this.setState({
|
||||
isFocused: false
|
||||
});
|
||||
},
|
||||
handleSuccess(){
|
||||
this.setState({
|
||||
isFocused: false,
|
||||
@ -83,22 +91,35 @@ let Property = React.createClass({
|
||||
return ReactAddons.addons.cloneWithProps(child, {
|
||||
value: this.state.value,
|
||||
onChange: this.handleChange,
|
||||
onFocus: this.handleFocus,
|
||||
onBlur: this.handleBlur,
|
||||
disabled: !this.props.editable,
|
||||
ref: 'input'
|
||||
});
|
||||
});
|
||||
},
|
||||
render() {
|
||||
|
||||
let tooltip = <span/>;
|
||||
if (this.props.tooltip){
|
||||
tooltip = (
|
||||
<Tooltip>
|
||||
{this.props.tooltip}
|
||||
</Tooltip>);
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={'ascribe-settings-wrapper ' + this.getClassName()}
|
||||
onClick={this.handleFocus}>
|
||||
<div className="ascribe-settings-property">
|
||||
{this.state.errors}
|
||||
<span>{ this.props.label}</span>
|
||||
{this.renderChildren()}
|
||||
</div>
|
||||
onClick={this.handleFocus} onfocus={this.handleFocus}>
|
||||
<OverlayTrigger
|
||||
delay={500}
|
||||
placement="top"
|
||||
overlay={tooltip}>
|
||||
<div className="ascribe-settings-property">
|
||||
{this.state.errors}
|
||||
<span>{ this.props.label}</span>
|
||||
{this.renderChildren()}
|
||||
</div>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -6,9 +6,7 @@ import Router from 'react-router';
|
||||
import UserActions from '../actions/user_actions';
|
||||
import UserStore from '../stores/user_store';
|
||||
|
||||
import apiUrls from '../constants/api_urls.js';
|
||||
//import PieceListActions from '../actions/piece_list_actions';
|
||||
import requests from '../utils/requests';
|
||||
import Alt from '../alt';
|
||||
|
||||
import Nav from 'react-bootstrap/lib/Nav';
|
||||
import Navbar from 'react-bootstrap/lib/Navbar';
|
||||
@ -16,8 +14,8 @@ import NavItem from 'react-bootstrap/lib/NavItem';
|
||||
import DropdownButton from 'react-bootstrap/lib/DropdownButton';
|
||||
import MenuItem from 'react-bootstrap/lib/MenuItem';
|
||||
import MenuItemLink from 'react-router-bootstrap/lib/MenuItemLink';
|
||||
import NavItemLink from 'react-router-bootstrap/lib/NavItemLink';
|
||||
|
||||
import LoginModal from '../components/ascribe_modal/modal_login';
|
||||
import SignupModal from '../components/ascribe_modal/modal_signup';
|
||||
|
||||
|
||||
@ -26,7 +24,7 @@ import { getLangText } from '../utils/lang_utils';
|
||||
let Link = Router.Link;
|
||||
|
||||
let Header = React.createClass({
|
||||
//mixins: [Router.Navigation],
|
||||
mixins: [Router.Navigation],
|
||||
|
||||
getInitialState() {
|
||||
return UserStore.getState();
|
||||
@ -41,17 +39,13 @@ let Header = React.createClass({
|
||||
UserStore.unlisten(this.onChange);
|
||||
},
|
||||
handleLogout(){
|
||||
requests
|
||||
.get(apiUrls.users_logout)
|
||||
.then(this.refreshData);
|
||||
UserActions.logoutCurrentUser();
|
||||
Alt.flush();
|
||||
},
|
||||
onChange(state) {
|
||||
this.setState(state);
|
||||
},
|
||||
|
||||
refreshData(){
|
||||
location.reload();
|
||||
},
|
||||
render() {
|
||||
let account = null;
|
||||
let signup = null;
|
||||
@ -63,15 +57,12 @@ let Header = React.createClass({
|
||||
<MenuItem eventKey="2" href="/art/faq/">{getLangText('FAQ')}</MenuItem>
|
||||
<MenuItem eventKey="3" href="/art/terms/">{getLangText('Terms of Service')}</MenuItem>
|
||||
<MenuItem divider />
|
||||
<MenuItem eventKey="4" href="#" onClick={this.handleLogout}>{getLangText('Log out')}</MenuItem>
|
||||
<MenuItem eventKey="4" onClick={this.handleLogout}>{getLangText('Log out')}</MenuItem>
|
||||
</DropdownButton>
|
||||
);
|
||||
}
|
||||
else {
|
||||
account = (
|
||||
<LoginModal
|
||||
button={<NavItem to="pieces">LOGIN</NavItem>}
|
||||
handleSuccess={this.refreshData}/>);
|
||||
account = <NavItemLink to="login">LOGIN</NavItemLink>;
|
||||
signup = (
|
||||
<SignupModal
|
||||
button={<NavItem to="pieces">SIGNUP</NavItem>} />);
|
||||
|
83
js/components/login_container.js
Normal file
83
js/components/login_container.js
Normal file
@ -0,0 +1,83 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import Router from 'react-router';
|
||||
|
||||
import UserActions from '../actions/user_actions';
|
||||
import UserStore from '../stores/user_store';
|
||||
|
||||
import GlobalNotificationModel from '../models/global_notification_model';
|
||||
import GlobalNotificationActions from '../actions/global_notification_actions';
|
||||
|
||||
import Form from './ascribe_forms/form';
|
||||
import Property from './ascribe_forms/property';
|
||||
|
||||
import apiUrls from '../constants/api_urls';
|
||||
|
||||
|
||||
let LoginContainer = React.createClass({
|
||||
mixins: [Router.Navigation],
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="ascribe-login-wrapper">
|
||||
<br/>
|
||||
<div className="ascribe-login-text ascribe-login-header">
|
||||
Log in to ascribe...
|
||||
</div>
|
||||
|
||||
<LoginForm />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
let LoginForm = React.createClass({
|
||||
mixins: [Router.Navigation],
|
||||
|
||||
|
||||
handleSuccess(){
|
||||
let notification = new GlobalNotificationModel('Login successsful', 'success', 10000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
this.transitionTo('pieces');
|
||||
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<Form
|
||||
url={apiUrls.users_login}
|
||||
handleSuccess={this.handleSuccess}>
|
||||
<Property
|
||||
name='email'
|
||||
label="Email">
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Enter your email"
|
||||
autoComplete="on"
|
||||
required/>
|
||||
</Property>
|
||||
<Property
|
||||
name='password'
|
||||
label="Password">
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Enter your password"
|
||||
autoComplete="on"
|
||||
required/>
|
||||
</Property>
|
||||
<hr />
|
||||
<div className="ascribe-login-text">
|
||||
Not an ascribe user? Sign up...<br/>
|
||||
Forgot my password? Rescue me...
|
||||
</div>
|
||||
<button type="submit" className="btn ascribe-btn ascribe-btn-login">
|
||||
Log in to ascribe
|
||||
</button>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
export default LoginContainer;
|
@ -9,7 +9,7 @@ import ReactS3FineUploader from 'ReactS3FineUploader';
|
||||
|
||||
let RegisterPiece = React.createClass( {
|
||||
render() {
|
||||
console.log(AppConstants.serverUrl)
|
||||
|
||||
return (
|
||||
|
||||
<div>
|
||||
|
114
js/components/signup_container.js
Normal file
114
js/components/signup_container.js
Normal file
@ -0,0 +1,114 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import Router from 'react-router';
|
||||
|
||||
|
||||
import GlobalNotificationModel from '../models/global_notification_model';
|
||||
import GlobalNotificationActions from '../actions/global_notification_actions';
|
||||
|
||||
import Form from './ascribe_forms/form';
|
||||
import Property from './ascribe_forms/property';
|
||||
import InputCheckbox from './ascribe_forms/input_checkbox';
|
||||
|
||||
import apiUrls from '../constants/api_urls';
|
||||
|
||||
|
||||
let LoginContainer = React.createClass({
|
||||
mixins: [Router.Navigation],
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="ascribe-login-wrapper">
|
||||
<br/>
|
||||
<div className="ascribe-login-text ascribe-login-header">
|
||||
Welcome to ascribe...
|
||||
</div>
|
||||
<LoginForm />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
let LoginForm = React.createClass({
|
||||
mixins: [Router.Navigation],
|
||||
|
||||
|
||||
handleSuccess(){
|
||||
let notification = new GlobalNotificationModel('Login successsful', 'success', 10000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
this.transitionTo('pieces');
|
||||
|
||||
},
|
||||
render() {
|
||||
let tooltipPassword = 'Your password must be at least 10 characters.\n ' +
|
||||
'This password is securing your digital property like a bank account.\n ' +
|
||||
'Store it in a safe place!';
|
||||
return (
|
||||
<Form
|
||||
url={apiUrls.users_login}
|
||||
handleSuccess={this.handleSuccess}>
|
||||
<Property
|
||||
name='email'
|
||||
label="Email">
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Enter your email"
|
||||
autoComplete="on"
|
||||
required/>
|
||||
</Property>
|
||||
<Property
|
||||
name='username'
|
||||
label="Username">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Choose a username"
|
||||
autoComplete="on"
|
||||
required/>
|
||||
</Property>
|
||||
<Property
|
||||
name='password'
|
||||
label="Password"
|
||||
tooltip={tooltipPassword}>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Enter your password"
|
||||
autoComplete="on"
|
||||
required/>
|
||||
</Property>
|
||||
<Property
|
||||
name='password_confirm'
|
||||
label="Confirm Password"
|
||||
tooltip={tooltipPassword}>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Enter your password once again"
|
||||
autoComplete="on"
|
||||
required/>
|
||||
</Property>
|
||||
<Property
|
||||
name='promo_code'
|
||||
label="Promocode (Optional)">
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Enter a promocode here"/>
|
||||
</Property>
|
||||
<InputCheckbox
|
||||
ref="terms"
|
||||
required="required"
|
||||
label={
|
||||
<div>
|
||||
I agree to the
|
||||
<a href="/terms" target="_blank"> Terms of Service</a>
|
||||
</div>}/>
|
||||
<hr />
|
||||
<button type="submit" className="btn ascribe-btn ascribe-btn-login">
|
||||
Sign up to ascribe
|
||||
</button>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default LoginContainer;
|
@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
import requests from '../utils/requests';
|
||||
|
||||
import apiUrls from '../constants/api_urls';
|
||||
|
||||
let UserFetcher = {
|
||||
/**
|
||||
@ -10,6 +10,10 @@ let UserFetcher = {
|
||||
*/
|
||||
fetchOne() {
|
||||
return requests.get('user');
|
||||
},
|
||||
|
||||
logout() {
|
||||
return requests.get(apiUrls.users_logout);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -6,11 +6,14 @@ import Router from 'react-router';
|
||||
import AscribeApp from './components/ascribe_app';
|
||||
import PieceList from './components/piece_list';
|
||||
import EditionContainer from './components/edition_container';
|
||||
|
||||
import LoginContainer from './components/login_container';
|
||||
import SignupContainer from './components/signup_container';
|
||||
import PasswordResetContainer from './components/password_reset_container';
|
||||
|
||||
import SettingsContainer from './components/settings_container';
|
||||
import AppConstants from './constants/application_constants';
|
||||
import RegisterPiece from './components/register_piece';
|
||||
//import LoginModalHandler from './components/login_modal_handler';
|
||||
|
||||
let Route = Router.Route;
|
||||
let Redirect = Router.Redirect;
|
||||
@ -19,6 +22,8 @@ let baseUrl = AppConstants.baseUrl;
|
||||
|
||||
let routes = (
|
||||
<Route name="app" path={baseUrl} handler={AscribeApp}>
|
||||
<Route name="signup" path="signup" handler={SignupContainer} />
|
||||
<Route name="login" path="login" handler={LoginContainer} />
|
||||
<Route name="pieces" path="collection" handler={PieceList} />
|
||||
<Route name="edition" path="editions/:editionId" handler={EditionContainer} />
|
||||
<Route name="password_reset" path="password_reset" handler={PasswordResetContainer} />
|
||||
|
@ -13,6 +13,9 @@ class UserStore {
|
||||
onUpdateCurrentUser(user) {
|
||||
this.currentUser = user;
|
||||
}
|
||||
onDeleteCurrentUser() {
|
||||
this.currentUser = {};
|
||||
}
|
||||
}
|
||||
|
||||
export default alt.createStore(UserStore, 'UserStore');
|
||||
|
@ -89,6 +89,9 @@ class Requests {
|
||||
}
|
||||
|
||||
get(url, params) {
|
||||
if (url === undefined){
|
||||
throw new Error('Url undefined');
|
||||
}
|
||||
let paramsCopy = this._merge(params);
|
||||
let newUrl = this.prepareUrl(url, paramsCopy, true);
|
||||
return this.request('get', newUrl);
|
||||
|
63
sass/ascribe_login.scss
Normal file
63
sass/ascribe_login.scss
Normal file
@ -0,0 +1,63 @@
|
||||
$break-small: 764px;
|
||||
|
||||
.ascribe-btn-login {
|
||||
padding: 1.5em;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
background-color: rgba(2, 182, 163, 1);
|
||||
color: white;
|
||||
font-size: 1.2em;
|
||||
border-radius: 0;
|
||||
width: 100%;
|
||||
border:none;
|
||||
|
||||
|
||||
&:hover {
|
||||
color: white;
|
||||
background-color: rgba(2, 182, 163, 0.8);
|
||||
border: none;
|
||||
}
|
||||
&:active, &:focus {
|
||||
color: white;
|
||||
background-color: rgba(2, 182, 163, 0.6);
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ascribe-login-wrapper {
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
max-width: 600px;
|
||||
@media screen and (max-width: $break-small) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.ascribe-login-text {
|
||||
font-size: 1em;
|
||||
padding: 0 0 1em 0;
|
||||
margin-left: 0.4em;
|
||||
}
|
||||
|
||||
.ascribe-login-header {
|
||||
font-size: 2em;
|
||||
padding: 0 0 0.5em 0;
|
||||
}
|
||||
|
||||
.ascribe-form {
|
||||
hr {
|
||||
color: rgba(0, 0, 0, 0.2);
|
||||
border: none;
|
||||
height: 1px;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
%vertical-align {
|
||||
position: relative;
|
||||
top: 50%;
|
||||
-webkit-transform: translateY(-50%);
|
||||
-ms-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
|
||||
border-left: 3px solid rgba(0,0,0,0);
|
||||
|
||||
&:last-child {
|
||||
&div:last-of-type {
|
||||
border-bottom: 1px solid rgba(0,0,0,.2);
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ $BASE_URL: '<%= BASE_URL %>';
|
||||
@import '../node_modules/react-datepicker/dist/react-datepicker';
|
||||
@import './ascribe-fonts/style';
|
||||
@import './ascribe-fonts/ascribe-fonts';
|
||||
@import 'ascribe_login';
|
||||
@import 'ascribe_table';
|
||||
@import 'ascribe_accordion_list';
|
||||
@import 'ascribe_piece_list_bulk_modal';
|
||||
@ -35,6 +36,7 @@ body {
|
||||
border-right:0;
|
||||
margin-bottom: 1.5em;
|
||||
border-top:0;
|
||||
border-color: #CCC
|
||||
}
|
||||
|
||||
.navbar-right {
|
||||
@ -50,6 +52,17 @@ body {
|
||||
color: $ascribe-color;
|
||||
}
|
||||
|
||||
.tooltip-inner{
|
||||
max-width: 300px;
|
||||
padding: 3px 8px;
|
||||
color: #000;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
background-color: #FCFAFA;
|
||||
border-radius: 0;
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
/* Taken from http://stackoverflow.com/a/20548578 */
|
||||
.vcenter {
|
||||
display: inline-block;
|
||||
|
Loading…
Reference in New Issue
Block a user