mirror of
https://github.com/ascribe/onion.git
synced 2025-01-03 10:25:08 +01:00
Merge branch 'AD-416-account-settings-page-navbar' of bitbucket.org:ascribe/onion into AD-416-account-settings-page-navbar
This commit is contained in:
commit
cb3349c416
@ -49,7 +49,7 @@ var config = {
|
|||||||
filesToWatch: [
|
filesToWatch: [
|
||||||
'build/css/*.css',
|
'build/css/*.css',
|
||||||
'build/js/*.js',
|
'build/js/*.js',
|
||||||
'node_modules/react-s3-fineuploader/*.js'
|
'node_modules/react-s3-fine_uploader/*.js'
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ gulp.task('js:build', function() {
|
|||||||
bundle(false);
|
bundle(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('serve', ['browser-sync', 'run-server', 'sass:build', 'sass:watch', 'copy'], function() {
|
gulp.task('serve', ['browser-sync', 'run-server', 'sass:watch', 'copy'], function() {
|
||||||
bundle(true);
|
bundle(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
<script>
|
<script>
|
||||||
window.BASE_URL = '<%= BASE_URL %>';
|
window.BASE_URL = '<%= BASE_URL %>';
|
||||||
window.API_ENDPOINT = '<%= API_ENDPOINT %>';
|
window.API_ENDPOINT = '<%= API_ENDPOINT %>';
|
||||||
|
window.SERVER_URL = '<%= SERVER_URL %>';
|
||||||
<% DEBUG && print('window.DEBUG = true'); %>
|
<% DEBUG && print('window.DEBUG = true'); %>
|
||||||
<% DEBUG && print('window.CREDENTIALS = \'' + CREDENTIALS + '\''); %>
|
<% DEBUG && print('window.CREDENTIALS = \'' + CREDENTIALS + '\''); %>
|
||||||
</script>
|
</script>
|
||||||
|
@ -7,7 +7,8 @@ import UserFetcher from '../fetchers/user_fetcher';
|
|||||||
class UserActions {
|
class UserActions {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.generateActions(
|
this.generateActions(
|
||||||
'updateCurrentUser'
|
'updateCurrentUser',
|
||||||
|
'deleteCurrentUser'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,6 +21,15 @@ class UserActions {
|
|||||||
console.log(err);
|
console.log(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
logoutCurrentUser() {
|
||||||
|
UserFetcher.logout()
|
||||||
|
.then(() => {
|
||||||
|
this.actions.deleteCurrentUser();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default alt.createActions(UserActions);
|
export default alt.createActions(UserActions);
|
||||||
|
@ -85,14 +85,21 @@ let Form = React.createClass({
|
|||||||
},
|
},
|
||||||
clearErrors(){
|
clearErrors(){
|
||||||
for (var ref in this.refs){
|
for (var ref in this.refs){
|
||||||
if ('clearError' in this.refs[ref]){
|
if ('clearErrors' in this.refs[ref]){
|
||||||
this.refs[ref].clearErrors();
|
this.refs[ref].clearErrors();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setState({errors: []});
|
this.setState({errors: []});
|
||||||
},
|
},
|
||||||
getButtons() {
|
getButtons() {
|
||||||
|
if (this.state.submitted){
|
||||||
|
return this.props.spinner;
|
||||||
|
}
|
||||||
|
if (this.props.buttons){
|
||||||
|
return this.props.buttons;
|
||||||
|
}
|
||||||
let buttons = null;
|
let buttons = null;
|
||||||
|
|
||||||
if (this.state.edited){
|
if (this.state.edited){
|
||||||
buttons = (
|
buttons = (
|
||||||
<div className="pull-right">
|
<div className="pull-right">
|
||||||
@ -122,8 +129,9 @@ let Form = React.createClass({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form role="form" onSubmit={this.submit}>
|
<form role="form" className="ascribe-form" onSubmit={this.submit} autoComplete="on">
|
||||||
{this.getErrors()}
|
{this.getErrors()}
|
||||||
{this.renderChildren()}
|
{this.renderChildren()}
|
||||||
{this.getButtons()}
|
{this.getButtons()}
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactAddons from 'react/addons';
|
import ReactAddons from 'react/addons';
|
||||||
|
|
||||||
|
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
|
||||||
|
import Tooltip from 'react-bootstrap/lib/Tooltip';
|
||||||
|
|
||||||
let Property = React.createClass({
|
let Property = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
editable: React.PropTypes.bool,
|
editable: React.PropTypes.bool,
|
||||||
@ -49,6 +52,11 @@ let Property = React.createClass({
|
|||||||
isFocused: true
|
isFocused: true
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
handleBlur() {
|
||||||
|
this.setState({
|
||||||
|
isFocused: false
|
||||||
|
});
|
||||||
|
},
|
||||||
handleSuccess(){
|
handleSuccess(){
|
||||||
this.setState({
|
this.setState({
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
@ -83,22 +91,35 @@ let Property = React.createClass({
|
|||||||
return ReactAddons.addons.cloneWithProps(child, {
|
return ReactAddons.addons.cloneWithProps(child, {
|
||||||
value: this.state.value,
|
value: this.state.value,
|
||||||
onChange: this.handleChange,
|
onChange: this.handleChange,
|
||||||
|
onFocus: this.handleFocus,
|
||||||
|
onBlur: this.handleBlur,
|
||||||
disabled: !this.props.editable,
|
disabled: !this.props.editable,
|
||||||
ref: 'input'
|
ref: 'input'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
|
let tooltip = <span/>;
|
||||||
|
if (this.props.tooltip){
|
||||||
|
tooltip = (
|
||||||
|
<Tooltip>
|
||||||
|
{this.props.tooltip}
|
||||||
|
</Tooltip>);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={'ascribe-settings-wrapper ' + this.getClassName()}
|
className={'ascribe-settings-wrapper ' + this.getClassName()}
|
||||||
onClick={this.handleFocus}>
|
onClick={this.handleFocus} onfocus={this.handleFocus}>
|
||||||
<div className="ascribe-settings-property">
|
<OverlayTrigger
|
||||||
{this.state.errors}
|
delay={500}
|
||||||
<span>{ this.props.label}</span>
|
placement="top"
|
||||||
{this.renderChildren()}
|
overlay={tooltip}>
|
||||||
</div>
|
<div className="ascribe-settings-property">
|
||||||
|
{this.state.errors}
|
||||||
|
<span>{ this.props.label}</span>
|
||||||
|
{this.renderChildren()}
|
||||||
|
</div>
|
||||||
|
</OverlayTrigger>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,19 +6,15 @@ import Router from 'react-router';
|
|||||||
import UserActions from '../actions/user_actions';
|
import UserActions from '../actions/user_actions';
|
||||||
import UserStore from '../stores/user_store';
|
import UserStore from '../stores/user_store';
|
||||||
|
|
||||||
import apiUrls from '../constants/api_urls.js';
|
import Alt from '../alt';
|
||||||
//import PieceListActions from '../actions/piece_list_actions';
|
|
||||||
import requests from '../utils/requests';
|
|
||||||
|
|
||||||
import Nav from 'react-bootstrap/lib/Nav';
|
import Nav from 'react-bootstrap/lib/Nav';
|
||||||
import Navbar from 'react-bootstrap/lib/Navbar';
|
import Navbar from 'react-bootstrap/lib/Navbar';
|
||||||
import NavItem from 'react-bootstrap/lib/NavItem';
|
import CollapsibleNav from 'react-bootstrap/lib/CollapsibleNav';
|
||||||
import DropdownButton from 'react-bootstrap/lib/DropdownButton';
|
import DropdownButton from 'react-bootstrap/lib/DropdownButton';
|
||||||
import MenuItem from 'react-bootstrap/lib/MenuItem';
|
import MenuItem from 'react-bootstrap/lib/MenuItem';
|
||||||
import MenuItemLink from 'react-router-bootstrap/lib/MenuItemLink';
|
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';
|
|
||||||
|
|
||||||
|
|
||||||
import { getLangText } from '../utils/lang_utils';
|
import { getLangText } from '../utils/lang_utils';
|
||||||
@ -26,7 +22,7 @@ import { getLangText } from '../utils/lang_utils';
|
|||||||
let Link = Router.Link;
|
let Link = Router.Link;
|
||||||
|
|
||||||
let Header = React.createClass({
|
let Header = React.createClass({
|
||||||
//mixins: [Router.Navigation],
|
mixins: [Router.Navigation],
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return UserStore.getState();
|
return UserStore.getState();
|
||||||
@ -41,53 +37,46 @@ let Header = React.createClass({
|
|||||||
UserStore.unlisten(this.onChange);
|
UserStore.unlisten(this.onChange);
|
||||||
},
|
},
|
||||||
handleLogout(){
|
handleLogout(){
|
||||||
requests
|
UserActions.logoutCurrentUser();
|
||||||
.get(apiUrls.users_logout)
|
Alt.flush();
|
||||||
.then(this.refreshData);
|
|
||||||
},
|
},
|
||||||
onChange(state) {
|
onChange(state) {
|
||||||
this.setState(state);
|
this.setState(state);
|
||||||
},
|
},
|
||||||
|
|
||||||
refreshData(){
|
|
||||||
location.reload();
|
|
||||||
},
|
|
||||||
render() {
|
render() {
|
||||||
let account = null;
|
let account = null;
|
||||||
let signup = null;
|
let signup = null;
|
||||||
if (this.state.currentUser.username){
|
if (this.state.currentUser.username){
|
||||||
account = (
|
account = (
|
||||||
<DropdownButton eventKey="1" title={this.state.currentUser.username}>
|
<DropdownButton eventKey="1" title={this.state.currentUser.username}>
|
||||||
<MenuItemLink to="settings">{getLangText('Account Settings')}</MenuItemLink>
|
<MenuItemLink to="settings">{getLangText('Account Settings')}</MenuItemLink>
|
||||||
<li className="divider"></li>
|
<li className="divider"></li>
|
||||||
<MenuItem eventKey="2" href="/art/faq/">{getLangText('FAQ')}</MenuItem>
|
<MenuItem eventKey="2" href="/art/faq/">{getLangText('FAQ')}</MenuItem>
|
||||||
<MenuItem eventKey="3" href="/art/terms/">{getLangText('Terms of Service')}</MenuItem>
|
<MenuItem eventKey="3" href="/art/terms/">{getLangText('Terms of Service')}</MenuItem>
|
||||||
<MenuItem divider />
|
<MenuItem divider />
|
||||||
<MenuItem eventKey="4" href="#" onClick={this.handleLogout}>{getLangText('Log out')}</MenuItem>
|
<MenuItem eventKey="4" onClick={this.handleLogout}>{getLangText('Log out')}</MenuItem>
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
account = (
|
account = <NavItemLink to="login">LOGIN</NavItemLink>;
|
||||||
<LoginModal
|
signup = <NavItemLink to="signup">SIGNUP</NavItemLink>;
|
||||||
button={<NavItem to="pieces">LOGIN</NavItem>}
|
|
||||||
handleSuccess={this.refreshData}/>);
|
|
||||||
signup = (
|
|
||||||
<SignupModal
|
|
||||||
button={<NavItem to="pieces">SIGNUP</NavItem>} />);
|
|
||||||
}
|
}
|
||||||
return (
|
let brand = (<Link className="navbar-brand" to="pieces" path="/?page=1">
|
||||||
<Navbar>
|
|
||||||
<Nav>
|
|
||||||
<Link className="navbar-brand" to="pieces" path="/?page=1">
|
|
||||||
<span>ascribe </span>
|
<span>ascribe </span>
|
||||||
<span className="glyph-ascribe-spool-chunked ascribe-color"></span>
|
<span className="glyph-ascribe-spool-chunked ascribe-color"></span>
|
||||||
</Link>
|
</Link>);
|
||||||
</Nav>
|
return (
|
||||||
<Nav right>
|
|
||||||
{account}
|
<Navbar brand={brand} toggleNavKey={0}>
|
||||||
{signup}
|
<CollapsibleNav eventKey={0}>
|
||||||
</Nav>
|
<Nav navbar />
|
||||||
|
<Nav navbar right>
|
||||||
|
{account}
|
||||||
|
{signup}
|
||||||
|
</Nav>
|
||||||
|
</CollapsibleNav>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
86
js/components/login_container.js
Normal file
86
js/components/login_container.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
'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 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}
|
||||||
|
buttons={
|
||||||
|
<button type="submit" className="btn ascribe-btn ascribe-btn-login">
|
||||||
|
Log in to ascribe
|
||||||
|
</button>}
|
||||||
|
spinner={
|
||||||
|
<button className="btn ascribe-btn ascribe-btn-login ascribe-btn-login-spinner">
|
||||||
|
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
|
||||||
|
</button>
|
||||||
|
}>
|
||||||
|
<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>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export default LoginContainer;
|
@ -5,73 +5,149 @@ import React from 'react';
|
|||||||
import AppConstants from '../constants/application_constants';
|
import AppConstants from '../constants/application_constants';
|
||||||
import fineUploader from 'fineUploader';
|
import fineUploader from 'fineUploader';
|
||||||
|
|
||||||
|
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 apiUrls from '../constants/api_urls';
|
||||||
|
|
||||||
import ReactS3FineUploader from 'ReactS3FineUploader';
|
import ReactS3FineUploader from 'ReactS3FineUploader';
|
||||||
|
|
||||||
let RegisterPiece = React.createClass( {
|
let RegisterPiece = React.createClass( {
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="row ascribe-row">
|
||||||
<ReactS3FineUploader
|
<div className="col-md-6">
|
||||||
keyRoutine={{
|
<FileUploader />
|
||||||
url: AppConstants.serverUrl + 's3/key/',
|
|
||||||
fileClass: 'digitalwork'
|
|
||||||
}}
|
|
||||||
autoUpload={true}
|
|
||||||
debug={false}
|
|
||||||
objectProperties={{
|
|
||||||
acl: 'public-read',
|
|
||||||
bucket: 'ascribe0'
|
|
||||||
}}
|
|
||||||
request={{
|
|
||||||
endpoint: 'https://ascribe0.s3.amazonaws.com',
|
|
||||||
accessKey: 'AKIAIVCZJ33WSCBQ3QDA'
|
|
||||||
}}
|
|
||||||
signature={{
|
|
||||||
endpoint: AppConstants.serverUrl + 's3/signature/'
|
|
||||||
}}
|
|
||||||
uploadSuccess={{
|
|
||||||
params: {
|
|
||||||
isBrowserPreviewCapable: fineUploader.supportedFeatures.imagePreviews
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
cors={{
|
|
||||||
expected: true
|
|
||||||
}}
|
|
||||||
chunking={{
|
|
||||||
enabled: true
|
|
||||||
}}
|
|
||||||
resume={{
|
|
||||||
enabled: true
|
|
||||||
}}
|
|
||||||
retry={{
|
|
||||||
enableAuto: false
|
|
||||||
}}
|
|
||||||
deleteFile={{
|
|
||||||
enabled: true,
|
|
||||||
method: 'DELETE',
|
|
||||||
endpoint: AppConstants.serverUrl + 's3/delete'
|
|
||||||
}}
|
|
||||||
validation={{
|
|
||||||
itemLimit: 100000,
|
|
||||||
sizeLimit: '25000000000'
|
|
||||||
}}
|
|
||||||
session={{
|
|
||||||
endpoint: null
|
|
||||||
}}
|
|
||||||
messages={{
|
|
||||||
unsupportedBrowser: '<h3>Upload is not functional in IE7 as IE7 has no support for CORS!</h3>'
|
|
||||||
}}
|
|
||||||
formatFileName={(name) => {// fix maybe
|
|
||||||
if (name !== undefined && name.length > 26) {
|
|
||||||
name = name.slice(0, 15) + '...' + name.slice(-15);
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}}
|
|
||||||
multiple={true}/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="col-md-6">
|
||||||
|
<LoginForm />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default RegisterPiece;
|
|
||||||
|
let FileUploader = React.createClass( {
|
||||||
|
render() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ReactS3FineUploader
|
||||||
|
keyRoutine={{
|
||||||
|
url: AppConstants.serverUrl + 's3/key/',
|
||||||
|
fileClass: 'digitalwork'
|
||||||
|
}}
|
||||||
|
autoUpload={true}
|
||||||
|
debug={false}
|
||||||
|
objectProperties={{
|
||||||
|
acl: 'public-read',
|
||||||
|
bucket: 'ascribe0'
|
||||||
|
}}
|
||||||
|
request={{
|
||||||
|
endpoint: 'https://ascribe0.s3.amazonaws.com',
|
||||||
|
accessKey: 'AKIAIVCZJ33WSCBQ3QDA'
|
||||||
|
}}
|
||||||
|
signature={{
|
||||||
|
endpoint: AppConstants.serverUrl + 's3/signature/'
|
||||||
|
}}
|
||||||
|
uploadSuccess={{
|
||||||
|
params: {
|
||||||
|
isBrowserPreviewCapable: fineUploader.supportedFeatures.imagePreviews
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
cors={{
|
||||||
|
expected: true
|
||||||
|
}}
|
||||||
|
chunking={{
|
||||||
|
enabled: true
|
||||||
|
}}
|
||||||
|
resume={{
|
||||||
|
enabled: true
|
||||||
|
}}
|
||||||
|
retry={{
|
||||||
|
enableAuto: false
|
||||||
|
}}
|
||||||
|
deleteFile={{
|
||||||
|
enabled: true,
|
||||||
|
method: 'DELETE',
|
||||||
|
endpoint: AppConstants.serverUrl + 's3/delete'
|
||||||
|
}}
|
||||||
|
validation={{
|
||||||
|
itemLimit: 100000,
|
||||||
|
sizeLimit: '25000000000'
|
||||||
|
}}
|
||||||
|
session={{
|
||||||
|
endpoint: null
|
||||||
|
}}
|
||||||
|
messages={{
|
||||||
|
unsupportedBrowser: '<h3>Upload is not functional in IE7 as IE7 has no support for CORS!</h3>'
|
||||||
|
}}
|
||||||
|
formatFileName={(name) => {// fix maybe
|
||||||
|
if (name !== undefined && name.length > 26) {
|
||||||
|
name = name.slice(0, 15) + '...' + name.slice(-15);
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}}
|
||||||
|
multiple={true}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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}
|
||||||
|
buttons={
|
||||||
|
<button type="submit" className="btn ascribe-btn ascribe-btn-login">
|
||||||
|
Log in to ascribe
|
||||||
|
</button>}
|
||||||
|
spinner={
|
||||||
|
<button className="btn ascribe-btn ascribe-btn-login ascribe-btn-login-spinner">
|
||||||
|
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
|
||||||
|
</button>
|
||||||
|
}>
|
||||||
|
<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>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
export default RegisterPiece;
|
||||||
|
135
js/components/signup_container.js
Normal file
135
js/components/signup_container.js
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
'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 SignupContainer = React.createClass({
|
||||||
|
mixins: [Router.Navigation],
|
||||||
|
|
||||||
|
getInitialState(){
|
||||||
|
return ({
|
||||||
|
submitted: false,
|
||||||
|
message: null
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleSuccess(message){
|
||||||
|
this.setState({
|
||||||
|
submitted: true,
|
||||||
|
message: message
|
||||||
|
});
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
if (this.state.submitted){
|
||||||
|
return (
|
||||||
|
<div className="ascribe-login-wrapper">
|
||||||
|
<br/>
|
||||||
|
<div className="ascribe-login-text ascribe-login-header">
|
||||||
|
{this.state.message}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="ascribe-login-wrapper">
|
||||||
|
<br/>
|
||||||
|
<div className="ascribe-login-text ascribe-login-header">
|
||||||
|
Welcome to ascribe...
|
||||||
|
</div>
|
||||||
|
<SignupForm handleSuccess={this.handleSuccess}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
let SignupForm = React.createClass({
|
||||||
|
mixins: [Router.Navigation],
|
||||||
|
|
||||||
|
handleSuccess(response){
|
||||||
|
|
||||||
|
let notificationText = 'Sign up successful';
|
||||||
|
let notification = new GlobalNotificationModel(notificationText, 'success', 50000);
|
||||||
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||||
|
this.props.handleSuccess('We sent an email to your address ' + response.user.email + ', please confirm.');
|
||||||
|
|
||||||
|
},
|
||||||
|
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
|
||||||
|
ref='form'
|
||||||
|
url={apiUrls.users_signup}
|
||||||
|
handleSuccess={this.handleSuccess}
|
||||||
|
buttons={
|
||||||
|
<button type="submit" className="btn ascribe-btn ascribe-btn-login">
|
||||||
|
Sign up to ascribe
|
||||||
|
</button>}
|
||||||
|
spinner={
|
||||||
|
<button className="btn ascribe-btn ascribe-btn-login ascribe-btn-login-spinner">
|
||||||
|
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
|
||||||
|
</button>
|
||||||
|
}>
|
||||||
|
<Property
|
||||||
|
name='email'
|
||||||
|
label="Email">
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
placeholder="Enter your email"
|
||||||
|
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">
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
placeholder="Enter a promocode here (Optional)"/>
|
||||||
|
</Property>
|
||||||
|
<hr />
|
||||||
|
<InputCheckbox
|
||||||
|
name='terms'
|
||||||
|
required="required"
|
||||||
|
label={
|
||||||
|
<div>
|
||||||
|
I agree to the
|
||||||
|
<a href="/terms" target="_blank"> Terms of Service</a>
|
||||||
|
</div>}/>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default SignupContainer;
|
@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import requests from '../utils/requests';
|
import requests from '../utils/requests';
|
||||||
|
import apiUrls from '../constants/api_urls';
|
||||||
|
|
||||||
let UserFetcher = {
|
let UserFetcher = {
|
||||||
/**
|
/**
|
||||||
@ -10,6 +10,10 @@ let UserFetcher = {
|
|||||||
*/
|
*/
|
||||||
fetchOne() {
|
fetchOne() {
|
||||||
return requests.get('user');
|
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 AscribeApp from './components/ascribe_app';
|
||||||
import PieceList from './components/piece_list';
|
import PieceList from './components/piece_list';
|
||||||
import EditionContainer from './components/edition_container';
|
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 PasswordResetContainer from './components/password_reset_container';
|
||||||
|
|
||||||
import SettingsContainer from './components/settings_container';
|
import SettingsContainer from './components/settings_container';
|
||||||
import AppConstants from './constants/application_constants';
|
import AppConstants from './constants/application_constants';
|
||||||
import RegisterPiece from './components/register_piece';
|
import RegisterPiece from './components/register_piece';
|
||||||
//import LoginModalHandler from './components/login_modal_handler';
|
|
||||||
|
|
||||||
let Route = Router.Route;
|
let Route = Router.Route;
|
||||||
let Redirect = Router.Redirect;
|
let Redirect = Router.Redirect;
|
||||||
@ -19,6 +22,8 @@ let baseUrl = AppConstants.baseUrl;
|
|||||||
|
|
||||||
let routes = (
|
let routes = (
|
||||||
<Route name="app" path={baseUrl} handler={AscribeApp}>
|
<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="pieces" path="collection" handler={PieceList} />
|
||||||
<Route name="edition" path="editions/:editionId" handler={EditionContainer} />
|
<Route name="edition" path="editions/:editionId" handler={EditionContainer} />
|
||||||
<Route name="password_reset" path="password_reset" handler={PasswordResetContainer} />
|
<Route name="password_reset" path="password_reset" handler={PasswordResetContainer} />
|
||||||
|
@ -13,6 +13,9 @@ class UserStore {
|
|||||||
onUpdateCurrentUser(user) {
|
onUpdateCurrentUser(user) {
|
||||||
this.currentUser = user;
|
this.currentUser = user;
|
||||||
}
|
}
|
||||||
|
onDeleteCurrentUser() {
|
||||||
|
this.currentUser = {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default alt.createStore(UserStore, 'UserStore');
|
export default alt.createStore(UserStore, 'UserStore');
|
||||||
|
@ -89,6 +89,9 @@ class Requests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get(url, params) {
|
get(url, params) {
|
||||||
|
if (url === undefined){
|
||||||
|
throw new Error('Url undefined');
|
||||||
|
}
|
||||||
let paramsCopy = this._merge(params);
|
let paramsCopy = this._merge(params);
|
||||||
let newUrl = this.prepareUrl(url, paramsCopy, true);
|
let newUrl = this.prepareUrl(url, paramsCopy, true);
|
||||||
return this.request('get', newUrl);
|
return this.request('get', newUrl);
|
||||||
|
2
node_modules/react-s3-fineuploader
generated
vendored
2
node_modules/react-s3-fineuploader
generated
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 2161e28e1bf5f69719c8b376ecb24fd3334806ba
|
Subproject commit e2890cf3d28d010abc04e0de3b218afe8c949596
|
@ -27,7 +27,6 @@
|
|||||||
"compact": false
|
"compact": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"reactify",
|
|
||||||
"browserify-shim"
|
"browserify-shim"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
79
sass/ascribe_login.scss
Normal file
79
sass/ascribe_login.scss
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
$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-btn-login-spinner{
|
||||||
|
background-color: rgba(2, 182, 163, 0.4);
|
||||||
|
padding: 0.8em;
|
||||||
|
img {
|
||||||
|
height: 3em;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(2, 182, 163, 0.4);
|
||||||
|
}
|
||||||
|
&:active, &:focus {
|
||||||
|
background-color: rgba(2, 182, 163, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.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
sass/ascribe_piece_register.scss
Normal file
7
sass/ascribe_piece_register.scss
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
$break-small: 764px;
|
||||||
|
|
||||||
|
|
||||||
|
.ascribe-row {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto
|
||||||
|
}
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
border-left: 3px solid rgba(0,0,0,0);
|
border-left: 3px solid rgba(0,0,0,0);
|
||||||
|
|
||||||
&:last-child {
|
&div:last-of-type {
|
||||||
border-bottom: 1px solid rgba(0,0,0,.2);
|
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 '../node_modules/react-datepicker/dist/react-datepicker';
|
||||||
@import './ascribe-fonts/style';
|
@import './ascribe-fonts/style';
|
||||||
@import './ascribe-fonts/ascribe-fonts';
|
@import './ascribe-fonts/ascribe-fonts';
|
||||||
|
@import 'ascribe_login';
|
||||||
@import 'ascribe_table';
|
@import 'ascribe_table';
|
||||||
@import 'ascribe_accordion_list';
|
@import 'ascribe_accordion_list';
|
||||||
@import 'ascribe_piece_list_bulk_modal';
|
@import 'ascribe_piece_list_bulk_modal';
|
||||||
@ -17,6 +18,7 @@ $BASE_URL: '<%= BASE_URL %>';
|
|||||||
@import 'ascribe_textarea';
|
@import 'ascribe_textarea';
|
||||||
@import 'ascribe_media_player';
|
@import 'ascribe_media_player';
|
||||||
@import 'ascribe-global-notification';
|
@import 'ascribe-global-notification';
|
||||||
|
@import 'ascribe_piece_register';
|
||||||
@import 'offset_right';
|
@import 'offset_right';
|
||||||
@import 'ascribe_settings';
|
@import 'ascribe_settings';
|
||||||
@import 'ascribe_react_s3_fineuploader';
|
@import 'ascribe_react_s3_fineuploader';
|
||||||
@ -31,10 +33,12 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.navbar-default {
|
.navbar-default {
|
||||||
|
border: none;
|
||||||
border-left:0;
|
border-left:0;
|
||||||
border-right:0;
|
border-right:0;
|
||||||
margin-bottom: 1.5em;
|
margin-bottom: 1.5em;
|
||||||
border-top:0;
|
border-top:0;
|
||||||
|
border-color: #CCC
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-right {
|
.navbar-right {
|
||||||
@ -50,6 +54,17 @@ body {
|
|||||||
color: $ascribe-color;
|
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 */
|
/* Taken from http://stackoverflow.com/a/20548578 */
|
||||||
.vcenter {
|
.vcenter {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
Loading…
Reference in New Issue
Block a user