18 KiB
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:
- Menu Principale con Sub-tabs (Contabilità, Stabili)
- Sub-menu Navigazione (Tabelle Millesimali)
- Cards Statistiche (Dashboard style)
- 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