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.
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* 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');
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user