@@ -3,11 +3,12 @@ import { formatUptime } from "../formatUptime";
33import { IconAlert , IconExternal , IconInfo , IconRefresh , IconX } from "../icons" ;
44import { Trans } from "../i18n/provider" ;
55import { useI18n , type TKey } from "../i18n/shared" ;
6- import { settingsPollMayCommit , startupRiskDetailKey } from "../startup-health-ui" ;
6+ import { settingsPollMayCommit } from "../startup-health-ui" ;
77import { formatTokens } from "../format-tokens" ;
88import { EmptyState , Select } from "../ui" ;
99
1010interface HealthData { status : string ; version : string ; uptime : number }
11+ type StartupHealthStatus = "native" | "protected" | "at-risk" | "error" ;
1112interface ProviderInfo { name : string ; adapter : string ; baseUrl : string ; defaultModel ?: string ; hasApiKey : boolean }
1213interface ModelInfo { id : string ; provider : string ; owned_by ?: string }
1314interface SettingsData {
@@ -188,6 +189,7 @@ function useModalDialog(open: boolean, triggerRef: RefObject<HTMLButtonElement |
188189export default function Dashboard ( { apiBase } : { apiBase : string } ) {
189190 const { locale, t } = useI18n ( ) ;
190191 const [ health , setHealth ] = useState < HealthData | null > ( null ) ;
192+ const [ startupHealth , setStartupHealth ] = useState < StartupHealthStatus | null > ( null ) ;
191193 const [ providers , setProviders ] = useState < ProviderInfo [ ] > ( [ ] ) ;
192194 const [ models , setModels ] = useState < ModelInfo [ ] > ( [ ] ) ;
193195 const [ settings , setSettings ] = useState < SettingsData | null > ( null ) ;
@@ -245,6 +247,29 @@ export default function Dashboard({ apiBase }: { apiBase: string }) {
245247 }
246248 } , [ ] ) ;
247249
250+ useEffect ( ( ) => {
251+ let cancelled = false ;
252+ const readStartupHealth = async ( ) => {
253+ try {
254+ const response = await fetch ( `${ apiBase } /api/startup-health` ) ;
255+ if ( ! response . ok ) throw new Error ( "startup health unavailable" ) ;
256+ const data = await response . json ( ) as { status ?: unknown ; diagnosticStale ?: unknown } ;
257+ const status = data . status ;
258+ const validStatus = status === "native" || status === "protected" || status === "at-risk" ;
259+ if ( ! validStatus ) throw new Error ( "invalid startup health response" ) ;
260+ if ( ! cancelled ) setStartupHealth ( data . diagnosticStale === true ? "at-risk" : status ) ;
261+ } catch {
262+ if ( ! cancelled ) setStartupHealth ( "error" ) ;
263+ }
264+ } ;
265+ void readStartupHealth ( ) ;
266+ const interval = window . setInterval ( ( ) => { void readStartupHealth ( ) ; } , 30_000 ) ;
267+ return ( ) => {
268+ cancelled = true ;
269+ window . clearInterval ( interval ) ;
270+ } ;
271+ } , [ apiBase ] ) ;
272+
248273 useEffect ( ( ) => {
249274 const fetchData = async ( ) => {
250275 const settingsRequestEpoch = ++ settingsRequestEpochRef . current ;
@@ -648,16 +673,19 @@ export default function Dashboard({ apiBase }: { apiBase: string }) {
648673 </ div >
649674 </ div >
650675
651- { ( settings ?. startupHealth ?. status === "at-risk" || settings ?. startupHealth ?. diagnosticStale ) && (
652- < div className = "notice notice-warn" role = "alert" style = { { marginTop : - 12 , marginBottom : 24 } } >
653- < IconAlert />
654- < span >
655- { settings . startupHealth . diagnosticStale
656- ? t ( "startup.staleData" )
657- : t ( startupRiskDetailKey ( settings . startupHealth ) ) } { " " }
658- < a href = "#startup" > { t ( "startup.title" ) } </ a >
676+ { startupHealth && (
677+ < a className = "startup-health-bar" href = "#startup" >
678+ < span className = { `dot ${ startupHealth === "error" ? "dot-red" : startupHealth === "at-risk" ? "dot-amber" : "dot-green" } ` } aria-hidden = "true" />
679+ < span className = "startup-health-bar__summary" >
680+ { t ( startupHealth === "error"
681+ ? "startup.error"
682+ : startupHealth === "at-risk"
683+ ? "startup.summary.atRisk"
684+ : startupHealth === "protected"
685+ ? "startup.summary.protected"
686+ : "startup.summary.native" ) }
659687 </ span >
660- </ div >
688+ </ a >
661689 ) }
662690
663691 { projectConfigWarnings . length > 0 && (
0 commit comments