/**
* Main JavaScript functionality for QR Code Management System
* static/js/script.js
*/
// Global configuration
const QRManager = {
config: {
modalCloseDelay: 300,
searchDelay: 300,
animationDuration: 300,
toastDuration: 5000,
},
// Utility functions
utils: {
// Show toast notification
showToast(message, type = "info") {
const toast = document.createElement("div");
toast.className = `toast toast-${type}`;
toast.innerHTML = `
${message}
`;
document.body.appendChild(toast);
// Auto remove after delay
setTimeout(() => {
if (toast.parentElement) {
toast.remove();
}
}, QRManager.config.toastDuration);
},
getToastIcon(type) {
const icons = {
success: "fa-check-circle",
error: "fa-exclamation-circle",
warning: "fa-exclamation-triangle",
info: "fa-info-circle",
};
return icons[type] || icons.info;
},
// Debounce function
debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
},
// Format date
formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString("en-US", {
year: "numeric",
month: "short",
day: "numeric",
});
},
// Format time
formatTime(dateString) {
const date = new Date(dateString);
return date.toLocaleTimeString("en-US", {
hour: "2-digit",
minute: "2-digit",
});
},
},
};
// Navigation functionality
class NavigationManager {
constructor() {
this.initMobileMenu();
this.initDropdowns();
}
initMobileMenu() {
const mobileMenuBtn = document.getElementById("mobile-menu");
const navMenu = document.getElementById("navMenu");
if (mobileMenuBtn && navMenu) {
mobileMenuBtn.addEventListener("click", () => {
mobileMenuBtn.classList.toggle("active");
navMenu.classList.toggle("active");
});
// Close menu when clicking nav links
const navLinks = navMenu.querySelectorAll(".nav-link");
navLinks.forEach((link) => {
link.addEventListener("click", () => {
mobileMenuBtn.classList.remove("active");
navMenu.classList.remove("active");
});
});
}
}
initDropdowns() {
const dropdowns = document.querySelectorAll(".dropdown");
dropdowns.forEach((dropdown) => {
const trigger = dropdown.querySelector(".dropdown-trigger");
const menu = dropdown.querySelector(".dropdown-menu");
if (trigger && menu) {
trigger.addEventListener("click", (e) => {
e.stopPropagation();
this.toggleDropdown(dropdown);
});
}
});
// Close dropdowns when clicking outside
document.addEventListener("click", () => {
this.closeAllDropdowns();
});
}
toggleDropdown(dropdown) {
const menu = dropdown.querySelector(".dropdown-menu");
const isOpen = menu.classList.contains("show");
this.closeAllDropdowns();
if (!isOpen) {
menu.classList.add("show");
}
}
closeAllDropdowns() {
const openMenus = document.querySelectorAll(".dropdown-menu.show");
openMenus.forEach((menu) => {
menu.classList.remove("show");
});
}
}
// Modal management
class ModalManager {
constructor() {
this.initModals();
}
initModals() {
const modals = document.querySelectorAll(".modal");
modals.forEach((modal) => {
// Close button functionality
const closeBtn = modal.querySelector(".modal-close");
if (closeBtn) {
closeBtn.addEventListener("click", () => this.closeModal(modal));
}
// Click outside to close
modal.addEventListener("click", (e) => {
if (e.target === modal) {
this.closeModal(modal);
}
});
});
// Escape key to close all modals
document.addEventListener("keydown", (e) => {
if (e.key === "Escape") {
this.closeAllModals();
}
});
}
closeModal(modal) {
modal.classList.remove("show");
setTimeout(() => {
modal.style.display = "none";
}, QRManager.config.modalCloseDelay);
}
closeAllModals() {
const openModals = document.querySelectorAll('.modal[style*="flex"]');
openModals.forEach((modal) => this.closeModal(modal));
}
openModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.style.display = "flex";
setTimeout(() => {
modal.classList.add("show");
}, 10);
}
}
}
// Search functionality
class SearchManager {
constructor(searchInputId, resultsContainerId) {
this.searchInput = document.getElementById(searchInputId);
this.resultsContainer = document.getElementById(resultsContainerId);
this.originalItems = [];
if (this.searchInput && this.resultsContainer) {
this.init();
}
}
init() {
// Store original items
this.originalItems = Array.from(this.resultsContainer.children);
// Add search event listener with debouncing
this.searchInput.addEventListener(
"input",
QRManager.utils.debounce(
() => this.performSearch(),
QRManager.config.searchDelay
)
);
}
performSearch() {
const searchTerm = this.searchInput.value.toLowerCase().trim();
this.originalItems.forEach((item) => {
const searchableText = this.getSearchableText(item);
const matches = searchableText.includes(searchTerm);
if (matches || searchTerm === "") {
this.showItem(item);
} else {
this.hideItem(item);
}
});
this.updateResultsCount();
}
getSearchableText(item) {
// Get text content from data attributes or text content
const name = item.dataset.name || "";
const location = item.dataset.location || "";
const textContent = item.textContent || "";
return (name + " " + location + " " + textContent).toLowerCase();
}
showItem(item) {
item.style.display = "block";
item.classList.remove("fade-out");
item.classList.add("fade-in");
}
hideItem(item) {
item.classList.remove("fade-in");
item.classList.add("fade-out");
setTimeout(() => {
if (item.classList.contains("fade-out")) {
item.style.display = "none";
}
}, QRManager.config.animationDuration);
}
updateResultsCount() {
const visibleItems = this.originalItems.filter(
(item) => item.style.display !== "none"
);
const counter = document.querySelector(".results-counter");
if (counter) {
counter.textContent = `${visibleItems.length} results`;
}
}
}
// Download functionality
class DownloadManager {
static downloadQR(base64Image, filename) {
try {
const link = document.createElement("a");
link.href = "data:image/png;base64," + base64Image;
link.download =
filename.replace(/[^a-z0-9]/gi, "_").toLowerCase() + "_qr_code.png";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
QRManager.utils.showToast("QR code downloaded successfully!", "success");
} catch (error) {
console.error("Download error:", error);
QRManager.utils.showToast("Failed to download QR code", "error");
}
}
static downloadModalQR() {
if (window.currentModalQR) {
const base64Data = window.currentModalQR.image.split("base64,")[1];
this.downloadQR(base64Data, window.currentModalQR.name);
}
}
}
// Theme management
class ThemeManager {
constructor() {
this.initThemeToggle();
this.loadSavedTheme();
}
initThemeToggle() {
const themeToggle = document.getElementById("themeToggle");
if (themeToggle) {
themeToggle.addEventListener("click", () => {
this.toggleTheme();
});
}
}
toggleTheme() {
const currentTheme = document.documentElement.getAttribute("data-theme");
const newTheme = currentTheme === "dark" ? "light" : "dark";
document.documentElement.setAttribute("data-theme", newTheme);
localStorage.setItem("theme", newTheme);
this.updateThemeIcon(newTheme);
}
loadSavedTheme() {
const savedTheme = localStorage.getItem("theme") || "light";
document.documentElement.setAttribute("data-theme", savedTheme);
this.updateThemeIcon(savedTheme);
}
updateThemeIcon(theme) {
const themeIcon = document.querySelector("#themeToggle i");
if (themeIcon) {
themeIcon.className = theme === "dark" ? "fas fa-sun" : "fas fa-moon";
}
}
}
// Form validation
class FormValidator {
constructor(formId) {
this.form = document.getElementById(formId);
if (this.form) {
this.init();
}
}
init() {
this.form.addEventListener("submit", (e) => {
if (!this.validateForm()) {
e.preventDefault();
}
});
// Real-time validation
const inputs = this.form.querySelectorAll("input, select, textarea");
inputs.forEach((input) => {
input.addEventListener("blur", () => this.validateField(input));
input.addEventListener("input", () => this.clearFieldError(input));
});
}
validateForm() {
const inputs = this.form.querySelectorAll(
"input[required], select[required], textarea[required]"
);
let isValid = true;
inputs.forEach((input) => {
if (!this.validateField(input)) {
isValid = false;
}
});
return isValid;
}
validateField(field) {
const value = field.value.trim();
const isRequired = field.hasAttribute("required");
const fieldType = field.type;
// Clear previous errors
this.clearFieldError(field);
// Required field validation
if (isRequired && !value) {
this.showFieldError(field, "This field is required");
return false;
}
// Email validation
if (fieldType === "email" && value) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(value)) {
this.showFieldError(field, "Please enter a valid email address");
return false;
}
}
// Password validation
if (fieldType === "password" && value) {
if (value.length < 6) {
this.showFieldError(
field,
"Password must be at least 6 characters long"
);
return false;
}
}
return true;
}
showFieldError(field, message) {
field.classList.add("error");
// Remove existing error message
const existingError = field.parentNode.querySelector(".field-error");
if (existingError) {
existingError.remove();
}
// Add new error message
const errorElement = document.createElement("div");
errorElement.className = "field-error";
errorElement.textContent = message;
field.parentNode.appendChild(errorElement);
}
clearFieldError(field) {
field.classList.remove("error");
const errorElement = field.parentNode.querySelector(".field-error");
if (errorElement) {
errorElement.remove();
}
}
}
// Initialize when DOM is loaded
document.addEventListener("DOMContentLoaded", function () {
// Initialize managers
window.navigationManager = new NavigationManager();
window.modalManager = new ModalManager();
window.themeManager = new ThemeManager();
// Initialize search if search input exists
const searchInput =
document.getElementById("searchInput") ||
document.getElementById("qrSearch") ||
document.getElementById("searchUsers");
if (searchInput) {
const containerId = searchInput.dataset.container || "searchResults";
window.searchManager = new SearchManager(searchInput.id, containerId);
}
// Initialize form validation for forms with validation class
const forms = document.querySelectorAll(".validate-form");
forms.forEach((form) => {
new FormValidator(form.id);
});
// Global function assignments for inline event handlers
window.downloadQR = DownloadManager.downloadQR;
window.downloadModalQR = DownloadManager.downloadModalQR;
window.showToast = QRManager.utils.showToast;
});
// Global utility functions
window.QRManager = QRManager;