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

mendersoftware / mender-server / 10423

11 Nov 2025 04:53PM UTC coverage: 74.435% (-0.1%) from 74.562%
10423

push

gitlab-ci

web-flow
Merge pull request #1071 from mendersoftware/dependabot/npm_and_yarn/frontend/main/development-dependencies-92732187be

3868 of 5393 branches covered (71.72%)

Branch coverage included in aggregate %.

5 of 5 new or added lines in 2 files covered. (100.0%)

176 existing lines in 95 files now uncovered.

64605 of 86597 relevant lines covered (74.6%)

7.74 hits per line

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

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

2✔
18
import { Typography, tableCellClasses } from '@mui/material';
2✔
19
import { makeStyles } from 'tss-react/mui';
2✔
20

2✔
21
import DetailsTable from '@northern.tech/common-ui/DetailsTable';
2✔
22
import { DefaultUpgradeNotification } from '@northern.tech/common-ui/EnterpriseNotification';
2✔
23
import Loader from '@northern.tech/common-ui/Loader';
2✔
24
import Pagination from '@northern.tech/common-ui/Pagination';
2✔
25
import { MaybeTime } from '@northern.tech/common-ui/Time';
2✔
26
import storeActions from '@northern.tech/store/actions';
2✔
27
import { DEVICE_LIST_DEFAULTS, SORTING_OPTIONS } from '@northern.tech/store/constants';
2✔
28
import { getDeltaJobsById, getDeltaJobsListState, getIsEnterprise, getSelectedJob } from '@northern.tech/store/selectors';
2✔
29
import { getDeltaGenerationJobs } from '@northern.tech/store/thunks';
2✔
30
import { formatTime } from '@northern.tech/utils/helpers';
2✔
31

2✔
32
import DeltaGenerationDetailsDrawer, { StatusIndicator } from './DeltaGenerationDetailsDrawer';
2✔
33

2✔
34
const { setDeltaJobsListState, setSelectedJob } = storeActions;
7✔
35

2✔
36
const useStyles = makeStyles()(() => ({
8✔
37
  table: {
2✔
38
    [`.${tableCellClasses.body}, .${tableCellClasses.head}`]: {
2✔
39
      paddingLeft: 0
2✔
40
    }
2✔
41
  }
2✔
42
}));
2✔
43

2✔
44
const deltaJobColumns = [
7✔
45
  {
2✔
46
    key: 'to_release',
2✔
47
    title: 'To version',
2✔
48
    sortable: true,
2✔
49
    cellProps: { style: { width: '20%' } },
2✔
50
    render: ({ to_release, to_version }) => <Link to={`/releases/${to_release || to_version || '-'}`}>{to_release || to_version || '-'}</Link>
24!
51
  },
2✔
52
  {
2✔
53
    key: 'from_release',
2✔
54
    title: 'From version',
2✔
55
    sortable: true,
2✔
56
    cellProps: { style: { width: '20%' } },
2✔
57
    render: ({ from_release, from_version }) => <Link to={`/releases/${from_release || from_version}`}>{from_release || from_version || '-'}</Link>
24!
58
  },
2✔
59
  {
2✔
60
    key: 'device_types_compatible',
2✔
61
    title: 'Device types compatible',
2✔
62
    sortable: false,
2✔
63
    cellProps: { style: { width: '25%' } },
2✔
64
    render: ({ device_types_compatible, devices_types_compatible }) => (device_types_compatible || devices_types_compatible || []).join(', ') || '-'
24!
65
  },
2✔
66
  {
2✔
67
    key: 'started',
2✔
68
    title: 'Started',
2✔
69
    sortable: true,
2✔
70
    cellProps: { style: { width: '10%' } },
2✔
71
    render: ({ started }) => <MaybeTime value={formatTime(started)} />
24✔
72
  },
2✔
73
  {
2✔
74
    key: 'status',
2✔
75
    title: 'Status',
2✔
76
    sortable: true,
2✔
77
    cellProps: { style: { width: '20%' } },
2✔
78
    render: StatusIndicator
2✔
79
  },
2✔
80
  {
2✔
81
    key: 'spacer',
2✔
82
    title: '',
2✔
83
    sortable: false,
2✔
84
    cellProps: { style: { width: '5%' } },
2✔
85
    render: () => ''
24✔
86
  }
2✔
87
];
2✔
88

2✔
89
const { page: defaultPage, perPage: defaultPerPage } = DEVICE_LIST_DEFAULTS;
7✔
90

2✔
91
export const DeltaProgress = ({ className = '' }) => {
7✔
92
  const dispatch = useDispatch();
18✔
93
  const isEnterprise = useSelector(getIsEnterprise);
18✔
94
  const { jobIds, total, sort, page = defaultPage, perPage = defaultPerPage } = useSelector(getDeltaJobsListState);
18✔
95
  const byId = useSelector(getDeltaJobsById);
18✔
96
  const selectedJob = useSelector(getSelectedJob);
18✔
97
  const [isLoading, setIsLoading] = useState(false);
18✔
98
  const { classes } = useStyles();
18✔
99

2✔
100
  useEffect(() => {
18✔
101
    if (!isEnterprise) {
8✔
102
      return;
3✔
103
    }
2✔
104
    setIsLoading(true);
7✔
105
    dispatch(getDeltaGenerationJobs({ sort, page, perPage }))
7✔
106
      .unwrap()
2✔
107
      .finally(() => setIsLoading(false));
7✔
108
  }, [dispatch, isEnterprise, sort, page, perPage]);
2✔
109

2✔
110
  const jobsList = useMemo(() => jobIds.map(id => byId[id]).filter(Boolean), [byId, jobIds]);
18✔
111

2✔
112
  const onChangeSorting = useCallback(
18✔
113
    key => {
2✔
114
      const direction = sort.key === key && sort.direction === SORTING_OPTIONS.desc ? SORTING_OPTIONS.asc : SORTING_OPTIONS.desc;
2!
115
      const newSort = { key, direction };
2✔
116
      dispatch(setDeltaJobsListState({ sort: newSort, page: 1 }));
2✔
117
    },
2✔
118
    [dispatch, sort]
2✔
119
  );
2✔
120

2✔
121
  const onChangePagination = useCallback(
18✔
122
    (newPage, currentPerPage = perPage) => {
2!
123
      dispatch(setDeltaJobsListState({ page: newPage, perPage: currentPerPage }));
2✔
124
    },
2✔
125
    [dispatch, perPage]
2✔
126
  );
2✔
127

2✔
128
  const onJobSelect = useCallback(selection => dispatch(setSelectedJob(selection.id)), [dispatch]);
18✔
129

2✔
130
  const onCloseDetailsDrawer = () => onJobSelect({ id: null });
18✔
131

2✔
132
  if (!isEnterprise) {
18✔
133
    return (
3✔
134
      <div className={`dashboard-placeholder ${className}`} style={{ display: 'grid', placeContent: 'center' }}>
2✔
135
        <DefaultUpgradeNotification />
2✔
136
      </div>
2✔
137
    );
2✔
138
  }
2✔
139

2✔
140
  if (!total) {
17✔
141
    return (
4✔
142
      <div className={`dashboard-placeholder ${className}`} style={{ display: 'grid', placeContent: 'center' }}>
2✔
143
        No Delta Artifacts have been generated in the last 30 days.
2✔
144
      </div>
2✔
145
    );
2✔
146
  }
2✔
147

2✔
148
  return (
15✔
149
    <div className={className}>
2✔
150
      <Typography className="margin-bottom" variant="subtitle1">
2✔
151
        Generated Delta Artifacts
2✔
152
      </Typography>
2✔
153
      <DetailsTable
2✔
154
        className={classes.table}
2✔
155
        columns={deltaJobColumns}
2✔
156
        items={jobsList}
2✔
157
        onChangeSorting={onChangeSorting}
2✔
158
        onItemClick={onJobSelect}
2✔
159
        sort={sort}
2✔
160
      />
2✔
161
      <div className="flexbox">
2✔
162
        <Pagination
2✔
163
          className="margin-top-none"
2✔
164
          count={total}
2✔
165
          onChangePage={onChangePagination}
2✔
UNCOV
166
          onChangeRowsPerPage={newPerPage => onChangePagination(1, newPerPage)}
2✔
167
          page={page}
2✔
168
          rowsPerPage={perPage}
2✔
169
        />
2✔
170
        <Loader show={isLoading} small />
2✔
171
      </div>
2✔
172
      <DeltaGenerationDetailsDrawer jobId={selectedJob?.id} open={!!selectedJob?.id} onClose={onCloseDetailsDrawer} />
2✔
173
    </div>
2✔
174
  );
2✔
175
};
2✔
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