netgescon-day0/docs/90-UI-interfaccia-unica/02-SIDEBAR.md

629 lines
17 KiB
Markdown
Executable File

# 📋 SIDEBAR - Documentazione Completa NetGescon
> **Componente Sidebar dell'interfaccia universale**
> **📅 Creato:** 21/07/2025
> **🔄 Ultimo aggiornamento:** 21/07/2025
> **🎯 Priorità:** 🔥 CRITICA
---
## 📋 **OVERVIEW SIDEBAR**
La Sidebar NetGescon è il componente **NAVIGATION PRINCIPALE** che contiene:
- 🏠 **Dashboard Links**
- 🏢 **Stabili Management**
- 🏘️ **Condomini Management**
- 💰 **Contabilità & Fiscale**
- 👥 **User & Admin Tools**
- ⚙️ **Sistema Permessi Integrato**
### 📐 **Specifiche Layout**
- **Larghezza:** `280px` desktop, `100%` mobile
- **Posizione:** `fixed left` sempre visibile desktop
- **Altezza:** `calc(100vh - 70px)` (header escluso)
- **Scroll:** Interno quando contenuto > viewport
---
## 🎨 **DESIGN SPECIFICATIONS**
### 🌈 **Color Palette**
```css
/* Sidebar Background */
.netgescon-sidebar {
background: var(--netgescon-gray-900);
border-right: 1px solid var(--netgescon-gray-700);
}
/* Menu Items */
.sidebar-link {
color: var(--netgescon-gray-300);
background: transparent;
}
.sidebar-link:hover {
color: var(--netgescon-gray-100);
background: var(--netgescon-gray-800);
}
.sidebar-link.active {
color: var(--netgescon-primary-light);
background: rgba(37, 99, 235, 0.1);
border-right: 3px solid var(--netgescon-primary);
}
```
### 📏 **Dimensions**
```css
.netgescon-sidebar {
width: 280px;
height: calc(100vh - 70px);
top: 70px;
left: 0;
position: fixed;
z-index: 999;
overflow-y: auto;
overflow-x: hidden;
}
/* Mobile */
@media (max-width: 767px) {
.netgescon-sidebar {
width: 100%;
transform: translateX(-100%);
transition: transform 0.3s ease;
}
.netgescon-sidebar.mobile-open {
transform: translateX(0);
}
}
```
---
## 🧩 **STRUTTURA MODULARE**
### 📁 **Directory Structure**
```
resources/views/components/menu/
├── sidebar.blade.php # File principale
└── sections/ # Componenti modulari
├── header.blade.php # Header sidebar con user info
├── dashboard.blade.php # Dashboard links
├── stabili.blade.php # Gestione stabili (submenu)
├── condomini.blade.php # Gestione condomini (submenu)
├── contabilita.blade.php # Contabilità (submenu)
├── fiscale.blade.php # Fiscale (submenu)
├── menu-semplici.blade.php # Menu senza submenu
├── footer.blade.php # Footer sidebar
├── menu-helpers.blade.php # Helper funzioni
└── permissions.blade.php # Sistema permessi
```
### 🎯 **Main Sidebar Component**
```blade
<!-- resources/views/components/menu/sidebar.blade.php -->
<nav class="netgescon-sidebar">
<!-- Sidebar Header -->
@include('components.menu.sections.header')
<!-- Navigation Menu -->
<div class="sidebar-nav">
<!-- Dashboard -->
@include('components.menu.sections.dashboard')
<!-- Stabili (con permessi) -->
@if(canUserAccessMenu('stabili'))
@include('components.menu.sections.stabili')
@endif
<!-- Condomini (con permessi) -->
@if(canUserAccessMenu('condomini'))
@include('components.menu.sections.condomini')
@endif
<!-- Sezione Economica -->
@if(canUserAccessAnyMenu(['contabilita', 'fiscale']))
<div class="menu-divider"></div>
<h6 class="menu-section-title">Gestione Economica</h6>
@if(canUserAccessMenu('contabilita'))
@include('components.menu.sections.contabilita')
@endif
@if(canUserAccessMenu('fiscale'))
@include('components.menu.sections.fiscale')
@endif
@endif
<!-- Menu Semplici -->
@include('components.menu.sections.menu-semplici')
</div>
<!-- Sidebar Footer -->
@include('components.menu.sections.footer')
</nav>
<!-- Overlay mobile -->
<div class="mobile-overlay hidden md:hidden" onclick="toggleMobileMenu()"></div>
```
---
## 👥 **SISTEMA PERMESSI**
### 🔐 **Ruoli Definiti**
```php
// Definiti in config/netgescon.php
'roles' => [
'super_admin' => 'Super Amministratore',
'admin' => 'Amministratore',
'amministratore' => 'Amministratore Condominio',
'collaboratore' => 'Collaboratore',
'ragioniere' => 'Ragioniere/Commercialista',
'condomino' => 'Condomino',
'guest' => 'Ospite'
];
```
### 🛡️ **Helper Functions**
```blade
<!-- resources/views/components/menu/sections/menu-helpers.blade.php -->
@php
/**
* Verifica se l'utente può accedere a una sezione menu
*/
function canUserAccessMenu($menuSection, $userRole = null) {
$userRole = $userRole ?? (auth()->user()->role ?? 'guest');
$permissions = [
'super_admin' => ['*'], // Accesso totale
'admin' => [
'dashboard', 'stabili', 'condomini', 'unita',
'contabilita', 'fiscale', 'comunicazioni',
'utenti', 'reports', 'impostazioni'
],
'amministratore' => [
'dashboard', 'stabili', 'condomini', 'unita',
'contabilita', 'comunicazioni', 'reports'
],
'collaboratore' => [
'dashboard', 'stabili', 'condomini', 'unita', 'comunicazioni'
],
'ragioniere' => [
'dashboard', 'contabilita', 'fiscale', 'reports'
],
'condomino' => [
'dashboard', 'comunicazioni', 'tickets'
],
'guest' => []
];
return in_array('*', $permissions[$userRole] ?? []) ||
in_array($menuSection, $permissions[$userRole] ?? []);
}
/**
* Verifica accesso a qualsiasi menu di una lista
*/
function canUserAccessAnyMenu($menuSections, $userRole = null) {
foreach ($menuSections as $section) {
if (canUserAccessMenu($section, $userRole)) {
return true;
}
}
return false;
}
@endphp
```
---
## 🏠 **MENU SECTIONS DETAILED**
### 1. **📊 DASHBOARD**
```blade
<!-- components/menu/sections/dashboard.blade.php -->
<div class="menu-section">
<a href="{{ route('dashboard') }}"
class="sidebar-link {{ request()->routeIs('dashboard') ? 'active' : '' }}">
<i class="fas fa-tachometer-alt w-5"></i>
<span>Dashboard</span>
</a>
</div>
```
### 2. **🏢 STABILI (con Submenu)**
```blade
<!-- components/menu/sections/stabili.blade.php -->
<div class="menu-section">
<div class="menu-item-with-submenu">
<button class="sidebar-toggle" data-target="submenu-stabili">
<i class="fas fa-building w-5"></i>
<span>Gestione Stabili</span>
<i class="fas fa-chevron-down ml-auto transition-transform"></i>
</button>
<div id="submenu-stabili" class="submenu {{ request()->is('admin/stabili*') ? 'open' : '' }}">
<a href="{{ route('admin.stabili.index') }}"
class="submenu-link {{ request()->routeIs('admin.stabili.index') ? 'active' : '' }}">
<i class="fas fa-list w-4"></i>
<span>Elenco Stabili</span>
</a>
<a href="{{ route('admin.stabili.create') }}"
class="submenu-link {{ request()->routeIs('admin.stabili.create') ? 'active' : '' }}">
<i class="fas fa-plus w-4"></i>
<span>Nuovo Stabile</span>
</a>
<a href="{{ route('admin.stabili.reports') }}"
class="submenu-link {{ request()->routeIs('admin.stabili.reports') ? 'active' : '' }}">
<i class="fas fa-chart-bar w-4"></i>
<span>Report Stabili</span>
</a>
</div>
</div>
</div>
```
### 3. **🏘️ CONDOMINI (con Submenu)**
```blade
<!-- components/menu/sections/condomini.blade.php -->
<div class="menu-section">
<div class="menu-item-with-submenu">
<button class="sidebar-toggle" data-target="submenu-condomini">
<i class="fas fa-home w-5"></i>
<span>Gestione Condomini</span>
<i class="fas fa-chevron-down ml-auto transition-transform"></i>
</button>
<div id="submenu-condomini" class="submenu {{ request()->is('admin/condomini*') ? 'open' : '' }}">
<a href="{{ route('admin.condomini.index') }}"
class="submenu-link {{ request()->routeIs('admin.condomini.index') ? 'active' : '' }}">
<i class="fas fa-list w-4"></i>
<span>Elenco Condomini</span>
</a>
<a href="{{ route('admin.condomini.create') }}"
class="submenu-link {{ request()->routeIs('admin.condomini.create') ? 'active' : '' }}">
<i class="fas fa-plus w-4"></i>
<span>Nuovo Condominio</span>
</a>
<a href="{{ route('admin.unita.index') }}"
class="submenu-link {{ request()->routeIs('admin.unita*') ? 'active' : '' }}">
<i class="fas fa-door-open w-4"></i>
<span>Unità Immobiliari</span>
</a>
</div>
</div>
</div>
```
### 4. **💰 CONTABILITÀ (con Submenu)**
```blade
<!-- components/menu/sections/contabilita.blade.php -->
<div class="menu-section">
<div class="menu-item-with-submenu">
<button class="sidebar-toggle" data-target="submenu-contabilita">
<i class="fas fa-calculator w-5"></i>
<span>Contabilità</span>
<i class="fas fa-chevron-down ml-auto transition-transform"></i>
</button>
<div id="submenu-contabilita" class="submenu {{ request()->is('admin/contabilita*') ? 'open' : '' }}">
<a href="{{ route('admin.contabilita.budget') }}"
class="submenu-link">
<i class="fas fa-chart-pie w-4"></i>
<span>Budget Annuale</span>
</a>
<a href="{{ route('admin.contabilita.spese') }}"
class="submenu-link">
<i class="fas fa-receipt w-4"></i>
<span>Gestione Spese</span>
</a>
<a href="{{ route('admin.contabilita.ripartizioni') }}"
class="submenu-link">
<i class="fas fa-percentage w-4"></i>
<span>Ripartizioni</span>
</a>
</div>
</div>
</div>
```
---
## ⚡ **JAVASCRIPT FUNCTIONALITY**
### 📁 **File:** `/public/js/components/sidebar.js`
```javascript
/**
* Sidebar NetGescon - JavaScript Functions
*/
// Inizializza sidebar
function initializeSidebar() {
initializeSubmenuToggles();
initializeActiveStates();
initializeMobileOverlay();
initializeScrollBehavior();
}
// Gestione submenu toggle
function initializeSubmenuToggles() {
const toggles = document.querySelectorAll('.sidebar-toggle');
toggles.forEach(toggle => {
toggle.addEventListener('click', function() {
const targetId = this.getAttribute('data-target');
const submenu = document.getElementById(targetId);
const chevron = this.querySelector('.fa-chevron-down');
// Toggle submenu
submenu.classList.toggle('open');
// Rotate chevron
chevron.classList.toggle('rotate-180');
// Store state in localStorage
localStorage.setItem(`submenu-${targetId}`, submenu.classList.contains('open'));
});
});
}
// Ripristina stato submenu da localStorage
function restoreSubmenuStates() {
const submenus = document.querySelectorAll('.submenu');
submenus.forEach(submenu => {
const stored = localStorage.getItem(`submenu-${submenu.id}`);
if (stored === 'true') {
submenu.classList.add('open');
const toggle = document.querySelector(`[data-target="${submenu.id}"]`);
const chevron = toggle.querySelector('.fa-chevron-down');
chevron.classList.add('rotate-180');
}
});
}
// Active states management
function initializeActiveStates() {
const currentUrl = window.location.pathname;
const links = document.querySelectorAll('.sidebar-link, .submenu-link');
links.forEach(link => {
const href = link.getAttribute('href');
if (href && currentUrl.startsWith(href)) {
link.classList.add('active');
// Se è un submenu link, apri anche il parent
if (link.classList.contains('submenu-link')) {
const submenu = link.closest('.submenu');
if (submenu) {
submenu.classList.add('open');
const toggle = document.querySelector(`[data-target="${submenu.id}"]`);
const chevron = toggle.querySelector('.fa-chevron-down');
chevron.classList.add('rotate-180');
}
}
}
});
}
// Mobile behavior
function toggleMobileMenu() {
const sidebar = document.querySelector('.netgescon-sidebar');
const overlay = document.querySelector('.mobile-overlay');
sidebar.classList.toggle('mobile-open');
if (overlay) {
overlay.classList.toggle('hidden');
} else {
createMobileOverlay();
}
}
function createMobileOverlay() {
const overlay = document.createElement('div');
overlay.className = 'mobile-overlay fixed inset-0 bg-black bg-opacity-50 z-998 md:hidden';
overlay.onclick = toggleMobileMenu;
document.body.appendChild(overlay);
}
// Smooth scroll interno sidebar
function initializeScrollBehavior() {
const sidebar = document.querySelector('.netgescon-sidebar');
// Scroll to active item
const activeItem = sidebar.querySelector('.active');
if (activeItem) {
activeItem.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}
}
// Initialize on DOM ready
document.addEventListener('DOMContentLoaded', function() {
initializeSidebar();
restoreSubmenuStates();
});
```
---
## 📱 **RESPONSIVE BEHAVIOR**
### 🖥️ **Desktop (≥768px)**
```css
.netgescon-sidebar {
position: fixed;
left: 0;
transform: none;
width: 280px;
}
.content-area {
margin-left: 280px;
}
```
### 📱 **Mobile (≤767px)**
```css
.netgescon-sidebar {
position: fixed;
left: 0;
width: 100%;
transform: translateX(-100%);
transition: transform 0.3s ease;
z-index: 1001;
}
.netgescon-sidebar.mobile-open {
transform: translateX(0);
}
.content-area {
margin-left: 0;
}
.mobile-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
}
```
---
## 🎨 **STYLING CLASSES**
### 🔧 **CSS Custom Properties**
```css
/* Sidebar Animations */
.submenu {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.submenu.open {
max-height: 500px;
}
/* Hover Effects */
.sidebar-link:hover {
transform: translateX(4px);
transition: all 0.2s ease;
}
/* Active State */
.sidebar-link.active::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3px;
background: var(--netgescon-primary);
}
/* Icon Spacing */
.sidebar-link i {
min-width: 20px;
margin-right: 12px;
}
/* Mobile Specific */
@media (max-width: 767px) {
.sidebar-link {
padding: 16px 20px;
font-size: 16px;
}
.submenu-link {
padding: 12px 40px;
}
}
```
---
## 🧪 **TESTING CHECKLIST**
### ✅ **Functional Tests**
- [ ] Submenu expand/collapse
- [ ] Active state highlighting
- [ ] Permission-based visibility
- [ ] Mobile menu toggle
- [ ] Scroll behavior
- [ ] State persistence
### ✅ **Visual Tests**
- [ ] Icon alignment
- [ ] Hover effects
- [ ] Active states styling
- [ ] Mobile responsive
- [ ] Smooth animations
### ✅ **Accessibility Tests**
- [ ] Keyboard navigation
- [ ] Screen reader compatibility
- [ ] Focus indicators
- [ ] ARIA labels
---
## 🐛 **COMMON ISSUES & FIXES**
### 🚨 **Submenu non si apre**
**Causa:** JavaScript non inizializzato o selettori errati
**Fix:** Verifica inizializzazione e selettori CSS
### 🚨 **Active state non funziona**
**Causa:** URL routing non corrispondente
**Fix:** Aggiorna logica comparazione URL
### 🚨 **Mobile overlay non scompare**
**Causa:** Event listener click non agganciato
**Fix:** Verifica function toggleMobileMenu()
### 🚨 **Permessi non funzionano**
**Causa:** Helper functions non caricate o ruoli errati
**Fix:** Verifica file permissions.blade.php e user roles
---
## 📚 **FILES CORRELATI**
### 🧩 **Templates**
- `/resources/views/components/menu/sidebar.blade.php`
- `/resources/views/components/menu/sections/` (directory)
### 🎨 **Styles**
- `/public/css/components/sidebar.css`
- `/public/css/netgescon-admin.css`
### ⚡ **JavaScript**
- `/public/js/components/sidebar.js`
- `/public/js/netgescon-admin.js`
---
## 🎯 **NEXT STEPS**
### 🔮 **Planned Improvements**
- [ ] **Sidebar Customization** - User preferences
- [ ] **Quick Search** - Search in menu items
- [ ] **Favorites** - Pin frequently used items
- [ ] **Breadcrumb Integration** - Sync with sidebar state
- [ ] **Dark Mode** - Theme switch
---
*📝 Documento mantenuto da: GitHub Copilot AI Assistant*
*🔗 Componente: Sidebar NetGescon Interface v2.1.0*