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

mendersoftware / gui / 963002358

pending completion
963002358

Pull #3870

gitlab-ci

mzedel
chore: cleaned up left over onboarding tooltips & aligned with updated design

Signed-off-by: Manuel Zedel <manuel.zedel@northern.tech>
Pull Request #3870: MEN-5413

4348 of 6319 branches covered (68.81%)

95 of 122 new or added lines in 24 files covered. (77.87%)

1734 existing lines in 160 files now uncovered.

8174 of 9951 relevant lines covered (82.14%)

178.12 hits per line

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

91.23
/src/js/components/devices/device-details/connection.js
1
// Copyright 2021 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 { Link } from 'react-router-dom';
16

17
import { ImportExport as ImportExportIcon, InfoOutlined as InfoIcon, Launch as LaunchIcon } from '@mui/icons-material';
18
import { Button, Typography } from '@mui/material';
19
import { useTheme } from '@mui/material/styles';
20

21
import { mdiConsole as ConsoleIcon } from '@mdi/js';
22

23
import { BEGINNING_OF_TIME } from '../../../constants/appConstants';
24
import { ALL_DEVICES, DEVICE_CONNECT_STATES } from '../../../constants/deviceConstants';
25
import { AUDIT_LOGS_TYPES } from '../../../constants/organizationConstants';
26
import { checkPermissionsObject, uiPermissionsById } from '../../../constants/userConstants';
27
import { formatAuditlogs } from '../../../utils/locationutils';
28
import DocsLink from '../../common/docslink';
29
import MaterialDesignIcon from '../../common/materialdesignicon';
30
import MenderTooltip from '../../common/mendertooltip';
31
import Time from '../../common/time';
32
import Troubleshootdialog from '../dialogs/troubleshootdialog';
33
import DeviceDataCollapse from './devicedatacollapse';
34

35
const buttonStyle = { textTransform: 'none', textAlign: 'left' };
11✔
36
export const PortForwardLink = () => (
11✔
37
  <MenderTooltip
3✔
38
    arrow
39
    title={
40
      <div style={{ whiteSpace: 'normal' }}>
41
        <h3>Port forwarding</h3>
42
        <p>Port forwarding allows you to troubleshoot or use services on or via the device, without opening any ports on the device itself.</p>
43
        <p>
44
          To enable port forwarding you will need to install and configure the necessary software on the device and your workstation. Follow the link to learn
45
          more.
46
        </p>
47
      </div>
48
    }
49
  >
50
    <DocsLink className="flexbox centered margin-left" path="add-ons/port-forward">
51
      Enable port forwarding
52
      <LaunchIcon className="margin-left-small" fontSize="small" />
53
    </DocsLink>
54
  </MenderTooltip>
55
);
56

57
export const DeviceConnectionNote = ({ children, style = buttonStyle }) => {
11✔
58
  const theme = useTheme();
7✔
59
  return (
7✔
60
    <div className="flexbox muted">
61
      <InfoIcon fontSize="small" style={{ marginRight: theme.spacing() }} />
62
      <Typography variant="body1" style={style}>
63
        {children}
64
      </Typography>
65
    </div>
66
  );
67
};
68

69
export const DeviceConnectionMissingNote = () => (
11✔
70
  <DeviceConnectionNote>
3✔
71
    The troubleshoot add-on does not seem to be enabled on this device.
72
    <br />
73
    Please <DocsLink path="add-ons/remote-terminal" title="see the documentation" /> for a description on how it works and how to enable it.
74
  </DeviceConnectionNote>
75
);
76

77
export const DeviceDisconnectedNote = ({ lastConnectionTs }) => (
11✔
78
  <DeviceConnectionNote>
3✔
79
    The troubleshoot add-on is not currently connected on this device, it was last connected on <Time value={lastConnectionTs} />.
80
    <br />
81
    Please <DocsLink path="add-ons/remote-terminal" title="see the documentation" /> for more information.
82
  </DeviceConnectionNote>
83
);
84

85
export const TroubleshootButton = ({ disabled, item, onClick }) => {
11✔
86
  const theme = useTheme();
4✔
87
  return (
4✔
UNCOV
88
    <Button onClick={() => onClick(item.key)} disabled={disabled} startIcon={item.icon} style={{ marginRight: theme.spacing(2) }}>
×
89
      <Typography variant="subtitle2" style={buttonStyle}>
90
        {item.title}
91
      </Typography>
92
    </Button>
93
  );
94
};
95

96
const troubleshootingTools = [
11✔
97
  {
98
    key: 'terminal',
99
    title: 'Launch a new Remote Terminal session',
100
    icon: <MaterialDesignIcon path={ConsoleIcon} />,
101
    needsWriteAccess: true,
102
    needsTroubleshoot: true
103
  },
104
  { key: 'transfer', title: 'Launch File Transfer', icon: <ImportExportIcon />, needsWriteAccess: false, needsTroubleshoot: false },
105
  { key: 'portForward', component: PortForwardLink, needsWriteAccess: false, needsTroubleshoot: true }
106
];
107

108
const deviceAuditlogType = AUDIT_LOGS_TYPES.find(type => type.value === 'device');
33✔
109

110
export const DeviceConnection = ({ className = '', device, hasAuditlogs, socketClosed, startTroubleshoot, userCapabilities }) => {
11✔
111
  const [availableTabs, setAvailableTabs] = useState(troubleshootingTools);
6✔
112

113
  const { canAuditlog, canTroubleshoot, canWriteDevices: hasWriteAccess, groupsPermissions } = userCapabilities;
6✔
114

115
  useEffect(() => {
6✔
116
    const allowedTabs = troubleshootingTools.reduce((accu, tab) => {
3✔
117
      if (
9!
118
        (tab.needsWriteAccess && (!hasWriteAccess || !checkPermissionsObject(groupsPermissions, uiPermissionsById.connect.value, device.group, ALL_DEVICES))) ||
30✔
119
        (tab.needsTroubleshoot && !canTroubleshoot)
120
      ) {
UNCOV
121
        return accu;
×
122
      }
123
      accu.push(tab);
9✔
124
      return accu;
9✔
125
    }, []);
126
    setAvailableTabs(allowedTabs);
3✔
127
    // eslint-disable-next-line react-hooks/exhaustive-deps
128
  }, [hasWriteAccess, canTroubleshoot, JSON.stringify(groupsPermissions), device.group]);
129

130
  const { connect_status = DEVICE_CONNECT_STATES.unknown, connect_updated_ts } = device;
6✔
131
  return (
6✔
132
    <DeviceDataCollapse
133
      header={
134
        <div className={`flexbox ${className}`}>
135
          {connect_status === DEVICE_CONNECT_STATES.unknown && <DeviceConnectionMissingNote />}
8✔
136
          {connect_status === DEVICE_CONNECT_STATES.disconnected && <DeviceDisconnectedNote lastConnectionTs={connect_updated_ts} />}
8✔
137
          {connect_status === DEVICE_CONNECT_STATES.connected &&
8✔
138
            availableTabs.map(item => {
139
              let Component = TroubleshootButton;
6✔
140
              if (item.component) {
6✔
141
                Component = item.component;
2✔
142
              }
143
              return <Component key={item.key} onClick={startTroubleshoot} disabled={socketClosed} item={item} />;
6✔
144
            })}
145
          {canAuditlog && hasAuditlogs && connect_status !== DEVICE_CONNECT_STATES.unknown && (
16✔
146
            <Link
147
              className="flexbox center-aligned margin-left"
148
              to={`/auditlog?${formatAuditlogs({ pageState: { type: deviceAuditlogType, detail: device.id, startDate: BEGINNING_OF_TIME } }, {})}`}
149
            >
150
              List all log entries for this device
151
            </Link>
152
          )}
153
        </div>
154
      }
155
      isAddOn
156
      title="Troubleshoot"
157
    ></DeviceDataCollapse>
158
  );
159
};
160

161
export default DeviceConnection;
162

163
export const TroubleshootTab = ({
11✔
164
  classes,
165
  device,
166
  tenantCapabilities: { hasAuditlogs },
167
  socketClosed,
168
  launchTroubleshoot,
169
  userCapabilities,
170
  troubleshootType,
171
  setTroubleshootType,
172
  setSocketClosed
173
}) => (
UNCOV
174
  <>
×
175
    <DeviceConnection
176
      className={classes.deviceConnection}
177
      device={device}
178
      hasAuditlogs={hasAuditlogs}
179
      socketClosed={socketClosed}
180
      startTroubleshoot={launchTroubleshoot}
181
      userCapabilities={userCapabilities}
182
    />
183
    <Troubleshootdialog
184
      device={device}
185
      open={Boolean(troubleshootType)}
UNCOV
186
      onCancel={() => setTroubleshootType()}
×
187
      setSocketClosed={setSocketClosed}
188
      type={troubleshootType}
189
    />
190
  </>
191
);
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