From 5149ce140c2135db3b27be9ab3f591bb65a7edf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Sat, 18 Oct 2025 23:46:21 +0200 Subject: [PATCH] refator_comm1 --- static/js/admin.js | 62 +++++++++++++++++++++++++--------------------- static/styles.css | 33 +++++++++++------------- 2 files changed, 48 insertions(+), 47 deletions(-) diff --git a/static/js/admin.js b/static/js/admin.js index 2ae615a..572938d 100644 --- a/static/js/admin.js +++ b/static/js/admin.js @@ -37,7 +37,7 @@ function setBadgeCell(cell, textOrState) { let html = ''; const s = low(textOrState); if (/running|online|started/.test(s)) html = badge('running','ok'); - else if (/stopp|shutdown|offline/.test(s)) html = badge('stopped','dark'); + else if (/stopp|shutdown|offline|stopped/.test(s)) html = badge('stopped','dark'); else if (/working|progress|busy/.test(s)) html = badge('working','info'); else html = badge(textOrState || '—','dark'); if (cell.innerHTML !== html) { cell.innerHTML = html; return true; } @@ -55,25 +55,22 @@ function rebuildTargetSelect(selectEl, currentNode, nodes) { if (!selectEl) return []; const current = String(currentNode || '').trim(); const all = (nodes || []).map(n => String(n && (n.name || n.node || n)).trim()).filter(Boolean); - const others = all.filter(n => n !== current); + const others = all.filter(n => n && n !== current); selectEl.innerHTML = others.map(n => ``).join(''); - // default: pick the "second node" if exists, otherwise first available other - var idx = 0; - if (all.length >= 2) { - const preferred = all[1]; - const j = others.indexOf(preferred); - if (j >= 0) idx = j; - } - if (selectEl.options.length > 0) selectEl.selectedIndex = idx; + // wybierz pierwszy sensowny inny node + if (selectEl.options.length > 0) selectEl.selectedIndex = 0; return others; } -function updateMigrateButton(tr, isRunning) { +function updateMigrateButton(tr, rawStatus) { const btn = q(tr, '.act-migrate'); const targetSel = q(tr, '.target-node'); - const hasTarget = !!(targetSel && targetSel.options && targetSel.options.length > 0); - const enable = !!(isRunning && hasTarget); if (!btn) return; + const hasTarget = !!(targetSel && targetSel.options && targetSel.options.length > 0); + const s = low(rawStatus || ''); + const busy = /working|progress|busy/.test(s); + // pozwól offline i online migrate — byle jest docelowy węzeł i nie trwa inna akcja + const enable = hasTarget && !busy && s !== ''; if (enable) { btn.removeAttribute('disabled'); btn.classList.remove('disabled'); } else { btn.setAttribute('disabled',''); btn.classList.add('disabled'); } } @@ -119,11 +116,13 @@ function ensureWatchOn() { const nodeCell = tr2.children[3]; const targetSel = q(tr2, '.target-node'); - if (msg.type === 'status') { - const stRaw = low(msg.status); + // dopasowane do serwera: type="vm" i "moved" + if (msg.type === 'vm') { + const cur = msg.current || {}; + const stRaw = low(cur.status || cur.qmpstatus || ''); const changed = setBadgeCell(statusCell, stRaw); const isRunning = /running|online|started/.test(stRaw); - updateMigrateButton(tr2, isRunning); + updateMigrateButton(tr2, stRaw); updateActionButtons(tr2, isRunning); if (changed) flashDot(nameCell); if (stRaw && /running|stopped|shutdown/.test(stRaw)) { @@ -131,8 +130,8 @@ function ensureWatchOn() { } } - if (msg.type === 'node' && msg.node) { - const newNode = String(msg.node).trim(); + if (msg.type === 'moved' && msg.new_node) { + const newNode = String(msg.new_node).trim(); if (nodeCell && newNode && txt(nodeCell).trim() !== newNode) { nodeCell.textContent = newNode; rebuildTargetSelect(targetSel, newNode, window.__nodesCache || []); @@ -191,7 +190,10 @@ export async function startAdminWatches() { const targetSel = q(tr, '.target-node'); const currentNode = txt(nodeCell).trim(); rebuildTargetSelect(targetSel, currentNode, availableNodes); - updateMigrateButton(tr, /running|online|started/i.test(tr.children[4].innerText)); + const stRaw = low(tr.children[4].innerText); + const isRunning = /running|online|started/.test(stRaw); + updateMigrateButton(tr, stRaw); + updateActionButtons(tr, isRunning); }); // delegated events (reliable after refreshes) @@ -199,21 +201,24 @@ export async function startAdminWatches() { const t = ev.target; const btn = t && t.closest ? t.closest('.act-start,.act-stop,.act-shutdown,.act-unlock,.act-migrate') : null; if (!btn) return; + ev.preventDefault(); ev.stopPropagation(); // <— kluczowe, żeby zawsze doszedł POST const tr = btn.closest ? btn.closest('tr[data-sid]') : null; if (!tr) return; const sid = tr.getAttribute('data-sid'); const nameCell = tr.children[2]; + const statusCell = tr.children[4]; const targetSel = q(tr, '.target-node'); + async function doAction(kind, needsTarget) { try { const targetNode = needsTarget ? (targetSel ? targetSel.value : undefined) : undefined; activeSids.add(sid); - setBadgeCell(tr.children[4], 'working'); - updateMigrateButton(tr, false); + setBadgeCell(statusCell, 'working'); + updateMigrateButton(tr, 'working'); const res = await api.vmAction(sid, kind, targetNode); - if (res && res.ok) { showToast(`Task ${kind} started for ${safe(txt(nameCell))}`); } - else { showToast(`Task ${kind} failed for ${safe(txt(nameCell))}`, 'danger'); } - } catch(e) { showToast(`Error: ${e && e.message ? e.message : e}`, 'danger'); } + if (res && res.ok) { showToast('OK', `Task ${kind} started for ${safe(txt(nameCell))}`, 'success'); } + else { showToast('Error', `Task ${kind} failed for ${safe(txt(nameCell))}`, 'danger'); } + } catch(e) { showToast('Error', String(e && e.message ? e.message : e), 'danger'); } } if (btn.classList.contains('act-start')) return doAction('start'); if (btn.classList.contains('act-stop')) return doAction('stop'); @@ -255,7 +260,7 @@ export async function startAdminWatches() { if (stRaw) { const changed = setBadgeCell(statusCell, stRaw); const isRunning = /running|online|started/.test(stRaw); - updateMigrateButton(tr, isRunning); + updateMigrateButton(tr, stRaw); updateActionButtons(tr, isRunning); if (changed) flashDot(nameCell); } @@ -284,7 +289,7 @@ export async function startAdminWatches() { const stRaw = low((detail.current && (detail.current.status || detail.current.qmpstatus)) || ''); const changed = setBadgeCell(statusCell, stRaw); const isRunning = /running|online|started/.test(stRaw); - updateMigrateButton(tr, isRunning); + updateMigrateButton(tr, stRaw); updateActionButtons(tr, isRunning); if (changed) flashDot(nameCell); @@ -304,9 +309,10 @@ export async function startAdminWatches() { } catch(e){} }, 10000); + ensureWatchOn(); window.addEventListener('beforeunload', stopAllAdminWatches, { once: true }); } catch (e) { - showToast(`Failed to load list: ${e && e.message ? e.message : e}`, 'danger'); + showToast('Error', `Failed to load list: ${e && e.message ? e.message : e}`, 'danger'); } } @@ -314,7 +320,7 @@ export async function startAdminWatches() { export async function renderVMAdmin() { try { await startAdminWatches(); } catch (e) { - showToast(`VM Admin initialization error: ${e && e.message ? e.message : e}`, 'danger'); + showToast('Error', `VM Admin initialization error: ${e && e.message ? e.message : e}`, 'danger'); console.error(e); } } diff --git a/static/styles.css b/static/styles.css index 0b03b80..9738582 100644 --- a/static/styles.css +++ b/static/styles.css @@ -144,17 +144,6 @@ footer.site-footer a:hover { } } -#toast-container { - position: fixed; - right: max(env(safe-area-inset-right), 1rem); - bottom: max(env(safe-area-inset-bottom), 1rem); - z-index: 1080; - pointer-events: none; -} - -#toast-container .toast { - pointer-events: auto; -} #vm-admin table { overflow: visible; @@ -169,14 +158,20 @@ footer.site-footer a:hover { z-index: 3; } -#vm-admin .btn.btn-sm { - line-height: 1.2; +#toast-container { + position: fixed; + right: max(env(safe-area-inset-right), 1rem); + bottom: max(env(safe-area-inset-bottom), 1rem); + z-index: 1080; + pointer-events: none; + width: min(480px, 96vw); + max-width: min(480px, 96vw); } -#vm-admin .btn-group .btn { - min-width: 4.2rem; -} - -#vm-admin .act-migrate { - white-space: nowrap; +#toast-container .toast { + pointer-events: auto; + max-width: 100%; + overflow-wrap: anywhere; + word-break: break-word; + white-space: normal; } \ No newline at end of file