diff --git a/backoffice/app/Filament/Resources/ClienteResource.php b/backoffice/app/Filament/Resources/ClienteResource.php index 8d1db14..954f14a 100644 --- a/backoffice/app/Filament/Resources/ClienteResource.php +++ b/backoffice/app/Filament/Resources/ClienteResource.php @@ -97,10 +97,15 @@ class ClienteResource extends Resource ->actions([ Tables\Actions\ViewAction::make(), Tables\Actions\EditAction::make(), + Tables\Actions\DeleteAction::make() + ->modalDescription('Verrà eliminato definitivamente il cliente insieme a tutti i suoi preventivi e ai file associati (PDF, immagini)') + ->successRedirectUrl(static::getUrl('index')), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ - Tables\Actions\DeleteBulkAction::make(), + Tables\Actions\DeleteBulkAction::make() + ->modalDescription('I clienti selezionati verranno eliminati definitivamente insieme a tutti i loro preventivi e ai file associati (PDF, immagini)') + ->successRedirectUrl(static::getUrl('index')), ]), ]); } diff --git a/backoffice/app/Filament/Resources/ClienteResource/Pages/EditCliente.php b/backoffice/app/Filament/Resources/ClienteResource/Pages/EditCliente.php index 69298da..a3bf81c 100644 --- a/backoffice/app/Filament/Resources/ClienteResource/Pages/EditCliente.php +++ b/backoffice/app/Filament/Resources/ClienteResource/Pages/EditCliente.php @@ -17,7 +17,8 @@ class EditCliente extends EditRecord { return [ Actions\ViewAction::make(), - Actions\DeleteAction::make(), + Actions\DeleteAction::make() + ->modalDescription('Verrà eliminato definitivamente il cliente insieme a tutti i suoi preventivi e ai file associati (PDF, immagini).'), ]; } diff --git a/backoffice/app/Filament/Resources/ClienteResource/Pages/ViewCliente.php b/backoffice/app/Filament/Resources/ClienteResource/Pages/ViewCliente.php index bf5b73c..4d4c987 100644 --- a/backoffice/app/Filament/Resources/ClienteResource/Pages/ViewCliente.php +++ b/backoffice/app/Filament/Resources/ClienteResource/Pages/ViewCliente.php @@ -14,6 +14,8 @@ class ViewCliente extends ViewRecord { return [ Actions\EditAction::make(), + Actions\DeleteAction::make() + ->modalDescription('Verrà eliminato definitivamente il cliente insieme a tutti i suoi preventivi e ai file associati (PDF, immagini).'), ]; } } diff --git a/backoffice/app/Filament/Resources/PreventivoResource.php b/backoffice/app/Filament/Resources/PreventivoResource.php index 19ae337..51c4745 100644 --- a/backoffice/app/Filament/Resources/PreventivoResource.php +++ b/backoffice/app/Filament/Resources/PreventivoResource.php @@ -4,6 +4,7 @@ namespace App\Filament\Resources; use App\Filament\Resources\PreventivoResource\Pages; use App\Filament\Resources\PreventivoResource\RelationManagers\ItemsRelationManager; +use App\Models\Cliente; use App\Models\Preventivo; use App\Support\PreventivoPdf; use Filament\Forms; @@ -44,7 +45,13 @@ class PreventivoResource extends Resource Forms\Components\Select::make('cliente_id') ->label('Cliente') ->relationship('cliente', 'nome') - ->searchable() + ->getOptionLabelFromRecordUsing(function (Cliente $record): string { + $nome = trim("{$record->nome} {$record->cognome}"); + $parti = array_filter([$record->ragione_sociale, $nome]); + + return $parti ? implode(' — ', $parti) : (string) $record->username; + }) + ->searchable(['ragione_sociale', 'nome', 'cognome']) ->preload(), ]); } @@ -58,9 +65,14 @@ class PreventivoResource extends Resource ->label('N.') ->sortable() ->placeholder('—'), - Tables\Columns\TextColumn::make('cliente.nominativo') + Tables\Columns\TextColumn::make('cliente.ragione_sociale') + ->label('Ragione sociale') + ->searchable() + ->toggleable() + ->placeholder('—'), + Tables\Columns\TextColumn::make('cliente.nome_completo') ->label('Cliente') - ->searchable(['nome', 'cognome', 'ragione_sociale']) + ->searchable(['nome', 'cognome']) ->placeholder('—'), Tables\Columns\TextColumn::make('cliente.email') ->label('Email') @@ -116,7 +128,8 @@ class PreventivoResource extends Resource ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ - Tables\Actions\DeleteBulkAction::make(), + Tables\Actions\DeleteBulkAction::make() + ->successRedirectUrl(static::getUrl('index')), ]), ]); } diff --git a/backoffice/app/Http/Controllers/Api/CarrelloController.php b/backoffice/app/Http/Controllers/Api/CarrelloController.php index 31a953c..49395db 100644 --- a/backoffice/app/Http/Controllers/Api/CarrelloController.php +++ b/backoffice/app/Http/Controllers/Api/CarrelloController.php @@ -12,6 +12,7 @@ use App\Support\ConfigurazioneFormatter; use App\Support\PreventivoPdf; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Storage; @@ -122,8 +123,21 @@ class CarrelloController extends Controller 'note' => ['nullable', 'string', 'max:2000'], ]); - DB::transaction(function () use ($preventivo, $dati) { - $cliente = Cliente::create($dati); + // Se la richiesta arriva da un cliente loggato nel configuratore (token Sanctum + // valido), il preventivo va agganciato al suo record esistente — aggiornando + // l'anagrafica con i dati confermati nel form — e NON a un nuovo cliente. + // Il guard Sanctum legge il token Bearer direttamente dalla richiesta: + // per i guest (o token assenti/scaduti/non validi) restituisce null. + $clienteLoggato = Auth::guard('sanctum')->user(); + + DB::transaction(function () use ($preventivo, $dati, $clienteLoggato) { + $cliente = $clienteLoggato; + + if ($cliente) { + $cliente->update($dati); + } else { + $cliente = Cliente::create($dati); + } $preventivo->cliente_id = $cliente->id; $preventivo->stato = 'inviato'; diff --git a/backoffice/app/Models/Cliente.php b/backoffice/app/Models/Cliente.php index deb452d..bd73e0d 100644 --- a/backoffice/app/Models/Cliente.php +++ b/backoffice/app/Models/Cliente.php @@ -31,6 +31,16 @@ class Cliente extends Model return $this->hasMany(Preventivo::class); } + protected static function booted(): void + { + static::deleting(function (Cliente $cliente): void { + // Elimina di conseguenza i preventivi del cliente (con i relativi file) + // e revoca i token API usati per l'accesso al configuratore. + $cliente->preventivi()->get()->each->delete(); + $cliente->tokens()->delete(); + }); + } + public function getNominativoAttribute(): string { if ($this->ragione_sociale) { @@ -41,4 +51,11 @@ class Cliente extends Model return $nome !== '' ? $nome : $this->username; } + + public function getNomeCompletoAttribute(): string + { + $nome = trim("{$this->nome} {$this->cognome}"); + + return $nome !== '' ? $nome : $this->username; + } } diff --git a/backoffice/app/Models/Preventivo.php b/backoffice/app/Models/Preventivo.php index 026249c..d2deb2d 100644 --- a/backoffice/app/Models/Preventivo.php +++ b/backoffice/app/Models/Preventivo.php @@ -5,6 +5,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Support\Facades\Storage; class Preventivo extends Model { @@ -26,6 +27,24 @@ class Preventivo extends Model 'chiuso' => 'Chiuso', ]; + protected static function booted(): void + { + static::deleting(function (Preventivo $preventivo): void { + // Elimina immagini degli item (disk 'public') e PDF (disk 'local') prima della cancellazione. + foreach ($preventivo->items()->get() as $item) { + if ($item->immagine_path && Storage::disk('public')->exists($item->immagine_path)) { + Storage::disk('public')->delete($item->immagine_path); + } + + $item->delete(); + } + + if ($preventivo->pdf_path && Storage::disk('local')->exists($preventivo->pdf_path)) { + Storage::disk('local')->delete($preventivo->pdf_path); + } + }); + } + public function cliente(): BelongsTo { return $this->belongsTo(Cliente::class); diff --git a/backoffice/cliente/dist/index.html b/backoffice/cliente/dist/index.html index 95e030f..bb7cc59 100644 --- a/backoffice/cliente/dist/index.html +++ b/backoffice/cliente/dist/index.html @@ -5,7 +5,7 @@