@flightctl/ui-components 0.0.5 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/components/Device/DeviceDetails/DeviceDetailsPage.d.ts +3 -1
- package/dist/src/components/Device/DeviceDetails/DeviceDetailsPage.d.ts.map +1 -1
- package/dist/src/components/Device/DeviceDetails/DeviceDetailsPage.js +2 -2
- package/dist/src/components/Device/DeviceDetails/DeviceDetailsPage.js.map +1 -1
- package/dist/src/components/Device/DevicesPage/DeviceTableToolbar.d.ts +2 -2
- package/dist/src/components/Device/DevicesPage/DeviceTableToolbar.d.ts.map +1 -1
- package/dist/src/components/Device/DevicesPage/DeviceTableToolbar.js +8 -8
- package/dist/src/components/Device/DevicesPage/DeviceTableToolbar.js.map +1 -1
- package/dist/src/components/Device/DevicesPage/DeviceToolbarFilters.d.ts +3 -3
- package/dist/src/components/Device/DevicesPage/DeviceToolbarFilters.d.ts.map +1 -1
- package/dist/src/components/Device/DevicesPage/DeviceToolbarFilters.js +23 -3
- package/dist/src/components/Device/DevicesPage/DeviceToolbarFilters.js.map +1 -1
- package/dist/src/components/Device/DevicesPage/DevicesPage.d.ts +7 -1
- package/dist/src/components/Device/DevicesPage/DevicesPage.d.ts.map +1 -1
- package/dist/src/components/Device/DevicesPage/DevicesPage.js +23 -23
- package/dist/src/components/Device/DevicesPage/DevicesPage.js.map +1 -1
- package/dist/src/components/Device/DevicesPage/EnrollmentRequestList.d.ts +0 -7
- package/dist/src/components/Device/DevicesPage/EnrollmentRequestList.d.ts.map +1 -1
- package/dist/src/components/Device/DevicesPage/EnrollmentRequestList.js +42 -51
- package/dist/src/components/Device/DevicesPage/EnrollmentRequestList.js.map +1 -1
- package/dist/src/components/Device/DevicesPage/useDeviceBackendFilters.d.ts +2 -0
- package/dist/src/components/Device/DevicesPage/useDeviceBackendFilters.d.ts.map +1 -1
- package/dist/src/components/Device/DevicesPage/useDeviceBackendFilters.js +10 -3
- package/dist/src/components/Device/DevicesPage/useDeviceBackendFilters.js.map +1 -1
- package/dist/src/components/Device/DevicesPage/useDevices.d.ts +10 -4
- package/dist/src/components/Device/DevicesPage/useDevices.d.ts.map +1 -1
- package/dist/src/components/Device/DevicesPage/useDevices.js +25 -26
- package/dist/src/components/Device/DevicesPage/useDevices.js.map +1 -1
- package/dist/src/components/EnrollmentRequest/PendingEnrollmentRequestsBadge.css +10 -0
- package/dist/src/components/EnrollmentRequest/PendingEnrollmentRequestsBadge.d.ts +5 -0
- package/dist/src/components/EnrollmentRequest/PendingEnrollmentRequestsBadge.d.ts.map +1 -0
- package/dist/src/components/EnrollmentRequest/PendingEnrollmentRequestsBadge.js +22 -0
- package/dist/src/components/EnrollmentRequest/PendingEnrollmentRequestsBadge.js.map +1 -0
- package/dist/src/components/Fleet/FleetDetails/FleetDevices.d.ts.map +1 -1
- package/dist/src/components/Fleet/FleetDetails/FleetDevices.js +7 -7
- package/dist/src/components/Fleet/FleetDetails/FleetDevices.js.map +1 -1
- package/dist/src/components/Fleet/FleetsPage.d.ts.map +1 -1
- package/dist/src/components/Fleet/FleetsPage.js +17 -23
- package/dist/src/components/Fleet/FleetsPage.js.map +1 -1
- package/dist/src/components/Fleet/useFleets.d.ts +18 -0
- package/dist/src/components/Fleet/useFleets.d.ts.map +1 -0
- package/dist/src/components/Fleet/useFleets.js +61 -0
- package/dist/src/components/Fleet/useFleets.js.map +1 -0
- package/dist/src/components/OverviewPage/Cards/Status/StatusCard.d.ts.map +1 -1
- package/dist/src/components/OverviewPage/Cards/Status/StatusCard.js +4 -3
- package/dist/src/components/OverviewPage/Cards/Status/StatusCard.js.map +1 -1
- package/dist/src/components/OverviewPage/Cards/ToDo/ToDoCard.d.ts.map +1 -1
- package/dist/src/components/OverviewPage/Cards/ToDo/ToDoCard.js +5 -9
- package/dist/src/components/OverviewPage/Cards/ToDo/ToDoCard.js.map +1 -1
- package/dist/src/components/OverviewPage/Overview.js +1 -1
- package/dist/src/components/OverviewPage/Overview.js.map +1 -1
- package/dist/src/components/Repository/CreateRepository/CreateRepository.js +2 -2
- package/dist/src/components/Repository/CreateRepository/CreateRepository.js.map +1 -1
- package/dist/src/components/Repository/RepositoryDetails/DeleteRepositoryModal.js +1 -1
- package/dist/src/components/Repository/RepositoryDetails/DeleteRepositoryModal.js.map +1 -1
- package/dist/src/components/Repository/RepositoryList.d.ts.map +1 -1
- package/dist/src/components/Repository/RepositoryList.js +1 -1
- package/dist/src/components/Repository/RepositoryList.js.map +1 -1
- package/dist/src/components/ResourceSync/RepositoryResourceSyncList.d.ts.map +1 -1
- package/dist/src/components/ResourceSync/RepositoryResourceSyncList.js +2 -2
- package/dist/src/components/ResourceSync/RepositoryResourceSyncList.js.map +1 -1
- package/dist/src/components/Table/Table.d.ts +12 -2
- package/dist/src/components/Table/Table.d.ts.map +1 -1
- package/dist/src/components/Table/Table.js +3 -3
- package/dist/src/components/Table/Table.js.map +1 -1
- package/dist/src/components/charts/DonutChart.css +5 -0
- package/dist/src/components/charts/DonutChart.js +1 -1
- package/dist/src/components/charts/DonutChart.js.map +1 -1
- package/dist/src/components/common/LeaveFormConfirmation.js +1 -1
- package/dist/src/components/common/LeaveFormConfirmation.js.map +1 -1
- package/dist/src/components/modals/massModals/MassDeleteRepositoryModal/MassDeleteRepositoryModal.d.ts.map +1 -1
- package/dist/src/components/modals/massModals/MassDeleteRepositoryModal/MassDeleteRepositoryModal.js +2 -2
- package/dist/src/components/modals/massModals/MassDeleteRepositoryModal/MassDeleteRepositoryModal.js.map +1 -1
- package/dist/src/hooks/useApiTableSort.d.ts +8 -0
- package/dist/src/hooks/useApiTableSort.d.ts.map +1 -0
- package/dist/src/hooks/useApiTableSort.js +44 -0
- package/dist/src/hooks/useApiTableSort.js.map +1 -0
- package/dist/src/hooks/useAppContext.d.ts +4 -4
- package/dist/src/hooks/useAppContext.d.ts.map +1 -1
- package/dist/src/hooks/useNavigate.d.ts +3 -3
- package/dist/src/hooks/useNavigate.d.ts.map +1 -1
- package/dist/src/hooks/useNavigate.js +3 -3
- package/dist/src/hooks/useNavigate.js.map +1 -1
- package/dist/src/hooks/usePendingEnrollmentRequestsCount.d.ts +2 -0
- package/dist/src/hooks/usePendingEnrollmentRequestsCount.d.ts.map +1 -0
- package/dist/src/hooks/usePendingEnrollmentRequestsCount.js +13 -0
- package/dist/src/hooks/usePendingEnrollmentRequestsCount.js.map +1 -0
- package/dist/src/utils/query.d.ts +6 -0
- package/dist/src/utils/query.d.ts.map +1 -0
- package/dist/src/utils/query.js +32 -0
- package/dist/src/utils/query.js.map +1 -0
- package/dist/src/utils/sort/generic.d.ts +1 -4
- package/dist/src/utils/sort/generic.d.ts.map +1 -1
- package/dist/src/utils/sort/generic.js +1 -28
- package/dist/src/utils/sort/generic.js.map +1 -1
- package/dist/src/utils/status/devices.d.ts +2 -1
- package/dist/src/utils/status/devices.d.ts.map +1 -1
- package/dist/src/utils/status/devices.js +1 -0
- package/dist/src/utils/status/devices.js.map +1 -1
- package/package.json +6 -6
- package/src/components/Device/DeviceDetails/DeviceDetailsPage.tsx +2 -2
- package/src/components/Device/DevicesPage/DeviceTableToolbar.tsx +13 -13
- package/src/components/Device/DevicesPage/DeviceToolbarFilters.tsx +35 -8
- package/src/components/Device/DevicesPage/DevicesPage.tsx +41 -27
- package/src/components/Device/DevicesPage/EnrollmentRequestList.tsx +91 -116
- package/src/components/Device/DevicesPage/useDeviceBackendFilters.ts +14 -3
- package/src/components/Device/DevicesPage/useDevices.ts +43 -32
- package/src/components/EnrollmentRequest/PendingEnrollmentRequestsBadge.css +10 -0
- package/src/components/EnrollmentRequest/PendingEnrollmentRequestsBadge.tsx +27 -0
- package/src/components/Fleet/FleetDetails/FleetDevices.tsx +12 -18
- package/src/components/Fleet/FleetsPage.tsx +39 -28
- package/src/components/Fleet/useFleets.ts +86 -0
- package/src/components/OverviewPage/Cards/Status/StatusCard.tsx +4 -3
- package/src/components/OverviewPage/Cards/ToDo/ToDoCard.tsx +6 -10
- package/src/components/OverviewPage/Overview.tsx +1 -1
- package/src/components/Repository/CreateRepository/CreateRepository.tsx +2 -2
- package/src/components/Repository/RepositoryDetails/DeleteRepositoryModal.tsx +1 -1
- package/src/components/Repository/RepositoryList.tsx +1 -0
- package/src/components/ResourceSync/RepositoryResourceSyncList.tsx +2 -1
- package/src/components/Table/Table.tsx +19 -5
- package/src/components/charts/DonutChart.css +5 -0
- package/src/components/charts/DonutChart.tsx +1 -1
- package/src/components/modals/massModals/MassDeleteRepositoryModal/MassDeleteRepositoryModal.tsx +4 -2
- package/src/hooks/useApiTableSort.ts +49 -0
- package/src/hooks/useNavigate.tsx +3 -3
- package/src/hooks/usePendingEnrollmentRequestsCount.ts +12 -0
- package/src/utils/query.ts +29 -0
- package/src/utils/sort/generic.ts +1 -30
- package/src/utils/status/devices.ts +1 -0
- package/dist/src/components/Device/DevicesPage/useDeviceFilters.d.ts +0 -8
- package/dist/src/components/Device/DevicesPage/useDeviceFilters.d.ts.map +0 -1
- package/dist/src/components/Device/DevicesPage/useDeviceFilters.js +0 -16
- package/dist/src/components/Device/DevicesPage/useDeviceFilters.js.map +0 -1
- package/dist/src/utils/sort/device.d.ts +0 -4
- package/dist/src/utils/sort/device.d.ts.map +0 -1
- package/dist/src/utils/sort/device.js +0 -49
- package/dist/src/utils/sort/device.js.map +0 -1
- package/dist/src/utils/sort/fleet.d.ts +0 -4
- package/dist/src/utils/sort/fleet.d.ts.map +0 -1
- package/dist/src/utils/sort/fleet.js +0 -18
- package/dist/src/utils/sort/fleet.js.map +0 -1
- package/src/components/Device/DevicesPage/useDeviceFilters.ts +0 -15
- package/src/utils/sort/device.ts +0 -60
- package/src/utils/sort/fleet.ts +0 -16
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Badge } from '@patternfly/react-core';
|
|
3
|
+
|
|
4
|
+
import { useTranslation } from '../../hooks/useTranslation';
|
|
5
|
+
import { usePendingEnrollmentRequestsCount } from '../../hooks/usePendingEnrollmentRequestsCount';
|
|
6
|
+
import WithTooltip from '../common/WithTooltip';
|
|
7
|
+
|
|
8
|
+
import './PendingEnrollmentRequestsBadge.css';
|
|
9
|
+
|
|
10
|
+
const PendingEnrollmentRequestsBadge = () => {
|
|
11
|
+
const { t } = useTranslation();
|
|
12
|
+
const [count] = usePendingEnrollmentRequestsCount();
|
|
13
|
+
if (count === 0) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const text = t('{{ count }} devices pending approval', { count });
|
|
18
|
+
return (
|
|
19
|
+
<Badge className="fctl-pending-ers-badge pf-v5-u-ml-lg" screenReaderText={text}>
|
|
20
|
+
<WithTooltip showTooltip content={text}>
|
|
21
|
+
<span>{count}</span>
|
|
22
|
+
</WithTooltip>
|
|
23
|
+
</Badge>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default PendingEnrollmentRequestsBadge;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { Flex, FlexItem } from '@patternfly/react-core';
|
|
3
3
|
|
|
4
4
|
import { DevicesSummary } from '@flightctl/types';
|
|
5
5
|
import { useTranslation } from '../../../hooks/useTranslation';
|
|
@@ -92,23 +92,17 @@ const DevicesByDeviceStatusChart = ({
|
|
|
92
92
|
|
|
93
93
|
const FleetDevices = ({ devicesSummary, fleetId }: FleetDevicesProps) => {
|
|
94
94
|
return (
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
{devicesSummary.updateStatus && (
|
|
107
|
-
<GridItem md={6}>
|
|
108
|
-
<DevicesByUpdateStatusChart fleetId={fleetId} updateStatus={devicesSummary.updateStatus} />
|
|
109
|
-
</GridItem>
|
|
110
|
-
)}
|
|
111
|
-
</Grid>
|
|
95
|
+
<Flex justifyContent={{ default: 'justifyContentSpaceAround' }}>
|
|
96
|
+
<FlexItem>
|
|
97
|
+
<DevicesByAppStatusChart fleetId={fleetId} applicationStatus={devicesSummary.applicationStatus} />
|
|
98
|
+
</FlexItem>
|
|
99
|
+
<FlexItem>
|
|
100
|
+
<DevicesByDeviceStatusChart fleetId={fleetId} deviceStatus={devicesSummary.summaryStatus} />
|
|
101
|
+
</FlexItem>
|
|
102
|
+
<FlexItem>
|
|
103
|
+
<DevicesByUpdateStatusChart fleetId={fleetId} updateStatus={devicesSummary.updateStatus} />
|
|
104
|
+
</FlexItem>
|
|
105
|
+
</Flex>
|
|
112
106
|
);
|
|
113
107
|
};
|
|
114
108
|
|
|
@@ -13,21 +13,16 @@ import {
|
|
|
13
13
|
ToolbarGroup,
|
|
14
14
|
ToolbarItem,
|
|
15
15
|
} from '@patternfly/react-core';
|
|
16
|
-
import { Tbody } from '@patternfly/react-table';
|
|
16
|
+
import { Tbody, ThProps } from '@patternfly/react-table';
|
|
17
17
|
import { TopologyIcon } from '@patternfly/react-icons/dist/js/icons/topology-icon';
|
|
18
18
|
import { Trans } from 'react-i18next';
|
|
19
19
|
import { TFunction } from 'i18next';
|
|
20
20
|
|
|
21
|
-
import { Fleet
|
|
22
|
-
import { useFetchPeriodically } from '../../hooks/useFetchPeriodically';
|
|
21
|
+
import { Fleet } from '@flightctl/types';
|
|
23
22
|
import ListPage from '../ListPage/ListPage';
|
|
24
23
|
import ListPageBody from '../ListPage/ListPageBody';
|
|
25
|
-
import { sortByName } from '../../utils/sort/generic';
|
|
26
|
-
import { sortByStatus, sortFleetsByOSImg } from '../../utils/sort/fleet';
|
|
27
24
|
import TableTextSearch from '../Table/TableTextSearch';
|
|
28
|
-
import Table, {
|
|
29
|
-
import { useTableTextSearch } from '../../hooks/useTableTextSearch';
|
|
30
|
-
import { useTableSort } from '../../hooks/useTableSort';
|
|
25
|
+
import Table, { ApiSortTableColumn } from '../Table/Table';
|
|
31
26
|
import { useTableSelect } from '../../hooks/useTableSelect';
|
|
32
27
|
import TableActions from '../Table/TableActions';
|
|
33
28
|
import { getResourceId } from '../../utils/resource';
|
|
@@ -38,6 +33,8 @@ import { useTranslation } from '../../hooks/useTranslation';
|
|
|
38
33
|
import { ROUTE, useNavigate } from '../../hooks/useNavigate';
|
|
39
34
|
import DeleteFleetModal from './DeleteFleetModal/DeleteFleetModal';
|
|
40
35
|
import FleetResourceSyncs from './FleetResourceSyncs';
|
|
36
|
+
import { useApiTableSort } from '../../hooks/useApiTableSort';
|
|
37
|
+
import { useFleetBackendFilters, useFleets } from './useFleets';
|
|
41
38
|
|
|
42
39
|
const FleetPageActions = ({ createText }: { createText?: string }) => {
|
|
43
40
|
const { t } = useTranslation();
|
|
@@ -78,37 +75,40 @@ const FleetEmptyState = () => {
|
|
|
78
75
|
);
|
|
79
76
|
};
|
|
80
77
|
|
|
81
|
-
const getColumns = (t: TFunction):
|
|
78
|
+
const getColumns = (t: TFunction): ApiSortTableColumn[] => [
|
|
82
79
|
{
|
|
83
80
|
name: t('Name'),
|
|
84
|
-
|
|
81
|
+
sortableField: 'metadata.name',
|
|
82
|
+
defaultSort: true,
|
|
85
83
|
},
|
|
86
84
|
{
|
|
87
85
|
name: t('System image'),
|
|
88
|
-
|
|
86
|
+
sortableField: 'spec.template.spec.os.image',
|
|
89
87
|
},
|
|
90
88
|
{
|
|
91
89
|
name: t('Devices'),
|
|
92
90
|
},
|
|
93
91
|
{
|
|
94
92
|
name: t('Status'),
|
|
95
|
-
onSort: sortByStatus,
|
|
96
93
|
},
|
|
97
94
|
];
|
|
98
95
|
|
|
99
|
-
|
|
96
|
+
type FleetTableProps = {
|
|
97
|
+
fleetColumns: ApiSortTableColumn[];
|
|
98
|
+
fleetLoad: FleetLoad;
|
|
99
|
+
getSortParams: (columnIndex: number) => ThProps['sort'];
|
|
100
|
+
hasFiltersEnabled: boolean;
|
|
101
|
+
name: string | undefined;
|
|
102
|
+
setName: (name: string) => void;
|
|
103
|
+
};
|
|
100
104
|
|
|
101
|
-
const FleetTable = ({
|
|
105
|
+
const FleetTable = ({ name, setName, hasFiltersEnabled, getSortParams, fleetColumns, fleetLoad }: FleetTableProps) => {
|
|
102
106
|
const { t } = useTranslation();
|
|
103
107
|
|
|
104
108
|
const [isMassDeleteModalOpen, setIsMassDeleteModalOpen] = React.useState(false);
|
|
105
109
|
const [fleetToDeleteId, setFleetToDeleteId] = React.useState<string>();
|
|
110
|
+
const [fleets, loading, error, isFilterUpdating, refetch] = fleetLoad;
|
|
106
111
|
|
|
107
|
-
const [fleetList, loading, error, refetch] = fleetLoad;
|
|
108
|
-
const columns = React.useMemo(() => getColumns(t), [t]);
|
|
109
|
-
const fleets = fleetList?.items || [];
|
|
110
|
-
const { search, setSearch, filteredData } = useTableTextSearch(fleets, getSearchText);
|
|
111
|
-
const { getSortParams, sortedData: sortedFleets } = useTableSort(filteredData, columns);
|
|
112
112
|
const { onRowSelect, isAllSelected, hasSelectedRows, isRowSelected, setAllSelected } = useTableSelect();
|
|
113
113
|
|
|
114
114
|
return (
|
|
@@ -117,7 +117,7 @@ const FleetTable = ({ fleetLoad }: { fleetLoad: FleetLoad }) => {
|
|
|
117
117
|
<ToolbarContent>
|
|
118
118
|
<ToolbarGroup>
|
|
119
119
|
<ToolbarItem variant="search-filter">
|
|
120
|
-
<TableTextSearch value={
|
|
120
|
+
<TableTextSearch value={name} setValue={setName} placeholder={t('Search by name')} />
|
|
121
121
|
</ToolbarItem>
|
|
122
122
|
</ToolbarGroup>
|
|
123
123
|
<ToolbarItem>
|
|
@@ -134,15 +134,16 @@ const FleetTable = ({ fleetLoad }: { fleetLoad: FleetLoad }) => {
|
|
|
134
134
|
</Toolbar>
|
|
135
135
|
<Table
|
|
136
136
|
aria-label={t('Fleets table')}
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
loading={isFilterUpdating}
|
|
138
|
+
columns={fleetColumns}
|
|
139
|
+
emptyFilters={!hasFiltersEnabled}
|
|
139
140
|
emptyData={fleets.length === 0}
|
|
140
141
|
getSortParams={getSortParams}
|
|
141
142
|
isAllSelected={isAllSelected}
|
|
142
143
|
onSelectAll={setAllSelected}
|
|
143
144
|
>
|
|
144
145
|
<Tbody>
|
|
145
|
-
{
|
|
146
|
+
{fleets.map((fleet, rowIndex) => (
|
|
146
147
|
<FleetRow
|
|
147
148
|
key={getResourceId(fleet)}
|
|
148
149
|
fleet={fleet}
|
|
@@ -171,7 +172,7 @@ const FleetTable = ({ fleetLoad }: { fleetLoad: FleetLoad }) => {
|
|
|
171
172
|
{isMassDeleteModalOpen && (
|
|
172
173
|
<MassDeleteFleetModal
|
|
173
174
|
onClose={() => setIsMassDeleteModalOpen(false)}
|
|
174
|
-
fleets={
|
|
175
|
+
fleets={fleets.filter(isRowSelected)}
|
|
175
176
|
onDeleteSuccess={() => {
|
|
176
177
|
setIsMassDeleteModalOpen(false);
|
|
177
178
|
refetch();
|
|
@@ -182,20 +183,30 @@ const FleetTable = ({ fleetLoad }: { fleetLoad: FleetLoad }) => {
|
|
|
182
183
|
);
|
|
183
184
|
};
|
|
184
185
|
|
|
185
|
-
type FleetLoad = [
|
|
186
|
+
type FleetLoad = [Fleet[], boolean, unknown, boolean, VoidFunction];
|
|
186
187
|
|
|
187
188
|
const FleetsPage = () => {
|
|
188
189
|
const { t } = useTranslation();
|
|
189
190
|
|
|
190
191
|
// TODO move the fetch down to FleetTable when the API includes the filter for pending / errored resource syncs
|
|
191
|
-
const
|
|
192
|
+
const columns = React.useMemo(() => getColumns(t), [t]);
|
|
193
|
+
const { name, setName, hasFiltersEnabled } = useFleetBackendFilters();
|
|
194
|
+
const { getSortParams, sortField, direction } = useApiTableSort(columns);
|
|
195
|
+
const fleetLoad = useFleets({ name, addDevicesCount: true, sortField, direction });
|
|
192
196
|
|
|
193
197
|
return (
|
|
194
198
|
<>
|
|
195
|
-
<FleetResourceSyncs fleets={fleetLoad[0]
|
|
199
|
+
<FleetResourceSyncs fleets={fleetLoad[0] || []} />
|
|
196
200
|
|
|
197
201
|
<ListPage title={t('Fleets')}>
|
|
198
|
-
<FleetTable
|
|
202
|
+
<FleetTable
|
|
203
|
+
name={name}
|
|
204
|
+
setName={setName}
|
|
205
|
+
hasFiltersEnabled={hasFiltersEnabled}
|
|
206
|
+
getSortParams={getSortParams}
|
|
207
|
+
fleetLoad={fleetLoad}
|
|
208
|
+
fleetColumns={columns}
|
|
209
|
+
/>
|
|
199
210
|
</ListPage>
|
|
200
211
|
</>
|
|
201
212
|
);
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { useAppContext } from '../../hooks/useAppContext';
|
|
4
|
+
import { useFetchPeriodically } from '../../hooks/useFetchPeriodically';
|
|
5
|
+
import { Fleet, FleetList, SortOrder } from '@flightctl/types';
|
|
6
|
+
import { useDebounce } from 'use-debounce';
|
|
7
|
+
|
|
8
|
+
export enum FleetSearchParams {
|
|
9
|
+
Name = 'name',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type FleetsEndpointArgs = {
|
|
13
|
+
name?: string;
|
|
14
|
+
addDevicesCount?: boolean;
|
|
15
|
+
sortField?: string;
|
|
16
|
+
direction?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const useFleetBackendFilters = () => {
|
|
20
|
+
const {
|
|
21
|
+
router: { useSearchParams },
|
|
22
|
+
} = useAppContext();
|
|
23
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
24
|
+
const paramsRef = React.useRef(searchParams);
|
|
25
|
+
const name = searchParams.get(FleetSearchParams.Name) || undefined;
|
|
26
|
+
|
|
27
|
+
const setName = React.useCallback(
|
|
28
|
+
(nameVal: string) => {
|
|
29
|
+
const newParams = new URLSearchParams({
|
|
30
|
+
[FleetSearchParams.Name]: nameVal,
|
|
31
|
+
});
|
|
32
|
+
paramsRef.current = newParams;
|
|
33
|
+
setSearchParams(newParams);
|
|
34
|
+
},
|
|
35
|
+
[setSearchParams],
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const hasFiltersEnabled = !!name;
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
name,
|
|
42
|
+
setName,
|
|
43
|
+
hasFiltersEnabled,
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const getFleetsEndpoint = ({
|
|
48
|
+
addDevicesCount,
|
|
49
|
+
name,
|
|
50
|
+
sortField,
|
|
51
|
+
direction,
|
|
52
|
+
}: {
|
|
53
|
+
addDevicesCount?: boolean;
|
|
54
|
+
name?: string;
|
|
55
|
+
sortField?: string;
|
|
56
|
+
direction?: string;
|
|
57
|
+
}) => {
|
|
58
|
+
const params = new URLSearchParams();
|
|
59
|
+
if (name) {
|
|
60
|
+
params.set('fieldSelector', `metadata.name contains ${name}`);
|
|
61
|
+
}
|
|
62
|
+
if (addDevicesCount) {
|
|
63
|
+
params.set('addDevicesCount', 'true');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (sortField) {
|
|
67
|
+
params.set('sortBy', sortField);
|
|
68
|
+
params.set('sortOrder', direction || SortOrder.ASC);
|
|
69
|
+
}
|
|
70
|
+
return params.size ? `fleets?${params.toString()}` : 'fleets';
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const useFleetsEndpoint = (args: FleetsEndpointArgs): [string, boolean] => {
|
|
74
|
+
const endpoint = getFleetsEndpoint(args);
|
|
75
|
+
const [fleetsEndpointDebounced] = useDebounce(endpoint, 1000);
|
|
76
|
+
return [fleetsEndpointDebounced, endpoint !== fleetsEndpointDebounced];
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const useFleets = (args: FleetsEndpointArgs): [Fleet[], boolean, unknown, boolean, VoidFunction] => {
|
|
80
|
+
const [fleetsEndpoint, fleetsDebouncing] = useFleetsEndpoint(args);
|
|
81
|
+
const [fleetsList, fleetsLoading, fleetsError, fleetsRefetch, updating] = useFetchPeriodically<FleetList>({
|
|
82
|
+
endpoint: fleetsEndpoint,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return [fleetsList?.items || [], fleetsLoading, fleetsError, updating || fleetsDebouncing, fleetsRefetch];
|
|
86
|
+
};
|
|
@@ -35,12 +35,13 @@ const StatusCard = () => {
|
|
|
35
35
|
labels,
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
// TODO
|
|
39
|
-
const [
|
|
38
|
+
// TODO https://issues.redhat.com/browse/EDM-684 Use the new API endpoint to retrieve device labels
|
|
39
|
+
const [, /* devices */ loading, error, , , allLabels] = useDevices({
|
|
40
40
|
ownerFleets: fleets,
|
|
41
41
|
labels,
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
+
// TODO https://issues.redhat.com/browse/EDM-683 Use the new API endpoint to retrieve fleet names
|
|
44
45
|
const [fleetsList, flLoading, flError] = useFetchPeriodically<FleetList>({
|
|
45
46
|
endpoint: 'fleets',
|
|
46
47
|
});
|
|
@@ -59,7 +60,7 @@ const StatusCard = () => {
|
|
|
59
60
|
<Stack>
|
|
60
61
|
<StackItem>
|
|
61
62
|
<TextContent>
|
|
62
|
-
<Text component={TextVariants.small}>{t('{{count}} Devices', { count:
|
|
63
|
+
<Text component={TextVariants.small}>{t('{{count}} Devices', { count: devicesSummary?.total || 0 })}</Text>
|
|
63
64
|
</TextContent>
|
|
64
65
|
</StackItem>
|
|
65
66
|
<StackItem>
|
|
@@ -13,18 +13,15 @@ import {
|
|
|
13
13
|
TextContent,
|
|
14
14
|
TextVariants,
|
|
15
15
|
} from '@patternfly/react-core';
|
|
16
|
-
import { EnrollmentRequestList } from '@flightctl/types';
|
|
17
16
|
import { useTranslation } from '../../../../hooks/useTranslation';
|
|
18
|
-
import {
|
|
19
|
-
import { EnrollmentRequestStatus, getApprovalStatus } from '../../../../utils/status/enrollmentRequest';
|
|
17
|
+
import { usePendingEnrollmentRequestsCount } from '../../../../hooks/usePendingEnrollmentRequestsCount';
|
|
20
18
|
import { Link, ROUTE } from '../../../../hooks/useNavigate';
|
|
21
19
|
import ErrorAlert from '../../../ErrorAlert/ErrorAlert';
|
|
22
20
|
|
|
23
21
|
const ToDoCard = () => {
|
|
24
22
|
const { t } = useTranslation();
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
});
|
|
23
|
+
|
|
24
|
+
const [pendingErCount, loading, error] = usePendingEnrollmentRequestsCount();
|
|
28
25
|
|
|
29
26
|
let content: React.ReactNode;
|
|
30
27
|
if (loading) {
|
|
@@ -36,15 +33,14 @@ const ToDoCard = () => {
|
|
|
36
33
|
} else if (error) {
|
|
37
34
|
content = <ErrorAlert error={error} />;
|
|
38
35
|
} else {
|
|
39
|
-
|
|
40
|
-
if (pendingErs?.length) {
|
|
36
|
+
if (pendingErCount) {
|
|
41
37
|
content = (
|
|
42
38
|
<List>
|
|
43
39
|
<ListItem>
|
|
44
40
|
<Split hasGutter>
|
|
45
|
-
<SplitItem isFilled>{t('{{ count }} devices pending approval', { count:
|
|
41
|
+
<SplitItem isFilled>{t('{{ count }} devices pending approval', { count: pendingErCount })}</SplitItem>
|
|
46
42
|
<SplitItem>
|
|
47
|
-
<Link to={ROUTE.DEVICES}>{t('Review pending devices', { count:
|
|
43
|
+
<Link to={ROUTE.DEVICES}>{t('Review pending devices', { count: pendingErCount })}</Link>
|
|
48
44
|
</SplitItem>
|
|
49
45
|
</Split>
|
|
50
46
|
</ListItem>
|
|
@@ -44,7 +44,7 @@ const CreateRepository = () => {
|
|
|
44
44
|
try {
|
|
45
45
|
const results = await Promise.allSettled([
|
|
46
46
|
get<Repository>(`repositories/${repositoryId}`),
|
|
47
|
-
get<ResourceSyncList>(`resourcesyncs?repository=${repositoryId}`),
|
|
47
|
+
get<ResourceSyncList>(`resourcesyncs?fieldSelector=spec.repository=${repositoryId}`),
|
|
48
48
|
]);
|
|
49
49
|
|
|
50
50
|
if (isPromiseFulfilled(results[0])) {
|
|
@@ -70,7 +70,7 @@ const CreateRepository = () => {
|
|
|
70
70
|
const reload = async () => {
|
|
71
71
|
try {
|
|
72
72
|
setIsLoading(true);
|
|
73
|
-
const rsList = await get<ResourceSyncList>(`resourcesyncs?
|
|
73
|
+
const rsList = await get<ResourceSyncList>(`resourcesyncs?fieldSelector=spec.repository${repositoryId}`);
|
|
74
74
|
setResourceSyncs(rsList.items);
|
|
75
75
|
setRsError(undefined);
|
|
76
76
|
} catch (e) {
|
|
@@ -46,7 +46,7 @@ const DeleteRepositoryModal = ({ repositoryId, onClose, onDeleteSuccess }: Delet
|
|
|
46
46
|
|
|
47
47
|
const loadRS = React.useCallback(async () => {
|
|
48
48
|
try {
|
|
49
|
-
const resourceSyncs = await get<ResourceSyncList>(`resourcesyncs?repository=${repositoryId}`);
|
|
49
|
+
const resourceSyncs = await get<ResourceSyncList>(`resourcesyncs?fieldSelector=spec.repository=${repositoryId}`);
|
|
50
50
|
setResourceSyncIds(resourceSyncs.items.map((rs) => rs.metadata.name || ''));
|
|
51
51
|
setRsError(undefined);
|
|
52
52
|
} catch (e) {
|
|
@@ -136,6 +136,7 @@ const RepositoryTable = () => {
|
|
|
136
136
|
</Toolbar>
|
|
137
137
|
<Table
|
|
138
138
|
aria-label={t('Repositories table')}
|
|
139
|
+
loading={loading}
|
|
139
140
|
emptyFilters={filteredData.length === 0}
|
|
140
141
|
emptyData={(repositoryList?.items.length || 0) === 0}
|
|
141
142
|
isAllSelected={isAllSelected}
|
|
@@ -177,7 +177,7 @@ const CreateResourceSyncModal = ({
|
|
|
177
177
|
|
|
178
178
|
const RepositoryResourceSyncList = ({ repositoryId }: { repositoryId: string }) => {
|
|
179
179
|
const [rsList, isLoading, error, refetch] = useFetchPeriodically<ResourceSyncList>({
|
|
180
|
-
endpoint: `resourcesyncs?repository=${repositoryId}`,
|
|
180
|
+
endpoint: `resourcesyncs?fieldSelector=spec.repository=${repositoryId}`,
|
|
181
181
|
});
|
|
182
182
|
|
|
183
183
|
const resourceSyncs = rsList?.items || [];
|
|
@@ -249,6 +249,7 @@ const RepositoryResourceSyncList = ({ repositoryId }: { repositoryId: string })
|
|
|
249
249
|
)}
|
|
250
250
|
<Table
|
|
251
251
|
aria-label={t('Resource syncs table')}
|
|
252
|
+
loading={isLoading}
|
|
252
253
|
isAllSelected={isAllSelected}
|
|
253
254
|
onSelectAll={setAllSelected}
|
|
254
255
|
columns={columns}
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { Bullseye, PageSection } from '@patternfly/react-core';
|
|
2
|
+
import { Bullseye, PageSection, Spinner } from '@patternfly/react-core';
|
|
3
3
|
import { Table as PFTable, Td, Th, ThProps, Thead, Tr } from '@patternfly/react-table';
|
|
4
4
|
import { useTranslation } from '../../hooks/useTranslation';
|
|
5
5
|
import WithHelperText from '../common/WithHelperText';
|
|
6
6
|
|
|
7
|
+
export type ApiSortTableColumn = {
|
|
8
|
+
name: string;
|
|
9
|
+
sortableField?: string;
|
|
10
|
+
defaultSort?: boolean;
|
|
11
|
+
helperText?: string;
|
|
12
|
+
thProps?: Omit<ThProps, 'sort'> & {
|
|
13
|
+
ref?: React.Ref<HTMLTableCellElement> | undefined;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
|
|
7
17
|
export type TableColumn<D> = {
|
|
8
18
|
name: string;
|
|
9
19
|
onSort?: (data: D[]) => D[];
|
|
@@ -17,8 +27,9 @@ export type TableColumn<D> = {
|
|
|
17
27
|
type TableProps<D> = {
|
|
18
28
|
columns: TableColumn<D>[];
|
|
19
29
|
children: React.ReactNode;
|
|
20
|
-
|
|
21
|
-
|
|
30
|
+
loading: boolean;
|
|
31
|
+
emptyFilters?: boolean;
|
|
32
|
+
emptyData?: boolean;
|
|
22
33
|
'aria-label': string;
|
|
23
34
|
getSortParams: (columnIndex: number) => ThProps['sort'];
|
|
24
35
|
onSelectAll?: (isSelected: boolean) => void;
|
|
@@ -30,6 +41,7 @@ type TableFC = <D>(props: TableProps<D>) => JSX.Element;
|
|
|
30
41
|
const Table: TableFC = ({
|
|
31
42
|
columns,
|
|
32
43
|
children,
|
|
44
|
+
loading,
|
|
33
45
|
emptyFilters,
|
|
34
46
|
emptyData,
|
|
35
47
|
getSortParams,
|
|
@@ -38,8 +50,10 @@ const Table: TableFC = ({
|
|
|
38
50
|
...rest
|
|
39
51
|
}) => {
|
|
40
52
|
const { t } = useTranslation();
|
|
41
|
-
if (
|
|
42
|
-
return (
|
|
53
|
+
if (emptyData && !emptyFilters) {
|
|
54
|
+
return loading ? (
|
|
55
|
+
<Spinner size="md" />
|
|
56
|
+
) : (
|
|
43
57
|
<PageSection variant="light">
|
|
44
58
|
<Bullseye>{t('No resources are matching the current filters.')}</Bullseye>
|
|
45
59
|
</PageSection>
|
|
@@ -75,7 +75,7 @@ const DonutChart = ({ data, title, helperText }: { data: Data[]; title: string;
|
|
|
75
75
|
>
|
|
76
76
|
<FlexItem className="fctl-charts__donut">
|
|
77
77
|
<div style={{ height: '230px', width: '230px' }}>
|
|
78
|
-
<ChartContainer>
|
|
78
|
+
<ChartContainer className="fctl-charts__donut-container">
|
|
79
79
|
<foreignObject x="0" y="0" width="230px" height="230px">
|
|
80
80
|
<Flex
|
|
81
81
|
alignItems={{ default: 'alignItemsCenter' }}
|
package/src/components/modals/massModals/MassDeleteRepositoryModal/MassDeleteRepositoryModal.tsx
CHANGED
|
@@ -37,7 +37,9 @@ const MassDeleteRepositoryModal: React.FC<MassDeleteRepositoryModalProps> = ({
|
|
|
37
37
|
const rsCount = {};
|
|
38
38
|
const promises = repositories.map(async (r) => {
|
|
39
39
|
const repositoryId = r.metadata.name || '';
|
|
40
|
-
const resourceSyncs = await get<ResourceSyncList>(
|
|
40
|
+
const resourceSyncs = await get<ResourceSyncList>(
|
|
41
|
+
`resourcesyncs?fieldSelector=spec.repository=${repositoryId}&limit=1`,
|
|
42
|
+
);
|
|
41
43
|
rsCount[repositoryId] = getApiListCount(resourceSyncs);
|
|
42
44
|
});
|
|
43
45
|
await Promise.allSettled(promises);
|
|
@@ -51,7 +53,7 @@ const MassDeleteRepositoryModal: React.FC<MassDeleteRepositoryModalProps> = ({
|
|
|
51
53
|
setProgress(0);
|
|
52
54
|
const promises = repositories.map(async (r) => {
|
|
53
55
|
const repositoryId = r.metadata.name || '';
|
|
54
|
-
const resourceSyncs = await get<ResourceSyncList>(`resourcesyncs?repository=${repositoryId}`);
|
|
56
|
+
const resourceSyncs = await get<ResourceSyncList>(`resourcesyncs?fieldSelector=spec.repository=${repositoryId}`);
|
|
55
57
|
const rsyncPromises = resourceSyncs.items.map((rsync) => remove(`resourcesyncs/${rsync.metadata.name}`));
|
|
56
58
|
const rsyncResults = await Promise.allSettled(rsyncPromises);
|
|
57
59
|
const rejectedResults = rsyncResults.filter(isPromiseRejected);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ThProps } from '@patternfly/react-table';
|
|
3
|
+
|
|
4
|
+
import { ApiSortTableColumn } from '../components/Table/Table';
|
|
5
|
+
import { SortOrder } from '@flightctl/types';
|
|
6
|
+
|
|
7
|
+
const getDefaultSortField = (columns: ApiSortTableColumn[]) => {
|
|
8
|
+
let defaultSortCol = columns.find((c) => c.defaultSort);
|
|
9
|
+
if (!defaultSortCol) {
|
|
10
|
+
defaultSortCol = columns.find((c) => !!c.sortableField);
|
|
11
|
+
}
|
|
12
|
+
return defaultSortCol?.sortableField || '';
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const useApiTableSort = (columns: ApiSortTableColumn[]) => {
|
|
16
|
+
const [activeSortField, setActiveSortField] = React.useState<string>(getDefaultSortField(columns));
|
|
17
|
+
const [activeSortDirection, setActiveSortDirection] = React.useState<SortOrder>(SortOrder.ASC);
|
|
18
|
+
|
|
19
|
+
const getSortParams = React.useCallback(
|
|
20
|
+
(columnIndex: number): ThProps['sort'] => {
|
|
21
|
+
const columnData = columns[columnIndex];
|
|
22
|
+
if (!columnData.sortableField) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const activeColumnIndex = columns.findIndex((column) => column.sortableField === activeSortField);
|
|
27
|
+
return {
|
|
28
|
+
sortBy: {
|
|
29
|
+
index: activeColumnIndex === -1 ? 0 : activeColumnIndex,
|
|
30
|
+
direction: activeSortDirection === SortOrder.ASC ? 'asc' : 'desc',
|
|
31
|
+
defaultDirection: 'asc',
|
|
32
|
+
},
|
|
33
|
+
onSort: (_, index, direction) => {
|
|
34
|
+
const column = columns[index];
|
|
35
|
+
setActiveSortField(column.sortableField || '');
|
|
36
|
+
setActiveSortDirection(direction === 'asc' ? SortOrder.ASC : SortOrder.DESC);
|
|
37
|
+
},
|
|
38
|
+
columnIndex,
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
[columns, activeSortField, activeSortDirection],
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
getSortParams,
|
|
46
|
+
sortField: activeSortField,
|
|
47
|
+
direction: activeSortField ? activeSortDirection : '',
|
|
48
|
+
};
|
|
49
|
+
};
|
|
@@ -12,7 +12,7 @@ export enum ROUTE {
|
|
|
12
12
|
FLEETS = 'FLEETS',
|
|
13
13
|
FLEET_CREATE = 'FLEET_CREATE',
|
|
14
14
|
FLEET_IMPORT = 'FLEET_IMPORT',
|
|
15
|
-
FLEET_DETAILS = '
|
|
15
|
+
FLEET_DETAILS = 'FLEET_DETAILS',
|
|
16
16
|
FLEET_EDIT = 'FLEET_EDIT',
|
|
17
17
|
DEVICES = 'DEVICES',
|
|
18
18
|
DEVICE_DETAILS = 'DEVICE_DETAILS',
|
|
@@ -20,8 +20,8 @@ export enum ROUTE {
|
|
|
20
20
|
REPOSITORIES = 'REPOSITORIES',
|
|
21
21
|
REPO_CREATE = 'REPO_CREATE',
|
|
22
22
|
REPO_EDIT = 'REPO_EDIT',
|
|
23
|
-
REPO_DETAILS = '
|
|
24
|
-
RESOURCE_SYNC_DETAILS = '
|
|
23
|
+
REPO_DETAILS = 'REPO_DETAILS',
|
|
24
|
+
RESOURCE_SYNC_DETAILS = 'RESOURCE_SYNC_DETAILS',
|
|
25
25
|
ENROLLMENT_REQUESTS = 'ENROLLMENT_REQUESTS',
|
|
26
26
|
ENROLLMENT_REQUEST_DETAILS = 'ENROLLMENT_REQUEST_DETAILS',
|
|
27
27
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { EnrollmentRequestList } from '@flightctl/types';
|
|
2
|
+
|
|
3
|
+
import { useFetchPeriodically } from './useFetchPeriodically';
|
|
4
|
+
import { getApiListCount } from '../utils/api';
|
|
5
|
+
|
|
6
|
+
export const usePendingEnrollmentRequestsCount = (): [number, boolean, unknown] => {
|
|
7
|
+
const [erList, loading, error] = useFetchPeriodically<EnrollmentRequestList>({
|
|
8
|
+
endpoint: 'enrollmentrequests?fieldSelector=!status.approval.approved&limit=1',
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
return [getApiListCount(erList) || 0, loading, error];
|
|
12
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { FlightCtlLabel } from '../types/extraTypes';
|
|
2
|
+
|
|
3
|
+
const addQueryConditions = (fieldSelectors: string[], fieldSelector: string, values?: string[]) => {
|
|
4
|
+
if (values?.length === 1) {
|
|
5
|
+
fieldSelectors.push(`${fieldSelector}=${values[0]}`);
|
|
6
|
+
} else if (values?.length) {
|
|
7
|
+
fieldSelectors.push(`${fieldSelector} in (${values.join(',')})`);
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const addTextContainsCondition = (fieldSelectors: string[], fieldSelector: string, value: string) => {
|
|
12
|
+
fieldSelectors.push(`${fieldSelector} contains ${value}`); // contains operator
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const setLabelParams = (params: URLSearchParams, labels?: FlightCtlLabel[]) => {
|
|
16
|
+
if (labels?.length) {
|
|
17
|
+
const labelSelector = labels.reduce((acc, curr) => {
|
|
18
|
+
if (!acc) {
|
|
19
|
+
acc = `${curr.key}=${curr.value || ''}`;
|
|
20
|
+
} else {
|
|
21
|
+
acc += `,${curr.key}=${curr.value || ''}`;
|
|
22
|
+
}
|
|
23
|
+
return acc;
|
|
24
|
+
}, '');
|
|
25
|
+
params.append('labelSelector', labelSelector);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export { addQueryConditions, addTextContainsCondition, setLabelParams };
|