Files
gh-christianlouis-leagueledger/app/static/js/main.js
T
Christian Krakau-Louis d595b468e9 Add Impressum and QR code management templates
- Created a new Impressum page with legal information and contact details.
- Developed admin link page for linking QR codes to events with form functionality.
- Implemented QR code dashboard for managing QR code sets, including creation and quick actions.
- Added detailed view for QR code sets, allowing addition of QR codes and management actions.
- Introduced static file serving for favicon and related images.
- Established views for static content pages (about, contact, privacy, terms).
- Implemented translation management scripts for compiling and updating translations.
2025-04-14 03:19:41 +02:00

191 lines
4.9 KiB
JavaScript

/**
* LeagueLedger Main JavaScript
* Version: 1.0.0
* Date: April 13, 2025
*/
// Initialize all components when document is ready
document.addEventListener('DOMContentLoaded', () => {
initializeQrScanner();
setupModalHandlers();
initializeTooltips();
initializeDropdowns();
});
/**
* QR Code Scanner initialization
*/
function initializeQrScanner() {
const scannerContainer = document.getElementById('qr-reader');
if (!scannerContainer) return; // Exit if scanner container doesn't exist
// Check if camera access is available
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
// QR Scanner library configuration
const html5QrCode = new Html5Qrcode("qr-reader");
const config = { fps: 10, qrbox: 250 };
// Remove overlay once scanner is ready
const overlay = document.getElementById('scanner-overlay');
if (overlay) {
overlay.style.display = 'none';
}
// Start scanner
html5QrCode.start(
{ facingMode: "environment" }, // Use rear camera
config,
onScanSuccess,
onScanFailure
);
// Save scanner instance for later use
window.qrScanner = html5QrCode;
} else {
// Camera unavailable, show manual entry form
const manualEntry = document.getElementById('manual-entry-container');
if (manualEntry) {
manualEntry.style.display = 'block';
}
const scannerElement = document.getElementById('scanner-container');
if (scannerElement) {
scannerElement.style.display = 'none';
}
console.warn("Camera access not available");
}
}
/**
* Handle successful QR code scan
*/
function onScanSuccess(decodedText) {
// Stop scanner once code is detected
if (window.qrScanner) {
window.qrScanner.stop();
}
// Extract code from URL if present
let code = decodedText;
if (decodedText.includes('/redeem/')) {
code = decodedText.split('/redeem/').pop();
}
// Redirect to redemption page
window.location.href = '/redeem/' + code;
}
/**
* Handle QR scan errors
*/
function onScanFailure(error) {
// We don't need to show errors for normal operation
console.debug("QR scan error: " + error);
}
/**
* Initialize modal handlers
*/
function setupModalHandlers() {
// Find all elements meant to open modals
const modalOpeners = document.querySelectorAll('[data-modal-target]');
const modalClosers = document.querySelectorAll('[data-modal-close]');
modalOpeners.forEach(opener => {
opener.addEventListener('click', (e) => {
e.preventDefault();
const modalId = opener.getAttribute('data-modal-target');
const modal = document.getElementById(modalId);
if (modal) {
modal.classList.remove('hidden');
}
});
});
modalClosers.forEach(closer => {
closer.addEventListener('click', (e) => {
e.preventDefault();
const modal = closer.closest('.modal');
if (modal) {
modal.classList.add('hidden');
}
});
});
// Close modal when clicking outside
document.addEventListener('click', (e) => {
const modals = document.querySelectorAll('.modal:not(.hidden)');
modals.forEach(modal => {
if (e.target === modal) {
modal.classList.add('hidden');
}
});
});
}
/**
* Initialize tooltips
*/
function initializeTooltips() {
const tooltips = document.querySelectorAll('[data-tooltip]');
tooltips.forEach(tooltip => {
tooltip.addEventListener('mouseenter', (e) => {
const text = tooltip.getAttribute('data-tooltip');
// Create tooltip element
const tooltipEl = document.createElement('div');
tooltipEl.classList.add('tooltip');
tooltipEl.textContent = text;
// Position the tooltip
const rect = tooltip.getBoundingClientRect();
tooltipEl.style.top = (rect.top - 30) + 'px';
tooltipEl.style.left = (rect.left + rect.width/2) + 'px';
// Add to DOM
document.body.appendChild(tooltipEl);
// Save reference to remove it later
tooltip._tooltipElement = tooltipEl;
});
tooltip.addEventListener('mouseleave', () => {
if (tooltip._tooltipElement) {
tooltip._tooltipElement.remove();
tooltip._tooltipElement = null;
}
});
});
}
/**
* Initialize dropdown menus
*/
function initializeDropdowns() {
const dropdowns = document.querySelectorAll('.dropdown-toggle');
dropdowns.forEach(dropdown => {
dropdown.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
const menu = dropdown.nextElementSibling;
if (menu && menu.classList.contains('dropdown-menu')) {
menu.classList.toggle('hidden');
}
});
});
// Close dropdowns when clicking elsewhere
document.addEventListener('click', () => {
const openDropdowns = document.querySelectorAll('.dropdown-menu:not(.hidden)');
openDropdowns.forEach(menu => {
menu.classList.add('hidden');
});
});
}