aboutsummaryrefslogtreecommitdiffstats
path: root/src/features/trialStatusBar
diff options
context:
space:
mode:
Diffstat (limited to 'src/features/trialStatusBar')
-rw-r--r--src/features/trialStatusBar/actions.js13
-rw-r--r--src/features/trialStatusBar/components/ProgressBar.js45
-rw-r--r--src/features/trialStatusBar/components/TrialStatusBar.js135
-rw-r--r--src/features/trialStatusBar/containers/TrialStatusBarScreen.js112
-rw-r--r--src/features/trialStatusBar/index.js30
-rw-r--r--src/features/trialStatusBar/store.js72
6 files changed, 0 insertions, 407 deletions
diff --git a/src/features/trialStatusBar/actions.js b/src/features/trialStatusBar/actions.js
deleted file mode 100644
index 38df76458..000000000
--- a/src/features/trialStatusBar/actions.js
+++ /dev/null
@@ -1,13 +0,0 @@
1import PropTypes from 'prop-types';
2import { createActionsFromDefinitions } from '../../actions/lib/actions';
3
4export const trialStatusBarActions = createActionsFromDefinitions({
5 upgradeAccount: {
6 planId: PropTypes.string.isRequired,
7 onCloseWindow: PropTypes.func.isRequired,
8 },
9 downgradeAccount: {},
10 hideOverlay: {},
11}, PropTypes.checkPropTypes);
12
13export default trialStatusBarActions;
diff --git a/src/features/trialStatusBar/components/ProgressBar.js b/src/features/trialStatusBar/components/ProgressBar.js
deleted file mode 100644
index 41b74d396..000000000
--- a/src/features/trialStatusBar/components/ProgressBar.js
+++ /dev/null
@@ -1,45 +0,0 @@
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import { observer } from 'mobx-react';
4import injectSheet from 'react-jss';
5
6const styles = theme => ({
7 root: {
8 background: theme.trialStatusBar.progressBar.background,
9 width: '25%',
10 maxWidth: 200,
11 height: 8,
12 display: 'flex',
13 alignItems: 'center',
14 borderRadius: theme.borderRadius,
15 overflow: 'hidden',
16 },
17 progress: {
18 background: theme.trialStatusBar.progressBar.progressIndicator,
19 width: ({ percent }) => `${percent}%`,
20 height: '100%',
21 },
22});
23
24@injectSheet(styles) @observer
25class ProgressBar extends Component {
26 static propTypes = {
27 classes: PropTypes.object.isRequired,
28 };
29
30 render() {
31 const {
32 classes,
33 } = this.props;
34
35 return (
36 <div
37 className={classes.root}
38 >
39 <div className={classes.progress} />
40 </div>
41 );
42 }
43}
44
45export default ProgressBar;
diff --git a/src/features/trialStatusBar/components/TrialStatusBar.js b/src/features/trialStatusBar/components/TrialStatusBar.js
deleted file mode 100644
index b8fe4acc9..000000000
--- a/src/features/trialStatusBar/components/TrialStatusBar.js
+++ /dev/null
@@ -1,135 +0,0 @@
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import { observer } from 'mobx-react';
4import injectSheet from 'react-jss';
5import { defineMessages, intlShape } from 'react-intl';
6import { Icon } from '@meetfranz/ui';
7import { mdiArrowRight, mdiWindowClose } from '@mdi/js';
8import classnames from 'classnames';
9
10import ProgressBar from './ProgressBar';
11
12const messages = defineMessages({
13 restTime: {
14 id: 'feature.trialStatusBar.restTime',
15 defaultMessage: '!!!Your Free Franz {plan} Trial ends in {time}.',
16 },
17 expired: {
18 id: 'feature.trialStatusBar.expired',
19 defaultMessage: '!!!Your free Franz {plan} Trial has expired, please upgrade your account.',
20 },
21 cta: {
22 id: 'feature.trialStatusBar.cta',
23 defaultMessage: '!!!Upgrade now',
24 },
25});
26
27const styles = theme => ({
28 root: {
29 background: theme.trialStatusBar.bar.background,
30 width: '100%',
31 height: 25,
32 order: 10,
33 display: 'flex',
34 alignItems: 'center',
35 fontSize: 12,
36 padding: [0, 10],
37 justifyContent: 'flex-end',
38 },
39 ended: {
40 background: theme.styleTypes.warning.accent,
41 color: theme.styleTypes.warning.contrast,
42 },
43 message: {
44 marginLeft: 20,
45 },
46 action: {
47 marginLeft: 20,
48 fontSize: 12,
49 color: theme.colorText,
50 textDecoration: 'underline',
51 display: 'flex',
52
53 '& svg': {
54 margin: [1, 2, 0, 0],
55 },
56 },
57});
58
59@injectSheet(styles) @observer
60class TrialStatusBar extends Component {
61 static propTypes = {
62 planName: PropTypes.string.isRequired,
63 percent: PropTypes.number.isRequired,
64 upgradeAccount: PropTypes.func.isRequired,
65 hideOverlay: PropTypes.func.isRequired,
66 trialEnd: PropTypes.string.isRequired,
67 hasEnded: PropTypes.bool.isRequired,
68 classes: PropTypes.object.isRequired,
69 };
70
71 static contextTypes = {
72 intl: intlShape,
73 };
74
75 render() {
76 const {
77 planName,
78 percent,
79 upgradeAccount,
80 hideOverlay,
81 trialEnd,
82 hasEnded,
83 classes,
84 } = this.props;
85
86 const { intl } = this.context;
87
88 return (
89 <div
90 className={classnames({
91 [classes.root]: true,
92 [classes.ended]: hasEnded,
93 })}
94 >
95 <ProgressBar
96 percent={percent}
97 />
98 {' '}
99 <span className={classes.message}>
100 {!hasEnded ? (
101 intl.formatMessage(messages.restTime, {
102 plan: planName,
103 time: trialEnd,
104 })
105 ) : (
106 intl.formatMessage(messages.expired, {
107 plan: planName,
108 })
109 )}
110 </span>
111 <button
112 className={classes.action}
113 type="button"
114 onClick={() => {
115 upgradeAccount();
116 }}
117 >
118 <Icon icon={mdiArrowRight} />
119 {intl.formatMessage(messages.cta)}
120 </button>
121 <button
122 className={classes.action}
123 type="button"
124 onClick={() => {
125 hideOverlay();
126 }}
127 >
128 <Icon icon={mdiWindowClose} />
129 </button>
130 </div>
131 );
132 }
133}
134
135export default TrialStatusBar;
diff --git a/src/features/trialStatusBar/containers/TrialStatusBarScreen.js b/src/features/trialStatusBar/containers/TrialStatusBarScreen.js
deleted file mode 100644
index e0f5ab5f2..000000000
--- a/src/features/trialStatusBar/containers/TrialStatusBarScreen.js
+++ /dev/null
@@ -1,112 +0,0 @@
1import React, { Component } from 'react';
2import { observer, inject } from 'mobx-react';
3import PropTypes from 'prop-types';
4import ms from 'ms';
5import { intlShape } from 'react-intl';
6
7import FeaturesStore from '../../../stores/FeaturesStore';
8import UserStore from '../../../stores/UserStore';
9import TrialStatusBar from '../components/TrialStatusBar';
10import ErrorBoundary from '../../../components/util/ErrorBoundary';
11import { trialStatusBarStore } from '..';
12import { i18nPlanName } from '../../../helpers/plan-helpers';
13import PaymentStore from '../../../stores/PaymentStore';
14
15@inject('stores', 'actions')
16@observer
17class TrialStatusBarScreen extends Component {
18 static contextTypes = {
19 intl: intlShape,
20 };
21
22 state = {
23 showOverlay: true,
24 percent: 0,
25 restTime: '',
26 hasEnded: false,
27 };
28
29 percentInterval = null;
30
31 componentDidMount() {
32 this.percentInterval = setInterval(() => {
33 this.calculateRestTime();
34 }, ms('1m'));
35
36 this.calculateRestTime();
37 }
38
39 componentWillUnmount() {
40 clearInterval(this.percentInterval);
41 }
42
43 calculateRestTime() {
44 const { trialEndTime } = trialStatusBarStore;
45 const percent = (
46 Math.abs(100 - Math.abs(trialEndTime.asMilliseconds()) * 100) / ms('14d')
47 ).toFixed(2);
48 const restTime = trialEndTime.humanize();
49 const hasEnded = trialEndTime.asMilliseconds() > 0;
50
51 this.setState({
52 percent,
53 restTime,
54 hasEnded,
55 });
56 }
57
58 hideOverlay() {
59 this.setState({
60 showOverlay: false,
61 });
62 }
63
64 render() {
65 const { intl } = this.context;
66
67 const {
68 showOverlay, percent, restTime, hasEnded,
69 } = this.state;
70
71 if (
72 !trialStatusBarStore
73 || !trialStatusBarStore.isFeatureActive
74 || !showOverlay
75 || !trialStatusBarStore.showTrialStatusBarOverlay
76 ) {
77 return null;
78 }
79
80 const { user } = this.props.stores;
81 const { upgradeAccount } = this.props.actions.payment;
82
83 const planName = i18nPlanName(user.team.plan, intl);
84
85 return (
86 <ErrorBoundary>
87 <TrialStatusBar
88 planName={planName}
89 percent={parseFloat(percent < 5 ? 5 : percent)}
90 trialEnd={restTime}
91 upgradeAccount={() => upgradeAccount({
92 planId: user.team.plan,
93 })}
94 hideOverlay={() => this.hideOverlay()}
95 hasEnded={hasEnded}
96 />
97 </ErrorBoundary>
98 );
99 }
100}
101
102export default TrialStatusBarScreen;
103
104TrialStatusBarScreen.wrappedComponent.propTypes = {
105 stores: PropTypes.shape({
106 features: PropTypes.instanceOf(FeaturesStore).isRequired,
107 user: PropTypes.instanceOf(UserStore).isRequired,
108 }).isRequired,
109 actions: PropTypes.shape({
110 payment: PropTypes.instanceOf(PaymentStore),
111 }).isRequired,
112};
diff --git a/src/features/trialStatusBar/index.js b/src/features/trialStatusBar/index.js
deleted file mode 100644
index 987b5c04e..000000000
--- a/src/features/trialStatusBar/index.js
+++ /dev/null
@@ -1,30 +0,0 @@
1import { reaction } from 'mobx';
2import TrialStatusBarStore from './store';
3
4const debug = require('debug')('Ferdi:feature:trialStatusBar');
5
6export const GA_CATEGORY_TRIAL_STATUS_BAR = 'trialStatusBar';
7
8export const trialStatusBarStore = new TrialStatusBarStore();
9
10export default function initTrialStatusBar(stores, actions) {
11 stores.trialStatusBar = trialStatusBarStore;
12 const { features } = stores;
13
14 // Toggle trialStatusBar feature
15 reaction(
16 () => features.features.isTrialStatusBarEnabled,
17 (isEnabled) => {
18 if (isEnabled) {
19 debug('Initializing `trialStatusBar` feature');
20 trialStatusBarStore.start(stores, actions);
21 } else if (trialStatusBarStore.isFeatureActive) {
22 debug('Disabling `trialStatusBar` feature');
23 trialStatusBarStore.stop();
24 }
25 },
26 {
27 fireImmediately: true,
28 },
29 );
30}
diff --git a/src/features/trialStatusBar/store.js b/src/features/trialStatusBar/store.js
deleted file mode 100644
index 858a08238..000000000
--- a/src/features/trialStatusBar/store.js
+++ /dev/null
@@ -1,72 +0,0 @@
1import {
2 action,
3 observable,
4 computed,
5} from 'mobx';
6import moment from 'moment';
7
8import { trialStatusBarActions } from './actions';
9import { FeatureStore } from '../utils/FeatureStore';
10import { createActionBindings } from '../utils/ActionBinding';
11
12const debug = require('debug')('Ferdi:feature:trialStatusBar:store');
13
14export default class TrialStatusBarStore extends FeatureStore {
15 @observable isFeatureActive = false;
16
17 @observable isFeatureEnabled = false;
18
19 @computed get showTrialStatusBarOverlay() {
20 if (this.isFeatureActive) {
21 const { team } = this.stores.user;
22 if (team && !this.hideOverlay) {
23 return team.state !== 'expired' && team.isTrial;
24 }
25 }
26
27 return false;
28 }
29
30 @computed get trialEndTime() {
31 if (this.isFeatureActive) {
32 const { team } = this.stores.user;
33
34 if (team && !this.hideOverlay) {
35 return moment.duration(moment().diff(team.trialEnd));
36 }
37 }
38
39 return moment.duration();
40 }
41
42 // ========== PUBLIC API ========= //
43
44 @action start(stores, actions, api) {
45 debug('TrialStatusBarStore::start');
46 this.stores = stores;
47 this.actions = actions;
48 this.api = api;
49
50 // ACTIONS
51
52 this._registerActions(createActionBindings([
53 [trialStatusBarActions.hideOverlay, this._hideOverlay],
54 ]));
55
56 this.isFeatureActive = true;
57 }
58
59 @action stop() {
60 super.stop();
61 debug('TrialStatusBarStore::stop');
62 this.isFeatureActive = false;
63 }
64
65 // ========== PRIVATE METHODS ========= //
66
67 // Actions
68
69 @action _hideOverlay = () => {
70 this.hideOverlay = true;
71 }
72}