From 84e5d55592f7e7782b4ceaba61262339b6de6bed Mon Sep 17 00:00:00 2001 From: Claude Agent Date: Thu, 9 Apr 2026 15:24:26 +0000 Subject: [PATCH] fix(dashboard): fix kebab menu delete/resync and status dot refresh Kebab dropdown delete/resync used inlineConfirmAction which breaks inside Bootstrap dropdowns (dropdown closes on click, hiding confirm state). Replaced with confirm() dialog + direct async action with row feedback. Detail modal resync/delete/retry now trigger onStatusChange callback to refresh the orders table, so status dots update without page reload. Co-Authored-By: Claude Opus 4.5 --- api/app/static/js/dashboard.js | 101 +++++++++++++------------------ api/app/static/js/shared.js | 3 + api/app/templates/base.html | 2 +- api/app/templates/dashboard.html | 2 +- 4 files changed, 48 insertions(+), 60 deletions(-) diff --git a/api/app/static/js/dashboard.js b/api/app/static/js/dashboard.js index 0c9846c..0ff385d 100644 --- a/api/app/static/js/dashboard.js +++ b/api/app/static/js/dashboard.js @@ -572,6 +572,7 @@ function openDashOrderDetail(orderNumber) { _sharedModalQuickMapFn = openDashQuickMap; renderOrderDetailModal(orderNumber, { onQuickMap: openDashQuickMap, + onStatusChange: loadDashOrders, onAfterRender: function() { /* nothing extra needed */ } }); } @@ -598,67 +599,51 @@ function openDashQuickMap(sku, productName, orderNumber, itemIdx) { // ── Dashboard row action handlers ──────────────── -function dashResyncOrder(orderNumber, btn) { - inlineConfirmAction(btn, '?', async (b) => { - try { - const res = await fetch(`/api/orders/${encodeURIComponent(orderNumber)}/resync`, { method: 'POST' }); - const data = await res.json(); - if (data.success) { - b.innerHTML = ''; - b.className = 'btn btn-xs btn-success'; - setTimeout(() => loadDashOrders(), 1500); - } else { - b.innerHTML = ''; - b.className = 'btn btn-xs btn-danger'; - b.title = data.message || 'Eroare'; - setTimeout(() => { - b.innerHTML = ''; - b.className = 'btn btn-xs btn-outline-warning'; - b.disabled = false; - b.title = 'Resync'; - }, 3000); - } - } catch (err) { - b.innerHTML = ''; - b.disabled = false; +async function dashResyncOrder(orderNumber, btn) { + // Close dropdown immediately + const dd = btn.closest('.dropdown-menu'); + if (dd) bootstrap.Dropdown.getInstance(dd.previousElementSibling)?.hide(); + // Find the table row for visual feedback + const row = document.querySelector(`tr[data-order="${orderNumber}"]`) || + btn.closest('tr'); + try { + if (row) row.style.opacity = '0.5'; + const res = await fetch(`/api/orders/${encodeURIComponent(orderNumber)}/resync`, { method: 'POST' }); + const data = await res.json(); + if (data.success) { + loadDashOrders(); + } else { + if (row) row.style.opacity = ''; + alert(data.message || 'Eroare la resync'); } - }, { - defaultHtml: '', - loadingText: '', - confirmClass: 'btn-warning', - defaultBtnClass: 'btn-outline-warning' - }); + } catch (err) { + if (row) row.style.opacity = ''; + alert('Eroare conexiune la resync'); + } } -function dashDeleteOrder(orderNumber, btn) { - inlineConfirmAction(btn, '?', async (b) => { - try { - const res = await fetch(`/api/orders/${encodeURIComponent(orderNumber)}/delete`, { method: 'POST' }); - const data = await res.json(); - if (data.success) { - b.innerHTML = ''; - b.className = 'btn btn-xs btn-danger'; - setTimeout(() => loadDashOrders(), 1500); - } else { - b.innerHTML = ''; - b.className = 'btn btn-xs btn-danger'; - b.title = data.message || 'Eroare'; - setTimeout(() => { - b.innerHTML = ''; - b.className = 'btn btn-xs btn-outline-danger'; - b.disabled = false; - b.title = 'Sterge din ROA'; - }, 3000); - } - } catch (err) { - b.innerHTML = ''; - b.disabled = false; +async function dashDeleteOrder(orderNumber, btn) { + // Close dropdown immediately + const dd = btn.closest('.dropdown-menu'); + if (dd) bootstrap.Dropdown.getInstance(dd.previousElementSibling)?.hide(); + // Confirm before delete + if (!confirm(`Stergi comanda ${orderNumber} din ROA?`)) return; + // Find the table row for visual feedback + const row = document.querySelector(`tr[data-order="${orderNumber}"]`) || + btn.closest('tr'); + try { + if (row) row.style.opacity = '0.5'; + const res = await fetch(`/api/orders/${encodeURIComponent(orderNumber)}/delete`, { method: 'POST' }); + const data = await res.json(); + if (data.success) { + loadDashOrders(); + } else { + if (row) row.style.opacity = ''; + alert(data.message || 'Eroare la stergere'); } - }, { - defaultHtml: '', - loadingText: '', - confirmClass: 'btn-danger', - defaultBtnClass: 'btn-outline-danger' - }); + } catch (err) { + if (row) row.style.opacity = ''; + alert('Eroare conexiune la stergere'); + } } diff --git a/api/app/static/js/shared.js b/api/app/static/js/shared.js index c92151b..8edbcb5 100644 --- a/api/app/static/js/shared.js +++ b/api/app/static/js/shared.js @@ -751,6 +751,7 @@ async function renderOrderDetailModal(orderNumber, opts) { if (data.success) { retryBtn.innerHTML = ' ' + (data.message || 'Reimportat'); retryBtn.className = 'btn btn-sm btn-success'; + if (opts.onStatusChange) opts.onStatusChange(); // Refresh modal after short delay setTimeout(() => renderOrderDetailModal(orderNumber, opts), 1500); } else { @@ -795,6 +796,7 @@ async function renderOrderDetailModal(orderNumber, opts) { if (data.success) { btn.innerHTML = ' Reimportat'; btn.className = 'btn btn-sm btn-success'; + if (opts.onStatusChange) opts.onStatusChange(); setTimeout(() => renderOrderDetailModal(orderNumber, opts), 1500); } else { btn.innerHTML = ' ' + (data.message || 'Eroare'); @@ -845,6 +847,7 @@ async function renderOrderDetailModal(orderNumber, opts) { if (data.success) { btn.innerHTML = ' Sters'; btn.className = 'btn btn-sm btn-danger'; + if (opts.onStatusChange) opts.onStatusChange(); setTimeout(() => renderOrderDetailModal(orderNumber, opts), 1500); } else { btn.innerHTML = ' ' + (data.message || 'Eroare'); diff --git a/api/app/templates/base.html b/api/app/templates/base.html index 9e9d060..ca8fa95 100644 --- a/api/app/templates/base.html +++ b/api/app/templates/base.html @@ -169,7 +169,7 @@ - + + {% endblock %}