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.js316
-rw-r--r--src/components/settings/navigation/SettingsNavigation.js4
-rw-r--r--src/components/settings/recipes/RecipesDashboard.js6
-rw-r--r--src/components/settings/settings/EditSettingsForm.js4
4 files changed, 191 insertions, 139 deletions
diff --git a/src/components/settings/account/AccountDashboard.js b/src/components/settings/account/AccountDashboard.js
index 83dc34a52..7d6bad883 100644
--- a/src/components/settings/account/AccountDashboard.js
+++ b/src/components/settings/account/AccountDashboard.js
@@ -3,9 +3,7 @@ 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 { 6import { ProBadge, H1, H2 } from '@meetfranz/ui';
7 ProBadge, H1, H2,
8} from '@meetfranz/ui';
9import moment from 'moment'; 7import moment from 'moment';
10 8
11import Loader from '../../ui/Loader'; 9import Loader from '../../ui/Loader';
@@ -13,6 +11,7 @@ import Button from '../../ui/Button';
13import Infobox from '../../ui/Infobox'; 11import Infobox from '../../ui/Infobox';
14import SubscriptionForm from '../../../containers/subscription/SubscriptionFormScreen'; 12import SubscriptionForm from '../../../containers/subscription/SubscriptionFormScreen';
15import { i18nPlanName } from '../../../helpers/plan-helpers'; 13import { i18nPlanName } from '../../../helpers/plan-helpers';
14import { LOCAL_SERVER } from '../../../config';
16 15
17const messages = defineMessages({ 16const messages = defineMessages({
18 headline: { 17 headline: {
@@ -69,11 +68,13 @@ const messages = defineMessages({
69 }, 68 },
70 deleteInfo: { 69 deleteInfo: {
71 id: 'settings.account.deleteInfo', 70 id: 'settings.account.deleteInfo',
72 defaultMessage: '!!!If you don\'t need your Ferdi account any longer, you can delete your account and all related data here.', 71 defaultMessage:
72 "!!!If you don't need your Ferdi account any longer, you can delete your account and all related data here.",
73 }, 73 },
74 deleteEmailSent: { 74 deleteEmailSent: {
75 id: 'settings.account.deleteEmailSent', 75 id: 'settings.account.deleteEmailSent',
76 defaultMessage: '!!!You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!', 76 defaultMessage:
77 '!!!You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!',
77 }, 78 },
78 trial: { 79 trial: {
79 id: 'settings.account.trial', 80 id: 'settings.account.trial',
@@ -89,7 +90,16 @@ const messages = defineMessages({
89 }, 90 },
90 trialUpdateBillingInformation: { 91 trialUpdateBillingInformation: {
91 id: 'settings.account.trialUpdateBillingInfo', 92 id: 'settings.account.trialUpdateBillingInfo',
92 defaultMessage: '!!!Please update your billing info to continue using {license} after your trial period.', 93 defaultMessage:
94 '!!!Please update your billing info to continue using {license} after your trial period.',
95 },
96 accountUnavailable: {
97 id: 'settings.account.accountUnavailable',
98 defaultMessage: 'Account is unavailable',
99 },
100 accountUnavailableInfo: {
101 id: 'settings.account.accountUnavailableInfo',
102 defaultMessage: 'You are using Ferdi without an account. If you want to use Ferdi with an account and keep your services synchronized across installations, please select a server in the Settings tab then login.',
93 }, 103 },
94}); 104});
95 105
@@ -110,6 +120,7 @@ class AccountDashboard extends Component {
110 upgradeToPro: PropTypes.func.isRequired, 120 upgradeToPro: PropTypes.func.isRequired,
111 openInvoices: PropTypes.func.isRequired, 121 openInvoices: PropTypes.func.isRequired,
112 onCloseSubscriptionWindow: PropTypes.func.isRequired, 122 onCloseSubscriptionWindow: PropTypes.func.isRequired,
123 server: PropTypes.string.isRequired,
113 }; 124 };
114 125
115 static contextTypes = { 126 static contextTypes = {
@@ -132,6 +143,7 @@ class AccountDashboard extends Component {
132 upgradeToPro, 143 upgradeToPro,
133 openInvoices, 144 openInvoices,
134 onCloseSubscriptionWindow, 145 onCloseSubscriptionWindow,
146 server,
135 } = this.props; 147 } = this.props;
136 const { intl } = this.context; 148 const { intl } = this.context;
137 149
@@ -141,6 +153,8 @@ class AccountDashboard extends Component {
141 planName = i18nPlanName(user.team.plan, intl); 153 planName = i18nPlanName(user.team.plan, intl);
142 } 154 }
143 155
156 const isUsingWithoutAccount = server === LOCAL_SERVER;
157
144 return ( 158 return (
145 <div className="settings__main"> 159 <div className="settings__main">
146 <div className="settings__header"> 160 <div className="settings__header">
@@ -149,154 +163,186 @@ class AccountDashboard extends Component {
149 </span> 163 </span>
150 </div> 164 </div>
151 <div className="settings__body"> 165 <div className="settings__body">
152 {isLoading && ( 166 {isUsingWithoutAccount && (
153 <Loader /> 167 <>
168 <h1 style={{ marginBottom: 0 }}>
169 {intl.formatMessage(messages.accountUnavailable)}
170 </h1>
171 <p
172 className="settings__message"
173 style={{
174 borderTop: 0,
175 marginTop: 0,
176 }}
177 >
178 {intl.formatMessage(messages.accountUnavailableInfo)}
179 </p>
180 </>
154 )} 181 )}
182 {!isUsingWithoutAccount && (
183 <>
184 {isLoading && <Loader />}
155 185
156 {!isLoading && userInfoRequestFailed && ( 186 {!isLoading && userInfoRequestFailed && (
157 <Infobox 187 <Infobox
158 icon="alert" 188 icon="alert"
159 type="danger" 189 type="danger"
160 ctaLabel={intl.formatMessage(messages.tryReloadUserInfoRequest)} 190 ctaLabel={intl.formatMessage(
161 ctaLoading={isLoading} 191 messages.tryReloadUserInfoRequest,
162 ctaOnClick={retryUserInfoRequest} 192 )}
163 > 193 ctaLoading={isLoading}
164 {intl.formatMessage(messages.userInfoRequestFailed)} 194 ctaOnClick={retryUserInfoRequest}
165 </Infobox> 195 >
166 )} 196 {intl.formatMessage(messages.userInfoRequestFailed)}
197 </Infobox>
198 )}
167 199
168 {!userInfoRequestFailed && ( 200 {!userInfoRequestFailed && (
169 <>
170 {!isLoading && (
171 <> 201 <>
172 <div className="account"> 202 {!isLoading && (
173 <div className="account__box account__box--flex"> 203 <>
174 <div className="account__avatar"> 204 <div className="account">
175 <img 205 <div className="account__box account__box--flex">
176 src="./assets/images/logo.svg" 206 <div className="account__avatar">
177 alt="" 207 <img src="./assets/images/logo.svg" alt="" />
178 /> 208 </div>
179 </div> 209 <div className="account__info">
180 <div className="account__info"> 210 <H1>
181 <H1> 211 <span className="username">{`${user.firstname} ${user.lastname}`}</span>
182 <span className="username">{`${user.firstname} ${user.lastname}`}</span> 212 {user.isPremium && (
183 {user.isPremium && ( 213 <>
184 <> 214 {' '}
185 {' '} 215 <ProBadge />
186 <ProBadge /> 216 </>
187 </> 217 )}
188 )} 218 </H1>
189 </H1> 219 <p>
190 <p> 220 {user.organization && `${user.organization}, `}
191 {user.organization && `${user.organization}, `} 221 {user.email}
192 {user.email} 222 </p>
193 </p> 223 {user.isPremium && (
194 {user.isPremium && ( 224 <div className="manage-user-links">
195 <div className="manage-user-links"> 225 <Button
226 label={intl.formatMessage(
227 messages.accountEditButton,
228 )}
229 className="franz-form__button--inverted"
230 onClick={openEditAccount}
231 />
232 </div>
233 )}
234 </div>
235 {!user.isPremium && (
196 <Button 236 <Button
197 label={intl.formatMessage(messages.accountEditButton)} 237 label={intl.formatMessage(
238 messages.accountEditButton,
239 )}
198 className="franz-form__button--inverted" 240 className="franz-form__button--inverted"
199 onClick={openEditAccount} 241 onClick={openEditAccount}
200 /> 242 />
201 </div>
202 )}
203 </div>
204 {!user.isPremium && (
205 <Button
206 label={intl.formatMessage(messages.accountEditButton)}
207 className="franz-form__button--inverted"
208 onClick={openEditAccount}
209 />
210 )}
211 </div>
212 </div>
213 {user.isPremium && user.isSubscriptionOwner && (
214 <div className="account">
215 <div className="account__box">
216 <H2>
217 {intl.formatMessage(messages.yourLicense)}
218 </H2>
219 <p>
220 Franz
221 {' '}
222 {isPremiumOverrideUser ? 'Premium' : planName}
223 {user.team.isTrial && (
224 <>
225 {' – '}
226 {intl.formatMessage(messages.trial)}
227 </>
228 )} 243 )}
229 </p> 244 </div>
230 {user.team.isTrial && ( 245 </div>
231 <> 246 {user.isPremium && user.isSubscriptionOwner && (
232 <br /> 247 <div className="account">
233 <p> 248 <div className="account__box">
234 {intl.formatMessage(messages.trialEndsIn, { 249 <H2>{intl.formatMessage(messages.yourLicense)}</H2>
235 duration: moment.duration(moment().diff(user.team.trialEnd)).humanize(),
236 })}
237 </p>
238 <p> 250 <p>
239 {intl.formatMessage(messages.trialUpdateBillingInformation, { 251 Franz
240 license: planName, 252 {' '}
241 })} 253 {isPremiumOverrideUser ? 'Premium' : planName}
254 {user.team.isTrial && (
255 <>
256 {' – '}
257 {intl.formatMessage(messages.trial)}
258 </>
259 )}
242 </p> 260 </p>
243 </> 261 {user.team.isTrial && (
244 )} 262 <>
245 {!isProUser && ( 263 <br />
246 <div className="manage-user-links"> 264 <p>
247 <Button 265 {intl.formatMessage(messages.trialEndsIn, {
248 label={intl.formatMessage(messages.upgradeAccountToPro)} 266 duration: moment
249 className="franz-form__button--primary" 267 .duration(
250 onClick={upgradeToPro} 268 moment().diff(user.team.trialEnd),
269 )
270 .humanize(),
271 })}
272 </p>
273 <p>
274 {intl.formatMessage(
275 messages.trialUpdateBillingInformation,
276 {
277 license: planName,
278 },
279 )}
280 </p>
281 </>
282 )}
283 {!isProUser && (
284 <div className="manage-user-links">
285 <Button
286 label={intl.formatMessage(
287 messages.upgradeAccountToPro,
288 )}
289 className="franz-form__button--primary"
290 onClick={upgradeToPro}
291 />
292 </div>
293 )}
294 <div className="manage-user-links">
295 <Button
296 label={intl.formatMessage(
297 messages.manageSubscriptionButtonLabel,
298 )}
299 className="franz-form__button--inverted"
300 onClick={openBilling}
301 />
302 <Button
303 label={intl.formatMessage(
304 messages.invoicesButton,
305 )}
306 className="franz-form__button--inverted"
307 onClick={openInvoices}
308 />
309 </div>
310 </div>
311 </div>
312 )}
313 {!user.isPremium && (
314 <div className="account franz-form">
315 <div className="account__box">
316 <SubscriptionForm
317 onCloseWindow={onCloseSubscriptionWindow}
251 /> 318 />
252 </div> 319 </div>
253 )} 320 </div>
254 <div className="manage-user-links"> 321 )}
255 <Button 322 </>
256 label={intl.formatMessage(messages.manageSubscriptionButtonLabel)} 323 )}
257 className="franz-form__button--inverted" 324
258 onClick={openBilling} 325 <div className="account franz-form">
259 /> 326 <div className="account__box">
327 <H2>{intl.formatMessage(messages.headlineDangerZone)}</H2>
328 {!isDeleteAccountSuccessful && (
329 <div className="account__subscription">
330 <p>{intl.formatMessage(messages.deleteInfo)}</p>
260 <Button 331 <Button
261 label={intl.formatMessage(messages.invoicesButton)} 332 label={intl.formatMessage(messages.deleteAccount)}
262 className="franz-form__button--inverted" 333 buttonType="danger"
263 onClick={openInvoices} 334 onClick={() => deleteAccount()}
335 loaded={!isLoadingDeleteAccount}
264 /> 336 />
265 </div> 337 </div>
266 </div> 338 )}
267 </div> 339 {isDeleteAccountSuccessful && (
268 )} 340 <p>{intl.formatMessage(messages.deleteEmailSent)}</p>
269 {!user.isPremium && ( 341 )}
270 <div className="account franz-form">
271 <div className="account__box">
272 <SubscriptionForm
273 onCloseWindow={onCloseSubscriptionWindow}
274 />
275 </div>
276 </div> 342 </div>
277 )} 343 </div>
278 </> 344 </>
279 )} 345 )}
280
281 <div className="account franz-form">
282 <div className="account__box">
283 <H2>{intl.formatMessage(messages.headlineDangerZone)}</H2>
284 {!isDeleteAccountSuccessful && (
285 <div className="account__subscription">
286 <p>{intl.formatMessage(messages.deleteInfo)}</p>
287 <Button
288 label={intl.formatMessage(messages.deleteAccount)}
289 buttonType="danger"
290 onClick={() => deleteAccount()}
291 loaded={!isLoadingDeleteAccount}
292 />
293 </div>
294 )}
295 {isDeleteAccountSuccessful && (
296 <p>{intl.formatMessage(messages.deleteEmailSent)}</p>
297 )}
298 </div>
299 </div>
300 </> 346 </>
301 )} 347 )}
302 </div> 348 </div>
diff --git a/src/components/settings/navigation/SettingsNavigation.js b/src/components/settings/navigation/SettingsNavigation.js
index 192cfde7a..eb3249fa0 100644
--- a/src/components/settings/navigation/SettingsNavigation.js
+++ b/src/components/settings/navigation/SettingsNavigation.js
@@ -119,6 +119,7 @@ export default @inject('stores', 'actions') @observer class SettingsNavigation e
119 to="/settings/services" 119 to="/settings/services"
120 className="settings-navigation__link" 120 className="settings-navigation__link"
121 activeClassName="is-active" 121 activeClassName="is-active"
122 disabled={!isLoggedIn}
122 > 123 >
123 {intl.formatMessage(messages.yourServices)} 124 {intl.formatMessage(messages.yourServices)}
124 {' '} 125 {' '}
@@ -134,6 +135,7 @@ export default @inject('stores', 'actions') @observer class SettingsNavigation e
134 to="/settings/workspaces" 135 to="/settings/workspaces"
135 className="settings-navigation__link" 136 className="settings-navigation__link"
136 activeClassName="is-active" 137 activeClassName="is-active"
138 disabled={!isLoggedIn}
137 > 139 >
138 {intl.formatMessage(messages.yourWorkspaces)} 140 {intl.formatMessage(messages.yourWorkspaces)}
139 {' '} 141 {' '}
@@ -148,6 +150,7 @@ export default @inject('stores', 'actions') @observer class SettingsNavigation e
148 to="/settings/user" 150 to="/settings/user"
149 className="settings-navigation__link" 151 className="settings-navigation__link"
150 activeClassName="is-active" 152 activeClassName="is-active"
153 disabled={!isLoggedIn}
151 > 154 >
152 {intl.formatMessage(messages.account)} 155 {intl.formatMessage(messages.account)}
153 </Link> 156 </Link>
@@ -155,6 +158,7 @@ export default @inject('stores', 'actions') @observer class SettingsNavigation e
155 to="/settings/team" 158 to="/settings/team"
156 className="settings-navigation__link" 159 className="settings-navigation__link"
157 activeClassName="is-active" 160 activeClassName="is-active"
161 disabled={!isLoggedIn}
158 > 162 >
159 {intl.formatMessage(messages.team)} 163 {intl.formatMessage(messages.team)}
160 {!user.data.isPremium && ( 164 {!user.data.isPremium && (
diff --git a/src/components/settings/recipes/RecipesDashboard.js b/src/components/settings/recipes/RecipesDashboard.js
index 877cbc588..d08e6cbc2 100644
--- a/src/components/settings/recipes/RecipesDashboard.js
+++ b/src/components/settings/recipes/RecipesDashboard.js
@@ -153,6 +153,8 @@ export default @injectSheet(styles) @observer class RecipesDashboard extends Com
153 const communityRecipes = recipes.filter(r => !r.isDevRecipe); 153 const communityRecipes = recipes.filter(r => !r.isDevRecipe);
154 const devRecipes = recipes.filter(r => r.isDevRecipe); 154 const devRecipes = recipes.filter(r => r.isDevRecipe);
155 155
156 const isLoggedIn = Boolean(localStorage.getItem('authToken'));
157
156 return ( 158 return (
157 <div className="settings__main"> 159 <div className="settings__main">
158 <div className="settings__header"> 160 <div className="settings__header">
@@ -265,7 +267,7 @@ export default @injectSheet(styles) @observer class RecipesDashboard extends Com
265 <RecipeItem 267 <RecipeItem
266 key={recipe.id} 268 key={recipe.id}
267 recipe={recipe} 269 recipe={recipe}
268 onClick={() => showAddServiceInterface({ recipeId: recipe.id })} 270 onClick={() => isLoggedIn && showAddServiceInterface({ recipeId: recipe.id })}
269 /> 271 />
270 ))} 272 ))}
271 </div> 273 </div>
@@ -278,7 +280,7 @@ export default @injectSheet(styles) @observer class RecipesDashboard extends Com
278 <RecipeItem 280 <RecipeItem
279 key={recipe.id} 281 key={recipe.id}
280 recipe={recipe} 282 recipe={recipe}
281 onClick={() => showAddServiceInterface({ recipeId: recipe.id })} 283 onClick={() => isLoggedIn && showAddServiceInterface({ recipeId: recipe.id })}
282 /> 284 />
283 ))} 285 ))}
284 </div> 286 </div>
diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js
index 3d0213f81..74f5924ea 100644
--- a/src/components/settings/settings/EditSettingsForm.js
+++ b/src/components/settings/settings/EditSettingsForm.js
@@ -50,7 +50,7 @@ const messages = defineMessages({
50 }, 50 },
51 lockedPassword: { 51 lockedPassword: {
52 id: 'settings.app.lockedPassword', 52 id: 'settings.app.lockedPassword',
53 defaultMessage: '!!!Ferdi Lock Password', 53 defaultMessage: '!!!Password',
54 }, 54 },
55 lockedPasswordInfo: { 55 lockedPasswordInfo: {
56 id: 'settings.app.lockedPasswordInfo', 56 id: 'settings.app.lockedPasswordInfo',
@@ -58,7 +58,7 @@ const messages = defineMessages({
58 }, 58 },
59 lockInfo: { 59 lockInfo: {
60 id: 'settings.app.lockInfo', 60 id: 'settings.app.lockInfo',
61 defaultMessage: '!!!Ferdi password lock allows you to keep your messages protected.\nUsing Ferdi password lock, you will be prompted to enter your password everytime you start Ferdi or lock Ferdi yourself using the lock symbol in the bottom left corner or the shortcut CMD/CTRL+Shift+L.', 61 defaultMessage: '!!!Password Lock allows you to keep your messages protected.\nUsing Password Lock, you will be prompted to enter your password everytime you start Ferdi or lock Ferdi yourself using the lock symbol in the bottom left corner or the shortcut CMD/CTRL+Shift+L.',
62 }, 62 },
63 scheduledDNDTimeInfo: { 63 scheduledDNDTimeInfo: {
64 id: 'settings.app.scheduledDNDTimeInfo', 64 id: 'settings.app.scheduledDNDTimeInfo',