04/07 updated timezone, report via email, etc
This commit is contained in:
@@ -65,6 +65,10 @@
|
||||
<table class="table mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:36px;">
|
||||
<input type="checkbox" id="select-all" title="Select all"
|
||||
style="accent-color:var(--accent);width:15px;height:15px;cursor:pointer;"/>
|
||||
</th>
|
||||
<th>Ticket #</th>
|
||||
<th>Title</th>
|
||||
<th>Category</th>
|
||||
@@ -79,6 +83,10 @@
|
||||
<tbody>
|
||||
{% for t in tickets.items %}
|
||||
<tr>
|
||||
<td>
|
||||
<input type="checkbox" class="ticket-cb" value="{{ t.id }}"
|
||||
style="accent-color:var(--accent);width:15px;height:15px;cursor:pointer;"/>
|
||||
</td>
|
||||
<td><a href="{{ url_for('tickets.ticket_detail', ticket_id=t.id) }}" class="mono" style="font-size:11px;color:var(--accent3);">{{ t.ticket_number }}</a></td>
|
||||
<td style="font-size:13px;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">{{ t.title }}</td>
|
||||
<td style="font-size:12px;color:var(--muted);">{{ t.category.replace('_',' ').title() }}</td>
|
||||
@@ -86,7 +94,7 @@
|
||||
<td><span class="badge badge-{{ t.priority }}">{{ t.priority.upper() }}</span></td>
|
||||
<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.strftime('%b %d') }}</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>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@@ -121,4 +129,101 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 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"
|
||||
style="position:fixed;bottom:24px;left:50%;transform:translateX(-50%);
|
||||
background:#1e293b;color:#fff;border-radius:12px;padding:12px 20px;
|
||||
box-shadow:0 8px 32px rgba(0,0,0,.3);z-index:1000;
|
||||
align-items:center;gap:12px;min-width:420px;">
|
||||
<span id="bulk-count" style="font-size:13px;font-weight:600;white-space:nowrap;"></span>
|
||||
<div style="flex:1;"></div>
|
||||
<form method="POST" action="{{ url_for('admin.bulk_ticket_action') }}" id="bulk-form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<input type="hidden" name="status" value="{{ status }}"/>
|
||||
<input type="hidden" name="priority" value="{{ priority }}"/>
|
||||
<input type="hidden" name="assigned" value="{{ assigned }}"/>
|
||||
<input type="hidden" name="q" value="{{ search }}"/>
|
||||
<div id="bulk-hidden-ids"></div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" name="action" value="resolve"
|
||||
class="btn btn-sm" style="background:#059669;color:#fff;border:none;">
|
||||
<i class="bi bi-check2-circle me-1"></i>Resolve
|
||||
</button>
|
||||
<button type="submit" name="action" value="close"
|
||||
class="btn btn-sm" style="background:#6b7280;color:#fff;border:none;">
|
||||
<i class="bi bi-archive me-1"></i>Close
|
||||
</button>
|
||||
<button type="submit" name="action" value="assign_me"
|
||||
class="btn btn-sm" style="background:#2563eb;color:#fff;border:none;">
|
||||
<i class="bi bi-person-check me-1"></i>Assign to Me
|
||||
</button>
|
||||
<button type="submit" name="action" value="unassign"
|
||||
class="btn btn-sm" style="color:#fff;border:1px solid #6b7280;background:transparent;">
|
||||
<i class="bi bi-person-dash me-1"></i>Unassign
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<button type="button" onclick="clearSelection()"
|
||||
style="background:none;border:none;color:#9ca3af;font-size:20px;cursor:pointer;
|
||||
line-height:1;padding:0 0 0 4px;flex-shrink:0;" title="Clear selection">×</button>
|
||||
</div>
|
||||
|
||||
<!-- Script is AFTER all DOM elements so querySelectorAll finds the checkboxes -->
|
||||
<script>
|
||||
(function () {
|
||||
const bar = document.getElementById('bulk-action-bar');
|
||||
const countEl = document.getElementById('bulk-count');
|
||||
const hiddenIds = document.getElementById('bulk-hidden-ids');
|
||||
const selectAll = document.getElementById('select-all');
|
||||
|
||||
// Start hidden — JS controls visibility entirely
|
||||
bar.style.display = 'none';
|
||||
|
||||
function getChecked() {
|
||||
return Array.from(document.querySelectorAll('.ticket-cb:checked'));
|
||||
}
|
||||
|
||||
function updateBar() {
|
||||
const checked = getChecked();
|
||||
if (checked.length === 0) {
|
||||
bar.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
bar.style.display = 'flex';
|
||||
countEl.textContent = `${checked.length} ticket${checked.length !== 1 ? 's' : ''} selected`;
|
||||
hiddenIds.innerHTML = checked.map(cb =>
|
||||
`<input type="hidden" name="ticket_ids" value="${cb.value}"/>`
|
||||
).join('');
|
||||
}
|
||||
|
||||
window.clearSelection = function () {
|
||||
document.querySelectorAll('.ticket-cb').forEach(cb => cb.checked = false);
|
||||
if (selectAll) { selectAll.checked = false; selectAll.indeterminate = false; }
|
||||
updateBar();
|
||||
};
|
||||
|
||||
if (selectAll) {
|
||||
selectAll.addEventListener('change', () => {
|
||||
document.querySelectorAll('.ticket-cb').forEach(cb => {
|
||||
cb.checked = selectAll.checked;
|
||||
});
|
||||
updateBar();
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll('.ticket-cb').forEach(cb => {
|
||||
cb.addEventListener('change', () => {
|
||||
const all = document.querySelectorAll('.ticket-cb');
|
||||
const done = document.querySelectorAll('.ticket-cb:checked');
|
||||
if (selectAll) {
|
||||
selectAll.indeterminate = done.length > 0 && done.length < all.length;
|
||||
selectAll.checked = done.length === all.length && all.length > 0;
|
||||
}
|
||||
updateBar();
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user