aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/api/PaymentApi.js8
-rw-r--r--src/api/server/ServerApi.js17
-rw-r--r--src/app.js2
-rw-r--r--src/components/settings/account/AccountDashboard.js114
-rw-r--r--src/components/settings/navigation/SettingsNavigation.js11
-rw-r--r--src/components/settings/team/TeamDashboard.js123
-rw-r--r--src/config.js9
-rw-r--r--src/containers/settings/AccountScreen.js51
-rw-r--r--src/containers/settings/TeamScreen.js58
-rw-r--r--src/environment.js22
-rwxr-xr-xsrc/features/settingsWS/actions.js10
-rwxr-xr-xsrc/features/settingsWS/index.js35
-rwxr-xr-xsrc/features/settingsWS/state.js13
-rwxr-xr-xsrc/features/settingsWS/store.js104
-rw-r--r--src/i18n/locales/defaultMessages.json182
-rw-r--r--src/i18n/locales/en-US.json9
-rw-r--r--src/i18n/messages/src/components/settings/account/AccountDashboard.json82
-rw-r--r--src/i18n/messages/src/components/settings/navigation/SettingsNavigation.json41
-rw-r--r--src/i18n/messages/src/components/settings/team/TeamDashboard.json80
-rw-r--r--src/stores/FeaturesStore.js2
-rw-r--r--src/stores/PaymentStore.js17
-rw-r--r--src/styles/badge.scss1
-rw-r--r--src/styles/settings.scss11
23 files changed, 745 insertions, 257 deletions
diff --git a/src/api/PaymentApi.js b/src/api/PaymentApi.js
index 3f6bb442e..7325151e9 100644
--- a/src/api/PaymentApi.js
+++ b/src/api/PaymentApi.js
@@ -11,12 +11,4 @@ export default class PaymentApi {
11 getHostedPage(planId) { 11 getHostedPage(planId) {
12 return this.server.getHostedPage(planId); 12 return this.server.getHostedPage(planId);
13 } 13 }
14
15 getDashboardUrl() {
16 return this.server.getPaymentDashboardUrl();
17 }
18
19 getOrders() {
20 return this.server.getSubscriptionOrders();
21 }
22} 14}
diff --git a/src/api/server/ServerApi.js b/src/api/server/ServerApi.js
index bafeef005..f6bc9c283 100644
--- a/src/api/server/ServerApi.js
+++ b/src/api/server/ServerApi.js
@@ -418,23 +418,6 @@ export default class ServerApi {
418 return data; 418 return data;
419 } 419 }
420 420
421 async getPaymentDashboardUrl() {
422 const request = await sendAuthRequest(`${API_URL}/me/billing`);
423 if (!request.ok) throw request;
424 const data = await request.json();
425 debug('ServerApi::getPaymentDashboardUrl resolves', data);
426 return data;
427 }
428
429 async getSubscriptionOrders() {
430 const request = await sendAuthRequest(`${API_URL}/me/subscription`);
431 if (!request.ok) throw request;
432 const data = await request.json();
433 const orders = this._mapOrderModels(data);
434 debug('ServerApi::getSubscriptionOrders resolves', orders);
435 return orders;
436 }
437
438 // News 421 // News
439 async getLatestNews() { 422 async getLatestNews() {
440 const url = `${API_URL}/news?platform=${os.platform()}&arch=${os.arch()}&version=${app.getVersion()}`; 423 const url = `${API_URL}/news?platform=${os.platform()}&arch=${os.arch()}&version=${app.getVersion()}`;
diff --git a/src/app.js b/src/app.js
index fb9f1c6ab..f6092bf60 100644
--- a/src/app.js
+++ b/src/app.js
@@ -27,6 +27,7 @@ import RecipesScreen from './containers/settings/RecipesScreen';
27import ServicesScreen from './containers/settings/ServicesScreen'; 27import ServicesScreen from './containers/settings/ServicesScreen';
28import EditServiceScreen from './containers/settings/EditServiceScreen'; 28import EditServiceScreen from './containers/settings/EditServiceScreen';
29import AccountScreen from './containers/settings/AccountScreen'; 29import AccountScreen from './containers/settings/AccountScreen';
30import TeamScreen from './containers/settings/TeamScreen';
30import EditUserScreen from './containers/settings/EditUserScreen'; 31import EditUserScreen from './containers/settings/EditUserScreen';
31import EditSettingsScreen from './containers/settings/EditSettingsScreen'; 32import EditSettingsScreen from './containers/settings/EditSettingsScreen';
32import InviteSettingsScreen from './containers/settings/InviteScreen'; 33import InviteSettingsScreen from './containers/settings/InviteScreen';
@@ -82,6 +83,7 @@ window.addEventListener('load', () => {
82 <Route path={WORKSPACES_ROUTES.EDIT} component={EditWorkspaceScreen} /> 83 <Route path={WORKSPACES_ROUTES.EDIT} component={EditWorkspaceScreen} />
83 <Route path="/settings/user" component={AccountScreen} /> 84 <Route path="/settings/user" component={AccountScreen} />
84 <Route path="/settings/user/edit" component={EditUserScreen} /> 85 <Route path="/settings/user/edit" component={EditUserScreen} />
86 <Route path="/settings/team" component={TeamScreen} />
85 <Route path="/settings/app" component={EditSettingsScreen} /> 87 <Route path="/settings/app" component={EditSettingsScreen} />
86 <Route path="/settings/invite" component={InviteSettingsScreen} /> 88 <Route path="/settings/invite" component={InviteSettingsScreen} />
87 </Route> 89 </Route>
diff --git a/src/components/settings/account/AccountDashboard.js b/src/components/settings/account/AccountDashboard.js
index 181b95c8c..ea335815c 100644
--- a/src/components/settings/account/AccountDashboard.js
+++ b/src/components/settings/account/AccountDashboard.js
@@ -3,12 +3,10 @@ import PropTypes from 'prop-types';
3import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; 3import { observer, PropTypes as MobxPropTypes } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl'; 4import { defineMessages, intlShape } from 'react-intl';
5import ReactTooltip from 'react-tooltip'; 5import ReactTooltip from 'react-tooltip';
6import moment from 'moment';
7 6
8import Loader from '../../ui/Loader'; 7import Loader from '../../ui/Loader';
9import Button from '../../ui/Button'; 8import Button from '../../ui/Button';
10import Infobox from '../../ui/Infobox'; 9import Infobox from '../../ui/Infobox';
11import Link from '../../ui/Link';
12import SubscriptionForm from '../../../containers/subscription/SubscriptionFormScreen'; 10import SubscriptionForm from '../../../containers/subscription/SubscriptionFormScreen';
13 11
14const messages = defineMessages({ 12const messages = defineMessages({
@@ -24,10 +22,6 @@ const messages = defineMessages({
24 id: 'settings.account.headlineUpgrade', 22 id: 'settings.account.headlineUpgrade',
25 defaultMessage: '!!!Upgrade your Account', 23 defaultMessage: '!!!Upgrade your Account',
26 }, 24 },
27 headlineInvoices: {
28 id: 'settings.account.headlineInvoices',
29 defaultMessage: '!!Invoices',
30 },
31 headlineDangerZone: { 25 headlineDangerZone: {
32 id: 'settings.account.headlineDangerZone', 26 id: 'settings.account.headlineDangerZone',
33 defaultMessage: '!!Danger Zone', 27 defaultMessage: '!!Danger Zone',
@@ -48,6 +42,10 @@ const messages = defineMessages({
48 id: 'settings.account.account.editButton', 42 id: 'settings.account.account.editButton',
49 defaultMessage: '!!!Edit Account', 43 defaultMessage: '!!!Edit Account',
50 }, 44 },
45 invoicesButton: {
46 id: 'settings.account.headlineInvoices',
47 defaultMessage: '!!Invoices',
48 },
51 invoiceDownload: { 49 invoiceDownload: {
52 id: 'settings.account.invoiceDownload', 50 id: 'settings.account.invoiceDownload',
53 defaultMessage: '!!!Download', 51 defaultMessage: '!!!Download',
@@ -77,19 +75,17 @@ const messages = defineMessages({
77export default @observer class AccountDashboard extends Component { 75export default @observer class AccountDashboard extends Component {
78 static propTypes = { 76 static propTypes = {
79 user: MobxPropTypes.observableObject.isRequired, 77 user: MobxPropTypes.observableObject.isRequired,
80 orders: MobxPropTypes.arrayOrObservableArray.isRequired,
81 isLoading: PropTypes.bool.isRequired, 78 isLoading: PropTypes.bool.isRequired,
82 isLoadingOrdersInfo: PropTypes.bool.isRequired,
83 isLoadingPlans: PropTypes.bool.isRequired, 79 isLoadingPlans: PropTypes.bool.isRequired,
84 isCreatingPaymentDashboardUrl: PropTypes.bool.isRequired,
85 userInfoRequestFailed: PropTypes.bool.isRequired, 80 userInfoRequestFailed: PropTypes.bool.isRequired,
86 retryUserInfoRequest: PropTypes.func.isRequired, 81 retryUserInfoRequest: PropTypes.func.isRequired,
87 openDashboard: PropTypes.func.isRequired,
88 openExternalUrl: PropTypes.func.isRequired,
89 onCloseSubscriptionWindow: PropTypes.func.isRequired, 82 onCloseSubscriptionWindow: PropTypes.func.isRequired,
90 deleteAccount: PropTypes.func.isRequired, 83 deleteAccount: PropTypes.func.isRequired,
91 isLoadingDeleteAccount: PropTypes.bool.isRequired, 84 isLoadingDeleteAccount: PropTypes.bool.isRequired,
92 isDeleteAccountSuccessful: PropTypes.bool.isRequired, 85 isDeleteAccountSuccessful: PropTypes.bool.isRequired,
86 openEditAccount: PropTypes.func.isRequired,
87 openBilling: PropTypes.func.isRequired,
88 openInvoices: PropTypes.func.isRequired,
93 }; 89 };
94 90
95 static contextTypes = { 91 static contextTypes = {
@@ -99,12 +95,7 @@ export default @observer class AccountDashboard extends Component {
99 render() { 95 render() {
100 const { 96 const {
101 user, 97 user,
102 orders,
103 isLoading, 98 isLoading,
104 isCreatingPaymentDashboardUrl,
105 openDashboard,
106 openExternalUrl,
107 isLoadingOrdersInfo,
108 isLoadingPlans, 99 isLoadingPlans,
109 userInfoRequestFailed, 100 userInfoRequestFailed,
110 retryUserInfoRequest, 101 retryUserInfoRequest,
@@ -112,6 +103,9 @@ export default @observer class AccountDashboard extends Component {
112 deleteAccount, 103 deleteAccount,
113 isLoadingDeleteAccount, 104 isLoadingDeleteAccount,
114 isDeleteAccountSuccessful, 105 isDeleteAccountSuccessful,
106 openEditAccount,
107 openBilling,
108 openInvoices,
115 } = this.props; 109 } = this.props;
116 const { intl } = this.context; 110 const { intl } = this.context;
117 111
@@ -161,68 +155,48 @@ export default @observer class AccountDashboard extends Component {
161 <div className="account__info"> 155 <div className="account__info">
162 <h2> 156 <h2>
163 {`${user.firstname} ${user.lastname}`} 157 {`${user.firstname} ${user.lastname}`}
158 {user.isPremium && (
159 <>
160 {' '}
161 <span className="badge badge--premium">{intl.formatMessage(messages.accountTypePremium)}</span>
162 </>
163 )}
164 </h2> 164 </h2>
165 {user.organization && `${user.organization}, `} 165 {user.organization && `${user.organization}, `}
166 {user.email} 166 {user.email}
167 <br />
168 {user.isPremium && ( 167 {user.isPremium && (
169 <span className="badge badge--premium">{intl.formatMessage(messages.accountTypePremium)}</span> 168 <div className="manage-user-links">
169 <Button
170 label={intl.formatMessage(messages.accountEditButton)}
171 className="franz-form__button--inverted"
172 onClick={openEditAccount}
173 />
174 {user.isSubscriptionOwner && (
175 <>
176 <Button
177 label={intl.formatMessage(messages.manageSubscriptionButtonLabel)}
178 className="franz-form__button--inverted"
179 onClick={openBilling}
180 />
181 <Button
182 label={intl.formatMessage(messages.invoicesButton)}
183 className="franz-form__button--inverted"
184 onClick={openInvoices}
185 />
186 </>
187 )}
188 </div>
170 )} 189 )}
171 </div> 190 </div>
172 <Link to="/settings/user/edit" className="button"> 191 {!user.isPremium && (
173 {intl.formatMessage(messages.accountEditButton)} 192 <Button
174 </Link> 193 label={intl.formatMessage(messages.accountEditButton)}
175 {user.emailValidated} 194 className="franz-form__button--inverted"
176 </div> 195 onClick={openEditAccount}
177 </div> 196 />
178 )}
179
180 {user.isSubscriptionOwner && (
181 isLoadingOrdersInfo ? (
182 <Loader />
183 ) : (
184 <div className="account franz-form">
185 {orders.length > 0 && (
186 <Fragment>
187 <div className="account__box">
188 <h2>{intl.formatMessage(messages.headlineSubscription)}</h2>
189 <div className="account__subscription">
190 {orders[0].name}
191 <span className="badge">{orders[0].price}</span>
192 <Button
193 label={intl.formatMessage(messages.manageSubscriptionButtonLabel)}
194 className="account__subscription-button franz-form__button--inverted"
195 loaded={!isCreatingPaymentDashboardUrl}
196 onClick={() => openDashboard()}
197 />
198 </div>
199 </div>
200 <div className="account__box">
201 <h2>{intl.formatMessage(messages.headlineInvoices)}</h2>
202 <table className="invoices">
203 <tbody>
204 {orders.map(order => (
205 <tr key={order.id}>
206 <td className="invoices__date">
207 {moment(order.date).format('DD.MM.YYYY')}
208 </td>
209 <td className="invoices__action">
210 <button
211 type="button"
212 onClick={() => openExternalUrl(order.invoiceUrl)}
213 >
214 {intl.formatMessage(messages.invoiceDownload)}
215 </button>
216 </td>
217 </tr>
218 ))}
219 </tbody>
220 </table>
221 </div>
222 </Fragment>
223 )} 197 )}
224 </div> 198 </div>
225 ) 199 </div>
226 )} 200 )}
227 201
228 {!user.isPremium && ( 202 {!user.isPremium && (
diff --git a/src/components/settings/navigation/SettingsNavigation.js b/src/components/settings/navigation/SettingsNavigation.js
index 993b0a44a..cab6f23d7 100644
--- a/src/components/settings/navigation/SettingsNavigation.js
+++ b/src/components/settings/navigation/SettingsNavigation.js
@@ -25,6 +25,10 @@ const messages = defineMessages({
25 id: 'settings.navigation.account', 25 id: 'settings.navigation.account',
26 defaultMessage: '!!!Account', 26 defaultMessage: '!!!Account',
27 }, 27 },
28 team: {
29 id: 'settings.navigation.team',
30 defaultMessage: '!!!Manage Team',
31 },
28 settings: { 32 settings: {
29 id: 'settings.navigation.settings', 33 id: 'settings.navigation.settings',
30 defaultMessage: '!!!Settings', 34 defaultMessage: '!!!Settings',
@@ -98,6 +102,13 @@ export default @inject('stores') @observer class SettingsNavigation extends Comp
98 {intl.formatMessage(messages.account)} 102 {intl.formatMessage(messages.account)}
99 </Link> 103 </Link>
100 <Link 104 <Link
105 to="/settings/team"
106 className="settings-navigation__link"
107 activeClassName="is-active"
108 >
109 {intl.formatMessage(messages.team)}
110 </Link>
111 <Link
101 to="/settings/app" 112 to="/settings/app"
102 className="settings-navigation__link" 113 className="settings-navigation__link"
103 activeClassName="is-active" 114 activeClassName="is-active"
diff --git a/src/components/settings/team/TeamDashboard.js b/src/components/settings/team/TeamDashboard.js
new file mode 100644
index 000000000..e222bb14c
--- /dev/null
+++ b/src/components/settings/team/TeamDashboard.js
@@ -0,0 +1,123 @@
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import { observer, PropTypes as MobxPropTypes } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl';
5import ReactTooltip from 'react-tooltip';
6import injectSheet from 'react-jss';
7
8import Loader from '../../ui/Loader';
9import Button from '../../ui/Button';
10import Infobox from '../../ui/Infobox';
11import PremiumFeatureContainer from '../../ui/PremiumFeatureContainer';
12
13const messages = defineMessages({
14 headline: {
15 id: 'settings.team.headline',
16 defaultMessage: '!!!Team',
17 },
18 contentHeadline: {
19 id: 'settings.team.contentHeadline',
20 defaultMessage: '!!!Franz for Teams',
21 },
22 intro: {
23 id: 'settings.team.intro',
24 defaultMessage: '!!!You and your team use Franz? You can now manage Premium subscriptions for as many colleagues, friends or family members as you want, all from within one account.',
25 },
26 copy: {
27 id: 'settings.team.copy',
28 defaultMessage: '!!!Franz for Teams gives you the option to invite co-workers to your team by sending them email invitations and manage their subscriptions in your account’s preferences. Don’t waste time setting up subscriptions for every team member individually, forget about multiple invoices and different billing cycles - one team to rule them all!',
29 },
30 manageButton: {
31 id: 'settings.team.manageAction',
32 defaultMessage: '!!!Manage your Team',
33 },
34 upgradeButton: {
35 id: 'settings.team.upgradeAction',
36 defaultMessage: '!!!Upgrade your Account',
37 },
38});
39
40const styles = {
41 cta: {
42 marginTop: 40,
43 },
44};
45
46
47export default @injectSheet(styles) @observer class TeamDashboard extends Component {
48 static propTypes = {
49 user: MobxPropTypes.observableObject.isRequired,
50 isLoading: PropTypes.bool.isRequired,
51 userInfoRequestFailed: PropTypes.bool.isRequired,
52 retryUserInfoRequest: PropTypes.func.isRequired,
53 openTeamManagement: PropTypes.func.isRequired,
54 classes: PropTypes.object.isRequired,
55 };
56
57 static contextTypes = {
58 intl: intlShape,
59 };
60
61 render() {
62 const {
63 user,
64 isLoading,
65 userInfoRequestFailed,
66 retryUserInfoRequest,
67 openTeamManagement,
68 classes,
69 } = this.props;
70 const { intl } = this.context;
71
72 return (
73 <div className="settings__main">
74 <div className="settings__header">
75 <span className="settings__header-item">
76 {intl.formatMessage(messages.headline)}
77 </span>
78 </div>
79 <div className="settings__body">
80 {isLoading && (
81 <Loader />
82 )}
83
84 {!isLoading && userInfoRequestFailed && (
85 <Infobox
86 icon="alert"
87 type="danger"
88 ctaLabel={intl.formatMessage(messages.tryReloadUserInfoRequest)}
89 ctaLoading={isLoading}
90 ctaOnClick={retryUserInfoRequest}
91 >
92 {intl.formatMessage(messages.userInfoRequestFailed)}
93 </Infobox>
94 )}
95
96 {!userInfoRequestFailed && (
97 <>
98 {!isLoading && (
99 <>
100 <PremiumFeatureContainer>
101 <>
102 <h1>{intl.formatMessage(messages.contentHeadline)}</h1>
103 <p>{intl.formatMessage(messages.intro)}</p>
104 <p>{intl.formatMessage(messages.copy)}</p>
105 {user.isSubscriptionOwner && (
106 <Button
107 label={intl.formatMessage(messages.manageButton)}
108 onClick={openTeamManagement}
109 className={classes.cta}
110 />
111 )}
112 </>
113 </PremiumFeatureContainer>
114 </>
115 )}
116 </>
117 )}
118 </div>
119 <ReactTooltip place="right" type="dark" effect="solid" />
120 </div>
121 );
122 }
123}
diff --git a/src/config.js b/src/config.js
index 242675762..0a47aa7d7 100644
--- a/src/config.js
+++ b/src/config.js
@@ -12,6 +12,15 @@ export const CHECK_INTERVAL = ms('1h'); // How often should we perform checks
12export const LOCAL_API = 'http://localhost:3000'; 12export const LOCAL_API = 'http://localhost:3000';
13export const DEV_API = 'https://dev.franzinfra.com'; 13export const DEV_API = 'https://dev.franzinfra.com';
14export const LIVE_API = 'https://api.franzinfra.com'; 14export const LIVE_API = 'https://api.franzinfra.com';
15
16export const LOCAL_WS_API = 'ws://localhost:3000';
17export const DEV_WS_API = 'wss://dev.franzinfra.com';
18export const LIVE_WS_API = 'wss://api.franzinfra.com';
19
20export const LOCAL_API_WEBSITE = 'http://localhost:3333';
21export const DEV_API_WEBSITE = 'https://meetfranz.com';
22export const LIVE_API_WEBSITE = 'https://meetfranz.com';
23
15export const GA_ID = !isDevMode ? 'UA-74126766-10' : 'UA-74126766-12'; 24export const GA_ID = !isDevMode ? 'UA-74126766-10' : 'UA-74126766-12';
16 25
17export const DEFAULT_APP_SETTINGS = { 26export const DEFAULT_APP_SETTINGS = {
diff --git a/src/containers/settings/AccountScreen.js b/src/containers/settings/AccountScreen.js
index ce1b9c333..b3f967353 100644
--- a/src/containers/settings/AccountScreen.js
+++ b/src/containers/settings/AccountScreen.js
@@ -1,4 +1,3 @@
1import { remote } from 'electron';
2import React, { Component } from 'react'; 1import React, { Component } from 'react';
3import PropTypes from 'prop-types'; 2import PropTypes from 'prop-types';
4import { inject, observer } from 'mobx-react'; 3import { inject, observer } from 'mobx-react';
@@ -9,84 +8,52 @@ import AppStore from '../../stores/AppStore';
9 8
10import AccountDashboard from '../../components/settings/account/AccountDashboard'; 9import AccountDashboard from '../../components/settings/account/AccountDashboard';
11import ErrorBoundary from '../../components/util/ErrorBoundary'; 10import ErrorBoundary from '../../components/util/ErrorBoundary';
12 11import { WEBSITE } from '../../environment';
13const { BrowserWindow } = remote;
14 12
15export default @inject('stores', 'actions') @observer class AccountScreen extends Component { 13export default @inject('stores', 'actions') @observer class AccountScreen extends Component {
16 componentWillMount() {
17 const {
18 user,
19 } = this.props.stores;
20
21 user.getUserInfoRequest.invalidate({ immediately: true });
22 }
23
24 onCloseWindow() { 14 onCloseWindow() {
25 const { user, payment } = this.props.stores; 15 const { user } = this.props.stores;
26 user.getUserInfoRequest.invalidate({ immediately: true }); 16 user.getUserInfoRequest.invalidate({ immediately: true });
27 payment.ordersDataRequest.invalidate({ immediately: true });
28 } 17 }
29 18
30 reloadData() { 19 reloadData() {
31 const { user, payment } = this.props.stores; 20 const { user, payment } = this.props.stores;
32 21
33 user.getUserInfoRequest.reload(); 22 user.getUserInfoRequest.reload();
34 payment.ordersDataRequest.reload();
35 payment.plansRequest.reload(); 23 payment.plansRequest.reload();
36 } 24 }
37 25
38 async handlePaymentDashboard() { 26 handleWebsiteLink(route) {
39 const { actions, stores } = this.props; 27 const { actions, stores } = this.props;
40 28
41 actions.payment.createDashboardUrl(); 29 const url = `${WEBSITE}${route}?authToken=${stores.user.authToken}&utm_source=app&utm_medium=account_dashboard`;
42 30 console.log(url);
43 const dashboard = await stores.payment.createDashboardUrlRequest;
44
45 if (dashboard.url) {
46 const paymentWindow = new BrowserWindow({
47 title: '🔒 Franz Subscription Dashboard',
48 parent: remote.getCurrentWindow(),
49 modal: false,
50 width: 900,
51 minWidth: 600,
52 webPreferences: {
53 nodeIntegration: false,
54 },
55 });
56 paymentWindow.loadURL(dashboard.url);
57 31
58 paymentWindow.on('closed', () => { 32 actions.app.openExternalUrl({ url });
59 this.onCloseWindow();
60 });
61 }
62 } 33 }
63 34
64 render() { 35 render() {
65 const { user, payment } = this.props.stores; 36 const { user, payment } = this.props.stores;
66 const { openExternalUrl } = this.props.actions.app;
67 const { user: userActions } = this.props.actions; 37 const { user: userActions } = this.props.actions;
68 38
69 const isLoadingUserInfo = user.getUserInfoRequest.isExecuting; 39 const isLoadingUserInfo = user.getUserInfoRequest.isExecuting;
70 const isLoadingOrdersInfo = payment.ordersDataRequest.isExecuting;
71 const isLoadingPlans = payment.plansRequest.isExecuting; 40 const isLoadingPlans = payment.plansRequest.isExecuting;
72 41
73 return ( 42 return (
74 <ErrorBoundary> 43 <ErrorBoundary>
75 <AccountDashboard 44 <AccountDashboard
76 user={user.data} 45 user={user.data}
77 orders={payment.orders}
78 isLoading={isLoadingUserInfo} 46 isLoading={isLoadingUserInfo}
79 isLoadingOrdersInfo={isLoadingOrdersInfo}
80 isLoadingPlans={isLoadingPlans} 47 isLoadingPlans={isLoadingPlans}
81 userInfoRequestFailed={user.getUserInfoRequest.wasExecuted && user.getUserInfoRequest.isError} 48 userInfoRequestFailed={user.getUserInfoRequest.wasExecuted && user.getUserInfoRequest.isError}
82 retryUserInfoRequest={() => this.reloadData()} 49 retryUserInfoRequest={() => this.reloadData()}
83 isCreatingPaymentDashboardUrl={payment.createDashboardUrlRequest.isExecuting}
84 openDashboard={price => this.handlePaymentDashboard(price)}
85 openExternalUrl={url => openExternalUrl({ url })}
86 onCloseSubscriptionWindow={() => this.onCloseWindow()} 50 onCloseSubscriptionWindow={() => this.onCloseWindow()}
87 deleteAccount={userActions.delete} 51 deleteAccount={userActions.delete}
88 isLoadingDeleteAccount={user.deleteAccountRequest.isExecuting} 52 isLoadingDeleteAccount={user.deleteAccountRequest.isExecuting}
89 isDeleteAccountSuccessful={user.deleteAccountRequest.wasExecuted && !user.deleteAccountRequest.isError} 53 isDeleteAccountSuccessful={user.deleteAccountRequest.wasExecuted && !user.deleteAccountRequest.isError}
54 openEditAccount={() => this.handleWebsiteLink('/user/profile')}
55 openBilling={() => this.handleWebsiteLink('/user/billing')}
56 openInvoices={() => this.handleWebsiteLink('/user/invoices')}
90 /> 57 />
91 </ErrorBoundary> 58 </ErrorBoundary>
92 ); 59 );
diff --git a/src/containers/settings/TeamScreen.js b/src/containers/settings/TeamScreen.js
new file mode 100644
index 000000000..30b0895e3
--- /dev/null
+++ b/src/containers/settings/TeamScreen.js
@@ -0,0 +1,58 @@
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import { inject, observer } from 'mobx-react';
4
5import UserStore from '../../stores/UserStore';
6import AppStore from '../../stores/AppStore';
7
8import TeamDashboard from '../../components/settings/team/TeamDashboard';
9import ErrorBoundary from '../../components/util/ErrorBoundary';
10import { WEBSITE } from '../../environment';
11
12export default @inject('stores', 'actions') @observer class TeamScreen extends Component {
13 handleWebsiteLink(route) {
14 const { actions, stores } = this.props;
15
16 const url = `${WEBSITE}${route}?authToken=${stores.user.authToken}&utm_source=app&utm_medium=account_dashboard`;
17 console.log(url);
18
19 actions.app.openExternalUrl({ url });
20 }
21
22 render() {
23 const { user } = this.props.stores;
24
25 const isLoadingUserInfo = user.getUserInfoRequest.isExecuting;
26
27 return (
28 <ErrorBoundary>
29 <TeamDashboard
30 user={user.data}
31 isLoading={isLoadingUserInfo}
32 userInfoRequestFailed={user.getUserInfoRequest.wasExecuted && user.getUserInfoRequest.isError}
33 retryUserInfoRequest={() => this.reloadData()}
34 openTeamManagement={() => this.handleWebsiteLink('/user/team')}
35 />
36 </ErrorBoundary>
37 );
38 }
39}
40
41TeamScreen.wrappedComponent.propTypes = {
42 stores: PropTypes.shape({
43 user: PropTypes.instanceOf(UserStore).isRequired,
44 app: PropTypes.instanceOf(AppStore).isRequired,
45 }).isRequired,
46 actions: PropTypes.shape({
47 payment: PropTypes.shape({
48 createDashboardUrl: PropTypes.func.isRequired,
49 }).isRequired,
50 app: PropTypes.shape({
51 openExternalUrl: PropTypes.func.isRequired,
52 }).isRequired,
53 user: PropTypes.shape({
54 update: PropTypes.func.isRequired,
55 delete: PropTypes.func.isRequired,
56 }).isRequired,
57 }).isRequired,
58};
diff --git a/src/environment.js b/src/environment.js
index d67fd6adb..ae7a67e4d 100644
--- a/src/environment.js
+++ b/src/environment.js
@@ -1,6 +1,16 @@
1import isDev from 'electron-is-dev'; 1import isDev from 'electron-is-dev';
2 2
3import { LIVE_API, DEV_API, LOCAL_API } from './config'; 3import {
4 LIVE_API,
5 DEV_API,
6 LOCAL_API,
7 LOCAL_API_WEBSITE,
8 DEV_API_WEBSITE,
9 LIVE_API_WEBSITE,
10 LIVE_WS_API,
11 LOCAL_WS_API,
12 DEV_WS_API,
13} from './config';
4 14
5export const isDevMode = isDev; 15export const isDevMode = isDev;
6export const useLiveAPI = process.env.LIVE_API; 16export const useLiveAPI = process.env.LIVE_API;
@@ -19,13 +29,23 @@ export const ctrlKey = isMac ? '⌘' : 'Ctrl';
19export const cmdKey = isMac ? 'Cmd' : 'Ctrl'; 29export const cmdKey = isMac ? 'Cmd' : 'Ctrl';
20 30
21let api; 31let api;
32let wsApi;
33let web;
22if (!isDevMode || (isDevMode && useLiveAPI)) { 34if (!isDevMode || (isDevMode && useLiveAPI)) {
23 api = LIVE_API; 35 api = LIVE_API;
36 wsApi = LIVE_WS_API;
37 web = LIVE_API_WEBSITE;
24} else if (isDevMode && useLocalAPI) { 38} else if (isDevMode && useLocalAPI) {
25 api = LOCAL_API; 39 api = LOCAL_API;
40 wsApi = LOCAL_WS_API;
41 web = LOCAL_API_WEBSITE;
26} else { 42} else {
27 api = DEV_API; 43 api = DEV_API;
44 wsApi = DEV_WS_API;
45 web = DEV_API_WEBSITE;
28} 46}
29 47
30export const API = api; 48export const API = api;
31export const API_VERSION = 'v1'; 49export const API_VERSION = 'v1';
50export const WS_API = wsApi;
51export const WEBSITE = web;
diff --git a/src/features/settingsWS/actions.js b/src/features/settingsWS/actions.js
new file mode 100755
index 000000000..631670c8a
--- /dev/null
+++ b/src/features/settingsWS/actions.js
@@ -0,0 +1,10 @@
1import PropTypes from 'prop-types';
2import { createActionsFromDefinitions } from '../../actions/lib/actions';
3
4export const settingsWSActions = createActionsFromDefinitions({
5 greet: {
6 name: PropTypes.string.isRequired,
7 },
8}, PropTypes.checkPropTypes);
9
10export default settingsWSActions;
diff --git a/src/features/settingsWS/index.js b/src/features/settingsWS/index.js
new file mode 100755
index 000000000..1e268f184
--- /dev/null
+++ b/src/features/settingsWS/index.js
@@ -0,0 +1,35 @@
1import { reaction, runInAction } from 'mobx';
2import { SettingsWSStore } from './store';
3import state, { resetState } from './state';
4
5const debug = require('debug')('Franz:feature:settingsWS');
6
7let store = null;
8
9export default function initAnnouncements(stores, actions) {
10 const { features } = stores;
11
12 // Toggle workspace feature
13 reaction(
14 () => (
15 features.features.isSettingsWSEnabled
16 ),
17 (isEnabled) => {
18 if (isEnabled) {
19 debug('Initializing `settingsWS` feature');
20 store = new SettingsWSStore(stores, null, actions, state);
21 store.initialize();
22 runInAction(() => { state.isFeatureActive = true; });
23 } else if (store) {
24 debug('Disabling `settingsWS` feature');
25 runInAction(() => { state.isFeatureActive = false; });
26 store.teardown();
27 store = null;
28 resetState(); // Reset state to default
29 }
30 },
31 {
32 fireImmediately: true,
33 },
34 );
35}
diff --git a/src/features/settingsWS/state.js b/src/features/settingsWS/state.js
new file mode 100755
index 000000000..7b16b2b6e
--- /dev/null
+++ b/src/features/settingsWS/state.js
@@ -0,0 +1,13 @@
1import { observable } from 'mobx';
2
3const defaultState = {
4 isFeatureActive: false,
5};
6
7export const settingsWSState = observable(defaultState);
8
9export function resetState() {
10 Object.assign(settingsWSState, defaultState);
11}
12
13export default settingsWSState;
diff --git a/src/features/settingsWS/store.js b/src/features/settingsWS/store.js
new file mode 100755
index 000000000..3cc74d8ef
--- /dev/null
+++ b/src/features/settingsWS/store.js
@@ -0,0 +1,104 @@
1import { observable, reaction } from 'mobx';
2import WebSocket from 'ws';
3import ms from 'ms';
4
5import Store from '../../stores/lib/Store';
6import { WS_API } from '../../environment';
7
8const debug = require('debug')('Franz:feature:settingsWS:store');
9
10export class SettingsWSStore extends Store {
11 ws = null;
12
13 @observable connected = false;
14
15 pingTimeout = null;
16
17 reconnectTimeout = null;
18
19 constructor(stores, api, actions, state) {
20 super(stores, api, actions);
21 this.state = state;
22 }
23
24 setup() {
25 this.connect();
26
27 reaction(() => !this.connected, this.reconnect.bind(this));
28 }
29
30 connect() {
31 try {
32 const wsURL = `${WS_API}/ws/${this.stores.user.data.id}`;
33 debug('Setting up WebSocket to', wsURL);
34
35 this.ws = new WebSocket(wsURL);
36
37 this.ws.on('open', () => {
38 debug('Opened WebSocket');
39 this.send({
40 action: 'authorize',
41 token: this.stores.user.authToken,
42 });
43
44 this.connected = true;
45
46 this.heartbeat();
47 });
48
49 this.ws.on('message', (data) => {
50 const resp = JSON.parse(data);
51 debug('Received message', resp);
52
53 if (resp.id) {
54 this.stores.user.getUserInfoRequest.patch((result) => {
55 if (!result) return;
56
57 debug('Patching user object with new values');
58 Object.assign(result, resp);
59 });
60 }
61 });
62
63 this.ws.on('ping', this.heartbeat.bind(this));
64 } catch (err) {
65 console.err(err);
66 }
67 }
68
69 heartbeat() {
70 debug('Heartbeat');
71 clearTimeout(this.pingTimeout);
72
73 this.pingTimeout = setTimeout(() => {
74 debug('Terminating connection reconnecting in 35');
75 this.ws.terminate();
76
77 this.connected = false;
78 }, ms('35s'));
79 }
80
81 send(data) {
82 if (this.ws) {
83 this.ws.send(JSON.stringify(data));
84 debug('Sending data', data);
85 } else {
86 debug('WebSocket is not initialized');
87 }
88 }
89
90 reconnect() {
91 if (!this.connected) {
92 debug('Trying to reconnect in 30s');
93 this.reconnectTimeout = setInterval(() => {
94 debug('Trying to reconnect');
95 this.connect();
96 }, ms('30s'));
97 } else {
98 debug('Clearing reconnect interval');
99 clearInterval(this.reconnectTimeout);
100 }
101 }
102}
103
104export default SettingsWSStore;
diff --git a/src/i18n/locales/defaultMessages.json b/src/i18n/locales/defaultMessages.json
index 65799b614..8fe5e8852 100644
--- a/src/i18n/locales/defaultMessages.json
+++ b/src/i18n/locales/defaultMessages.json
@@ -1102,195 +1102,195 @@
1102 "defaultMessage": "!!!Account", 1102 "defaultMessage": "!!!Account",
1103 "end": { 1103 "end": {
1104 "column": 3, 1104 "column": 3,
1105 "line": 18 1105 "line": 16
1106 }, 1106 },
1107 "file": "src/components/settings/account/AccountDashboard.js", 1107 "file": "src/components/settings/account/AccountDashboard.js",
1108 "id": "settings.account.headline", 1108 "id": "settings.account.headline",
1109 "start": { 1109 "start": {
1110 "column": 12, 1110 "column": 12,
1111 "line": 15 1111 "line": 13
1112 } 1112 }
1113 }, 1113 },
1114 { 1114 {
1115 "defaultMessage": "!!!Your Subscription", 1115 "defaultMessage": "!!!Your Subscription",
1116 "end": { 1116 "end": {
1117 "column": 3, 1117 "column": 3,
1118 "line": 22 1118 "line": 20
1119 }, 1119 },
1120 "file": "src/components/settings/account/AccountDashboard.js", 1120 "file": "src/components/settings/account/AccountDashboard.js",
1121 "id": "settings.account.headlineSubscription", 1121 "id": "settings.account.headlineSubscription",
1122 "start": { 1122 "start": {
1123 "column": 24, 1123 "column": 24,
1124 "line": 19 1124 "line": 17
1125 } 1125 }
1126 }, 1126 },
1127 { 1127 {
1128 "defaultMessage": "!!!Upgrade your Account", 1128 "defaultMessage": "!!!Upgrade your Account",
1129 "end": { 1129 "end": {
1130 "column": 3, 1130 "column": 3,
1131 "line": 26 1131 "line": 24
1132 }, 1132 },
1133 "file": "src/components/settings/account/AccountDashboard.js", 1133 "file": "src/components/settings/account/AccountDashboard.js",
1134 "id": "settings.account.headlineUpgrade", 1134 "id": "settings.account.headlineUpgrade",
1135 "start": { 1135 "start": {
1136 "column": 19, 1136 "column": 19,
1137 "line": 23 1137 "line": 21
1138 }
1139 },
1140 {
1141 "defaultMessage": "!!Invoices",
1142 "end": {
1143 "column": 3,
1144 "line": 30
1145 },
1146 "file": "src/components/settings/account/AccountDashboard.js",
1147 "id": "settings.account.headlineInvoices",
1148 "start": {
1149 "column": 20,
1150 "line": 27
1151 } 1138 }
1152 }, 1139 },
1153 { 1140 {
1154 "defaultMessage": "!!Danger Zone", 1141 "defaultMessage": "!!Danger Zone",
1155 "end": { 1142 "end": {
1156 "column": 3, 1143 "column": 3,
1157 "line": 34 1144 "line": 28
1158 }, 1145 },
1159 "file": "src/components/settings/account/AccountDashboard.js", 1146 "file": "src/components/settings/account/AccountDashboard.js",
1160 "id": "settings.account.headlineDangerZone", 1147 "id": "settings.account.headlineDangerZone",
1161 "start": { 1148 "start": {
1162 "column": 22, 1149 "column": 22,
1163 "line": 31 1150 "line": 25
1164 } 1151 }
1165 }, 1152 },
1166 { 1153 {
1167 "defaultMessage": "!!!Manage your subscription", 1154 "defaultMessage": "!!!Manage your subscription",
1168 "end": { 1155 "end": {
1169 "column": 3, 1156 "column": 3,
1170 "line": 38 1157 "line": 32
1171 }, 1158 },
1172 "file": "src/components/settings/account/AccountDashboard.js", 1159 "file": "src/components/settings/account/AccountDashboard.js",
1173 "id": "settings.account.manageSubscription.label", 1160 "id": "settings.account.manageSubscription.label",
1174 "start": { 1161 "start": {
1175 "column": 33, 1162 "column": 33,
1176 "line": 35 1163 "line": 29
1177 } 1164 }
1178 }, 1165 },
1179 { 1166 {
1180 "defaultMessage": "!!!Basic Account", 1167 "defaultMessage": "!!!Basic Account",
1181 "end": { 1168 "end": {
1182 "column": 3, 1169 "column": 3,
1183 "line": 42 1170 "line": 36
1184 }, 1171 },
1185 "file": "src/components/settings/account/AccountDashboard.js", 1172 "file": "src/components/settings/account/AccountDashboard.js",
1186 "id": "settings.account.accountType.basic", 1173 "id": "settings.account.accountType.basic",
1187 "start": { 1174 "start": {
1188 "column": 20, 1175 "column": 20,
1189 "line": 39 1176 "line": 33
1190 } 1177 }
1191 }, 1178 },
1192 { 1179 {
1193 "defaultMessage": "!!!Premium Supporter Account", 1180 "defaultMessage": "!!!Premium Supporter Account",
1194 "end": { 1181 "end": {
1195 "column": 3, 1182 "column": 3,
1196 "line": 46 1183 "line": 40
1197 }, 1184 },
1198 "file": "src/components/settings/account/AccountDashboard.js", 1185 "file": "src/components/settings/account/AccountDashboard.js",
1199 "id": "settings.account.accountType.premium", 1186 "id": "settings.account.accountType.premium",
1200 "start": { 1187 "start": {
1201 "column": 22, 1188 "column": 22,
1202 "line": 43 1189 "line": 37
1203 } 1190 }
1204 }, 1191 },
1205 { 1192 {
1206 "defaultMessage": "!!!Edit Account", 1193 "defaultMessage": "!!!Edit Account",
1207 "end": { 1194 "end": {
1208 "column": 3, 1195 "column": 3,
1209 "line": 50 1196 "line": 44
1210 }, 1197 },
1211 "file": "src/components/settings/account/AccountDashboard.js", 1198 "file": "src/components/settings/account/AccountDashboard.js",
1212 "id": "settings.account.account.editButton", 1199 "id": "settings.account.account.editButton",
1213 "start": { 1200 "start": {
1214 "column": 21, 1201 "column": 21,
1215 "line": 47 1202 "line": 41
1203 }
1204 },
1205 {
1206 "defaultMessage": "!!Invoices",
1207 "end": {
1208 "column": 3,
1209 "line": 48
1210 },
1211 "file": "src/components/settings/account/AccountDashboard.js",
1212 "id": "settings.account.headlineInvoices",
1213 "start": {
1214 "column": 18,
1215 "line": 45
1216 } 1216 }
1217 }, 1217 },
1218 { 1218 {
1219 "defaultMessage": "!!!Download", 1219 "defaultMessage": "!!!Download",
1220 "end": { 1220 "end": {
1221 "column": 3, 1221 "column": 3,
1222 "line": 54 1222 "line": 52
1223 }, 1223 },
1224 "file": "src/components/settings/account/AccountDashboard.js", 1224 "file": "src/components/settings/account/AccountDashboard.js",
1225 "id": "settings.account.invoiceDownload", 1225 "id": "settings.account.invoiceDownload",
1226 "start": { 1226 "start": {
1227 "column": 19, 1227 "column": 19,
1228 "line": 51 1228 "line": 49
1229 } 1229 }
1230 }, 1230 },
1231 { 1231 {
1232 "defaultMessage": "!!!Could not load user information", 1232 "defaultMessage": "!!!Could not load user information",
1233 "end": { 1233 "end": {
1234 "column": 3, 1234 "column": 3,
1235 "line": 58 1235 "line": 56
1236 }, 1236 },
1237 "file": "src/components/settings/account/AccountDashboard.js", 1237 "file": "src/components/settings/account/AccountDashboard.js",
1238 "id": "settings.account.userInfoRequestFailed", 1238 "id": "settings.account.userInfoRequestFailed",
1239 "start": { 1239 "start": {
1240 "column": 25, 1240 "column": 25,
1241 "line": 55 1241 "line": 53
1242 } 1242 }
1243 }, 1243 },
1244 { 1244 {
1245 "defaultMessage": "!!!Try again", 1245 "defaultMessage": "!!!Try again",
1246 "end": { 1246 "end": {
1247 "column": 3, 1247 "column": 3,
1248 "line": 62 1248 "line": 60
1249 }, 1249 },
1250 "file": "src/components/settings/account/AccountDashboard.js", 1250 "file": "src/components/settings/account/AccountDashboard.js",
1251 "id": "settings.account.tryReloadUserInfoRequest", 1251 "id": "settings.account.tryReloadUserInfoRequest",
1252 "start": { 1252 "start": {
1253 "column": 28, 1253 "column": 28,
1254 "line": 59 1254 "line": 57
1255 } 1255 }
1256 }, 1256 },
1257 { 1257 {
1258 "defaultMessage": "!!!Delete account", 1258 "defaultMessage": "!!!Delete account",
1259 "end": { 1259 "end": {
1260 "column": 3, 1260 "column": 3,
1261 "line": 66 1261 "line": 64
1262 }, 1262 },
1263 "file": "src/components/settings/account/AccountDashboard.js", 1263 "file": "src/components/settings/account/AccountDashboard.js",
1264 "id": "settings.account.deleteAccount", 1264 "id": "settings.account.deleteAccount",
1265 "start": { 1265 "start": {
1266 "column": 17, 1266 "column": 17,
1267 "line": 63 1267 "line": 61
1268 } 1268 }
1269 }, 1269 },
1270 { 1270 {
1271 "defaultMessage": "!!!If you don't need your Franz account any longer, you can delete your account and all related data here.", 1271 "defaultMessage": "!!!If you don't need your Franz account any longer, you can delete your account and all related data here.",
1272 "end": { 1272 "end": {
1273 "column": 3, 1273 "column": 3,
1274 "line": 70 1274 "line": 68
1275 }, 1275 },
1276 "file": "src/components/settings/account/AccountDashboard.js", 1276 "file": "src/components/settings/account/AccountDashboard.js",
1277 "id": "settings.account.deleteInfo", 1277 "id": "settings.account.deleteInfo",
1278 "start": { 1278 "start": {
1279 "column": 14, 1279 "column": 14,
1280 "line": 67 1280 "line": 65
1281 } 1281 }
1282 }, 1282 },
1283 { 1283 {
1284 "defaultMessage": "!!!You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!", 1284 "defaultMessage": "!!!You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!",
1285 "end": { 1285 "end": {
1286 "column": 3, 1286 "column": 3,
1287 "line": 74 1287 "line": 72
1288 }, 1288 },
1289 "file": "src/components/settings/account/AccountDashboard.js", 1289 "file": "src/components/settings/account/AccountDashboard.js",
1290 "id": "settings.account.deleteEmailSent", 1290 "id": "settings.account.deleteEmailSent",
1291 "start": { 1291 "start": {
1292 "column": 19, 1292 "column": 19,
1293 "line": 71 1293 "line": 69
1294 } 1294 }
1295 } 1295 }
1296 ], 1296 ],
@@ -1351,12 +1351,25 @@
1351 } 1351 }
1352 }, 1352 },
1353 { 1353 {
1354 "defaultMessage": "!!!Settings", 1354 "defaultMessage": "!!!Manage Team",
1355 "end": { 1355 "end": {
1356 "column": 3, 1356 "column": 3,
1357 "line": 31 1357 "line": 31
1358 }, 1358 },
1359 "file": "src/components/settings/navigation/SettingsNavigation.js", 1359 "file": "src/components/settings/navigation/SettingsNavigation.js",
1360 "id": "settings.navigation.team",
1361 "start": {
1362 "column": 8,
1363 "line": 21
1364 }
1365 },
1366 {
1367 "defaultMessage": "!!!Settings",
1368 "end": {
1369 "column": 3,
1370 "line": 28
1371 },
1372 "file": "src/components/settings/navigation/SettingsNavigation.js",
1360 "id": "settings.navigation.settings", 1373 "id": "settings.navigation.settings",
1361 "start": { 1374 "start": {
1362 "column": 12, 1375 "column": 12,
@@ -2244,6 +2257,89 @@
2244 { 2257 {
2245 "descriptors": [ 2258 "descriptors": [
2246 { 2259 {
2260 "defaultMessage": "!!!Team",
2261 "end": {
2262 "column": 3,
2263 "line": 17
2264 },
2265 "file": "src/components/settings/team/TeamDashboard.js",
2266 "id": "settings.team.headline",
2267 "start": {
2268 "column": 12,
2269 "line": 14
2270 }
2271 },
2272 {
2273 "defaultMessage": "!!!Franz for Teams",
2274 "end": {
2275 "column": 3,
2276 "line": 21
2277 },
2278 "file": "src/components/settings/team/TeamDashboard.js",
2279 "id": "settings.team.contentHeadline",
2280 "start": {
2281 "column": 19,
2282 "line": 18
2283 }
2284 },
2285 {
2286 "defaultMessage": "!!!You and your team use Franz? You can now manage Premium subscriptions for as many colleagues, friends or family members as you want, all from within one account.",
2287 "end": {
2288 "column": 3,
2289 "line": 25
2290 },
2291 "file": "src/components/settings/team/TeamDashboard.js",
2292 "id": "settings.team.intro",
2293 "start": {
2294 "column": 9,
2295 "line": 22
2296 }
2297 },
2298 {
2299 "defaultMessage": "!!!Franz for Teams gives you the option to invite co-workers to your team by sending them email invitations and manage their subscriptions in your account’s preferences. Don’t waste time setting up subscriptions for every team member individually, forget about multiple invoices and different billing cycles - one team to rule them all!",
2300 "end": {
2301 "column": 3,
2302 "line": 29
2303 },
2304 "file": "src/components/settings/team/TeamDashboard.js",
2305 "id": "settings.team.copy",
2306 "start": {
2307 "column": 8,
2308 "line": 26
2309 }
2310 },
2311 {
2312 "defaultMessage": "!!!Manage your Team",
2313 "end": {
2314 "column": 3,
2315 "line": 33
2316 },
2317 "file": "src/components/settings/team/TeamDashboard.js",
2318 "id": "settings.team.manageAction",
2319 "start": {
2320 "column": 16,
2321 "line": 30
2322 }
2323 },
2324 {
2325 "defaultMessage": "!!!Upgrade your Account",
2326 "end": {
2327 "column": 3,
2328 "line": 37
2329 },
2330 "file": "src/components/settings/team/TeamDashboard.js",
2331 "id": "settings.team.upgradeAction",
2332 "start": {
2333 "column": 17,
2334 "line": 34
2335 }
2336 }
2337 ],
2338 "path": "src/components/settings/team/TeamDashboard.json"
2339 },
2340 {
2341 "descriptors": [
2342 {
2247 "defaultMessage": "!!!Account", 2343 "defaultMessage": "!!!Account",
2248 "end": { 2344 "end": {
2249 "column": 3, 2345 "column": 3,
@@ -4421,4 +4517,4 @@
4421 ], 4517 ],
4422 "path": "src/lib/Menu.json" 4518 "path": "src/lib/Menu.json"
4423 } 4519 }
4424] \ No newline at end of file 4520]
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 84a71117a..bcdfb8220 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -174,6 +174,7 @@
174 "settings.navigation.inviteFriends": "Invite Friends", 174 "settings.navigation.inviteFriends": "Invite Friends",
175 "settings.navigation.logout": "Logout", 175 "settings.navigation.logout": "Logout",
176 "settings.navigation.settings": "Settings", 176 "settings.navigation.settings": "Settings",
177 "settings.navigation.team": "Manage Team",
177 "settings.navigation.yourServices": "Your services", 178 "settings.navigation.yourServices": "Your services",
178 "settings.navigation.yourWorkspaces": "Your workspaces", 179 "settings.navigation.yourWorkspaces": "Your workspaces",
179 "settings.recipes.all": "All services", 180 "settings.recipes.all": "All services",
@@ -233,6 +234,12 @@
233 "settings.services.tooltip.isMuted": "All sounds are muted", 234 "settings.services.tooltip.isMuted": "All sounds are muted",
234 "settings.services.tooltip.notificationsDisabled": "Notifications are disabled", 235 "settings.services.tooltip.notificationsDisabled": "Notifications are disabled",
235 "settings.services.updatedInfo": "Your changes have been saved", 236 "settings.services.updatedInfo": "Your changes have been saved",
237 "settings.team.contentHeadline": "Franz for Teams",
238 "settings.team.copy": "Franz for Teams gives you the option to invite co-workers to your team by sending them email invitations and manage their subscriptions in your account’s preferences. Don’t waste time setting up subscriptions for every team member individually, forget about multiple invoices and different billing cycles - one team to rule them all!",
239 "settings.team.headline": "Team",
240 "settings.team.intro": "You and your team use Franz? You can now manage Premium subscriptions for as many colleagues, friends or family members as you want, all from within one account.",
241 "settings.team.manageAction": "Manage your Team on meetfranz.com",
242 "settings.team.upgradeAction": "Upgrade your Account",
236 "settings.user.form.accountType.company": "Company", 243 "settings.user.form.accountType.company": "Company",
237 "settings.user.form.accountType.individual": "Individual", 244 "settings.user.form.accountType.individual": "Individual",
238 "settings.user.form.accountType.label": "Account type", 245 "settings.user.form.accountType.label": "Account type",
@@ -317,4 +324,4 @@
317 "workspaceDrawer.workspaceFeatureInfo": "<p>Franz Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time.</p><p>You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.</p>", 324 "workspaceDrawer.workspaceFeatureInfo": "<p>Franz Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time.</p><p>You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.</p>",
318 "workspaceDrawer.workspacesSettingsTooltip": "Edit workspaces settings", 325 "workspaceDrawer.workspacesSettingsTooltip": "Edit workspaces settings",
319 "workspaces.switchingIndicator.switchingTo": "Switching to" 326 "workspaces.switchingIndicator.switchingTo": "Switching to"
320} \ No newline at end of file 327}
diff --git a/src/i18n/messages/src/components/settings/account/AccountDashboard.json b/src/i18n/messages/src/components/settings/account/AccountDashboard.json
index 603950395..22eaf0381 100644
--- a/src/i18n/messages/src/components/settings/account/AccountDashboard.json
+++ b/src/i18n/messages/src/components/settings/account/AccountDashboard.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!Account", 4 "defaultMessage": "!!!Account",
5 "file": "src/components/settings/account/AccountDashboard.js", 5 "file": "src/components/settings/account/AccountDashboard.js",
6 "start": { 6 "start": {
7 "line": 15, 7 "line": 13,
8 "column": 12 8 "column": 12
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 18, 11 "line": 16,
12 "column": 3 12 "column": 3
13 } 13 }
14 }, 14 },
@@ -17,11 +17,11 @@
17 "defaultMessage": "!!!Your Subscription", 17 "defaultMessage": "!!!Your Subscription",
18 "file": "src/components/settings/account/AccountDashboard.js", 18 "file": "src/components/settings/account/AccountDashboard.js",
19 "start": { 19 "start": {
20 "line": 19, 20 "line": 17,
21 "column": 24 21 "column": 24
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 22, 24 "line": 20,
25 "column": 3 25 "column": 3
26 } 26 }
27 }, 27 },
@@ -30,24 +30,11 @@
30 "defaultMessage": "!!!Upgrade your Account", 30 "defaultMessage": "!!!Upgrade your Account",
31 "file": "src/components/settings/account/AccountDashboard.js", 31 "file": "src/components/settings/account/AccountDashboard.js",
32 "start": { 32 "start": {
33 "line": 23, 33 "line": 21,
34 "column": 19 34 "column": 19
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 26, 37 "line": 24,
38 "column": 3
39 }
40 },
41 {
42 "id": "settings.account.headlineInvoices",
43 "defaultMessage": "!!Invoices",
44 "file": "src/components/settings/account/AccountDashboard.js",
45 "start": {
46 "line": 27,
47 "column": 20
48 },
49 "end": {
50 "line": 30,
51 "column": 3 38 "column": 3
52 } 39 }
53 }, 40 },
@@ -56,11 +43,11 @@
56 "defaultMessage": "!!Danger Zone", 43 "defaultMessage": "!!Danger Zone",
57 "file": "src/components/settings/account/AccountDashboard.js", 44 "file": "src/components/settings/account/AccountDashboard.js",
58 "start": { 45 "start": {
59 "line": 31, 46 "line": 25,
60 "column": 22 47 "column": 22
61 }, 48 },
62 "end": { 49 "end": {
63 "line": 34, 50 "line": 28,
64 "column": 3 51 "column": 3
65 } 52 }
66 }, 53 },
@@ -69,11 +56,11 @@
69 "defaultMessage": "!!!Manage your subscription", 56 "defaultMessage": "!!!Manage your subscription",
70 "file": "src/components/settings/account/AccountDashboard.js", 57 "file": "src/components/settings/account/AccountDashboard.js",
71 "start": { 58 "start": {
72 "line": 35, 59 "line": 29,
73 "column": 33 60 "column": 33
74 }, 61 },
75 "end": { 62 "end": {
76 "line": 38, 63 "line": 32,
77 "column": 3 64 "column": 3
78 } 65 }
79 }, 66 },
@@ -82,11 +69,11 @@
82 "defaultMessage": "!!!Basic Account", 69 "defaultMessage": "!!!Basic Account",
83 "file": "src/components/settings/account/AccountDashboard.js", 70 "file": "src/components/settings/account/AccountDashboard.js",
84 "start": { 71 "start": {
85 "line": 39, 72 "line": 33,
86 "column": 20 73 "column": 20
87 }, 74 },
88 "end": { 75 "end": {
89 "line": 42, 76 "line": 36,
90 "column": 3 77 "column": 3
91 } 78 }
92 }, 79 },
@@ -95,11 +82,11 @@
95 "defaultMessage": "!!!Premium Supporter Account", 82 "defaultMessage": "!!!Premium Supporter Account",
96 "file": "src/components/settings/account/AccountDashboard.js", 83 "file": "src/components/settings/account/AccountDashboard.js",
97 "start": { 84 "start": {
98 "line": 43, 85 "line": 37,
99 "column": 22 86 "column": 22
100 }, 87 },
101 "end": { 88 "end": {
102 "line": 46, 89 "line": 40,
103 "column": 3 90 "column": 3
104 } 91 }
105 }, 92 },
@@ -108,11 +95,24 @@
108 "defaultMessage": "!!!Edit Account", 95 "defaultMessage": "!!!Edit Account",
109 "file": "src/components/settings/account/AccountDashboard.js", 96 "file": "src/components/settings/account/AccountDashboard.js",
110 "start": { 97 "start": {
111 "line": 47, 98 "line": 41,
112 "column": 21 99 "column": 21
113 }, 100 },
114 "end": { 101 "end": {
115 "line": 50, 102 "line": 44,
103 "column": 3
104 }
105 },
106 {
107 "id": "settings.account.headlineInvoices",
108 "defaultMessage": "!!Invoices",
109 "file": "src/components/settings/account/AccountDashboard.js",
110 "start": {
111 "line": 45,
112 "column": 18
113 },
114 "end": {
115 "line": 48,
116 "column": 3 116 "column": 3
117 } 117 }
118 }, 118 },
@@ -121,11 +121,11 @@
121 "defaultMessage": "!!!Download", 121 "defaultMessage": "!!!Download",
122 "file": "src/components/settings/account/AccountDashboard.js", 122 "file": "src/components/settings/account/AccountDashboard.js",
123 "start": { 123 "start": {
124 "line": 51, 124 "line": 49,
125 "column": 19 125 "column": 19
126 }, 126 },
127 "end": { 127 "end": {
128 "line": 54, 128 "line": 52,
129 "column": 3 129 "column": 3
130 } 130 }
131 }, 131 },
@@ -134,11 +134,11 @@
134 "defaultMessage": "!!!Could not load user information", 134 "defaultMessage": "!!!Could not load user information",
135 "file": "src/components/settings/account/AccountDashboard.js", 135 "file": "src/components/settings/account/AccountDashboard.js",
136 "start": { 136 "start": {
137 "line": 55, 137 "line": 53,
138 "column": 25 138 "column": 25
139 }, 139 },
140 "end": { 140 "end": {
141 "line": 58, 141 "line": 56,
142 "column": 3 142 "column": 3
143 } 143 }
144 }, 144 },
@@ -147,11 +147,11 @@
147 "defaultMessage": "!!!Try again", 147 "defaultMessage": "!!!Try again",
148 "file": "src/components/settings/account/AccountDashboard.js", 148 "file": "src/components/settings/account/AccountDashboard.js",
149 "start": { 149 "start": {
150 "line": 59, 150 "line": 57,
151 "column": 28 151 "column": 28
152 }, 152 },
153 "end": { 153 "end": {
154 "line": 62, 154 "line": 60,
155 "column": 3 155 "column": 3
156 } 156 }
157 }, 157 },
@@ -160,11 +160,11 @@
160 "defaultMessage": "!!!Delete account", 160 "defaultMessage": "!!!Delete account",
161 "file": "src/components/settings/account/AccountDashboard.js", 161 "file": "src/components/settings/account/AccountDashboard.js",
162 "start": { 162 "start": {
163 "line": 63, 163 "line": 61,
164 "column": 17 164 "column": 17
165 }, 165 },
166 "end": { 166 "end": {
167 "line": 66, 167 "line": 64,
168 "column": 3 168 "column": 3
169 } 169 }
170 }, 170 },
@@ -173,11 +173,11 @@
173 "defaultMessage": "!!!If you don't need your Franz account any longer, you can delete your account and all related data here.", 173 "defaultMessage": "!!!If you don't need your Franz account any longer, you can delete your account and all related data here.",
174 "file": "src/components/settings/account/AccountDashboard.js", 174 "file": "src/components/settings/account/AccountDashboard.js",
175 "start": { 175 "start": {
176 "line": 67, 176 "line": 65,
177 "column": 14 177 "column": 14
178 }, 178 },
179 "end": { 179 "end": {
180 "line": 70, 180 "line": 68,
181 "column": 3 181 "column": 3
182 } 182 }
183 }, 183 },
@@ -186,11 +186,11 @@
186 "defaultMessage": "!!!You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!", 186 "defaultMessage": "!!!You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!",
187 "file": "src/components/settings/account/AccountDashboard.js", 187 "file": "src/components/settings/account/AccountDashboard.js",
188 "start": { 188 "start": {
189 "line": 71, 189 "line": 69,
190 "column": 19 190 "column": 19
191 }, 191 },
192 "end": { 192 "end": {
193 "line": 74, 193 "line": 72,
194 "column": 3 194 "column": 3
195 } 195 }
196 } 196 }
diff --git a/src/i18n/messages/src/components/settings/navigation/SettingsNavigation.json b/src/i18n/messages/src/components/settings/navigation/SettingsNavigation.json
index 77b0ed8a4..70a989211 100644
--- a/src/i18n/messages/src/components/settings/navigation/SettingsNavigation.json
+++ b/src/i18n/messages/src/components/settings/navigation/SettingsNavigation.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!Available services", 4 "defaultMessage": "!!!Available services",
5 "file": "src/components/settings/navigation/SettingsNavigation.js", 5 "file": "src/components/settings/navigation/SettingsNavigation.js",
6 "start": { 6 "start": {
7 "line": 12, 7 "line": 13,
8 "column": 21 8 "column": 21
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 15, 11 "line": 16,
12 "column": 3 12 "column": 3
13 } 13 }
14 }, 14 },
@@ -17,11 +17,11 @@
17 "defaultMessage": "!!!Your services", 17 "defaultMessage": "!!!Your services",
18 "file": "src/components/settings/navigation/SettingsNavigation.js", 18 "file": "src/components/settings/navigation/SettingsNavigation.js",
19 "start": { 19 "start": {
20 "line": 16, 20 "line": 17,
21 "column": 16 21 "column": 16
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 19, 24 "line": 20,
25 "column": 3 25 "column": 3
26 } 26 }
27 }, 27 },
@@ -30,11 +30,11 @@
30 "defaultMessage": "!!!Your workspaces", 30 "defaultMessage": "!!!Your workspaces",
31 "file": "src/components/settings/navigation/SettingsNavigation.js", 31 "file": "src/components/settings/navigation/SettingsNavigation.js",
32 "start": { 32 "start": {
33 "line": 20, 33 "line": 21,
34 "column": 18 34 "column": 18
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 23, 37 "line": 24,
38 "column": 3 38 "column": 3
39 } 39 }
40 }, 40 },
@@ -43,11 +43,24 @@
43 "defaultMessage": "!!!Account", 43 "defaultMessage": "!!!Account",
44 "file": "src/components/settings/navigation/SettingsNavigation.js", 44 "file": "src/components/settings/navigation/SettingsNavigation.js",
45 "start": { 45 "start": {
46 "line": 24, 46 "line": 25,
47 "column": 11 47 "column": 11
48 }, 48 },
49 "end": { 49 "end": {
50 "line": 27, 50 "line": 28,
51 "column": 3
52 }
53 },
54 {
55 "id": "settings.navigation.team",
56 "defaultMessage": "!!!Manage Team",
57 "file": "src/components/settings/navigation/SettingsNavigation.js",
58 "start": {
59 "line": 29,
60 "column": 8
61 },
62 "end": {
63 "line": 32,
51 "column": 3 64 "column": 3
52 } 65 }
53 }, 66 },
@@ -56,11 +69,11 @@
56 "defaultMessage": "!!!Settings", 69 "defaultMessage": "!!!Settings",
57 "file": "src/components/settings/navigation/SettingsNavigation.js", 70 "file": "src/components/settings/navigation/SettingsNavigation.js",
58 "start": { 71 "start": {
59 "line": 28, 72 "line": 33,
60 "column": 12 73 "column": 12
61 }, 74 },
62 "end": { 75 "end": {
63 "line": 31, 76 "line": 36,
64 "column": 3 77 "column": 3
65 } 78 }
66 }, 79 },
@@ -69,11 +82,11 @@
69 "defaultMessage": "!!!Invite Friends", 82 "defaultMessage": "!!!Invite Friends",
70 "file": "src/components/settings/navigation/SettingsNavigation.js", 83 "file": "src/components/settings/navigation/SettingsNavigation.js",
71 "start": { 84 "start": {
72 "line": 32, 85 "line": 37,
73 "column": 17 86 "column": 17
74 }, 87 },
75 "end": { 88 "end": {
76 "line": 35, 89 "line": 40,
77 "column": 3 90 "column": 3
78 } 91 }
79 }, 92 },
@@ -82,11 +95,11 @@
82 "defaultMessage": "!!!Logout", 95 "defaultMessage": "!!!Logout",
83 "file": "src/components/settings/navigation/SettingsNavigation.js", 96 "file": "src/components/settings/navigation/SettingsNavigation.js",
84 "start": { 97 "start": {
85 "line": 36, 98 "line": 41,
86 "column": 10 99 "column": 10
87 }, 100 },
88 "end": { 101 "end": {
89 "line": 39, 102 "line": 44,
90 "column": 3 103 "column": 3
91 } 104 }
92 } 105 }
diff --git a/src/i18n/messages/src/components/settings/team/TeamDashboard.json b/src/i18n/messages/src/components/settings/team/TeamDashboard.json
new file mode 100644
index 000000000..91b6754ea
--- /dev/null
+++ b/src/i18n/messages/src/components/settings/team/TeamDashboard.json
@@ -0,0 +1,80 @@
1[
2 {
3 "id": "settings.team.headline",
4 "defaultMessage": "!!!Team",
5 "file": "src/components/settings/team/TeamDashboard.js",
6 "start": {
7 "line": 14,
8 "column": 12
9 },
10 "end": {
11 "line": 17,
12 "column": 3
13 }
14 },
15 {
16 "id": "settings.team.contentHeadline",
17 "defaultMessage": "!!!Franz for Teams",
18 "file": "src/components/settings/team/TeamDashboard.js",
19 "start": {
20 "line": 18,
21 "column": 19
22 },
23 "end": {
24 "line": 21,
25 "column": 3
26 }
27 },
28 {
29 "id": "settings.team.intro",
30 "defaultMessage": "!!!You and your team use Franz? You can now manage Premium subscriptions for as many colleagues, friends or family members as you want, all from within one account.",
31 "file": "src/components/settings/team/TeamDashboard.js",
32 "start": {
33 "line": 22,
34 "column": 9
35 },
36 "end": {
37 "line": 25,
38 "column": 3
39 }
40 },
41 {
42 "id": "settings.team.copy",
43 "defaultMessage": "!!!Franz for Teams gives you the option to invite co-workers to your team by sending them email invitations and manage their subscriptions in your account’s preferences. Don’t waste time setting up subscriptions for every team member individually, forget about multiple invoices and different billing cycles - one team to rule them all!",
44 "file": "src/components/settings/team/TeamDashboard.js",
45 "start": {
46 "line": 26,
47 "column": 8
48 },
49 "end": {
50 "line": 29,
51 "column": 3
52 }
53 },
54 {
55 "id": "settings.team.manageAction",
56 "defaultMessage": "!!!Manage your Team",
57 "file": "src/components/settings/team/TeamDashboard.js",
58 "start": {
59 "line": 30,
60 "column": 16
61 },
62 "end": {
63 "line": 33,
64 "column": 3
65 }
66 },
67 {
68 "id": "settings.team.upgradeAction",
69 "defaultMessage": "!!!Upgrade your Account",
70 "file": "src/components/settings/team/TeamDashboard.js",
71 "start": {
72 "line": 34,
73 "column": 17
74 },
75 "end": {
76 "line": 37,
77 "column": 3
78 }
79 }
80] \ No newline at end of file
diff --git a/src/stores/FeaturesStore.js b/src/stores/FeaturesStore.js
index 8fe576813..6200c9a16 100644
--- a/src/stores/FeaturesStore.js
+++ b/src/stores/FeaturesStore.js
@@ -14,6 +14,7 @@ import serviceProxy from '../features/serviceProxy';
14import basicAuth from '../features/basicAuth'; 14import basicAuth from '../features/basicAuth';
15import workspaces from '../features/workspaces'; 15import workspaces from '../features/workspaces';
16import shareFranz from '../features/shareFranz'; 16import shareFranz from '../features/shareFranz';
17import settingsWS from '../features/settingsWS';
17 18
18import { DEFAULT_FEATURES_CONFIG } from '../config'; 19import { DEFAULT_FEATURES_CONFIG } from '../config';
19 20
@@ -71,5 +72,6 @@ export default class FeaturesStore extends Store {
71 basicAuth(this.stores, this.actions); 72 basicAuth(this.stores, this.actions);
72 workspaces(this.stores, this.actions); 73 workspaces(this.stores, this.actions);
73 shareFranz(this.stores, this.actions); 74 shareFranz(this.stores, this.actions);
75 settingsWS(this.stores, this.actions);
74 } 76 }
75} 77}
diff --git a/src/stores/PaymentStore.js b/src/stores/PaymentStore.js
index 4cabee194..d4de476c8 100644
--- a/src/stores/PaymentStore.js
+++ b/src/stores/PaymentStore.js
@@ -10,15 +10,10 @@ export default class PaymentStore extends Store {
10 10
11 @observable createHostedPageRequest = new Request(this.api.payment, 'getHostedPage'); 11 @observable createHostedPageRequest = new Request(this.api.payment, 'getHostedPage');
12 12
13 @observable createDashboardUrlRequest = new Request(this.api.payment, 'getDashboardUrl');
14
15 @observable ordersDataRequest = new CachedRequest(this.api.payment, 'getOrders');
16
17 constructor(...args) { 13 constructor(...args) {
18 super(...args); 14 super(...args);
19 15
20 this.actions.payment.createHostedPage.listen(this._createHostedPage.bind(this)); 16 this.actions.payment.createHostedPage.listen(this._createHostedPage.bind(this));
21 this.actions.payment.createDashboardUrl.listen(this._createDashboardUrl.bind(this));
22 } 17 }
23 18
24 @computed get plan() { 19 @computed get plan() {
@@ -28,10 +23,6 @@ export default class PaymentStore extends Store {
28 return this.plansRequest.execute().result || {}; 23 return this.plansRequest.execute().result || {};
29 } 24 }
30 25
31 @computed get orders() {
32 return this.ordersDataRequest.execute().result || [];
33 }
34
35 @action _createHostedPage({ planId }) { 26 @action _createHostedPage({ planId }) {
36 const request = this.createHostedPageRequest.execute(planId); 27 const request = this.createHostedPageRequest.execute(planId);
37 28
@@ -39,12 +30,4 @@ export default class PaymentStore extends Store {
39 30
40 return request; 31 return request;
41 } 32 }
42
43 @action _createDashboardUrl() {
44 const request = this.createDashboardUrlRequest.execute();
45
46 gaEvent('Payment', 'createDashboardUrl');
47
48 return request;
49 }
50} 33}
diff --git a/src/styles/badge.scss b/src/styles/badge.scss
index f9fac039a..be3a1055d 100644
--- a/src/styles/badge.scss
+++ b/src/styles/badge.scss
@@ -19,6 +19,7 @@
19 display: inline-block; 19 display: inline-block;
20 font-size: 14px; 20 font-size: 14px;
21 padding: 5px 10px; 21 padding: 5px 10px;
22 letter-spacing: 0;
22 23
23 &.badge--primary, 24 &.badge--primary,
24 &.badge--premium { 25 &.badge--premium {
diff --git a/src/styles/settings.scss b/src/styles/settings.scss
index dd6f56d2b..efa0ab942 100644
--- a/src/styles/settings.scss
+++ b/src/styles/settings.scss
@@ -62,9 +62,14 @@
62 .account { 62 .account {
63 .account__box { background: $dark-theme-gray-darker; } 63 .account__box { background: $dark-theme-gray-darker; }
64 64
65 .invoices { 65 .badge--premium {
66 td { border-bottom: 1px solid $dark-theme-gray-darker; } 66 margin-left: 10px;
67 .invoices__action button { color: $theme-brand-primary; } 67 }
68
69 .manage-user-links {
70 margin-top: 20px;
71 display: flex;
72 justify-content: space-between;
68 } 73 }
69 } 74 }
70 75