@@ -292,6 +292,7 @@ function rootApp() {
292292 online : true ,
293293 moduleStatus : { } ,
294294 updateAvailable : false ,
295+ panelVersion : '' ,
295296 updateModal : {
296297 show :false , current :'v3.1.0' , latest :'' , name :'' ,
297298 body :'' , published :'' , url :'' , error :'' ,
@@ -412,6 +413,11 @@ function rootApp() {
412413 const r = await get ( '/api/modules' ) ;
413414 if ( r . ok ) r . modules . forEach ( m => { this . moduleStatus [ m . id ] = m . installed ; } ) ;
414415 } catch { }
416+ // Sidebar version display
417+ try {
418+ const uv = await get ( '/api/update/version' ) ;
419+ if ( uv . ok ) this . panelVersion = uv . version ;
420+ } catch { }
415421 // Silent update check after 3s
416422 setTimeout ( ( ) => this . silentUpdateCheck ( ) , 3000 ) ;
417423 document . dispatchEvent ( new CustomEvent ( 'vortex-logged-in' ) ) ;
@@ -466,10 +472,7 @@ function rootApp() {
466472 } ,
467473
468474 async openUpdateModal ( ) {
469- this . updateModal . show = true ;
470- this . $nextTick ( ( ) => {
471- document . dispatchEvent ( new CustomEvent ( 'vortex-check-update' ) ) ;
472- } ) ;
475+ document . dispatchEvent ( new CustomEvent ( 'vortex-open-update-modal' ) ) ;
473476 } ,
474477 } ;
475478}
@@ -498,7 +501,7 @@ function dashboardPage() {
498501 async init ( ) {
499502 await Promise . all ( [ this . loadStats ( ) , this . loadServices ( ) , this . loadSslAlerts ( ) ] ) ;
500503 this . loading = false ;
501- this . $nextTick ( ( ) => this . _initCharts ( ) ) ;
504+ this . _waitForChartJs ( ) ;
502505 setInterval ( ( ) => this . loadStats ( ) , 5000 ) ;
503506 setInterval ( ( ) => this . loadSslAlerts ( ) , 60000 ) ; // recheck every minute, not every 5s
504507 document . addEventListener ( "vortex-logged-in" , ( ) => { this . init ( ) ; } ) ;
@@ -556,6 +559,12 @@ function dashboardPage() {
556559 } ;
557560 } ,
558561
562+ _waitForChartJs ( attempt = 0 ) {
563+ if ( typeof Chart !== 'undefined' ) { this . $nextTick ( ( ) => this . _initCharts ( ) ) ; return ; }
564+ if ( attempt >= 40 ) { console . warn ( '[VortexPanel] Chart.js failed to load after 10s — dashboard charts will stay empty. Check the Network tab for a failed request to cdnjs.cloudflare.com/ajax/libs/Chart.js.' ) ; return ; }
565+ setTimeout ( ( ) => this . _waitForChartJs ( attempt + 1 ) , 250 ) ;
566+ } ,
567+
559568 _initCharts ( ) {
560569 if ( typeof Chart === 'undefined' ) return ; // CDN not loaded yet — retry next tick
561570 const c = this . _chartColors ( ) ;
@@ -4676,12 +4685,14 @@ function cdnPage() {
46764685// --- UPDATE MODAL ---------------------------------------------------------------
46774686function updateModalData ( ) {
46784687 return {
4688+ show : false , current : '' , latest : '' , name : '' , body : '' , published : '' ,
46794689 checkState : 'checking' ,
46804690 updating : false , updateDone : false , updateSuccess : false ,
46814691 updateError : '' , updateLines : [ ] , updateProgress : 0 ,
46824692 errorMsg : '' , _pollTimer : null ,
46834693
46844694 async init ( ) {
4695+ document . addEventListener ( 'vortex-open-update-modal' , ( ) => { this . show = true ; this . checkForUpdates ( ) ; } ) ;
46854696 document . addEventListener ( 'vortex-check-update' , ( ) => this . checkForUpdates ( ) ) ;
46864697 await this . checkForUpdates ( ) ;
46874698 } ,
@@ -4691,18 +4702,19 @@ function updateModalData() {
46914702 try {
46924703 const r = await get ( '/api/update/check' ) ;
46934704
4694- // Sync version to parent rootApp
4705+ this . current = r . current || 'v3.0.0' ;
4706+ this . latest = r . latest || r . current || 'v3.0.0' ;
4707+ this . name = r . name || 'VortexPanel' ;
4708+ this . body = r . body || '' ;
4709+ this . published = r . published || '' ;
4710+
4711+ // Sync updateAvailable to rootApp -- that flag drives the topbar
4712+ // button's glow/badge, which genuinely lives in rootApp's own scope
4713+ // (unlike this modal, which sits in the disconnected portal).
46954714 try {
46964715 const appEl = document . querySelector ( '[x-data="rootApp()"]' ) ;
46974716 const app = appEl ? Alpine . $data ( appEl ) : null ;
4698- if ( app ) {
4699- app . updateModal . current = r . current || 'v3.0.0' ;
4700- app . updateModal . latest = r . latest || r . current || 'v3.0.0' ;
4701- app . updateModal . name = r . name || 'VortexPanel' ;
4702- app . updateModal . body = r . body || '' ;
4703- app . updateModal . published = r . published || '' ;
4704- if ( r . has_update ) app . updateAvailable = true ;
4705- }
4717+ if ( app && r . has_update ) app . updateAvailable = true ;
47064718 } catch { }
47074719
47084720 if ( r . error && ! r . current ) { this . errorMsg = r . error ; this . checkState = 'error' ; return ; }
@@ -4715,11 +4727,7 @@ function updateModalData() {
47154727 } ,
47164728
47174729 async startUpdate ( ) {
4718- let version = '' ;
4719- try {
4720- const appEl = document . querySelector ( '[x-data="rootApp()"]' ) ;
4721- if ( appEl ) version = Alpine . $data ( appEl ) . updateModal . latest || '' ;
4722- } catch { }
4730+ const version = this . latest || '' ;
47234731 this . updating = true ; this . updateDone = false ; this . updateSuccess = false ;
47244732 this . updateLines = [ `🚀 Starting update to ${ version } …` ] ; this . updateProgress = 5 ;
47254733 const r = await post ( '/api/update/start' , { version} ) ;
0 commit comments