/** * Asset Management System - JavaScript */ document.addEventListener('DOMContentLoaded', function() { // Initialize all components initAlerts(); initDropdowns(); initForms(); initTables(); initModals(); initFileUploads(); }); /** * Alert handling */ function initAlerts() { // Auto-hide alerts after 5 seconds const alerts = document.querySelectorAll('.alert'); alerts.forEach(alert => { setTimeout(() => { if (alert.parentElement) { alert.style.opacity = '0'; setTimeout(() => { alert.remove(); }, 300); } }, 5000); }); // Close button functionality document.addEventListener('click', function(e) { if (e.target.classList.contains('alert-close')) { const alert = e.target.closest('.alert'); if (alert) { alert.style.opacity = '0'; setTimeout(() => { alert.remove(); }, 300); } } }); } /** * Dropdown handling */ function initDropdowns() { // Close dropdowns when clicking outside document.addEventListener('click', function(e) { if (!e.target.closest('.nav-dropdown, .user-dropdown')) { const dropdowns = document.querySelectorAll('.nav-dropdown-menu, .user-dropdown-menu'); dropdowns.forEach(dropdown => { dropdown.style.opacity = '0'; dropdown.style.visibility = 'hidden'; dropdown.style.transform = 'translateY(-10px)'; }); } }); } /** * Form handling */ function initForms() { // Form validation const forms = document.querySelectorAll('form'); forms.forEach(form => { form.addEventListener('submit', function(e) { const requiredFields = form.querySelectorAll('[required]'); let isValid = true; requiredFields.forEach(field => { if (!field.value.trim()) { isValid = false; field.classList.add('error'); } else { field.classList.remove('error'); } }); if (!isValid) { e.preventDefault(); showAlert('Bitte füllen Sie alle erforderlichen Felder aus.', 'error'); } }); }); // Real-time validation document.addEventListener('input', function(e) { if (e.target.hasAttribute('required')) { if (e.target.value.trim()) { e.target.classList.remove('error'); } else { e.target.classList.add('error'); } } }); } /** * Table handling */ function initTables() { // Sortable tables const sortableHeaders = document.querySelectorAll('.table th[data-sort]'); sortableHeaders.forEach(header => { header.addEventListener('click', function() { const table = this.closest('.table'); const column = this.dataset.sort; const currentOrder = this.dataset.order || 'asc'; const newOrder = currentOrder === 'asc' ? 'desc' : 'asc'; // Update URL with sort parameters const url = new URL(window.location); url.searchParams.set('sort', column); url.searchParams.set('order', newOrder); window.location.href = url.toString(); }); }); // Bulk actions const bulkCheckbox = document.getElementById('bulk-select-all'); if (bulkCheckbox) { bulkCheckbox.addEventListener('change', function() { const checkboxes = document.querySelectorAll('.bulk-select'); checkboxes.forEach(checkbox => { checkbox.checked = this.checked; }); updateBulkActions(); }); } document.addEventListener('change', function(e) { if (e.target.classList.contains('bulk-select')) { updateBulkActions(); } }); } /** * Update bulk actions visibility */ function updateBulkActions() { const selectedItems = document.querySelectorAll('.bulk-select:checked'); const bulkActions = document.querySelector('.bulk-actions'); if (bulkActions) { if (selectedItems.length > 0) { bulkActions.style.display = 'block'; bulkActions.querySelector('.selected-count').textContent = selectedItems.length; } else { bulkActions.style.display = 'none'; } } } /** * Modal handling */ function initModals() { // Open modal document.addEventListener('click', function(e) { if (e.target.hasAttribute('data-modal')) { const modalId = e.target.dataset.modal; const modal = document.getElementById(modalId); if (modal) { modal.style.display = 'block'; document.body.style.overflow = 'hidden'; } } }); // Close modal document.addEventListener('click', function(e) { if (e.target.classList.contains('modal-close') || e.target.classList.contains('modal')) { const modal = e.target.closest('.modal'); if (modal) { modal.style.display = 'none'; document.body.style.overflow = 'auto'; } } }); // Close modal with Escape key document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { const openModal = document.querySelector('.modal[style*="display: block"]'); if (openModal) { openModal.style.display = 'none'; document.body.style.overflow = 'auto'; } } }); } /** * File upload handling */ function initFileUploads() { const fileInputs = document.querySelectorAll('input[type="file"]'); fileInputs.forEach(input => { input.addEventListener('change', function() { const files = Array.from(this.files); const maxSize = this.dataset.maxSize || 52428800; // 50MB default const allowedTypes = this.dataset.allowedTypes ? this.dataset.allowedTypes.split(',') : []; files.forEach(file => { // Check file size if (file.size > maxSize) { showAlert(`Datei "${file.name}" ist zu groß. Maximale Größe: ${formatBytes(maxSize)}`, 'error'); this.value = ''; return; } // Check file type if (allowedTypes.length > 0 && !allowedTypes.includes(file.type)) { showAlert(`Dateityp "${file.type}" ist nicht erlaubt.`, 'error'); this.value = ''; return; } }); }); }); } /** * Search functionality */ function initSearch() { const searchInput = document.getElementById('search-input'); if (searchInput) { let searchTimeout; searchInput.addEventListener('input', function() { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { const query = this.value.trim(); if (query.length >= 2 || query.length === 0) { performSearch(query); } }, 300); }); } } /** * Perform search */ function performSearch(query) { const url = new URL(window.location); if (query) { url.searchParams.set('search', query); } else { url.searchParams.delete('search'); } url.searchParams.delete('page'); // Reset to first page window.location.href = url.toString(); } /** * Export functionality */ function exportData(format) { const url = new URL(window.location); url.searchParams.set('export', format); window.location.href = url.toString(); } /** * Print functionality */ function printPage() { window.print(); } /** * QR Code scanner */ function initQRScanner() { const scanButton = document.getElementById('scan-qr'); if (scanButton) { scanButton.addEventListener('click', function() { // This would integrate with a QR code scanner library // For now, we'll show a prompt const code = prompt('Bitte QR-Code scannen oder Inventarnummer eingeben:'); if (code) { document.getElementById('inventory-number').value = code; document.getElementById('scan-form').submit(); } }); } } /** * Asset assignment */ function assignAsset(assetId) { const userId = prompt('Bitte Benutzer-ID eingeben:'); if (userId) { const form = document.createElement('form'); form.method = 'POST'; form.action = `/assets/${assetId}/assign`; const csrfInput = document.createElement('input'); csrfInput.type = 'hidden'; csrfInput.name = 'csrf_token'; csrfInput.value = document.querySelector('input[name="csrf_token"]').value; const userInput = document.createElement('input'); userInput.type = 'hidden'; userInput.name = 'user_id'; userInput.value = userId; form.appendChild(csrfInput); form.appendChild(userInput); document.body.appendChild(form); form.submit(); } } /** * Delete confirmation */ function confirmDelete(message = 'Sind Sie sicher, dass Sie diesen Eintrag löschen möchten?') { return confirm(message); } /** * Show alert message */ function showAlert(message, type = 'info') { const alertContainer = document.querySelector('.flash-messages') || document.body; const alert = document.createElement('div'); alert.className = `alert alert-${type}`; alert.innerHTML = ` ${message} `; alertContainer.appendChild(alert); // Auto-hide after 5 seconds setTimeout(() => { alert.style.opacity = '0'; setTimeout(() => { alert.remove(); }, 300); }, 5000); } /** * Format bytes to human readable */ function formatBytes(bytes) { const units = ['B', 'KB', 'MB', 'GB', 'TB']; let size = bytes; let unitIndex = 0; while (size >= 1024 && unitIndex < units.length - 1) { size /= 1024; unitIndex++; } return `${size.toFixed(2)} ${units[unitIndex]}`; } /** * Debounce function */ function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } /** * Throttle function */ function throttle(func, limit) { let inThrottle; return function() { const args = arguments; const context = this; if (!inThrottle) { func.apply(context, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } /** * AJAX helper */ function ajax(url, options = {}) { const defaultOptions = { method: 'GET', headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' } }; const config = { ...defaultOptions, ...options }; return fetch(url, config) .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) .catch(error => { console.error('AJAX error:', error); throw error; }); } /** * Local storage helper */ const Storage = { set: function(key, value) { try { localStorage.setItem(key, JSON.stringify(value)); } catch (e) { console.error('Error saving to localStorage', e); } }, get: function(key, defaultValue = null) { try { const item = localStorage.getItem(key); return item ? JSON.parse(item) : defaultValue; } catch (e) { console.error('Error reading from localStorage', e); return defaultValue; } }, remove: function(key) { try { localStorage.removeItem(key); } catch (e) { console.error('Error removing from localStorage', e); } } }; // Initialize search if on a page with search if (document.getElementById('search-input')) { initSearch(); } // Initialize QR scanner if on inventory page if (document.getElementById('scan-qr')) { initQRScanner(); }