import React from 'react'; import {Flex, Tab, TabList, TabProvider} from '@gravity-ui/uikit'; import {skipToken} from '@reduxjs/toolkit/query'; import {Helmet} from 'react-helmet-async'; import {useParams} from 'react-router-dom'; import {useQueryParams} from 'use-query-params'; import {z} from 'zod'; import {StickyAutoRefreshControl} from '../../components/AutoRefreshControl/StickyAutoRefreshControl'; import {EmptyStateWrapper} from '../../components/EmptyState'; import {EntityPageTitle} from '../../components/EntityPageTitle/EntityPageTitle'; import {ResponseError} from '../../components/Errors/ResponseError'; import {InternalLink} from '../../components/InternalLink'; import {LoaderWrapper} from '../../components/LoaderWrapper/LoaderWrapper'; import {PageMeta} from '../../components/PageMeta/PageMeta'; import {tabletPageQueryParams, useTabletPagePath} from '../../routes'; import {useTabletDevUiSecurePath} from '../../store/reducers/capabilities/hooks'; import {setHeaderBreadcrumbs} from '../../store/reducers/header/header'; import {tabletApi} from '../../store/reducers/tablet'; import {EFlag} from '../../types/api/enums'; import type {TTabletStateInfo} from '../../types/api/tablet'; import type {ITabletPreparedHistoryItem} from '../../types/store/tablet'; import {cn} from '../../utils/cn'; import {CLUSTER_DEFAULT_TITLE} from '../../utils/constants'; import {useAutoRefreshInterval, useTypedDispatch} from '../../utils/hooks'; import {useIsUserAllowedToMakeChanges} from '../../utils/hooks/useIsUserAllowedToMakeChanges'; import {TabletControls} from './components/TabletControls'; import {TabletInfo} from './components/TabletInfo'; import {TabletStorageInfo} from './components/TabletStorageInfo/TabletStorageInfo'; import {TabletTable} from './components/TabletTable'; import i18n from './i18n'; import {getTabletObjectKind, hasHive} from './utils'; import './Tablet.scss'; const b = cn('ydb-tablet-page'); const TABLET_TABS_IDS = { history: 'history', channels: 'channels', } as const; const TABLET_PAGE_TABS = [ { id: TABLET_TABS_IDS.history, get title() { return i18n('label_tablet-history'); }, }, { id: TABLET_TABS_IDS.channels, get title() { return i18n('label_tablet-channels'); }, isAdvanced: true, }, ]; const tabletTabSchema = z.nativeEnum(TABLET_TABS_IDS).catch(TABLET_TABS_IDS.history); export function Tablet() { const dispatch = useTypedDispatch(); const {id} = useParams<{id: string}>(); const [{database: queryDatabase, clusterName: queryClusterName, followerId: queryFollowerId}] = useQueryParams(tabletPageQueryParams); const database = queryDatabase?.toString(); const [autoRefreshInterval] = useAutoRefreshInterval(); const {currentData, isFetching, error} = tabletApi.useGetTabletQuery( {id, database, followerId: queryFollowerId ?? undefined}, {pollingInterval: autoRefreshInterval}, ); const loading = isFetching && currentData === undefined; const {data: tablet = {}, history = []} = currentData || {}; const {currentData: databaseFullPath} = tabletApi.useGetTabletDescribeQuery( tablet.TenantId ? {tenantId: tablet.TenantId, database} : skipToken, ); const tabletType = tablet.Type; React.useEffect(() => { dispatch( setHeaderBreadcrumbs('tablet', { database, tabletId: id, tabletType, databaseName: databaseFullPath, }), ); }, [dispatch, database, id, tabletType, databaseFullPath]); const {Leader} = tablet; const metaItems: string[] = []; if (databaseFullPath) { metaItems.push(`${i18n('tablet.meta-database')}: ${databaseFullPath}`); } // Add "Tablet" instead of tablet type to metadata metaItems.push(i18n('tablet.header')); if (Leader === false) { metaItems.push(i18n('tablet.meta-follower').toUpperCase()); } return (
{`${id} — ${i18n('tablet.header')} — ${ databaseFullPath || queryClusterName || CLUSTER_DEFAULT_TITLE }`} {error ? : null} {currentData ? ( ) : null}
); } function TabletContent({ id, tablet, history, database, }: { id: string; tablet: TTabletStateInfo; history: ITabletPreparedHistoryItem[]; database?: string; }) { const isUserAllowedToMakeChanges = useIsUserAllowedToMakeChanges(); const isEmpty = !Object.keys(tablet).length; const {Overall, HiveId, FollowerId, TabletId, Type} = tablet; const objectKind = getTabletObjectKind(Type); const objectQueryArgs = isUserAllowedToMakeChanges && objectKind && TabletId && hasHive(HiveId) && database ? {id: TabletId, hiveId: HiveId, database} : skipToken; const {currentData: objectPath} = tabletApi.useGetTabletObjectPathQuery(objectQueryArgs); const visibleObjectPath = isUserAllowedToMakeChanges ? objectPath || undefined : undefined; const tabletName = `${id}${FollowerId ? `.${FollowerId}` : ''}`; return ( ); } function TabletTabs({ id, hiveId, history, }: { id: string; hiveId?: string; history: ITabletPreparedHistoryItem[]; }) { const getTabletPagePath = useTabletPagePath(); const [{activeTab, ...restParams}, setParams] = useQueryParams(tabletPageQueryParams); const isUserAllowedToMakeChanges = useIsUserAllowedToMakeChanges(); const noAdvancedInfo = !isUserAllowedToMakeChanges || !hasHive(hiveId); let tabletTab = tabletTabSchema.parse(activeTab); if (noAdvancedInfo && TABLET_PAGE_TABS.find((tab) => tab.id === tabletTab)?.isAdvanced) { tabletTab = TABLET_TABS_IDS.history; } React.useEffect(() => { if (activeTab !== tabletTab) { setParams({activeTab: tabletTab}, 'replaceIn'); } }, [activeTab, tabletTab, setParams]); return ( {/* block wrapper fror tabs to preserve height */}
{TABLET_PAGE_TABS.filter(({isAdvanced}) => isAdvanced ? !noAdvancedInfo : true, ).map(({id: tabId, title}) => { const path = getTabletPagePath(id, {...restParams, activeTab: tabId}); return ( {title} ); })}
{tabletTab === 'history' ? : null} {tabletTab === 'channels' && !noAdvancedInfo ? ( ) : null}
); } function Channels({id, hiveId}: {id: string; hiveId: string}) { const [autoRefreshInterval] = useAutoRefreshInterval(); const useSecurePath = useTabletDevUiSecurePath(); const {currentData, error, isFetching} = tabletApi.useGetAdvancedTableInfoQuery( {id, hiveId, useSecurePath}, { pollingInterval: autoRefreshInterval, }, ); const loading = isFetching && currentData === undefined; return ( {error ? : null} {currentData ? : null} ); }