diff options
Diffstat (limited to 'src/components')
20 files changed, 118 insertions, 1437 deletions
diff --git a/src/components/TrialActivationInfoBar.js b/src/components/TrialActivationInfoBar.js deleted file mode 100644 index 77ab97565..000000000 --- a/src/components/TrialActivationInfoBar.js +++ /dev/null | |||
@@ -1,94 +0,0 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { defineMessages, intlShape } from 'react-intl'; | ||
4 | import ms from 'ms'; | ||
5 | import injectSheet from 'react-jss'; | ||
6 | import classnames from 'classnames'; | ||
7 | |||
8 | import InfoBar from './ui/InfoBar'; | ||
9 | |||
10 | const messages = defineMessages({ | ||
11 | message: { | ||
12 | id: 'infobar.trialActivated', | ||
13 | defaultMessage: '!!!Your trial was successfully activated. Happy messaging!', | ||
14 | }, | ||
15 | }); | ||
16 | |||
17 | const styles = { | ||
18 | notification: { | ||
19 | height: 'auto', | ||
20 | position: 'absolute', | ||
21 | top: -50, | ||
22 | transition: 'top 0.3s', | ||
23 | zIndex: 500, | ||
24 | width: 'calc(100% - 300px)', | ||
25 | }, | ||
26 | show: { | ||
27 | top: 0, | ||
28 | }, | ||
29 | }; | ||
30 | |||
31 | @injectSheet(styles) | ||
32 | class TrialActivationInfoBar extends Component { | ||
33 | static propTypes = { | ||
34 | // eslint-disable-next-line | ||
35 | classes: PropTypes.object.isRequired, | ||
36 | }; | ||
37 | |||
38 | static contextTypes = { | ||
39 | intl: intlShape, | ||
40 | }; | ||
41 | |||
42 | state = { | ||
43 | showing: false, | ||
44 | removed: false, | ||
45 | } | ||
46 | |||
47 | componentDidMount() { | ||
48 | setTimeout(() => { | ||
49 | this.setState({ | ||
50 | showing: true, | ||
51 | }); | ||
52 | }, 0); | ||
53 | |||
54 | setTimeout(() => { | ||
55 | this.setState({ | ||
56 | showing: false, | ||
57 | }); | ||
58 | }, ms('6s')); | ||
59 | |||
60 | setTimeout(() => { | ||
61 | this.setState({ | ||
62 | removed: true, | ||
63 | }); | ||
64 | }, ms('7s')); | ||
65 | } | ||
66 | |||
67 | render() { | ||
68 | const { classes } = this.props; | ||
69 | const { showing, removed } = this.state; | ||
70 | const { intl } = this.context; | ||
71 | |||
72 | if (removed) return null; | ||
73 | |||
74 | return ( | ||
75 | <div | ||
76 | className={classnames({ | ||
77 | [classes.notification]: true, | ||
78 | [classes.show]: showing, | ||
79 | })} | ||
80 | > | ||
81 | <InfoBar | ||
82 | type="primary" | ||
83 | position="top" | ||
84 | sticky | ||
85 | > | ||
86 | <span className="mdi mdi-information" /> | ||
87 | {intl.formatMessage(messages.message)} | ||
88 | </InfoBar> | ||
89 | </div> | ||
90 | ); | ||
91 | } | ||
92 | } | ||
93 | |||
94 | export default TrialActivationInfoBar; | ||
diff --git a/src/components/auth/Pricing.js b/src/components/auth/Pricing.js deleted file mode 100644 index 2fcabe54d..000000000 --- a/src/components/auth/Pricing.js +++ /dev/null | |||
@@ -1,270 +0,0 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | ||
4 | import { defineMessages, intlShape } from 'react-intl'; | ||
5 | import injectSheet from 'react-jss'; | ||
6 | import { H2, Loader } from '@meetfranz/ui'; | ||
7 | import classnames from 'classnames'; | ||
8 | |||
9 | import { Button } from '@meetfranz/forms'; | ||
10 | import { FeatureItem } from '../ui/FeatureItem'; | ||
11 | import { FeatureList } from '../ui/FeatureList'; | ||
12 | |||
13 | const messages = defineMessages({ | ||
14 | headline: { | ||
15 | id: 'pricing.trial.headline.pro', | ||
16 | defaultMessage: '!!!Hi {name}, welcome to Franz', | ||
17 | }, | ||
18 | specialTreat: { | ||
19 | id: 'pricing.trial.intro.specialTreat', | ||
20 | defaultMessage: '!!!We have a special treat for you.', | ||
21 | }, | ||
22 | tryPro: { | ||
23 | id: 'pricing.trial.intro.tryPro', | ||
24 | defaultMessage: '!!!Enjoy the full Franz Professional experience completely free for 14 days.', | ||
25 | }, | ||
26 | happyMessaging: { | ||
27 | id: 'pricing.trial.intro.happyMessaging', | ||
28 | defaultMessage: '!!!Happy messaging,', | ||
29 | }, | ||
30 | noStringsAttachedHeadline: { | ||
31 | id: 'pricing.trial.terms.headline', | ||
32 | defaultMessage: '!!!No strings attached', | ||
33 | }, | ||
34 | noCreditCard: { | ||
35 | id: 'pricing.trial.terms.noCreditCard', | ||
36 | defaultMessage: '!!!No credit card required', | ||
37 | }, | ||
38 | automaticTrialEnd: { | ||
39 | id: 'pricing.trial.terms.automaticTrialEnd', | ||
40 | defaultMessage: '!!!Your free trial ends automatically after 14 days', | ||
41 | }, | ||
42 | trialWorth: { | ||
43 | id: 'pricing.trial.terms.trialWorth', | ||
44 | defaultMessage: '!!!Free trial (normally {currency}{price} per month)', | ||
45 | }, | ||
46 | activationError: { | ||
47 | id: 'pricing.trial.error', | ||
48 | defaultMessage: '!!!Sorry, we could not activate your trial!', | ||
49 | }, | ||
50 | ctaAccept: { | ||
51 | id: 'pricing.trial.cta.accept', | ||
52 | defaultMessage: '!!!Start my 14-day Franz Professional Trial ', | ||
53 | }, | ||
54 | ctaStart: { | ||
55 | id: 'pricing.trial.cta.start', | ||
56 | defaultMessage: '!!!Start using Franz', | ||
57 | }, | ||
58 | ctaSkip: { | ||
59 | id: 'pricing.trial.cta.skip', | ||
60 | defaultMessage: '!!!Continue to Ferdi', | ||
61 | }, | ||
62 | featuresHeadline: { | ||
63 | id: 'pricing.trial.features.headline', | ||
64 | defaultMessage: '!!!Franz Professional includes:', | ||
65 | }, | ||
66 | }); | ||
67 | |||
68 | const styles = theme => ({ | ||
69 | root: { | ||
70 | width: '500px !important', | ||
71 | textAlign: 'center', | ||
72 | padding: 20, | ||
73 | zIndex: 100, | ||
74 | |||
75 | '& h1': { | ||
76 | }, | ||
77 | }, | ||
78 | container: { | ||
79 | position: 'relative', | ||
80 | marginLeft: -150, | ||
81 | }, | ||
82 | welcomeOffer: { | ||
83 | textAlign: 'center', | ||
84 | fontWeight: 'bold', | ||
85 | marginBottom: '6 !important', | ||
86 | }, | ||
87 | keyTerms: { | ||
88 | textAlign: 'center', | ||
89 | }, | ||
90 | content: { | ||
91 | position: 'relative', | ||
92 | zIndex: 20, | ||
93 | }, | ||
94 | featureContainer: { | ||
95 | width: 300, | ||
96 | position: 'absolute', | ||
97 | left: 'calc(100% / 2 + 250px)', | ||
98 | marginTop: 20, | ||
99 | background: theme.signup.pricing.feature.background, | ||
100 | height: 'auto', | ||
101 | padding: 20, | ||
102 | borderTopRightRadius: theme.borderRadius, | ||
103 | borderBottomRightRadius: theme.borderRadius, | ||
104 | zIndex: 10, | ||
105 | }, | ||
106 | featureItem: { | ||
107 | borderBottom: [1, 'solid', theme.signup.pricing.feature.border], | ||
108 | }, | ||
109 | cta: { | ||
110 | marginTop: 40, | ||
111 | width: '100%', | ||
112 | }, | ||
113 | skipLink: { | ||
114 | textAlign: 'center', | ||
115 | marginTop: 10, | ||
116 | }, | ||
117 | error: { | ||
118 | margin: [20, 0, 0], | ||
119 | color: theme.styleTypes.danger.accent, | ||
120 | }, | ||
121 | priceContainer: { | ||
122 | display: 'flex', | ||
123 | justifyContent: 'space-evenly', | ||
124 | margin: [10, 0, 15], | ||
125 | }, | ||
126 | price: { | ||
127 | '& sup': { | ||
128 | verticalAlign: 14, | ||
129 | fontSize: 20, | ||
130 | }, | ||
131 | }, | ||
132 | figure: { | ||
133 | fontSize: 40, | ||
134 | }, | ||
135 | regularPrice: { | ||
136 | position: 'relative', | ||
137 | |||
138 | '&:before': { | ||
139 | content: '" "', | ||
140 | position: 'absolute', | ||
141 | width: '130%', | ||
142 | height: 1, | ||
143 | top: 14, | ||
144 | left: -12, | ||
145 | borderBottom: [3, 'solid', 'red'], | ||
146 | transform: 'rotateZ(-20deg)', | ||
147 | }, | ||
148 | }, | ||
149 | }); | ||
150 | |||
151 | export default @injectSheet(styles) @observer class Signup extends Component { | ||
152 | static propTypes = { | ||
153 | onSubmit: PropTypes.func.isRequired, | ||
154 | isLoadingRequiredData: PropTypes.bool.isRequired, | ||
155 | isActivatingTrial: PropTypes.bool.isRequired, | ||
156 | trialActivationError: PropTypes.bool.isRequired, | ||
157 | canSkipTrial: PropTypes.bool.isRequired, | ||
158 | classes: PropTypes.object.isRequired, | ||
159 | currency: PropTypes.string.isRequired, | ||
160 | price: PropTypes.number.isRequired, | ||
161 | name: PropTypes.string.isRequired, | ||
162 | }; | ||
163 | |||
164 | static contextTypes = { | ||
165 | intl: intlShape, | ||
166 | }; | ||
167 | |||
168 | render() { | ||
169 | const { | ||
170 | onSubmit, | ||
171 | isLoadingRequiredData, | ||
172 | isActivatingTrial, | ||
173 | trialActivationError, | ||
174 | canSkipTrial, | ||
175 | classes, | ||
176 | currency, | ||
177 | price, | ||
178 | name, | ||
179 | } = this.props; | ||
180 | const { intl } = this.context; | ||
181 | |||
182 | const [intPart, fractionPart] = (price).toString().split('.'); | ||
183 | |||
184 | return ( | ||
185 | <> | ||
186 | <div className={classnames('auth__container', classes.root, classes.container)}> | ||
187 | <form className="franz-form auth__form"> | ||
188 | {isLoadingRequiredData ? <Loader /> : ( | ||
189 | <img | ||
190 | src="./assets/images/sm.png" | ||
191 | className="auth__logo auth__logo--sm" | ||
192 | alt="" | ||
193 | /> | ||
194 | )} | ||
195 | <h1>{intl.formatMessage(messages.headline, { name })}</h1> | ||
196 | <div className="auth__letter"> | ||
197 | <p> | ||
198 | {intl.formatMessage(messages.specialTreat)} | ||
199 | <br /> | ||
200 | </p> | ||
201 | <p> | ||
202 | {intl.formatMessage(messages.tryPro)} | ||
203 | <br /> | ||
204 | </p> | ||
205 | <p> | ||
206 | {intl.formatMessage(messages.happyMessaging)} | ||
207 | </p> | ||
208 | <p> | ||
209 | <strong>Stefan Malzner</strong> | ||
210 | </p> | ||
211 | </div> | ||
212 | <div className={classes.priceContainer}> | ||
213 | <p className={classnames(classes.price, classes.regularPrice)}> | ||
214 | <span className={classes.figure}> | ||
215 | {currency} | ||
216 | {intPart} | ||
217 | </span> | ||
218 | <sup>{fractionPart}</sup> | ||
219 | </p> | ||
220 | <p className={classnames(classes.price, classes.trialPrice)}> | ||
221 | <span className={classes.figure}> | ||
222 | {currency} | ||
223 | 0 | ||
224 | </span> | ||
225 | <sup>00</sup> | ||
226 | </p> | ||
227 | </div> | ||
228 | <div className={classes.keyTerms}> | ||
229 | <H2> | ||
230 | {intl.formatMessage(messages.noStringsAttachedHeadline)} | ||
231 | </H2> | ||
232 | <ul className={classes.keyTermsList}> | ||
233 | <FeatureItem | ||
234 | icon="👉" | ||
235 | name={intl.formatMessage(messages.trialWorth, { | ||
236 | currency, | ||
237 | price, | ||
238 | })} | ||
239 | /> | ||
240 | <FeatureItem icon="👉" name={intl.formatMessage(messages.noCreditCard)} /> | ||
241 | <FeatureItem icon="👉" name={intl.formatMessage(messages.automaticTrialEnd)} /> | ||
242 | </ul> | ||
243 | </div> | ||
244 | {trialActivationError && ( | ||
245 | <p className={classes.error}>{intl.formatMessage(messages.activationError)}</p> | ||
246 | )} | ||
247 | <Button | ||
248 | label={intl.formatMessage(!canSkipTrial ? messages.ctaStart : messages.ctaAccept)} | ||
249 | className={classes.cta} | ||
250 | onClick={onSubmit} | ||
251 | busy={isActivatingTrial} | ||
252 | disabled={isLoadingRequiredData || isActivatingTrial} | ||
253 | /> | ||
254 | {canSkipTrial && ( | ||
255 | <p className={classes.skipLink}> | ||
256 | <a href="#/">{intl.formatMessage(messages.ctaSkip)}</a> | ||
257 | </p> | ||
258 | )} | ||
259 | </form> | ||
260 | </div> | ||
261 | <div className={classes.featureContainer}> | ||
262 | <H2> | ||
263 | {intl.formatMessage(messages.featuresHeadline)} | ||
264 | </H2> | ||
265 | <FeatureList /> | ||
266 | </div> | ||
267 | </> | ||
268 | ); | ||
269 | } | ||
270 | } | ||
diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js index a60270a6f..7e4d0e53e 100644 --- a/src/components/layout/AppLayout.js +++ b/src/components/layout/AppLayout.js | |||
@@ -19,10 +19,7 @@ import { isWindows } from '../../environment'; | |||
19 | import WorkspaceSwitchingIndicator from '../../features/workspaces/components/WorkspaceSwitchingIndicator'; | 19 | import WorkspaceSwitchingIndicator from '../../features/workspaces/components/WorkspaceSwitchingIndicator'; |
20 | import { workspaceStore } from '../../features/workspaces'; | 20 | import { workspaceStore } from '../../features/workspaces'; |
21 | import AppUpdateInfoBar from '../AppUpdateInfoBar'; | 21 | import AppUpdateInfoBar from '../AppUpdateInfoBar'; |
22 | import TrialActivationInfoBar from '../TrialActivationInfoBar'; | ||
23 | import Todos from '../../features/todos/containers/TodosScreen'; | 22 | import Todos from '../../features/todos/containers/TodosScreen'; |
24 | import PlanSelection from '../../features/planSelection/containers/PlanSelectionScreen'; | ||
25 | import TrialStatusBar from '../../features/trialStatusBar/containers/TrialStatusBarScreen'; | ||
26 | 23 | ||
27 | function createMarkup(HTMLString) { | 24 | function createMarkup(HTMLString) { |
28 | return { __html: HTMLString }; | 25 | return { __html: HTMLString }; |
@@ -79,7 +76,6 @@ class AppLayout extends Component { | |||
79 | areRequiredRequestsSuccessful: PropTypes.bool.isRequired, | 76 | areRequiredRequestsSuccessful: PropTypes.bool.isRequired, |
80 | retryRequiredRequests: PropTypes.func.isRequired, | 77 | retryRequiredRequests: PropTypes.func.isRequired, |
81 | areRequiredRequestsLoading: PropTypes.bool.isRequired, | 78 | areRequiredRequestsLoading: PropTypes.bool.isRequired, |
82 | hasActivatedTrial: PropTypes.bool.isRequired, | ||
83 | }; | 79 | }; |
84 | 80 | ||
85 | state = { | 81 | state = { |
@@ -115,7 +111,6 @@ class AppLayout extends Component { | |||
115 | areRequiredRequestsSuccessful, | 111 | areRequiredRequestsSuccessful, |
116 | retryRequiredRequests, | 112 | retryRequiredRequests, |
117 | areRequiredRequestsLoading, | 113 | areRequiredRequestsLoading, |
118 | hasActivatedTrial, | ||
119 | } = this.props; | 114 | } = this.props; |
120 | 115 | ||
121 | const { intl } = this.context; | 116 | const { intl } = this.context; |
@@ -148,9 +143,6 @@ class AppLayout extends Component { | |||
148 | /> | 143 | /> |
149 | </InfoBar> | 144 | </InfoBar> |
150 | ))} | 145 | ))} |
151 | {hasActivatedTrial && ( | ||
152 | <TrialActivationInfoBar /> | ||
153 | )} | ||
154 | {!areRequiredRequestsSuccessful && showRequiredRequestsError && ( | 146 | {!areRequiredRequestsSuccessful && showRequiredRequestsError && ( |
155 | <InfoBar | 147 | <InfoBar |
156 | type="danger" | 148 | type="danger" |
@@ -202,11 +194,9 @@ class AppLayout extends Component { | |||
202 | <PublishDebugInfo /> | 194 | <PublishDebugInfo /> |
203 | {services} | 195 | {services} |
204 | {children} | 196 | {children} |
205 | <TrialStatusBar /> | ||
206 | </div> | 197 | </div> |
207 | <Todos /> | 198 | <Todos /> |
208 | </div> | 199 | </div> |
209 | <PlanSelection /> | ||
210 | </div> | 200 | </div> |
211 | </ErrorBoundary> | 201 | </ErrorBoundary> |
212 | ); | 202 | ); |
diff --git a/src/components/services/content/ServiceRestricted.js b/src/components/services/content/ServiceRestricted.js deleted file mode 100644 index 4b8d926aa..000000000 --- a/src/components/services/content/ServiceRestricted.js +++ /dev/null | |||
@@ -1,78 +0,0 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | ||
4 | import { defineMessages, intlShape } from 'react-intl'; | ||
5 | |||
6 | import { serviceLimitStore } from '../../../features/serviceLimit'; | ||
7 | import Button from '../../ui/Button'; | ||
8 | import { RESTRICTION_TYPES } from '../../../models/Service'; | ||
9 | |||
10 | const messages = defineMessages({ | ||
11 | headlineServiceLimit: { | ||
12 | id: 'service.restrictedHandler.serviceLimit.headline', | ||
13 | defaultMessage: '!!!You have reached your service limit.', | ||
14 | }, | ||
15 | textServiceLimit: { | ||
16 | id: 'service.restrictedHandler.serviceLimit.text', | ||
17 | defaultMessage: '!!!Please upgrade your account to use more than {count} services.', | ||
18 | }, | ||
19 | headlineCustomUrl: { | ||
20 | id: 'service.restrictedHandler.customUrl.headline', | ||
21 | defaultMessage: '!!!Franz Professional Plan required', | ||
22 | }, | ||
23 | textCustomUrl: { | ||
24 | id: 'service.restrictedHandler.customUrl.text', | ||
25 | defaultMessage: '!!!Please upgrade to the Franz Professional plan to use custom urls & self hosted services.', | ||
26 | }, | ||
27 | action: { | ||
28 | id: 'service.restrictedHandler.action', | ||
29 | defaultMessage: '!!!Upgrade Account', | ||
30 | }, | ||
31 | }); | ||
32 | |||
33 | export default @observer class ServiceRestricted extends Component { | ||
34 | static propTypes = { | ||
35 | name: PropTypes.string.isRequired, | ||
36 | upgrade: PropTypes.func.isRequired, | ||
37 | type: PropTypes.number.isRequired, | ||
38 | }; | ||
39 | |||
40 | static contextTypes = { | ||
41 | intl: intlShape, | ||
42 | }; | ||
43 | |||
44 | countdownInterval = null; | ||
45 | |||
46 | countdownIntervalTimeout = 1000; | ||
47 | |||
48 | render() { | ||
49 | const { | ||
50 | name, | ||
51 | upgrade, | ||
52 | type, | ||
53 | } = this.props; | ||
54 | const { intl } = this.context; | ||
55 | |||
56 | return ( | ||
57 | <div className="services__info-layer"> | ||
58 | {type === RESTRICTION_TYPES.SERVICE_LIMIT && ( | ||
59 | <> | ||
60 | <h1>{intl.formatMessage(messages.headlineServiceLimit)}</h1> | ||
61 | <p>{intl.formatMessage(messages.textServiceLimit, { count: serviceLimitStore.serviceLimit })}</p> | ||
62 | </> | ||
63 | )} | ||
64 | {type === RESTRICTION_TYPES.CUSTOM_URL && ( | ||
65 | <> | ||
66 | <h1>{intl.formatMessage(messages.headlineCustomUrl)}</h1> | ||
67 | <p>{intl.formatMessage(messages.textCustomUrl)}</p> | ||
68 | </> | ||
69 | )} | ||
70 | <Button | ||
71 | label={intl.formatMessage(messages.action, { name })} | ||
72 | buttonType="inverted" | ||
73 | onClick={() => upgrade()} | ||
74 | /> | ||
75 | </div> | ||
76 | ); | ||
77 | } | ||
78 | } | ||
diff --git a/src/components/services/content/Services.js b/src/components/services/content/Services.js index caa3cf9aa..6e46a60d2 100644 --- a/src/components/services/content/Services.js +++ b/src/components/services/content/Services.js | |||
@@ -54,7 +54,6 @@ export default @injectSheet(styles) @inject('actions') @observer class Services | |||
54 | openSettings: PropTypes.func.isRequired, | 54 | openSettings: PropTypes.func.isRequired, |
55 | update: PropTypes.func.isRequired, | 55 | update: PropTypes.func.isRequired, |
56 | userHasCompletedSignup: PropTypes.bool.isRequired, | 56 | userHasCompletedSignup: PropTypes.bool.isRequired, |
57 | hasActivatedTrial: PropTypes.bool.isRequired, | ||
58 | classes: PropTypes.object.isRequired, | 57 | classes: PropTypes.object.isRequired, |
59 | actions: PropTypes.object.isRequired, | 58 | actions: PropTypes.object.isRequired, |
60 | isSpellcheckerEnabled: PropTypes.bool.isRequired, | 59 | isSpellcheckerEnabled: PropTypes.bool.isRequired, |
@@ -109,7 +108,6 @@ export default @injectSheet(styles) @inject('actions') @observer class Services | |||
109 | openSettings, | 108 | openSettings, |
110 | update, | 109 | update, |
111 | userHasCompletedSignup, | 110 | userHasCompletedSignup, |
112 | hasActivatedTrial, | ||
113 | classes, | 111 | classes, |
114 | isSpellcheckerEnabled, | 112 | isSpellcheckerEnabled, |
115 | } = this.props; | 113 | } = this.props; |
@@ -123,7 +121,7 @@ export default @injectSheet(styles) @inject('actions') @observer class Services | |||
123 | 121 | ||
124 | return ( | 122 | return ( |
125 | <div className="services"> | 123 | <div className="services"> |
126 | {(userHasCompletedSignup || hasActivatedTrial) && ( | 124 | {userHasCompletedSignup && ( |
127 | <div className={classes.confettiContainer}> | 125 | <div className={classes.confettiContainer}> |
128 | <Confetti | 126 | <Confetti |
129 | width={window.width} | 127 | width={window.width} |
@@ -186,7 +184,6 @@ export default @injectSheet(styles) @inject('actions') @observer class Services | |||
186 | }, | 184 | }, |
187 | redirect: false, | 185 | redirect: false, |
188 | })} | 186 | })} |
189 | upgrade={() => openSettings({ path: 'user' })} | ||
190 | isSpellcheckerEnabled={isSpellcheckerEnabled} | 187 | isSpellcheckerEnabled={isSpellcheckerEnabled} |
191 | /> | 188 | /> |
192 | ))} | 189 | ))} |
diff --git a/src/components/settings/account/AccountDashboard.js b/src/components/settings/account/AccountDashboard.js index 68d88e218..d3d75a979 100644 --- a/src/components/settings/account/AccountDashboard.js +++ b/src/components/settings/account/AccountDashboard.js | |||
@@ -3,14 +3,11 @@ 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, H1, H2 } from '@meetfranz/ui'; | 6 | import { H1, H2 } from '@meetfranz/ui'; |
7 | import moment from 'moment'; | ||
8 | 7 | ||
9 | import Loader from '../../ui/Loader'; | 8 | import Loader from '../../ui/Loader'; |
10 | import Button from '../../ui/Button'; | 9 | import Button from '../../ui/Button'; |
11 | import Infobox from '../../ui/Infobox'; | 10 | import Infobox from '../../ui/Infobox'; |
12 | import SubscriptionForm from '../../../containers/subscription/SubscriptionFormScreen'; | ||
13 | import { i18nPlanName } from '../../../helpers/plan-helpers'; | ||
14 | import { LOCAL_SERVER, LIVE_FRANZ_API } from '../../../config'; | 11 | import { LOCAL_SERVER, LIVE_FRANZ_API } from '../../../config'; |
15 | 12 | ||
16 | const messages = defineMessages({ | 13 | const messages = defineMessages({ |
@@ -30,18 +27,6 @@ const messages = defineMessages({ | |||
30 | id: 'settings.account.manageSubscription.label', | 27 | id: 'settings.account.manageSubscription.label', |
31 | defaultMessage: '!!!Manage your subscription', | 28 | defaultMessage: '!!!Manage your subscription', |
32 | }, | 29 | }, |
33 | upgradeAccountToPro: { | ||
34 | id: 'settings.account.upgradeToPro.label', | ||
35 | defaultMessage: '!!!Upgrade to Franz Professional', | ||
36 | }, | ||
37 | accountTypeBasic: { | ||
38 | id: 'settings.account.accountType.basic', | ||
39 | defaultMessage: '!!!Basic Account', | ||
40 | }, | ||
41 | accountTypePremium: { | ||
42 | id: 'settings.account.accountType.premium', | ||
43 | defaultMessage: '!!!Premium Supporter Account', | ||
44 | }, | ||
45 | accountEditButton: { | 30 | accountEditButton: { |
46 | id: 'settings.account.account.editButton', | 31 | id: 'settings.account.account.editButton', |
47 | defaultMessage: '!!!Edit Account', | 32 | defaultMessage: '!!!Edit Account', |
@@ -76,23 +61,10 @@ const messages = defineMessages({ | |||
76 | defaultMessage: | 61 | defaultMessage: |
77 | '!!!You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!', | 62 | '!!!You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!', |
78 | }, | 63 | }, |
79 | trial: { | ||
80 | id: 'settings.account.trial', | ||
81 | defaultMessage: '!!!Free Trial', | ||
82 | }, | ||
83 | yourLicense: { | 64 | yourLicense: { |
84 | id: 'settings.account.yourLicense', | 65 | id: 'settings.account.yourLicense', |
85 | defaultMessage: '!!!Your Franz License:', | 66 | defaultMessage: '!!!Your Franz License:', |
86 | }, | 67 | }, |
87 | trialEndsIn: { | ||
88 | id: 'settings.account.trialEndsIn', | ||
89 | defaultMessage: '!!!Your free trial ends in {duration}.', | ||
90 | }, | ||
91 | trialUpdateBillingInformation: { | ||
92 | id: 'settings.account.trialUpdateBillingInfo', | ||
93 | defaultMessage: | ||
94 | '!!!Please update your billing info to continue using {license} after your trial period.', | ||
95 | }, | ||
96 | accountUnavailable: { | 68 | accountUnavailable: { |
97 | id: 'settings.account.accountUnavailable', | 69 | id: 'settings.account.accountUnavailable', |
98 | defaultMessage: 'Account is unavailable', | 70 | defaultMessage: 'Account is unavailable', |
@@ -107,8 +79,6 @@ const messages = defineMessages({ | |||
107 | class AccountDashboard extends Component { | 79 | class AccountDashboard extends Component { |
108 | static propTypes = { | 80 | static propTypes = { |
109 | user: MobxPropTypes.observableObject.isRequired, | 81 | user: MobxPropTypes.observableObject.isRequired, |
110 | isPremiumOverrideUser: PropTypes.bool.isRequired, | ||
111 | isProUser: PropTypes.bool.isRequired, | ||
112 | isLoading: PropTypes.bool.isRequired, | 82 | isLoading: PropTypes.bool.isRequired, |
113 | userInfoRequestFailed: PropTypes.bool.isRequired, | 83 | userInfoRequestFailed: PropTypes.bool.isRequired, |
114 | retryUserInfoRequest: PropTypes.func.isRequired, | 84 | retryUserInfoRequest: PropTypes.func.isRequired, |
@@ -116,10 +86,7 @@ class AccountDashboard extends Component { | |||
116 | isLoadingDeleteAccount: PropTypes.bool.isRequired, | 86 | isLoadingDeleteAccount: PropTypes.bool.isRequired, |
117 | isDeleteAccountSuccessful: PropTypes.bool.isRequired, | 87 | isDeleteAccountSuccessful: PropTypes.bool.isRequired, |
118 | openEditAccount: PropTypes.func.isRequired, | 88 | openEditAccount: PropTypes.func.isRequired, |
119 | openBilling: PropTypes.func.isRequired, | ||
120 | upgradeToPro: PropTypes.func.isRequired, | ||
121 | openInvoices: PropTypes.func.isRequired, | 89 | openInvoices: PropTypes.func.isRequired, |
122 | onCloseSubscriptionWindow: PropTypes.func.isRequired, | ||
123 | server: PropTypes.string.isRequired, | 90 | server: PropTypes.string.isRequired, |
124 | }; | 91 | }; |
125 | 92 | ||
@@ -130,8 +97,6 @@ class AccountDashboard extends Component { | |||
130 | render() { | 97 | render() { |
131 | const { | 98 | const { |
132 | user, | 99 | user, |
133 | isPremiumOverrideUser, | ||
134 | isProUser, | ||
135 | isLoading, | 100 | isLoading, |
136 | userInfoRequestFailed, | 101 | userInfoRequestFailed, |
137 | retryUserInfoRequest, | 102 | retryUserInfoRequest, |
@@ -139,20 +104,11 @@ class AccountDashboard extends Component { | |||
139 | isLoadingDeleteAccount, | 104 | isLoadingDeleteAccount, |
140 | isDeleteAccountSuccessful, | 105 | isDeleteAccountSuccessful, |
141 | openEditAccount, | 106 | openEditAccount, |
142 | openBilling, | ||
143 | upgradeToPro, | ||
144 | openInvoices, | 107 | openInvoices, |
145 | onCloseSubscriptionWindow, | ||
146 | server, | 108 | server, |
147 | } = this.props; | 109 | } = this.props; |
148 | const { intl } = this.context; | 110 | const { intl } = this.context; |
149 | 111 | ||
150 | let planName = ''; | ||
151 | |||
152 | if (user.team && user.team.plan) { | ||
153 | planName = i18nPlanName(user.team.plan, intl); | ||
154 | } | ||
155 | |||
156 | const isUsingWithoutAccount = server === LOCAL_SERVER; | 112 | const isUsingWithoutAccount = server === LOCAL_SERVER; |
157 | const isUsingFranzServer = server === LIVE_FRANZ_API; | 113 | const isUsingFranzServer = server === LIVE_FRANZ_API; |
158 | 114 | ||
@@ -210,98 +166,40 @@ class AccountDashboard extends Component { | |||
210 | <div className="account__info"> | 166 | <div className="account__info"> |
211 | <H1> | 167 | <H1> |
212 | <span className="username">{`${user.firstname} ${user.lastname}`}</span> | 168 | <span className="username">{`${user.firstname} ${user.lastname}`}</span> |
213 | {user.isPremium && ( | ||
214 | <> | ||
215 | {' '} | ||
216 | <ProBadge /> | ||
217 | </> | ||
218 | )} | ||
219 | </H1> | 169 | </H1> |
220 | <p> | 170 | <p> |
221 | {user.organization && `${user.organization}, `} | 171 | {user.organization && `${user.organization}, `} |
222 | {user.email} | 172 | {user.email} |
223 | </p> | 173 | </p> |
224 | {user.isPremium && ( | 174 | <div className="manage-user-links"> |
225 | <div className="manage-user-links"> | 175 | <Button |
226 | <Button | 176 | label={intl.formatMessage( |
227 | label={intl.formatMessage( | 177 | messages.accountEditButton, |
228 | messages.accountEditButton, | 178 | )} |
229 | )} | 179 | className="franz-form__button--inverted" |
230 | className="franz-form__button--inverted" | 180 | onClick={openEditAccount} |
231 | onClick={openEditAccount} | 181 | /> |
232 | /> | 182 | </div> |
233 | </div> | ||
234 | )} | ||
235 | </div> | 183 | </div> |
236 | {!user.isPremium && ( | 184 | <Button |
237 | <Button | 185 | label={intl.formatMessage( |
238 | label={intl.formatMessage( | 186 | messages.accountEditButton, |
239 | messages.accountEditButton, | 187 | )} |
240 | )} | 188 | className="franz-form__button--inverted" |
241 | className="franz-form__button--inverted" | 189 | onClick={openEditAccount} |
242 | onClick={openEditAccount} | 190 | /> |
243 | /> | ||
244 | )} | ||
245 | </div> | 191 | </div> |
246 | </div> | 192 | </div> |
247 | {user.isPremium && user.isSubscriptionOwner && isUsingFranzServer && ( | 193 | {user.isSubscriptionOwner && isUsingFranzServer && ( |
248 | <div className="account"> | 194 | <div className="account"> |
249 | <div className="account__box"> | 195 | <div className="account__box"> |
250 | <H2>{intl.formatMessage(messages.yourLicense)}</H2> | 196 | <H2>{intl.formatMessage(messages.yourLicense)}</H2> |
251 | <p> | 197 | <p> |
252 | Franz | 198 | Franz |
253 | {' '} | ||
254 | {isPremiumOverrideUser ? 'Premium' : planName} | ||
255 | {user.team.isTrial && ( | ||
256 | <> | ||
257 | {' – '} | ||
258 | {intl.formatMessage(messages.trial)} | ||
259 | </> | ||
260 | )} | ||
261 | </p> | 199 | </p> |
262 | {user.team.isTrial && ( | ||
263 | <> | ||
264 | <br /> | ||
265 | <p> | ||
266 | {intl.formatMessage(messages.trialEndsIn, { | ||
267 | duration: moment | ||
268 | .duration( | ||
269 | moment().diff(user.team.trialEnd), | ||
270 | ) | ||
271 | .humanize(), | ||
272 | })} | ||
273 | </p> | ||
274 | <p> | ||
275 | {intl.formatMessage( | ||
276 | messages.trialUpdateBillingInformation, | ||
277 | { | ||
278 | license: planName, | ||
279 | }, | ||
280 | )} | ||
281 | </p> | ||
282 | </> | ||
283 | )} | ||
284 | {!isProUser && ( | ||
285 | <div className="manage-user-links"> | ||
286 | <Button | ||
287 | label={intl.formatMessage( | ||
288 | messages.upgradeAccountToPro, | ||
289 | )} | ||
290 | className="franz-form__button--primary" | ||
291 | onClick={upgradeToPro} | ||
292 | /> | ||
293 | </div> | ||
294 | )} | ||
295 | <div className="manage-user-links"> | 200 | <div className="manage-user-links"> |
296 | <Button | 201 | <Button |
297 | label={intl.formatMessage( | 202 | label={intl.formatMessage( |
298 | messages.manageSubscriptionButtonLabel, | ||
299 | )} | ||
300 | className="franz-form__button--inverted" | ||
301 | onClick={openBilling} | ||
302 | /> | ||
303 | <Button | ||
304 | label={intl.formatMessage( | ||
305 | messages.invoicesButton, | 203 | messages.invoicesButton, |
306 | )} | 204 | )} |
307 | className="franz-form__button--inverted" | 205 | className="franz-form__button--inverted" |
@@ -311,15 +209,6 @@ class AccountDashboard extends Component { | |||
311 | </div> | 209 | </div> |
312 | </div> | 210 | </div> |
313 | )} | 211 | )} |
314 | {!user.isPremium && ( | ||
315 | <div className="account franz-form"> | ||
316 | <div className="account__box"> | ||
317 | <SubscriptionForm | ||
318 | onCloseWindow={onCloseSubscriptionWindow} | ||
319 | /> | ||
320 | </div> | ||
321 | </div> | ||
322 | )} | ||
323 | </> | 212 | </> |
324 | )} | 213 | )} |
325 | 214 | ||
diff --git a/src/components/settings/navigation/SettingsNavigation.js b/src/components/settings/navigation/SettingsNavigation.js index cebab2f12..02cae6b69 100644 --- a/src/components/settings/navigation/SettingsNavigation.js +++ b/src/components/settings/navigation/SettingsNavigation.js | |||
@@ -2,7 +2,6 @@ import React, { Component } from 'react'; | |||
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { defineMessages, intlShape } from 'react-intl'; | 3 | import { defineMessages, intlShape } from 'react-intl'; |
4 | import { inject, observer } from 'mobx-react'; | 4 | import { inject, observer } from 'mobx-react'; |
5 | import { ProBadge } from '@meetfranz/ui'; | ||
6 | import { RouterStore } from 'mobx-react-router'; | 5 | import { RouterStore } from 'mobx-react-router'; |
7 | 6 | ||
8 | import { LOCAL_SERVER, LIVE_FERDI_API, LIVE_FRANZ_API } from '../../../config'; | 7 | import { LOCAL_SERVER, LIVE_FERDI_API, LIVE_FRANZ_API } from '../../../config'; |
@@ -11,7 +10,6 @@ import { workspaceStore } from '../../../features/workspaces'; | |||
11 | import UIStore from '../../../stores/UIStore'; | 10 | import UIStore from '../../../stores/UIStore'; |
12 | import SettingsStore from '../../../stores/SettingsStore'; | 11 | import SettingsStore from '../../../stores/SettingsStore'; |
13 | import UserStore from '../../../stores/UserStore'; | 12 | import UserStore from '../../../stores/UserStore'; |
14 | import { serviceLimitStore } from '../../../features/serviceLimit'; | ||
15 | 13 | ||
16 | const messages = defineMessages({ | 14 | const messages = defineMessages({ |
17 | availableServices: { | 15 | availableServices: { |
@@ -98,8 +96,6 @@ export default @inject('stores', 'actions') @observer class SettingsNavigation e | |||
98 | 96 | ||
99 | render() { | 97 | render() { |
100 | const { serviceCount, workspaceCount, stores } = this.props; | 98 | const { serviceCount, workspaceCount, stores } = this.props; |
101 | const { isDarkThemeActive } = stores.ui; | ||
102 | const { router, user } = stores; | ||
103 | const { intl } = this.context; | 99 | const { intl } = this.context; |
104 | const isLoggedIn = Boolean(localStorage.getItem('authToken')); | 100 | const isLoggedIn = Boolean(localStorage.getItem('authToken')); |
105 | const isUsingWithoutAccount = stores.settings.app.server === LOCAL_SERVER; | 101 | const isUsingWithoutAccount = stores.settings.app.server === LOCAL_SERVER; |
@@ -124,9 +120,6 @@ export default @inject('stores', 'actions') @observer class SettingsNavigation e | |||
124 | {' '} | 120 | {' '} |
125 | <span className="badge"> | 121 | <span className="badge"> |
126 | {serviceCount} | 122 | {serviceCount} |
127 | {serviceLimitStore.serviceLimit !== 0 && ( | ||
128 | `/${serviceLimitStore.serviceLimit}` | ||
129 | )} | ||
130 | </span> | 123 | </span> |
131 | </Link> | 124 | </Link> |
132 | {workspaceStore.isFeatureEnabled ? ( | 125 | {workspaceStore.isFeatureEnabled ? ( |
@@ -138,11 +131,7 @@ export default @inject('stores', 'actions') @observer class SettingsNavigation e | |||
138 | > | 131 | > |
139 | {intl.formatMessage(messages.yourWorkspaces)} | 132 | {intl.formatMessage(messages.yourWorkspaces)} |
140 | {' '} | 133 | {' '} |
141 | {workspaceStore.isPremiumUpgradeRequired ? ( | 134 | <span className="badge">{workspaceCount}</span> |
142 | <ProBadge inverted={!isDarkThemeActive && workspaceStore.isSettingsRouteActive} /> | ||
143 | ) : ( | ||
144 | <span className="badge">{workspaceCount}</span> | ||
145 | )} | ||
146 | </Link> | 135 | </Link> |
147 | ) : null} | 136 | ) : null} |
148 | {!isUsingWithoutAccount && ( | 137 | {!isUsingWithoutAccount && ( |
@@ -163,9 +152,6 @@ export default @inject('stores', 'actions') @observer class SettingsNavigation e | |||
163 | disabled={!isLoggedIn} | 152 | disabled={!isLoggedIn} |
164 | > | 153 | > |
165 | {intl.formatMessage(messages.team)} | 154 | {intl.formatMessage(messages.team)} |
166 | {!user.data.isPremium && ( | ||
167 | <ProBadge inverted={!isDarkThemeActive && router.location.pathname === '/settings/team'} /> | ||
168 | )} | ||
169 | </Link> | 155 | </Link> |
170 | )} | 156 | )} |
171 | <Link | 157 | <Link |
diff --git a/src/components/settings/recipes/RecipesDashboard.js b/src/components/settings/recipes/RecipesDashboard.js index b4e2fc05c..e82324cff 100644 --- a/src/components/settings/recipes/RecipesDashboard.js +++ b/src/components/settings/recipes/RecipesDashboard.js | |||
@@ -13,8 +13,6 @@ import RecipeItem from './RecipeItem'; | |||
13 | import Loader from '../../ui/Loader'; | 13 | import Loader from '../../ui/Loader'; |
14 | import Appear from '../../ui/effects/Appear'; | 14 | import Appear from '../../ui/effects/Appear'; |
15 | 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'; | ||
18 | import RecipePreview from '../../../models/RecipePreview'; | 16 | import RecipePreview from '../../../models/RecipePreview'; |
19 | 17 | ||
20 | const messages = defineMessages({ | 18 | const messages = defineMessages({ |
@@ -120,7 +118,6 @@ export default @injectSheet(styles) @observer class RecipesDashboard extends Com | |||
120 | openRecipeDirectory: PropTypes.func.isRequired, | 118 | openRecipeDirectory: PropTypes.func.isRequired, |
121 | openDevDocs: PropTypes.func.isRequired, | 119 | openDevDocs: PropTypes.func.isRequired, |
122 | classes: PropTypes.object.isRequired, | 120 | classes: PropTypes.object.isRequired, |
123 | isCommunityRecipesIncludedInCurrentPlan: PropTypes.bool.isRequired, | ||
124 | }; | 121 | }; |
125 | 122 | ||
126 | static defaultProps = { | 123 | static defaultProps = { |
@@ -148,7 +145,6 @@ export default @injectSheet(styles) @observer class RecipesDashboard extends Com | |||
148 | openRecipeDirectory, | 145 | openRecipeDirectory, |
149 | openDevDocs, | 146 | openDevDocs, |
150 | classes, | 147 | classes, |
151 | isCommunityRecipesIncludedInCurrentPlan, | ||
152 | } = this.props; | 148 | } = this.props; |
153 | const { intl } = this.context; | 149 | const { intl } = this.context; |
154 | 150 | ||
@@ -163,7 +159,6 @@ export default @injectSheet(styles) @observer class RecipesDashboard extends Com | |||
163 | <div className="settings__header"> | 159 | <div className="settings__header"> |
164 | <h1>{intl.formatMessage(messages.headline)}</h1> | 160 | <h1>{intl.formatMessage(messages.headline)}</h1> |
165 | </div> | 161 | </div> |
166 | <LimitReachedInfobox /> | ||
167 | <div className="settings__body recipes"> | 162 | <div className="settings__body recipes"> |
168 | {serviceStatus.length > 0 && serviceStatus.includes('created') && ( | 163 | {serviceStatus.length > 0 && serviceStatus.includes('created') && ( |
169 | <Appear> | 164 | <Appear> |
@@ -223,9 +218,6 @@ export default @injectSheet(styles) @observer class RecipesDashboard extends Com | |||
223 | <> | 218 | <> |
224 | <H2> | 219 | <H2> |
225 | {intl.formatMessage(messages.headlineCustomRecipes)} | 220 | {intl.formatMessage(messages.headlineCustomRecipes)} |
226 | {!isCommunityRecipesIncludedInCurrentPlan && ( | ||
227 | <ProBadge className={classes.proBadge} /> | ||
228 | )} | ||
229 | </H2> | 221 | </H2> |
230 | <div className={classes.devRecipeIntroContainer}> | 222 | <div className={classes.devRecipeIntroContainer}> |
231 | <p> | 223 | <p> |
@@ -251,37 +243,33 @@ export default @injectSheet(styles) @observer class RecipesDashboard extends Com | |||
251 | </div> | 243 | </div> |
252 | </> | 244 | </> |
253 | )} | 245 | )} |
254 | <PremiumFeatureContainer | 246 | {recipeFilter === 'dev' && communityRecipes.length > 0 && ( |
255 | condition={(recipeFilter === 'dev' && communityRecipes.length > 0) && !isCommunityRecipesIncludedInCurrentPlan} | 247 | <H3>{intl.formatMessage(messages.headlineCommunityRecipes)}</H3> |
256 | > | 248 | )} |
257 | {recipeFilter === 'dev' && communityRecipes.length > 0 && ( | 249 | <div className="recipes__list"> |
258 | <H3>{intl.formatMessage(messages.headlineCommunityRecipes)}</H3> | 250 | {hasLoadedRecipes && recipes.length === 0 && recipeFilter !== 'dev' && ( |
259 | )} | 251 | <div className="align-middle settings__empty-state"> |
260 | <div className="recipes__list"> | 252 | <span className="emoji"> |
261 | {hasLoadedRecipes && recipes.length === 0 && recipeFilter !== 'dev' && ( | 253 | <img src="./assets/images/emoji/dontknow.png" alt="" /> |
262 | <div className="align-middle settings__empty-state"> | 254 | </span> |
263 | <span className="emoji"> | ||
264 | <img src="./assets/images/emoji/dontknow.png" alt="" /> | ||
265 | </span> | ||
266 | 255 | ||
267 | <p className="settings__empty-state-text">{intl.formatMessage(messages.nothingFound)}</p> | 256 | <p className="settings__empty-state-text">{intl.formatMessage(messages.nothingFound)}</p> |
268 | 257 | ||
269 | <RecipeItem | ||
270 | key={customWebsiteRecipe.id} | ||
271 | recipe={customWebsiteRecipe} | ||
272 | onClick={() => isLoggedIn && showAddServiceInterface({ recipeId: customWebsiteRecipe.id })} | ||
273 | /> | ||
274 | </div> | ||
275 | )} | ||
276 | {communityRecipes.map(recipe => ( | ||
277 | <RecipeItem | 258 | <RecipeItem |
278 | key={recipe.id} | 259 | key={customWebsiteRecipe.id} |
279 | recipe={recipe} | 260 | recipe={customWebsiteRecipe} |
280 | onClick={() => isLoggedIn && showAddServiceInterface({ recipeId: recipe.id })} | 261 | onClick={() => isLoggedIn && showAddServiceInterface({ recipeId: customWebsiteRecipe.id })} |
281 | /> | 262 | /> |
282 | ))} | 263 | </div> |
283 | </div> | 264 | )} |
284 | </PremiumFeatureContainer> | 265 | {communityRecipes.map(recipe => ( |
266 | <RecipeItem | ||
267 | key={recipe.id} | ||
268 | recipe={recipe} | ||
269 | onClick={() => isLoggedIn && showAddServiceInterface({ recipeId: recipe.id })} | ||
270 | /> | ||
271 | ))} | ||
272 | </div> | ||
285 | {recipeFilter === 'dev' && devRecipes.length > 0 && ( | 273 | {recipeFilter === 'dev' && devRecipes.length > 0 && ( |
286 | <div className={classes.devRecipeList}> | 274 | <div className={classes.devRecipeList}> |
287 | <H3>{intl.formatMessage(messages.headlineDevRecipes)}</H3> | 275 | <H3>{intl.formatMessage(messages.headlineDevRecipes)}</H3> |
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js index 513c75eed..0f7c29de5 100644 --- a/src/components/settings/services/EditServiceForm.js +++ b/src/components/settings/services/EditServiceForm.js | |||
@@ -6,7 +6,6 @@ import { defineMessages, intlShape } from 'react-intl'; | |||
6 | import normalizeUrl from 'normalize-url'; | 6 | import normalizeUrl from 'normalize-url'; |
7 | 7 | ||
8 | import Form from '../../../lib/Form'; | 8 | import Form from '../../../lib/Form'; |
9 | import User from '../../../models/User'; | ||
10 | import Recipe from '../../../models/Recipe'; | 9 | import Recipe from '../../../models/Recipe'; |
11 | import Service from '../../../models/Service'; | 10 | import Service from '../../../models/Service'; |
12 | import Tabs, { TabItem } from '../../ui/Tabs'; | 11 | import Tabs, { TabItem } from '../../ui/Tabs'; |
@@ -17,9 +16,6 @@ import Button from '../../ui/Button'; | |||
17 | import ImageUpload from '../../ui/ImageUpload'; | 16 | import ImageUpload from '../../ui/ImageUpload'; |
18 | import Select from '../../ui/Select'; | 17 | import Select from '../../ui/Select'; |
19 | 18 | ||
20 | import PremiumFeatureContainer from '../../ui/PremiumFeatureContainer'; | ||
21 | import LimitReachedInfobox from '../../../features/serviceLimit/components/LimitReachedInfobox'; | ||
22 | import { serviceLimitStore } from '../../../features/serviceLimit'; | ||
23 | import { isMac } from '../../../environment'; | 19 | import { isMac } from '../../../environment'; |
24 | import globalMessages from '../../../i18n/globalMessages'; | 20 | import globalMessages from '../../../i18n/globalMessages'; |
25 | 21 | ||
@@ -80,14 +76,6 @@ const messages = defineMessages({ | |||
80 | id: 'settings.service.form.customUrlValidationError', | 76 | id: 'settings.service.form.customUrlValidationError', |
81 | defaultMessage: '!!!Could not validate custom {name} server.', | 77 | defaultMessage: '!!!Could not validate custom {name} server.', |
82 | }, | 78 | }, |
83 | customUrlPremiumInfo: { | ||
84 | id: 'settings.service.form.customUrlPremiumInfo', | ||
85 | defaultMessage: '!!!To add self hosted services, you need a Ferdi Premium Supporter Account.', | ||
86 | }, | ||
87 | customUrlUpgradeAccount: { | ||
88 | id: 'settings.service.form.customUrlUpgradeAccount', | ||
89 | defaultMessage: '!!!Upgrade your account', | ||
90 | }, | ||
91 | indirectMessageInfo: { | 79 | indirectMessageInfo: { |
92 | id: 'settings.service.form.indirectMessageInfo', | 80 | id: 'settings.service.form.indirectMessageInfo', |
93 | defaultMessage: '!!!You will be notified about all new messages in a channel, not just @username, @channel, @here, ...', | 81 | defaultMessage: '!!!You will be notified about all new messages in a channel, not just @username, @channel, @here, ...', |
@@ -149,7 +137,6 @@ export default @observer class EditServiceForm extends Component { | |||
149 | 137 | ||
150 | return null; | 138 | return null; |
151 | }, | 139 | }, |
152 | user: PropTypes.instanceOf(User).isRequired, | ||
153 | action: PropTypes.string.isRequired, | 140 | action: PropTypes.string.isRequired, |
154 | form: PropTypes.instanceOf(Form).isRequired, | 141 | form: PropTypes.instanceOf(Form).isRequired, |
155 | onSubmit: PropTypes.func.isRequired, | 142 | onSubmit: PropTypes.func.isRequired, |
@@ -158,8 +145,6 @@ export default @observer class EditServiceForm extends Component { | |||
158 | isSaving: PropTypes.bool.isRequired, | 145 | isSaving: PropTypes.bool.isRequired, |
159 | isDeleting: PropTypes.bool.isRequired, | 146 | isDeleting: PropTypes.bool.isRequired, |
160 | isProxyFeatureEnabled: PropTypes.bool.isRequired, | 147 | isProxyFeatureEnabled: PropTypes.bool.isRequired, |
161 | isServiceProxyIncludedInCurrentPlan: PropTypes.bool.isRequired, | ||
162 | isSpellcheckerIncludedInCurrentPlan: PropTypes.bool.isRequired, | ||
163 | isHibernationFeatureActive: PropTypes.bool.isRequired, | 148 | isHibernationFeatureActive: PropTypes.bool.isRequired, |
164 | }; | 149 | }; |
165 | 150 | ||
@@ -217,15 +202,12 @@ export default @observer class EditServiceForm extends Component { | |||
217 | recipe, | 202 | recipe, |
218 | service, | 203 | service, |
219 | action, | 204 | action, |
220 | user, | ||
221 | form, | 205 | form, |
222 | isSaving, | 206 | isSaving, |
223 | isDeleting, | 207 | isDeleting, |
224 | onDelete, | 208 | onDelete, |
225 | openRecipeFile, | 209 | openRecipeFile, |
226 | isProxyFeatureEnabled, | 210 | isProxyFeatureEnabled, |
227 | isServiceProxyIncludedInCurrentPlan, | ||
228 | isSpellcheckerIncludedInCurrentPlan, | ||
229 | isHibernationFeatureActive, | 211 | isHibernationFeatureActive, |
230 | } = this.props; | 212 | } = this.props; |
231 | const { intl } = this.context; | 213 | const { intl } = this.context; |
@@ -285,7 +267,6 @@ export default @observer class EditServiceForm extends Component { | |||
285 | )} | 267 | )} |
286 | </span> | 268 | </span> |
287 | </div> | 269 | </div> |
288 | <LimitReachedInfobox /> | ||
289 | <div className="settings__body"> | 270 | <div className="settings__body"> |
290 | <form onSubmit={e => this.submit(e)} id="form"> | 271 | <form onSubmit={e => this.submit(e)} id="form"> |
291 | <div className="service-name"> | 272 | <div className="service-name"> |
@@ -311,24 +292,11 @@ export default @observer class EditServiceForm extends Component { | |||
311 | )} | 292 | )} |
312 | {recipe.hasCustomUrl && ( | 293 | {recipe.hasCustomUrl && ( |
313 | <TabItem title={intl.formatMessage(messages.tabOnPremise)}> | 294 | <TabItem title={intl.formatMessage(messages.tabOnPremise)}> |
314 | {user.isPremium || recipe.author.find(a => a.email === user.email) ? ( | 295 | <Input field={form.$('customUrl')} /> |
315 | <> | 296 | {form.error === 'url-validation-error' && ( |
316 | <Input field={form.$('customUrl')} /> | 297 | <p className="franz-form__error"> |
317 | {form.error === 'url-validation-error' && ( | 298 | {intl.formatMessage(messages.customUrlValidationError, { name: recipe.name })} |
318 | <p className="franz-form__error"> | 299 | </p> |
319 | {intl.formatMessage(messages.customUrlValidationError, { name: recipe.name })} | ||
320 | </p> | ||
321 | )} | ||
322 | </> | ||
323 | ) : ( | ||
324 | <div className="center premium-info"> | ||
325 | <p>{intl.formatMessage(messages.customUrlPremiumInfo)}</p> | ||
326 | <p> | ||
327 | <Link to="/settings/user" className="button"> | ||
328 | {intl.formatMessage(messages.customUrlUpgradeAccount)} | ||
329 | </Link> | ||
330 | </p> | ||
331 | </div> | ||
332 | )} | 300 | )} |
333 | </TabItem> | 301 | </TabItem> |
334 | )} | 302 | )} |
@@ -403,56 +371,46 @@ export default @observer class EditServiceForm extends Component { | |||
403 | </div> | 371 | </div> |
404 | 372 | ||
405 | {!isMac && ( | 373 | {!isMac && ( |
406 | <PremiumFeatureContainer | 374 | <div className="settings__settings-group"> |
407 | condition={!isSpellcheckerIncludedInCurrentPlan} | 375 | <Select field={form.$('spellcheckerLanguage')} /> |
408 | gaEventInfo={{ category: 'User', event: 'upgrade', label: 'spellchecker' }} | 376 | </div> |
409 | > | ||
410 | <div className="settings__settings-group"> | ||
411 | <Select field={form.$('spellcheckerLanguage')} /> | ||
412 | </div> | ||
413 | </PremiumFeatureContainer> | ||
414 | )} | 377 | )} |
415 | 378 | ||
416 | {isProxyFeatureEnabled && ( | 379 | {isProxyFeatureEnabled && ( |
417 | <PremiumFeatureContainer | 380 | <div className="settings__settings-group"> |
418 | condition={!isServiceProxyIncludedInCurrentPlan} | 381 | <h3> |
419 | gaEventInfo={{ category: 'User', event: 'upgrade', label: 'proxy' }} | 382 | {intl.formatMessage(messages.headlineProxy)} |
420 | > | 383 | <span className="badge badge--success">beta</span> |
421 | <div className="settings__settings-group"> | 384 | </h3> |
422 | <h3> | 385 | <Toggle field={form.$('proxy.isEnabled')} /> |
423 | {intl.formatMessage(messages.headlineProxy)} | 386 | {form.$('proxy.isEnabled').value && ( |
424 | <span className="badge badge--success">beta</span> | 387 | <> |
425 | </h3> | 388 | <div className="grid"> |
426 | <Toggle field={form.$('proxy.isEnabled')} /> | 389 | <div className="grid__row"> |
427 | {form.$('proxy.isEnabled').value && ( | 390 | <Input field={form.$('proxy.host')} className="proxyHost" /> |
428 | <> | 391 | <Input field={form.$('proxy.port')} /> |
429 | <div className="grid"> | ||
430 | <div className="grid__row"> | ||
431 | <Input field={form.$('proxy.host')} className="proxyHost" /> | ||
432 | <Input field={form.$('proxy.port')} /> | ||
433 | </div> | ||
434 | </div> | 392 | </div> |
435 | <div className="grid"> | 393 | </div> |
436 | <div className="grid__row"> | 394 | <div className="grid"> |
437 | <Input field={form.$('proxy.user')} /> | 395 | <div className="grid__row"> |
438 | <Input | 396 | <Input field={form.$('proxy.user')} /> |
439 | field={form.$('proxy.password')} | 397 | <Input |
440 | showPasswordToggle | 398 | field={form.$('proxy.password')} |
441 | /> | 399 | showPasswordToggle |
442 | </div> | 400 | /> |
443 | </div> | 401 | </div> |
444 | <p> | 402 | </div> |
445 | <span className="mdi mdi-information" /> | 403 | <p> |
446 | {intl.formatMessage(messages.proxyRestartInfo)} | 404 | <span className="mdi mdi-information" /> |
447 | </p> | 405 | {intl.formatMessage(messages.proxyRestartInfo)} |
448 | <p> | 406 | </p> |
449 | <span className="mdi mdi-information" /> | 407 | <p> |
450 | {intl.formatMessage(messages.proxyInfo)} | 408 | <span className="mdi mdi-information" /> |
451 | </p> | 409 | {intl.formatMessage(messages.proxyInfo)} |
452 | </> | 410 | </p> |
453 | )} | 411 | </> |
454 | </div> | 412 | )} |
455 | </PremiumFeatureContainer> | 413 | </div> |
456 | )} | 414 | )} |
457 | 415 | ||
458 | <div className="user-agent"> | 416 | <div className="user-agent"> |
@@ -512,7 +470,7 @@ export default @observer class EditServiceForm extends Component { | |||
512 | type="submit" | 470 | type="submit" |
513 | label={intl.formatMessage(messages.saveService)} | 471 | label={intl.formatMessage(messages.saveService)} |
514 | htmlForm="form" | 472 | htmlForm="form" |
515 | disabled={action !== 'edit' && ((form.isPristine && requiresUserInput) || serviceLimitStore.userHasReachedServiceLimit)} | 473 | disabled={action !== 'edit' && (form.isPristine && requiresUserInput)} |
516 | /> | 474 | /> |
517 | )} | 475 | )} |
518 | </div> | 476 | </div> |
diff --git a/src/components/settings/services/ServicesDashboard.js b/src/components/settings/services/ServicesDashboard.js index a0f05fd20..a05af5da0 100644 --- a/src/components/settings/services/ServicesDashboard.js +++ b/src/components/settings/services/ServicesDashboard.js | |||
@@ -10,7 +10,6 @@ import Loader from '../../ui/Loader'; | |||
10 | import FAB from '../../ui/FAB'; | 10 | import FAB from '../../ui/FAB'; |
11 | import ServiceItem from './ServiceItem'; | 11 | import ServiceItem from './ServiceItem'; |
12 | import Appear from '../../ui/effects/Appear'; | 12 | import Appear from '../../ui/effects/Appear'; |
13 | import LimitReachedInfobox from '../../../features/serviceLimit/components/LimitReachedInfobox'; | ||
14 | 13 | ||
15 | const messages = defineMessages({ | 14 | const messages = defineMessages({ |
16 | headline: { | 15 | headline: { |
@@ -93,7 +92,6 @@ export default @observer class ServicesDashboard extends Component { | |||
93 | <div className="settings__header"> | 92 | <div className="settings__header"> |
94 | <h1>{intl.formatMessage(messages.headline)}</h1> | 93 | <h1>{intl.formatMessage(messages.headline)}</h1> |
95 | </div> | 94 | </div> |
96 | <LimitReachedInfobox /> | ||
97 | <div className="settings__body"> | 95 | <div className="settings__body"> |
98 | {(services.length !== 0 || searchNeedle) && !isLoading && ( | 96 | {(services.length !== 0 || searchNeedle) && !isLoading && ( |
99 | <SearchInput | 97 | <SearchInput |
diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js index 52b26d65b..a8ba8748d 100644 --- a/src/components/settings/settings/EditSettingsForm.js +++ b/src/components/settings/settings/EditSettingsForm.js | |||
@@ -10,7 +10,6 @@ import Button from '../../ui/Button'; | |||
10 | import Toggle from '../../ui/Toggle'; | 10 | import Toggle from '../../ui/Toggle'; |
11 | import ToggleRaw from '../../ui/ToggleRaw'; | 11 | import ToggleRaw from '../../ui/ToggleRaw'; |
12 | import Select from '../../ui/Select'; | 12 | import Select from '../../ui/Select'; |
13 | import PremiumFeatureContainer from '../../ui/PremiumFeatureContainer'; | ||
14 | import Input from '../../ui/Input'; | 13 | import Input from '../../ui/Input'; |
15 | 14 | ||
16 | import { | 15 | import { |
@@ -168,7 +167,6 @@ export default @observer class EditSettingsForm extends Component { | |||
168 | isClearingAllCache: PropTypes.bool.isRequired, | 167 | isClearingAllCache: PropTypes.bool.isRequired, |
169 | onClearAllCache: PropTypes.func.isRequired, | 168 | onClearAllCache: PropTypes.func.isRequired, |
170 | getCacheSize: PropTypes.func.isRequired, | 169 | getCacheSize: PropTypes.func.isRequired, |
171 | isSpellcheckerIncludedInCurrentPlan: PropTypes.bool.isRequired, | ||
172 | isTodosEnabled: PropTypes.bool.isRequired, | 170 | isTodosEnabled: PropTypes.bool.isRequired, |
173 | isTodosActivated: PropTypes.bool.isRequired, | 171 | isTodosActivated: PropTypes.bool.isRequired, |
174 | isWorkspaceEnabled: PropTypes.bool.isRequired, | 172 | isWorkspaceEnabled: PropTypes.bool.isRequired, |
@@ -224,7 +222,6 @@ export default @observer class EditSettingsForm extends Component { | |||
224 | isClearingAllCache, | 222 | isClearingAllCache, |
225 | onClearAllCache, | 223 | onClearAllCache, |
226 | getCacheSize, | 224 | getCacheSize, |
227 | isSpellcheckerIncludedInCurrentPlan, | ||
228 | isTodosEnabled, | 225 | isTodosEnabled, |
229 | isWorkspaceEnabled, | 226 | isWorkspaceEnabled, |
230 | automaticUpdates, | 227 | automaticUpdates, |
@@ -564,22 +561,16 @@ export default @observer class EditSettingsForm extends Component { | |||
564 | 561 | ||
565 | <Hr /> | 562 | <Hr /> |
566 | 563 | ||
567 | <PremiumFeatureContainer | 564 | <Toggle |
568 | condition={!isSpellcheckerIncludedInCurrentPlan} | 565 | field={form.$('enableSpellchecking')} |
569 | gaEventInfo={{ category: 'User', event: 'upgrade', label: 'spellchecker' }} | 566 | /> |
570 | > | 567 | {!isMac && form.$('enableSpellchecking').value && ( |
571 | <> | 568 | <Select field={form.$('spellcheckerLanguage')} /> |
572 | <Toggle | 569 | )} |
573 | field={form.$('enableSpellchecking')} | 570 | {isMac && form.$('enableSpellchecking').value && ( |
574 | /> | 571 | <p>{intl.formatMessage(messages.spellCheckerLanguageInfo)}</p> |
575 | {!isMac && form.$('enableSpellchecking').value && ( | 572 | )} |
576 | <Select field={form.$('spellcheckerLanguage')} /> | 573 | |
577 | )} | ||
578 | {isMac && form.$('enableSpellchecking').value && ( | ||
579 | <p>{intl.formatMessage(messages.spellCheckerLanguageInfo)}</p> | ||
580 | )} | ||
581 | </> | ||
582 | </PremiumFeatureContainer> | ||
583 | <a | 574 | <a |
584 | href={FRANZ_TRANSLATION} | 575 | href={FRANZ_TRANSLATION} |
585 | target="_blank" | 576 | target="_blank" |
diff --git a/src/components/settings/team/TeamDashboard.js b/src/components/settings/team/TeamDashboard.js index 602d6e490..437225058 100644 --- a/src/components/settings/team/TeamDashboard.js +++ b/src/components/settings/team/TeamDashboard.js | |||
@@ -6,12 +6,9 @@ import ReactTooltip from 'react-tooltip'; | |||
6 | import injectSheet from 'react-jss'; | 6 | import injectSheet from 'react-jss'; |
7 | import classnames from 'classnames'; | 7 | import classnames from 'classnames'; |
8 | 8 | ||
9 | import { Badge } from '@meetfranz/ui'; | ||
10 | import Loader from '../../ui/Loader'; | 9 | import Loader from '../../ui/Loader'; |
11 | import Button from '../../ui/Button'; | 10 | import Button from '../../ui/Button'; |
12 | import Infobox from '../../ui/Infobox'; | 11 | import Infobox from '../../ui/Infobox'; |
13 | import globalMessages from '../../../i18n/globalMessages'; | ||
14 | import UpgradeButton from '../../ui/UpgradeButton'; | ||
15 | import { LIVE_FRANZ_API } from '../../../config'; | 12 | import { LIVE_FRANZ_API } from '../../../config'; |
16 | 13 | ||
17 | const messages = defineMessages({ | 14 | const messages = defineMessages({ |
@@ -35,10 +32,6 @@ const messages = defineMessages({ | |||
35 | id: 'settings.team.manageAction', | 32 | id: 'settings.team.manageAction', |
36 | defaultMessage: '!!!Manage your Team on meetfranz.com', | 33 | defaultMessage: '!!!Manage your Team on meetfranz.com', |
37 | }, | 34 | }, |
38 | upgradeButton: { | ||
39 | id: 'settings.team.upgradeAction', | ||
40 | defaultMessage: '!!!Upgrade your Account', | ||
41 | }, | ||
42 | teamsUnavailable: { | 35 | teamsUnavailable: { |
43 | id: 'settings.team.teamsUnavailable', | 36 | id: 'settings.team.teamsUnavailable', |
44 | defaultMessage: '!!!Teams are unavailable', | 37 | defaultMessage: '!!!Teams are unavailable', |
@@ -88,10 +81,6 @@ const styles = { | |||
88 | headlineWithSpacing: { | 81 | headlineWithSpacing: { |
89 | marginBottom: 'inherit', | 82 | marginBottom: 'inherit', |
90 | }, | 83 | }, |
91 | proRequired: { | ||
92 | margin: [10, 0, 40], | ||
93 | height: 'auto', | ||
94 | }, | ||
95 | buttonContainer: { | 84 | buttonContainer: { |
96 | display: 'flex', | 85 | display: 'flex', |
97 | height: 'auto', | 86 | height: 'auto', |
@@ -105,7 +94,6 @@ export default @injectSheet(styles) @observer class TeamDashboard extends Compon | |||
105 | retryUserInfoRequest: PropTypes.func.isRequired, | 94 | retryUserInfoRequest: PropTypes.func.isRequired, |
106 | openTeamManagement: PropTypes.func.isRequired, | 95 | openTeamManagement: PropTypes.func.isRequired, |
107 | classes: PropTypes.object.isRequired, | 96 | classes: PropTypes.object.isRequired, |
108 | isProUser: PropTypes.bool.isRequired, | ||
109 | server: PropTypes.string.isRequired, | 97 | server: PropTypes.string.isRequired, |
110 | }; | 98 | }; |
111 | 99 | ||
@@ -119,7 +107,6 @@ export default @injectSheet(styles) @observer class TeamDashboard extends Compon | |||
119 | userInfoRequestFailed, | 107 | userInfoRequestFailed, |
120 | retryUserInfoRequest, | 108 | retryUserInfoRequest, |
121 | openTeamManagement, | 109 | openTeamManagement, |
122 | isProUser, | ||
123 | classes, | 110 | classes, |
124 | server, | 111 | server, |
125 | } = this.props; | 112 | } = this.props; |
@@ -157,37 +144,25 @@ export default @injectSheet(styles) @observer class TeamDashboard extends Compon | |||
157 | <> | 144 | <> |
158 | <h1 className={classnames({ | 145 | <h1 className={classnames({ |
159 | [classes.headline]: true, | 146 | [classes.headline]: true, |
160 | [classes.headlineWithSpacing]: isProUser, | 147 | [classes.headlineWithSpacing]: true, |
161 | })} | 148 | })} |
162 | > | 149 | > |
163 | {intl.formatMessage(messages.contentHeadline)} | 150 | {intl.formatMessage(messages.contentHeadline)} |
164 | 151 | ||
165 | </h1> | 152 | </h1> |
166 | {!isProUser && ( | ||
167 | <Badge className={classes.proRequired}>{intl.formatMessage(globalMessages.proRequired)}</Badge> | ||
168 | )} | ||
169 | <div className={classes.container}> | 153 | <div className={classes.container}> |
170 | <div className={classes.content}> | 154 | <div className={classes.content}> |
171 | <p>{intl.formatMessage(messages.intro)}</p> | 155 | <p>{intl.formatMessage(messages.intro)}</p> |
172 | <p>{intl.formatMessage(messages.copy)}</p> | 156 | <p>{intl.formatMessage(messages.copy)}</p> |
173 | </div> | 157 | </div> |
174 | <img className={classes.image} src="https://cdn.franzinfra.com/announcements/assets/teams.png" alt="Franz for Teams" /> | 158 | <img className={classes.image} src="https://cdn.franzinfra.com/announcements/assets/teams.png" alt="Ferdi for Teams" /> |
175 | </div> | 159 | </div> |
176 | <div className={classes.buttonContainer}> | 160 | <div className={classes.buttonContainer}> |
177 | {!isProUser ? ( | 161 | <Button |
178 | <UpgradeButton | 162 | label={intl.formatMessage(messages.manageButton)} |
179 | className={classes.cta} | 163 | onClick={openTeamManagement} |
180 | gaEventInfo={{ category: 'Todos', event: 'upgrade' }} | 164 | className={classes.cta} |
181 | requiresPro | 165 | /> |
182 | short | ||
183 | /> | ||
184 | ) : ( | ||
185 | <Button | ||
186 | label={intl.formatMessage(messages.manageButton)} | ||
187 | onClick={openTeamManagement} | ||
188 | className={classes.cta} | ||
189 | /> | ||
190 | )} | ||
191 | </div> | 166 | </div> |
192 | </> | 167 | </> |
193 | </> | 168 | </> |
diff --git a/src/components/subscription/SubscriptionForm.js b/src/components/subscription/SubscriptionForm.js deleted file mode 100644 index ec486e5d0..000000000 --- a/src/components/subscription/SubscriptionForm.js +++ /dev/null | |||
@@ -1,78 +0,0 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | ||
4 | import { defineMessages, intlShape } from 'react-intl'; | ||
5 | import injectSheet from 'react-jss'; | ||
6 | |||
7 | import { H3, H2 } from '@meetfranz/ui'; | ||
8 | |||
9 | import { Button } from '@meetfranz/forms'; | ||
10 | import { FeatureList } from '../ui/FeatureList'; | ||
11 | |||
12 | const messages = defineMessages({ | ||
13 | submitButtonLabel: { | ||
14 | id: 'subscription.cta.choosePlan', | ||
15 | defaultMessage: '!!!Choose your plan', | ||
16 | }, | ||
17 | teaserHeadline: { | ||
18 | id: 'settings.account.headlineUpgradeAccount', | ||
19 | defaultMessage: '!!!Upgrade your account and get the full Franz experience', | ||
20 | }, | ||
21 | teaserText: { | ||
22 | id: 'subscription.teaser.intro', | ||
23 | defaultMessage: '!!!Franz 5 comes with a wide range of new features to boost up your everyday communication - batteries included. Check out our new plans and find out which one suits you most!', | ||
24 | }, | ||
25 | includedFeatures: { | ||
26 | id: 'subscription.teaser.includedFeatures', | ||
27 | defaultMessage: '!!!Paid Franz Plans include:', | ||
28 | }, | ||
29 | }); | ||
30 | |||
31 | const styles = () => ({ | ||
32 | activateTrialButton: { | ||
33 | margin: [40, 'auto', 50], | ||
34 | display: 'flex', | ||
35 | }, | ||
36 | }); | ||
37 | |||
38 | export default @injectSheet(styles) @observer class SubscriptionForm extends Component { | ||
39 | static propTypes = { | ||
40 | selectPlan: PropTypes.func.isRequired, | ||
41 | isActivatingTrial: PropTypes.bool.isRequired, | ||
42 | classes: PropTypes.object.isRequired, | ||
43 | }; | ||
44 | |||
45 | static contextTypes = { | ||
46 | intl: intlShape, | ||
47 | }; | ||
48 | |||
49 | render() { | ||
50 | const { | ||
51 | isActivatingTrial, | ||
52 | selectPlan, | ||
53 | classes, | ||
54 | } = this.props; | ||
55 | const { intl } = this.context; | ||
56 | |||
57 | return ( | ||
58 | <> | ||
59 | <H2>{intl.formatMessage(messages.teaserHeadline)}</H2> | ||
60 | <p>{intl.formatMessage(messages.teaserText)}</p> | ||
61 | <Button | ||
62 | label={intl.formatMessage(messages.submitButtonLabel)} | ||
63 | className={classes.activateTrialButton} | ||
64 | busy={isActivatingTrial} | ||
65 | onClick={selectPlan} | ||
66 | /> | ||
67 | <div className="subscription__premium-info"> | ||
68 | <H3> | ||
69 | {intl.formatMessage(messages.includedFeatures)} | ||
70 | </H3> | ||
71 | <div className="subscription"> | ||
72 | <FeatureList /> | ||
73 | </div> | ||
74 | </div> | ||
75 | </> | ||
76 | ); | ||
77 | } | ||
78 | } | ||
diff --git a/src/components/subscription/SubscriptionPopup.js b/src/components/subscription/SubscriptionPopup.js deleted file mode 100644 index 0df43fd4b..000000000 --- a/src/components/subscription/SubscriptionPopup.js +++ /dev/null | |||
@@ -1,84 +0,0 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | ||
4 | import { defineMessages, intlShape } from 'react-intl'; | ||
5 | import Webview from 'react-electron-web-view'; | ||
6 | import ms from 'ms'; | ||
7 | |||
8 | import Button from '../ui/Button'; | ||
9 | |||
10 | const messages = defineMessages({ | ||
11 | buttonCancel: { | ||
12 | id: 'subscriptionPopup.buttonCancel', | ||
13 | defaultMessage: '!!!Cancel', | ||
14 | }, | ||
15 | buttonDone: { | ||
16 | id: 'subscriptionPopup.buttonDone', | ||
17 | defaultMessage: '!!!Done', | ||
18 | }, | ||
19 | }); | ||
20 | |||
21 | export default @observer class SubscriptionPopup extends Component { | ||
22 | static propTypes = { | ||
23 | url: PropTypes.string.isRequired, | ||
24 | closeWindow: PropTypes.func.isRequired, | ||
25 | completeCheck: PropTypes.func.isRequired, | ||
26 | isCompleted: PropTypes.bool.isRequired, | ||
27 | }; | ||
28 | |||
29 | static contextTypes = { | ||
30 | intl: intlShape, | ||
31 | }; | ||
32 | |||
33 | state = { | ||
34 | isFakeLoading: false, | ||
35 | }; | ||
36 | |||
37 | // We delay the window closing a bit in order to give | ||
38 | // the Recurly webhook a few seconds to do it's magic | ||
39 | delayedCloseWindow() { | ||
40 | this.setState({ | ||
41 | isFakeLoading: true, | ||
42 | }); | ||
43 | |||
44 | setTimeout(() => { | ||
45 | this.props.closeWindow(); | ||
46 | }, ms('1s')); | ||
47 | } | ||
48 | |||
49 | render() { | ||
50 | const { | ||
51 | url, closeWindow, completeCheck, isCompleted, | ||
52 | } = this.props; | ||
53 | const { intl } = this.context; | ||
54 | |||
55 | return ( | ||
56 | <div className="subscription-popup"> | ||
57 | <div className="subscription-popup__content"> | ||
58 | <Webview | ||
59 | className="subscription-popup__webview" | ||
60 | autosize | ||
61 | allowpopups | ||
62 | src={encodeURI(url)} | ||
63 | onDidNavigate={completeCheck} | ||
64 | onDidNavigateInPage={completeCheck} | ||
65 | /> | ||
66 | </div> | ||
67 | <div className="subscription-popup__toolbar franz-form"> | ||
68 | <Button | ||
69 | label={intl.formatMessage(messages.buttonCancel)} | ||
70 | buttonType="secondary" | ||
71 | onClick={closeWindow} | ||
72 | disabled={isCompleted} | ||
73 | /> | ||
74 | <Button | ||
75 | label={intl.formatMessage(messages.buttonDone)} | ||
76 | onClick={() => this.delayedCloseWindow()} | ||
77 | disabled={!isCompleted} | ||
78 | loaded={!this.state.isFakeLoading} | ||
79 | /> | ||
80 | </div> | ||
81 | </div> | ||
82 | ); | ||
83 | } | ||
84 | } | ||
diff --git a/src/components/subscription/TrialForm.js b/src/components/subscription/TrialForm.js deleted file mode 100644 index d61b779ed..000000000 --- a/src/components/subscription/TrialForm.js +++ /dev/null | |||
@@ -1,115 +0,0 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | ||
4 | import { defineMessages, intlShape } from 'react-intl'; | ||
5 | import injectSheet from 'react-jss'; | ||
6 | |||
7 | import { H3, H2 } from '@meetfranz/ui'; | ||
8 | |||
9 | import { Button } from '@meetfranz/forms'; | ||
10 | import { FeatureList } from '../ui/FeatureList'; | ||
11 | import { FeatureItem } from '../ui/FeatureItem'; | ||
12 | |||
13 | const messages = defineMessages({ | ||
14 | submitButtonLabel: { | ||
15 | id: 'subscription.cta.activateTrial', | ||
16 | defaultMessage: '!!!Yes, start the free Franz Professional trial', | ||
17 | }, | ||
18 | allOptionsButton: { | ||
19 | id: 'subscription.cta.allOptions', | ||
20 | defaultMessage: '!!!See all options', | ||
21 | }, | ||
22 | teaserHeadline: { | ||
23 | id: 'settings.account.headlineTrialUpgrade', | ||
24 | defaultMessage: '!!!Get the free 14 day Franz Professional Trial', | ||
25 | }, | ||
26 | includedFeatures: { | ||
27 | id: 'subscription.includedProFeatures', | ||
28 | defaultMessage: '!!!The Franz Professional Plan includes:', | ||
29 | }, | ||
30 | noStringsAttachedHeadline: { | ||
31 | id: 'pricing.trial.terms.headline', | ||
32 | defaultMessage: '!!!No strings attached', | ||
33 | }, | ||
34 | noCreditCard: { | ||
35 | id: 'pricing.trial.terms.noCreditCard', | ||
36 | defaultMessage: '!!!No credit card required', | ||
37 | }, | ||
38 | automaticTrialEnd: { | ||
39 | id: 'pricing.trial.terms.automaticTrialEnd', | ||
40 | defaultMessage: '!!!Your free trial ends automatically after 14 days', | ||
41 | }, | ||
42 | }); | ||
43 | |||
44 | const styles = theme => ({ | ||
45 | activateTrialButton: { | ||
46 | margin: [40, 'auto', 10], | ||
47 | display: 'flex', | ||
48 | }, | ||
49 | allOptionsButton: { | ||
50 | margin: [0, 0, 40], | ||
51 | background: 'none', | ||
52 | border: 'none', | ||
53 | color: theme.colorText, | ||
54 | }, | ||
55 | keyTerms: { | ||
56 | marginTop: 20, | ||
57 | }, | ||
58 | }); | ||
59 | |||
60 | export default @injectSheet(styles) @observer class TrialForm extends Component { | ||
61 | static propTypes = { | ||
62 | activateTrial: PropTypes.func.isRequired, | ||
63 | isActivatingTrial: PropTypes.bool.isRequired, | ||
64 | showAllOptions: PropTypes.func.isRequired, | ||
65 | classes: PropTypes.object.isRequired, | ||
66 | }; | ||
67 | |||
68 | static contextTypes = { | ||
69 | intl: intlShape, | ||
70 | }; | ||
71 | |||
72 | render() { | ||
73 | const { | ||
74 | isActivatingTrial, | ||
75 | activateTrial, | ||
76 | showAllOptions, | ||
77 | classes, | ||
78 | } = this.props; | ||
79 | const { intl } = this.context; | ||
80 | |||
81 | return ( | ||
82 | <> | ||
83 | <H2>{intl.formatMessage(messages.teaserHeadline)}</H2> | ||
84 | <H3 className={classes.keyTerms}> | ||
85 | {intl.formatMessage(messages.noStringsAttachedHeadline)} | ||
86 | </H3> | ||
87 | <ul> | ||
88 | <FeatureItem icon="👉" name={intl.formatMessage(messages.noCreditCard)} /> | ||
89 | <FeatureItem icon="👉" name={intl.formatMessage(messages.automaticTrialEnd)} /> | ||
90 | </ul> | ||
91 | |||
92 | <Button | ||
93 | label={intl.formatMessage(messages.submitButtonLabel)} | ||
94 | className={classes.activateTrialButton} | ||
95 | busy={isActivatingTrial} | ||
96 | onClick={activateTrial} | ||
97 | /> | ||
98 | <Button | ||
99 | label={intl.formatMessage(messages.allOptionsButton)} | ||
100 | className={classes.allOptionsButton} | ||
101 | onClick={showAllOptions} | ||
102 | stretch | ||
103 | /> | ||
104 | <div className="subscription__premium-info"> | ||
105 | <H3> | ||
106 | {intl.formatMessage(messages.includedFeatures)} | ||
107 | </H3> | ||
108 | <div className="subscription"> | ||
109 | <FeatureList /> | ||
110 | </div> | ||
111 | </div> | ||
112 | </> | ||
113 | ); | ||
114 | } | ||
115 | } | ||
diff --git a/src/components/ui/ActivateTrialButton/index.js b/src/components/ui/ActivateTrialButton/index.js deleted file mode 100644 index 8f4d21f64..000000000 --- a/src/components/ui/ActivateTrialButton/index.js +++ /dev/null | |||
@@ -1,107 +0,0 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { inject, observer } from 'mobx-react'; | ||
4 | import { defineMessages, intlShape } from 'react-intl'; | ||
5 | import classnames from 'classnames'; | ||
6 | |||
7 | import { Button } from '@meetfranz/forms'; | ||
8 | |||
9 | import UserStore from '../../../stores/UserStore'; | ||
10 | import UIStore from '../../../stores/UIStore'; | ||
11 | |||
12 | const messages = defineMessages({ | ||
13 | action: { | ||
14 | id: 'feature.delayApp.upgrade.action', | ||
15 | defaultMessage: '!!!Get a Franz Supporter License', | ||
16 | }, | ||
17 | actionTrial: { | ||
18 | id: 'feature.delayApp.trial.action', | ||
19 | defaultMessage: '!!!Yes, I want the free 14 day trial of Franz Professional', | ||
20 | }, | ||
21 | shortAction: { | ||
22 | id: 'feature.delayApp.upgrade.actionShort', | ||
23 | defaultMessage: '!!!Upgrade account', | ||
24 | }, | ||
25 | shortActionTrial: { | ||
26 | id: 'feature.delayApp.trial.actionShort', | ||
27 | defaultMessage: '!!!Activate the free Franz Professional trial', | ||
28 | }, | ||
29 | noStringsAttachedHeadline: { | ||
30 | id: 'pricing.trial.terms.headline', | ||
31 | defaultMessage: '!!!No strings attached', | ||
32 | }, | ||
33 | noCreditCard: { | ||
34 | id: 'pricing.trial.terms.noCreditCard', | ||
35 | defaultMessage: '!!!No credit card required', | ||
36 | }, | ||
37 | automaticTrialEnd: { | ||
38 | id: 'pricing.trial.terms.automaticTrialEnd', | ||
39 | defaultMessage: '!!!Your free trial ends automatically after 14 days', | ||
40 | }, | ||
41 | }); | ||
42 | |||
43 | @inject('stores', 'actions') @observer | ||
44 | class ActivateTrialButton extends Component { | ||
45 | static propTypes = { | ||
46 | className: PropTypes.string, | ||
47 | short: PropTypes.bool, | ||
48 | gaEventInfo: PropTypes.shape({ | ||
49 | category: PropTypes.string.isRequired, | ||
50 | event: PropTypes.string.isRequired, | ||
51 | label: PropTypes.string, | ||
52 | }), | ||
53 | }; | ||
54 | |||
55 | static defaultProps = { | ||
56 | className: '', | ||
57 | short: false, | ||
58 | gaEventInfo: null, | ||
59 | } | ||
60 | |||
61 | static contextTypes = { | ||
62 | intl: intlShape, | ||
63 | }; | ||
64 | |||
65 | handleCTAClick() { | ||
66 | const { actions } = this.props; | ||
67 | |||
68 | actions.ui.openSettings({ path: 'user' }); | ||
69 | } | ||
70 | |||
71 | render() { | ||
72 | const { stores, className, short } = this.props; | ||
73 | const { intl } = this.context; | ||
74 | |||
75 | const { hadSubscription } = stores.user.data; | ||
76 | |||
77 | let label; | ||
78 | if (hadSubscription) { | ||
79 | label = short ? messages.shortAction : messages.action; | ||
80 | } else { | ||
81 | label = short ? messages.shortActionTrial : messages.actionTrial; | ||
82 | } | ||
83 | |||
84 | return ( | ||
85 | <Button | ||
86 | label={intl.formatMessage(label)} | ||
87 | className={classnames({ | ||
88 | [className]: className, | ||
89 | })} | ||
90 | buttonType="inverted" | ||
91 | onClick={this.handleCTAClick.bind(this)} | ||
92 | busy={stores.user.activateTrialRequest.isExecuting} | ||
93 | /> | ||
94 | ); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | export default ActivateTrialButton; | ||
99 | |||
100 | ActivateTrialButton.wrappedComponent.propTypes = { | ||
101 | stores: PropTypes.shape({ | ||
102 | user: PropTypes.instanceOf(UserStore).isRequired, | ||
103 | }).isRequired, | ||
104 | actions: PropTypes.shape({ | ||
105 | ui: PropTypes.instanceOf(UIStore).isRequired, | ||
106 | }).isRequired, | ||
107 | }; | ||
diff --git a/src/components/ui/FeatureList.js b/src/components/ui/FeatureList.js index 72c799819..ada15244b 100644 --- a/src/components/ui/FeatureList.js +++ b/src/components/ui/FeatureList.js | |||
@@ -3,12 +3,11 @@ import PropTypes from 'prop-types'; | |||
3 | import { defineMessages, intlShape } from 'react-intl'; | 3 | import { defineMessages, intlShape } from 'react-intl'; |
4 | 4 | ||
5 | import { FeatureItem } from './FeatureItem'; | 5 | import { FeatureItem } from './FeatureItem'; |
6 | import { PLANS } from '../../config'; | ||
7 | 6 | ||
8 | const messages = defineMessages({ | 7 | const messages = defineMessages({ |
9 | availableRecipes: { | 8 | availableRecipes: { |
10 | id: 'pricing.features.recipes', | 9 | id: 'pricing.features.recipes', |
11 | defaultMessage: '!!!Choose from more than 70 Services', | 10 | defaultMessage: '!!!Choose from more than 70 Services', // TODO: Make this dynamic |
12 | }, | 11 | }, |
13 | accountSync: { | 12 | accountSync: { |
14 | id: 'pricing.features.accountSync', | 13 | id: 'pricing.features.accountSync', |
@@ -22,14 +21,6 @@ const messages = defineMessages({ | |||
22 | id: 'pricing.features.unlimitedServices', | 21 | id: 'pricing.features.unlimitedServices', |
23 | defaultMessage: '!!!Add unlimited services', | 22 | defaultMessage: '!!!Add unlimited services', |
24 | }, | 23 | }, |
25 | upToThreeServices: { | ||
26 | id: 'pricing.features.upToThreeServices', | ||
27 | defaultMessage: '!!!Add up to 3 services', | ||
28 | }, | ||
29 | upToSixServices: { | ||
30 | id: 'pricing.features.upToSixServices', | ||
31 | defaultMessage: '!!!Add up to 6 services', | ||
32 | }, | ||
33 | spellchecker: { | 24 | spellchecker: { |
34 | id: 'pricing.features.spellchecker', | 25 | id: 'pricing.features.spellchecker', |
35 | defaultMessage: '!!!Spellchecker support', | 26 | defaultMessage: '!!!Spellchecker support', |
@@ -58,31 +49,17 @@ const messages = defineMessages({ | |||
58 | id: 'pricing.features.teamManagement', | 49 | id: 'pricing.features.teamManagement', |
59 | defaultMessage: '!!!Team Management', | 50 | defaultMessage: '!!!Team Management', |
60 | }, | 51 | }, |
61 | appDelays: { | ||
62 | id: 'pricing.features.appDelays', | ||
63 | defaultMessage: '!!!No Waiting Screens', | ||
64 | }, | ||
65 | adFree: { | ||
66 | id: 'pricing.features.adFree', | ||
67 | defaultMessage: '!!!Forever ad-free', | ||
68 | }, | ||
69 | appDelayEnabled: { | ||
70 | id: 'pricing.features.appDelaysEnabled', | ||
71 | defaultMessage: '!!!Occasional Waiting Screens', | ||
72 | }, | ||
73 | }); | 52 | }); |
74 | 53 | ||
75 | export class FeatureList extends Component { | 54 | export class FeatureList extends Component { |
76 | static propTypes = { | 55 | static propTypes = { |
77 | className: PropTypes.string, | 56 | className: PropTypes.string, |
78 | featureClassName: PropTypes.string, | 57 | featureClassName: PropTypes.string, |
79 | plan: PropTypes.oneOf(Object.keys(PLANS)), | ||
80 | }; | 58 | }; |
81 | 59 | ||
82 | static defaultProps = { | 60 | static defaultProps = { |
83 | className: '', | 61 | className: '', |
84 | featureClassName: '', | 62 | featureClassName: '', |
85 | plan: false, | ||
86 | } | 63 | } |
87 | 64 | ||
88 | static contextTypes = { | 65 | static contextTypes = { |
@@ -93,49 +70,25 @@ export class FeatureList extends Component { | |||
93 | const { | 70 | const { |
94 | className, | 71 | className, |
95 | featureClassName, | 72 | featureClassName, |
96 | plan, | ||
97 | } = this.props; | 73 | } = this.props; |
98 | const { intl } = this.context; | 74 | const { intl } = this.context; |
99 | 75 | ||
100 | const features = []; | 76 | const features = [ |
101 | if (plan === PLANS.FREE) { | 77 | messages.availableRecipes, |
102 | features.push( | 78 | messages.accountSync, |
103 | messages.appDelayEnabled, | 79 | messages.desktopNotifications, |
104 | messages.upToThreeServices, | 80 | |
105 | messages.availableRecipes, | 81 | messages.spellchecker, |
106 | messages.accountSync, | 82 | |
107 | messages.desktopNotifications, | 83 | messages.workspaces, |
108 | ); | 84 | messages.customWebsites, |
109 | } else if (plan === PLANS.PERSONAL) { | 85 | messages.thirdPartyServices, |
110 | features.push( | 86 | |
111 | messages.upToSixServices, | 87 | messages.unlimitedServices, |
112 | messages.spellchecker, | 88 | messages.onPremise, |
113 | messages.appDelays, | 89 | messages.serviceProxies, |
114 | messages.adFree, | 90 | messages.teamManagement, |
115 | ); | 91 | ]; |
116 | } else if (plan === PLANS.PRO) { | ||
117 | features.push( | ||
118 | messages.unlimitedServices, | ||
119 | messages.workspaces, | ||
120 | messages.customWebsites, | ||
121 | // messages.onPremise, | ||
122 | messages.thirdPartyServices, | ||
123 | // messages.serviceProxies, | ||
124 | ); | ||
125 | } else { | ||
126 | features.push( | ||
127 | messages.unlimitedServices, | ||
128 | messages.spellchecker, | ||
129 | messages.workspaces, | ||
130 | messages.customWebsites, | ||
131 | messages.onPremise, | ||
132 | messages.thirdPartyServices, | ||
133 | messages.serviceProxies, | ||
134 | messages.teamManagement, | ||
135 | messages.appDelays, | ||
136 | messages.adFree, | ||
137 | ); | ||
138 | } | ||
139 | 92 | ||
140 | return ( | 93 | return ( |
141 | <ul className={className}> | 94 | <ul className={className}> |
diff --git a/src/components/ui/PremiumFeatureContainer/index.js b/src/components/ui/PremiumFeatureContainer/index.js deleted file mode 100644 index 1e100f9d8..000000000 --- a/src/components/ui/PremiumFeatureContainer/index.js +++ /dev/null | |||
@@ -1,101 +0,0 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import { inject, observer } from 'mobx-react'; | ||
3 | import PropTypes from 'prop-types'; | ||
4 | import { defineMessages, intlShape } from 'react-intl'; | ||
5 | import injectSheet from 'react-jss'; | ||
6 | |||
7 | import { oneOrManyChildElements } from '../../../prop-types'; | ||
8 | |||
9 | import UserStore from '../../../stores/UserStore'; | ||
10 | |||
11 | import styles from './styles'; | ||
12 | import FeaturesStore from '../../../stores/FeaturesStore'; | ||
13 | import UIStore from '../../../stores/UIStore'; | ||
14 | |||
15 | const messages = defineMessages({ | ||
16 | action: { | ||
17 | id: 'premiumFeature.button.upgradeAccount', | ||
18 | defaultMessage: '!!!Upgrade account', | ||
19 | }, | ||
20 | }); | ||
21 | |||
22 | @inject('stores', 'actions') @injectSheet(styles) @observer | ||
23 | class PremiumFeatureContainer extends Component { | ||
24 | static propTypes = { | ||
25 | classes: PropTypes.object.isRequired, | ||
26 | condition: PropTypes.oneOfType([ | ||
27 | PropTypes.bool, | ||
28 | PropTypes.func, | ||
29 | ]), | ||
30 | gaEventInfo: PropTypes.shape({ | ||
31 | category: PropTypes.string.isRequired, | ||
32 | event: PropTypes.string.isRequired, | ||
33 | label: PropTypes.string, | ||
34 | }), | ||
35 | }; | ||
36 | |||
37 | static defaultProps = { | ||
38 | condition: null, | ||
39 | gaEventInfo: null, | ||
40 | }; | ||
41 | |||
42 | static contextTypes = { | ||
43 | intl: intlShape, | ||
44 | }; | ||
45 | |||
46 | render() { | ||
47 | const { | ||
48 | classes, | ||
49 | children, | ||
50 | actions, | ||
51 | condition, | ||
52 | stores, | ||
53 | } = this.props; | ||
54 | |||
55 | const { intl } = this.context; | ||
56 | |||
57 | let showWrapper = !!condition; | ||
58 | |||
59 | if (condition === null) { | ||
60 | showWrapper = !stores.user.data.isPremium; | ||
61 | } else if (typeof condition === 'function') { | ||
62 | showWrapper = condition({ | ||
63 | isPremium: stores.user.data.isPremium, | ||
64 | features: stores.features.features, | ||
65 | }); | ||
66 | } | ||
67 | |||
68 | return showWrapper ? ( | ||
69 | <div className={classes.container}> | ||
70 | <div className={classes.titleContainer}> | ||
71 | <p className={classes.title}>Premium Feature</p> | ||
72 | <button | ||
73 | className={classes.actionButton} | ||
74 | type="button" | ||
75 | onClick={() => { | ||
76 | actions.ui.openSettings({ path: 'user' }); | ||
77 | }} | ||
78 | > | ||
79 | {intl.formatMessage(messages.action)} | ||
80 | </button> | ||
81 | </div> | ||
82 | <div className={classes.content}> | ||
83 | {children} | ||
84 | </div> | ||
85 | </div> | ||
86 | ) : children; | ||
87 | } | ||
88 | } | ||
89 | |||
90 | PremiumFeatureContainer.wrappedComponent.propTypes = { | ||
91 | children: oneOrManyChildElements.isRequired, | ||
92 | stores: PropTypes.shape({ | ||
93 | user: PropTypes.instanceOf(UserStore).isRequired, | ||
94 | features: PropTypes.instanceOf(FeaturesStore).isRequired, | ||
95 | }).isRequired, | ||
96 | actions: PropTypes.shape({ | ||
97 | ui: PropTypes.instanceOf(UIStore).isRequired, | ||
98 | }).isRequired, | ||
99 | }; | ||
100 | |||
101 | export default PremiumFeatureContainer; | ||
diff --git a/src/components/ui/PremiumFeatureContainer/styles.js b/src/components/ui/PremiumFeatureContainer/styles.js deleted file mode 100644 index 41881e044..000000000 --- a/src/components/ui/PremiumFeatureContainer/styles.js +++ /dev/null | |||
@@ -1,34 +0,0 @@ | |||
1 | export default theme => ({ | ||
2 | container: { | ||
3 | background: theme.colorSubscriptionContainerBackground, | ||
4 | border: theme.colorSubscriptionContainerBorder, | ||
5 | margin: [0, 0, 20, -20], | ||
6 | padding: 20, | ||
7 | 'border-radius': theme.borderRadius, | ||
8 | pointerEvents: 'none', | ||
9 | height: 'auto', | ||
10 | }, | ||
11 | titleContainer: { | ||
12 | display: 'flex', | ||
13 | }, | ||
14 | title: { | ||
15 | 'font-weight': 'bold', | ||
16 | color: theme.colorSubscriptionContainerTitle, | ||
17 | }, | ||
18 | actionButton: { | ||
19 | background: theme.colorSubscriptionContainerActionButtonBackground, | ||
20 | color: theme.colorSubscriptionContainerActionButtonColor, | ||
21 | 'margin-left': 'auto', | ||
22 | 'border-radius': theme.borderRadiusSmall, | ||
23 | padding: [4, 8], | ||
24 | 'font-size': 12, | ||
25 | pointerEvents: 'initial', | ||
26 | }, | ||
27 | content: { | ||
28 | opacity: 0.5, | ||
29 | 'margin-top': 20, | ||
30 | '& > :last-child': { | ||
31 | 'margin-bottom': 0, | ||
32 | }, | ||
33 | }, | ||
34 | }); | ||
diff --git a/src/components/ui/UpgradeButton/index.js b/src/components/ui/UpgradeButton/index.js deleted file mode 100644 index eade46cfd..000000000 --- a/src/components/ui/UpgradeButton/index.js +++ /dev/null | |||
@@ -1,83 +0,0 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { inject, observer } from 'mobx-react'; | ||
4 | import { defineMessages, intlShape } from 'react-intl'; | ||
5 | |||
6 | import { Button } from '@meetfranz/forms'; | ||
7 | |||
8 | import UserStore from '../../../stores/UserStore'; | ||
9 | import ActivateTrialButton from '../ActivateTrialButton'; | ||
10 | import UIStore from '../../../stores/UIStore'; | ||
11 | |||
12 | const messages = defineMessages({ | ||
13 | upgradeToPro: { | ||
14 | id: 'global.upgradeButton.upgradeToPro', | ||
15 | defaultMessage: '!!!Upgrade to Franz Professional', | ||
16 | }, | ||
17 | }); | ||
18 | |||
19 | @inject('stores', 'actions') @observer | ||
20 | class UpgradeButton extends Component { | ||
21 | static propTypes = { | ||
22 | // eslint-disable-next-line | ||
23 | classes: PropTypes.object.isRequired, | ||
24 | className: PropTypes.string, | ||
25 | gaEventInfo: PropTypes.shape({ | ||
26 | category: PropTypes.string.isRequired, | ||
27 | event: PropTypes.string.isRequired, | ||
28 | label: PropTypes.string, | ||
29 | }), | ||
30 | requiresPro: PropTypes.bool, | ||
31 | }; | ||
32 | |||
33 | static defaultProps = { | ||
34 | className: '', | ||
35 | gaEventInfo: null, | ||
36 | requiresPro: false, | ||
37 | } | ||
38 | |||
39 | static contextTypes = { | ||
40 | intl: intlShape, | ||
41 | }; | ||
42 | |||
43 | handleCTAClick() { | ||
44 | const { actions } = this.props; | ||
45 | |||
46 | actions.ui.openSettings({ path: 'user' }); | ||
47 | } | ||
48 | |||
49 | render() { | ||
50 | const { stores, requiresPro } = this.props; | ||
51 | const { intl } = this.context; | ||
52 | |||
53 | const { isPremium, isPersonal } = stores.user; | ||
54 | |||
55 | if (isPremium && isPersonal && requiresPro) { | ||
56 | return ( | ||
57 | <Button | ||
58 | label={intl.formatMessage(messages.upgradeToPro)} | ||
59 | onClick={this.handleCTAClick.bind(this)} | ||
60 | className={this.props.className} | ||
61 | buttonType="inverted" | ||
62 | /> | ||
63 | ); | ||
64 | } | ||
65 | |||
66 | if (!isPremium) { | ||
67 | return <ActivateTrialButton {...this.props} />; | ||
68 | } | ||
69 | |||
70 | return null; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | export default UpgradeButton; | ||
75 | |||
76 | UpgradeButton.wrappedComponent.propTypes = { | ||
77 | stores: PropTypes.shape({ | ||
78 | user: PropTypes.instanceOf(UserStore).isRequired, | ||
79 | }).isRequired, | ||
80 | actions: PropTypes.shape({ | ||
81 | ui: PropTypes.instanceOf(UIStore).isRequired, | ||
82 | }).isRequired, | ||
83 | }; | ||