++ Primo Caricamento

This commit is contained in:
2026-03-30 19:15:13 +02:00
commit 663a68d59b
47 changed files with 3561 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
<?php
namespace App\Http\Controllers;
// ─────────────────────────────────────────────────────────────────────────────
// Controller base — tutti i controller del progetto estendono questa classe
//
// In Laravel 11 il controller base è praticamente vuoto: serve solo come
// punto di estensione comune, nel caso in cui in futuro tu voglia aggiungere
// metodi o middleware condivisi tra tutti i controller.
// ─────────────────────────────────────────────────────────────────────────────
abstract class Controller
{
//
}

View File

@@ -0,0 +1,146 @@
<?php
namespace App\Http\Controllers;
// ─────────────────────────────────────────────────────────────────────────────
// CustomerController — CRUD completo per la gestione clienti
//
// CRUD = Create, Read, Update, Delete
//
// Laravel usa la convenzione "Resource Controller": 7 metodi standard
// che corrispondono alle operazioni CRUD via HTTP:
//
// index() → GET /customers → lista clienti
// create() → GET /customers/create → form nuovo cliente
// store() → POST /customers → salva nuovo cliente
// show() → GET /customers/{id} → dettaglio cliente
// edit() → GET /customers/{id}/edit → form modifica
// update() → PUT /customers/{id} → aggiorna cliente
// destroy() → DELETE /customers/{id} → elimina cliente
// ─────────────────────────────────────────────────────────────────────────────
use App\Models\Customer;
use App\Services\SettingService;
use Illuminate\Http\Request;
class CustomerController extends Controller
{
public function __construct(
private SettingService $settings
) {}
// ─── Lista clienti ─────────────────────────────────────────────────────
public function index(Request $request)
{
// Recupera il numero di elementi per pagina dalle impostazioni dinamiche
$perPage = $this->settings->get('items_per_page', 15);
// Costruisce la query con filtri opzionali dalla URL
// Es: /customers?search=mario&type=privato&status=attivo
$query = Customer::query();
if ($search = $request->input('search')) {
$query->search($search); // Usa lo scope definito nel Model
}
if ($type = $request->input('type')) {
$query->byType($type);
}
if ($status = $request->input('status')) {
$query->where('status', $status);
}
// paginate() divide i risultati in pagine e genera automaticamente
// i link prev/next, passati alla view come $customers->links()
$customers = $query->latest()->paginate($perPage)->withQueryString();
return view('customers.index', compact('customers'));
}
// ─── Form nuovo cliente ────────────────────────────────────────────────
public function create()
{
return view('customers.create');
}
// ─── Salva nuovo cliente ───────────────────────────────────────────────
public function store(Request $request)
{
// Validazione: se fallisce, Laravel reindirizza automaticamente
// al form precedente con gli errori e i valori inseriti.
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:customers,email',
'phone' => 'nullable|string|max:50',
'city' => 'nullable|string|max:100',
'address' => 'nullable|string|max:255',
'vat_number' => 'nullable|string|max:20',
'fiscal_code' => 'nullable|string|max:20',
'type' => 'required|in:privato,azienda',
'status' => 'required|in:attivo,inattivo,prospect',
'notes' => 'nullable|string',
'contract_value' => 'nullable|numeric|min:0',
]);
$customer = Customer::create($validated);
// redirect() rimanda il browser a un'altra pagina
// with('success', ...) aggiunge un messaggio flash (mostrato una volta)
return redirect()
->route('customers.show', $customer)
->with('success', "Cliente \"{$customer->name}\" creato con successo.");
}
// ─── Dettaglio cliente ─────────────────────────────────────────────────
// Route Model Binding: Laravel trova automaticamente il Customer dall'URL
// Non serve scrivere: $customer = Customer::findOrFail($id);
public function show(Customer $customer)
{
return view('customers.show', compact('customer'));
}
// ─── Form modifica cliente ─────────────────────────────────────────────
public function edit(Customer $customer)
{
return view('customers.edit', compact('customer'));
}
// ─── Aggiorna cliente ─────────────────────────────────────────────────
public function update(Request $request, Customer $customer)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
// unique: ignora il record corrente (altrimenti failerebbe sempre)
'email' => "required|email|unique:customers,email,{$customer->id}",
'phone' => 'nullable|string|max:50',
'city' => 'nullable|string|max:100',
'address' => 'nullable|string|max:255',
'vat_number' => 'nullable|string|max:20',
'fiscal_code' => 'nullable|string|max:20',
'type' => 'required|in:privato,azienda',
'status' => 'required|in:attivo,inattivo,prospect',
'notes' => 'nullable|string',
'contract_value' => 'nullable|numeric|min:0',
]);
$customer->update($validated);
return redirect()
->route('customers.show', $customer)
->with('success', "Cliente \"{$customer->name}\" aggiornato.");
}
// ─── Elimina cliente (soft delete) ────────────────────────────────────
// Grazie a SoftDeletes nel Model, il record non viene cancellato:
// viene impostato `deleted_at` e non compare più nelle query normali.
public function destroy(Customer $customer)
{
$name = $customer->name;
$customer->delete();
return redirect()
->route('customers.index')
->with('success', "Cliente \"{$name}\" eliminato.");
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace App\Http\Controllers;
// ─────────────────────────────────────────────────────────────────────────────
// DashboardController — gestisce la pagina principale del portale
//
// Un Controller riceve la richiesta HTTP, recupera i dati necessari
// e li passa alla View (template Blade) per la visualizzazione.
//
// Flusso di una richiesta:
// Browser → routes/web.php → Controller → View → risposta HTML
// ─────────────────────────────────────────────────────────────────────────────
use App\Models\Customer;
use App\Services\SettingService;
class DashboardController extends Controller
{
// Dependency Injection: Laravel istanzia SettingService automaticamente
public function __construct(
private SettingService $settings
) {}
// Corrisponde alla route: GET /
public function index()
{
// Statistiche aggregate sui clienti
$stats = [
'total' => Customer::count(),
'active' => Customer::active()->count(),
'prospect' => Customer::where('status', 'prospect')->count(),
'inactive' => Customer::where('status', 'inattivo')->count(),
// Somma contratti clienti attivi
'total_contract_value' => Customer::active()->sum('contract_value'),
];
// Ultimi 5 clienti aggiunti (per "Attività recente")
$recentCustomers = Customer::latest()->take(5)->get();
// Clienti per città (top 5 - per widget grafico)
$byCity = Customer::selectRaw('city, count(*) as total')
->groupBy('city')
->orderByDesc('total')
->take(5)
->pluck('total', 'city')
->toArray();
// Messaggio di benvenuto dinamico (da impostazioni)
$welcomeMessage = $this->settings->get('welcome_message');
// compact() è una shorthand PHP per creare un array associativo
// equivalente a: ['stats' => $stats, 'recentCustomers' => $recentCustomers, ...]
return view('dashboard', compact(
'stats',
'recentCustomers',
'byCity',
'welcomeMessage'
));
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace App\Http\Controllers;
// ─────────────────────────────────────────────────────────────────────────────
// SettingController — pannello impostazioni dinamiche
//
// Permette all'admin di modificare le impostazioni dell'applicazione
// senza toccare il codice o riavviare i container.
// ─────────────────────────────────────────────────────────────────────────────
use App\Services\SettingService;
use Illuminate\Http\Request;
class SettingController extends Controller
{
public function __construct(
private SettingService $settings
) {}
// ─── Mostra il pannello impostazioni ──────────────────────────────────
public function index()
{
// Tutte le impostazioni correnti dal servizio
$current = $this->settings->all();
// Configurazione per la UI (label, gruppi, tipi)
$config = config('settings');
return view('settings.index', compact('current', 'config'));
}
// ─── Salva le modifiche ────────────────────────────────────────────────
public function update(Request $request)
{
$types = config('settings.types', []);
$defaults = config('settings.defaults', []);
// Costruisce le regole di validazione dinamicamente
// in base ai tipi definiti in config/settings.php
$rules = [];
foreach ($defaults as $key => $default) {
$type = $types[$key] ?? 'string';
$rules[$key] = match ($type) {
'integer' => 'nullable|integer|min:1',
'boolean' => 'nullable|boolean',
'string' => 'nullable|string|max:255',
'text' => 'nullable|string',
default => 'nullable|string',
};
}
$validated = $request->validate($rules);
// I checkbox non inviati dal form hanno valore null → false per i boolean
foreach ($types as $key => $type) {
if ($type === 'boolean' && ! array_key_exists($key, $validated)) {
$validated[$key] = false;
}
}
$this->settings->setMany($validated);
return redirect()
->route('settings.index')
->with('success', 'Impostazioni salvate con successo. La cache è stata aggiornata.');
}
}