Files
termanager2/app/Livewire/Home.php
Francesco Picone ce98165667 ++ gruppi: le azioni del sorvegliante non finiscono piu' in 403; priorita' e filtri nel pacchetto; nome del sorvegliante
- LimitaSorvegliante: la rotta delle azioni di Livewire 3.7 si chiama
  "default.livewire.update"; con 'livewire.*' ogni clic era un 403.
- Territorio::ordinaPerPriorita(): la regola di "Da assegnare" della Home in
  un metodo solo, usato dalla Home, dall'aggiunta al pacchetto e da "Il mio
  gruppo".
- Aggiunta al pacchetto: ricerca, zona, tipologia, solo prioritari,
  "Seleziona tutti quelli mostrati", badge e giorni di fermo.
- Il mio gruppo: da dare in ordine di priorita' poi i gia' dati; filtri
  stato, zona, tipologia, solo prioritari; sotto il titolo nome e cognome
  del sorvegliante.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 13:58:53 +02:00

153 lines
5.9 KiB
PHP

<?php
namespace App\Livewire;
use Livewire\Component;
use App\Models\Territorio;
use App\Models\Proclamatore;
use App\Models\Assegnazione;
use App\Models\AnnoTeocratico;
use App\Models\Campagna;
use App\Models\Setting;
use Barryvdh\DomPDF\Facade\Pdf;
class Home extends Component
{
public function downloadPdfDaAssegnare()
{
$this->authorize('territori.assign');
$settings = Setting::instance();
$territori = Territorio::ordinaPerPriorita(
Territorio::inReparto()->with('zona', 'tipologia', 'ultimaAssegnazione')->get()
);
$pdf = Pdf::loadView('pdf.territori-lista', [
'titolo' => 'Territori da Assegnare',
'congregazione' => $settings->congregazione_nome ?? 'TerManager2',
'data' => now()->format('d/m/Y'),
'territori' => $territori,
'tipo' => 'assegnare',
]);
return response()->streamDownload(
fn () => print($pdf->output()),
'territori-da-assegnare-' . now()->format('Y-m-d') . '.pdf'
);
}
public function downloadPdfDaRientrare()
{
$this->authorize('territori.return');
$settings = Setting::instance();
$territori = Territorio::daRientrare()
->with(['zona', 'assegnazioneCorrente.proclamatore'])
->get();
$pdf = Pdf::loadView('pdf.territori-lista', [
'titolo' => 'Territori da Rientrare',
'congregazione' => $settings->congregazione_nome ?? 'TerManager2',
'data' => now()->format('d/m/Y'),
'territori' => $territori,
'tipo' => 'rientrare',
]);
return response()->streamDownload(
fn () => print($pdf->output()),
'territori-da-rientrare-' . now()->format('Y-m-d') . '.pdf'
);
}
public function render()
{
$settings = Setting::instance();
$homeLimit = max(1, (int) ($settings->home_limit_list ?? 10));
$annoCorrente = AnnoTeocratico::corrente();
$campagnaAttiva = Campagna::attiva();
// Territory counts
$totTerritoriAttivi = Territorio::where('attivo', true)->count();
$totAssegnati = Territorio::assegnato()->count();
$totInReparto = Territorio::inReparto()->count();
// Coverage: returned territories per current theocratic year
$territoriPercorsi = 0;
if ($annoCorrente) {
$territoriPercorsi = Assegnazione::where('anno_teocratico_id', $annoCorrente->id)
->whereNotNull('returned_at')
->distinct('territorio_id')
->count('territorio_id');
}
// Monthly average (territories/month this year)
$mediaPercorrenzaMensile = 0;
if ($annoCorrente && $annoCorrente->mesi_trascorsi > 0) {
$mediaPercorrenzaMensile = round($territoriPercorsi / $annoCorrente->mesi_trascorsi, 1);
}
// Average assignment duration in months (current year only, matches old app "Media Percorrenza Congregazione")
//
// Il calcolo sta in PHP e non in SQL. La versione precedente usava
// DATEDIFF() e YEAR(), che sono funzioni MySQL: rendevano questa pagina
// — la piu' visitata dell'applicazione — l'unica non verificabile dalla
// suite di test, che gira su SQLite. Il costo e' due colonne di date per
// le assegnazioni chiuse di un anno di servizio: qualche centinaio di
// righe, non un tavolo intero.
//
// I due filtri sono quelli di prima: le date assurde arrivate dalla
// conversione del vecchio database, e le durate non positive, che
// altrimenti abbasserebbero la media senza significare niente.
$avgGiorni = null;
if ($annoCorrente) {
$durate = Assegnazione::where('anno_teocratico_id', $annoCorrente->id)
->whereNotNull('returned_at')
->get(['assigned_at', 'returned_at'])
->filter(fn (Assegnazione $a) => $a->assigned_at?->year >= 1900)
->map(fn (Assegnazione $a) => $a->assigned_at->diffInDays($a->returned_at, false))
->filter(fn (float $giorni) => $giorni > 0);
$avgGiorni = $durate->isEmpty() ? null : $durate->avg();
}
$mediaDurataPercorrenzaMesi = $avgGiorni ? round($avgGiorni / 30.44, 1) : 0;
// Campaign stats
$campagnaStats = null;
if ($campagnaAttiva) {
$campagnaStats = [
'descrizione' => $campagnaAttiva->descrizione,
'percentuale' => $campagnaAttiva->percentuale_percorrenza,
'fine' => $campagnaAttiva->end_date->format('d/m/Y'),
];
}
// Quick lists: la logica di priorità sta in Territorio::ordinaPerPriorita
// (la usano anche il pacchetto dei gruppi di servizio e "Il mio gruppo").
$territoriDaAssegnare = Territorio::ordinaPerPriorita(
Territorio::inReparto()->with('zona', 'tipologia', 'ultimaAssegnazione')->get()
)->take($homeLimit)->values();
$daRientrare = Territorio::daRientrare()
->with(['zona', 'assegnazioneCorrente.proclamatore'])
->take($homeLimit)
->get();
return view('livewire.home', [
'congregazione' => $settings->congregazione_nome ?? 'TerManager2',
'annoCorrente' => $annoCorrente,
'totTerritoriAttivi' => $totTerritoriAttivi,
'totAssegnati' => $totAssegnati,
'totInReparto' => $totInReparto,
'territoriPercorsi' => $territoriPercorsi,
'mediaPercorrenzaMensile' => $mediaPercorrenzaMensile,
'mediaDurataPercorrenzaMesi' => $mediaDurataPercorrenzaMesi,
'campagnaStats' => $campagnaStats,
'homeLimit' => $homeLimit,
'territoriDaAssegnare' => $territoriDaAssegnare,
'daRientrare' => $daRientrare,
]);
}
}