// 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 = ` `; 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 = ` `; 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 = ''; }); }); }); // Bulk Status Change function showBulkStatusModal() { const ticketIds = getSelectedTicketIds(); if (ticketIds.length === 0) { alert('No tickets selected'); return; } const modalHtml = ` `; document.body.insertAdjacentHTML('beforeend', modalHtml); } function closeBulkStatusModal() { const modal = document.getElementById('bulkStatusModal'); if (modal) { modal.remove(); } } function performBulkStatusChange() { const status = document.getElementById('bulkStatus').value; const ticketIds = getSelectedTicketIds(); if (!status) { alert('Please select a status'); return; } fetch('/api/bulk_operation.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ operation_type: 'bulk_status', ticket_ids: ticketIds, parameters: { status: status } }) }) .then(response => response.json()) .then(data => { closeBulkStatusModal(); if (data.success) { window.location.reload(); } else { alert('Error: ' + (data.error || 'Unknown error')); } }) .catch(error => { console.error('Error performing bulk status change:', error); alert('Error performing bulk status change: ' + error.message); }); } // Bulk Delete function showBulkDeleteModal() { const ticketIds = getSelectedTicketIds(); if (ticketIds.length === 0) { alert('No tickets selected'); return; } const modalHtml = ` `; document.body.insertAdjacentHTML('beforeend', modalHtml); } function closeBulkDeleteModal() { const modal = document.getElementById('bulkDeleteModal'); if (modal) { modal.remove(); } } function performBulkDelete() { const ticketIds = getSelectedTicketIds(); fetch('/api/bulk_operation.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ operation_type: 'bulk_delete', ticket_ids: ticketIds }) }) .then(response => response.json()) .then(data => { closeBulkDeleteModal(); if (data.success) { if (typeof toast !== 'undefined') { toast.success(`Successfully deleted ${ticketIds.length} ticket(s)`); } setTimeout(() => window.location.reload(), 1000); } else { alert('Error: ' + (data.error || 'Unknown error')); } }) .catch(error => { console.error('Error performing bulk delete:', error); alert('Error performing bulk delete: ' + error.message); }); }