11import React from 'react' ;
22
3+ import type { TabsItemProps } from '@gravity-ui/uikit' ;
34import { Tabs } from '@gravity-ui/uikit' ;
5+ import { skipToken } from '@reduxjs/toolkit/query' ;
46import { Helmet } from 'react-helmet-async' ;
57import { useRouteMatch } from 'react-router-dom' ;
68import { useQueryParams } from 'use-query-params' ;
@@ -18,6 +20,7 @@ import {
1820} from '../../store/reducers/capabilities/hooks' ;
1921import { setHeaderBreadcrumbs } from '../../store/reducers/header/header' ;
2022import { nodeApi } from '../../store/reducers/node/node' ;
23+ import type { PreparedNode } from '../../store/reducers/node/types' ;
2124import { cn } from '../../utils/cn' ;
2225import { useAutoRefreshInterval , useTypedDispatch } from '../../utils/hooks' ;
2326import { PaginatedStorage } from '../Storage/PaginatedStorage' ;
@@ -41,20 +44,21 @@ export function Node() {
4144
4245 const match = useRouteMatch < { id : string ; activeTab : string } > ( routes . node ) ;
4346
44- // NodeId is always defined here because the page is wrapped with specific route Router
45- const nodeId = match ?. params . id as string ;
47+ const nodeId = match ?. params . id ;
4648 const activeTabIdFromQuery = match ?. params . activeTab ;
4749
4850 const [ { database : tenantNameFromQuery } ] = useQueryParams ( nodePageQueryParams ) ;
4951
5052 const activeTabId = nodePageTabSchema . parse ( activeTabIdFromQuery ) ;
5153
5254 const [ autoRefreshInterval ] = useAutoRefreshInterval ( ) ;
55+
56+ const params = nodeId ? { nodeId} : skipToken ;
5357 const {
5458 currentData : node ,
5559 isLoading,
5660 error,
57- } = nodeApi . useGetNodeInfoQuery ( { nodeId } , { pollingInterval : autoRefreshInterval } ) ;
61+ } = nodeApi . useGetNodeInfoQuery ( params , { pollingInterval : autoRefreshInterval } ) ;
5862
5963 const capabilitiesLoaded = useCapabilitiesLoaded ( ) ;
6064 const isDiskPagesAvailable = useDiskPagesAvailable ( ) ;
@@ -92,57 +96,113 @@ export function Node() {
9296 }
9397 } , [ dispatch , tenantName , nodeId , isLoading , isStorageNode ] ) ;
9498
95- const renderHelmet = ( ) => {
96- const host = node ?. Host ? node . Host : i18n ( 'node' ) ;
97- return (
98- < Helmet
99- titleTemplate = { `%s — ${ host } — YDB Monitoring` }
100- defaultTitle = { `${ host } — YDB Monitoring` }
101- >
102- < title > { activeTab . title } </ title >
103- </ Helmet >
104- ) ;
105- } ;
99+ return (
100+ < div className = { b ( null ) } ref = { container } >
101+ { < NodePageHelmet node = { node } activeTabTitle = { activeTab . title } /> }
102+ { < NodePageMeta node = { node } loading = { pageLoading } /> }
103+ { < NodePageTitle node = { node } /> }
104+ { error ? < ResponseError error = { error } className = { b ( 'error' ) } /> : null }
105+ { < NodePageInfo node = { node } loading = { pageLoading } /> }
106+ { nodeId ? (
107+ < NodePageContent
108+ nodeId = { nodeId }
109+ tenantName = { tenantName }
110+ activeTabId = { activeTab . id }
111+ tabs = { nodeTabs }
112+ parentContainer = { container }
113+ />
114+ ) : null }
115+ </ div >
116+ ) ;
117+ }
106118
107- const renderPageMeta = ( ) => {
108- const hostItem = node ?. Host ? `${ i18n ( 'fqdn' ) } : ${ node . Host } ` : undefined ;
109- const dcItem = node ?. DC ? `${ i18n ( 'dc' ) } : ${ node . DC } ` : undefined ;
119+ interface NodePageHelmetProps {
120+ node ?: PreparedNode ;
121+ activeTabTitle ?: string ;
122+ }
110123
111- return (
112- < PageMetaWithAutorefresh
113- loading = { pageLoading }
114- items = { [ hostItem , dcItem ] }
115- className = { b ( 'meta' ) }
116- />
117- ) ;
118- } ;
124+ function NodePageHelmet ( { node, activeTabTitle} : NodePageHelmetProps ) {
125+ const host = node ?. Host ? node . Host : i18n ( 'node' ) ;
126+ return (
127+ < Helmet
128+ titleTemplate = { `%s — ${ host } — YDB Monitoring` }
129+ defaultTitle = { `${ host } — YDB Monitoring` }
130+ >
131+ < title > { activeTabTitle } </ title >
132+ </ Helmet >
133+ ) ;
134+ }
119135
120- const renderPageTitle = ( ) => {
121- return (
122- < EntityPageTitle
123- entityName = { i18n ( 'node' ) }
124- status = { node ?. SystemState }
125- id = { node ?. NodeId }
126- className = { b ( 'title' ) }
127- />
128- ) ;
129- } ;
136+ interface NodePageMetaProps {
137+ node ?: PreparedNode ;
138+ loading ?: boolean ;
139+ }
130140
131- const renderInfo = ( ) => {
132- if ( pageLoading ) {
133- return < InfoViewerSkeleton className = { b ( 'info' ) } rows = { 10 } /> ;
134- }
141+ function NodePageMeta ( { node, loading} : NodePageMetaProps ) {
142+ const hostItem = node ?. Host ? `${ i18n ( 'fqdn' ) } : ${ node . Host } ` : undefined ;
143+ const dcItem = node ?. DC ? `${ i18n ( 'dc' ) } : ${ node . DC } ` : undefined ;
135144
136- return < FullNodeViewer node = { node } className = { b ( 'info' ) } /> ;
137- } ;
145+ return (
146+ < PageMetaWithAutorefresh
147+ loading = { loading }
148+ items = { [ hostItem , dcItem ] }
149+ className = { b ( 'meta' ) }
150+ />
151+ ) ;
152+ }
153+
154+ interface NodePageTitleProps {
155+ node ?: PreparedNode ;
156+ }
157+
158+ function NodePageTitle ( { node} : NodePageTitleProps ) {
159+ return (
160+ < EntityPageTitle
161+ entityName = { i18n ( 'node' ) }
162+ status = { node ?. SystemState }
163+ id = { node ?. NodeId }
164+ className = { b ( 'title' ) }
165+ />
166+ ) ;
167+ }
168+
169+ interface NodePageInfoProps {
170+ node ?: PreparedNode ;
171+ loading ?: boolean ;
172+ }
173+
174+ function NodePageInfo ( { node, loading} : NodePageInfoProps ) {
175+ if ( loading ) {
176+ return < InfoViewerSkeleton className = { b ( 'info' ) } rows = { 10 } /> ;
177+ }
138178
179+ return < FullNodeViewer node = { node } className = { b ( 'info' ) } /> ;
180+ }
181+
182+ interface NodePageContentProps {
183+ nodeId : string ;
184+ tenantName ?: string ;
185+
186+ activeTabId : NodeTab ;
187+ tabs : TabsItemProps [ ] ;
188+
189+ parentContainer : React . RefObject < HTMLDivElement > ;
190+ }
191+
192+ function NodePageContent ( {
193+ nodeId,
194+ tenantName,
195+ activeTabId,
196+ tabs,
197+ parentContainer,
198+ } : NodePageContentProps ) {
139199 const renderTabs = ( ) => {
140200 return (
141201 < div className = { b ( 'tabs' ) } >
142202 < Tabs
143203 size = "l"
144- items = { nodeTabs }
145- activeTab = { activeTab . id }
204+ items = { tabs }
205+ activeTab = { activeTabId }
146206 wrapTo = { ( { id} , tabNode ) => {
147207 const path = getDefaultNodePath (
148208 nodeId ,
@@ -161,14 +221,14 @@ export function Node() {
161221 } ;
162222
163223 const renderTabContent = ( ) => {
164- switch ( activeTab . id ) {
224+ switch ( activeTabId ) {
165225 case 'storage' : {
166226 return (
167227 < PaginatedStorage
168228 nodeId = { nodeId }
169- parentRef = { container }
229+ parentRef = { parentContainer }
170230 viewContext = { {
171- nodeId : nodeId ?. toString ( ) ,
231+ nodeId : nodeId ,
172232 } }
173233 />
174234 ) ;
@@ -186,23 +246,10 @@ export function Node() {
186246 }
187247 } ;
188248
189- const renderError = ( ) => {
190- if ( ! error ) {
191- return null ;
192- }
193-
194- return < ResponseError error = { error } className = { b ( 'error' ) } /> ;
195- } ;
196-
197249 return (
198- < div className = { b ( null ) } ref = { container } >
199- { renderHelmet ( ) }
200- { renderPageMeta ( ) }
201- { renderPageTitle ( ) }
202- { renderError ( ) }
203- { renderInfo ( ) }
250+ < React . Fragment >
204251 { renderTabs ( ) }
205252 { renderTabContent ( ) }
206- </ div >
253+ </ React . Fragment >
207254 ) ;
208255}
0 commit comments