Skip to content

Commit

Permalink
feat(Nodes): add grouping
Browse files Browse the repository at this point in the history
  • Loading branch information
artemmufazalov committed Nov 6, 2024
1 parent 697921a commit 23d4212
Show file tree
Hide file tree
Showing 22 changed files with 556 additions and 221 deletions.
2 changes: 1 addition & 1 deletion src/components/PaginatedTable/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,4 @@ export type RenderControls = (params: ControlsParams) => React.ReactNode;
export type RenderEmptyDataMessage = () => React.ReactNode;
export type RenderErrorMessage = (error: IResponseError) => React.ReactNode;

export type GetRowClassName<T> = (row: T) => string | undefined;
export type GetRowClassName<T> = (row: T) => string;
3 changes: 3 additions & 0 deletions src/components/Search/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const b = cn('ydb-search');
interface SearchProps {
onChange: (value: string) => void;
value?: string;
width?: React.CSSProperties['width'];
className?: string;
debounce?: number;
placeholder?: string;
Expand All @@ -19,6 +20,7 @@ interface SearchProps {
export const Search = ({
onChange,
value = '',
width,
className,
debounce = 200,
placeholder,
Expand Down Expand Up @@ -50,6 +52,7 @@ export const Search = ({
<TextInput
hasClear
autoFocus
style={{width}}
className={b(null, className)}
placeholder={placeholder}
value={searchValue}
Expand Down
10 changes: 10 additions & 0 deletions src/components/nodesColumns/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ export const NODES_COLUMNS_WIDTH_LS_KEY = 'nodesTableColumnsWidth';

export const NODES_COLUMNS_IDS = {
NodeId: 'NodeId',
SystemState: 'SystemState',
Host: 'Host',
Database: 'Database',
NodeName: 'NodeName',
DC: 'DC',
Rack: 'Rack',
Expand Down Expand Up @@ -35,9 +37,15 @@ export const NODES_COLUMNS_TITLES = {
get NodeId() {
return i18n('node-id');
},
get SystemState() {
return i18n('system-state');
},
get Host() {
return i18n('host');
},
get Database() {
return i18n('database');
},
get NodeName() {
return i18n('node-name');
},
Expand Down Expand Up @@ -95,7 +103,9 @@ export const NODES_COLUMNS_TITLES = {
// Also for some columns we may use more than one field
export const NODES_COLUMNS_TO_DATA_FIELDS: Record<NodesColumnId, NodesRequiredField[]> = {
NodeId: ['NodeId'],
SystemState: ['SystemState'],
Host: ['Host', 'Rack', 'Database', 'SystemState'],
Database: ['Database'],
NodeName: ['NodeName'],
DC: ['DC'],
Rack: ['Rack'],
Expand Down
2 changes: 2 additions & 0 deletions src/components/nodesColumns/i18n/en.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"node-id": "Node ID",
"system-state": "System State",
"host": "Host",
"database": "Database",
"node-name": "Node Name",
"dc": "DC",
"rack": "Rack",
Expand Down
4 changes: 4 additions & 0 deletions src/containers/Nodes/Nodes.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@
&__node_unavailable {
opacity: 0.6;
}

&__groups-wrapper {
padding-right: 20px;
}
}
92 changes: 17 additions & 75 deletions src/containers/Nodes/Nodes.tsx
Original file line number Diff line number Diff line change
@@ -1,68 +1,41 @@
import React from 'react';

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

import {EntitiesCount} from '../../components/EntitiesCount';
import {AccessDenied} from '../../components/Errors/403';
import {isAccessError} from '../../components/Errors/PageError/PageError';
import {ResponseError} from '../../components/Errors/ResponseError';
import {Illustration} from '../../components/Illustration';
import {ProblemFilter} from '../../components/ProblemFilter';
import {ResizeableDataTable} from '../../components/ResizeableDataTable/ResizeableDataTable';
import {Search} from '../../components/Search';
import {TableWithControlsLayout} from '../../components/TableWithControlsLayout/TableWithControlsLayout';
import {UptimeFilter} from '../../components/UptimeFIlter';
import {NODES_COLUMNS_WIDTH_LS_KEY} from '../../components/nodesColumns/constants';
import {nodesApi} from '../../store/reducers/nodes/nodes';
import {filterNodes} from '../../store/reducers/nodes/selectors';
import type {NodesSortParams} from '../../store/reducers/nodes/types';
import {
ProblemFilterValues,
changeFilter,
selectProblemFilter,
} from '../../store/reducers/settings/settings';
import type {ProblemFilterValue} from '../../store/reducers/settings/types';
import {useProblemFilter} from '../../store/reducers/settings/hooks';
import type {AdditionalNodesProps} from '../../types/additionalProps';
import {cn} from '../../utils/cn';
import {DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
import {
useAutoRefreshInterval,
useTableSort,
useTypedDispatch,
useTypedSelector,
} from '../../utils/hooks';
import {
NodesUptimeFilterValues,
isUnavailableNode,
nodesUptimeFilterValuesSchema,
} from '../../utils/nodes';
import {useAutoRefreshInterval, useTableSort} from '../../utils/hooks';
import {NodesUptimeFilterValues} from '../../utils/nodes';

import {NodesControls} from './NodesControls/NodesControls';
import {useNodesSelectedColumns} from './columns/hooks';
import i18n from './i18n';
import {getRowClassName} from './shared';
import {useNodesPageQueryParams} from './useNodesPageQueryParams';

import './Nodes.scss';

const b = cn('ydb-nodes');

interface NodesProps {
path?: string;
database?: string;
additionalNodesProps?: AdditionalNodesProps;
}

export const Nodes = ({path, database, additionalNodesProps = {}}: NodesProps) => {
const [queryParams, setQueryParams] = useQueryParams({
uptimeFilter: StringParam,
search: StringParam,
});
const uptimeFilter = nodesUptimeFilterValuesSchema.parse(queryParams.uptimeFilter);
const searchValue = queryParams.search ?? '';

const dispatch = useTypedDispatch();
const {searchValue, uptimeFilter} = useNodesPageQueryParams();
const {problemFilter} = useProblemFilter();

const problemFilter = useTypedSelector(selectProblemFilter);
const [autoRefreshInterval] = useAutoRefreshInterval();

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

const handleSearchQueryChange = (value: string) => {
setQueryParams({search: value || undefined}, 'replaceIn');
};

const handleProblemFilterChange = (value: ProblemFilterValue) => {
dispatch(changeFilter(value));
};

const handleUptimeFilterChange = (value: NodesUptimeFilterValues) => {
setQueryParams({uptimeFilter: value}, 'replaceIn');
};

const nodes = React.useMemo(() => {
return filterNodes(data?.Nodes, {searchValue, uptimeFilter, problemFilter});
}, [data, searchValue, uptimeFilter, problemFilter]);
Expand All @@ -104,38 +65,19 @@ export const Nodes = ({path, database, additionalNodesProps = {}}: NodesProps) =

const renderControls = () => {
return (
<React.Fragment>
<Search
onChange={handleSearchQueryChange}
placeholder="Host name"
className={b('search')}
value={searchValue}
/>
<ProblemFilter value={problemFilter} onChange={handleProblemFilterChange} />
<UptimeFilter value={uptimeFilter} onChange={handleUptimeFilterChange} />
<TableColumnSetup
popupWidth={200}
items={columnsToSelect}
showStatus
onUpdate={setColumns}
sortable={false}
/>
<EntitiesCount
total={totalNodes}
current={nodes.length}
label={'Nodes'}
loading={isLoading}
/>
</React.Fragment>
<NodesControls
columnsToSelect={columnsToSelect}
handleSelectedColumnsUpdate={setColumns}
entitiesCountCurrent={nodes.length}
entitiesCountTotal={totalNodes}
entitiesLoading={isLoading}
/>
);
};

const renderTable = () => {
if (nodes.length === 0) {
if (
problemFilter !== ProblemFilterValues.ALL ||
uptimeFilter !== NodesUptimeFilterValues.All
) {
if (problemFilter !== 'All' || uptimeFilter !== NodesUptimeFilterValues.All) {
return <Illustration name="thumbsUp" width="200" />;
}
}
Expand All @@ -149,7 +91,7 @@ export const Nodes = ({path, database, additionalNodesProps = {}}: NodesProps) =
sortOrder={sort}
onSort={handleSort}
emptyDataMessage={i18n('empty.default')}
rowClassName={(row) => b('node', {unavailable: isUnavailableNode(row)})}
rowClassName={getRowClassName}
/>
);
};
Expand Down
97 changes: 97 additions & 0 deletions src/containers/Nodes/NodesControls/NodesControls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import React from 'react';

import type {TableColumnSetupItem} from '@gravity-ui/uikit';
import {Select, TableColumnSetup, Text} from '@gravity-ui/uikit';

import {EntitiesCount} from '../../../components/EntitiesCount';
import {ProblemFilter} from '../../../components/ProblemFilter';
import {Search} from '../../../components/Search';
import {UptimeFilter} from '../../../components/UptimeFIlter';
import {useViewerNodesHandlerHasGroupingBySystemState} from '../../../store/reducers/capabilities/hooks';
import {useProblemFilter} from '../../../store/reducers/settings/hooks';
import {getNodesGroupByOptions} from '../columns/constants';
import i18n from '../i18n';
import {useNodesPageQueryParams} from '../useNodesPageQueryParams';

interface NodesControlsProps {
withGroupBySelect?: boolean;

columnsToSelect: TableColumnSetupItem[];
handleSelectedColumnsUpdate: (updated: TableColumnSetupItem[]) => void;

entitiesCountCurrent: number;
entitiesCountTotal?: number;
entitiesLoading: boolean;
}

export function NodesControls({
withGroupBySelect,

columnsToSelect,
handleSelectedColumnsUpdate,

entitiesCountCurrent,
entitiesCountTotal,
entitiesLoading,
}: NodesControlsProps) {
const {
searchValue,
uptimeFilter,
groupByParam,

handleSearchQueryChange,
handleUptimeFilterChange,
handleGroupByParamChange,
} = useNodesPageQueryParams();
const {problemFilter, handleProblemFilterChange} = useProblemFilter();

const systemStateGroupingAvailable = useViewerNodesHandlerHasGroupingBySystemState();
const groupByoptions = getNodesGroupByOptions(systemStateGroupingAvailable);

const handleGroupBySelectUpdate = (value: string[]) => {
handleGroupByParamChange(value[0]);
};

return (
<React.Fragment>
<Search
onChange={handleSearchQueryChange}
placeholder={i18n('controls_search-placeholder')}
width={238}
value={searchValue}
/>
{systemStateGroupingAvailable && withGroupBySelect ? null : (
<ProblemFilter value={problemFilter} onChange={handleProblemFilterChange} />
)}
{withGroupBySelect ? null : (
<UptimeFilter value={uptimeFilter} onChange={handleUptimeFilterChange} />
)}
<TableColumnSetup
popupWidth={200}
items={columnsToSelect}
showStatus
onUpdate={handleSelectedColumnsUpdate}
sortable={false}
/>
{withGroupBySelect ? (
<React.Fragment>
<Text variant="body-2">{i18n('controls_group-by-placeholder')}</Text>
<Select
hasClear
placeholder={'-'}
width={150}
defaultValue={groupByParam ? [groupByParam] : undefined}
onUpdate={handleGroupBySelectUpdate}
options={groupByoptions}
/>
</React.Fragment>
) : null}
<EntitiesCount
current={entitiesCountCurrent}
total={entitiesCountTotal}
label={i18n('nodes')}
loading={entitiesLoading}
/>
</React.Fragment>
);
}
Loading

0 comments on commit 23d4212

Please sign in to comment.