diff --git a/client/main.js b/client/main.js index 090ed9e..d6d9069 100644 --- a/client/main.js +++ b/client/main.js @@ -1 +1 @@ -import '/imports/startup/client'; +import '/imports/client/app/index'; diff --git a/imports/client/app/index.js b/imports/client/app/index.js new file mode 100644 index 0000000..e9ffe89 --- /dev/null +++ b/imports/client/app/index.js @@ -0,0 +1,37 @@ +import _ from 'lodash'; +import moment from 'moment'; +import { Meteor } from 'meteor/meteor'; +import { Router, browserHistory } from 'react-router'; + +import { Orgs } from '/imports/collections/orgs/index'; +import { Users } from '/imports/collections/users/index';; + + +import { Bert } from 'meteor/themeteorchef:bert'; +import './routes.js'; +import './shelf.js'; +// import 'react-quill/dist/quill.snow.css'; + +Bert.defaults = { + hideDelay: 3500, + // Accepts: a number in milliseconds. + style: 'fixed-top', + // Accepts: fixed-top, fixed-bottom, growl-top-left, growl-top-right, + // growl-bottom-left, growl-bottom-right. + type: 'default' + // Accepts: default, success, info, warning, danger. +}; + +_.assign(window, { + _, +}); + +if(Meteor.settings.public.environment === 'development') { + _.assign(window, { + Meteor, + Shelf, + + Orgs, + Users, + }); +} diff --git a/imports/client/app/routes.js b/imports/client/app/routes.js new file mode 100644 index 0000000..b2cea51 --- /dev/null +++ b/imports/client/app/routes.js @@ -0,0 +1,104 @@ +/* eslint-disable max-len */ + +import React from 'react'; +import { render } from 'react-dom'; +import { Router, Route, + IndexRoute, browserHistory } from 'react-router'; +import { Meteor } from 'meteor/meteor'; + +/** + * General Components + */ +import Index from '/imports/client/views/app/module/Index'; + +/** + * Org Components + */ + +import { App } from '/imports/client/layouts/OrgApp'; +import { AppModule } from '/imports/client/views/org/app/module/Index'; +import { Orgs } from '/imports/collections/orgs/index'; +import NotFound from '/imports/client/views/org/NotFound'; + +/** + * NonOrg Components + */ +import Signup from '/imports/client/views/nonOrg/enter/SignupView'; + +/** + * Invalid Org Components + */ + +const authenticate = (nextState, replace) => { + if (!Meteor.loggingIn() && !Meteor.userId()) { + replace({ + pathname: '/login', + state: { nextPathname: nextState.location.pathname }, + }); + } +}; + + +const detectOrg = () => { + orgSlug = ""; + var hostnameArray = document.location.hostname.split( "." ); + if(hostnameArray[1]=='localhost'){ + orgSlug = hostnameArray[0]; + } + if(orgSlug!=""){ + Meteor.call('checkExistingOrg', {slug:orgSlug}, function(err, res) { + if(res){ + Session.set('orgId', res._id._str); + Session.set('orgSlug', orgSlug); + render(getOrgRoutes(),document.getElementById('app')); + }else{ + render(getInvalidOrgRoute(),document.getElementById('app')); + } + }); + }else{ + render(getNonOrgRoutes(),document.getElementById('app')); + } +} +const checkSlug = (nextState, replace) => { + orgId = Session.get('orgId'); +} + +/** +There are three types of routes +1)getOrgRoutes: all the routes that should be present for a registered org +2)getInvalidOrgRoute: all the routes where someone tries to enter a subdomain which hasn't been registered yet (404 mostly :D) +3)getNonOrgRoutes: all routes linked to normal site, ie signing up a new org. CHeking out demo and everything internal +**/ +const getOrgRoutes = () => ( + + + + + + +) + + +const getInvalidOrgRoute = () => ( + + + + + + +) + +const getNonOrgRoutes = () => ( + + + + + + + +) + + +Meteor.startup(() => { + detectOrg(); +}); diff --git a/imports/client/app/shelf.js b/imports/client/app/shelf.js new file mode 100644 index 0000000..51b03cd --- /dev/null +++ b/imports/client/app/shelf.js @@ -0,0 +1,12 @@ +import { ReactiveVar } from 'meteor/reactive-var' + + +window.Shelf = {}; + +Shelf.layout = new ReactiveVar({}); +// { +// bulb: new ReactiveVar(''), // Active menu tab +// subbulb: new ReactiveVar(''), // Active submenu tab +// }; + + diff --git a/imports/client/app/utils/changePassword.js b/imports/client/app/utils/changePassword.js new file mode 100644 index 0000000..d551843 --- /dev/null +++ b/imports/client/app/utils/changePassword.js @@ -0,0 +1,7 @@ +import $ from 'jquery'; +import 'jquery-validation'; +import { Accounts } from 'meteor/accounts-base'; +import { Bert } from 'meteor/themeteorchef:bert'; +import { getInputValue } from './get-input-value'; + +let component; diff --git a/imports/client/app/utils/get-input-value.js b/imports/client/app/utils/get-input-value.js new file mode 100644 index 0000000..31d71cc --- /dev/null +++ b/imports/client/app/utils/get-input-value.js @@ -0,0 +1,5 @@ +// TODO WTF: what is this thing? -H + +import ReactDOM from 'react-dom'; + +export const getInputValue = (component) => ReactDOM.findDOMNode(component).value; diff --git a/imports/client/app/utils/login.js b/imports/client/app/utils/login.js new file mode 100644 index 0000000..aa10ac6 --- /dev/null +++ b/imports/client/app/utils/login.js @@ -0,0 +1,70 @@ +import $ from 'jquery'; +import 'jquery-validation'; +import { browserHistory } from 'react-router'; +import { Meteor } from 'meteor/meteor'; +import { Bert } from 'meteor/themeteorchef:bert'; +import { getInputValue } from './get-input-value'; + +let component; + +const login = () => { + const email = getInputValue(component.refs.emailAddress); + const password = getInputValue(component.refs.password); + + Meteor.call('checkEmailVerification', email, ( error, data ) => { + if ( error ) { + Bert.alert( error.reason, 'danger' ); + } + else { + if ( data == "verified" ) { + Meteor.loginWithPassword(email, password, (error) => { + if ( error ) { + Bert.alert(error.reason, 'warning'); + } else { + Bert.alert('Logged in!', 'success'); + + const { location } = component.props; + if (location.state && location.state.nextPathname) { + browserHistory.push(location.state.nextPathname); + } else { + browserHistory.push('/'); + } + } + }); + } else if ( data == "unverified" ){ + Bert.alert("Check your email for a verification link"); + } else { + Bert.alert("Either email or password is incorrect"); + } + } + }); +}; + +const validate = () => { + $(component.refs.login).validate({ + rules: { + emailAddress: { + required: true, + email: true, + }, + password: { + required: true, + }, + }, + messages: { + emailAddress: { + required: 'Need an email address here.', + email: 'Is this email address legit?', + }, + password: { + required: 'Need a password here.', + }, + }, + submitHandler() { login(); }, + }); +}; + +export const handleLogin = (options) => { + component = options.component; + validate(); +}; diff --git a/imports/client/app/utils/loginMethods.js b/imports/client/app/utils/loginMethods.js new file mode 100644 index 0000000..a969d58 --- /dev/null +++ b/imports/client/app/utils/loginMethods.js @@ -0,0 +1,10 @@ +import { browserHistory } from 'react-router'; + + +export const logout = () => { + Meteor.logout(); + browserHistory.push('/'); +}; + + + diff --git a/imports/client/app/utils/rate-limit.js b/imports/client/app/utils/rate-limit.js new file mode 100644 index 0000000..498d782 --- /dev/null +++ b/imports/client/app/utils/rate-limit.js @@ -0,0 +1,18 @@ +import { _ } from 'meteor/underscore'; +import { Meteor } from 'meteor/meteor'; +import { DDPRateLimiter } from 'meteor/ddp-rate-limiter'; + +const fetchMethodNames = (methods) => _.pluck(methods, 'name'); + +const assignLimits = ({ methods, limit, timeRange }) => { + const methodNames = fetchMethodNames(methods); + + if (Meteor.isServer) { + DDPRateLimiter.addRule({ + name(name) { return _.contains(methodNames, name); }, + connectionId() { return true; }, + }, limit, timeRange); + } +}; + +export const rateLimit = (options) => assignLimits(options); diff --git a/imports/client/app/utils/recover-password.js b/imports/client/app/utils/recover-password.js new file mode 100644 index 0000000..e2c846b --- /dev/null +++ b/imports/client/app/utils/recover-password.js @@ -0,0 +1,42 @@ +import $ from 'jquery'; +import 'jquery-validation'; +import { Accounts } from 'meteor/accounts-base'; +import { Bert } from 'meteor/themeteorchef:bert'; +import { getInputValue } from './get-input-value'; + +let component; + +const handleRecovery = () => { + Accounts.forgotPassword({ + email: getInputValue(component.refs.emailAddress), + }, (error) => { + if (error) { + Bert.alert(error.reason, 'warning'); + } else { + Bert.alert('Check your inbox for a reset link!', 'success'); + } + }); +}; + +const validate = () => { + $(component.refs.recoverPassword).validate({ + rules: { + emailAddress: { + required: true, + email: true, + }, + }, + messages: { + emailAddress: { + required: 'Need an email address here.', + email: 'Is this email address legit?', + }, + }, + submitHandler() { handleRecovery(); }, + }); +}; + +export const handleRecoverPassword = (options) => { + component = options.component; + validate(); +}; diff --git a/imports/client/app/utils/reset-password.js b/imports/client/app/utils/reset-password.js new file mode 100644 index 0000000..9d18e9b --- /dev/null +++ b/imports/client/app/utils/reset-password.js @@ -0,0 +1,54 @@ +import $ from 'jquery'; +import 'jquery-validation'; +import { browserHistory } from 'react-router'; +import { Accounts } from 'meteor/accounts-base'; +import { Bert } from 'meteor/themeteorchef:bert'; +import { getInputValue } from './get-input-value'; + +let component; +let token; + +const handleReset = () => { + const password = getInputValue(component.refs.newPassword); + Accounts.resetPassword(token, password, (error) => { + if (error) { + Bert.alert(error.reason, 'danger'); + } else { + browserHistory.push('/'); + Bert.alert('Password reset!', 'success'); + } + }); +}; + +const validate = () => { + $(component.refs.resetPassword).validate({ + rules: { + newPassword: { + required: true, + minlength: 6, + }, + repeatNewPassword: { + required: true, + minlength: 6, + equalTo: '[name="newPassword"]', + }, + }, + messages: { + newPassword: { + required: 'Enter a new password, please.', + minlength: 'Use at least six characters, please.', + }, + repeatNewPassword: { + required: 'Repeat your new password, please.', + equalTo: 'Hmm, your passwords don\'t match. Try again?', + }, + }, + submitHandler() { handleReset(); }, + }); +}; + +export const handleResetPassword = (options) => { + component = options.component; + token = options.token; + validate(); +}; diff --git a/imports/client/app/utils/setQueryParam.js b/imports/client/app/utils/setQueryParam.js new file mode 100644 index 0000000..eddb1a2 --- /dev/null +++ b/imports/client/app/utils/setQueryParam.js @@ -0,0 +1,9 @@ +import _ from 'lodash'; + +export const setQueryParam = (location, query) => ({ + pathname: location.pathname, + hash: location.hash, + query: _.assign({}, location.query, query), +}); + + diff --git a/imports/client/app/utils/signup.js b/imports/client/app/utils/signup.js new file mode 100644 index 0000000..39b07c9 --- /dev/null +++ b/imports/client/app/utils/signup.js @@ -0,0 +1,76 @@ +import $ from 'jquery'; +import 'jquery-validation'; +import { browserHistory } from 'react-router'; +import { Accounts } from 'meteor/accounts-base'; +import { Bert } from 'meteor/themeteorchef:bert'; +import { getInputValue } from './get-input-value'; + +let component; + +const getUserData = () => ({ + email: getInputValue(component.refs.emailAddress), + password: getInputValue(component.refs.password), + profile: { + name: { + first: getInputValue(component.refs.firstName), + last: getInputValue(component.refs.lastName), + }, + }, +}); + +const signUp = () => { + const user = getUserData(); + + Accounts.createUser(user, (error) => { + if (error) { + Bert.alert(error.reason, 'danger'); + } else { + Bert.alert( 'Welcome!', 'success' ); + Session.set('signedEmail', user.email); + browserHistory.push('/users/verify'); + } + }); +}; + +const validate = () => { + $(component.refs.signup).validate({ + rules: { + firstName: { + required: true, + }, + lastName: { + required: true, + }, + emailAddress: { + required: true, + email: true, + }, + password: { + required: true, + minlength: 6, + }, + }, + messages: { + firstName: { + required: 'First name?', + }, + lastName: { + required: 'Last name?', + }, + emailAddress: { + required: 'Need an email address here.', + email: 'Is this email address legit?', + }, + password: { + required: 'Need a password here.', + minlength: 'Use at least six characters, please.', + }, + }, + submitHandler() { signUp(); }, + }); +}; + +export const handleSignup = (options) => { + component = options.component; + validate(); +}; diff --git a/imports/client/assets/css/icons/icomoon/styles.css b/imports/client/assets/css/icons/icomoon/styles.css index beca53d..65eccd5 100644 --- a/imports/client/assets/css/icons/icomoon/styles.css +++ b/imports/client/assets/css/icons/icomoon/styles.css @@ -2,7 +2,7 @@ font-family: 'icomoon'; src:url('fonts/icomoon.eot?3p0rtw'); src:url('fonts/icomoon.eot?#iefix3p0rtw') format('embedded-opentype'), - url('fonts/icomoon.woff?3p0rtw') format('woff'), + url('fonts/icomoon.woff?3p0rtw') format('font-woff'), url('fonts/icomoon.ttf?3p0rtw') format('truetype'), url('fonts/icomoon.svg?3p0rtw#icomoon') format('svg'); font-weight: normal; diff --git a/imports/client/layouts/OrgApp.js b/imports/client/layouts/OrgApp.js index 2974b4d..687b44a 100644 --- a/imports/client/layouts/OrgApp.js +++ b/imports/client/layouts/OrgApp.js @@ -1,18 +1,24 @@ -import React from 'react'; -import { Grid } from 'react-bootstrap'; -import AppNavigation from '/imports/client/views/org/app/module/navigation/AppNavigation'; +import React, { Component } from 'react'; +import { Grid } from 'react-bootstrap'; +import {AppNavigationController} from '/imports/client/views/org/app/module/navigation/index'; +/** + * user based redirection will take place here + */ + export class App extends Component { + constructor(props) { + super(props); + this.state = { -const App = ({ children }) => ( -
- - - { children } - -
-); - -App.propTypes = { - children: React.PropTypes.node, -}; - -export default App; + }; + }; + render(){ + return ( +
+ + + { this.props.children } + +
+ ) + } + } diff --git a/imports/client/views/etc/index.js b/imports/client/views/etc/index.js index 9098c73..6579f02 100644 --- a/imports/client/views/etc/index.js +++ b/imports/client/views/etc/index.js @@ -5,22 +5,22 @@ import { compose, composeAll } from 'react-komposer'; -import { InviteSignupView } from './InviteSignupView'; import { Loading } from '/imports/client/components/Loading'; -import { Invitations } from '/imports/collections/invitations/index'; import { Orgs } from '/imports/collections/orgs/index'; +import { Users } from '/imports/collections/users/index'; const meteorTick = (props, onData) => { const handles = [ - Meteor.subscribe('invitations.withToken') + Meteor.subscribe('users.current') ]; if(_.every(handles, (handle) => (handle.ready()) )) { - const invitation = Invitations.findOne({}); + const user = Users.current(); onData(null, { data: { + user: user, }, }); } diff --git a/imports/client/views/org/app/module/AppLayout.js b/imports/client/views/org/app/module/AppLayout.js index fcaa68d..3458058 100644 --- a/imports/client/views/org/app/module/AppLayout.js +++ b/imports/client/views/org/app/module/AppLayout.js @@ -5,21 +5,22 @@ import { Link } from 'react-router'; import { Avatar } from '/imports/client/components/Avatar'; import { Icon } from '/imports/client/components/Icon'; import classNames from 'classnames'; +import { EnterModule } from '/imports/client/views/org/enter/module/index'; +// import { VerifyModule } from '/imports/client/views/verify/module/index'; export class AppLayout extends Component { - - - - - render() { + console.log(this.props); const {user} = this.props.data; if(!user) { return ( -
+ ); } return ( diff --git a/imports/client/views/org/app/module/navigation/AppNavigation.js b/imports/client/views/org/app/module/navigation/AppNavigation.js index ff33f9a..c280710 100644 --- a/imports/client/views/org/app/module/navigation/AppNavigation.js +++ b/imports/client/views/org/app/module/navigation/AppNavigation.js @@ -1,30 +1,54 @@ -import React from 'react'; +import React, { Component } from 'react'; import { Navbar } from 'react-bootstrap'; import { Link } from 'react-router'; -import PublicNavigation from './PublicNavigation.js'; -import AuthenticatedNavigation from './AuthenticatedNavigation.js'; +import {PublicNavigation} from './PublicNavigation.js'; +import {AuthenticatedNavigation} from './AuthenticatedNavigation.js'; import '/imports/client/assets/css/icons/icomoon/styles.css'; import '/imports/client/assets/css/bootstrap.css'; import '/imports/client/assets/css/core.css'; import '/imports/client/assets/css/components.css'; import '/imports/client/assets/css/colors.css'; -const renderNavigation = hasUser => (hasUser ? : ); -const AppNavigation = ({ hasUser }) => ( - - - - Home - - - - - { renderNavigation(hasUser) } - - -); +import '/imports/client/assets/css/colors.css'; +export class AppNavigation extends Component { -AppNavigation.propTypes = { - hasUser: React.PropTypes.object, -}; + constructor(props) { + super(props); + this.state = { + + }; + }; + + onUpdate(key, value) { + this.setState({[key]: value}); + }; + render() { + const {user} = this.props.data; + if(user){ + return( + + ) + }else{ + return( + + + + Application Name + + + + + + + -export default AppNavigation; + ) + } + + return ( +
+ ); + }; + +}; diff --git a/imports/client/views/org/app/module/navigation/AuthenticatedNavigation.js b/imports/client/views/org/app/module/navigation/AuthenticatedNavigation.js index 87a0c38..5f1c2f1 100644 --- a/imports/client/views/org/app/module/navigation/AuthenticatedNavigation.js +++ b/imports/client/views/org/app/module/navigation/AuthenticatedNavigation.js @@ -1,30 +1,303 @@ -import React from 'react'; -import { browserHistory } from 'react-router'; -import { LinkContainer } from 'react-router-bootstrap'; -import { Nav, NavItem, NavDropdown, MenuItem } from 'react-bootstrap'; -import { Meteor } from 'meteor/meteor'; +import React, { Component } from 'react'; +import { browserHistory } from 'react-router'; +import { LinkContainer } from 'react-router-bootstrap'; +import { Nav, NavItem, + NavDropdown, MenuItem } from 'react-bootstrap'; +import { Meteor } from 'meteor/meteor'; const handleLogout = () => Meteor.logout(() => browserHistory.push('/login')); -const userName = () => { - const user = Meteor.user(); - const name = user && user.profile ? user.profile.name : ''; - return user ? `${name.first} ${name.last}` : ''; -}; +export class AuthenticatedNavigation extends Component { + constructor(props) { + super(props); + this.state = { -const AuthenticatedNavigation = () => ( + }; + }; +render(){ + const {user} = this.props.data; + return(
- - +
+
+ + YOUNGDESK + + +
    +
  • +
  • +
+
+ + +
); - -export default AuthenticatedNavigation; +} +} diff --git a/imports/client/views/org/app/module/navigation/PublicNavigation.js b/imports/client/views/org/app/module/navigation/PublicNavigation.js index d04fc80..82cf700 100644 --- a/imports/client/views/org/app/module/navigation/PublicNavigation.js +++ b/imports/client/views/org/app/module/navigation/PublicNavigation.js @@ -1,16 +1,27 @@ -import React from 'react'; -import { LinkContainer } from 'react-router-bootstrap'; -import { Nav, NavItem } from 'react-bootstrap'; +import React, { Component } from 'react'; +import { LinkContainer } from 'react-router-bootstrap'; +import { setQueryParam } from '/imports/client/app/utils/setQueryParam'; +import { browserHistory } from 'react-router'; +import { Nav, NavItem } from 'react-bootstrap'; -const PublicNavigation = () => ( - -); -export default PublicNavigation; +export class PublicNavigation extends Component { + constructor(props) { + super(props); + this.state = { + + }; + }; +render(){ + console.log(this.props); + var mainSite = document.location.hostname.split( "." )[1]; + var signup = `http://${mainSite}/signup`; + return( + + ) +} +} diff --git a/imports/client/views/org/app/module/navigation/index.js b/imports/client/views/org/app/module/navigation/index.js index 65f743d..0965b10 100644 --- a/imports/client/views/org/app/module/navigation/index.js +++ b/imports/client/views/org/app/module/navigation/index.js @@ -1,7 +1,46 @@ -import { composeWithTracker } from 'react-komposer'; -import { Meteor } from 'meteor/meteor'; -import AppNavigation from '../components/AppNavigation.js'; +// import { InviteSignupController } from '/imports/client/views/invite/signup/index' +import _ from 'lodash'; +import { + composeWithTracker, + compose, + composeAll + } from 'react-komposer'; +import { Loading } from '/imports/client/components/Loading'; -const composer = (props, onData) => onData(null, { hasUser: Meteor.user() }); +import { Orgs } from '/imports/collections/orgs/index'; +import { Users } from '/imports/collections/users/index'; -export default composeWithTracker(composer, {}, {}, { pure: false })(AppNavigation); +import { AppNavigation } from './AppNavigation'; + +const meteorTick = (props, onData) => { + + const handles = [ + Meteor.subscribe('users.current') + ]; + + if(_.every(handles, (handle) => (handle.ready()) )) { + const user = Users.current(); + onData(null, { + data: { + user: user, + }, + }); + } + + return () => { + _.each(handles, (handle) => handle.stop() ); + }; +}; + + +const reduxTick = (props, onData) => { + onData(null, { + data: {} + }); +}; + + +export const AppNavigationController = composeAll( + composeWithTracker(meteorTick, Loading), + compose(reduxTick, Loading), +)(AppNavigation); diff --git a/imports/client/views/org/enter/ForgotPane.js b/imports/client/views/org/enter/ForgotPane.js new file mode 100644 index 0000000..47292ac --- /dev/null +++ b/imports/client/views/org/enter/ForgotPane.js @@ -0,0 +1,69 @@ +import React from 'react'; +import { Link } from 'react-router'; +import { ActionButton } from '/imports/client/components/ActionButton'; +import { setQueryParam } from '/imports/client/app/utils/setQueryParam'; +import { Button, Form, FormGroup, Label, Input, FormText, Col } from 'reactstrap'; +import { Alert } from 'reactstrap'; +import 'velocity-animate'; +import 'velocity-animate/velocity.ui'; +import { VelocityComponent, VelocityTransitionGroup, velocityHelpers } from 'velocity-react'; +import ReactSVG from 'react-svg' + +export class ForgotPane extends React.Component { + + render() { + return ( + + +
+ + + +
+
+ Reset your password +
+ + { this.props.data.error } + + + { this.props.data.message } + +
this.props.onForgot(e) }> + + this.props.onUpdate('email', e.currentTarget.value) } + name="email" + id="email" + placeholder="Enter your email" /> + + + + + + + + + Log In + + +
+
+
+
+ + ); + } +} diff --git a/imports/client/views/org/enter/LoginPane.js b/imports/client/views/org/enter/LoginPane.js new file mode 100644 index 0000000..729f8d0 --- /dev/null +++ b/imports/client/views/org/enter/LoginPane.js @@ -0,0 +1,78 @@ +import React from 'react'; +import { Link } from 'react-router'; +import { ActionButton } from '/imports/client/components/ActionButton'; +import { setQueryParam } from '/imports/client/app/utils/setQueryParam'; +import { Button, Form, FormGroup, Label, Input, FormText, Col } from 'reactstrap'; +import { Alert } from 'reactstrap'; +import 'velocity-animate'; +import 'velocity-animate/velocity.ui'; +import { VelocityComponent, VelocityTransitionGroup, velocityHelpers } from 'velocity-react'; +import ReactSVG from 'react-svg' + + +export class LoginPane extends React.Component { + + render() { + return ( + + +
+ + + +
+
+ Login +
+ + { this.props.data.error } + + + { this.props.data.message } + +
this.props.onLogin(e) }> + + this.props.onUpdate('email', e.currentTarget.value) } + name="email" + id="email" + placeholder="Enter your email" /> + + + this.props.onUpdate('password', e.currentTarget.value) } + name="password" + id="password" + placeholder="Enter your password" /> + + + + + + + + + Forgotten password? + + +
+
+
+
+ + ); + } +} diff --git a/imports/client/views/org/enter/RecoverPassword.js b/imports/client/views/org/enter/RecoverPassword.js deleted file mode 100644 index ba4adc5..0000000 --- a/imports/client/views/org/enter/RecoverPassword.js +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react'; -import { Row, Col, Alert, FormGroup, FormControl, Button } from 'react-bootstrap'; -import handleRecoverPassword from './module/recover-password'; - -export default class RecoverPassword extends React.Component { - componentDidMount() { - handleRecoverPassword({ component: this }); - } - - handleSubmit(event) { - event.preventDefault(); - } - - render() { - return ( -
- - -

Recover Password

- - Enter your email address below to receive a link to reset your password. - -
(this.recoverPasswordForm = form) } - className="recover-password" - onSubmit={ this.handleSubmit } - > - - - - -
- -
-
- ); - } -} diff --git a/imports/client/views/org/enter/ResetPane.js b/imports/client/views/org/enter/ResetPane.js new file mode 100644 index 0000000..bd2a9b4 --- /dev/null +++ b/imports/client/views/org/enter/ResetPane.js @@ -0,0 +1,63 @@ +import React from 'react'; +import { Link } from 'react-router'; +import { ActionButton } from '/imports/client/components/ActionButton'; +import { setQueryParam } from '/imports/client/app/utils/setQueryParam'; +import { Button, Form, FormGroup, Label, Input, FormText, Col } from 'reactstrap'; +import { Alert } from 'reactstrap'; +import 'velocity-animate'; +import 'velocity-animate/velocity.ui'; +import { VelocityComponent, VelocityTransitionGroup, velocityHelpers } from 'velocity-react'; +import ReactSVG from 'react-svg' + + +export class ResetPane extends React.Component { + + render() { + return ( + + +
+ + + +
+
+ Set new password +
+ + { this.props.data.error } + + + { this.props.data.message } + +
this.props.onReset(e) }> + + this.props.onUpdate('password', e.currentTarget.value) } + name="password" + id="password" + placeholder="Set new password" /> + + + + + + +
+
+
+
+ + ); + } +} diff --git a/imports/client/views/org/enter/ResetPassword.js b/imports/client/views/org/enter/ResetPassword.js deleted file mode 100644 index c4893aa..0000000 --- a/imports/client/views/org/enter/ResetPassword.js +++ /dev/null @@ -1,58 +0,0 @@ -import React from 'react'; -import { Row, Col, Alert, FormGroup, ControlLabel, FormControl, Button } from 'react-bootstrap'; -import handleResetPassword from './module/reset-password'; - -export default class ResetPassword extends React.Component { - componentDidMount() { - handleResetPassword({ component: this, token: this.props.params.token }); - } - - handleSubmit(event) { - event.preventDefault(); - } - - render() { - return ( -
- - -

Reset Password

- - To reset your password, enter a new one below. You will be logged in - with your new password. - -
(this.resetPasswordForm = form) } - className="reset-password" - onSubmit={ this.handleSubmit } - > - - New Password - - - - Repeat New Password - - - -
- -
-
- ); - } -} - -ResetPassword.propTypes = { - params: React.PropTypes.object, -}; diff --git a/imports/client/views/org/enter/SignupPane.js b/imports/client/views/org/enter/SignupPane.js new file mode 100644 index 0000000..df7056a --- /dev/null +++ b/imports/client/views/org/enter/SignupPane.js @@ -0,0 +1,91 @@ +import React from 'react'; +import { Link } from 'react-router'; +import { ActionButton } from '/imports/client/components/ActionButton'; +import { setQueryParam } from '/imports/client/app/utils/setQueryParam'; +import { Button, Form, FormGroup, Label, Input, FormText, Col } from 'reactstrap'; +import 'velocity-animate'; +import 'velocity-animate/velocity.ui'; +import { VelocityComponent, VelocityTransitionGroup, velocityHelpers } from 'velocity-react'; +import ReactSVG from 'react-svg' + + +export class SignupPane extends React.Component { + + render() { + return ( + + +
+ + + +
+
+ Start a free 30-day trial +
+
this.props.onSignup(e) }> + + this.props.onUpdate('firstName', e.currentTarget.value) } + name="firstname" + id="firstname" + placeholder="Enter your first name" /> + + + this.props.onUpdate('lastName', e.currentTarget.value) } + name="lastname" + id="lastname" + placeholder="Enter your last name" /> + + + this.props.onUpdate('orgName', e.currentTarget.value) } + name="companyname" + id="companyname" + placeholder="Enter your company name" /> + + + this.props.onUpdate('email', e.currentTarget.value) } + name="email" + id="email" + placeholder="Enter your email address" /> + + + this.props.onUpdate('password', e.currentTarget.value) } + name="password" + id="password" + placeholder="Your password" /> + + + + +
+
+ + + Already have an account? Log in. + + +
+
+ + ); + } +} diff --git a/imports/client/views/org/enter/_old/check-email.js b/imports/client/views/org/enter/_old/check-email.js new file mode 100644 index 0000000..c1b5b2c --- /dev/null +++ b/imports/client/views/org/enter/_old/check-email.js @@ -0,0 +1,24 @@ +import React from 'react'; +import { Link } from 'react-router'; +import { Row, Col } from 'react-bootstrap'; + +export class CheckEmail extends React.Component { + handleSubmit(event) { + event.preventDefault(); + } + + render() { + const signedEmail = Session.get('signedEmail', ''); + + return + +
+ +

Verify Your Email

+

We sent a verification email to {signedEmail}. Click the link in the email to get started!

+ Email did not arrive or want to use a different email? +
+ +
; + } +} diff --git a/imports/client/views/org/enter/_old/recover-password.js b/imports/client/views/org/enter/_old/recover-password.js new file mode 100644 index 0000000..10017ce --- /dev/null +++ b/imports/client/views/org/enter/_old/recover-password.js @@ -0,0 +1,35 @@ +import React from 'react'; +import { Row, Col, Alert, FormGroup, FormControl, Button } from 'react-bootstrap'; +import { handleRecoverPassword } from '../../modules/recover-password'; + +export class RecoverPassword extends React.Component { + componentDidMount() { + handleRecoverPassword({ component: this }); + } + + handleSubmit(event) { + event.preventDefault(); + } + + render() { + return + +

Recover Password

+ + Enter your email address below to receive a link to reset your password. + +
+ + + + +
+ +
; + } +} diff --git a/imports/client/views/org/enter/_old/reset-password.js b/imports/client/views/org/enter/_old/reset-password.js new file mode 100644 index 0000000..ca52b17 --- /dev/null +++ b/imports/client/views/org/enter/_old/reset-password.js @@ -0,0 +1,53 @@ +import React from 'react'; +import { Row, Col, Alert, FormGroup, ControlLabel, FormControl, Button } from 'react-bootstrap'; +import { handleResetPassword } from '../../modules/reset-password'; + +export class ResetPassword extends React.Component { + componentDidMount() { + handleResetPassword({ + component: this, + token: this.props.params.token, + }); + } + + handleSubmit(event) { + event.preventDefault(); + } + + render() { + return + +

Reset Password

+ + To reset your password, enter a new one below. You will be logged in +with your new password. + +
+ + New Password + + + + Repeat New Password + + + +
+ +
; + } +} + +ResetPassword.propTypes = { + params: React.PropTypes.object, +}; diff --git a/imports/client/views/org/enter/_old/signup.js b/imports/client/views/org/enter/_old/signup.js new file mode 100644 index 0000000..16acb67 --- /dev/null +++ b/imports/client/views/org/enter/_old/signup.js @@ -0,0 +1,68 @@ +import React from 'react'; +import { Link } from 'react-router'; +import { Row, Col, FormGroup, ControlLabel, FormControl, Button } from 'react-bootstrap'; +import { handleSignup } from '../../modules/signup'; + +export class Signup extends React.Component { + componentDidMount() { + handleSignup({ component: this }); + } + + handleSubmit(event) { + event.preventDefault(); + } + + render() { + return + +

Sign Up

+
+ + + + First Name + + + + + + Last Name + + + + + + Email Address + + + + Password + + + +
+

Already have an account? Log In.

+ +
; + } +} diff --git a/imports/client/views/org/enter/login/LoginView.js b/imports/client/views/org/enter/login/LoginView.js deleted file mode 100644 index 31f67bf..0000000 --- a/imports/client/views/org/enter/login/LoginView.js +++ /dev/null @@ -1,56 +0,0 @@ -import React,{ Component } from 'react'; -import { Link } from 'react-router'; -import { Row, Col, FormGroup, - ControlLabel, FormControl, - Button } from 'react-bootstrap'; -import handleLogin from './login'; - -export class LoginView extends React.Component { - componentDidMount() { - handleLogin({ component: this }); - } - - handleSubmit(event) { - event.preventDefault(); - } - - render() { - return ( -
- - -

Login

-
(this.loginForm = form) } - className="login" - onSubmit={ this.handleSubmit } - > - - Email Address - - - - - Password - Forgot Password? - - - - -
- -
-
- ); - } -} diff --git a/imports/client/views/org/enter/login/index.js b/imports/client/views/org/enter/login/index.js deleted file mode 100644 index 4959901..0000000 --- a/imports/client/views/org/enter/login/index.js +++ /dev/null @@ -1,40 +0,0 @@ -// import { InviteSignupController } from '/imports/client/views/invite/signup/index' -import _ from 'lodash'; -import { - composeWithTracker, - compose, - composeAll - } from 'react-komposer'; -import { Loading } from '/imports/client/components/Loading'; -import { Orgs } from '/imports/collections/orgs/index'; -import { LoginView } from './LoginView' - -const meteorTick = (props, onData) => { - - const handles = [ - ]; - - if(_.every(handles, (handle) => (handle.ready()) )) { - onData(null, { - data: { - }, - }); - } - - return () => { - _.each(handles, (handle) => handle.stop() ); - }; -}; - - -const reduxTick = (props, onData) => { - onData(null, { - data: {} - }); -}; - - -export const orgLoginController = composeAll( - composeWithTracker(meteorTick, Loading), - compose(reduxTick, Loading), -)(LoginView); diff --git a/imports/client/views/org/enter/login/login.js b/imports/client/views/org/enter/login/login.js deleted file mode 100644 index a83a0d3..0000000 --- a/imports/client/views/org/enter/login/login.js +++ /dev/null @@ -1,57 +0,0 @@ -/* eslint-disable no-undef */ - -import { browserHistory } from 'react-router'; -import { Meteor } from 'meteor/meteor'; -import { Bert } from 'meteor/themeteorchef:bert'; -import '/imports/client/components/validation'; - -let component; - -const login = () => { - const email = document.querySelector('[name="emailAddress"]').value; - const password = document.querySelector('[name="password"]').value; - - Meteor.loginWithPassword(email, password, (error) => { - if (error) { - Bert.alert(error.reason, 'warning'); - } else { - Bert.alert('Logged in!', 'success'); - - const { location } = component.props; - if (location.state && location.state.nextPathname) { - browserHistory.push(location.state.nextPathname); - } else { - browserHistory.push('/'); - } - } - }); -}; - -const validate = () => { - $(component.loginForm).validate({ - rules: { - emailAddress: { - required: true, - email: true, - }, - password: { - required: true, - }, - }, - messages: { - emailAddress: { - required: 'Need an email address here.', - email: 'Is this email address legit?', - }, - password: { - required: 'Need a password here.', - }, - }, - submitHandler() { login(); }, - }); -}; - -export default function handleLogin(options) { - component = options.component; - validate(); -} diff --git a/imports/client/views/org/enter/module/EnterLayout.js b/imports/client/views/org/enter/module/EnterLayout.js new file mode 100644 index 0000000..7a5a45b --- /dev/null +++ b/imports/client/views/org/enter/module/EnterLayout.js @@ -0,0 +1,166 @@ +import _ from 'lodash'; +import { Meteor } from 'meteor/meteor'; +import { SimpleSchema } from 'meteor/aldeed:simple-schema'; +import { Bert } from 'meteor/themeteorchef:bert'; + +import React from 'react'; +import { Container, Row, Col } from 'reactstrap'; +import { Link } from 'react-router'; +import { If, Case } from '/imports/client/components/Logic'; + +import { LoginPane } from '/imports/client/views/org/enter/LoginPane'; +import { ForgotPane } from '/imports/client/views/org/enter/ForgotPane'; +import { ResetPane } from '/imports/client/views/org/enter/ResetPane'; +import Validation from '/imports/validation/validationMethods'; + +// const signupSchemaValidator = new SimpleSchema({ +// email: { type: String, regEx: SimpleSchema.RegEx.Email}, +// firstName: { type: String, }, +// lastName: { type: String, }, +// password: { type: String, min: 6}, +// }).validator(); + +let validation = new Validation(); + +export class EnterLayout extends React.Component { + + + constructor(props) { + super(props); + this.state = { + email: '', + password: '', + firstName: '', + lastName: '', + orgName: '', + loading: false, + error: '', + message: '', + }; + }; + + componentWillReceiveProps(nextProps) { + console.log(this.props.pane); + console.log("this.props.pane"); + if(this.props.pane !== nextProps.pane) { + this.onClearState(); + } + }; + + + onClearState(loading) { + this.setState({ + loading: loading || false, + error: '', + message: '', + }); + }; + + onUpdate(key, value) { + this.setState({[key]: value}); + }; + + onForgot(e) { + e.preventDefault(); + this.onClearState(true); + + Accounts.forgotPassword({email: this.state.email || '...'}, (error) => { + this.onClearState(); + if(error) { + this.setState({error: 'An error occured.'}); + } else { + this.setState({message: 'Reset email has been sent. Check your inbox.'}); + } + }); + }; + + onReset(e) { + e.preventDefault(); + this.onClearState(true); + if(!validation.passwordValidation(this.state.password)){ + Bert.alert('Password must be a minimum of 6 characters in length.', 'danger'); + this.onClearState(); + return false; + }else if(!validation.noQwertysAllowed(this.state.password)){ + Bert.alert('No qwertys allowed!', 'danger'); + this.onClearState(); + return false; + } else{ + Accounts.resetPassword( + this.props.location.query.token, + this.state.password, + (error) => { + this.onClearState(); + if(error) { + this.setState({error: 'An error occured.'}); + } + } + ); + } + }; + + onLogin(e) { + e.preventDefault(); + this.onClearState(true); + if(this.state.email.trim() == '' || !validation.validateEmail(this.state.email)){ + this.onClearState(); + Bert.alert('Please enter a valid email address!', 'danger'); + return false; + } + if(this.state.password.trim() == ''){ + Bert.alert('Please enter your password!', 'danger'); + this.onClearState(); + return false; + } + Meteor.loginWithPassword( + this.state.email, + this.state.password, + (e, r) => { + this.onClearState(); + if(e) { + this.setState({error: 'Wrong email or password.'}) + } + } + ); + }; + + render() { + return ( +
+ + + ( + this.onUpdate(...a)} + onForgot = {(...a) => this.onForgot(...a)} + /> + )} + case1 = "reset" + then1 = {() => ( + this.onUpdate(...a)} + onReset = {(...a) => this.onReset(...a)} + /> + )} + else = {() => ( + this.onUpdate(...a)} + onLogin = {(...a) => this.onLogin(...a)} + /> + )} + /> + + +
+ ); + }; +}; diff --git a/imports/client/views/org/enter/module/index.js b/imports/client/views/org/enter/module/index.js new file mode 100644 index 0000000..f529f43 --- /dev/null +++ b/imports/client/views/org/enter/module/index.js @@ -0,0 +1,46 @@ +// import { EnterModule } from '/imports/client/views/enter/module/index' +import { + composeWithTracker, + compose, + composeAll + } from 'react-komposer'; +import { EnterLayout } from './EnterLayout'; +import { Loading } from '/imports/client/components/Loading'; + + +const meteorTick = (props, onData) => { + + const handles = [ + ]; + + if(_.every(handles, (handle) => (handle.ready()) )) { + onData(null, { + location: props.location, + data: { + }, + }); + } + + return () => { + _.each(handles, (handle) => handle.stop() ); + }; +}; + + +const reduxTick = (props, onData) => { + onData(null, { + data: {} + }); +}; + + +export const EnterModule = composeAll( + composeWithTracker(meteorTick, Loading), + compose(reduxTick, Loading), +)(EnterLayout); + + + + + + diff --git a/imports/client/views/org/enter/module/recover-password.js b/imports/client/views/org/enter/module/recover-password.js deleted file mode 100644 index 60aa93a..0000000 --- a/imports/client/views/org/enter/module/recover-password.js +++ /dev/null @@ -1,42 +0,0 @@ -/* eslint-disable no-undef */ - -import { Accounts } from 'meteor/accounts-base'; -import { Bert } from 'meteor/themeteorchef:bert'; -import '/imports/client/components/validation'; - -let component; - -const handleRecovery = () => { - Accounts.forgotPassword({ - email: document.querySelector('[name="emailAddress"]').value, - }, (error) => { - if (error) { - Bert.alert(error.reason, 'warning'); - } else { - Bert.alert('Check your inbox for a reset link!', 'success'); - } - }); -}; - -const validate = () => { - $(component.recoverPasswordForm).validate({ - rules: { - emailAddress: { - required: true, - email: true, - }, - }, - messages: { - emailAddress: { - required: 'Need an email address here.', - email: 'Is this email address legit?', - }, - }, - submitHandler() { handleRecovery(); }, - }); -}; - -export default function handleRecoverPassword(options) { - component = options.component; - validate(); -} diff --git a/imports/client/views/org/enter/module/reset-password.js b/imports/client/views/org/enter/module/reset-password.js deleted file mode 100644 index 614a18e..0000000 --- a/imports/client/views/org/enter/module/reset-password.js +++ /dev/null @@ -1,54 +0,0 @@ -/* eslint-disable no-undef */ - -import { browserHistory } from 'react-router'; -import { Accounts } from 'meteor/accounts-base'; -import { Bert } from 'meteor/themeteorchef:bert'; -import '/imports/client/components/validation'; - -let component; -let token; - -const handleReset = () => { - const password = document.querySelector('[name="newPassword"]').value; - Accounts.resetPassword(token, password, (error) => { - if (error) { - Bert.alert(error.reason, 'danger'); - } else { - browserHistory.push('/'); - Bert.alert('Password reset!', 'success'); - } - }); -}; - -const validate = () => { - $(component.resetPasswordForm).validate({ - rules: { - newPassword: { - required: true, - minlength: 6, - }, - repeatNewPassword: { - required: true, - minlength: 6, - equalTo: '[name="newPassword"]', - }, - }, - messages: { - newPassword: { - required: 'Enter a new password, please.', - minlength: 'Use at least six characters, please.', - }, - repeatNewPassword: { - required: 'Repeat your new password, please.', - equalTo: 'Hmm, your passwords don\'t match. Try again?', - }, - }, - submitHandler() { handleReset(); }, - }); -}; - -export default function handleResetPassword(options) { - component = options.component; - token = options.token; - validate(); -} diff --git a/imports/client/views/org/enter_1/RecoverPassword.js b/imports/client/views/org/enter_1/RecoverPassword.js new file mode 100644 index 0000000..ba4adc5 --- /dev/null +++ b/imports/client/views/org/enter_1/RecoverPassword.js @@ -0,0 +1,43 @@ +import React from 'react'; +import { Row, Col, Alert, FormGroup, FormControl, Button } from 'react-bootstrap'; +import handleRecoverPassword from './module/recover-password'; + +export default class RecoverPassword extends React.Component { + componentDidMount() { + handleRecoverPassword({ component: this }); + } + + handleSubmit(event) { + event.preventDefault(); + } + + render() { + return ( +
+ + +

Recover Password

+ + Enter your email address below to receive a link to reset your password. + +
(this.recoverPasswordForm = form) } + className="recover-password" + onSubmit={ this.handleSubmit } + > + + + + +
+ +
+
+ ); + } +} diff --git a/imports/client/views/org/enter_1/ResetPassword.js b/imports/client/views/org/enter_1/ResetPassword.js new file mode 100644 index 0000000..c4893aa --- /dev/null +++ b/imports/client/views/org/enter_1/ResetPassword.js @@ -0,0 +1,58 @@ +import React from 'react'; +import { Row, Col, Alert, FormGroup, ControlLabel, FormControl, Button } from 'react-bootstrap'; +import handleResetPassword from './module/reset-password'; + +export default class ResetPassword extends React.Component { + componentDidMount() { + handleResetPassword({ component: this, token: this.props.params.token }); + } + + handleSubmit(event) { + event.preventDefault(); + } + + render() { + return ( +
+ + +

Reset Password

+ + To reset your password, enter a new one below. You will be logged in + with your new password. + +
(this.resetPasswordForm = form) } + className="reset-password" + onSubmit={ this.handleSubmit } + > + + New Password + + + + Repeat New Password + + + +
+ +
+
+ ); + } +} + +ResetPassword.propTypes = { + params: React.PropTypes.object, +}; diff --git a/imports/client/views/org/enter_1/login/LoginView.js b/imports/client/views/org/enter_1/login/LoginView.js new file mode 100644 index 0000000..31f67bf --- /dev/null +++ b/imports/client/views/org/enter_1/login/LoginView.js @@ -0,0 +1,56 @@ +import React,{ Component } from 'react'; +import { Link } from 'react-router'; +import { Row, Col, FormGroup, + ControlLabel, FormControl, + Button } from 'react-bootstrap'; +import handleLogin from './login'; + +export class LoginView extends React.Component { + componentDidMount() { + handleLogin({ component: this }); + } + + handleSubmit(event) { + event.preventDefault(); + } + + render() { + return ( +
+ + +

Login

+
(this.loginForm = form) } + className="login" + onSubmit={ this.handleSubmit } + > + + Email Address + + + + + Password + Forgot Password? + + + + +
+ +
+
+ ); + } +} diff --git a/imports/client/views/org/enter_1/login/index.js b/imports/client/views/org/enter_1/login/index.js new file mode 100644 index 0000000..a196396 --- /dev/null +++ b/imports/client/views/org/enter_1/login/index.js @@ -0,0 +1,40 @@ +// import { orgLoginController } from '/imports/client/views/org/enter/login/index'; +import _ from 'lodash'; +import { + composeWithTracker, + compose, + composeAll + } from 'react-komposer'; +import { Loading } from '/imports/client/components/Loading'; +import { Orgs } from '/imports/collections/orgs/index'; +import { LoginView } from './LoginView' + +const meteorTick = (props, onData) => { + + const handles = [ + ]; + + if(_.every(handles, (handle) => (handle.ready()) )) { + onData(null, { + data: { + }, + }); + } + + return () => { + _.each(handles, (handle) => handle.stop() ); + }; +}; + + +const reduxTick = (props, onData) => { + onData(null, { + data: {} + }); +}; + + +export const orgLoginController = composeAll( + composeWithTracker(meteorTick, Loading), + compose(reduxTick, Loading), +)(LoginView); diff --git a/imports/client/views/org/enter_1/login/login.js b/imports/client/views/org/enter_1/login/login.js new file mode 100644 index 0000000..a83a0d3 --- /dev/null +++ b/imports/client/views/org/enter_1/login/login.js @@ -0,0 +1,57 @@ +/* eslint-disable no-undef */ + +import { browserHistory } from 'react-router'; +import { Meteor } from 'meteor/meteor'; +import { Bert } from 'meteor/themeteorchef:bert'; +import '/imports/client/components/validation'; + +let component; + +const login = () => { + const email = document.querySelector('[name="emailAddress"]').value; + const password = document.querySelector('[name="password"]').value; + + Meteor.loginWithPassword(email, password, (error) => { + if (error) { + Bert.alert(error.reason, 'warning'); + } else { + Bert.alert('Logged in!', 'success'); + + const { location } = component.props; + if (location.state && location.state.nextPathname) { + browserHistory.push(location.state.nextPathname); + } else { + browserHistory.push('/'); + } + } + }); +}; + +const validate = () => { + $(component.loginForm).validate({ + rules: { + emailAddress: { + required: true, + email: true, + }, + password: { + required: true, + }, + }, + messages: { + emailAddress: { + required: 'Need an email address here.', + email: 'Is this email address legit?', + }, + password: { + required: 'Need a password here.', + }, + }, + submitHandler() { login(); }, + }); +}; + +export default function handleLogin(options) { + component = options.component; + validate(); +} diff --git a/imports/client/views/org/enter_1/module/recover-password.js b/imports/client/views/org/enter_1/module/recover-password.js new file mode 100644 index 0000000..60aa93a --- /dev/null +++ b/imports/client/views/org/enter_1/module/recover-password.js @@ -0,0 +1,42 @@ +/* eslint-disable no-undef */ + +import { Accounts } from 'meteor/accounts-base'; +import { Bert } from 'meteor/themeteorchef:bert'; +import '/imports/client/components/validation'; + +let component; + +const handleRecovery = () => { + Accounts.forgotPassword({ + email: document.querySelector('[name="emailAddress"]').value, + }, (error) => { + if (error) { + Bert.alert(error.reason, 'warning'); + } else { + Bert.alert('Check your inbox for a reset link!', 'success'); + } + }); +}; + +const validate = () => { + $(component.recoverPasswordForm).validate({ + rules: { + emailAddress: { + required: true, + email: true, + }, + }, + messages: { + emailAddress: { + required: 'Need an email address here.', + email: 'Is this email address legit?', + }, + }, + submitHandler() { handleRecovery(); }, + }); +}; + +export default function handleRecoverPassword(options) { + component = options.component; + validate(); +} diff --git a/imports/client/views/org/enter_1/module/reset-password.js b/imports/client/views/org/enter_1/module/reset-password.js new file mode 100644 index 0000000..614a18e --- /dev/null +++ b/imports/client/views/org/enter_1/module/reset-password.js @@ -0,0 +1,54 @@ +/* eslint-disable no-undef */ + +import { browserHistory } from 'react-router'; +import { Accounts } from 'meteor/accounts-base'; +import { Bert } from 'meteor/themeteorchef:bert'; +import '/imports/client/components/validation'; + +let component; +let token; + +const handleReset = () => { + const password = document.querySelector('[name="newPassword"]').value; + Accounts.resetPassword(token, password, (error) => { + if (error) { + Bert.alert(error.reason, 'danger'); + } else { + browserHistory.push('/'); + Bert.alert('Password reset!', 'success'); + } + }); +}; + +const validate = () => { + $(component.resetPasswordForm).validate({ + rules: { + newPassword: { + required: true, + minlength: 6, + }, + repeatNewPassword: { + required: true, + minlength: 6, + equalTo: '[name="newPassword"]', + }, + }, + messages: { + newPassword: { + required: 'Enter a new password, please.', + minlength: 'Use at least six characters, please.', + }, + repeatNewPassword: { + required: 'Repeat your new password, please.', + equalTo: 'Hmm, your passwords don\'t match. Try again?', + }, + }, + submitHandler() { handleReset(); }, + }); +}; + +export default function handleResetPassword(options) { + component = options.component; + token = options.token; + validate(); +} diff --git a/imports/startup/client/index.js b/imports/startup/client/index.js deleted file mode 100644 index dde0719..0000000 --- a/imports/startup/client/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import { Bert } from 'meteor/themeteorchef:bert'; -import 'bootstrap/dist/css/bootstrap.min.css'; -import './routes.js'; - -Bert.defaults.style = 'growl-top-right'; diff --git a/imports/startup/client/routes.js b/imports/startup/client/routes.js deleted file mode 100644 index 1dfcb3e..0000000 --- a/imports/startup/client/routes.js +++ /dev/null @@ -1,109 +0,0 @@ -/* eslint-disable max-len */ - -import React from 'react'; -import { render } from 'react-dom'; -import { Router, Route, - IndexRoute, browserHistory } from 'react-router'; -import { Meteor } from 'meteor/meteor'; - -/** - * General Components - */ -import Index from '/imports/client/views/app/module/Index'; - -/** - * Org Components - */ - -import App from '/imports/client/layouts/OrgApp'; -import { AppModule } from '/imports/client/views/org/app/module/Index'; -import { orgLoginController } from '/imports/client/views/org/enter/login/index'; -import RecoverPassword from '/imports/client/views/org/enter/RecoverPassword'; -import ResetPassword from '/imports/client/views/org/enter/ResetPassword'; -import { Orgs } from '/imports/collections/orgs/index'; -import NotFound from '/imports/client/views/org/NotFound'; - -/** - * NonOrg Components - */ -import Signup from '/imports/client/views/nonOrg/enter/SignupView'; - -/** - * Invalid Org Components - */ - -const authenticate = (nextState, replace) => { - if (!Meteor.loggingIn() && !Meteor.userId()) { - replace({ - pathname: '/login', - state: { nextPathname: nextState.location.pathname }, - }); - } -}; - - -const detectOrg = () => { - orgSlug = ""; - var hostnameArray = document.location.hostname.split( "." ); - if(hostnameArray[1]=='localhost'){ - orgSlug = hostnameArray[0]; - } - if(orgSlug!=""){ - Meteor.call('checkExistingOrg', {slug:orgSlug}, function(err, res) { - if(res){ - Session.set('orgId', res._id._str); - render(getOrgRoutes(),document.getElementById('app')); - }else{ - render(getInvalidOrgRoute(),document.getElementById('app')); - } - }); - }else{ - render(getNonOrgRoutes(),document.getElementById('app')); - } -} -const checkSlug = (nextState, replace) => { - orgId = Session.get('orgId'); -} - -/** -There are three types of routes -1)getOrgRoutes: all the routes that should be present for a registered org -2)getInvalidOrgRoute: all the routes where someone tries to enter a subdomain which hasn't been registered yet (404 mostly :D) -3)getNonOrgRoutes: all routes linked to normal site, ie signing up a new org. CHeking out demo and everything internal -**/ -const getOrgRoutes = () => ( - - - - - - - - - -) - - -const getInvalidOrgRoute = () => ( - - - - - - -) - -const getNonOrgRoutes = () => ( - - - - - - - -) - - -Meteor.startup(() => { - detectOrg(); -}); diff --git a/imports/validation/validationMethods.js b/imports/validation/validationMethods.js new file mode 100644 index 0000000..d5b0ba5 --- /dev/null +++ b/imports/validation/validationMethods.js @@ -0,0 +1,101 @@ +export default class Validation{ + + validateEmail (value) { + // regex from http://stackoverflow.com/questions/46155/validate-email-address-in-javascript + 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,}))$/; + return re.test(value); + }; + + // containsNoSpecialCharacters(str){ + // return !/[~`!#$%\^&*+=\-\[\]\\';,/{}|\\":<>\?]/g.test(str); + // } + + noSpecialChars(str){ + str = String(str); + return !/[~`!#$%\^&*+=\-\[\]\\';,/{}|\\":<>\?]/g.test(str); + } + + noQwertysAllowed (str){ + str = str.toLowerCase(); + if(str.toLowerCase().indexOf("qwerty") >-1){ + return false; + } else{ + return true; + } + } + + passwordValidation (str){ + if(str.length <6){ + return false; + } else{ + return true; + } + } + + isNumberOnly(str){ + if(!/^\d+$/.test(str)){ + return false; + }else { + return true; + } + } + isPositiveNotZeroNumber(str){ + str = parseFloat(str); + if(str <=0){ + return false; + }else { + return true; + } + } + isNumeric(n) { + return !isNaN(parseFloat(n)) && isFinite(n); + } + + isValidACN(str){ + str = String(str); + //Remove any whitespace. + str = str.replace(/\s+/g, ''); + console.log(str); + console.log(str.length); + if(/^\d+$/.test(str) && str.length ==9){ + return true; + } else{ + return false; + } + } + isValidShortCode(str){ + str = String(str); + console.log(str); + if(str.length < 5 && str.length >2){ + return true; + } else{ + return false; + } + } + containsNumbers (str){ + if(/\d/g.test(str)){ + return true; + }else{ + return false; + } + } + isInt(n){ + console.log(typeof Number(n)); + return Number(n) % 1 === 0; + } + + isFloat(n){ + return Number(n) % 1 !== 0; + } + decimalPlaces(num) { + num = parseFloat(num); + var match = (''+num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/); + if (!match) { return 0; } + return Math.max( + 0, + // Number of digits right of decimal point. + (match[1] ? match[1].length : 0) + // Adjust for scientific notation. + - (match[2] ? +match[2] : 0)); + } +}; diff --git a/package.json b/package.json index 607925b..5de9f9b 100644 --- a/package.json +++ b/package.json @@ -68,12 +68,17 @@ "jquery": "^2.2.4", "jquery-validation": "^1.15.1", "react": "^15.3.2", + "react-addons-css-transition-group": "^15.4.2", "react-addons-pure-render-mixin": "^15.3.2", + "react-addons-transition-group": "^15.4.2", "react-bootstrap": "^0.30.5", "react-dom": "^15.3.2", "react-komposer": "^1.13.1", "react-router": "^2.6.1", "react-router-bootstrap": "^0.23.1", - "react-svg": "^2.1.19" + "react-svg": "^2.1.19", + "reactstrap": "^4.2.0", + "velocity-animate": "^1.4.3", + "velocity-react": "^1.2.1" } }