diff --git a/js/actions/user_actions.js b/js/actions/user_actions.js index 90e931b1..c5095391 100644 --- a/js/actions/user_actions.js +++ b/js/actions/user_actions.js @@ -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); diff --git a/js/components/ascribe_forms/form.js b/js/components/ascribe_forms/form.js index 8d25531b..e1a81eab 100644 --- a/js/components/ascribe_forms/form.js +++ b/js/components/ascribe_forms/form.js @@ -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 ( -
+ {this.getErrors()} {this.renderChildren()} - {this.getButtons()}
); diff --git a/js/components/ascribe_forms/property.js b/js/components/ascribe_forms/property.js index 7e27c1fe..6181dbc9 100644 --- a/js/components/ascribe_forms/property.js +++ b/js/components/ascribe_forms/property.js @@ -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 = ; + if (this.props.tooltip){ + tooltip = ( + + {this.props.tooltip} + ); + } return (
-
- {this.state.errors} - { this.props.label} - {this.renderChildren()} -
+ onClick={this.handleFocus} onfocus={this.handleFocus}> + +
+ {this.state.errors} + { this.props.label} + {this.renderChildren()} +
+
); } diff --git a/js/components/header.js b/js/components/header.js index 66984c62..15983e46 100644 --- a/js/components/header.js +++ b/js/components/header.js @@ -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({ {getLangText('FAQ')} {getLangText('Terms of Service')} - {getLangText('Log out')} + {getLangText('Log out')} ); } else { - account = ( - LOGIN} - handleSuccess={this.refreshData}/>); + account = LOGIN; signup = ( SIGNUP} />); diff --git a/js/components/login_container.js b/js/components/login_container.js new file mode 100644 index 00000000..97e8befb --- /dev/null +++ b/js/components/login_container.js @@ -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 ( +
+
+
+ Log in to ascribe... +
+ + +
+ ); + } +}); + + +let LoginForm = React.createClass({ + mixins: [Router.Navigation], + + + handleSuccess(){ + let notification = new GlobalNotificationModel('Login successsful', 'success', 10000); + GlobalNotificationActions.appendGlobalNotification(notification); + this.transitionTo('pieces'); + + }, + render() { + return ( +
+ + + + + + +
+
+ Not an ascribe user? Sign up...
+ Forgot my password? Rescue me... +
+ +
+ ); + } +}); + + +export default LoginContainer; \ No newline at end of file diff --git a/js/components/register_piece.js b/js/components/register_piece.js index e1e1824f..d420d32b 100644 --- a/js/components/register_piece.js +++ b/js/components/register_piece.js @@ -9,7 +9,7 @@ import ReactS3FineUploader from 'ReactS3FineUploader'; let RegisterPiece = React.createClass( { render() { - console.log(AppConstants.serverUrl) + return (
diff --git a/js/components/signup_container.js b/js/components/signup_container.js new file mode 100644 index 00000000..82ca0714 --- /dev/null +++ b/js/components/signup_container.js @@ -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 ( +
+
+
+ Welcome to ascribe... +
+ +
+ ); + } +}); + + +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 ( +
+ + + + + + + + + + + + + + + + + I agree to the  + Terms of Service +
}/> +
+ + + ); + } +}); + +export default LoginContainer; \ No newline at end of file diff --git a/js/fetchers/user_fetcher.js b/js/fetchers/user_fetcher.js index 5120fbd9..a175c644 100644 --- a/js/fetchers/user_fetcher.js +++ b/js/fetchers/user_fetcher.js @@ -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); } }; diff --git a/js/routes.js b/js/routes.js index 72eff7e9..cb73daf3 100644 --- a/js/routes.js +++ b/js/routes.js @@ -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 = ( + + diff --git a/js/stores/user_store.js b/js/stores/user_store.js index 9c715283..14eb1f90 100644 --- a/js/stores/user_store.js +++ b/js/stores/user_store.js @@ -13,6 +13,9 @@ class UserStore { onUpdateCurrentUser(user) { this.currentUser = user; } + onDeleteCurrentUser() { + this.currentUser = {}; + } } export default alt.createStore(UserStore, 'UserStore'); diff --git a/js/utils/requests.js b/js/utils/requests.js index 20e8ef65..e10a8a99 100644 --- a/js/utils/requests.js +++ b/js/utils/requests.js @@ -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); diff --git a/sass/ascribe_login.scss b/sass/ascribe_login.scss new file mode 100644 index 00000000..956b46a5 --- /dev/null +++ b/sass/ascribe_login.scss @@ -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%); +} \ No newline at end of file diff --git a/sass/ascribe_settings.scss b/sass/ascribe_settings.scss index c3ca7445..37a8c31e 100644 --- a/sass/ascribe_settings.scss +++ b/sass/ascribe_settings.scss @@ -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); } } diff --git a/sass/main.scss b/sass/main.scss index d6a6681b..e6cb524a 100644 --- a/sass/main.scss +++ b/sass/main.scss @@ -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;