aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Stefan Malzner <stefan@adlk.io>2019-04-16 11:53:30 +0200
committerLibravatar Stefan Malzner <stefan@adlk.io>2019-04-16 11:53:30 +0200
commit03950e3f8a43691bdb1371625b860b29f40a6e60 (patch)
tree4765ba98e1e3dade746a7d0216b6bde6c9d1cce6 /src
parentMerge branch 'feature/announcements' into develop (diff)
parentMerge pull request #1393 from meetfranz/chore/streamline-dashboard (diff)
downloadferdium-app-03950e3f8a43691bdb1371625b860b29f40a6e60.tar.gz
ferdium-app-03950e3f8a43691bdb1371625b860b29f40a6e60.tar.zst
ferdium-app-03950e3f8a43691bdb1371625b860b29f40a6e60.zip
Merge branch 'develop' of https://github.com/meetfranz/franz into develop
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.js126
-rw-r--r--src/components/settings/navigation/SettingsNavigation.js17
-rw-r--r--src/components/settings/team/TeamDashboard.js152
-rw-r--r--src/config.js11
-rw-r--r--src/containers/settings/AccountScreen.js51
-rw-r--r--src/containers/settings/TeamScreen.js57
-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.js120
-rw-r--r--src/i18n/locales/defaultMessages.json182
-rw-r--r--src/i18n/locales/en-US.json7
-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.scss31
23 files changed, 808 insertions, 276 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 ab27e92bf..a9ce202ff 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..3f6964b6b 100644
--- a/src/components/settings/account/AccountDashboard.js
+++ b/src/components/settings/account/AccountDashboard.js
@@ -3,12 +3,11 @@ 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'; 6import { ProBadge } from '@meetfranz/ui';
7 7
8import Loader from '../../ui/Loader'; 8import Loader from '../../ui/Loader';
9import Button from '../../ui/Button'; 9import Button from '../../ui/Button';
10import Infobox from '../../ui/Infobox'; 10import Infobox from '../../ui/Infobox';
11import Link from '../../ui/Link';
12import SubscriptionForm from '../../../containers/subscription/SubscriptionFormScreen'; 11import SubscriptionForm from '../../../containers/subscription/SubscriptionFormScreen';
13 12
14const messages = defineMessages({ 13const messages = defineMessages({
@@ -24,10 +23,6 @@ const messages = defineMessages({
24 id: 'settings.account.headlineUpgrade', 23 id: 'settings.account.headlineUpgrade',
25 defaultMessage: '!!!Upgrade your Account', 24 defaultMessage: '!!!Upgrade your Account',
26 }, 25 },
27 headlineInvoices: {
28 id: 'settings.account.headlineInvoices',
29 defaultMessage: '!!Invoices',
30 },
31 headlineDangerZone: { 26 headlineDangerZone: {
32 id: 'settings.account.headlineDangerZone', 27 id: 'settings.account.headlineDangerZone',
33 defaultMessage: '!!Danger Zone', 28 defaultMessage: '!!Danger Zone',
@@ -48,6 +43,10 @@ const messages = defineMessages({
48 id: 'settings.account.account.editButton', 43 id: 'settings.account.account.editButton',
49 defaultMessage: '!!!Edit Account', 44 defaultMessage: '!!!Edit Account',
50 }, 45 },
46 invoicesButton: {
47 id: 'settings.account.headlineInvoices',
48 defaultMessage: '!!Invoices',
49 },
51 invoiceDownload: { 50 invoiceDownload: {
52 id: 'settings.account.invoiceDownload', 51 id: 'settings.account.invoiceDownload',
53 defaultMessage: '!!!Download', 52 defaultMessage: '!!!Download',
@@ -77,19 +76,17 @@ const messages = defineMessages({
77export default @observer class AccountDashboard extends Component { 76export default @observer class AccountDashboard extends Component {
78 static propTypes = { 77 static propTypes = {
79 user: MobxPropTypes.observableObject.isRequired, 78 user: MobxPropTypes.observableObject.isRequired,
80 orders: MobxPropTypes.arrayOrObservableArray.isRequired,
81 isLoading: PropTypes.bool.isRequired, 79 isLoading: PropTypes.bool.isRequired,
82 isLoadingOrdersInfo: PropTypes.bool.isRequired,
83 isLoadingPlans: PropTypes.bool.isRequired, 80 isLoadingPlans: PropTypes.bool.isRequired,
84 isCreatingPaymentDashboardUrl: PropTypes.bool.isRequired,
85 userInfoRequestFailed: PropTypes.bool.isRequired, 81 userInfoRequestFailed: PropTypes.bool.isRequired,
86 retryUserInfoRequest: PropTypes.func.isRequired, 82 retryUserInfoRequest: PropTypes.func.isRequired,
87 openDashboard: PropTypes.func.isRequired,
88 openExternalUrl: PropTypes.func.isRequired,
89 onCloseSubscriptionWindow: PropTypes.func.isRequired, 83 onCloseSubscriptionWindow: PropTypes.func.isRequired,
90 deleteAccount: PropTypes.func.isRequired, 84 deleteAccount: PropTypes.func.isRequired,
91 isLoadingDeleteAccount: PropTypes.bool.isRequired, 85 isLoadingDeleteAccount: PropTypes.bool.isRequired,
92 isDeleteAccountSuccessful: PropTypes.bool.isRequired, 86 isDeleteAccountSuccessful: PropTypes.bool.isRequired,
87 openEditAccount: PropTypes.func.isRequired,
88 openBilling: PropTypes.func.isRequired,
89 openInvoices: PropTypes.func.isRequired,
93 }; 90 };
94 91
95 static contextTypes = { 92 static contextTypes = {
@@ -99,12 +96,7 @@ export default @observer class AccountDashboard extends Component {
99 render() { 96 render() {
100 const { 97 const {
101 user, 98 user,
102 orders,
103 isLoading, 99 isLoading,
104 isCreatingPaymentDashboardUrl,
105 openDashboard,
106 openExternalUrl,
107 isLoadingOrdersInfo,
108 isLoadingPlans, 100 isLoadingPlans,
109 userInfoRequestFailed, 101 userInfoRequestFailed,
110 retryUserInfoRequest, 102 retryUserInfoRequest,
@@ -112,6 +104,9 @@ export default @observer class AccountDashboard extends Component {
112 deleteAccount, 104 deleteAccount,
113 isLoadingDeleteAccount, 105 isLoadingDeleteAccount,
114 isDeleteAccountSuccessful, 106 isDeleteAccountSuccessful,
107 openEditAccount,
108 openBilling,
109 openInvoices,
115 } = this.props; 110 } = this.props;
116 const { intl } = this.context; 111 const { intl } = this.context;
117 112
@@ -149,80 +144,53 @@ export default @observer class AccountDashboard extends Component {
149 src="./assets/images/logo.svg" 144 src="./assets/images/logo.svg"
150 alt="" 145 alt=""
151 /> 146 />
152 {user.isPremium && (
153 <span
154 className="account__avatar-premium emoji"
155 data-tip="Premium Supporter Account"
156 >
157 <img src="./assets/images/emoji/star.png" alt="" />
158 </span>
159 )}
160 </div> 147 </div>
161 <div className="account__info"> 148 <div className="account__info">
162 <h2> 149 <h2>
163 {`${user.firstname} ${user.lastname}`} 150 <span className="username">{`${user.firstname} ${user.lastname}`}</span>
151 {user.isPremium && (
152 <>
153 {' '}
154 <ProBadge />
155 <span className="badge badge--premium">{intl.formatMessage(messages.accountTypePremium)}</span>
156 </>
157 )}
164 </h2> 158 </h2>
165 {user.organization && `${user.organization}, `} 159 {user.organization && `${user.organization}, `}
166 {user.email} 160 {user.email}
167 <br />
168 {user.isPremium && ( 161 {user.isPremium && (
169 <span className="badge badge--premium">{intl.formatMessage(messages.accountTypePremium)}</span> 162 <div className="manage-user-links">
163 <Button
164 label={intl.formatMessage(messages.accountEditButton)}
165 className="franz-form__button--inverted"
166 onClick={openEditAccount}
167 />
168 {user.isSubscriptionOwner && (
169 <>
170 <Button
171 label={intl.formatMessage(messages.manageSubscriptionButtonLabel)}
172 className="franz-form__button--inverted"
173 onClick={openBilling}
174 />
175 <Button
176 label={intl.formatMessage(messages.invoicesButton)}
177 className="franz-form__button--inverted"
178 onClick={openInvoices}
179 />
180 </>
181 )}
182 </div>
170 )} 183 )}
171 </div> 184 </div>
172 <Link to="/settings/user/edit" className="button"> 185 {!user.isPremium && (
173 {intl.formatMessage(messages.accountEditButton)} 186 <Button
174 </Link> 187 label={intl.formatMessage(messages.accountEditButton)}
175 {user.emailValidated} 188 className="franz-form__button--inverted"
176 </div> 189 onClick={openEditAccount}
177 </div> 190 />
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 )} 191 )}
224 </div> 192 </div>
225 ) 193 </div>
226 )} 194 )}
227 195
228 {!user.isPremium && ( 196 {!user.isPremium && (
diff --git a/src/components/settings/navigation/SettingsNavigation.js b/src/components/settings/navigation/SettingsNavigation.js
index 993b0a44a..df4b3b3b2 100644
--- a/src/components/settings/navigation/SettingsNavigation.js
+++ b/src/components/settings/navigation/SettingsNavigation.js
@@ -7,6 +7,7 @@ import { ProBadge } from '@meetfranz/ui';
7import Link from '../../ui/Link'; 7import Link from '../../ui/Link';
8import { workspaceStore } from '../../../features/workspaces'; 8import { workspaceStore } from '../../../features/workspaces';
9import UIStore from '../../../stores/UIStore'; 9import UIStore from '../../../stores/UIStore';
10import UserStore from '../../../stores/UserStore';
10 11
11const messages = defineMessages({ 12const messages = defineMessages({
12 availableServices: { 13 availableServices: {
@@ -25,6 +26,10 @@ const messages = defineMessages({
25 id: 'settings.navigation.account', 26 id: 'settings.navigation.account',
26 defaultMessage: '!!!Account', 27 defaultMessage: '!!!Account',
27 }, 28 },
29 team: {
30 id: 'settings.navigation.team',
31 defaultMessage: '!!!Manage Team',
32 },
28 settings: { 33 settings: {
29 id: 'settings.navigation.settings', 34 id: 'settings.navigation.settings',
30 defaultMessage: '!!!Settings', 35 defaultMessage: '!!!Settings',
@@ -43,6 +48,7 @@ export default @inject('stores') @observer class SettingsNavigation extends Comp
43 static propTypes = { 48 static propTypes = {
44 stores: PropTypes.shape({ 49 stores: PropTypes.shape({
45 ui: PropTypes.instanceOf(UIStore).isRequired, 50 ui: PropTypes.instanceOf(UIStore).isRequired,
51 user: PropTypes.instanceOf(UserStore).isRequired,
46 }).isRequired, 52 }).isRequired,
47 serviceCount: PropTypes.number.isRequired, 53 serviceCount: PropTypes.number.isRequired,
48 workspaceCount: PropTypes.number.isRequired, 54 workspaceCount: PropTypes.number.isRequired,
@@ -55,6 +61,7 @@ export default @inject('stores') @observer class SettingsNavigation extends Comp
55 render() { 61 render() {
56 const { serviceCount, workspaceCount, stores } = this.props; 62 const { serviceCount, workspaceCount, stores } = this.props;
57 const { isDarkThemeActive } = stores.ui; 63 const { isDarkThemeActive } = stores.ui;
64 const { router, user } = stores;
58 const { intl } = this.context; 65 const { intl } = this.context;
59 66
60 return ( 67 return (
@@ -98,6 +105,16 @@ export default @inject('stores') @observer class SettingsNavigation extends Comp
98 {intl.formatMessage(messages.account)} 105 {intl.formatMessage(messages.account)}
99 </Link> 106 </Link>
100 <Link 107 <Link
108 to="/settings/team"
109 className="settings-navigation__link"
110 activeClassName="is-active"
111 >
112 {intl.formatMessage(messages.team)}
113 {!user.data.isPremium && (
114 <ProBadge inverted={!isDarkThemeActive && router.location.pathname === '/settings/team'} />
115 )}
116 </Link>
117 <Link
101 to="/settings/app" 118 to="/settings/app"
102 className="settings-navigation__link" 119 className="settings-navigation__link"
103 activeClassName="is-active" 120 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..82c517fcb
--- /dev/null
+++ b/src/components/settings/team/TeamDashboard.js
@@ -0,0 +1,152 @@
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import { observer } 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 on meetfranz.com',
33 },
34 upgradeButton: {
35 id: 'settings.team.upgradeAction',
36 defaultMessage: '!!!Upgrade your Account',
37 },
38});
39
40const styles = {
41 cta: {
42 margin: [40, 'auto'],
43 },
44 container: {
45 display: 'flex',
46 flexDirection: 'column',
47 height: 'auto',
48
49 '@media(min-width: 800px)': {
50 flexDirection: 'row',
51 },
52 },
53 content: {
54 height: 'auto',
55 order: 1,
56
57 '@media(min-width: 800px)': {
58 order: 0,
59 },
60 },
61 image: {
62 display: 'block',
63 height: 150,
64 order: 0,
65 margin: [0, 'auto', 40, 'auto'],
66
67 '@media(min-width: 800px)': {
68 marginLeft: 40,
69 order: 1,
70 },
71 },
72};
73
74
75export default @injectSheet(styles) @observer class TeamDashboard extends Component {
76 static propTypes = {
77 isLoading: PropTypes.bool.isRequired,
78 userInfoRequestFailed: PropTypes.bool.isRequired,
79 retryUserInfoRequest: PropTypes.func.isRequired,
80 openTeamManagement: PropTypes.func.isRequired,
81 classes: PropTypes.object.isRequired,
82 };
83
84 static contextTypes = {
85 intl: intlShape,
86 };
87
88 render() {
89 const {
90 isLoading,
91 userInfoRequestFailed,
92 retryUserInfoRequest,
93 openTeamManagement,
94 classes,
95 } = this.props;
96 const { intl } = this.context;
97
98 return (
99 <div className="settings__main">
100 <div className="settings__header">
101 <span className="settings__header-item">
102 {intl.formatMessage(messages.headline)}
103 </span>
104 </div>
105 <div className="settings__body">
106 {isLoading && (
107 <Loader />
108 )}
109
110 {!isLoading && userInfoRequestFailed && (
111 <Infobox
112 icon="alert"
113 type="danger"
114 ctaLabel={intl.formatMessage(messages.tryReloadUserInfoRequest)}
115 ctaLoading={isLoading}
116 ctaOnClick={retryUserInfoRequest}
117 >
118 {intl.formatMessage(messages.userInfoRequestFailed)}
119 </Infobox>
120 )}
121
122 {!userInfoRequestFailed && (
123 <>
124 {!isLoading && (
125 <>
126 <PremiumFeatureContainer>
127 <>
128 <h1>{intl.formatMessage(messages.contentHeadline)}</h1>
129 <div className={classes.container}>
130 <div className={classes.content}>
131 <p>{intl.formatMessage(messages.intro)}</p>
132 <p>{intl.formatMessage(messages.copy)}</p>
133 </div>
134 <img className={classes.image} src="https://cdn.franzinfra.com/announcements/assets/teams.png" alt="Franz for Teams" />
135 </div>
136 <Button
137 label={intl.formatMessage(messages.manageButton)}
138 onClick={openTeamManagement}
139 className={classes.cta}
140 />
141 </>
142 </PremiumFeatureContainer>
143 </>
144 )}
145 </>
146 )}
147 </div>
148 <ReactTooltip place="right" type="dark" effect="solid" />
149 </div>
150 );
151 }
152}
diff --git a/src/config.js b/src/config.js
index 3b5ce7dda..5bc318545 100644
--- a/src/config.js
+++ b/src/config.js
@@ -9,10 +9,21 @@ const app = process.type === 'renderer' ? electron.remote.app : electron.app;
9const systemPreferences = process.type === 'renderer' ? electron.remote.systemPreferences : electron.systemPreferences; 9const systemPreferences = process.type === 'renderer' ? electron.remote.systemPreferences : electron.systemPreferences;
10 10
11export const CHECK_INTERVAL = ms('1h'); // How often should we perform checks 11export const CHECK_INTERVAL = ms('1h'); // How often should we perform checks
12
12export const LOCAL_API = 'http://localhost:3000'; 13export const LOCAL_API = 'http://localhost:3000';
13export const DEV_API = 'https://dev.franzinfra.com'; 14export const DEV_API = 'https://dev.franzinfra.com';
14export const LIVE_API = 'https://api.franzinfra.com'; 15export const LIVE_API = 'https://api.franzinfra.com';
16
17export const LOCAL_WS_API = 'ws://localhost:3000';
18export const DEV_WS_API = 'wss://dev.franzinfra.com';
19export const LIVE_WS_API = 'wss://api.franzinfra.com';
20
21export const LOCAL_API_WEBSITE = 'http://localhost:3333';
22export const DEV_API_WEBSITE = 'https://meetfranz.com';
23export const LIVE_API_WEBSITE = 'https://meetfranz.com';
24
15export const STATS_API = 'https://stats.franzinfra.com'; 25export const STATS_API = 'https://stats.franzinfra.com';
26
16export const GA_ID = !isDevMode ? 'UA-74126766-10' : 'UA-74126766-12'; 27export const GA_ID = !isDevMode ? 'UA-74126766-10' : 'UA-74126766-12';
17 28
18export const DEFAULT_APP_SETTINGS = { 29export 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..c69d5ad08
--- /dev/null
+++ b/src/containers/settings/TeamScreen.js
@@ -0,0 +1,57 @@
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 isLoading={isLoadingUserInfo}
31 userInfoRequestFailed={user.getUserInfoRequest.wasExecuted && user.getUserInfoRequest.isError}
32 retryUserInfoRequest={() => this.reloadData()}
33 openTeamManagement={() => this.handleWebsiteLink('/user/team')}
34 />
35 </ErrorBoundary>
36 );
37 }
38}
39
40TeamScreen.wrappedComponent.propTypes = {
41 stores: PropTypes.shape({
42 user: PropTypes.instanceOf(UserStore).isRequired,
43 app: PropTypes.instanceOf(AppStore).isRequired,
44 }).isRequired,
45 actions: PropTypes.shape({
46 payment: PropTypes.shape({
47 createDashboardUrl: PropTypes.func.isRequired,
48 }).isRequired,
49 app: PropTypes.shape({
50 openExternalUrl: PropTypes.func.isRequired,
51 }).isRequired,
52 user: PropTypes.shape({
53 update: PropTypes.func.isRequired,
54 delete: PropTypes.func.isRequired,
55 }).isRequired,
56 }).isRequired,
57};
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..4049ae814
--- /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 initSettingsWebSocket(stores, actions) {
10 const { features } = stores;
11
12 // Toggle SettingsWebSocket 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..0f1feebb9
--- /dev/null
+++ b/src/features/settingsWS/store.js
@@ -0,0 +1,120 @@
1import { observable } 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 this.registerReactions([
24 this._initialize.bind(this),
25 this._reconnect.bind(this),
26 this._close.bind(this),
27 ]);
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 // Reactions
91
92 _initialize() {
93 if (this.stores.user.data.id) {
94 this.connect();
95 }
96 }
97
98 _reconnect() {
99 if (!this.connected) {
100 debug('Trying to reconnect in 30s');
101 this.reconnectTimeout = setInterval(() => {
102 debug('Trying to reconnect');
103 this.connect();
104 }, ms('30s'));
105 } else {
106 debug('Clearing reconnect interval');
107 clearInterval(this.reconnectTimeout);
108 }
109 }
110
111 _close() {
112 if (!this.stores.user.isLoggedIn && this.ws) {
113 debug('Terminating connection');
114 this.ws.terminate();
115 this.ws = null;
116 }
117 }
118}
119
120export default SettingsWSStore;
diff --git a/src/i18n/locales/defaultMessages.json b/src/i18n/locales/defaultMessages.json
index 910dac727..9c9dee340 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,
@@ -2257,6 +2270,89 @@
2257 { 2270 {
2258 "descriptors": [ 2271 "descriptors": [
2259 { 2272 {
2273 "defaultMessage": "!!!Team",
2274 "end": {
2275 "column": 3,
2276 "line": 17
2277 },
2278 "file": "src/components/settings/team/TeamDashboard.js",
2279 "id": "settings.team.headline",
2280 "start": {
2281 "column": 12,
2282 "line": 14
2283 }
2284 },
2285 {
2286 "defaultMessage": "!!!Franz for Teams",
2287 "end": {
2288 "column": 3,
2289 "line": 21
2290 },
2291 "file": "src/components/settings/team/TeamDashboard.js",
2292 "id": "settings.team.contentHeadline",
2293 "start": {
2294 "column": 19,
2295 "line": 18
2296 }
2297 },
2298 {
2299 "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.",
2300 "end": {
2301 "column": 3,
2302 "line": 25
2303 },
2304 "file": "src/components/settings/team/TeamDashboard.js",
2305 "id": "settings.team.intro",
2306 "start": {
2307 "column": 9,
2308 "line": 22
2309 }
2310 },
2311 {
2312 "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!",
2313 "end": {
2314 "column": 3,
2315 "line": 29
2316 },
2317 "file": "src/components/settings/team/TeamDashboard.js",
2318 "id": "settings.team.copy",
2319 "start": {
2320 "column": 8,
2321 "line": 26
2322 }
2323 },
2324 {
2325 "defaultMessage": "!!!Manage your Team",
2326 "end": {
2327 "column": 3,
2328 "line": 33
2329 },
2330 "file": "src/components/settings/team/TeamDashboard.js",
2331 "id": "settings.team.manageAction",
2332 "start": {
2333 "column": 16,
2334 "line": 30
2335 }
2336 },
2337 {
2338 "defaultMessage": "!!!Upgrade your Account",
2339 "end": {
2340 "column": 3,
2341 "line": 37
2342 },
2343 "file": "src/components/settings/team/TeamDashboard.js",
2344 "id": "settings.team.upgradeAction",
2345 "start": {
2346 "column": 17,
2347 "line": 34
2348 }
2349 }
2350 ],
2351 "path": "src/components/settings/team/TeamDashboard.json"
2352 },
2353 {
2354 "descriptors": [
2355 {
2260 "defaultMessage": "!!!Account", 2356 "defaultMessage": "!!!Account",
2261 "end": { 2357 "end": {
2262 "column": 3, 2358 "column": 3,
@@ -4504,4 +4600,4 @@
4504 ], 4600 ],
4505 "path": "src/lib/Menu.json" 4601 "path": "src/lib/Menu.json"
4506 } 4602 }
4507] \ No newline at end of file 4603]
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 1090584c2..1d8fb8d18 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -177,6 +177,7 @@
177 "settings.navigation.inviteFriends": "Invite Friends", 177 "settings.navigation.inviteFriends": "Invite Friends",
178 "settings.navigation.logout": "Logout", 178 "settings.navigation.logout": "Logout",
179 "settings.navigation.settings": "Settings", 179 "settings.navigation.settings": "Settings",
180 "settings.navigation.team": "Manage Team",
180 "settings.navigation.yourServices": "Your services", 181 "settings.navigation.yourServices": "Your services",
181 "settings.navigation.yourWorkspaces": "Your workspaces", 182 "settings.navigation.yourWorkspaces": "Your workspaces",
182 "settings.recipes.all": "All services", 183 "settings.recipes.all": "All services",
@@ -236,6 +237,12 @@
236 "settings.services.tooltip.isMuted": "All sounds are muted", 237 "settings.services.tooltip.isMuted": "All sounds are muted",
237 "settings.services.tooltip.notificationsDisabled": "Notifications are disabled", 238 "settings.services.tooltip.notificationsDisabled": "Notifications are disabled",
238 "settings.services.updatedInfo": "Your changes have been saved", 239 "settings.services.updatedInfo": "Your changes have been saved",
240 "settings.team.contentHeadline": "Franz for Teams",
241 "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!",
242 "settings.team.headline": "Team",
243 "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.",
244 "settings.team.manageAction": "Manage your Team on meetfranz.com",
245 "settings.team.upgradeAction": "Upgrade your Account",
239 "settings.user.form.accountType.company": "Company", 246 "settings.user.form.accountType.company": "Company",
240 "settings.user.form.accountType.individual": "Individual", 247 "settings.user.form.accountType.individual": "Individual",
241 "settings.user.form.accountType.label": "Account type", 248 "settings.user.form.accountType.label": "Account type",
diff --git a/src/i18n/messages/src/components/settings/account/AccountDashboard.json b/src/i18n/messages/src/components/settings/account/AccountDashboard.json
index 603950395..4969db910 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": 14,
8 "column": 12 8 "column": 12
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 18, 11 "line": 17,
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": 18,
21 "column": 24 21 "column": 24
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 22, 24 "line": 21,
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": 22,
34 "column": 19 34 "column": 19
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 26, 37 "line": 25,
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": 26,
60 "column": 22 47 "column": 22
61 }, 48 },
62 "end": { 49 "end": {
63 "line": 34, 50 "line": 29,
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": 30,
73 "column": 33 60 "column": 33
74 }, 61 },
75 "end": { 62 "end": {
76 "line": 38, 63 "line": 33,
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": 34,
86 "column": 20 73 "column": 20
87 }, 74 },
88 "end": { 75 "end": {
89 "line": 42, 76 "line": 37,
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": 38,
99 "column": 22 86 "column": 22
100 }, 87 },
101 "end": { 88 "end": {
102 "line": 46, 89 "line": 41,
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": 42,
112 "column": 21 99 "column": 21
113 }, 100 },
114 "end": { 101 "end": {
115 "line": 50, 102 "line": 45,
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": 46,
112 "column": 18
113 },
114 "end": {
115 "line": 49,
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": 50,
125 "column": 19 125 "column": 19
126 }, 126 },
127 "end": { 127 "end": {
128 "line": 54, 128 "line": 53,
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": 54,
138 "column": 25 138 "column": 25
139 }, 139 },
140 "end": { 140 "end": {
141 "line": 58, 141 "line": 57,
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": 58,
151 "column": 28 151 "column": 28
152 }, 152 },
153 "end": { 153 "end": {
154 "line": 62, 154 "line": 61,
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": 62,
164 "column": 17 164 "column": 17
165 }, 165 },
166 "end": { 166 "end": {
167 "line": 66, 167 "line": 65,
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": 66,
177 "column": 14 177 "column": 14
178 }, 178 },
179 "end": { 179 "end": {
180 "line": 70, 180 "line": 69,
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": 70,
190 "column": 19 190 "column": 19
191 }, 191 },
192 "end": { 192 "end": {
193 "line": 74, 193 "line": 73,
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..6bccc25ff
--- /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 on meetfranz.com",
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 dcda021c1..e7832088b 100644
--- a/src/stores/FeaturesStore.js
+++ b/src/stores/FeaturesStore.js
@@ -15,6 +15,7 @@ import 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 announcements from '../features/announcements'; 17import announcements from '../features/announcements';
18import settingsWS from '../features/settingsWS';
18 19
19import { DEFAULT_FEATURES_CONFIG } from '../config'; 20import { DEFAULT_FEATURES_CONFIG } from '../config';
20 21
@@ -73,5 +74,6 @@ export default class FeaturesStore extends Store {
73 workspaces(this.stores, this.actions); 74 workspaces(this.stores, this.actions);
74 shareFranz(this.stores, this.actions); 75 shareFranz(this.stores, this.actions);
75 announcements(this.stores, this.actions); 76 announcements(this.stores, this.actions);
77 settingsWS(this.stores, this.actions);
76 } 78 }
77} 79}
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..1baff8b54 100644
--- a/src/styles/settings.scss
+++ b/src/styles/settings.scss
@@ -61,11 +61,6 @@
61 61
62 .account { 62 .account {
63 .account__box { background: $dark-theme-gray-darker; } 63 .account__box { background: $dark-theme-gray-darker; }
64
65 .invoices {
66 td { border-bottom: 1px solid $dark-theme-gray-darker; }
67 .invoices__action button { color: $theme-brand-primary; }
68 }
69 } 64 }
70 65
71 .premium-info { 66 .premium-info {
@@ -329,15 +324,6 @@
329 .account__avatar { 324 .account__avatar {
330 margin-right: 20px; 325 margin-right: 20px;
331 position: relative; 326 position: relative;
332
333 .emoji img { width: 30px; }
334 }
335
336 .account__avatar-premium {
337 font-size: 26px;
338 position: absolute;
339 right: 2px;
340 top: 2px;
341 } 327 }
342 328
343 .account__info { 329 .account__info {
@@ -345,6 +331,7 @@
345 331
346 h2 { margin-bottom: 5px; } 332 h2 { margin-bottom: 5px; }
347 .badge { margin-top: 5px; } 333 .badge { margin-top: 5px; }
334 .username { margin-right: 10 }
348 } 335 }
349 336
350 .account__subscription { 337 .account__subscription {
@@ -354,6 +341,22 @@
354 .badge { margin-left: 10px; } 341 .badge { margin-left: 10px; }
355 } 342 }
356 343
344 .badge--premium {
345 margin-left: 1px;
346 position: relative;
347 top: -3px;
348 padding-top: 4px;
349 color: #FFF;
350 border-radius: 3px;
351 }
352
353
354 .manage-user-links {
355 margin-top: 20px;
356 display: flex;
357 justify-content: space-between;
358 }
359
357 .account__subscription-button { margin-left: auto; } 360 .account__subscription-button { margin-left: auto; }
358 .franz-form__button { white-space: nowrap; } 361 .franz-form__button { white-space: nowrap; }
359 div { height: auto; } 362 div { height: auto; }