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

mendersoftware / mender-server / 1495380963

14 Oct 2024 03:35PM UTC coverage: 70.373% (-2.5%) from 72.904%
1495380963

Pull #101

gitlab-ci

mineralsfree
feat: tenant list added

Ticket: MEN-7568
Changelog: None

Signed-off-by: Mikita Pilinka <mikita.pilinka@northern.tech>
Pull Request #101: feat: tenant list added

4406 of 6391 branches covered (68.94%)

Branch coverage included in aggregate %.

88 of 183 new or added lines in 10 files covered. (48.09%)

2623 existing lines in 65 files now uncovered.

36673 of 51982 relevant lines covered (70.55%)

31.07 hits per line

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

0.0
/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 React, { useCallback, useEffect, useState } from 'react';
15
import { useDispatch, useSelector } from 'react-redux';
16

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

19
import { getDeviceLimit } from '@northern.tech/store/devicesSlice/selectors';
20
import { getTenantsList } from '@northern.tech/store/organizationSlice/selectors';
21
import { getTenants, setTenantsListState } from '@northern.tech/store/organizationSlice/thunks';
22
import { AppDispatch } from '@northern.tech/store/store';
23
import dayjs from 'dayjs';
24

25
import { ColumnHeader, CommonList, ListItemComponentProps } from '../common/list';
26
import { ExpandedTenant } from './expanded-tenant';
27
import { Tenant } from './types';
28

29
interface RendererProp {
30
  column: ColumnHeader;
31
  tenant: Tenant;
32
}
33

NEW
34
export const defaultTextRender = (props: RendererProp) => {
×
NEW
35
  const { column, tenant } = props;
×
NEW
36
  const attributeValue = tenant[column.attribute.name];
×
NEW
37
  return typeof attributeValue === 'object' ? JSON.stringify(attributeValue) : attributeValue;
×
38
};
NEW
39
export const DeviceLimitRender = (props: RendererProp) => {
×
NEW
40
  const deviceLimit = useSelector(getDeviceLimit);
×
NEW
41
  const { column, tenant } = props;
×
NEW
42
  const attributeValue = tenant[column.attribute.name];
×
NEW
43
  return `${attributeValue}/${deviceLimit}`;
×
44
};
NEW
45
export const boolRender = (props: RendererProp) => {
×
NEW
46
  const { column, tenant } = props;
×
NEW
47
  return <div>{tenant[column.attribute.name] ? <CheckIcon /> : <div>-</div>}</div>;
×
48
};
NEW
49
const AttributeRenderer = ({ content, textContent }) => (
×
NEW
50
  <div title={typeof textContent === 'string' ? textContent : ''}>
×
51
    <div className="text-overflow">{content}</div>
52
  </div>
53
);
NEW
54
const dateRender = (props: RendererProp) => {
×
NEW
55
  const { column, tenant } = props;
×
NEW
56
  const attributeValue = dayjs(tenant[column.attribute.name]).format('YYYY-MM-DD HH:mm');
×
NEW
57
  return <AttributeRenderer content={attributeValue} textContent={tenant[column.attribute.name]}></AttributeRenderer>;
×
58
};
NEW
59
const moreDetailsRender = () => {
×
NEW
60
  return (
×
61
    <div className="link-color">
62
      View details <ArrowForwardIcon sx={{ fontSize: 12 }} />
63
    </div>
64
  );
65
};
NEW
66
const columnHeaders: ColumnHeader[] = [
×
67
  {
68
    title: 'Name',
69
    attribute: {
70
      name: 'name',
71
      scope: ''
72
    },
73
    sortable: false,
74
    textRender: defaultTextRender
75
  },
76
  {
77
    title: 'Devices',
78
    attribute: {
79
      name: 'device_limit',
80
      scope: ''
81
    },
82
    sortable: false,
83
    textRender: DeviceLimitRender
84
  },
85
  {
86
    title: 'Delta updates enabled ',
87
    attribute: {
88
      name: 'binary_delta',
89
      scope: ''
90
    },
91
    sortable: false,
92
    textRender: boolRender
93
  },
94
  {
95
    title: 'Created',
96
    attribute: {
97
      name: 'created_at',
98
      scope: ''
99
    },
100
    sortable: false,
101
    textRender: dateRender
102
  },
103
  {
104
    title: 'More details',
105
    attribute: {
106
      name: '',
107
      scope: ''
108
    },
109
    sortable: false,
110
    textRender: moreDetailsRender
111
  }
112
];
113

114
interface DefaultAttributeRendererProps {
115
  column: ColumnHeader;
116
  tenant: Tenant;
117
}
118

NEW
119
const DefaultAttributeRenderer = (props: DefaultAttributeRendererProps) => {
×
NEW
120
  const { column, tenant } = props;
×
NEW
121
  const text = column.textRender({ tenant, column });
×
NEW
122
  return <AttributeRenderer content={text} textContent={text} />;
×
123
};
124

NEW
125
const TenantListItem = (props: ListItemComponentProps<Tenant>) => {
×
NEW
126
  const { listItem, columnHeaders, onClick } = props;
×
NEW
127
  const handleOnClick = useCallback(() => {
×
NEW
128
    onClick(listItem);
×
129
  }, [listItem.id, onClick]);
130

NEW
131
  return (
×
132
    <div onClick={handleOnClick} className={`deviceListRow deviceListItem clickable`}>
133
      {columnHeaders.map((column: ColumnHeader) => {
NEW
134
        const Component = column.component ? column.component : DefaultAttributeRenderer;
×
NEW
135
        return <Component column={column} tenant={listItem} key={column.title} />;
×
136
      })}
137
    </div>
138
  );
139
};
NEW
140
export const TenantList = ({ customColumnSizes, idAttribute }) => {
×
NEW
141
  const [selectedTenant, setSelectedTenant] = useState<null | Tenant>(null);
×
NEW
142
  const tenantListState = useSelector(getTenantsList);
×
NEW
143
  const { tenants, perPage } = tenantListState;
×
NEW
144
  const dispatch: AppDispatch = useDispatch();
×
NEW
145
  useEffect(() => {
×
NEW
146
    dispatch(getTenants());
×
147
  }, [dispatch]);
NEW
148
  const onExpandClick = (tenant: Tenant) => {
×
NEW
149
    setSelectedTenant(tenant);
×
150
  };
NEW
151
  const onCloseClick = () => {
×
NEW
152
    setSelectedTenant(null);
×
153
  };
NEW
154
  const onChangePagination = (page, currentPerPage = perPage) => dispatch(setTenantsListState({ page, perPage: currentPerPage }));
×
NEW
155
  return (
×
156
    <div>
157
      <CommonList
158
        columnHeaders={columnHeaders}
159
        customColumnSizes={customColumnSizes}
160
        listItems={tenants}
161
        listState={tenantListState}
162
        idAttribute={idAttribute}
NEW
163
        onChangeRowsPerPage={newPerPage => onChangePagination(1, newPerPage)}
×
164
        onExpandClick={onExpandClick}
165
        onPageChange={onChangePagination}
166
        onResizeColumns={false}
167
        onSelect={false}
168
        onSort={() => {}}
169
        pageLoading={false}
170
        PaginationProps={{}}
171
        sortingNotes={{}}
172
        ListItemComponent={TenantListItem}
173
      ></CommonList>
174
      {selectedTenant && <ExpandedTenant onCloseClick={onCloseClick} tenantId={selectedTenant.id}></ExpandedTenant>}
×
175
    </div>
176
  );
177
};
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