netgescon-master/docs/02-architettura-laravel/06-interfaccia-universale/LAYOUT-UNIVERSALE.md
Pikappa2 480e7eafbd 🎯 NETGESCON - Setup iniziale repository completo
📋 Commit iniziale con:
-  Documentazione unificata in docs/
-  Codice Laravel in netgescon-laravel/
-  Script automazione in scripts/
-  Configurazione sync rsync
-  Struttura organizzata e pulita

🔄 Versione: 2025.07.19-1644
🎯 Sistema pronto per Git distribuito
2025-07-19 16:44:47 +02:00

20 KiB

NETGESCON - INTERFACCIA UNIVERSALE UNIFICATA

📋 OVERVIEW

Documentazione completa dell'implementazione dell'interfaccia universale unificata di NetGesCon, un sistema avanzato che fornisce un layout responsive e dinamico che si adatta automaticamente al ruolo dell'utente.

🎨 ARCHITETTURA INTERFACCIA ESISTENTE

🏗️ Layout Universale Bootstrap

File principale: resources/views/layouts/app-universal.blade.php

Struttura HTML

<!DOCTYPE html>
<html lang="it">
<head>
    <!-- Bootstrap 5.3.2 CDN -->
    <!-- FontAwesome 6.0.0 CDN -->
    <!-- CSS personalizzato -->
</head>
<body>
    <div class="d-flex h-100">
        <!-- Sidebar Dinamica -->
        <nav id="sidebar" class="sidebar">
            @include('components.menu.sidebar')
        </nav>
        
        <!-- Container Principale -->
        <div class="main-content flex-fill">
            <!-- Header/Launcher Bar -->
            <header class="launcher-bar">
                @include('components.menu.launcher')
            </header>
            
            <!-- Content Area -->
            <main class="content-area">
                @yield('content')
            </main>
            
            <!-- Footer (se necessario) -->
            <footer class="main-footer">
                @yield('footer')
            </footer>
        </div>
    </div>
    
    <!-- Scripts Bootstrap e custom -->
</body>
</html>

CDN e Dipendenze

<!-- Bootstrap 5.3.2 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>

<!-- FontAwesome 6.0.0 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">

<!-- CSS Personalizzato -->
<link href="{{ asset('css/netgescon-universal.css') }}" rel="stylesheet">

📱 Sidebar Dinamica

File: resources/views/components/menu/sidebar.blade.php

Caratteristiche Implementate

{{-- Sidebar che si adatta al ruolo utente --}}
@props([
    'userRole' => 'admin', // Determinato da Auth::user()->role
    'permissions' => [],   // Array permessi utente
    'activeStabile' => null // Stabile attualmente selezionato
])

<div class="sidebar bg-light border-end">
    <!-- Header Sidebar -->
    <div class="sidebar-header p-3">
        <h5 class="mb-0">NetGesCon</h5>
        <small class="text-muted">{{ $userRole }}</small>
    </div>
    
    <!-- Menu Dinamico -->
    <nav class="sidebar-nav">
        @if(Auth::user()->can('view_dashboard'))
            <a href="{{ route('dashboard') }}" class="nav-link">
                <i class="fas fa-tachometer-alt"></i> Dashboard
            </a>
        @endif
        
        @if(Auth::user()->can('manage_stabili'))
            <!-- Menu Stabili con submenu -->
            <div class="nav-group">
                <a href="#" class="nav-link dropdown-toggle">
                    <i class="fas fa-building"></i> Stabili
                </a>
                <div class="nav-submenu">
                    <a href="{{ route('admin.stabili.index') }}">Lista Stabili</a>
                    <a href="{{ route('admin.stabili.create') }}">Nuovo Stabile</a>
                </div>
            </div>
        @endif
        
        <!-- Altri menu basati su permessi -->
        @include('components.menu.sections.soggetti')
        @include('components.menu.sections.fornitori')
        @include('components.menu.sections.tickets')
    </nav>
</div>

CSS Bootstrap Customizzato

/* Sidebar responsiva */
.sidebar {
    width: 250px;
    min-height: 100vh;
    transition: transform 0.3s ease-in-out;
}

@media (max-width: 768px) {
    .sidebar {
        position: fixed;
        z-index: 1050;
        transform: translateX(-100%);
    }
    
    .sidebar.show {
        transform: translateX(0);
    }
}

/* Navigation links */
.sidebar .nav-link {
    padding: 0.75rem 1rem;
    color: #495057;
    border-radius: 0.375rem;
    margin: 0.125rem 0.5rem;
    transition: all 0.2s ease-in-out;
}

.sidebar .nav-link:hover {
    background-color: #e9ecef;
    color: #0d6efd;
}

.sidebar .nav-link.active {
    background-color: #0d6efd;
    color: white;
}

/* Submenu */
.nav-submenu {
    display: none;
    padding-left: 2rem;
}

.nav-submenu.show {
    display: block;
}

🚀 Launcher Bar

File: resources/views/components/menu/launcher.blade.php

Funzionalità

<div class="launcher-bar bg-white border-bottom px-3 py-2">
    <div class="d-flex justify-content-between align-items-center">
        <!-- Breadcrumb dinamico -->
        <nav aria-label="breadcrumb">
            <ol class="breadcrumb mb-0">
                <li class="breadcrumb-item"><a href="{{ route('dashboard') }}">Home</a></li>
                @yield('breadcrumb')
            </ol>
        </nav>
        
        <!-- Azioni rapide -->
        <div class="launcher-actions">
            <!-- Toggle sidebar mobile -->
            <button class="btn btn-outline-secondary d-md-none me-2" id="sidebarToggle">
                <i class="fas fa-bars"></i>
            </button>
            
            <!-- Notifiche -->
            @if(Auth::user()->can('view_notifications'))
                <div class="dropdown me-2">
                    <button class="btn btn-outline-primary dropdown-toggle" data-bs-toggle="dropdown">
                        <i class="fas fa-bell"></i>
                        <span class="badge bg-danger">3</span>
                    </button>
                    <ul class="dropdown-menu">
                        <li><a class="dropdown-item" href="#">Nuovo ticket urgente</a></li>
                        <li><a class="dropdown-item" href="#">Scadenza rate</a></li>
                    </ul>
                </div>
            @endif
            
            <!-- Profilo utente -->
            <div class="dropdown">
                <button class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown">
                    <i class="fas fa-user"></i> {{ Auth::user()->name }}
                </button>
                <ul class="dropdown-menu">
                    <li><a class="dropdown-item" href="{{ route('profile') }}">Profilo</a></li>
                    <li><hr class="dropdown-divider"></li>
                    <li><a class="dropdown-item" href="{{ route('logout') }}">Logout</a></li>
                </ul>
            </div>
        </div>
    </div>
</div>

🔐 SISTEMA PERMESSI INTEGRATO

👥 Gestione Ruoli e Permessi

Integrazione: Sistema permessi si integra perfettamente con l'interfaccia

Helper Blade per Controllo Permessi

{{-- In qualsiasi vista Blade --}}
@can('manage_stabili')
    <a href="{{ route('admin.stabili.create') }}" class="btn btn-primary">
        <i class="fas fa-plus"></i> Nuovo Stabile
    </a>
@endcan

@cannot('view_financial_data') 
    <div class="alert alert-warning">
        Non hai i permessi per visualizzare i dati finanziari.
    </div>
@endcannot

{{-- Controllo permessi con parametri --}}
@can('edit_stabile', $stabile)
    <a href="{{ route('admin.stabili.edit', $stabile) }}" class="btn btn-warning">
        <i class="fas fa-edit"></i> Modifica
    </a>
@endcan

Middleware per Controllo Accessi

// Route con middleware permessi
Route::group(['middleware' => ['auth', 'permission:manage_stabili']], function () {
    Route::resource('admin/stabili', StabiliController::class);
});

// Middleware personalizzato per ruoli
Route::group(['middleware' => ['auth', 'role:admin|super-admin']], function () {
    Route::get('/admin/settings', [SettingsController::class, 'index']);
});

📋 Menu Dinamici Basati su Ruoli

Configurazione Menu

File: config/menu.php

<?php

return [
    'sidebar_menu' => [
        'dashboard' => [
            'label' => 'Dashboard',
            'icon' => 'fas fa-tachometer-alt',
            'route' => 'dashboard',
            'permission' => 'view_dashboard',
            'roles' => ['admin', 'condomino', 'fornitore']
        ],
        
        'stabili' => [
            'label' => 'Stabili',
            'icon' => 'fas fa-building',
            'permission' => 'manage_stabili',
            'roles' => ['admin', 'super-admin'],
            'submenu' => [
                'index' => [
                    'label' => 'Lista Stabili',
                    'route' => 'admin.stabili.index',
                    'permission' => 'view_stabili'
                ],
                'create' => [
                    'label' => 'Nuovo Stabile',
                    'route' => 'admin.stabili.create',
                    'permission' => 'create_stabili'
                ]
            ]
        ],
        
        // Altri menu...
    ]
];

Builder Menu Dinamico

<?php

class MenuBuilder
{
    public static function buildSidebarMenu($user)
    {
        $menuConfig = config('menu.sidebar_menu');
        $menu = [];
        
        foreach ($menuConfig as $key => $item) {
            // Controllo permessi
            if (isset($item['permission']) && !$user->can($item['permission'])) {
                continue;
            }
            
            // Controllo ruoli
            if (isset($item['roles']) && !$user->hasAnyRole($item['roles'])) {
                continue;
            }
            
            // Build submenu se presente
            if (isset($item['submenu'])) {
                $item['submenu'] = self::buildSubmenu($item['submenu'], $user);
                
                // Se nessun submenu è accessibile, salta il menu principale
                if (empty($item['submenu'])) {
                    continue;
                }
            }
            
            $menu[$key] = $item;
        }
        
        return $menu;
    }
    
    private static function buildSubmenu($submenuConfig, $user)
    {
        $submenu = [];
        
        foreach ($submenuConfig as $key => $item) {
            if (isset($item['permission']) && !$user->can($item['permission'])) {
                continue;
            }
            
            $submenu[$key] = $item;
        }
        
        return $submenu;
    }
}

📱 RESPONSIVE DESIGN E MOBILE

🔧 Breakpoints Bootstrap

/* Mobile First Approach */

/* Extra small devices (phones, less than 576px) */
@media (max-width: 575.98px) {
    .sidebar {
        width: 100%;
        transform: translateX(-100%);
    }
    
    .main-content {
        margin-left: 0;
    }
    
    .launcher-bar .breadcrumb {
        display: none;
    }
}

/* Small devices (landscape phones, 576px and up) */
@media (min-width: 576px) and (max-width: 767.98px) {
    .sidebar {
        width: 200px;
    }
}

/* Medium devices (tablets, 768px and up) */
@media (min-width: 768px) {
    .sidebar {
        position: relative;
        transform: translateX(0);
    }
    
    .main-content {
        margin-left: 0;
    }
}

/* Large devices (desktops, 992px and up) */
@media (min-width: 992px) {
    .sidebar {
        width: 250px;
    }
}

📱 JavaScript per Mobile

// Toggle sidebar su mobile
document.addEventListener('DOMContentLoaded', function() {
    const sidebarToggle = document.getElementById('sidebarToggle');
    const sidebar = document.querySelector('.sidebar');
    const overlay = document.createElement('div');
    overlay.className = 'sidebar-overlay d-md-none';
    
    sidebarToggle?.addEventListener('click', function() {
        sidebar.classList.toggle('show');
        
        if (sidebar.classList.contains('show')) {
            document.body.appendChild(overlay);
            overlay.style.cssText = `
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background: rgba(0,0,0,0.5);
                z-index: 1040;
            `;
        } else {
            overlay.remove();
        }
    });
    
    // Chiudi sidebar quando si clicca sull'overlay
    overlay.addEventListener('click', function() {
        sidebar.classList.remove('show');
        overlay.remove();
    });
});

// Auto-collapse submenu su mobile
function handleSubmenuToggle() {
    const dropdownToggles = document.querySelectorAll('.dropdown-toggle');
    
    dropdownToggles.forEach(toggle => {
        toggle.addEventListener('click', function(e) {
            e.preventDefault();
            
            const submenu = this.nextElementSibling;
            const isVisible = submenu.classList.contains('show');
            
            // Chiudi tutti gli altri submenu
            document.querySelectorAll('.nav-submenu.show').forEach(menu => {
                if (menu !== submenu) {
                    menu.classList.remove('show');
                }
            });
            
            // Toggle submenu corrente
            submenu.classList.toggle('show', !isVisible);
        });
    });
}

handleSubmenuToggle();

🎨 PERSONALIZZAZIONE E TEMI

🎨 CSS Custom Properties

:root {
    /* Colori brand NetGesCon */
    --netgescon-primary: #0d6efd;
    --netgescon-secondary: #6c757d;
    --netgescon-success: #198754;
    --netgescon-danger: #dc3545;
    --netgescon-warning: #ffc107;
    --netgescon-info: #0dcaf0;
    
    /* Sidebar colors */
    --sidebar-bg: #f8f9fa;
    --sidebar-text: #495057;
    --sidebar-hover-bg: #e9ecef;
    --sidebar-active-bg: var(--netgescon-primary);
    --sidebar-active-text: white;
    
    /* Layout spacing */
    --sidebar-width: 250px;
    --launcher-height: 60px;
    --content-padding: 1.5rem;
}

/* Dark theme (per il futuro) */
[data-theme="dark"] {
    --sidebar-bg: #212529;
    --sidebar-text: #adb5bd;
    --sidebar-hover-bg: #343a40;
}

🔧 Sistema Configurazione UI

<?php
// config/ui.php

return [
    'theme' => env('NETGESCON_THEME', 'light'),
    
    'sidebar' => [
        'width' => env('SIDEBAR_WIDTH', '250px'),
        'collapsible' => env('SIDEBAR_COLLAPSIBLE', true),
        'auto_hide_mobile' => env('SIDEBAR_AUTO_HIDE_MOBILE', true),
    ],
    
    'launcher' => [
        'show_breadcrumb' => env('LAUNCHER_SHOW_BREADCRUMB', true),
        'show_notifications' => env('LAUNCHER_SHOW_NOTIFICATIONS', true),
        'show_quick_actions' => env('LAUNCHER_SHOW_QUICK_ACTIONS', true),
    ],
    
    'branding' => [
        'logo_url' => env('NETGESCON_LOGO_URL', '/images/logo-netgescon.png'),
        'app_name' => env('NETGESCON_APP_NAME', 'NetGesCon'),
        'tagline' => env('NETGESCON_TAGLINE', 'Gestione Condominiale Avanzata'),
    ]
];

🧪 TESTING E QUALITÀ

🔬 Test Case Interfaccia

<?php

use Tests\TestCase;
use App\Models\User;

class InterfacciaUniversaleTest extends TestCase
{
    /** @test */
    public function sidebar_si_adatta_al_ruolo_utente()
    {
        $admin = User::factory()->create(['role' => 'admin']);
        $condomino = User::factory()->create(['role' => 'condomino']);
        
        // Test admin vede menu stabili
        $this->actingAs($admin)
             ->get('/dashboard')
             ->assertSee('Stabili')
             ->assertSee('Nuovo Stabile');
        
        // Test condomino non vede menu stabili
        $this->actingAs($condomino)
             ->get('/dashboard')
             ->assertDontSee('Stabili')
             ->assertDontSee('Nuovo Stabile');
    }
    
    /** @test */
    public function layout_universale_e_responsive()
    {
        $user = User::factory()->create();
        
        $response = $this->actingAs($user)->get('/dashboard');
        
        $response->assertStatus(200)
                ->assertSee('sidebar')
                ->assertSee('launcher-bar')
                ->assertSee('main-content');
    }
    
    /** @test */ 
    public function permessi_controllano_accesso_menu()
    {
        $user = User::factory()->create();
        $user->revokePermissionTo('manage_stabili');
        
        $this->actingAs($user)
             ->get('/admin/stabili')
             ->assertStatus(403);
    }
}

📊 Performance Monitoring

// Performance monitoring per UI
class UIPerformanceMonitor {
    static init() {
        // Misura tempo caricamento sidebar
        performance.mark('sidebar-start');
        
        window.addEventListener('load', () => {
            performance.mark('sidebar-end');
            performance.measure('sidebar-load', 'sidebar-start', 'sidebar-end');
            
            const sidebarTime = performance.getEntriesByName('sidebar-load')[0];
            console.log(`Sidebar load time: ${sidebarTime.duration}ms`);
            
            // Alert se troppo lento
            if (sidebarTime.duration > 1000) {
                console.warn('Sidebar loading too slow');
            }
        });
    }
    
    static measureMenuToggle() {
        const toggleButtons = document.querySelectorAll('.dropdown-toggle');
        
        toggleButtons.forEach(button => {
            button.addEventListener('click', (e) => {
                const start = performance.now();
                
                // Misura tempo toggle submenu
                setTimeout(() => {
                    const end = performance.now();
                    const duration = end - start;
                    
                    if (duration > 100) {
                        console.warn(`Menu toggle slow: ${duration}ms`);
                    }
                }, 0);
            });
        });
    }
}

UIPerformanceMonitor.init();

🚀 PROSSIMI MIGLIORAMENTI

📋 Roadmap Interfaccia

Implementato

  • Layout universale Bootstrap responsive
  • Sidebar dinamica con permessi
  • Launcher bar con azioni rapide
  • Menu configurabili
  • Sistema ruoli integrato

🔄 In Corso

  • Testing cross-browser approfondito
  • Ottimizzazione performance mobile
  • Accessibilità WCAG 2.1

Pianificato

  • Dark theme completo
  • Personalizzazione UI per cliente
  • Animazioni avanzate
  • PWA (Progressive Web App)
  • Offline mode base

🎯 Obiettivi Qualità

  • Performance: Caricamento < 2 secondi
  • Accessibilità: WCAG 2.1 AA compliance
  • Mobile: 100% funzionalità su smartphone
  • Cross-browser: IE11+, Chrome, Firefox, Safari, Edge

📞 SUPPORTO E MANUTENZIONE

🔧 Debug e Troubleshooting

// Helper per debug interfaccia
if (app()->environment('local')) {
    // Mostra info debug nella sidebar
    echo "<!-- Debug Info:";
    echo "User Role: " . Auth::user()->role ?? 'Guest';
    echo "Permissions: " . implode(', ', Auth::user()->getAllPermissions()->pluck('name')->toArray() ?? []);
    echo "Active Menu: " . request()->route()->getName() ?? 'Unknown';
    echo "-->";
}

📈 Monitoring Produzione

  • Error tracking: Sentry per errori JavaScript
  • Performance: New Relic per monitoring performance
  • User analytics: Google Analytics per utilizzo interfaccia
  • Uptime: Pingdom per availability

L'interfaccia universale di NetGesCon rappresenta uno dei punti di forza del sistema, fornendo un'esperienza utente moderna, responsive e altamente personalizzabile che si adatta dinamicamente al ruolo dell'utente.