feat(rubrica): inline contact editing without modal

This commit is contained in:
michele 2026-03-12 17:12:14 +00:00
parent 84215abe73
commit e0e3c7d16d
3 changed files with 213 additions and 0 deletions

View File

@ -22,6 +22,7 @@
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class RubricaUniversaleScheda extends Page class RubricaUniversaleScheda extends Page
{ {
@ -65,6 +66,11 @@ class RubricaUniversaleScheda extends Page
/** @var array<int, int> */ /** @var array<int, int> */
public array $adminStabileIds = []; public array $adminStabileIds = [];
public bool $isInlineEditing = false;
/** @var array<string,mixed> */
public array $inlineForm = [];
public function mount(int | string $record): void public function mount(int | string $record): void
{ {
$user = Auth::user(); $user = Auth::user();
@ -232,6 +238,77 @@ public function mount(int | string $record): void
'principale' => (bool) ($c->is_principale ?? false), 'principale' => (bool) ($c->is_principale ?? false),
]) ])
->all(); ->all();
$this->fillInlineForm();
}
public function startInlineEdit(): void
{
$this->fillInlineForm();
$this->isInlineEditing = true;
}
public function cancelInlineEdit(): void
{
$this->fillInlineForm();
$this->isInlineEditing = false;
}
public function saveInlineEdit(): void
{
$data = validator($this->inlineForm, [
'nome' => ['nullable', 'string', 'max:255'],
'cognome' => ['nullable', 'string', 'max:255'],
'ragione_sociale' => ['nullable', 'string', 'max:255'],
'tipo_contatto' => ['nullable', 'string', 'max:120'],
'categoria' => ['nullable', 'string', 'max:120'],
'codice_fiscale' => ['nullable', 'string', 'max:32'],
'partita_iva' => ['nullable', 'string', 'max:32'],
'email' => ['nullable', 'string', 'max:255'],
'pec' => ['nullable', 'string', 'max:255'],
'telefono_ufficio' => ['nullable', 'string', 'max:64'],
'telefono_cellulare' => ['nullable', 'string', 'max:64'],
'telefono_casa' => ['nullable', 'string', 'max:64'],
'indirizzo_completo' => ['nullable', 'string', 'max:255'],
'tipo_utenza_call' => ['nullable', 'string', 'max:120'],
'riferimento_stabile' => ['nullable', 'string', 'max:255'],
'riferimento_unita' => ['nullable', 'string', 'max:255'],
'note' => ['nullable', 'string', 'max:5000'],
'note_segreteria' => ['nullable', 'string', 'max:5000'],
])->validate();
$this->rubrica->fill([
'nome' => $this->cleanNullable($data['nome'] ?? null),
'cognome' => $this->cleanNullable($data['cognome'] ?? null),
'ragione_sociale' => $this->cleanNullable($data['ragione_sociale'] ?? null),
'tipo_contatto' => $this->cleanNullable($data['tipo_contatto'] ?? null),
'categoria' => $this->cleanNullable($data['categoria'] ?? null),
'codice_fiscale' => $this->cleanNullable($data['codice_fiscale'] ?? null),
'partita_iva' => $this->cleanNullable($data['partita_iva'] ?? null),
'email' => $this->cleanNullable($data['email'] ?? null),
'pec' => $this->cleanNullable($data['pec'] ?? null),
'telefono_ufficio' => $this->cleanNullable($data['telefono_ufficio'] ?? null),
'telefono_cellulare' => $this->cleanNullable($data['telefono_cellulare'] ?? null),
'telefono_casa' => $this->cleanNullable($data['telefono_casa'] ?? null),
'indirizzo_completo' => $this->cleanNullable($data['indirizzo_completo'] ?? null),
'tipo_utenza_call' => $this->cleanNullable($data['tipo_utenza_call'] ?? null),
'riferimento_stabile' => $this->cleanNullable($data['riferimento_stabile'] ?? null),
'riferimento_unita' => $this->cleanNullable($data['riferimento_unita'] ?? null),
'note' => $this->cleanNullable($data['note'] ?? null),
'note_segreteria' => $this->cleanNullable($data['note_segreteria'] ?? null),
'data_ultima_modifica' => now()->toDateString(),
'modificato_da' => Auth::id(),
]);
$this->rubrica->save();
$this->mount((int) $this->rubrica->id);
$this->isInlineEditing = false;
Notification::make()
->title('Contatto aggiornato')
->success()
->send();
} }
private function initArchivioNavigation(): void private function initArchivioNavigation(): void
@ -445,6 +522,13 @@ protected function getHeaderActions(): array
Action::make('contatti') Action::make('contatti')
->label('Contatti') ->label('Contatti')
->icon('heroicon-o-phone') ->icon('heroicon-o-phone')
->action(function (): void {
$this->startInlineEdit();
}),
Action::make('contatti_avanzati')
->label('Contatti avanzati')
->icon('heroicon-o-rectangle-stack')
->modalWidth('7xl') ->modalWidth('7xl')
->form([ ->form([
Repeater::make('canali') Repeater::make('canali')
@ -799,6 +883,41 @@ protected function getHeaderActions(): array
]; ];
} }
private function fillInlineForm(): void
{
$this->inlineForm = [
'nome' => (string) ($this->rubrica->nome ?? ''),
'cognome' => (string) ($this->rubrica->cognome ?? ''),
'ragione_sociale' => (string) ($this->rubrica->ragione_sociale ?? ''),
'tipo_contatto' => (string) ($this->rubrica->tipo_contatto ?? ''),
'categoria' => (string) ($this->rubrica->categoria ?? ''),
'codice_fiscale' => (string) ($this->rubrica->codice_fiscale ?? ''),
'partita_iva' => (string) ($this->rubrica->partita_iva ?? ''),
'email' => (string) ($this->rubrica->email ?? ''),
'pec' => (string) ($this->rubrica->pec ?? ''),
'telefono_ufficio' => (string) ($this->rubrica->telefono_ufficio ?? ''),
'telefono_cellulare' => (string) ($this->rubrica->telefono_cellulare ?? ''),
'telefono_casa' => (string) ($this->rubrica->telefono_casa ?? ''),
'indirizzo_completo' => (string) ($this->rubrica->indirizzo_completo ?? ''),
'tipo_utenza_call' => (string) ($this->rubrica->tipo_utenza_call ?? ''),
'riferimento_stabile' => (string) ($this->rubrica->riferimento_stabile ?? ''),
'riferimento_unita' => (string) ($this->rubrica->riferimento_unita ?? ''),
'note' => (string) ($this->rubrica->note ?? ''),
'note_segreteria' => (string) ($this->rubrica->note_segreteria ?? ''),
];
}
private function cleanNullable(mixed $value): mixed
{
if (! is_string($value)) {
return $value;
}
$trim = trim($value);
return $trim === '' ? null : $trim;
}
public function getUrlUnita(?int $unitaId): ?string public function getUrlUnita(?int $unitaId): ?string
{ {
if (! $unitaId) { if (! $unitaId) {

View File

@ -26,3 +26,4 @@ # Modifiche NetGescon
| 12/03/2026 | 0.8.x-dev | Supporto / Modifiche / Update | Pagina `admin-filament/supporto/modifiche` resa compatta (font piccolo), aggiunta tabella ultimi commit con descrizione e pulsante per eseguire `netgescon:update --apply` con esito/output e versione corrente visibile | [U] | | 12/03/2026 | 0.8.x-dev | Supporto / Modifiche / Update | Pagina `admin-filament/supporto/modifiche` resa compatta (font piccolo), aggiunta tabella ultimi commit con descrizione e pulsante per eseguire `netgescon:update --apply` con esito/output e versione corrente visibile | [U] |
| 12/03/2026 | 0.8.x-dev | Fornitori / Ticket Operativi | Estesa area fornitore con elenco pratiche operativo (ingresso/contatto/telefono click-to-call/problema/stato), scheda a TAB con allegati segnalazione, storico interventi correlati, ricambi, note rapide e flusso chiusura/proforma; aggiunto sollecito rapido verso studio | [U] | | 12/03/2026 | 0.8.x-dev | Fornitori / Ticket Operativi | Estesa area fornitore con elenco pratiche operativo (ingresso/contatto/telefono click-to-call/problema/stato), scheda a TAB con allegati segnalazione, storico interventi correlati, ricambi, note rapide e flusso chiusura/proforma; aggiunto sollecito rapido verso studio | [U] |
| 12/03/2026 | 0.8.x-dev | Supporto Ticket / TAB Fornitori | Sezione "Fornitori attivi su ticket aperti" spostata in Tab 3 dedicata in `supporto/ticket-gestione` per allineare il flusso operativo elenco -> scheda -> fornitori | [U] | | 12/03/2026 | 0.8.x-dev | Supporto Ticket / TAB Fornitori | Sezione "Fornitori attivi su ticket aperti" spostata in Tab 3 dedicata in `supporto/ticket-gestione` per allineare il flusso operativo elenco -> scheda -> fornitori | [U] |
| 12/03/2026 | 0.8.x-dev | Rubrica Universale / Scheda | Introdotta modifica contatto inline su `gescon/anagrafica/rubrica/{record}` (senza modal): campi principali editabili direttamente nella blade con azioni Salva/Annulla e persistenza immediata | [U] |

View File

@ -1,4 +1,97 @@
<x-filament-panels::page> <x-filament-panels::page>
@if($isInlineEditing)
<x-filament::section>
<x-slot name="heading">Modifica Contatto Inline</x-slot>
<x-slot name="description">Modifica i campi direttamente in pagina, senza modal.</x-slot>
<div class="grid grid-cols-1 gap-3 md:grid-cols-3">
<label class="text-sm">
<span class="mb-1 block text-gray-600">Nome</span>
<input type="text" wire:model.defer="inlineForm.nome" class="w-full rounded-md border-gray-300 text-sm" />
</label>
<label class="text-sm">
<span class="mb-1 block text-gray-600">Cognome</span>
<input type="text" wire:model.defer="inlineForm.cognome" class="w-full rounded-md border-gray-300 text-sm" />
</label>
<label class="text-sm">
<span class="mb-1 block text-gray-600">Ragione sociale</span>
<input type="text" wire:model.defer="inlineForm.ragione_sociale" class="w-full rounded-md border-gray-300 text-sm" />
</label>
<label class="text-sm">
<span class="mb-1 block text-gray-600">Tipo contatto</span>
<input type="text" wire:model.defer="inlineForm.tipo_contatto" class="w-full rounded-md border-gray-300 text-sm" />
</label>
<label class="text-sm">
<span class="mb-1 block text-gray-600">Categoria</span>
<input type="text" wire:model.defer="inlineForm.categoria" class="w-full rounded-md border-gray-300 text-sm" />
</label>
<label class="text-sm">
<span class="mb-1 block text-gray-600">Codice fiscale</span>
<input type="text" wire:model.defer="inlineForm.codice_fiscale" class="w-full rounded-md border-gray-300 text-sm" />
</label>
<label class="text-sm">
<span class="mb-1 block text-gray-600">Partita IVA</span>
<input type="text" wire:model.defer="inlineForm.partita_iva" class="w-full rounded-md border-gray-300 text-sm" />
</label>
<label class="text-sm">
<span class="mb-1 block text-gray-600">Email</span>
<input type="text" wire:model.defer="inlineForm.email" class="w-full rounded-md border-gray-300 text-sm" />
</label>
<label class="text-sm">
<span class="mb-1 block text-gray-600">PEC</span>
<input type="text" wire:model.defer="inlineForm.pec" class="w-full rounded-md border-gray-300 text-sm" />
</label>
<label class="text-sm">
<span class="mb-1 block text-gray-600">Telefono ufficio</span>
<input type="text" wire:model.defer="inlineForm.telefono_ufficio" class="w-full rounded-md border-gray-300 text-sm" />
</label>
<label class="text-sm">
<span class="mb-1 block text-gray-600">Cellulare</span>
<input type="text" wire:model.defer="inlineForm.telefono_cellulare" class="w-full rounded-md border-gray-300 text-sm" />
</label>
<label class="text-sm">
<span class="mb-1 block text-gray-600">Telefono casa</span>
<input type="text" wire:model.defer="inlineForm.telefono_casa" class="w-full rounded-md border-gray-300 text-sm" />
</label>
<label class="text-sm md:col-span-3">
<span class="mb-1 block text-gray-600">Indirizzo completo</span>
<input type="text" wire:model.defer="inlineForm.indirizzo_completo" class="w-full rounded-md border-gray-300 text-sm" />
</label>
<label class="text-sm">
<span class="mb-1 block text-gray-600">Profilo chiamante</span>
<input type="text" wire:model.defer="inlineForm.tipo_utenza_call" class="w-full rounded-md border-gray-300 text-sm" />
</label>
<label class="text-sm">
<span class="mb-1 block text-gray-600">Rif. stabile</span>
<input type="text" wire:model.defer="inlineForm.riferimento_stabile" class="w-full rounded-md border-gray-300 text-sm" />
</label>
<label class="text-sm">
<span class="mb-1 block text-gray-600">Rif. unità</span>
<input type="text" wire:model.defer="inlineForm.riferimento_unita" class="w-full rounded-md border-gray-300 text-sm" />
</label>
<label class="text-sm md:col-span-3">
<span class="mb-1 block text-gray-600">Note</span>
<textarea wire:model.defer="inlineForm.note" rows="3" class="w-full rounded-md border-gray-300 text-sm"></textarea>
</label>
<label class="text-sm md:col-span-3">
<span class="mb-1 block text-gray-600">Note segreteria</span>
<textarea wire:model.defer="inlineForm.note_segreteria" rows="3" class="w-full rounded-md border-gray-300 text-sm"></textarea>
</label>
</div>
<div class="mt-4 flex flex-wrap gap-2">
<x-filament::button type="button" wire:click="saveInlineEdit">Salva modifiche</x-filament::button>
<x-filament::button type="button" color="gray" wire:click="cancelInlineEdit">Annulla</x-filament::button>
</div>
</x-filament::section>
@endif
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<div class="flex items-center justify-between gap-3"> <div class="flex items-center justify-between gap-3">
@php @php