// 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 initSearch(); initStatusFilter(); initTableSorting(); console.log('Creating hamburger menu for dashboard...'); try { createHamburgerMenu(); console.log('Hamburger menu created successfully'); } catch (error) { console.error('Error creating hamburger menu:', error); } } else if (isTicketPage) { // Ticket page initialization console.log('Creating hamburger menu for ticket page...'); try { createHamburgerMenu(); console.log('Hamburger menu created successfully'); } catch (error) { console.error('Error creating hamburger menu:', error); } } // Initialize for all pages initThemeToggle(); initSettingsModal(); // Load saved theme preference const savedTheme = localStorage.getItem('theme') || 'light'; document.documentElement.setAttribute('data-theme', savedTheme); }); 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 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)); } function createSettingsModal() { const backdrop = document.createElement('div'); backdrop.className = 'settings-modal-backdrop'; backdrop.innerHTML = `

Dashboard Settings

Rows per Page

`; document.body.appendChild(backdrop); // Load saved rows per page setting const savedRowsPerPage = localStorage.getItem('ticketsPerPage') || '15'; const rowsPerPageSelect = backdrop.querySelector('#rows-per-page'); rowsPerPageSelect.value = savedRowsPerPage; // Event listeners backdrop.querySelector('.close-modal').addEventListener('click', closeSettingsModal); backdrop.querySelector('.cancel-settings').addEventListener('click', closeSettingsModal); backdrop.querySelector('.save-settings').addEventListener('click', saveSettings); backdrop.addEventListener('click', (e) => { if (e.target === backdrop) { closeSettingsModal(); } }); } function closeSettingsModal() { const backdrop = document.querySelector('.settings-modal-backdrop'); if (backdrop) { backdrop.remove(); } } function saveSettings() { // Save rows per page const rowsPerPage = document.querySelector('#rows-per-page').value; localStorage.setItem('ticketsPerPage', rowsPerPage); // Set cookie for PHP to read document.cookie = `ticketsPerPage=${rowsPerPage}; path=/`; // Reload page to apply pagination changes window.location.reload(); } function initThemeToggle() { const toggle = document.createElement('button'); toggle.className = 'theme-toggle'; toggle.innerHTML = '🌓'; toggle.onclick = () => { const currentTheme = document.documentElement.getAttribute('data-theme'); const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; document.documentElement.setAttribute('data-theme', newTheme); localStorage.setItem('theme', newTheme); }; document.body.appendChild(toggle); } function initSearch() { const searchBox = document.createElement('input'); searchBox.type = 'text'; searchBox.placeholder = 'Search tickets...'; searchBox.className = 'search-box'; searchBox.oninput = (e) => { const searchTerm = e.target.value.toLowerCase(); const rows = document.querySelectorAll('tbody tr'); rows.forEach(row => { const text = row.textContent.toLowerCase(); row.style.display = text.includes(searchTerm) ? '' : 'none'; }); }; document.querySelector('h1').after(searchBox); } 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); }); } function createHamburgerMenu() { console.log('createHamburgerMenu called'); // Remove any existing hamburger menu first const existingMenu = document.querySelector('.hamburger-menu'); if (existingMenu) { console.log('Removing existing menu'); existingMenu.remove(); } const hamburgerMenu = document.createElement('div'); hamburgerMenu.className = 'hamburger-menu'; // Better detection for ticket pages const isTicketPage = window.location.pathname.includes('/ticket/') || window.location.href.includes('ticket.php') || document.querySelector('.ticket-details') !== null; console.log('Is ticket page:', isTicketPage); console.log('Has ticketData:', !!window.ticketData); console.log('TicketData contents:', window.ticketData); if (isTicketPage) { // Wait for ticketData if it's not loaded yet if (!window.ticketData) { console.log('Waiting for ticket data...'); setTimeout(() => { if (window.ticketData) { console.log('Ticket data now available, recreating menu'); createHamburgerMenu(); } }, 100); return; } console.log('Creating ticket hamburger menu with data:', window.ticketData); // Ticket page hamburger menu with inline editing hamburgerMenu.innerHTML = `

Ticket Actions

${window.ticketData.status}
P${window.ticketData.priority}
${window.ticketData.category}
${window.ticketData.type}
`; console.log('Ticket hamburger menu HTML created'); // Add inline editing functionality setupInlineEditing(hamburgerMenu); } else { console.log('Creating dashboard hamburger menu'); // Dashboard hamburger menu (your existing code) hamburgerMenu.innerHTML = `

Filters

Categories

Types

`; // Get current URL parameters const urlParams = new URLSearchParams(window.location.search); const currentCategories = urlParams.get('category') ? urlParams.get('category').split(',') : []; const currentTypes = urlParams.get('type') ? urlParams.get('type').split(',') : []; // Get containers const categoriesContainer = hamburgerMenu.querySelector('#category-filters'); const typesContainer = hamburgerMenu.querySelector('#type-filters'); // Get data from body attributes const categories = JSON.parse(document.body.dataset.categories || '[]'); const types = JSON.parse(document.body.dataset.types || '[]'); // Create checkboxes for categories categories.forEach(category => { const label = document.createElement('label'); label.style.display = 'block'; label.style.marginBottom = '5px'; const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.value = category; checkbox.name = 'category'; const isChecked = currentCategories.includes(category); label.appendChild(checkbox); label.appendChild(document.createTextNode(' ' + category)); categoriesContainer.appendChild(label); checkbox.checked = isChecked; }); // Create checkboxes for types types.forEach(type => { const label = document.createElement('label'); label.style.display = 'block'; label.style.marginBottom = '5px'; const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.value = type; checkbox.name = 'type'; const isChecked = currentTypes.includes(type); label.appendChild(checkbox); label.appendChild(document.createTextNode(' ' + type)); typesContainer.appendChild(label); checkbox.checked = isChecked; }); // Apply filters event const applyFiltersBtn = hamburgerMenu.querySelector('#apply-filters'); applyFiltersBtn.addEventListener('click', () => { const selectedCategories = Array.from( categoriesContainer.querySelectorAll('input:checked') ).map(cb => cb.value); const selectedTypes = Array.from( typesContainer.querySelectorAll('input:checked') ).map(cb => cb.value); const params = new URLSearchParams(window.location.search); 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'); } params.set('page', '1'); window.location.search = params.toString(); }); // Clear filters event const clearFiltersBtn = hamburgerMenu.querySelector('#clear-filters'); clearFiltersBtn.addEventListener('click', () => { const params = new URLSearchParams(window.location.search); params.delete('category'); params.delete('type'); params.set('page', '1'); window.location.search = params.toString(); }); } console.log('Adding hamburger menu to body'); // Add to body document.body.appendChild(hamburgerMenu); console.log('Hamburger menu added, setting up event listeners'); // Toggle hamburger menu const hamburgerIcon = hamburgerMenu.querySelector('.hamburger-icon'); const hamburgerContent = hamburgerMenu.querySelector('.hamburger-content'); if (hamburgerIcon && hamburgerContent) { hamburgerIcon.addEventListener('click', () => { console.log('Hamburger icon clicked'); hamburgerContent.classList.toggle('open'); document.body.classList.toggle('menu-open'); }); // Close hamburger menu const closeButton = hamburgerMenu.querySelector('.close-hamburger'); if (closeButton) { closeButton.addEventListener('click', () => { console.log('Close button clicked'); hamburgerContent.classList.remove('open'); document.body.classList.remove('menu-open'); }); } console.log('Hamburger menu created successfully'); } else { console.error('Failed to find hamburger icon or content'); } } function setupInlineEditing(hamburgerMenu) { const editableFields = hamburgerMenu.querySelectorAll('.editable-field'); editableFields.forEach(field => { const valueSpan = field.querySelector('.editable-value'); const dropdown = field.querySelector('.edit-dropdown'); const select = field.querySelector('.field-select'); const saveBtn = field.querySelector('.save-btn'); const cancelBtn = field.querySelector('.cancel-btn'); const fieldName = field.dataset.field; // Make value span clickable valueSpan.style.cursor = 'pointer'; valueSpan.style.padding = '4px 8px'; valueSpan.style.borderRadius = '4px'; valueSpan.style.transition = 'background-color 0.2s'; // Hover effect valueSpan.addEventListener('mouseenter', () => { valueSpan.style.backgroundColor = 'var(--hover-bg, #f0f0f0)'; }); valueSpan.addEventListener('mouseleave', () => { if (dropdown.style.display === 'none') { valueSpan.style.backgroundColor = 'transparent'; } }); // Click to edit valueSpan.addEventListener('click', () => { dropdown.style.display = 'block'; valueSpan.style.backgroundColor = 'var(--hover-bg, #f0f0f0)'; select.focus(); }); // Save changes saveBtn.addEventListener('click', () => { const newValue = select.value; const oldValue = valueSpan.dataset.current; if (newValue !== oldValue) { saveFieldChange(fieldName, newValue, valueSpan, dropdown); } else { cancelEdit(valueSpan, dropdown); } }); // Cancel changes cancelBtn.addEventListener('click', () => { select.value = valueSpan.dataset.current; cancelEdit(valueSpan, dropdown); }); // Cancel on escape key select.addEventListener('keydown', (e) => { if (e.key === 'Escape') { select.value = valueSpan.dataset.current; cancelEdit(valueSpan, dropdown); } else if (e.key === 'Enter') { saveBtn.click(); } }); // Cancel when clicking outside document.addEventListener('click', (e) => { if (!field.contains(e.target) && dropdown.style.display === 'block') { select.value = valueSpan.dataset.current; cancelEdit(valueSpan, dropdown); } }); }); } function saveFieldChange(fieldName, newValue, valueSpan, dropdown) { if (!window.ticketData) { console.error('No ticket data available'); return; } const data = { ticket_id: parseInt(window.ticketData.id), [fieldName]: fieldName === 'priority' ? parseInt(newValue) : newValue }; console.log('Saving field change:', data); // Show loading state valueSpan.style.opacity = '0.6'; fetch('/api/update_ticket.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }) .then(response => { return response.text().then(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 if (fieldName === 'priority') { valueSpan.textContent = 'P' + newValue; } else { valueSpan.textContent = newValue; } valueSpan.dataset.current = newValue; // Update window.ticketData window.ticketData[fieldName] = fieldName === 'priority' ? parseInt(newValue) : newValue; // Update main page elements if (fieldName === 'status') { const statusDisplay = document.getElementById('statusDisplay'); if (statusDisplay) { // Remove all existing status classes statusDisplay.className = statusDisplay.className.replace(/status-\S+/g, '').trim(); // Create the correct CSS class name to match your CSS // "Open" -> "status-Open" // "In Progress" -> "status-In-Progress" // "Closed" -> "status-Closed" const cssClass = newValue.replace(/\s+/g, '-'); // "In Progress" -> "In-Progress" const fullClassName = `status-${cssClass}`; statusDisplay.className = fullClassName; statusDisplay.textContent = newValue; console.log('Updated status display class to:', fullClassName); console.log('Status display element:', statusDisplay); } } if (fieldName === 'priority') { const priorityDisplay = document.querySelector('.priority-indicator'); if (priorityDisplay) { // Remove all priority classes first priorityDisplay.className = priorityDisplay.className.replace(/priority-\d+/g, ''); priorityDisplay.className = `priority-indicator priority-${newValue}`; priorityDisplay.textContent = 'P' + newValue; } // Update the ticket container's data-priority attribute for styling const ticketContainer = document.querySelector('.ticket-container'); if (ticketContainer) { ticketContainer.setAttribute('data-priority', newValue); } } console.log('Field updated successfully'); cancelEdit(valueSpan, dropdown); } else { console.error('Error updating field:', result.error || 'Unknown error'); alert('Error updating ' + fieldName + ': ' + (result.error || 'Unknown error')); cancelEdit(valueSpan, dropdown); } }) .catch(error => { console.error('Error updating field:', error); alert('Error updating ' + fieldName + ': ' + error.message); cancelEdit(valueSpan, dropdown); }) .finally(() => { valueSpan.style.opacity = '1'; }); } function cancelEdit(valueSpan, dropdown) { dropdown.style.display = 'none'; valueSpan.style.backgroundColor = 'transparent'; } // 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; } } }); }