Sidebar with no hamburger menu
This commit is contained in:
@@ -17,23 +17,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
// 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);
|
||||
}
|
||||
initSidebarFilters();
|
||||
}
|
||||
|
||||
// Initialize for all pages
|
||||
@@ -55,6 +39,72 @@ function initTableSorting() {
|
||||
});
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -354,452 +404,6 @@ function quickSave() {
|
||||
});
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
<div class="ascii-subsection-header">Ticket Actions</div>
|
||||
|
||||
<div class="ascii-frame-inner">
|
||||
<div class="ticket-info-editable">
|
||||
<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>
|
||||
</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>
|
||||
|
||||
<div class="ascii-subsection-header">Filters</div>
|
||||
|
||||
<div class="ascii-frame-inner">
|
||||
<div class="dashboard-filters">
|
||||
<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>
|
||||
</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() {
|
||||
@@ -921,14 +525,16 @@ function toggleSelectAll() {
|
||||
function updateSelectionCount() {
|
||||
const checkboxes = document.querySelectorAll('.ticket-checkbox:checked');
|
||||
const count = checkboxes.length;
|
||||
const toolbar = document.querySelector('.bulk-actions-toolbar');
|
||||
const toolbar = document.querySelector('.bulk-actions-inline');
|
||||
const countDisplay = document.getElementById('selected-count');
|
||||
|
||||
if (count > 0) {
|
||||
toolbar.style.display = 'flex';
|
||||
countDisplay.textContent = count;
|
||||
} else {
|
||||
toolbar.style.display = 'none';
|
||||
if (toolbar && countDisplay) {
|
||||
if (count > 0) {
|
||||
toolbar.style.display = 'flex';
|
||||
countDisplay.textContent = count;
|
||||
} else {
|
||||
toolbar.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user