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

mendersoftware / mender-server / 1593965839

18 Dec 2024 10:58AM UTC coverage: 73.514% (+0.7%) from 72.829%
1593965839

Pull #253

gitlab-ci

mineralsfree
chore(gui): aligned tests with edit billing profile

Ticket: MEN-7466
Changelog: None

Signed-off-by: Mikita Pilinka <mikita.pilinka@northern.tech>
Pull Request #253: MEN-7466-feat: updated billing section in My Organization settings

4257 of 6185 branches covered (68.83%)

Branch coverage included in aggregate %.

53 of 87 new or added lines in 11 files covered. (60.92%)

43 existing lines in 11 files now uncovered.

40083 of 54130 relevant lines covered (74.05%)

22.98 hits per line

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

73.97
/frontend/src/js/components/tenants/TenantList.tsx
1
// Copyright 2024 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 { useCallback, useEffect } from 'react';
15
import { useDispatch, useSelector } from 'react-redux';
16

17
import { Check as CheckIcon, Warning as WarningIcon } from '@mui/icons-material';
18

19
import DetailsIndicator from '@northern.tech/common-ui/detailsindicator';
20
import { ColumnHeader, CommonList, ListItemComponentProps, RendererProp } from '@northern.tech/common-ui/list';
21
import { SORTING_OPTIONS } from '@northern.tech/store/commonConstants';
22
import { getTenantsList } from '@northern.tech/store/selectors';
23
import { AppDispatch } from '@northern.tech/store/store';
24
import { setTenantsListState } from '@northern.tech/store/thunks';
25
import { useLocationParams } from '@northern.tech/utils/liststatehook';
26
import dayjs from 'dayjs';
27

28
import { LIMIT_THRESHOLD } from '../header/devicecount';
29
import { ExpandedTenant } from './ExpandedTenant';
30
import { Tenant } from './types';
31

32
export const defaultTextRender = (props: RendererProp<Tenant>) => {
5✔
33
  const { column, item } = props;
8✔
34
  const attributeValue = item?.[column.attribute.name];
8✔
35
  return typeof attributeValue === 'object' ? JSON.stringify(attributeValue) : attributeValue;
8!
36
};
37
export const DeviceLimitRender = (props: RendererProp<Tenant>) => {
5✔
38
  const { column, item } = props;
4✔
39
  const attributeValue = item?.[column.attribute.name];
4✔
40
  const deviceCount = item?.device_count;
4✔
41
  return (
4✔
42
    <div>
43
      {deviceCount}/{attributeValue}
44
      <div className="margin-left-small margin-top-x-small">
45
        {Number(deviceCount) / Number(attributeValue) > LIMIT_THRESHOLD && <WarningIcon sx={{ fontSize: '20px' }} />}
4!
46
      </div>
47
    </div>
48
  );
49
};
50
export const BoolRender = (props: RendererProp<Tenant>) => {
5✔
51
  const { column, item } = props;
4✔
52
  return <div>{item?.[column.attribute.name] ? <CheckIcon /> : <div>-</div>}</div>;
4!
53
};
54
const AttributeRenderer = ({ content, textContent }) => (
5✔
55
  <div title={typeof textContent === 'string' ? textContent : ''}>
8!
56
    <div className="text-overflow">{content}</div>
57
  </div>
58
);
59
const DateRender = (props: RendererProp<Tenant>) => {
5✔
60
  const { column, item } = props;
4✔
61
  const attributeValue = dayjs(item?.[column.attribute.name]).format('YYYY-MM-DD HH:mm');
4✔
62
  return <AttributeRenderer content={attributeValue} textContent={item?.[column.attribute.name]} />;
4✔
63
};
64
export const columnHeaders: ColumnHeader<Tenant>[] = [
5✔
65
  {
66
    component: () => <></>,
×
67
    title: 'Name',
68
    attribute: {
69
      name: 'name',
70
      scope: ''
71
    },
72
    sortable: false,
73
    textRender: defaultTextRender
74
  },
75
  {
76
    title: 'Devices',
77
    attribute: {
78
      name: 'device_limit',
79
      scope: ''
80
    },
81
    sortable: false,
82
    component: DeviceLimitRender
83
  },
84
  {
85
    title: 'Delta updates enabled ',
86
    attribute: {
87
      name: 'binary_delta',
88
      scope: ''
89
    },
90
    sortable: false,
91
    component: BoolRender
92
  },
93
  {
94
    title: 'Created',
95
    attribute: {
96
      name: 'created_at',
97
      scope: ''
98
    },
99
    sortable: false,
100
    component: DateRender
101
  },
102
  {
103
    title: 'More details',
104
    attribute: {
105
      name: '',
106
      scope: ''
107
    },
108
    sortable: false,
109
    component: DetailsIndicator
110
  }
111
];
112

113
export const TenantListItem = (props: ListItemComponentProps<Tenant>) => {
5✔
114
  const { listItem, columnHeaders, onClick } = props;
4✔
115
  const handleOnClick = useCallback(() => {
4✔
116
    onClick(listItem);
1✔
117
    // eslint-disable-next-line react-hooks/exhaustive-deps
118
  }, [listItem.id, onClick]);
119

120
  return (
4✔
121
    <div onClick={handleOnClick} className={`deviceListRow deviceListItem clickable`}>
122
      {columnHeaders.map((column: ColumnHeader<Tenant>) => {
123
        const { classes = {}, component: Component, textRender } = column;
20✔
124
        if (textRender) {
20✔
125
          return <AttributeRenderer content={textRender({ item: listItem, column })} key={column.title} textContent={textRender({ item: listItem, column })} />;
4✔
126
        }
127
        return <Component classes={classes} column={column} item={listItem} key={column.title} />;
16✔
128
      })}
129
    </div>
130
  );
131
};
132
export const TenantList = () => {
5✔
133
  const tenantListState = useSelector(getTenantsList);
1✔
134
  const { tenants, perPage, selectedTenant, sort = {} } = tenantListState;
1!
135
  const dispatch: AppDispatch = useDispatch();
1✔
136

137
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
138
  const [locationParams, setLocationParams] = useLocationParams('tenants', {
1✔
139
    defaults: {
140
      direction: SORTING_OPTIONS.desc,
141
      key: 'name',
142
      sort: {}
143
    }
144
  });
145

146
  useEffect(() => {
1✔
147
    const { selectedTenant: selectedTenantName } = locationParams;
1✔
148
    if (selectedTenantName) {
1!
UNCOV
149
      dispatch(setTenantsListState({ selectedTenant: selectedTenantName }));
×
150
    }
151
  }, [dispatch, locationParams]);
152

153
  useEffect(() => {
1✔
154
    if (selectedTenant) {
1!
UNCOV
155
      setLocationParams({ pageState: { ...tenantListState, selectedTenant } });
×
156
    }
157
    // eslint-disable-next-line react-hooks/exhaustive-deps
158
  }, [setLocationParams, JSON.stringify(sort), selectedTenant]);
159

160
  const onExpandClick = useCallback(
1✔
161
    (tenant: Tenant) => {
UNCOV
162
      return dispatch(setTenantsListState({ selectedTenant: tenant.id }));
×
163
    },
164
    [dispatch]
165
  );
166

167
  const onCloseClick = useCallback(() => {
1✔
168
    setLocationParams({ pageState: { ...tenantListState, selectedTenant: '' } });
×
UNCOV
169
    return dispatch(setTenantsListState({ selectedTenant: null }));
×
170
    // eslint-disable-next-line react-hooks/exhaustive-deps
171
  }, [dispatch, setLocationParams, JSON.stringify(tenantListState)]);
172

173
  const onChangePagination = useCallback(
1✔
174
    (page, currentPerPage = perPage) => {
×
UNCOV
175
      dispatch(setTenantsListState({ page, perPage: currentPerPage }));
×
176
    },
177
    [dispatch, perPage]
178
  );
179

180
  const tenant = selectedTenant && tenants.find((tenant: Tenant) => selectedTenant === tenant.id);
1!
181
  return (
1✔
182
    <div>
183
      <CommonList
184
        columnHeaders={columnHeaders}
185
        listItems={tenants}
186
        listState={tenantListState}
UNCOV
187
        onChangeRowsPerPage={newPerPage => onChangePagination(1, newPerPage)}
×
188
        onExpandClick={onExpandClick}
189
        onPageChange={onChangePagination}
190
        onResizeColumns={false}
191
        onSelect={false}
192
        pageLoading={false}
193
        ListItemComponent={TenantListItem}
194
      />
195
      {selectedTenant && tenant && <ExpandedTenant onCloseClick={onCloseClick} tenant={tenant} />}
1!
196
    </div>
197
  );
198
};
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