2024-11-30 20:26:30 -05:00
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
|
// Only initialize filters if we're on the dashboard
|
|
|
|
|
|
if (document.querySelector('table')) {
|
|
|
|
|
|
initSearch();
|
|
|
|
|
|
initStatusFilter();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Keep theme toggle for all pages
|
|
|
|
|
|
initThemeToggle();
|
2025-03-11 20:40:20 -04:00
|
|
|
|
createHamburgerMenu();
|
|
|
|
|
|
|
2024-11-30 20:26:30 -05:00
|
|
|
|
// Load saved theme preference
|
|
|
|
|
|
const savedTheme = localStorage.getItem('theme') || 'light';
|
|
|
|
|
|
document.documentElement.setAttribute('data-theme', savedTheme);
|
|
|
|
|
|
|
|
|
|
|
|
// Add sorting functionality
|
|
|
|
|
|
const tableHeaders = document.querySelectorAll('th');
|
|
|
|
|
|
tableHeaders.forEach(header => {
|
|
|
|
|
|
header.addEventListener('click', () => {
|
|
|
|
|
|
const table = header.closest('table');
|
|
|
|
|
|
const index = Array.from(header.parentElement.children).indexOf(header);
|
|
|
|
|
|
sortTable(table, index);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Add settings modal functionality
|
|
|
|
|
|
const settingsIcon = document.querySelector('.settings-icon');
|
|
|
|
|
|
if (settingsIcon) {
|
|
|
|
|
|
settingsIcon.addEventListener('click', function(e) {
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
createSettingsModal();
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
function sortTable(table, column) {
|
|
|
|
|
|
// Remove existing sort indicators from all headers
|
|
|
|
|
|
const headers = table.querySelectorAll('th');
|
|
|
|
|
|
headers.forEach(header => {
|
|
|
|
|
|
header.classList.remove('sort-asc', 'sort-desc');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const rows = Array.from(table.querySelectorAll('tbody tr'));
|
|
|
|
|
|
|
|
|
|
|
|
// Determine current sort direction
|
|
|
|
|
|
const currentDirection = table.dataset.sortColumn === column
|
|
|
|
|
|
? (table.dataset.sortDirection === 'asc' ? 'desc' : 'asc')
|
|
|
|
|
|
: 'asc';
|
|
|
|
|
|
|
|
|
|
|
|
// Store current sorting column and direction
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
|
|
|
|
// Try numeric sorting first, fallback to string 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);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Add sort indicator to the current header
|
|
|
|
|
|
const currentHeader = headers[column];
|
|
|
|
|
|
currentHeader.classList.add(currentDirection === 'asc' ? 'sort-asc' : 'sort-desc');
|
|
|
|
|
|
|
|
|
|
|
|
// Reorder rows in the tbody
|
|
|
|
|
|
const tbody = table.querySelector('tbody');
|
|
|
|
|
|
rows.forEach(row => tbody.appendChild(row));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Add this to the DOMContentLoaded event listener to persist sorting on page load
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
|
const table = document.querySelector('table');
|
|
|
|
|
|
if (table) {
|
|
|
|
|
|
const savedSortColumn = localStorage.getItem('sortColumn');
|
|
|
|
|
|
const savedSortDirection = localStorage.getItem('sortDirection');
|
|
|
|
|
|
|
|
|
|
|
|
if (savedSortColumn !== null && savedSortDirection !== null) {
|
|
|
|
|
|
const headers = table.querySelectorAll('th');
|
|
|
|
|
|
const columnIndex = Array.from(headers).findIndex(header =>
|
|
|
|
|
|
header.textContent.toLowerCase().replace(' ', '_') === savedSortColumn
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (columnIndex !== -1) {
|
|
|
|
|
|
table.dataset.sortColumn = columnIndex;
|
|
|
|
|
|
table.dataset.sortDirection = savedSortDirection;
|
|
|
|
|
|
|
|
|
|
|
|
const header = headers[columnIndex];
|
|
|
|
|
|
header.classList.add(savedSortDirection === 'asc' ? 'sort-asc' : 'sort-desc');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Modify the existing event listeners for table headers
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
|
const tableHeaders = document.querySelectorAll('th');
|
|
|
|
|
|
tableHeaders.forEach((header, index) => {
|
|
|
|
|
|
header.addEventListener('click', () => {
|
|
|
|
|
|
const table = header.closest('table');
|
|
|
|
|
|
sortTable(table, index);
|
|
|
|
|
|
|
|
|
|
|
|
// Save sorting preferences
|
|
|
|
|
|
const columnName = header.textContent.toLowerCase().replace(' ', '_');
|
|
|
|
|
|
localStorage.setItem('sortColumn', columnName);
|
|
|
|
|
|
localStorage.setItem('sortDirection', table.dataset.sortDirection);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
function createSettingsModal() {
|
|
|
|
|
|
// Create modal backdrop
|
|
|
|
|
|
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>Toggle Columns</h3>
|
|
|
|
|
|
<div class="column-toggles">
|
|
|
|
|
|
<label>
|
|
|
|
|
|
<input type="checkbox" value="ticket_id" checked> Ticket ID
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<label>
|
|
|
|
|
|
<input type="checkbox" value="title" checked> Title
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<label>
|
|
|
|
|
|
<input type="checkbox" value="category" checked> Category
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<label>
|
|
|
|
|
|
<input type="checkbox" value="type" checked> Type
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<label>
|
|
|
|
|
|
<input type="checkbox" value="status" checked> Status
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<label>
|
|
|
|
|
|
<input type="checkbox" value="priority" checked> Priority
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<label>
|
|
|
|
|
|
<input type="checkbox" value="created" checked> Created
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<label>
|
|
|
|
|
|
<input type="checkbox" value="updated" checked> Updated
|
|
|
|
|
|
</label>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<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>
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
// Add to body
|
|
|
|
|
|
document.body.appendChild(backdrop);
|
|
|
|
|
|
|
|
|
|
|
|
// Load saved column visibility settings
|
|
|
|
|
|
const savedColumnSettings = JSON.parse(localStorage.getItem('columnVisibility') || '{}');
|
|
|
|
|
|
const checkboxes = backdrop.querySelectorAll('.column-toggles input');
|
|
|
|
|
|
checkboxes.forEach(checkbox => {
|
|
|
|
|
|
checkbox.checked = savedColumnSettings[checkbox.value] !== false;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Load saved rows per page setting
|
|
|
|
|
|
const savedRowsPerPage = localStorage.getItem('ticketsPerPage') || '5';
|
|
|
|
|
|
const rowsPerPageSelect = backdrop.querySelector('#rows-per-page');
|
|
|
|
|
|
rowsPerPageSelect.value = savedRowsPerPage;
|
|
|
|
|
|
|
|
|
|
|
|
// Close modal events
|
|
|
|
|
|
backdrop.querySelector('.close-modal').addEventListener('click', closeSettingsModal);
|
|
|
|
|
|
backdrop.querySelector('.cancel-settings').addEventListener('click', closeSettingsModal);
|
|
|
|
|
|
backdrop.querySelector('.save-settings').addEventListener('click', saveSettings);
|
|
|
|
|
|
|
|
|
|
|
|
// Close modal on backdrop click
|
|
|
|
|
|
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 column visibility
|
|
|
|
|
|
const checkboxes = document.querySelectorAll('.column-toggles input');
|
|
|
|
|
|
const columnVisibility = {};
|
|
|
|
|
|
|
|
|
|
|
|
checkboxes.forEach(checkbox => {
|
|
|
|
|
|
columnVisibility[checkbox.value] = checkbox.checked;
|
|
|
|
|
|
});
|
|
|
|
|
|
localStorage.setItem('columnVisibility', JSON.stringify(columnVisibility));
|
|
|
|
|
|
|
|
|
|
|
|
// 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=/`;
|
|
|
|
|
|
|
|
|
|
|
|
// Apply column visibility
|
|
|
|
|
|
applyColumnVisibility();
|
|
|
|
|
|
|
|
|
|
|
|
// Reload page to apply pagination changes
|
|
|
|
|
|
window.location.reload();
|
|
|
|
|
|
|
|
|
|
|
|
// Close modal
|
|
|
|
|
|
closeSettingsModal();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function applyColumnVisibility() {
|
|
|
|
|
|
const savedColumnSettings = JSON.parse(localStorage.getItem('columnVisibility') || '{}');
|
|
|
|
|
|
const table = document.querySelector('table');
|
|
|
|
|
|
|
|
|
|
|
|
if (table) {
|
|
|
|
|
|
const headers = table.querySelectorAll('th');
|
|
|
|
|
|
const rows = table.querySelectorAll('tbody tr');
|
|
|
|
|
|
|
|
|
|
|
|
headers.forEach((header, index) => {
|
|
|
|
|
|
const columnValue = header.textContent.toLowerCase().replace(' ', '_');
|
|
|
|
|
|
const isVisible = savedColumnSettings[columnValue] !== false;
|
|
|
|
|
|
|
|
|
|
|
|
header.style.display = isVisible ? '' : 'none';
|
|
|
|
|
|
|
|
|
|
|
|
rows.forEach(row => {
|
|
|
|
|
|
row.children[index].style.display = isVisible ? '' : 'none';
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Apply column visibility on page load
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', applyColumnVisibility);
|
|
|
|
|
|
// Dark mode toggle
|
|
|
|
|
|
function initThemeToggle() {
|
|
|
|
|
|
const toggle = document.createElement('button');
|
|
|
|
|
|
toggle.className = 'theme-toggle';
|
|
|
|
|
|
toggle.innerHTML = '🌓';
|
|
|
|
|
|
toggle.onclick = () => {
|
|
|
|
|
|
document.documentElement.setAttribute('data-theme',
|
|
|
|
|
|
document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'
|
|
|
|
|
|
);
|
|
|
|
|
|
localStorage.setItem('theme', document.documentElement.getAttribute('data-theme'));
|
|
|
|
|
|
};
|
|
|
|
|
|
document.body.appendChild(toggle);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Search functionality
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Filter by status
|
|
|
|
|
|
function initStatusFilter() {
|
2024-12-02 21:21:10 -05:00
|
|
|
|
const filterContainer = document.createElement('div');
|
|
|
|
|
|
filterContainer.className = 'status-filter-container';
|
|
|
|
|
|
|
|
|
|
|
|
// Create dropdown container
|
|
|
|
|
|
const dropdown = document.createElement('div');
|
|
|
|
|
|
dropdown.className = 'status-dropdown';
|
|
|
|
|
|
|
|
|
|
|
|
// Create dropdown header
|
|
|
|
|
|
const dropdownHeader = document.createElement('div');
|
|
|
|
|
|
dropdownHeader.className = 'dropdown-header';
|
|
|
|
|
|
dropdownHeader.textContent = 'Status Filter';
|
|
|
|
|
|
|
|
|
|
|
|
// Create dropdown content
|
|
|
|
|
|
const dropdownContent = document.createElement('div');
|
|
|
|
|
|
dropdownContent.className = 'dropdown-content';
|
|
|
|
|
|
|
|
|
|
|
|
const statuses = ['Open', '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()}`;
|
|
|
|
|
|
|
|
|
|
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
|
|
|
|
const currentStatuses = urlParams.get('status') ? urlParams.get('status').split(',') : [];
|
|
|
|
|
|
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);
|
|
|
|
|
|
localStorage.setItem('statusFilter', selectedStatuses.join(','));
|
|
|
|
|
|
window.location.href = selectedStatuses.length ? `?status=${selectedStatuses.join(',')}` : '?';
|
|
|
|
|
|
dropdown.classList.remove('active');
|
2024-11-30 20:26:30 -05:00
|
|
|
|
};
|
2024-12-02 21:21:10 -05:00
|
|
|
|
|
|
|
|
|
|
// Toggle dropdown on header click
|
|
|
|
|
|
dropdownHeader.onclick = () => {
|
|
|
|
|
|
dropdown.classList.toggle('active');
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
dropdown.appendChild(dropdownHeader);
|
|
|
|
|
|
dropdown.appendChild(dropdownContent);
|
|
|
|
|
|
dropdownContent.appendChild(saveButton);
|
|
|
|
|
|
filterContainer.appendChild(dropdown);
|
|
|
|
|
|
|
|
|
|
|
|
document.querySelector('.table-controls .table-actions').prepend(filterContainer);
|
2024-11-30 20:26:30 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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 (Created or Updated)
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Existing numeric and string comparison logic
|
|
|
|
|
|
const numA = parseFloat(aValue);
|
|
|
|
|
|
const numB = parseFloat(bValue);
|
|
|
|
|
|
|
|
|
|
|
|
if (!isNaN(numA) && !isNaN(numB)) {
|
|
|
|
|
|
return currentDirection === 'asc' ? numA - numB : numB - numA;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Modify the CSS to ensure arrows are more visible
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
|
const tableHeaders = document.querySelectorAll('th');
|
|
|
|
|
|
tableHeaders.forEach((header, index) => {
|
|
|
|
|
|
header.style.cursor = 'pointer'; // Make headers look clickable
|
|
|
|
|
|
header.addEventListener('click', () => {
|
|
|
|
|
|
const table = header.closest('table');
|
|
|
|
|
|
sortTable(table, index);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-03-11 20:46:28 -04:00
|
|
|
|
function toggleHamburgerEditMode() {
|
|
|
|
|
|
const editables = document.querySelectorAll('.hamburger-content .editable');
|
2025-03-11 20:55:04 -04:00
|
|
|
|
const editButton = document.getElementById('hamburgerEditButton');
|
|
|
|
|
|
const editModeButtons = document.getElementById('editModeButtons');
|
|
|
|
|
|
|
|
|
|
|
|
editButton.style.display = 'none';
|
|
|
|
|
|
editModeButtons.style.display = 'block';
|
|
|
|
|
|
editables.forEach(field => field.disabled = false);
|
|
|
|
|
|
}
|
2025-03-11 20:46:28 -04:00
|
|
|
|
|
2025-03-11 20:55:04 -04:00
|
|
|
|
function saveHamburgerChanges() {
|
|
|
|
|
|
saveTicket();
|
|
|
|
|
|
resetHamburgerEditMode();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function cancelHamburgerEdit() {
|
|
|
|
|
|
// Reset values to original
|
|
|
|
|
|
location.reload();
|
|
|
|
|
|
resetHamburgerEditMode();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function resetHamburgerEditMode() {
|
|
|
|
|
|
const editables = document.querySelectorAll('.hamburger-content .editable');
|
|
|
|
|
|
const editButton = document.getElementById('hamburgerEditButton');
|
|
|
|
|
|
const editModeButtons = document.getElementById('editModeButtons');
|
|
|
|
|
|
|
|
|
|
|
|
editButton.style.display = 'block';
|
|
|
|
|
|
editModeButtons.style.display = 'none';
|
|
|
|
|
|
editables.forEach(field => field.disabled = true);
|
2025-03-11 20:46:28 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-30 20:26:30 -05:00
|
|
|
|
function createHamburgerMenu() {
|
|
|
|
|
|
// Create hamburger menu container
|
|
|
|
|
|
const hamburgerMenu = document.createElement('div');
|
|
|
|
|
|
hamburgerMenu.className = 'hamburger-menu';
|
|
|
|
|
|
|
2025-03-11 20:42:38 -04:00
|
|
|
|
// Check if we're on a ticket page
|
|
|
|
|
|
const isTicketPage = window.location.pathname.includes('ticket.php');
|
2025-03-11 20:23:36 -04:00
|
|
|
|
|
2025-03-11 20:42:38 -04:00
|
|
|
|
if (isTicketPage) {
|
|
|
|
|
|
// Get current values from existing select elements
|
|
|
|
|
|
const selects = document.querySelectorAll('select.editable');
|
|
|
|
|
|
const values = {};
|
|
|
|
|
|
selects.forEach(select => {
|
|
|
|
|
|
values[select.dataset.field] = select.value;
|
|
|
|
|
|
});
|
2025-03-11 20:40:20 -04:00
|
|
|
|
|
2025-03-11 20:42:38 -04:00
|
|
|
|
hamburgerMenu.innerHTML = `
|
|
|
|
|
|
<div class="hamburger-icon">☰</div>
|
|
|
|
|
|
<div class="hamburger-content">
|
|
|
|
|
|
<div class="close-hamburger">☰</div>
|
|
|
|
|
|
<h3>Ticket Controls</h3>
|
|
|
|
|
|
<div class="menu-group">
|
|
|
|
|
|
<label>Status</label>
|
2025-03-11 20:52:11 -04:00
|
|
|
|
<select class="editable" data-field="status" disabled>
|
2025-03-11 20:42:38 -04:00
|
|
|
|
<option value="Open" ${values.status === 'Open' ? 'selected' : ''}>Open</option>
|
|
|
|
|
|
<option value="Closed" ${values.status === 'Closed' ? 'selected' : ''}>Closed</option>
|
|
|
|
|
|
</select>
|
2025-03-11 20:23:36 -04:00
|
|
|
|
</div>
|
2025-03-11 20:42:38 -04:00
|
|
|
|
<div class="menu-group">
|
|
|
|
|
|
<label>Priority</label>
|
2025-03-11 20:52:11 -04:00
|
|
|
|
<select class="editable" data-field="priority" disabled>
|
2025-03-11 20:42:38 -04:00
|
|
|
|
<option value="1" ${values.priority === '1' ? 'selected' : ''}>P1 - Critical Impact</option>
|
|
|
|
|
|
<option value="2" ${values.priority === '2' ? 'selected' : ''}>P2 - High Impact</option>
|
|
|
|
|
|
<option value="3" ${values.priority === '3' ? 'selected' : ''}>P3 - Medium Impact</option>
|
|
|
|
|
|
<option value="4" ${values.priority === '4' ? 'selected' : ''}>P4 - Low Impact</option>
|
|
|
|
|
|
</select>
|
2025-03-11 20:23:36 -04:00
|
|
|
|
</div>
|
2025-03-11 20:42:38 -04:00
|
|
|
|
<div class="menu-group">
|
|
|
|
|
|
<label>Category</label>
|
2025-03-11 20:52:11 -04:00
|
|
|
|
<select class="editable" data-field="category" disabled>
|
2025-03-11 20:42:38 -04:00
|
|
|
|
<option value="Hardware" ${values.category === 'Hardware' ? 'selected' : ''}>Hardware</option>
|
|
|
|
|
|
<option value="Software" ${values.category === 'Software' ? 'selected' : ''}>Software</option>
|
|
|
|
|
|
<option value="Network" ${values.category === 'Network' ? 'selected' : ''}>Network</option>
|
|
|
|
|
|
<option value="Security" ${values.category === 'Security' ? 'selected' : ''}>Security</option>
|
|
|
|
|
|
<option value="Other" ${values.category === 'Other' ? 'selected' : ''}>Other</option>
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="menu-group">
|
|
|
|
|
|
<label>Type</label>
|
2025-03-11 20:52:11 -04:00
|
|
|
|
<select class="editable" data-field="type" disabled>
|
2025-03-11 20:42:38 -04:00
|
|
|
|
<option value="Maintenance" ${values.type === 'Maintenance' ? 'selected' : ''}>Maintenance</option>
|
|
|
|
|
|
<option value="Install" ${values.type === 'Install' ? 'selected' : ''}>Install</option>
|
|
|
|
|
|
<option value="Task" ${values.type === 'Task' ? 'selected' : ''}>Task</option>
|
|
|
|
|
|
<option value="Upgrade" ${values.type === 'Upgrade' ? 'selected' : ''}>Upgrade</option>
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
2025-03-11 20:46:28 -04:00
|
|
|
|
<div class="menu-controls">
|
2025-03-11 20:52:11 -04:00
|
|
|
|
<button id="hamburgerEditButton" class="btn primary" onclick="toggleHamburgerEditMode()">Edit Ticket</button>
|
2025-03-11 20:55:04 -04:00
|
|
|
|
<div id="editModeButtons" style="display: none; margin-top: 10px;">
|
|
|
|
|
|
<button onclick="saveHamburgerChanges()" class="btn primary">Save Changes</button>
|
|
|
|
|
|
<button onclick="cancelHamburgerEdit()" class="btn">Cancel</button>
|
|
|
|
|
|
</div>
|
2025-03-11 20:46:28 -04:00
|
|
|
|
</div>
|
2025-03-11 20:42:38 -04:00
|
|
|
|
</div>
|
|
|
|
|
|
`;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
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>
|
|
|
|
|
|
`;
|
2024-11-30 20:26:30 -05:00
|
|
|
|
|
2025-03-11 20:42:38 -04:00
|
|
|
|
// Populate categories and types from data attributes
|
|
|
|
|
|
const categoriesContainer = hamburgerMenu.querySelector('#category-filters');
|
|
|
|
|
|
const typesContainer = hamburgerMenu.querySelector('#type-filters');
|
|
|
|
|
|
|
|
|
|
|
|
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');
|
|
|
|
|
|
const checkbox = document.createElement('input');
|
|
|
|
|
|
checkbox.type = 'checkbox';
|
|
|
|
|
|
checkbox.value = category;
|
|
|
|
|
|
checkbox.name = 'category';
|
|
|
|
|
|
label.appendChild(checkbox);
|
|
|
|
|
|
label.appendChild(document.createTextNode(category));
|
|
|
|
|
|
categoriesContainer.appendChild(label);
|
|
|
|
|
|
});
|
2024-11-30 20:26:30 -05:00
|
|
|
|
|
2025-03-11 20:42:38 -04:00
|
|
|
|
// Create checkboxes for types
|
|
|
|
|
|
types.forEach(type => {
|
|
|
|
|
|
const label = document.createElement('label');
|
|
|
|
|
|
const checkbox = document.createElement('input');
|
|
|
|
|
|
checkbox.type = 'checkbox';
|
|
|
|
|
|
checkbox.value = type;
|
|
|
|
|
|
checkbox.name = 'type';
|
|
|
|
|
|
label.appendChild(checkbox);
|
|
|
|
|
|
label.appendChild(document.createTextNode(type));
|
|
|
|
|
|
typesContainer.appendChild(label);
|
|
|
|
|
|
});
|
2024-11-30 20:26:30 -05:00
|
|
|
|
|
2025-03-11 20:42:38 -04:00
|
|
|
|
// Apply filters
|
|
|
|
|
|
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);
|
2024-11-30 20:26:30 -05:00
|
|
|
|
|
2025-03-11 20:42:38 -04:00
|
|
|
|
// Construct URL with filters
|
|
|
|
|
|
const params = new URLSearchParams(window.location.search);
|
|
|
|
|
|
|
|
|
|
|
|
if (selectedCategories.length > 0) {
|
|
|
|
|
|
params.set('category', selectedCategories.join(','));
|
|
|
|
|
|
} else {
|
2025-03-11 20:23:36 -04:00
|
|
|
|
params.delete('category');
|
2025-03-11 20:42:38 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (selectedTypes.length > 0) {
|
|
|
|
|
|
params.set('type', selectedTypes.join(','));
|
|
|
|
|
|
} else {
|
2025-03-11 20:23:36 -04:00
|
|
|
|
params.delete('type');
|
2025-03-11 20:42:38 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Reload with new filters
|
|
|
|
|
|
window.location.search = params.toString();
|
2025-03-11 20:26:15 -04:00
|
|
|
|
});
|
|
|
|
|
|
|
2025-03-11 20:42:38 -04:00
|
|
|
|
// Clear filters
|
|
|
|
|
|
const clearFiltersBtn = hamburgerMenu.querySelector('#clear-filters');
|
|
|
|
|
|
clearFiltersBtn.addEventListener('click', () => {
|
|
|
|
|
|
const params = new URLSearchParams(window.location.search);
|
|
|
|
|
|
params.delete('category');
|
|
|
|
|
|
params.delete('type');
|
|
|
|
|
|
window.location.search = params.toString();
|
2025-03-11 20:26:15 -04:00
|
|
|
|
});
|
2025-03-11 20:42:38 -04:00
|
|
|
|
}
|
|
|
|
|
|
// Add to body
|
|
|
|
|
|
document.body.appendChild(hamburgerMenu);
|
|
|
|
|
|
|
|
|
|
|
|
// Toggle hamburger menu
|
|
|
|
|
|
const hamburgerIcon = hamburgerMenu.querySelector('.hamburger-icon');
|
|
|
|
|
|
const hamburgerContent = hamburgerMenu.querySelector('.hamburger-content');
|
|
|
|
|
|
hamburgerIcon.addEventListener('click', () => {
|
|
|
|
|
|
hamburgerContent.classList.toggle('open');
|
|
|
|
|
|
document.body.classList.toggle('menu-open');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Close hamburger menu
|
|
|
|
|
|
const closeButton = hamburgerMenu.querySelector('.close-hamburger');
|
|
|
|
|
|
closeButton.addEventListener('click', () => {
|
|
|
|
|
|
hamburgerContent.classList.remove('open');
|
|
|
|
|
|
document.body.classList.remove('menu-open');
|
|
|
|
|
|
});
|
2024-11-30 20:26:30 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Add to DOMContentLoaded
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', createHamburgerMenu);
|