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

mendersoftware / gui / 1081664682

22 Nov 2023 02:11PM UTC coverage: 82.798% (-17.2%) from 99.964%
1081664682

Pull #4214

gitlab-ci

tranchitella
fix: Fixed the infinite page redirects when the back button is pressed

Remove the location and navigate from the useLocationParams.setValue callback
dependencies as they change the set function that is presented in other
useEffect dependencies. This happens when the back button is clicked, which
leads to the location changing infinitely.

Changelog: Title
Ticket: MEN-6847
Ticket: MEN-6796

Signed-off-by: Ihor Aleksandrychiev <ihor.aleksandrychiev@northern.tech>
Signed-off-by: Fabio Tranchitella <fabio.tranchitella@northern.tech>
Pull Request #4214: fix: Fixed the infinite page redirects when the back button is pressed

4319 of 6292 branches covered (0.0%)

8332 of 10063 relevant lines covered (82.8%)

191.0 hits per line

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

76.19
/src/js/components/settings/organization/billing.js
1
// Copyright 2022 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, { useState } from 'react';
15
import { useDispatch, useSelector } from 'react-redux';
16
import { Link, useNavigate } from 'react-router-dom';
17

18
// material ui
19
import { Error as ErrorIcon, OpenInNew as OpenInNewIcon } from '@mui/icons-material';
20
import { LinearProgress, List } from '@mui/material';
21
import { makeStyles } from 'tss-react/mui';
22

23
import moment from 'moment';
24

25
import { cancelRequest } from '../../../actions/organizationActions';
26
import { ADDONS, PLANS } from '../../../constants/appConstants';
27
import { toggle } from '../../../helpers';
28
import { getAcceptedDevices, getDeviceLimit, getIsEnterprise, getOrganization, getUserRoles } from '../../../selectors';
29
import Alert from '../../common/alert';
30
import CancelRequestDialog from '../dialogs/cancelrequest';
31
import OrganizationPaymentSettings from './organizationpaymentsettings';
32
import OrganizationSettingsItem, { maxWidth } from './organizationsettingsitem';
33

34
const useStyles = makeStyles()(theme => ({
5✔
35
  deviceLimitBar: { backgroundColor: theme.palette.grey[500], margin: '15px 0' },
36
  wrapper: {
37
    backgroundColor: theme.palette.background.lightgrey,
38
    marginTop: theme.spacing(6),
39
    padding: theme.spacing(2),
40
    '&>h5': { marginTop: 0, marginBottom: 0 }
41
  }
42
}));
43

44
export const TrialExpirationNote = ({ trial_expiration }) => (
5✔
45
  <div className="flexbox centered muted">
7✔
46
    <ErrorIcon fontSize="small" />
47
    <span className="margin-left-small">
48
      Your trial expires in {moment().from(moment(trial_expiration), true)}. <Link to="/settings/upgrade">Upgrade to a paid plan</Link>
49
    </span>
50
  </div>
51
);
52

53
export const DeviceLimitExpansionNotification = ({ isTrial }) => (
5✔
54
  <div className="flexbox centered">
1✔
55
    <ErrorIcon className="muted margin-right-small" fontSize="small" />
56
    <div className="muted" style={{ marginRight: 4 }}>
57
      To increase your device limit,{' '}
58
    </div>
59
    {isTrial ? (
1!
60
      <Link to="/settings/upgrade">upgrade to a paid plan</Link>
61
    ) : (
62
      <a href="mailto:contact@mender.io" target="_blank" rel="noopener noreferrer">
63
        contact our sales team
64
      </a>
65
    )}
66
    <div className="muted">.</div>
67
  </div>
68
);
69

70
export const CancelSubscriptionAlert = () => (
5✔
71
  <Alert className="margin-top-large" severity="error" style={{ maxWidth }}>
1✔
72
    <p>We&#39;ve started the process to cancel your plan and deactivate your account.</p>
73
    <p>
74
      We&#39;ll send you an email confirming your deactivation. If you have any question at all, contact us at our{' '}
75
      <strong>
76
        <a href="https://support.northern.tech" target="_blank" rel="noopener noreferrer">
77
          support portal
78
        </a>
79
        .
80
      </strong>
81
    </p>
82
  </Alert>
83
);
84

85
export const CancelSubscriptionButton = ({ handleCancelSubscription, isTrial }) => (
5✔
86
  <p className="margin-left-small margin-right-small" style={{ maxWidth }}>
13✔
87
    <a href="" onClick={handleCancelSubscription}>
88
      {isTrial ? 'End trial' : 'Cancel subscription'} and deactivate account
13✔
89
    </a>
90
  </p>
91
);
92

93
export const Billing = () => {
5✔
94
  const [cancelSubscription, setCancelSubscription] = useState(false);
12✔
95
  const [cancelSubscriptionConfirmation, setCancelSubscriptionConfirmation] = useState(false);
12✔
96
  const { isAdmin } = useSelector(getUserRoles);
12✔
97
  const { total: acceptedDevices = 0 } = useSelector(getAcceptedDevices);
12!
98
  const deviceLimit = useSelector(getDeviceLimit);
12✔
99
  const isEnterprise = useSelector(getIsEnterprise);
12✔
100
  const organization = useSelector(getOrganization);
12✔
101
  const { plan: currentPlan = PLANS.os.id } = organization;
12!
102
  const dispatch = useDispatch();
12✔
103
  const navigate = useNavigate();
12✔
104
  const { classes } = useStyles();
12✔
105

106
  const planName = PLANS[currentPlan].name;
12✔
107

108
  const enabledAddOns =
109
    organization.addons?.reduce((accu, addon) => {
12!
110
      if (addon.enabled) {
6!
111
        const { title } = ADDONS[addon.name];
6✔
112
        let addonPrice = '';
6✔
113
        if (!organization.trial && !isEnterprise) {
6!
114
          const planAddon = ADDONS[addon.name][currentPlan] ? ADDONS[addon.name][currentPlan] : ADDONS[addon.name].os;
×
115
          addonPrice = ` - ${planAddon.price}`;
×
116
        }
117
        accu.push(`${title}${addonPrice}`);
6✔
118
      }
119
      return accu;
6✔
120
    }, []) || [];
121

122
  const cancelSubscriptionSubmit = async reason =>
12✔
123
    dispatch(cancelRequest(organization.id, reason)).then(() => {
×
124
      setCancelSubscription(false);
×
125
      setCancelSubscriptionConfirmation(true);
×
126
    });
127

128
  const handleCancelSubscription = e => {
12✔
129
    if (e !== undefined) {
×
130
      e.preventDefault();
×
131
    }
132
    setCancelSubscription(toggle);
×
133
  };
134

135
  return (
12✔
136
    <div className={classes.wrapper}>
137
      <h5>Billing</h5>
138
      <List>
139
        <OrganizationSettingsItem
140
          title="Current plan"
141
          content={{
142
            action: { title: 'Compare product plans', internal: false, target: 'https://mender.io/plans/pricing' },
143
            description: organization.trial ? 'Trial' : planName
12✔
144
          }}
145
          notification={organization.trial ? <TrialExpirationNote trial_expiration={organization.trial_expiration} /> : null}
12✔
146
        />
147
        {deviceLimit > 0 && (
12!
148
          <OrganizationSettingsItem
149
            title={`Device limit: ${acceptedDevices}/${deviceLimit}`}
150
            content={{}}
151
            secondary={<LinearProgress className={classes.deviceLimitBar} variant="determinate" value={(acceptedDevices * 100) / deviceLimit} />}
152
            notification={<DeviceLimitExpansionNotification isTrial={organization.trial} />}
153
          />
154
        )}
155
        <OrganizationSettingsItem
156
          title="Current add-ons"
157
          content={{
158
            action: { title: 'Purchase an add-on', internal: true, action: () => navigate('/settings/upgrade') },
×
159
            description: enabledAddOns.length ? enabledAddOns.join(', ') : `You currently don't have any add-ons`
12✔
160
          }}
161
          notification={organization.trial && <TrialExpirationNote trial_expiration={organization.trial_expiration} />}
15✔
162
          sideBarContent={
163
            <div className="margin-left-small margin-bottom">
164
              <a className="flexbox center-aligned" href="https://mender.io/plans/pricing" target="_blank" rel="noopener noreferrer">
165
                <div style={{ maxWidth: 200 }}>Compare plans and add-ons at mender.io</div>
166
                <OpenInNewIcon fontSize="small" />
167
              </a>
168
            </div>
169
          }
170
        />
171
        {!organization.trial && !isEnterprise && <OrganizationPaymentSettings />}
21!
172
      </List>
173
      {cancelSubscriptionConfirmation && <CancelSubscriptionAlert />}
12!
174
      {isAdmin && !cancelSubscriptionConfirmation && (
36✔
175
        <CancelSubscriptionButton handleCancelSubscription={handleCancelSubscription} isTrial={organization.trial} />
176
      )}
177
      {cancelSubscription && <CancelRequestDialog onCancel={() => setCancelSubscription(false)} onSubmit={cancelSubscriptionSubmit} />}
×
178
    </div>
179
  );
180
};
181

182
export default Billing;
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