fix(dashboard): replace hover row-actions with kebab menu, fix modal button reset and delete color

- Bug 1: hover actions covered total column; replaced with kebab dropdown in dedicated 44px column
- Bug 2: resync/delete buttons kept stale state across modal opens; reset in modal init block
- Bug 3: delete success button was green (btn-success); changed to red (btn-danger)
- Dropdown styled per DESIGN.md: warm shadow, 8px radius, dark mode tokens

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-04-09 15:10:40 +00:00
parent 25f73db64d
commit e223128565
5 changed files with 35 additions and 21 deletions

View File

@@ -393,19 +393,27 @@ input[type="checkbox"] {
padding: 4px 8px;
}
/* Dashboard row hover actions */
#dashOrdersBody tr { position: relative; }
#dashOrdersBody tr .row-actions {
display: none;
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
gap: 4px;
z-index: 1;
/* Dashboard kebab dropdown */
.kebab-dropdown .btn { color: var(--text-muted); }
.kebab-dropdown .btn:hover { color: var(--text-secondary); }
.kebab-dropdown .dropdown-menu {
box-shadow: var(--card-shadow);
border-radius: 8px;
border: 1px solid var(--border);
background: var(--surface);
font-family: var(--font-body);
font-size: 13px;
min-width: 160px;
}
#dashOrdersBody tr:hover .row-actions {
display: inline-flex;
.kebab-dropdown .dropdown-item { font-size: 13px; padding: 6px 12px; }
.kebab-dropdown .dropdown-item:hover { background: var(--surface-raised); }
[data-theme="dark"] .kebab-dropdown .dropdown-menu {
background: var(--surface);
border-color: var(--border);
color: var(--text-primary);
}
[data-theme="dark"] .kebab-dropdown .dropdown-item:hover {
background: var(--surface-raised);
}
/* ── Forms ───────────────────────────────────────── */

View File

@@ -360,7 +360,7 @@ async function loadDashOrders() {
const orders = data.orders || [];
if (orders.length === 0) {
tbody.innerHTML = '<tr><td colspan="9" class="text-center text-muted py-3">Nicio comanda</td></tr>';
tbody.innerHTML = '<tr><td colspan="10" class="text-center text-muted py-3">Nicio comanda</td></tr>';
} else {
tbody.innerHTML = orders.map(o => {
const dateStr = fmtDate(o.order_date);
@@ -375,7 +375,8 @@ async function loadDashOrders() {
<td>${o.items_count || 0}</td>
<td class="text-end text-muted">${fmtCost(o.delivery_cost)}</td>
<td class="text-end text-muted">${fmtCost(o.discount_total)}</td>
<td class="text-end fw-bold" style="position:relative">${orderTotal}${(o.status === 'IMPORTED' || o.status === 'ALREADY_IMPORTED') && !(o.invoice && o.invoice.facturat) ? '<span class="row-actions"><button class="btn btn-xs btn-outline-warning" aria-label="Resync comanda" title="Resync" onclick="event.stopPropagation(); dashResyncOrder(\'' + esc(o.order_number) + '\', this)"><i class="bi bi-arrow-repeat"></i></button><button class="btn btn-xs btn-outline-danger" aria-label="Sterge din ROA" title="Sterge din ROA" onclick="event.stopPropagation(); dashDeleteOrder(\'' + esc(o.order_number) + '\', this)"><i class="bi bi-trash"></i></button></span>' : ''}</td>
<td class="text-end fw-bold">${orderTotal}</td>
<td class="kebab-dropdown" onclick="event.stopPropagation()">${(o.status === 'IMPORTED' || o.status === 'ALREADY_IMPORTED') && !(o.invoice && o.invoice.facturat) ? '<div class="dropdown"><button class="btn btn-sm border-0" aria-label="Actiuni comanda" data-bs-toggle="dropdown"><i class="bi bi-three-dots-vertical"></i></button><ul class="dropdown-menu dropdown-menu-end"><li><button class="dropdown-item" onclick="dashResyncOrder(\'' + esc(o.order_number) + '\', this)"><i class="bi bi-arrow-repeat me-2"></i>Resync</button></li><li><button class="dropdown-item text-danger" onclick="dashDeleteOrder(\'' + esc(o.order_number) + '\', this)"><i class="bi bi-trash me-2"></i>Sterge din ROA</button></li></ul></div>' : ''}</td>
</tr>`;
}).join('');
}
@@ -442,7 +443,7 @@ async function loadDashOrders() {
});
} catch (err) {
document.getElementById('dashOrdersBody').innerHTML =
`<tr><td colspan="9" class="text-center text-danger">${esc(err.message)}</td></tr>`;
`<tr><td colspan="10" class="text-center text-danger">${esc(err.message)}</td></tr>`;
}
}
@@ -636,7 +637,7 @@ function dashDeleteOrder(orderNumber, btn) {
const data = await res.json();
if (data.success) {
b.innerHTML = '<i class="bi bi-check-circle"></i>';
b.className = 'btn btn-xs btn-success';
b.className = 'btn btn-xs btn-danger';
setTimeout(() => loadDashOrders(), 1500);
} else {
b.innerHTML = '<i class="bi bi-exclamation-triangle"></i>';

View File

@@ -507,6 +507,10 @@ async function renderOrderDetailModal(orderNumber, opts) {
document.getElementById('detailError').style.display = 'none';
const retryBtn = document.getElementById('detailRetryBtn');
if (retryBtn) { retryBtn.style.display = 'none'; retryBtn.disabled = false; retryBtn.innerHTML = '<i class="bi bi-arrow-clockwise"></i> Reimporta'; retryBtn.className = 'btn btn-sm btn-outline-primary'; }
const resyncBtn = document.getElementById('detailResyncBtn');
if (resyncBtn) { resyncBtn.style.display = 'none'; resyncBtn.disabled = false; resyncBtn.innerHTML = '<i class="bi bi-arrow-repeat"></i> Resync'; resyncBtn.className = 'btn btn-sm btn-outline-warning'; }
const deleteBtn = document.getElementById('detailDeleteBtn');
if (deleteBtn) { deleteBtn.style.display = 'none'; deleteBtn.disabled = false; deleteBtn.innerHTML = '<i class="bi bi-trash"></i> Sterge din ROA'; deleteBtn.className = 'btn btn-sm btn-outline-danger'; }
const receiptEl = document.getElementById('detailReceipt');
if (receiptEl) receiptEl.innerHTML = '';
const receiptMEl = document.getElementById('detailReceiptMobile');
@@ -840,7 +844,7 @@ async function renderOrderDetailModal(orderNumber, opts) {
const data = await res.json();
if (data.success) {
btn.innerHTML = '<i class="bi bi-check-circle"></i> Sters';
btn.className = 'btn btn-sm btn-success';
btn.className = 'btn btn-sm btn-danger';
setTimeout(() => renderOrderDetailModal(orderNumber, opts), 1500);
} else {
btn.innerHTML = '<i class="bi bi-exclamation-triangle"></i> ' + (data.message || 'Eroare');