diff options
author | 2019-10-15 21:40:14 +0200 | |
---|---|---|
committer | 2019-10-15 21:40:14 +0200 | |
commit | 91a0fb20ef02dfa342cf26df3e047b2bd4370b9f (patch) | |
tree | f411b3d7d83a24b015a2a1ed723df2e2a324cc0c /src | |
parent | Optimize button width (diff) | |
download | ferdium-app-91a0fb20ef02dfa342cf26df3e047b2bd4370b9f.tar.gz ferdium-app-91a0fb20ef02dfa342cf26df3e047b2bd4370b9f.tar.zst ferdium-app-91a0fb20ef02dfa342cf26df3e047b2bd4370b9f.zip |
simplify plan selection
Diffstat (limited to 'src')
29 files changed, 1587 insertions, 102 deletions
diff --git a/src/actions/index.js b/src/actions/index.js index 336344d76..1c033fb96 100644 --- a/src/actions/index.js +++ b/src/actions/index.js | |||
@@ -14,6 +14,7 @@ import requests from './requests'; | |||
14 | import announcements from '../features/announcements/actions'; | 14 | import announcements from '../features/announcements/actions'; |
15 | import workspaces from '../features/workspaces/actions'; | 15 | import workspaces from '../features/workspaces/actions'; |
16 | import todos from '../features/todos/actions'; | 16 | import todos from '../features/todos/actions'; |
17 | import planSelection from '../features/planSelection/actions'; | ||
17 | 18 | ||
18 | const actions = Object.assign({}, { | 19 | const actions = Object.assign({}, { |
19 | service, | 20 | service, |
@@ -33,4 +34,5 @@ export default Object.assign( | |||
33 | { announcements }, | 34 | { announcements }, |
34 | { workspaces }, | 35 | { workspaces }, |
35 | { todos }, | 36 | { todos }, |
37 | { planSelection }, | ||
36 | ); | 38 | ); |
diff --git a/src/components/auth/Pricing.js b/src/components/auth/Pricing.js index cbeaaa5d9..40ce49814 100644 --- a/src/components/auth/Pricing.js +++ b/src/components/auth/Pricing.js | |||
@@ -18,7 +18,7 @@ const messages = defineMessages({ | |||
18 | }, | 18 | }, |
19 | personalOffer: { | 19 | personalOffer: { |
20 | id: 'pricing.trial.subheadline', | 20 | id: 'pricing.trial.subheadline', |
21 | defaultMessage: '!!!Your personal welcome offer:', | 21 | defaultMessage: '!!!Here\'s a special welcome for you:', |
22 | }, | 22 | }, |
23 | noStringsAttachedHeadline: { | 23 | noStringsAttachedHeadline: { |
24 | id: 'pricing.trial.terms.headline', | 24 | id: 'pricing.trial.terms.headline', |
@@ -38,7 +38,7 @@ const messages = defineMessages({ | |||
38 | }, | 38 | }, |
39 | ctaAccept: { | 39 | ctaAccept: { |
40 | id: 'pricing.trial.cta.accept', | 40 | id: 'pricing.trial.cta.accept', |
41 | defaultMessage: '!!!Yes, upgrade my account to Franz Professional', | 41 | defaultMessage: '!!!Start my 14-day Franz Professional Trial ', |
42 | }, | 42 | }, |
43 | ctaSkip: { | 43 | ctaSkip: { |
44 | id: 'pricing.trial.cta.skip', | 44 | id: 'pricing.trial.cta.skip', |
@@ -58,6 +58,7 @@ const styles = theme => ({ | |||
58 | welcomeOffer: { | 58 | welcomeOffer: { |
59 | textAlign: 'center', | 59 | textAlign: 'center', |
60 | fontWeight: 'bold', | 60 | fontWeight: 'bold', |
61 | marginBottom: '6 !important', | ||
61 | }, | 62 | }, |
62 | keyTerms: { | 63 | keyTerms: { |
63 | textAlign: 'center', | 64 | textAlign: 'center', |
@@ -101,6 +102,7 @@ export default @observer @injectSheet(styles) class Signup extends Component { | |||
101 | isLoadingRequiredData: PropTypes.bool.isRequired, | 102 | isLoadingRequiredData: PropTypes.bool.isRequired, |
102 | isActivatingTrial: PropTypes.bool.isRequired, | 103 | isActivatingTrial: PropTypes.bool.isRequired, |
103 | trialActivationError: PropTypes.bool.isRequired, | 104 | trialActivationError: PropTypes.bool.isRequired, |
105 | canSkipTrial: PropTypes.bool.isRequired, | ||
104 | classes: PropTypes.object.isRequired, | 106 | classes: PropTypes.object.isRequired, |
105 | }; | 107 | }; |
106 | 108 | ||
@@ -114,6 +116,7 @@ export default @observer @injectSheet(styles) class Signup extends Component { | |||
114 | isLoadingRequiredData, | 116 | isLoadingRequiredData, |
115 | isActivatingTrial, | 117 | isActivatingTrial, |
116 | trialActivationError, | 118 | trialActivationError, |
119 | canSkipTrial, | ||
117 | classes, | 120 | classes, |
118 | } = this.props; | 121 | } = this.props; |
119 | const { intl } = this.context; | 122 | const { intl } = this.context; |
@@ -138,7 +141,7 @@ export default @observer @injectSheet(styles) class Signup extends Component { | |||
138 | <br /> | 141 | <br /> |
139 | </p> | 142 | </p> |
140 | <p> | 143 | <p> |
141 | Get the free 14 day Franz Professional trial and see your communication evolving. | 144 | For the next 14 days, we are going to give you the full Franz Professional experience so you can watch your communication evolve! |
142 | <br /> | 145 | <br /> |
143 | </p> | 146 | </p> |
144 | <p> | 147 | <p> |
@@ -167,9 +170,11 @@ export default @observer @injectSheet(styles) class Signup extends Component { | |||
167 | busy={isActivatingTrial} | 170 | busy={isActivatingTrial} |
168 | disabled={isLoadingRequiredData || isActivatingTrial} | 171 | disabled={isLoadingRequiredData || isActivatingTrial} |
169 | /> | 172 | /> |
170 | <p className={classes.skipLink}> | 173 | {canSkipTrial && ( |
171 | <a href="#/">{intl.formatMessage(messages.ctaSkip)}</a> | 174 | <p className={classes.skipLink}> |
172 | </p> | 175 | <a href="#/">{intl.formatMessage(messages.ctaSkip)}</a> |
176 | </p> | ||
177 | )} | ||
173 | </form> | 178 | </form> |
174 | </div> | 179 | </div> |
175 | <div className={classes.featureContainer}> | 180 | <div className={classes.featureContainer}> |
diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js index 200777ae6..fe81b1911 100644 --- a/src/components/layout/AppLayout.js +++ b/src/components/layout/AppLayout.js | |||
@@ -19,6 +19,7 @@ import { workspaceStore } from '../../features/workspaces'; | |||
19 | import AppUpdateInfoBar from '../AppUpdateInfoBar'; | 19 | import AppUpdateInfoBar from '../AppUpdateInfoBar'; |
20 | import TrialActivationInfoBar from '../TrialActivationInfoBar'; | 20 | import TrialActivationInfoBar from '../TrialActivationInfoBar'; |
21 | import Todos from '../../features/todos/containers/TodosScreen'; | 21 | import Todos from '../../features/todos/containers/TodosScreen'; |
22 | import PlanSelection from '../../features/planSelection/containers/PlanSelectionScreen'; | ||
22 | 23 | ||
23 | function createMarkup(HTMLString) { | 24 | function createMarkup(HTMLString) { |
24 | return { __html: HTMLString }; | 25 | return { __html: HTMLString }; |
@@ -176,6 +177,7 @@ class AppLayout extends Component { | |||
176 | </div> | 177 | </div> |
177 | <Todos /> | 178 | <Todos /> |
178 | </div> | 179 | </div> |
180 | <PlanSelection /> | ||
179 | </div> | 181 | </div> |
180 | </ErrorBoundary> | 182 | </ErrorBoundary> |
181 | ); | 183 | ); |
diff --git a/src/components/settings/account/AccountDashboard.js b/src/components/settings/account/AccountDashboard.js index 08e86fda6..9a1b31d0f 100644 --- a/src/components/settings/account/AccountDashboard.js +++ b/src/components/settings/account/AccountDashboard.js | |||
@@ -217,7 +217,8 @@ class AccountDashboard extends Component { | |||
217 | {intl.formatMessage(messages.yourLicense)} | 217 | {intl.formatMessage(messages.yourLicense)} |
218 | </H2> | 218 | </H2> |
219 | <p> | 219 | <p> |
220 | {isPremiumOverrideUser ? 'Franz Premium' : planName} | 220 | Franz |
221 | {isPremiumOverrideUser ? 'Premium' : planName} | ||
221 | {user.team.isTrial && ( | 222 | {user.team.isTrial && ( |
222 | <> | 223 | <> |
223 | {' – '} | 224 | {' – '} |
diff --git a/src/components/ui/FeatureItem.js b/src/components/ui/FeatureItem.js index 7c482c4d4..4926df470 100644 --- a/src/components/ui/FeatureItem.js +++ b/src/components/ui/FeatureItem.js | |||
@@ -10,6 +10,7 @@ const styles = theme => ({ | |||
10 | padding: [8, 0], | 10 | padding: [8, 0], |
11 | display: 'flex', | 11 | display: 'flex', |
12 | alignItems: 'center', | 12 | alignItems: 'center', |
13 | textAlign: 'left', | ||
13 | }, | 14 | }, |
14 | featureIcon: { | 15 | featureIcon: { |
15 | fill: theme.brandSuccess, | 16 | fill: theme.brandSuccess, |
diff --git a/src/components/ui/FeatureList.js b/src/components/ui/FeatureList.js index 62944ad75..732b40e40 100644 --- a/src/components/ui/FeatureList.js +++ b/src/components/ui/FeatureList.js | |||
@@ -3,12 +3,33 @@ 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'; | ||
6 | 7 | ||
7 | const messages = defineMessages({ | 8 | const messages = defineMessages({ |
9 | availableRecipes: { | ||
10 | id: 'pricing.features.recipes', | ||
11 | defaultMessage: '!!!Choose from more than 70 Services', | ||
12 | }, | ||
13 | accountSync: { | ||
14 | id: 'pricing.features.accountSync', | ||
15 | defaultMessage: '!!!Account Synchronisation', | ||
16 | }, | ||
17 | desktopNotifications: { | ||
18 | id: 'pricing.features.desktopNotifications', | ||
19 | defaultMessage: '!!!Desktop Notifications', | ||
20 | }, | ||
8 | unlimitedServices: { | 21 | unlimitedServices: { |
9 | id: 'pricing.features.unlimitedServices', | 22 | id: 'pricing.features.unlimitedServices', |
10 | defaultMessage: '!!!Add unlimited services', | 23 | defaultMessage: '!!!Add unlimited services', |
11 | }, | 24 | }, |
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 | }, | ||
12 | spellchecker: { | 33 | spellchecker: { |
13 | id: 'pricing.features.spellchecker', | 34 | id: 'pricing.features.spellchecker', |
14 | defaultMessage: '!!!Spellchecker support', | 35 | defaultMessage: '!!!Spellchecker support', |
@@ -51,6 +72,7 @@ export class FeatureList extends Component { | |||
51 | static propTypes = { | 72 | static propTypes = { |
52 | className: PropTypes.string, | 73 | className: PropTypes.string, |
53 | featureClassName: PropTypes.string, | 74 | featureClassName: PropTypes.string, |
75 | plan: PropTypes.oneOf(PLANS).isRequired, | ||
54 | }; | 76 | }; |
55 | 77 | ||
56 | static defaultProps = { | 78 | static defaultProps = { |
@@ -66,21 +88,52 @@ export class FeatureList extends Component { | |||
66 | const { | 88 | const { |
67 | className, | 89 | className, |
68 | featureClassName, | 90 | featureClassName, |
91 | plan, | ||
69 | } = this.props; | 92 | } = this.props; |
70 | const { intl } = this.context; | 93 | const { intl } = this.context; |
71 | 94 | ||
95 | const features = []; | ||
96 | if (plan === PLANS.FREE) { | ||
97 | features.push( | ||
98 | messages.upToThreeServices, | ||
99 | messages.availableRecipes, | ||
100 | messages.accountSync, | ||
101 | messages.desktopNotifications, | ||
102 | ); | ||
103 | } else if (plan === PLANS.PERSONAL) { | ||
104 | features.push( | ||
105 | messages.upToSixServices, | ||
106 | messages.spellchecker, | ||
107 | messages.appDelays, | ||
108 | messages.adFree, | ||
109 | ); | ||
110 | } else if (plan === PLANS.PRO) { | ||
111 | features.push( | ||
112 | messages.unlimitedServices, | ||
113 | messages.workspaces, | ||
114 | messages.customWebsites, | ||
115 | // messages.onPremise, | ||
116 | messages.thirdPartyServices, | ||
117 | // messages.serviceProxies, | ||
118 | ); | ||
119 | } else { | ||
120 | features.push( | ||
121 | messages.unlimitedServices, | ||
122 | messages.spellchecker, | ||
123 | messages.workspaces, | ||
124 | messages.customWebsites, | ||
125 | messages.onPremise, | ||
126 | messages.thirdPartyServices, | ||
127 | messages.serviceProxies, | ||
128 | messages.teamManagement, | ||
129 | messages.appDelays, | ||
130 | messages.adFree, | ||
131 | ); | ||
132 | } | ||
133 | |||
72 | return ( | 134 | return ( |
73 | <ul className={className}> | 135 | <ul className={className}> |
74 | <FeatureItem name={intl.formatMessage(messages.unlimitedServices)} className={featureClassName} /> | 136 | {features.map(feature => <FeatureItem name={intl.formatMessage(feature)} className={featureClassName} />)} |
75 | <FeatureItem name={intl.formatMessage(messages.spellchecker)} className={featureClassName} /> | ||
76 | <FeatureItem name={intl.formatMessage(messages.workspaces)} className={featureClassName} /> | ||
77 | <FeatureItem name={intl.formatMessage(messages.customWebsites)} className={featureClassName} /> | ||
78 | <FeatureItem name={intl.formatMessage(messages.onPremise)} className={featureClassName} /> | ||
79 | <FeatureItem name={intl.formatMessage(messages.thirdPartyServices)} className={featureClassName} /> | ||
80 | <FeatureItem name={intl.formatMessage(messages.serviceProxies)} className={featureClassName} /> | ||
81 | <FeatureItem name={intl.formatMessage(messages.teamManagement)} className={featureClassName} /> | ||
82 | <FeatureItem name={intl.formatMessage(messages.appDelays)} className={featureClassName} /> | ||
83 | <FeatureItem name={intl.formatMessage(messages.adFree)} className={featureClassName} /> | ||
84 | </ul> | 137 | </ul> |
85 | ); | 138 | ); |
86 | } | 139 | } |
diff --git a/src/config.js b/src/config.js index 78a92d948..11e6cb91f 100644 --- a/src/config.js +++ b/src/config.js | |||
@@ -91,10 +91,10 @@ export const ALLOWED_PROTOCOLS = [ | |||
91 | ]; | 91 | ]; |
92 | 92 | ||
93 | export const PLANS = { | 93 | export const PLANS = { |
94 | PERSONAL: 'PERSONAL', | 94 | PERSONAL: 'personal', |
95 | PRO: 'PRO', | 95 | PRO: 'pro', |
96 | LEGACY: 'LEGACY', | 96 | LEGACY: 'legacy', |
97 | FREE: 'FREE', | 97 | FREE: 'free', |
98 | }; | 98 | }; |
99 | 99 | ||
100 | export const PLANS_MAPPING = { | 100 | export const PLANS_MAPPING = { |
diff --git a/src/containers/auth/PricingScreen.js b/src/containers/auth/PricingScreen.js index af1651931..8e0ded16a 100644 --- a/src/containers/auth/PricingScreen.js +++ b/src/containers/auth/PricingScreen.js | |||
@@ -38,7 +38,7 @@ export default @inject('stores', 'actions') @observer class PricingScreen extend | |||
38 | } = this.props; | 38 | } = this.props; |
39 | 39 | ||
40 | const { getUserInfoRequest, activateTrialRequest } = stores.user; | 40 | const { getUserInfoRequest, activateTrialRequest } = stores.user; |
41 | const { featuresRequest } = stores.features; | 41 | const { featuresRequest, features } = stores.features; |
42 | 42 | ||
43 | return ( | 43 | return ( |
44 | <Pricing | 44 | <Pricing |
@@ -46,6 +46,7 @@ export default @inject('stores', 'actions') @observer class PricingScreen extend | |||
46 | isLoadingRequiredData={(getUserInfoRequest.isExecuting || !getUserInfoRequest.wasExecuted) || (featuresRequest.isExecuting || !featuresRequest.wasExecuted)} | 46 | isLoadingRequiredData={(getUserInfoRequest.isExecuting || !getUserInfoRequest.wasExecuted) || (featuresRequest.isExecuting || !featuresRequest.wasExecuted)} |
47 | isActivatingTrial={activateTrialRequest.isExecuting} | 47 | isActivatingTrial={activateTrialRequest.isExecuting} |
48 | trialActivationError={activateTrialRequest.isError} | 48 | trialActivationError={activateTrialRequest.isError} |
49 | canSkipTrial={features.canSkipTrial} | ||
49 | error={error} | 50 | error={error} |
50 | /> | 51 | /> |
51 | ); | 52 | ); |
diff --git a/src/features/planSelection/actions.js b/src/features/planSelection/actions.js new file mode 100644 index 000000000..21aa38ace --- /dev/null +++ b/src/features/planSelection/actions.js | |||
@@ -0,0 +1,13 @@ | |||
1 | import PropTypes from 'prop-types'; | ||
2 | import { createActionsFromDefinitions } from '../../actions/lib/actions'; | ||
3 | |||
4 | export const planSelectionActions = createActionsFromDefinitions({ | ||
5 | upgradeAccount: { | ||
6 | planId: PropTypes.string.isRequired, | ||
7 | onCloseWindow: PropTypes.func.isRequired, | ||
8 | }, | ||
9 | downgradeAccount: {}, | ||
10 | hideOverlay: {}, | ||
11 | }, PropTypes.checkPropTypes); | ||
12 | |||
13 | export default planSelectionActions; | ||
diff --git a/src/features/planSelection/api.js b/src/features/planSelection/api.js new file mode 100644 index 000000000..734643f10 --- /dev/null +++ b/src/features/planSelection/api.js | |||
@@ -0,0 +1,26 @@ | |||
1 | import { sendAuthRequest } from '../../api/utils/auth'; | ||
2 | import { API, API_VERSION } from '../../environment'; | ||
3 | import Request from '../../stores/lib/Request'; | ||
4 | |||
5 | const debug = require('debug')('Franz:feature:planSelection:api'); | ||
6 | |||
7 | export const planSelectionApi = { | ||
8 | downgrade: async () => { | ||
9 | const url = `${API}/${API_VERSION}/payment/downgrade`; | ||
10 | const options = { | ||
11 | method: 'PUT', | ||
12 | }; | ||
13 | debug('downgrade UPDATE', url, options); | ||
14 | const result = await sendAuthRequest(url, options); | ||
15 | debug('downgrade RESULT', result); | ||
16 | if (!result.ok) throw result; | ||
17 | |||
18 | return result.ok; | ||
19 | }, | ||
20 | }; | ||
21 | |||
22 | export const downgradeUserRequest = new Request(planSelectionApi, 'downgrade'); | ||
23 | |||
24 | export const resetApiRequests = () => { | ||
25 | downgradeUserRequest.reset(); | ||
26 | }; | ||
diff --git a/src/features/planSelection/components/PlanItem.js b/src/features/planSelection/components/PlanItem.js new file mode 100644 index 000000000..a49cd40d3 --- /dev/null +++ b/src/features/planSelection/components/PlanItem.js | |||
@@ -0,0 +1,186 @@ | |||
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 classnames from 'classnames'; | ||
7 | import color from 'color'; | ||
8 | |||
9 | import { H2 } from '@meetfranz/ui'; | ||
10 | |||
11 | import { Button } from '@meetfranz/forms'; | ||
12 | import { mdiArrowRight } from '@mdi/js'; | ||
13 | // import { FeatureList } from '../ui/FeatureList'; | ||
14 | // import { PLANS, PAYMENT_INTERVAL } from '../../config'; | ||
15 | // import { i18nPlanName, i18nIntervalName } from '../../helpers/plan-helpers'; | ||
16 | // import { PLAN_INTERVAL_CONFIG_TYPE } from './types'; | ||
17 | |||
18 | const messages = defineMessages({ | ||
19 | perMonth: { | ||
20 | id: 'subscription.interval.perMonth', | ||
21 | defaultMessage: '!!!per month', | ||
22 | }, | ||
23 | perMonthPerUser: { | ||
24 | id: 'subscription.interval.perMonthPerUser', | ||
25 | defaultMessage: '!!!per month & user', | ||
26 | }, | ||
27 | }); | ||
28 | |||
29 | const styles = theme => ({ | ||
30 | root: { | ||
31 | display: 'flex', | ||
32 | flexDirection: 'column', | ||
33 | borderRadius: theme.borderRadius, | ||
34 | flex: 1, | ||
35 | color: theme.styleTypes.primary.accent, | ||
36 | overflow: 'hidden', | ||
37 | textAlign: 'center', | ||
38 | |||
39 | '& h2': { | ||
40 | textAlign: 'center', | ||
41 | marginBottom: 20, | ||
42 | fontSize: 30, | ||
43 | color: theme.styleTypes.primary.contrast, | ||
44 | // fontWeight: 'bold', | ||
45 | }, | ||
46 | }, | ||
47 | currency: { | ||
48 | fontSize: 35, | ||
49 | }, | ||
50 | priceWrapper: { | ||
51 | height: 50, | ||
52 | }, | ||
53 | price: { | ||
54 | fontSize: 50, | ||
55 | |||
56 | '& sup': { | ||
57 | fontSize: 20, | ||
58 | verticalAlign: 20, | ||
59 | }, | ||
60 | }, | ||
61 | interval: { | ||
62 | // paddingBottom: 40, | ||
63 | }, | ||
64 | text: { | ||
65 | marginBottom: 'auto', | ||
66 | }, | ||
67 | cta: { | ||
68 | background: theme.styleTypes.primary.accent, | ||
69 | color: theme.styleTypes.primary.contrast, | ||
70 | margin: [40, 'auto', 0, 'auto'], | ||
71 | |||
72 | // '&:active': { | ||
73 | // opacity: 0.7, | ||
74 | // }, | ||
75 | }, | ||
76 | divider: { | ||
77 | width: 40, | ||
78 | border: 0, | ||
79 | borderTop: [1, 'solid', theme.styleTypes.primary.contrast], | ||
80 | margin: [30, 'auto'], | ||
81 | }, | ||
82 | header: { | ||
83 | padding: 20, | ||
84 | background: color(theme.styleTypes.primary.accent).darken(0.25).hex(), | ||
85 | color: theme.styleTypes.primary.contrast, | ||
86 | }, | ||
87 | content: { | ||
88 | padding: 20, | ||
89 | // border: [1, 'solid', 'red'], | ||
90 | background: '#EFEFEF', | ||
91 | }, | ||
92 | simpleCTA: { | ||
93 | background: 'none', | ||
94 | color: theme.styleTypes.primary.accent, | ||
95 | |||
96 | '& svg': { | ||
97 | fill: theme.styleTypes.primary.accent, | ||
98 | }, | ||
99 | }, | ||
100 | }); | ||
101 | |||
102 | |||
103 | export default @observer @injectSheet(styles) class PlanItem extends Component { | ||
104 | static propTypes = { | ||
105 | name: PropTypes.string.isRequired, | ||
106 | text: PropTypes.string.isRequired, | ||
107 | price: PropTypes.number.isRequired, | ||
108 | currency: PropTypes.string.isRequired, | ||
109 | upgrade: PropTypes.func.isRequired, | ||
110 | ctaLabel: PropTypes.string.isRequired, | ||
111 | simpleCTA: PropTypes.bool, | ||
112 | perUser: PropTypes.bool, | ||
113 | classes: PropTypes.object.isRequired, | ||
114 | children: PropTypes.element, | ||
115 | }; | ||
116 | |||
117 | static defaultProps = { | ||
118 | simpleCTA: false, | ||
119 | perUser: false, | ||
120 | children: null, | ||
121 | } | ||
122 | |||
123 | static contextTypes = { | ||
124 | intl: intlShape, | ||
125 | }; | ||
126 | |||
127 | render() { | ||
128 | const { | ||
129 | name, | ||
130 | text, | ||
131 | price, | ||
132 | currency, | ||
133 | classes, | ||
134 | upgrade, | ||
135 | ctaLabel, | ||
136 | simpleCTA, | ||
137 | perUser, | ||
138 | children, | ||
139 | } = this.props; | ||
140 | const { intl } = this.context; | ||
141 | |||
142 | const priceParts = `${price}`.split('.'); | ||
143 | // const intervalName = i18nIntervalName(PAYMENT_INTERVAL.MONTHLY, intl); | ||
144 | |||
145 | return ( | ||
146 | <div className={classes.root}> | ||
147 | <div className={classes.header}> | ||
148 | <H2 className={classes.planName}>{name}</H2> | ||
149 | <p className={classes.text}> | ||
150 | {text} | ||
151 | </p> | ||
152 | <hr className={classes.divider} /> | ||
153 | <p className={classes.priceWrapper}> | ||
154 | <span className={classes.currency}>{currency}</span> | ||
155 | <span className={classes.price}> | ||
156 | {priceParts[0]} | ||
157 | <sup>{priceParts[1]}</sup> | ||
158 | </span> | ||
159 | </p> | ||
160 | <p className={classes.interval}> | ||
161 | {intl.formatMessage(perUser ? messages.perMonthPerUser : messages.perMonth)} | ||
162 | </p> | ||
163 | </div> | ||
164 | |||
165 | <div className={classes.content}> | ||
166 | {children} | ||
167 | |||
168 | <Button | ||
169 | className={classnames({ | ||
170 | [classes.cta]: true, | ||
171 | [classes.simpleCTA]: simpleCTA, | ||
172 | })} | ||
173 | icon={simpleCTA ? mdiArrowRight : null} | ||
174 | label={( | ||
175 | <> | ||
176 | {ctaLabel} | ||
177 | </> | ||
178 | )} | ||
179 | onClick={upgrade} | ||
180 | /> | ||
181 | </div> | ||
182 | |||
183 | </div> | ||
184 | ); | ||
185 | } | ||
186 | } | ||
diff --git a/src/features/planSelection/components/PlanSelection.js b/src/features/planSelection/components/PlanSelection.js new file mode 100644 index 000000000..84d2d9e89 --- /dev/null +++ b/src/features/planSelection/components/PlanSelection.js | |||
@@ -0,0 +1,233 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | ||
4 | import injectSheet from 'react-jss'; | ||
5 | import { defineMessages, intlShape } from 'react-intl'; | ||
6 | import { H1, H2, Icon } from '@meetfranz/ui'; | ||
7 | import color from 'color'; | ||
8 | |||
9 | import { mdiRocket } from '@mdi/js'; | ||
10 | import PlanItem from './PlanItem'; | ||
11 | import { i18nPlanName } from '../../../helpers/plan-helpers'; | ||
12 | import { PLANS } from '../../../config'; | ||
13 | import { FeatureList } from '../../../components/ui/FeatureList'; | ||
14 | |||
15 | const messages = defineMessages({ | ||
16 | welcome: { | ||
17 | id: 'feature.planSelection.fullscreen.welcome', | ||
18 | defaultMessage: '!!!Welcome back, {name}', | ||
19 | }, | ||
20 | subheadline: { | ||
21 | id: 'feature.planSelection.fullscreen.subheadline', | ||
22 | defaultMessage: '!!!It\'s time to make a choice. Franz works best on our Personal and Professional plans. Please have a look and choose the best one for you.', | ||
23 | }, | ||
24 | textFree: { | ||
25 | id: 'feature.planSelection.free.text', | ||
26 | defaultMessage: '!!!Basic functionality', | ||
27 | }, | ||
28 | textPersonal: { | ||
29 | id: 'feature.planSelection.personal.text', | ||
30 | defaultMessage: '!!!More services, no waiting - ideal for personal use.', | ||
31 | }, | ||
32 | textProfessional: { | ||
33 | id: 'feature.planSelection.pro.text', | ||
34 | defaultMessage: '!!!Unlimited services and professional features for you - and your team.', | ||
35 | }, | ||
36 | ctaStayOnFree: { | ||
37 | id: 'feature.planSelection.cta.stayOnFree', | ||
38 | defaultMessage: '!!!Stay on Free', | ||
39 | }, | ||
40 | ctaDowngradeFree: { | ||
41 | id: 'feature.planSelection.cta.ctaDowngradeFree', | ||
42 | defaultMessage: '!!!Downgrade to Free', | ||
43 | }, | ||
44 | actionTrial: { | ||
45 | id: 'feature.planSelection.cta.trial', | ||
46 | defaultMessage: '!!!Start my free 14-days Trial', | ||
47 | }, | ||
48 | shortActionPersonal: { | ||
49 | id: 'feature.planSelection.cta.upgradePersonal', | ||
50 | defaultMessage: '!!!Choose Personal', | ||
51 | }, | ||
52 | shortActionPro: { | ||
53 | id: 'feature.planSelection.cta.upgradePro', | ||
54 | defaultMessage: '!!!Choose Professional', | ||
55 | }, | ||
56 | fullFeatureList: { | ||
57 | id: 'feature.planSelection.fullFeatureList', | ||
58 | defaultMessage: '!!!Complete comparison of all plans', | ||
59 | }, | ||
60 | }); | ||
61 | |||
62 | const styles = theme => ({ | ||
63 | root: { | ||
64 | background: theme.colorModalOverlayBackground, | ||
65 | width: '100%', | ||
66 | height: '100%', | ||
67 | position: 'absolute', | ||
68 | top: 0, | ||
69 | left: 0, | ||
70 | display: 'flex', | ||
71 | justifyContent: 'center', | ||
72 | alignItems: 'center', | ||
73 | zIndex: 999999, | ||
74 | }, | ||
75 | container: { | ||
76 | width: '80%', | ||
77 | height: 'auto', | ||
78 | background: theme.styleTypes.primary.accent, | ||
79 | padding: 40, | ||
80 | borderRadius: theme.borderRadius, | ||
81 | maxWidth: 1000, | ||
82 | |||
83 | '& h1, & h2': { | ||
84 | textAlign: 'center', | ||
85 | }, | ||
86 | }, | ||
87 | plans: { | ||
88 | display: 'flex', | ||
89 | margin: [40, 0, 0], | ||
90 | height: 'auto', | ||
91 | |||
92 | '& > div': { | ||
93 | margin: [0, 15], | ||
94 | flex: 1, | ||
95 | height: 'auto', | ||
96 | background: theme.styleTypes.primary.contrast, | ||
97 | boxShadow: [0, 2, 30, color('#000').alpha(0.1).rgb().string()], | ||
98 | }, | ||
99 | }, | ||
100 | bigIcon: { | ||
101 | background: theme.styleTypes.danger.accent, | ||
102 | width: 120, | ||
103 | height: 120, | ||
104 | display: 'flex', | ||
105 | alignItems: 'center', | ||
106 | borderRadius: '100%', | ||
107 | justifyContent: 'center', | ||
108 | margin: [-100, 'auto', 20], | ||
109 | |||
110 | '& svg': { | ||
111 | width: '80px !important', | ||
112 | }, | ||
113 | }, | ||
114 | headline: { | ||
115 | fontSize: 40, | ||
116 | }, | ||
117 | subheadline: { | ||
118 | maxWidth: 660, | ||
119 | fontSize: 22, | ||
120 | lineHeight: 1.1, | ||
121 | margin: [0, 'auto'], | ||
122 | }, | ||
123 | featureList: { | ||
124 | '& li': { | ||
125 | borderBottom: [1, 'solid', '#CECECE'], | ||
126 | }, | ||
127 | }, | ||
128 | fullFeatureList: { | ||
129 | marginTop: 40, | ||
130 | textAlign: 'center', | ||
131 | display: 'block', | ||
132 | color: `${theme.styleTypes.primary.contrast} !important`, | ||
133 | }, | ||
134 | }); | ||
135 | |||
136 | @injectSheet(styles) @observer | ||
137 | class PlanSelection extends Component { | ||
138 | static propTypes = { | ||
139 | classes: PropTypes.object.isRequired, | ||
140 | firstname: PropTypes.string.isRequired, | ||
141 | plans: PropTypes.object.isRequired, | ||
142 | currency: PropTypes.string.isRequired, | ||
143 | subscriptionExpired: PropTypes.bool.isRequired, | ||
144 | upgradeAccount: PropTypes.func.isRequired, | ||
145 | stayOnFree: PropTypes.func.isRequired, | ||
146 | hadSubscription: PropTypes.bool.isRequired, | ||
147 | }; | ||
148 | |||
149 | static contextTypes = { | ||
150 | intl: intlShape, | ||
151 | }; | ||
152 | |||
153 | render() { | ||
154 | const { | ||
155 | classes, | ||
156 | firstname, | ||
157 | plans, | ||
158 | currency, | ||
159 | subscriptionExpired, | ||
160 | upgradeAccount, | ||
161 | stayOnFree, | ||
162 | hadSubscription, | ||
163 | } = this.props; | ||
164 | |||
165 | const { intl } = this.context; | ||
166 | |||
167 | return ( | ||
168 | <div | ||
169 | className={classes.root} | ||
170 | > | ||
171 | <div className={classes.container}> | ||
172 | <div className={classes.bigIcon}> | ||
173 | <Icon icon={mdiRocket} /> | ||
174 | </div> | ||
175 | <H1 className={classes.headline}>{intl.formatMessage(messages.welcome, { name: firstname })}</H1> | ||
176 | <H2 className={classes.subheadline}>{intl.formatMessage(messages.subheadline)}</H2> | ||
177 | <div className={classes.plans}> | ||
178 | <PlanItem | ||
179 | name={i18nPlanName(PLANS.FREE, intl)} | ||
180 | text={intl.formatMessage(messages.textFree)} | ||
181 | price={0} | ||
182 | currency={currency} | ||
183 | ctaLabel={intl.formatMessage(subscriptionExpired ? messages.ctaDowngradeFree : messages.ctaStayOnFree)} | ||
184 | upgrade={() => stayOnFree()} | ||
185 | simpleCTA | ||
186 | > | ||
187 | <FeatureList | ||
188 | plan={PLANS.FREE} | ||
189 | className={classes.featureList} | ||
190 | /> | ||
191 | </PlanItem> | ||
192 | <PlanItem | ||
193 | name={i18nPlanName(plans.personal.yearly.id, intl)} | ||
194 | text={intl.formatMessage(messages.textPersonal)} | ||
195 | price={plans.personal.yearly.price} | ||
196 | currency={currency} | ||
197 | ctaLabel={intl.formatMessage(hadSubscription ? messages.shortActionPersonal : messages.actionTrial)} | ||
198 | upgrade={() => upgradeAccount(plans.personal.yearly.id)} | ||
199 | > | ||
200 | <FeatureList | ||
201 | plan={PLANS.PERSONAL} | ||
202 | className={classes.featureList} | ||
203 | /> | ||
204 | </PlanItem> | ||
205 | <PlanItem | ||
206 | name={i18nPlanName(plans.pro.yearly.id, intl)} | ||
207 | text={intl.formatMessage(messages.textProfessional)} | ||
208 | price={plans.pro.yearly.price} | ||
209 | currency={currency} | ||
210 | ctaLabel={intl.formatMessage(hadSubscription ? messages.shortActionPro : messages.actionTrial)} | ||
211 | upgrade={() => upgradeAccount(plans.personal.yearly.id)} | ||
212 | perUser | ||
213 | > | ||
214 | <FeatureList | ||
215 | plan={PLANS.PRO} | ||
216 | className={classes.featureList} | ||
217 | /> | ||
218 | </PlanItem> | ||
219 | </div> | ||
220 | <a | ||
221 | href="https://meetfranz.com/pricing" | ||
222 | target="_blank" | ||
223 | className={classes.fullFeatureList} | ||
224 | > | ||
225 | {intl.formatMessage(messages.fullFeatureList)} | ||
226 | </a> | ||
227 | </div> | ||
228 | </div> | ||
229 | ); | ||
230 | } | ||
231 | } | ||
232 | |||
233 | export default PlanSelection; | ||
diff --git a/src/features/planSelection/containers/PlanSelectionScreen.js b/src/features/planSelection/containers/PlanSelectionScreen.js new file mode 100644 index 000000000..b0d9b5ab5 --- /dev/null +++ b/src/features/planSelection/containers/PlanSelectionScreen.js | |||
@@ -0,0 +1,138 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import { observer, inject } from 'mobx-react'; | ||
3 | import PropTypes from 'prop-types'; | ||
4 | import { remote } from 'electron'; | ||
5 | import { defineMessages, intlShape } from 'react-intl'; | ||
6 | |||
7 | import FeaturesStore from '../../../stores/FeaturesStore'; | ||
8 | import UserStore from '../../../stores/UserStore'; | ||
9 | import PlanSelection from '../components/PlanSelection'; | ||
10 | import ErrorBoundary from '../../../components/util/ErrorBoundary'; | ||
11 | import { planSelectionStore } from '..'; | ||
12 | |||
13 | const { dialog, app } = remote; | ||
14 | |||
15 | const messages = defineMessages({ | ||
16 | dialogTitle: { | ||
17 | id: 'feature.planSelection.fullscreen.dialog.title', | ||
18 | defaultMessage: '!!!Downgrade your Franz Plan', | ||
19 | }, | ||
20 | dialogMessage: { | ||
21 | id: 'feature.planSelection.fullscreen.dialog.message', | ||
22 | defaultMessage: '!!!You\'re about to downgrade to our Free account. Are you sure? Click here instead to get more services and functionality for just {currency}{price} a month.', | ||
23 | }, | ||
24 | dialogCTADowngrade: { | ||
25 | id: 'feature.planSelection.fullscreen.dialog.cta.downgrade', | ||
26 | defaultMessage: '!!!Downgrade to Free', | ||
27 | }, | ||
28 | dialogCTAUpgrade: { | ||
29 | id: 'feature.planSelection.fullscreen.dialog.cta.upgrade', | ||
30 | defaultMessage: '!!!Choose Personal', | ||
31 | }, | ||
32 | }); | ||
33 | |||
34 | @inject('stores', 'actions') @observer | ||
35 | class PlanSelectionScreen extends Component { | ||
36 | static contextTypes = { | ||
37 | intl: intlShape, | ||
38 | }; | ||
39 | |||
40 | upgradeAccount(planId) { | ||
41 | const { user, features } = this.props.stores; | ||
42 | const { upgradeAccount, hideOverlay } = this.props.actions.planSelection; | ||
43 | |||
44 | upgradeAccount({ | ||
45 | planId, | ||
46 | onCloseWindow: () => { | ||
47 | hideOverlay(); | ||
48 | user.getUserInfoRequest.invalidate({ immediately: true }); | ||
49 | features.featuresRequest.invalidate({ immediately: true }); | ||
50 | }, | ||
51 | }); | ||
52 | } | ||
53 | |||
54 | render() { | ||
55 | if (!planSelectionStore || !planSelectionStore.isFeatureActive || !planSelectionStore.showPlanSelectionOverlay) { | ||
56 | return null; | ||
57 | } | ||
58 | |||
59 | const { intl } = this.context; | ||
60 | |||
61 | const { user, features } = this.props.stores; | ||
62 | const { plans, currency } = features.features.pricingConfig; | ||
63 | const { activateTrial } = this.props.actions.user; | ||
64 | const { upgradeAccount, downgradeAccount, hideOverlay } = this.props.actions.planSelection; | ||
65 | |||
66 | // const planConfig = [{ | ||
67 | // id: 'free', | ||
68 | // price: 0, | ||
69 | // }, { | ||
70 | // id: plans.personal.yearly.id, | ||
71 | // price: plans.personal.yearly.price, | ||
72 | // }, { | ||
73 | // id: plans.pro.yearly.id, | ||
74 | // price: plans.pro.yearly.price, | ||
75 | // }]; | ||
76 | |||
77 | return ( | ||
78 | <ErrorBoundary> | ||
79 | <PlanSelection | ||
80 | firstname={user.data.firstname} | ||
81 | plans={plans} | ||
82 | currency={currency} | ||
83 | upgradeAccount={(planId) => { | ||
84 | if (user.data.hadSubscription) { | ||
85 | this.upgradeAccount(planId); | ||
86 | } else { | ||
87 | activateTrial({ | ||
88 | planId, | ||
89 | }); | ||
90 | } | ||
91 | }} | ||
92 | stayOnFree={() => { | ||
93 | const selection = dialog.showMessageBoxSync(app.mainWindow, { | ||
94 | type: 'question', | ||
95 | message: intl.formatMessage(messages.dialogTitle), | ||
96 | detail: intl.formatMessage(messages.dialogMessage, { | ||
97 | currency, | ||
98 | price: plans.personal.yearly.price, | ||
99 | }), | ||
100 | buttons: [ | ||
101 | intl.formatMessage(messages.dialogCTADowngrade), | ||
102 | intl.formatMessage(messages.dialogCTAUpgrade), | ||
103 | ], | ||
104 | }); | ||
105 | |||
106 | if (selection === 0) { | ||
107 | downgradeAccount(); | ||
108 | hideOverlay(); | ||
109 | } else { | ||
110 | upgradeAccount(plans.personal.yearly.id); | ||
111 | } | ||
112 | }} | ||
113 | subscriptionExpired={user.team && user.team.state === 'expired' && !user.team.userHasDowngraded} | ||
114 | hadSubscription={user.data.hadSubscription} | ||
115 | /> | ||
116 | </ErrorBoundary> | ||
117 | ); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | export default PlanSelectionScreen; | ||
122 | |||
123 | PlanSelectionScreen.wrappedComponent.propTypes = { | ||
124 | stores: PropTypes.shape({ | ||
125 | features: PropTypes.instanceOf(FeaturesStore).isRequired, | ||
126 | user: PropTypes.instanceOf(UserStore).isRequired, | ||
127 | }).isRequired, | ||
128 | actions: PropTypes.shape({ | ||
129 | planSelection: PropTypes.shape({ | ||
130 | upgradeAccount: PropTypes.func.isRequired, | ||
131 | downgradeAccount: PropTypes.func.isRequired, | ||
132 | hideOverlay: PropTypes.func.isRequired, | ||
133 | }), | ||
134 | user: PropTypes.shape({ | ||
135 | activateTrial: PropTypes.func.isRequired, | ||
136 | }), | ||
137 | }).isRequired, | ||
138 | }; | ||
diff --git a/src/features/planSelection/index.js b/src/features/planSelection/index.js new file mode 100644 index 000000000..81189207a --- /dev/null +++ b/src/features/planSelection/index.js | |||
@@ -0,0 +1,30 @@ | |||
1 | import { reaction } from 'mobx'; | ||
2 | import PlanSelectionStore from './store'; | ||
3 | |||
4 | const debug = require('debug')('Franz:feature:planSelection'); | ||
5 | |||
6 | export const GA_CATEGORY_PLAN_SELECTION = 'planSelection'; | ||
7 | |||
8 | export const planSelectionStore = new PlanSelectionStore(); | ||
9 | |||
10 | export default function initPlanSelection(stores, actions) { | ||
11 | stores.planSelection = planSelectionStore; | ||
12 | const { features } = stores; | ||
13 | |||
14 | // Toggle planSelection feature | ||
15 | reaction( | ||
16 | () => features.features.isPlanSelectionEnabled, | ||
17 | (isEnabled) => { | ||
18 | if (isEnabled) { | ||
19 | debug('Initializing `planSelection` feature'); | ||
20 | planSelectionStore.start(stores, actions); | ||
21 | } else if (planSelectionStore.isFeatureActive) { | ||
22 | debug('Disabling `planSelection` feature'); | ||
23 | planSelectionStore.stop(); | ||
24 | } | ||
25 | }, | ||
26 | { | ||
27 | fireImmediately: true, | ||
28 | }, | ||
29 | ); | ||
30 | } | ||
diff --git a/src/features/planSelection/store.js b/src/features/planSelection/store.js new file mode 100644 index 000000000..50e46dfb3 --- /dev/null +++ b/src/features/planSelection/store.js | |||
@@ -0,0 +1,113 @@ | |||
1 | import { | ||
2 | action, | ||
3 | observable, | ||
4 | computed, | ||
5 | } from 'mobx'; | ||
6 | import { remote } from 'electron'; | ||
7 | |||
8 | import { planSelectionActions } from './actions'; | ||
9 | import { FeatureStore } from '../utils/FeatureStore'; | ||
10 | // import { createReactions } from '../../stores/lib/Reaction'; | ||
11 | import { createActionBindings } from '../utils/ActionBinding'; | ||
12 | import { downgradeUserRequest } from './api'; | ||
13 | |||
14 | const debug = require('debug')('Franz:feature:planSelection:store'); | ||
15 | |||
16 | const { BrowserWindow } = remote; | ||
17 | |||
18 | export default class PlanSelectionStore extends FeatureStore { | ||
19 | @observable isFeatureEnabled = false; | ||
20 | |||
21 | @observable isFeatureActive = false; | ||
22 | |||
23 | @observable hideOverlay = false; | ||
24 | |||
25 | @computed get showPlanSelectionOverlay() { | ||
26 | const { team } = this.stores.user; | ||
27 | if (team && !this.hideOverlay) { | ||
28 | return team.state === 'expired' && !team.userHasDowngraded; | ||
29 | } | ||
30 | |||
31 | return false; | ||
32 | } | ||
33 | |||
34 | // ========== PUBLIC API ========= // | ||
35 | |||
36 | @action start(stores, actions, api) { | ||
37 | debug('PlanSelectionStore::start'); | ||
38 | this.stores = stores; | ||
39 | this.actions = actions; | ||
40 | this.api = api; | ||
41 | |||
42 | // ACTIONS | ||
43 | |||
44 | this._registerActions(createActionBindings([ | ||
45 | [planSelectionActions.upgradeAccount, this._upgradeAccount], | ||
46 | [planSelectionActions.downgradeAccount, this._downgradeAccount], | ||
47 | [planSelectionActions.hideOverlay, this._hideOverlay], | ||
48 | ])); | ||
49 | |||
50 | // REACTIONS | ||
51 | |||
52 | // this._allReactions = createReactions([ | ||
53 | // this._setFeatureEnabledReaction, | ||
54 | // this._updateTodosConfig, | ||
55 | // this._firstLaunchReaction, | ||
56 | // this._routeCheckReaction, | ||
57 | // ]); | ||
58 | |||
59 | // this._registerReactions(this._allReactions); | ||
60 | |||
61 | this.isFeatureActive = true; | ||
62 | } | ||
63 | |||
64 | @action stop() { | ||
65 | super.stop(); | ||
66 | debug('PlanSelectionStore::stop'); | ||
67 | this.reset(); | ||
68 | this.isFeatureActive = false; | ||
69 | } | ||
70 | |||
71 | // ========== PRIVATE METHODS ========= // | ||
72 | |||
73 | // Actions | ||
74 | |||
75 | @action _upgradeAccount = ({ planId, onCloseWindow = () => null }) => { | ||
76 | let hostedPageURL = this.stores.features.features.subscribeURL; | ||
77 | |||
78 | const parsedUrl = new URL(hostedPageURL); | ||
79 | const params = new URLSearchParams(parsedUrl.search.slice(1)); | ||
80 | |||
81 | params.set('plan', planId); | ||
82 | |||
83 | hostedPageURL = this.stores.user.getAuthURL(`${parsedUrl.origin}${parsedUrl.pathname}?${params.toString()}`); | ||
84 | |||
85 | const win = new BrowserWindow({ | ||
86 | parent: remote.getCurrentWindow(), | ||
87 | modal: true, | ||
88 | title: '🔒 Upgrade Your Franz Account', | ||
89 | width: 800, | ||
90 | height: window.innerHeight - 100, | ||
91 | maxWidth: 800, | ||
92 | minWidth: 600, | ||
93 | webPreferences: { | ||
94 | nodeIntegration: true, | ||
95 | webviewTag: true, | ||
96 | }, | ||
97 | }); | ||
98 | win.loadURL(`file://${__dirname}/../../index.html#/payment/${encodeURIComponent(hostedPageURL)}`); | ||
99 | |||
100 | win.on('closed', () => { | ||
101 | onCloseWindow(); | ||
102 | }); | ||
103 | }; | ||
104 | |||
105 | @action _downgradeAccount = () => { | ||
106 | console.log('downgrade to free', downgradeUserRequest); | ||
107 | downgradeUserRequest.execute(); | ||
108 | } | ||
109 | |||
110 | @action _hideOverlay = () => { | ||
111 | this.hideOverlay = true; | ||
112 | } | ||
113 | } | ||
diff --git a/src/features/shareFranz/index.js b/src/features/shareFranz/index.js index 87deacef4..a39d7a6e6 100644 --- a/src/features/shareFranz/index.js +++ b/src/features/shareFranz/index.js | |||
@@ -3,6 +3,7 @@ import ms from 'ms'; | |||
3 | 3 | ||
4 | import { state as delayAppState } from '../delayApp'; | 4 | import { state as delayAppState } from '../delayApp'; |
5 | import { gaEvent, gaPage } from '../../lib/analytics'; | 5 | import { gaEvent, gaPage } from '../../lib/analytics'; |
6 | import { planSelectionStore } from '../planSelection'; | ||
6 | 7 | ||
7 | export { default as Component } from './Component'; | 8 | export { default as Component } from './Component'; |
8 | 9 | ||
@@ -35,7 +36,7 @@ export default function initialize(stores) { | |||
35 | () => stores.user.isLoggedIn, | 36 | () => stores.user.isLoggedIn, |
36 | () => { | 37 | () => { |
37 | setTimeout(() => { | 38 | setTimeout(() => { |
38 | if (stores.settings.stats.appStarts % 50 === 0) { | 39 | if (stores.settings.stats.appStarts % 50 === 0 && !planSelectionStore.showPlanSelectionOverlay) { |
39 | if (delayAppState.isDelayAppScreenVisible) { | 40 | if (delayAppState.isDelayAppScreenVisible) { |
40 | debug('Delaying share modal by 5 minutes'); | 41 | debug('Delaying share modal by 5 minutes'); |
41 | setTimeout(() => showModal(), ms('5m')); | 42 | setTimeout(() => showModal(), ms('5m')); |
diff --git a/src/helpers/plan-helpers.js b/src/helpers/plan-helpers.js index e0f1fd89a..ee22e4471 100644 --- a/src/helpers/plan-helpers.js +++ b/src/helpers/plan-helpers.js | |||
@@ -4,19 +4,19 @@ import { PLANS_MAPPING, PLANS } from '../config'; | |||
4 | const messages = defineMessages({ | 4 | const messages = defineMessages({ |
5 | [PLANS.PRO]: { | 5 | [PLANS.PRO]: { |
6 | id: 'pricing.plan.pro', | 6 | id: 'pricing.plan.pro', |
7 | defaultMessage: '!!!Franz Professional', | 7 | defaultMessage: '!!!Professional', |
8 | }, | 8 | }, |
9 | [PLANS.PERSONAL]: { | 9 | [PLANS.PERSONAL]: { |
10 | id: 'pricing.plan.personal', | 10 | id: 'pricing.plan.personal', |
11 | defaultMessage: '!!!Franz Personal', | 11 | defaultMessage: '!!!Personal', |
12 | }, | 12 | }, |
13 | [PLANS.FREE]: { | 13 | [PLANS.FREE]: { |
14 | id: 'pricing.plan.free', | 14 | id: 'pricing.plan.free', |
15 | defaultMessage: '!!!Franz Free', | 15 | defaultMessage: '!!!Free', |
16 | }, | 16 | }, |
17 | [PLANS.LEGACY]: { | 17 | [PLANS.LEGACY]: { |
18 | id: 'pricing.plan.legacy', | 18 | id: 'pricing.plan.legacy', |
19 | defaultMessage: '!!!Franz Premium', | 19 | defaultMessage: '!!!Premium', |
20 | }, | 20 | }, |
21 | }); | 21 | }); |
22 | 22 | ||
diff --git a/src/i18n/locales/defaultMessages.json b/src/i18n/locales/defaultMessages.json index 703f800f9..210ea001a 100644 --- a/src/i18n/locales/defaultMessages.json +++ b/src/i18n/locales/defaultMessages.json | |||
@@ -430,7 +430,7 @@ | |||
430 | } | 430 | } |
431 | }, | 431 | }, |
432 | { | 432 | { |
433 | "defaultMessage": "!!!Your personal welcome offer:", | 433 | "defaultMessage": "!!!Here's a special welcome for you:", |
434 | "end": { | 434 | "end": { |
435 | "column": 3, | 435 | "column": 3, |
436 | "line": 22 | 436 | "line": 22 |
@@ -495,7 +495,7 @@ | |||
495 | } | 495 | } |
496 | }, | 496 | }, |
497 | { | 497 | { |
498 | "defaultMessage": "!!!Yes, upgrade my account to Franz Professional", | 498 | "defaultMessage": "!!!Start my 14-day Franz Professional Trial", |
499 | "end": { | 499 | "end": { |
500 | "column": 3, | 500 | "column": 3, |
501 | "line": 42 | 501 | "line": 42 |
@@ -721,39 +721,39 @@ | |||
721 | "defaultMessage": "!!!Your services have been updated.", | 721 | "defaultMessage": "!!!Your services have been updated.", |
722 | "end": { | 722 | "end": { |
723 | "column": 3, | 723 | "column": 3, |
724 | "line": 31 | 724 | "line": 32 |
725 | }, | 725 | }, |
726 | "file": "src/components/layout/AppLayout.js", | 726 | "file": "src/components/layout/AppLayout.js", |
727 | "id": "infobar.servicesUpdated", | 727 | "id": "infobar.servicesUpdated", |
728 | "start": { | 728 | "start": { |
729 | "column": 19, | 729 | "column": 19, |
730 | "line": 28 | 730 | "line": 29 |
731 | } | 731 | } |
732 | }, | 732 | }, |
733 | { | 733 | { |
734 | "defaultMessage": "!!!Reload services", | 734 | "defaultMessage": "!!!Reload services", |
735 | "end": { | 735 | "end": { |
736 | "column": 3, | 736 | "column": 3, |
737 | "line": 35 | 737 | "line": 36 |
738 | }, | 738 | }, |
739 | "file": "src/components/layout/AppLayout.js", | 739 | "file": "src/components/layout/AppLayout.js", |
740 | "id": "infobar.buttonReloadServices", | 740 | "id": "infobar.buttonReloadServices", |
741 | "start": { | 741 | "start": { |
742 | "column": 24, | 742 | "column": 24, |
743 | "line": 32 | 743 | "line": 33 |
744 | } | 744 | } |
745 | }, | 745 | }, |
746 | { | 746 | { |
747 | "defaultMessage": "!!!Could not load services and user information", | 747 | "defaultMessage": "!!!Could not load services and user information", |
748 | "end": { | 748 | "end": { |
749 | "column": 3, | 749 | "column": 3, |
750 | "line": 39 | 750 | "line": 40 |
751 | }, | 751 | }, |
752 | "file": "src/components/layout/AppLayout.js", | 752 | "file": "src/components/layout/AppLayout.js", |
753 | "id": "infobar.requiredRequestsFailed", | 753 | "id": "infobar.requiredRequestsFailed", |
754 | "start": { | 754 | "start": { |
755 | "column": 26, | 755 | "column": 26, |
756 | "line": 36 | 756 | "line": 37 |
757 | } | 757 | } |
758 | } | 758 | } |
759 | ], | 759 | ], |
@@ -3017,133 +3017,198 @@ | |||
3017 | { | 3017 | { |
3018 | "descriptors": [ | 3018 | "descriptors": [ |
3019 | { | 3019 | { |
3020 | "defaultMessage": "!!!Choose from more than 70 Services", | ||
3021 | "end": { | ||
3022 | "column": 3, | ||
3023 | "line": 12 | ||
3024 | }, | ||
3025 | "file": "src/components/ui/FeatureList.js", | ||
3026 | "id": "pricing.features.recipes", | ||
3027 | "start": { | ||
3028 | "column": 20, | ||
3029 | "line": 9 | ||
3030 | } | ||
3031 | }, | ||
3032 | { | ||
3033 | "defaultMessage": "!!!Account Synchronisation", | ||
3034 | "end": { | ||
3035 | "column": 3, | ||
3036 | "line": 16 | ||
3037 | }, | ||
3038 | "file": "src/components/ui/FeatureList.js", | ||
3039 | "id": "pricing.features.accountSync", | ||
3040 | "start": { | ||
3041 | "column": 15, | ||
3042 | "line": 13 | ||
3043 | } | ||
3044 | }, | ||
3045 | { | ||
3046 | "defaultMessage": "!!!Desktop Notifications", | ||
3047 | "end": { | ||
3048 | "column": 3, | ||
3049 | "line": 20 | ||
3050 | }, | ||
3051 | "file": "src/components/ui/FeatureList.js", | ||
3052 | "id": "pricing.features.desktopNotifications", | ||
3053 | "start": { | ||
3054 | "column": 24, | ||
3055 | "line": 17 | ||
3056 | } | ||
3057 | }, | ||
3058 | { | ||
3020 | "defaultMessage": "!!!Add unlimited services", | 3059 | "defaultMessage": "!!!Add unlimited services", |
3021 | "end": { | 3060 | "end": { |
3022 | "column": 3, | 3061 | "column": 3, |
3023 | "line": 11 | 3062 | "line": 24 |
3024 | }, | 3063 | }, |
3025 | "file": "src/components/ui/FeatureList.js", | 3064 | "file": "src/components/ui/FeatureList.js", |
3026 | "id": "pricing.features.unlimitedServices", | 3065 | "id": "pricing.features.unlimitedServices", |
3027 | "start": { | 3066 | "start": { |
3028 | "column": 21, | 3067 | "column": 21, |
3029 | "line": 8 | 3068 | "line": 21 |
3069 | } | ||
3070 | }, | ||
3071 | { | ||
3072 | "defaultMessage": "!!!Add up to 3 services", | ||
3073 | "end": { | ||
3074 | "column": 3, | ||
3075 | "line": 28 | ||
3076 | }, | ||
3077 | "file": "src/components/ui/FeatureList.js", | ||
3078 | "id": "pricing.features.upToThreeServices", | ||
3079 | "start": { | ||
3080 | "column": 21, | ||
3081 | "line": 25 | ||
3082 | } | ||
3083 | }, | ||
3084 | { | ||
3085 | "defaultMessage": "!!!Add up to 6 services", | ||
3086 | "end": { | ||
3087 | "column": 3, | ||
3088 | "line": 32 | ||
3089 | }, | ||
3090 | "file": "src/components/ui/FeatureList.js", | ||
3091 | "id": "pricing.features.upToSixServices", | ||
3092 | "start": { | ||
3093 | "column": 19, | ||
3094 | "line": 29 | ||
3030 | } | 3095 | } |
3031 | }, | 3096 | }, |
3032 | { | 3097 | { |
3033 | "defaultMessage": "!!!Spellchecker support", | 3098 | "defaultMessage": "!!!Spellchecker support", |
3034 | "end": { | 3099 | "end": { |
3035 | "column": 3, | 3100 | "column": 3, |
3036 | "line": 15 | 3101 | "line": 36 |
3037 | }, | 3102 | }, |
3038 | "file": "src/components/ui/FeatureList.js", | 3103 | "file": "src/components/ui/FeatureList.js", |
3039 | "id": "pricing.features.spellchecker", | 3104 | "id": "pricing.features.spellchecker", |
3040 | "start": { | 3105 | "start": { |
3041 | "column": 16, | 3106 | "column": 16, |
3042 | "line": 12 | 3107 | "line": 33 |
3043 | } | 3108 | } |
3044 | }, | 3109 | }, |
3045 | { | 3110 | { |
3046 | "defaultMessage": "!!!Workspaces", | 3111 | "defaultMessage": "!!!Workspaces", |
3047 | "end": { | 3112 | "end": { |
3048 | "column": 3, | 3113 | "column": 3, |
3049 | "line": 19 | 3114 | "line": 40 |
3050 | }, | 3115 | }, |
3051 | "file": "src/components/ui/FeatureList.js", | 3116 | "file": "src/components/ui/FeatureList.js", |
3052 | "id": "pricing.features.workspaces", | 3117 | "id": "pricing.features.workspaces", |
3053 | "start": { | 3118 | "start": { |
3054 | "column": 14, | 3119 | "column": 14, |
3055 | "line": 16 | 3120 | "line": 37 |
3056 | } | 3121 | } |
3057 | }, | 3122 | }, |
3058 | { | 3123 | { |
3059 | "defaultMessage": "!!!Add Custom Websites", | 3124 | "defaultMessage": "!!!Add Custom Websites", |
3060 | "end": { | 3125 | "end": { |
3061 | "column": 3, | 3126 | "column": 3, |
3062 | "line": 23 | 3127 | "line": 44 |
3063 | }, | 3128 | }, |
3064 | "file": "src/components/ui/FeatureList.js", | 3129 | "file": "src/components/ui/FeatureList.js", |
3065 | "id": "pricing.features.customWebsites", | 3130 | "id": "pricing.features.customWebsites", |
3066 | "start": { | 3131 | "start": { |
3067 | "column": 18, | 3132 | "column": 18, |
3068 | "line": 20 | 3133 | "line": 41 |
3069 | } | 3134 | } |
3070 | }, | 3135 | }, |
3071 | { | 3136 | { |
3072 | "defaultMessage": "!!!On-premise & other Hosted Services", | 3137 | "defaultMessage": "!!!On-premise & other Hosted Services", |
3073 | "end": { | 3138 | "end": { |
3074 | "column": 3, | 3139 | "column": 3, |
3075 | "line": 27 | 3140 | "line": 48 |
3076 | }, | 3141 | }, |
3077 | "file": "src/components/ui/FeatureList.js", | 3142 | "file": "src/components/ui/FeatureList.js", |
3078 | "id": "pricing.features.onPremise", | 3143 | "id": "pricing.features.onPremise", |
3079 | "start": { | 3144 | "start": { |
3080 | "column": 13, | 3145 | "column": 13, |
3081 | "line": 24 | 3146 | "line": 45 |
3082 | } | 3147 | } |
3083 | }, | 3148 | }, |
3084 | { | 3149 | { |
3085 | "defaultMessage": "!!!Install 3rd party services", | 3150 | "defaultMessage": "!!!Install 3rd party services", |
3086 | "end": { | 3151 | "end": { |
3087 | "column": 3, | 3152 | "column": 3, |
3088 | "line": 31 | 3153 | "line": 52 |
3089 | }, | 3154 | }, |
3090 | "file": "src/components/ui/FeatureList.js", | 3155 | "file": "src/components/ui/FeatureList.js", |
3091 | "id": "pricing.features.thirdPartyServices", | 3156 | "id": "pricing.features.thirdPartyServices", |
3092 | "start": { | 3157 | "start": { |
3093 | "column": 22, | 3158 | "column": 22, |
3094 | "line": 28 | 3159 | "line": 49 |
3095 | } | 3160 | } |
3096 | }, | 3161 | }, |
3097 | { | 3162 | { |
3098 | "defaultMessage": "!!!Service Proxies", | 3163 | "defaultMessage": "!!!Service Proxies", |
3099 | "end": { | 3164 | "end": { |
3100 | "column": 3, | 3165 | "column": 3, |
3101 | "line": 35 | 3166 | "line": 56 |
3102 | }, | 3167 | }, |
3103 | "file": "src/components/ui/FeatureList.js", | 3168 | "file": "src/components/ui/FeatureList.js", |
3104 | "id": "pricing.features.serviceProxies", | 3169 | "id": "pricing.features.serviceProxies", |
3105 | "start": { | 3170 | "start": { |
3106 | "column": 18, | 3171 | "column": 18, |
3107 | "line": 32 | 3172 | "line": 53 |
3108 | } | 3173 | } |
3109 | }, | 3174 | }, |
3110 | { | 3175 | { |
3111 | "defaultMessage": "!!!Team Management", | 3176 | "defaultMessage": "!!!Team Management", |
3112 | "end": { | 3177 | "end": { |
3113 | "column": 3, | 3178 | "column": 3, |
3114 | "line": 39 | 3179 | "line": 60 |
3115 | }, | 3180 | }, |
3116 | "file": "src/components/ui/FeatureList.js", | 3181 | "file": "src/components/ui/FeatureList.js", |
3117 | "id": "pricing.features.teamManagement", | 3182 | "id": "pricing.features.teamManagement", |
3118 | "start": { | 3183 | "start": { |
3119 | "column": 18, | 3184 | "column": 18, |
3120 | "line": 36 | 3185 | "line": 57 |
3121 | } | 3186 | } |
3122 | }, | 3187 | }, |
3123 | { | 3188 | { |
3124 | "defaultMessage": "!!!No Waiting Screens", | 3189 | "defaultMessage": "!!!No Waiting Screens", |
3125 | "end": { | 3190 | "end": { |
3126 | "column": 3, | 3191 | "column": 3, |
3127 | "line": 43 | 3192 | "line": 64 |
3128 | }, | 3193 | }, |
3129 | "file": "src/components/ui/FeatureList.js", | 3194 | "file": "src/components/ui/FeatureList.js", |
3130 | "id": "pricing.features.appDelays", | 3195 | "id": "pricing.features.appDelays", |
3131 | "start": { | 3196 | "start": { |
3132 | "column": 13, | 3197 | "column": 13, |
3133 | "line": 40 | 3198 | "line": 61 |
3134 | } | 3199 | } |
3135 | }, | 3200 | }, |
3136 | { | 3201 | { |
3137 | "defaultMessage": "!!!Forever ad-free", | 3202 | "defaultMessage": "!!!Forever ad-free", |
3138 | "end": { | 3203 | "end": { |
3139 | "column": 3, | 3204 | "column": 3, |
3140 | "line": 47 | 3205 | "line": 68 |
3141 | }, | 3206 | }, |
3142 | "file": "src/components/ui/FeatureList.js", | 3207 | "file": "src/components/ui/FeatureList.js", |
3143 | "id": "pricing.features.adFree", | 3208 | "id": "pricing.features.adFree", |
3144 | "start": { | 3209 | "start": { |
3145 | "column": 10, | 3210 | "column": 10, |
3146 | "line": 44 | 3211 | "line": 65 |
3147 | } | 3212 | } |
3148 | } | 3213 | } |
3149 | ], | 3214 | ], |
@@ -3834,6 +3899,273 @@ | |||
3834 | { | 3899 | { |
3835 | "descriptors": [ | 3900 | "descriptors": [ |
3836 | { | 3901 | { |
3902 | "defaultMessage": "!!!per month", | ||
3903 | "end": { | ||
3904 | "column": 3, | ||
3905 | "line": 22 | ||
3906 | }, | ||
3907 | "file": "src/features/planSelection/components/PlanItem.js", | ||
3908 | "id": "subscription.interval.perMonth", | ||
3909 | "start": { | ||
3910 | "column": 12, | ||
3911 | "line": 19 | ||
3912 | } | ||
3913 | }, | ||
3914 | { | ||
3915 | "defaultMessage": "!!!per month & user", | ||
3916 | "end": { | ||
3917 | "column": 3, | ||
3918 | "line": 26 | ||
3919 | }, | ||
3920 | "file": "src/features/planSelection/components/PlanItem.js", | ||
3921 | "id": "subscription.interval.perMonthPerUser", | ||
3922 | "start": { | ||
3923 | "column": 19, | ||
3924 | "line": 23 | ||
3925 | } | ||
3926 | } | ||
3927 | ], | ||
3928 | "path": "src/features/planSelection/components/PlanItem.json" | ||
3929 | }, | ||
3930 | { | ||
3931 | "descriptors": [ | ||
3932 | { | ||
3933 | "defaultMessage": "!!!Welcome back, {name}", | ||
3934 | "end": { | ||
3935 | "column": 3, | ||
3936 | "line": 19 | ||
3937 | }, | ||
3938 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
3939 | "id": "feature.planSelection.fullscreen.welcome", | ||
3940 | "start": { | ||
3941 | "column": 11, | ||
3942 | "line": 16 | ||
3943 | } | ||
3944 | }, | ||
3945 | { | ||
3946 | "defaultMessage": "!!!It's time to make a choice. Franz works best on our Personal and Professional plans. Please have a look and choose the best one for you.", | ||
3947 | "end": { | ||
3948 | "column": 3, | ||
3949 | "line": 23 | ||
3950 | }, | ||
3951 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
3952 | "id": "feature.planSelection.fullscreen.subheadline", | ||
3953 | "start": { | ||
3954 | "column": 15, | ||
3955 | "line": 20 | ||
3956 | } | ||
3957 | }, | ||
3958 | { | ||
3959 | "defaultMessage": "!!!Basic functionality", | ||
3960 | "end": { | ||
3961 | "column": 3, | ||
3962 | "line": 27 | ||
3963 | }, | ||
3964 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
3965 | "id": "feature.planSelection.free.text", | ||
3966 | "start": { | ||
3967 | "column": 12, | ||
3968 | "line": 24 | ||
3969 | } | ||
3970 | }, | ||
3971 | { | ||
3972 | "defaultMessage": "!!!More services, no waiting - ideal for personal use.", | ||
3973 | "end": { | ||
3974 | "column": 3, | ||
3975 | "line": 31 | ||
3976 | }, | ||
3977 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
3978 | "id": "feature.planSelection.personal.text", | ||
3979 | "start": { | ||
3980 | "column": 16, | ||
3981 | "line": 28 | ||
3982 | } | ||
3983 | }, | ||
3984 | { | ||
3985 | "defaultMessage": "!!!Unlimited services and professional features for you - and your team.", | ||
3986 | "end": { | ||
3987 | "column": 3, | ||
3988 | "line": 35 | ||
3989 | }, | ||
3990 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
3991 | "id": "feature.planSelection.pro.text", | ||
3992 | "start": { | ||
3993 | "column": 20, | ||
3994 | "line": 32 | ||
3995 | } | ||
3996 | }, | ||
3997 | { | ||
3998 | "defaultMessage": "!!!Stay on Free", | ||
3999 | "end": { | ||
4000 | "column": 3, | ||
4001 | "line": 39 | ||
4002 | }, | ||
4003 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
4004 | "id": "feature.planSelection.cta.stayOnFree", | ||
4005 | "start": { | ||
4006 | "column": 17, | ||
4007 | "line": 36 | ||
4008 | } | ||
4009 | }, | ||
4010 | { | ||
4011 | "defaultMessage": "!!!Downgrade to Free", | ||
4012 | "end": { | ||
4013 | "column": 3, | ||
4014 | "line": 43 | ||
4015 | }, | ||
4016 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
4017 | "id": "feature.planSelection.cta.ctaDowngradeFree", | ||
4018 | "start": { | ||
4019 | "column": 20, | ||
4020 | "line": 40 | ||
4021 | } | ||
4022 | }, | ||
4023 | { | ||
4024 | "defaultMessage": "!!!Start my free 14-days Trial", | ||
4025 | "end": { | ||
4026 | "column": 3, | ||
4027 | "line": 47 | ||
4028 | }, | ||
4029 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
4030 | "id": "feature.planSelection.cta.trial", | ||
4031 | "start": { | ||
4032 | "column": 15, | ||
4033 | "line": 44 | ||
4034 | } | ||
4035 | }, | ||
4036 | { | ||
4037 | "defaultMessage": "!!!Choose Personal", | ||
4038 | "end": { | ||
4039 | "column": 3, | ||
4040 | "line": 51 | ||
4041 | }, | ||
4042 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
4043 | "id": "feature.planSelection.cta.upgradePersonal", | ||
4044 | "start": { | ||
4045 | "column": 23, | ||
4046 | "line": 48 | ||
4047 | } | ||
4048 | }, | ||
4049 | { | ||
4050 | "defaultMessage": "!!!Choose Professional", | ||
4051 | "end": { | ||
4052 | "column": 3, | ||
4053 | "line": 55 | ||
4054 | }, | ||
4055 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
4056 | "id": "feature.planSelection.cta.upgradePro", | ||
4057 | "start": { | ||
4058 | "column": 18, | ||
4059 | "line": 52 | ||
4060 | } | ||
4061 | }, | ||
4062 | { | ||
4063 | "defaultMessage": "!!!Complete comparison of all plans", | ||
4064 | "end": { | ||
4065 | "column": 3, | ||
4066 | "line": 59 | ||
4067 | }, | ||
4068 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
4069 | "id": "feature.planSelection.fullFeatureList", | ||
4070 | "start": { | ||
4071 | "column": 19, | ||
4072 | "line": 56 | ||
4073 | } | ||
4074 | } | ||
4075 | ], | ||
4076 | "path": "src/features/planSelection/components/PlanSelection.json" | ||
4077 | }, | ||
4078 | { | ||
4079 | "descriptors": [ | ||
4080 | { | ||
4081 | "defaultMessage": "!!!per {interval}", | ||
4082 | "end": { | ||
4083 | "column": 3, | ||
4084 | "line": 19 | ||
4085 | }, | ||
4086 | "file": "src/features/planSelection/components/PlanTeaser.js", | ||
4087 | "id": "subscription.interval.per", | ||
4088 | "start": { | ||
4089 | "column": 7, | ||
4090 | "line": 16 | ||
4091 | } | ||
4092 | }, | ||
4093 | { | ||
4094 | "defaultMessage": "!!!Upgrade Account", | ||
4095 | "end": { | ||
4096 | "column": 3, | ||
4097 | "line": 23 | ||
4098 | }, | ||
4099 | "file": "src/features/planSelection/components/PlanTeaser.js", | ||
4100 | "id": "subscription.planItem.upgradeAccount", | ||
4101 | "start": { | ||
4102 | "column": 7, | ||
4103 | "line": 20 | ||
4104 | } | ||
4105 | } | ||
4106 | ], | ||
4107 | "path": "src/features/planSelection/components/PlanTeaser.json" | ||
4108 | }, | ||
4109 | { | ||
4110 | "descriptors": [ | ||
4111 | { | ||
4112 | "defaultMessage": "!!!Downgrade your Franz Plan", | ||
4113 | "end": { | ||
4114 | "column": 3, | ||
4115 | "line": 19 | ||
4116 | }, | ||
4117 | "file": "src/features/planSelection/containers/PlanSelectionScreen.js", | ||
4118 | "id": "feature.planSelection.fullscreen.dialog.title", | ||
4119 | "start": { | ||
4120 | "column": 15, | ||
4121 | "line": 16 | ||
4122 | } | ||
4123 | }, | ||
4124 | { | ||
4125 | "defaultMessage": "!!!You're about to downgrade to our Free account. Are you sure? Click here instead to get more services and functionality for just {currency}{price} a month.", | ||
4126 | "end": { | ||
4127 | "column": 3, | ||
4128 | "line": 23 | ||
4129 | }, | ||
4130 | "file": "src/features/planSelection/containers/PlanSelectionScreen.js", | ||
4131 | "id": "feature.planSelection.fullscreen.dialog.message", | ||
4132 | "start": { | ||
4133 | "column": 17, | ||
4134 | "line": 20 | ||
4135 | } | ||
4136 | }, | ||
4137 | { | ||
4138 | "defaultMessage": "!!!Downgrade to Free", | ||
4139 | "end": { | ||
4140 | "column": 3, | ||
4141 | "line": 27 | ||
4142 | }, | ||
4143 | "file": "src/features/planSelection/containers/PlanSelectionScreen.js", | ||
4144 | "id": "feature.planSelection.fullscreen.dialog.cta.downgrade", | ||
4145 | "start": { | ||
4146 | "column": 22, | ||
4147 | "line": 24 | ||
4148 | } | ||
4149 | }, | ||
4150 | { | ||
4151 | "defaultMessage": "!!!Choose Personal", | ||
4152 | "end": { | ||
4153 | "column": 3, | ||
4154 | "line": 31 | ||
4155 | }, | ||
4156 | "file": "src/features/planSelection/containers/PlanSelectionScreen.js", | ||
4157 | "id": "feature.planSelection.fullscreen.dialog.cta.upgrade", | ||
4158 | "start": { | ||
4159 | "column": 20, | ||
4160 | "line": 28 | ||
4161 | } | ||
4162 | } | ||
4163 | ], | ||
4164 | "path": "src/features/planSelection/containers/PlanSelectionScreen.json" | ||
4165 | }, | ||
4166 | { | ||
4167 | "descriptors": [ | ||
4168 | { | ||
3837 | "defaultMessage": "!!!Changes in Franz {version}", | 4169 | "defaultMessage": "!!!Changes in Franz {version}", |
3838 | "end": { | 4170 | "end": { |
3839 | "column": 3, | 4171 | "column": 3, |
@@ -4487,7 +4819,7 @@ | |||
4487 | { | 4819 | { |
4488 | "descriptors": [ | 4820 | "descriptors": [ |
4489 | { | 4821 | { |
4490 | "defaultMessage": "!!!Franz Professional", | 4822 | "defaultMessage": "!!!Professional", |
4491 | "end": { | 4823 | "end": { |
4492 | "column": 3, | 4824 | "column": 3, |
4493 | "line": 8 | 4825 | "line": 8 |
@@ -4500,7 +4832,7 @@ | |||
4500 | } | 4832 | } |
4501 | }, | 4833 | }, |
4502 | { | 4834 | { |
4503 | "defaultMessage": "!!!Franz Personal", | 4835 | "defaultMessage": "!!!Personal", |
4504 | "end": { | 4836 | "end": { |
4505 | "column": 3, | 4837 | "column": 3, |
4506 | "line": 12 | 4838 | "line": 12 |
@@ -4513,7 +4845,7 @@ | |||
4513 | } | 4845 | } |
4514 | }, | 4846 | }, |
4515 | { | 4847 | { |
4516 | "defaultMessage": "!!!Franz Free", | 4848 | "defaultMessage": "!!!Free", |
4517 | "end": { | 4849 | "end": { |
4518 | "column": 3, | 4850 | "column": 3, |
4519 | "line": 16 | 4851 | "line": 16 |
@@ -4526,7 +4858,7 @@ | |||
4526 | } | 4858 | } |
4527 | }, | 4859 | }, |
4528 | { | 4860 | { |
4529 | "defaultMessage": "!!!Franz Premium", | 4861 | "defaultMessage": "!!!Premium", |
4530 | "end": { | 4862 | "end": { |
4531 | "column": 3, | 4863 | "column": 3, |
4532 | "line": 20 | 4864 | "line": 20 |
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index aea74768d..4e1b01419 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json | |||
@@ -9,6 +9,21 @@ | |||
9 | "feature.delayApp.trial.headline": "Get the free Franz Professional 14 day trial and skip the line", | 9 | "feature.delayApp.trial.headline": "Get the free Franz Professional 14 day trial and skip the line", |
10 | "feature.delayApp.upgrade.action": "Get a Franz Supporter License", | 10 | "feature.delayApp.upgrade.action": "Get a Franz Supporter License", |
11 | "feature.delayApp.upgrade.actionShort": "Upgrade account", | 11 | "feature.delayApp.upgrade.actionShort": "Upgrade account", |
12 | "feature.planSelection.cta.ctaDowngradeFree": "Downgrade to Free", | ||
13 | "feature.planSelection.cta.stayOnFree": "Stay on Free", | ||
14 | "feature.planSelection.cta.trial": "Start my free 14-days Trial", | ||
15 | "feature.planSelection.cta.upgradePersonal": "Choose Personal", | ||
16 | "feature.planSelection.cta.upgradePro": "Choose Professional", | ||
17 | "feature.planSelection.free.text": "Basic functionality", | ||
18 | "feature.planSelection.fullFeatureList": "Complete comparison of all plans", | ||
19 | "feature.planSelection.fullscreen.dialog.cta.downgrade": "Downgrade to Free", | ||
20 | "feature.planSelection.fullscreen.dialog.cta.upgrade": "Choose Personal", | ||
21 | "feature.planSelection.fullscreen.dialog.message": "You're about to downgrade to our Free account. Are you sure? Click here instead to get more services and functionality for just {currency}{price} a month.", | ||
22 | "feature.planSelection.fullscreen.dialog.title": "Downgrade your Franz Plan", | ||
23 | "feature.planSelection.fullscreen.subheadline": "It's time to make a choice. Franz works best on our Personal and Professional plans. Please have a look and choose the best one for you.", | ||
24 | "feature.planSelection.fullscreen.welcome": "Welcome back, {name}", | ||
25 | "feature.planSelection.personal.text": "More services, no waiting - ideal for personal use.", | ||
26 | "feature.planSelection.pro.text": "Unlimited services and professional features for you - and your team.", | ||
12 | "feature.serviceLimit.limitReached": "You have added {amount} out of {limit} services that are included in your plan. Please upgrade your account to add more services.", | 27 | "feature.serviceLimit.limitReached": "You have added {amount} out of {limit} services that are included in your plan. Please upgrade your account to add more services.", |
13 | "feature.shareFranz.action.email": "Send as email", | 28 | "feature.shareFranz.action.email": "Send as email", |
14 | "feature.shareFranz.action.facebook": "Share on Facebook", | 29 | "feature.shareFranz.action.facebook": "Share on Facebook", |
@@ -123,30 +138,35 @@ | |||
123 | "password.submit.label": "Submit", | 138 | "password.submit.label": "Submit", |
124 | "password.successInfo": "Please check your email", | 139 | "password.successInfo": "Please check your email", |
125 | "premiumFeature.button.upgradeAccount": "Upgrade account", | 140 | "premiumFeature.button.upgradeAccount": "Upgrade account", |
141 | "pricing.features.accountSync": "Account Synchronisation", | ||
126 | "pricing.features.adFree": "Forever ad-free", | 142 | "pricing.features.adFree": "Forever ad-free", |
127 | "pricing.features.appDelays": "No Waiting Screens", | 143 | "pricing.features.appDelays": "No Waiting Screens", |
128 | "pricing.features.customWebsites": "Add Custom Websites", | 144 | "pricing.features.customWebsites": "Add Custom Websites", |
145 | "pricing.features.desktopNotifications": "Desktop Notifications", | ||
129 | "pricing.features.onPremise": "On-premise & other Hosted Services", | 146 | "pricing.features.onPremise": "On-premise & other Hosted Services", |
147 | "pricing.features.recipes": "Choose from more than 70 Services", | ||
130 | "pricing.features.serviceProxies": "Service Proxies", | 148 | "pricing.features.serviceProxies": "Service Proxies", |
131 | "pricing.features.spellchecker": "Spellchecker support", | 149 | "pricing.features.spellchecker": "Spellchecker support", |
132 | "pricing.features.teamManagement": "Team Management", | 150 | "pricing.features.teamManagement": "Team Management", |
133 | "pricing.features.thirdPartyServices": "Install 3rd party services", | 151 | "pricing.features.thirdPartyServices": "Install 3rd party services", |
134 | "pricing.features.unlimitedServices": "Add unlimited services", | 152 | "pricing.features.unlimitedServices": "Add unlimited services", |
153 | "pricing.features.upToSixServices": "Add up to 6 services", | ||
154 | "pricing.features.upToThreeServices": "Add up to 3 services", | ||
135 | "pricing.features.workspaces": "Workspaces", | 155 | "pricing.features.workspaces": "Workspaces", |
136 | "pricing.plan.free": "Franz Free", | 156 | "pricing.plan.free": "Free", |
137 | "pricing.plan.legacy": "Franz Premium", | 157 | "pricing.plan.legacy": "Premium", |
138 | "pricing.plan.personal": "Franz Personal", | 158 | "pricing.plan.personal": "Personal", |
139 | "pricing.plan.personal-monthly": "Franz Personal Monthly", | 159 | "pricing.plan.personal-monthly": "Personal Monthly", |
140 | "pricing.plan.personal-yearly": "Franz Personal Yearly", | 160 | "pricing.plan.personal-yearly": "Personal Yearly", |
141 | "pricing.plan.pro": "Franz Professional", | 161 | "pricing.plan.pro": "Professional", |
142 | "pricing.plan.pro-monthly": "Franz Professional Monthly", | 162 | "pricing.plan.pro-monthly": "Professional Monthly", |
143 | "pricing.plan.pro-yearly": "Franz Professional Yearly", | 163 | "pricing.plan.pro-yearly": "Professional Yearly", |
144 | "pricing.trial.cta.accept": "Yes, upgrade my account to Franz Professional", | 164 | "pricing.trial.cta.accept": "Start my 14-day Franz Professional Trial ", |
145 | "pricing.trial.cta.skip": "Continue to Franz", | 165 | "pricing.trial.cta.skip": "Continue to Franz", |
146 | "pricing.trial.error": "Sorry, we could not activate your trial!", | 166 | "pricing.trial.error": "Sorry, we could not activate your trial!", |
147 | "pricing.trial.features.headline": "Franz Professional includes:", | 167 | "pricing.trial.features.headline": "Franz Professional includes:", |
148 | "pricing.trial.headline": "Franz Professional", | 168 | "pricing.trial.headline": "Franz Professional", |
149 | "pricing.trial.subheadline": "Your personal welcome offer:", | 169 | "pricing.trial.subheadline": "Here's a special welcome for you:", |
150 | "pricing.trial.terms.automaticTrialEnd": "Your free trial ends automatically after 14 days", | 170 | "pricing.trial.terms.automaticTrialEnd": "Your free trial ends automatically after 14 days", |
151 | "pricing.trial.terms.headline": "No strings attached", | 171 | "pricing.trial.terms.headline": "No strings attached", |
152 | "pricing.trial.terms.noCreditCard": "No credit card required", | 172 | "pricing.trial.terms.noCreditCard": "No credit card required", |
@@ -353,6 +373,10 @@ | |||
353 | "subscription.cta.allOptions": "See all options", | 373 | "subscription.cta.allOptions": "See all options", |
354 | "subscription.cta.choosePlan": "Choose your plan", | 374 | "subscription.cta.choosePlan": "Choose your plan", |
355 | "subscription.includedProFeatures": "The Franz Professional Plan includes:", | 375 | "subscription.includedProFeatures": "The Franz Professional Plan includes:", |
376 | "subscription.interval.per": "per {interval}", | ||
377 | "subscription.interval.perMonth": "per month", | ||
378 | "subscription.interval.perMonthPerUser": "per month & user", | ||
379 | "subscription.planItem.upgradeAccount": "Upgrade Account", | ||
356 | "subscription.teaser.includedFeatures": "Paid Franz Plans include:", | 380 | "subscription.teaser.includedFeatures": "Paid Franz Plans include:", |
357 | "subscription.teaser.intro": "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!", | 381 | "subscription.teaser.intro": "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!", |
358 | "subscriptionPopup.buttonCancel": "Cancel", | 382 | "subscriptionPopup.buttonCancel": "Cancel", |
@@ -389,4 +413,4 @@ | |||
389 | "workspaceDrawer.workspaceFeatureInfo": "<p>Franz Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time.</p><p>You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.</p>", | 413 | "workspaceDrawer.workspaceFeatureInfo": "<p>Franz Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time.</p><p>You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.</p>", |
390 | "workspaceDrawer.workspacesSettingsTooltip": "Edit workspaces settings", | 414 | "workspaceDrawer.workspacesSettingsTooltip": "Edit workspaces settings", |
391 | "workspaces.switchingIndicator.switchingTo": "Switching to" | 415 | "workspaces.switchingIndicator.switchingTo": "Switching to" |
392 | } \ No newline at end of file | 416 | } |
diff --git a/src/i18n/messages/src/components/auth/Pricing.json b/src/i18n/messages/src/components/auth/Pricing.json index f15617ca5..2d09c9ebb 100644 --- a/src/i18n/messages/src/components/auth/Pricing.json +++ b/src/i18n/messages/src/components/auth/Pricing.json | |||
@@ -14,7 +14,7 @@ | |||
14 | }, | 14 | }, |
15 | { | 15 | { |
16 | "id": "pricing.trial.subheadline", | 16 | "id": "pricing.trial.subheadline", |
17 | "defaultMessage": "!!!Your personal welcome offer:", | 17 | "defaultMessage": "!!!Here's a special welcome for you:", |
18 | "file": "src/components/auth/Pricing.js", | 18 | "file": "src/components/auth/Pricing.js", |
19 | "start": { | 19 | "start": { |
20 | "line": 19, | 20 | "line": 19, |
@@ -79,7 +79,7 @@ | |||
79 | }, | 79 | }, |
80 | { | 80 | { |
81 | "id": "pricing.trial.cta.accept", | 81 | "id": "pricing.trial.cta.accept", |
82 | "defaultMessage": "!!!Yes, upgrade my account to Franz Professional", | 82 | "defaultMessage": "!!!Start my 14-day Franz Professional Trial", |
83 | "file": "src/components/auth/Pricing.js", | 83 | "file": "src/components/auth/Pricing.js", |
84 | "start": { | 84 | "start": { |
85 | "line": 39, | 85 | "line": 39, |
diff --git a/src/i18n/messages/src/components/layout/AppLayout.json b/src/i18n/messages/src/components/layout/AppLayout.json index 44cf4fab9..22f11cedd 100644 --- a/src/i18n/messages/src/components/layout/AppLayout.json +++ b/src/i18n/messages/src/components/layout/AppLayout.json | |||
@@ -4,11 +4,11 @@ | |||
4 | "defaultMessage": "!!!Your services have been updated.", | 4 | "defaultMessage": "!!!Your services have been updated.", |
5 | "file": "src/components/layout/AppLayout.js", | 5 | "file": "src/components/layout/AppLayout.js", |
6 | "start": { | 6 | "start": { |
7 | "line": 28, | 7 | "line": 29, |
8 | "column": 19 | 8 | "column": 19 |
9 | }, | 9 | }, |
10 | "end": { | 10 | "end": { |
11 | "line": 31, | 11 | "line": 32, |
12 | "column": 3 | 12 | "column": 3 |
13 | } | 13 | } |
14 | }, | 14 | }, |
@@ -17,11 +17,11 @@ | |||
17 | "defaultMessage": "!!!Reload services", | 17 | "defaultMessage": "!!!Reload services", |
18 | "file": "src/components/layout/AppLayout.js", | 18 | "file": "src/components/layout/AppLayout.js", |
19 | "start": { | 19 | "start": { |
20 | "line": 32, | 20 | "line": 33, |
21 | "column": 24 | 21 | "column": 24 |
22 | }, | 22 | }, |
23 | "end": { | 23 | "end": { |
24 | "line": 35, | 24 | "line": 36, |
25 | "column": 3 | 25 | "column": 3 |
26 | } | 26 | } |
27 | }, | 27 | }, |
@@ -30,11 +30,11 @@ | |||
30 | "defaultMessage": "!!!Could not load services and user information", | 30 | "defaultMessage": "!!!Could not load services and user information", |
31 | "file": "src/components/layout/AppLayout.js", | 31 | "file": "src/components/layout/AppLayout.js", |
32 | "start": { | 32 | "start": { |
33 | "line": 36, | 33 | "line": 37, |
34 | "column": 26 | 34 | "column": 26 |
35 | }, | 35 | }, |
36 | "end": { | 36 | "end": { |
37 | "line": 39, | 37 | "line": 40, |
38 | "column": 3 | 38 | "column": 3 |
39 | } | 39 | } |
40 | } | 40 | } |
diff --git a/src/i18n/messages/src/components/ui/FeatureList.json b/src/i18n/messages/src/components/ui/FeatureList.json index 497e299a4..3201115b3 100644 --- a/src/i18n/messages/src/components/ui/FeatureList.json +++ b/src/i18n/messages/src/components/ui/FeatureList.json | |||
@@ -1,14 +1,79 @@ | |||
1 | [ | 1 | [ |
2 | { | 2 | { |
3 | "id": "pricing.features.recipes", | ||
4 | "defaultMessage": "!!!Choose from more than 70 Services", | ||
5 | "file": "src/components/ui/FeatureList.js", | ||
6 | "start": { | ||
7 | "line": 9, | ||
8 | "column": 20 | ||
9 | }, | ||
10 | "end": { | ||
11 | "line": 12, | ||
12 | "column": 3 | ||
13 | } | ||
14 | }, | ||
15 | { | ||
16 | "id": "pricing.features.accountSync", | ||
17 | "defaultMessage": "!!!Account Synchronisation", | ||
18 | "file": "src/components/ui/FeatureList.js", | ||
19 | "start": { | ||
20 | "line": 13, | ||
21 | "column": 15 | ||
22 | }, | ||
23 | "end": { | ||
24 | "line": 16, | ||
25 | "column": 3 | ||
26 | } | ||
27 | }, | ||
28 | { | ||
29 | "id": "pricing.features.desktopNotifications", | ||
30 | "defaultMessage": "!!!Desktop Notifications", | ||
31 | "file": "src/components/ui/FeatureList.js", | ||
32 | "start": { | ||
33 | "line": 17, | ||
34 | "column": 24 | ||
35 | }, | ||
36 | "end": { | ||
37 | "line": 20, | ||
38 | "column": 3 | ||
39 | } | ||
40 | }, | ||
41 | { | ||
3 | "id": "pricing.features.unlimitedServices", | 42 | "id": "pricing.features.unlimitedServices", |
4 | "defaultMessage": "!!!Add unlimited services", | 43 | "defaultMessage": "!!!Add unlimited services", |
5 | "file": "src/components/ui/FeatureList.js", | 44 | "file": "src/components/ui/FeatureList.js", |
6 | "start": { | 45 | "start": { |
7 | "line": 8, | 46 | "line": 21, |
8 | "column": 21 | 47 | "column": 21 |
9 | }, | 48 | }, |
10 | "end": { | 49 | "end": { |
11 | "line": 11, | 50 | "line": 24, |
51 | "column": 3 | ||
52 | } | ||
53 | }, | ||
54 | { | ||
55 | "id": "pricing.features.upToThreeServices", | ||
56 | "defaultMessage": "!!!Add up to 3 services", | ||
57 | "file": "src/components/ui/FeatureList.js", | ||
58 | "start": { | ||
59 | "line": 25, | ||
60 | "column": 21 | ||
61 | }, | ||
62 | "end": { | ||
63 | "line": 28, | ||
64 | "column": 3 | ||
65 | } | ||
66 | }, | ||
67 | { | ||
68 | "id": "pricing.features.upToSixServices", | ||
69 | "defaultMessage": "!!!Add up to 6 services", | ||
70 | "file": "src/components/ui/FeatureList.js", | ||
71 | "start": { | ||
72 | "line": 29, | ||
73 | "column": 19 | ||
74 | }, | ||
75 | "end": { | ||
76 | "line": 32, | ||
12 | "column": 3 | 77 | "column": 3 |
13 | } | 78 | } |
14 | }, | 79 | }, |
@@ -17,11 +82,11 @@ | |||
17 | "defaultMessage": "!!!Spellchecker support", | 82 | "defaultMessage": "!!!Spellchecker support", |
18 | "file": "src/components/ui/FeatureList.js", | 83 | "file": "src/components/ui/FeatureList.js", |
19 | "start": { | 84 | "start": { |
20 | "line": 12, | 85 | "line": 33, |
21 | "column": 16 | 86 | "column": 16 |
22 | }, | 87 | }, |
23 | "end": { | 88 | "end": { |
24 | "line": 15, | 89 | "line": 36, |
25 | "column": 3 | 90 | "column": 3 |
26 | } | 91 | } |
27 | }, | 92 | }, |
@@ -30,11 +95,11 @@ | |||
30 | "defaultMessage": "!!!Workspaces", | 95 | "defaultMessage": "!!!Workspaces", |
31 | "file": "src/components/ui/FeatureList.js", | 96 | "file": "src/components/ui/FeatureList.js", |
32 | "start": { | 97 | "start": { |
33 | "line": 16, | 98 | "line": 37, |
34 | "column": 14 | 99 | "column": 14 |
35 | }, | 100 | }, |
36 | "end": { | 101 | "end": { |
37 | "line": 19, | 102 | "line": 40, |
38 | "column": 3 | 103 | "column": 3 |
39 | } | 104 | } |
40 | }, | 105 | }, |
@@ -43,11 +108,11 @@ | |||
43 | "defaultMessage": "!!!Add Custom Websites", | 108 | "defaultMessage": "!!!Add Custom Websites", |
44 | "file": "src/components/ui/FeatureList.js", | 109 | "file": "src/components/ui/FeatureList.js", |
45 | "start": { | 110 | "start": { |
46 | "line": 20, | 111 | "line": 41, |
47 | "column": 18 | 112 | "column": 18 |
48 | }, | 113 | }, |
49 | "end": { | 114 | "end": { |
50 | "line": 23, | 115 | "line": 44, |
51 | "column": 3 | 116 | "column": 3 |
52 | } | 117 | } |
53 | }, | 118 | }, |
@@ -56,11 +121,11 @@ | |||
56 | "defaultMessage": "!!!On-premise & other Hosted Services", | 121 | "defaultMessage": "!!!On-premise & other Hosted Services", |
57 | "file": "src/components/ui/FeatureList.js", | 122 | "file": "src/components/ui/FeatureList.js", |
58 | "start": { | 123 | "start": { |
59 | "line": 24, | 124 | "line": 45, |
60 | "column": 13 | 125 | "column": 13 |
61 | }, | 126 | }, |
62 | "end": { | 127 | "end": { |
63 | "line": 27, | 128 | "line": 48, |
64 | "column": 3 | 129 | "column": 3 |
65 | } | 130 | } |
66 | }, | 131 | }, |
@@ -69,11 +134,11 @@ | |||
69 | "defaultMessage": "!!!Install 3rd party services", | 134 | "defaultMessage": "!!!Install 3rd party services", |
70 | "file": "src/components/ui/FeatureList.js", | 135 | "file": "src/components/ui/FeatureList.js", |
71 | "start": { | 136 | "start": { |
72 | "line": 28, | 137 | "line": 49, |
73 | "column": 22 | 138 | "column": 22 |
74 | }, | 139 | }, |
75 | "end": { | 140 | "end": { |
76 | "line": 31, | 141 | "line": 52, |
77 | "column": 3 | 142 | "column": 3 |
78 | } | 143 | } |
79 | }, | 144 | }, |
@@ -82,11 +147,11 @@ | |||
82 | "defaultMessage": "!!!Service Proxies", | 147 | "defaultMessage": "!!!Service Proxies", |
83 | "file": "src/components/ui/FeatureList.js", | 148 | "file": "src/components/ui/FeatureList.js", |
84 | "start": { | 149 | "start": { |
85 | "line": 32, | 150 | "line": 53, |
86 | "column": 18 | 151 | "column": 18 |
87 | }, | 152 | }, |
88 | "end": { | 153 | "end": { |
89 | "line": 35, | 154 | "line": 56, |
90 | "column": 3 | 155 | "column": 3 |
91 | } | 156 | } |
92 | }, | 157 | }, |
@@ -95,11 +160,11 @@ | |||
95 | "defaultMessage": "!!!Team Management", | 160 | "defaultMessage": "!!!Team Management", |
96 | "file": "src/components/ui/FeatureList.js", | 161 | "file": "src/components/ui/FeatureList.js", |
97 | "start": { | 162 | "start": { |
98 | "line": 36, | 163 | "line": 57, |
99 | "column": 18 | 164 | "column": 18 |
100 | }, | 165 | }, |
101 | "end": { | 166 | "end": { |
102 | "line": 39, | 167 | "line": 60, |
103 | "column": 3 | 168 | "column": 3 |
104 | } | 169 | } |
105 | }, | 170 | }, |
@@ -108,11 +173,11 @@ | |||
108 | "defaultMessage": "!!!No Waiting Screens", | 173 | "defaultMessage": "!!!No Waiting Screens", |
109 | "file": "src/components/ui/FeatureList.js", | 174 | "file": "src/components/ui/FeatureList.js", |
110 | "start": { | 175 | "start": { |
111 | "line": 40, | 176 | "line": 61, |
112 | "column": 13 | 177 | "column": 13 |
113 | }, | 178 | }, |
114 | "end": { | 179 | "end": { |
115 | "line": 43, | 180 | "line": 64, |
116 | "column": 3 | 181 | "column": 3 |
117 | } | 182 | } |
118 | }, | 183 | }, |
@@ -121,11 +186,11 @@ | |||
121 | "defaultMessage": "!!!Forever ad-free", | 186 | "defaultMessage": "!!!Forever ad-free", |
122 | "file": "src/components/ui/FeatureList.js", | 187 | "file": "src/components/ui/FeatureList.js", |
123 | "start": { | 188 | "start": { |
124 | "line": 44, | 189 | "line": 65, |
125 | "column": 10 | 190 | "column": 10 |
126 | }, | 191 | }, |
127 | "end": { | 192 | "end": { |
128 | "line": 47, | 193 | "line": 68, |
129 | "column": 3 | 194 | "column": 3 |
130 | } | 195 | } |
131 | } | 196 | } |
diff --git a/src/i18n/messages/src/features/planSelection/components/PlanItem.json b/src/i18n/messages/src/features/planSelection/components/PlanItem.json new file mode 100644 index 000000000..839686390 --- /dev/null +++ b/src/i18n/messages/src/features/planSelection/components/PlanItem.json | |||
@@ -0,0 +1,28 @@ | |||
1 | [ | ||
2 | { | ||
3 | "id": "subscription.interval.perMonth", | ||
4 | "defaultMessage": "!!!per month", | ||
5 | "file": "src/features/planSelection/components/PlanItem.js", | ||
6 | "start": { | ||
7 | "line": 19, | ||
8 | "column": 12 | ||
9 | }, | ||
10 | "end": { | ||
11 | "line": 22, | ||
12 | "column": 3 | ||
13 | } | ||
14 | }, | ||
15 | { | ||
16 | "id": "subscription.interval.perMonthPerUser", | ||
17 | "defaultMessage": "!!!per month & user", | ||
18 | "file": "src/features/planSelection/components/PlanItem.js", | ||
19 | "start": { | ||
20 | "line": 23, | ||
21 | "column": 19 | ||
22 | }, | ||
23 | "end": { | ||
24 | "line": 26, | ||
25 | "column": 3 | ||
26 | } | ||
27 | } | ||
28 | ] \ No newline at end of file | ||
diff --git a/src/i18n/messages/src/features/planSelection/components/PlanSelection.json b/src/i18n/messages/src/features/planSelection/components/PlanSelection.json new file mode 100644 index 000000000..3dc4f74f4 --- /dev/null +++ b/src/i18n/messages/src/features/planSelection/components/PlanSelection.json | |||
@@ -0,0 +1,145 @@ | |||
1 | [ | ||
2 | { | ||
3 | "id": "feature.planSelection.fullscreen.welcome", | ||
4 | "defaultMessage": "!!!Welcome back, {name}", | ||
5 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
6 | "start": { | ||
7 | "line": 16, | ||
8 | "column": 11 | ||
9 | }, | ||
10 | "end": { | ||
11 | "line": 19, | ||
12 | "column": 3 | ||
13 | } | ||
14 | }, | ||
15 | { | ||
16 | "id": "feature.planSelection.fullscreen.subheadline", | ||
17 | "defaultMessage": "!!!It's time to make a choice. Franz works best on our Personal and Professional plans. Please have a look and choose the best one for you.", | ||
18 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
19 | "start": { | ||
20 | "line": 20, | ||
21 | "column": 15 | ||
22 | }, | ||
23 | "end": { | ||
24 | "line": 23, | ||
25 | "column": 3 | ||
26 | } | ||
27 | }, | ||
28 | { | ||
29 | "id": "feature.planSelection.free.text", | ||
30 | "defaultMessage": "!!!Basic functionality", | ||
31 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
32 | "start": { | ||
33 | "line": 24, | ||
34 | "column": 12 | ||
35 | }, | ||
36 | "end": { | ||
37 | "line": 27, | ||
38 | "column": 3 | ||
39 | } | ||
40 | }, | ||
41 | { | ||
42 | "id": "feature.planSelection.personal.text", | ||
43 | "defaultMessage": "!!!More services, no waiting - ideal for personal use.", | ||
44 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
45 | "start": { | ||
46 | "line": 28, | ||
47 | "column": 16 | ||
48 | }, | ||
49 | "end": { | ||
50 | "line": 31, | ||
51 | "column": 3 | ||
52 | } | ||
53 | }, | ||
54 | { | ||
55 | "id": "feature.planSelection.pro.text", | ||
56 | "defaultMessage": "!!!Unlimited services and professional features for you - and your team.", | ||
57 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
58 | "start": { | ||
59 | "line": 32, | ||
60 | "column": 20 | ||
61 | }, | ||
62 | "end": { | ||
63 | "line": 35, | ||
64 | "column": 3 | ||
65 | } | ||
66 | }, | ||
67 | { | ||
68 | "id": "feature.planSelection.cta.stayOnFree", | ||
69 | "defaultMessage": "!!!Stay on Free", | ||
70 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
71 | "start": { | ||
72 | "line": 36, | ||
73 | "column": 17 | ||
74 | }, | ||
75 | "end": { | ||
76 | "line": 39, | ||
77 | "column": 3 | ||
78 | } | ||
79 | }, | ||
80 | { | ||
81 | "id": "feature.planSelection.cta.ctaDowngradeFree", | ||
82 | "defaultMessage": "!!!Downgrade to Free", | ||
83 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
84 | "start": { | ||
85 | "line": 40, | ||
86 | "column": 20 | ||
87 | }, | ||
88 | "end": { | ||
89 | "line": 43, | ||
90 | "column": 3 | ||
91 | } | ||
92 | }, | ||
93 | { | ||
94 | "id": "feature.planSelection.cta.trial", | ||
95 | "defaultMessage": "!!!Start my free 14-days Trial", | ||
96 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
97 | "start": { | ||
98 | "line": 44, | ||
99 | "column": 15 | ||
100 | }, | ||
101 | "end": { | ||
102 | "line": 47, | ||
103 | "column": 3 | ||
104 | } | ||
105 | }, | ||
106 | { | ||
107 | "id": "feature.planSelection.cta.upgradePersonal", | ||
108 | "defaultMessage": "!!!Choose Personal", | ||
109 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
110 | "start": { | ||
111 | "line": 48, | ||
112 | "column": 23 | ||
113 | }, | ||
114 | "end": { | ||
115 | "line": 51, | ||
116 | "column": 3 | ||
117 | } | ||
118 | }, | ||
119 | { | ||
120 | "id": "feature.planSelection.cta.upgradePro", | ||
121 | "defaultMessage": "!!!Choose Professional", | ||
122 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
123 | "start": { | ||
124 | "line": 52, | ||
125 | "column": 18 | ||
126 | }, | ||
127 | "end": { | ||
128 | "line": 55, | ||
129 | "column": 3 | ||
130 | } | ||
131 | }, | ||
132 | { | ||
133 | "id": "feature.planSelection.fullFeatureList", | ||
134 | "defaultMessage": "!!!Complete comparison of all plans", | ||
135 | "file": "src/features/planSelection/components/PlanSelection.js", | ||
136 | "start": { | ||
137 | "line": 56, | ||
138 | "column": 19 | ||
139 | }, | ||
140 | "end": { | ||
141 | "line": 59, | ||
142 | "column": 3 | ||
143 | } | ||
144 | } | ||
145 | ] \ No newline at end of file | ||
diff --git a/src/i18n/messages/src/features/planSelection/components/PlanTeaser.json b/src/i18n/messages/src/features/planSelection/components/PlanTeaser.json new file mode 100644 index 000000000..015304a2e --- /dev/null +++ b/src/i18n/messages/src/features/planSelection/components/PlanTeaser.json | |||
@@ -0,0 +1,28 @@ | |||
1 | [ | ||
2 | { | ||
3 | "id": "subscription.interval.per", | ||
4 | "defaultMessage": "!!!per {interval}", | ||
5 | "file": "src/features/planSelection/components/PlanTeaser.js", | ||
6 | "start": { | ||
7 | "line": 16, | ||
8 | "column": 7 | ||
9 | }, | ||
10 | "end": { | ||
11 | "line": 19, | ||
12 | "column": 3 | ||
13 | } | ||
14 | }, | ||
15 | { | ||
16 | "id": "subscription.planItem.upgradeAccount", | ||
17 | "defaultMessage": "!!!Upgrade Account", | ||
18 | "file": "src/features/planSelection/components/PlanTeaser.js", | ||
19 | "start": { | ||
20 | "line": 20, | ||
21 | "column": 7 | ||
22 | }, | ||
23 | "end": { | ||
24 | "line": 23, | ||
25 | "column": 3 | ||
26 | } | ||
27 | } | ||
28 | ] \ No newline at end of file | ||
diff --git a/src/i18n/messages/src/features/planSelection/containers/PlanSelectionScreen.json b/src/i18n/messages/src/features/planSelection/containers/PlanSelectionScreen.json new file mode 100644 index 000000000..04b2144b4 --- /dev/null +++ b/src/i18n/messages/src/features/planSelection/containers/PlanSelectionScreen.json | |||
@@ -0,0 +1,54 @@ | |||
1 | [ | ||
2 | { | ||
3 | "id": "feature.planSelection.fullscreen.dialog.title", | ||
4 | "defaultMessage": "!!!Downgrade your Franz Plan", | ||
5 | "file": "src/features/planSelection/containers/PlanSelectionScreen.js", | ||
6 | "start": { | ||
7 | "line": 16, | ||
8 | "column": 15 | ||
9 | }, | ||
10 | "end": { | ||
11 | "line": 19, | ||
12 | "column": 3 | ||
13 | } | ||
14 | }, | ||
15 | { | ||
16 | "id": "feature.planSelection.fullscreen.dialog.message", | ||
17 | "defaultMessage": "!!!You're about to downgrade to our Free account. Are you sure? Click here instead to get more services and functionality for just {currency}{price} a month.", | ||
18 | "file": "src/features/planSelection/containers/PlanSelectionScreen.js", | ||
19 | "start": { | ||
20 | "line": 20, | ||
21 | "column": 17 | ||
22 | }, | ||
23 | "end": { | ||
24 | "line": 23, | ||
25 | "column": 3 | ||
26 | } | ||
27 | }, | ||
28 | { | ||
29 | "id": "feature.planSelection.fullscreen.dialog.cta.downgrade", | ||
30 | "defaultMessage": "!!!Downgrade to Free", | ||
31 | "file": "src/features/planSelection/containers/PlanSelectionScreen.js", | ||
32 | "start": { | ||
33 | "line": 24, | ||
34 | "column": 22 | ||
35 | }, | ||
36 | "end": { | ||
37 | "line": 27, | ||
38 | "column": 3 | ||
39 | } | ||
40 | }, | ||
41 | { | ||
42 | "id": "feature.planSelection.fullscreen.dialog.cta.upgrade", | ||
43 | "defaultMessage": "!!!Choose Personal", | ||
44 | "file": "src/features/planSelection/containers/PlanSelectionScreen.js", | ||
45 | "start": { | ||
46 | "line": 28, | ||
47 | "column": 20 | ||
48 | }, | ||
49 | "end": { | ||
50 | "line": 31, | ||
51 | "column": 3 | ||
52 | } | ||
53 | } | ||
54 | ] \ No newline at end of file | ||
diff --git a/src/i18n/messages/src/helpers/plan-helpers.json b/src/i18n/messages/src/helpers/plan-helpers.json index df8ee19e3..3f3e7e85d 100644 --- a/src/i18n/messages/src/helpers/plan-helpers.json +++ b/src/i18n/messages/src/helpers/plan-helpers.json | |||
@@ -1,7 +1,7 @@ | |||
1 | [ | 1 | [ |
2 | { | 2 | { |
3 | "id": "pricing.plan.pro", | 3 | "id": "pricing.plan.pro", |
4 | "defaultMessage": "!!!Franz Professional", | 4 | "defaultMessage": "!!!Professional", |
5 | "file": "src/helpers/plan-helpers.js", | 5 | "file": "src/helpers/plan-helpers.js", |
6 | "start": { | 6 | "start": { |
7 | "line": 5, | 7 | "line": 5, |
@@ -14,7 +14,7 @@ | |||
14 | }, | 14 | }, |
15 | { | 15 | { |
16 | "id": "pricing.plan.personal", | 16 | "id": "pricing.plan.personal", |
17 | "defaultMessage": "!!!Franz Personal", | 17 | "defaultMessage": "!!!Personal", |
18 | "file": "src/helpers/plan-helpers.js", | 18 | "file": "src/helpers/plan-helpers.js", |
19 | "start": { | 19 | "start": { |
20 | "line": 9, | 20 | "line": 9, |
@@ -27,7 +27,7 @@ | |||
27 | }, | 27 | }, |
28 | { | 28 | { |
29 | "id": "pricing.plan.free", | 29 | "id": "pricing.plan.free", |
30 | "defaultMessage": "!!!Franz Free", | 30 | "defaultMessage": "!!!Free", |
31 | "file": "src/helpers/plan-helpers.js", | 31 | "file": "src/helpers/plan-helpers.js", |
32 | "start": { | 32 | "start": { |
33 | "line": 13, | 33 | "line": 13, |
@@ -40,7 +40,7 @@ | |||
40 | }, | 40 | }, |
41 | { | 41 | { |
42 | "id": "pricing.plan.legacy", | 42 | "id": "pricing.plan.legacy", |
43 | "defaultMessage": "!!!Franz Premium", | 43 | "defaultMessage": "!!!Premium", |
44 | "file": "src/helpers/plan-helpers.js", | 44 | "file": "src/helpers/plan-helpers.js", |
45 | "start": { | 45 | "start": { |
46 | "line": 17, | 46 | "line": 17, |
diff --git a/src/stores/FeaturesStore.js b/src/stores/FeaturesStore.js index cf28b6bec..bffcb01bc 100644 --- a/src/stores/FeaturesStore.js +++ b/src/stores/FeaturesStore.js | |||
@@ -19,6 +19,7 @@ import settingsWS from '../features/settingsWS'; | |||
19 | import serviceLimit from '../features/serviceLimit'; | 19 | import serviceLimit from '../features/serviceLimit'; |
20 | import communityRecipes from '../features/communityRecipes'; | 20 | import communityRecipes from '../features/communityRecipes'; |
21 | import todos from '../features/todos'; | 21 | import todos from '../features/todos'; |
22 | import planSelection from '../features/planSelection'; | ||
22 | 23 | ||
23 | import { DEFAULT_FEATURES_CONFIG } from '../config'; | 24 | import { DEFAULT_FEATURES_CONFIG } from '../config'; |
24 | 25 | ||
@@ -81,5 +82,6 @@ export default class FeaturesStore extends Store { | |||
81 | serviceLimit(this.stores, this.actions); | 82 | serviceLimit(this.stores, this.actions); |
82 | communityRecipes(this.stores, this.actions); | 83 | communityRecipes(this.stores, this.actions); |
83 | todos(this.stores, this.actions); | 84 | todos(this.stores, this.actions); |
85 | planSelection(this.stores, this.actions); | ||
84 | } | 86 | } |
85 | } | 87 | } |
diff --git a/src/stores/index.js b/src/stores/index.js index 10dd56665..4eeef7982 100644 --- a/src/stores/index.js +++ b/src/stores/index.js | |||
@@ -15,6 +15,7 @@ import { announcementsStore } from '../features/announcements'; | |||
15 | import { serviceLimitStore } from '../features/serviceLimit'; | 15 | import { serviceLimitStore } from '../features/serviceLimit'; |
16 | import { communityRecipesStore } from '../features/communityRecipes'; | 16 | import { communityRecipesStore } from '../features/communityRecipes'; |
17 | import { todosStore } from '../features/todos'; | 17 | import { todosStore } from '../features/todos'; |
18 | import { planSelectionStore } from '../features/planSelection'; | ||
18 | 19 | ||
19 | export default (api, actions, router) => { | 20 | export default (api, actions, router) => { |
20 | const stores = {}; | 21 | const stores = {}; |
@@ -37,6 +38,7 @@ export default (api, actions, router) => { | |||
37 | serviceLimit: serviceLimitStore, | 38 | serviceLimit: serviceLimitStore, |
38 | communityRecipes: communityRecipesStore, | 39 | communityRecipes: communityRecipesStore, |
39 | todos: todosStore, | 40 | todos: todosStore, |
41 | planSelection: planSelectionStore, | ||
40 | }); | 42 | }); |
41 | // Initialize all stores | 43 | // Initialize all stores |
42 | Object.keys(stores).forEach((name) => { | 44 | Object.keys(stores).forEach((name) => { |