@@ -7,6 +7,7 @@ import IconButton from "@mui/material/IconButton";
77import Tooltip from "@mui/material/Tooltip" ;
88import { health , refreshHealth } from "api/queries/debug" ;
99import type { HealthSeverity } from "api/typesGenerated" ;
10+ import { ErrorAlert } from "components/Alert/ErrorAlert" ;
1011import { Loader } from "components/Loader/Loader" ;
1112import { type ClassName , useClassName } from "hooks/useClassName" ;
1213import kebabCase from "lodash/fp/kebabCase" ;
@@ -22,7 +23,11 @@ import { HealthIcon } from "./Content";
2223export const HealthLayout : FC = ( ) => {
2324 const theme = useTheme ( ) ;
2425 const queryClient = useQueryClient ( ) ;
25- const { data : healthStatus } = useQuery ( {
26+ const {
27+ data : healthStatus ,
28+ isLoading,
29+ error,
30+ } = useQuery ( {
2631 ...health ( ) ,
2732 refetchInterval : 30_000 ,
2833 } ) ;
@@ -42,161 +47,171 @@ export const HealthLayout: FC = () => {
4247 const link = useClassName ( classNames . link , [ ] ) ;
4348 const activeLink = useClassName ( classNames . activeLink , [ ] ) ;
4449
50+ if ( isLoading || ! healthStatus ) {
51+ return (
52+ < div className = "p-6" >
53+ < Loader />
54+ </ div >
55+ ) ;
56+ }
57+
58+ if ( error ) {
59+ return (
60+ < div className = "p-6" >
61+ < ErrorAlert error = { error } />
62+ </ div >
63+ ) ;
64+ }
65+
4566 return (
4667 < >
4768 < Helmet >
4869 < title > { pageTitle ( "Health" ) } </ title >
4970 </ Helmet >
5071
51- { healthStatus ? (
52- < DashboardFullPage >
72+ < DashboardFullPage >
73+ < div
74+ css = { {
75+ display : "flex" ,
76+ flexBasis : 0 ,
77+ flex : 1 ,
78+ overflow : "hidden" ,
79+ } }
80+ >
5381 < div
5482 css = { {
55- display : "flex" ,
56- flexBasis : 0 ,
57- flex : 1 ,
58- overflow : "hidden" ,
83+ width : 256 ,
84+ flexShrink : 0 ,
85+ borderRight : `1px solid ${ theme . palette . divider } ` ,
86+ fontSize : 14 ,
5987 } }
6088 >
6189 < div
6290 css = { {
63- width : 256 ,
64- flexShrink : 0 ,
65- borderRight : `1px solid ${ theme . palette . divider } ` ,
66- fontSize : 14 ,
91+ padding : 24 ,
92+ display : "flex" ,
93+ flexDirection : "column" ,
94+ gap : 16 ,
6795 } }
6896 >
69- < div
70- css = { {
71- padding : 24 ,
72- display : "flex" ,
73- flexDirection : "column" ,
74- gap : 16 ,
75- } }
76- >
77- < div >
78- < div
79- css = { {
80- display : "flex" ,
81- alignItems : "center" ,
82- justifyContent : "space-between" ,
83- } }
84- >
85- < HealthIcon size = { 32 } severity = { healthStatus . severity } />
86-
87- < Tooltip title = "Refresh health checks" >
88- < IconButton
89- size = "small"
90- disabled = { isRefreshing }
91- data-testid = "healthcheck-refresh-button"
92- onClick = { ( ) => {
93- forceRefresh ( ) ;
94- } }
95- >
96- { isRefreshing ? (
97- < CircularProgress size = { 16 } />
98- ) : (
99- < ReplayIcon css = { { width : 20 , height : 20 } } />
100- ) }
101- </ IconButton >
102- </ Tooltip >
103- </ div >
104- < div css = { { fontWeight : 500 , marginTop : 16 } } >
105- { healthStatus . healthy ? "Healthy" : "Unhealthy" }
106- </ div >
107- < div
108- css = { {
109- color : theme . palette . text . secondary ,
110- lineHeight : "150%" ,
111- } }
112- >
113- { healthStatus . healthy
114- ? Object . keys ( visibleSections ) . some ( ( key ) => {
115- const section =
116- healthStatus [ key as keyof typeof visibleSections ] ;
117- return (
118- section . warnings && section . warnings . length > 0
119- ) ;
120- } )
121- ? "All systems operational, but performance might be degraded"
122- : "All systems operational"
123- : "Some issues have been detected" }
124- </ div >
125- </ div >
97+ < div >
98+ < div
99+ css = { {
100+ display : "flex" ,
101+ alignItems : "center" ,
102+ justifyContent : "space-between" ,
103+ } }
104+ >
105+ < HealthIcon size = { 32 } severity = { healthStatus . severity } />
126106
127- < div css = { { display : "flex" , flexDirection : "column" } } >
128- < span css = { { fontWeight : 500 } } > Last check</ span >
129- < span
130- data-chromatic = "ignore"
131- css = { {
132- color : theme . palette . text . secondary ,
133- lineHeight : "150%" ,
134- } }
135- >
136- { createDayString ( healthStatus . time ) }
137- </ span >
107+ < Tooltip title = "Refresh health checks" >
108+ < IconButton
109+ size = "small"
110+ disabled = { isRefreshing }
111+ data-testid = "healthcheck-refresh-button"
112+ onClick = { ( ) => {
113+ forceRefresh ( ) ;
114+ } }
115+ >
116+ { isRefreshing ? (
117+ < CircularProgress size = { 16 } />
118+ ) : (
119+ < ReplayIcon css = { { width : 20 , height : 20 } } />
120+ ) }
121+ </ IconButton >
122+ </ Tooltip >
138123 </ div >
139-
140- < div css = { { display : "flex" , flexDirection : "column" } } >
141- < span css = { { fontWeight : 500 } } > Version</ span >
142- < span
143- data-chromatic = "ignore"
144- css = { {
145- color : theme . palette . text . secondary ,
146- lineHeight : "150%" ,
147- } }
148- >
149- { healthStatus . coder_version }
150- </ span >
124+ < div css = { { fontWeight : 500 , marginTop : 16 } } >
125+ { healthStatus . healthy ? "Healthy" : "Unhealthy" }
126+ </ div >
127+ < div
128+ css = { {
129+ color : theme . palette . text . secondary ,
130+ lineHeight : "150%" ,
131+ } }
132+ >
133+ { healthStatus . healthy
134+ ? Object . keys ( visibleSections ) . some ( ( key ) => {
135+ const section =
136+ healthStatus [ key as keyof typeof visibleSections ] ;
137+ return section . warnings && section . warnings . length > 0 ;
138+ } )
139+ ? "All systems operational, but performance might be degraded"
140+ : "All systems operational"
141+ : "Some issues have been detected" }
151142 </ div >
152143 </ div >
153144
154- < nav css = { { display : "flex" , flexDirection : "column" , gap : 1 } } >
155- { Object . entries ( visibleSections )
156- . sort ( )
157- . map ( ( [ key , label ] ) => {
158- const healthSection =
159- healthStatus [ key as keyof typeof visibleSections ] ;
160-
161- return (
162- < NavLink
163- end
164- key = { key }
165- to = { `/health/${ kebabCase ( key ) } ` }
166- className = { ( { isActive } ) =>
167- cx ( [ link , isActive && activeLink ] )
168- }
169- >
170- < HealthIcon
171- size = { 16 }
172- severity = { healthSection . severity as HealthSeverity }
173- />
174- { label }
175- { healthSection . dismissed && (
176- < NotificationsOffOutlined
177- css = { {
178- fontSize : 14 ,
179- marginLeft : "auto" ,
180- color : theme . palette . text . disabled ,
181- } }
182- />
183- ) }
184- </ NavLink >
185- ) ;
186- } ) }
187- </ nav >
188- </ div >
145+ < div css = { { display : "flex" , flexDirection : "column" } } >
146+ < span css = { { fontWeight : 500 } } > Last check</ span >
147+ < span
148+ data-chromatic = "ignore"
149+ css = { {
150+ color : theme . palette . text . secondary ,
151+ lineHeight : "150%" ,
152+ } }
153+ >
154+ { createDayString ( healthStatus . time ) }
155+ </ span >
156+ </ div >
189157
190- < div css = { { overflowY : "auto" , width : "100%" } } >
191- < Suspense fallback = { < Loader /> } >
192- < Outlet context = { healthStatus } />
193- </ Suspense >
158+ < div css = { { display : "flex" , flexDirection : "column" } } >
159+ < span css = { { fontWeight : 500 } } > Version</ span >
160+ < span
161+ data-chromatic = "ignore"
162+ css = { {
163+ color : theme . palette . text . secondary ,
164+ lineHeight : "150%" ,
165+ } }
166+ >
167+ { healthStatus . coder_version }
168+ </ span >
169+ </ div >
194170 </ div >
171+
172+ < nav css = { { display : "flex" , flexDirection : "column" , gap : 1 } } >
173+ { Object . entries ( visibleSections )
174+ . sort ( )
175+ . map ( ( [ key , label ] ) => {
176+ const healthSection =
177+ healthStatus [ key as keyof typeof visibleSections ] ;
178+
179+ return (
180+ < NavLink
181+ end
182+ key = { key }
183+ to = { `/health/${ kebabCase ( key ) } ` }
184+ className = { ( { isActive } ) =>
185+ cx ( [ link , isActive && activeLink ] )
186+ }
187+ >
188+ < HealthIcon
189+ size = { 16 }
190+ severity = { healthSection . severity as HealthSeverity }
191+ />
192+ { label }
193+ { healthSection . dismissed && (
194+ < NotificationsOffOutlined
195+ css = { {
196+ fontSize : 14 ,
197+ marginLeft : "auto" ,
198+ color : theme . palette . text . disabled ,
199+ } }
200+ />
201+ ) }
202+ </ NavLink >
203+ ) ;
204+ } ) }
205+ </ nav >
195206 </ div >
196- </ DashboardFullPage >
197- ) : (
198- < Loader />
199- ) }
207+
208+ < div css = { { overflowY : "auto" , width : "100%" } } >
209+ < Suspense fallback = { < Loader /> } >
210+ < Outlet context = { healthStatus } />
211+ </ Suspense >
212+ </ div >
213+ </ div >
214+ </ DashboardFullPage >
200215 </ >
201216 ) ;
202217} ;
0 commit comments