diff options
Diffstat (limited to 'src/components/settings')
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'; | |||
3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | 3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; |
4 | import { defineMessages, intlShape } from 'react-intl'; | 4 | import { defineMessages, intlShape } from 'react-intl'; |
5 | import ReactTooltip from 'react-tooltip'; | 5 | import ReactTooltip from 'react-tooltip'; |
6 | import { | 6 | import { ProBadge, H1, H2 } from '@meetfranz/ui'; |
7 | ProBadge, H1, H2, | ||
8 | } from '@meetfranz/ui'; | ||
9 | import moment from 'moment'; | 7 | import moment from 'moment'; |
10 | 8 | ||
11 | import Loader from '../../ui/Loader'; | 9 | import Loader from '../../ui/Loader'; |
@@ -13,6 +11,7 @@ import Button from '../../ui/Button'; | |||
13 | import Infobox from '../../ui/Infobox'; | 11 | import Infobox from '../../ui/Infobox'; |
14 | import SubscriptionForm from '../../../containers/subscription/SubscriptionFormScreen'; | 12 | import SubscriptionForm from '../../../containers/subscription/SubscriptionFormScreen'; |
15 | import { i18nPlanName } from '../../../helpers/plan-helpers'; | 13 | import { i18nPlanName } from '../../../helpers/plan-helpers'; |
14 | import { LOCAL_SERVER } from '../../../config'; | ||
16 | 15 | ||
17 | const messages = defineMessages({ | 16 | const 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', |