fix(ui-clean): strip redundant top Filament header actions from Rubrica and Stabile pages, and set Elenco href to anagrafica-unica (task-d095b0e23b)

This commit is contained in:
michele 2026-08-28 17:12:59 +02:00
parent ac1f2353df
commit 47badb7b09
3 changed files with 3 additions and 578 deletions

View File

@ -1093,488 +1093,7 @@ public function getPerPageUrl(int $perPage): string
protected function getHeaderActions(): array
{
return [
Action::make('titolo')
->label('Titolo')
->icon('heroicon-o-tag')
->form([
Select::make('titolo_id')
->label('Titolo')
->native(false)
->searchable()
->options(fn() => $this->getTitoliOptions()),
])
->fillForm(fn(): array=> [
'titolo_id' => $this->rubrica->titolo_id ? (int) $this->rubrica->titolo_id : null,
])
->action(function (array $data): void {
$titoloId = isset($data['titolo_id']) && is_numeric($data['titolo_id'])
? (int) $data['titolo_id']
: null;
$this->rubrica->titolo_id = $titoloId;
$this->rubrica->data_ultima_modifica = now()->toDateString();
$this->rubrica->modificato_da = Auth::id();
$this->rubrica->save();
$this->mount((int) $this->rubrica->id);
Notification::make()
->title('Titolo aggiornato')
->success()
->send();
}),
Action::make('contatti')
->label('Modifica anagrafica')
->icon('heroicon-o-phone')
->action(function (): void {
$this->startInlineEdit();
}),
Action::make('aggiorna_vuoti_legacy')
->label('Aggiorna vuoti da legacy')
->icon('heroicon-o-arrow-path')
->requiresConfirmation()
->modalHeading('Aggiorna campi vuoti da legacy')
->modalDescription('Compila solo i campi mancanti usando gescon_import.condomin. I dati gia inseriti manualmente non vengono sovrascritti.')
->action(function (): void {
try {
Artisan::call('gescon:qa-rubrica-vuoti', [
'--id' => [(string) $this->rubrica->id],
'--apply' => true,
]);
$output = trim(Artisan::output());
$this->rubrica->refresh();
$this->mount((int) $this->rubrica->id);
Notification::make()
->title('Aggiornamento legacy completato')
->body(Str::limit($output, 240))
->success()
->send();
} catch (\Throwable $e) {
Notification::make()
->title('Aggiornamento legacy fallito')
->body($e->getMessage())
->danger()
->send();
}
}),
Action::make('abilita_accesso_netgescon')
->label('Abilita accesso')
->icon('heroicon-o-key')
->requiresConfirmation()
->modalHeading('Abilita accesso NetGescon')
->modalDescription('Crea o collega un utente per questo nominativo e invia il link impostazione password all\'email disponibile in rubrica.')
->action(function (): void {
$this->abilitaAccessoRubrica();
}),
Action::make('email_multiple')
->label('Email aggiuntive')
->icon('heroicon-o-envelope')
->modalWidth('4xl')
->form([
Repeater::make('emails')
->label('Email aggiuntive')
->defaultItems(0)
->reorderable(true)
->schema([
Hidden::make('id'),
TextInput::make('email')
->label('Email')
->email()
->required()
->maxLength(255)
->columnSpan(6),
Select::make('tipo_email')
->label('Tipo')
->native(false)
->options([
'secondaria' => 'Secondaria',
'lavoro' => 'Lavoro',
'personale' => 'Personale',
'pec' => 'PEC',
'altro' => 'Altro',
])
->default('secondaria')
->columnSpan(4),
Toggle::make('attiva')
->label('Attiva')
->default(true)
->columnSpan(2),
])
->columns(12),
])
->fillForm(fn(): array=> ['emails' => $this->getAdditionalEmailsFormRows()])
->action(function (array $data): void {
$this->saveAdditionalEmails($data['emails'] ?? []);
$this->mount((int) $this->rubrica->id);
Notification::make()
->title('Email aggiuntive aggiornate')
->success()
->send();
}),
Action::make('contatti_avanzati')
->label('Contatti avanzati')
->icon('heroicon-o-rectangle-stack')
->visible(fn(): bool => Schema::hasTable('rubrica_contatti_canali'))
->modalWidth('7xl')
->form([
Repeater::make('canali')
->label('Contatti aggiuntivi')
->defaultItems(0)
->reorderable(true)
->schema([
Hidden::make('id'),
Select::make('tipo')
->label('Tipo')
->options([
'telefono' => 'Telefono',
'cellulare' => 'Cellulare',
'email' => 'Email',
'pec' => 'PEC',
'fax' => 'Fax',
'sito_web' => 'Sito web',
'altro' => 'Altro',
])
->native(false)
->required()
->columnSpan(2),
TextInput::make('etichetta')
->label('Etichetta')
->maxLength(50)
->columnSpan(2),
TextInput::make('valore')
->label('Valore')
->required()
->maxLength(255)
->columnSpan(4),
Toggle::make('is_principale')
->label('Principale')
->default(false)
->columnSpan(2),
Select::make('stabile_id')
->label('Stabile (opzionale)')
->native(false)
->searchable()
->options(fn() => $this->getStabiliOptions())
->reactive()
->afterStateUpdated(fn($set) => $set('unita_immobiliare_id', null))
->columnSpan(3),
Select::make('unita_immobiliare_id')
->label('Unità (opzionale)')
->native(false)
->searchable()
->options(fn($get) => $this->getUnitaOptions($get('stabile_id')))
->columnSpan(3),
DatePicker::make('data_inizio')
->label('Data inizio')
->columnSpan(2),
DatePicker::make('data_fine')
->label('Data fine')
->columnSpan(2),
TextInput::make('note')
->label('Note')
->maxLength(255)
->columnSpan(12),
])
->columns(12),
])
->fillForm(function (): array {
$canali = RubricaContattoCanale::query()
->where('rubrica_id', (int) $this->rubrica->id)
->orderByDesc('is_principale')
->orderByDesc('data_inizio')
->orderByDesc('id')
->limit(200)
->get();
return [
'canali' => $canali->map(fn(RubricaContattoCanale $c) => [
'id' => (int) $c->id,
'tipo' => (string) ($c->tipo ?? 'altro'),
'etichetta' => (string) ($c->etichetta ?? ''),
'valore' => (string) ($c->valore ?? ''),
'is_principale' => (bool) ($c->is_principale ?? false),
'stabile_id' => $c->stabile_id ? (int) $c->stabile_id : null,
'unita_immobiliare_id' => $c->unita_immobiliare_id ? (int) $c->unita_immobiliare_id : null,
'data_inizio' => $c->data_inizio?->format('Y-m-d'),
'data_fine' => $c->data_fine?->format('Y-m-d'),
'note' => (string) (($c->meta['note'] ?? null) ?: ''),
])->all(),
];
})
->action(function (array $data): void {
/** @var array<int, array<string, mixed>> $rows */
$rows = $data['canali'] ?? [];
$rows = is_array($rows) ? array_values($rows) : [];
DB::transaction(function () use ($rows): void {
$keepIds = [];
foreach ($rows as $row) {
$id = (int) ($row['id'] ?? 0);
$tipo = (string) ($row['tipo'] ?? 'altro');
if ($tipo === '') {
$tipo = 'altro';
}
$attrs = [
'rubrica_id' => (int) $this->rubrica->id,
'tipo' => $tipo,
'etichetta' => ! empty($row['etichetta']) ? (string) $row['etichetta'] : null,
'valore' => ! empty($row['valore']) ? (string) $row['valore'] : null,
'is_principale' => (bool) ($row['is_principale'] ?? false),
'stabile_id' => ! empty($row['stabile_id']) ? (int) $row['stabile_id'] : null,
'unita_immobiliare_id' => ! empty($row['unita_immobiliare_id']) ? (int) $row['unita_immobiliare_id'] : null,
'data_inizio' => $row['data_inizio'] ?? null,
'data_fine' => $row['data_fine'] ?? null,
'meta' => [
'note' => ! empty($row['note']) ? (string) $row['note'] : null,
],
];
if ($id > 0) {
$existing = RubricaContattoCanale::query()
->where('id', $id)
->where('rubrica_id', (int) $this->rubrica->id)
->first();
if ($existing) {
$existing->fill($attrs);
$existing->save();
$keepIds[] = (int) $existing->id;
continue;
}
}
$created = RubricaContattoCanale::query()->create($attrs);
$keepIds[] = (int) $created->id;
}
$deleteQuery = RubricaContattoCanale::query()->where('rubrica_id', (int) $this->rubrica->id);
if (! empty($keepIds)) {
$deleteQuery->whereNotIn('id', $keepIds);
}
$deleteQuery->delete();
});
$this->mount((int) $this->rubrica->id);
Notification::make()
->title('Contatti aggiornati')
->success()
->send();
}),
Action::make('riferimenti_chiamata')
->label('Riferimenti chiamata')
->icon('heroicon-o-phone-arrow-up-right')
->form([
Select::make('tipo_utenza_call')
->label('Profilo chiamante')
->native(false)
->options([
'condomino' => 'Condomino',
'inquilino' => 'Inquilino',
'fornitore' => 'Fornitore',
'amministratore' => 'Amministratore',
'altro' => 'Altro',
]),
TextInput::make('riferimento_stabile')
->label('Riferimento stabile')
->maxLength(255),
TextInput::make('riferimento_unita')
->label('Riferimento unità')
->maxLength(255),
TextInput::make('note_segreteria')
->label('Note segreteria')
->maxLength(2000),
])
->fillForm(fn(): array=> [
'tipo_utenza_call' => $this->rubrica->tipo_utenza_call,
'riferimento_stabile' => $this->rubrica->riferimento_stabile,
'riferimento_unita' => $this->rubrica->riferimento_unita,
'note_segreteria' => $this->rubrica->note_segreteria,
])
->action(function (array $data): void {
$this->rubrica->tipo_utenza_call = ! empty($data['tipo_utenza_call']) ? (string) $data['tipo_utenza_call'] : null;
$this->rubrica->riferimento_stabile = ! empty($data['riferimento_stabile']) ? (string) $data['riferimento_stabile'] : null;
$this->rubrica->riferimento_unita = ! empty($data['riferimento_unita']) ? (string) $data['riferimento_unita'] : null;
$this->rubrica->note_segreteria = ! empty($data['note_segreteria']) ? (string) $data['note_segreteria'] : null;
$this->rubrica->data_ultima_modifica = now()->toDateString();
$this->rubrica->modificato_da = Auth::id();
$this->rubrica->save();
$this->mount((int) $this->rubrica->id);
Notification::make()
->title('Riferimenti chiamata aggiornati')
->success()
->send();
}),
Action::make('ruoli_legami')
->label('Ruoli / legami')
->icon('heroicon-o-link')
->modalWidth('7xl')
->form([
Repeater::make('ruoli')
->label('Ruoli e legami')
->defaultItems(0)
->reorderable(true)
->schema([
Hidden::make('id'),
Select::make('ruolo_standard')
->label('Ruolo')
->options(self::getRuoliLabels())
->required()
->native(false)
->default('altro')
->columnSpan(2),
TextInput::make('ruolo_custom')
->label('Ruolo custom')
->maxLength(255)
->columnSpan(2),
Select::make('stabile_id')
->label('Stabile')
->native(false)
->searchable()
->options(fn() => $this->getStabiliOptions())
->reactive()
->afterStateUpdated(fn($set) => $set('unita_immobiliare_id', null))
->columnSpan(3),
Select::make('unita_immobiliare_id')
->label('Unità')
->native(false)
->searchable()
->options(fn($get) => $this->getUnitaOptions($get('stabile_id')))
->columnSpan(3),
DatePicker::make('data_inizio')
->label('Data inizio')
->columnSpan(2),
DatePicker::make('data_fine')
->label('Data fine')
->columnSpan(2),
Toggle::make('is_preferito')
->label('Preferito')
->default(false)
->columnSpan(2),
Toggle::make('is_attivo')
->label('Attivo')
->default(true)
->columnSpan(2),
])
->columns(12),
])
->fillForm(function (): array {
$ruoli = RubricaRuolo::query()
->where('rubrica_id', (int) $this->rubrica->id)
->orderByDesc('is_attivo')
->orderByDesc('is_preferito')
->orderByDesc('data_inizio')
->orderByDesc('id')
->limit(200)
->get();
return [
'ruoli' => $ruoli->map(fn(RubricaRuolo $r) => [
'id' => (int) $r->id,
'ruolo_standard' => (string) ($r->ruolo_standard ?? 'altro'),
'ruolo_custom' => (string) ($r->ruolo_custom ?? ''),
'stabile_id' => $r->stabile_id ? (int) $r->stabile_id : null,
'unita_immobiliare_id' => $r->unita_immobiliare_id ? (int) $r->unita_immobiliare_id : null,
'data_inizio' => $r->data_inizio?->format('Y-m-d'),
'data_fine' => $r->data_fine?->format('Y-m-d'),
'is_preferito' => (bool) ($r->is_preferito ?? false),
'is_attivo' => (bool) ($r->is_attivo ?? true),
])->all(),
];
})
->action(function (array $data): void {
/** @var array<int, array<string, mixed>> $rows */
$rows = $data['ruoli'] ?? [];
$rows = is_array($rows) ? array_values($rows) : [];
DB::transaction(function () use ($rows): void {
$keepIds = [];
foreach ($rows as $row) {
$id = (int) ($row['id'] ?? 0);
$ruoloStandard = (string) ($row['ruolo_standard'] ?? 'altro');
$attrs = [
'rubrica_id' => (int) $this->rubrica->id,
'ruolo_standard' => $ruoloStandard ?: 'altro',
'ruolo_custom' => ! empty($row['ruolo_custom']) ? (string) $row['ruolo_custom'] : null,
'stabile_id' => ! empty($row['stabile_id']) ? (int) $row['stabile_id'] : null,
'unita_immobiliare_id' => ! empty($row['unita_immobiliare_id']) ? (int) $row['unita_immobiliare_id'] : null,
'data_inizio' => $row['data_inizio'] ?? null,
'data_fine' => $row['data_fine'] ?? null,
'is_preferito' => (bool) ($row['is_preferito'] ?? false),
'is_attivo' => (bool) ($row['is_attivo'] ?? true),
];
if ($id > 0) {
$existing = RubricaRuolo::query()
->where('id', $id)
->where('rubrica_id', (int) $this->rubrica->id)
->first();
if ($existing) {
$existing->fill($attrs);
$existing->save();
$keepIds[] = (int) $existing->id;
continue;
}
}
$created = RubricaRuolo::query()->create($attrs);
$keepIds[] = (int) $created->id;
}
$deleteQuery = RubricaRuolo::query()->where('rubrica_id', (int) $this->rubrica->id);
if (! empty($keepIds)) {
$deleteQuery->whereNotIn('id', $keepIds);
}
$deleteQuery->delete();
});
$this->mount((int) $this->rubrica->id);
Notification::make()
->title('Ruoli / legami aggiornati')
->success()
->send();
}),
Action::make('torna')
->label('Torna')
->icon('heroicon-o-arrow-left')
->url(fn() => AnagraficaUnica::getUrl(panel: 'admin-filament')),
];
return [];
}
private function fillInlineForm(): void

View File

@ -62,100 +62,6 @@ protected function getBackUrl(): string
protected function getHeaderActions(): array
{
return [
Action::make('sync_gescon')
->label('Sync GESCON')
->icon('heroicon-o-arrow-path')
->visible(function (): bool {
$user = Auth::user();
return $user instanceof User && $user->can('gescon-import.execute');
})
->form([
TextInput::make('path')
->label('Percorso archivio GESCON')
->helperText('Cartella base che contiene /<codice_stabile>/... e /dbc/Stabili.mdb')
->default('/mnt/gescon-archives/gescon')
->maxLength(500),
TextInput::make('anno')
->label('Anno gestione (opzionale)')
->helperText('Inserisci un anno (es. 2025) o "all" per tutte le cartelle.'),
Checkbox::make('dry')->label('Dry-run (solo simulazione)')->default(false),
Checkbox::make('with_unita')->label('Includi unità immobiliari')->default(false),
Checkbox::make('with_soggetti')->label('Includi anagrafiche (soggetti)')->default(false),
Checkbox::make('with_diritti')->label('Includi diritti (proprietà/inquilini)')->default(false),
Checkbox::make('with_millesimi')->label('Includi tabelle millesimali')->default(false),
Checkbox::make('with_voci')->label('Includi voci (piano dei conti / voci spesa)')->default(false),
Checkbox::make('with_gestioni')->label('Includi gestioni')->default(false),
Checkbox::make('with_banche')->label('Includi banche')->default(false),
])
->requiresConfirmation()
->action(function (array $data): void {
$user = Auth::user();
if (! $user instanceof User) {
Notification::make()->title('Utente non valido')->danger()->send();
return;
}
$amm = $user->amministratore;
if (! $amm) {
Notification::make()
->title('Amministratore non associato')
->body('Impossibile eseguire sync senza amministratore associato all\'utente.')
->danger()
->send();
return;
}
$legacyCode = trim((string) ($this->stabile->codice_stabile ?? ''));
if ($legacyCode === '') {
$legacyCode = trim((string) ($this->stabile->codice_interno ?? ''));
}
if ($legacyCode === '') {
Notification::make()
->title('Codice legacy mancante')
->body('Lo stabile non ha codice_stabile/codice_interno: non so quale cartella GESCON usare.')
->danger()
->send();
return;
}
$svc = new EssentialImportService();
$res = $svc->syncStabile($amm, $legacyCode, [
'path' => (string) ($data['path'] ?? '/mnt/gescon-archives/gescon'),
'anno' => isset($data['anno']) && trim((string) $data['anno']) !== '' ? trim((string) $data['anno']) : null,
'with_unita' => (bool) ($data['with_unita'] ?? false),
'with_soggetti' => (bool) ($data['with_soggetti'] ?? false),
'with_diritti' => (bool) ($data['with_diritti'] ?? false),
'with_millesimi' => (bool) ($data['with_millesimi'] ?? false),
'with_voci' => (bool) ($data['with_voci'] ?? false),
'with_gestioni' => (bool) ($data['with_gestioni'] ?? false),
'with_banche' => (bool) ($data['with_banche'] ?? false),
'dry' => (bool) ($data['dry'] ?? false),
]);
$title = ($res['changed'] ?? false)
? 'Sync completato'
: 'Nessuna variazione (hash invariato)';
Notification::make()
->title($title)
->body('Stabile: ' . $legacyCode)
->success()
->send();
}),
Action::make('apri_rubrica')
->label('Apri anagrafica unica')
->icon('heroicon-o-user')
->visible(fn(): bool => (int) ($this->stabile->rubrica_id ?? 0) > 0)
->url(fn() => RubricaUniversaleScheda::getUrl([
'record' => (int) $this->stabile->rubrica_id,
], panel: 'admin-filament')),
Action::make('torna')
->label('Torna')
->icon('heroicon-o-arrow-left')
->url(fn() => $this->getBackUrl()),
];
return [];
}
}

View File

@ -3,7 +3,7 @@
<div class="mb-5 bg-slate-900 text-slate-100 p-3.5 rounded-xl border border-slate-800 shadow-md">
<div class="flex flex-wrap gap-2 items-center justify-between">
<div class="flex flex-wrap gap-2 items-center">
<a href="/admin-filament/gescon/anagrafica/rubrica" class="px-3.5 py-1.5 rounded-lg bg-sky-600 hover:bg-sky-500 text-white font-semibold text-xs transition">🏢 Elenco</a>
<a href="/admin-filament/gescon/anagrafica/anagrafica-unica" class="px-3.5 py-1.5 rounded-lg bg-sky-600 hover:bg-sky-500 text-white font-semibold text-xs transition">🏢 Elenco</a>
<button type="button" wire:click="toggleInlineEditing" class="px-3.5 py-1.5 rounded-lg bg-amber-600 hover:bg-amber-500 text-white font-semibold text-xs transition">✏️ Modifica (#{{ $rubrica->id }})</button>
</div>