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:
@@ -572,6 +572,7 @@ function openDashOrderDetail(orderNumber) {
|
|||||||
_sharedModalQuickMapFn = openDashQuickMap;
|
_sharedModalQuickMapFn = openDashQuickMap;
|
||||||
renderOrderDetailModal(orderNumber, {
|
renderOrderDetailModal(orderNumber, {
|
||||||
onQuickMap: openDashQuickMap,
|
onQuickMap: openDashQuickMap,
|
||||||
|
onStatusChange: loadDashOrders,
|
||||||
onAfterRender: function() { /* nothing extra needed */ }
|
onAfterRender: function() { /* nothing extra needed */ }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -598,67 +599,51 @@ function openDashQuickMap(sku, productName, orderNumber, itemIdx) {
|
|||||||
|
|
||||||
// ── Dashboard row action handlers ────────────────
|
// ── Dashboard row action handlers ────────────────
|
||||||
|
|
||||||
function dashResyncOrder(orderNumber, btn) {
|
async function dashResyncOrder(orderNumber, btn) {
|
||||||
inlineConfirmAction(btn, '?', async (b) => {
|
// Close dropdown immediately
|
||||||
try {
|
const dd = btn.closest('.dropdown-menu');
|
||||||
const res = await fetch(`/api/orders/${encodeURIComponent(orderNumber)}/resync`, { method: 'POST' });
|
if (dd) bootstrap.Dropdown.getInstance(dd.previousElementSibling)?.hide();
|
||||||
const data = await res.json();
|
// Find the table row for visual feedback
|
||||||
if (data.success) {
|
const row = document.querySelector(`tr[data-order="${orderNumber}"]`) ||
|
||||||
b.innerHTML = '<i class="bi bi-check-circle"></i>';
|
btn.closest('tr');
|
||||||
b.className = 'btn btn-xs btn-success';
|
try {
|
||||||
setTimeout(() => loadDashOrders(), 1500);
|
if (row) row.style.opacity = '0.5';
|
||||||
} else {
|
const res = await fetch(`/api/orders/${encodeURIComponent(orderNumber)}/resync`, { method: 'POST' });
|
||||||
b.innerHTML = '<i class="bi bi-exclamation-triangle"></i>';
|
const data = await res.json();
|
||||||
b.className = 'btn btn-xs btn-danger';
|
if (data.success) {
|
||||||
b.title = data.message || 'Eroare';
|
loadDashOrders();
|
||||||
setTimeout(() => {
|
} else {
|
||||||
b.innerHTML = '<i class="bi bi-arrow-repeat"></i>';
|
if (row) row.style.opacity = '';
|
||||||
b.className = 'btn btn-xs btn-outline-warning';
|
alert(data.message || 'Eroare la resync');
|
||||||
b.disabled = false;
|
|
||||||
b.title = 'Resync';
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
b.innerHTML = '<i class="bi bi-exclamation-triangle"></i>';
|
|
||||||
b.disabled = false;
|
|
||||||
}
|
}
|
||||||
}, {
|
} catch (err) {
|
||||||
defaultHtml: '<i class="bi bi-arrow-repeat"></i>',
|
if (row) row.style.opacity = '';
|
||||||
loadingText: '',
|
alert('Eroare conexiune la resync');
|
||||||
confirmClass: 'btn-warning',
|
}
|
||||||
defaultBtnClass: 'btn-outline-warning'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function dashDeleteOrder(orderNumber, btn) {
|
async function dashDeleteOrder(orderNumber, btn) {
|
||||||
inlineConfirmAction(btn, '?', async (b) => {
|
// Close dropdown immediately
|
||||||
try {
|
const dd = btn.closest('.dropdown-menu');
|
||||||
const res = await fetch(`/api/orders/${encodeURIComponent(orderNumber)}/delete`, { method: 'POST' });
|
if (dd) bootstrap.Dropdown.getInstance(dd.previousElementSibling)?.hide();
|
||||||
const data = await res.json();
|
// Confirm before delete
|
||||||
if (data.success) {
|
if (!confirm(`Stergi comanda ${orderNumber} din ROA?`)) return;
|
||||||
b.innerHTML = '<i class="bi bi-check-circle"></i>';
|
// Find the table row for visual feedback
|
||||||
b.className = 'btn btn-xs btn-danger';
|
const row = document.querySelector(`tr[data-order="${orderNumber}"]`) ||
|
||||||
setTimeout(() => loadDashOrders(), 1500);
|
btn.closest('tr');
|
||||||
} else {
|
try {
|
||||||
b.innerHTML = '<i class="bi bi-exclamation-triangle"></i>';
|
if (row) row.style.opacity = '0.5';
|
||||||
b.className = 'btn btn-xs btn-danger';
|
const res = await fetch(`/api/orders/${encodeURIComponent(orderNumber)}/delete`, { method: 'POST' });
|
||||||
b.title = data.message || 'Eroare';
|
const data = await res.json();
|
||||||
setTimeout(() => {
|
if (data.success) {
|
||||||
b.innerHTML = '<i class="bi bi-trash"></i>';
|
loadDashOrders();
|
||||||
b.className = 'btn btn-xs btn-outline-danger';
|
} else {
|
||||||
b.disabled = false;
|
if (row) row.style.opacity = '';
|
||||||
b.title = 'Sterge din ROA';
|
alert(data.message || 'Eroare la stergere');
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
b.innerHTML = '<i class="bi bi-exclamation-triangle"></i>';
|
|
||||||
b.disabled = false;
|
|
||||||
}
|
}
|
||||||
}, {
|
} catch (err) {
|
||||||
defaultHtml: '<i class="bi bi-trash"></i>',
|
if (row) row.style.opacity = '';
|
||||||
loadingText: '',
|
alert('Eroare conexiune la stergere');
|
||||||
confirmClass: 'btn-danger',
|
}
|
||||||
defaultBtnClass: 'btn-outline-danger'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -751,6 +751,7 @@ async function renderOrderDetailModal(orderNumber, opts) {
|
|||||||
if (data.success) {
|
if (data.success) {
|
||||||
retryBtn.innerHTML = '<i class="bi bi-check-circle"></i> ' + (data.message || 'Reimportat');
|
retryBtn.innerHTML = '<i class="bi bi-check-circle"></i> ' + (data.message || 'Reimportat');
|
||||||
retryBtn.className = 'btn btn-sm btn-success';
|
retryBtn.className = 'btn btn-sm btn-success';
|
||||||
|
if (opts.onStatusChange) opts.onStatusChange();
|
||||||
// Refresh modal after short delay
|
// Refresh modal after short delay
|
||||||
setTimeout(() => renderOrderDetailModal(orderNumber, opts), 1500);
|
setTimeout(() => renderOrderDetailModal(orderNumber, opts), 1500);
|
||||||
} else {
|
} else {
|
||||||
@@ -795,6 +796,7 @@ async function renderOrderDetailModal(orderNumber, opts) {
|
|||||||
if (data.success) {
|
if (data.success) {
|
||||||
btn.innerHTML = '<i class="bi bi-check-circle"></i> Reimportat';
|
btn.innerHTML = '<i class="bi bi-check-circle"></i> Reimportat';
|
||||||
btn.className = 'btn btn-sm btn-success';
|
btn.className = 'btn btn-sm btn-success';
|
||||||
|
if (opts.onStatusChange) opts.onStatusChange();
|
||||||
setTimeout(() => renderOrderDetailModal(orderNumber, opts), 1500);
|
setTimeout(() => renderOrderDetailModal(orderNumber, opts), 1500);
|
||||||
} else {
|
} else {
|
||||||
btn.innerHTML = '<i class="bi bi-exclamation-triangle"></i> ' + (data.message || 'Eroare');
|
btn.innerHTML = '<i class="bi bi-exclamation-triangle"></i> ' + (data.message || 'Eroare');
|
||||||
@@ -845,6 +847,7 @@ async function renderOrderDetailModal(orderNumber, opts) {
|
|||||||
if (data.success) {
|
if (data.success) {
|
||||||
btn.innerHTML = '<i class="bi bi-check-circle"></i> Sters';
|
btn.innerHTML = '<i class="bi bi-check-circle"></i> Sters';
|
||||||
btn.className = 'btn btn-sm btn-danger';
|
btn.className = 'btn btn-sm btn-danger';
|
||||||
|
if (opts.onStatusChange) opts.onStatusChange();
|
||||||
setTimeout(() => renderOrderDetailModal(orderNumber, opts), 1500);
|
setTimeout(() => renderOrderDetailModal(orderNumber, opts), 1500);
|
||||||
} else {
|
} else {
|
||||||
btn.innerHTML = '<i class="bi bi-exclamation-triangle"></i> ' + (data.message || 'Eroare');
|
btn.innerHTML = '<i class="bi bi-exclamation-triangle"></i> ' + (data.message || 'Eroare');
|
||||||
|
|||||||
@@ -169,7 +169,7 @@
|
|||||||
|
|
||||||
<script>window.ROOT_PATH = "{{ rp }}";</script>
|
<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="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>
|
<script>
|
||||||
// Dark mode toggle
|
// Dark mode toggle
|
||||||
function toggleDarkMode() {
|
function toggleDarkMode() {
|
||||||
|
|||||||
@@ -115,5 +115,5 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% 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 %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user