Files
termanager2/resources/views/livewire/home.blade.php
Francesco Picone 7904c96db8 ++ Richieste dal modulo pubblico, territori cartacei, S-13 mensile per email
- /richiesta: modulo pubblico (nuovo territorio o riconsegna) con campo
  trappola, tempo minimo e limite orario; nome e note cifrati, avviso per
  email a chi ha richieste.gestisci (amministratore, assistente)
- Home: riquadro Richieste con i proclamatori dal nome simile; porta su
  Assegna/Rientra già esistenti, che chiudono la richiesta; Scarta
- Cartaceo sull'assegnazione (Assegna e Il mio gruppo), etichetta
  nell'elenco degli assegnati, pagina Cartacei, passa per l'XML
- registro:invia-s13 agli amministratori l'ultima domenica del mese;
  richieste:pulizia dopo 90 giorni dalla gestione
- Privacy, README e HANDOFF aggiornati; test in RichiesteTest

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 17:32:09 +02:00

244 lines
17 KiB
PHP

<div>
{{-- Header --}}
<div class="mb-6">
<h1 class="text-2xl font-bold text-gray-900">Dashboard</h1>
@if($annoCorrente)
<p class="text-sm text-gray-500 mt-1">Anno Teocratico {{ $annoCorrente->label }} — {{ $annoCorrente->mesi_trascorsi }} mesi trascorsi</p>
@endif
</div>
{{-- Stats cards --}}
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 card-hover card-accent-indigo">
<div class="flex items-center justify-between">
<div>
<p class="text-xs font-medium text-gray-500 uppercase">Territori Attivi</p>
<p class="mt-1 text-3xl font-bold text-gray-900">{{ $totTerritoriAttivi }}</p>
</div>
<div class="h-10 w-10 rounded-lg flex items-center justify-center" style="background:#eef2ff">
<svg class="h-5 w-5" style="color:#6366f1" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7"/></svg>
</div>
</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 card-hover card-accent-blue">
<div class="flex items-center justify-between">
<div>
<p class="text-xs font-medium text-gray-500 uppercase">Assegnati</p>
<p class="mt-1 text-3xl font-bold" style="color:#3b82f6">{{ $totAssegnati }}</p>
<p class="text-xs text-gray-500">{{ $totInReparto }} in reparto</p>
</div>
<div class="h-10 w-10 rounded-lg flex items-center justify-center" style="background:#dbeafe">
<svg class="h-5 w-5" style="color:#3b82f6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
</div>
</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 card-hover card-accent-green">
<div class="flex items-center justify-between">
<div>
<p class="text-xs font-medium text-gray-500 uppercase">Percorsi (anno)</p>
<p class="mt-1 text-3xl font-bold" style="color:#22c55e">{{ $territoriPercorsi }}</p>
<p class="text-xs text-gray-500">durata media {{ $mediaDurataPercorrenzaMesi }} mesi</p>
</div>
<div class="h-10 w-10 rounded-lg flex items-center justify-center" style="background:#dcfce7">
<svg class="h-5 w-5" style="color:#22c55e" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
</div>
</div>
</div>
@if($campagnaStats)
<div class="rounded-xl shadow-sm border p-4 card-hover" style="background:#fffbeb;border-color:#fde68a;border-left:4px solid #f59e0b">
<div class="flex items-center justify-between mb-1">
<p class="text-xs font-medium uppercase" style="color:#d97706">Campagna</p>
<div class="h-8 w-8 rounded-lg flex items-center justify-center" style="background:#fef3c7">
<svg class="h-4 w-4" style="color:#f59e0b" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z"/></svg>
</div>
</div>
<p class="text-sm font-bold" style="color:#92400e">{{ $campagnaStats['descrizione'] }}</p>
<div class="mt-2">
<div class="flex justify-between text-xs mb-1" style="color:#b45309">
<span>{{ $campagnaStats['percentuale'] }}%</span>
<span>scade {{ $campagnaStats['fine'] }}</span>
</div>
<div class="w-full rounded-full h-2" style="background:#fde68a">
<div class="h-2 rounded-full transition-all" style="background:#f59e0b;width:{{ min($campagnaStats['percentuale'], 100) }}%"></div>
</div>
</div>
</div>
@else
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 card-hover card-accent-amber">
<div class="flex items-center justify-between">
<div>
<p class="text-xs font-medium text-gray-500 uppercase">Campagna</p>
<p class="mt-1 text-sm text-gray-400">Nessuna campagna attiva</p>
</div>
<div class="h-10 w-10 rounded-lg flex items-center justify-center" style="background:#fef3c7">
<svg class="h-5 w-5" style="color:#d97706" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z"/></svg>
</div>
</div>
</div>
@endif
</div>
{{-- Richieste dal modulo pubblico: si confermano portandole sul modulo
di assegnazione o di rientro, che le chiude da solo. --}}
@can('richieste.gestisci')
<section class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden mb-6" aria-labelledby="titolo-richieste">
<div class="px-5 py-4 border-b flex flex-wrap items-center gap-3" style="background:linear-gradient(135deg,#eef2ff,#e0e7ff);border-color:#c7d2fe">
<div>
<h3 id="titolo-richieste" class="text-sm font-semibold" style="color:#3730a3">Richieste ({{ $richieste->count() }})</h3>
<p class="text-xs" style="color:#4f46e5">Dal modulo pubblico: <code class="select-all">{{ route('richiesta') }}</code></p>
</div>
</div>
@forelse($richieste as $voce)
@php
$r = $voce['richiesta'];
$a = $voce['assegnazione'];
@endphp
<div class="px-5 py-4 border-b border-gray-100 last:border-b-0" wire:key="richiesta-{{ $r->id }}">
<div class="flex flex-wrap items-baseline justify-between gap-2">
<p class="text-sm text-gray-900">
<span class="inline-flex rounded-full px-2 py-0.5 text-xs font-medium {{ $r->tipo === \App\Models\Richiesta::RICONSEGNA ? 'bg-amber-50 text-amber-800' : 'bg-indigo-50 text-indigo-700' }}">{{ $r->tipo_etichetta }}</span>
<strong class="ml-1">{{ $r->nome }}</strong>
@if($r->numero_territorio) · territorio N° {{ $r->numero_territorio }} @endif
</p>
<span class="text-xs text-gray-500">{{ $r->created_at->format('d/m/Y H:i') }}</span>
</div>
@if($r->note)
<p class="mt-1 text-sm text-gray-600 italic">«{{ $r->note }}»</p>
@endif
<div class="mt-3 flex flex-wrap items-center gap-2">
@if($r->tipo === \App\Models\Richiesta::NUOVO)
<span class="text-xs text-gray-500">{{ $voce['simili']->isNotEmpty() ? 'Chi è? Assegna a:' : 'Nessun proclamatore con un nome simile.' }}</span>
@foreach($voce['simili'] as $p)
<x-btn data-ritorno variante="secondary" tono="tenue" dimensione="sm"
:href="route('assegnazioni.assegna', ['proclamatore' => $p->id, 'richiesta' => $r->id, 'dalla_home' => 1])">{{ $p->nome_completo }} →</x-btn>
@endforeach
<x-btn data-ritorno variante="ghost" dimensione="sm"
:href="route('assegnazioni.assegna', ['richiesta' => $r->id, 'dalla_home' => 1])">{{ $voce['simili']->isNotEmpty() ? 'Nessuno di questi: apri il modulo' : 'Apri il modulo' }}</x-btn>
@elseif($a)
<span class="text-xs text-gray-500">Assegnato a <strong>{{ $a->proclamatore?->nome_completo }}</strong> dal {{ $a->assigned_at->format('d/m/Y') }}</span>
<x-btn data-ritorno variante="primary" tono="tenue" dimensione="sm"
:href="route('assegnazioni.rientra', ['assegnazione' => $a->id, 'richiesta' => $r->id])">Registra rientro →</x-btn>
@else
<span class="text-xs text-amber-800">Il territorio N° {{ $r->numero_territorio }} non risulta assegnato.</span>
@endif
<x-btn variante="danger" tono="tenue" dimensione="sm" class="ml-auto"
wire:click="scartaRichiesta({{ $r->id }})"
wire:confirm="Scartare la richiesta di {{ $r->nome }}?">Scarta</x-btn>
</div>
</div>
@empty
<p class="px-5 py-4 text-sm text-gray-500">Nessuna richiesta aperta.</p>
@endforelse
</section>
@endcan
{{-- Quick lists --}}
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
{{-- Da assegnare --}}
<div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden card-hover">
<div class="px-5 py-4 border-b flex flex-wrap items-center gap-3" style="background:linear-gradient(135deg,#f0fdf4,#dcfce7);border-color:#bbf7d0">
<div class="h-8 w-8 rounded-lg flex items-center justify-center" style="background:#22c55e">
<svg class="h-4 w-4 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/></svg>
</div>
<div>
<h3 class="text-sm font-semibold" style="color:#166534">Da Assegnare</h3>
<p class="text-xs" style="color:#15803d">Prima i prioritari, poi i territori con più tempo in reparto</p>
</div>
{{-- "Vedi tutti" in testata, non in fondo all'elenco. --}}
<div class="ml-auto flex flex-none items-center gap-2">
@if($territoriDaAssegnare->count() >= $homeLimit)
<x-btn variante="secondary" tono="tenue" dimensione="sm" :href="route('territori.index', ['filterStato' => 'in_reparto'])">Vedi tutti</x-btn>
@endif
@hasanyrole('amministratore|assistente')
<x-btn variante="secondary" tono="tenue" dimensione="sm" icona
wire:click="downloadPdfDaAssegnare"
title="Scarica PDF" aria-label="Scarica in PDF l elenco dei territori da assegnare">
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
</x-btn>
@endhasanyrole
</div>
</div>
<ul class="divide-y divide-gray-100">
@forelse($territoriDaAssegnare as $t)
<li class="px-5 py-3 flex items-center justify-between hover:bg-gray-50 transition-colors">
<div>
<div class="flex items-center gap-2">
<a data-ritorno href="{{ route('territori.show', $t) }}" class="text-sm font-semibold text-gray-900 hover:text-indigo-600 transition-colors">N° {{ $t->numero }}</a>
@if($t->in_priorita)
<span class="inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide badge-pulse" style="background:#fef3c7;color:#92400e">★ Prioritario</span>
@endif
</div>
<p class="text-xs text-gray-500">
{{ $t->zona?->nome }} — {{ $t->tipologia?->nome }}
@if($t->giacenza_giorni > 0)
— <span class="font-medium">{{ $t->giacenza_giorni }} gg</span> in reparto
@endif
</p>
</div>
@can('territori.assign')
<x-btn data-ritorno variante="primary" tono="tenue" dimensione="sm" :href="route('assegnazioni.assegna', ['territorioId' => $t->id, 'dalla_home' => 1])">Assegna →</x-btn>
@endcan
</li>
@empty
<li class="empty-state">
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
<span class="text-sm">Tutti i territori sono assegnati</span>
</li>
@endforelse
</ul>
</div>
{{-- Da rientrare --}}
<div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden card-hover">
<div class="px-5 py-4 border-b flex flex-wrap items-center gap-3" style="background:linear-gradient(135deg,#fef2f2,#fee2e2);border-color:#fecaca">
<div class="h-8 w-8 rounded-lg flex items-center justify-center" style="background:#ef4444">
<svg class="h-4 w-4 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
</div>
<div>
<h3 class="text-sm font-semibold" style="color:#991b1b">Da Rientrare</h3>
<p class="text-xs" style="color:#b91c1c">Territori assegnati da più tempo</p>
</div>
<div class="ml-auto flex flex-none items-center gap-2">
@if($daRientrare->count() >= $homeLimit)
<x-btn variante="secondary" tono="tenue" dimensione="sm" :href="route('territori.index', ['filterStato' => 'da_rientrare'])">Vedi tutti</x-btn>
@endif
@hasanyrole('amministratore|assistente')
{{-- Scaricare un elenco non è distruttivo: era rosso solo perché
lo è la scheda che lo contiene. --}}
<x-btn variante="secondary" tono="tenue" dimensione="sm" icona
wire:click="downloadPdfDaRientrare"
title="Scarica PDF" aria-label="Scarica in PDF l elenco dei territori da rientrare">
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
</x-btn>
@endhasanyrole
</div>
</div>
<ul class="divide-y divide-gray-100">
@forelse($daRientrare as $t)
@php($assegnazioneCorrente = $t->assegnazioneCorrente)
<li class="px-5 py-3 flex items-center justify-between hover:bg-gray-50 transition-colors">
<div>
<a data-ritorno href="{{ route('territori.show', $t) }}" class="text-sm font-semibold text-gray-900 hover:text-indigo-600 transition-colors">N° {{ $t->numero }}</a>
<p class="text-xs text-gray-500">
{{ $assegnazioneCorrente?->proclamatore?->nome_completo }}
— <span class="font-medium" style="color:#dc2626">{{ $assegnazioneCorrente?->giorni }} gg</span>
</p>
</div>
@can('territori.return')
@if($assegnazioneCorrente)
<x-btn data-ritorno variante="primary" tono="tenue" dimensione="sm" :href="route('assegnazioni.rientra', ['assegnazione' => $assegnazioneCorrente->id])">Rientra →</x-btn>
@endif
@endcan
</li>
@empty
<li class="empty-state">
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
<span class="text-sm">Nessun territorio da rientrare</span>
</li>
@endforelse
</ul>
</div>
</div>
</div>