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 <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-04-09 15:24:26 +00:00
parent e223128565
commit 84e5d55592
4 changed files with 48 additions and 60 deletions

View File

@@ -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 = '<i class="bi bi-check-circle"></i>';
b.className = 'btn btn-xs btn-success';
setTimeout(() => loadDashOrders(), 1500);
} else {
b.innerHTML = '<i class="bi bi-exclamation-triangle"></i>';
b.className = 'btn btn-xs btn-danger';
b.title = data.message || 'Eroare';
setTimeout(() => {
b.innerHTML = '<i class="bi bi-arrow-repeat"></i>';
b.className = 'btn btn-xs btn-outline-warning';
b.disabled = false;
b.title = 'Resync';
}, 3000);
}
} catch (err) {
b.innerHTML = '<i class="bi bi-exclamation-triangle"></i>';
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: '<i class="bi bi-arrow-repeat"></i>',
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 = '<i class="bi bi-check-circle"></i>';
b.className = 'btn btn-xs btn-danger';
setTimeout(() => loadDashOrders(), 1500);
} else {
b.innerHTML = '<i class="bi bi-exclamation-triangle"></i>';
b.className = 'btn btn-xs btn-danger';
b.title = data.message || 'Eroare';
setTimeout(() => {
b.innerHTML = '<i class="bi bi-trash"></i>';
b.className = 'btn btn-xs btn-outline-danger';
b.disabled = false;
b.title = 'Sterge din ROA';
}, 3000);
}
} catch (err) {
b.innerHTML = '<i class="bi bi-exclamation-triangle"></i>';
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: '<i class="bi bi-trash"></i>',
loadingText: '',
confirmClass: 'btn-danger',
defaultBtnClass: 'btn-outline-danger'
});
} catch (err) {
if (row) row.style.opacity = '';
alert('Eroare conexiune la stergere');
}
}

View File

@@ -751,6 +751,7 @@ async function renderOrderDetailModal(orderNumber, opts) {
if (data.success) {
retryBtn.innerHTML = '<i class="bi bi-check-circle"></i> ' + (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 = '<i class="bi bi-check-circle"></i> Reimportat';
btn.className = 'btn btn-sm btn-success';
if (opts.onStatusChange) opts.onStatusChange();
setTimeout(() => renderOrderDetailModal(orderNumber, opts), 1500);
} else {
btn.innerHTML = '<i class="bi bi-exclamation-triangle"></i> ' + (data.message || 'Eroare');
@@ -845,6 +847,7 @@ async function renderOrderDetailModal(orderNumber, opts) {
if (data.success) {
btn.innerHTML = '<i class="bi bi-check-circle"></i> Sters';
btn.className = 'btn btn-sm btn-danger';
if (opts.onStatusChange) opts.onStatusChange();
setTimeout(() => renderOrderDetailModal(orderNumber, opts), 1500);
} else {
btn.innerHTML = '<i class="bi bi-exclamation-triangle"></i> ' + (data.message || 'Eroare');

View File

@@ -169,7 +169,7 @@
<script>window.ROOT_PATH = "{{ rp }}";</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="{{ rp }}/static/js/shared.js?v=44"></script>
<script src="{{ rp }}/static/js/shared.js?v=45"></script>
<script>
// Dark mode toggle
function toggleDarkMode() {

View File

@@ -115,5 +115,5 @@
{% endblock %}
{% block scripts %}
<script src="{{ request.scope.get('root_path', '') }}/static/js/dashboard.js?v=48"></script>
<script src="{{ request.scope.get('root_path', '') }}/static/js/dashboard.js?v=49"></script>
{% endblock %}