fix(ui): show Reimporta button on DELETED_IN_ROA orders

mark_order_deleted_in_roa wipes order_items, so renderOrderDetailModal
hit the items.length===0 early-return BEFORE configuring footer buttons —
leaving DELETED_IN_ROA orders with no way to retry from the UI.

Extract the 3 button configurators (Retry/Resync/Delete) into
_configureDetailButtons() and call it before the early-return. Also
fire onAfterRender on the empty-items path for consistency.

Cache-bust shared.js v47 → v48.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-05-04 07:28:55 +00:00
parent 18acfd2226
commit b13d9a466c
2 changed files with 143 additions and 138 deletions

View File

@@ -601,6 +601,143 @@ function _renderReceipt(items, order) {
}
// ── Order Detail Modal (shared) ──────────────────
/**
function _configureDetailButtons(order, orderNumber, opts) {
const status = (order.status || '').toUpperCase();
const isInvoiced = !!(order.factura_numar);
const retryBtn = document.getElementById('detailRetryBtn');
if (retryBtn) {
const canRetry = [ORDER_STATUS.ERROR, ORDER_STATUS.SKIPPED, ORDER_STATUS.DELETED_IN_ROA].includes(status);
retryBtn.style.display = canRetry ? '' : 'none';
if (canRetry) {
retryBtn.onclick = async () => {
retryBtn.disabled = true;
retryBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span> Reimportare...';
try {
const res = await fetch(`/api/orders/${encodeURIComponent(orderNumber)}/retry`, { method: 'POST' });
const data = await res.json();
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();
setTimeout(() => renderOrderDetailModal(orderNumber, opts), 1500);
} else {
retryBtn.innerHTML = '<i class="bi bi-exclamation-triangle"></i> ' + (data.message || 'Eroare');
retryBtn.className = 'btn btn-sm btn-danger';
setTimeout(() => {
retryBtn.innerHTML = '<i class="bi bi-arrow-clockwise"></i> Reimporta';
retryBtn.className = 'btn btn-sm btn-outline-primary';
retryBtn.disabled = false;
}, 3000);
}
} catch (err) {
retryBtn.innerHTML = 'Eroare: ' + err.message;
retryBtn.disabled = false;
}
};
}
}
const resyncBtn = document.getElementById('detailResyncBtn');
if (resyncBtn) {
const canResync = [ORDER_STATUS.IMPORTED, ORDER_STATUS.ALREADY_IMPORTED].includes(status);
resyncBtn.style.display = canResync ? '' : 'none';
if (canResync) {
if (isInvoiced) {
resyncBtn.disabled = true;
resyncBtn.style.opacity = '0.5';
resyncBtn.style.pointerEvents = 'none';
resyncBtn.title = 'Comanda facturata';
} else {
resyncBtn.disabled = false;
resyncBtn.style.opacity = '';
resyncBtn.style.pointerEvents = '';
resyncBtn.title = '';
resyncBtn.onclick = () => {
inlineConfirmAction(resyncBtn, 'Confirmi resync?', async (btn) => {
try {
const res = await fetch(`/api/orders/${encodeURIComponent(orderNumber)}/resync`, { method: 'POST' });
const data = await res.json();
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');
btn.className = 'btn btn-sm btn-danger';
setTimeout(() => {
btn.innerHTML = '<i class="bi bi-arrow-repeat"></i> Resync';
btn.className = 'btn btn-sm btn-outline-warning';
btn.disabled = false;
}, 3000);
}
} catch (err) {
btn.innerHTML = 'Eroare: ' + err.message;
btn.disabled = false;
}
}, {
defaultHtml: '<i class="bi bi-arrow-repeat"></i> Resync',
loadingText: 'Resync...',
confirmClass: 'btn-warning',
defaultBtnClass: 'btn-outline-warning'
});
};
}
}
}
const deleteBtn = document.getElementById('detailDeleteBtn');
if (deleteBtn) {
const canDelete = [ORDER_STATUS.IMPORTED, ORDER_STATUS.ALREADY_IMPORTED].includes(status);
deleteBtn.style.display = canDelete ? '' : 'none';
if (canDelete) {
if (isInvoiced) {
deleteBtn.disabled = true;
deleteBtn.style.opacity = '0.5';
deleteBtn.style.pointerEvents = 'none';
deleteBtn.title = 'Comanda facturata';
} else {
deleteBtn.disabled = false;
deleteBtn.style.opacity = '';
deleteBtn.style.pointerEvents = '';
deleteBtn.title = '';
deleteBtn.onclick = () => {
inlineConfirmAction(deleteBtn, 'Confirmi stergerea?', async (btn) => {
try {
const res = await fetch(`/api/orders/${encodeURIComponent(orderNumber)}/delete`, { method: 'POST' });
const data = await res.json();
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');
btn.className = 'btn btn-sm btn-danger';
setTimeout(() => {
btn.innerHTML = '<i class="bi bi-trash"></i> Sterge din ROA';
btn.className = 'btn btn-sm btn-outline-danger';
btn.disabled = false;
}, 3000);
}
} catch (err) {
btn.innerHTML = 'Eroare: ' + err.message;
btn.disabled = false;
}
}, {
defaultHtml: '<i class="bi bi-trash"></i> Sterge din ROA',
loadingText: 'Stergere...',
confirmClass: 'btn-danger',
defaultBtnClass: 'btn-outline-danger'
});
};
}
}
}
}
/**
* Render and show the order detail modal.
* @param {string} orderNumber
@@ -717,9 +854,14 @@ async function renderOrderDetailModal(orderNumber, opts) {
document.getElementById('detailError').style.display = '';
}
// Configure footer action buttons BEFORE any early-return on items —
// DELETED_IN_ROA orders have no items but must still expose the Reimporta button.
_configureDetailButtons(order, orderNumber, opts);
const items = data.items || [];
if (items.length === 0) {
document.getElementById('detailItemsBody').innerHTML = '<tr><td colspan="9" class="text-center text-muted">Niciun articol</td></tr>';
if (opts.onAfterRender) opts.onAfterRender(order, items);
return;
}
@@ -853,143 +995,6 @@ async function renderOrderDetailModal(orderNumber, opts) {
document.getElementById('detailItemsBody').innerHTML = tableHtml;
_renderReceipt(items, order);
// Retry button (only for ERROR/SKIPPED orders)
const retryBtn = document.getElementById('detailRetryBtn');
if (retryBtn) {
const canRetry = [ORDER_STATUS.ERROR, ORDER_STATUS.SKIPPED, ORDER_STATUS.DELETED_IN_ROA].includes((order.status || '').toUpperCase());
retryBtn.style.display = canRetry ? '' : 'none';
if (canRetry) {
retryBtn.onclick = async () => {
retryBtn.disabled = true;
retryBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span> Reimportare...';
try {
const res = await fetch(`/api/orders/${encodeURIComponent(orderNumber)}/retry`, { method: 'POST' });
const data = await res.json();
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 {
retryBtn.innerHTML = '<i class="bi bi-exclamation-triangle"></i> ' + (data.message || 'Eroare');
retryBtn.className = 'btn btn-sm btn-danger';
setTimeout(() => {
retryBtn.innerHTML = '<i class="bi bi-arrow-clockwise"></i> Reimporta';
retryBtn.className = 'btn btn-sm btn-outline-primary';
retryBtn.disabled = false;
}, 3000);
}
} catch (err) {
retryBtn.innerHTML = 'Eroare: ' + err.message;
retryBtn.disabled = false;
}
};
}
}
// Resync button (IMPORTED/ALREADY_IMPORTED only)
const resyncBtn = document.getElementById('detailResyncBtn');
if (resyncBtn) {
const canResync = [ORDER_STATUS.IMPORTED, ORDER_STATUS.ALREADY_IMPORTED].includes((order.status || '').toUpperCase());
resyncBtn.style.display = canResync ? '' : 'none';
if (canResync) {
const isInvoiced = !!(order.factura_numar);
if (isInvoiced) {
resyncBtn.disabled = true;
resyncBtn.style.opacity = '0.5';
resyncBtn.style.pointerEvents = 'none';
resyncBtn.title = 'Comanda facturata';
} else {
resyncBtn.disabled = false;
resyncBtn.style.opacity = '';
resyncBtn.style.pointerEvents = '';
resyncBtn.title = '';
resyncBtn.onclick = () => {
inlineConfirmAction(resyncBtn, 'Confirmi resync?', async (btn) => {
try {
const res = await fetch(`/api/orders/${encodeURIComponent(orderNumber)}/resync`, { method: 'POST' });
const data = await res.json();
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');
btn.className = 'btn btn-sm btn-danger';
setTimeout(() => {
btn.innerHTML = '<i class="bi bi-arrow-repeat"></i> Resync';
btn.className = 'btn btn-sm btn-outline-warning';
btn.disabled = false;
}, 3000);
}
} catch (err) {
btn.innerHTML = 'Eroare: ' + err.message;
btn.disabled = false;
}
}, {
defaultHtml: '<i class="bi bi-arrow-repeat"></i> Resync',
loadingText: 'Resync...',
confirmClass: 'btn-warning',
defaultBtnClass: 'btn-outline-warning'
});
};
}
}
}
// Delete button (IMPORTED/ALREADY_IMPORTED only)
const deleteBtn = document.getElementById('detailDeleteBtn');
if (deleteBtn) {
const canDelete = [ORDER_STATUS.IMPORTED, ORDER_STATUS.ALREADY_IMPORTED].includes((order.status || '').toUpperCase());
deleteBtn.style.display = canDelete ? '' : 'none';
if (canDelete) {
const isInvoiced = !!(order.factura_numar);
if (isInvoiced) {
deleteBtn.disabled = true;
deleteBtn.style.opacity = '0.5';
deleteBtn.style.pointerEvents = 'none';
deleteBtn.title = 'Comanda facturata';
} else {
deleteBtn.disabled = false;
deleteBtn.style.opacity = '';
deleteBtn.style.pointerEvents = '';
deleteBtn.title = '';
deleteBtn.onclick = () => {
inlineConfirmAction(deleteBtn, 'Confirmi stergerea?', async (btn) => {
try {
const res = await fetch(`/api/orders/${encodeURIComponent(orderNumber)}/delete`, { method: 'POST' });
const data = await res.json();
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');
btn.className = 'btn btn-sm btn-danger';
setTimeout(() => {
btn.innerHTML = '<i class="bi bi-trash"></i> Sterge din ROA';
btn.className = 'btn btn-sm btn-outline-danger';
btn.disabled = false;
}, 3000);
}
} catch (err) {
btn.innerHTML = 'Eroare: ' + err.message;
btn.disabled = false;
}
}, {
defaultHtml: '<i class="bi bi-trash"></i> Sterge din ROA',
loadingText: 'Stergere...',
confirmClass: 'btn-danger',
defaultBtnClass: 'btn-outline-danger'
});
};
}
}
}
if (opts.onAfterRender) opts.onAfterRender(order, items);
} catch (err) {
document.getElementById('detailError').textContent = err.message;

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=47"></script>
<script src="{{ rp }}/static/js/shared.js?v=48"></script>
<script>
// Dark mode toggle
function toggleDarkMode() {