netgescon-day0/docs/90-UI-interfaccia-unica/90-01-CONTENT-MENU-STANDARD.md

18 KiB

🎯 NETGESCON CONTENT MENU STANDARD

Manuale per replicare struttura menu e contenuti

📅 Versione: 2.0 - 5 Agosto 2025

🎯 Obiettivo: Standardizzazione interfaccia per tutte le pagine


🎨 PATTERN FUNZIONANTE IDENTIFICATO

Strutture Menu che Funzionano:

  1. Menu Principale con Sub-tabs (Contabilità, Stabili)
  2. Sub-menu Navigazione (Tabelle Millesimali)
  3. Cards Statistiche (Dashboard style)
  4. Layout Standard NetGescon

🗂️ TEMPLATE MASTER - STRUTTURA STANDARD

📋 1. LAYOUT BASE OBBLIGATORIO

@extends('admin.layouts.netgescon')

@section('title', 'Nome Modulo')

@section('content')
<div class="content-wrapper">
    <!-- HEADER BREADCRUMB -->
    <div class="content-header">
        <div class="container-fluid">
            <div class="row mb-2">
                <div class="col-sm-6">
                    <h1 class="m-0">Nome Modulo</h1>
                </div>
                <div class="col-sm-6">
                    <ol class="breadcrumb float-sm-right">
                        <li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}">Home</a></li>
                        <li class="breadcrumb-item active">Nome Modulo</li>
                    </ol>
                </div>
            </div>
        </div>
    </div>

    <!-- SUB-MENU TABS PATTERN -->
    <div class="content-header">
        <div class="container-fluid">
            <div class="row">
                <div class="col-12">
                    <div class="card card-primary card-tabs">
                        <div class="card-header p-0 pt-1">
                            <ul class="nav nav-tabs" id="custom-tabs-one-tab" role="tablist">
                                <!-- TAB ITEMS - PATTERN REPLICABILE -->
                                @yield('menu-tabs')
                            </ul>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <!-- STATISTICHE CARDS PATTERN -->
    <section class="content">
        <div class="container-fluid">
            <!-- STATS ROW - PATTERN REPLICABILE -->
            @yield('stats-cards')
            
            <!-- MAIN CONTENT AREA -->
            @yield('main-content')
        </div>
    </section>
</div>
@endsection

<!-- STYLES PATTERN -->
@push('styles')
@yield('module-styles')
@endpush

🏗️ PATTERN 1: MENU PRINCIPALE CON SUB-TABS

🎯 Utilizzo: Moduli principali (Contabilità, Stabili, Condomini)

📝 Template Tab Menu:

@section('menu-tabs')
<li class="nav-item">
    <a class="nav-link active" 
       href="{{ route('admin.MODULO.index') }}"
       role="tab">
        <i class="fas fa-list"></i> Lista Principale
    </a>
</li>
<li class="nav-item">
    <a class="nav-link" 
       href="{{ route('admin.MODULO.create') }}"
       role="tab">
        <i class="fas fa-plus"></i> Nuovo Item
    </a>
</li>
<li class="nav-item">
    <a class="nav-link" 
       href="{{ route('admin.MODULO.reports') }}"
       role="tab">
        <i class="fas fa-chart-bar"></i> Reports
    </a>
</li>
<li class="nav-item">
    <a class="nav-link" 
       href="{{ route('admin.MODULO.config') }}"
       role="tab">
        <i class="fas fa-cog"></i> Configurazione
    </a>
</li>
@endsection

💡 Icone Standard per Moduli:

// Mapping icone per consistency
$moduleIcons = [
    'list' => 'fas fa-list',
    'create' => 'fas fa-plus', 
    'edit' => 'fas fa-edit',
    'show' => 'fas fa-eye',
    'reports' => 'fas fa-chart-bar',
    'config' => 'fas fa-cog',
    'export' => 'fas fa-download',
    'import' => 'fas fa-upload',
    'search' => 'fas fa-search',
    'filter' => 'fas fa-filter'
];

🏗️ PATTERN 2: SUB-MENU NAVIGAZIONE

🎯 Utilizzo: Sezioni dettaglio con tabs (Stabile Detail, etc.)

📝 Template Tabs Detail:

<!-- Tabs Container -->
<div class="bg-white rounded-lg shadow-sm border">
    <!-- Tab Navigation -->
    <div class="border-b border-gray-200 px-6 pt-6">
        <nav class="-mb-px flex space-x-8" aria-label="Tabs" role="tablist">
            <button class="netgescon-tab-btn active" 
                    data-tab="dati-generali" 
                    type="button" 
                    role="tab">
                <i class="fas fa-info-circle mr-2"></i>
                Dati Generali
            </button>
            <button class="netgescon-tab-btn" 
                    data-tab="dettagli" 
                    type="button" 
                    role="tab">
                <i class="fas fa-list mr-2"></i>
                Dettagli
            </button>
            <button class="netgescon-tab-btn" 
                    data-tab="documenti" 
                    type="button" 
                    role="tab">
                <i class="fas fa-file mr-2"></i>
                Documenti
            </button>
            <!-- Altri tabs del modulo -->
        </nav>
    </div>
    
    <!-- Tab Content -->
    <div class="p-6">
        <div id="dati-generali" class="netgescon-tab-content active">
            @yield('tab-dati-generali')
        </div>
        <div id="dettagli" class="netgescon-tab-content">
            @yield('tab-dettagli') 
        </div>
        <div id="documenti" class="netgescon-tab-content">
            @yield('tab-documenti')
        </div>
    </div>
</div>

🏗️ PATTERN 3: CARDS STATISTICHE

🎯 Utilizzo: Dashboard e pagine overview

📝 Template Stats Cards:

@section('stats-cards')
<div class="row mb-3">
    <div class="col-md-3">
        <div class="small-box bg-primary">
            <div class="inner">
                <h3>{{ $stats['totale'] ?? 0 }}</h3>
                <p>Totale Items</p>
            </div>
            <div class="icon">
                <i class="fas fa-list"></i>
            </div>
        </div>
    </div>
    <div class="col-md-3">
        <div class="small-box bg-success">
            <div class="inner">
                <h3>{{ $stats['attivi'] ?? 0 }}</h3>
                <p>Attivi</p>
            </div>
            <div class="icon">
                <i class="fas fa-check"></i>
            </div>
        </div>
    </div>
    <div class="col-md-3">
        <div class="small-box bg-warning">
            <div class="inner">
                <h3>{{ $stats['pending'] ?? 0 }}</h3>
                <p>In Sospeso</p>
            </div>
            <div class="icon">
                <i class="fas fa-clock"></i>
            </div>
        </div>
    </div>
    <div class="col-md-3">
        <div class="small-box bg-danger">
            <div class="inner">
                <h3>{{ $stats['problemi'] ?? 0 }}</h3>
                <p>Problemi</p>
            </div>
            <div class="icon">
                <i class="fas fa-exclamation"></i>
            </div>
        </div>
    </div>
</div>
@endsection

🎨 Colori Standard Cards:

/* Color mapping per consistency */
.small-box.bg-primary { background: #007bff; }    /* Totali */
.small-box.bg-success { background: #28a745; }    /* Positivi/Attivi */
.small-box.bg-warning { background: #ffc107; }    /* In Attesa/Sospeso */
.small-box.bg-danger { background: #dc3545; }     /* Errori/Problemi */
.small-box.bg-info { background: #17a2b8; }       /* Info/Neutro */
.small-box.bg-secondary { background: #6c757d; }  /* Secondario */

📊 TEMPLATE CONTROLLER STANDARD

🎯 Controller Pattern per Menu Funzionante:

<?php
// app/Http/Controllers/Admin/{Module}Controller.php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class ModuleController extends Controller
{
    /**
     * Display main listing with stats and menu
     */
    public function index()
    {
        // Calcola statistiche per cards
        $stats = $this->calculateStats();
        
        // Dati principale per listing
        $items = $this->getMainData();
        
        // Menu configuration
        $menuTabs = $this->getMenuTabs();
        
        return view('admin.module.index', compact('stats', 'items', 'menuTabs'));
    }
    
    /**
     * Show detail with sub-tabs
     */
    public function show($id)
    {
        $item = $this->findItem($id);
        $detailTabs = $this->getDetailTabs($item);
        $tabContent = $this->getTabContent($item);
        
        return view('admin.module.show', compact('item', 'detailTabs', 'tabContent'));
    }
    
    /**
     * Calculate statistics for cards
     */
    private function calculateStats()
    {
        return [
            'totale' => Model::count(),
            'attivi' => Model::where('stato', 'attivo')->count(),
            'pending' => Model::where('stato', 'pending')->count(),
            'problemi' => Model::where('stato', 'errore')->count(),
        ];
    }
    
    /**
     * Get menu tabs configuration
     */
    private function getMenuTabs()
    {
        return [
            ['label' => 'Lista', 'route' => 'admin.module.index', 'icon' => 'fas fa-list', 'active' => true],
            ['label' => 'Nuovo', 'route' => 'admin.module.create', 'icon' => 'fas fa-plus'],
            ['label' => 'Reports', 'route' => 'admin.module.reports', 'icon' => 'fas fa-chart-bar'],
            ['label' => 'Config', 'route' => 'admin.module.config', 'icon' => 'fas fa-cog'],
        ];
    }
}

🗄️ DATABASE STANDARD CONNECTION

🔑 Credenziali Database NetGescon:

# File: .env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=netgescon
DB_USERNAME=netgescon_user
DB_PASSWORD=NetGescon2024!

📊 Pattern Query Standard:

// In Controller
use Illuminate\Support\Facades\DB;

// Query con relazioni standard
$items = DB::table('main_table')
    ->leftJoin('stabili', 'main_table.stabile_id', '=', 'stabili.id')
    ->leftJoin('gestioni', 'main_table.gestione_id', '=', 'gestioni.id_gestione')
    ->select([
        'main_table.*',
        'stabili.denominazione as stabile_nome',
        'gestioni.anno_gestione',
        'gestioni.tipo_gestione'
    ])
    ->where('stabili.amministratore_id', auth()->user()->amministratore_id ?? null)
    ->orderBy('main_table.created_at', 'desc')
    ->paginate(20);

// Stats query standard
$stats = [
    'totale' => DB::table('main_table')->count(),
    'questo_mese' => DB::table('main_table')
        ->whereMonth('created_at', now()->month)
        ->count(),
    'attivi' => DB::table('main_table')
        ->where('stato', 'attivo')
        ->count()
];

🎨 CSS STANDARD NETGESCON

📁 File: /public/css/netgescon-standard.css

/* NetGescon Standard Module Styles */

/* Menu Tabs Standard */
.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;
}

.nav-tabs .nav-link:hover {
    border-color: #e9ecef #e9ecef #dee2e6;
    color: #007bff;
}

.nav-tabs .nav-link.active {
    background-color: #fff;
    border-color: #007bff #007bff #fff;
    color: #007bff;
    font-weight: 600;
}

/* NetGescon Tab Buttons */
.netgescon-tab-btn {
    border-bottom: 2px solid transparent;
    color: #6b7280;
    font-weight: 500;
    padding: 0.75rem 0;
    transition: all 0.3s ease;
    white-space: nowrap;
}

.netgescon-tab-btn:hover {
    border-bottom-color: #3b82f6;
    color: #3b82f6;
}

.netgescon-tab-btn.active {
    border-bottom-color: #3b82f6;
    color: #3b82f6;
    font-weight: 600;
}

/* Tab Content */
.netgescon-tab-content {
    display: none;
}

.netgescon-tab-content.active {
    display: block;
}

/* Small Box Cards */
.small-box {
    border-radius: 0.375rem;
    color: #fff;
    overflow: hidden;
    position: relative;
    margin-bottom: 1rem;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    transition: all 0.3s ease;
}

.small-box:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}

.small-box .inner {
    padding: 1rem;
}

.small-box h3 {
    font-size: 2rem;
    font-weight: 700;
    margin: 0;
    white-space: nowrap;
}

.small-box p {
    font-size: 1rem;
    margin: 0.5rem 0 0 0;
}

.small-box .icon {
    position: absolute;
    top: auto;
    bottom: 10px;
    right: 10px;
    font-size: 3rem;
    color: rgba(255,255,255,0.15);
}

/* Responsive Cards */
@media (max-width: 768px) {
    .small-box h3 {
        font-size: 1.5rem;
    }
    
    .small-box .icon {
        font-size: 2rem;
        bottom: 5px;
        right: 5px;
    }
}

JAVASCRIPT STANDARD

📁 File: /public/js/netgescon-modules.js

/**
 * NetGescon Standard Module JavaScript
 */

// Inizializzazione moduli
document.addEventListener('DOMContentLoaded', function() {
    initializeNetGesconTabs();
    initializeStatCards();
    initializeMenuHighlight();
});

// Tab Management
function initializeNetGesconTabs() {
    const tabButtons = document.querySelectorAll('.netgescon-tab-btn');
    const tabContents = document.querySelectorAll('.netgescon-tab-content');
    
    tabButtons.forEach(button => {
        button.addEventListener('click', function() {
            const tabId = this.getAttribute('data-tab');
            
            // Remove active class from all buttons and contents
            tabButtons.forEach(btn => btn.classList.remove('active'));
            tabContents.forEach(content => content.classList.remove('active'));
            
            // Add active class to clicked button and corresponding content
            this.classList.add('active');
            const targetContent = document.getElementById(tabId);
            if (targetContent) {
                targetContent.classList.add('active');
            }
        });
    });
}

// Stat Cards Animation
function initializeStatCards() {
    const cards = document.querySelectorAll('.small-box');
    
    cards.forEach(card => {
        card.addEventListener('mouseenter', function() {
            this.style.transform = 'translateY(-3px)';
        });
        
        card.addEventListener('mouseleave', function() {
            this.style.transform = 'translateY(0)';
        });
    });
}

// Menu Active Highlight
function initializeMenuHighlight() {
    const currentPath = window.location.pathname;
    const menuLinks = document.querySelectorAll('.nav-tabs .nav-link');
    
    menuLinks.forEach(link => {
        if (link.getAttribute('href') === currentPath) {
            link.classList.add('active');
        }
    });
}

// Utility: Show loading state
function showLoading(element) {
    element.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Caricamento...';
    element.disabled = true;
}

// Utility: Hide loading state
function hideLoading(element, originalText) {
    element.innerHTML = originalText;
    element.disabled = false;
}

// Utility: Show notification
function showNotification(message, type = 'info') {
    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: 300px;';
    notification.innerHTML = `
        ${message}
        <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
    `;
    
    document.body.appendChild(notification);
    
    setTimeout(() => {
        notification.remove();
    }, 5000);
}

📋 CHECKLIST IMPLEMENTAZIONE

Per Ogni Nuovo Modulo:

  • Layout Base - Extends admin.layouts.netgescon
  • Breadcrumb - Header con navigazione corretta
  • Sub-menu Tabs - Pattern menu funzionante
  • Stats Cards - 4 cards statistiche standard
  • Main Content - Area contenuto principale
  • CSS Classes - NetGescon design system
  • JavaScript - Tab functionality standard
  • Controller - Stats calculation e data retrieval
  • Database - Query con relazioni standard
  • Routes - RESTful con naming consistency

File da Creare per Modulo:

resources/views/admin/{module}/
├── index.blade.php      # Lista principale con menu
├── show.blade.php       # Dettaglio con sub-tabs  
├── create.blade.php     # Form creazione
├── edit.blade.php       # Form modifica
└── _form.blade.php      # Form partial

app/Http/Controllers/Admin/
└── {Module}Controller.php

routes/
└── admin.php (aggiungere routes del modulo)

🚀 ESEMPI MODULI DA REPLICARE

1. Modulo Semplice (es: Fornitori):

# Usa Pattern 1: Menu principale con sub-tabs
# Stats: Totale, Attivi, Inattivi, Questo mese
# Tabs: Lista, Nuovo, Categorie, Reports

2. Modulo Complesso (es: Gestioni):

# Usa Pattern 1 + Pattern 2
# Menu principale: Lista, Nuovo, Archivio, Reports
# Detail sub-tabs: Dati, Movimenti, Documenti, Bilanci

3. Modulo Report (es: Analytics):

# Usa Pattern 3: Solo cards statistiche
# Focus su visualizzazione dati
# Grafici e tabelle dinamiche

🔗 RIFERIMENTI E CONNESSIONI

📁 Files Sorgente Pattern:

  • Contabilità: /resources/views/modules/contabilita/index.blade.php
  • Stabili: /resources/views/admin/stabili/show.blade.php
  • Layout: /resources/views/admin/layouts/netgescon.blade.php

🗄️ Database Schema Principale:

-- Tabelle core sempre presenti
stabili (id, denominazione, ...)
gestioni (id_gestione, anno_gestione, ...)
unita_immobiliari (id, stabile_id, ...)
amministratori (id, ...)

-- Pattern naming nuove tabelle
{modulo}_registrazioni
{modulo}_configurazioni  
{modulo}_statistiche

🎯 Route Pattern Standard:

// routes/admin.php
Route::prefix('admin')->name('admin.')->group(function () {
    Route::resource('{modulo}', '{Modulo}Controller');
    Route::get('{modulo}/{id}/detail', ['{Modulo}Controller', 'detail'])->name('{modulo}.detail');
    Route::get('{modulo}/reports', ['{Modulo}Controller', 'reports'])->name('{modulo}.reports');
});

🎯 MANUALE STANDARD CREATO
Per ogni nuovo modulo: segui questo pattern e avrai consistenza UI/UX