aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/settings
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/settings')
-rw-r--r--src/components/settings/account/AccountDashboard.js124
-rw-r--r--src/components/settings/navigation/SettingsNavigation.js17
-rw-r--r--src/components/settings/team/TeamDashboard.js154
3 files changed, 217 insertions, 78 deletions
diff --git a/src/components/settings/account/AccountDashboard.js b/src/components/settings/account/AccountDashboard.js
index 181b95c8c..4a73b51d9 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 {`${user.firstname} ${user.lastname}`}
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..63381c4ed
--- /dev/null
+++ b/src/components/settings/team/TeamDashboard.js
@@ -0,0 +1,154 @@
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 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 user: MobxPropTypes.observableObject.isRequired,
78 isLoading: PropTypes.bool.isRequired,
79 userInfoRequestFailed: PropTypes.bool.isRequired,
80 retryUserInfoRequest: PropTypes.func.isRequired,
81 openTeamManagement: PropTypes.func.isRequired,
82 classes: PropTypes.object.isRequired,
83 };
84
85 static contextTypes = {
86 intl: intlShape,
87 };
88
89 render() {
90 const {
91 user,
92 isLoading,
93 userInfoRequestFailed,
94 retryUserInfoRequest,
95 openTeamManagement,
96 classes,
97 } = this.props;
98 const { intl } = this.context;
99
100 return (
101 <div className="settings__main">
102 <div className="settings__header">
103 <span className="settings__header-item">
104 {intl.formatMessage(messages.headline)}
105 </span>
106 </div>
107 <div className="settings__body">
108 {isLoading && (
109 <Loader />
110 )}
111
112 {!isLoading && userInfoRequestFailed && (
113 <Infobox
114 icon="alert"
115 type="danger"
116 ctaLabel={intl.formatMessage(messages.tryReloadUserInfoRequest)}
117 ctaLoading={isLoading}
118 ctaOnClick={retryUserInfoRequest}
119 >
120 {intl.formatMessage(messages.userInfoRequestFailed)}
121 </Infobox>
122 )}
123
124 {!userInfoRequestFailed && (
125 <>
126 {!isLoading && (
127 <>
128 <PremiumFeatureContainer>
129 <>
130 <h1>{intl.formatMessage(messages.contentHeadline)}</h1>
131 <div className={classes.container}>
132 <div className={classes.content}>
133 <p>{intl.formatMessage(messages.intro)}</p>
134 <p>{intl.formatMessage(messages.copy)}</p>
135 </div>
136 <img className={classes.image} src="https://cdn.franzinfra.com/announcements/assets/teams.png" alt="Franz for Teams" />
137 </div>
138 <Button
139 label={intl.formatMessage(messages.manageButton)}
140 onClick={openTeamManagement}
141 className={classes.cta}
142 />
143 </>
144 </PremiumFeatureContainer>
145 </>
146 )}
147 </>
148 )}
149 </div>
150 <ReactTooltip place="right" type="dark" effect="solid" />
151 </div>
152 );
153 }
154}