import React from 'react';
import {ShieldKeyhole} from '@gravity-ui/icons';
import DataTable from '@gravity-ui/react-data-table';
import {Flex, Icon, Label, Popover} from '@gravity-ui/uikit';
import {isNil} from 'lodash';
import {CellWithPopover} from '../../../../components/CellWithPopover/CellWithPopover';
import {DiskStatusLabel} from '../../../../components/DiskStatus/DiskStatus';
import {EntityName} from '../../../../components/EntityName/EntityName';
import {StatusIcon} from '../../../../components/StatusIcon/StatusIcon';
import {TitleWithHelpMark} from '../../../../components/TitleWithHelpmark/TitleWithHelpmark';
import {UsageLabel} from '../../../../components/UsageLabel/UsageLabel';
import {
getCapacityAlertColumn,
getNormalizedOccupancyColumn,
getPDiskUsageColumn,
getVDiskRawUsageColumn,
getVDiskSlotUsageColumn,
} from '../../../../components/capacityMetricsColumns/columns';
import {CAPACITY_CONFIGURATION_HELP_TEXT} from '../../../../components/capacityMetricsColumns/constants';
import {formatCapacityUnitCount} from '../../../../components/capacityMetricsColumns/formatters';
import {useStorageGroupPath} from '../../../../routes';
import type {PreparedStorageGroup} from '../../../../store/reducers/storage/types';
import {cn} from '../../../../utils/cn';
import {EMPTY_DATA_PLACEHOLDER, YDB_POPOVER_CLASS_NAME} from '../../../../utils/constants';
import {formatNumber} from '../../../../utils/dataFormatters/dataFormatters';
import {getUsageSeverity} from '../../../../utils/generateEvaluator';
import {formatToMs} from '../../../../utils/timeParsers';
import {bytesToGB, bytesToSpeed} from '../../../../utils/utils';
import {Disks} from '../../Disks/Disks';
import {
VDISKS_CONTAINER_WIDTH,
getAllPDisksContainerWidthExpansion,
getAllVDisksContainerWidth,
} from '../../Disks/constants';
import {VDisks} from '../../VDisks/VDisks';
import {getDegradedSeverity} from '../../utils';
import {getStorageGroupStateLabel} from '../../utils/getStorageGroupStateLabel';
import i18n from '../i18n';
import {
STORAGE_GROUPS_COLUMNS_IDS,
STORAGE_GROUPS_COLUMNS_TITLES,
STORAGE_GROUPS_DISKS_COLUMN_TITLES,
isSortableStorageGroupsColumn,
} from './constants';
import type {GetStorageColumnsData, StorageColumnsGetter, StorageGroupsColumn} from './types';
import './StorageGroupsColumns.scss';
const b = cn('ydb-storage-groups-columns');
const DISKS_COLUMN_WIDTH = 825;
const poolNameColumn: StorageGroupsColumn = {
name: STORAGE_GROUPS_COLUMNS_IDS.PoolName,
header: STORAGE_GROUPS_COLUMNS_TITLES.PoolName,
width: 250,
render: ({row}) => {
return row.PoolName ? (
{row.PoolName}
) : (
EMPTY_DATA_PLACEHOLDER
);
},
align: DataTable.LEFT,
};
const pileNameColumn: StorageGroupsColumn = {
name: STORAGE_GROUPS_COLUMNS_IDS.PileName,
header: STORAGE_GROUPS_COLUMNS_TITLES.PileName,
width: 120,
render: ({row}) => row.PileName || EMPTY_DATA_PLACEHOLDER,
align: DataTable.LEFT,
};
const typeColumn: StorageGroupsColumn = {
name: STORAGE_GROUPS_COLUMNS_IDS.MediaType,
header: STORAGE_GROUPS_COLUMNS_TITLES.MediaType,
width: 100,
resizeMinWidth: 100,
align: DataTable.LEFT,
render: ({row}) => (
{'\u00a0'}
{row.Encryption && (
)}
),
sortable: false,
};
const erasureColumn: StorageGroupsColumn = {
name: STORAGE_GROUPS_COLUMNS_IDS.Erasure,
header: STORAGE_GROUPS_COLUMNS_TITLES.Erasure,
width: 100,
sortAccessor: (row) => row.ErasureSpecies,
render: ({row}) => (row.ErasureSpecies ? row.ErasureSpecies : EMPTY_DATA_PLACEHOLDER),
align: DataTable.LEFT,
};
const degradedColumn: StorageGroupsColumn = {
name: STORAGE_GROUPS_COLUMNS_IDS.Degraded,
header: STORAGE_GROUPS_COLUMNS_TITLES.Degraded,
width: 110,
resizeMinWidth: 110,
render: ({row}) =>
row.Degraded ? (
) : (
EMPTY_DATA_PLACEHOLDER
),
align: DataTable.LEFT,
defaultOrder: DataTable.DESCENDING,
};
const stateColumn: StorageGroupsColumn = {
name: STORAGE_GROUPS_COLUMNS_IDS.State,
header: STORAGE_GROUPS_COLUMNS_TITLES.State,
width: 150,
render: ({row}) => {
const state = getStorageGroupStateLabel(row.State);
return state ? (
) : (
EMPTY_DATA_PLACEHOLDER
);
},
align: DataTable.LEFT,
defaultOrder: DataTable.DESCENDING,
};
const usageColumn: StorageGroupsColumn = {
name: STORAGE_GROUPS_COLUMNS_IDS.Usage,
header: STORAGE_GROUPS_COLUMNS_TITLES.Usage,
width: 85,
resizeMinWidth: 75,
render: ({row}) => {
return isNil(row.Usage) ? (
EMPTY_DATA_PLACEHOLDER
) : (
);
},
align: DataTable.LEFT,
};
const diskSpaceUsageColumn: StorageGroupsColumn = {
name: STORAGE_GROUPS_COLUMNS_IDS.DiskSpaceUsage,
header: STORAGE_GROUPS_COLUMNS_TITLES.DiskSpaceUsage,
width: 115,
resizeMinWidth: 75,
render: ({row}) => {
return isNil(row.DiskSpaceUsage) ? (
EMPTY_DATA_PLACEHOLDER
) : (
);
},
align: DataTable.LEFT,
};
const groupIdColumn: StorageGroupsColumn = {
name: STORAGE_GROUPS_COLUMNS_IDS.GroupId,
header: STORAGE_GROUPS_COLUMNS_TITLES.GroupId,
width: 140,
render: ({row}) => {
return row.GroupId ? : EMPTY_DATA_PLACEHOLDER;
},
sortAccessor: (row) => Number(row.GroupId),
align: DataTable.LEFT,
};
const usedColumn: StorageGroupsColumn = {
name: STORAGE_GROUPS_COLUMNS_IDS.Used,
header: STORAGE_GROUPS_COLUMNS_TITLES.Used,
width: 100,
render: ({row}) => {
return bytesToGB(row.Used, true);
},
align: DataTable.RIGHT,
};
const limitColumn: StorageGroupsColumn = {
name: STORAGE_GROUPS_COLUMNS_IDS.Limit,
header: STORAGE_GROUPS_COLUMNS_TITLES.Limit,
width: 100,
render: ({row}) => {
return bytesToGB(row.Limit);
},
align: DataTable.RIGHT,
};
const usedSpaceFlagColumn: StorageGroupsColumn = {
name: STORAGE_GROUPS_COLUMNS_IDS.DiskSpace,
header: STORAGE_GROUPS_COLUMNS_TITLES.DiskSpace,
width: 70,
render: ({row}) => {
return ;
},
align: DataTable.CENTER,
};
const readColumn: StorageGroupsColumn = {
name: STORAGE_GROUPS_COLUMNS_IDS.Read,
header: STORAGE_GROUPS_COLUMNS_TITLES.Read,
width: 100,
render: ({row}) => {
return row.Read ? bytesToSpeed(row.Read) : EMPTY_DATA_PLACEHOLDER;
},
align: DataTable.RIGHT,
};
const writeColumn: StorageGroupsColumn = {
name: STORAGE_GROUPS_COLUMNS_IDS.Write,
header: STORAGE_GROUPS_COLUMNS_TITLES.Write,
width: 100,
render: ({row}) => {
return row.Write ? bytesToSpeed(row.Write) : EMPTY_DATA_PLACEHOLDER;
},
align: DataTable.RIGHT,
};
const latencyColumn: StorageGroupsColumn = {
name: STORAGE_GROUPS_COLUMNS_IDS.Latency,
header: STORAGE_GROUPS_COLUMNS_TITLES.Latency,
width: 100,
render: ({row}) => {
return isNil(row.LatencyPutTabletLogMs)
? EMPTY_DATA_PLACEHOLDER
: formatToMs(row.LatencyPutTabletLogMs);
},
align: DataTable.RIGHT,
};
const allocationUnitsColumn: StorageGroupsColumn = {
name: STORAGE_GROUPS_COLUMNS_IDS.AllocationUnits,
header: STORAGE_GROUPS_COLUMNS_TITLES.AllocationUnits,
width: 150,
render: ({row}) => {
return isNil(row.AllocationUnits)
? EMPTY_DATA_PLACEHOLDER
: formatNumber(row.AllocationUnits);
},
align: DataTable.RIGHT,
};
const groupSizeInUnitsColumn: StorageGroupsColumn = {
name: STORAGE_GROUPS_COLUMNS_IDS.GroupSizeInUnits,
header: (
),
width: 160,
render: ({row}) => formatCapacityUnitCount(row.GroupSizeInUnits),
align: DataTable.RIGHT,
sortable: false,
};
const getVDisksColumn = (data?: GetStorageColumnsData): StorageGroupsColumn => {
return {
name: STORAGE_GROUPS_COLUMNS_IDS.VDisks,
header: STORAGE_GROUPS_COLUMNS_TITLES.VDisks,
className: b('vdisks-column'),
render: ({row}) => (
),
align: DataTable.CENTER,
width: 475, // usually 8-9 vdisks, this width corresponds to 8 vdisks, column is expanded if more
resizeable: false,
sortable: false,
};
};
const getDisksColumnHeader = (vDisksContainerWidth: number) => {
return (
{STORAGE_GROUPS_DISKS_COLUMN_TITLES.VDisks}
{STORAGE_GROUPS_DISKS_COLUMN_TITLES.PDisks}
);
};
const getDisksColumn = (data?: GetStorageColumnsData): StorageGroupsColumn => {
const vDisksContainerWidth = data?.isAllVDisksLayout
? getAllVDisksContainerWidth()
: VDISKS_CONTAINER_WIDTH;
const pDisksContainerWidthExpansion = data?.isAllPDisksLayout
? getAllPDisksContainerWidthExpansion()
: 0;
return {
name: STORAGE_GROUPS_COLUMNS_IDS.VDisksPDisks,
header: getDisksColumnHeader(vDisksContainerWidth),
className: b('disks-column'),
render: ({row}) => (
),
align: DataTable.LEFT,
width:
DISKS_COLUMN_WIDTH +
(vDisksContainerWidth - VDISKS_CONTAINER_WIDTH) +
pDisksContainerWidthExpansion,
resizeable: false,
sortable: false,
};
};
export function getStorageTopGroupsColumns(capacityMetricsEnabled = false): StorageGroupsColumn[] {
if (!capacityMetricsEnabled) {
return [groupIdColumn, typeColumn, erasureColumn, usageColumn, usedColumn, limitColumn];
}
return [
groupIdColumn,
typeColumn,
erasureColumn,
getVDiskSlotUsageColumn(),
getCapacityAlertColumn(),
groupSizeInUnitsColumn,
usedColumn,
limitColumn,
];
}
export const getStorageGroupsColumns: StorageColumnsGetter = (data) => {
const columns = [
groupIdColumn,
poolNameColumn,
pileNameColumn,
typeColumn,
erasureColumn,
degradedColumn,
stateColumn,
usageColumn,
diskSpaceUsageColumn,
usedColumn,
limitColumn,
usedSpaceFlagColumn,
readColumn,
writeColumn,
latencyColumn,
allocationUnitsColumn,
getVDisksColumn(data),
getDisksColumn(data),
getPDiskUsageColumn(),
getVDiskSlotUsageColumn(),
getVDiskRawUsageColumn(),
getNormalizedOccupancyColumn(),
getCapacityAlertColumn(),
];
return columns.map((column) => ({
...column,
sortable: isSortableStorageGroupsColumn(column.name),
}));
};
function GroupId({id}: {id: string | number}) {
const getStorageGroupPath = useStorageGroupPath();
return ;
}