467 lines
13 KiB
JavaScript
467 lines
13 KiB
JavaScript
/**
|
|
* 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}
|
|
<button type="button" class="alert-close">×</button>
|
|
`;
|
|
|
|
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();
|
|
}
|