diff --git a/HANDOFF.md b/HANDOFF.md
index 67e37c9..93f4087 100644
--- a/HANDOFF.md
+++ b/HANDOFF.md
@@ -177,9 +177,35 @@ larghezza supera i 640px → torna la tabella normale (per questo landscape "non
problemi"). Convenzioni: ogni `
` dati ha `data-label="..."`; la cella azioni
ha classe `cell-actions` (niente label, occupa la riga a piena larghezza); le
celle `colspan` (stato vuoto, form di modifica utente inline) restano `block` a
-piena larghezza. Applicata a: territori, proclamatori, campagne, registro,
-utenti, audit-log e i due cestini. La pagina *Assegnazioni* era già ottimizzata e
-non è stata toccata.
+piena larghezza. Applicata a tutte le liste e sottopagine con tabelle: territori,
+proclamatori, campagne, registro, utenti, audit-log, i due cestini, le tabelle
+"storico assegnazioni" di territorio-show/proclamatore-show, "territori
+conteggiati" di campagna-show e le due tabelle di esito import in xml-exchange
+(una a classi Tailwind, una a stili inline — `border:none !important` nella regola
+gestisce anche i bordi inline). La pagina *Assegnazioni* era già ottimizzata.
+
+### Scadenza link PDF unificata con la soglia rientro
+
+La "Validità link PDF (mesi)" è stata rimossa: il link condivisibile ora scade
+esattamente quando l'assegnazione raggiunge la **soglia rientro**
+(`giorni_per_smarrito`), cioè `assigned_at + giorni_per_smarrito` — la stessa
+data di `Assegnazione::data_consegna_presunta` e del chip "Scadenza" nel viewer.
+`AssignmentPdfController::linkScaduto` e `ShortPdfLinkController` calcolano ora
+`addDays(giorni_per_smarrito)` invece di `addMonths(ttl)`; la scadenza vale solo
+per i non autenticati e il link muore comunque al rientro (`is_aperta`). Rimossi
+il campo Impostazioni e la proprietà `assignment_link_ttl_months` in
+`SettingsEdit`. **La colonna `assignment_link_ttl_hours` resta nel DB** (niente
+migration distruttiva): è ormai vestigiale per la logica ma l'export/import XML
+continua a leggerla/scriverla senza effetti.
+
+### Ricerca rapida nella lista link (pagina Assegna)
+
+La sezione "Territori Assegnati" (i link PDF) ha un unico campo che filtra per
+numero territorio, zona e proclamatore. Il filtro è **in memoria** (proprietà
+`linkSearch`, collection `assegnazioniAperteVisibili`) perché nome/cognome del
+proclamatore sono cifrati e non filtrabili via SQL — stesso approccio del
+Registro. La sezione resta visibile anche a risultato vuoto (mostra "Nessun
+risultato"), perché l'`@if` esterno guarda la lista non filtrata.
### Assegnare un territorio ne azzera il flag "prioritario"
@@ -204,12 +230,14 @@ Tre ritocchi minori ma con qualche implicazione:
ogni utente loggato (nel gruppo `auth`, nessun permesso): l'utente corrente può
modificare il proprio nome/cognome ed email (`updateProfile`) e cambiare la
password (`updatePassword`, con verifica della password attuale via regola
- `current_password`; il cast `hashed` cifra al salvataggio). Nell'header il
- blocco utente è un link a questa pagina, con nome in evidenza ed
- `email · qualifica` sotto. Non esiste un campo "username" né "cognome"
- separato: `name` è il nome completo (etichettato "Nome e cognome") e l'email fa
- da identificativo. La qualifica (ruolo) non è modificabile dal profilo — resta
- in gestione all'admin.
+ `current_password`; il cast `hashed` cifra al salvataggio). Dopo il salvataggio
+ dei dati `updateProfile` fa `redirect(navigate:true)` sulla stessa pagina, così
+ header (nel layout, fuori dal componente) e card mostrano subito i nuovi valori.
+ Il link al profilo è nell'header su desktop e, su mobile, in fondo alla sidebar
+ (`lg:hidden`, dopo un separatore) — sull'header è `hidden sm:flex`. Non esiste
+ un campo "username" né "cognome" separato: `name` è il nome completo
+ (etichettato "Nome e cognome") e l'email fa da identificativo. La qualifica
+ (ruolo) non è modificabile dal profilo — resta in gestione all'admin.
- **Viewer PDF pubblico**: oltre a territorio/proclamatore/data mostra ora
*giorni assegnato*, *consegna prevista* e *scadenza*. Sono attributi calcolati
su `Assegnazione` (`data_consegna_presunta`, `giorni_alla_scadenza`) basati
diff --git a/app/Http/Controllers/AssignmentPdfController.php b/app/Http/Controllers/AssignmentPdfController.php
index fe104b7..f1443bd 100644
--- a/app/Http/Controllers/AssignmentPdfController.php
+++ b/app/Http/Controllers/AssignmentPdfController.php
@@ -65,9 +65,11 @@ class AssignmentPdfController extends Controller
return null;
}
- $ttlMonths = max(1, (int) \App\Models\Setting::getValue('assignment_link_ttl_hours', 1));
+ // Il link scade quando l'assegnazione raggiunge la soglia rientro
+ // (giorni_per_smarrito), cioè quando il territorio diventa "da rientrare".
+ $soglia = max(1, (int) \App\Models\Setting::getValue('giorni_per_smarrito', 120));
- if ($assignment->assigned_at->copy()->addMonths($ttlMonths)->isPast()) {
+ if ($assignment->assigned_at->copy()->addDays($soglia)->isPast()) {
return view('assignments.link-scaduto', [
'numero' => $assignment->territorio?->numero,
]);
diff --git a/app/Http/Controllers/ShortPdfLinkController.php b/app/Http/Controllers/ShortPdfLinkController.php
index 5bf395a..d5fbc4b 100644
--- a/app/Http/Controllers/ShortPdfLinkController.php
+++ b/app/Http/Controllers/ShortPdfLinkController.php
@@ -15,10 +15,11 @@ class ShortPdfLinkController extends Controller
abort_unless($assignment->is_aperta, 403);
abort_unless($assignment->territorio?->pdf_path, 404);
- // Unauthenticated users (proclamatori) are subject to link TTL
+ // Unauthenticated users (proclamatori) are subject to the link expiry:
+ // il link scade al raggiungimento della soglia rientro (giorni_per_smarrito).
if (! auth()->check()) {
- $ttlMonths = max(1, (int) \App\Models\Setting::getValue('assignment_link_ttl_hours', 1));
- if ($assignment->assigned_at->copy()->addMonths($ttlMonths)->isPast()) {
+ $soglia = max(1, (int) \App\Models\Setting::getValue('giorni_per_smarrito', 120));
+ if ($assignment->assigned_at->copy()->addDays($soglia)->isPast()) {
return view('assignments.link-scaduto', [
'numero' => $assignment->territorio?->numero,
]);
diff --git a/app/Livewire/Assegnazioni/Assegna.php b/app/Livewire/Assegnazioni/Assegna.php
index 6380824..00a7661 100644
--- a/app/Livewire/Assegnazioni/Assegna.php
+++ b/app/Livewire/Assegnazioni/Assegna.php
@@ -16,6 +16,7 @@ class Assegna extends Component
public ?int $proclamatore_id = null;
public string $assigned_at = '';
public string $territorioSearch = '';
+ public string $linkSearch = '';
// Optional pre-selection from parent context
public ?int $preselectedTerritorioId = null;
@@ -129,13 +130,31 @@ class Assegna extends Component
->get()
->sortBy(fn($a) => (int) $a->territorio?->numero);
- $linkTtlMonths = max(1, (int) \App\Models\Setting::getValue('assignment_link_ttl_hours', 1));
+ // Ricerca rapida nella lista dei link: numero territorio, zona,
+ // proclamatore in un solo campo. In memoria perché nome/cognome del
+ // proclamatore sono cifrati e non filtrabili via SQL.
+ $assegnazioniAperteVisibili = $assegnazioniAperte;
+ if (trim($this->linkSearch) !== '') {
+ $term = mb_strtolower(trim($this->linkSearch));
+ $assegnazioniAperteVisibili = $assegnazioniAperte->filter(function ($a) use ($term) {
+ $haystack = mb_strtolower(
+ ($a->territorio?->numero ?? '') . ' ' .
+ ($a->territorio?->zona?->nome ?? '') . ' ' .
+ ($a->proclamatore?->nome_completo ?? '')
+ );
+ return str_contains($haystack, $term);
+ })->values();
+ }
+
+ // Il link scade alla soglia rientro (giorni_per_smarrito).
+ $sogliaRientro = max(1, (int) \App\Models\Setting::getValue('giorni_per_smarrito', 120));
return view('livewire.assegnazioni.assegna', [
'territoriDisponibili' => $territoriDisponibili,
'proclamatoriAttivi' => $proclamatoriAttivi,
'assegnazioniAperte' => $assegnazioniAperte,
- 'linkTtlMonths' => $linkTtlMonths,
+ 'assegnazioniAperteVisibili' => $assegnazioniAperteVisibili,
+ 'sogliaRientro' => $sogliaRientro,
]);
}
}
diff --git a/app/Livewire/Profile.php b/app/Livewire/Profile.php
index d21f60b..ef487b6 100644
--- a/app/Livewire/Profile.php
+++ b/app/Livewire/Profile.php
@@ -21,7 +21,7 @@ class Profile extends Component
$this->email = $user->email;
}
- public function updateProfile(): void
+ public function updateProfile()
{
$user = auth()->user();
@@ -36,6 +36,10 @@ class Profile extends Component
]);
session()->flash('profile_success', 'Profilo aggiornato con successo.');
+
+ // Ricarica la pagina così header e card mostrano subito i nuovi dati
+ // (l'header vive nel layout, fuori dal componente).
+ return $this->redirect(route('profile'), navigate: true);
}
public function updatePassword(): void
diff --git a/app/Livewire/Settings/SettingsEdit.php b/app/Livewire/Settings/SettingsEdit.php
index ac01b39..24d371d 100644
--- a/app/Livewire/Settings/SettingsEdit.php
+++ b/app/Livewire/Settings/SettingsEdit.php
@@ -12,7 +12,6 @@ class SettingsEdit extends Component
public int $giorni_giacenza_prioritari = 180;
public int $giorni_per_smarrito = 120;
public int $home_limit_list = 10;
- public int $assignment_link_ttl_months = 1;
public bool $pdf_viewer_show_download = true;
public int $audit_retention_days = 365;
@@ -24,7 +23,6 @@ class SettingsEdit extends Component
$this->giorni_giacenza_prioritari = $settings->giorni_giacenza_prioritari ?? 180;
$this->giorni_per_smarrito = $settings->giorni_per_smarrito ?? 120;
$this->home_limit_list = $settings->home_limit_list ?? 10;
- $this->assignment_link_ttl_months = $settings->assignment_link_ttl_hours ?? 1;
$this->pdf_viewer_show_download = $settings->pdf_viewer_show_download ?? true;
$this->audit_retention_days = $settings->audit_retention_days ?? 365;
}
@@ -37,7 +35,6 @@ class SettingsEdit extends Component
'giorni_giacenza_prioritari' => 'required|integer|min:1|max:730',
'giorni_per_smarrito' => 'required|integer|min:30|max:365',
'home_limit_list' => 'required|integer|min:1|max:100',
- 'assignment_link_ttl_months' => 'required|integer|min:1|max:24',
'pdf_viewer_show_download' => 'required|boolean',
'audit_retention_days' => 'required|integer|min:30|max:3650',
];
@@ -54,7 +51,6 @@ class SettingsEdit extends Component
'giorni_giacenza_prioritari' => $this->giorni_giacenza_prioritari,
'giorni_per_smarrito' => $this->giorni_per_smarrito,
'home_limit_list' => $this->home_limit_list,
- 'assignment_link_ttl_hours' => $this->assignment_link_ttl_months,
'pdf_viewer_show_download' => $this->pdf_viewer_show_download,
'audit_retention_days' => $this->audit_retention_days,
]);
diff --git a/app/Livewire/Territori/TerritorioShow.php b/app/Livewire/Territori/TerritorioShow.php
index 9cecc9f..b0dc808 100644
--- a/app/Livewire/Territori/TerritorioShow.php
+++ b/app/Livewire/Territori/TerritorioShow.php
@@ -5,7 +5,6 @@ namespace App\Livewire\Territori;
use Livewire\Component;
use App\Models\Territorio;
use App\Models\Assegnazione;
-use App\Models\Setting;
class TerritorioShow extends Component
{
@@ -26,7 +25,7 @@ class TerritorioShow extends Component
return view('livewire.territori.territorio-show', [
'activeAssignment' => $this->territorio->assegnazioneCorrente,
- 'assignmentLinkTtlMonths' => (int) Setting::getValue('assignment_link_ttl_hours', 1),
+ 'assignmentScadenza' => $this->territorio->assegnazioneCorrente?->data_consegna_presunta,
'assegnazioniPerAnno' => $assegnazioni,
]);
}
diff --git a/resources/views/components/layouts/app.blade.php b/resources/views/components/layouts/app.blade.php
index 8a45775..57ca7ac 100644
--- a/resources/views/components/layouts/app.blade.php
+++ b/resources/views/components/layouts/app.blade.php
@@ -160,6 +160,16 @@
@endcan
+
+ {{-- Utente (solo mobile: su desktop è nell'header) --}}
+
+
+
+ {{ auth()->user()->name }}
+ {{ ucfirst(auth()->user()->roles->first()?->name ?? 'utente') }}
+
+
diff --git a/resources/views/livewire/assegnazioni/assegna.blade.php b/resources/views/livewire/assegnazioni/assegna.blade.php
index 6c69e06..65dbd7c 100644
--- a/resources/views/livewire/assegnazioni/assegna.blade.php
+++ b/resources/views/livewire/assegnazioni/assegna.blade.php
@@ -99,14 +99,25 @@