netgescon-day0/docs/SPECIFICA-SVILUPPO-MODULI.md

34 KiB
Executable File

📋 GUIDA COMPLETA SVILUPPO MODULI NETGESCON

Versione: 2.0 - Data: 01 Agosto 2025


🎯 INTRODUZIONE

NetGescon utilizza un sistema modulare avanzato che permette di sviluppare, installare e monetizzare moduli aggiuntivi. Ogni modulo è completamente autonomo e può essere distribuito come prodotto commerciale.

AGGIORNAMENTI VERSIONE 2.0:

  • Layout template unificato (modules.layouts.base)
  • Sistema permessi automatico migliorato
  • Integrazione grafica perfetta con NetGescon
  • Sidebar dinamica con stati moduli
  • Cache management ottimizzato

🏗️ ARCHITETTURA MODULARE

NetGescon/
├── app/
│   ├── Services/
│   │   ├── ModuleManager.php    # Gestione moduli
│   │   └── HookManager.php      # Sistema eventi
│   ├── Providers/
│   │   └── ModuleServiceProvider.php  # Caricamento automatico moduli
│   └── Modules/
│       └── [NomeModulo]/
│           ├── module.json      # Configurazione modulo
│           ├── Controllers/     # Controller Laravel
│           ├── Routes/          # File route
│           ├── Migrations/      # Migrazioni database
│           ├── Seeders/         # Dati iniziali
│           └── Views/           # Template Blade
├── resources/views/
│   └── modules/
│       └── layouts/
│           └── base.blade.php   # Template base moduli
└── docs/
    ├── SPECIFICA-SVILUPPO-MODULI.md
    └── TEMPLATE-SVILUPPO-MODULI.md

🎨 NUOVO SISTEMA LAYOUT UNIFICATO

Template Base Standard

Tutti i moduli DEVONO utilizzare il template base unificato:

@extends('modules.layouts.base')

@php
    $moduleTitle = 'Nome del Modulo';
    $moduleIcon = 'fas fa-calculator';
    $moduleBreadcrumb = 'Moduli > Nome Modulo';
@endphp

@section('module-actions')
<div class="btn-group" role="group">
    @can('modulo_create')
        <a href="{{ route('modulo.create') }}" class="btn btn-module-primary">
            <i class="fas fa-plus me-1"></i>
            Nuovo Elemento
        </a>
    @endcan
    <button type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown">
        <i class="fas fa-cog"></i>
    </button>
    <ul class="dropdown-menu">
        <li><a class="dropdown-item" href="#"><i class="fas fa-file-export me-2"></i>Esporta</a></li>
        <li><a class="dropdown-item" href="#"><i class="fas fa-cog me-2"></i>Impostazioni</a></li>
    </ul>
</div>
@endsection

@section('module-content')
    <!-- Il tuo contenuto qui -->
@endsection

Classi CSS Standardizzate

Il sistema fornisce queste classi CSS preconfigurate:

/* Card statistiche */
.module-stats-card          /* Card per statistiche rapide */
.module-stats-number        /* Numero grande nelle statistiche */  
.module-stats-label         /* Label sotto il numero */

/* Pulsanti */
.btn-module-primary         /* Pulsante primario modulo */

/* Layout */
.module-card               /* Card generica per contenuti */
.module-card-header        /* Header della card */
.module-card-body          /* Body della card */
.module-card-title         /* Titolo della card */
.module-empty-state        /* Stato vuoto con icona */

🔐 NUOVO SISTEMA PERMESSI AUTOMATICO

Comando di Installazione Permessi

Ogni modulo deve avere un comando Artisan per installare i permessi:

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
use Illuminate\Support\Facades\DB;

class UpdateNomeModuloPermissions extends Command
{
    protected $signature = 'nomemodulo:permissions';
    protected $description = 'Installa i permessi per il modulo NomeModulo';

    public function handle()
    {
        $this->info('🔧 Installazione permessi modulo NomeModulo...');
        
        // Definisci i permessi del modulo
        $permissions = [
            'nomemodulo_view' => 'Visualizzare elementi del modulo',
            'nomemodulo_create' => 'Creare nuovi elementi', 
            'nomemodulo_edit' => 'Modificare elementi esistenti',
            'nomemodulo_delete' => 'Eliminare elementi',
            'nomemodulo_export' => 'Esportare dati',
            'nomemodulo_settings' => 'Gestire impostazioni modulo'
        ];

        // Crea i permessi
        foreach ($permissions as $name => $description) {
            $permission = Permission::firstOrCreate(
                ['name' => $name],
                ['guard_name' => 'web']
            );
            $this->line("✅ Permesso: {$name}");
        }

        // Assegna permessi ai ruoli amministrativi
        $adminRoles = ['admin', 'amministratore', 'super-admin'];
        
        foreach ($adminRoles as $roleName) {
            $role = Role::where('name', $roleName)->first();
            if ($role) {
                foreach (array_keys($permissions) as $permissionName) {
                    $permission = Permission::where('name', $permissionName)->first();
                    if ($permission && !$role->hasPermissionTo($permission)) {
                        $role->givePermissionTo($permission);
                    }
                }
                $this->line("✅ Permessi assegnati al ruolo: {$roleName}");
            }
        }

        // Aggiorna il record del modulo nel database
        DB::table('modules')
            ->where('name', 'NomeModulo')
            ->update([
                'permissions' => json_encode(array_keys($permissions)),
                'updated_at' => now()
            ]);

        $this->info('🎉 Permessi modulo NomeModulo installati con successo!');
        return 0;
    }
}

Utilizzo Permessi nei Controller

public function __construct()
{
    $this->middleware('auth');
    $this->middleware('permission:nomemodulo_view')->only(['index', 'show']);
    $this->middleware('permission:nomemodulo_create')->only(['create', 'store']);
    $this->middleware('permission:nomemodulo_edit')->only(['edit', 'update']);
    $this->middleware('permission:nomemodulo_delete')->only(['destroy']);
}

📊 SIDEBAR DINAMICA E STATI MODULI

Integrazione nella Sidebar

Il sistema gestisce automaticamente l'apparizione dei moduli nella sidebar. La sidebar mostra:

  • ATTIVO - Modulo installato e funzionante
  • Prezzo - Modulo disponibile ma non installato
  • 🔒 Disabilitato - Modulo installato ma non attivo

Aggiornamento SidebarComposer

Il SidebarComposer è stato aggiornato per fornire la lista activeModules:

public function compose(View $view)
{
    // ...codice esistente...
    
    $view->with([
        'stabili' => $stabili,
        'stabileAttivo' => $stabileAttivo,
        'anni' => $gestioni->pluck('anno_gestione')->unique(),
        'annoAttivo' => $annoAttivo,
        'gestione' => $gestioneAttiva,
        'activeModules' => Cache::remember('netgescon.active_modules', 3600, function() {
            return DB::table('modules')->where('status', 'active')->pluck('name')->toArray();
        }),
    ]);
}

🚀 PROCESSO DI INSTALLAZIONE SEMPLIFICATO

1. Registrazione Database

// Via Tinker o comando
DB::table('modules')->insert([
    'name' => 'NomeModulo',
    'display_name' => 'Nome Visualizzato',
    'description' => 'Descrizione del modulo',
    'version' => '1.0.0',
    'status' => 'active',
    'license' => 'commercial',
    'price' => 99.00,
    'currency' => 'EUR',
    'billing' => 'yearly',
    'config' => '{}',
    'dependencies' => '[]',
    'permissions' => '[]',
    'created_at' => now(),
    'updated_at' => now()
]);

2. Installazione Permessi

php artisan nomemodulo:permissions

3. Pulizia Cache

php artisan cache:clear
php artisan config:clear
php artisan view:clear

CHECKLIST MODULO COMPLETO V2.0

Files Obbligatori

  • Controller con CRUD base
  • Route definitions (Routes/web.php)
  • Migration database
  • Views usando @extends('modules.layouts.base')
  • Comando permessi (php artisan make:command UpdateNomeModuloPermissions)
  • Middleware permessi nel controller
  • Integrazione nella sidebar

Aspetti Tecnici

  • Utilizza template base unificato
  • Permessi installati e assegnati
  • Cache management corretto
  • Responsive design (Bootstrap 5)
  • Tooltips e UX enhancements
  • Error handling robusto
  • Empty states informativi

Testing

  • Modulo visibile in sidebar
  • Permessi funzionanti (403 risolto)
  • Layout grafico coerente
  • Funzionalità CRUD complete
  • Performance accettabili

🎯 ESEMPIO PRATICO: MODULO CONTABILITÀ

Il modulo Contabilità è il nostro modulo di riferimento che implementa tutte le best practices:

Struttura File

app/Modules/Contabilita/
├── Controllers/ContabilitaController.php
├── Routes/web.php
├── Migrations/2025_01_22_170000_create_piano_conti_tables.php
└── Views/
    └── index.blade.php

Features Implementate

  • Layout unificato con template base
  • Statistiche rapide in card
  • Tabella responsive con azioni
  • Filtri di ricerca avanzati
  • Sistema permessi completo
  • Empty state informativo
  • Pagination integrata
  • Tooltips e UX enhancements

📞 SUPPORTO E RISORSE

Documentazione di Riferimento

  • docs/SPECIFICA-SVILUPPO-MODULI.md - Questa guida
  • docs/TEMPLATE-SVILUPPO-MODULI.md - Template da copiare
  • resources/views/modules/layouts/base.blade.php - Template base

Moduli di Esempio

  • Contabilità - Modulo complesso con statistiche e CRUD
  • StampeRate - Modulo con processing e file generation

Comandi Utili

# Installa permessi modulo
php artisan nomemodulo:permissions

# Controlla moduli attivi  
php artisan tinker --execute="DB::table('modules')->where('status', 'active')->get(['name', 'status'])"

# Pulisci cache moduli
php artisan cache:clear

Il sistema modulare NetGescon è ora maturo, stabile e facilmente estendibile. Seguendo questa guida potrai creare moduli professionali che si integrano perfettamente con l'ecosistema esistente.


🎯 INTRODUZIONE

NetGescon utilizza un sistema modulare avanzato che permette di sviluppare, installare e monetizzare moduli aggiuntivi. Ogni modulo è completamente autonomo e può essere distribuito come prodotto commerciale.

🏗️ ARCHITETTURA MODULARE

NetGescon/
├── app/
│   ├── Services/
│   │   ├── ModuleManager.php    # Gestione moduli
│   │   └── HookManager.php      # Sistema eventi
│   └── Modules/
│       └── [NomeModulo]/
│           ├── module.json      # Configurazione modulo
│           ├── Controllers/     # Controller Laravel
│           ├── Routes/          # File route
│           ├── Migrations/      # Migrazioni database
│           ├── Seeders/         # Dati iniziali
│           ├── Views/           # Template Blade
│           └── Assets/          # CSS/JS specifici

🔧 STRUTTURA MODULO STANDARD

1. Configurazione Base (module.json)

{
    "name": "NomeModulo",
    "display_name": "Nome Visualizzato",
    "version": "1.0.0",
    "description": "Descrizione del modulo",
    "author": "Nome Sviluppatore",
    "email": "email@sviluppatore.com",
    "license": "commercial",
    "price": 99.00,
    "currency": "EUR",
    "billing": "yearly",
    
    "requirements": {
        "laravel": ">=10.0",
        "php": ">=8.1",
        "netgescon": ">=1.0"
    },
    
    "dependencies": [],
    
    "permissions": {
        "modulo_view": "Visualizzare il modulo",
        "modulo_create": "Creare elementi",
        "modulo_edit": "Modificare elementi", 
        "modulo_delete": "Eliminare elementi",
        "modulo_admin": "Amministrazione completa"
    },
    
    "menu_items": [
        {
            "title": "Dashboard Modulo",
            "route": "modulo.index",
            "icon": "fa-dashboard",
            "permission": "modulo_view",
            "order": 1
        }
    ],
    
    "hooks": [
        "modulo.before_create",
        "modulo.after_create",
        "modulo.before_update",
        "modulo.after_update",
        "modulo.before_delete",
        "modulo.after_delete"
    ],
    
    "config": {
        "auto_install_routes": true,
        "auto_install_migrations": true,
        "cache_enabled": true,
        "api_enabled": true
    }
}

2. Controller Base

<?php

namespace App\Modules\NomeModulo\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use App\Services\HookManager;

class NomeModuloController extends Controller
{
    protected $hookManager;
    
    public function __construct()
    {
        // Middleware di autenticazione
        $this->middleware('auth');
        
        // Middleware permessi specifici del modulo
        $this->middleware('permission:modulo_view')->only(['index', 'show']);
        $this->middleware('permission:modulo_create')->only(['create', 'store']);
        $this->middleware('permission:modulo_edit')->only(['edit', 'update']);
        $this->middleware('permission:modulo_delete')->only(['destroy']);
        
        $this->hookManager = app(HookManager::class);
    }
    
    public function index(Request $request)
    {
        try {
            // Hook pre-azione
            $this->hookManager->execute('modulo.before_index', [$request]);
            
            // Logica del controller
            $data = DB::table('modulo_table')->paginate(20);
            
            // Hook post-azione
            $this->hookManager->execute('modulo.after_index', [$data]);
            
            return view('modules.nome-modulo.index', compact('data'));
            
        } catch (\Exception $e) {
            Log::error("Errore modulo: " . $e->getMessage());
            return back()->with('error', 'Errore: ' . $e->getMessage());
        }
    }
    
    // Altri metodi CRUD...
}

3. Routes (web.php)

<?php

use Illuminate\Support\Facades\Route;
use App\Modules\NomeModulo\Controllers\NomeModuloController;

// Gruppo route con middleware
Route::middleware(['auth', 'verified'])->group(function () {
    
    // Route principali
    Route::prefix('nome-modulo')->name('modulo.')->group(function () {
        Route::get('/', [NomeModuloController::class, 'index'])->name('index');
        Route::get('/create', [NomeModuloController::class, 'create'])->name('create');
        Route::post('/', [NomeModuloController::class, 'store'])->name('store');
        Route::get('/{id}', [NomeModuloController::class, 'show'])->name('show');
        Route::get('/{id}/edit', [NomeModuloController::class, 'edit'])->name('edit');
        Route::put('/{id}', [NomeModuloController::class, 'update'])->name('update');
        Route::delete('/{id}', [NomeModuloController::class, 'destroy'])->name('destroy');
    });
    
    // Route API per AJAX
    Route::prefix('api/nome-modulo')->name('api.modulo.')->group(function () {
        Route::get('/data', [NomeModuloController::class, 'getData'])->name('data');
        Route::post('/action', [NomeModuloController::class, 'action'])->name('action');
    });
});

4. Migration Standard

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        // Tabella principale del modulo
        Schema::create('modulo_table', function (Blueprint $table) {
            $table->id();
            $table->string('nome');
            $table->text('descrizione')->nullable();
            $table->enum('status', ['attivo', 'inattivo'])->default('attivo');
            $table->json('config')->nullable();
            
            // Audit fields standard
            $table->unsignedBigInteger('created_by')->nullable();
            $table->unsignedBigInteger('updated_by')->nullable();
            $table->timestamps();
            $table->softDeletes();
            
            // Indici per performance
            $table->index(['status', 'created_at']);
            $table->index('nome');
            
            // Foreign keys
            $table->foreign('created_by')->references('id')->on('users')->onDelete('set null');
            $table->foreign('updated_by')->references('id')->on('users')->onDelete('set null');
        });
        
        // Tabella log/cronologia
        Schema::create('modulo_history', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('record_id');
            $table->string('action');
            $table->json('old_data')->nullable();
            $table->json('new_data')->nullable();
            $table->unsignedBigInteger('user_id')->nullable();
            $table->timestamp('created_at');
            
            $table->index(['record_id', 'created_at']);
            $table->foreign('record_id')->references('id')->on('modulo_table')->onDelete('cascade');
            $table->foreign('user_id')->references('id')->on('users')->onDelete('set null');
        });
    }
    
    public function down(): void
    {
        Schema::dropIfExists('modulo_history');
        Schema::dropIfExists('modulo_table');
    }
};

5. Seeder con Dati Base

<?php

namespace App\Modules\NomeModulo\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class NomeModuloSeeder extends Seeder
{
    public function run(): void
    {
        // Configurazioni base
        $configs = [
            [
                'chiave' => 'modulo.default_setting',
                'valore' => 'valore_default',
                'descrizione' => 'Impostazione di default',
                'created_at' => now(),
                'updated_at' => now()
            ]
        ];
        
        foreach ($configs as $config) {
            DB::table('modulo_config')->insert($config);
        }
        
        // Dati di esempio (solo in development)
        if (app()->environment('local', 'development')) {
            $esempi = [
                [
                    'nome' => 'Esempio 1',
                    'descrizione' => 'Dato di esempio',
                    'status' => 'attivo',
                    'created_by' => 1,
                    'created_at' => now(),
                    'updated_at' => now()
                ]
            ];
            
            foreach ($esempi as $esempio) {
                DB::table('modulo_table')->insert($esempio);
            }
        }
        
        echo "✅ Seeder NomeModulo completato\n";
    }
}

🔌 SISTEMA HOOK E EVENTI

Hook Disponibili nel Sistema

// Hook di sistema core
'app.boot'                    // All'avvio dell'applicazione
'app.before_request'          // Prima di ogni richiesta
'app.after_request'           // Dopo ogni richiesta

// Hook moduli
'module.before_install'       // Prima installazione modulo
'module.after_install'        // Dopo installazione modulo
'module.before_uninstall'     // Prima disinstallazione
'module.after_uninstall'      // Dopo disinstallazione

// Hook utenti
'user.before_login'           // Prima del login
'user.after_login'            // Dopo il login
'user.before_logout'          // Prima del logout

// Hook del tuo modulo
'modulo.before_create'        // Prima della creazione
'modulo.after_create'         // Dopo la creazione
'modulo.before_update'        // Prima dell'aggiornamento
'modulo.after_update'         // Dopo l'aggiornamento
'modulo.before_delete'        // Prima dell'eliminazione
'modulo.after_delete'         // Dopo l'eliminazione

Utilizzo Hook nel Modulo

// Nel controller
$this->hookManager->execute('modulo.before_create', [$data]);

// Registrazione hook listener (nel boot del modulo)
$this->hookManager->register('modulo.after_create', function($data) {
    // Logica eseguita dopo la creazione
    Log::info('Nuovo elemento creato: ' . $data['nome']);
});

💾 DATABASE E PERFORMANCE

Best Practices Database

  1. Nomi Tabelle: Sempre prefisso modulo (modulo_tabella)
  2. Indici: Su campi più interrogati (status, created_at, ecc.)
  3. Foreign Keys: Sempre con onDelete('set null') o cascade
  4. Soft Deletes: Per dati importanti
  5. JSON Fields: Per configurazioni flessibili
  6. Audit Trail: Campi created_by, updated_by, deleted_by

Ottimizzazioni Consigliate

// Uso di scope nei Model
public function scopeAttivo($query)
{
    return $query->where('status', 'attivo');
}

// Cache per query pesanti
$data = Cache::tags(['modulo_data'])->remember('modulo_list', 3600, function() {
    return DB::table('modulo_table')->where('status', 'attivo')->get();
});

// Query Builder ottimizzate
$risultati = DB::table('modulo_table')
    ->select(['id', 'nome', 'status'])  // Solo campi necessari
    ->where('status', 'attivo')
    ->orderBy('created_at', 'desc')
    ->limit(20)
    ->get();

🔐 SICUREZZA E PERMESSI

Sistema Permessi

// Definizione permessi nel module.json
"permissions": {
    "modulo_view": "Visualizzare elementi del modulo",
    "modulo_create": "Creare nuovi elementi",
    "modulo_edit": "Modificare elementi esistenti",
    "modulo_delete": "Eliminare elementi",
    "modulo_export": "Esportare dati",
    "modulo_admin": "Amministrazione completa modulo"
}

// Uso nei controller
$this->middleware('permission:modulo_view')->only(['index', 'show']);
$this->middleware('permission:modulo_create')->only(['create', 'store']);

// Controllo nei template Blade
@can('modulo_create')
    <a href="{{ route('modulo.create') }}" class="btn btn-primary">Nuovo</a>
@endcan

Validazione Input

// Nel controller
public function store(Request $request)
{
    $validated = $request->validate([
        'nome' => 'required|string|max:255|unique:modulo_table',
        'email' => 'required|email',
        'importo' => 'required|numeric|min:0',
        'data' => 'required|date|after:today'
    ]);
    
    // Sanitizzazione aggiuntiva
    $validated['nome'] = strip_tags($validated['nome']);
    
    // Creazione record...
}

📦 INSTALLAZIONE E DISTRIBUZIONE

Processo di Installazione

  1. Preparazione ZIP:

    NomeModulo.zip
    ├── module.json
    ├── Controllers/
    ├── Routes/
    ├── Migrations/
    ├── Seeders/
    └── Views/
    
  2. Installazione via ModuleManager:

    $moduleManager = app(ModuleManager::class);
    $result = $moduleManager->installModule('NomeModulo', $zipPath);
    
  3. Registrazione Automatica:

    • Route caricate automaticamente
    • Migration eseguite
    • Permessi registrati
    • Menu integrato

Command Artisan per Installazione

// app/Console/Commands/InstallModule.php
class InstallModule extends Command
{
    protected $signature = 'module:install {name} {--zip=}';
    
    public function handle()
    {
        $name = $this->argument('name');
        $zipPath = $this->option('zip');
        
        $moduleManager = app(ModuleManager::class);
        
        try {
            $result = $moduleManager->installModule($name, $zipPath);
            $this->info("✅ Modulo {$name} installato con successo!");
        } catch (\Exception $e) {
            $this->error("❌ Errore installazione: " . $e->getMessage());
        }
    }
}

🎨 FRONTEND E UX

Template Blade Base

{{-- resources/views/modules/nome-modulo/index.blade.php --}}
@extends('layouts.app')

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

@section('content')
<div class="container-fluid">
    <div class="row">
        <div class="col-12">
            <div class="card">
                <div class="card-header d-flex justify-content-between align-items-center">
                    <h3 class="card-title">Nome Modulo</h3>
                    @can('modulo_create')
                        <a href="{{ route('modulo.create') }}" class="btn btn-primary">
                            <i class="fas fa-plus"></i> Nuovo
                        </a>
                    @endcan
                </div>
                
                <div class="card-body">
                    {{-- Filtri di ricerca --}}
                    <form method="GET" class="mb-3">
                        <div class="row">
                            <div class="col-md-4">
                                <input type="text" name="search" class="form-control" 
                                       placeholder="Cerca..." value="{{ request('search') }}">
                            </div>
                            <div class="col-md-2">
                                <button type="submit" class="btn btn-outline-primary">
                                    <i class="fas fa-search"></i>
                                </button>
                            </div>
                        </div>
                    </form>
                    
                    {{-- Tabella dati --}}
                    <div class="table-responsive">
                        <table class="table table-striped">
                            <thead>
                                <tr>
                                    <th>Nome</th>
                                    <th>Status</th>
                                    <th>Data Creazione</th>
                                    <th>Azioni</th>
                                </tr>
                            </thead>
                            <tbody>
                                @forelse($data as $item)
                                    <tr>
                                        <td>{{ $item->nome }}</td>
                                        <td>
                                            <span class="badge badge-{{ $item->status == 'attivo' ? 'success' : 'secondary' }}">
                                                {{ ucfirst($item->status) }}
                                            </span>
                                        </td>
                                        <td>{{ $item->created_at->format('d/m/Y H:i') }}</td>
                                        <td>
                                            @can('modulo_view')
                                                <a href="{{ route('modulo.show', $item->id) }}" class="btn btn-sm btn-info">
                                                    <i class="fas fa-eye"></i>
                                                </a>
                                            @endcan
                                            @can('modulo_edit')
                                                <a href="{{ route('modulo.edit', $item->id) }}" class="btn btn-sm btn-warning">
                                                    <i class="fas fa-edit"></i>
                                                </a>
                                            @endcan
                                            @can('modulo_delete')
                                                <form method="POST" action="{{ route('modulo.destroy', $item->id) }}" 
                                                      style="display: inline-block;">
                                                    @csrf
                                                    @method('DELETE')
                                                    <button type="submit" class="btn btn-sm btn-danger" 
                                                            onclick="return confirm('Sei sicuro?')">
                                                        <i class="fas fa-trash"></i>
                                                    </button>
                                                </form>
                                            @endcan
                                        </td>
                                    </tr>
                                @empty
                                    <tr>
                                        <td colspan="4" class="text-center">Nessun elemento trovato</td>
                                    </tr>
                                @endforelse
                            </tbody>
                        </table>
                    </div>
                    
                    {{-- Paginazione --}}
                    {{ $data->links() }}
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

CSS/JS Specifici del Modulo

/* public/css/modules/nome-modulo.css */
.modulo-card {
    border-radius: 10px;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

.modulo-stats {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    border-radius: 8px;
    padding: 20px;
}

🧪 TESTING E DEBUG

Unit Test Base

<?php

namespace Tests\Modules\NomeModulo;

use Tests\TestCase;
use App\Modules\NomeModulo\Controllers\NomeModuloController;

class NomeModuloTest extends TestCase
{
    public function test_index_displays_data()
    {
        $user = User::factory()->create();
        $user->givePermissionTo('modulo_view');
        
        $response = $this->actingAs($user)->get(route('modulo.index'));
        
        $response->assertStatus(200);
        $response->assertViewIs('modules.nome-modulo.index');
    }
    
    public function test_create_requires_permission()
    {
        $user = User::factory()->create();
        
        $response = $this->actingAs($user)->get(route('modulo.create'));
        
        $response->assertStatus(403);
    }
    
    public function test_store_validates_input()
    {
        $user = User::factory()->create();
        $user->givePermissionTo('modulo_create');
        
        $response = $this->actingAs($user)->post(route('modulo.store'), []);
        
        $response->assertSessionHasErrors(['nome', 'email']);
    }
}

Logging e Debug

// Nel controller
use Illuminate\Support\Facades\Log;

public function action()
{
    Log::info('Modulo: Inizio azione', ['user' => auth()->id()]);
    
    try {
        // Logica
        Log::debug('Modulo: Dati elaborati', ['data' => $processedData]);
        
    } catch (\Exception $e) {
        Log::error('Modulo: Errore azione', [
            'error' => $e->getMessage(),
            'trace' => $e->getTraceAsString(),
            'user' => auth()->id()
        ]);
    }
}

💰 MODELLO BUSINESS E PRICING

Categorie di Moduli

  1. Gratuiti (€0):

    • Moduli base/community
    • Funzionalità essenziali
  2. Standard (€49-99/anno):

    • Moduli specializzati
    • Funzionalità avanzate
  3. Premium (€199-299/anno):

    • Moduli complessi
    • Integrazione esterna
    • Support prioritario
  4. Enterprise (€499+/anno):

    • Moduli mission-critical
    • Customizzazione
    • SLA garantiti

Licenze e Distribuzione

{
    "license": "commercial",
    "price": 99.00,
    "currency": "EUR", 
    "billing": "yearly",
    "trial_days": 30,
    "support_included": true,
    "updates_included": true,
    "installation_limit": 1
}

🚀 DEPLOYMENT E MANUTENZIONE

Checklist Pre-Release

  • Test su ambiente locale
  • Validazione sicurezza
  • Performance test
  • Documentazione completa
  • Screenshot/demo
  • Pricing definito
  • Support plan

Processo di Aggiornamento

// Migration di aggiornamento
public function up(): void
{
    // Verifica versione precedente
    $currentVersion = DB::table('modules')
        ->where('name', 'NomeModulo')
        ->value('version');
    
    if (version_compare($currentVersion, '1.1.0', '<')) {
        // Modifiche per v1.1.0
        Schema::table('modulo_table', function (Blueprint $table) {
            $table->string('nuovo_campo')->nullable();
        });
    }
}

📚 RISORSE E SUPPORTO

Documentazione Obbligatoria

  1. README.md - Panoramica e installazione
  2. CHANGELOG.md - Log delle modifiche
  3. API.md - Documentazione API se presente
  4. HELP.md - Guida utente

Support e Community

  • Forum sviluppatori NetGescon
  • Documentation wiki
  • Code repository template
  • Issue tracking system

CHECKLIST MODULO COMPLETO

Files Essenziali

  • module.json configurato
  • Controller con CRUD base
  • Route definitions
  • Migration database
  • Seeder con dati base
  • Template Blade base
  • Middleware permessi
  • Hook integration
  • Unit tests
  • Documentazione

Funzionalità Base

  • Autenticazione/Autorizzazione
  • CRUD operations
  • Ricerca e filtri
  • Paginazione
  • Export dati
  • Logging attività
  • Cache optimization
  • Error handling
  • Input validation
  • API endpoints

Qualità Codice

  • PSR-4 compliance
  • Commenti e documentation
  • Error handling robusto
  • Security best practices
  • Performance optimization
  • Mobile responsive UI
  • Cross-browser compatibility
  • Accessibility standards

🎯 CONCLUSIONE

Questa guida fornisce tutto il necessario per sviluppare moduli professionali per NetGescon. Il sistema modulare è progettato per essere:

  • Scalabile: Supporta crescita dell'applicazione
  • Sicuro: Permessi granulari e validazione
  • Performante: Cache e ottimizzazioni database
  • Manutenibile: Codice pulito e documentato
  • Commerciale: Modello business integrato

Utilizzando questa specifica, puoi sviluppare moduli che si integrano perfettamente con NetGescon e possono essere distribuiti come prodotti commerciali.


📞 CONTATTI SVILUPPO