2025-12-29 11:08:37 +00:00
'use strict' ;
'require view' ;
'require ui' ;
'require system-hub/api as API' ;
'require secubox-theme/theme as Theme' ;
2025-12-29 20:57:12 +00:00
'require system-hub/theme-assets as ThemeAssets' ;
2025-12-29 11:08:37 +00:00
'require system-hub/nav as HubNav' ;
var shLang = ( typeof L !== 'undefined' && L . env && L . env . lang ) ||
( document . documentElement && document . documentElement . getAttribute ( 'lang' ) ) ||
( navigator . language ? navigator . language . split ( '-' ) [ 0 ] : 'en' ) ;
Theme . init ( { language : shLang } ) ;
return view . extend ( {
settings : null ,
fieldRefs : null ,
load : function ( ) {
return API . getSettings ( ) ;
} ,
render : function ( data ) {
this . settings = data || { } ;
this . fieldRefs = { } ;
var container = E ( 'div' , { 'class' : 'system-hub-dashboard sh-settings-view' } , [
E ( 'link' , { 'rel' : 'stylesheet' , 'href' : L . resource ( 'secubox-theme/secubox-theme.css' ) } ) ,
2025-12-29 20:57:12 +00:00
ThemeAssets . stylesheet ( 'common.css' ) ,
ThemeAssets . stylesheet ( 'dashboard.css' ) ,
2025-12-29 11:08:37 +00:00
HubNav . renderTabs ( 'settings' ) ,
this . renderHeader ( ) ,
this . renderGeneralSection ( ) ,
this . renderThresholdSection ( ) ,
this . renderSupportSection ( ) ,
this . renderActions ( )
] ) ;
return container ;
} ,
renderHeader : function ( ) {
var general = this . settings . general || { } ;
var autoRefresh = this . boolValue ( general . auto _refresh , true ) ;
var healthCheck = this . boolValue ( general . health _check , true ) ;
return E ( 'div' , { 'class' : 'sh-page-header sh-page-header-lite' } , [
E ( 'div' , { } , [
E ( 'h2' , { 'class' : 'sh-page-title' } , [
E ( 'span' , { 'class' : 'sh-page-title-icon' } , '⚙️' ) ,
_ ( 'System Hub Preferences' )
] ) ,
E ( 'p' , { 'class' : 'sh-page-subtitle' } ,
_ ( 'Control health checks, refresh cadence, and alert thresholds for every System Hub widget.' ) )
] ) ,
E ( 'div' , { 'class' : 'sh-header-meta' } , [
this . renderChip ( '⏱️' , _ ( 'Auto refresh' ) , autoRefresh ? _ ( 'Enabled' ) : _ ( 'Disabled' ) ) ,
this . renderChip ( '🩺' , _ ( 'Health monitor' ) , healthCheck ? _ ( 'Active' ) : _ ( 'Paused' ) ) ,
this . renderChip ( '🧪' , _ ( 'Diagnostics' ) , _ ( 'Manual triggers' ) )
] )
] ) ;
} ,
renderChip : function ( icon , label , value ) {
return E ( 'div' , { 'class' : 'sh-header-chip' } , [
E ( 'span' , { 'class' : 'sh-chip-icon' } , icon ) ,
E ( 'div' , { 'class' : 'sh-chip-text' } , [
E ( 'span' , { 'class' : 'sh-chip-label' } , label ) ,
E ( 'strong' , { } , value || '—' )
] )
] ) ;
} ,
renderGeneralSection : function ( ) {
var general = this . settings . general || { } ;
var refresh = ( general . refresh _interval != null ) ? String ( general . refresh _interval ) : '30' ;
return E ( 'section' , { 'class' : 'sh-card' } , [
E ( 'div' , { 'class' : 'sh-card-header' } , [
E ( 'div' , { 'class' : 'sh-card-title' } , [
E ( 'span' , { 'class' : 'sh-card-title-icon' } , '🛠️' ) ,
_ ( 'Automation & Refresh' )
] )
] ) ,
E ( 'div' , { 'class' : 'sh-card-body' } , [
E ( 'div' , { 'class' : 'sh-settings-grid' } , [
this . renderToggle ( 'auto_refresh' , _ ( 'Auto refresh' ) , _ ( 'Poll services & metrics every few seconds' ) , this . boolValue ( general . auto _refresh , true ) , '♻️' ) ,
this . renderToggle ( 'health_check' , _ ( 'Health monitor' ) , _ ( 'Run background probes to populate the Health tab' ) , this . boolValue ( general . health _check , true ) , '🩺' ) ,
this . renderToggle ( 'debug_mode' , _ ( 'Debug mode' ) , _ ( 'Surface extra logs and RPC payloads (development only)' ) , this . boolValue ( general . debug _mode , false ) , '🐛' )
] ) ,
E ( 'div' , { 'class' : 'sh-settings-grid sh-settings-grid--compact' , 'style' : 'margin-top: 20px;' } , [
this . renderSelect ( 'refresh_interval' , _ ( 'Refresh cadence' ) , [
{ value : '15' , label : _ ( 'Every 15 seconds' ) } ,
{ value : '30' , label : _ ( 'Every 30 seconds' ) } ,
{ value : '60' , label : _ ( 'Every minute' ) } ,
{ value : '120' , label : _ ( 'Every 2 minutes' ) } ,
{ value : '0' , label : _ ( 'Manual refresh only' ) }
] , refresh ) ,
this . renderNumber ( 'log_retention' , _ ( 'Log retention (days)' ) , general . log _retention || 30 , 1 , 365 )
] )
] )
] ) ;
} ,
renderThresholdSection : function ( ) {
var th = this . settings . thresholds || { } ;
return E ( 'section' , { 'class' : 'sh-card' } , [
E ( 'div' , { 'class' : 'sh-card-header' } , [
E ( 'div' , { 'class' : 'sh-card-title' } , [
E ( 'span' , { 'class' : 'sh-card-title-icon' } , '🚨' ) ,
_ ( 'Alert thresholds' )
] ) ,
E ( 'div' , { 'class' : 'sh-card-subtitle' } , _ ( 'Define warning/critical limits used by Health dashboards.' ) )
] ) ,
E ( 'div' , { 'class' : 'sh-card-body' } , [
E ( 'div' , { 'class' : 'sh-threshold-grid' } , [
this . renderThresholdRow ( 'cpu' , _ ( 'CPU usage (%)' ) , th . cpu _warning || 80 , th . cpu _critical || 95 ) ,
this . renderThresholdRow ( 'mem' , _ ( 'Memory usage (%)' ) , th . mem _warning || 80 , th . mem _critical || 95 ) ,
this . renderThresholdRow ( 'disk' , _ ( 'Disk usage (%)' ) , th . disk _warning || 80 , th . disk _critical || 95 ) ,
this . renderThresholdRow ( 'temp' , _ ( 'Temperature (°C)' ) , th . temp _warning || 70 , th . temp _critical || 85 )
] )
] )
] ) ;
} ,
renderSupportSection : function ( ) {
var support = this . settings . support || { } ;
var upload = this . settings . upload || { } ;
return E ( 'section' , { 'class' : 'sh-card' } , [
E ( 'div' , { 'class' : 'sh-card-header' } , [
E ( 'div' , { 'class' : 'sh-card-title' } , [
E ( 'span' , { 'class' : 'sh-card-title-icon' } , '🤝' ) ,
_ ( 'Support & export' )
] )
] ) ,
E ( 'div' , { 'class' : 'sh-card-body' } , [
E ( 'div' , { 'class' : 'sh-support-grid' } , [
E ( 'div' , { 'class' : 'sh-support-card' } , [
E ( 'div' , { 'class' : 'sh-support-label' } , _ ( 'Provider' ) ) ,
E ( 'strong' , { } , support . provider || _ ( 'Unknown' ) ) ,
E ( 'div' , { 'class' : 'sh-support-desc' } , support . email || '' )
] ) ,
E ( 'div' , { 'class' : 'sh-support-card' } , [
E ( 'div' , { 'class' : 'sh-support-label' } , _ ( 'Documentation' ) ) ,
E ( 'a' , { 'href' : support . docs || '#' , 'target' : '_blank' , 'rel' : 'noreferrer' } , support . docs || _ ( 'Unavailable' ) )
] ) ,
E ( 'div' , { 'class' : 'sh-support-card' } , [
E ( 'div' , { 'class' : 'sh-support-label' } , _ ( 'Auto upload' ) ) ,
E ( 'strong' , { } , this . boolValue ( upload . auto _upload , false ) ? _ ( 'Enabled' ) : _ ( 'Disabled' ) ) ,
E ( 'div' , { 'class' : 'sh-support-desc' } , upload . url || _ ( 'No endpoint configured' ) )
] )
] )
] )
] ) ;
} ,
renderActions : function ( ) {
return E ( 'section' , { 'class' : 'sh-card' } , [
E ( 'div' , { 'class' : 'sh-card-header' } , [
E ( 'div' , { 'class' : 'sh-card-title' } , [
E ( 'span' , { 'class' : 'sh-card-title-icon' } , '💾' ) ,
_ ( 'Apply changes' )
] )
] ) ,
E ( 'div' , { 'class' : 'sh-card-body sh-btn-group' } , [
E ( 'button' , {
'class' : 'sh-btn sh-btn-primary' ,
'click' : L . bind ( this . saveSettings , this )
} , [ '✅ ' , _ ( 'Save preferences' ) ] ) ,
E ( 'button' , {
'class' : 'sh-btn sh-btn-secondary' ,
'click' : L . bind ( this . resetView , this )
} , [ '↺ ' , _ ( 'Reset' ) ] )
] )
] ) ;
} ,
renderToggle : function ( key , label , desc , active , icon ) {
var self = this ;
var switchEl = E ( 'div' , {
'class' : 'sh-toggle-switch' + ( active ? ' active' : '' ) ,
'data-key' : key ,
'click' : function ( ev ) {
ev . target . classList . toggle ( 'active' ) ;
}
} ) ;
this . fieldRefs [ key ] = { type : 'toggle' , node : switchEl } ;
return E ( 'div' , { 'class' : 'sh-toggle' } , [
E ( 'div' , { 'class' : 'sh-toggle-info' } , [
E ( 'span' , { 'class' : 'sh-toggle-icon' } , icon || '•' ) ,
E ( 'div' , { } , [
E ( 'div' , { 'class' : 'sh-toggle-label' } , label ) ,
E ( 'div' , { 'class' : 'sh-toggle-desc' } , desc )
] )
] ) ,
switchEl
] ) ;
} ,
renderSelect : function ( key , label , options , current ) {
var select = E ( 'select' , {
'class' : 'sh-input' ,
'change' : function ( ev ) {
ev . target . setAttribute ( 'data-value' , ev . target . value ) ;
}
} , options . map ( function ( opt ) {
return E ( 'option' , {
'value' : opt . value ,
'selected' : opt . value === current
} , opt . label ) ;
} ) ) ;
select . setAttribute ( 'data-value' , current ) ;
this . fieldRefs [ key ] = { type : 'select' , node : select } ;
return E ( 'div' , { 'class' : 'sh-input-group' } , [
E ( 'label' , { 'class' : 'sh-input-label' } , label ) ,
select
] ) ;
} ,
renderNumber : function ( key , label , value , min , max ) {
var input = E ( 'input' , {
'type' : 'number' ,
'class' : 'sh-input' ,
'value' : value ,
'min' : min ,
'max' : max
} ) ;
this . fieldRefs [ key ] = { type : 'number' , node : input } ;
return E ( 'div' , { 'class' : 'sh-input-group' } , [
E ( 'label' , { 'class' : 'sh-input-label' } , label ) ,
input
] ) ;
} ,
renderThresholdRow : function ( prefix , label , warning , critical ) {
var warnKey = prefix + '_warning' ;
var critKey = prefix + '_critical' ;
var warnInput = E ( 'input' , {
'type' : 'number' ,
'class' : 'sh-input' ,
'value' : warning ,
'min' : 0 ,
'max' : 200
} ) ;
var critInput = E ( 'input' , {
'type' : 'number' ,
'class' : 'sh-input' ,
'value' : critical ,
'min' : 0 ,
'max' : 200
} ) ;
this . fieldRefs [ warnKey ] = { type : 'number' , node : warnInput } ;
this . fieldRefs [ critKey ] = { type : 'number' , node : critInput } ;
return E ( 'div' , { 'class' : 'sh-threshold-row' } , [
E ( 'div' , { 'class' : 'sh-threshold-label' } , label ) ,
E ( 'div' , { 'class' : 'sh-threshold-inputs' } , [
E ( 'label' , { } , [
_ ( 'Warning' ) ,
warnInput
] ) ,
E ( 'label' , { } , [
_ ( 'Critical' ) ,
critInput
] )
] )
] ) ;
} ,
boolValue : function ( value , fallback ) {
if ( value === 0 || value === '0' )
return false ;
if ( value === 1 || value === '1' )
return true ;
return ! ! fallback ;
} ,
collectPayload : function ( ) {
var payload = { } ;
var self = this ;
function readBool ( key ) {
var ref = self . fieldRefs [ key ] ;
return ref && ref . node . classList . contains ( 'active' ) ? 1 : 0 ;
}
function readNumber ( key ) {
var ref = self . fieldRefs [ key ] ;
return ref ? parseInt ( ref . node . value , 10 ) || 0 : 0 ;
}
function readSelect ( key ) {
var ref = self . fieldRefs [ key ] ;
return ref ? ref . node . getAttribute ( 'data-value' ) || ref . node . value : '' ;
}
payload . auto _refresh = readBool ( 'auto_refresh' ) ;
payload . health _check = readBool ( 'health_check' ) ;
payload . debug _mode = readBool ( 'debug_mode' ) ;
payload . refresh _interval = readSelect ( 'refresh_interval' ) ;
payload . log _retention = readNumber ( 'log_retention' ) ;
[ 'cpu' , 'mem' , 'disk' , 'temp' ] . forEach ( function ( prefix ) {
payload [ prefix + '_warning' ] = readNumber ( prefix + '_warning' ) ;
payload [ prefix + '_critical' ] = readNumber ( prefix + '_critical' ) ;
} ) ;
return payload ;
} ,
saveSettings : function ( ev ) {
ev && ev . preventDefault ( ) ;
var payload = this . collectPayload ( ) ;
ui . showModal ( _ ( 'Saving preferences…' ) , [
E ( 'p' , { } , _ ( 'Applying thresholds and refresh cadence' ) ) ,
E ( 'div' , { 'class' : 'spinning' } )
] ) ;
API . saveSettings ( payload ) . then ( L . bind ( function ( result ) {
ui . hideModal ( ) ;
if ( result && result . success ) {
ui . addNotification ( null , E ( 'p' , { } , _ ( 'Preferences saved.' ) ) , 'info' ) ;
this . reloadView ( ) ;
} else {
ui . addNotification ( null , E ( 'p' , { } , ( result && result . error ) || _ ( 'Unable to save settings' ) ) , 'error' ) ;
}
} , this ) ) . catch ( function ( err ) {
ui . hideModal ( ) ;
ui . addNotification ( null , E ( 'p' , { } , err . message || err ) , 'error' ) ;
} ) ;
} ,
resetView : function ( ev ) {
ev && ev . preventDefault ( ) ;
this . reloadView ( ) ;
} ,
reloadView : function ( ) {
this . load ( ) . then ( L . bind ( function ( data ) {
var node = this . render ( data ) ;
var root = document . querySelector ( '.system-hub-dashboard' ) ;
if ( root && root . parentNode ) {
root . parentNode . replaceChild ( node , root ) ;
}
} , this ) ) ;
} ,
handleSaveApply : null ,
handleSave : null ,
handleReset : null
} ) ;