Refine water services and bank movement views

This commit is contained in:
michele 2026-04-21 17:57:51 +00:00
parent 6ac70ce38e
commit e9cf216fa4
8 changed files with 179 additions and 191 deletions

View File

@ -4,6 +4,7 @@ # Changelog
## [Unreleased]
- Surfaced ACQUA invoices in the servizi-utenze area even when the supplier is only flagged via FE water features, compacted the supplier scheda actions into a single toolbar without page-level horizontal overflow, standardized the bank movements grid to the requested visible columns with a richer raw-data modal, and made affitti canoni start from the effective management takeover date.
- Reworked the banking operations hub so the topbar stays full-width, the sidebar stays fixed below it, the movements tab remains focused on imported/manual bank movements, and archive or gestione structure moved into a dedicated tab. The same slice also added first-pass QIF cleanup metadata for CBILL, SIA, commissions and provider hints, plus stronger bank-to-supplier matching and ACQ assignment for water invoices extracted from FE/PDF data.
- Fixed document archive visibility queries to work safely even when single-user ACL columns are not yet present in older databases, and added the missing `documenti.allowed_user_ids` schema field for per-document access control.
- Restored a cleaner two-column layout in Post-it Gestione boards and hid visual noise from calls that terminate on internal response group `601` while keeping the underlying records in the system.

View File

@ -1 +1 @@
0.8.2
0.8.3

View File

@ -621,6 +621,7 @@ private function loadAffitti(): void
'nome_inquilino' => $a->nome_inquilino,
'proprietario_nome' => $a->proprietario_nome,
'inizio_contratto' => $a->inizio_contratto,
'presa_in_carico_operativa_dal' => $a->presa_in_carico_operativa_dal,
'prossima_registrazione' => $a->prossima_registrazione,
'tipo_riga' => $a->tipo_riga,
'stabile_id' => $a->stabile_id,
@ -657,12 +658,44 @@ private function buildAffittoWarnings(AffittoImmobile $affitto): array
}
}
$managementStart = $this->resolveCanoniManagementStart($affitto);
$scadutiNonPagati = AffittoCanoneDovuto::query()
->where('affitto_id', $affitto->id)
->when($managementStart, function ($query) use ($managementStart) {
$query->where(function ($inner) use ($managementStart) {
$inner->whereDate('data_scadenza', '>=', $managementStart->toDateString())
->orWhere(function ($fallback) use ($managementStart) {
$fallback->whereNull('data_scadenza')
->where(function ($monthQuery) use ($managementStart) {
$monthQuery->where('anno', '>', (int) $managementStart->year)
->orWhere(function ($sameYear) use ($managementStart) {
$sameYear->where('anno', (int) $managementStart->year)
->where('mese', '>=', (int) $managementStart->month);
});
});
});
});
})
->whereDate('data_scadenza', '<', $today->toDateString())
->sum('totale')
- AffittoCanonePagato::query()
->where('affitto_id', $affitto->id)
->when($managementStart, function ($query) use ($managementStart) {
$query->where(function ($inner) use ($managementStart) {
$inner->whereDate('data_pagamento', '>=', $managementStart->toDateString())
->orWhere(function ($fallback) use ($managementStart) {
$fallback->whereNull('data_pagamento')
->where(function ($monthQuery) use ($managementStart) {
$monthQuery->where('anno', '>', (int) $managementStart->year)
->orWhere(function ($sameYear) use ($managementStart) {
$sameYear->where('anno', (int) $managementStart->year)
->where('mese', '>=', (int) $managementStart->month);
});
});
});
});
})
->whereDate('data_pagamento', '<=', $today->toDateString())
->sum('totale');
@ -786,6 +819,7 @@ private function loadSelected(): void
'numero_registrazione_ade' => null,
'data_registrazione_ade' => null,
'inizio_contratto' => null,
'presa_in_carico_operativa_dal' => null,
'ultimo_rinnovo' => null,
'prossima_scadenza' => null,
'prossima_registrazione' => null,
@ -852,8 +886,9 @@ private function loadSelected(): void
$this->ensureRicevute($record, $dovuti);
$pagati = AffittoCanonePagato::where('affitto_id', $record->id)->orderByDesc('anno')->orderByDesc('mese')->get();
if (! empty($record->inizio_contratto)) {
$start = Carbon::parse($record->inizio_contratto)->startOfMonth();
$managementStart = $this->resolveCanoniManagementStart($record);
if ($managementStart) {
$start = $managementStart->copy()->startOfMonth();
$dovuti = $dovuti->filter(function (AffittoCanoneDovuto $row) use ($start) {
$month = Carbon::create((int) $row->anno, (int) $row->mese, 1);
return $month->gte($start);
@ -1219,11 +1254,12 @@ private function emitCanoniAutomaticiForRecord(AffittoImmobile $record): int
return 0;
}
if (empty($record->inizio_contratto)) {
$managementStart = $this->resolveCanoniManagementStart($record);
if (! $managementStart) {
return 0;
}
$start = Carbon::parse($record->inizio_contratto)->startOfMonth();
$start = $managementStart->copy()->startOfMonth();
$todayMonth = Carbon::today()->startOfMonth();
$end = $todayMonth;
@ -1406,6 +1442,31 @@ private function emitCanoniAutomaticiForRecord(AffittoImmobile $record): int
return $created;
}
private function resolveCanoniManagementStart(AffittoImmobile $record): ?Carbon
{
$dates = [];
if (! empty($record->inizio_contratto)) {
$dates[] = Carbon::parse($record->inizio_contratto)->startOfMonth();
}
if (! empty($record->presa_in_carico_operativa_dal)) {
$dates[] = Carbon::parse($record->presa_in_carico_operativa_dal)->startOfMonth();
}
if ($record->stabile && $record->stabile->latestAmministratoreTransfer?->created_at) {
$dates[] = $record->stabile->latestAmministratoreTransfer->created_at->copy()->startOfMonth();
}
if ($dates === []) {
return null;
}
usort($dates, fn(Carbon $left, Carbon $right): int => $left->getTimestamp() <=> $right->getTimestamp());
return $dates[count($dates) - 1];
}
private function resolveIstatQuotaFromClause(AffittoContrattoClausola $clausola): float
{
$content = trim((string) ($clausola->titolo ?? '') . ' ' . (string) ($clausola->testo ?? ''));

View File

@ -1429,6 +1429,32 @@ public function getAcquaFatturePerGestioneProperty(): array
->values()
->all();
if (Schema::hasTable('fornitori') && Schema::hasColumn('fornitori', 'fe_features')) {
$waterEnabledSupplierIds = Fornitore::query()
->where(function (Builder $query) use ($stabileId): Builder {
return $query->whereExists(function ($subQuery) use ($stabileId) {
$subQuery->selectRaw('1')
->from('fatture_elettroniche as fe')
->whereColumn('fe.fornitore_id', 'fornitori.id')
->where('fe.stabile_id', $stabileId);
})->orWhereExists(function ($subQuery) use ($stabileId) {
$subQuery->selectRaw('1')
->from('contabilita_fatture_fornitori as cff')
->whereColumn('cff.fornitore_id', 'fornitori.id')
->where('cff.stabile_id', $stabileId);
});
})
->whereRaw("JSON_EXTRACT(fe_features, '$.acqua.enabled') = true")
->pluck('id')
->map(fn($v) => (int) $v)
->filter(fn($v) => $v > 0)
->unique()
->values()
->all();
$fornitoreIds = array_values(array_unique(array_merge($fornitoreIds, $waterEnabledSupplierIds)));
}
if ($fornitoreIds !== []) {
$contabili = DB::table('contabilita_fatture_fornitori as f')
->leftJoin('gestioni_contabili as g', 'g.id', '=', 'f.gestione_id')

View File

@ -1305,149 +1305,6 @@ public function table(Table $table): Table
}),
])
->columns([
TextColumn::make('codice_descrizione')
->label('Codice')
->state(function (MovimentoBanca $record): string {
$descr = (string) ($record->descrizione ?? '');
$raw = strtoupper($descr);
if (str_contains($raw, 'ACCREDITO_BEU_CON_CONTABILE')) {
return 'ACCBEUCONCON';
}
if (str_contains($raw, 'COMBONMYB')) {
return 'COMBONMYB';
}
$raw = preg_replace('/[^A-Z0-9]+/', ' ', $raw) ?? $raw;
$parts = array_filter(explode(' ', $raw), fn($v) => $v !== '');
if (! empty($parts)) {
$code = '';
foreach ($parts as $p) {
$chunk = substr($p, 0, 3);
$code .= $chunk;
if (strlen($code) >= 12) {
break;
}
}
$code = substr($code, 0, 12);
if ($code !== '') {
return $code;
}
}
$raw = preg_replace('/[^A-Z0-9]+/', '_', $raw) ?? $raw;
$raw = trim($raw, '_');
$raw = preg_replace('/_+/', '_', $raw) ?? $raw;
return mb_substr($raw, 0, 32);
})
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('disp')
->label('Disp')
->state(function (MovimentoBanca $record): string {
$match = is_array($record->match_data ?? null) ? $record->match_data : [];
$disp = $match['cod_disp'] ?? null;
return is_string($disp) ? trim($disp) : '';
})
->toggleable(),
TextColumn::make('cash')
->label('Cash')
->state(function (MovimentoBanca $record): string {
$match = is_array($record->match_data ?? null) ? $record->match_data : [];
$cash = $match['cash'] ?? null;
return is_string($cash) ? trim($cash) : '';
})
->toggleable(),
TextColumn::make('mittente')
->label('Mittente')
->state(function (MovimentoBanca $record): string {
$match = is_array($record->match_data ?? null) ? $record->match_data : [];
$mitt = $match['mittente'] ?? null;
return is_string($mitt) ? trim($mitt) : '';
})
->wrap()
->toggleable(),
BadgeColumn::make('commissioni')
->label('Comm.')
->getStateUsing(function (MovimentoBanca $record): string {
$raw = strtoupper((string) ($record->descrizione ?? ''));
return str_contains($raw, 'COMBONMYB') ? 'si' : 'no';
})
->colors([
'success' => 'si',
'gray' => 'no',
])
->formatStateUsing(fn(string $state): string => $state === 'si' ? 'SI' : '—')
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('disp')
->label('Disp')
->state(function (MovimentoBanca $record): string {
$match = is_array($record->match_data ?? null) ? $record->match_data : [];
$disp = $match['cod_disp'] ?? null;
return is_string($disp) ? trim($disp) : '';
})
->toggleable(),
BadgeColumn::make('da_confermare')
->label('Conferma')
->getStateUsing(fn(MovimentoBanca $record): string => ! empty($record->da_confermare) ? 'da_confermare' : 'ok')
->colors([
'warning' => 'da_confermare',
'success' => 'ok',
])
->formatStateUsing(fn(string $state): string => $state === 'da_confermare' ? 'DA CONF.' : 'OK')
->toggleable(isToggledHiddenByDefault: true),
BadgeColumn::make('stato_quadratura')
->label('Stato')
->getStateUsing(fn(MovimentoBanca $record): string => $this->resolveStatoQuadratura($record))
->colors([
'gray' => 'non_collegato',
'warning' => 'collegato_non_quadrato',
'success' => 'quadrato',
])
->formatStateUsing(function (string $state): string {
return match ($state) {
'quadrato' => 'OK',
'collegato_non_quadrato' => 'ATT',
default => 'NO',
};
})
->sortable(false),
TextColumn::make('voci_collegate')
->label('Voce spesa')
->state(function (MovimentoBanca $record): string {
if (! $record->registrazione_id || ! Schema::hasColumn('contabilita_movimenti', 'voce_spesa_id')) {
return '—';
}
static $cache = [];
$regId = (int) $record->registrazione_id;
if (array_key_exists($regId, $cache)) {
return $cache[$regId];
}
$rows = DB::table('contabilita_movimenti as m')
->leftJoin('voci_spesa as v', 'v.id', '=', 'm.voce_spesa_id')
->where('m.registrazione_id', $regId)
->whereNotNull('m.voce_spesa_id')
->select('v.codice', 'v.descrizione')
->distinct()
->limit(5)
->get();
if ($rows->isEmpty()) {
return $cache[$regId] = '—';
}
$labels = [];
foreach ($rows as $row) {
$code = trim((string) ($row->codice ?? ''));
$desc = trim((string) ($row->descrizione ?? ''));
$labels[] = $code !== '' ? ($code . ($desc !== '' ? ' · ' . $desc : '')) : ($desc !== '' ? $desc : 'Voce');
}
return $cache[$regId] = implode(' | ', array_values(array_unique($labels)));
})
->wrap()
->toggleable(),
TextColumn::make('data')
->label('Data')
->date('d/m/Y')
@ -1455,8 +1312,10 @@ public function table(Table $table): Table
TextColumn::make('valuta')
->label('Valuta')
->date('d/m/Y')
->toggleable(isToggledHiddenByDefault: true),
->date('d/m/Y'),
TextColumn::make('causale')
->label('Caus'),
TextColumn::make('descrizione')
->label('Descrizione')
@ -1466,7 +1325,8 @@ public function table(Table $table): Table
TextColumn::make('gestioneContabile.denominazione')
->label('Gestione')
->toggleable(isToggledHiddenByDefault: true),
->toggleable(isToggledHiddenByDefault: true)
->toggledHiddenByDefault(),
TextColumn::make('entrate')
->label('Entrate')
@ -1492,17 +1352,14 @@ public function table(Table $table): Table
->toggleable(isToggledHiddenByDefault: true)
->formatStateUsing(fn($state) => '€ ' . number_format((float) $state, 2, ',', '.')),
TextColumn::make('causale')
->label('Caus.')
->toggleable(),
TextColumn::make('iban')
->label('IBAN')
->toggleable(isToggledHiddenByDefault: true),
->toggleable(isToggledHiddenByDefault: true)
->toggledHiddenByDefault(),
])
->actions([
Action::make('dettaglio_movimento')
->label('Dettaglio')
->label('Apri dati')
->icon('heroicon-o-eye')
->modalWidth('7xl')
->action(function (MovimentoBanca $record): void {

View File

@ -41,6 +41,7 @@ class AffittoImmobile extends Model
'numero_registrazione_ade',
'data_registrazione_ade',
'inizio_contratto',
'presa_in_carico_operativa_dal',
'ultimo_rinnovo',
'prossima_scadenza',
'prossima_registrazione',
@ -77,6 +78,7 @@ class AffittoImmobile extends Model
'data_registrazione_ade' => 'date',
'obsoleto' => 'boolean',
'obsoleto_dal' => 'date',
'presa_in_carico_operativa_dal' => 'date',
'giorno_emissione_canone' => 'integer',
'mese_adempimenti_annuali' => 'integer',
'giorno_adempimenti_annuali' => 'integer',

View File

@ -23,10 +23,18 @@
<div class="text-gray-500">Data</div>
<div class="font-semibold">{{ optional($record->data)->format('d/m/Y') ?: '—' }}</div>
</div>
<div class="rounded-lg border bg-white p-3 text-xs">
<div class="text-gray-500">Valuta</div>
<div class="font-semibold">{{ optional($record->valuta)->format('d/m/Y') ?: '—' }}</div>
</div>
<div class="rounded-lg border bg-white p-3 text-xs">
<div class="text-gray-500">Importo</div>
<div class="font-semibold"> {{ number_format((float) $record->importo, 2, ',', '.') }}</div>
</div>
<div class="rounded-lg border bg-white p-3 text-xs">
<div class="text-gray-500">Causale</div>
<div class="font-semibold">{{ $record->causale ?? '—' }}</div>
</div>
<div class="rounded-lg border bg-white p-3 text-xs">
<div class="text-gray-500">Descrizione</div>
<div class="font-semibold">{{ $record->descrizione ?? '—' }}</div>
@ -35,6 +43,20 @@
<div class="text-gray-500">Descrizione estesa</div>
<div class="font-semibold">{{ $record->descrizione_estesa_pulita ?? $record->descrizione_estesa ?? '—' }}</div>
</div>
<div class="rounded-lg border bg-white p-3 text-xs">
<div class="text-gray-500">Saldo progressivo</div>
<div class="font-semibold">
@if(isset($record->saldo_progressivo) && is_numeric($record->saldo_progressivo))
{{ number_format((float) $record->saldo_progressivo, 2, ',', '.') }}
@else
@endif
</div>
</div>
<div class="rounded-lg border bg-white p-3 text-xs">
<div class="text-gray-500">File sorgente</div>
<div class="font-semibold">{{ $record->source_file ?? '—' }}</div>
</div>
<div class="rounded-lg border bg-white p-3 text-xs">
<div class="text-gray-500">Disp</div>
<div class="font-semibold">{{ is_array($record->match_data ?? null) ? ($record->match_data['cod_disp'] ?? '—') : '—' }}</div>
@ -47,6 +69,10 @@
<div class="text-gray-500">Mittente</div>
<div class="font-semibold">{{ is_array($record->match_data ?? null) ? ($record->match_data['mittente'] ?? '—') : '—' }}</div>
</div>
<div class="rounded-lg border bg-white p-3 text-xs md:col-span-2">
<div class="text-gray-500">Riga raw importata</div>
<div class="font-semibold break-words">{{ $record->raw_line ?? '—' }}</div>
</div>
</div>
@php
@ -63,10 +89,5 @@
</div>
@endif
<div class="flex flex-wrap gap-2">
<x-filament::button size="sm" color="primary" disabled>Conferma</x-filament::button>
<x-filament::button size="sm" color="gray" disabled>Genera prima nota</x-filament::button>
<x-filament::button size="sm" color="gray" disabled>Paga fornitore</x-filament::button>
</div>
</div>
@endif

View File

@ -7,9 +7,21 @@
'catalogo' => 'Catalogo e prodotti',
'contabilita' => 'Contabilita',
];
$catalogoActions = [
['action' => 'nuovo_prodotto', 'label' => 'Nuovo prodotto', 'color' => 'gray'],
['action' => 'estrai_prodotti_fe', 'label' => 'Estrai da FE', 'color' => 'primary'],
['action' => 'importa_listino_csv', 'label' => 'Importa listino CSV', 'color' => 'success'],
['action' => 'scarica_asset_catalogo', 'label' => 'Internalizza asset', 'color' => 'warning'],
['action' => 'genera_referral_amazon', 'label' => 'Prepara link Amazon', 'color' => 'gray'],
];
$assistenzaActions = [
['action' => 'impostazioni_fe', 'label' => 'Impostazioni FE', 'color' => 'gray'],
['action' => 'acqua_config', 'label' => 'Configura acqua', 'color' => 'gray'],
['action' => 'acqua_scan', 'label' => 'Scansiona FE acqua', 'color' => 'primary'],
];
@endphp
<div class="space-y-6" x-data="{ tab: @js($this->sectionTab) }">
<div class="space-y-6 overflow-x-hidden" x-data="{ tab: @js($this->sectionTab) }">
<div class="rounded-xl border bg-white p-4">
<div class="flex flex-wrap items-start justify-between gap-4">
<div>
@ -41,6 +53,37 @@ class="inline-flex items-center rounded-full px-4 py-2 text-sm font-medium"
Qui restano visibili modulo fornitore e catalogo operativo. Il magazzino e la gestione completa del prodotto possono essere spostati in menu dedicati mantenendo qui il quadro sintetico e i collegamenti rapidi.
</div>
<div class="rounded-xl border border-slate-200 bg-white p-4" x-show="tab === 'catalogo' || tab === 'assistenza'" x-cloak>
<div class="flex flex-wrap items-start justify-between gap-3">
<div>
<div class="text-sm font-semibold text-slate-900" x-show="tab === 'catalogo'">Azioni catalogo</div>
<div class="text-sm font-semibold text-slate-900" x-show="tab === 'assistenza'">Azioni automazioni</div>
<div class="mt-1 text-xs text-slate-500" x-show="tab === 'catalogo'">Un solo punto per le azioni operative del catalogo, senza duplicarle in più riquadri.</div>
<div class="mt-1 text-xs text-slate-500" x-show="tab === 'assistenza'">FE e acqua raccolte in una toolbar compatta, così la colonna destra non spinge più il layout.</div>
</div>
<div class="flex max-w-full flex-wrap justify-end gap-2">
@foreach($catalogoActions as $action)
<x-filament::button
x-show="tab === 'catalogo'"
size="sm"
:color="$action['color']"
type="button"
wire:click="mountAction('{{ $action['action'] }}')"
>{{ $action['label'] }}</x-filament::button>
@endforeach
@foreach($assistenzaActions as $action)
<x-filament::button
x-show="tab === 'assistenza'"
size="sm"
:color="$action['color']"
type="button"
wire:click="mountAction('{{ $action['action'] }}')"
>{{ $action['label'] }}</x-filament::button>
@endforeach
</div>
</div>
</div>
<div class="rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-900" x-show="tab === 'assistenza'" x-cloak>
Questa tab concentra TecnoRepair, seriali e automazioni collegate al fornitore. I ticket operativi e il protocollo comunicazioni possono poi evolvere come viste dedicate, senza ricadere in una scheda unica confusa.
</div>
@ -49,8 +92,8 @@ class="inline-flex items-center rounded-full px-4 py-2 text-sm font-medium"
Qui si prepara il perimetro economico del fornitore: pagamenti, fatture, anteprime contabili e ritenute. Preventivi, ordini e fatturazione completa possono innestarsi in questa area senza mischiarsi con catalogo o assistenza.
</div>
<div class="grid grid-cols-1 gap-6 lg:grid-cols-3">
<div class="space-y-6 lg:col-span-2">
<div class="grid min-w-0 grid-cols-1 gap-6 lg:grid-cols-3">
<div class="min-w-0 space-y-6 lg:col-span-2">
<div class="rounded-lg border p-4" x-show="tab === 'profilo'" x-cloak>
<div class="text-lg font-semibold">Fornitore</div>
<div class="mt-2 grid grid-cols-1 gap-2 md:grid-cols-2">
@ -261,13 +304,6 @@ class="inline-flex items-center rounded-full px-4 py-2 text-sm font-medium"
</div>
</div>
<div class="mt-4 flex flex-wrap gap-2">
<x-filament::button size="sm" color="gray" type="button" wire:click="mountAction('nuovo_prodotto')">Nuovo prodotto</x-filament::button>
<x-filament::button size="sm" color="primary" type="button" wire:click="mountAction('estrai_prodotti_fe')">Estrai da FE</x-filament::button>
<x-filament::button size="sm" color="success" type="button" wire:click="mountAction('importa_listino_csv')">Importa listino CSV</x-filament::button>
<x-filament::button size="sm" color="warning" type="button" wire:click="mountAction('scarica_asset_catalogo')">Internalizza asset</x-filament::button>
<x-filament::button size="sm" color="gray" type="button" wire:click="mountAction('genera_referral_amazon')">Prepara link Amazon</x-filament::button>
</div>
</div>
<div class="rounded-lg border p-4" x-show="tab === 'catalogo'" x-cloak>
@ -276,13 +312,7 @@ class="inline-flex items-center rounded-full px-4 py-2 text-sm font-medium"
<div class="text-lg font-semibold">Catalogo operativo interno</div>
<div class="mt-1 text-xs text-gray-500">Anagrafica prodotti/servizi del fornitore. I riferimenti esterni importati restano interni al gestionale e la pubblicazione usa il codice interno del catalogo.</div>
</div>
<div class="flex flex-wrap gap-2">
<x-filament::button size="sm" color="gray" type="button" wire:click="mountAction('nuovo_prodotto')">Nuovo prodotto</x-filament::button>
<x-filament::button size="sm" color="primary" type="button" wire:click="mountAction('estrai_prodotti_fe')">Estrai da FE</x-filament::button>
<x-filament::button size="sm" color="success" type="button" wire:click="mountAction('importa_listino_csv')">Importa CSV</x-filament::button>
<x-filament::button size="sm" color="warning" type="button" wire:click="mountAction('scarica_asset_catalogo')">Asset interni</x-filament::button>
<x-filament::button size="sm" color="gray" type="button" wire:click="mountAction('genera_referral_amazon')">Amazon</x-filament::button>
</div>
<div class="rounded-md bg-slate-100 px-3 py-2 text-xs text-slate-600">Le azioni operative sono state raccolte sopra.</div>
</div>
<div class="mt-4 grid grid-cols-2 gap-3 md:grid-cols-4">
@ -457,7 +487,7 @@ class="w-full rounded-lg border-gray-300 text-sm"
</div>
</div>
<div class="space-y-6">
<div class="min-w-0 space-y-6">
@php
$features = is_array($box['fornitore_features'] ?? null) ? $box['fornitore_features'] : [];
$acqua = is_array($features['acqua'] ?? null) ? $features['acqua'] : [];
@ -537,17 +567,7 @@ class="w-full rounded-lg border-gray-300 text-sm"
</div>
</div>
<div class="mt-4 flex flex-col gap-2">
<x-filament::button type="button" color="gray" wire:click="mountAction('impostazioni_fe')">
Impostazioni FE
</x-filament::button>
<x-filament::button type="button" color="gray" wire:click="mountAction('acqua_config')">
Configura acqua
</x-filament::button>
<x-filament::button type="button" color="primary" wire:click="mountAction('acqua_scan')">
Scansiona FE acqua
</x-filament::button>
</div>
<div class="mt-4 rounded-md bg-slate-100 px-3 py-2 text-xs text-slate-600">Le azioni FE/acqua sono state raccolte nella toolbar in alto.</div>
</div>
<div class="rounded-lg border p-4" x-show="tab === 'contabilita'" x-cloak>