Commit c4d3e07d0fe924ffef30abd83dce85b192f45d77

Authored by Deepak
1 parent 39d8f536dc
Exists in master

added login/reset and forgot password

Showing 49 changed files with 2040 additions and 557 deletions   Show diff stats
1   -import '/imports/startup/client';
  1 +import '/imports/client/app/index';
... ...
imports/client/app/index.js
... ... @@ -0,0 +1,37 @@
  1 +import _ from 'lodash';
  2 +import moment from 'moment';
  3 +import { Meteor } from 'meteor/meteor';
  4 +import { Router, browserHistory } from 'react-router';
  5 +
  6 +import { Orgs } from '/imports/collections/orgs/index';
  7 +import { Users } from '/imports/collections/users/index';;
  8 +
  9 +
  10 +import { Bert } from 'meteor/themeteorchef:bert';
  11 +import './routes.js';
  12 +import './shelf.js';
  13 +// import 'react-quill/dist/quill.snow.css';
  14 +
  15 +Bert.defaults = {
  16 + hideDelay: 3500,
  17 + // Accepts: a number in milliseconds.
  18 + style: 'fixed-top',
  19 + // Accepts: fixed-top, fixed-bottom, growl-top-left, growl-top-right,
  20 + // growl-bottom-left, growl-bottom-right.
  21 + type: 'default'
  22 + // Accepts: default, success, info, warning, danger.
  23 +};
  24 +
  25 +_.assign(window, {
  26 + _,
  27 +});
  28 +
  29 +if(Meteor.settings.public.environment === 'development') {
  30 + _.assign(window, {
  31 + Meteor,
  32 + Shelf,
  33 +
  34 + Orgs,
  35 + Users,
  36 + });
  37 +}
... ...
imports/client/app/routes.js
... ... @@ -0,0 +1,104 @@
  1 +/* eslint-disable max-len */
  2 +
  3 +import React from 'react';
  4 +import { render } from 'react-dom';
  5 +import { Router, Route,
  6 + IndexRoute, browserHistory } from 'react-router';
  7 +import { Meteor } from 'meteor/meteor';
  8 +
  9 +/**
  10 + * General Components
  11 + */
  12 +import Index from '/imports/client/views/app/module/Index';
  13 +
  14 +/**
  15 + * Org Components
  16 + */
  17 +
  18 +import { App } from '/imports/client/layouts/OrgApp';
  19 +import { AppModule } from '/imports/client/views/org/app/module/Index';
  20 +import { Orgs } from '/imports/collections/orgs/index';
  21 +import NotFound from '/imports/client/views/org/NotFound';
  22 +
  23 +/**
  24 + * NonOrg Components
  25 + */
  26 +import Signup from '/imports/client/views/nonOrg/enter/SignupView';
  27 +
  28 +/**
  29 + * Invalid Org Components
  30 + */
  31 +
  32 +const authenticate = (nextState, replace) => {
  33 + if (!Meteor.loggingIn() && !Meteor.userId()) {
  34 + replace({
  35 + pathname: '/login',
  36 + state: { nextPathname: nextState.location.pathname },
  37 + });
  38 + }
  39 +};
  40 +
  41 +
  42 +const detectOrg = () => {
  43 + orgSlug = "";
  44 + var hostnameArray = document.location.hostname.split( "." );
  45 + if(hostnameArray[1]=='localhost'){
  46 + orgSlug = hostnameArray[0];
  47 + }
  48 + if(orgSlug!=""){
  49 + Meteor.call('checkExistingOrg', {slug:orgSlug}, function(err, res) {
  50 + if(res){
  51 + Session.set('orgId', res._id._str);
  52 + Session.set('orgSlug', orgSlug);
  53 + render(getOrgRoutes(),document.getElementById('app'));
  54 + }else{
  55 + render(getInvalidOrgRoute(),document.getElementById('app'));
  56 + }
  57 + });
  58 + }else{
  59 + render(getNonOrgRoutes(),document.getElementById('app'));
  60 + }
  61 +}
  62 +const checkSlug = (nextState, replace) => {
  63 + orgId = Session.get('orgId');
  64 +}
  65 +
  66 +/**
  67 +There are three types of routes
  68 +1)getOrgRoutes: all the routes that should be present for a registered org
  69 +2)getInvalidOrgRoute: all the routes where someone tries to enter a subdomain which hasn't been registered yet (404 mostly :D)
  70 +3)getNonOrgRoutes: all routes linked to normal site, ie signing up a new org. CHeking out demo and everything internal
  71 +**/
  72 +const getOrgRoutes = () => (
  73 + <Router history={ browserHistory }>
  74 + <Route path="/" component={ App }>
  75 + <IndexRoute name="index" component={ AppModule } />
  76 + <Route path="*" component={ NotFound } />
  77 + </Route>
  78 + </Router>
  79 +)
  80 +
  81 +
  82 +const getInvalidOrgRoute = () => (
  83 + <Router history={ browserHistory }>
  84 + <Route path="/" component={ App }>
  85 + <IndexRoute name="index" component={ NotFound } />
  86 + <Route path="*" component={ NotFound } />
  87 + </Route>
  88 + </Router>
  89 +)
  90 +
  91 +const getNonOrgRoutes = () => (
  92 + <Router history={ browserHistory }>
  93 + <Route path="/" component={ App }>
  94 + <IndexRoute name="index" component={ Index } />
  95 + <Route name="signup" path="/signup" component={ Signup } />
  96 + <Route path="*" component={ NotFound } />
  97 + </Route>
  98 + </Router>
  99 +)
  100 +
  101 +
  102 +Meteor.startup(() => {
  103 + detectOrg();
  104 +});
... ...
imports/client/app/shelf.js
... ... @@ -0,0 +1,12 @@
  1 +import { ReactiveVar } from 'meteor/reactive-var'
  2 +
  3 +
  4 +window.Shelf = {};
  5 +
  6 +Shelf.layout = new ReactiveVar({});
  7 +// {
  8 +// bulb: new ReactiveVar(''), // Active menu tab
  9 +// subbulb: new ReactiveVar(''), // Active submenu tab
  10 +// };
  11 +
  12 +
... ...
imports/client/app/utils/changePassword.js
... ... @@ -0,0 +1,7 @@
  1 +import $ from 'jquery';
  2 +import 'jquery-validation';
  3 +import { Accounts } from 'meteor/accounts-base';
  4 +import { Bert } from 'meteor/themeteorchef:bert';
  5 +import { getInputValue } from './get-input-value';
  6 +
  7 +let component;
... ...
imports/client/app/utils/get-input-value.js
... ... @@ -0,0 +1,5 @@
  1 +// TODO WTF: what is this thing? -H
  2 +
  3 +import ReactDOM from 'react-dom';
  4 +
  5 +export const getInputValue = (component) => ReactDOM.findDOMNode(component).value;
... ...
imports/client/app/utils/login.js
... ... @@ -0,0 +1,70 @@
  1 +import $ from 'jquery';
  2 +import 'jquery-validation';
  3 +import { browserHistory } from 'react-router';
  4 +import { Meteor } from 'meteor/meteor';
  5 +import { Bert } from 'meteor/themeteorchef:bert';
  6 +import { getInputValue } from './get-input-value';
  7 +
  8 +let component;
  9 +
  10 +const login = () => {
  11 + const email = getInputValue(component.refs.emailAddress);
  12 + const password = getInputValue(component.refs.password);
  13 +
  14 + Meteor.call('checkEmailVerification', email, ( error, data ) => {
  15 + if ( error ) {
  16 + Bert.alert( error.reason, 'danger' );
  17 + }
  18 + else {
  19 + if ( data == "verified" ) {
  20 + Meteor.loginWithPassword(email, password, (error) => {
  21 + if ( error ) {
  22 + Bert.alert(error.reason, 'warning');
  23 + } else {
  24 + Bert.alert('Logged in!', 'success');
  25 +
  26 + const { location } = component.props;
  27 + if (location.state && location.state.nextPathname) {
  28 + browserHistory.push(location.state.nextPathname);
  29 + } else {
  30 + browserHistory.push('/');
  31 + }
  32 + }
  33 + });
  34 + } else if ( data == "unverified" ){
  35 + Bert.alert("Check your email for a verification link");
  36 + } else {
  37 + Bert.alert("Either email or password is incorrect");
  38 + }
  39 + }
  40 + });
  41 +};
  42 +
  43 +const validate = () => {
  44 + $(component.refs.login).validate({
  45 + rules: {
  46 + emailAddress: {
  47 + required: true,
  48 + email: true,
  49 + },
  50 + password: {
  51 + required: true,
  52 + },
  53 + },
  54 + messages: {
  55 + emailAddress: {
  56 + required: 'Need an email address here.',
  57 + email: 'Is this email address legit?',
  58 + },
  59 + password: {
  60 + required: 'Need a password here.',
  61 + },
  62 + },
  63 + submitHandler() { login(); },
  64 + });
  65 +};
  66 +
  67 +export const handleLogin = (options) => {
  68 + component = options.component;
  69 + validate();
  70 +};
... ...
imports/client/app/utils/loginMethods.js
... ... @@ -0,0 +1,10 @@
  1 +import { browserHistory } from 'react-router';
  2 +
  3 +
  4 +export const logout = () => {
  5 + Meteor.logout();
  6 + browserHistory.push('/');
  7 +};
  8 +
  9 +
  10 +
... ...
imports/client/app/utils/rate-limit.js
... ... @@ -0,0 +1,18 @@
  1 +import { _ } from 'meteor/underscore';
  2 +import { Meteor } from 'meteor/meteor';
  3 +import { DDPRateLimiter } from 'meteor/ddp-rate-limiter';
  4 +
  5 +const fetchMethodNames = (methods) => _.pluck(methods, 'name');
  6 +
  7 +const assignLimits = ({ methods, limit, timeRange }) => {
  8 + const methodNames = fetchMethodNames(methods);
  9 +
  10 + if (Meteor.isServer) {
  11 + DDPRateLimiter.addRule({
  12 + name(name) { return _.contains(methodNames, name); },
  13 + connectionId() { return true; },
  14 + }, limit, timeRange);
  15 + }
  16 +};
  17 +
  18 +export const rateLimit = (options) => assignLimits(options);
... ...
imports/client/app/utils/recover-password.js
... ... @@ -0,0 +1,42 @@
  1 +import $ from 'jquery';
  2 +import 'jquery-validation';
  3 +import { Accounts } from 'meteor/accounts-base';
  4 +import { Bert } from 'meteor/themeteorchef:bert';
  5 +import { getInputValue } from './get-input-value';
  6 +
  7 +let component;
  8 +
  9 +const handleRecovery = () => {
  10 + Accounts.forgotPassword({
  11 + email: getInputValue(component.refs.emailAddress),
  12 + }, (error) => {
  13 + if (error) {
  14 + Bert.alert(error.reason, 'warning');
  15 + } else {
  16 + Bert.alert('Check your inbox for a reset link!', 'success');
  17 + }
  18 + });
  19 +};
  20 +
  21 +const validate = () => {
  22 + $(component.refs.recoverPassword).validate({
  23 + rules: {
  24 + emailAddress: {
  25 + required: true,
  26 + email: true,
  27 + },
  28 + },
  29 + messages: {
  30 + emailAddress: {
  31 + required: 'Need an email address here.',
  32 + email: 'Is this email address legit?',
  33 + },
  34 + },
  35 + submitHandler() { handleRecovery(); },
  36 + });
  37 +};
  38 +
  39 +export const handleRecoverPassword = (options) => {
  40 + component = options.component;
  41 + validate();
  42 +};
... ...
imports/client/app/utils/reset-password.js
... ... @@ -0,0 +1,54 @@
  1 +import $ from 'jquery';
  2 +import 'jquery-validation';
  3 +import { browserHistory } from 'react-router';
  4 +import { Accounts } from 'meteor/accounts-base';
  5 +import { Bert } from 'meteor/themeteorchef:bert';
  6 +import { getInputValue } from './get-input-value';
  7 +
  8 +let component;
  9 +let token;
  10 +
  11 +const handleReset = () => {
  12 + const password = getInputValue(component.refs.newPassword);
  13 + Accounts.resetPassword(token, password, (error) => {
  14 + if (error) {
  15 + Bert.alert(error.reason, 'danger');
  16 + } else {
  17 + browserHistory.push('/');
  18 + Bert.alert('Password reset!', 'success');
  19 + }
  20 + });
  21 +};
  22 +
  23 +const validate = () => {
  24 + $(component.refs.resetPassword).validate({
  25 + rules: {
  26 + newPassword: {
  27 + required: true,
  28 + minlength: 6,
  29 + },
  30 + repeatNewPassword: {
  31 + required: true,
  32 + minlength: 6,
  33 + equalTo: '[name="newPassword"]',
  34 + },
  35 + },
  36 + messages: {
  37 + newPassword: {
  38 + required: 'Enter a new password, please.',
  39 + minlength: 'Use at least six characters, please.',
  40 + },
  41 + repeatNewPassword: {
  42 + required: 'Repeat your new password, please.',
  43 + equalTo: 'Hmm, your passwords don\'t match. Try again?',
  44 + },
  45 + },
  46 + submitHandler() { handleReset(); },
  47 + });
  48 +};
  49 +
  50 +export const handleResetPassword = (options) => {
  51 + component = options.component;
  52 + token = options.token;
  53 + validate();
  54 +};
... ...
imports/client/app/utils/setQueryParam.js
... ... @@ -0,0 +1,9 @@
  1 +import _ from 'lodash';
  2 +
  3 +export const setQueryParam = (location, query) => ({
  4 + pathname: location.pathname,
  5 + hash: location.hash,
  6 + query: _.assign({}, location.query, query),
  7 +});
  8 +
  9 +
... ...
imports/client/app/utils/signup.js
... ... @@ -0,0 +1,76 @@
  1 +import $ from 'jquery';
  2 +import 'jquery-validation';
  3 +import { browserHistory } from 'react-router';
  4 +import { Accounts } from 'meteor/accounts-base';
  5 +import { Bert } from 'meteor/themeteorchef:bert';
  6 +import { getInputValue } from './get-input-value';
  7 +
  8 +let component;
  9 +
  10 +const getUserData = () => ({
  11 + email: getInputValue(component.refs.emailAddress),
  12 + password: getInputValue(component.refs.password),
  13 + profile: {
  14 + name: {
  15 + first: getInputValue(component.refs.firstName),
  16 + last: getInputValue(component.refs.lastName),
  17 + },
  18 + },
  19 +});
  20 +
  21 +const signUp = () => {
  22 + const user = getUserData();
  23 +
  24 + Accounts.createUser(user, (error) => {
  25 + if (error) {
  26 + Bert.alert(error.reason, 'danger');
  27 + } else {
  28 + Bert.alert( 'Welcome!', 'success' );
  29 + Session.set('signedEmail', user.email);
  30 + browserHistory.push('/users/verify');
  31 + }
  32 + });
  33 +};
  34 +
  35 +const validate = () => {
  36 + $(component.refs.signup).validate({
  37 + rules: {
  38 + firstName: {
  39 + required: true,
  40 + },
  41 + lastName: {
  42 + required: true,
  43 + },
  44 + emailAddress: {
  45 + required: true,
  46 + email: true,
  47 + },
  48 + password: {
  49 + required: true,
  50 + minlength: 6,
  51 + },
  52 + },
  53 + messages: {
  54 + firstName: {
  55 + required: 'First name?',
  56 + },
  57 + lastName: {
  58 + required: 'Last name?',
  59 + },
  60 + emailAddress: {
  61 + required: 'Need an email address here.',
  62 + email: 'Is this email address legit?',
  63 + },
  64 + password: {
  65 + required: 'Need a password here.',
  66 + minlength: 'Use at least six characters, please.',
  67 + },
  68 + },
  69 + submitHandler() { signUp(); },
  70 + });
  71 +};
  72 +
  73 +export const handleSignup = (options) => {
  74 + component = options.component;
  75 + validate();
  76 +};
... ...
imports/client/assets/css/icons/icomoon/styles.css
... ... @@ -2,7 +2,7 @@
2 2 font-family: 'icomoon';
3 3 src:url('fonts/icomoon.eot?3p0rtw');
4 4 src:url('fonts/icomoon.eot?#iefix3p0rtw') format('embedded-opentype'),
5   - url('fonts/icomoon.woff?3p0rtw') format('woff'),
  5 + url('fonts/icomoon.woff?3p0rtw') format('font-woff'),
6 6 url('fonts/icomoon.ttf?3p0rtw') format('truetype'),
7 7 url('fonts/icomoon.svg?3p0rtw#icomoon') format('svg');
8 8 font-weight: normal;
... ...
imports/client/layouts/OrgApp.js
1   -import React from 'react';
2   -import { Grid } from 'react-bootstrap';
3   -import AppNavigation from '/imports/client/views/org/app/module/navigation/AppNavigation';
  1 +import React, { Component } from 'react';
  2 +import { Grid } from 'react-bootstrap';
  3 +import {AppNavigationController} from '/imports/client/views/org/app/module/navigation/index';
  4 +/**
  5 + * user based redirection will take place here
  6 + */
  7 + export class App extends Component {
  8 + constructor(props) {
  9 + super(props);
  10 + this.state = {
4 11  
5   -const App = ({ children }) => (
6   - <div>
7   - <AppNavigation />
8   - <Grid>
9   - { children }
10   - </Grid>
11   - </div>
12   -);
13   -
14   -App.propTypes = {
15   - children: React.PropTypes.node,
16   -};
17   -
18   -export default App;
  12 + };
  13 + };
  14 + render(){
  15 + return (
  16 + <div>
  17 + <AppNavigationController />
  18 + <Grid>
  19 + { this.props.children }
  20 + </Grid>
  21 + </div>
  22 + )
  23 + }
  24 + }
... ...
imports/client/views/etc/index.js
... ... @@ -5,22 +5,22 @@ import {
5 5 compose,
6 6 composeAll
7 7 } from 'react-komposer';
8   -import { InviteSignupView } from './InviteSignupView';
9 8 import { Loading } from '/imports/client/components/Loading';
10 9  
11   -import { Invitations } from '/imports/collections/invitations/index';
12 10 import { Orgs } from '/imports/collections/orgs/index';
  11 +import { Users } from '/imports/collections/users/index';
13 12  
14 13 const meteorTick = (props, onData) => {
15 14  
16 15 const handles = [
17   - Meteor.subscribe('invitations.withToken')
  16 + Meteor.subscribe('users.current')
18 17 ];
19 18  
20 19 if(_.every(handles, (handle) => (handle.ready()) )) {
21   - const invitation = Invitations.findOne({});
  20 + const user = Users.current();
22 21 onData(null, {
23 22 data: {
  23 + user: user,
24 24 },
25 25 });
26 26 }
... ...
imports/client/views/org/app/module/AppLayout.js
... ... @@ -5,21 +5,22 @@ import { Link } from &#39;react-router&#39;;
5 5 import { Avatar } from '/imports/client/components/Avatar';
6 6 import { Icon } from '/imports/client/components/Icon';
7 7 import classNames from 'classnames';
  8 +import { EnterModule } from '/imports/client/views/org/enter/module/index';
  9 +// import { VerifyModule } from '/imports/client/views/verify/module/index';
8 10  
9 11  
10 12 export class AppLayout extends Component {
11 13  
12   -
13   -
14   -
15   -
16   -
17 14 render() {
  15 + console.log(this.props);
18 16 const {user} = this.props.data;
19 17  
20 18 if(!user) {
21 19 return (
22   - <div className="NotLoggedIn"></div>
  20 + <EnterModule
  21 + pane = {this.props.location.query.enter}
  22 + location = {this.props.location}
  23 + />
23 24 );
24 25 }
25 26 return (
... ...
imports/client/views/org/app/module/navigation/AppNavigation.js
1   -import React from 'react';
  1 +import React, { Component } from 'react';
2 2 import { Navbar } from 'react-bootstrap';
3 3 import { Link } from 'react-router';
4   -import PublicNavigation from './PublicNavigation.js';
5   -import AuthenticatedNavigation from './AuthenticatedNavigation.js';
  4 +import {PublicNavigation} from './PublicNavigation.js';
  5 +import {AuthenticatedNavigation} from './AuthenticatedNavigation.js';
6 6 import '/imports/client/assets/css/icons/icomoon/styles.css';
7 7 import '/imports/client/assets/css/bootstrap.css';
8 8 import '/imports/client/assets/css/core.css';
9 9 import '/imports/client/assets/css/components.css';
10 10 import '/imports/client/assets/css/colors.css';
11   -const renderNavigation = hasUser => (hasUser ? <AuthenticatedNavigation /> : <PublicNavigation />);
12   -const AppNavigation = ({ hasUser }) => (
13   - <Navbar className="navbar-inverse bg-crimson">
14   - <Navbar.Header>
15   - <Navbar.Brand>
16   - <Link to="/">Home</Link>
17   - </Navbar.Brand>
18   - <Navbar.Toggle />
19   - </Navbar.Header>
20   - <Navbar.Collapse>
21   - { renderNavigation(hasUser) }
22   - </Navbar.Collapse>
23   - </Navbar>
24   -);
  11 +import '/imports/client/assets/css/colors.css';
  12 +export class AppNavigation extends Component {
25 13  
26   -AppNavigation.propTypes = {
27   - hasUser: React.PropTypes.object,
28   -};
  14 + constructor(props) {
  15 + super(props);
  16 + this.state = {
  17 +
  18 + };
  19 + };
  20 +
  21 + onUpdate(key, value) {
  22 + this.setState({[key]: value});
  23 + };
  24 + render() {
  25 + const {user} = this.props.data;
  26 + if(user){
  27 + return(
  28 + <AuthenticatedNavigation
  29 + data = {this.props.data}
  30 + />
  31 + )
  32 + }else{
  33 + return(
  34 + <Navbar>
  35 + <Navbar.Header>
  36 + <Navbar.Brand>
  37 + <Link to="/">Application Name</Link>
  38 + </Navbar.Brand>
  39 + <Navbar.Toggle />
  40 + </Navbar.Header>
  41 + <Navbar.Collapse>
  42 + <PublicNavigation />
  43 + </Navbar.Collapse>
  44 + </Navbar>
29 45  
30   -export default AppNavigation;
  46 + )
  47 + }
  48 +
  49 + return (
  50 + <div></div>
  51 + );
  52 + };
  53 +
  54 +};
... ...
imports/client/views/org/app/module/navigation/AuthenticatedNavigation.js
1   -import React from 'react';
2   -import { browserHistory } from 'react-router';
3   -import { LinkContainer } from 'react-router-bootstrap';
4   -import { Nav, NavItem, NavDropdown, MenuItem } from 'react-bootstrap';
5   -import { Meteor } from 'meteor/meteor';
  1 +import React, { Component } from 'react';
  2 +import { browserHistory } from 'react-router';
  3 +import { LinkContainer } from 'react-router-bootstrap';
  4 +import { Nav, NavItem,
  5 + NavDropdown, MenuItem } from 'react-bootstrap';
  6 +import { Meteor } from 'meteor/meteor';
6 7  
7 8 const handleLogout = () => Meteor.logout(() => browserHistory.push('/login'));
8 9  
9   -const userName = () => {
10   - const user = Meteor.user();
11   - const name = user && user.profile ? user.profile.name : '';
12   - return user ? `${name.first} ${name.last}` : '';
13   -};
  10 +export class AuthenticatedNavigation extends Component {
  11 + constructor(props) {
  12 + super(props);
  13 + this.state = {
14 14  
15   -const AuthenticatedNavigation = () => (
  15 + };
  16 + };
  17 +render(){
  18 + const {user} = this.props.data;
  19 + return(
16 20 <div>
17   - <Nav>
18   - <LinkContainer to="/documents">
19   - <NavItem eventKey={ 2 } href="/documents">Documents</NavItem>
20   - </LinkContainer>
21   - </Nav>
22   - <Nav pullRight>
23   - <NavDropdown eventKey={ 3 } title={ userName() } id="basic-nav-dropdown">
24   - <MenuItem eventKey={ 3.1 } onClick={ handleLogout }>Logout</MenuItem>
25   - </NavDropdown>
26   - </Nav>
  21 + <div className="navbar navbar-inverse bg-crimson">
  22 + <div className="navbar-header">
  23 + <a className="navbar-brand" href="index.html"><span className="glyphicon glyphicon-link"></span>
  24 + <span> YOUNGDESK </span>
  25 + </a>
  26 +
  27 + <ul className="nav navbar-nav visible-xs-block">
  28 + <li><a data-toggle="collapse" data-target="#navbar-mobile"><i className="icon-tree5"></i></a></li>
  29 + <li><a className="sidebar-mobile-main-toggle"><i className="icon-paragraph-justify3"></i></a></li>
  30 + </ul>
  31 + </div>
  32 +
  33 + <div className="navbar-collapse collapse" id="navbar-mobile">
  34 + <ul className="nav navbar-nav">
  35 + <li><a className="sidebar-control sidebar-main-toggle hidden-xs"><i className="icon-paragraph-justify3"></i></a></li>
  36 +
  37 + <li className="dropdown">
  38 + <a href="#" className="dropdown-toggle" data-toggle="dropdown">
  39 + <i className="icon-puzzle3"></i>
  40 + <span className="visible-xs-inline-block position-right">Git updates</span>
  41 + <span className="status-mark border-orange-400"></span>
  42 + </a>
  43 +
  44 + <div className="dropdown-menu dropdown-content">
  45 + <div className="dropdown-content-heading">
  46 + Git updates
  47 + <ul className="icons-list">
  48 + <li><a href="#"><i className="icon-sync"></i></a></li>
  49 + </ul>
  50 + </div>
  51 +
  52 + <ul className="media-list dropdown-content-body width-350">
  53 + <li className="media">
  54 + <div className="media-left">
  55 + <a href="#" className="btn border-primary text-primary btn-flat btn-rounded btn-icon btn-sm"><i className="icon-git-pull-request"></i></a>
  56 + </div>
  57 +
  58 + <div className="media-body">
  59 + Drop the IE <a href="#">specific hacks</a> for temporal inputs
  60 + <div className="media-annotation">4 minutes ago</div>
  61 + </div>
  62 + </li>
  63 +
  64 + <li className="media">
  65 + <div className="media-left">
  66 + <a href="#" className="btn border-warning text-warning btn-flat btn-rounded btn-icon btn-sm"><i className="icon-git-commit"></i></a>
  67 + </div>
  68 +
  69 + <div className="media-body">
  70 + Add full font overrides for popovers and tooltips
  71 + <div className="media-annotation">36 minutes ago</div>
  72 + </div>
  73 + </li>
  74 +
  75 + <li className="media">
  76 + <div className="media-left">
  77 + <a href="#" className="btn border-info text-info btn-flat btn-rounded btn-icon btn-sm"><i className="icon-git-branch"></i></a>
  78 + </div>
  79 +
  80 + <div className="media-body">
  81 + <a href="#">Chris Arney</a> created a new <span className="text-semibold">Design</span> branch
  82 + <div className="media-annotation">2 hours ago</div>
  83 + </div>
  84 + </li>
  85 +
  86 + <li className="media">
  87 + <div className="media-left">
  88 + <a href="#" className="btn border-success text-success btn-flat btn-rounded btn-icon btn-sm"><i className="icon-git-merge"></i></a>
  89 + </div>
  90 +
  91 + <div className="media-body">
  92 + <a href="#">Eugene Kopyov</a> merged <span className="text-semibold">Master</span> and <span className="text-semibold">Dev</span> branches
  93 + <div className="media-annotation">Dec 18, 18:36</div>
  94 + </div>
  95 + </li>
  96 +
  97 + <li className="media">
  98 + <div className="media-left">
  99 + <a href="#" className="btn border-primary text-primary btn-flat btn-rounded btn-icon btn-sm"><i className="icon-git-pull-request"></i></a>
  100 + </div>
  101 +
  102 + <div className="media-body">
  103 + Have Carousel ignore keyboard events
  104 + <div className="media-annotation">Dec 12, 05:46</div>
  105 + </div>
  106 + </li>
  107 + </ul>
  108 +
  109 + <div className="dropdown-content-footer">
  110 + <a href="#" data-popup="tooltip" title="All activity"><i className="icon-menu display-block"></i></a>
  111 + </div>
  112 + </div>
  113 + </li>
  114 + </ul>
  115 +
  116 + <div className="navbar-right">
  117 + <p className="navbar-text">Hello {`${user.firstName} ${user.lastName}`}!</p>
  118 + <p className="navbar-text"><span className="label bg-success-400">Online</span></p>
  119 +
  120 + <ul className="nav navbar-nav">
  121 + <li className="dropdown">
  122 + <a href="#" className="dropdown-toggle" data-toggle="dropdown">
  123 + <i className="icon-bell2"></i>
  124 + <span className="visible-xs-inline-block position-right">Activity</span>
  125 + <span className="status-mark border-orange-400"></span>
  126 + </a>
  127 +
  128 + <div className="dropdown-menu dropdown-content">
  129 + <div className="dropdown-content-heading">
  130 + Activity
  131 + <ul className="icons-list">
  132 + <li><a href="#"><i className="icon-menu7"></i></a></li>
  133 + </ul>
  134 + </div>
  135 +
  136 + <ul className="media-list dropdown-content-body width-350">
  137 + <li className="media">
  138 + <div className="media-left">
  139 + <a href="#" className="btn bg-success-400 btn-rounded btn-icon btn-xs"><i className="icon-mention"></i></a>
  140 + </div>
  141 +
  142 + <div className="media-body">
  143 + <a href="#">Taylor Swift</a> mentioned you in a post "Angular JS. Tips and tricks"
  144 + <div className="media-annotation">4 minutes ago</div>
  145 + </div>
  146 + </li>
  147 +
  148 + <li className="media">
  149 + <div className="media-left">
  150 + <a href="#" className="btn bg-pink-400 btn-rounded btn-icon btn-xs"><i className="icon-paperplane"></i></a>
  151 + </div>
  152 +
  153 + <div className="media-body">
  154 + Special offers have been sent to subscribed users by <a href="#">Donna Gordon</a>
  155 + <div className="media-annotation">36 minutes ago</div>
  156 + </div>
  157 + </li>
  158 +
  159 + <li className="media">
  160 + <div className="media-left">
  161 + <a href="#" className="btn bg-blue btn-rounded btn-icon btn-xs"><i className="icon-plus3"></i></a>
  162 + </div>
  163 +
  164 + <div className="media-body">
  165 + <a href="#">Chris Arney</a> created a new <span className="text-semibold">Design</span> branch in <span className="text-semibold">Limitless</span> repository
  166 + <div className="media-annotation">2 hours ago</div>
  167 + </div>
  168 + </li>
  169 +
  170 + <li className="media">
  171 + <div className="media-left">
  172 + <a href="#" className="btn bg-purple-300 btn-rounded btn-icon btn-xs"><i className="icon-truck"></i></a>
  173 + </div>
  174 +
  175 + <div className="media-body">
  176 + Shipping cost to the Netherlands has been reduced, database updated
  177 + <div className="media-annotation">Feb 8, 11:30</div>
  178 + </div>
  179 + </li>
  180 +
  181 + <li className="media">
  182 + <div className="media-left">
  183 + <a href="#" className="btn bg-warning-400 btn-rounded btn-icon btn-xs"><i className="icon-bubble8"></i></a>
  184 + </div>
  185 +
  186 + <div className="media-body">
  187 + New review received on <a href="#">Server side integration</a> services
  188 + <div className="media-annotation">Feb 2, 10:20</div>
  189 + </div>
  190 + </li>
  191 +
  192 + <li className="media">
  193 + <div className="media-left">
  194 + <a href="#" className="btn bg-teal-400 btn-rounded btn-icon btn-xs"><i className="icon-spinner11"></i></a>
  195 + </div>
  196 +
  197 + <div className="media-body">
  198 + <strong>January, 2016</strong> - 1320 new users, 3284 orders, $49,390 revenue
  199 + <div className="media-annotation">Feb 1, 05:46</div>
  200 + </div>
  201 + </li>
  202 + </ul>
  203 + </div>
  204 + </li>
  205 +
  206 + <li className="dropdown">
  207 + <a href="#" className="dropdown-toggle" data-toggle="dropdown">
  208 + <i className="icon-bubble8"></i>
  209 + <span className="visible-xs-inline-block position-right">Messages</span>
  210 + <span className="status-mark border-orange-400"></span>
  211 + </a>
  212 +
  213 + <div className="dropdown-menu dropdown-content width-350">
  214 + <div className="dropdown-content-heading">
  215 + Messages
  216 + <ul className="icons-list">
  217 + <li><a href="#"><i className="icon-compose"></i></a></li>
  218 + </ul>
  219 + </div>
  220 +
  221 + <ul className="media-list dropdown-content-body">
  222 + <li className="media">
  223 + <div className="media-left">
  224 + <img src="assets/images/placeholder.jpg" className="img-circle img-sm" alt="" />
  225 + <span className="badge bg-danger-400 media-badge">5</span>
  226 + </div>
  227 +
  228 + <div className="media-body">
  229 + <a href="#" className="media-heading">
  230 + <span className="text-semibold">James Alexander</span>
  231 + <span className="media-annotation pull-right">04:58</span>
  232 + </a>
  233 +
  234 + <span className="text-muted">who knows, maybe that would be the best thing for me...</span>
  235 + </div>
  236 + </li>
  237 +
  238 + <li className="media">
  239 + <div className="media-left">
  240 + <img src="assets/images/placeholder.jpg" className="img-circle img-sm" alt="" />
  241 + <span className="badge bg-danger-400 media-badge">4</span>
  242 + </div>
  243 +
  244 + <div className="media-body">
  245 + <a href="#" className="media-heading">
  246 + <span className="text-semibold">Margo Baker</span>
  247 + <span className="media-annotation pull-right">12:16</span>
  248 + </a>
  249 +
  250 + <span className="text-muted">That was something he was unable to do because...</span>
  251 + </div>
  252 + </li>
  253 +
  254 + <li className="media">
  255 + <div className="media-left"><img src="assets/images/placeholder.jpg" className="img-circle img-sm" alt="" /></div>
  256 + <div className="media-body">
  257 + <a href="#" className="media-heading">
  258 + <span className="text-semibold">Jeremy Victorino</span>
  259 + <span className="media-annotation pull-right">22:48</span>
  260 + </a>
  261 +
  262 + <span className="text-muted">But that would be extremely strained and suspicious...</span>
  263 + </div>
  264 + </li>
  265 +
  266 + <li className="media">
  267 + <div className="media-left"><img src="assets/images/placeholder.jpg" className="img-circle img-sm" alt="" /></div>
  268 + <div className="media-body">
  269 + <a href="#" className="media-heading">
  270 + <span className="text-semibold">Beatrix Diaz</span>
  271 + <span className="media-annotation pull-right">Tue</span>
  272 + </a>
  273 +
  274 + <span className="text-muted">What a strenuous career it is that I have chosen...</span>
  275 + </div>
  276 + </li>
  277 +
  278 + <li className="media">
  279 + <div className="media-left"><img src="assets/images/placeholder.jpg" className="img-circle img-sm" alt="" /></div>
  280 + <div className="media-body">
  281 + <a href="#" className="media-heading">
  282 + <span className="text-semibold">Richard Vango</span>
  283 + <span className="media-annotation pull-right">Mon</span>
  284 + </a>
  285 +
  286 + <span className="text-muted">Other travelling salesmen live a life of luxury...</span>
  287 + </div>
  288 + </li>
  289 + </ul>
  290 +
  291 + <div className="dropdown-content-footer">
  292 + <a href="#" data-popup="tooltip" title="All messages"><i className="icon-menu display-block"></i></a>
  293 + </div>
  294 + </div>
  295 + </li>
  296 + </ul>
  297 + </div>
  298 + </div>
  299 + </div>
27 300 </div>
28 301 );
29   -
30   -export default AuthenticatedNavigation;
  302 +}
  303 +}
... ...
imports/client/views/org/app/module/navigation/PublicNavigation.js
1   -import React from 'react';
2   -import { LinkContainer } from 'react-router-bootstrap';
3   -import { Nav, NavItem } from 'react-bootstrap';
  1 +import React, { Component } from 'react';
  2 +import { LinkContainer } from 'react-router-bootstrap';
  3 +import { setQueryParam } from '/imports/client/app/utils/setQueryParam';
  4 +import { browserHistory } from 'react-router';
  5 +import { Nav, NavItem } from 'react-bootstrap';
4 6  
5   -const PublicNavigation = () => (
6   - <Nav pullRight>
7   - <LinkContainer to="signup">
8   - <NavItem eventKey={ 1 } href="/signup">Sign Up</NavItem>
9   - </LinkContainer>
10   - <LinkContainer to="login">
11   - <NavItem eventKey={ 2 } href="/login">Log In</NavItem>
12   - </LinkContainer>
13   - </Nav>
14   -);
15 7  
16   -export default PublicNavigation;
  8 +export class PublicNavigation extends Component {
  9 + constructor(props) {
  10 + super(props);
  11 + this.state = {
  12 +
  13 + };
  14 + };
  15 +render(){
  16 + console.log(this.props);
  17 + var mainSite = document.location.hostname.split( "." )[1];
  18 + var signup = `http://${mainSite}/signup`;
  19 + return(
  20 + <Nav pullRight>
  21 + <LinkContainer to={ setQueryParam(this.props.location, { enter: 'login' }) }>
  22 + <NavItem eventKey={ 2 } href="/login">Log In</NavItem>
  23 + </LinkContainer>
  24 + </Nav>
  25 + )
  26 +}
  27 +}
... ...
imports/client/views/org/app/module/navigation/index.js
1   -import { composeWithTracker } from 'react-komposer';
2   -import { Meteor } from 'meteor/meteor';
3   -import AppNavigation from '../components/AppNavigation.js';
  1 +// import { InviteSignupController } from '/imports/client/views/invite/signup/index'
  2 +import _ from 'lodash';
  3 +import {
  4 + composeWithTracker,
  5 + compose,
  6 + composeAll
  7 + } from 'react-komposer';
  8 +import { Loading } from '/imports/client/components/Loading';
4 9  
5   -const composer = (props, onData) => onData(null, { hasUser: Meteor.user() });
  10 +import { Orgs } from '/imports/collections/orgs/index';
  11 +import { Users } from '/imports/collections/users/index';
6 12  
7   -export default composeWithTracker(composer, {}, {}, { pure: false })(AppNavigation);
  13 +import { AppNavigation } from './AppNavigation';
  14 +
  15 +const meteorTick = (props, onData) => {
  16 +
  17 + const handles = [
  18 + Meteor.subscribe('users.current')
  19 + ];
  20 +
  21 + if(_.every(handles, (handle) => (handle.ready()) )) {
  22 + const user = Users.current();
  23 + onData(null, {
  24 + data: {
  25 + user: user,
  26 + },
  27 + });
  28 + }
  29 +
  30 + return () => {
  31 + _.each(handles, (handle) => handle.stop() );
  32 + };
  33 +};
  34 +
  35 +
  36 +const reduxTick = (props, onData) => {
  37 + onData(null, {
  38 + data: {}
  39 + });
  40 +};
  41 +
  42 +
  43 +export const AppNavigationController = composeAll(
  44 + composeWithTracker(meteorTick, Loading),
  45 + compose(reduxTick, Loading),
  46 +)(AppNavigation);
... ...
imports/client/views/org/enter/ForgotPane.js
... ... @@ -0,0 +1,69 @@
  1 +import React from 'react';
  2 +import { Link } from 'react-router';
  3 +import { ActionButton } from '/imports/client/components/ActionButton';
  4 +import { setQueryParam } from '/imports/client/app/utils/setQueryParam';
  5 +import { Button, Form, FormGroup, Label, Input, FormText, Col } from 'reactstrap';
  6 +import { Alert } from 'reactstrap';
  7 +import 'velocity-animate';
  8 +import 'velocity-animate/velocity.ui';
  9 +import { VelocityComponent, VelocityTransitionGroup, velocityHelpers } from 'velocity-react';
  10 +import ReactSVG from 'react-svg'
  11 +
  12 +export class ForgotPane extends React.Component {
  13 +
  14 + render() {
  15 + return (
  16 + <Col lg={{ size: 6, pull: 2, push: 2, offset: 1 }} md={{ size: 9, pull: 1, push: 1 }} sm={{ size: 10, push: 1, pull: 1 }}>
  17 + <VelocityTransitionGroup enter={{ animation: "transition.slideRightIn"}} leave={{animation: "transition.fadeOut" }} runOnMount={true}>
  18 + <div className="enterPane__centerVerticallyWrapper">
  19 + <Link
  20 + to={ setQueryParam(this.props.location, { enter: 'login' }) }>
  21 + <ReactSVG
  22 + path="/files/images/svg/logo2--white.svg"
  23 + className="enterPane__logo"
  24 + />
  25 + </Link>
  26 + <div className="enterPane__box">
  27 + <div className="enterPane__header">
  28 + Reset your password
  29 + </div>
  30 + <Alert color="danger" isOpen={ this.props.data.error.length !== 0 }>
  31 + { this.props.data.error }
  32 + </Alert>
  33 + <Alert color="success" isOpen={ this.props.data.message.length !== 0 }>
  34 + { this.props.data.message }
  35 + </Alert>
  36 + <Form onSubmit={ (e) => this.props.onForgot(e) }>
  37 + <FormGroup>
  38 + <Input type="email" size="lg"
  39 + value={ this.props.data.email }
  40 + onChange={ (e) => this.props.onUpdate('email', e.currentTarget.value) }
  41 + name="email"
  42 + id="email"
  43 + placeholder="Enter your email" />
  44 + </FormGroup>
  45 + <FormGroup>
  46 + <ActionButton
  47 + loading={ this.props.data.loading }>
  48 + <Button
  49 + color="success" size="lg"
  50 + type="submit"
  51 + block>
  52 + Reset password
  53 + </Button>
  54 + </ActionButton>
  55 + </FormGroup>
  56 + <FormGroup className="enterPane__formGroup--center">
  57 + <Link className="enterPane__link"
  58 + to={ setQueryParam(this.props.location, { enter: 'login' }) }>
  59 + Log In
  60 + </Link>
  61 + </FormGroup>
  62 + </Form>
  63 + </div>
  64 + </div>
  65 + </VelocityTransitionGroup>
  66 + </Col>
  67 + );
  68 + }
  69 +}
... ...
imports/client/views/org/enter/LoginPane.js
... ... @@ -0,0 +1,78 @@
  1 +import React from 'react';
  2 +import { Link } from 'react-router';
  3 +import { ActionButton } from '/imports/client/components/ActionButton';
  4 +import { setQueryParam } from '/imports/client/app/utils/setQueryParam';
  5 +import { Button, Form, FormGroup, Label, Input, FormText, Col } from 'reactstrap';
  6 +import { Alert } from 'reactstrap';
  7 +import 'velocity-animate';
  8 +import 'velocity-animate/velocity.ui';
  9 +import { VelocityComponent, VelocityTransitionGroup, velocityHelpers } from 'velocity-react';
  10 +import ReactSVG from 'react-svg'
  11 +
  12 +
  13 +export class LoginPane extends React.Component {
  14 +
  15 + render() {
  16 + return (
  17 + <Col lg={{ size: 6, pull: 2, push: 2, offset: 1 }} md={{ size: 9, pull: 1, push: 1 }} sm={{ size: 10, push: 1, pull: 1 }}>
  18 + <VelocityTransitionGroup enter={{ animation: "transition.slideRightIn"}} leave={{animation: "transition.fadeOut" }} runOnMount={true}>
  19 + <div className="enterPane__centerVerticallyWrapper">
  20 + <Link
  21 + to={ setQueryParam(this.props.location, { enter: 'login' }) }>
  22 + <ReactSVG
  23 + path="/files/images/svg/logo2--white.svg"
  24 + className="enterPane__logo"
  25 + />
  26 + </Link>
  27 + <div className="enterPane__box">
  28 + <div className="enterPane__header">
  29 + Login
  30 + </div>
  31 + <Alert color="danger" isOpen={ this.props.data.visible || this.props.data.error.length !== 0 }>
  32 + { this.props.data.error }
  33 + </Alert>
  34 + <Alert color="success" isOpen={ this.props.data.visible || this.props.data.message.length !== 0 }>
  35 + { this.props.data.message }
  36 + </Alert>
  37 + <Form onSubmit={ (e) => this.props.onLogin(e) }>
  38 + <FormGroup>
  39 + <Input type="email" size="lg"
  40 + value={ this.props.data.email }
  41 + onChange={ (e) => this.props.onUpdate('email', e.currentTarget.value) }
  42 + name="email"
  43 + id="email"
  44 + placeholder="Enter your email" />
  45 + </FormGroup>
  46 + <FormGroup>
  47 + <Input type="password" size="lg"
  48 + value={ this.props.data.password }
  49 + onChange={ (e) => this.props.onUpdate('password', e.currentTarget.value) }
  50 + name="password"
  51 + id="password"
  52 + placeholder="Enter your password" />
  53 + </FormGroup>
  54 + <FormGroup>
  55 + <ActionButton
  56 + loading={ this.props.data.loading }>
  57 + <Button
  58 + color="success" size="lg"
  59 + type="submit"
  60 + block>
  61 + Log in
  62 + </Button>
  63 + </ActionButton>
  64 + </FormGroup>
  65 + <FormGroup className="enterPane__formGroupLogin--center">
  66 + <Link className="enterPane__link"
  67 + to={ setQueryParam(this.props.location, { enter: 'forgot' }) }>
  68 + Forgotten password?
  69 + </Link>
  70 + </FormGroup>
  71 + </Form>
  72 + </div>
  73 + </div>
  74 + </VelocityTransitionGroup>
  75 + </Col>
  76 + );
  77 + }
  78 +}
... ...
imports/client/views/org/enter/RecoverPassword.js
... ... @@ -1,43 +0,0 @@
1   -import React from 'react';
2   -import { Row, Col, Alert, FormGroup, FormControl, Button } from 'react-bootstrap';
3   -import handleRecoverPassword from './module/recover-password';
4   -
5   -export default class RecoverPassword extends React.Component {
6   - componentDidMount() {
7   - handleRecoverPassword({ component: this });
8   - }
9   -
10   - handleSubmit(event) {
11   - event.preventDefault();
12   - }
13   -
14   - render() {
15   - return (
16   - <div className="RecoverPassword">
17   - <Row>
18   - <Col xs={ 12 } sm={ 6 } md={ 4 }>
19   - <h4 className="page-header">Recover Password</h4>
20   - <Alert bsStyle="info">
21   - Enter your email address below to receive a link to reset your password.
22   - </Alert>
23   - <form
24   - ref={ form => (this.recoverPasswordForm = form) }
25   - className="recover-password"
26   - onSubmit={ this.handleSubmit }
27   - >
28   - <FormGroup>
29   - <FormControl
30   - type="email"
31   - ref="emailAddress"
32   - name="emailAddress"
33   - placeholder="Email Address"
34   - />
35   - </FormGroup>
36   - <Button type="submit" bsStyle="success">Recover Password</Button>
37   - </form>
38   - </Col>
39   - </Row>
40   - </div>
41   - );
42   - }
43   -}
imports/client/views/org/enter/ResetPane.js
... ... @@ -0,0 +1,63 @@
  1 +import React from 'react';
  2 +import { Link } from 'react-router';
  3 +import { ActionButton } from '/imports/client/components/ActionButton';
  4 +import { setQueryParam } from '/imports/client/app/utils/setQueryParam';
  5 +import { Button, Form, FormGroup, Label, Input, FormText, Col } from 'reactstrap';
  6 +import { Alert } from 'reactstrap';
  7 +import 'velocity-animate';
  8 +import 'velocity-animate/velocity.ui';
  9 +import { VelocityComponent, VelocityTransitionGroup, velocityHelpers } from 'velocity-react';
  10 +import ReactSVG from 'react-svg'
  11 +
  12 +
  13 +export class ResetPane extends React.Component {
  14 +
  15 + render() {
  16 + return (
  17 + <Col lg={{ size: 6, pull: 2, push: 2, offset: 1 }} md={{ size: 9, pull: 1, push: 1 }} sm={{ size: 10, push: 1, pull: 1 }}>
  18 + <VelocityTransitionGroup enter={{ animation: "transition.slideRightIn"}} leave={{animation: "transition.fadeOut" }} runOnMount={true}>
  19 + <div className="enterPane__centerVerticallyWrapper">
  20 + <Link
  21 + to={ setQueryParam(this.props.location, { enter: 'login' }) }>
  22 + <ReactSVG
  23 + path="/files/images/svg/logo2--white.svg"
  24 + className="enterPane__logo"
  25 + />
  26 + </Link>
  27 + <div className = "enterPane__box">
  28 + <div className = "enterPane__header">
  29 + Set new password
  30 + </div>
  31 + <Alert color="danger" isOpen={ this.props.data.error.length !== 0 }>
  32 + { this.props.data.error }
  33 + </Alert>
  34 + <Alert color="success" isOpen={ this.props.data.message.length !== 0 }>
  35 + { this.props.data.message }
  36 + </Alert>
  37 + <Form onSubmit={ (e) => this.props.onReset(e) }>
  38 + <FormGroup>
  39 + <Input type="password"
  40 + value={ this.props.data.password }
  41 + onChange={ (e) => this.props.onUpdate('password', e.currentTarget.value) }
  42 + name="password"
  43 + id="password"
  44 + placeholder="Set new password" />
  45 + </FormGroup>
  46 + <FormGroup>
  47 + <ActionButton
  48 + loading={ this.props.data.loading }>
  49 + <Button color="success" size="lg"
  50 + type="submit"
  51 + block>
  52 + Reset password
  53 + </Button>
  54 + </ActionButton>
  55 + </FormGroup>
  56 + </Form>
  57 + </div>
  58 + </div>
  59 + </VelocityTransitionGroup>
  60 + </Col>
  61 + );
  62 + }
  63 +}
... ...
imports/client/views/org/enter/ResetPassword.js
... ... @@ -1,58 +0,0 @@
1   -import React from 'react';
2   -import { Row, Col, Alert, FormGroup, ControlLabel, FormControl, Button } from 'react-bootstrap';
3   -import handleResetPassword from './module/reset-password';
4   -
5   -export default class ResetPassword extends React.Component {
6   - componentDidMount() {
7   - handleResetPassword({ component: this, token: this.props.params.token });
8   - }
9   -
10   - handleSubmit(event) {
11   - event.preventDefault();
12   - }
13   -
14   - render() {
15   - return (
16   - <div className="ResetPassword">
17   - <Row>
18   - <Col xs={ 12 } sm={ 6 } md={ 4 }>
19   - <h4 className="page-header">Reset Password</h4>
20   - <Alert bsStyle="info">
21   - To reset your password, enter a new one below. You will be logged in
22   - with your new password.
23   - </Alert>
24   - <form
25   - ref={ form => (this.resetPasswordForm = form) }
26   - className="reset-password"
27   - onSubmit={ this.handleSubmit }
28   - >
29   - <FormGroup>
30   - <ControlLabel>New Password</ControlLabel>
31   - <FormControl
32   - type="password"
33   - ref="newPassword"
34   - name="newPassword"
35   - placeholder="New Password"
36   - />
37   - </FormGroup>
38   - <FormGroup>
39   - <ControlLabel>Repeat New Password</ControlLabel>
40   - <FormControl
41   - type="password"
42   - ref="repeatNewPassword"
43   - name="repeatNewPassword"
44   - placeholder="Repeat New Password"
45   - />
46   - </FormGroup>
47   - <Button type="submit" bsStyle="success">Reset Password &amp; Login</Button>
48   - </form>
49   - </Col>
50   - </Row>
51   - </div>
52   - );
53   - }
54   -}
55   -
56   -ResetPassword.propTypes = {
57   - params: React.PropTypes.object,
58   -};
imports/client/views/org/enter/SignupPane.js
... ... @@ -0,0 +1,91 @@
  1 +import React from 'react';
  2 +import { Link } from 'react-router';
  3 +import { ActionButton } from '/imports/client/components/ActionButton';
  4 +import { setQueryParam } from '/imports/client/app/utils/setQueryParam';
  5 +import { Button, Form, FormGroup, Label, Input, FormText, Col } from 'reactstrap';
  6 +import 'velocity-animate';
  7 +import 'velocity-animate/velocity.ui';
  8 +import { VelocityComponent, VelocityTransitionGroup, velocityHelpers } from 'velocity-react';
  9 +import ReactSVG from 'react-svg'
  10 +
  11 +
  12 +export class SignupPane extends React.Component {
  13 +
  14 + render() {
  15 + return (
  16 + <Col lg={{ size: 6, pull: 2, push: 2, offset: 1 }} md={{ size: 9, pull: 1, push: 1 }} sm={{ size: 10, push: 1, pull: 1 }}>
  17 + <VelocityTransitionGroup enter={{animation: "transition.slideRightIn"}} leave={{animation: "transition.fadeOut"}} runOnMount={true}>
  18 + <div className="enterPane__centerVerticallyWrapper">
  19 + <Link
  20 + to={ setQueryParam(this.props.location, { enter: 'login' }) }>
  21 + <ReactSVG
  22 + path="/files/images/svg/logo--white.svg"
  23 + className="enterPane__logo"
  24 + />
  25 + </Link>
  26 + <div className = "enterPane__box">
  27 + <div className = "enterPane__header">
  28 + Start a free 30-day trial
  29 + </div>
  30 + <Form onSubmit={ (e) => this.props.onSignup(e) }>
  31 + <FormGroup>
  32 + <Input type="text" size="lg"
  33 + value={ this.props.data.firstName }
  34 + onChange={ (e) => this.props.onUpdate('firstName', e.currentTarget.value) }
  35 + name="firstname"
  36 + id="firstname"
  37 + placeholder="Enter your first name" />
  38 + </FormGroup>
  39 + <FormGroup>
  40 + <Input type="text" size="lg"
  41 + value={ this.props.data.lastName }
  42 + onChange={ (e) => this.props.onUpdate('lastName', e.currentTarget.value) }
  43 + name="lastname"
  44 + id="lastname"
  45 + placeholder="Enter your last name" />
  46 + </FormGroup>
  47 + <FormGroup>
  48 + <Input type="text" size="lg"
  49 + value={ this.props.data.orgName }
  50 + onChange={ (e) => this.props.onUpdate('orgName', e.currentTarget.value) }
  51 + name="companyname"
  52 + id="companyname"
  53 + placeholder="Enter your company name" />
  54 + </FormGroup>
  55 + <FormGroup>
  56 + <Input type="email" size="lg"
  57 + value={ this.props.data.email }
  58 + onChange={ (e) => this.props.onUpdate('email', e.currentTarget.value) }
  59 + name="email"
  60 + id="email"
  61 + placeholder="Enter your email address" />
  62 + </FormGroup>
  63 + <FormGroup>
  64 + <Input type="password" size="lg"
  65 + value={ this.props.data.password }
  66 + onChange={ (e) => this.props.onUpdate('password', e.currentTarget.value) }
  67 + name="password"
  68 + id="password"
  69 + placeholder="Your password" />
  70 + </FormGroup>
  71 + <FormGroup>
  72 + <Button color="success" size="lg"
  73 + type="submit"
  74 + block>
  75 + Create Account
  76 + </Button>
  77 + </FormGroup>
  78 + </Form>
  79 + </div>
  80 + <FormGroup className="enterPane__formGroup--center">
  81 + <Link className="enterPane__login"
  82 + to={ setQueryParam(this.props.location, { enter: 'login' }) }>
  83 + Already have an account? Log in.
  84 + </Link>
  85 + </FormGroup>
  86 + </div>
  87 + </VelocityTransitionGroup>
  88 + </Col>
  89 + );
  90 + }
  91 +}
... ...
imports/client/views/org/enter/_old/check-email.js
... ... @@ -0,0 +1,24 @@
  1 +import React from 'react';
  2 +import { Link } from 'react-router';
  3 +import { Row, Col } from 'react-bootstrap';
  4 +
  5 +export class CheckEmail extends React.Component {
  6 + handleSubmit(event) {
  7 + event.preventDefault();
  8 + }
  9 +
  10 + render() {
  11 + const signedEmail = Session.get('signedEmail', '');
  12 +
  13 + return <Row>
  14 + <Col xs={ 12 } sm={ 12 } md={ 12 }>
  15 + <div className="check-email" >
  16 + <img src="/email_verify.png" width="100" />
  17 + <h1>Verify Your Email</h1>
  18 + <h4 className="page-header">We sent a verification email to <b>{signedEmail}</b>. Click the link in the email to get started!</h4>
  19 + <Link to='/'>Email did not arrive or want to use a different email?</Link>
  20 + </div>
  21 + </Col>
  22 + </Row>;
  23 + }
  24 +}
... ...
imports/client/views/org/enter/_old/recover-password.js
... ... @@ -0,0 +1,35 @@
  1 +import React from 'react';
  2 +import { Row, Col, Alert, FormGroup, FormControl, Button } from 'react-bootstrap';
  3 +import { handleRecoverPassword } from '../../modules/recover-password';
  4 +
  5 +export class RecoverPassword extends React.Component {
  6 + componentDidMount() {
  7 + handleRecoverPassword({ component: this });
  8 + }
  9 +
  10 + handleSubmit(event) {
  11 + event.preventDefault();
  12 + }
  13 +
  14 + render() {
  15 + return <Row>
  16 + <Col xs={ 12 } sm={ 6 } md={ 4 }>
  17 + <h4 className="page-header">Recover Password</h4>
  18 + <Alert bsStyle="info">
  19 + Enter your email address below to receive a link to reset your password.
  20 + </Alert>
  21 + <form ref="recoverPassword" className="recover-password" onSubmit={ this.handleSubmit }>
  22 + <FormGroup>
  23 + <FormControl
  24 + type="email"
  25 + ref="emailAddress"
  26 + name="emailAddress"
  27 + placeholder="Email Address"
  28 + />
  29 + </FormGroup>
  30 + <Button type="submit" bsStyle="success">Recover Password</Button>
  31 + </form>
  32 + </Col>
  33 + </Row>;
  34 + }
  35 +}
... ...
imports/client/views/org/enter/_old/reset-password.js
... ... @@ -0,0 +1,53 @@
  1 +import React from 'react';
  2 +import { Row, Col, Alert, FormGroup, ControlLabel, FormControl, Button } from 'react-bootstrap';
  3 +import { handleResetPassword } from '../../modules/reset-password';
  4 +
  5 +export class ResetPassword extends React.Component {
  6 + componentDidMount() {
  7 + handleResetPassword({
  8 + component: this,
  9 + token: this.props.params.token,
  10 + });
  11 + }
  12 +
  13 + handleSubmit(event) {
  14 + event.preventDefault();
  15 + }
  16 +
  17 + render() {
  18 + return <Row>
  19 + <Col xs={ 12 } sm={ 6 } md={ 4 }>
  20 + <h4 className="page-header">Reset Password</h4>
  21 + <Alert bsStyle="info">
  22 + To reset your password, enter a new one below. You will be logged in
  23 +with your new password.
  24 + </Alert>
  25 + <form ref="resetPassword" className="reset-password" onSubmit={ this.handleSubmit }>
  26 + <FormGroup>
  27 + <ControlLabel>New Password</ControlLabel>
  28 + <FormControl
  29 + type="password"
  30 + ref="newPassword"
  31 + name="newPassword"
  32 + placeholder="New Password"
  33 + />
  34 + </FormGroup>
  35 + <FormGroup>
  36 + <ControlLabel>Repeat New Password</ControlLabel>
  37 + <FormControl
  38 + type="password"
  39 + ref="repeatNewPassword"
  40 + name="repeatNewPassword"
  41 + placeholder="Repeat New Password"
  42 + />
  43 + </FormGroup>
  44 + <Button type="submit" bsStyle="success">Reset Password &amp; Login</Button>
  45 + </form>
  46 + </Col>
  47 + </Row>;
  48 + }
  49 +}
  50 +
  51 +ResetPassword.propTypes = {
  52 + params: React.PropTypes.object,
  53 +};
... ...
imports/client/views/org/enter/_old/signup.js
... ... @@ -0,0 +1,68 @@
  1 +import React from 'react';
  2 +import { Link } from 'react-router';
  3 +import { Row, Col, FormGroup, ControlLabel, FormControl, Button } from 'react-bootstrap';
  4 +import { handleSignup } from '../../modules/signup';
  5 +
  6 +export class Signup extends React.Component {
  7 + componentDidMount() {
  8 + handleSignup({ component: this });
  9 + }
  10 +
  11 + handleSubmit(event) {
  12 + event.preventDefault();
  13 + }
  14 +
  15 + render() {
  16 + return <Row>
  17 + <Col xs={ 12 } sm={ 6 } md={ 4 }>
  18 + <h4 className="page-header">Sign Up</h4>
  19 + <form ref="signup" className="signup" onSubmit={ this.handleSubmit }>
  20 + <Row>
  21 + <Col xs={ 6 } sm={ 6 }>
  22 + <FormGroup>
  23 + <ControlLabel>First Name</ControlLabel>
  24 + <FormControl
  25 + type="text"
  26 + ref="firstName"
  27 + name="firstName"
  28 + placeholder="First Name"
  29 + />
  30 + </FormGroup>
  31 + </Col>
  32 + <Col xs={ 6 } sm={ 6 }>
  33 + <FormGroup>
  34 + <ControlLabel>Last Name</ControlLabel>
  35 + <FormControl
  36 + type="text"
  37 + ref="lastName"
  38 + name="lastName"
  39 + placeholder="Last Name"
  40 + />
  41 + </FormGroup>
  42 + </Col>
  43 + </Row>
  44 + <FormGroup>
  45 + <ControlLabel>Email Address</ControlLabel>
  46 + <FormControl
  47 + type="text"
  48 + ref="emailAddress"
  49 + name="emailAddress"
  50 + placeholder="Email Address"
  51 + />
  52 + </FormGroup>
  53 + <FormGroup>
  54 + <ControlLabel>Password</ControlLabel>
  55 + <FormControl
  56 + type="password"
  57 + ref="password"
  58 + name="password"
  59 + placeholder="Password"
  60 + />
  61 + </FormGroup>
  62 + <Button type="submit" bsStyle="success">Sign Up</Button>
  63 + </form>
  64 + <p>Already have an account? <Link to="/login">Log In</Link>.</p>
  65 + </Col>
  66 + </Row>;
  67 + }
  68 +}
... ...
imports/client/views/org/enter/login/LoginView.js
... ... @@ -1,56 +0,0 @@
1   -import React,{ Component } from 'react';
2   -import { Link } from 'react-router';
3   -import { Row, Col, FormGroup,
4   - ControlLabel, FormControl,
5   - Button } from 'react-bootstrap';
6   -import handleLogin from './login';
7   -
8   -export class LoginView extends React.Component {
9   - componentDidMount() {
10   - handleLogin({ component: this });
11   - }
12   -
13   - handleSubmit(event) {
14   - event.preventDefault();
15   - }
16   -
17   - render() {
18   - return (
19   - <div className="Login">
20   - <Row>
21   - <Col xs={ 12 } sm={ 6 } md={ 4 }>
22   - <h4 className="page-header">Login</h4>
23   - <form
24   - ref={ form => (this.loginForm = form) }
25   - className="login"
26   - onSubmit={ this.handleSubmit }
27   - >
28   - <FormGroup>
29   - <ControlLabel>Email Address</ControlLabel>
30   - <FormControl
31   - type="email"
32   - ref="emailAddress"
33   - name="emailAddress"
34   - placeholder="Email Address"
35   - />
36   - </FormGroup>
37   - <FormGroup>
38   - <ControlLabel>
39   - <span className="pull-left">Password</span>
40   - <Link className="pull-right" to="/recover-password">Forgot Password?</Link>
41   - </ControlLabel>
42   - <FormControl
43   - type="password"
44   - ref="password"
45   - name="password"
46   - placeholder="Password"
47   - />
48   - </FormGroup>
49   - <Button type="submit" bsStyle="success">Login</Button>
50   - </form>
51   - </Col>
52   - </Row>
53   - </div>
54   - );
55   - }
56   -}
imports/client/views/org/enter/login/index.js
... ... @@ -1,40 +0,0 @@
1   -// import { InviteSignupController } from '/imports/client/views/invite/signup/index'
2   -import _ from 'lodash';
3   -import {
4   - composeWithTracker,
5   - compose,
6   - composeAll
7   - } from 'react-komposer';
8   -import { Loading } from '/imports/client/components/Loading';
9   -import { Orgs } from '/imports/collections/orgs/index';
10   -import { LoginView } from './LoginView'
11   -
12   -const meteorTick = (props, onData) => {
13   -
14   - const handles = [
15   - ];
16   -
17   - if(_.every(handles, (handle) => (handle.ready()) )) {
18   - onData(null, {
19   - data: {
20   - },
21   - });
22   - }
23   -
24   - return () => {
25   - _.each(handles, (handle) => handle.stop() );
26   - };
27   -};
28   -
29   -
30   -const reduxTick = (props, onData) => {
31   - onData(null, {
32   - data: {}
33   - });
34   -};
35   -
36   -
37   -export const orgLoginController = composeAll(
38   - composeWithTracker(meteorTick, Loading),
39   - compose(reduxTick, Loading),
40   -)(LoginView);
imports/client/views/org/enter/login/login.js
... ... @@ -1,57 +0,0 @@
1   -/* eslint-disable no-undef */
2   -
3   -import { browserHistory } from 'react-router';
4   -import { Meteor } from 'meteor/meteor';
5   -import { Bert } from 'meteor/themeteorchef:bert';
6   -import '/imports/client/components/validation';
7   -
8   -let component;
9   -
10   -const login = () => {
11   - const email = document.querySelector('[name="emailAddress"]').value;
12   - const password = document.querySelector('[name="password"]').value;
13   -
14   - Meteor.loginWithPassword(email, password, (error) => {
15   - if (error) {
16   - Bert.alert(error.reason, 'warning');
17   - } else {
18   - Bert.alert('Logged in!', 'success');
19   -
20   - const { location } = component.props;
21   - if (location.state && location.state.nextPathname) {
22   - browserHistory.push(location.state.nextPathname);
23   - } else {
24   - browserHistory.push('/');
25   - }
26   - }
27   - });
28   -};
29   -
30   -const validate = () => {
31   - $(component.loginForm).validate({
32   - rules: {
33   - emailAddress: {
34   - required: true,
35   - email: true,
36   - },
37   - password: {
38   - required: true,
39   - },
40   - },
41   - messages: {
42   - emailAddress: {
43   - required: 'Need an email address here.',
44   - email: 'Is this email address legit?',
45   - },
46   - password: {
47   - required: 'Need a password here.',
48   - },
49   - },
50   - submitHandler() { login(); },
51   - });
52   -};
53   -
54   -export default function handleLogin(options) {
55   - component = options.component;
56   - validate();
57   -}
imports/client/views/org/enter/module/EnterLayout.js
... ... @@ -0,0 +1,166 @@
  1 +import _ from 'lodash';
  2 +import { Meteor } from 'meteor/meteor';
  3 +import { SimpleSchema } from 'meteor/aldeed:simple-schema';
  4 +import { Bert } from 'meteor/themeteorchef:bert';
  5 +
  6 +import React from 'react';
  7 +import { Container, Row, Col } from 'reactstrap';
  8 +import { Link } from 'react-router';
  9 +import { If, Case } from '/imports/client/components/Logic';
  10 +
  11 +import { LoginPane } from '/imports/client/views/org/enter/LoginPane';
  12 +import { ForgotPane } from '/imports/client/views/org/enter/ForgotPane';
  13 +import { ResetPane } from '/imports/client/views/org/enter/ResetPane';
  14 +import Validation from '/imports/validation/validationMethods';
  15 +
  16 +// const signupSchemaValidator = new SimpleSchema({
  17 +// email: { type: String, regEx: SimpleSchema.RegEx.Email},
  18 +// firstName: { type: String, },
  19 +// lastName: { type: String, },
  20 +// password: { type: String, min: 6},
  21 +// }).validator();
  22 +
  23 +let validation = new Validation();
  24 +
  25 +export class EnterLayout extends React.Component {
  26 +
  27 +
  28 + constructor(props) {
  29 + super(props);
  30 + this.state = {
  31 + email: '',
  32 + password: '',
  33 + firstName: '',
  34 + lastName: '',
  35 + orgName: '',
  36 + loading: false,
  37 + error: '',
  38 + message: '',
  39 + };
  40 + };
  41 +
  42 + componentWillReceiveProps(nextProps) {
  43 + console.log(this.props.pane);
  44 + console.log("this.props.pane");
  45 + if(this.props.pane !== nextProps.pane) {
  46 + this.onClearState();
  47 + }
  48 + };
  49 +
  50 +
  51 + onClearState(loading) {
  52 + this.setState({
  53 + loading: loading || false,
  54 + error: '',
  55 + message: '',
  56 + });
  57 + };
  58 +
  59 + onUpdate(key, value) {
  60 + this.setState({[key]: value});
  61 + };
  62 +
  63 + onForgot(e) {
  64 + e.preventDefault();
  65 + this.onClearState(true);
  66 +
  67 + Accounts.forgotPassword({email: this.state.email || '...'}, (error) => {
  68 + this.onClearState();
  69 + if(error) {
  70 + this.setState({error: 'An error occured.'});
  71 + } else {
  72 + this.setState({message: 'Reset email has been sent. Check your inbox.'});
  73 + }
  74 + });
  75 + };
  76 +
  77 + onReset(e) {
  78 + e.preventDefault();
  79 + this.onClearState(true);
  80 + if(!validation.passwordValidation(this.state.password)){
  81 + Bert.alert('Password must be a minimum of 6 characters in length.', 'danger');
  82 + this.onClearState();
  83 + return false;
  84 + }else if(!validation.noQwertysAllowed(this.state.password)){
  85 + Bert.alert('No qwertys allowed!', 'danger');
  86 + this.onClearState();
  87 + return false;
  88 + } else{
  89 + Accounts.resetPassword(
  90 + this.props.location.query.token,
  91 + this.state.password,
  92 + (error) => {
  93 + this.onClearState();
  94 + if(error) {
  95 + this.setState({error: 'An error occured.'});
  96 + }
  97 + }
  98 + );
  99 + }
  100 + };
  101 +
  102 + onLogin(e) {
  103 + e.preventDefault();
  104 + this.onClearState(true);
  105 + if(this.state.email.trim() == '' || !validation.validateEmail(this.state.email)){
  106 + this.onClearState();
  107 + Bert.alert('Please enter a valid email address!', 'danger');
  108 + return false;
  109 + }
  110 + if(this.state.password.trim() == ''){
  111 + Bert.alert('Please enter your password!', 'danger');
  112 + this.onClearState();
  113 + return false;
  114 + }
  115 + Meteor.loginWithPassword(
  116 + this.state.email,
  117 + this.state.password,
  118 + (e, r) => {
  119 + this.onClearState();
  120 + if(e) {
  121 + this.setState({error: 'Wrong email or password.'})
  122 + }
  123 + }
  124 + );
  125 + };
  126 +
  127 + render() {
  128 + return (
  129 + <div className = "enterLayout__content--bg">
  130 + <Container>
  131 + <Row>
  132 + <Case
  133 + switch = {this.props.pane}
  134 + case0 = "forgot"
  135 + then0 = {() => (
  136 + <ForgotPane
  137 + data = {this.state}
  138 + location = {this.props.location}
  139 + onUpdate = {(...a) => this.onUpdate(...a)}
  140 + onForgot = {(...a) => this.onForgot(...a)}
  141 + />
  142 + )}
  143 + case1 = "reset"
  144 + then1 = {() => (
  145 + <ResetPane
  146 + data = {this.state}
  147 + location = {this.props.location}
  148 + onUpdate = {(...a) => this.onUpdate(...a)}
  149 + onReset = {(...a) => this.onReset(...a)}
  150 + />
  151 + )}
  152 + else = {() => (
  153 + <LoginPane
  154 + data = {this.state}
  155 + location = {this.props.location}
  156 + onUpdate = {(...a) => this.onUpdate(...a)}
  157 + onLogin = {(...a) => this.onLogin(...a)}
  158 + />
  159 + )}
  160 + />
  161 + </Row>
  162 + </Container>
  163 + </div>
  164 + );
  165 + };
  166 +};
... ...
imports/client/views/org/enter/module/index.js
... ... @@ -0,0 +1,46 @@
  1 +// import { EnterModule } from '/imports/client/views/enter/module/index'
  2 +import {
  3 + composeWithTracker,
  4 + compose,
  5 + composeAll
  6 + } from 'react-komposer';
  7 +import { EnterLayout } from './EnterLayout';
  8 +import { Loading } from '/imports/client/components/Loading';
  9 +
  10 +
  11 +const meteorTick = (props, onData) => {
  12 +
  13 + const handles = [
  14 + ];
  15 +
  16 + if(_.every(handles, (handle) => (handle.ready()) )) {
  17 + onData(null, {
  18 + location: props.location,
  19 + data: {
  20 + },
  21 + });
  22 + }
  23 +
  24 + return () => {
  25 + _.each(handles, (handle) => handle.stop() );
  26 + };
  27 +};
  28 +
  29 +
  30 +const reduxTick = (props, onData) => {
  31 + onData(null, {
  32 + data: {}
  33 + });
  34 +};
  35 +
  36 +
  37 +export const EnterModule = composeAll(
  38 + composeWithTracker(meteorTick, Loading),
  39 + compose(reduxTick, Loading),
  40 +)(EnterLayout);
  41 +
  42 +
  43 +
  44 +
  45 +
  46 +
... ...
imports/client/views/org/enter/module/recover-password.js
... ... @@ -1,42 +0,0 @@
1   -/* eslint-disable no-undef */
2   -
3   -import { Accounts } from 'meteor/accounts-base';
4   -import { Bert } from 'meteor/themeteorchef:bert';
5   -import '/imports/client/components/validation';
6   -
7   -let component;
8   -
9   -const handleRecovery = () => {
10   - Accounts.forgotPassword({
11   - email: document.querySelector('[name="emailAddress"]').value,
12   - }, (error) => {
13   - if (error) {
14   - Bert.alert(error.reason, 'warning');
15   - } else {
16   - Bert.alert('Check your inbox for a reset link!', 'success');
17   - }
18   - });
19   -};
20   -
21   -const validate = () => {
22   - $(component.recoverPasswordForm).validate({
23   - rules: {
24   - emailAddress: {
25   - required: true,
26   - email: true,
27   - },
28   - },
29   - messages: {
30   - emailAddress: {
31   - required: 'Need an email address here.',
32   - email: 'Is this email address legit?',
33   - },
34   - },
35   - submitHandler() { handleRecovery(); },
36   - });
37   -};
38   -
39   -export default function handleRecoverPassword(options) {
40   - component = options.component;
41   - validate();
42   -}
imports/client/views/org/enter/module/reset-password.js
... ... @@ -1,54 +0,0 @@
1   -/* eslint-disable no-undef */
2   -
3   -import { browserHistory } from 'react-router';
4   -import { Accounts } from 'meteor/accounts-base';
5   -import { Bert } from 'meteor/themeteorchef:bert';
6   -import '/imports/client/components/validation';
7   -
8   -let component;
9   -let token;
10   -
11   -const handleReset = () => {
12   - const password = document.querySelector('[name="newPassword"]').value;
13   - Accounts.resetPassword(token, password, (error) => {
14   - if (error) {
15   - Bert.alert(error.reason, 'danger');
16   - } else {
17   - browserHistory.push('/');
18   - Bert.alert('Password reset!', 'success');
19   - }
20   - });
21   -};
22   -
23   -const validate = () => {
24   - $(component.resetPasswordForm).validate({
25   - rules: {
26   - newPassword: {
27   - required: true,
28   - minlength: 6,
29   - },
30   - repeatNewPassword: {
31   - required: true,
32   - minlength: 6,
33   - equalTo: '[name="newPassword"]',
34   - },
35   - },
36   - messages: {
37   - newPassword: {
38   - required: 'Enter a new password, please.',
39   - minlength: 'Use at least six characters, please.',
40   - },
41   - repeatNewPassword: {
42   - required: 'Repeat your new password, please.',
43   - equalTo: 'Hmm, your passwords don\'t match. Try again?',
44   - },
45   - },
46   - submitHandler() { handleReset(); },
47   - });
48   -};
49   -
50   -export default function handleResetPassword(options) {
51   - component = options.component;
52   - token = options.token;
53   - validate();
54   -}
imports/client/views/org/enter_1/RecoverPassword.js
... ... @@ -0,0 +1,43 @@
  1 +import React from 'react';
  2 +import { Row, Col, Alert, FormGroup, FormControl, Button } from 'react-bootstrap';
  3 +import handleRecoverPassword from './module/recover-password';
  4 +
  5 +export default class RecoverPassword extends React.Component {
  6 + componentDidMount() {
  7 + handleRecoverPassword({ component: this });
  8 + }
  9 +
  10 + handleSubmit(event) {
  11 + event.preventDefault();
  12 + }
  13 +
  14 + render() {
  15 + return (
  16 + <div className="RecoverPassword">
  17 + <Row>
  18 + <Col xs={ 12 } sm={ 6 } md={ 4 }>
  19 + <h4 className="page-header">Recover Password</h4>
  20 + <Alert bsStyle="info">
  21 + Enter your email address below to receive a link to reset your password.
  22 + </Alert>
  23 + <form
  24 + ref={ form => (this.recoverPasswordForm = form) }
  25 + className="recover-password"
  26 + onSubmit={ this.handleSubmit }
  27 + >
  28 + <FormGroup>
  29 + <FormControl
  30 + type="email"
  31 + ref="emailAddress"
  32 + name="emailAddress"
  33 + placeholder="Email Address"
  34 + />
  35 + </FormGroup>
  36 + <Button type="submit" bsStyle="success">Recover Password</Button>
  37 + </form>
  38 + </Col>
  39 + </Row>
  40 + </div>
  41 + );
  42 + }
  43 +}
... ...
imports/client/views/org/enter_1/ResetPassword.js
... ... @@ -0,0 +1,58 @@
  1 +import React from 'react';
  2 +import { Row, Col, Alert, FormGroup, ControlLabel, FormControl, Button } from 'react-bootstrap';
  3 +import handleResetPassword from './module/reset-password';
  4 +
  5 +export default class ResetPassword extends React.Component {
  6 + componentDidMount() {
  7 + handleResetPassword({ component: this, token: this.props.params.token });
  8 + }
  9 +
  10 + handleSubmit(event) {
  11 + event.preventDefault();
  12 + }
  13 +
  14 + render() {
  15 + return (
  16 + <div className="ResetPassword">
  17 + <Row>
  18 + <Col xs={ 12 } sm={ 6 } md={ 4 }>
  19 + <h4 className="page-header">Reset Password</h4>
  20 + <Alert bsStyle="info">
  21 + To reset your password, enter a new one below. You will be logged in
  22 + with your new password.
  23 + </Alert>
  24 + <form
  25 + ref={ form => (this.resetPasswordForm = form) }
  26 + className="reset-password"
  27 + onSubmit={ this.handleSubmit }
  28 + >
  29 + <FormGroup>
  30 + <ControlLabel>New Password</ControlLabel>
  31 + <FormControl
  32 + type="password"
  33 + ref="newPassword"
  34 + name="newPassword"
  35 + placeholder="New Password"
  36 + />
  37 + </FormGroup>
  38 + <FormGroup>
  39 + <ControlLabel>Repeat New Password</ControlLabel>
  40 + <FormControl
  41 + type="password"
  42 + ref="repeatNewPassword"
  43 + name="repeatNewPassword"
  44 + placeholder="Repeat New Password"
  45 + />
  46 + </FormGroup>
  47 + <Button type="submit" bsStyle="success">Reset Password &amp; Login</Button>
  48 + </form>
  49 + </Col>
  50 + </Row>
  51 + </div>
  52 + );
  53 + }
  54 +}
  55 +
  56 +ResetPassword.propTypes = {
  57 + params: React.PropTypes.object,
  58 +};
... ...
imports/client/views/org/enter_1/login/LoginView.js
... ... @@ -0,0 +1,56 @@
  1 +import React,{ Component } from 'react';
  2 +import { Link } from 'react-router';
  3 +import { Row, Col, FormGroup,
  4 + ControlLabel, FormControl,
  5 + Button } from 'react-bootstrap';
  6 +import handleLogin from './login';
  7 +
  8 +export class LoginView extends React.Component {
  9 + componentDidMount() {
  10 + handleLogin({ component: this });
  11 + }
  12 +
  13 + handleSubmit(event) {
  14 + event.preventDefault();
  15 + }
  16 +
  17 + render() {
  18 + return (
  19 + <div className="Login">
  20 + <Row>
  21 + <Col xs={ 12 } sm={ 6 } md={ 4 }>
  22 + <h4 className="page-header">Login</h4>
  23 + <form
  24 + ref={ form => (this.loginForm = form) }
  25 + className="login"
  26 + onSubmit={ this.handleSubmit }
  27 + >
  28 + <FormGroup>
  29 + <ControlLabel>Email Address</ControlLabel>
  30 + <FormControl
  31 + type="email"
  32 + ref="emailAddress"
  33 + name="emailAddress"
  34 + placeholder="Email Address"
  35 + />
  36 + </FormGroup>
  37 + <FormGroup>
  38 + <ControlLabel>
  39 + <span className="pull-left">Password</span>
  40 + <Link className="pull-right" to="/recover-password">Forgot Password?</Link>
  41 + </ControlLabel>
  42 + <FormControl
  43 + type="password"
  44 + ref="password"
  45 + name="password"
  46 + placeholder="Password"
  47 + />
  48 + </FormGroup>
  49 + <Button type="submit" bsStyle="success">Login</Button>
  50 + </form>
  51 + </Col>
  52 + </Row>
  53 + </div>
  54 + );
  55 + }
  56 +}
... ...
imports/client/views/org/enter_1/login/index.js
... ... @@ -0,0 +1,40 @@
  1 +// import { orgLoginController } from '/imports/client/views/org/enter/login/index';
  2 +import _ from 'lodash';
  3 +import {
  4 + composeWithTracker,
  5 + compose,
  6 + composeAll
  7 + } from 'react-komposer';
  8 +import { Loading } from '/imports/client/components/Loading';
  9 +import { Orgs } from '/imports/collections/orgs/index';
  10 +import { LoginView } from './LoginView'
  11 +
  12 +const meteorTick = (props, onData) => {
  13 +
  14 + const handles = [
  15 + ];
  16 +
  17 + if(_.every(handles, (handle) => (handle.ready()) )) {
  18 + onData(null, {
  19 + data: {
  20 + },
  21 + });
  22 + }
  23 +
  24 + return () => {
  25 + _.each(handles, (handle) => handle.stop() );
  26 + };
  27 +};
  28 +
  29 +
  30 +const reduxTick = (props, onData) => {
  31 + onData(null, {
  32 + data: {}
  33 + });
  34 +};
  35 +
  36 +
  37 +export const orgLoginController = composeAll(
  38 + composeWithTracker(meteorTick, Loading),
  39 + compose(reduxTick, Loading),
  40 +)(LoginView);
... ...
imports/client/views/org/enter_1/login/login.js
... ... @@ -0,0 +1,57 @@
  1 +/* eslint-disable no-undef */
  2 +
  3 +import { browserHistory } from 'react-router';
  4 +import { Meteor } from 'meteor/meteor';
  5 +import { Bert } from 'meteor/themeteorchef:bert';
  6 +import '/imports/client/components/validation';
  7 +
  8 +let component;
  9 +
  10 +const login = () => {
  11 + const email = document.querySelector('[name="emailAddress"]').value;
  12 + const password = document.querySelector('[name="password"]').value;
  13 +
  14 + Meteor.loginWithPassword(email, password, (error) => {
  15 + if (error) {
  16 + Bert.alert(error.reason, 'warning');
  17 + } else {
  18 + Bert.alert('Logged in!', 'success');
  19 +
  20 + const { location } = component.props;
  21 + if (location.state && location.state.nextPathname) {
  22 + browserHistory.push(location.state.nextPathname);
  23 + } else {
  24 + browserHistory.push('/');
  25 + }
  26 + }
  27 + });
  28 +};
  29 +
  30 +const validate = () => {
  31 + $(component.loginForm).validate({
  32 + rules: {
  33 + emailAddress: {
  34 + required: true,
  35 + email: true,
  36 + },
  37 + password: {
  38 + required: true,
  39 + },
  40 + },
  41 + messages: {
  42 + emailAddress: {
  43 + required: 'Need an email address here.',
  44 + email: 'Is this email address legit?',
  45 + },
  46 + password: {
  47 + required: 'Need a password here.',
  48 + },
  49 + },
  50 + submitHandler() { login(); },
  51 + });
  52 +};
  53 +
  54 +export default function handleLogin(options) {
  55 + component = options.component;
  56 + validate();
  57 +}
... ...
imports/client/views/org/enter_1/module/recover-password.js
... ... @@ -0,0 +1,42 @@
  1 +/* eslint-disable no-undef */
  2 +
  3 +import { Accounts } from 'meteor/accounts-base';
  4 +import { Bert } from 'meteor/themeteorchef:bert';
  5 +import '/imports/client/components/validation';
  6 +
  7 +let component;
  8 +
  9 +const handleRecovery = () => {
  10 + Accounts.forgotPassword({
  11 + email: document.querySelector('[name="emailAddress"]').value,
  12 + }, (error) => {
  13 + if (error) {
  14 + Bert.alert(error.reason, 'warning');
  15 + } else {
  16 + Bert.alert('Check your inbox for a reset link!', 'success');
  17 + }
  18 + });
  19 +};
  20 +
  21 +const validate = () => {
  22 + $(component.recoverPasswordForm).validate({
  23 + rules: {
  24 + emailAddress: {
  25 + required: true,
  26 + email: true,
  27 + },
  28 + },
  29 + messages: {
  30 + emailAddress: {
  31 + required: 'Need an email address here.',
  32 + email: 'Is this email address legit?',
  33 + },
  34 + },
  35 + submitHandler() { handleRecovery(); },
  36 + });
  37 +};
  38 +
  39 +export default function handleRecoverPassword(options) {
  40 + component = options.component;
  41 + validate();
  42 +}
... ...
imports/client/views/org/enter_1/module/reset-password.js
... ... @@ -0,0 +1,54 @@
  1 +/* eslint-disable no-undef */
  2 +
  3 +import { browserHistory } from 'react-router';
  4 +import { Accounts } from 'meteor/accounts-base';
  5 +import { Bert } from 'meteor/themeteorchef:bert';
  6 +import '/imports/client/components/validation';
  7 +
  8 +let component;
  9 +let token;
  10 +
  11 +const handleReset = () => {
  12 + const password = document.querySelector('[name="newPassword"]').value;
  13 + Accounts.resetPassword(token, password, (error) => {
  14 + if (error) {
  15 + Bert.alert(error.reason, 'danger');
  16 + } else {
  17 + browserHistory.push('/');
  18 + Bert.alert('Password reset!', 'success');
  19 + }
  20 + });
  21 +};
  22 +
  23 +const validate = () => {
  24 + $(component.resetPasswordForm).validate({
  25 + rules: {
  26 + newPassword: {
  27 + required: true,
  28 + minlength: 6,
  29 + },
  30 + repeatNewPassword: {
  31 + required: true,
  32 + minlength: 6,
  33 + equalTo: '[name="newPassword"]',
  34 + },
  35 + },
  36 + messages: {
  37 + newPassword: {
  38 + required: 'Enter a new password, please.',
  39 + minlength: 'Use at least six characters, please.',
  40 + },
  41 + repeatNewPassword: {
  42 + required: 'Repeat your new password, please.',
  43 + equalTo: 'Hmm, your passwords don\'t match. Try again?',
  44 + },
  45 + },
  46 + submitHandler() { handleReset(); },
  47 + });
  48 +};
  49 +
  50 +export default function handleResetPassword(options) {
  51 + component = options.component;
  52 + token = options.token;
  53 + validate();
  54 +}
... ...
imports/startup/client/index.js
... ... @@ -1,5 +0,0 @@
1   -import { Bert } from 'meteor/themeteorchef:bert';
2   -import 'bootstrap/dist/css/bootstrap.min.css';
3   -import './routes.js';
4   -
5   -Bert.defaults.style = 'growl-top-right';
imports/startup/client/routes.js
... ... @@ -1,109 +0,0 @@
1   -/* eslint-disable max-len */
2   -
3   -import React from 'react';
4   -import { render } from 'react-dom';
5   -import { Router, Route,
6   - IndexRoute, browserHistory } from 'react-router';
7   -import { Meteor } from 'meteor/meteor';
8   -
9   -/**
10   - * General Components
11   - */
12   -import Index from '/imports/client/views/app/module/Index';
13   -
14   -/**
15   - * Org Components
16   - */
17   -
18   -import App from '/imports/client/layouts/OrgApp';
19   -import { AppModule } from '/imports/client/views/org/app/module/Index';
20   -import { orgLoginController } from '/imports/client/views/org/enter/login/index';
21   -import RecoverPassword from '/imports/client/views/org/enter/RecoverPassword';
22   -import ResetPassword from '/imports/client/views/org/enter/ResetPassword';
23   -import { Orgs } from '/imports/collections/orgs/index';
24   -import NotFound from '/imports/client/views/org/NotFound';
25   -
26   -/**
27   - * NonOrg Components
28   - */
29   -import Signup from '/imports/client/views/nonOrg/enter/SignupView';
30   -
31   -/**
32   - * Invalid Org Components
33   - */
34   -
35   -const authenticate = (nextState, replace) => {
36   - if (!Meteor.loggingIn() && !Meteor.userId()) {
37   - replace({
38   - pathname: '/login',
39   - state: { nextPathname: nextState.location.pathname },
40   - });
41   - }
42   -};
43   -
44   -
45   -const detectOrg = () => {
46   - orgSlug = "";
47   - var hostnameArray = document.location.hostname.split( "." );
48   - if(hostnameArray[1]=='localhost'){
49   - orgSlug = hostnameArray[0];
50   - }
51   - if(orgSlug!=""){
52   - Meteor.call('checkExistingOrg', {slug:orgSlug}, function(err, res) {
53   - if(res){
54   - Session.set('orgId', res._id._str);
55   - render(getOrgRoutes(),document.getElementById('app'));
56   - }else{
57   - render(getInvalidOrgRoute(),document.getElementById('app'));
58   - }
59   - });
60   - }else{
61   - render(getNonOrgRoutes(),document.getElementById('app'));
62   - }
63   -}
64   -const checkSlug = (nextState, replace) => {
65   - orgId = Session.get('orgId');
66   -}
67   -
68   -/**
69   -There are three types of routes
70   -1)getOrgRoutes: all the routes that should be present for a registered org
71   -2)getInvalidOrgRoute: all the routes where someone tries to enter a subdomain which hasn't been registered yet (404 mostly :D)
72   -3)getNonOrgRoutes: all routes linked to normal site, ie signing up a new org. CHeking out demo and everything internal
73   -**/
74   -const getOrgRoutes = () => (
75   - <Router history={ browserHistory }>
76   - <Route path="/" component={ App }>
77   - <IndexRoute name="index" component={ AppModule } />
78   - <Route name="login" path="/login" component={ orgLoginController } />
79   - <Route name="recover-password" path="/recover-password" component={ RecoverPassword } />
80   - <Route name="reset-password" path="/reset-password/:token" component={ ResetPassword } />
81   - <Route path="*" component={ NotFound } />
82   - </Route>
83   - </Router>
84   -)
85   -
86   -
87   -const getInvalidOrgRoute = () => (
88   - <Router history={ browserHistory }>
89   - <Route path="/" component={ App }>
90   - <IndexRoute name="index" component={ NotFound } />
91   - <Route path="*" component={ NotFound } />
92   - </Route>
93   - </Router>
94   -)
95   -
96   -const getNonOrgRoutes = () => (
97   - <Router history={ browserHistory }>
98   - <Route path="/" component={ App }>
99   - <IndexRoute name="index" component={ Index } />
100   - <Route name="signup" path="/signup" component={ Signup } />
101   - <Route path="*" component={ NotFound } />
102   - </Route>
103   - </Router>
104   -)
105   -
106   -
107   -Meteor.startup(() => {
108   - detectOrg();
109   -});
imports/validation/validationMethods.js
... ... @@ -0,0 +1,101 @@
  1 +export default class Validation{
  2 +
  3 + validateEmail (value) {
  4 + // regex from http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
  5 + var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
  6 + return re.test(value);
  7 + };
  8 +
  9 + // containsNoSpecialCharacters(str){
  10 + // return !/[~`!#$%\^&*+=\-\[\]\\';,/{}|\\":<>\?]/g.test(str);
  11 + // }
  12 +
  13 + noSpecialChars(str){
  14 + str = String(str);
  15 + return !/[~`!#$%\^&*+=\-\[\]\\';,/{}|\\":<>\?]/g.test(str);
  16 + }
  17 +
  18 + noQwertysAllowed (str){
  19 + str = str.toLowerCase();
  20 + if(str.toLowerCase().indexOf("qwerty") >-1){
  21 + return false;
  22 + } else{
  23 + return true;
  24 + }
  25 + }
  26 +
  27 + passwordValidation (str){
  28 + if(str.length <6){
  29 + return false;
  30 + } else{
  31 + return true;
  32 + }
  33 + }
  34 +
  35 + isNumberOnly(str){
  36 + if(!/^\d+$/.test(str)){
  37 + return false;
  38 + }else {
  39 + return true;
  40 + }
  41 + }
  42 + isPositiveNotZeroNumber(str){
  43 + str = parseFloat(str);
  44 + if(str <=0){
  45 + return false;
  46 + }else {
  47 + return true;
  48 + }
  49 + }
  50 + isNumeric(n) {
  51 + return !isNaN(parseFloat(n)) && isFinite(n);
  52 + }
  53 +
  54 + isValidACN(str){
  55 + str = String(str);
  56 + //Remove any whitespace.
  57 + str = str.replace(/\s+/g, '');
  58 + console.log(str);
  59 + console.log(str.length);
  60 + if(/^\d+$/.test(str) && str.length ==9){
  61 + return true;
  62 + } else{
  63 + return false;
  64 + }
  65 + }
  66 + isValidShortCode(str){
  67 + str = String(str);
  68 + console.log(str);
  69 + if(str.length < 5 && str.length >2){
  70 + return true;
  71 + } else{
  72 + return false;
  73 + }
  74 + }
  75 + containsNumbers (str){
  76 + if(/\d/g.test(str)){
  77 + return true;
  78 + }else{
  79 + return false;
  80 + }
  81 + }
  82 + isInt(n){
  83 + console.log(typeof Number(n));
  84 + return Number(n) % 1 === 0;
  85 + }
  86 +
  87 + isFloat(n){
  88 + return Number(n) % 1 !== 0;
  89 + }
  90 + decimalPlaces(num) {
  91 + num = parseFloat(num);
  92 + var match = (''+num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
  93 + if (!match) { return 0; }
  94 + return Math.max(
  95 + 0,
  96 + // Number of digits right of decimal point.
  97 + (match[1] ? match[1].length : 0)
  98 + // Adjust for scientific notation.
  99 + - (match[2] ? +match[2] : 0));
  100 + }
  101 +};
... ...
... ... @@ -68,12 +68,17 @@
68 68 "jquery": "^2.2.4",
69 69 "jquery-validation": "^1.15.1",
70 70 "react": "^15.3.2",
  71 + "react-addons-css-transition-group": "^15.4.2",
71 72 "react-addons-pure-render-mixin": "^15.3.2",
  73 + "react-addons-transition-group": "^15.4.2",
72 74 "react-bootstrap": "^0.30.5",
73 75 "react-dom": "^15.3.2",
74 76 "react-komposer": "^1.13.1",
75 77 "react-router": "^2.6.1",
76 78 "react-router-bootstrap": "^0.23.1",
77   - "react-svg": "^2.1.19"
  79 + "react-svg": "^2.1.19",
  80 + "reactstrap": "^4.2.0",
  81 + "velocity-animate": "^1.4.3",
  82 + "velocity-react": "^1.2.1"
78 83 }
79 84 }
... ...