Spaces:
Running
Running
// Main JavaScript functionality for Ticket Collection System | |
document.addEventListener('DOMContentLoaded', function() { | |
initializeAdmin(); | |
}); | |
// Initialize admin dashboard functionality | |
function initializeAdmin() { | |
if (window.location.pathname === '/admin') { | |
initializeTableFeatures(); | |
initializeSearch(); | |
initializeFilters(); | |
updateLastUpdated(); | |
} | |
} | |
// Initialize table sorting and filtering features | |
function initializeTableFeatures() { | |
const table = document.getElementById('ticketsTable'); | |
if (!table) return; | |
// Store original table data for filtering | |
window.originalTableData = Array.from(table.querySelectorAll('tbody tr')); | |
updateShowingCount(); | |
} | |
// Table sorting functionality | |
let sortDirection = {}; | |
function sortTable(columnIndex) { | |
const table = document.getElementById('ticketsTable'); | |
const tbody = table.querySelector('tbody'); | |
const rows = Array.from(tbody.querySelectorAll('tr')); | |
// Determine sort direction | |
const currentDir = sortDirection[columnIndex] || 'asc'; | |
const newDir = currentDir === 'asc' ? 'desc' : 'asc'; | |
sortDirection[columnIndex] = newDir; | |
// Update sort icons | |
updateSortIcons(columnIndex, newDir); | |
// Sort rows | |
rows.sort((a, b) => { | |
const aText = a.cells[columnIndex].textContent.trim(); | |
const bText = b.cells[columnIndex].textContent.trim(); | |
// Handle numeric columns | |
if (columnIndex === 3) { // Tickets column | |
const aNum = parseInt(aText) || 0; | |
const bNum = parseInt(bText) || 0; | |
return newDir === 'asc' ? aNum - bNum : bNum - aNum; | |
} | |
// Handle date columns | |
if (columnIndex === 7) { // Timestamp column | |
const aDate = new Date(aText); | |
const bDate = new Date(bText); | |
return newDir === 'asc' ? aDate - bDate : bDate - aDate; | |
} | |
// Handle text columns | |
const comparison = aText.localeCompare(bText); | |
return newDir === 'asc' ? comparison : -comparison; | |
}); | |
// Rebuild table | |
rows.forEach(row => tbody.appendChild(row)); | |
updateShowingCount(); | |
} | |
// Update sort direction icons | |
function updateSortIcons(activeColumn, direction) { | |
const headers = document.querySelectorAll('#ticketsTable thead th'); | |
headers.forEach((header, index) => { | |
const icon = header.querySelector('i.fa-sort, i.fa-sort-up, i.fa-sort-down'); | |
if (icon) { | |
if (index === activeColumn) { | |
icon.className = direction === 'asc' ? 'fas fa-sort-up' : 'fas fa-sort-down'; | |
} else { | |
icon.className = 'fas fa-sort'; | |
} | |
} | |
}); | |
} | |
// Search functionality | |
function initializeSearch() { | |
const searchInput = document.getElementById('searchInput'); | |
if (!searchInput) return; | |
searchInput.addEventListener('input', function() { | |
const searchTerm = this.value.toLowerCase(); | |
filterTable(); | |
}); | |
} | |
// Filter functionality | |
function initializeFilters() { | |
const countryFilter = document.getElementById('countryFilter'); | |
if (!countryFilter) return; | |
countryFilter.addEventListener('change', function() { | |
filterTable(); | |
}); | |
} | |
// Combined filter and search function | |
function filterTable() { | |
if (!window.originalTableData) return; | |
const searchTerm = document.getElementById('searchInput')?.value.toLowerCase() || ''; | |
const countryFilter = document.getElementById('countryFilter')?.value || ''; | |
const tbody = document.querySelector('#ticketsTable tbody'); | |
// Clear current table | |
tbody.innerHTML = ''; | |
let visibleCount = 0; | |
window.originalTableData.forEach(row => { | |
const rowText = row.textContent.toLowerCase(); | |
const countryCell = row.cells[5].textContent.trim(); | |
const matchesSearch = searchTerm === '' || rowText.includes(searchTerm); | |
const matchesCountry = countryFilter === '' || countryCell === countryFilter; | |
if (matchesSearch && matchesCountry) { | |
tbody.appendChild(row.cloneNode(true)); | |
visibleCount++; | |
} | |
}); | |
// Update showing count | |
document.getElementById('showingCount').textContent = visibleCount; | |
// Show empty state if no results | |
if (visibleCount === 0) { | |
showEmptySearchResults(); | |
} | |
} | |
// Show empty search results | |
function showEmptySearchResults() { | |
const tbody = document.querySelector('#ticketsTable tbody'); | |
const emptyRow = document.createElement('tr'); | |
emptyRow.innerHTML = ` | |
<td colspan="9" class="text-center py-4"> | |
<i class="fas fa-search fa-2x text-muted mb-2"></i> | |
<p class="text-muted mb-0">No tickets match your search criteria</p> | |
</td> | |
`; | |
tbody.appendChild(emptyRow); | |
} | |
// Update showing count | |
function updateShowingCount() { | |
const tbody = document.querySelector('#ticketsTable tbody'); | |
if (!tbody) return; | |
const visibleRows = tbody.querySelectorAll('tr').length; | |
const showingElement = document.getElementById('showingCount'); | |
if (showingElement) { | |
showingElement.textContent = visibleRows; | |
} | |
} | |
// Refresh data function | |
function refreshData() { | |
const refreshBtn = document.querySelector('button[onclick="refreshData()"]'); | |
if (refreshBtn) { | |
refreshBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i> Refreshing...'; | |
refreshBtn.disabled = true; | |
} | |
// Simulate refresh delay | |
setTimeout(() => { | |
location.reload(); | |
}, 500); | |
} | |
// Export visible data function | |
function exportVisible() { | |
const table = document.getElementById('ticketsTable'); | |
if (!table) return; | |
const visibleRows = Array.from(table.querySelectorAll('tbody tr')); | |
if (visibleRows.length === 0) { | |
alert('No data to export'); | |
return; | |
} | |
// Create CSV content | |
let csvContent = 'Email,Phone,Name,Tickets,Ticket Number,Country,Region,Timestamp\n'; | |
visibleRows.forEach(row => { | |
const cells = Array.from(row.cells).slice(0, 8); // Exclude actions column | |
const rowData = cells.map(cell => { | |
let text = cell.textContent.trim(); | |
// Clean up badge text | |
if (cell.querySelector('.badge')) { | |
text = cell.querySelector('.badge').textContent.trim(); | |
} | |
// Clean up code text | |
if (cell.querySelector('code')) { | |
text = cell.querySelector('code').textContent.trim(); | |
} | |
// Escape quotes and commas | |
if (text.includes(',') || text.includes('"')) { | |
text = '"' + text.replace(/"/g, '""') + '"'; | |
} | |
return text; | |
}); | |
csvContent += rowData.join(',') + '\n'; | |
}); | |
// Download CSV | |
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); | |
const link = document.createElement('a'); | |
if (link.download !== undefined) { | |
const url = URL.createObjectURL(blob); | |
link.setAttribute('href', url); | |
link.setAttribute('download', `ticket_data_filtered_${new Date().toISOString().split('T')[0]}.csv`); | |
link.style.visibility = 'hidden'; | |
document.body.appendChild(link); | |
link.click(); | |
document.body.removeChild(link); | |
} | |
} | |
// Update last updated timestamp | |
function updateLastUpdated() { | |
const element = document.getElementById('lastUpdated'); | |
if (element) { | |
const now = new Date(); | |
element.textContent = now.toISOString().slice(0, 19).replace('T', ' '); | |
} | |
} | |
// Utility function to show notifications | |
function showNotification(message, type = 'info') { | |
const alertDiv = document.createElement('div'); | |
alertDiv.className = `alert alert-${type} alert-dismissible fade show position-fixed`; | |
alertDiv.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;'; | |
alertDiv.innerHTML = ` | |
${message} | |
<button type="button" class="btn-close" data-bs-dismiss="alert"></button> | |
`; | |
document.body.appendChild(alertDiv); | |
// Auto-remove after 5 seconds | |
setTimeout(() => { | |
if (alertDiv.parentNode) { | |
alertDiv.parentNode.removeChild(alertDiv); | |
} | |
}, 5000); | |
} | |
// Handle form submissions with loading states | |
document.addEventListener('submit', function(e) { | |
const form = e.target; | |
if (form.tagName === 'FORM') { | |
const submitBtn = form.querySelector('button[type="submit"]'); | |
if (submitBtn) { | |
submitBtn.disabled = true; | |
const originalHtml = submitBtn.innerHTML; | |
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i> Processing...'; | |
// Re-enable after 3 seconds (fallback) | |
setTimeout(() => { | |
submitBtn.disabled = false; | |
submitBtn.innerHTML = originalHtml; | |
}, 3000); | |
} | |
} | |
}); | |
// Add keyboard shortcuts | |
document.addEventListener('keydown', function(e) { | |
// Ctrl+R or F5 for refresh | |
if ((e.ctrlKey && e.key === 'r') || e.key === 'F5') { | |
if (window.location.pathname === '/admin') { | |
e.preventDefault(); | |
refreshData(); | |
} | |
} | |
// Ctrl+F for search focus | |
if (e.ctrlKey && e.key === 'f') { | |
const searchInput = document.getElementById('searchInput'); | |
if (searchInput) { | |
e.preventDefault(); | |
searchInput.focus(); | |
searchInput.select(); | |
} | |
} | |
// Escape to clear search | |
if (e.key === 'Escape') { | |
const searchInput = document.getElementById('searchInput'); | |
if (searchInput && searchInput.value) { | |
searchInput.value = ''; | |
filterTable(); | |
} | |
} | |
}); | |
// Add smooth scrolling for anchor links | |
document.querySelectorAll('a[href^="#"]').forEach(anchor => { | |
anchor.addEventListener('click', function (e) { | |
e.preventDefault(); | |
const target = document.querySelector(this.getAttribute('href')); | |
if (target) { | |
target.scrollIntoView({ | |
behavior: 'smooth', | |
block: 'start' | |
}); | |
} | |
}); | |
}); | |