From 491df5957f465265e0414c77d430a1a4446a8634 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 16 Jul 2025 11:17:19 +0000
Subject: [PATCH 1/8] Initial plan
From a2b355566fa8cbded5781725bf47c82d00fcfab0 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 16 Jul 2025 11:37:59 +0000
Subject: [PATCH 2/8] Implement VDisk tablets feature with tab navigation
Co-authored-by: adameat <34044711+adameat@users.noreply.github.com>
---
src/containers/VDiskPage/VDiskPage.scss | 7 +-
src/containers/VDiskPage/VDiskPage.tsx | 109 +++++++++++++++---
.../VDiskPage/VDiskTablets/VDiskTablets.scss | 9 ++
.../VDiskPage/VDiskTablets/VDiskTablets.tsx | 71 ++++++++++++
.../VDiskPage/VDiskTablets/columns.tsx | 66 +++++++++++
.../VDiskPage/VDiskTablets/index.ts | 1 +
src/containers/VDiskPage/i18n/en.json | 6 +
src/services/api/viewer.ts | 24 ++++
src/store/reducers/api.ts | 1 +
src/store/reducers/vdisk/vdisk.ts | 20 ++++
src/types/api/vdiskBlobIndex.ts | 25 ++++
11 files changed, 317 insertions(+), 22 deletions(-)
create mode 100644 src/containers/VDiskPage/VDiskTablets/VDiskTablets.scss
create mode 100644 src/containers/VDiskPage/VDiskTablets/VDiskTablets.tsx
create mode 100644 src/containers/VDiskPage/VDiskTablets/columns.tsx
create mode 100644 src/containers/VDiskPage/VDiskTablets/index.ts
create mode 100644 src/types/api/vdiskBlobIndex.ts
diff --git a/src/containers/VDiskPage/VDiskPage.scss b/src/containers/VDiskPage/VDiskPage.scss
index 5af1027dc3..eb752d819f 100644
--- a/src/containers/VDiskPage/VDiskPage.scss
+++ b/src/containers/VDiskPage/VDiskPage.scss
@@ -12,7 +12,7 @@
&__title,
&__controls,
&__info,
- &__storage-title {
+ &__tabs {
position: sticky;
left: 0;
@@ -29,8 +29,7 @@
gap: var(--g-spacing-2);
}
- &__storage-title {
- margin-bottom: 0;
- @include mixins.header-1-typography();
+ &__tablets-content {
+ margin-top: 16px;
}
}
diff --git a/src/containers/VDiskPage/VDiskPage.tsx b/src/containers/VDiskPage/VDiskPage.tsx
index c9371c001a..35155c5fa2 100644
--- a/src/containers/VDiskPage/VDiskPage.tsx
+++ b/src/containers/VDiskPage/VDiskPage.tsx
@@ -1,17 +1,20 @@
import React from 'react';
import {ArrowsOppositeToDots} from '@gravity-ui/icons';
-import {Icon} from '@gravity-ui/uikit';
+import {Icon, Tab, TabList, TabProvider} from '@gravity-ui/uikit';
import {skipToken} from '@reduxjs/toolkit/query';
import {Helmet} from 'react-helmet-async';
import {StringParam, useQueryParams} from 'use-query-params';
+import {z} from 'zod';
import {ButtonWithConfirmDialog} from '../../components/ButtonWithConfirmDialog/ButtonWithConfirmDialog';
import {EntityPageTitle} from '../../components/EntityPageTitle/EntityPageTitle';
import {ResponseError} from '../../components/Errors/ResponseError';
import {InfoViewerSkeleton} from '../../components/InfoViewerSkeleton/InfoViewerSkeleton';
+import {InternalLink} from '../../components/InternalLink/InternalLink';
import {PageMetaWithAutorefresh} from '../../components/PageMeta/PageMeta';
import {VDiskInfo} from '../../components/VDiskInfo/VDiskInfo';
+import {getVDiskPagePath} from '../../routes';
import {api} from '../../store/reducers/api';
import {useDiskPagesAvailable} from '../../store/reducers/capabilities/hooks';
import {setHeaderBreadcrumbs} from '../../store/reducers/header/header';
@@ -25,12 +28,35 @@ import {useAutoRefreshInterval, useTypedDispatch} from '../../utils/hooks';
import {useIsUserAllowedToMakeChanges} from '../../utils/hooks/useIsUserAllowedToMakeChanges';
import {PaginatedStorage} from '../Storage/PaginatedStorage';
+import {VDiskTablets} from './VDiskTablets';
import {vDiskPageKeyset} from './i18n';
import './VDiskPage.scss';
const vDiskPageCn = cn('ydb-vdisk-page');
+const VDISK_TABS_IDS = {
+ storage: 'storage',
+ tablets: 'tablets',
+} as const;
+
+const VDISK_PAGE_TABS = [
+ {
+ id: VDISK_TABS_IDS.storage,
+ get title() {
+ return vDiskPageKeyset('storage');
+ },
+ },
+ {
+ id: VDISK_TABS_IDS.tablets,
+ get title() {
+ return vDiskPageKeyset('tablets');
+ },
+ },
+];
+
+const vDiskTabSchema = z.nativeEnum(VDISK_TABS_IDS).catch(VDISK_TABS_IDS.storage);
+
export function VDiskPage() {
const dispatch = useTypedDispatch();
@@ -38,13 +64,16 @@ export function VDiskPage() {
const isUserAllowedToMakeChanges = useIsUserAllowedToMakeChanges();
const newDiskApiAvailable = useDiskPagesAvailable();
- const [{nodeId, pDiskId, vDiskSlotId, vDiskId: vDiskIdParam}] = useQueryParams({
+ const [{nodeId, pDiskId, vDiskSlotId, vDiskId: vDiskIdParam, activeTab}] = useQueryParams({
nodeId: StringParam,
pDiskId: StringParam,
vDiskSlotId: StringParam,
vDiskId: StringParam,
+ activeTab: StringParam,
});
+ const vDiskTab = vDiskTabSchema.parse(activeTab);
+
React.useEffect(() => {
dispatch(setHeaderBreadcrumbs('vDisk', {nodeId, pDiskId, vDiskSlotId}));
}, [dispatch, nodeId, pDiskId, vDiskSlotId]);
@@ -185,24 +214,67 @@ export function VDiskPage() {
return