Skip to content

feat: implement simple and narrow vertical progress bar #1560

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Nov 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no sorting for new columns if table is paginated.

Currently, for every column name is checked with isSortableNodesProperty, if name is in the list, the column is sortable and it's name is sent to backend as sort param.

I'm not sure what's to do in this case, since there are two columns with different ids, but they have the same sort fields (CPU and Memory).

Probably, you can use not column name, but add separate sortField property to every column object (in such case it's better to do it in a separate PR, since it's a lot of refactoring). Maybe there is a better and easier solution

Copy link
Collaborator Author

@astandrik astandrik Nov 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

created dictionary for sort values

not sure if it's the best solution in the world, but as far as we decided to rename CPU to Pools looks like there are not so many choices left...

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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks strange when resized. Let's do either with fixed bar width or non-resizable column.
Screenshot 2024-10-31 at 18 16 58

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reworked

};
}
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
Loading