++ fix: Rientro assegnazione e territorio

This commit is contained in:
2026-04-08 11:55:14 +00:00
parent 777f239c7a
commit 6f8010514d
36 changed files with 1186 additions and 91 deletions

View File

@@ -49,15 +49,26 @@ class Home extends Component
}
// Quick lists
$daAssegnare = Territorio::daAssegnare()
$territoriDaAssegnare = Territorio::inReparto()
->with('zona', 'tipologia', 'ultimaAssegnazione')
->take(10)
->get();
->get()
->sort(function (Territorio $left, Territorio $right) {
$priorityComparison = (int) $right->is_prioritario <=> (int) $left->is_prioritario;
$prioritari = Territorio::prioritari()
->with('zona', 'tipologia', 'ultimaAssegnazione')
if ($priorityComparison !== 0) {
return $priorityComparison;
}
$giacenzaComparison = $right->giorni_giacenza <=> $left->giorni_giacenza;
if ($giacenzaComparison !== 0) {
return $giacenzaComparison;
}
return strnatcasecmp((string) $left->numero, (string) $right->numero);
})
->take(10)
->get();
->values();
$daRientrare = Territorio::daRientrare()
->with(['zona', 'assegnazioneCorrente.proclamatore'])
@@ -73,8 +84,7 @@ class Home extends Component
'territoriPercorsi' => $territoriPercorsi,
'mediaPercorrenzaMensile' => $mediaPercorrenzaMensile,
'campagnaStats' => $campagnaStats,
'daAssegnare' => $daAssegnare,
'prioritari' => $prioritari,
'territoriDaAssegnare' => $territoriDaAssegnare,
'daRientrare' => $daRientrare,
]);
}

View File

@@ -2,6 +2,7 @@
namespace App\Livewire\Settings;
use App\Jobs\ImportTerritoryPdfFolder;
use App\Models\AnnoTeocratico;
use App\Models\Assegnazione;
use App\Models\Campagna;
@@ -11,9 +12,12 @@ use App\Models\Territorio;
use App\Models\Tipologia;
use App\Models\User;
use App\Models\Zona;
use App\Services\TerritorioPdfImportDispatcher;
use App\Services\TerritorioPdfImportState;
use Barryvdh\DomPDF\Facade\Pdf;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Livewire\Component;
use Livewire\WithFileUploads;
@@ -25,6 +29,22 @@ class XmlExchange extends Component
public $xmlImport;
public array $importStats = [];
public array $importIssues = [];
public array $pdfFolder = [];
public array $pdfImportLogs = [];
public array $pdfImportStats = [];
public array $pdfImportIssues = [];
public ?string $currentPdfImportId = null;
public string $pdfImportStatus = 'idle';
public string $pdfImportLogText = '';
public function mount(): void
{
$this->currentPdfImportId = request()->query('pdf-import');
if ($this->currentPdfImportId) {
$this->refreshPdfImportStatus();
}
}
public function convertLegacySqlToXml()
{
@@ -257,6 +277,70 @@ class XmlExchange extends Component
session()->flash('success', $message);
}
public function importTerritoryPdfFolder(): void
{
$this->validate([
'pdfFolder' => ['required', 'array', 'min:1'],
'pdfFolder.*' => ['file', 'mimes:pdf', 'max:10240'],
]);
$importId = (string) Str::uuid();
$storedFiles = [];
foreach ($this->pdfFolder as $index => $file) {
$originalName = $file->getClientOriginalName();
$safeName = Str::slug(pathinfo($originalName, PATHINFO_FILENAME));
$extension = strtolower($file->getClientOriginalExtension() ?: 'pdf');
$storedPath = $file->storeAs(
'bulk-territori-imports/' . $importId,
str_pad((string) $index, 4, '0', STR_PAD_LEFT) . '-' . $safeName . '.' . $extension,
'local'
);
$storedFiles[] = [
'original_name' => $originalName,
'stored_path' => $storedPath,
];
}
$this->pdfFolder = [];
}
public function refreshPdfImportStatus(): void
{
if (! $this->currentPdfImportId) {
return;
}
$state = app(TerritorioPdfImportState::class)->get($this->currentPdfImportId);
if (! $state) {
return;
}
$this->pdfImportStatus = $state['status'] ?? 'idle';
$this->pdfImportStats = $state['stats'] ?? [];
$this->pdfImportLogs = $state['logs'] ?? [];
$this->pdfImportIssues = $state['issues'] ?? [];
$this->pdfImportLogText = implode(PHP_EOL, $this->pdfImportLogs);
}
protected function dispatchPdfImport(string $importId, array $storedFiles, string $initialLog): void
{
$state = app(TerritorioPdfImportDispatcher::class)
->dispatchStoredFiles($importId, $storedFiles, auth()->id(), $initialLog);
$this->currentPdfImportId = $importId;
$this->pdfImportStatus = $state['status'] ?? 'queued';
$this->pdfImportStats = $state['stats'] ?? [];
$this->pdfImportLogs = $state['logs'] ?? [];
$this->pdfImportIssues = $state['issues'] ?? [];
$this->refreshPdfImportStatus();
session()->flash('success', 'Import PDF avviato in background. I log si aggiorneranno automaticamente.');
}
public function downloadImportLogPdf()
{
if (empty($this->importStats)) {

View File

@@ -2,6 +2,7 @@
namespace App\Livewire\Territori;
use Illuminate\Pagination\LengthAwarePaginator;
use Livewire\Component;
use Livewire\WithPagination;
use App\Models\Territorio;
@@ -16,6 +17,9 @@ class TerritorioIndex extends Component
public string $filterZona = '';
public string $filterTipologia = '';
public string $filterStato = '';
public string $filterPriorita = '';
public string $filterPdf = '';
public string $filterContenuti = '';
public string $sortField = 'numero';
public string $sortDirection = 'asc';
@@ -24,6 +28,9 @@ class TerritorioIndex extends Component
'filterZona' => ['except' => ''],
'filterTipologia' => ['except' => ''],
'filterStato' => ['except' => ''],
'filterPriorita' => ['except' => ''],
'filterPdf' => ['except' => ''],
'filterContenuti' => ['except' => ''],
];
public function updatingSearch()
@@ -31,6 +38,36 @@ class TerritorioIndex extends Component
$this->resetPage();
}
public function updatingFilterZona()
{
$this->resetPage();
}
public function updatingFilterTipologia()
{
$this->resetPage();
}
public function updatingFilterStato()
{
$this->resetPage();
}
public function updatingFilterPriorita()
{
$this->resetPage();
}
public function updatingFilterPdf()
{
$this->resetPage();
}
public function updatingFilterContenuti()
{
$this->resetPage();
}
public function sortBy(string $field)
{
if ($this->sortField === $field) {
@@ -41,6 +78,22 @@ class TerritorioIndex extends Component
}
}
public function clearFilters(): void
{
$this->reset([
'search',
'filterZona',
'filterTipologia',
'filterStato',
'filterPriorita',
'filterPdf',
'filterContenuti',
]);
$this->sortField = 'numero';
$this->sortDirection = 'asc';
$this->resetPage();
}
public function toggleActive(Territorio $territorio)
{
$territorio->update(['attivo' => !$territorio->attivo]);
@@ -73,7 +126,14 @@ class TerritorioIndex extends Component
$query = Territorio::with(['zona', 'tipologia', 'assegnazioneCorrente.proclamatore']);
if ($this->search) {
$query->where('numero', 'like', "%{$this->search}%");
$search = $this->search;
$query->where(function ($subQuery) use ($search) {
$subQuery->where('numero', 'like', "%{$search}%")
->orWhere('note', 'like', "%{$search}%")
->orWhere('confini', 'like', "%{$search}%")
->orWhereHas('zona', fn($zonaQuery) => $zonaQuery->where('nome', 'like', "%{$search}%"))
->orWhereHas('tipologia', fn($tipologiaQuery) => $tipologiaQuery->where('nome', 'like', "%{$search}%"));
});
}
if ($this->filterZona) {
@@ -90,16 +150,104 @@ class TerritorioIndex extends Component
'assegnato' => $query->assegnato(),
'da_rientrare' => $query->daRientrare(),
'inattivo' => $query->where('attivo', false),
'prioritari' => $query->inReparto(),
default => null,
};
}
$query->orderBy($this->sortField, $this->sortDirection);
if ($this->filterPdf) {
match ($this->filterPdf) {
'con_pdf' => $query->whereNotNull('pdf_path'),
'senza_pdf' => $query->whereNull('pdf_path'),
'con_thumbnail' => $query->whereNotNull('thumbnail_path'),
'senza_thumbnail' => $query->whereNull('thumbnail_path'),
default => null,
};
}
if ($this->filterContenuti) {
match ($this->filterContenuti) {
'con_note' => $query->whereNotNull('note')->where('note', '!=', ''),
'senza_note' => $query->where(function ($subQuery) {
$subQuery->whereNull('note')->orWhere('note', '');
}),
'con_confini' => $query->whereNotNull('confini')->where('confini', '!=', ''),
'senza_confini' => $query->where(function ($subQuery) {
$subQuery->whereNull('confini')->orWhere('confini', '');
}),
default => null,
};
}
$territori = $query->get();
if ($this->filterStato === 'prioritari') {
$territori = $territori->filter(fn(Territorio $territorio) => $territorio->is_prioritario)->values();
}
if ($this->filterPriorita) {
$territori = $territori->filter(function (Territorio $territorio) {
return match ($this->filterPriorita) {
'prioritari' => $territorio->is_prioritario,
'manuali' => $territorio->prioritario,
'automatici' => $territorio->is_prioritario && !$territorio->prioritario,
'non_prioritari' => !$territorio->is_prioritario,
default => true,
};
})->values();
}
if ($this->usesPriorityOrdering()) {
$territori = $territori->sort(function (Territorio $left, Territorio $right) {
$priorityComparison = (int) $right->is_prioritario <=> (int) $left->is_prioritario;
if ($priorityComparison !== 0) {
return $priorityComparison;
}
$giacenzaComparison = $right->giorni_giacenza <=> $left->giorni_giacenza;
if ($giacenzaComparison !== 0) {
return $giacenzaComparison;
}
return strnatcasecmp((string) $left->numero, (string) $right->numero);
})->values();
} else {
$territori = $territori->sort(function (Territorio $left, Territorio $right) {
$result = strnatcasecmp((string) data_get($left, $this->sortField), (string) data_get($right, $this->sortField));
return $this->sortDirection === 'asc' ? $result : -$result;
})->values();
}
$perPage = 20;
$page = $this->getPage();
$items = $territori->slice(($page - 1) * $perPage, $perPage)->values();
$paginatedTerritori = new LengthAwarePaginator(
$items,
$territori->count(),
$perPage,
$page,
['path' => request()->url(), 'query' => request()->query()]
);
return view('livewire.territori.territorio-index', [
'territori' => $query->paginate(20),
'territori' => $paginatedTerritori,
'zone' => Zona::attive()->get(),
'tipologie' => Tipologia::attive()->get(),
'usesPriorityOrdering' => $this->usesPriorityOrdering(),
]);
}
protected function usesPriorityOrdering(): bool
{
return $this->sortField === 'numero'
&& $this->sortDirection === 'asc'
&& (
in_array($this->filterStato, ['in_reparto', 'prioritari'], true)
|| $this->filterPriorita !== ''
);
}
}