Skip to content

Commit

Permalink
feat: implement simple and narrow vertical progress bar (#1560)
Browse files Browse the repository at this point in the history
  • Loading branch information
astandrik authored Nov 2, 2024
1 parent 3287f99 commit e5d0823
Show file tree
Hide file tree
Showing 18 changed files with 206 additions and 18 deletions.
4 changes: 4 additions & 0 deletions src/components/CellWithPopover/CellWithPopover.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,9 @@
.g-popover__handler {
display: inline;
}

&_full-width {
width: 100%;
}
}
}
6 changes: 4 additions & 2 deletions src/components/CellWithPopover/CellWithPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const b = cn('ydb-cell-with-popover');

interface CellWithPopoverProps extends PopoverProps {
wrapperClassName?: string;
fullWidth?: boolean;
}

const DELAY_TIMEOUT = 100;
Expand All @@ -17,14 +18,15 @@ export function CellWithPopover({
children,
className,
wrapperClassName,
fullWidth,
...props
}: CellWithPopoverProps) {
return (
<div className={b(null, wrapperClassName)}>
<div className={b({fullWidth}, wrapperClassName)}>
<Popover
delayClosing={DELAY_TIMEOUT}
delayOpening={DELAY_TIMEOUT}
className={b('popover', className)}
className={b('popover', {'full-width': fullWidth}, className)}
{...props}
>
{children}
Expand Down
4 changes: 3 additions & 1 deletion src/components/ProgressViewer/ProgressViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface ProgressViewerProps {
inverseColorize?: boolean;
warningThreshold?: number;
dangerThreshold?: number;
hideCapacity?: boolean;
}

export function ProgressViewer({
Expand All @@ -61,6 +62,7 @@ export function ProgressViewer({
inverseColorize,
warningThreshold = 60,
dangerThreshold = 80,
hideCapacity,
}: ProgressViewerProps) {
const theme = useTheme();

Expand Down Expand Up @@ -94,7 +96,7 @@ export function ProgressViewer({
};

const renderContent = () => {
if (isNumeric(capacity)) {
if (isNumeric(capacity) && !hideCapacity) {
return `${valueText} ${divider} ${capacityText}`;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type {TPoolStats} from '../../../types/api/nodes';
import {InfoViewer, createInfoFormatter, formatObject} from '../../InfoViewer';

const formatPool = createInfoFormatter<TPoolStats>({
export const formatPool = createInfoFormatter<TPoolStats>({
values: {
Usage: (value) => value && `${(Number(value) * 100).toFixed(2)} %`,
},
Expand Down
6 changes: 6 additions & 0 deletions src/components/nodesColumns/NodesColumns.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.ydb-nodes-columns {
&__column-ram,
&__column-cpu {
min-width: 40px;
}
}
130 changes: 126 additions & 4 deletions src/components/nodesColumns/columns.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,36 @@
import DataTable from '@gravity-ui/react-data-table';
import {DefinitionList} from '@gravity-ui/uikit';

import {getLoadSeverityForNode} from '../../store/reducers/nodes/utils';
import type {TPoolStats} from '../../types/api/nodes';
import type {TTabletStateInfo} from '../../types/api/tablet';
import {valueIsDefined} from '../../utils';
import {cn} from '../../utils/cn';
import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants';
import {formatStorageValuesToGb} from '../../utils/dataFormatters/dataFormatters';
import {
formatStorageValues,
formatStorageValuesToGb,
} from '../../utils/dataFormatters/dataFormatters';
import {getSpaceUsageSeverity} from '../../utils/storage';
import type {Column} from '../../utils/tableUtils/types';
import {isNumeric} from '../../utils/utils';
import {CellWithPopover} from '../CellWithPopover/CellWithPopover';
import {NodeHostWrapper} from '../NodeHostWrapper/NodeHostWrapper';
import type {NodeHostData} from '../NodeHostWrapper/NodeHostWrapper';
import {PoolsGraph} from '../PoolsGraph/PoolsGraph';
import {ProgressViewer} from '../ProgressViewer/ProgressViewer';
import {TabletsStatistic} from '../TabletsStatistic';
import {formatPool} from '../TooltipsContent';
import {UsageLabel} from '../UsageLabel/UsageLabel';

import {NODES_COLUMNS_IDS, NODES_COLUMNS_TITLES} from './constants';
import i18n from './i18n';
import type {GetNodesColumnsParams} from './types';

import './NodesColumns.scss';

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

export function getNodeIdColumn<T extends {NodeId?: string | number}>(): Column<T> {
return {
name: NODES_COLUMNS_IDS.NodeId,
Expand Down Expand Up @@ -111,6 +123,57 @@ export function getMemoryColumn<
resizeMinWidth: 170,
};
}

export function getRAMColumn<T extends {MemoryUsed?: string; MemoryLimit?: string}>(): Column<T> {
return {
name: NODES_COLUMNS_IDS.RAM,
header: NODES_COLUMNS_TITLES.RAM,
sortAccessor: ({MemoryUsed = 0}) => Number(MemoryUsed),
defaultOrder: DataTable.DESCENDING,
render: ({row}) => {
const [memoryUsed, memoryLimit] =
isNumeric(row.MemoryUsed) && isNumeric(row.MemoryLimit)
? formatStorageValues(
Number(row.MemoryUsed),
Number(row.MemoryLimit),
'gb',
undefined,
true,
)
: [0, 0];
return (
<CellWithPopover
placement={['top', 'auto']}
fullWidth
content={
<DefinitionList responsive>
<DefinitionList.Item name={i18n('field_memory-used')}>
{memoryUsed}
</DefinitionList.Item>
<DefinitionList.Item name={i18n('field_memory-limit')}>
{memoryLimit}
</DefinitionList.Item>
</DefinitionList>
}
>
<ProgressViewer
value={row.MemoryUsed}
capacity={row.MemoryLimit}
formatValues={(value, total) =>
formatStorageValues(value, total, 'gb', undefined, true)
}
className={b('column-ram')}
colorizeProgress
hideCapacity
/>
</CellWithPopover>
);
},
align: DataTable.LEFT,
width: 80,
resizeMinWidth: 40,
};
}
export function getSharedCacheUsageColumn<
T extends {SharedCacheUsed?: string | number; SharedCacheLimit?: string | number},
>(): Column<T> {
Expand All @@ -130,10 +193,10 @@ export function getSharedCacheUsageColumn<
resizeMinWidth: 170,
};
}
export function getCpuColumn<T extends {PoolStats?: TPoolStats[]}>(): Column<T> {
export function getPoolsColumn<T extends {PoolStats?: TPoolStats[]}>(): Column<T> {
return {
name: NODES_COLUMNS_IDS.CPU,
header: NODES_COLUMNS_TITLES.CPU,
name: NODES_COLUMNS_IDS.Pools,
header: NODES_COLUMNS_TITLES.Pools,
sortAccessor: ({PoolStats = []}) => Math.max(...PoolStats.map(({Usage}) => Number(Usage))),
defaultOrder: DataTable.DESCENDING,
render: ({row}) =>
Expand All @@ -143,6 +206,65 @@ export function getCpuColumn<T extends {PoolStats?: TPoolStats[]}>(): Column<T>
resizeMinWidth: 60,
};
}
export function getCpuColumn<
T extends {PoolStats?: TPoolStats[]; CoresUsed?: number; CoresTotal?: number},
>(): Column<T> {
return {
name: NODES_COLUMNS_IDS.CPU,
header: NODES_COLUMNS_TITLES.CPU,
sortAccessor: ({PoolStats = []}) => Math.max(...PoolStats.map(({Usage}) => Number(Usage))),
defaultOrder: DataTable.DESCENDING,
render: ({row}) => {
if (!row.PoolStats) {
return EMPTY_DATA_PLACEHOLDER;
}

let totalPoolUsage =
isNumeric(row.CoresUsed) && isNumeric(row.CoresTotal)
? row.CoresUsed / row.CoresTotal
: undefined;

if (totalPoolUsage === undefined) {
let totalThreadsCount = 0;
totalPoolUsage = row.PoolStats.reduce((acc, pool) => {
totalThreadsCount += Number(pool.Threads);
return acc + Number(pool.Usage) * Number(pool.Threads);
}, 0);

totalPoolUsage = totalPoolUsage / totalThreadsCount;
}

return (
<CellWithPopover
placement={['top', 'auto']}
fullWidth
content={
<DefinitionList responsive>
{row.PoolStats.map((pool) =>
isNumeric(pool.Usage) ? (
<DefinitionList.Item key={pool.Name} name={pool.Name}>
{formatPool('Usage', pool.Usage).value}
</DefinitionList.Item>
) : null,
)}
</DefinitionList>
}
>
<ProgressViewer
className={b('column-cpu')}
value={totalPoolUsage}
capacity={1}
colorizeProgress
percents
/>
</CellWithPopover>
);
},
align: DataTable.LEFT,
width: 80,
resizeMinWidth: 40,
};
}
export function getLoadAverageColumn<T extends {LoadAveragePercents?: number[]}>(): Column<T> {
return {
name: NODES_COLUMNS_IDS.LoadAverage,
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 @@ -14,7 +14,9 @@ export const NODES_COLUMNS_IDS = {
Version: 'Version',
Uptime: 'Uptime',
Memory: 'Memory',
RAM: 'RAM',
CPU: 'CPU',
Pools: 'Pools',
LoadAverage: 'LoadAverage',
Load: 'Load',
DiskSpaceUsage: 'DiskSpaceUsage',
Expand Down Expand Up @@ -54,6 +56,12 @@ export const NODES_COLUMNS_TITLES = {
get Memory() {
return i18n('memory');
},
get RAM() {
return i18n('ram');
},
get Pools() {
return i18n('pools');
},
get CPU() {
return i18n('cpu');
},
Expand Down Expand Up @@ -94,6 +102,8 @@ export const NODES_COLUMNS_TO_DATA_FIELDS: Record<NodesColumnId, NodesRequiredFi
Version: ['Version'],
Uptime: ['Uptime'],
Memory: ['Memory'],
RAM: ['Memory'],
Pools: ['CPU'],
CPU: ['CPU'],
LoadAverage: ['LoadAverage'],
Load: ['LoadAverage'],
Expand Down
6 changes: 5 additions & 1 deletion src/components/nodesColumns/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@
"version": "Version",
"uptime": "Uptime",
"memory": "Memory",
"ram": "RAM",
"cpu": "CPU",
"pools": "Pools",
"disk-usage": "Disk usage",
"tablets": "Tablets",
"load-average": "Load Average",
"load": "Load",
"caches": "Caches",
"sessions": "Sessions",
"missing": "Missing",
"pdisks": "PDisks"
"pdisks": "PDisks",
"field_memory-used": "Memory used",
"field_memory-limit": "Memory limit"
}
4 changes: 4 additions & 0 deletions src/containers/Nodes/columns/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
getMemoryColumn,
getNodeIdColumn,
getNodeNameColumn,
getPoolsColumn,
getRAMColumn,
getRackColumn,
getTabletsColumn,
getUptimeColumn,
Expand All @@ -26,6 +28,8 @@ export function getNodesColumns(params: GetNodesColumnsParams): Column<NodesPrep
getVersionColumn<NodesPreparedEntity>(),
getUptimeColumn<NodesPreparedEntity>(),
getMemoryColumn<NodesPreparedEntity>(),
getRAMColumn<NodesPreparedEntity>(),
getPoolsColumn<NodesPreparedEntity>(),
getCpuColumn<NodesPreparedEntity>(),
getLoadAverageColumn<NodesPreparedEntity>(),
getTabletsColumn<NodesPreparedEntity>(params),
Expand Down
2 changes: 1 addition & 1 deletion src/containers/Nodes/columns/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const DEFAULT_NODES_COLUMNS: NodesColumnId[] = [
'Version',
'Uptime',
'Memory',
'CPU',
'Pools',
'LoadAverage',
'Tablets',
];
Expand Down
3 changes: 2 additions & 1 deletion src/containers/Nodes/getNodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {prepareNodesData} from '../../store/reducers/nodes/utils';
import type {NodesRequestParams} from '../../types/api/nodes';
import {prepareSortValue} from '../../utils/filters';
import {
NODES_SORT_VALUE_TO_FIELD,
getProblemParamValue,
getUptimeParamValue,
isSortableNodesProperty,
Expand Down Expand Up @@ -35,7 +36,7 @@ export const getNodes: FetchData<
const {path, database, searchValue, problemFilter, uptimeFilter} = filters ?? {};

const sort = isSortableNodesProperty(columnId)
? prepareSortValue(columnId, sortOrder)
? prepareSortValue(NODES_SORT_VALUE_TO_FIELD[columnId], sortOrder)
: undefined;

const dataFieldsRequired = getRequiredDataFields(columnsIds, NODES_COLUMNS_TO_DATA_FIELDS);
Expand Down
4 changes: 4 additions & 0 deletions src/containers/Storage/StorageNodes/columns/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
getMissingDisksColumn,
getNodeIdColumn,
getNodeNameColumn,
getPoolsColumn,
getRAMColumn,
getRackColumn,
getUptimeColumn,
getVersionColumn,
Expand Down Expand Up @@ -72,6 +74,8 @@ export const getStorageNodesColumns = ({
getRackColumn<PreparedStorageNode>(),
getVersionColumn<PreparedStorageNode>(),
getMemoryColumn<PreparedStorageNode>(),
getRAMColumn<PreparedStorageNode>(),
getPoolsColumn<PreparedStorageNode>(),
getCpuColumn<PreparedStorageNode>(),
getDiskSpaceUsageColumn<PreparedStorageNode>(),
getUptimeColumn<PreparedStorageNode>(),
Expand Down
2 changes: 1 addition & 1 deletion src/containers/Storage/StorageNodes/columns/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const DEFAULT_STORAGE_NODES_COLUMNS: NodesColumnId[] = [
'Host',
'DC',
'Rack',
'CPU',
'Pools',
'Uptime',
'PDisks',
];
Expand Down
8 changes: 6 additions & 2 deletions src/containers/Storage/StorageNodes/getNodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import type {
import {prepareStorageNodesResponse} from '../../../store/reducers/storage/utils';
import type {NodesRequestParams} from '../../../types/api/nodes';
import {prepareSortValue} from '../../../utils/filters';
import {getUptimeParamValue, isSortableNodesProperty} from '../../../utils/nodes';
import {
NODES_SORT_VALUE_TO_FIELD,
getUptimeParamValue,
isSortableNodesProperty,
} from '../../../utils/nodes';
import {getRequiredDataFields} from '../../../utils/tableUtils/getRequiredDataFields';

export const getStorageNodes: FetchData<
Expand Down Expand Up @@ -37,7 +41,7 @@ export const getStorageNodes: FetchData<
const {sortOrder, columnId} = sortParams ?? {};

const sort = isSortableNodesProperty(columnId)
? prepareSortValue(columnId, sortOrder)
? prepareSortValue(NODES_SORT_VALUE_TO_FIELD[columnId], sortOrder)
: undefined;

const dataFieldsRequired = getRequiredDataFields(columnsIds, NODES_COLUMNS_TO_DATA_FIELDS);
Expand Down
Loading

0 comments on commit e5d0823

Please sign in to comment.