• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / gui / 1113439055

19 Dec 2023 09:01PM UTC coverage: 82.752% (-17.2%) from 99.964%
1113439055

Pull #4258

gitlab-ci

mender-test-bot
chore: Types update

Signed-off-by: Mender Test Bot <mender@northern.tech>
Pull Request #4258: chore: Types update

4326 of 6319 branches covered (0.0%)

8348 of 10088 relevant lines covered (82.75%)

189.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.04
/src/js/components/settings/upgrade.js
1
// Copyright 2020 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14
import React, { useEffect, useState } from 'react';
15
import { useDispatch, useSelector } from 'react-redux';
16
import { useNavigate } from 'react-router-dom';
17

18
import { InfoOutlined as InfoOutlinedIcon, LocalOffer as LocalOfferIcon } from '@mui/icons-material';
19

20
import moment from 'moment';
21

22
import { setSnackbar } from '../../actions/appActions';
23
import { getDeviceLimit } from '../../actions/deviceActions';
24
import { cancelUpgrade, completeUpgrade, getUserOrganization, requestPlanChange, startUpgrade } from '../../actions/organizationActions';
25
import { PLANS, TIMEOUTS } from '../../constants/appConstants';
26
import { getFeatures, getOrganization } from '../../selectors';
27
import InfoText from '../common/infotext';
28
import Loader from '../common/loader';
29
import AddOnSelection from './addonselection';
30
import CardSection from './cardsection';
31
import PlanSelection from './planselection';
32
import QuoteRequestForm from './quoterequestform';
33

34
const offerTag = (
35
  <span className="offerTag">
4✔
36
    <LocalOfferIcon /> End of year offer
37
  </span>
38
);
39

40
export const PostUpgradeNote = ({ newPlan }) => (
4✔
41
  <div style={{ maxWidth: 750 }} className="margin-top-small">
1✔
42
    <h2 style={{ marginTop: 15 }}>Upgrade now</h2>
43
    <div>
44
      <p>
45
        <b>Your upgrade was successful! </b>You are now signed up to the <b>{newPlan}</b> plan.
46
      </p>
47
      <p>Redirecting you to your organization page...</p>
48
      <Loader show={true} />
49
    </div>
50
  </div>
51
);
52

53
export const PricingContactNote = () => (
4✔
54
  <InfoText>
1✔
55
    <InfoOutlinedIcon style={{ fontSize: '14px', margin: '0 4px 4px 0', verticalAlign: 'middle' }} />
56
    If you have any questions about the plan pricing or device limits,{' '}
57
    <a href="mailto:support@mender.io" target="_blank" rel="noopener noreferrer">
58
      contact our team
59
    </a>
60
    .
61
  </InfoText>
62
);
63

64
const upgradeNotes = {
4✔
65
  default: {
66
    title: 'Upgrades and add-ons',
67
    description: 'Upgrade your plan or purchase an add-on to connect more devices, access more features and advanced support.'
68
  },
69
  trial: {
70
    title: 'Upgrade now',
71
    description: 'Upgrade to one of our plans to connect more devices, continue using advanced features, and get access to support.'
72
  }
73
};
74

75
export const Upgrade = () => {
4✔
76
  const offerValid = moment().isBefore('2021-01-01');
2✔
77
  const [addOns, setAddOns] = useState([]);
2✔
78
  const [updatedPlan, setUpdatedPlan] = useState(PLANS.os.id);
2✔
79
  const [upgraded, setUpgraded] = useState(false);
2✔
80
  const navigate = useNavigate();
2✔
81
  const dispatch = useDispatch();
2✔
82
  const features = useSelector(getFeatures);
2✔
83
  const org = useSelector(getOrganization);
2✔
84
  const { addons: orgAddOns = [], plan: currentPlan = PLANS.os.id, trial: isTrial = true } = org;
2!
85

86
  useEffect(() => {
2✔
87
    dispatch(getUserOrganization());
1✔
88
  }, [dispatch]);
89

90
  useEffect(() => {
2✔
91
    const currentAddOns = orgAddOns.reduce((accu, addon) => {
1✔
92
      if (addon.enabled) {
×
93
        accu.push(addon);
×
94
      }
95
      return accu;
×
96
    }, []);
97
    const plan = Object.values(PLANS).find(plan => plan.id === (isTrial ? PLANS.os.id : currentPlan));
1!
98
    setAddOns(currentAddOns);
1✔
99
    setUpdatedPlan(plan.id);
1✔
100
    // eslint-disable-next-line react-hooks/exhaustive-deps
101
  }, [currentPlan, isTrial, JSON.stringify(orgAddOns)]);
102

103
  if (upgraded) {
2!
104
    return <PostUpgradeNote newPlan={PLANS[updatedPlan].name} />;
×
105
  }
106

107
  const handleUpgrade = async () =>
2✔
108
    dispatch(completeUpgrade(org.id, updatedPlan)).then(() => {
×
109
      setUpgraded(true);
×
110
      setTimeout(() => {
×
111
        dispatch(getDeviceLimit());
×
112
        navigate('/settings/organization-and-billing');
×
113
      }, TIMEOUTS.threeSeconds);
114
    });
115

116
  const addOnsToString = (addons = []) =>
2!
117
    addons
×
118
      .reduce((accu, item) => {
119
        if (item.enabled) {
×
120
          accu.push(item.name);
×
121
        }
122
        return accu;
×
123
      }, [])
124
      .join(', ');
125

126
  const onSendRequest = (message, addons = addOns) =>
2!
127
    dispatch(
×
128
      requestPlanChange(org.id, {
129
        current_plan: PLANS[org.plan || PLANS.os.id].name,
×
130
        requested_plan: PLANS[updatedPlan].name,
131
        current_addons: addOnsToString(org.addons) || '-',
×
132
        requested_addons: addOnsToString(addons) || '-',
×
133
        user_message: message
134
      })
135
    );
136

137
  const { description, title } = isTrial ? upgradeNotes.trial : upgradeNotes.default;
2!
138
  return (
2✔
139
    <div style={{ maxWidth: 750 }} className="margin-top-small">
140
      <h2 style={{ marginTop: 15 }}>{title}</h2>
141
      <p>{description}</p>
142
      <p>
143
        Learn more about the different plans and at {/* eslint-disable-next-line react/jsx-no-target-blank */}
144
        <a href="https://mender.io/plans/pricing" target="_blank" rel="noopener">
145
          mender.io/plans/pricing
146
        </a>
147
        . Prices can change at larger device counts, please see our {/* eslint-disable-next-line react/jsx-no-target-blank */}
148
        <a href="https://mender.io/plans/pricing#calculator" target="_blank" rel="noopener">
149
          price calculator
150
        </a>{' '}
151
        for more.
152
      </p>
153
      <PlanSelection
154
        currentPlan={currentPlan}
155
        isTrial={isTrial}
156
        offerValid={offerValid}
157
        offerTag={offerTag}
158
        setUpdatedPlan={setUpdatedPlan}
159
        updatedPlan={updatedPlan}
160
      />
161
      {isTrial && offerValid && (
2!
162
        <p className="offerBox">
163
          {offerTag} – upgrade before December 31st to get a 20% discount for 6 months on Mender Basic and Mender Professional plans. The discount will be
164
          automatically applied to your account.
165
        </p>
166
      )}
167
      {isTrial ? <PricingContactNote /> : <AddOnSelection addons={addOns} features={features} updatedPlan={updatedPlan} onChange={setAddOns} />}
2!
168
      {isTrial && updatedPlan !== PLANS.enterprise.id && (
2!
169
        <>
170
          <h3 className="margin-top-large">2. Enter your payment details</h3>
171
          <p>
172
            You are upgrading to{' '}
173
            <b>
174
              Mender <span className="capitalized-start">{PLANS[updatedPlan].name}</span>
175
            </b>{' '}
176
            for <b>{PLANS[updatedPlan].price}</b>
177
          </p>
178
          <CardSection
179
            onCancel={() => Promise.resolve(dispatch(cancelUpgrade(org.id)))}
×
180
            onComplete={handleUpgrade}
181
            onSubmit={() => Promise.resolve(dispatch(startUpgrade(org.id)))}
×
182
            setSnackbar={message => dispatch(setSnackbar(message))}
×
183
            isSignUp={true}
184
          />
185
        </>
186
      )}
187
      {(!isTrial || updatedPlan === PLANS.enterprise.id) && (
4!
188
        <QuoteRequestForm addOns={addOns} currentPlan={currentPlan} isTrial={isTrial} updatedPlan={updatedPlan} onSendMessage={onSendRequest} />
189
      )}
190
    </div>
191
  );
192
};
193

194
export default Upgrade;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc