++ fix e controlli

This commit is contained in:
2026-07-30 17:05:39 +02:00
parent 712336d652
commit 440d63a6b9
54 changed files with 4921 additions and 1322 deletions

View File

@@ -0,0 +1,174 @@
{{--
Ultima zona della pagina: l'azzeramento.
Il secondo tempo (verifica del backup, password, conferma) compare QUI, dove
e' stato premuto il primo. Prima usciva in un pannello di avanzamento in cima
alla pagina, a schermate di distanza dal pulsante che lo aveva avviato: chi
lo cercava non lo trovava, e la conferma di un'operazione irreversibile e'
l'ultimo posto in cui ci si puo' permettere di far cercare qualcosa.
Regole del blocco x-data qui sotto, come nel resto della pagina: nessuna
virgoletta doppia, da nessuna parte, neanche nei commenti — un attributo HTML
finisce alla prima. Gli apostrofi stanno nei backtick.
--}}
@php
$stato = $operazione['status'] ?? null;
$attesa = $this->attesaDownload;
$mioAzzeramento = $this->azzeramentoInCorso;
// Il posto puo' essere occupato da un backup o da un ripristino avviati
// nelle zone di sopra: il pulsante si spegne, ma per un motivo diverso.
$occupatoDaAltri = $this->inCorso && ! $mioAzzeramento;
@endphp
<div x-data="{
password: '',
azzera() {
if (!this.password) {
return;
}
if (!confirm('Hai verificato il backup scaricato? Confermando, i dati vengono cancellati definitivamente.')) {
return;
}
const password = this.password;
// Svuotata subito: se e' sbagliata si ridigita, e intanto non resta
// nel campo di una scheda lasciata aperta.
this.password = '';
this.$dispatch('console-apri');
this.$wire.confermaAzzeramento(password);
},
}"
{{-- Vedi la nota gemella in backup-manager: il poll si spegne da solo
quando l'operazione chiude. --}}
@if($this->inCorso) wire:poll.{{ $this->intervalloPoll }}="aggiorna" @endif>
<section id="zona-azzeramento"
class="tm-zona rounded-xl border-2 border-red-300 bg-red-50/40 p-6 space-y-4 {{ $mioAzzeramento ? 'tm-zona-attiva' : '' }}"
aria-labelledby="titolo-azzeramento">
<div class="flex items-start gap-3">
<svg class="h-6 w-6 flex-none text-red-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01M5.07 19h13.86c1.54 0 2.5-1.67 1.73-3L13.73 4a2 2 0 00-3.46 0L3.34 16c-.77 1.33.19 3 1.73 3z"/></svg>
<div>
<h2 id="titolo-azzeramento" class="text-lg font-semibold text-red-900">5 &middot; Zona pericolosa</h2>
<p class="text-xs text-red-800 mt-1">Operazioni irreversibili. Leggi prima di premere.</p>
</div>
</div>
@error('confermaReset')
<p class="rounded-lg border border-red-300 bg-white px-3 py-2 text-xs font-medium text-red-700" role="alert">{{ $message }}</p>
@enderror
{{-- ─── Secondo tempo: l'archivio c'e', manca la conferma ────────── --}}
@if($attesa)
<div class="rounded-lg border-2 border-amber-400 bg-amber-50 p-4 space-y-3"
x-init="window.tmFocusZona('zona-azzeramento', false)">
<p class="text-sm text-amber-900">
<strong>L&rsquo;archivio &egrave; pronto e il download &egrave; partito.</strong>
Non &egrave; stato cancellato ancora nulla: l&rsquo;azzeramento aspetta la tua conferma.
</p>
<p class="text-xs text-amber-800">
Apri il file scaricato e verifica che sia integro. Il server sa di aver <em>spedito</em>
l&rsquo;archivio, non pu&ograve; sapere se &egrave; arrivato intero: quella verifica tocca a te,
ed &egrave; l&rsquo;ultima possibilit&agrave; di tornare indietro.
</p>
{{-- La password sta in Alpine, non in una proprietà del
componente: le proprietà pubbliche di Livewire tornano
al browser dentro lo snapshot a ogni risposta, e una
password non ha motivo di fare quel viaggio. Passata
come parametro del metodo, viaggia solo in andata. --}}
<div class="max-w-xs">
<label for="passwordConferma" class="block text-xs font-medium text-amber-900">
Conferma con la tua password
</label>
<input type="password" id="passwordConferma" autocomplete="current-password"
data-zona-focus
x-model="password"
x-on:keydown.enter.prevent="azzera()"
class="mt-1 block w-full rounded-lg border-amber-300 shadow-sm focus:ring-red-500 focus:border-red-500 text-sm">
@error('passwordConferma') <p class="mt-1 text-xs font-medium text-red-700" role="alert">{{ $message }}</p> @enderror
</div>
<div class="flex flex-wrap items-center gap-3">
<x-btn variante="secondary" :href="route('backup.file', $operationId)">
Scarica di nuovo
</x-btn>
<x-btn variante="danger"
x-on:click="azzera()"
x-bind:disabled="!password">
Ho salvato il backup, azzera tutto
</x-btn>
<x-btn variante="ghost" wire:click="annullaAzzeramento">
Annulla l&rsquo;azzeramento
</x-btn>
</div>
</div>
@endif
{{-- ─── Primo tempo ─────────────────────────────────────────────── --}}
<div class="rounded-lg border border-red-300 bg-white p-4 space-y-3">
<h3 class="text-sm font-semibold text-gray-900">Azzera tutto e riparti da zero</h3>
<p class="text-xs text-gray-600">
Riporta l&rsquo;applicazione allo stato di installazione appena eseguita. In ordine:
</p>
<ol class="text-xs text-gray-600 list-decimal list-inside space-y-1">
<li><strong>Viene creato e scaricato un backup completo</strong> di tutto ci&ograve; che c&rsquo;&egrave; adesso.</li>
<li>L&rsquo;operazione si ferma e aspetta: <strong>controlli il file scaricato, digiti la tua password e confermi</strong>. Fino a quel clic non viene toccato nulla, e puoi annullare.</li>
<li>Vengono eliminati territori, proclamatori, assegnazioni, campagne, zone, tipologie, anni di servizio, il registro attivit&agrave; e <strong>tutti i file caricati</strong>.</li>
<li>Le impostazioni tornano ai valori di default e i ruoli vengono riseminati.</li>
<li><strong>Tutti gli altri utenti vengono eliminati.</strong> Resta solo il tuo account di amministratore, con la password attuale &mdash; come un&rsquo;installazione nuova, che ha un amministratore solo.</li>
</ol>
<div class="rounded-lg border border-red-200 bg-red-50 p-3 text-xs text-red-800">
Il backup scaricato &egrave; l&rsquo;<strong>unico</strong> modo per tornare indietro. Verifica di averlo
salvato prima di chiudere il browser.
</div>
<div>
<label for="confermaReset" class="block text-xs font-medium text-gray-700">
Per confermare, scrivi <code class="rounded bg-gray-100 px-1 py-0.5 font-mono">{{ \App\Livewire\Settings\ZonaPericolosa::CONFERMA_AZZERAMENTO }}</code>
</label>
<input wire:model="confermaReset" type="text" id="confermaReset" autocomplete="off"
data-zona-focus
@disabled($attesa)
class="mt-1 block w-full max-w-xs rounded-lg border-gray-300 shadow-sm focus:ring-red-500 focus:border-red-500 text-sm font-mono">
</div>
<div class="flex flex-wrap items-center gap-3">
<x-btn variante="danger"
wire:click="avviaAzzeramento"
wire:confirm="Ultima conferma. Verrà scaricato un backup e poi l'applicazione sarà azzerata. L'operazione non è reversibile."
x-on:click="$dispatch('console-apri')"
{{-- Vedi la nota in components/btn.blade.php: dentro un
tag di componente serve :disabled, non @disabled. --}}
:disabled="$this->inCorso">
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.87 12.14A2 2 0 0116.14 21H7.86a2 2 0 01-1.99-1.86L5 7m5 4v6m4-6v6M9 7V4a1 1 0 011-1h4a1 1 0 011 1v3M4 7h16"/></svg>
Scarica backup e azzera tutto
</x-btn>
@if($occupatoDaAltri)
<span class="text-xs text-gray-600">
Un backup o un ripristino sono in corso: l&rsquo;azzeramento pu&ograve; partire solo a operazione finita.
</span>
@elseif($mioAzzeramento && ! $attesa)
<span class="text-xs text-gray-600">
Azzeramento in corso: l&rsquo;avanzamento &egrave; nella console in fondo alla finestra.
</span>
@endif
</div>
@if($stato === 'failed' && $mioAzzeramento)
<p class="text-sm text-red-800" role="alert">{{ $operazione['error'] ?? 'Errore non specificato.' }}</p>
@endif
@if($stato === 'completed' && ($operazione['result']['messaggio'] ?? null) && ($operazione['tipo'] ?? null) === 'reset')
<p class="text-sm text-green-900">{{ $operazione['result']['messaggio'] }}</p>
@endif
</div>
</section>
</div>