# 🎨 CSS E JAVASCRIPT STANDARD ## Stili e script per NetGescon modules --- ## 📁 **FILE CSS STANDARD** ### **File**: `/public/css/netgescon-standard.css` ```css /** * NetGescon Standard Module Styles * Version: 2.0 - Agosto 2025 */ /* ===== MENU TABS STANDARD ===== */ /* AdminLTE Nav Tabs Override */ .nav-tabs .nav-link { border: 1px solid transparent; border-radius: 0.375rem 0.375rem 0 0; color: #6c757d; font-weight: 500; padding: 0.75rem 1rem; transition: all 0.3s ease; position: relative; } .nav-tabs .nav-link:hover { border-color: #e9ecef #e9ecef #dee2e6; color: #007bff; transform: translateY(-1px); } .nav-tabs .nav-link.active { background-color: #fff; border-color: #007bff #007bff #fff; color: #007bff; font-weight: 600; box-shadow: 0 2px 4px rgba(0,123,255,0.1); } .nav-tabs .nav-link i { margin-right: 0.5rem; font-size: 0.9rem; } /* Card Tabs Container */ .card-tabs { box-shadow: 0 2px 4px rgba(0,0,0,0.1); border: none; } .card-tabs .card-header { background: linear-gradient(90deg, #f8f9fa 0%, #e9ecef 100%); border-bottom: 1px solid #dee2e6; } /* ===== NETGESCON TAB BUTTONS ===== */ /* Sub-menu Detail Tabs */ .netgescon-tab-btn { background: none; border: none; border-bottom: 3px solid transparent; color: #6b7280; cursor: pointer; font-weight: 500; font-size: 0.9rem; padding: 0.75rem 1rem; transition: all 0.3s ease; white-space: nowrap; position: relative; } .netgescon-tab-btn:hover { border-bottom-color: #3b82f6; color: #3b82f6; background-color: rgba(59, 130, 246, 0.05); } .netgescon-tab-btn.active { border-bottom-color: #3b82f6; color: #3b82f6; font-weight: 600; background-color: rgba(59, 130, 246, 0.1); } .netgescon-tab-btn:focus { outline: none; box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2); } /* Tab Content Areas */ .netgescon-tab-content { display: none; animation: fadeIn 0.3s ease; } .netgescon-tab-content.active { display: block; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } /* ===== SMALL BOX CARDS ===== */ /* Enhanced Small Box */ .small-box { border-radius: 0.5rem; color: #fff; overflow: hidden; position: relative; margin-bottom: 1.5rem; box-shadow: 0 2px 8px rgba(0,0,0,0.1); transition: all 0.3s ease; cursor: pointer; } .small-box:hover { transform: translateY(-3px); box-shadow: 0 6px 16px rgba(0,0,0,0.15); } .small-box .inner { padding: 1.25rem; position: relative; z-index: 2; } .small-box h3 { font-size: 2.2rem; font-weight: 700; margin: 0; white-space: nowrap; text-shadow: 0 1px 3px rgba(0,0,0,0.3); } .small-box p { font-size: 1rem; margin: 0.5rem 0 0 0; opacity: 0.9; font-weight: 500; } .small-box .icon { position: absolute; top: auto; bottom: 10px; right: 15px; font-size: 3.5rem; color: rgba(255,255,255,0.2); z-index: 1; transition: all 0.3s ease; } .small-box:hover .icon { transform: scale(1.1); color: rgba(255,255,255,0.3); } /* Color Variants */ .small-box.bg-primary { background: linear-gradient(135deg, #007bff 0%, #0056b3 100%); } .small-box.bg-success { background: linear-gradient(135deg, #28a745 0%, #1e7e34 100%); } .small-box.bg-warning { background: linear-gradient(135deg, #ffc107 0%, #e0a800 100%); color: #212529; } .small-box.bg-warning h3, .small-box.bg-warning p { color: #212529; text-shadow: none; } .small-box.bg-warning .icon { color: rgba(33, 37, 41, 0.2); } .small-box.bg-danger { background: linear-gradient(135deg, #dc3545 0%, #c82333 100%); } .small-box.bg-info { background: linear-gradient(135deg, #17a2b8 0%, #138496 100%); } .small-box.bg-secondary { background: linear-gradient(135deg, #6c757d 0%, #545b62 100%); } /* Small Box Footer */ .small-box-footer { background: rgba(0,0,0,0.15); color: rgba(255,255,255,0.9); display: block; padding: 0.5rem 0; text-align: center; text-decoration: none; font-size: 0.875rem; font-weight: 500; transition: background 0.3s ease; } .small-box-footer:hover { background: rgba(0,0,0,0.25); color: #fff; text-decoration: none; } /* Links for Clickable Cards */ .small-box-link { color: inherit; text-decoration: none; display: block; } .small-box-link:hover { color: inherit; text-decoration: none; } /* ===== RESPONSIVE DESIGN ===== */ /* Tablet */ @media (max-width: 992px) { .nav-tabs .nav-link { padding: 0.5rem 0.75rem; font-size: 0.9rem; } .small-box h3 { font-size: 1.8rem; } .small-box .icon { font-size: 2.5rem; bottom: 8px; right: 10px; } } /* Mobile */ @media (max-width: 768px) { .nav-tabs .nav-link { padding: 0.5rem; font-size: 0.85rem; } .nav-tabs .nav-link i { display: none; /* Hide icons on very small screens */ } .netgescon-tab-btn { padding: 0.5rem 0.75rem; font-size: 0.85rem; } .small-box { margin-bottom: 1rem; } .small-box h3 { font-size: 1.5rem; } .small-box p { font-size: 0.875rem; margin-bottom: 0; } .small-box .icon { font-size: 2rem; bottom: 5px; right: 8px; } .small-box .inner { padding: 1rem; } } /* Small Mobile */ @media (max-width: 576px) { .nav-tabs { flex-wrap: wrap; } .nav-tabs .nav-link { flex: 1 1 auto; text-align: center; min-width: 0; padding: 0.4rem 0.25rem; } .small-box h3 { font-size: 1.3rem; } .small-box .icon { display: none; /* Hide icons on very small screens */ } } /* ===== UTILITIES ===== */ /* Loading States */ .loading { opacity: 0.6; pointer-events: none; } .loading::after { content: ''; position: absolute; top: 50%; left: 50%; width: 20px; height: 20px; margin: -10px 0 0 -10px; border: 2px solid #f3f3f3; border-top: 2px solid #007bff; border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } /* Animations */ .fade-in { animation: fadeIn 0.5s ease; } .slide-up { animation: slideUp 0.3s ease; } @keyframes slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } /* Focus States */ .nav-tabs .nav-link:focus { outline: none; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); } /* Print Styles */ @media print { .nav-tabs, .netgescon-tab-btn, .small-box .icon { display: none !important; } .small-box { break-inside: avoid; box-shadow: none; border: 1px solid #000; } } /* Dark Mode Support */ @media (prefers-color-scheme: dark) { .nav-tabs .nav-link { color: #adb5bd; } .nav-tabs .nav-link:hover { color: #6ea8fe; } .nav-tabs .nav-link.active { color: #6ea8fe; border-color: #6ea8fe #6ea8fe #fff; } } /* High Contrast Mode */ @media (prefers-contrast: high) { .small-box { border: 2px solid; } .nav-tabs .nav-link { border-width: 2px; } } ``` --- ## ⚡ **FILE JAVASCRIPT STANDARD** ### **File**: `/public/js/netgescon-modules.js` ```javascript /** * NetGescon Standard Module JavaScript * Version: 2.0 - Agosto 2025 */ // Global NetGescon object window.NetGescon = window.NetGescon || {}; // Initialize when DOM is ready document.addEventListener('DOMContentLoaded', function() { NetGescon.init(); }); // Main initialization NetGescon.init = function() { this.initializeNetGesconTabs(); this.initializeStatCards(); this.initializeMenuHighlight(); this.initializeTooltips(); this.initializeSearchFilters(); this.initializeFormValidation(); console.log('NetGescon modules initialized'); }; // ===== TAB MANAGEMENT ===== NetGescon.initializeNetGesconTabs = function() { const tabButtons = document.querySelectorAll('.netgescon-tab-btn'); const tabContents = document.querySelectorAll('.netgescon-tab-content'); if (tabButtons.length === 0) return; tabButtons.forEach(button => { button.addEventListener('click', function(e) { e.preventDefault(); const tabId = this.getAttribute('data-tab'); // Remove active class from all buttons and contents tabButtons.forEach(btn => { btn.classList.remove('active'); btn.setAttribute('aria-selected', 'false'); }); tabContents.forEach(content => { content.classList.remove('active'); content.setAttribute('aria-hidden', 'true'); }); // Add active class to clicked button and corresponding content this.classList.add('active'); this.setAttribute('aria-selected', 'true'); const targetContent = document.getElementById(tabId); if (targetContent) { targetContent.classList.add('active'); targetContent.setAttribute('aria-hidden', 'false'); // Trigger custom event window.dispatchEvent(new CustomEvent('netgescon:tab-changed', { detail: { tabId: tabId, button: this, content: targetContent } })); } }); // Keyboard navigation button.addEventListener('keydown', function(e) { let nextButton = null; switch(e.key) { case 'ArrowRight': nextButton = this.nextElementSibling || tabButtons[0]; break; case 'ArrowLeft': nextButton = this.previousElementSibling || tabButtons[tabButtons.length - 1]; break; case 'Home': nextButton = tabButtons[0]; break; case 'End': nextButton = tabButtons[tabButtons.length - 1]; break; } if (nextButton) { e.preventDefault(); nextButton.focus(); nextButton.click(); } }); }); console.log(`Initialized ${tabButtons.length} NetGescon tabs`); }; // ===== STAT CARDS ANIMATION ===== NetGescon.initializeStatCards = function() { const cards = document.querySelectorAll('.small-box'); cards.forEach(card => { // Hover effects card.addEventListener('mouseenter', function() { this.style.transform = 'translateY(-3px)'; this.style.boxShadow = '0 6px 16px rgba(0,0,0,0.15)'; }); card.addEventListener('mouseleave', function() { this.style.transform = 'translateY(0)'; this.style.boxShadow = '0 2px 8px rgba(0,0,0,0.1)'; }); // Click animation card.addEventListener('click', function(e) { // Create ripple effect const ripple = document.createElement('div'); const rect = this.getBoundingClientRect(); const size = Math.max(rect.width, rect.height); const x = e.clientX - rect.left - size / 2; const y = e.clientY - rect.top - size / 2; ripple.style.cssText = ` position: absolute; width: ${size}px; height: ${size}px; left: ${x}px; top: ${y}px; background: rgba(255,255,255,0.3); border-radius: 50%; transform: scale(0); animation: ripple 0.6s linear; pointer-events: none; z-index: 10; `; this.style.position = 'relative'; this.appendChild(ripple); setTimeout(() => { ripple.remove(); }, 600); }); }); console.log(`Initialized ${cards.length} stat cards`); }; // ===== MENU ACTIVE HIGHLIGHT ===== NetGescon.initializeMenuHighlight = function() { const currentPath = window.location.pathname; const menuLinks = document.querySelectorAll('.nav-tabs .nav-link'); menuLinks.forEach(link => { const linkPath = new URL(link.href, window.location.origin).pathname; if (linkPath === currentPath) { link.classList.add('active'); link.setAttribute('aria-current', 'page'); } else { link.classList.remove('active'); link.removeAttribute('aria-current'); } }); console.log('Menu highlighting initialized'); }; // ===== TOOLTIPS ===== NetGescon.initializeTooltips = function() { // Initialize Bootstrap tooltips if available if (typeof bootstrap !== 'undefined' && bootstrap.Tooltip) { const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); tooltipTriggerList.map(function (tooltipTriggerEl) { return new bootstrap.Tooltip(tooltipTriggerEl); }); } // Custom simple tooltips for non-Bootstrap environments const customTooltips = document.querySelectorAll('[data-tooltip]'); customTooltips.forEach(element => { element.addEventListener('mouseenter', function() { NetGescon.showTooltip(this, this.getAttribute('data-tooltip')); }); element.addEventListener('mouseleave', function() { NetGescon.hideTooltip(); }); }); }; NetGescon.showTooltip = function(element, text) { const tooltip = document.createElement('div'); tooltip.className = 'netgescon-tooltip'; tooltip.textContent = text; tooltip.style.cssText = ` position: absolute; background: #333; color: white; padding: 0.5rem; border-radius: 0.25rem; font-size: 0.875rem; z-index: 1000; pointer-events: none; white-space: nowrap; `; document.body.appendChild(tooltip); const rect = element.getBoundingClientRect(); tooltip.style.left = rect.left + (rect.width / 2) - (tooltip.offsetWidth / 2) + 'px'; tooltip.style.top = rect.top - tooltip.offsetHeight - 8 + 'px'; this.currentTooltip = tooltip; }; NetGescon.hideTooltip = function() { if (this.currentTooltip) { this.currentTooltip.remove(); this.currentTooltip = null; } }; // ===== SEARCH AND FILTERS ===== NetGescon.initializeSearchFilters = function() { const searchInput = document.querySelector('#search-input'); const filterSelects = document.querySelectorAll('.filter-select'); if (searchInput) { // Debounced search let searchTimeout; searchInput.addEventListener('input', function() { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { NetGescon.performSearch(this.value); }, 300); }); } filterSelects.forEach(select => { select.addEventListener('change', function() { NetGescon.applyFilters(); }); }); }; NetGescon.performSearch = function(query) { // Auto-submit search form or AJAX search const searchForm = document.querySelector('#search-form'); if (searchForm && query.length >= 2) { searchForm.submit(); } }; NetGescon.applyFilters = function() { const filterForm = document.querySelector('#filter-form'); if (filterForm) { filterForm.submit(); } }; // ===== FORM VALIDATION ===== NetGescon.initializeFormValidation = function() { const forms = document.querySelectorAll('.needs-validation'); forms.forEach(form => { form.addEventListener('submit', function(e) { if (!form.checkValidity()) { e.preventDefault(); e.stopPropagation(); // Focus first invalid field const firstInvalid = form.querySelector(':invalid'); if (firstInvalid) { firstInvalid.focus(); } } form.classList.add('was-validated'); }); }); }; // ===== UTILITY FUNCTIONS ===== NetGescon.showLoading = function(element) { if (!element) return; const originalText = element.innerHTML; element.dataset.originalText = originalText; element.innerHTML = 'Caricamento...'; element.disabled = true; element.classList.add('loading'); }; NetGescon.hideLoading = function(element) { if (!element) return; const originalText = element.dataset.originalText || 'Completato'; element.innerHTML = originalText; element.disabled = false; element.classList.remove('loading'); delete element.dataset.originalText; }; NetGescon.showNotification = function(message, type = 'info', duration = 5000) { const notification = document.createElement('div'); notification.className = `alert alert-${type} alert-dismissible fade show position-fixed`; notification.style.cssText = ` top: 20px; right: 20px; z-index: 9999; max-width: 350px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); `; notification.innerHTML = `