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

678 lines
20 KiB
Markdown

# 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
```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
```html
<!-- 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
```php
{{-- 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
```css
/* 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à
```html
<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
```php
{{-- 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
```php
// 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
<?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
<?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
```css
/* 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
```javascript
// 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
```css
: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
<?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
<?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
```javascript
// 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
```php
// 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.