@@ -1,11 +1,171 @@
// ================================================================
// Command Log Component
// Command Log & Build Queue Component
// ================================================================
// -- Panel state: 'queue' | 'log' ----------------------------
window . _logPanelMode = 'queue' ;
// ---- Toggle buttons -------------------------------------------
window . switchToQueueMode = function ( ) {
window . _logPanelMode = 'queue' ;
document . getElementById ( 'tab-queue' ) . classList . add ( 'tab-active' ) ;
document . getElementById ( 'tab-log' ) . classList . remove ( 'tab-active' ) ;
window . refreshLogPanel ( ) ;
} ;
window . switchToLogMode = function ( ) {
window . _logPanelMode = 'log' ;
document . getElementById ( 'tab-log' ) . classList . add ( 'tab-active' ) ;
document . getElementById ( 'tab-queue' ) . classList . remove ( 'tab-active' ) ;
window . fetchLog ( ) ;
} ;
// ---- Main dispatcher ------------------------------------------
window . refreshLogPanel = function ( ) {
if ( window . _logPanelMode === 'queue' ) {
const town = window . getSelectedTown ( ) ;
if ( town ) {
window . fetchBuildQueue ( town . town _id ) ;
} else {
document . getElementById ( 'log-content' ) . innerHTML =
'<p style="color:#555;font-size:0.85rem;padding:12px 0;">← Επιλέξτε πόλη γ ι α ν α δείτε την ουρά.</p>' ;
}
}
} ;
// ================================================================
// BUILD QUEUE (per-town, draggable)
// ================================================================
window . fetchBuildQueue = async function ( townId ) {
if ( window . _logPanelMode !== 'queue' ) return ;
try {
const res = await fetch ( ` /dashboard/commands/queue?player_id= ${ window . PLAYER _ID } &town_id= ${ encodeURIComponent ( townId ) } ` ) ;
const cmds = await res . json ( ) ;
window . renderBuildQueue ( cmds , townId ) ;
} catch ( e ) { }
} ;
// Drag state
let _dragSrcIdx = null ;
window . renderBuildQueue = function ( cmds , townId ) {
const el = document . getElementById ( 'log-content' ) ;
if ( ! cmds || cmds . length === 0 ) {
el . innerHTML = `
<div style="text-align:center;padding:2rem 1rem;color:#444;">
<div style="font-size:2rem;margin-bottom:0.5rem;">🏗️</div>
<p style="font-size:0.85rem;">Η ουρά κατασκευών είναι κενή.</p>
<p style="font-size:0.75rem;color:#333;margin-top:0.3rem;">Χρησιμοποιήστε την φόρμα γ ι α ν α προσθέσετε κατασκευές.</p>
</div> ` ;
return ;
}
const rows = cmds . map ( ( cmd , idx ) => {
const p = typeof cmd . payload === 'string' ? JSON . parse ( cmd . payload ) : cmd . payload ;
const nameGr = window . BUILDING _NAMES _GR ? . [ p . building _id ] || p . building _id || '?' ;
const icon = window . BUILDING _ICONS ? . [ p . building _id ] || '🏗️' ;
const isExec = cmd . status === 'executing' ;
const statusDot = isExec
? ` <span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#4acc64;box-shadow:0 0 5px #4acc64;flex-shrink:0;" title="Εκτελείται"></span> `
: ` <span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#555;flex-shrink:0;" title="Σε αναμονή"></span> ` ;
return `
<div class="bq-row" draggable="true"
data-idx=" ${ idx } " data-id=" ${ cmd . id } " data-town=" ${ townId } "
ondragstart="window._bqDragStart(event, ${ idx } )"
ondragover="window._bqDragOver(event)"
ondrop="window._bqDrop(event, ${ idx } ,' ${ townId } ')"
ondragend="window._bqDragEnd(event)">
<span class="bq-handle" title="Σύρε γ ι α αναδιάταξη">⠿</span>
<span class="bq-pos"> ${ idx + 1 } </span>
${ statusDot }
<span class="bq-icon"> ${ icon } </span>
<span class="bq-name"> ${ nameGr } </span>
<button class="bq-cancel-btn" onclick="window._bqCancel( ${ cmd . id } )" title="Ακύρωση">✕</button>
</div> ` ;
} ) . join ( '' ) ;
el . innerHTML = ` <div id="bq-list"> ${ rows } </div> ` ;
} ;
// ---- Drag-and-drop handlers -----------------------------------
window . _bqDragStart = function ( e , idx ) {
_dragSrcIdx = idx ;
e . dataTransfer . effectAllowed = 'move' ;
setTimeout ( ( ) => {
const rows = document . querySelectorAll ( '.bq-row' ) ;
if ( rows [ idx ] ) rows [ idx ] . style . opacity = '0.4' ;
} , 0 ) ;
} ;
window . _bqDragOver = function ( e ) {
e . preventDefault ( ) ;
e . dataTransfer . dropEffect = 'move' ;
// Highlight target row
document . querySelectorAll ( '.bq-row' ) . forEach ( r => r . classList . remove ( 'bq-drag-over' ) ) ;
const row = e . currentTarget ;
if ( row ) row . classList . add ( 'bq-drag-over' ) ;
} ;
window . _bqDrop = function ( e , targetIdx , townId ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
if ( _dragSrcIdx === null || _dragSrcIdx === targetIdx ) return ;
// Re-order the DOM
const list = document . getElementById ( 'bq-list' ) ;
const rows = Array . from ( list . querySelectorAll ( '.bq-row' ) ) ;
const movedRow = rows . splice ( _dragSrcIdx , 1 ) [ 0 ] ;
rows . splice ( targetIdx , 0 , movedRow ) ;
// Update numbering & opacity
rows . forEach ( ( r , i ) => {
r . style . opacity = '1' ;
r . classList . remove ( 'bq-drag-over' ) ;
r . dataset . idx = i ;
r . querySelector ( '.bq-pos' ) . textContent = i + 1 ;
r . setAttribute ( 'ondragstart' , ` window._bqDragStart(event, ${ i } ) ` ) ;
r . setAttribute ( 'ondrop' , ` window._bqDrop(event, ${ i } ,' ${ townId } ') ` ) ;
} ) ;
list . innerHTML = '' ;
rows . forEach ( r => list . appendChild ( r ) ) ;
// Persist new order to server
const orderedIds = rows . map ( r => parseInt ( r . dataset . id ) ) ;
fetch ( '/dashboard/commands/reorder' , {
method : 'POST' ,
headers : { 'Content-Type' : 'application/json' } ,
body : JSON . stringify ( { player _id : window . PLAYER _ID , town _id : townId , order : orderedIds } )
} ) ;
_dragSrcIdx = null ;
} ;
window . _bqDragEnd = function ( e ) {
document . querySelectorAll ( '.bq-row' ) . forEach ( r => {
r . style . opacity = '1' ;
r . classList . remove ( 'bq-drag-over' ) ;
} ) ;
_dragSrcIdx = null ;
} ;
window . _bqCancel = async function ( id ) {
await fetch ( ` /dashboard/commands/ ${ id } ` , { method : 'DELETE' } ) ;
// Refresh the queue for the currently selected town
const town = window . getSelectedTown ( ) ;
if ( town ) window . fetchBuildQueue ( town . town _id ) ;
} ;
// ================================================================
// COMMAND LOG (full history, existing behaviour)
// ================================================================
window . renderLog = function ( cmds ) {
if ( window . _logPanelMode !== 'log' ) return ;
const el = document . getElementById ( 'log-content' ) ;
if ( ! cmds . length ) {
el . innerHTML = '<p id="empty-log">No commands sent yet.</p>' ;
el . innerHTML = '<p id="empty-log" style="color:#555;font-size:0.85rem;padding:12px 0;" >No commands sent yet.</p>' ;
return ;
}
@@ -20,12 +180,13 @@ window.renderLog = function(cmds) {
desc = ` Recruit: ${ p . amount } x ${ nameGr } ` ;
} else if ( cmd . type === 'market_offer' ) {
desc = ` Market: ${ p . offer } ${ p . offer _type } ➞ ${ p . demand } ${ p . demand _type } ` ;
} else {
desc = cmd . type ;
}
const statusClass = ` status- ${ cmd . status } ` ;
const cancelBtn = ` <button class="btn btn-danger btn-sm" onclick="cancelCommand( ${ cmd . id } )">✕</button> ` ;
const timeStr = new Date ( cmd . created _at + 'Z' ) . toLocaleTimeString ( [ ] , { hour : '2-digit' , minute : '2-digit' , second : '2-digit' , hour12 : false } ) ;
return ` <tr>
<td style="color:#888;font-size:0.75rem"># ${ cmd . id } <br><span style="font-size:0.65rem;color:#555;"> ${ timeStr } </span></td>
<td> ${ cmd . town _name || cmd . town _id } </td>