class DashboardManager { constructor() { this.selectedQRCodes = new Set(); this.allExpanded = false; this.init(); } init() { this.setupEventListeners(); this.addScrollAnimations(); } animateOut(element, callback) { element.classList.add("fade-out"); setTimeout(() => { if (callback) callback(); element.style.display = "none"; }, 300); } // Setup event listeners setupEventListeners() { // Keyboard shortcuts document.addEventListener("keydown", (e) => { // ESC to close modals if (e.key === "Escape") { this.closeQRModal(); } // Ctrl/Cmd + F to focus search if ((e.ctrlKey || e.metaKey) && e.key === "f") { e.preventDefault(); const searchInput = document.getElementById("qrSearch"); if (searchInput) searchInput.focus(); } // Delete key for bulk delete (when items selected) if (e.key === "Delete" && this.selectedQRCodes.size > 0) { e.preventDefault(); this.bulkDeleteQRCodes(); } }); // Expand/collapse all toggle const expandToggle = document.getElementById("expandAllToggle"); if (expandToggle) { expandToggle.addEventListener("click", () => { this.toggleExpandAll(); }); } } // Add scroll animations for QR items addScrollAnimations() { const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { entry.target.classList.add("animate-in"); } }); }, { threshold: 0.1 } ); const qrItems = document.querySelectorAll(".qr-item"); qrItems.forEach((item) => observer.observe(item)); } // FIXED: QR Code Toggle Status toggleQRCodeStatus(qrId) { const qrItem = document.querySelector(`[data-qr-id="${qrId}"]`); if (!qrItem) return; const currentStatus = qrItem.dataset.status; const newStatus = currentStatus === "active" ? "inactive" : "active"; // Show loading state const toggleBtn = document.getElementById(`toggle-btn-${qrId}`); const toggleIcon = document.getElementById(`toggle-icon-${qrId}`); if (toggleBtn && toggleIcon) { toggleBtn.classList.add("status-loading"); toggleIcon.className = "fas fa-spinner fa-spin"; toggleBtn.disabled = true; } // FIXED: Use correct endpoint with POST method for JSON response fetch(`/qr-codes/${qrId}/toggle-status`, { method: "POST", headers: { "Content-Type": "application/json", "X-Requested-With": "XMLHttpRequest", }, }) .then((response) => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) .then((result) => { if (result.success) { // Update UI with new status this.updateQRStatus(qrId, result.new_status ? "active" : "inactive"); window.showToast(result.message, "success"); } else { throw new Error(result.message || "Failed to update status"); } }) .catch((error) => { console.error("Status update error:", error); window.showToast("Failed to update QR code status", "error"); }) .finally(() => { // Remove loading state if (toggleBtn && toggleIcon) { toggleBtn.classList.remove("status-loading"); toggleBtn.disabled = false; // Restore icon based on current status const currentStatus = qrItem.dataset.status; toggleIcon.className = `fas ${ currentStatus === "active" ? "fa-pause" : "fa-play" }`; } }); } // Update QR status in UI updateQRStatus(qrId, newStatus) { const qrItem = document.querySelector(`[data-qr-id="${qrId}"]`); if (!qrItem) return; // Update data attribute qrItem.dataset.status = newStatus; // Update status badge const statusBadge = qrItem.querySelector(".qr-status"); if (statusBadge) { statusBadge.className = `qr-status ${newStatus}`; statusBadge.innerHTML = ` ${newStatus === "active" ? "Active" : "Inactive"} `; } // Update toggle buttons this.updateToggleButton(qrId, newStatus); } // Update toggle button appearance updateToggleButton(qrId, status) { const toggleBtn = document.getElementById(`toggle-btn-${qrId}`); const toggleIcon = document.getElementById(`toggle-icon-${qrId}`); const detailToggleBtn = document.getElementById( `detail-toggle-btn-${qrId}` ); if (toggleBtn && toggleIcon) { // Update quick action button toggleBtn.className = `action-btn btn-status ${ status === "active" ? "btn-deactivate" : "btn-activate" }`; toggleBtn.title = `${ status === "active" ? "Deactivate" : "Activate" } QR Code`; toggleIcon.className = `fas ${ status === "active" ? "fa-pause" : "fa-play" }`; } if (detailToggleBtn) { // Update detail action button detailToggleBtn.className = `btn ${ status === "active" ? "btn-warning" : "btn-success" }`; detailToggleBtn.innerHTML = ` ${status === "active" ? "Deactivate" : "Activate"} QR Code `; } } async deleteQRCode(qrId, qrName) { if (!confirm(`Delete "${qrName}"? This cannot be undone.`)) return; try { // Show loading state const deleteBtn = document.querySelector(`[onclick*="deleteQRCode(${qrId}"]`); if (deleteBtn) { deleteBtn.disabled = true; deleteBtn.innerHTML = ' Deleting...'; } const response = await fetch(`/qr-codes/${qrId}/delete`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, }); // Don't try to parse as JSON - just check if request was successful if (response.ok) { // Remove QR item from page immediately const qrItem = document.querySelector(`[data-qr-id="${qrId}"]`); if (qrItem) { qrItem.style.transition = "opacity 0.3s"; qrItem.style.opacity = "0"; setTimeout(() => { qrItem.remove(); if (this.updateResultsCount) this.updateResultsCount(); }, 300); } // Use simple alert instead of problematic showToast alert(`QR code "${qrName}" deleted successfully!`); } else { throw new Error(`Server error: ${response.status}`); } } catch (error) { console.error("Delete error:", error); // Restore button if there was an error const deleteBtn = document.querySelector(`[onclick*="deleteQRCode(${qrId}"]`); if (deleteBtn) { deleteBtn.disabled = false; deleteBtn.innerHTML = ''; } // Use simple alert instead of problematic showToast alert("Failed to delete QR code. Please try again."); } } // Show custom delete confirmation dialog showDeleteConfirmation(qrName) { return new Promise((resolve) => { const modal = document.createElement("div"); modal.className = "modal"; modal.style.display = "flex"; modal.innerHTML = `
`; document.body.appendChild(modal); window.confirmDelete = () => { document.body.removeChild(modal); delete window.confirmDelete; delete window.cancelDelete; resolve(true); }; window.cancelDelete = () => { document.body.removeChild(modal); delete window.confirmDelete; delete window.cancelDelete; resolve(false); }; // Close on ESC key const escHandler = (e) => { if (e.key === "Escape") { window.cancelDelete(); document.removeEventListener("keydown", escHandler); } }; document.addEventListener("keydown", escHandler); // Close on backdrop click modal.addEventListener("click", (e) => { if (e.target === modal) { window.cancelDelete(); } }); }); } // Bulk delete functionality async bulkDeleteQRCodes() { if (this.selectedQRCodes.size === 0) return; const confirmed = await this.showBulkDeleteConfirmation(this.selectedQRCodes.size); if (!confirmed) return; const deletePromises = Array.from(this.selectedQRCodes).map(qrId => { const qrItem = document.querySelector(`[data-qr-id="${qrId}"]`); const qrName = qrItem ? qrItem.querySelector('.qr-name')?.textContent || 'Unknown' : 'Unknown'; return this.deleteQRCode(qrId, qrName); }); try { await Promise.all(deletePromises); this.selectedQRCodes.clear(); window.showToast(`Successfully deleted ${deletePromises.length} QR codes`, "success"); } catch (error) { console.error("Bulk delete error:", error); window.showToast("Some QR codes could not be deleted", "error"); } } showBulkDeleteConfirmation(count) { return new Promise((resolve) => { const modal = document.createElement("div"); modal.className = "modal"; modal.style.display = "flex"; modal.innerHTML = ` `; document.body.appendChild(modal); window.confirmBulkDelete = () => { document.body.removeChild(modal); delete window.confirmBulkDelete; delete window.cancelBulkDelete; resolve(true); }; window.cancelBulkDelete = () => { document.body.removeChild(modal); delete window.confirmBulkDelete; delete window.cancelBulkDelete; resolve(false); }; }); } // QR Modal functions openQRModal(qrData) { const modal = document.getElementById("qrModal"); const modalImage = document.getElementById("modalQRImage"); const modalTitle = document.getElementById("modalTitle"); if (modal && modalImage && modalTitle) { modalTitle.textContent = `QR Code: ${qrData.name}`; modalImage.src = qrData.image; modal.style.display = "flex"; } } closeQRModal() { const modal = document.getElementById("qrModal"); if (modal) { modal.style.display = "none"; } } // Expand/collapse functionality toggleExpandAll() { const qrItems = document.querySelectorAll(".qr-item"); const expandToggle = document.getElementById("expandAllToggle"); this.allExpanded = !this.allExpanded; qrItems.forEach((item) => { if (this.allExpanded) { item.classList.add("expanded"); } else { item.classList.remove("expanded"); } }); if (expandToggle) { expandToggle.innerHTML = this.allExpanded ? ' Collapse All' : ' Expand All'; } } toggleQRItem(element) { element.classList.toggle("expanded"); } // Copy QR data to clipboard copyQRData(name, location, address, event) { const data = `QR Code: ${name}\nLocation: ${location}\nAddress: ${address}\nEvent: ${event}`; navigator.clipboard .writeText(data) .then(() => { window.showToast("QR code information copied to clipboard!", "success"); }) .catch(() => { window.showToast("Failed to copy to clipboard", "error"); }); } // Update results display updateResultsDisplay(count) { const resultsDisplay = document.getElementById("resultsDisplay"); if (resultsDisplay) { resultsDisplay.textContent = `${count} QR codes found`; } } updateResultsCount() { const qrItems = document.querySelectorAll( '.qr-item[style*="block"], .qr-item:not([style*="none"])' ); const counter = document.querySelector(".results-counter"); if (counter) { counter.textContent = `${qrItems.length} results`; } } } // Initialize dashboard when DOM is loaded document.addEventListener("DOMContentLoaded", function () { window.dashboardManager = new DashboardManager(); // Global functions for inline event handlers window.toggleQRCodeStatus = (qrId) => window.dashboardManager.toggleQRCodeStatus(qrId); window.deleteQRCode = (qrId, qrName) => window.dashboardManager.deleteQRCode(qrId, qrName); window.openQRModal = (qrData) => window.dashboardManager.openQRModal(qrData); window.closeQRModal = () => window.dashboardManager.closeQRModal(); window.toggleQRItem = (element) => window.dashboardManager.toggleQRItem(element); window.copyQRData = (name, location, address, event) => window.dashboardManager.copyQRData(name, location, address, event); });