Commit c4d3e07d0fe924ffef30abd83dce85b192f45d77
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
client/main.js
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
imports/client/app/utils/changePassword.js
imports/client/app/utils/get-input-value.js
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
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
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 'react-router'; |
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 & 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 & 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 & 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
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 | +}; | ... | ... |
package.json
... | ... | @@ -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 | } | ... | ... |