207 lines
7.9 KiB
JavaScript
207 lines
7.9 KiB
JavaScript
// ================================================================
|
||
// 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" style="color:#555;font-size:0.85rem;padding:12px 0;">No commands sent yet.</p>';
|
||
return;
|
||
}
|
||
|
||
const rows = cmds.map(cmd => {
|
||
const p = typeof cmd.payload === 'string' ? JSON.parse(cmd.payload) : cmd.payload;
|
||
let desc = '';
|
||
if (cmd.type === 'build') {
|
||
const nameGr = window.BUILDING_NAMES_GR?.[p.building_id] || p.building_id;
|
||
desc = `Build: ${nameGr}`;
|
||
} else if (cmd.type === 'recruit') {
|
||
const nameGr = window.UNIT_NAMES_GR?.[p.unit_id] || p.unit_id;
|
||
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>
|
||
<td>${desc}</td>
|
||
<td><span class="status-badge ${statusClass}">${cmd.status}</span></td>
|
||
<td style="color:#666;font-size:0.72rem">${cmd.result_msg || ''}</td>
|
||
<td>${cancelBtn}</td>
|
||
</tr>`;
|
||
}).join('');
|
||
|
||
el.innerHTML = `<table>
|
||
<thead><tr>
|
||
<th>#</th><th>Town</th><th>Command</th><th>Status</th><th>Result</th><th></th>
|
||
</tr></thead>
|
||
<tbody>${rows}</tbody>
|
||
</table>`;
|
||
};
|