854 lines
33 KiB
JavaScript
854 lines
33 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();
|
||
|
||
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 = `
|
||
<div class="settings-modal">
|
||
<div class="settings-modal-header">
|
||
<h2>Dashboard Settings</h2>
|
||
<button class="close-modal">×</button>
|
||
</div>
|
||
<div class="settings-modal-content">
|
||
<div class="setting-group">
|
||
<h3>Rows per Page</h3>
|
||
<select id="rows-per-page">
|
||
<option value="15">15</option>
|
||
<option value="25">25</option>
|
||
<option value="50">50</option>
|
||
<option value="100">100</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="settings-modal-footer">
|
||
<button class="save-settings">Save Settings</button>
|
||
<button class="cancel-settings">Cancel</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
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 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 = `
|
||
<div class="hamburger-icon">☰</div>
|
||
<div class="hamburger-content">
|
||
<div class="close-hamburger">☰</div>
|
||
<h3>Ticket Actions</h3>
|
||
<div class="ticket-info-editable">
|
||
<div class="editable-field" data-field="status">
|
||
<label><strong>Status:</strong></label>
|
||
<span class="editable-value" data-current="${window.ticketData.status}">${window.ticketData.status}</span>
|
||
<div class="edit-dropdown" style="display: none;">
|
||
<select class="field-select">
|
||
<option value="Open" ${window.ticketData.status === 'Open' ? 'selected' : ''}>Open</option>
|
||
<option value="In Progress" ${window.ticketData.status === 'In Progress' ? 'selected' : ''}>In Progress</option>
|
||
<option value="Closed" ${window.ticketData.status === 'Closed' ? 'selected' : ''}>Closed</option>
|
||
</select>
|
||
<div class="edit-actions">
|
||
<button class="save-btn">✓</button>
|
||
<button class="cancel-btn">✗</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="editable-field" data-field="priority">
|
||
<label><strong>Priority:</strong></label>
|
||
<span class="editable-value" data-current="${window.ticketData.priority}">P${window.ticketData.priority}</span>
|
||
<div class="edit-dropdown" style="display: none;">
|
||
<select class="field-select">
|
||
<option value="1" ${String(window.ticketData.priority) === '1' ? 'selected' : ''}>P1 - Critical</option>
|
||
<option value="2" ${String(window.ticketData.priority) === '2' ? 'selected' : ''}>P2 - High</option>
|
||
<option value="3" ${String(window.ticketData.priority) === '3' ? 'selected' : ''}>P3 - Medium</option>
|
||
<option value="4" ${String(window.ticketData.priority) === '4' ? 'selected' : ''}>P4 - Low</option>
|
||
<option value="5" ${String(window.ticketData.priority) === '5' ? 'selected' : ''}>P5 - Lowest</option>
|
||
</select>
|
||
<div class="edit-actions">
|
||
<button class="save-btn">✓</button>
|
||
<button class="cancel-btn">✗</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="editable-field" data-field="category">
|
||
<label><strong>Category:</strong></label>
|
||
<span class="editable-value" data-current="${window.ticketData.category}">${window.ticketData.category}</span>
|
||
<div class="edit-dropdown" style="display: none;">
|
||
<select class="field-select">
|
||
<option value="Hardware" ${window.ticketData.category === 'Hardware' ? 'selected' : ''}>Hardware</option>
|
||
<option value="Software" ${window.ticketData.category === 'Software' ? 'selected' : ''}>Software</option>
|
||
</select>
|
||
<div class="edit-actions">
|
||
<button class="save-btn">✓</button>
|
||
<button class="cancel-btn">✗</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="editable-field" data-field="type">
|
||
<label><strong>Type:</strong></label>
|
||
<span class="editable-value" data-current="${window.ticketData.type}">${window.ticketData.type}</span>
|
||
<div class="edit-dropdown" style="display: none;">
|
||
<select class="field-select">
|
||
<option value="Install" ${window.ticketData.type === 'Install' ? 'selected' : ''}>Install</option>
|
||
<option value="Maintenance" ${window.ticketData.type === 'Maintenance' ? 'selected' : ''}>Maintenance</option>
|
||
<option value="Problem" ${window.ticketData.type === 'Problem' ? 'selected' : ''}>Problem</option>
|
||
<option value="Request" ${window.ticketData.type === 'Request' ? 'selected' : ''}>Request</option>
|
||
<option value="Task" ${window.ticketData.type === 'Task' ? 'selected' : ''}>Task</option>
|
||
<option value="Upgrade" ${window.ticketData.type === 'Upgrade' ? 'selected' : ''}>Upgrade</option>
|
||
</select>
|
||
<div class="edit-actions">
|
||
<button class="save-btn">✓</button>
|
||
<button class="cancel-btn">✗</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
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 = `
|
||
<div class="hamburger-icon">☰</div>
|
||
<div class="hamburger-content">
|
||
<div class="close-hamburger">☰</div>
|
||
<h3>Filters</h3>
|
||
<div class="filter-section">
|
||
<h4>Categories</h4>
|
||
<div id="category-filters"></div>
|
||
</div>
|
||
<div class="filter-section">
|
||
<h4>Types</h4>
|
||
<div id="type-filters"></div>
|
||
</div>
|
||
<div class="filter-actions">
|
||
<button id="apply-filters">Apply Filters</button>
|
||
<button id="clear-filters">Clear Filters</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
// 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;
|
||
}
|
||
}
|
||
});
|
||
} |