745 lines
25 KiB
JavaScript
745 lines
25 KiB
JavaScript
// Main initialization
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
console.log('DOM loaded, initializing dashboard...');
|
|
|
|
// Check if we're on the dashboard page
|
|
const hasTable = document.querySelector('table');
|
|
const isTicketPage = window.location.pathname.includes('/ticket/') ||
|
|
window.location.href.includes('ticket.php') ||
|
|
document.querySelector('.ticket-details') !== null;
|
|
const isDashboard = hasTable && !isTicketPage;
|
|
|
|
console.log('Has table:', hasTable);
|
|
console.log('Is ticket page:', isTicketPage);
|
|
console.log('Is dashboard:', isDashboard);
|
|
|
|
if (isDashboard) {
|
|
// Dashboard-specific initialization
|
|
initStatusFilter();
|
|
initTableSorting();
|
|
initSidebarFilters();
|
|
}
|
|
|
|
// Initialize for all pages
|
|
initSettingsModal();
|
|
|
|
// Force dark mode only (terminal aesthetic - no theme switching)
|
|
document.documentElement.setAttribute('data-theme', 'dark');
|
|
document.body.classList.add('dark-mode');
|
|
});
|
|
|
|
function initTableSorting() {
|
|
const tableHeaders = document.querySelectorAll('th');
|
|
tableHeaders.forEach((header, index) => {
|
|
header.style.cursor = 'pointer';
|
|
header.addEventListener('click', () => {
|
|
const table = header.closest('table');
|
|
sortTable(table, index);
|
|
});
|
|
});
|
|
}
|
|
|
|
function initSidebarFilters() {
|
|
const applyFiltersBtn = document.getElementById('apply-filters-btn');
|
|
const clearFiltersBtn = document.getElementById('clear-filters-btn');
|
|
|
|
if (applyFiltersBtn) {
|
|
applyFiltersBtn.addEventListener('click', () => {
|
|
const params = new URLSearchParams(window.location.search);
|
|
|
|
// Collect selected statuses
|
|
const selectedStatuses = Array.from(
|
|
document.querySelectorAll('.filter-group input[name="status"]:checked')
|
|
).map(cb => cb.value);
|
|
|
|
// Collect selected categories
|
|
const selectedCategories = Array.from(
|
|
document.querySelectorAll('.filter-group input[name="category"]:checked')
|
|
).map(cb => cb.value);
|
|
|
|
// Collect selected types
|
|
const selectedTypes = Array.from(
|
|
document.querySelectorAll('.filter-group input[name="type"]:checked')
|
|
).map(cb => cb.value);
|
|
|
|
// Update URL parameters
|
|
if (selectedStatuses.length > 0) {
|
|
params.set('status', selectedStatuses.join(','));
|
|
} else {
|
|
params.delete('status');
|
|
}
|
|
|
|
if (selectedCategories.length > 0) {
|
|
params.set('category', selectedCategories.join(','));
|
|
} else {
|
|
params.delete('category');
|
|
}
|
|
|
|
if (selectedTypes.length > 0) {
|
|
params.set('type', selectedTypes.join(','));
|
|
} else {
|
|
params.delete('type');
|
|
}
|
|
|
|
// Reset to page 1 when filters change
|
|
params.set('page', '1');
|
|
|
|
// Reload with new parameters
|
|
window.location.search = params.toString();
|
|
});
|
|
}
|
|
|
|
if (clearFiltersBtn) {
|
|
clearFiltersBtn.addEventListener('click', () => {
|
|
const params = new URLSearchParams(window.location.search);
|
|
|
|
// Remove filter parameters
|
|
params.delete('status');
|
|
params.delete('category');
|
|
params.delete('type');
|
|
params.set('page', '1');
|
|
|
|
// Reload with cleared filters
|
|
window.location.search = params.toString();
|
|
});
|
|
}
|
|
}
|
|
|
|
function initSettingsModal() {
|
|
const settingsIcon = document.querySelector('.settings-icon');
|
|
if (settingsIcon) {
|
|
settingsIcon.addEventListener('click', function(e) {
|
|
e.preventDefault();
|
|
createSettingsModal();
|
|
});
|
|
}
|
|
}
|
|
|
|
function sortTable(table, column) {
|
|
const headers = table.querySelectorAll('th');
|
|
headers.forEach(header => {
|
|
header.classList.remove('sort-asc', 'sort-desc');
|
|
});
|
|
|
|
const rows = Array.from(table.querySelectorAll('tbody tr'));
|
|
const currentDirection = table.dataset.sortColumn == column
|
|
? (table.dataset.sortDirection === 'asc' ? 'desc' : 'asc')
|
|
: 'asc';
|
|
|
|
table.dataset.sortColumn = column;
|
|
table.dataset.sortDirection = currentDirection;
|
|
|
|
rows.sort((a, b) => {
|
|
const aValue = a.children[column].textContent.trim();
|
|
const bValue = b.children[column].textContent.trim();
|
|
|
|
// Check if this is a date column
|
|
const headerText = headers[column].textContent.toLowerCase();
|
|
if (headerText === 'created' || headerText === 'updated') {
|
|
const dateA = new Date(aValue);
|
|
const dateB = new Date(bValue);
|
|
return currentDirection === 'asc' ? dateA - dateB : dateB - dateA;
|
|
}
|
|
|
|
// Numeric comparison
|
|
const numA = parseFloat(aValue);
|
|
const numB = parseFloat(bValue);
|
|
|
|
if (!isNaN(numA) && !isNaN(numB)) {
|
|
return currentDirection === 'asc' ? numA - numB : numB - numA;
|
|
}
|
|
|
|
// String comparison
|
|
return currentDirection === 'asc'
|
|
? aValue.localeCompare(bValue)
|
|
: bValue.localeCompare(aValue);
|
|
});
|
|
|
|
const currentHeader = headers[column];
|
|
currentHeader.classList.add(currentDirection === 'asc' ? 'sort-asc' : 'sort-desc');
|
|
|
|
const tbody = table.querySelector('tbody');
|
|
rows.forEach(row => tbody.appendChild(row));
|
|
}
|
|
|
|
// Old settings modal functions removed - now using settings.js with new settings modal
|
|
|
|
function initStatusFilter() {
|
|
const filterContainer = document.createElement('div');
|
|
filterContainer.className = 'status-filter-container';
|
|
|
|
const dropdown = document.createElement('div');
|
|
dropdown.className = 'status-dropdown';
|
|
|
|
const dropdownHeader = document.createElement('div');
|
|
dropdownHeader.className = 'dropdown-header';
|
|
dropdownHeader.textContent = 'Status Filter';
|
|
|
|
const dropdownContent = document.createElement('div');
|
|
dropdownContent.className = 'dropdown-content';
|
|
|
|
const statuses = ['Open', 'In Progress', 'Closed'];
|
|
statuses.forEach(status => {
|
|
const label = document.createElement('label');
|
|
const checkbox = document.createElement('input');
|
|
checkbox.type = 'checkbox';
|
|
checkbox.value = status;
|
|
checkbox.id = `status-${status.toLowerCase().replace(/\s+/g, '-')}`;
|
|
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const currentStatuses = urlParams.get('status') ? urlParams.get('status').split(',') : [];
|
|
const showAll = urlParams.get('show_all');
|
|
|
|
// FIXED LOGIC: Determine checkbox state
|
|
if (showAll === '1') {
|
|
// If show_all=1 parameter exists, all should be checked
|
|
checkbox.checked = true;
|
|
} else if (currentStatuses.length === 0) {
|
|
// No status parameter - default: Open and In Progress checked, Closed unchecked
|
|
checkbox.checked = status !== 'Closed';
|
|
} else {
|
|
// Status parameter exists - check if this status is in the list
|
|
checkbox.checked = currentStatuses.includes(status);
|
|
}
|
|
|
|
label.appendChild(checkbox);
|
|
label.appendChild(document.createTextNode(' ' + status));
|
|
dropdownContent.appendChild(label);
|
|
});
|
|
|
|
const saveButton = document.createElement('button');
|
|
saveButton.className = 'btn save-filter';
|
|
saveButton.textContent = 'Apply Filter';
|
|
|
|
saveButton.onclick = () => {
|
|
const checkedBoxes = dropdownContent.querySelectorAll('input:checked');
|
|
const selectedStatuses = Array.from(checkedBoxes).map(cb => cb.value);
|
|
|
|
const params = new URLSearchParams(window.location.search);
|
|
|
|
if (selectedStatuses.length === 0) {
|
|
// No statuses selected - show default (Open + In Progress)
|
|
params.delete('status');
|
|
params.delete('show_all');
|
|
} else if (selectedStatuses.length === 3) {
|
|
// All statuses selected - show all tickets
|
|
params.delete('status');
|
|
params.set('show_all', '1');
|
|
} else {
|
|
// Some statuses selected - set the parameter
|
|
params.set('status', selectedStatuses.join(','));
|
|
params.delete('show_all');
|
|
}
|
|
|
|
params.set('page', '1');
|
|
window.location.search = params.toString();
|
|
dropdown.classList.remove('active');
|
|
};
|
|
|
|
dropdownHeader.onclick = () => {
|
|
dropdown.classList.toggle('active');
|
|
};
|
|
|
|
dropdown.appendChild(dropdownHeader);
|
|
dropdown.appendChild(dropdownContent);
|
|
dropdownContent.appendChild(saveButton);
|
|
filterContainer.appendChild(dropdown);
|
|
|
|
const tableActions = document.querySelector('.table-controls .table-actions');
|
|
if (tableActions) {
|
|
tableActions.prepend(filterContainer);
|
|
}
|
|
}
|
|
|
|
function quickSave() {
|
|
if (!window.ticketData) {
|
|
console.error('No ticket data available');
|
|
return;
|
|
}
|
|
|
|
const statusSelect = document.getElementById('status-select');
|
|
const prioritySelect = document.getElementById('priority-select');
|
|
|
|
if (!statusSelect || !prioritySelect) {
|
|
console.error('Status or priority select not found');
|
|
return;
|
|
}
|
|
|
|
const data = {
|
|
ticket_id: parseInt(window.ticketData.id),
|
|
status: statusSelect.value,
|
|
priority: parseInt(prioritySelect.value)
|
|
};
|
|
|
|
console.log('Saving ticket data:', data);
|
|
|
|
fetch('/api/update_ticket.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(data)
|
|
})
|
|
.then(response => {
|
|
console.log('Response status:', response.status);
|
|
return response.text().then(text => {
|
|
console.log('Raw response:', text);
|
|
try {
|
|
return JSON.parse(text);
|
|
} catch (e) {
|
|
throw new Error('Invalid JSON response: ' + text);
|
|
}
|
|
});
|
|
})
|
|
.then(result => {
|
|
console.log('Update result:', result);
|
|
if (result.success) {
|
|
// Update the hamburger menu display
|
|
const hamburgerStatus = document.getElementById('hamburger-status');
|
|
const hamburgerPriority = document.getElementById('hamburger-priority');
|
|
|
|
if (hamburgerStatus) hamburgerStatus.textContent = statusSelect.value;
|
|
if (hamburgerPriority) hamburgerPriority.textContent = 'P' + prioritySelect.value;
|
|
|
|
// Update window.ticketData
|
|
window.ticketData.status = statusSelect.value;
|
|
window.ticketData.priority = parseInt(prioritySelect.value);
|
|
|
|
// Update main page elements if they exist
|
|
const statusDisplay = document.getElementById('statusDisplay');
|
|
if (statusDisplay) {
|
|
statusDisplay.className = `status-${statusSelect.value}`;
|
|
statusDisplay.textContent = statusSelect.value;
|
|
}
|
|
|
|
console.log('Ticket updated successfully');
|
|
|
|
// Close hamburger menu after successful save
|
|
const hamburgerContent = document.querySelector('.hamburger-content');
|
|
if (hamburgerContent) {
|
|
hamburgerContent.classList.remove('open');
|
|
document.body.classList.remove('menu-open');
|
|
}
|
|
|
|
} else {
|
|
console.error('Error updating ticket:', result.error || 'Unknown error');
|
|
alert('Error updating ticket: ' + (result.error || 'Unknown error'));
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error updating ticket:', error);
|
|
alert('Error updating ticket: ' + error.message);
|
|
});
|
|
}
|
|
|
|
|
|
// Ticket page functions (if needed)
|
|
function saveTicket() {
|
|
const editables = document.querySelectorAll('.editable');
|
|
const data = {};
|
|
|
|
let ticketId;
|
|
if (window.location.href.includes('?id=')) {
|
|
ticketId = window.location.href.split('id=')[1];
|
|
} else {
|
|
const matches = window.location.pathname.match(/\/ticket\/(\d+)/);
|
|
ticketId = matches ? matches[1] : null;
|
|
}
|
|
|
|
editables.forEach(field => {
|
|
if (field.dataset.field) {
|
|
data[field.dataset.field] = field.value;
|
|
}
|
|
});
|
|
|
|
fetch('/api/update_ticket.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
ticket_id: ticketId,
|
|
...data
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if(data.success) {
|
|
const statusDisplay = document.getElementById('statusDisplay');
|
|
if (statusDisplay) {
|
|
statusDisplay.className = `status-${data.status}`;
|
|
statusDisplay.textContent = data.status;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Load template data into the create ticket form
|
|
*/
|
|
function loadTemplate() {
|
|
const templateSelect = document.getElementById('templateSelect');
|
|
const templateId = templateSelect.value;
|
|
|
|
if (!templateId) {
|
|
// Clear form when "No Template" is selected
|
|
document.getElementById('title').value = '';
|
|
document.getElementById('description').value = '';
|
|
document.getElementById('priority').value = '4';
|
|
document.getElementById('category').value = 'General';
|
|
document.getElementById('type').value = 'Issue';
|
|
return;
|
|
}
|
|
|
|
// Fetch template data
|
|
fetch(`/api/get_template.php?template_id=${templateId}`)
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('Failed to fetch template');
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
if (data.success && data.template) {
|
|
const template = data.template;
|
|
|
|
// Populate form fields with template data
|
|
if (template.title_template) {
|
|
document.getElementById('title').value = template.title_template;
|
|
}
|
|
|
|
if (template.description_template) {
|
|
document.getElementById('description').value = template.description_template;
|
|
}
|
|
|
|
if (template.category) {
|
|
document.getElementById('category').value = template.category;
|
|
}
|
|
|
|
if (template.type) {
|
|
document.getElementById('type').value = template.type;
|
|
}
|
|
|
|
if (template.default_priority) {
|
|
document.getElementById('priority').value = template.default_priority;
|
|
}
|
|
|
|
console.log('Template loaded:', template.template_name);
|
|
} else {
|
|
console.error('Failed to load template:', data.error);
|
|
alert('Failed to load template: ' + (data.error || 'Unknown error'));
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading template:', error);
|
|
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-inline');
|
|
const countDisplay = document.getElementById('selected-count');
|
|
|
|
if (toolbar && countDisplay) {
|
|
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 ascii-frame-outer">
|
|
<span class="bottom-left-corner">╚</span>
|
|
<span class="bottom-right-corner">╝</span>
|
|
|
|
<div class="ascii-section-header">Assign ${ticketIds.length} Ticket(s)</div>
|
|
|
|
<div class="ascii-content">
|
|
<div class="ascii-frame-inner">
|
|
<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>
|
|
</div>
|
|
|
|
<div class="ascii-divider"></div>
|
|
|
|
<div class="ascii-content">
|
|
<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>
|
|
</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 ascii-frame-outer">
|
|
<span class="bottom-left-corner">╚</span>
|
|
<span class="bottom-right-corner">╝</span>
|
|
|
|
<div class="ascii-section-header">Change Priority for ${ticketIds.length} Ticket(s)</div>
|
|
|
|
<div class="ascii-content">
|
|
<div class="ascii-frame-inner">
|
|
<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>
|
|
</div>
|
|
|
|
<div class="ascii-divider"></div>
|
|
|
|
<div class="ascii-content">
|
|
<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>
|
|
</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);
|
|
});
|
|
}
|
|
|
|
// Make table rows clickable
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const tableRows = document.querySelectorAll('tbody tr');
|
|
tableRows.forEach(row => {
|
|
// Skip if row already has click handler
|
|
if (row.dataset.clickable) return;
|
|
|
|
row.dataset.clickable = 'true';
|
|
row.style.cursor = 'pointer';
|
|
|
|
row.addEventListener('click', function(e) {
|
|
// Don't navigate if clicking on a link, button, checkbox, or select
|
|
if (e.target.tagName === 'A' ||
|
|
e.target.tagName === 'BUTTON' ||
|
|
e.target.tagName === 'INPUT' ||
|
|
e.target.tagName === 'SELECT' ||
|
|
e.target.closest('a') ||
|
|
e.target.closest('button')) {
|
|
return;
|
|
}
|
|
|
|
// Find the ticket link in the row
|
|
const ticketLink = row.querySelector('.ticket-link');
|
|
if (ticketLink) {
|
|
window.location.href = ticketLink.href;
|
|
}
|
|
});
|
|
|
|
// Add hover effect
|
|
row.addEventListener('mouseenter', function() {
|
|
this.style.backgroundColor = 'rgba(0, 255, 65, 0.08)';
|
|
});
|
|
|
|
row.addEventListener('mouseleave', function() {
|
|
this.style.backgroundColor = '';
|
|
});
|
|
});
|
|
});
|