Feature 5: Implement Bulk Actions (Admin Only)
Add comprehensive bulk operations system for admins: - Created BulkOperationsModel.php with operation tracking and processing - Added bulk_operation.php API endpoint for bulk operations - Created get_users.php API endpoint for user dropdown in bulk assign - Updated DashboardView.php with checkboxes and bulk actions toolbar - Added JavaScript functions for: - Select all/clear selection - Bulk close tickets - Bulk assign tickets - Bulk change priority - Added comprehensive CSS for bulk actions toolbar and modals - All bulk operations are admin-only (enforced server-side) - Operations tracked in bulk_operations table with audit logging - Supports bulk_close, bulk_assign, and bulk_priority operations Admins can now select multiple tickets and perform batch operations, significantly improving workflow efficiency. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -913,3 +913,244 @@ function loadTemplate() {
|
||||
alert('Error loading template: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk Actions Functions (Admin only)
|
||||
*/
|
||||
|
||||
function toggleSelectAll() {
|
||||
const selectAll = document.getElementById('selectAllCheckbox');
|
||||
const checkboxes = document.querySelectorAll('.ticket-checkbox');
|
||||
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.checked = selectAll.checked;
|
||||
});
|
||||
|
||||
updateSelectionCount();
|
||||
}
|
||||
|
||||
function updateSelectionCount() {
|
||||
const checkboxes = document.querySelectorAll('.ticket-checkbox:checked');
|
||||
const count = checkboxes.length;
|
||||
const toolbar = document.querySelector('.bulk-actions-toolbar');
|
||||
const countDisplay = document.getElementById('selected-count');
|
||||
|
||||
if (count > 0) {
|
||||
toolbar.style.display = 'flex';
|
||||
countDisplay.textContent = count;
|
||||
} else {
|
||||
toolbar.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function getSelectedTicketIds() {
|
||||
const checkboxes = document.querySelectorAll('.ticket-checkbox:checked');
|
||||
return Array.from(checkboxes).map(cb => parseInt(cb.value));
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
document.querySelectorAll('.ticket-checkbox').forEach(cb => cb.checked = false);
|
||||
const selectAll = document.getElementById('selectAllCheckbox');
|
||||
if (selectAll) selectAll.checked = false;
|
||||
updateSelectionCount();
|
||||
}
|
||||
|
||||
function bulkClose() {
|
||||
const ticketIds = getSelectedTicketIds();
|
||||
|
||||
if (ticketIds.length === 0) {
|
||||
alert('No tickets selected');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm(`Close ${ticketIds.length} ticket(s)?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/api/bulk_operation.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
operation_type: 'bulk_close',
|
||||
ticket_ids: ticketIds
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
alert(`Bulk Close Complete:\n${data.processed} succeeded\n${data.failed} failed`);
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert('Error: ' + (data.error || 'Unknown error'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error performing bulk close:', error);
|
||||
alert('Error performing bulk close: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function showBulkAssignModal() {
|
||||
const ticketIds = getSelectedTicketIds();
|
||||
|
||||
if (ticketIds.length === 0) {
|
||||
alert('No tickets selected');
|
||||
return;
|
||||
}
|
||||
|
||||
// Create modal HTML
|
||||
const modalHtml = `
|
||||
<div class="modal-overlay" id="bulkAssignModal">
|
||||
<div class="modal-content">
|
||||
<h3>Assign ${ticketIds.length} Ticket(s)</h3>
|
||||
<div class="modal-body">
|
||||
<label for="bulkAssignUser">Assign to:</label>
|
||||
<select id="bulkAssignUser" class="editable">
|
||||
<option value="">Select User...</option>
|
||||
<!-- Users will be loaded dynamically -->
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button onclick="performBulkAssign()" class="btn btn-bulk">Assign</button>
|
||||
<button onclick="closeBulkAssignModal()" class="btn btn-secondary">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
||||
|
||||
// Fetch users for the dropdown
|
||||
fetch('/api/get_users.php')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success && data.users) {
|
||||
const select = document.getElementById('bulkAssignUser');
|
||||
data.users.forEach(user => {
|
||||
const option = document.createElement('option');
|
||||
option.value = user.user_id;
|
||||
option.textContent = user.display_name || user.username;
|
||||
select.appendChild(option);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error loading users:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function closeBulkAssignModal() {
|
||||
const modal = document.getElementById('bulkAssignModal');
|
||||
if (modal) {
|
||||
modal.remove();
|
||||
}
|
||||
}
|
||||
|
||||
function performBulkAssign() {
|
||||
const userId = document.getElementById('bulkAssignUser').value;
|
||||
const ticketIds = getSelectedTicketIds();
|
||||
|
||||
if (!userId) {
|
||||
alert('Please select a user');
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/api/bulk_operation.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
operation_type: 'bulk_assign',
|
||||
ticket_ids: ticketIds,
|
||||
parameters: { assigned_to: parseInt(userId) }
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
alert(`Bulk Assign Complete:\n${data.processed} succeeded\n${data.failed} failed`);
|
||||
closeBulkAssignModal();
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert('Error: ' + (data.error || 'Unknown error'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error performing bulk assign:', error);
|
||||
alert('Error performing bulk assign: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function showBulkPriorityModal() {
|
||||
const ticketIds = getSelectedTicketIds();
|
||||
|
||||
if (ticketIds.length === 0) {
|
||||
alert('No tickets selected');
|
||||
return;
|
||||
}
|
||||
|
||||
const modalHtml = `
|
||||
<div class="modal-overlay" id="bulkPriorityModal">
|
||||
<div class="modal-content">
|
||||
<h3>Change Priority for ${ticketIds.length} Ticket(s)</h3>
|
||||
<div class="modal-body">
|
||||
<label for="bulkPriority">Priority:</label>
|
||||
<select id="bulkPriority" class="editable">
|
||||
<option value="">Select Priority...</option>
|
||||
<option value="1">P1 - Critical Impact</option>
|
||||
<option value="2">P2 - High Impact</option>
|
||||
<option value="3">P3 - Medium Impact</option>
|
||||
<option value="4">P4 - Low Impact</option>
|
||||
<option value="5">P5 - Minimal Impact</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button onclick="performBulkPriority()" class="btn btn-bulk">Update</button>
|
||||
<button onclick="closeBulkPriorityModal()" class="btn btn-secondary">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
||||
}
|
||||
|
||||
function closeBulkPriorityModal() {
|
||||
const modal = document.getElementById('bulkPriorityModal');
|
||||
if (modal) {
|
||||
modal.remove();
|
||||
}
|
||||
}
|
||||
|
||||
function performBulkPriority() {
|
||||
const priority = document.getElementById('bulkPriority').value;
|
||||
const ticketIds = getSelectedTicketIds();
|
||||
|
||||
if (!priority) {
|
||||
alert('Please select a priority');
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/api/bulk_operation.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
operation_type: 'bulk_priority',
|
||||
ticket_ids: ticketIds,
|
||||
parameters: { priority: parseInt(priority) }
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
alert(`Bulk Priority Update Complete:\n${data.processed} succeeded\n${data.failed} failed`);
|
||||
closeBulkPriorityModal();
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert('Error: ' + (data.error || 'Unknown error'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error performing bulk priority update:', error);
|
||||
alert('Error performing bulk priority update: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user