diff --git a/app/Livewire/Assegnazioni/Assegna.php b/app/Livewire/Assegnazioni/Assegna.php index c8ca587..ee7f595 100644 --- a/app/Livewire/Assegnazioni/Assegna.php +++ b/app/Livewire/Assegnazioni/Assegna.php @@ -116,9 +116,16 @@ class Assegna extends Component ->get() ->sortBy(fn($p) => mb_strtolower($p->cognome . ' ' . $p->nome)); + // All currently assigned territories with links + $assegnazioniAperte = Assegnazione::aperte() + ->with(['territorio.zona', 'proclamatore']) + ->get() + ->sortBy(fn($a) => (int) $a->territorio?->numero); + return view('livewire.assegnazioni.assegna', [ 'territoriDisponibili' => $territoriDisponibili, 'proclamatoriAttivi' => $proclamatoriAttivi, + 'assegnazioniAperte' => $assegnazioniAperte, ]); } } diff --git a/app/Livewire/Home.php b/app/Livewire/Home.php index 4859359..5076db6 100644 --- a/app/Livewire/Home.php +++ b/app/Livewire/Home.php @@ -9,9 +9,83 @@ 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(); + $priorityThreshold = (int) ($settings->giorni_giacenza_prioritari ?? 180); + + $territori = Territorio::inReparto() + ->with('zona', 'tipologia', 'ultimaAssegnazione') + ->get() + ->map(function (Territorio $territorio) use ($priorityThreshold) { + $ultima = $territorio->ultimaAssegnazione; + if ($ultima && $ultima->returned_at) { + $giorniGiacenza = $ultima->returned_at->startOfDay()->diffInDays(today()); + } elseif (! $ultima) { + $giorniGiacenza = $territorio->created_at->startOfDay()->diffInDays(today()); + } else { + $giorniGiacenza = 0; + } + $territorio->setAttribute('home_giorni_giacenza', $giorniGiacenza); + $territorio->setAttribute( + 'home_is_prioritario', + (bool) $territorio->prioritario || $giorniGiacenza > $priorityThreshold + ); + return $territorio; + }) + ->sort(function (Territorio $left, Territorio $right) { + $p = (int) $right->home_is_prioritario <=> (int) $left->home_is_prioritario; + if ($p !== 0) return $p; + $g = $right->home_giorni_giacenza <=> $left->home_giorni_giacenza; + if ($g !== 0) return $g; + return strnatcasecmp((string) $left->numero, (string) $right->numero); + }) + ->values(); + + $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(); diff --git a/app/Livewire/Registro.php b/app/Livewire/Registro.php index 1f30906..40e5b4e 100644 --- a/app/Livewire/Registro.php +++ b/app/Livewire/Registro.php @@ -21,8 +21,8 @@ class Registro extends Component public string $filtroZona = ''; public string $filtroTipologia = ''; public string $filtroStato = ''; // aperte, chiuse - public string $sortField = 'assigned_at'; - public string $sortDirection = 'desc'; + public string $sortField = 'territorio_numero'; + public string $sortDirection = 'asc'; // ─── Modal create/edit ────────────────────────────────────── public bool $showModal = false; @@ -149,6 +149,14 @@ class Registro extends Component $this->showDeleteConfirm = false; } + public function mount(): void + { + if ($this->filtroAnno === '') { + $annoCorrente = AnnoTeocratico::corrente(); + $this->filtroAnno = (string) $annoCorrente->id; + } + } + public function render() { $query = Assegnazione::with(['territorio.zona', 'territorio.assegnazioneCorrente', 'proclamatore', 'annoTeocratico', 'campagna']); @@ -171,7 +179,14 @@ class Registro extends Component $query->whereHas('territorio', fn($q) => $q->where('tipologia_id', $this->filtroTipologia)); } - $query->orderBy($this->sortField, $this->sortDirection); + if ($this->sortField === 'territorio_numero') { + $dir = $this->sortDirection === 'asc' ? 'ASC' : 'DESC'; + $query->orderByRaw( + "CAST((SELECT numero FROM territori WHERE territori.id = assegnazioni.territorio_id) AS UNSIGNED) $dir" + ); + } else { + $query->orderBy($this->sortField, $this->sortDirection); + } // In-memory search for encrypted proclamatore fields / territorio numero if ($this->search !== '') { diff --git a/app/Livewire/Settings/SettingsEdit.php b/app/Livewire/Settings/SettingsEdit.php index 05b00af..2edf6c0 100644 --- a/app/Livewire/Settings/SettingsEdit.php +++ b/app/Livewire/Settings/SettingsEdit.php @@ -8,6 +8,7 @@ use App\Models\Setting; class SettingsEdit extends Component { public string $congregazione_nome = ''; + public string $public_base_url = ''; public int $giorni_giacenza_da_assegnare = 120; public int $giorni_giacenza_prioritari = 180; public int $giorni_per_smarrito = 120; @@ -19,6 +20,7 @@ class SettingsEdit extends Component { $settings = Setting::instance(); $this->congregazione_nome = $settings->congregazione_nome ?? ''; + $this->public_base_url = $settings->public_base_url ?? ''; $this->giorni_giacenza_da_assegnare = $settings->giorni_giacenza_da_assegnare ?? 120; $this->giorni_giacenza_prioritari = $settings->giorni_giacenza_prioritari ?? 180; $this->giorni_per_smarrito = $settings->giorni_per_smarrito ?? 120; @@ -31,6 +33,7 @@ class SettingsEdit extends Component { return [ 'congregazione_nome' => 'required|string|max:255', + 'public_base_url' => 'nullable|url|max:255', 'giorni_giacenza_da_assegnare' => 'required|integer|min:1|max:730', 'giorni_giacenza_prioritari' => 'required|integer|min:1|max:730', 'giorni_per_smarrito' => 'required|integer|min:30|max:365', @@ -47,6 +50,7 @@ class SettingsEdit extends Component $settings = Setting::instance(); $settings->update([ 'congregazione_nome' => $this->congregazione_nome, + 'public_base_url' => $this->public_base_url ?: null, 'giorni_giacenza_da_assegnare' => $this->giorni_giacenza_da_assegnare, 'giorni_giacenza_prioritari' => $this->giorni_giacenza_prioritari, 'giorni_per_smarrito' => $this->giorni_per_smarrito, diff --git a/app/Models/Assegnazione.php b/app/Models/Assegnazione.php index 2cce97f..23c1afd 100644 --- a/app/Models/Assegnazione.php +++ b/app/Models/Assegnazione.php @@ -105,7 +105,7 @@ class Assegnazione extends Model $months = max(1, (int) Setting::getValue('assignment_link_ttl_hours', 1)); - return URL::temporarySignedRoute( + $url = URL::temporarySignedRoute( 'assignments.pdf.viewer', now()->addMonths($months), [ @@ -113,6 +113,14 @@ class Assegnazione extends Model 'code' => $this->ensurePdfAccessCode(), ] ); + + $publicBase = Setting::getValue('public_base_url'); + if ($publicBase) { + $parsed = parse_url($url); + $url = rtrim($publicBase, '/') . ($parsed['path'] ?? '') . (isset($parsed['query']) ? '?' . $parsed['query'] : ''); + } + + return $url; } // ─── Scopes ───────────────────────────────────────────────── diff --git a/app/Models/Setting.php b/app/Models/Setting.php index 212446d..846bb2f 100644 --- a/app/Models/Setting.php +++ b/app/Models/Setting.php @@ -10,6 +10,7 @@ class Setting extends Model protected $fillable = [ 'congregazione_nome', + 'public_base_url', 'logo_path', 'giorni_giacenza_da_assegnare', 'giorni_giacenza_prioritari', diff --git a/database/migrations/2026_04_12_000001_add_public_base_url_to_settings_table.php b/database/migrations/2026_04_12_000001_add_public_base_url_to_settings_table.php new file mode 100644 index 0000000..33a4a0e --- /dev/null +++ b/database/migrations/2026_04_12_000001_add_public_base_url_to_settings_table.php @@ -0,0 +1,22 @@ +string('public_base_url')->nullable()->after('congregazione_nome'); + }); + } + + public function down(): void + { + Schema::table('settings', function (Blueprint $table) { + $table->dropColumn('public_base_url'); + }); + } +}; diff --git a/public/build/manifest.json b/public/build/manifest.json index 00ccfc7..39bb95d 100644 --- a/public/build/manifest.json +++ b/public/build/manifest.json @@ -1,6 +1,6 @@ { "resources/css/app.css": { - "file": "assets/app-DtfTB2Uz.css", + "file": "assets/app-D-SuF610.css", "src": "resources/css/app.css", "isEntry": true }, diff --git a/resources/views/livewire/assegnazioni/assegna.blade.php b/resources/views/livewire/assegnazioni/assegna.blade.php index 11f6561..40d70e1 100644 --- a/resources/views/livewire/assegnazioni/assegna.blade.php +++ b/resources/views/livewire/assegnazioni/assegna.blade.php @@ -8,7 +8,7 @@

Questa funzione consente un'assegnazione arbitraria, indipendente dalle tre schede della Home.

-
+
@if(!$preselectedTerritorioId) @@ -74,4 +74,54 @@
+ + {{-- Elenco territori attualmente assegnati con link --}} + @if($assegnazioniAperte->count()) +
+
+
+ +
+
+

Territori Assegnati ({{ $assegnazioniAperte->count() }})

+

Per ogni territorio è visibile il link da condividere

+
+
+
+ @foreach($assegnazioniAperte as $a) + @php($pdfUrl = $a->temporaryPdfViewerUrl()) +
+
+
+ N° {{ $a->territorio?->numero }} + {{ $a->territorio?->zona?->nome }} +
+
+ {{ $a->proclamatore?->nome_completo ?? 'N/A' }} + {{ $a->assigned_at->format('d/m/Y') }} + {{ $a->giorni }}g +
+
+ @if($pdfUrl) +
+ {{ $pdfUrl }} + + Copiato! +
+ @else +

Nessun PDF disponibile

+ @endif +
+ @endforeach +
+
+ @endif
diff --git a/resources/views/livewire/home.blade.php b/resources/views/livewire/home.blade.php index d6d34a6..fa6c8ef 100644 --- a/resources/views/livewire/home.blade.php +++ b/resources/views/livewire/home.blade.php @@ -90,6 +90,11 @@

Da Assegnare

Prima i prioritari, poi i territori con più tempo in reparto

+ @hasanyrole('amministratore|assistente') + + @endhasanyrole @if($territoriDaAssegnare->count() >= $homeLimit)
- Vedi tutti → + Vedi tutti →
@endif @@ -136,6 +141,11 @@

Da Rientrare

Territori assegnati da più tempo

+ @hasanyrole('amministratore|assistente') + + @endhasanyrole @if($daRientrare->count() >= $homeLimit)
- Vedi tutti → + Vedi tutti →
@endif diff --git a/resources/views/livewire/registro.blade.php b/resources/views/livewire/registro.blade.php index 758e293..326d55f 100644 --- a/resources/views/livewire/registro.blade.php +++ b/resources/views/livewire/registro.blade.php @@ -45,7 +45,9 @@ - +
Territorio + Territorio @if($sortField==='territorio_numero') {{ $sortDirection==='asc'?'▲':'▼' }} @endif + Proclamatore Assegnato @if($sortField==='assigned_at') {{ $sortDirection==='asc'?'▲':'▼' }} @endif @@ -99,22 +101,6 @@ @can('settings.manage')
- @can('territori.assign') - @if($a->territorio?->attivo && !$a->territorio?->assegnazioneCorrente) - - - - @endif - @endcan - @can('territori.return') - @if(!$a->returned_at) - - - - @endif - @endcan @if($temporaryPdfUrl) {{ $message }}

@enderror
+
+ +

L'indirizzo con cui l'app è raggiungibile dall'esterno (es. https://territori.miacongregazione.it). Se vuoto, verrà usato l'URL corrente del server.

+ + @error('public_base_url')

{{ $message }}

@enderror +
+

Dopo quanti giorni dal rientro un territorio compare nella lista "da assegnare".

diff --git a/resources/views/pdf/territori-lista.blade.php b/resources/views/pdf/territori-lista.blade.php new file mode 100644 index 0000000..058f5e1 --- /dev/null +++ b/resources/views/pdf/territori-lista.blade.php @@ -0,0 +1,102 @@ + + + + + {{ $titolo }} + + + +
+

{{ $titolo }}

+
{{ $congregazione }} — {{ $data }}
+
+ + @if($tipo === 'assegnare') + + + + + + + + + + + + + @forelse($territori as $t) + + + + + + + + + @empty + + @endforelse + +
ZonaTipologiaGiorni in repartoPrioritàNote
{{ $t->numero }}{{ $t->zona?->nome ?? '-' }}{{ $t->tipologia?->nome ?? '-' }}{{ $t->home_giorni_giacenza }} gg + @if($t->home_is_prioritario) + ★ Prioritario + @else + - + @endif + {{ \Illuminate\Support\Str::limit($t->note, 40) }}
Nessun territorio da assegnare
+
Totale: {{ $territori->count() }} territori
+ @endif + + @if($tipo === 'rientrare') + + + + + + + + + + + + + @forelse($territori as $t) + @php($a = $t->assegnazioneCorrente) + + + + + + + + + @empty + + @endforelse + +
ZonaAssegnatarioAssegnato ilGiorniNote
{{ $t->numero }}{{ $t->zona?->nome ?? '-' }}{{ $a?->proclamatore?->nome_completo ?? '-' }}{{ $a?->assigned_at?->format('d/m/Y') }} + {{ $a?->giorni ?? '-' }} gg + {{ \Illuminate\Support\Str::limit($t->note, 40) }}
Nessun territorio da rientrare
+
Totale: {{ $territori->count() }} territori
+ @endif + + + +