• 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

87.5
/src/js/components/devices/device-details/deployments.js
1
// Copyright 2023 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 } from 'react-redux';
16
import { Link } from 'react-router-dom';
17

18
import { Button, Table, TableBody, TableCell, TableHead, TableRow, buttonClasses, tableCellClasses } from '@mui/material';
19
import { makeStyles } from 'tss-react/mui';
20

21
import { deploymentsApiUrl, getDeviceDeployments, resetDeviceDeployments } from '../../../actions/deploymentActions';
22
import { deploymentDisplayStates, deploymentStatesToSubstates } from '../../../constants/deploymentConstants';
23
import { DEVICE_LIST_DEFAULTS } from '../../../constants/deviceConstants';
24
import Confirm from '../../common/confirm';
25
import Pagination from '../../common/pagination';
26
import { MaybeTime } from '../../common/time';
27
import { HELPTOOLTIPS, MenderHelpTooltip } from '../../helptips/helptooltips';
28
import { DeviceStateSelection } from '../authorized-devices';
29

30
const useStyles = makeStyles()(theme => ({
8✔
31
  deletion: { justifyContent: 'flex-end' },
32
  table: {
33
    minHeight: '10vh',
34
    [`.deleted > .${tableCellClasses.root}, .deleted a`]: {
35
      background: theme.palette.background.lightgrey,
36
      color: theme.palette.grey[700],
37
      [`.${buttonClasses.root}`]: { color: theme.palette.text.primary }
38
    }
39
  }
40
}));
41

42
const EmptyState = ({ isFiltered }) => (
8✔
43
  <div className="flexbox column centered margin-large">
×
44
    <p className="align-center muted">
45
      No deployments were found.
46
      <br />
47
      {isFiltered && <>Try adjusting the filters</>}
×
48
    </p>
49
  </div>
50
);
51

52
const columns = [
8✔
53
  { content: 'Release', key: 'release', Component: ({ deviceDeployment: { release } }) => <Link to={`/releases/${release}`}>{release}</Link> },
2✔
54
  {
55
    content: 'Target device(s)',
56
    key: 'target',
57
    Component: ({ deviceDeployment: { id, target, route } }) => <Link to={`/deployments/${route}?id=${id}&open=true`}>{target}</Link>
2✔
58
  },
59
  { content: 'Started', key: 'created', Component: ({ deviceDeployment: { created } }) => <MaybeTime value={created} /> },
2✔
60
  { content: 'Finished', key: 'finished', Component: ({ deviceDeployment: { finished } }) => <MaybeTime value={finished} /> },
2✔
61
  {
62
    content: 'Deployment progress',
63
    key: 'deploymentStatus',
64
    Component: ({ deviceDeployment: { deploymentStatus } }) => deploymentDisplayStates[deploymentStatus] ?? deploymentStatus
2!
65
  },
66
  { content: 'Device status', key: 'status', Component: ({ deviceDeployment: { status } }) => status },
2✔
67
  {
68
    content: '',
69
    key: 'log',
70
    Component: ({ deviceDeployment: { id, log, deviceId } }) =>
71
      log && (
2✔
72
        <Button component="a" href={`${window.location.origin}${deploymentsApiUrl}/deployments/${id}/devices/${deviceId}/log`} target="_blank">
73
          Log
74
        </Button>
75
      )
76
  }
77
];
78

79
const History = ({ className, items, page, perPage, setPage, setPerPage, total }) => {
8✔
80
  const onChangeRowsPerPage = perPage => {
2✔
81
    setPage(1);
×
82
    setPerPage(perPage);
×
83
  };
84
  const wasReset = items.reduce((accu, { deleted }) => {
2✔
85
    if (!accu) {
2!
86
      return !!deleted;
2✔
87
    }
88
    return accu;
×
89
  }, false);
90
  return (
2✔
91
    <div className={className}>
92
      <Table>
93
        <TableHead>
94
          <TableRow>
95
            {columns.map(({ content, key }) => (
96
              <TableCell key={key}>{content}</TableCell>
14✔
97
            ))}
98
          </TableRow>
99
        </TableHead>
100
        <TableBody>
101
          {items.map(item => (
102
            <TableRow className={item.deleted ? 'deleted' : ''} key={item.id}>
2!
103
              {columns.map(({ key, Component }) => (
104
                <TableCell key={`${item.id}-${key}`}>
14✔
105
                  <Component deviceDeployment={item} />
106
                </TableCell>
107
              ))}
108
            </TableRow>
109
          ))}
110
        </TableBody>
111
      </Table>
112
      <div className="flexbox space-between">
113
        <Pagination
114
          count={total}
115
          onChangePage={setPage}
116
          onChangeRowsPerPage={onChangeRowsPerPage}
117
          page={page}
118
          rowsPerPage={perPage}
119
          rowsPerPageOptions={[10, 20]}
120
        />
121
        {wasReset && <MenderHelpTooltip id={HELPTOOLTIPS.resetHistory.id} />}
2!
122
      </div>
123
    </div>
124
  );
125
};
126

127
const deploymentStates = {
8✔
128
  any: { key: 'any', title: () => 'any', values: [] },
2✔
129
  pending: { key: 'pending', title: () => 'pending', values: deploymentStatesToSubstates.pending },
2✔
130
  inprogress: { key: 'inprogress', title: () => 'in progress', values: deploymentStatesToSubstates.inprogress },
2✔
131
  paused: { key: 'paused', title: () => 'paused', values: deploymentStatesToSubstates.paused },
2✔
132
  failures: { key: 'failures', title: () => 'failures', values: deploymentStatesToSubstates.failures },
2✔
133
  successes: { key: 'successes', title: () => 'successes', values: deploymentStatesToSubstates.successes }
2✔
134
};
135

136
export const Deployments = ({ device }) => {
8✔
137
  const [filters, setFilters] = useState([deploymentStates.any.key]);
2✔
138
  const [page, setPage] = useState(DEVICE_LIST_DEFAULTS.page);
2✔
139
  const [perPage, setPerPage] = useState(10);
2✔
140
  const [isChecking, setIsChecking] = useState(false);
2✔
141
  const { classes } = useStyles();
2✔
142
  const dispatch = useDispatch();
2✔
143

144
  useEffect(() => {
2✔
145
    if (!device?.id) {
2!
146
      return;
×
147
    }
148
    const filterSelection = deploymentStates[filters[0]].values;
2✔
149
    dispatch(getDeviceDeployments(device.id, { filterSelection, page, perPage }));
2✔
150
  }, [device.id, dispatch, filters, page, perPage]);
151

152
  const onSelectStatus = status => setFilters([status]);
2✔
153

154
  const onResetStart = () => setIsChecking(true);
2✔
155

156
  const onResetConfirm = () => dispatch(resetDeviceDeployments(device.id)).then(() => setIsChecking(false));
2✔
157

158
  const { deviceDeployments = [], deploymentsCount } = device;
2!
159

160
  return (
2✔
161
    <div className="margin-bottom">
162
      <h4 className="margin-bottom-small">Deployments</h4>
163
      <div className="flexbox margin-bottom-small" style={{ alignSelf: 'flex-start' }}>
164
        <DeviceStateSelection onStateChange={onSelectStatus} selectedState={filters[0]} states={deploymentStates} />
165
      </div>
166

167
      {!deviceDeployments.length ? (
2!
168
        <EmptyState isFiltered={filters[0] !== deploymentStates.any.key} />
169
      ) : (
170
        <>
171
          <History
172
            className={classes.table}
173
            items={deviceDeployments}
174
            page={page}
175
            perPage={perPage}
176
            setPage={setPage}
177
            setPerPage={setPerPage}
178
            total={deploymentsCount}
179
          />
180
          <div className={`flexbox margin-top relative ${classes.deletion}`}>
181
            {isChecking && (
2!
182
              <Confirm
183
                classes="confirmation-overlay"
184
                cancel={() => setIsChecking(false)}
×
185
                action={onResetConfirm}
186
                message={`This will reset the stored device deployment history for this device. Are you sure?`}
187
                style={{ marginRight: 0 }}
188
              />
189
            )}
190
            <Button onClick={onResetStart} variant="contained">
191
              Reset device deployment history
192
            </Button>
193
          </div>
194
        </>
195
      )}
196
    </div>
197
  );
198
};
199

200
export default Deployments;
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