@ -1,126 +1,126 @@
/* -------------------------------------------------------------
1. Socket‑ IO connection & helper functions (unchanged)
- ------------------------------------------------------------ */
const socket = io ( ) ;
/* ------------------------------------------------------------
1. Socket- IO connection & helper functions (unchanged)
------------------------------------------------------------ */
const socket = io ( ) ;
socket . on ( 'host_stat s' , renderStatsTable ) ;
socket . on ( 'connect_error' , err => {
safeSetText ( 'host_stat s' , ` Could not connect to server - ${ err . message } ` ) ;
} ) ;
socket . on ( 'reconnect' , attempt => {
safeSetText ( 'host_stat s' , ` Re‑ connected (attempt ${ attempt } ) ` ) ;
} ) ;
socket . on ( 'host_metric s' , renderStatsTable ) ;
socket . on ( 'connect_error' , err => {
safeSetText ( 'host_metric s' , ` Could not connect to server - ${ err . message } ` ) ;
} ) ;
socket . on ( 'reconnect' , attempt => {
safeSetText ( 'host_metric s' , ` Re- connected (attempt ${ attempt } ) ` ) ;
} ) ;
function safeSetText ( id , txt ) {
const el = document . getElementById ( id ) ;
if ( el ) el . textContent = txt ;
function safeSetText ( id , txt ) {
const el = document . getElementById ( id ) ;
if ( el ) el . textContent = txt ;
}
/* ------------------------------------------------------------
2. Table rendering - the table remains a <table>
------------------------------------------------------------ */
function renderStatsTable ( data ) {
renderGenericTable ( 'host_metrics' , data , 'No Stats available' ) ;
}
function renderGenericTable ( containerId , data , emptyMsg ) {
const container = document . getElementById ( containerId ) ;
if ( ! Array . isArray ( data ) || ! data . length ) {
container . textContent = emptyMsg ;
return ;
}
/* -------------------------------------------------------------
2. Table rendering – the table remains a <table>
------------------------------------------------------------- */
function renderStatsTable ( data ) {
renderGenericTable ( 'host_stats' , data , 'No Stats available' ) ;
}
/* Merge rows by name (new logic) */
const mergedData = mergeRowsByName ( data ) ;
function renderGenericTable ( containerId , data , emptyMsg ) {
const container = document . getElementById ( containerId ) ;
if ( ! Array . isArray ( data ) || ! data . length ) {
container . textContent = emptyMsg ;
return ;
/* Build the table from the merged data */
const table = buildTable ( mergedData ) ;
table . id = 'host_metrics_table' ;
container . innerHTML = '' ;
container . appendChild ( table ) ;
}
/* ------------------------------------------------------------
3. Merge rows by name (new logic)
------------------------------------------------------------ */
function mergeRowsByName ( data ) {
const groups = { } ; // { name: { types: [], metrics: [], props: [], values: [] } }
data . forEach ( row => {
const name = row . name ;
if ( ! name ) return ; // ignore rows without a name
if ( ! groups [ name ] ) {
groups [ name ] = { types : [ ] , metrics : [ ] , props : [ ] , values : [ ] } ;
}
/* 2️ ⃣ Merge “System Class Variable” rows first */
const mergedData = mergeSystemClassVariableRows ( data ) ;
/* 3️ ⃣ Build the table from the merged data */
const table = buildTable ( mergedData ) ;
container . innerHTML = '' ;
container . appendChild ( table ) ;
}
/* -------------------------------------------------------------
3. Merge consecutive rows whose type === "System Class Variable"
------------------------------------------------------------- */
function mergeSystemClassVariableRows ( data ) {
const result = [ ] ;
let i = 0 ;
while ( i < data . length ) {
const cur = data [ i ] ;
if ( cur . type && cur . type . trim ( ) === 'System Class Variable' ) {
const group = [ ] ;
while (
i < data . length &&
data [ i ] . type &&
data [ i ] . type . trim ( ) === 'System Class Variable'
) {
group . push ( data [ i ] ) ;
i ++ ;
}
/* Build one merged object – keep each column as an array */
const merged = { type : 'System Class Variable' } ;
const cols = Object . keys ( group [ 0 ] ) . filter ( k => k !== 'type' ) ;
cols . forEach ( col => {
const vals = group
. map ( row => row [ col ] )
. filter ( v => v !== undefined && v !== null ) ;
merged [ col ] = vals ; // ← array, not joined string
} ) ;
result . push ( merged ) ;
} else {
/* Normal row – just copy it */
result . push ( cur ) ;
i ++ ;
}
// Metric rows - contain type + metric
if ( 'type' in row && 'metric' in row ) {
groups [ name ] . types . push ( row . type ) ;
groups [ name ] . metrics . push ( row . metric ) ;
}
// Property rows - contain property + value
else if ( 'property' in row && 'value' in row ) {
groups [ name ] . props . push ( row . property ) ;
groups [ name ] . values . push ( row . value ) ;
}
} ) ;
return result ;
}
// Convert each group into a single row object
const merged = [ ] ;
Object . entries ( groups ) . forEach ( ( [ name , grp ] ) => {
merged . push ( {
name ,
type : grp . types , // array of types
metric : grp . metrics , // array of metrics
property : grp . props , // array of property names
value : grp . values , // array of property values
} ) ;
} ) ;
/* -------------------------------------------------------------
4. Build an HTML table from an array of objects
------------------------------------------------------------- */
function buildTable ( data ) {
const cols = Object . keys ( data [ 0 ] ) ; // column order
const table = document . createElement ( 'table' ) ;
return merged ;
}
/* Header */
const thead = table . createTHead ( ) ;
const headerRow = thead . insertRow ( ) ;
/* ------------------------------------------------------------
4. Build an HTML table from an array of objects
------------------------------------------------------------ */
function buildTable ( data ) {
const cols = [ 'name' , 'type' , 'metric' , 'property' , 'value' ] ; // explicit order
const table = document . createElement ( 'table' ) ;
// Header
const thead = table . createTHead ( ) ;
const headerRow = thead . insertRow ( ) ;
cols . forEach ( col => {
const th = document . createElement ( 'th' ) ;
th . textContent = col ;
headerRow . appendChild ( th ) ;
} ) ;
// Body
const tbody = table . createTBody ( ) ;
data . forEach ( item => {
const tr = tbody . insertRow ( ) ;
cols . forEach ( col => {
const th = document . createElement ( 'th' ) ;
th . textContent = col ;
headerRow . appendChild ( th ) ;
const td = tr . insertCell ( ) ;
const val = item [ col ] ;
if ( Array . isArray ( val ) ) {
// Create a <span> for each item
val . forEach ( ( v , idx ) => {
td . id = 'host_metrics_column' ;
const span = document . createElement ( 'span' ) ;
span . textContent = v ;
td . appendChild ( span ) ;
// Insert a line break after every item except the last
if ( idx < val . length - 1 ) td . appendChild ( document . createElement ( 'br' ) ) ;
} ) ;
} else {
td . textContent = val !== undefined ? val : '' ;
}
} ) ;
} ) ;
/* Body */
const tbody = table . createTBody ( ) ;
data . forEach ( item => {
const tr = tbody . insertRow ( ) ;
cols . forEach ( col => {
const td = tr . insertCell ( ) ;
const val = item [ col ] ;
/* If the value is an array → render as <ol> */
if ( Array . isArray ( val ) ) {
const ol = document . createElement ( 'ol' ) ;
val . forEach ( v => {
const li = document . createElement ( 'li' ) ;
li . textContent = v ;
ol . appendChild ( li ) ;
} ) ;
td . appendChild ( ol ) ;
} else {
td . textContent = val ; // normal text
}
} ) ;
} ) ;
return table ;
}
return table ;
}