diff --git a/src/containers/Tenant/Diagnostics/Diagnostics.tsx b/src/containers/Tenant/Diagnostics/Diagnostics.tsx index a7ea19eaa7..00d8b9cdde 100644 --- a/src/containers/Tenant/Diagnostics/Diagnostics.tsx +++ b/src/containers/Tenant/Diagnostics/Diagnostics.tsx @@ -38,6 +38,7 @@ import {Partitions} from './Partitions/Partitions'; import {TopQueries} from './TopQueries'; import {TopShards} from './TopShards'; import {TopicData} from './TopicData/TopicData'; +import i18n from './i18n'; import './Diagnostics.scss'; @@ -60,7 +61,7 @@ function Diagnostics(props: DiagnosticsProps) { const tenantName = isDatabaseEntityType(type) ? path : database; - const {controlPlane} = useTenantBaseInfo(isDatabaseEntityType(type) ? path : ''); + const {controlPlane, databaseType} = useTenantBaseInfo(isDatabaseEntityType(type) ? path : ''); const hasFeatureFlags = useFeatureFlagsAvailable(); const hasTopicData = useTopicDataAvailable(); @@ -72,6 +73,7 @@ function Diagnostics(props: DiagnosticsProps) { hasBackups: typeof uiFactory.renderBackups === 'function' && Boolean(controlPlane), hasConfigs: isViewerUser, hasAccess: uiFactory.hasAccess, + databaseType, }); let activeTab = pages.find((el) => el.id === diagnosticsTab); if (!activeTab) { @@ -176,7 +178,7 @@ function Diagnostics(props: DiagnosticsProps) { }); } default: { - return
No data...
; + return
{i18n('no-data')}
; } } }; @@ -187,10 +189,10 @@ function Diagnostics(props: DiagnosticsProps) { {pages.map(({id, title}) => { - const path = getDiagnosticsPageLink(id); + const linkPath = getDiagnosticsPageLink(id); return ( - + {title} diff --git a/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts b/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts index a27170502f..ead594afe2 100644 --- a/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts +++ b/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts @@ -5,6 +5,7 @@ import {StringParam, useQueryParams} from 'use-query-params'; import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../store/reducers/tenant/constants'; import type {TenantDiagnosticsTab} from '../../../store/reducers/tenant/types'; import {EPathSubType, EPathType} from '../../../types/api/schema'; +import type {ETenantType} from '../../../types/api/tenant'; import type {TenantQuery} from '../TenantPages'; import {TenantTabsGroups, getTenantPath} from '../TenantPages'; import {isDatabaseEntityType, isTopicEntityType} from '../utils/schema'; @@ -14,6 +15,16 @@ type Page = { title: string; }; +interface GetPagesOptions { + hasFeatureFlags?: boolean; + hasTopicData?: boolean; + isTopLevel?: boolean; + hasBackups?: boolean; + hasConfigs?: boolean; + hasAccess?: boolean; + databaseType?: ETenantType; +} + const overview = { id: TENANT_DIAGNOSTICS_TABS_IDS.overview, title: 'Info', @@ -118,6 +129,16 @@ const DATABASE_PAGES = [ backups, ]; +const SERVERLESS_DATABASE_PAGES = [ + overview, + topQueries, + topShards, + tablets, + describe, + configs, + operations, +]; + const TABLE_PAGES = [overview, schema, topShards, nodes, graph, tablets, hotKeys, describe, access]; const COLUMN_TABLE_PAGES = [overview, schema, topShards, nodes, tablets, describe, access]; @@ -168,41 +189,52 @@ const pathSubTypeToPages: Record = { [EPathSubType.EPathSubTypeEmpty]: undefined, }; -export const getPagesByType = ( - type?: EPathType, - subType?: EPathSubType, - options?: { - hasFeatureFlags?: boolean; - hasTopicData?: boolean; - isTopLevel?: boolean; - hasBackups?: boolean; - hasConfigs?: boolean; - hasAccess?: boolean; - }, -) => { +function computeInitialPages(type?: EPathType, subType?: EPathSubType) { const subTypePages = subType ? pathSubTypeToPages[subType] : undefined; const typePages = type ? pathTypeToPages[type] : undefined; - let pages = subTypePages || typePages || DIR_PAGES; + return subTypePages || typePages || DIR_PAGES; +} + +function getDatabasePages(databaseType?: ETenantType) { + return databaseType === 'Serverless' ? SERVERLESS_DATABASE_PAGES : DATABASE_PAGES; +} + +function applyFilters(pages: Page[], type?: EPathType, options: GetPagesOptions = {}) { + let result = pages; - if (isTopicEntityType(type) && !options?.hasTopicData) { - pages = pages?.filter((item) => item.id !== TENANT_DIAGNOSTICS_TABS_IDS.topicData); + if (isTopicEntityType(type) && !options.hasTopicData) { + result = result.filter((p) => p.id !== TENANT_DIAGNOSTICS_TABS_IDS.topicData); } - if (isDatabaseEntityType(type) || options?.isTopLevel) { - pages = DATABASE_PAGES; - if (!options?.hasFeatureFlags) { - pages = pages.filter((item) => item.id !== TENANT_DIAGNOSTICS_TABS_IDS.configs); - } + + const removals: TenantDiagnosticsTab[] = []; + if (!options.hasBackups) { + removals.push(TENANT_DIAGNOSTICS_TABS_IDS.backups); } - if (!options?.hasBackups) { - pages = pages.filter((item) => item.id !== TENANT_DIAGNOSTICS_TABS_IDS.backups); + if (!options.hasConfigs) { + removals.push(TENANT_DIAGNOSTICS_TABS_IDS.configs); } - if (!options?.hasConfigs) { - pages = pages.filter((item) => item.id !== TENANT_DIAGNOSTICS_TABS_IDS.configs); + if (!options.hasAccess) { + removals.push(TENANT_DIAGNOSTICS_TABS_IDS.access); } - if (!options?.hasAccess) { - pages = pages.filter((item) => item.id !== TENANT_DIAGNOSTICS_TABS_IDS.access); + + return result.filter((p) => !removals.includes(p.id)); +} + +export const getPagesByType = ( + type?: EPathType, + subType?: EPathSubType, + options?: GetPagesOptions, +) => { + const base = computeInitialPages(type, subType); + const dbContext = isDatabaseEntityType(type) || options?.isTopLevel; + const seeded = dbContext ? getDatabasePages(options?.databaseType) : base; + + let withFlags = seeded; + if (!options?.hasFeatureFlags) { + withFlags = seeded.filter((p) => p.id !== TENANT_DIAGNOSTICS_TABS_IDS.configs); } - return pages; + + return applyFilters(withFlags, type, options); }; export const useDiagnosticsPageLinkGetter = () => { diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/MetricsTabs/CommonMetricsTabs.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/MetricsTabs/CommonMetricsTabs.tsx new file mode 100644 index 0000000000..39cf5213bc --- /dev/null +++ b/src/containers/Tenant/Diagnostics/TenantOverview/MetricsTabs/CommonMetricsTabs.tsx @@ -0,0 +1,89 @@ +import {Link} from 'react-router-dom'; + +import {TENANT_METRICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants'; +import type {TenantMetricsTab} from '../../../../../store/reducers/tenant/types'; +import type {ETenantType} from '../../../../../types/api/tenant'; +import {cn} from '../../../../../utils/cn'; +import { + formatCoresLegend, + formatStorageLegend, +} from '../../../../../utils/metrics/formatMetricLegend'; +import {TabCard} from '../TabCard/TabCard'; +import i18n from '../i18n'; + +const b = cn('tenant-metrics-tabs'); + +interface CommonMetricsTabsProps { + activeTab: TenantMetricsTab; + tabLinks: Record; + cpu: {totalUsed: number; totalLimit: number}; + storage: {totalUsed: number; totalLimit: number}; + storageGroupsCount?: number; + databaseType?: ETenantType; +} + +export function CommonMetricsTabs({ + activeTab, + tabLinks, + cpu, + storage, + storageGroupsCount, + databaseType, +}: CommonMetricsTabsProps) { + const isServerless = databaseType === 'Serverless'; + + return ( + <> +
+ + + +
+
+ + + +
+ + ); +} diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/MetricsTabs/DedicatedMetricsTabs.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/MetricsTabs/DedicatedMetricsTabs.tsx new file mode 100644 index 0000000000..21e0da39bb --- /dev/null +++ b/src/containers/Tenant/Diagnostics/TenantOverview/MetricsTabs/DedicatedMetricsTabs.tsx @@ -0,0 +1,70 @@ +import {Link} from 'react-router-dom'; + +import {TENANT_METRICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants'; +import type {TenantMetricsTab} from '../../../../../store/reducers/tenant/types'; +import {cn} from '../../../../../utils/cn'; +import { + formatSpeedLegend, + formatStorageLegend, +} from '../../../../../utils/metrics/formatMetricLegend'; +import {TabCard} from '../TabCard/TabCard'; +import i18n from '../i18n'; + +const b = cn('tenant-metrics-tabs'); + +interface DedicatedMetricsTabsProps { + activeTab: TenantMetricsTab; + tabLinks: Record; + memory: {totalUsed: number; totalLimit: number}; + network: {totalUsed: number; totalLimit: number} | null; + showNetwork: boolean; +} + +export function DedicatedMetricsTabs({ + activeTab, + tabLinks, + memory, + network, + showNetwork, +}: DedicatedMetricsTabsProps) { + return ( + <> +
+ + + +
+ {showNetwork && network && ( +
+ + + +
+ )} + + ); +} diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/MetricsTabs/MetricsTabs.scss b/src/containers/Tenant/Diagnostics/TenantOverview/MetricsTabs/MetricsTabs.scss index fe6b55b43c..c174af3667 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/MetricsTabs/MetricsTabs.scss +++ b/src/containers/Tenant/Diagnostics/TenantOverview/MetricsTabs/MetricsTabs.scss @@ -224,3 +224,13 @@ padding-right: 0; } } + +.tenant-metrics-tabs_serverless { + .tenant-metrics-tabs__link-container_placeholder { + pointer-events: none; + + .tenant-tab-card__card-container { + opacity: 0; + } + } +} diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/MetricsTabs/MetricsTabs.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/MetricsTabs/MetricsTabs.tsx index 0cedc6b33d..8a780224e5 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/MetricsTabs/MetricsTabs.tsx +++ b/src/containers/Tenant/Diagnostics/TenantOverview/MetricsTabs/MetricsTabs.tsx @@ -1,5 +1,7 @@ +import {useMemo} from 'react'; + import {Flex} from '@gravity-ui/uikit'; -import {Link, useLocation} from 'react-router-dom'; +import {useLocation} from 'react-router-dom'; import {parseQuery} from '../../../../../routes'; import {TENANT_METRICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants'; @@ -9,18 +11,17 @@ import type { TenantPoolsStats, TenantStorageStats, } from '../../../../../store/reducers/tenants/utils'; +import type {ETenantType} from '../../../../../types/api/tenant'; import {cn} from '../../../../../utils/cn'; import {SHOW_NETWORK_UTILIZATION} from '../../../../../utils/constants'; -import {useSetting, useTypedSelector} from '../../../../../utils/hooks'; +import {useSetting} from '../../../../../utils/hooks'; import {calculateMetricAggregates} from '../../../../../utils/metrics'; -import { - formatCoresLegend, - formatSpeedLegend, - formatStorageLegend, -} from '../../../../../utils/metrics/formatMetricLegend'; +// no direct legend formatters needed here – handled in subcomponents import {TenantTabsGroups, getTenantPath} from '../../../TenantPages'; -import {TabCard} from '../TabCard/TabCard'; -import i18n from '../i18n'; + +import {CommonMetricsTabs} from './CommonMetricsTabs'; +import {DedicatedMetricsTabs} from './DedicatedMetricsTabs'; +import {ServerlessPlaceholderTabs} from './ServerlessPlaceholderTabs'; import './MetricsTabs.scss'; @@ -33,6 +34,8 @@ interface MetricsTabsProps { tabletStorageStats?: TenantStorageStats[]; networkStats?: TenantMetricStats[]; storageGroupsCount?: number; + databaseType?: ETenantType; + activeTab: TenantMetricsTab; } export function MetricsTabs({ @@ -42,9 +45,10 @@ export function MetricsTabs({ tabletStorageStats, networkStats, storageGroupsCount, + databaseType, + activeTab, }: MetricsTabsProps) { const location = useLocation(); - const {metricsTab} = useTypedSelector((state) => state.tenant); const queryParams = parseQuery(location); const tabLinks: Record = { @@ -67,93 +71,60 @@ export function MetricsTabs({ }; // Use only pools that directly indicate resources available to perform user queries - const cpuPools = (poolsCpuStats || []).filter( - (pool) => !(pool.name === 'Batch' || pool.name === 'IO'), + const cpuPools = useMemo( + () => + (poolsCpuStats || []).filter((pool) => !(pool.name === 'Batch' || pool.name === 'IO')), + [poolsCpuStats], ); - const cpuMetrics = calculateMetricAggregates(cpuPools); + const cpuMetrics = useMemo(() => calculateMetricAggregates(cpuPools), [cpuPools]); // Calculate storage metrics using utility - const storageStats = tabletStorageStats || blobStorageStats || []; - const storageMetrics = calculateMetricAggregates(storageStats); + const storageStats = useMemo( + () => tabletStorageStats || blobStorageStats || [], + [tabletStorageStats, blobStorageStats], + ); + const storageMetrics = useMemo(() => calculateMetricAggregates(storageStats), [storageStats]); // Calculate memory metrics using utility - const memoryMetrics = calculateMetricAggregates(memoryStats); + const memoryMetrics = useMemo(() => calculateMetricAggregates(memoryStats), [memoryStats]); // Calculate network metrics using utility const [showNetworkUtilization] = useSetting(SHOW_NETWORK_UTILIZATION); - const networkMetrics = networkStats ? calculateMetricAggregates(networkStats) : null; + const networkMetrics = useMemo( + () => (networkStats ? calculateMetricAggregates(networkStats) : null), + [networkStats], + ); + + // card variant is handled within subcomponents + + const isServerless = databaseType === 'Serverless'; return ( - -
- - - -
-
- - - -
-
- - - -
- {showNetworkUtilization && networkStats && networkMetrics && ( -
- - - -
+ + + {isServerless ? ( + + ) : ( + )} ); diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/MetricsTabs/ServerlessPlaceholderTabs.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/MetricsTabs/ServerlessPlaceholderTabs.tsx new file mode 100644 index 0000000000..6eb3428e3c --- /dev/null +++ b/src/containers/Tenant/Diagnostics/TenantOverview/MetricsTabs/ServerlessPlaceholderTabs.tsx @@ -0,0 +1,32 @@ +import React from 'react'; + +import {cn} from '../../../../../utils/cn'; +import {NON_BREAKING_SPACE} from '../../../../../utils/constants'; +import {TabCard} from '../TabCard/TabCard'; + +import './MetricsTabs.scss'; + +const b = cn('tenant-metrics-tabs'); + +interface ServerlessPlaceholderTabsProps { + count?: number; +} + +export const ServerlessPlaceholderTabs: React.FC = React.memo( + ({count = 2}) => { + const items = React.useMemo(() => Array.from({length: count}, (_, i) => i), [count]); + + return items.map((idx) => ( +
+
+ +
+
+ )); + }, +); diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TabCard/TabCard.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TabCard/TabCard.tsx index 6c5f138bc2..ee30d0f347 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/TabCard/TabCard.tsx +++ b/src/containers/Tenant/Diagnostics/TenantOverview/TabCard/TabCard.tsx @@ -1,29 +1,41 @@ +import React, {useMemo} from 'react'; + import {Card, Flex} from '@gravity-ui/uikit'; import {DoughnutMetrics} from '../../../../../components/DoughnutMetrics/DoughnutMetrics'; import {getDiagramValues} from '../../../../../containers/Cluster/ClusterOverview/utils'; +import type {ETenantType} from '../../../../../types/api/tenant'; import {cn} from '../../../../../utils/cn'; import './TabCard.scss'; const b = cn('tenant-tab-card'); -interface TabCardProps { +interface TabCardBaseProps { text: string; - value: number; - limit: number; active?: boolean; helpText?: string; + subtitle?: string; +} + +interface TabCardDefaultProps extends TabCardBaseProps { + databaseType?: Exclude; + value: number; + limit: number; legendFormatter: (params: {value: number; capacity: number}) => string; } -export function TabCard({text, value, limit, active, helpText, legendFormatter}: TabCardProps) { - const {status, percents, legend, fill} = getDiagramValues({ - value, - capacity: limit, - legendFormatter, - }); +interface TabCardServerlessProps extends TabCardBaseProps { + databaseType: 'Serverless'; +} + +type TabCardProps = TabCardDefaultProps | TabCardServerlessProps; +function isServerlessProps(props: TabCardProps): props is TabCardServerlessProps { + return props.databaseType === 'Serverless'; +} + +function TabCardContainer({active, children}: {active?: boolean; children: React.ReactNode}) { return (
- - - - {percents} - - -
- - {legend} - - - {text} - -
-
+ {children}
); } + +const TabCardDefaultContent = React.memo(function TabCardDefaultContent({ + text, + value, + limit, + helpText, + legendFormatter, +}: { + text: string; + value: number; + limit: number; + helpText?: string; + legendFormatter: (params: {value: number; capacity: number}) => string; +}) { + const diagram = useMemo( + () => getDiagramValues({value, capacity: limit, legendFormatter}), + [value, limit, legendFormatter], + ); + + const {status, percents, legend, fill} = diagram; + + return ( + + + {percents} + +
+ {legend} + + {text} + +
+
+ ); +}); + +const TabCardServerlessContent = React.memo(function TabCardServerlessContent({ + text, + helpText, + subtitle, +}: { + text: string; + helpText?: string; + subtitle?: string; +}) { + return ( +
+ + {text} + + {subtitle ? ( + + {subtitle} + + ) : null} +
+ ); +}); + +export function TabCard(props: TabCardProps) { + const {active} = props; + + return ( + + {isServerlessProps(props) ? ( + + ) : ( + + )} + + ); +} diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TenantCpu.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TenantCpu.tsx index b04026f366..7aeba979dd 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TenantCpu.tsx +++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TenantCpu.tsx @@ -3,6 +3,7 @@ import {Flex} from '@gravity-ui/uikit'; import {setTopQueriesFilters} from '../../../../../store/reducers/executeTopQueries/executeTopQueries'; import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants'; import type {AdditionalNodesProps} from '../../../../../types/additionalProps'; +import type {ETenantType} from '../../../../../types/api/tenant'; import {useTypedDispatch} from '../../../../../utils/hooks'; import {useDiagnosticsPageLinkGetter} from '../../../Diagnostics/DiagnosticsPages'; import {StatsWrapper} from '../StatsWrapper/StatsWrapper'; @@ -18,9 +19,10 @@ import {cpuDashboardConfig} from './cpuDashboardConfig'; interface TenantCpuProps { tenantName: string; additionalNodesProps?: AdditionalNodesProps; + databaseType?: ETenantType; } -export function TenantCpu({tenantName, additionalNodesProps}: TenantCpuProps) { +export function TenantCpu({tenantName, additionalNodesProps, databaseType}: TenantCpuProps) { const dispatch = useTypedDispatch(); const getDiagnosticsPageLink = useDiagnosticsPageLinkGetter(); @@ -28,21 +30,33 @@ export function TenantCpu({tenantName, additionalNodesProps}: TenantCpuProps) { const topShardsLink = getDiagnosticsPageLink(TENANT_DIAGNOSTICS_TABS_IDS.topShards); const topQueriesLink = getDiagnosticsPageLink(TENANT_DIAGNOSTICS_TABS_IDS.topQueries); + const isServerless = databaseType === 'Serverless'; + return ( - - - - - - - + {!isServerless && ( + <> + + + + + + + + + )} diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss b/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss index 1256fafc68..90512455bd 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss +++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss @@ -24,6 +24,10 @@ line-height: 24px; } + &__serverless-tag-label { + margin-left: var(--g-spacing-1); + } + &__info { position: sticky; left: 0; diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx index 1638a3033d..9f70bb2da6 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx +++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx @@ -1,4 +1,4 @@ -import {Button, Flex, Icon} from '@gravity-ui/uikit'; +import {Button, Flex, HelpMark, Icon, Label} from '@gravity-ui/uikit'; import {EntityStatus} from '../../../../components/EntityStatus/EntityStatus'; import {LoaderWrapper} from '../../../../components/LoaderWrapper/LoaderWrapper'; @@ -21,6 +21,7 @@ import {TenantCpu} from './TenantCpu/TenantCpu'; import {TenantMemory} from './TenantMemory/TenantMemory'; import {TenantNetwork} from './TenantNetwork/TenantNetwork'; import {TenantStorage} from './TenantStorage/TenantStorage'; +import i18n from './i18n'; import {b} from './utils'; import './TenantOverview.scss'; @@ -48,6 +49,13 @@ export function TenantOverview({ ); const tenantLoading = isFetching && tenant === undefined; const {Name, Type, Overall} = tenant || {}; + const isServerless = Type === 'Serverless'; + const activeMetricsTab = + isServerless && + metricsTab !== TENANT_METRICS_TABS_IDS.cpu && + metricsTab !== TENANT_METRICS_TABS_IDS.storage + ? TENANT_METRICS_TABS_IDS.cpu + : metricsTab; const tenantType = mapDatabaseTypeToDBName(Type); // FIXME: remove after correct data is added to tenantInfo @@ -111,22 +119,41 @@ export function TenantOverview({ hasClipboardButton={Boolean(tenant)} clipboardButtonAlwaysVisible /> + {isServerless ? ( +
+ +
+ ) : null}
); }; const renderTabContent = () => { - switch (metricsTab) { + switch (activeMetricsTab) { case TENANT_METRICS_TABS_IDS.cpu: { return ( ); } case TENANT_METRICS_TABS_IDS.storage: { - return ; + return ( + + ); } case TENANT_METRICS_TABS_IDS.memory: { return ( @@ -146,6 +173,9 @@ export function TenantOverview({ /> ); } + default: { + return null; + } } }; @@ -186,6 +216,8 @@ export function TenantOverview({ ? Number(tenantData.StorageGroups) : undefined } + databaseType={Type} + activeTab={activeMetricsTab} />
diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TenantStorage.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TenantStorage.tsx index 4df7995609..b057707dae 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TenantStorage.tsx +++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TenantStorage.tsx @@ -4,6 +4,7 @@ import {InfoViewer} from '../../../../../components/InfoViewer/InfoViewer'; import {LabelWithPopover} from '../../../../../components/LabelWithPopover'; import {ProgressWrapper} from '../../../../../components/ProgressWrapper'; import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants'; +import type {ETenantType} from '../../../../../types/api/tenant'; import {formatStorageValues} from '../../../../../utils/dataFormatters/dataFormatters'; import {useSearchQuery} from '../../../../../utils/hooks'; import {TenantTabsGroups, getTenantPath} from '../../../TenantPages'; @@ -25,9 +26,10 @@ export interface TenantStorageMetrics { interface TenantStorageProps { tenantName: string; metrics: TenantStorageMetrics; + databaseType?: ETenantType; } -export function TenantStorage({tenantName, metrics}: TenantStorageProps) { +export function TenantStorage({tenantName, metrics, databaseType}: TenantStorageProps) { const {blobStorageUsed, tabletStorageUsed, blobStorageLimit, tabletStorageLimit} = metrics; const query = useSearchQuery(); @@ -66,6 +68,22 @@ export function TenantStorage({tenantName, metrics}: TenantStorageProps) { }, ]; + if (databaseType === 'Serverless') { + return ( + + + + + + ); + } + return ( diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json b/src/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json index 44fe1650f5..9ec4807cc0 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json +++ b/src/containers/Tenant/Diagnostics/TenantOverview/i18n/en.json @@ -36,6 +36,10 @@ "title_memory-details": "Memory Details", "field_memory-usage": "Memory usage", "context_capacity-usage": "{{value}} of {{capacity}}", - "network-stats-unavailable.title": "Network Statistics Unavailable", - "network-stats-unavailable.description": "Network ping and clock skew statistics require a newer backend version. Please upgrade your YDB installation to access these features." + "title_network-stats-unavailable": "Network Statistics Unavailable", + "context_network-stats-unavailable": "Network ping and clock skew statistics require a newer backend version. Please upgrade your YDB installation to access these features.", + "value_serverless": "Serverless", + "context_serverless-tooltip": "Some metrics are hidden in Serverless mode — resources scale automatically based on workload", + "context_serverless-autoscaled": "Auto-Scaled Resources", + "context_serverless-storage-subtitle": "{{legend}} | {{groups}} groups" } diff --git a/src/containers/Tenant/Diagnostics/i18n/en.json b/src/containers/Tenant/Diagnostics/i18n/en.json new file mode 100644 index 0000000000..839fe16e58 --- /dev/null +++ b/src/containers/Tenant/Diagnostics/i18n/en.json @@ -0,0 +1,3 @@ +{ + "no-data": "No data" +} diff --git a/src/containers/Tenant/Diagnostics/i18n/index.ts b/src/containers/Tenant/Diagnostics/i18n/index.ts new file mode 100644 index 0000000000..dfddf69419 --- /dev/null +++ b/src/containers/Tenant/Diagnostics/i18n/index.ts @@ -0,0 +1,7 @@ +import {registerKeysets} from '../../../../utils/i18n'; + +import en from './en.json'; + +const COMPONENT = 'ydb-diagnostics'; + +export default registerKeysets(COMPONENT, {en}); diff --git a/src/store/reducers/tenant/tenant.ts b/src/store/reducers/tenant/tenant.ts index 2da7ace466..abf18a72fe 100644 --- a/src/store/reducers/tenant/tenant.ts +++ b/src/store/reducers/tenant/tenant.ts @@ -135,10 +135,12 @@ export function useTenantBaseInfo(path: string) { {skip: !path}, ); - const {ControlPlane, Name} = currentData || {}; + const {ControlPlane, Name, Id, Type} = currentData || {}; return { controlPlane: ControlPlane, name: Name, + id: Id, + databaseType: Type, }; } diff --git a/src/utils/constants.ts b/src/utils/constants.ts index c4ae821be0..ab3a8dec61 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -100,6 +100,7 @@ export const SECTION_IDS = { export const TENANT_OVERVIEW_TABLES_LIMIT = 3; export const EMPTY_DATA_PLACEHOLDER = '—'; +export const NON_BREAKING_SPACE = '\u00A0'; export const QUERY_TECHNICAL_MARK = '/*UI-QUERY-EXCLUDE*/';