04/09: fixed searching ticket, and deleting/bulk deleting tickets
This commit is contained in:
@@ -95,7 +95,22 @@
|
||||
<td style="font-size:13px;">{{ t.creator.full_name }}</td>
|
||||
<td style="font-size:13px;color:var(--muted);">{{ t.assignee.full_name if t.assignee else '—' }}</td>
|
||||
<td style="font-size:11px;color:var(--muted);">{{ t.created_at | localtime("%b %d") }}</td>
|
||||
<td><a href="{{ url_for('tickets.ticket_detail', ticket_id=t.id) }}" class="btn btn-secondary btn-sm"><i class="bi bi-eye"></i></a></td>
|
||||
<td>
|
||||
<div class="d-flex gap-1">
|
||||
<a href="{{ url_for('tickets.ticket_detail', ticket_id=t.id) }}" class="btn btn-secondary btn-sm"><i class="bi bi-eye"></i></a>
|
||||
{% if current_user.is_admin %}
|
||||
<button type="button"
|
||||
class="btn btn-sm delete-ticket-btn"
|
||||
style="background:#fee2e2;color:#dc2626;border:1px solid #fca5a5;"
|
||||
data-ticket-id="{{ t.id }}"
|
||||
data-ticket-number="{{ t.ticket_number }}"
|
||||
data-ticket-title="{{ t.title | e }}"
|
||||
title="Delete ticket">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -130,6 +145,37 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Single-ticket delete form (hidden, submitted via JS) ────────────────── -->
|
||||
{% if current_user.is_admin %}
|
||||
<form id="single-delete-form" method="POST" action="" style="display:none;">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
</form>
|
||||
|
||||
<!-- ── Confirmation modal for delete actions ──────────────────────────────── -->
|
||||
<div id="delete-modal-overlay"
|
||||
style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.55);
|
||||
z-index:2000;align-items:center;justify-content:center;">
|
||||
<div style="background:var(--card-bg,#fff);border-radius:12px;padding:28px 32px;
|
||||
max-width:480px;width:90%;box-shadow:0 16px 48px rgba(0,0,0,.25);">
|
||||
<div style="display:flex;align-items:center;gap:12px;margin-bottom:16px;">
|
||||
<span style="background:#fee2e2;border-radius:50%;width:40px;height:40px;
|
||||
display:flex;align-items:center;justify-content:center;flex-shrink:0;">
|
||||
<i class="bi bi-exclamation-triangle-fill" style="color:#dc2626;font-size:18px;"></i>
|
||||
</span>
|
||||
<h5 id="modal-title" style="margin:0;font-size:16px;font-weight:700;">Delete Ticket</h5>
|
||||
</div>
|
||||
<p id="modal-body" style="font-size:14px;color:var(--muted,#64748b);margin-bottom:24px;line-height:1.6;"></p>
|
||||
<div class="d-flex gap-2 justify-content-end">
|
||||
<button type="button" id="modal-cancel-btn" class="btn btn-secondary btn-sm">Cancel</button>
|
||||
<button type="button" id="modal-confirm-btn"
|
||||
class="btn btn-sm" style="background:#dc2626;color:#fff;border:none;">
|
||||
<i class="bi bi-trash me-1"></i>Delete Permanently
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Bulk action bar: fixed at bottom, hidden until checkboxes are ticked -->
|
||||
<!-- NOTE: display is set entirely via JS — no inline display property here -->
|
||||
<div id="bulk-action-bar"
|
||||
@@ -163,6 +209,12 @@
|
||||
class="btn btn-sm" style="color:#fff;border:1px solid #6b7280;background:transparent;">
|
||||
<i class="bi bi-person-dash me-1"></i>Unassign
|
||||
</button>
|
||||
{% if current_user.is_admin %}
|
||||
<button type="button" id="bulk-delete-btn"
|
||||
class="btn btn-sm" style="background:#dc2626;color:#fff;border:none;">
|
||||
<i class="bi bi-trash me-1"></i>Delete
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
<button type="button" onclick="clearSelection()"
|
||||
@@ -224,6 +276,87 @@
|
||||
updateBar();
|
||||
});
|
||||
});
|
||||
|
||||
// ── Delete modal ──────────────────────────────────────────────────────────
|
||||
const overlay = document.getElementById('delete-modal-overlay');
|
||||
const modalTitle = document.getElementById('modal-title');
|
||||
const modalBody = document.getElementById('modal-body');
|
||||
const cancelBtn = document.getElementById('modal-cancel-btn');
|
||||
const confirmBtn = document.getElementById('modal-confirm-btn');
|
||||
|
||||
if (!overlay) return; // non-admin: modal not rendered
|
||||
|
||||
let pendingAction = null;
|
||||
|
||||
function showModal(title, body, onConfirm) {
|
||||
modalTitle.textContent = title;
|
||||
modalBody.innerHTML = body;
|
||||
pendingAction = onConfirm;
|
||||
overlay.style.display = 'flex';
|
||||
}
|
||||
|
||||
function hideModal() {
|
||||
overlay.style.display = 'none';
|
||||
pendingAction = null;
|
||||
}
|
||||
|
||||
cancelBtn.addEventListener('click', hideModal);
|
||||
overlay.addEventListener('click', function (e) {
|
||||
if (e.target === overlay) hideModal();
|
||||
});
|
||||
confirmBtn.addEventListener('click', function () {
|
||||
if (typeof pendingAction === 'function') pendingAction();
|
||||
hideModal();
|
||||
});
|
||||
|
||||
// ── Single-ticket delete ──────────────────────────────────────────────────
|
||||
document.querySelectorAll('.delete-ticket-btn').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
const ticketId = this.dataset.ticketId;
|
||||
const ticketNumber = this.dataset.ticketNumber;
|
||||
const ticketTitle = this.dataset.ticketTitle;
|
||||
|
||||
showModal(
|
||||
'Delete Ticket',
|
||||
'This will <strong>permanently delete</strong> ticket ' +
|
||||
'<code>' + ticketNumber + '</code> — <em>' + ticketTitle + '</em> ' +
|
||||
'and all associated comments, attachments, and history.' +
|
||||
'<br><br>This action <strong>cannot be undone</strong>.',
|
||||
function () {
|
||||
const form = document.getElementById('single-delete-form');
|
||||
form.action = '/admin/tickets/' + ticketId + '/delete';
|
||||
form.submit();
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Bulk delete ───────────────────────────────────────────────────────────
|
||||
const bulkDeleteBtn = document.getElementById('bulk-delete-btn');
|
||||
if (bulkDeleteBtn) {
|
||||
bulkDeleteBtn.addEventListener('click', function () {
|
||||
const checked = getChecked();
|
||||
if (checked.length === 0) return;
|
||||
const n = checked.length;
|
||||
|
||||
showModal(
|
||||
'Delete Selected Tickets',
|
||||
'This will <strong>permanently delete ' + n + ' ticket' + (n !== 1 ? 's' : '') + '</strong> ' +
|
||||
'and all associated comments, attachments, and history.' +
|
||||
'<br><br>This action <strong>cannot be undone</strong>.',
|
||||
function () {
|
||||
const form = document.getElementById('bulk-form');
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'action';
|
||||
input.value = 'delete';
|
||||
form.appendChild(input);
|
||||
form.submit();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user