aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/app.js2
-rw-r--r--src/components/auth/Invite.js64
-rw-r--r--src/components/settings/account/AccountDashboard.js7
-rw-r--r--src/components/settings/navigation/SettingsNavigation.js11
-rw-r--r--src/components/settings/services/ServiceError.js2
-rw-r--r--src/components/settings/user/EditUserForm.js2
-rw-r--r--src/components/ui/Link.js5
-rw-r--r--src/containers/auth/InviteScreen.js21
-rw-r--r--src/containers/settings/AccountScreen.js6
-rw-r--r--src/containers/settings/InviteScreen.js63
-rw-r--r--src/i18n/locales/de.json1
-rw-r--r--src/i18n/locales/en-US.json4
-rw-r--r--src/i18n/locales/es.json1
-rw-r--r--src/i18n/locales/fr.json1
-rw-r--r--src/i18n/locales/it.json1
-rw-r--r--src/models/User.js2
-rw-r--r--src/stores/UserStore.js17
-rw-r--r--src/styles/invite.scss15
-rw-r--r--src/styles/main.scss1
19 files changed, 203 insertions, 23 deletions
diff --git a/src/app.js b/src/app.js
index 8e62776d2..edcf273dc 100644
--- a/src/app.js
+++ b/src/app.js
@@ -27,6 +27,7 @@ import EditServiceScreen from './containers/settings/EditServiceScreen';
27import AccountScreen from './containers/settings/AccountScreen'; 27import AccountScreen from './containers/settings/AccountScreen';
28import EditUserScreen from './containers/settings/EditUserScreen'; 28import EditUserScreen from './containers/settings/EditUserScreen';
29import EditSettingsScreen from './containers/settings/EditSettingsScreen'; 29import EditSettingsScreen from './containers/settings/EditSettingsScreen';
30import InviteSettingsScreen from './containers/settings/InviteScreen';
30import WelcomeScreen from './containers/auth/WelcomeScreen'; 31import WelcomeScreen from './containers/auth/WelcomeScreen';
31import LoginScreen from './containers/auth/LoginScreen'; 32import LoginScreen from './containers/auth/LoginScreen';
32import PasswordScreen from './containers/auth/PasswordScreen'; 33import PasswordScreen from './containers/auth/PasswordScreen';
@@ -74,6 +75,7 @@ window.addEventListener('load', () => {
74 <Route path="/settings/user" component={AccountScreen} /> 75 <Route path="/settings/user" component={AccountScreen} />
75 <Route path="/settings/user/edit" component={EditUserScreen} /> 76 <Route path="/settings/user/edit" component={EditUserScreen} />
76 <Route path="/settings/app" component={EditSettingsScreen} /> 77 <Route path="/settings/app" component={EditSettingsScreen} />
78 <Route path="/settings/invite" component={InviteSettingsScreen} />
77 </Route> 79 </Route>
78 </Route> 80 </Route>
79 <Route path="/auth" component={AuthLayoutContainer}> 81 <Route path="/auth" component={AuthLayoutContainer}>
diff --git a/src/components/auth/Invite.js b/src/components/auth/Invite.js
index c6dca3a65..2dcce7f83 100644
--- a/src/components/auth/Invite.js
+++ b/src/components/auth/Invite.js
@@ -3,7 +3,10 @@ import PropTypes from 'prop-types';
3import { observer } from 'mobx-react'; 3import { observer } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl'; 4import { defineMessages, intlShape } from 'react-intl';
5import { Link } from 'react-router'; 5import { Link } from 'react-router';
6import classnames from 'classnames';
6 7
8import Infobox from '../ui/Infobox';
9import Appear from '../ui/effects/Appear';
7import Form from '../../lib/Form'; 10import Form from '../../lib/Form';
8import { email } from '../../helpers/validation-helpers'; 11import { email } from '../../helpers/validation-helpers';
9import Input from '../ui/Input'; 12import Input from '../ui/Input';
@@ -30,18 +33,39 @@ const messages = defineMessages({
30 id: 'invite.skip.label', 33 id: 'invite.skip.label',
31 defaultMessage: '!!!I want to do this later', 34 defaultMessage: '!!!I want to do this later',
32 }, 35 },
36 inviteSuccessInfo: {
37 id: 'invite.successInfo',
38 defaultMessage: '!!!Invitations sent successfully',
39 },
33}); 40});
34 41
35@observer 42@observer
36export default class Invite extends Component { 43export default class Invite extends Component {
37 static propTypes = { 44 static propTypes = {
38 onSubmit: PropTypes.func.isRequired, 45 onSubmit: PropTypes.func.isRequired,
46 embed: PropTypes.bool,
47 isInviteSuccessful: PropTypes.bool,
48 isLoadingInvite: PropTypes.bool,
49 };
50
51 static defaultProps = {
52 embed: false,
53 isInviteSuccessful: false,
54 isLoadingInvite: false,
39 }; 55 };
40 56
41 static contextTypes = { 57 static contextTypes = {
42 intl: intlShape, 58 intl: intlShape,
43 }; 59 };
44 60
61 state = { showSuccessInfo: false };
62
63 handlers = {
64 onChange: () => {
65 this.setState({ showSuccessInfo: false });
66 },
67 };
68
45 form = new Form({ 69 form = new Form({
46 fields: { 70 fields: {
47 invite: [...Array(3).fill({ 71 invite: [...Array(3).fill({
@@ -49,10 +73,13 @@ export default class Invite extends Component {
49 name: { 73 name: {
50 label: this.context.intl.formatMessage(messages.nameLabel), 74 label: this.context.intl.formatMessage(messages.nameLabel),
51 placeholder: this.context.intl.formatMessage(messages.nameLabel), 75 placeholder: this.context.intl.formatMessage(messages.nameLabel),
76 handlers: this.handlers,
77 // related: ['invite.0.email'], // path accepted but does not work
52 }, 78 },
53 email: { 79 email: {
54 label: this.context.intl.formatMessage(messages.emailLabel), 80 label: this.context.intl.formatMessage(messages.emailLabel),
55 placeholder: this.context.intl.formatMessage(messages.emailLabel), 81 placeholder: this.context.intl.formatMessage(messages.emailLabel),
82 handlers: this.handlers,
56 validators: [email], 83 validators: [email],
57 }, 84 },
58 }, 85 },
@@ -62,9 +89,15 @@ export default class Invite extends Component {
62 89
63 submit(e) { 90 submit(e) {
64 e.preventDefault(); 91 e.preventDefault();
92
65 this.form.submit({ 93 this.form.submit({
66 onSuccess: (form) => { 94 onSuccess: (form) => {
67 this.props.onSubmit({ invites: form.values().invite }); 95 this.props.onSubmit({ invites: form.values().invite });
96
97 this.form.clear();
98 // this.form.$('invite.0.name').focus(); // path accepted but does not focus ;(
99 document.querySelector('input:first-child').focus();
100 this.setState({ showSuccessInfo: true });
68 }, 101 },
69 onError: () => {}, 102 onError: () => {},
70 }); 103 });
@@ -73,20 +106,36 @@ export default class Invite extends Component {
73 render() { 106 render() {
74 const { form } = this; 107 const { form } = this;
75 const { intl } = this.context; 108 const { intl } = this.context;
109 const { embed, isInviteSuccessful, isLoadingInvite } = this.props;
76 110
77 const atLeastOneEmailAddress = form.$('invite') 111 const atLeastOneEmailAddress = form.$('invite')
78 .map(invite => invite.$('email').value) 112 .map(invite => invite.$('email').value)
79 .some(emailValue => emailValue.trim() !== ''); 113 .some(emailValue => emailValue.trim() !== '');
80 114
115 const sendButtonClassName = classnames({
116 auth__button: true,
117 'invite__embed--button': embed,
118 });
119
81 return ( 120 return (
82 <div className="auth__container auth__container--signup"> 121 <div>
122 {this.state.showSuccessInfo && isInviteSuccessful && (<Appear>
123 <Infobox
124 type="success"
125 icon="checkbox-marked-circle-outline"
126 dismissable
127 >
128 {intl.formatMessage(messages.inviteSuccessInfo)}
129 </Infobox>
130 </Appear>)}
131
83 <form className="franz-form auth__form" onSubmit={e => this.submit(e)}> 132 <form className="franz-form auth__form" onSubmit={e => this.submit(e)}>
84 <img 133 {!embed && (<img
85 src="./assets/images/logo.svg" 134 src="./assets/images/logo.svg"
86 className="auth__logo" 135 className="auth__logo"
87 alt="" 136 alt=""
88 /> 137 />)}
89 <h1> 138 <h1 className={embed && 'invite__embed'}>
90 {intl.formatMessage(messages.headline)} 139 {intl.formatMessage(messages.headline)}
91 </h1> 140 </h1>
92 {form.$('invite').map(invite => ( 141 {form.$('invite').map(invite => (
@@ -99,16 +148,17 @@ export default class Invite extends Component {
99 ))} 148 ))}
100 <Button 149 <Button
101 type="submit" 150 type="submit"
102 className="auth__button" 151 className={sendButtonClassName}
103 disabled={!atLeastOneEmailAddress} 152 disabled={!atLeastOneEmailAddress}
104 label={intl.formatMessage(messages.submitButtonLabel)} 153 label={intl.formatMessage(messages.submitButtonLabel)}
154 loaded={!isLoadingInvite}
105 /> 155 />
106 <Link 156 {!embed && (<Link
107 to="/" 157 to="/"
108 className="franz-form__button franz-form__button--secondary auth__button auth__button--skip" 158 className="franz-form__button franz-form__button--secondary auth__button auth__button--skip"
109 > 159 >
110 {intl.formatMessage(messages.skipButtonLabel)} 160 {intl.formatMessage(messages.skipButtonLabel)}
111 </Link> 161 </Link>)}
112 </form> 162 </form>
113 </div> 163 </div>
114 ); 164 );
diff --git a/src/components/settings/account/AccountDashboard.js b/src/components/settings/account/AccountDashboard.js
index 43272fe96..e079fd2f4 100644
--- a/src/components/settings/account/AccountDashboard.js
+++ b/src/components/settings/account/AccountDashboard.js
@@ -48,6 +48,10 @@ const messages = defineMessages({
48 id: 'settings.account.account.editButton', 48 id: 'settings.account.account.editButton',
49 defaultMessage: '!!!Edit Account', 49 defaultMessage: '!!!Edit Account',
50 }, 50 },
51 accountInviteButton: {
52 id: 'settings.account.account.inviteButton',
53 defaultMessage: '!!!Invite Friends',
54 },
51 invoiceDownload: { 55 invoiceDownload: {
52 id: 'settings.account.invoiceDownload', 56 id: 'settings.account.invoiceDownload',
53 defaultMessage: '!!!Download', 57 defaultMessage: '!!!Download',
@@ -174,10 +178,9 @@ export default class AccountDashboard extends Component {
174 <span className="badge badge--premium">{intl.formatMessage(messages.accountTypePremium)}</span> 178 <span className="badge badge--premium">{intl.formatMessage(messages.accountTypePremium)}</span>
175 )} 179 )}
176 </div> 180 </div>
177 <Link to="/settings/user/edit" className="button"> 181 <Link to="/settings/user/edit" className="button account__edit-button">
178 {intl.formatMessage(messages.accountEditButton)} 182 {intl.formatMessage(messages.accountEditButton)}
179 </Link> 183 </Link>
180
181 {user.emailValidated} 184 {user.emailValidated}
182 </div> 185 </div>
183 </div> 186 </div>
diff --git a/src/components/settings/navigation/SettingsNavigation.js b/src/components/settings/navigation/SettingsNavigation.js
index fea8d682d..66539f324 100644
--- a/src/components/settings/navigation/SettingsNavigation.js
+++ b/src/components/settings/navigation/SettingsNavigation.js
@@ -21,6 +21,10 @@ const messages = defineMessages({
21 id: 'settings.navigation.settings', 21 id: 'settings.navigation.settings',
22 defaultMessage: '!!!Settings', 22 defaultMessage: '!!!Settings',
23 }, 23 },
24 inviteFriends: {
25 id: 'settings.navigation.inviteFriends',
26 defaultMessage: '!!!Invite Friends',
27 },
24 logout: { 28 logout: {
25 id: 'settings.navigation.logout', 29 id: 'settings.navigation.logout',
26 defaultMessage: '!!!Logout', 30 defaultMessage: '!!!Logout',
@@ -70,6 +74,13 @@ export default class SettingsNavigation extends Component {
70 > 74 >
71 {intl.formatMessage(messages.settings)} 75 {intl.formatMessage(messages.settings)}
72 </Link> 76 </Link>
77 <Link
78 to="/settings/invite"
79 className="settings-navigation__link"
80 activeClassName="is-active"
81 >
82 {intl.formatMessage(messages.inviteFriends)}
83 </Link>
73 <span className="settings-navigation__expander" /> 84 <span className="settings-navigation__expander" />
74 <Link 85 <Link
75 to="/auth/logout" 86 to="/auth/logout"
diff --git a/src/components/settings/services/ServiceError.js b/src/components/settings/services/ServiceError.js
index 923053296..1f1512927 100644
--- a/src/components/settings/services/ServiceError.js
+++ b/src/components/settings/services/ServiceError.js
@@ -26,7 +26,7 @@ const messages = defineMessages({
26}); 26});
27 27
28@observer 28@observer
29export default class EditServiceForm extends Component { 29export default class ServiceError extends Component {
30 static contextTypes = { 30 static contextTypes = {
31 intl: intlShape, 31 intl: intlShape,
32 }; 32 };
diff --git a/src/components/settings/user/EditUserForm.js b/src/components/settings/user/EditUserForm.js
index f36887fc2..0a7b7c396 100644
--- a/src/components/settings/user/EditUserForm.js
+++ b/src/components/settings/user/EditUserForm.js
@@ -135,7 +135,7 @@ export default class EditServiceForm extends Component {
135 <Button 135 <Button
136 type="submit" 136 type="submit"
137 label={intl.formatMessage(messages.buttonSave)} 137 label={intl.formatMessage(messages.buttonSave)}
138 htmlForm="form" 138 htmlForm="form" // why is form attribute escaped in JSX?? couldn't find any info on that, did you mean "htmlFor"?
139 /> 139 />
140 )} 140 )}
141 </div> 141 </div>
diff --git a/src/components/ui/Link.js b/src/components/ui/Link.js
index f5da921fa..693be84ea 100644
--- a/src/components/ui/Link.js
+++ b/src/components/ui/Link.js
@@ -62,7 +62,10 @@ Link.wrappedComponent.propTypes = {
62 oneOrManyChildElements, 62 oneOrManyChildElements,
63 PropTypes.string, 63 PropTypes.string,
64 ]).isRequired, 64 ]).isRequired,
65 to: PropTypes.string.isRequired, 65 to: PropTypes.oneOfType([
66 PropTypes.string,
67 PropTypes.object,
68 ]).isRequired,
66 className: PropTypes.string, 69 className: PropTypes.string,
67 activeClassName: PropTypes.string, 70 activeClassName: PropTypes.string,
68 strictFilter: PropTypes.bool, 71 strictFilter: PropTypes.bool,
diff --git a/src/containers/auth/InviteScreen.js b/src/containers/auth/InviteScreen.js
index 51971f436..7102df0b9 100644
--- a/src/containers/auth/InviteScreen.js
+++ b/src/containers/auth/InviteScreen.js
@@ -11,11 +11,19 @@ export default class InviteScreen extends Component {
11 } 11 }
12 12
13 render() { 13 render() {
14 const { actions } = this.props; 14 const {
15 actions,
16 location,
17 } = this.props;
18
15 return ( 19 return (
16 <Invite 20 <div className="auth__container auth__container--signup">
17 onSubmit={actions.user.invite} 21 <Invite
18 /> 22 onSubmit={actions.user.invite}
23 from={location.query.from}
24 embed={false}
25 />
26 </div>
19 ); 27 );
20 } 28 }
21} 29}
@@ -26,4 +34,9 @@ InviteScreen.wrappedComponent.propTypes = {
26 invite: PropTypes.func.isRequired, 34 invite: PropTypes.func.isRequired,
27 }).isRequired, 35 }).isRequired,
28 }).isRequired, 36 }).isRequired,
37 location: PropTypes.shape({
38 query: PropTypes.shape({
39 from: PropTypes.string,
40 }),
41 }).isRequired,
29}; 42};
diff --git a/src/containers/settings/AccountScreen.js b/src/containers/settings/AccountScreen.js
index c5c2982b0..9ee93a9e9 100644
--- a/src/containers/settings/AccountScreen.js
+++ b/src/containers/settings/AccountScreen.js
@@ -61,7 +61,7 @@ export default class AccountScreen extends Component {
61 render() { 61 render() {
62 const { user, payment } = this.props.stores; 62 const { user, payment } = this.props.stores;
63 const { openExternalUrl } = this.props.actions.app; 63 const { openExternalUrl } = this.props.actions.app;
64 const { user: userActions } = this.props.actions; 64 const { user: userActions } = this.props.actions; // @adlk: :+1 what's the opposite of git blame?
65 65
66 const isLoadingUserInfo = user.getUserInfoRequest.isExecuting; 66 const isLoadingUserInfo = user.getUserInfoRequest.isExecuting;
67 const isLoadingOrdersInfo = payment.ordersDataRequest.isExecuting; 67 const isLoadingOrdersInfo = payment.ordersDataRequest.isExecuting;
@@ -83,6 +83,7 @@ export default class AccountScreen extends Component {
83 deleteAccount={userActions.delete} 83 deleteAccount={userActions.delete}
84 isLoadingDeleteAccount={user.deleteAccountRequest.isExecuting} 84 isLoadingDeleteAccount={user.deleteAccountRequest.isExecuting}
85 isDeleteAccountSuccessful={user.deleteAccountRequest.wasExecuted && !user.deleteAccountRequest.isError} 85 isDeleteAccountSuccessful={user.deleteAccountRequest.wasExecuted && !user.deleteAccountRequest.isError}
86 pathname={this.props.location.pathname}
86 /> 87 />
87 ); 88 );
88 } 89 }
@@ -106,4 +107,7 @@ AccountScreen.wrappedComponent.propTypes = {
106 delete: PropTypes.func.isRequired, 107 delete: PropTypes.func.isRequired,
107 }).isRequired, 108 }).isRequired,
108 }).isRequired, 109 }).isRequired,
110 location: PropTypes.shape({
111 pathname: PropTypes.string,
112 }).isRequired,
109}; 113};
diff --git a/src/containers/settings/InviteScreen.js b/src/containers/settings/InviteScreen.js
new file mode 100644
index 000000000..1947e79f0
--- /dev/null
+++ b/src/containers/settings/InviteScreen.js
@@ -0,0 +1,63 @@
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import { inject, observer } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl';
5
6import Invite from '../../components/auth/Invite';
7import { gaPage } from '../../lib/analytics';
8
9const messages = defineMessages({
10 headline: {
11 id: 'settings.invite.headline',
12 defaultMessage: '!!!Invite Friends',
13 },
14});
15
16@inject('stores', 'actions') @observer
17export default class InviteScreen extends Component {
18 static contextTypes = {
19 intl: intlShape,
20 };
21
22 componentDidMount() {
23 gaPage('Settings/Invite');
24 }
25
26 componentWillUnmount() {
27 this.props.stores.user.inviteRequest.reset();
28 }
29
30 render() {
31 const { actions } = this.props;
32 const { user } = this.props.stores;
33
34 return (
35 <div className="settings__main">
36 <div className="settings__header">
37 <h1>{this.context.intl.formatMessage(messages.headline)}</h1>
38 </div>
39 <div className="settings__body invite__form">
40 <Invite
41 onSubmit={actions.user.invite}
42 isLoadingInvite={user.inviteRequest.isExecuting}
43 isInviteSuccessful={user.inviteRequest.wasExecuted && !user.inviteRequest.isError}
44 embed
45 />
46 </div>
47 </div>
48 );
49 }
50}
51
52InviteScreen.wrappedComponent.propTypes = {
53 actions: PropTypes.shape({
54 user: PropTypes.shape({
55 invite: PropTypes.func.isRequired,
56 }).isRequired,
57 }).isRequired,
58 stores: PropTypes.shape({
59 user: PropTypes.shape({
60 inviteRequest: PropTypes.object,
61 }).isRequired,
62 }).isRequired,
63};
diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json
index 1ea0554ba..180db3018 100644
--- a/src/i18n/locales/de.json
+++ b/src/i18n/locales/de.json
@@ -45,6 +45,7 @@
45 "services.getStarted" : "Loslegen", 45 "services.getStarted" : "Loslegen",
46 "services.welcome" : "Willkommen bei Franz.", 46 "services.welcome" : "Willkommen bei Franz.",
47 "settings.account.account.editButton" : "Konto bearbeiten", 47 "settings.account.account.editButton" : "Konto bearbeiten",
48 "settings.account.account.inviteButton" : "Freunde einladen",
48 "settings.account.accountType.basic" : "Basis Konto", 49 "settings.account.accountType.basic" : "Basis Konto",
49 "settings.account.accountType.premium" : "Premium-Supporter Konto", 50 "settings.account.accountType.premium" : "Premium-Supporter Konto",
50 "settings.account.buttonSave" : "Profil aktualisieren", 51 "settings.account.buttonSave" : "Profil aktualisieren",
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 63d7ce60b..29b979838 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -45,6 +45,7 @@
45 "invite.name.label": "Name", 45 "invite.name.label": "Name",
46 "invite.email.label": "Email address", 46 "invite.email.label": "Email address",
47 "invite.skip.label": "I want to do this later", 47 "invite.skip.label": "I want to do this later",
48 "invite.successInfo": "Invitations sent successfully",
48 "subscription.submit.label": "I want to support the development of Franz", 49 "subscription.submit.label": "I want to support the development of Franz",
49 "subscription.paymentSessionError": "Could not initialize payment form", 50 "subscription.paymentSessionError": "Could not initialize payment form",
50 "subscription.includedFeatures": "Paid Franz Premium Supporter Account includes", 51 "subscription.includedFeatures": "Paid Franz Premium Supporter Account includes",
@@ -76,6 +77,7 @@
76 "settings.account.accountType.basic": "Basic Account", 77 "settings.account.accountType.basic": "Basic Account",
77 "settings.account.accountType.premium": "Premium Supporter Account", 78 "settings.account.accountType.premium": "Premium Supporter Account",
78 "settings.account.account.editButton": "Edit account", 79 "settings.account.account.editButton": "Edit account",
80 "settings.account.account.inviteButton" : "Invite friends",
79 "settings.account.invoiceDownload": "Download", 81 "settings.account.invoiceDownload": "Download",
80 "settings.account.userInfoRequestFailed": "Could not load user information", 82 "settings.account.userInfoRequestFailed": "Could not load user information",
81 "settings.account.tryReloadUserInfoRequest": "Try again", 83 "settings.account.tryReloadUserInfoRequest": "Try again",
@@ -87,10 +89,12 @@
87 "settings.account.deleteAccount": "Delete account", 89 "settings.account.deleteAccount": "Delete account",
88 "settings.account.deleteInfo": "If you don't need your Franz account any longer, you can delete your account and all related data here.", 90 "settings.account.deleteInfo": "If you don't need your Franz account any longer, you can delete your account and all related data here.",
89 "settings.account.deleteEmailSent": "You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!", 91 "settings.account.deleteEmailSent": "You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!",
92 "settings.invite.headline": "Invite Friends",
90 "settings.navigation.availableServices": "Available services", 93 "settings.navigation.availableServices": "Available services",
91 "settings.navigation.yourServices": "Your services", 94 "settings.navigation.yourServices": "Your services",
92 "settings.navigation.account": "Account", 95 "settings.navigation.account": "Account",
93 "settings.navigation.settings": "Settings", 96 "settings.navigation.settings": "Settings",
97 "settings.navigation.inviteFriends": "Invite Friends",
94 "settings.navigation.logout": "Logout", 98 "settings.navigation.logout": "Logout",
95 "settings.recipes.headline": "Available services", 99 "settings.recipes.headline": "Available services",
96 "settings.recipes.mostPopular": "Most popular", 100 "settings.recipes.mostPopular": "Most popular",
diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json
index c4f598bd1..dddd1bf1f 100644
--- a/src/i18n/locales/es.json
+++ b/src/i18n/locales/es.json
@@ -45,6 +45,7 @@
45 "services.getStarted" : "Primeros pasos", 45 "services.getStarted" : "Primeros pasos",
46 "services.welcome" : "Bienvenido a Franz", 46 "services.welcome" : "Bienvenido a Franz",
47 "settings.account.account.editButton" : "Editar cuenta", 47 "settings.account.account.editButton" : "Editar cuenta",
48 "settings.account.account.inviteButton" : "Invitar amigos",
48 "settings.account.accountType.basic" : "Cuenta Básica", 49 "settings.account.accountType.basic" : "Cuenta Básica",
49 "settings.account.accountType.premium" : "Cuenta Colaborador Premium", 50 "settings.account.accountType.premium" : "Cuenta Colaborador Premium",
50 "settings.account.buttonSave" : "Actualizar perfil", 51 "settings.account.buttonSave" : "Actualizar perfil",
diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json
index 4a06975bb..a717577be 100644
--- a/src/i18n/locales/fr.json
+++ b/src/i18n/locales/fr.json
@@ -45,6 +45,7 @@
45 "services.getStarted" : "Commencer", 45 "services.getStarted" : "Commencer",
46 "services.welcome" : "Bienvenue dans Franz", 46 "services.welcome" : "Bienvenue dans Franz",
47 "settings.account.account.editButton" : "Modifier le compte", 47 "settings.account.account.editButton" : "Modifier le compte",
48 "settings.account.account.inviteButton" : "Inviter des amis",
48 "settings.account.accountType.basic" : "Compte de base", 49 "settings.account.accountType.basic" : "Compte de base",
49 "settings.account.accountType.premium" : "Compte supporteur premium", 50 "settings.account.accountType.premium" : "Compte supporteur premium",
50 "settings.account.buttonSave" : "Mettre à jour le profil", 51 "settings.account.buttonSave" : "Mettre à jour le profil",
diff --git a/src/i18n/locales/it.json b/src/i18n/locales/it.json
index 1d2485b19..5b89b3a12 100644
--- a/src/i18n/locales/it.json
+++ b/src/i18n/locales/it.json
@@ -45,6 +45,7 @@
45 "services.getStarted" : "Iniziamo", 45 "services.getStarted" : "Iniziamo",
46 "services.welcome" : "Benvenuto in Franz", 46 "services.welcome" : "Benvenuto in Franz",
47 "settings.account.account.editButton" : "Modifica account", 47 "settings.account.account.editButton" : "Modifica account",
48 "settings.account.account.inviteButton" : "Invitare amicos",
48 "settings.account.accountType.basic" : "Account Basic", 49 "settings.account.accountType.basic" : "Account Basic",
49 "settings.account.accountType.premium" : "Premium Supporter Account", 50 "settings.account.accountType.premium" : "Premium Supporter Account",
50 "settings.account.buttonSave" : "Aggiorna profilo", 51 "settings.account.buttonSave" : "Aggiorna profilo",
diff --git a/src/models/User.js b/src/models/User.js
index 2e5df4795..d2a455e20 100644
--- a/src/models/User.js
+++ b/src/models/User.js
@@ -10,7 +10,7 @@ export default class User {
10 @observable emailIsConfirmed = true; // better assume it's confirmed to avoid noise 10 @observable emailIsConfirmed = true; // better assume it's confirmed to avoid noise
11 @observable subscription = {}; 11 @observable subscription = {};
12 @observable isSubscriptionOwner = false; 12 @observable isSubscriptionOwner = false;
13 @observable isPremium = false; 13 @observable isPremium = true;
14 @observable beta = false; 14 @observable beta = false;
15 @observable donor = {}; 15 @observable donor = {};
16 @observable isDonor = false; 16 @observable isDonor = false;
diff --git a/src/stores/UserStore.js b/src/stores/UserStore.js
index 09000dcdb..abec4df5d 100644
--- a/src/stores/UserStore.js
+++ b/src/stores/UserStore.js
@@ -2,6 +2,7 @@ import { observable, computed, action } from 'mobx';
2import moment from 'moment'; 2import moment from 'moment';
3import jwt from 'jsonwebtoken'; 3import jwt from 'jsonwebtoken';
4 4
5import { isDevMode } from '../environment';
5import Store from './lib/Store'; 6import Store from './lib/Store';
6import Request from './lib/Request'; 7import Request from './lib/Request';
7import CachedRequest from './lib/CachedRequest'; 8import CachedRequest from './lib/CachedRequest';
@@ -160,13 +161,17 @@ export default class UserStore extends Store {
160 gaEvent('User', 'retrievePassword'); 161 gaEvent('User', 'retrievePassword');
161 } 162 }
162 163
163 @action _invite({ invites }) { 164 @action async _invite({ invites }) {
164 const data = invites.filter(invite => invite.email !== ''); 165 const data = invites.filter(invite => invite.email !== '');
165 166
166 this.inviteRequest.execute(data); 167 const response = await this.inviteRequest.execute(data)._promise;
167 168
168 // we do not wait for a server response before redirecting the user 169 this.actionStatus = response.status || [];
169 this.stores.router.push('/'); 170
171 // we do not wait for a server response before redirecting the user ONLY DURING SIGNUP
172 if (this.stores.router.location.pathname.includes(this.INVITE_ROUTE)) {
173 this.stores.router.push('/');
174 }
170 175
171 gaEvent('User', 'inviteUsers'); 176 gaEvent('User', 'inviteUsers');
172 } 177 }
@@ -237,7 +242,9 @@ export default class UserStore extends Store {
237 && currentRoute.includes(this.BASE_ROUTE) 242 && currentRoute.includes(this.BASE_ROUTE)
238 && (this.hasCompletedSignup 243 && (this.hasCompletedSignup
239 || this.hasCompletedSignup === null)) { 244 || this.hasCompletedSignup === null)) {
240 this.stores.router.push('/'); 245 if (!isDevMode) {
246 this.stores.router.push('/');
247 }
241 } 248 }
242 }; 249 };
243 250
diff --git a/src/styles/invite.scss b/src/styles/invite.scss
new file mode 100644
index 000000000..bfb1a4b6b
--- /dev/null
+++ b/src/styles/invite.scss
@@ -0,0 +1,15 @@
1.invite__form {
2 /* play with values to see different layouts */
3 // display: flex;
4 align-items: center;
5 align-self: center;
6 justify-content: center;
7}
8
9.invite__embed {
10 text-align: center;
11}
12
13.invite__embed--button {
14 width: 100%;
15} \ No newline at end of file
diff --git a/src/styles/main.scss b/src/styles/main.scss
index 261396f6f..446bdca14 100644
--- a/src/styles/main.scss
+++ b/src/styles/main.scss
@@ -27,6 +27,7 @@ $mdi-font-path: '../node_modules/mdi/fonts';
27@import './subscription.scss'; 27@import './subscription.scss';
28@import './subscription-popup.scss'; 28@import './subscription-popup.scss';
29@import './content-tabs.scss'; 29@import './content-tabs.scss';
30@import './invite.scss';
30 31
31// form 32// form
32@import './input.scss'; 33@import './input.scss';