diff options
author | vantezzen <properly@protonmail.com> | 2019-09-07 15:50:23 +0200 |
---|---|---|
committer | vantezzen <properly@protonmail.com> | 2019-09-07 15:50:23 +0200 |
commit | e7a74514c1e7c3833dfdcf5900cb87f9e6e8354e (patch) | |
tree | b8314e4155503b135dcb07e8b4a0e847e25c19cf /src/components/settings | |
parent | Update CHANGELOG.md (diff) | |
parent | Update CHANGELOG.md (diff) | |
download | ferdium-app-e7a74514c1e7c3833dfdcf5900cb87f9e6e8354e.tar.gz ferdium-app-e7a74514c1e7c3833dfdcf5900cb87f9e6e8354e.tar.zst ferdium-app-e7a74514c1e7c3833dfdcf5900cb87f9e6e8354e.zip |
Merge branch 'master' of https://github.com/meetfranz/franz into franz-5.3.0
Diffstat (limited to 'src/components/settings')
-rw-r--r-- | src/components/settings/account/AccountDashboard.js | 208 | ||||
-rw-r--r-- | src/components/settings/navigation/SettingsNavigation.js | 8 | ||||
-rw-r--r-- | src/components/settings/recipes/RecipeItem.js | 2 | ||||
-rw-r--r-- | src/components/settings/recipes/RecipesDashboard.js | 194 | ||||
-rw-r--r-- | src/components/settings/services/EditServiceForm.js | 17 | ||||
-rw-r--r-- | src/components/settings/services/ServicesDashboard.js | 2 | ||||
-rw-r--r-- | src/components/settings/settings/EditSettingsForm.js | 11 | ||||
-rw-r--r-- | src/components/settings/team/TeamDashboard.js | 73 |
8 files changed, 378 insertions, 137 deletions
diff --git a/src/components/settings/account/AccountDashboard.js b/src/components/settings/account/AccountDashboard.js index 4b7637637..f588449f4 100644 --- a/src/components/settings/account/AccountDashboard.js +++ b/src/components/settings/account/AccountDashboard.js | |||
@@ -1,14 +1,18 @@ | |||
1 | import React, { Component, Fragment } from 'react'; | 1 | import React, { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | 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 { ProBadge } from '@meetfranz/ui'; | 6 | import { |
7 | ProBadge, H1, H2, | ||
8 | } from '@meetfranz/ui'; | ||
9 | import moment from 'moment'; | ||
7 | 10 | ||
8 | import Loader from '../../ui/Loader'; | 11 | import Loader from '../../ui/Loader'; |
9 | import Button from '../../ui/Button'; | 12 | import Button from '../../ui/Button'; |
10 | import Infobox from '../../ui/Infobox'; | 13 | import Infobox from '../../ui/Infobox'; |
11 | import SubscriptionForm from '../../../containers/subscription/SubscriptionFormScreen'; | 14 | import SubscriptionForm from '../../../containers/subscription/SubscriptionFormScreen'; |
15 | import { i18nPlanName } from '../../../helpers/plan-helpers'; | ||
12 | 16 | ||
13 | const messages = defineMessages({ | 17 | const messages = defineMessages({ |
14 | headline: { | 18 | headline: { |
@@ -19,10 +23,6 @@ const messages = defineMessages({ | |||
19 | id: 'settings.account.headlineSubscription', | 23 | id: 'settings.account.headlineSubscription', |
20 | defaultMessage: '!!!Your Subscription', | 24 | defaultMessage: '!!!Your Subscription', |
21 | }, | 25 | }, |
22 | headlineUpgrade: { | ||
23 | id: 'settings.account.headlineUpgrade', | ||
24 | defaultMessage: '!!!Upgrade your Account', | ||
25 | }, | ||
26 | headlineDangerZone: { | 26 | headlineDangerZone: { |
27 | id: 'settings.account.headlineDangerZone', | 27 | id: 'settings.account.headlineDangerZone', |
28 | defaultMessage: '!!Danger Zone', | 28 | defaultMessage: '!!Danger Zone', |
@@ -31,6 +31,10 @@ const messages = defineMessages({ | |||
31 | id: 'settings.account.manageSubscription.label', | 31 | id: 'settings.account.manageSubscription.label', |
32 | defaultMessage: '!!!Manage your subscription', | 32 | defaultMessage: '!!!Manage your subscription', |
33 | }, | 33 | }, |
34 | upgradeAccountToPro: { | ||
35 | id: 'settings.account.upgradeToPro.label', | ||
36 | defaultMessage: '!!!Upgrade to Franz Professional', | ||
37 | }, | ||
34 | accountTypeBasic: { | 38 | accountTypeBasic: { |
35 | id: 'settings.account.accountType.basic', | 39 | id: 'settings.account.accountType.basic', |
36 | defaultMessage: '!!!Basic Account', | 40 | defaultMessage: '!!!Basic Account', |
@@ -71,21 +75,39 @@ const messages = defineMessages({ | |||
71 | id: 'settings.account.deleteEmailSent', | 75 | id: 'settings.account.deleteEmailSent', |
72 | defaultMessage: '!!!You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!', | 76 | defaultMessage: '!!!You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!', |
73 | }, | 77 | }, |
78 | trial: { | ||
79 | id: 'settings.account.trial', | ||
80 | defaultMessage: '!!!Free Trial', | ||
81 | }, | ||
82 | yourLicense: { | ||
83 | id: 'settings.account.yourLicense', | ||
84 | defaultMessage: '!!!Your Franz License:', | ||
85 | }, | ||
86 | trialEndsIn: { | ||
87 | id: 'settings.account.trialEndsIn', | ||
88 | defaultMessage: '!!!Your free trial ends in {duration}.', | ||
89 | }, | ||
90 | trialUpdateBillingInformation: { | ||
91 | id: 'settings.account.trialUpdateBillingInfo', | ||
92 | defaultMessage: '!!!Please update your billing info to continue using {license} after your trial period.', | ||
93 | }, | ||
74 | }); | 94 | }); |
75 | 95 | ||
76 | export default @observer class AccountDashboard extends Component { | 96 | @observer |
97 | class AccountDashboard extends Component { | ||
77 | static propTypes = { | 98 | static propTypes = { |
78 | user: MobxPropTypes.observableObject.isRequired, | 99 | user: MobxPropTypes.observableObject.isRequired, |
100 | isPremiumOverrideUser: PropTypes.bool.isRequired, | ||
101 | isProUser: PropTypes.bool.isRequired, | ||
79 | isLoading: PropTypes.bool.isRequired, | 102 | isLoading: PropTypes.bool.isRequired, |
80 | isLoadingPlans: PropTypes.bool.isRequired, | ||
81 | userInfoRequestFailed: PropTypes.bool.isRequired, | 103 | userInfoRequestFailed: PropTypes.bool.isRequired, |
82 | retryUserInfoRequest: PropTypes.func.isRequired, | 104 | retryUserInfoRequest: PropTypes.func.isRequired, |
83 | onCloseSubscriptionWindow: PropTypes.func.isRequired, | ||
84 | deleteAccount: PropTypes.func.isRequired, | 105 | deleteAccount: PropTypes.func.isRequired, |
85 | isLoadingDeleteAccount: PropTypes.bool.isRequired, | 106 | isLoadingDeleteAccount: PropTypes.bool.isRequired, |
86 | isDeleteAccountSuccessful: PropTypes.bool.isRequired, | 107 | isDeleteAccountSuccessful: PropTypes.bool.isRequired, |
87 | openEditAccount: PropTypes.func.isRequired, | 108 | openEditAccount: PropTypes.func.isRequired, |
88 | openBilling: PropTypes.func.isRequired, | 109 | openBilling: PropTypes.func.isRequired, |
110 | upgradeToPro: PropTypes.func.isRequired, | ||
89 | openInvoices: PropTypes.func.isRequired, | 111 | openInvoices: PropTypes.func.isRequired, |
90 | }; | 112 | }; |
91 | 113 | ||
@@ -96,20 +118,27 @@ export default @observer class AccountDashboard extends Component { | |||
96 | render() { | 118 | render() { |
97 | const { | 119 | const { |
98 | user, | 120 | user, |
121 | isPremiumOverrideUser, | ||
122 | isProUser, | ||
99 | isLoading, | 123 | isLoading, |
100 | isLoadingPlans, | ||
101 | userInfoRequestFailed, | 124 | userInfoRequestFailed, |
102 | retryUserInfoRequest, | 125 | retryUserInfoRequest, |
103 | onCloseSubscriptionWindow, | ||
104 | deleteAccount, | 126 | deleteAccount, |
105 | isLoadingDeleteAccount, | 127 | isLoadingDeleteAccount, |
106 | isDeleteAccountSuccessful, | 128 | isDeleteAccountSuccessful, |
107 | openEditAccount, | 129 | openEditAccount, |
108 | openBilling, | 130 | openBilling, |
131 | upgradeToPro, | ||
109 | openInvoices, | 132 | openInvoices, |
110 | } = this.props; | 133 | } = this.props; |
111 | const { intl } = this.context; | 134 | const { intl } = this.context; |
112 | 135 | ||
136 | let planName = ''; | ||
137 | |||
138 | if (user.team && user.team.plan) { | ||
139 | planName = i18nPlanName(user.team.plan, intl); | ||
140 | } | ||
141 | |||
113 | return ( | 142 | return ( |
114 | <div className="settings__main"> | 143 | <div className="settings__main"> |
115 | <div className="settings__header"> | 144 | <div className="settings__header"> |
@@ -135,82 +164,115 @@ export default @observer class AccountDashboard extends Component { | |||
135 | )} | 164 | )} |
136 | 165 | ||
137 | {!userInfoRequestFailed && ( | 166 | {!userInfoRequestFailed && ( |
138 | <Fragment> | 167 | <> |
139 | {!isLoading && ( | 168 | {!isLoading && ( |
140 | <div className="account"> | 169 | <> |
141 | <div className="account__box account__box--flex"> | 170 | <div className="account"> |
142 | <div className="account__avatar"> | 171 | <div className="account__box account__box--flex"> |
143 | <img | 172 | <div className="account__avatar"> |
144 | src="./assets/images/logo.svg" | 173 | <img |
145 | alt="" | 174 | src="./assets/images/logo.svg" |
146 | /> | 175 | alt="" |
147 | </div> | 176 | /> |
148 | <div className="account__info"> | 177 | </div> |
149 | <h2> | 178 | <div className="account__info"> |
150 | <span className="username">{`${user.firstname} ${user.lastname}`}</span> | 179 | <H1> |
180 | <span className="username">{`${user.firstname} ${user.lastname}`}</span> | ||
181 | {user.isPremium && ( | ||
182 | <> | ||
183 | {' '} | ||
184 | <ProBadge /> | ||
185 | </> | ||
186 | )} | ||
187 | </H1> | ||
188 | <p> | ||
189 | {user.organization && `${user.organization}, `} | ||
190 | {user.email} | ||
191 | </p> | ||
151 | {user.isPremium && ( | 192 | {user.isPremium && ( |
193 | <div className="manage-user-links"> | ||
194 | <Button | ||
195 | label={intl.formatMessage(messages.accountEditButton)} | ||
196 | className="franz-form__button--inverted" | ||
197 | onClick={openEditAccount} | ||
198 | /> | ||
199 | </div> | ||
200 | )} | ||
201 | </div> | ||
202 | {!user.isPremium && ( | ||
203 | <Button | ||
204 | label={intl.formatMessage(messages.accountEditButton)} | ||
205 | className="franz-form__button--inverted" | ||
206 | onClick={openEditAccount} | ||
207 | /> | ||
208 | )} | ||
209 | </div> | ||
210 | </div> | ||
211 | {user.isPremium && user.isSubscriptionOwner && ( | ||
212 | <div className="account"> | ||
213 | <div className="account__box"> | ||
214 | <H2> | ||
215 | {intl.formatMessage(messages.yourLicense)} | ||
216 | </H2> | ||
217 | <p> | ||
218 | {isPremiumOverrideUser ? 'Franz Premium' : planName} | ||
219 | {user.team.isTrial && ( | ||
220 | <> | ||
221 | {' – '} | ||
222 | {intl.formatMessage(messages.trial)} | ||
223 | </> | ||
224 | )} | ||
225 | </p> | ||
226 | {user.team.isTrial && ( | ||
152 | <> | 227 | <> |
153 | {' '} | 228 | <br /> |
154 | <ProBadge /> | 229 | <p> |
155 | <span className="badge badge--premium">{intl.formatMessage(messages.accountTypePremium)}</span> | 230 | {intl.formatMessage(messages.trialEndsIn, { |
231 | duration: moment.duration(moment().diff(user.team.trialEnd)).humanize(), | ||
232 | })} | ||
233 | </p> | ||
234 | <p> | ||
235 | {intl.formatMessage(messages.trialUpdateBillingInformation, { | ||
236 | license: planName, | ||
237 | })} | ||
238 | </p> | ||
156 | </> | 239 | </> |
157 | )} | 240 | )} |
158 | </h2> | ||
159 | {user.organization && `${user.organization}, `} | ||
160 | {user.email} | ||
161 | {user.isPremium && ( | ||
162 | <div className="manage-user-links"> | 241 | <div className="manage-user-links"> |
242 | {!isProUser && ( | ||
243 | <Button | ||
244 | label={intl.formatMessage(messages.upgradeAccountToPro)} | ||
245 | className="franz-form__button--primary" | ||
246 | onClick={upgradeToPro} | ||
247 | /> | ||
248 | )} | ||
163 | <Button | 249 | <Button |
164 | label={intl.formatMessage(messages.accountEditButton)} | 250 | label={intl.formatMessage(messages.manageSubscriptionButtonLabel)} |
165 | className="franz-form__button--inverted" | 251 | className="franz-form__button--inverted" |
166 | onClick={openEditAccount} | 252 | onClick={openBilling} |
253 | /> | ||
254 | <Button | ||
255 | label={intl.formatMessage(messages.invoicesButton)} | ||
256 | className="franz-form__button--inverted" | ||
257 | onClick={openInvoices} | ||
167 | /> | 258 | /> |
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> | 259 | </div> |
183 | )} | 260 | </div> |
184 | </div> | 261 | </div> |
185 | {!user.isPremium && ( | 262 | )} |
186 | <Button | 263 | {!user.isPremium && ( |
187 | label={intl.formatMessage(messages.accountEditButton)} | 264 | <div className="account franz-form"> |
188 | className="franz-form__button--inverted" | 265 | <div className="account__box"> |
189 | onClick={openEditAccount} | 266 | <SubscriptionForm /> |
190 | /> | 267 | </div> |
191 | )} | ||
192 | </div> | ||
193 | </div> | ||
194 | )} | ||
195 | |||
196 | {!user.isPremium && ( | ||
197 | isLoadingPlans ? ( | ||
198 | <Loader /> | ||
199 | ) : ( | ||
200 | <div className="account franz-form"> | ||
201 | <div className="account__box"> | ||
202 | <h2>{intl.formatMessage(messages.headlineUpgrade)}</h2> | ||
203 | <SubscriptionForm | ||
204 | onCloseWindow={onCloseSubscriptionWindow} | ||
205 | /> | ||
206 | </div> | 268 | </div> |
207 | </div> | 269 | )} |
208 | ) | 270 | </> |
209 | )} | 271 | )} |
210 | 272 | ||
211 | <div className="account franz-form"> | 273 | <div className="account franz-form"> |
212 | <div className="account__box"> | 274 | <div className="account__box"> |
213 | <h2>{intl.formatMessage(messages.headlineDangerZone)}</h2> | 275 | <H2>{intl.formatMessage(messages.headlineDangerZone)}</H2> |
214 | {!isDeleteAccountSuccessful && ( | 276 | {!isDeleteAccountSuccessful && ( |
215 | <div className="account__subscription"> | 277 | <div className="account__subscription"> |
216 | <p>{intl.formatMessage(messages.deleteInfo)}</p> | 278 | <p>{intl.formatMessage(messages.deleteInfo)}</p> |
@@ -227,7 +289,7 @@ export default @observer class AccountDashboard extends Component { | |||
227 | )} | 289 | )} |
228 | </div> | 290 | </div> |
229 | </div> | 291 | </div> |
230 | </Fragment> | 292 | </> |
231 | )} | 293 | )} |
232 | </div> | 294 | </div> |
233 | <ReactTooltip place="right" type="dark" effect="solid" /> | 295 | <ReactTooltip place="right" type="dark" effect="solid" /> |
@@ -235,3 +297,5 @@ export default @observer class AccountDashboard extends Component { | |||
235 | ); | 297 | ); |
236 | } | 298 | } |
237 | } | 299 | } |
300 | |||
301 | export default AccountDashboard; | ||
diff --git a/src/components/settings/navigation/SettingsNavigation.js b/src/components/settings/navigation/SettingsNavigation.js index 6aa9bda03..201819526 100644 --- a/src/components/settings/navigation/SettingsNavigation.js +++ b/src/components/settings/navigation/SettingsNavigation.js | |||
@@ -8,6 +8,7 @@ import Link from '../../ui/Link'; | |||
8 | import { workspaceStore } from '../../../features/workspaces'; | 8 | import { workspaceStore } from '../../../features/workspaces'; |
9 | import UIStore from '../../../stores/UIStore'; | 9 | import UIStore from '../../../stores/UIStore'; |
10 | import UserStore from '../../../stores/UserStore'; | 10 | import UserStore from '../../../stores/UserStore'; |
11 | import { serviceLimitStore } from '../../../features/serviceLimit'; | ||
11 | 12 | ||
12 | const messages = defineMessages({ | 13 | const messages = defineMessages({ |
13 | availableServices: { | 14 | availableServices: { |
@@ -81,7 +82,12 @@ export default @inject('stores') @observer class SettingsNavigation extends Comp | |||
81 | > | 82 | > |
82 | {intl.formatMessage(messages.yourServices)} | 83 | {intl.formatMessage(messages.yourServices)} |
83 | {' '} | 84 | {' '} |
84 | <span className="badge">{serviceCount}</span> | 85 | <span className="badge"> |
86 | {serviceCount} | ||
87 | {serviceLimitStore.serviceLimit !== 0 && ( | ||
88 | `/${serviceLimitStore.serviceLimit}` | ||
89 | )} | ||
90 | </span> | ||
85 | </Link> | 91 | </Link> |
86 | {workspaceStore.isFeatureEnabled ? ( | 92 | {workspaceStore.isFeatureEnabled ? ( |
87 | <Link | 93 | <Link |
diff --git a/src/components/settings/recipes/RecipeItem.js b/src/components/settings/recipes/RecipeItem.js index 3bb0852b2..12e3775f6 100644 --- a/src/components/settings/recipes/RecipeItem.js +++ b/src/components/settings/recipes/RecipeItem.js | |||
@@ -19,7 +19,7 @@ export default @observer class RecipeItem extends Component { | |||
19 | className="recipe-teaser" | 19 | className="recipe-teaser" |
20 | onClick={onClick} | 20 | onClick={onClick} |
21 | > | 21 | > |
22 | {recipe.local && ( | 22 | {recipe.isDevRecipe && ( |
23 | <span className="recipe-teaser__dev-badge">dev</span> | 23 | <span className="recipe-teaser__dev-badge">dev</span> |
24 | )} | 24 | )} |
25 | <img | 25 | <img |
diff --git a/src/components/settings/recipes/RecipesDashboard.js b/src/components/settings/recipes/RecipesDashboard.js index 00cd725cf..877cbc588 100644 --- a/src/components/settings/recipes/RecipesDashboard.js +++ b/src/components/settings/recipes/RecipesDashboard.js | |||
@@ -4,12 +4,17 @@ import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | |||
4 | import { defineMessages, intlShape } from 'react-intl'; | 4 | import { defineMessages, intlShape } from 'react-intl'; |
5 | import { Link } from 'react-router'; | 5 | import { Link } from 'react-router'; |
6 | 6 | ||
7 | import { Button, Input } from '@meetfranz/forms'; | ||
8 | import injectSheet from 'react-jss'; | ||
9 | import { H3, H2, ProBadge } from '@meetfranz/ui'; | ||
7 | import SearchInput from '../../ui/SearchInput'; | 10 | import SearchInput from '../../ui/SearchInput'; |
8 | import Infobox from '../../ui/Infobox'; | 11 | import Infobox from '../../ui/Infobox'; |
9 | import RecipeItem from './RecipeItem'; | 12 | import RecipeItem from './RecipeItem'; |
10 | import Loader from '../../ui/Loader'; | 13 | import Loader from '../../ui/Loader'; |
11 | import Appear from '../../ui/effects/Appear'; | 14 | import Appear from '../../ui/effects/Appear'; |
12 | import { FRANZ_SERVICE_REQUEST } from '../../../config'; | 15 | import { FRANZ_SERVICE_REQUEST } from '../../../config'; |
16 | import LimitReachedInfobox from '../../../features/serviceLimit/components/LimitReachedInfobox'; | ||
17 | import PremiumFeatureContainer from '../../ui/PremiumFeatureContainer'; | ||
13 | 18 | ||
14 | const messages = defineMessages({ | 19 | const messages = defineMessages({ |
15 | headline: { | 20 | headline: { |
@@ -28,9 +33,9 @@ const messages = defineMessages({ | |||
28 | id: 'settings.recipes.all', | 33 | id: 'settings.recipes.all', |
29 | defaultMessage: '!!!All services', | 34 | defaultMessage: '!!!All services', |
30 | }, | 35 | }, |
31 | devRecipes: { | 36 | customRecipes: { |
32 | id: 'settings.recipes.dev', | 37 | id: 'settings.recipes.custom', |
33 | defaultMessage: '!!!Development', | 38 | defaultMessage: '!!!Custom Services', |
34 | }, | 39 | }, |
35 | nothingFound: { | 40 | nothingFound: { |
36 | id: 'settings.recipes.nothingFound', | 41 | id: 'settings.recipes.nothingFound', |
@@ -44,9 +49,61 @@ const messages = defineMessages({ | |||
44 | id: 'settings.recipes.missingService', | 49 | id: 'settings.recipes.missingService', |
45 | defaultMessage: '!!!Missing a service?', | 50 | defaultMessage: '!!!Missing a service?', |
46 | }, | 51 | }, |
52 | customRecipeIntro: { | ||
53 | id: 'settings.recipes.customService.intro', | ||
54 | defaultMessage: '!!!To add a custom service, copy the recipe folder into:', | ||
55 | }, | ||
56 | openFolder: { | ||
57 | id: 'settings.recipes.customService.openFolder', | ||
58 | defaultMessage: '!!!Open directory', | ||
59 | }, | ||
60 | openDevDocs: { | ||
61 | id: 'settings.recipes.customService.openDevDocs', | ||
62 | defaultMessage: '!!!Developer Documentation', | ||
63 | }, | ||
64 | headlineCustomRecipes: { | ||
65 | id: 'settings.recipes.customService.headline.customRecipes', | ||
66 | defaultMessage: '!!!Custom 3rd Party Recipes', | ||
67 | }, | ||
68 | headlineCommunityRecipes: { | ||
69 | id: 'settings.recipes.customService.headline.communityRecipes', | ||
70 | defaultMessage: '!!!Community 3rd Party Recipes', | ||
71 | }, | ||
72 | headlineDevRecipes: { | ||
73 | id: 'settings.recipes.customService.headline.devRecipes', | ||
74 | defaultMessage: '!!!Your Development Service Recipes', | ||
75 | }, | ||
47 | }); | 76 | }); |
48 | 77 | ||
49 | export default @observer class RecipesDashboard extends Component { | 78 | const styles = { |
79 | devRecipeIntroContainer: { | ||
80 | textAlign: 'center', | ||
81 | width: '100%', | ||
82 | height: 'auto', | ||
83 | margin: [40, 0], | ||
84 | }, | ||
85 | path: { | ||
86 | marginTop: 20, | ||
87 | |||
88 | '& > div': { | ||
89 | fontFamily: 'SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace', | ||
90 | }, | ||
91 | }, | ||
92 | actionContainer: { | ||
93 | '& button': { | ||
94 | margin: [0, 10], | ||
95 | }, | ||
96 | }, | ||
97 | devRecipeList: { | ||
98 | marginTop: 20, | ||
99 | height: 'auto', | ||
100 | }, | ||
101 | proBadge: { | ||
102 | marginLeft: '10px !important', | ||
103 | }, | ||
104 | }; | ||
105 | |||
106 | export default @injectSheet(styles) @observer class RecipesDashboard extends Component { | ||
50 | static propTypes = { | 107 | static propTypes = { |
51 | recipes: MobxPropTypes.arrayOrObservableArray.isRequired, | 108 | recipes: MobxPropTypes.arrayOrObservableArray.isRequired, |
52 | isLoading: PropTypes.bool.isRequired, | 109 | isLoading: PropTypes.bool.isRequired, |
@@ -55,12 +112,18 @@ export default @observer class RecipesDashboard extends Component { | |||
55 | searchRecipes: PropTypes.func.isRequired, | 112 | searchRecipes: PropTypes.func.isRequired, |
56 | resetSearch: PropTypes.func.isRequired, | 113 | resetSearch: PropTypes.func.isRequired, |
57 | serviceStatus: MobxPropTypes.arrayOrObservableArray.isRequired, | 114 | serviceStatus: MobxPropTypes.arrayOrObservableArray.isRequired, |
58 | devRecipesCount: PropTypes.number.isRequired, | ||
59 | searchNeedle: PropTypes.string, | 115 | searchNeedle: PropTypes.string, |
116 | recipeFilter: PropTypes.string, | ||
117 | recipeDirectory: PropTypes.string.isRequired, | ||
118 | openRecipeDirectory: PropTypes.func.isRequired, | ||
119 | openDevDocs: PropTypes.func.isRequired, | ||
120 | classes: PropTypes.object.isRequired, | ||
121 | isCommunityRecipesIncludedInCurrentPlan: PropTypes.bool.isRequired, | ||
60 | }; | 122 | }; |
61 | 123 | ||
62 | static defaultProps = { | 124 | static defaultProps = { |
63 | searchNeedle: '', | 125 | searchNeedle: '', |
126 | recipeFilter: 'all', | ||
64 | } | 127 | } |
65 | 128 | ||
66 | static contextTypes = { | 129 | static contextTypes = { |
@@ -76,16 +139,26 @@ export default @observer class RecipesDashboard extends Component { | |||
76 | searchRecipes, | 139 | searchRecipes, |
77 | resetSearch, | 140 | resetSearch, |
78 | serviceStatus, | 141 | serviceStatus, |
79 | devRecipesCount, | ||
80 | searchNeedle, | 142 | searchNeedle, |
143 | recipeFilter, | ||
144 | recipeDirectory, | ||
145 | openRecipeDirectory, | ||
146 | openDevDocs, | ||
147 | classes, | ||
148 | isCommunityRecipesIncludedInCurrentPlan, | ||
81 | } = this.props; | 149 | } = this.props; |
82 | const { intl } = this.context; | 150 | const { intl } = this.context; |
83 | 151 | ||
152 | |||
153 | const communityRecipes = recipes.filter(r => !r.isDevRecipe); | ||
154 | const devRecipes = recipes.filter(r => r.isDevRecipe); | ||
155 | |||
84 | return ( | 156 | return ( |
85 | <div className="settings__main"> | 157 | <div className="settings__main"> |
86 | <div className="settings__header"> | 158 | <div className="settings__header"> |
87 | <h1>{intl.formatMessage(messages.headline)}</h1> | 159 | <h1>{intl.formatMessage(messages.headline)}</h1> |
88 | </div> | 160 | </div> |
161 | <LimitReachedInfobox /> | ||
89 | <div className="settings__body recipes"> | 162 | <div className="settings__body recipes"> |
90 | {serviceStatus.length > 0 && serviceStatus.includes('created') && ( | 163 | {serviceStatus.length > 0 && serviceStatus.includes('created') && ( |
91 | <Appear> | 164 | <Appear> |
@@ -122,20 +195,14 @@ export default @observer class RecipesDashboard extends Component { | |||
122 | > | 195 | > |
123 | {intl.formatMessage(messages.allRecipes)} | 196 | {intl.formatMessage(messages.allRecipes)} |
124 | </Link> | 197 | </Link> |
125 | {devRecipesCount > 0 && ( | 198 | <Link |
126 | <Link | 199 | to="/settings/recipes/dev" |
127 | to="/settings/recipes/dev" | 200 | className="badge" |
128 | className="badge" | 201 | activeClassName={`${!searchNeedle ? 'badge--primary' : ''}`} |
129 | activeClassName={`${!searchNeedle ? 'badge--primary' : ''}`} | 202 | onClick={() => resetSearch()} |
130 | onClick={() => resetSearch()} | 203 | > |
131 | > | 204 | {intl.formatMessage(messages.customRecipes)} |
132 | {intl.formatMessage(messages.devRecipes)} | 205 | </Link> |
133 | {' '} | ||
134 | ( | ||
135 | {devRecipesCount} | ||
136 | ) | ||
137 | </Link> | ||
138 | )} | ||
139 | <a href={FRANZ_SERVICE_REQUEST} target="_blank" className="link recipes__service-request"> | 206 | <a href={FRANZ_SERVICE_REQUEST} target="_blank" className="link recipes__service-request"> |
140 | {intl.formatMessage(messages.missingService)} | 207 | {intl.formatMessage(messages.missingService)} |
141 | {' '} | 208 | {' '} |
@@ -146,23 +213,78 @@ export default @observer class RecipesDashboard extends Component { | |||
146 | {isLoading ? ( | 213 | {isLoading ? ( |
147 | <Loader /> | 214 | <Loader /> |
148 | ) : ( | 215 | ) : ( |
149 | <div className="recipes__list"> | 216 | <> |
150 | {hasLoadedRecipes && recipes.length === 0 && ( | 217 | {recipeFilter === 'dev' && ( |
151 | <p className="align-middle settings__empty-state"> | 218 | <> |
152 | <span className="emoji"> | 219 | <H2> |
153 | <img src="./assets/images/emoji/dontknow.png" alt="" /> | 220 | {intl.formatMessage(messages.headlineCustomRecipes)} |
154 | </span> | 221 | {!isCommunityRecipesIncludedInCurrentPlan && ( |
155 | {intl.formatMessage(messages.nothingFound)} | 222 | <ProBadge className={classes.proBadge} /> |
156 | </p> | 223 | )} |
224 | </H2> | ||
225 | <div className={classes.devRecipeIntroContainer}> | ||
226 | <p> | ||
227 | {intl.formatMessage(messages.customRecipeIntro)} | ||
228 | </p> | ||
229 | <Input | ||
230 | value={recipeDirectory} | ||
231 | className={classes.path} | ||
232 | showLabel={false} | ||
233 | /> | ||
234 | <div className={classes.actionContainer}> | ||
235 | <Button | ||
236 | onClick={openRecipeDirectory} | ||
237 | buttonType="secondary" | ||
238 | label={intl.formatMessage(messages.openFolder)} | ||
239 | /> | ||
240 | <Button | ||
241 | onClick={openDevDocs} | ||
242 | buttonType="secondary" | ||
243 | label={intl.formatMessage(messages.openDevDocs)} | ||
244 | /> | ||
245 | </div> | ||
246 | </div> | ||
247 | </> | ||
248 | )} | ||
249 | <PremiumFeatureContainer | ||
250 | condition={(recipeFilter === 'dev' && communityRecipes.length > 0) && !isCommunityRecipesIncludedInCurrentPlan} | ||
251 | > | ||
252 | {recipeFilter === 'dev' && communityRecipes.length > 0 && ( | ||
253 | <H3>{intl.formatMessage(messages.headlineCommunityRecipes)}</H3> | ||
254 | )} | ||
255 | <div className="recipes__list"> | ||
256 | {hasLoadedRecipes && recipes.length === 0 && recipeFilter !== 'dev' && ( | ||
257 | <p className="align-middle settings__empty-state"> | ||
258 | <span className="emoji"> | ||
259 | <img src="./assets/images/emoji/dontknow.png" alt="" /> | ||
260 | </span> | ||
261 | {intl.formatMessage(messages.nothingFound)} | ||
262 | </p> | ||
263 | )} | ||
264 | {communityRecipes.map(recipe => ( | ||
265 | <RecipeItem | ||
266 | key={recipe.id} | ||
267 | recipe={recipe} | ||
268 | onClick={() => showAddServiceInterface({ recipeId: recipe.id })} | ||
269 | /> | ||
270 | ))} | ||
271 | </div> | ||
272 | </PremiumFeatureContainer> | ||
273 | {recipeFilter === 'dev' && devRecipes.length > 0 && ( | ||
274 | <div className={classes.devRecipeList}> | ||
275 | <H3>{intl.formatMessage(messages.headlineDevRecipes)}</H3> | ||
276 | <div className="recipes__list"> | ||
277 | {devRecipes.map(recipe => ( | ||
278 | <RecipeItem | ||
279 | key={recipe.id} | ||
280 | recipe={recipe} | ||
281 | onClick={() => showAddServiceInterface({ recipeId: recipe.id })} | ||
282 | /> | ||
283 | ))} | ||
284 | </div> | ||
285 | </div> | ||
157 | )} | 286 | )} |
158 | {recipes.map(recipe => ( | 287 | </> |
159 | <RecipeItem | ||
160 | key={recipe.id} | ||
161 | recipe={recipe} | ||
162 | onClick={() => showAddServiceInterface({ recipeId: recipe.id })} | ||
163 | /> | ||
164 | ))} | ||
165 | </div> | ||
166 | )} | 288 | )} |
167 | </div> | 289 | </div> |
168 | </div> | 290 | </div> |
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js index 711b571e2..5fe00cb8b 100644 --- a/src/components/settings/services/EditServiceForm.js +++ b/src/components/settings/services/EditServiceForm.js | |||
@@ -17,6 +17,8 @@ import ImageUpload from '../../ui/ImageUpload'; | |||
17 | import Select from '../../ui/Select'; | 17 | import Select from '../../ui/Select'; |
18 | 18 | ||
19 | import PremiumFeatureContainer from '../../ui/PremiumFeatureContainer'; | 19 | import PremiumFeatureContainer from '../../ui/PremiumFeatureContainer'; |
20 | import LimitReachedInfobox from '../../../features/serviceLimit/components/LimitReachedInfobox'; | ||
21 | import { serviceLimitStore } from '../../../features/serviceLimit'; | ||
20 | 22 | ||
21 | const messages = defineMessages({ | 23 | const messages = defineMessages({ |
22 | saveService: { | 24 | saveService: { |
@@ -128,8 +130,8 @@ export default @observer class EditServiceForm extends Component { | |||
128 | isSaving: PropTypes.bool.isRequired, | 130 | isSaving: PropTypes.bool.isRequired, |
129 | isDeleting: PropTypes.bool.isRequired, | 131 | isDeleting: PropTypes.bool.isRequired, |
130 | isProxyFeatureEnabled: PropTypes.bool.isRequired, | 132 | isProxyFeatureEnabled: PropTypes.bool.isRequired, |
131 | isProxyPremiumFeature: PropTypes.bool.isRequired, | 133 | isServiceProxyIncludedInCurrentPlan: PropTypes.bool.isRequired, |
132 | isSpellcheckerPremiumFeature: PropTypes.bool.isRequired, | 134 | isSpellcheckerIncludedInCurrentPlan: PropTypes.bool.isRequired, |
133 | }; | 135 | }; |
134 | 136 | ||
135 | static defaultProps = { | 137 | static defaultProps = { |
@@ -192,8 +194,8 @@ export default @observer class EditServiceForm extends Component { | |||
192 | isDeleting, | 194 | isDeleting, |
193 | onDelete, | 195 | onDelete, |
194 | isProxyFeatureEnabled, | 196 | isProxyFeatureEnabled, |
195 | isProxyPremiumFeature, | 197 | isServiceProxyIncludedInCurrentPlan, |
196 | isSpellcheckerPremiumFeature, | 198 | isSpellcheckerIncludedInCurrentPlan, |
197 | } = this.props; | 199 | } = this.props; |
198 | const { intl } = this.context; | 200 | const { intl } = this.context; |
199 | 201 | ||
@@ -252,6 +254,7 @@ export default @observer class EditServiceForm extends Component { | |||
252 | )} | 254 | )} |
253 | </span> | 255 | </span> |
254 | </div> | 256 | </div> |
257 | <LimitReachedInfobox /> | ||
255 | <div className="settings__body"> | 258 | <div className="settings__body"> |
256 | <form onSubmit={e => this.submit(e)} id="form"> | 259 | <form onSubmit={e => this.submit(e)} id="form"> |
257 | <div className="service-name"> | 260 | <div className="service-name"> |
@@ -342,7 +345,7 @@ export default @observer class EditServiceForm extends Component { | |||
342 | </div> | 345 | </div> |
343 | 346 | ||
344 | <PremiumFeatureContainer | 347 | <PremiumFeatureContainer |
345 | condition={isSpellcheckerPremiumFeature} | 348 | condition={!isSpellcheckerIncludedInCurrentPlan} |
346 | gaEventInfo={{ category: 'User', event: 'upgrade', label: 'spellchecker' }} | 349 | gaEventInfo={{ category: 'User', event: 'upgrade', label: 'spellchecker' }} |
347 | > | 350 | > |
348 | <div className="settings__settings-group"> | 351 | <div className="settings__settings-group"> |
@@ -352,7 +355,7 @@ export default @observer class EditServiceForm extends Component { | |||
352 | 355 | ||
353 | {isProxyFeatureEnabled && ( | 356 | {isProxyFeatureEnabled && ( |
354 | <PremiumFeatureContainer | 357 | <PremiumFeatureContainer |
355 | condition={isProxyPremiumFeature} | 358 | condition={!isServiceProxyIncludedInCurrentPlan} |
356 | gaEventInfo={{ category: 'User', event: 'upgrade', label: 'proxy' }} | 359 | gaEventInfo={{ category: 'User', event: 'upgrade', label: 'proxy' }} |
357 | > | 360 | > |
358 | <div className="settings__settings-group"> | 361 | <div className="settings__settings-group"> |
@@ -418,7 +421,7 @@ export default @observer class EditServiceForm extends Component { | |||
418 | type="submit" | 421 | type="submit" |
419 | label={intl.formatMessage(messages.saveService)} | 422 | label={intl.formatMessage(messages.saveService)} |
420 | htmlForm="form" | 423 | htmlForm="form" |
421 | disabled={action !== 'edit' && form.isPristine && requiresUserInput} | 424 | disabled={action !== 'edit' && ((form.isPristine && requiresUserInput) || serviceLimitStore.userHasReachedServiceLimit)} |
422 | /> | 425 | /> |
423 | )} | 426 | )} |
424 | </div> | 427 | </div> |
diff --git a/src/components/settings/services/ServicesDashboard.js b/src/components/settings/services/ServicesDashboard.js index 53bae12df..78038e86a 100644 --- a/src/components/settings/services/ServicesDashboard.js +++ b/src/components/settings/services/ServicesDashboard.js | |||
@@ -9,6 +9,7 @@ import Infobox from '../../ui/Infobox'; | |||
9 | import Loader from '../../ui/Loader'; | 9 | import Loader from '../../ui/Loader'; |
10 | import ServiceItem from './ServiceItem'; | 10 | import ServiceItem from './ServiceItem'; |
11 | import Appear from '../../ui/effects/Appear'; | 11 | import Appear from '../../ui/effects/Appear'; |
12 | import LimitReachedInfobox from '../../../features/serviceLimit/components/LimitReachedInfobox'; | ||
12 | 13 | ||
13 | const messages = defineMessages({ | 14 | const messages = defineMessages({ |
14 | headline: { | 15 | headline: { |
@@ -91,6 +92,7 @@ export default @observer class ServicesDashboard extends Component { | |||
91 | <div className="settings__header"> | 92 | <div className="settings__header"> |
92 | <h1>{intl.formatMessage(messages.headline)}</h1> | 93 | <h1>{intl.formatMessage(messages.headline)}</h1> |
93 | </div> | 94 | </div> |
95 | <LimitReachedInfobox /> | ||
94 | <div className="settings__body"> | 96 | <div className="settings__body"> |
95 | {!isLoading && ( | 97 | {!isLoading && ( |
96 | <SearchInput | 98 | <SearchInput |
diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js index 660c3c109..19333fdff 100644 --- a/src/components/settings/settings/EditSettingsForm.js +++ b/src/components/settings/settings/EditSettingsForm.js | |||
@@ -105,7 +105,8 @@ export default @observer class EditSettingsForm extends Component { | |||
105 | isClearingAllCache: PropTypes.bool.isRequired, | 105 | isClearingAllCache: PropTypes.bool.isRequired, |
106 | onClearAllCache: PropTypes.func.isRequired, | 106 | onClearAllCache: PropTypes.func.isRequired, |
107 | cacheSize: PropTypes.string.isRequired, | 107 | cacheSize: PropTypes.string.isRequired, |
108 | isSpellcheckerPremiumFeature: PropTypes.bool.isRequired, | 108 | isSpellcheckerIncludedInCurrentPlan: PropTypes.bool.isRequired, |
109 | isTodosEnabled: PropTypes.bool.isRequired, | ||
109 | }; | 110 | }; |
110 | 111 | ||
111 | static contextTypes = { | 112 | static contextTypes = { |
@@ -135,7 +136,8 @@ export default @observer class EditSettingsForm extends Component { | |||
135 | isClearingAllCache, | 136 | isClearingAllCache, |
136 | onClearAllCache, | 137 | onClearAllCache, |
137 | cacheSize, | 138 | cacheSize, |
138 | isSpellcheckerPremiumFeature, | 139 | isSpellcheckerIncludedInCurrentPlan, |
140 | isTodosEnabled, | ||
139 | } = this.props; | 141 | } = this.props; |
140 | const { intl } = this.context; | 142 | const { intl } = this.context; |
141 | 143 | ||
@@ -178,6 +180,9 @@ export default @observer class EditSettingsForm extends Component { | |||
178 | { isLoggedIn && ( | 180 | { isLoggedIn && ( |
179 | <p>{ intl.formatMessage(messages.serverInfo) }</p> | 181 | <p>{ intl.formatMessage(messages.serverInfo) }</p> |
180 | )} | 182 | )} |
183 | {isTodosEnabled && ( | ||
184 | <Toggle field={form.$('enableTodos')} /> | ||
185 | )} | ||
181 | 186 | ||
182 | {/* Appearance */} | 187 | {/* Appearance */} |
183 | <h2 id="apperance">{intl.formatMessage(messages.headlineAppearance)}</h2> | 188 | <h2 id="apperance">{intl.formatMessage(messages.headlineAppearance)}</h2> |
@@ -189,7 +194,7 @@ export default @observer class EditSettingsForm extends Component { | |||
189 | <h2 id="language">{intl.formatMessage(messages.headlineLanguage)}</h2> | 194 | <h2 id="language">{intl.formatMessage(messages.headlineLanguage)}</h2> |
190 | <Select field={form.$('locale')} showLabel={false} /> | 195 | <Select field={form.$('locale')} showLabel={false} /> |
191 | <PremiumFeatureContainer | 196 | <PremiumFeatureContainer |
192 | condition={isSpellcheckerPremiumFeature} | 197 | condition={!isSpellcheckerIncludedInCurrentPlan} |
193 | gaEventInfo={{ category: 'User', event: 'upgrade', label: 'spellchecker' }} | 198 | gaEventInfo={{ category: 'User', event: 'upgrade', label: 'spellchecker' }} |
194 | > | 199 | > |
195 | <Fragment> | 200 | <Fragment> |
diff --git a/src/components/settings/team/TeamDashboard.js b/src/components/settings/team/TeamDashboard.js index 05c942a11..2bf46b48d 100644 --- a/src/components/settings/team/TeamDashboard.js +++ b/src/components/settings/team/TeamDashboard.js | |||
@@ -4,11 +4,14 @@ import { observer } 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 injectSheet from 'react-jss'; | 6 | import injectSheet from 'react-jss'; |
7 | import classnames from 'classnames'; | ||
7 | 8 | ||
9 | import { Badge } from '@meetfranz/ui'; | ||
8 | import Loader from '../../ui/Loader'; | 10 | import Loader from '../../ui/Loader'; |
9 | import Button from '../../ui/Button'; | 11 | import Button from '../../ui/Button'; |
10 | import Infobox from '../../ui/Infobox'; | 12 | import Infobox from '../../ui/Infobox'; |
11 | import PremiumFeatureContainer from '../../ui/PremiumFeatureContainer'; | 13 | import globalMessages from '../../../i18n/globalMessages'; |
14 | import UpgradeButton from '../../ui/UpgradeButton'; | ||
12 | 15 | ||
13 | const messages = defineMessages({ | 16 | const messages = defineMessages({ |
14 | headline: { | 17 | headline: { |
@@ -40,6 +43,7 @@ const messages = defineMessages({ | |||
40 | const styles = { | 43 | const styles = { |
41 | cta: { | 44 | cta: { |
42 | margin: [40, 'auto'], | 45 | margin: [40, 'auto'], |
46 | height: 'auto', | ||
43 | }, | 47 | }, |
44 | container: { | 48 | container: { |
45 | display: 'flex', | 49 | display: 'flex', |
@@ -69,6 +73,20 @@ const styles = { | |||
69 | order: 1, | 73 | order: 1, |
70 | }, | 74 | }, |
71 | }, | 75 | }, |
76 | headline: { | ||
77 | marginBottom: 0, | ||
78 | }, | ||
79 | headlineWithSpacing: { | ||
80 | marginBottom: 'inherit', | ||
81 | }, | ||
82 | proRequired: { | ||
83 | margin: [10, 0, 40], | ||
84 | height: 'auto', | ||
85 | }, | ||
86 | buttonContainer: { | ||
87 | display: 'flex', | ||
88 | height: 'auto', | ||
89 | }, | ||
72 | }; | 90 | }; |
73 | 91 | ||
74 | 92 | ||
@@ -79,6 +97,7 @@ export default @injectSheet(styles) @observer class TeamDashboard extends Compon | |||
79 | retryUserInfoRequest: PropTypes.func.isRequired, | 97 | retryUserInfoRequest: PropTypes.func.isRequired, |
80 | openTeamManagement: PropTypes.func.isRequired, | 98 | openTeamManagement: PropTypes.func.isRequired, |
81 | classes: PropTypes.object.isRequired, | 99 | classes: PropTypes.object.isRequired, |
100 | isProUser: PropTypes.bool.isRequired, | ||
82 | }; | 101 | }; |
83 | 102 | ||
84 | static contextTypes = { | 103 | static contextTypes = { |
@@ -91,6 +110,7 @@ export default @injectSheet(styles) @observer class TeamDashboard extends Compon | |||
91 | userInfoRequestFailed, | 110 | userInfoRequestFailed, |
92 | retryUserInfoRequest, | 111 | retryUserInfoRequest, |
93 | openTeamManagement, | 112 | openTeamManagement, |
113 | isProUser, | ||
94 | classes, | 114 | classes, |
95 | } = this.props; | 115 | } = this.props; |
96 | const { intl } = this.context; | 116 | const { intl } = this.context; |
@@ -123,23 +143,42 @@ export default @injectSheet(styles) @observer class TeamDashboard extends Compon | |||
123 | <> | 143 | <> |
124 | {!isLoading && ( | 144 | {!isLoading && ( |
125 | <> | 145 | <> |
126 | <PremiumFeatureContainer> | 146 | <> |
127 | <> | 147 | <h1 className={classnames({ |
128 | <h1>{intl.formatMessage(messages.contentHeadline)}</h1> | 148 | [classes.headline]: true, |
129 | <div className={classes.container}> | 149 | [classes.headlineWithSpacing]: isProUser, |
130 | <div className={classes.content}> | 150 | })} |
131 | <p>{intl.formatMessage(messages.intro)}</p> | 151 | > |
132 | <p>{intl.formatMessage(messages.copy)}</p> | 152 | {intl.formatMessage(messages.contentHeadline)} |
133 | </div> | 153 | |
134 | <img className={classes.image} src="https://cdn.franzinfra.com/announcements/assets/teams.png" alt="Ferdi for Teams" /> | 154 | </h1> |
155 | {!isProUser && ( | ||
156 | <Badge className={classes.proRequired}>{intl.formatMessage(globalMessages.proRequired)}</Badge> | ||
157 | )} | ||
158 | <div className={classes.container}> | ||
159 | <div className={classes.content}> | ||
160 | <p>{intl.formatMessage(messages.intro)}</p> | ||
161 | <p>{intl.formatMessage(messages.copy)}</p> | ||
135 | </div> | 162 | </div> |
136 | <Button | 163 | <img className={classes.image} src="https://cdn.franzinfra.com/announcements/assets/teams.png" alt="Franz for Teams" /> |
137 | label={intl.formatMessage(messages.manageButton)} | 164 | </div> |
138 | onClick={openTeamManagement} | 165 | <div className={classes.buttonContainer}> |
139 | className={classes.cta} | 166 | {!isProUser ? ( |
140 | /> | 167 | <UpgradeButton |
141 | </> | 168 | className={classes.cta} |
142 | </PremiumFeatureContainer> | 169 | gaEventInfo={{ category: 'Todos', event: 'upgrade' }} |
170 | requiresPro | ||
171 | short | ||
172 | /> | ||
173 | ) : ( | ||
174 | <Button | ||
175 | label={intl.formatMessage(messages.manageButton)} | ||
176 | onClick={openTeamManagement} | ||
177 | className={classes.cta} | ||
178 | /> | ||
179 | )} | ||
180 | </div> | ||
181 | </> | ||
143 | </> | 182 | </> |
144 | )} | 183 | )} |
145 | </> | 184 | </> |