Skip to content

Commit 23d4212

Browse files
feat(Nodes): add grouping
1 parent 697921a commit 23d4212

File tree

22 files changed

+556
-221
lines changed

22 files changed

+556
-221
lines changed

src/components/PaginatedTable/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,4 @@ export type RenderControls = (params: ControlsParams) => React.ReactNode;
6767
export type RenderEmptyDataMessage = () => React.ReactNode;
6868
export type RenderErrorMessage = (error: IResponseError) => React.ReactNode;
6969

70-
export type GetRowClassName<T> = (row: T) => string | undefined;
70+
export type GetRowClassName<T> = (row: T) => string;

src/components/Search/Search.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const b = cn('ydb-search');
1111
interface SearchProps {
1212
onChange: (value: string) => void;
1313
value?: string;
14+
width?: React.CSSProperties['width'];
1415
className?: string;
1516
debounce?: number;
1617
placeholder?: string;
@@ -19,6 +20,7 @@ interface SearchProps {
1920
export const Search = ({
2021
onChange,
2122
value = '',
23+
width,
2224
className,
2325
debounce = 200,
2426
placeholder,
@@ -50,6 +52,7 @@ export const Search = ({
5052
<TextInput
5153
hasClear
5254
autoFocus
55+
style={{width}}
5356
className={b(null, className)}
5457
placeholder={placeholder}
5558
value={searchValue}

src/components/nodesColumns/constants.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ export const NODES_COLUMNS_WIDTH_LS_KEY = 'nodesTableColumnsWidth';
77

88
export const NODES_COLUMNS_IDS = {
99
NodeId: 'NodeId',
10+
SystemState: 'SystemState',
1011
Host: 'Host',
12+
Database: 'Database',
1113
NodeName: 'NodeName',
1214
DC: 'DC',
1315
Rack: 'Rack',
@@ -35,9 +37,15 @@ export const NODES_COLUMNS_TITLES = {
3537
get NodeId() {
3638
return i18n('node-id');
3739
},
40+
get SystemState() {
41+
return i18n('system-state');
42+
},
3843
get Host() {
3944
return i18n('host');
4045
},
46+
get Database() {
47+
return i18n('database');
48+
},
4149
get NodeName() {
4250
return i18n('node-name');
4351
},
@@ -95,7 +103,9 @@ export const NODES_COLUMNS_TITLES = {
95103
// Also for some columns we may use more than one field
96104
export const NODES_COLUMNS_TO_DATA_FIELDS: Record<NodesColumnId, NodesRequiredField[]> = {
97105
NodeId: ['NodeId'],
106+
SystemState: ['SystemState'],
98107
Host: ['Host', 'Rack', 'Database', 'SystemState'],
108+
Database: ['Database'],
99109
NodeName: ['NodeName'],
100110
DC: ['DC'],
101111
Rack: ['Rack'],

src/components/nodesColumns/i18n/en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
{
22
"node-id": "Node ID",
3+
"system-state": "System State",
34
"host": "Host",
5+
"database": "Database",
46
"node-name": "Node Name",
57
"dc": "DC",
68
"rack": "Rack",

src/containers/Nodes/Nodes.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,8 @@
1515
&__node_unavailable {
1616
opacity: 0.6;
1717
}
18+
19+
&__groups-wrapper {
20+
padding-right: 20px;
21+
}
1822
}

src/containers/Nodes/Nodes.tsx

Lines changed: 17 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,41 @@
11
import React from 'react';
22

33
import {ASCENDING} from '@gravity-ui/react-data-table/build/esm/lib/constants';
4-
import {TableColumnSetup} from '@gravity-ui/uikit';
5-
import {StringParam, useQueryParams} from 'use-query-params';
64

7-
import {EntitiesCount} from '../../components/EntitiesCount';
85
import {AccessDenied} from '../../components/Errors/403';
96
import {isAccessError} from '../../components/Errors/PageError/PageError';
107
import {ResponseError} from '../../components/Errors/ResponseError';
118
import {Illustration} from '../../components/Illustration';
12-
import {ProblemFilter} from '../../components/ProblemFilter';
139
import {ResizeableDataTable} from '../../components/ResizeableDataTable/ResizeableDataTable';
14-
import {Search} from '../../components/Search';
1510
import {TableWithControlsLayout} from '../../components/TableWithControlsLayout/TableWithControlsLayout';
16-
import {UptimeFilter} from '../../components/UptimeFIlter';
1711
import {NODES_COLUMNS_WIDTH_LS_KEY} from '../../components/nodesColumns/constants';
1812
import {nodesApi} from '../../store/reducers/nodes/nodes';
1913
import {filterNodes} from '../../store/reducers/nodes/selectors';
2014
import type {NodesSortParams} from '../../store/reducers/nodes/types';
21-
import {
22-
ProblemFilterValues,
23-
changeFilter,
24-
selectProblemFilter,
25-
} from '../../store/reducers/settings/settings';
26-
import type {ProblemFilterValue} from '../../store/reducers/settings/types';
15+
import {useProblemFilter} from '../../store/reducers/settings/hooks';
2716
import type {AdditionalNodesProps} from '../../types/additionalProps';
28-
import {cn} from '../../utils/cn';
2917
import {DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
30-
import {
31-
useAutoRefreshInterval,
32-
useTableSort,
33-
useTypedDispatch,
34-
useTypedSelector,
35-
} from '../../utils/hooks';
36-
import {
37-
NodesUptimeFilterValues,
38-
isUnavailableNode,
39-
nodesUptimeFilterValuesSchema,
40-
} from '../../utils/nodes';
18+
import {useAutoRefreshInterval, useTableSort} from '../../utils/hooks';
19+
import {NodesUptimeFilterValues} from '../../utils/nodes';
4120

21+
import {NodesControls} from './NodesControls/NodesControls';
4222
import {useNodesSelectedColumns} from './columns/hooks';
4323
import i18n from './i18n';
24+
import {getRowClassName} from './shared';
25+
import {useNodesPageQueryParams} from './useNodesPageQueryParams';
4426

4527
import './Nodes.scss';
4628

47-
const b = cn('ydb-nodes');
48-
4929
interface NodesProps {
5030
path?: string;
5131
database?: string;
5232
additionalNodesProps?: AdditionalNodesProps;
5333
}
5434

5535
export const Nodes = ({path, database, additionalNodesProps = {}}: NodesProps) => {
56-
const [queryParams, setQueryParams] = useQueryParams({
57-
uptimeFilter: StringParam,
58-
search: StringParam,
59-
});
60-
const uptimeFilter = nodesUptimeFilterValuesSchema.parse(queryParams.uptimeFilter);
61-
const searchValue = queryParams.search ?? '';
62-
63-
const dispatch = useTypedDispatch();
36+
const {searchValue, uptimeFilter} = useNodesPageQueryParams();
37+
const {problemFilter} = useProblemFilter();
6438

65-
const problemFilter = useTypedSelector(selectProblemFilter);
6639
const [autoRefreshInterval] = useAutoRefreshInterval();
6740

6841
const {columnsToShow, columnsToSelect, setColumns} = useNodesSelectedColumns({
@@ -84,18 +57,6 @@ export const Nodes = ({path, database, additionalNodesProps = {}}: NodesProps) =
8457
setSortValue(sortParams as NodesSortParams);
8558
});
8659

87-
const handleSearchQueryChange = (value: string) => {
88-
setQueryParams({search: value || undefined}, 'replaceIn');
89-
};
90-
91-
const handleProblemFilterChange = (value: ProblemFilterValue) => {
92-
dispatch(changeFilter(value));
93-
};
94-
95-
const handleUptimeFilterChange = (value: NodesUptimeFilterValues) => {
96-
setQueryParams({uptimeFilter: value}, 'replaceIn');
97-
};
98-
9960
const nodes = React.useMemo(() => {
10061
return filterNodes(data?.Nodes, {searchValue, uptimeFilter, problemFilter});
10162
}, [data, searchValue, uptimeFilter, problemFilter]);
@@ -104,38 +65,19 @@ export const Nodes = ({path, database, additionalNodesProps = {}}: NodesProps) =
10465

10566
const renderControls = () => {
10667
return (
107-
<React.Fragment>
108-
<Search
109-
onChange={handleSearchQueryChange}
110-
placeholder="Host name"
111-
className={b('search')}
112-
value={searchValue}
113-
/>
114-
<ProblemFilter value={problemFilter} onChange={handleProblemFilterChange} />
115-
<UptimeFilter value={uptimeFilter} onChange={handleUptimeFilterChange} />
116-
<TableColumnSetup
117-
popupWidth={200}
118-
items={columnsToSelect}
119-
showStatus
120-
onUpdate={setColumns}
121-
sortable={false}
122-
/>
123-
<EntitiesCount
124-
total={totalNodes}
125-
current={nodes.length}
126-
label={'Nodes'}
127-
loading={isLoading}
128-
/>
129-
</React.Fragment>
68+
<NodesControls
69+
columnsToSelect={columnsToSelect}
70+
handleSelectedColumnsUpdate={setColumns}
71+
entitiesCountCurrent={nodes.length}
72+
entitiesCountTotal={totalNodes}
73+
entitiesLoading={isLoading}
74+
/>
13075
);
13176
};
13277

13378
const renderTable = () => {
13479
if (nodes.length === 0) {
135-
if (
136-
problemFilter !== ProblemFilterValues.ALL ||
137-
uptimeFilter !== NodesUptimeFilterValues.All
138-
) {
80+
if (problemFilter !== 'All' || uptimeFilter !== NodesUptimeFilterValues.All) {
13981
return <Illustration name="thumbsUp" width="200" />;
14082
}
14183
}
@@ -149,7 +91,7 @@ export const Nodes = ({path, database, additionalNodesProps = {}}: NodesProps) =
14991
sortOrder={sort}
15092
onSort={handleSort}
15193
emptyDataMessage={i18n('empty.default')}
152-
rowClassName={(row) => b('node', {unavailable: isUnavailableNode(row)})}
94+
rowClassName={getRowClassName}
15395
/>
15496
);
15597
};
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import React from 'react';
2+
3+
import type {TableColumnSetupItem} from '@gravity-ui/uikit';
4+
import {Select, TableColumnSetup, Text} from '@gravity-ui/uikit';
5+
6+
import {EntitiesCount} from '../../../components/EntitiesCount';
7+
import {ProblemFilter} from '../../../components/ProblemFilter';
8+
import {Search} from '../../../components/Search';
9+
import {UptimeFilter} from '../../../components/UptimeFIlter';
10+
import {useViewerNodesHandlerHasGroupingBySystemState} from '../../../store/reducers/capabilities/hooks';
11+
import {useProblemFilter} from '../../../store/reducers/settings/hooks';
12+
import {getNodesGroupByOptions} from '../columns/constants';
13+
import i18n from '../i18n';
14+
import {useNodesPageQueryParams} from '../useNodesPageQueryParams';
15+
16+
interface NodesControlsProps {
17+
withGroupBySelect?: boolean;
18+
19+
columnsToSelect: TableColumnSetupItem[];
20+
handleSelectedColumnsUpdate: (updated: TableColumnSetupItem[]) => void;
21+
22+
entitiesCountCurrent: number;
23+
entitiesCountTotal?: number;
24+
entitiesLoading: boolean;
25+
}
26+
27+
export function NodesControls({
28+
withGroupBySelect,
29+
30+
columnsToSelect,
31+
handleSelectedColumnsUpdate,
32+
33+
entitiesCountCurrent,
34+
entitiesCountTotal,
35+
entitiesLoading,
36+
}: NodesControlsProps) {
37+
const {
38+
searchValue,
39+
uptimeFilter,
40+
groupByParam,
41+
42+
handleSearchQueryChange,
43+
handleUptimeFilterChange,
44+
handleGroupByParamChange,
45+
} = useNodesPageQueryParams();
46+
const {problemFilter, handleProblemFilterChange} = useProblemFilter();
47+
48+
const systemStateGroupingAvailable = useViewerNodesHandlerHasGroupingBySystemState();
49+
const groupByoptions = getNodesGroupByOptions(systemStateGroupingAvailable);
50+
51+
const handleGroupBySelectUpdate = (value: string[]) => {
52+
handleGroupByParamChange(value[0]);
53+
};
54+
55+
return (
56+
<React.Fragment>
57+
<Search
58+
onChange={handleSearchQueryChange}
59+
placeholder={i18n('controls_search-placeholder')}
60+
width={238}
61+
value={searchValue}
62+
/>
63+
{systemStateGroupingAvailable && withGroupBySelect ? null : (
64+
<ProblemFilter value={problemFilter} onChange={handleProblemFilterChange} />
65+
)}
66+
{withGroupBySelect ? null : (
67+
<UptimeFilter value={uptimeFilter} onChange={handleUptimeFilterChange} />
68+
)}
69+
<TableColumnSetup
70+
popupWidth={200}
71+
items={columnsToSelect}
72+
showStatus
73+
onUpdate={handleSelectedColumnsUpdate}
74+
sortable={false}
75+
/>
76+
{withGroupBySelect ? (
77+
<React.Fragment>
78+
<Text variant="body-2">{i18n('controls_group-by-placeholder')}</Text>
79+
<Select
80+
hasClear
81+
placeholder={'-'}
82+
width={150}
83+
defaultValue={groupByParam ? [groupByParam] : undefined}
84+
onUpdate={handleGroupBySelectUpdate}
85+
options={groupByoptions}
86+
/>
87+
</React.Fragment>
88+
) : null}
89+
<EntitiesCount
90+
current={entitiesCountCurrent}
91+
total={entitiesCountTotal}
92+
label={i18n('nodes')}
93+
loading={entitiesLoading}
94+
/>
95+
</React.Fragment>
96+
);
97+
}

0 commit comments

Comments
 (0)