Initial commit of the Asset Management System, including project structure, Docker configuration, database migrations, and core application files. Added user authentication, asset management features, and basic UI components.
This commit is contained in:
534
public/assets/css/style.css
Normal file
534
public/assets/css/style.css
Normal file
@@ -0,0 +1,534 @@
|
||||
/* Reset and Base Styles */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.header {
|
||||
background: #2c3e50;
|
||||
color: white;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.logo h1 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Navigation */
|
||||
.nav-list {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.nav-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
background: white;
|
||||
color: #333;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||
border-radius: 4px;
|
||||
min-width: 200px;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transform: translateY(-10px);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.nav-dropdown:hover .nav-dropdown-menu {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.nav-dropdown-menu li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.nav-dropdown-menu a {
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
padding: 0.75rem 1rem;
|
||||
display: block;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.nav-dropdown-menu a:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
/* User Menu */
|
||||
.user-menu {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.user-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.user-link {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 4px;
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.user-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
background: white;
|
||||
color: #333;
|
||||
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||
border-radius: 4px;
|
||||
min-width: 150px;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transform: translateY(-10px);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.user-dropdown:hover .user-dropdown-menu {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.user-dropdown-menu li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.user-dropdown-menu a {
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
padding: 0.75rem 1rem;
|
||||
display: block;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.user-dropdown-menu a:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
/* Main Content */
|
||||
.main {
|
||||
min-height: calc(100vh - 140px);
|
||||
padding: 2rem 0;
|
||||
}
|
||||
|
||||
/* Flash Messages */
|
||||
.flash-messages {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background-color: #fff3cd;
|
||||
color: #856404;
|
||||
border: 1px solid #ffeaa7;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
background-color: #d1ecf1;
|
||||
color: #0c5460;
|
||||
border: 1px solid #bee5eb;
|
||||
}
|
||||
|
||||
.alert-close {
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 1rem;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Login Styles */
|
||||
.login-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
}
|
||||
|
||||
.login-box {
|
||||
background: white;
|
||||
padding: 2rem;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 10px 25px rgba(0,0,0,0.1);
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.login-header {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.login-header h2 {
|
||||
color: #2c3e50;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.login-header p {
|
||||
color: #7f8c8d;
|
||||
}
|
||||
|
||||
/* Forms */
|
||||
.form-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: #3498db;
|
||||
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: #95a5a6;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: #7f8c8d;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background-color: #27ae60;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-success:hover {
|
||||
background-color: #229954;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background-color: #e74c3c;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background-color: #c0392b;
|
||||
}
|
||||
|
||||
.btn-block {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
.table-container {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table td {
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.table th {
|
||||
background-color: #f8f9fa;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.table tbody tr:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
/* Cards */
|
||||
.card {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-bottom: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Dashboard Stats */
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: white;
|
||||
padding: 1.5rem;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: #3498db;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: #7f8c8d;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.pagination a,
|
||||
.pagination span {
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.pagination a:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.pagination .current {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
border-color: #3498db;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
background: #2c3e50;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 1rem 0;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.header-content {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.nav-list {
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.nav-dropdown-menu,
|
||||
.user-dropdown-menu {
|
||||
position: static;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-dropdown-menu a,
|
||||
.user-dropdown-menu a {
|
||||
color: white;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.table {
|
||||
min-width: 600px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Utility Classes */
|
||||
.text-center { text-align: center; }
|
||||
.text-right { text-align: right; }
|
||||
.text-left { text-align: left; }
|
||||
|
||||
.mb-1 { margin-bottom: 0.5rem; }
|
||||
.mb-2 { margin-bottom: 1rem; }
|
||||
.mb-3 { margin-bottom: 1.5rem; }
|
||||
.mb-4 { margin-bottom: 2rem; }
|
||||
|
||||
.mt-1 { margin-top: 0.5rem; }
|
||||
.mt-2 { margin-top: 1rem; }
|
||||
.mt-3 { margin-top: 1.5rem; }
|
||||
.mt-4 { margin-top: 2rem; }
|
||||
|
||||
.d-none { display: none; }
|
||||
.d-block { display: block; }
|
||||
.d-flex { display: flex; }
|
||||
.d-grid { display: grid; }
|
||||
|
||||
.justify-between { justify-content: space-between; }
|
||||
.justify-center { justify-content: center; }
|
||||
.align-center { align-items: center; }
|
||||
|
||||
.w-100 { width: 100%; }
|
||||
.h-100 { height: 100%; }
|
||||
|
||||
/* Print Styles */
|
||||
@media print {
|
||||
.header,
|
||||
.footer,
|
||||
.nav,
|
||||
.user-menu,
|
||||
.btn,
|
||||
.pagination {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.main {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: none;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
}
|
||||
466
public/assets/js/app.js
Normal file
466
public/assets/js/app.js
Normal file
@@ -0,0 +1,466 @@
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
26
public/index.php
Normal file
26
public/index.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* Front Controller - Asset Management System
|
||||
*/
|
||||
|
||||
// Start session
|
||||
session_start();
|
||||
|
||||
// Load Composer autoloader
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
// Load environment variables
|
||||
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/..');
|
||||
$dotenv->load();
|
||||
|
||||
// Load configuration
|
||||
require_once __DIR__ . '/../config/config.php';
|
||||
|
||||
// Load routes
|
||||
require_once __DIR__ . '/../config/routes.php';
|
||||
|
||||
// Initialize application
|
||||
$app = new App\Core\Application();
|
||||
|
||||
// Handle the request
|
||||
$app->handle();
|
||||
Reference in New Issue
Block a user