++ fix: Rientro assegnazione e territorio
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
<a href="{{ route('territori.index') }}" class="text-sm text-indigo-600 hover:text-indigo-800">← Torna ai territori</a>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6 max-w-lg">
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 sm:p-6 max-w-2xl">
|
||||
<form wire:submit="save" class="space-y-4">
|
||||
<div>
|
||||
@if(!$preselectedTerritorioId)
|
||||
@@ -31,14 +31,17 @@
|
||||
|
||||
@if($territorio_id)
|
||||
<div class="mt-3">
|
||||
<p class="text-xs text-gray-500 mb-1">Anteprima territorio</p>
|
||||
<p class="text-xs text-gray-500 mb-2">Anteprima territorio</p>
|
||||
@if($this->selectedThumbnailUrl)
|
||||
<img src="{{ $this->selectedThumbnailUrl }}"
|
||||
alt="Anteprima territorio"
|
||||
class="rounded-lg border border-gray-200 shadow-sm max-h-64 w-auto">
|
||||
<div class="overflow-hidden rounded-xl border border-gray-200 bg-gray-50 shadow-sm">
|
||||
<img src="{{ $this->selectedThumbnailUrl }}"
|
||||
alt="Thumbnail territorio selezionato"
|
||||
class="block w-full h-auto max-h-[70vh] object-contain bg-white">
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-gray-500">Miniatura del territorio ottimizzata per consultazione rapida anche da mobile.</p>
|
||||
@else
|
||||
<div class="rounded-lg border border-dashed border-gray-300 bg-gray-50 px-4 py-6 text-sm text-gray-500">
|
||||
Nessun PDF disponibile per questo territorio.
|
||||
Nessuna thumbnail disponibile per questo territorio.
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@@ -62,7 +65,7 @@
|
||||
@error('assigned_at') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3 pt-4">
|
||||
<div class="flex flex-col-reverse gap-3 pt-4 sm:flex-row sm:items-center">
|
||||
<button type="submit" class="px-4 py-2 text-sm font-medium text-white bg-indigo-600 rounded-lg hover:bg-indigo-700 transition">Assegna</button>
|
||||
<a href="{{ route('territori.index') }}" class="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200 transition">Annulla</a>
|
||||
</div>
|
||||
|
||||
@@ -46,18 +46,29 @@
|
||||
</div>
|
||||
|
||||
{{-- Quick lists --}}
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<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">
|
||||
<div class="px-4 py-3 bg-green-50 border-b border-green-100">
|
||||
<h3 class="text-sm font-semibold text-green-800">Da Assegnare</h3>
|
||||
<p class="mt-1 text-xs text-green-700">Prima i prioritari, poi i territori con piu tempo in reparto</p>
|
||||
</div>
|
||||
<ul class="divide-y divide-gray-100">
|
||||
@forelse($daAssegnare as $t)
|
||||
@forelse($territoriDaAssegnare as $t)
|
||||
<li class="px-4 py-2.5 flex items-center justify-between hover:bg-gray-50">
|
||||
<div>
|
||||
<a href="{{ route('territori.show', $t) }}" class="text-sm font-semibold text-gray-900 hover:text-indigo-600">N° {{ $t->numero }}</a>
|
||||
<p class="text-xs text-gray-500">{{ $t->zona?->nome }} — {{ $t->tipologia?->nome }}</p>
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('territori.show', $t) }}" class="text-sm font-semibold text-gray-900 hover:text-indigo-600">N° {{ $t->numero }}</a>
|
||||
@if($t->is_prioritario)
|
||||
<span class="inline-flex items-center rounded-full bg-amber-100 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-amber-800">Prioritario</span>
|
||||
@endif
|
||||
</div>
|
||||
<p class="text-xs text-gray-500">
|
||||
{{ $t->zona?->nome }} — {{ $t->tipologia?->nome }}
|
||||
@if($t->giorni_giacenza > 0)
|
||||
— in reparto da {{ $t->giorni_giacenza }} giorni
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
@can('territori.assign')
|
||||
<a href="{{ route('assegnazioni.assegna', ['territorioId' => $t->id]) }}" class="text-xs font-medium text-green-600 hover:text-green-800">Assegna →</a>
|
||||
@@ -67,45 +78,13 @@
|
||||
<li class="px-4 py-4 text-sm text-gray-400 text-center">Tutti assegnati</li>
|
||||
@endforelse
|
||||
</ul>
|
||||
@if($daAssegnare->count() >= 10)
|
||||
@if($territoriDaAssegnare->count() >= 10)
|
||||
<div class="px-4 py-2 bg-gray-50 border-t text-center">
|
||||
<a href="{{ route('territori.index') }}?filtroStato=in_reparto" class="text-xs text-indigo-600 hover:text-indigo-800">Vedi tutti →</a>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Prioritari --}}
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
||||
<div class="px-4 py-3 bg-amber-50 border-b border-amber-100">
|
||||
<h3 class="text-sm font-semibold text-amber-800">★ Prioritari</h3>
|
||||
</div>
|
||||
<ul class="divide-y divide-gray-100">
|
||||
@forelse($prioritari as $t)
|
||||
<li class="px-4 py-2.5 flex items-center justify-between hover:bg-gray-50">
|
||||
<div>
|
||||
<a href="{{ route('territori.show', $t) }}" class="text-sm font-semibold text-gray-900 hover:text-indigo-600">N° {{ $t->numero }}</a>
|
||||
<p class="text-xs text-gray-500">
|
||||
{{ $t->zona?->nome }}
|
||||
@if($t->ultimaAssegnazione?->returned_at)
|
||||
— ultimo rientro {{ $t->ultimaAssegnazione->returned_at->diffForHumans() }}
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
<span class="text-xs font-medium text-amber-600">
|
||||
{{ $t->prioritario ? 'Man' : 'Auto' }}
|
||||
</span>
|
||||
</li>
|
||||
@empty
|
||||
<li class="px-4 py-4 text-sm text-gray-400 text-center">Nessun territorio prioritario</li>
|
||||
@endforelse
|
||||
</ul>
|
||||
@if($prioritari->count() >= 10)
|
||||
<div class="px-4 py-2 bg-gray-50 border-t text-center">
|
||||
<a href="{{ route('territori.index') }}?filtroStato=prioritari" class="text-xs text-indigo-600 hover:text-indigo-800">Vedi tutti →</a>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Da rientrare --}}
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
||||
<div class="px-4 py-3 bg-red-50 border-b border-red-100">
|
||||
|
||||
@@ -66,8 +66,17 @@
|
||||
@forelse($assegnazioni as $a)
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-3 py-2">
|
||||
<a href="{{ route('territori.show', $a->territorio_id) }}" class="text-indigo-600 hover:underline font-medium">N° {{ $a->territorio?->numero }}</a>
|
||||
<span class="text-xs text-gray-400 ml-1">{{ $a->territorio?->zona?->nome }}</span>
|
||||
<div class="flex items-center gap-2">
|
||||
@if($a->territorio?->thumbnail_path)
|
||||
<img src="{{ asset('storage/' . $a->territorio->thumbnail_path) }}"
|
||||
alt="Thumbnail territorio {{ $a->territorio?->numero }}"
|
||||
class="h-8 w-6 rounded border border-gray-200 object-cover bg-gray-50 flex-none">
|
||||
@endif
|
||||
<div class="min-w-0">
|
||||
<a href="{{ route('territori.show', $a->territorio_id) }}" class="text-indigo-600 hover:underline font-medium">N° {{ $a->territorio?->numero }}</a>
|
||||
<span class="text-xs text-gray-400 ml-1">{{ $a->territorio?->zona?->nome }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-3 py-2">{{ $a->proclamatore?->nome_completo ?? 'N/A' }}</td>
|
||||
<td class="px-3 py-2">{{ $a->assigned_at->format('d/m/Y') }}</td>
|
||||
@@ -89,6 +98,14 @@
|
||||
</td>
|
||||
@can('settings.manage')
|
||||
<td class="px-3 py-2 whitespace-nowrap">
|
||||
@can('territori.return')
|
||||
@if(!$a->returned_at)
|
||||
<a href="{{ route('assegnazioni.rientra', ['assegnazione' => $a->id]) }}"
|
||||
class="inline-block text-xs font-medium text-red-600 hover:text-red-800 mr-3">
|
||||
Rientra
|
||||
</a>
|
||||
@endif
|
||||
@endcan
|
||||
<button wire:click="openEdit({{ $a->id }})"
|
||||
style="background:#e0e7ff;color:#4338ca;border:none;border-radius:6px;padding:4px 10px;font-size:12px;cursor:pointer;margin-right:4px;">
|
||||
Modifica
|
||||
@@ -102,7 +119,7 @@
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7" class="px-4 py-8 text-center text-gray-500">Nessuna assegnazione trovata.</td>
|
||||
<td colspan="8" class="px-4 py-8 text-center text-gray-500">Nessuna assegnazione trovata.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
|
||||
@@ -4,16 +4,13 @@
|
||||
</div>
|
||||
|
||||
<div class="mb-6 bg-white rounded-xl shadow-sm border border-gray-200 p-4 max-w-lg">
|
||||
<p class="text-sm font-medium text-gray-700 mb-3">Import / Export dati XML</p>
|
||||
<p class="text-sm font-medium text-gray-700 mb-3">Sezione Import</p>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<a href="{{ route('xml.exchange') }}" class="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-amber-600 rounded-lg hover:bg-amber-700 transition">
|
||||
Import XML
|
||||
</a>
|
||||
<a href="{{ route('xml.exchange') }}" class="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-emerald-600 rounded-lg hover:bg-emerald-700 transition">
|
||||
Export XML
|
||||
<a href="{{ route('xml.exchange') }}" class="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-indigo-600 rounded-lg hover:bg-indigo-700 transition">
|
||||
Apri strumenti di import
|
||||
</a>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 mt-2">I pulsanti aprono la sezione XML Exchange con gli strumenti di conversione, import e export.</p>
|
||||
<p class="text-xs text-gray-500 mt-2">Qui trovi import PDF territori, conversione legacy SQL, import XML ed export XML.</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6 max-w-lg">
|
||||
|
||||
@@ -1,7 +1,245 @@
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold text-gray-900">Import/Export XML</h1>
|
||||
<p class="text-sm text-gray-500 mt-1">Converti dump SQL legacy in XML, importa XML nell'app ed esporta i dati correnti in XML.</p>
|
||||
<h1 class="text-2xl font-bold text-gray-900">Import</h1>
|
||||
<p class="text-sm text-gray-500 mt-1">Centro importazioni: PDF territori, conversione legacy SQL, import XML ed export XML.</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6 space-y-4"
|
||||
x-data="{
|
||||
uploadLog: '',
|
||||
uploadProgress: 0,
|
||||
uploading: false,
|
||||
zipUploading: false,
|
||||
zipProgress: 0,
|
||||
selectedFiles: 0,
|
||||
selectedZip: '',
|
||||
append(message) {
|
||||
this.uploadLog = this.uploadLog ? this.uploadLog + '\n' + message : message;
|
||||
},
|
||||
submitZip(event) {
|
||||
const form = event.target;
|
||||
const formData = new FormData(form);
|
||||
|
||||
this.uploadLog = '';
|
||||
this.zipUploading = true;
|
||||
this.zipProgress = 0;
|
||||
this.append('Upload ZIP diretto al server avviato...');
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', form.action);
|
||||
xhr.setRequestHeader('Accept', 'application/json');
|
||||
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
|
||||
xhr.upload.addEventListener('progress', (uploadEvent) => {
|
||||
if (!uploadEvent.lengthComputable) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.zipProgress = Math.round((uploadEvent.loaded / uploadEvent.total) * 100);
|
||||
});
|
||||
|
||||
xhr.addEventListener('load', () => {
|
||||
this.zipUploading = false;
|
||||
|
||||
let payload = {};
|
||||
|
||||
try {
|
||||
payload = JSON.parse(xhr.responseText || '{}');
|
||||
} catch (error) {
|
||||
payload = {};
|
||||
}
|
||||
|
||||
if (xhr.status >= 200 && xhr.status < 300) {
|
||||
this.zipProgress = 100;
|
||||
this.append('Archivio ricevuto. Reindirizzamento alla console import...');
|
||||
window.location = payload.redirect_url || window.location.href;
|
||||
return;
|
||||
}
|
||||
|
||||
const message = payload.message || (payload.errors && payload.errors.pdfZip ? payload.errors.pdfZip[0] : 'Errore durante il caricamento dello ZIP.');
|
||||
this.append(message);
|
||||
});
|
||||
|
||||
xhr.addEventListener('error', () => {
|
||||
this.zipUploading = false;
|
||||
this.append('Errore di rete durante il caricamento dello ZIP.');
|
||||
});
|
||||
|
||||
xhr.send(formData);
|
||||
}
|
||||
}"
|
||||
x-on:livewire-upload-start="if ($event.detail.id === 'pdfFolder') { uploading = true; uploadProgress = 0; append('Upload cartella avviato...'); }"
|
||||
x-on:livewire-upload-progress="if ($event.detail.id === 'pdfFolder') { uploadProgress = $event.detail.progress; append('Upload Livewire in corso: ' + $event.detail.progress + '%'); }"
|
||||
x-on:livewire-upload-finish="if ($event.detail.id === 'pdfFolder') { uploading = false; uploadProgress = 100; append('Upload completato. Avvio preparazione import lato server...'); }"
|
||||
x-on:livewire-upload-error="if ($event.detail.id === 'pdfFolder') { uploading = false; append('Errore durante l\'upload temporaneo dei file.'); }">
|
||||
<h2 class="text-lg font-semibold text-gray-900">Import PDF territori</h2>
|
||||
<p class="text-xs text-gray-500">Puoi importare una cartella di PDF oppure, meglio per archivi grandi, un file ZIP contenente i PDF. Il nome file puo variare: basta che contenga il numero di un territorio gia presente nell'app. I PDF verranno associati ai territori esistenti e verra generata anche la thumbnail.</p>
|
||||
|
||||
<form wire:submit.prevent="importTerritoryPdfFolder" class="space-y-4">
|
||||
<div>
|
||||
<input wire:model="pdfFolder"
|
||||
x-on:change="selectedFiles = $event.target.files.length; uploadLog = ''; if (selectedFiles > 0) { append('Cartella selezionata: ' + selectedFiles + ' file.'); append('In attesa dell\'upload temporaneo Livewire...'); }"
|
||||
type="file"
|
||||
multiple
|
||||
webkitdirectory
|
||||
directory
|
||||
accept=".pdf,application/pdf"
|
||||
class="block w-full text-sm text-gray-600 file:mr-3 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-medium file:bg-indigo-50 file:text-indigo-700 hover:file:bg-indigo-100">
|
||||
@error('pdfFolder') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror
|
||||
@error('pdfFolder.*') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div x-show="selectedFiles > 0 || uploading" x-cloak class="rounded-lg border border-indigo-100 bg-indigo-50 px-3 py-3">
|
||||
<div class="flex items-center justify-between gap-3 text-xs text-indigo-800">
|
||||
<span x-text="uploading ? 'Upload file in corso...' : 'Upload file pronto'"></span>
|
||||
<span x-text="selectedFiles > 0 ? selectedFiles + ' file selezionati' : ''"></span>
|
||||
</div>
|
||||
<div class="mt-2 h-2 overflow-hidden rounded-full bg-indigo-100">
|
||||
<div class="h-full rounded-full bg-indigo-600 transition-all duration-300" :style="'width:' + uploadProgress + '%' "></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-center">
|
||||
<button
|
||||
type="submit"
|
||||
class="px-4 py-2 text-sm font-medium text-white bg-indigo-600 rounded-lg hover:bg-indigo-700 transition border border-indigo-700"
|
||||
style="display:inline-flex;align-items:center;justify-content:center;min-height:40px;background:#4f46e5;color:#fff;border:1px solid #3730a3;border-radius:10px;padding:10px 14px;"
|
||||
wire:loading.attr="disabled"
|
||||
@disabled(empty($pdfFolder))
|
||||
>
|
||||
Importa PDF territori
|
||||
</button>
|
||||
<div wire:loading wire:target="importTerritoryPdfFolder" class="text-sm text-indigo-700">Preparazione import in corso...</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="border-t border-gray-200 pt-4">
|
||||
<h3 class="text-sm font-semibold text-gray-900">Import da archivio ZIP</h3>
|
||||
<p class="mt-1 text-xs text-gray-500">Consigliato per grandi volumi: carichi un solo file e il server estrae automaticamente tutti i PDF.</p>
|
||||
|
||||
<form action="{{ route('imports.territori.pdf-zip') }}" method="POST" enctype="multipart/form-data" class="mt-3 space-y-4" @submit.prevent="submitZip">
|
||||
@csrf
|
||||
<div>
|
||||
<input name="pdfZip"
|
||||
x-on:change="selectedZip = $event.target.files[0] ? $event.target.files[0].name : ''; uploadLog = ''; zipProgress = 0; if (selectedZip) { append('Archivio ZIP selezionato: ' + selectedZip); append('Pronto per upload diretto al server.'); }"
|
||||
type="file"
|
||||
accept=".zip,application/zip"
|
||||
class="block w-full text-sm text-gray-600 file:mr-3 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-medium file:bg-emerald-50 file:text-emerald-700 hover:file:bg-emerald-100">
|
||||
@error('pdfZip') <p class="text-red-500 text-xs mt-1">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div x-show="selectedZip || zipUploading" x-cloak class="rounded-lg border border-emerald-100 bg-emerald-50 px-3 py-3">
|
||||
<div class="flex items-center justify-between gap-3 text-xs text-emerald-800">
|
||||
<span x-text="zipUploading ? 'Upload ZIP in corso...' : 'ZIP pronto per l\'invio' "></span>
|
||||
<span x-text="selectedZip"></span>
|
||||
</div>
|
||||
<div class="mt-2 h-2 overflow-hidden rounded-full bg-emerald-100">
|
||||
<div class="h-full rounded-full bg-emerald-600 transition-all duration-300" :style="'width:' + zipProgress + '%' "></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-center">
|
||||
<button
|
||||
type="submit"
|
||||
class="px-4 py-2 text-sm font-medium text-white bg-emerald-600 rounded-lg hover:bg-emerald-700 transition border border-emerald-700"
|
||||
style="display:inline-flex;align-items:center;justify-content:center;min-height:40px;background:#059669;color:#fff;border:1px solid #065f46;border-radius:10px;padding:10px 14px;"
|
||||
x-bind:disabled="!selectedZip || zipUploading"
|
||||
>
|
||||
Importa ZIP PDF
|
||||
</button>
|
||||
<div x-show="zipUploading" x-cloak class="text-sm text-emerald-700">Caricamento ZIP diretto in corso...</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div x-show="uploadLog || uploading || zipUploading || selectedZip" x-cloak>
|
||||
<div class="mb-2 text-sm font-medium text-gray-800">Console upload</div>
|
||||
<textarea readonly rows="8" x-bind:value="uploadLog" class="block w-full rounded-lg border border-gray-300 bg-gray-950 px-3 py-3 font-mono text-xs leading-5 text-gray-100 focus:border-indigo-500 focus:ring-indigo-500"></textarea>
|
||||
</div>
|
||||
|
||||
@if($currentPdfImportId && (!empty($pdfImportStats) || !empty($pdfImportLogs)))
|
||||
<div class="rounded-xl border border-indigo-200 bg-indigo-50/40 p-4" @if(in_array($pdfImportStatus, ['queued', 'running'], true)) wire:poll.1000ms="refreshPdfImportStatus" @endif>
|
||||
<div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<div class="text-sm font-medium text-gray-800">Stato import PDF</div>
|
||||
<div class="mt-1 text-xs text-gray-500">ID import: {{ $currentPdfImportId }}</div>
|
||||
</div>
|
||||
<span class="inline-flex items-center rounded-full px-2.5 py-1 text-xs font-semibold
|
||||
{{ match($pdfImportStatus) {
|
||||
'queued' => 'bg-amber-100 text-amber-800',
|
||||
'running' => 'bg-blue-100 text-blue-800',
|
||||
'completed' => 'bg-green-100 text-green-800',
|
||||
'failed' => 'bg-red-100 text-red-800',
|
||||
default => 'bg-gray-100 text-gray-700',
|
||||
} }}">
|
||||
{{ match($pdfImportStatus) {
|
||||
'queued' => 'In coda',
|
||||
'running' => 'In esecuzione',
|
||||
'completed' => 'Completato',
|
||||
'failed' => 'Fallito',
|
||||
default => 'Inattivo',
|
||||
} }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@if(!empty($pdfImportStats))
|
||||
<div class="mt-4 grid grid-cols-2 gap-3 text-sm sm:grid-cols-4">
|
||||
<div class="rounded-lg bg-white px-3 py-2 border border-gray-200">
|
||||
<div class="text-xs text-gray-500">Processati</div>
|
||||
<div class="font-semibold text-gray-900">{{ $pdfImportStats['processed'] ?? 0 }} / {{ $pdfImportStats['total'] ?? 0 }}</div>
|
||||
</div>
|
||||
<div class="rounded-lg bg-green-50 px-3 py-2 border border-green-100">
|
||||
<div class="text-xs text-green-700">Aggiornati</div>
|
||||
<div class="font-semibold text-green-900">{{ $pdfImportStats['updated'] ?? 0 }}</div>
|
||||
</div>
|
||||
<div class="rounded-lg bg-amber-50 px-3 py-2 border border-amber-100">
|
||||
<div class="text-xs text-amber-700">Saltati</div>
|
||||
<div class="font-semibold text-amber-900">{{ $pdfImportStats['skipped'] ?? 0 }}</div>
|
||||
</div>
|
||||
<div class="rounded-lg bg-red-50 px-3 py-2 border border-red-100">
|
||||
<div class="text-xs text-red-700">Errori</div>
|
||||
<div class="font-semibold text-red-900">{{ $pdfImportStats['errors'] ?? 0 }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-4">
|
||||
<div class="mb-2 flex items-center justify-between gap-3">
|
||||
<div class="text-sm font-medium text-gray-800">Log import PDF</div>
|
||||
<button type="button" wire:click="refreshPdfImportStatus" class="inline-flex items-center justify-center rounded-lg border border-gray-300 px-3 py-1.5 text-xs font-medium text-gray-700 hover:bg-white transition">
|
||||
Aggiorna log
|
||||
</button>
|
||||
</div>
|
||||
<textarea readonly rows="12" class="block w-full rounded-lg border border-gray-300 bg-gray-950 px-3 py-3 font-mono text-xs leading-5 text-gray-100 focus:border-indigo-500 focus:ring-indigo-500 sm:rows-14">{{ $pdfImportLogText }}</textarea>
|
||||
</div>
|
||||
|
||||
@if(!empty($pdfImportIssues))
|
||||
<div class="mt-4">
|
||||
<div class="mb-2 text-sm font-medium text-gray-800">Riepilogo file non associati o problematici</div>
|
||||
<div class="overflow-x-auto rounded-lg border border-gray-200 bg-white">
|
||||
<table class="min-w-full divide-y divide-gray-200 text-sm">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-3 py-2 text-left text-xs font-medium uppercase text-gray-500">File</th>
|
||||
<th class="px-3 py-2 text-left text-xs font-medium uppercase text-gray-500">Motivo</th>
|
||||
<th class="px-3 py-2 text-left text-xs font-medium uppercase text-gray-500">Territori rilevati</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100">
|
||||
@foreach($pdfImportIssues as $issue)
|
||||
<tr>
|
||||
<td class="px-3 py-2 text-gray-900">{{ $issue['file'] ?? '-' }}</td>
|
||||
<td class="px-3 py-2 text-gray-600">{{ $issue['message'] ?? '-' }}</td>
|
||||
<td class="px-3 py-2 text-gray-600">{{ !empty($issue['matched_numbers']) ? implode(', ', $issue['matched_numbers']) : '-' }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6 space-y-4">
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
{{-- Filters --}}
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 mb-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<input wire:model.live.debounce.300ms="search" type="text" placeholder="Cerca numero..."
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 xl:grid-cols-7 gap-3">
|
||||
<input wire:model.live.debounce.300ms="search" type="text" placeholder="Cerca numero, zona, tipologia, note..."
|
||||
class="rounded-lg border-gray-300 text-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
<select wire:model.live="filterZona" class="rounded-lg border-gray-300 text-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
<option value="">Tutte le zone</option>
|
||||
@@ -30,10 +30,46 @@
|
||||
<select wire:model.live="filterStato" class="rounded-lg border-gray-300 text-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
<option value="">Tutti gli stati</option>
|
||||
<option value="in_reparto">In reparto</option>
|
||||
<option value="prioritari">Prioritari</option>
|
||||
<option value="assegnato">Assegnato</option>
|
||||
<option value="da_rientrare">Da rientrare</option>
|
||||
<option value="inattivo">Inattivo</option>
|
||||
</select>
|
||||
<select wire:model.live="filterPriorita" class="rounded-lg border-gray-300 text-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
<option value="">Tutte le priorita</option>
|
||||
<option value="prioritari">Solo prioritari</option>
|
||||
<option value="manuali">Prioritari manuali</option>
|
||||
<option value="automatici">Prioritari automatici</option>
|
||||
<option value="non_prioritari">Solo non prioritari</option>
|
||||
</select>
|
||||
<select wire:model.live="filterPdf" class="rounded-lg border-gray-300 text-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
<option value="">PDF e thumbnail</option>
|
||||
<option value="con_pdf">Con PDF</option>
|
||||
<option value="senza_pdf">Senza PDF</option>
|
||||
<option value="con_thumbnail">Con thumbnail</option>
|
||||
<option value="senza_thumbnail">Senza thumbnail</option>
|
||||
</select>
|
||||
<select wire:model.live="filterContenuti" class="rounded-lg border-gray-300 text-sm focus:border-indigo-500 focus:ring-indigo-500">
|
||||
<option value="">Note e confini</option>
|
||||
<option value="con_note">Con note</option>
|
||||
<option value="senza_note">Senza note</option>
|
||||
<option value="con_confini">Con confini</option>
|
||||
<option value="senza_confini">Senza confini</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mt-3 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
||||
<p class="text-xs text-gray-500">
|
||||
@if($usesPriorityOrdering)
|
||||
Ordinamento attivo: prioritari prima, poi territori con piu tempo in reparto.
|
||||
@else
|
||||
Ordinamento predefinito: numero territorio dal piu piccolo al piu grande.
|
||||
@endif
|
||||
</p>
|
||||
<button wire:click="clearFilters"
|
||||
type="button"
|
||||
class="inline-flex items-center justify-center rounded-lg border border-gray-300 px-3 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 transition">
|
||||
Azzera filtri
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -57,7 +93,14 @@
|
||||
@forelse($territori as $territorio)
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-4 py-3 text-sm font-semibold text-gray-900">
|
||||
<a href="{{ route('territori.show', $territorio) }}" class="hover:text-indigo-600">{{ $territorio->numero }}</a>
|
||||
<div class="flex items-center gap-2">
|
||||
@if($territorio->thumbnail_path)
|
||||
<img src="{{ asset('storage/' . $territorio->thumbnail_path) }}"
|
||||
alt="Thumbnail territorio {{ $territorio->numero }}"
|
||||
class="h-8 w-6 rounded border border-gray-200 object-cover bg-gray-50 flex-none">
|
||||
@endif
|
||||
<a href="{{ route('territori.show', $territorio) }}" class="hover:text-indigo-600">{{ $territorio->numero }}</a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-sm text-gray-600">{{ $territorio->zona?->nome ?? '-' }}</td>
|
||||
<td class="px-4 py-3 text-sm text-gray-600">{{ $territorio->tipologia?->nome ?? '-' }}</td>
|
||||
@@ -85,6 +128,11 @@
|
||||
</td>
|
||||
<td class="px-4 py-3 text-sm text-right space-x-1">
|
||||
<a href="{{ route('territori.show', $territorio) }}" class="text-indigo-600 hover:text-indigo-800 text-xs font-medium">Dettaglio</a>
|
||||
@can('territori.return')
|
||||
@if($territorio->assegnazioneCorrente)
|
||||
<a href="{{ route('assegnazioni.rientra', ['assegnazione' => $territorio->assegnazioneCorrente->id]) }}" class="text-red-600 hover:text-red-800 text-xs font-medium">Rientra</a>
|
||||
@endif
|
||||
@endcan
|
||||
<a href="{{ route('territori.edit', $territorio) }}" class="text-gray-600 hover:text-gray-800 text-xs font-medium">Modifica</a>
|
||||
<button wire:click="toggleActive({{ $territorio->id }})" class="text-xs font-medium {{ $territorio->attivo ? 'text-amber-600 hover:text-amber-800' : 'text-green-600 hover:text-green-800' }}">
|
||||
{{ $territorio->attivo ? 'Disattiva' : 'Attiva' }}
|
||||
|
||||
@@ -4,7 +4,14 @@
|
||||
<h1 class="text-2xl font-bold text-gray-900">Territorio {{ $territorio->numero }}</h1>
|
||||
<a href="{{ route('territori.index') }}" class="text-sm text-indigo-600 hover:text-indigo-800">← Torna alla lista</a>
|
||||
</div>
|
||||
<a href="{{ route('territori.edit', $territorio) }}" class="px-4 py-2 text-sm font-medium text-white bg-indigo-600 rounded-lg hover:bg-indigo-700 transition">Modifica</a>
|
||||
<div class="flex items-center gap-2">
|
||||
@can('territori.return')
|
||||
@if($territorio->assegnazioneCorrente)
|
||||
<a href="{{ route('assegnazioni.rientra', ['assegnazione' => $territorio->assegnazioneCorrente->id]) }}" class="px-4 py-2 text-sm font-medium text-white bg-red-600 rounded-lg hover:bg-red-700 transition">Rientra</a>
|
||||
@endif
|
||||
@endcan
|
||||
<a href="{{ route('territori.edit', $territorio) }}" class="px-4 py-2 text-sm font-medium text-white bg-indigo-600 rounded-lg hover:bg-indigo-700 transition">Modifica</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Info card --}}
|
||||
|
||||
Reference in New Issue
Block a user