@@ -1,159 +1,33 @@
( ( ) => {
function initDashboardLiveUpdates ( ) {
// Optional live updates on dashboard
if ( ! document . getElementById ( 'tbl-displays' ) ) return ;
// Optional live updates on dashboard
if ( ! document . getElementById ( 'tbl-displays' ) ) return ;
const socket = io ( { transports : [ 'websocket' ] , upgrade : false } ) ;
socket . on ( 'connect' , ( ) => {
socket . emit ( 'admin_join' ) ;
} ) ;
const socket = io ( { transports : [ 'websocket' ] , upgrade : false } ) ;
socket . on ( 'connect' , ( ) => {
socket . emit ( 'admin_join' ) ;
} ) ;
function updateRow ( publicId , patch ) {
const row = document . querySelector ( ` tr[data-public-id=" ${ publicId } "] ` ) ;
if ( ! row ) return ;
if ( patch . is _online !== undefined ) row . querySelector ( '.st' ) . textContent = patch . is _online ? 'online' : 'offline' ;
if ( patch . last _seen ) row . querySelector ( '.ls' ) . textContent = patch . last _seen ;
if ( patch . latency _ms !== undefined ) row . querySelector ( '.lat' ) . textContent = patch . latency _ms ? ` ${ patch . latency _ms . toFixed ( 1 ) } ms ` : '' ;
if ( patch . offset _ms !== undefined ) row . querySelector ( '.off' ) . textContent = patch . offset _ms ? ` ${ patch . offset _ms . toFixed ( 1 ) } ms ` : '' ;
}
socket . on ( 'admin_snapshot' , ( msg ) => {
const live = msg . live || { } ;
Object . keys ( live ) . forEach ( pid => updateRow ( pid , { ... live [ pid ] , is _online : true } ) ) ;
} ) ;
socket . on ( 'admin_display_update' , ( msg ) => {
updateRow ( msg . public _id , msg ) ;
} ) ;
socket . on ( 'admin_event_triggered' , ( msg ) => {
const el = document . getElementById ( 'active-event' ) ;
if ( ! el ) return ;
el . innerHTML = ` <div><strong> ${ msg . event _name } </strong> (# ${ msg . event _id } )</div><div>Start: <code> ${ msg . start _time _ms . toFixed ( 3 ) } </code></div> ` ;
} ) ;
function updateRow ( publicId , patch ) {
const row = document . querySelector ( ` tr[data-public-id=" ${ publicId } "] ` ) ;
if ( ! row ) return ;
if ( patch . is _online !== undefined ) row . querySelector ( '.st' ) . textContent = patch . is _online ? 'online' : 'offline' ;
if ( patch . last _seen ) row . querySelector ( '.ls' ) . textContent = patch . last _seen ;
if ( patch . latency _ms !== undefined ) row . querySelector ( '.lat' ) . textContent = patch . latency _ms ? ` ${ patch . latency _ms . toFixed ( 1 ) } ms ` : '' ;
if ( patch . offset _ms !== undefined ) row . querySelector ( '.off' ) . textContent = patch . offset _ms ? ` ${ patch . offset _ms . toFixed ( 1 ) } ms ` : '' ;
}
function initVideoUploadProgress ( ) {
const form = document . getElementById ( 'video-upload-form' ) ;
if ( ! form ) return ;
socket . on ( 'admin_snapshot' , ( msg ) => {
const live = msg . live || { } ;
Object . keys ( live ) . forEach ( pid => updateRow ( pid , { ... live [ pid ] , is _online : true } ) ) ;
} ) ;
const fileInput = form . querySelector ( 'input[type="file"][name="file"]' ) ;
const submitBtn = form . querySelector ( 'button[type="submit"]' ) ;
const progressWrap = document . getElementById ( 'upload-progress' ) ;
const progressBar = document . getElementById ( 'upload-progress-bar' ) || ( progressWrap ? progressWrap . querySelector ( '.progress-bar' ) : null ) ;
const statusEl = document . getElementById ( 'upload-status' ) ;
socket . on ( 'admin_display_update' , ( msg ) => {
updateRow ( msg . public _id , msg ) ;
} ) ;
// Tiny boot indicator so it’ s obvious the JS is active on this page.
setStatus ( 'Ready to upload.' , 'form-text mt-2 text-muted ') ;
if ( fileInput ) {
fileInput . addEventListener ( 'change' , ( ) => {
if ( fileInput . files && fileInput . files . length > 0 ) {
showProgress ( ) ;
setProgress ( 0 , { indeterminate : false , variant : 'info' } ) ;
setStatus ( ` Selected: ${ fileInput . files [ 0 ] . name } ` , 'form-text mt-2 text-muted' ) ;
}
} ) ;
}
function setStatus ( msg , cls ) {
if ( ! statusEl ) return ;
statusEl . textContent = msg || '' ;
statusEl . className = cls ? cls : 'form-text mt-2' ;
}
function showProgress ( ) {
if ( ! progressWrap ) return ;
progressWrap . classList . remove ( 'd-none' ) ;
progressWrap . classList . add ( 'd-block' ) ;
}
function setProgress ( percent , opts = { } ) {
if ( ! progressWrap || ! progressBar ) return ;
const pct = Math . max ( 0 , Math . min ( 100 , Math . round ( percent ) ) ) ;
progressBar . setAttribute ( 'aria-valuenow' , String ( pct ) ) ;
progressBar . style . width = ` ${ pct } % ` ;
progressBar . textContent = ` ${ pct } % ` ;
if ( opts . indeterminate ) {
progressBar . classList . add ( 'progress-bar-striped' , 'progress-bar-animated' ) ;
} else {
progressBar . classList . remove ( 'progress-bar-animated' ) ;
// keep striped (optional) to make it visible
progressBar . classList . add ( 'progress-bar-striped' ) ;
}
if ( opts . variant ) {
progressBar . classList . remove ( 'bg-success' , 'bg-danger' , 'bg-warning' , 'bg-info' ) ;
progressBar . classList . add ( ` bg- ${ opts . variant } ` ) ;
}
}
form . addEventListener ( 'submit' , ( e ) => {
e . preventDefault ( ) ;
if ( ! fileInput || ! fileInput . files || fileInput . files . length === 0 ) {
setStatus ( 'Please select a file to upload.' , 'form-text mt-2 text-danger' ) ;
return ;
}
// Reset UI
showProgress ( ) ;
setProgress ( 0 , { indeterminate : true , variant : 'info' } ) ;
setStatus ( 'Uploading…' , 'form-text mt-2' ) ;
if ( submitBtn ) submitBtn . disabled = true ;
if ( fileInput ) fileInput . disabled = true ;
const xhr = new XMLHttpRequest ( ) ;
const targetUrl = form . getAttribute ( 'action' ) || window . location . href ;
xhr . open ( 'POST' , targetUrl , true ) ;
xhr . upload . addEventListener ( 'progress' , ( evt ) => {
if ( ! evt . lengthComputable ) {
setProgress ( 0 , { indeterminate : true , variant : 'info' } ) ;
return ;
}
const percent = ( evt . loaded / evt . total ) * 100 ;
setProgress ( percent , { indeterminate : false , variant : 'info' } ) ;
} ) ;
xhr . upload . addEventListener ( 'load' , ( ) => {
// Upload bytes are done; server might still be processing.
setProgress ( 100 , { indeterminate : true , variant : 'info' } ) ;
setStatus ( 'Processing on server…' , 'form-text mt-2' ) ;
} ) ;
xhr . addEventListener ( 'load' , ( ) => {
if ( xhr . status >= 200 && xhr . status < 300 ) {
setProgress ( 100 , { indeterminate : false , variant : 'success' } ) ;
setStatus ( 'Upload complete. Redirecting…' , 'form-text mt-2 text-success' ) ;
window . location . assign ( xhr . responseURL || targetUrl ) ;
} else {
setProgress ( 100 , { indeterminate : false , variant : 'danger' } ) ;
setStatus ( ` Upload failed (HTTP ${ xhr . status } ). ` , 'form-text mt-2 text-danger' ) ;
if ( submitBtn ) submitBtn . disabled = false ;
if ( fileInput ) fileInput . disabled = false ;
}
} ) ;
xhr . addEventListener ( 'error' , ( ) => {
setProgress ( 100 , { indeterminate : false , variant : 'danger' } ) ;
setStatus ( 'Upload failed (network error).' , 'form-text mt-2 text-danger' ) ;
if ( submitBtn ) submitBtn . disabled = false ;
if ( fileInput ) fileInput . disabled = false ;
} ) ;
xhr . addEventListener ( 'abort' , ( ) => {
setProgress ( 0 , { indeterminate : false , variant : 'warning' } ) ;
setStatus ( 'Upload aborted.' , 'form-text mt-2 text-warning' ) ;
if ( submitBtn ) submitBtn . disabled = false ;
if ( fileInput ) fileInput . disabled = false ;
} ) ;
const data = new FormData ( form ) ;
xhr . send ( data ) ;
} ) ;
}
initDashboardLiveUpdates ( ) ;
initVideoUploadProgress ( ) ;
socket . on ( 'admin_event_triggered' , ( msg ) => {
const el = document . getElementById ( 'active-event ') ;
if ( ! el ) return ;
el . innerHTML = ` <div><strong> ${ msg . event _name } </strong> (# ${ msg . event _id } )</div><div>Start: <code> ${ msg . start _time _ms . toFixed ( 3 ) } </code></div> ` ;
} ) ;
} ) ( ) ;