697 lines
54 KiB
PHP
697 lines
54 KiB
PHP
<x-filament-panels::page>
|
|
<div class="space-y-4">
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<a href="{{ $this->getTicketInserimentoUrl() }}" class="inline-flex items-center rounded-md bg-indigo-700 px-3 py-1.5 text-xs font-medium text-white hover:bg-indigo-600">Vai a Inserimento Ticket</a>
|
|
</div>
|
|
<div class="mt-3 grid grid-cols-2 gap-2 sm:grid-cols-4">
|
|
<div class="rounded-lg border bg-gray-50 px-3 py-2 text-xs">Aperti: <span class="font-semibold">{{ $ticketCounters['open'] ?? 0 }}</span></div>
|
|
<div class="rounded-lg border bg-gray-50 px-3 py-2 text-xs">Urgenti: <span class="font-semibold">{{ $ticketCounters['urgent'] ?? 0 }}</span></div>
|
|
<div class="rounded-lg border bg-gray-50 px-3 py-2 text-xs">Chiusi: <span class="font-semibold">{{ $ticketCounters['closed'] ?? 0 }}</span></div>
|
|
<div class="rounded-lg border bg-gray-50 px-3 py-2 text-xs">Totali: <span class="font-semibold">{{ $ticketCounters['all'] ?? 0 }}</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="flex flex-wrap items-center justify-between gap-3">
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<button type="button" wire:click="apriElenco" class="inline-flex items-center rounded-md px-3 py-1.5 text-xs font-medium {{ $activeTab === 'elenco' ? 'bg-slate-800 text-white' : 'bg-slate-100 text-slate-700 hover:bg-slate-200' }}">
|
|
Tab 1 - Elenco (stile excel)
|
|
</button>
|
|
<button type="button" wire:click="$set('activeTab', 'scheda')" class="inline-flex items-center rounded-md px-3 py-1.5 text-xs font-medium {{ $activeTab === 'scheda' ? 'bg-slate-800 text-white' : 'bg-slate-100 text-slate-700 hover:bg-slate-200' }}">
|
|
Tab 2 - Scheda ticket
|
|
</button>
|
|
<button type="button" wire:click="$set('activeTab', 'fornitori')" class="inline-flex items-center rounded-md px-3 py-1.5 text-xs font-medium {{ $activeTab === 'fornitori' ? 'bg-slate-800 text-white' : 'bg-slate-100 text-slate-700 hover:bg-slate-200' }}">
|
|
Tab 3 - Fornitori attivi
|
|
</button>
|
|
<button type="button" wire:click="apriAssicurazioneTicket" class="inline-flex items-center rounded-md px-3 py-1.5 text-xs font-medium {{ $activeTab === 'assicurazione' ? 'bg-slate-800 text-white' : 'bg-slate-100 text-slate-700 hover:bg-slate-200' }}">
|
|
Tab 4 - Assicurazioni
|
|
</button>
|
|
</div>
|
|
@if($activeTab === 'elenco')
|
|
<select wire:model.live="status" class="rounded-lg border-gray-300 text-sm">
|
|
<option value="open">Aperti</option>
|
|
<option value="urgent">Urgenti</option>
|
|
<option value="closed">Chiusi</option>
|
|
<option value="all">Tutti</option>
|
|
</select>
|
|
@endif
|
|
</div>
|
|
|
|
@if($activeTab === 'elenco')
|
|
<div class="mt-3 rounded-lg border bg-slate-50 p-3">
|
|
<div class="mb-2 text-xs font-semibold text-slate-700">Categorie ticket</div>
|
|
<div class="grid gap-2 md:grid-cols-3">
|
|
<label class="block text-xs">
|
|
<span class="mb-1 block font-medium">Categoria esistente</span>
|
|
<select wire:model.live="selectedCategoriaId" class="w-full rounded-lg border-gray-300 text-sm">
|
|
<option value="">Seleziona categoria</option>
|
|
@foreach($categorieOptions as $cat)
|
|
<option value="{{ (int) $cat['id'] }}">{{ $cat['nome'] }}</option>
|
|
@endforeach
|
|
</select>
|
|
</label>
|
|
<label class="block text-xs">
|
|
<span class="mb-1 block font-medium">Nome categoria</span>
|
|
<input type="text" wire:model.defer="categoriaNome" class="w-full rounded-lg border-gray-300 text-sm" placeholder="Es. Ascensore" />
|
|
</label>
|
|
<label class="block text-xs">
|
|
<span class="mb-1 block font-medium">Descrizione</span>
|
|
<input type="text" wire:model.defer="categoriaDescrizione" class="w-full rounded-lg border-gray-300 text-sm" placeholder="Descrizione breve" />
|
|
</label>
|
|
</div>
|
|
<div class="mt-2 flex flex-wrap items-center gap-2">
|
|
<x-filament::button size="sm" color="success" wire:click="creaCategoriaTicket">Aggiungi categoria</x-filament::button>
|
|
<x-filament::button size="sm" color="warning" wire:click="aggiornaCategoriaTicket">Modifica categoria selezionata</x-filament::button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-3 overflow-x-auto">
|
|
<table class="min-w-full border-collapse border text-xs">
|
|
<thead>
|
|
<tr class="bg-slate-100 text-slate-700">
|
|
<th class="border px-2 py-2 text-left">ID</th>
|
|
<th class="border px-2 py-2 text-left">Titolo</th>
|
|
<th class="border px-2 py-2 text-left">Categoria</th>
|
|
<th class="border px-2 py-2 text-left">Tipo intervento</th>
|
|
<th class="border px-2 py-2 text-left">Assegnato a</th>
|
|
<th class="border px-2 py-2 text-left">Priorita</th>
|
|
<th class="border px-2 py-2 text-left">Stato</th>
|
|
<th class="border px-2 py-2 text-left">Apertura</th>
|
|
<th class="border px-2 py-2 text-left">Azioni</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@forelse($tickets as $ticket)
|
|
<tr id="ticket-{{ (int) $ticket->id }}" class="hover:bg-slate-50">
|
|
<td class="border px-2 py-2">#{{ (int) $ticket->id }}</td>
|
|
<td class="border px-2 py-2">
|
|
<div class="flex items-center gap-1">
|
|
<span>{{ $ticket->titolo }}</span>
|
|
@if(((int) ($ticket->attachments_count ?? 0)) > 0)
|
|
<span title="Presenza allegati" class="inline-flex items-center rounded bg-emerald-100 px-1.5 py-0.5 text-[10px] font-semibold text-emerald-800">
|
|
Allegati {{ (int) $ticket->attachments_count }}
|
|
</span>
|
|
@endif
|
|
</div>
|
|
</td>
|
|
<td class="border px-2 py-2">{{ optional($ticket->categoriaTicket)->nome ?: '-' }}</td>
|
|
<td class="border px-2 py-2">{{ $this->getTipoInterventoLabel($ticket) }}</td>
|
|
<td class="border px-2 py-2">{{ optional($ticket->assegnatoAUser)->name ?: '-' }}</td>
|
|
<td class="border px-2 py-2">{{ $ticket->priorita }}</td>
|
|
<td class="border px-2 py-2">{{ $ticket->stato }}</td>
|
|
<td class="border px-2 py-2">{{ optional($ticket->data_apertura)->format('d/m/Y H:i') }}</td>
|
|
<td class="border px-2 py-2">
|
|
<div class="flex flex-wrap items-center gap-1">
|
|
<button type="button" wire:click="apriScheda({{ (int) $ticket->id }})" class="inline-flex items-center rounded-md bg-slate-700 px-2 py-1 text-[11px] font-medium text-white hover:bg-slate-600">Scheda</button>
|
|
|
|
@if((int) ($ticket->assegnato_a_fornitore_id ?? 0) > 0)
|
|
<button type="button" wire:click="apriScheda({{ (int) $ticket->id }})" class="inline-flex items-center rounded-md bg-fuchsia-700 px-2 py-1 text-[11px] font-medium text-white hover:bg-fuchsia-600">Assegnata</button>
|
|
@endif
|
|
|
|
@if(in_array($ticket->stato, ['Aperto'], true))
|
|
<button type="button" wire:click="prendiInCarico({{ (int) $ticket->id }})" class="inline-flex items-center rounded-md bg-blue-600 px-2 py-1 text-[11px] font-medium text-white hover:bg-blue-500">Carico</button>
|
|
@endif
|
|
@if(in_array($ticket->stato, ['Aperto', 'Preso in Carico'], true))
|
|
<button type="button" wire:click="avviaLavorazione({{ (int) $ticket->id }})" class="inline-flex items-center rounded-md bg-amber-600 px-2 py-1 text-[11px] font-medium text-white hover:bg-amber-500">Lavora</button>
|
|
@endif
|
|
@if(in_array($ticket->stato, ['Aperto', 'Preso in Carico', 'In Lavorazione', 'In Attesa Approvazione', 'In Attesa Ricambi'], true))
|
|
<button type="button" wire:click="risolviTicket({{ (int) $ticket->id }})" class="inline-flex items-center rounded-md bg-emerald-600 px-2 py-1 text-[11px] font-medium text-white hover:bg-emerald-500">Risolto</button>
|
|
@endif
|
|
@if(in_array($ticket->stato, ['Risolto'], true))
|
|
<button type="button" wire:click="chiudiTicket({{ (int) $ticket->id }})" class="inline-flex items-center rounded-md bg-gray-800 px-2 py-1 text-[11px] font-medium text-white hover:bg-gray-700">Chiudi</button>
|
|
@endif
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="9" class="border px-2 py-4 text-center text-gray-500">Nessun ticket per il filtro selezionato.</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
@elseif($activeTab === 'fornitori')
|
|
<div class="mt-4 rounded-xl border bg-white p-4">
|
|
<div class="text-sm font-semibold">Fornitori attivi su ticket aperti</div>
|
|
<div class="mt-1 text-xs text-gray-500">Mostra solo fornitori con ticket/interventi in corso, con stato operativo e ultimo aggiornamento.</div>
|
|
|
|
@if(filled($lastGeneratedPassword ?? null))
|
|
<div class="mt-3 rounded-md border border-amber-300 bg-amber-50 px-3 py-2 text-xs text-amber-900">
|
|
Ultima password temporanea generata: <span class="font-mono font-semibold">{{ $lastGeneratedPassword }}</span>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="mt-3 overflow-x-auto">
|
|
<table class="min-w-full border-collapse border text-xs">
|
|
<thead>
|
|
<tr class="bg-slate-100 text-slate-700">
|
|
<th class="border px-2 py-2 text-left">Fornitore</th>
|
|
<th class="border px-2 py-2 text-left">Contatti</th>
|
|
<th class="border px-2 py-2 text-left">Ticket attivi</th>
|
|
<th class="border px-2 py-2 text-left">Interventi aperti</th>
|
|
<th class="border px-2 py-2 text-left">Come procede</th>
|
|
<th class="border px-2 py-2 text-left">Accesso fornitore</th>
|
|
<th class="border px-2 py-2 text-left">Azioni</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@forelse($fornitoriAttiviRows as $f)
|
|
<tr class="hover:bg-slate-50">
|
|
<td class="border px-2 py-2 font-medium">{{ $f['nome'] ?: ('Fornitore #' . $f['id']) }}</td>
|
|
<td class="border px-2 py-2">{{ $f['email'] ?: '-' }}<br>{{ $f['telefono'] ?: '-' }}</td>
|
|
<td class="border px-2 py-2">{{ (int) $f['ticket_attivi'] }}</td>
|
|
<td class="border px-2 py-2">{{ (int) $f['interventi_aperti'] }}</td>
|
|
<td class="border px-2 py-2">
|
|
<div class="space-y-1 text-[11px]">
|
|
<div><span class="font-medium">Stati intervento:</span> {{ $f['intervento_stati'] ?: 'N/D' }}</div>
|
|
<div><span class="font-medium">Ultimo aggiornamento:</span> {{ $f['ultimo_aggiornamento'] ?: '-' }}</div>
|
|
@if(!empty($f['ticket_focus']))
|
|
<div class="text-gray-600">{{ implode(', ', $f['ticket_focus']) }}</div>
|
|
@endif
|
|
</div>
|
|
</td>
|
|
<td class="border px-2 py-2 text-[11px]">
|
|
<div>Dipendenti: <span class="font-semibold">{{ (int) ($f['dipendenti_count'] ?? 0) }}</span></div>
|
|
@if((int) ($f['linked_user_id'] ?? 0) > 0)
|
|
<div class="text-emerald-700">Accesso attivo (utente #{{ (int) $f['linked_user_id'] }})</div>
|
|
@else
|
|
<div class="text-amber-700">Nessun accesso attivo</div>
|
|
@endif
|
|
</td>
|
|
<td class="border px-2 py-2">
|
|
<div class="flex flex-wrap gap-1">
|
|
<button type="button" wire:click="abilitaAccessoFornitoreDaTicket({{ (int) $f['id'] }})" class="inline-flex items-center rounded-md bg-emerald-700 px-2 py-1 text-[11px] font-medium text-white hover:bg-emerald-600">Abilita accesso</button>
|
|
@if((int) ($f['linked_user_id'] ?? 0) > 0)
|
|
<button type="button" wire:click="resetPasswordFornitoreUser({{ (int) $f['linked_user_id'] }})" class="inline-flex items-center rounded-md bg-amber-700 px-2 py-1 text-[11px] font-medium text-white hover:bg-amber-600">Reset password</button>
|
|
@endif
|
|
<a href="{{ $this->getFornitoreOperativoUrl((int) $f['id']) }}" class="inline-flex items-center rounded-md bg-indigo-700 px-2 py-1 text-[11px] font-medium text-white hover:bg-indigo-600">Scheda operativa</a>
|
|
<a href="{{ $this->getFornitoreAnagraficaUrl((int) $f['id']) }}" class="inline-flex items-center rounded-md bg-gray-700 px-2 py-1 text-[11px] font-medium text-white hover:bg-gray-600">Anagrafica</a>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="7" class="border px-2 py-4 text-center text-gray-500">Nessun fornitore attivo su ticket aperti.</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
@elseif($activeTab === 'scheda')
|
|
@php
|
|
$ticket = $this->selectedTicket;
|
|
@endphp
|
|
@if(! $ticket)
|
|
<div class="mt-3 rounded-lg border border-amber-300 bg-amber-50 p-3 text-sm text-amber-800">
|
|
Nessun ticket selezionato. Vai nella TAB Elenco e premi "Scheda" sulla riga del ticket.
|
|
</div>
|
|
@else
|
|
<div class="mt-3 rounded-lg border p-3">
|
|
<div class="flex flex-wrap items-center justify-between gap-2">
|
|
<div>
|
|
<div class="text-sm font-semibold">#{{ $ticket->id }} - {{ $ticket->titolo }}</div>
|
|
<div class="text-xs text-gray-600">Stato: {{ $ticket->stato }} · Priorita: {{ $ticket->priorita }} · Categoria: {{ optional($ticket->categoriaTicket)->nome ?: '-' }}</div>
|
|
</div>
|
|
<div class="flex flex-wrap gap-2">
|
|
<button type="button" wire:click="apriAssicurazioneTicket" class="inline-flex items-center rounded-md bg-amber-700 px-3 py-1.5 text-xs font-medium text-white hover:bg-amber-600">Apri procedura assicurativa</button>
|
|
<a href="{{ $this->getInsuranceClaimsUrl() }}" class="inline-flex items-center rounded-md bg-slate-700 px-3 py-1.5 text-xs font-medium text-white hover:bg-slate-600">Elenco pratiche assicurative</a>
|
|
<button type="button" wire:click="apriElenco" class="inline-flex items-center rounded-md bg-gray-700 px-3 py-1.5 text-xs font-medium text-white hover:bg-gray-600">Torna all'elenco</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-3 whitespace-pre-wrap text-sm text-gray-700">{{ $ticket->descrizione }}</div>
|
|
|
|
<div class="mt-4 rounded-xl border border-amber-200 bg-amber-50 p-3 text-xs text-amber-900">
|
|
<div class="flex flex-wrap items-center justify-between gap-2">
|
|
<div>
|
|
<div class="font-semibold uppercase tracking-wide">Stato pratica assicurativa</div>
|
|
@if($ticket->insuranceClaim)
|
|
<div class="mt-1">
|
|
Pratica aperta · Stato {{ $ticket->insuranceClaim->status ?: 'aperta' }}
|
|
@if($ticket->insuranceClaim->claim_number)
|
|
· Sinistro {{ $ticket->insuranceClaim->claim_number }}
|
|
@endif
|
|
</div>
|
|
@if($ticket->insuranceClaim->policy_reference)
|
|
<div class="mt-1">Riferimento polizza: {{ $ticket->insuranceClaim->policy_reference }}</div>
|
|
@endif
|
|
@else
|
|
<div class="mt-1">Nessuna pratica assicurativa ancora aperta per questo ticket.</div>
|
|
@endif
|
|
</div>
|
|
<button type="button" wire:click="apriAssicurazioneTicket" class="inline-flex items-center rounded-md bg-amber-700 px-3 py-1.5 text-xs font-medium text-white hover:bg-amber-600">Gestisci in Tab 4</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-3 grid gap-4 lg:grid-cols-2">
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="text-sm font-semibold">Assegnazione fornitore</div>
|
|
<label class="mt-3 block text-sm">
|
|
<span class="mb-1 block font-medium">Ricerca fornitore</span>
|
|
<input type="text" wire:model.live.debounce.250ms="fornitoreSearch" class="w-full rounded-lg border-gray-300" placeholder="Es. idraulico, spurghi, 333..., Skynet, email" />
|
|
</label>
|
|
<div class="mt-2 text-xs text-gray-500">Ricerca per tag, telefono, nome, ragione sociale o email. Tocca il risultato per selezionarlo.</div>
|
|
|
|
@if($fornitoreId)
|
|
<div class="mt-3 rounded-lg border border-emerald-200 bg-emerald-50 p-3 text-xs text-emerald-800">
|
|
Fornitore selezionato: <span class="font-semibold">{{ $fornitoreSearch }}</span>
|
|
<button type="button" wire:click="resetFornitoreSelection" class="ml-2 inline-flex items-center rounded-md bg-white px-2 py-1 text-[11px] font-medium text-emerald-700 ring-1 ring-inset ring-emerald-300 hover:bg-emerald-100">Cambia</button>
|
|
</div>
|
|
@endif
|
|
|
|
@if(count($fornitoreMatches) > 0)
|
|
<div class="mt-3 space-y-2">
|
|
@foreach($fornitoreMatches as $match)
|
|
<button type="button" wire:click="selezionaFornitore({{ (int) $match['id'] }})" class="w-full rounded-lg border p-3 text-left {{ (int) $fornitoreId === (int) $match['id'] ? 'border-emerald-400 bg-emerald-50 ring-1 ring-emerald-200' : 'bg-white hover:bg-gray-50' }}">
|
|
<div class="text-sm font-medium">{{ $match['nome'] }}</div>
|
|
<div class="mt-1 text-xs text-gray-600">
|
|
@if($match['telefono'])
|
|
{{ $match['telefono'] }}
|
|
@else
|
|
Telefono non valorizzato
|
|
@endif
|
|
@if($match['email'])
|
|
· {{ $match['email'] }}
|
|
@endif
|
|
</div>
|
|
@if($match['tags_label'])
|
|
<div class="mt-1 text-xs text-indigo-700">Tag: {{ $match['tags_label'] }}</div>
|
|
@endif
|
|
@if($match['note'])
|
|
<div class="mt-1 text-xs text-gray-500">{{ $match['note'] }}</div>
|
|
@endif
|
|
</button>
|
|
@endforeach
|
|
</div>
|
|
@elseif(filled($fornitoreSearch) && ! $fornitoreId)
|
|
<div class="mt-3 text-xs text-gray-500">Nessun fornitore trovato con questa ricerca.</div>
|
|
@endif
|
|
|
|
<label class="mt-3 block text-sm">
|
|
<span class="mb-1 block font-medium">Note assegnazione/intervento</span>
|
|
<textarea rows="3" wire:model.defer="noteAssegnazione" class="w-full rounded-lg border-gray-300" placeholder="Istruzioni operative"></textarea>
|
|
</label>
|
|
<div class="mt-3">
|
|
<x-filament::button wire:click="assegnaFornitore">Assegna fornitore</x-filament::button>
|
|
@if((int) ($fornitoreId ?? 0) > 0)
|
|
<a href="{{ $this->getFornitoreOperativoUrl((int) $fornitoreId) }}" class="ml-2 inline-flex items-center rounded-md bg-purple-700 px-3 py-1.5 text-xs font-medium text-white hover:bg-purple-600">Vedi gestione fornitore</a>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="text-sm font-semibold">Nota interna</div>
|
|
<div class="mt-1 text-xs text-gray-500">Puoi aggiungere piu note: ogni nota viene salvata con data/ora automatica.</div>
|
|
<label class="mt-3 block text-sm">
|
|
<span class="mb-1 block font-medium">Nuova nota</span>
|
|
<textarea rows="4" wire:model.defer="notaInterna" class="w-full rounded-lg border-gray-300" placeholder="Aggiungi aggiornamento interno"></textarea>
|
|
</label>
|
|
<div class="mt-3">
|
|
<x-filament::button color="gray" wire:click="aggiungiNotaInterna">Aggiungi nota</x-filament::button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4 grid gap-4 lg:grid-cols-2">
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="text-sm font-semibold">Foto e documenti</div>
|
|
<label class="mt-3 block text-sm">
|
|
<span class="mb-1 block font-medium">Scatta foto (camera)</span>
|
|
<input type="file" wire:model="nuoveFoto" multiple accept="image/*" capture="environment" class="w-full rounded-lg border-gray-300 text-sm" />
|
|
</label>
|
|
<label class="mt-3 block text-sm">
|
|
<span class="mb-1 block font-medium">Allega file</span>
|
|
<input type="file" wire:model="nuoviAllegati" multiple accept="image/*,.pdf,.doc,.docx,.xls,.xlsx,.txt" class="w-full rounded-lg border-gray-300 text-sm" />
|
|
</label>
|
|
<label class="mt-3 block text-sm">
|
|
<span class="mb-1 block font-medium">Descrizione allegati</span>
|
|
<input type="text" wire:model.defer="descrizioneAllegati" class="w-full rounded-lg border-gray-300" placeholder="Es. foto danno balcone" />
|
|
</label>
|
|
<div class="mt-3">
|
|
<x-filament::button color="success" wire:click="caricaAllegati">Carica allegati</x-filament::button>
|
|
</div>
|
|
|
|
<div class="mt-4 grid gap-3 sm:grid-cols-2">
|
|
@forelse($ticket->attachments as $a)
|
|
@php($details = $this->getAttachmentDetails($a))
|
|
<div class="rounded-xl border bg-white p-3 text-xs shadow-sm">
|
|
<div class="aspect-[4/3] rounded-lg border bg-gray-50 p-2">
|
|
@if($a->isImage())
|
|
<img src="{{ $this->getAttachmentPublicUrl($a) }}" alt="{{ $a->original_file_name }}" class="h-full w-full rounded object-cover" />
|
|
@else
|
|
<div class="flex h-full items-center justify-center text-center text-gray-500">
|
|
<div>
|
|
<div class="text-sm font-semibold">{{ strtoupper(pathinfo((string) $a->original_file_name, PATHINFO_EXTENSION) ?: ($a->isPdf() ? 'PDF' : 'FILE')) }}</div>
|
|
<div class="mt-1 text-[11px]">Anteprima disponibile in modal</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
<div class="mt-2 font-medium">{{ $a->original_file_name }}</div>
|
|
<div class="mt-1 text-gray-700">{{ $details['primary_description'] ?: 'Nessuna descrizione' }}</div>
|
|
<div class="mt-2 flex flex-wrap gap-1">
|
|
@if(!empty($details['gps']))
|
|
<span class="inline-flex items-center rounded-full bg-emerald-100 px-2 py-0.5 text-[10px] font-semibold text-emerald-800">GPS</span>
|
|
@endif
|
|
@if(!empty($details['exif_datetime']))
|
|
<span class="inline-flex items-center rounded-full bg-sky-100 px-2 py-0.5 text-[10px] font-semibold text-sky-800">EXIF</span>
|
|
@endif
|
|
@if(!empty($details['device']))
|
|
<span class="inline-flex items-center rounded-full bg-slate-100 px-2 py-0.5 text-[10px] font-semibold text-slate-700">{{ $details['device'] }}</span>
|
|
@endif
|
|
</div>
|
|
@if(!empty($details['gps']) || !empty($details['exif_datetime']) || !empty($details['file_label']) || !empty($details['original_label']) || !empty($details['other']))
|
|
<div class="mt-2 rounded-lg border border-slate-200 bg-slate-50 p-2 text-[11px] text-slate-700">
|
|
@if(!empty($details['gps']))
|
|
<div><span class="font-medium">Coordinate:</span> {{ $details['gps']['label'] }}</div>
|
|
@endif
|
|
@if(!empty($details['exif_datetime']))
|
|
<div><span class="font-medium">Data EXIF:</span> {{ $details['exif_datetime'] }}</div>
|
|
@endif
|
|
@if(!empty($details['device']))
|
|
<div><span class="font-medium">Dispositivo:</span> {{ $details['device'] }}</div>
|
|
@endif
|
|
@if(!empty($details['file_label']))
|
|
<div><span class="font-medium">Nome file:</span> {{ $details['file_label'] }}</div>
|
|
@endif
|
|
@if(!empty($details['original_label']))
|
|
<div><span class="font-medium">Origine:</span> {{ $details['original_label'] }}</div>
|
|
@endif
|
|
@foreach($details['other'] as $otherPart)
|
|
<div>{{ $otherPart }}</div>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
<div class="mt-2 text-[11px] text-gray-500">{{ optional($a->created_at)->format('d/m/Y H:i') }}</div>
|
|
|
|
<div class="mt-2 flex flex-wrap gap-2">
|
|
<button type="button" wire:click="openAttachmentPreview({{ (int) $a->id }})" class="inline-flex items-center rounded-md bg-indigo-600 px-2 py-1 text-[11px] font-medium text-white hover:bg-indigo-500">Visualizza allegato</button>
|
|
@if(!empty($details['gps']))
|
|
<button type="button" wire:click="openAttachmentMapPreview({{ (int) $a->id }})" class="inline-flex items-center rounded-md bg-emerald-600 px-2 py-1 text-[11px] font-medium text-white hover:bg-emerald-500">Apri mappa</button>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@empty
|
|
<div class="text-xs text-gray-500">Nessun allegato.</div>
|
|
@endforelse
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="text-sm font-semibold">Storico interventi</div>
|
|
<div class="mt-3 space-y-2">
|
|
@forelse($ticket->interventi as $it)
|
|
<div class="rounded-md border p-2 text-xs">
|
|
<div class="font-medium">Stato: {{ $it->stato }}</div>
|
|
<div>
|
|
Fornitore:
|
|
{{ optional($it->fornitore)->ragione_sociale ?: trim((optional($it->fornitore)->nome ?? '') . ' ' . (optional($it->fornitore)->cognome ?? '')) ?: 'N/D' }}
|
|
</div>
|
|
@if($it->note_amministratore)
|
|
<div class="mt-1 whitespace-pre-wrap text-gray-600">{{ $it->note_amministratore }}</div>
|
|
@endif
|
|
</div>
|
|
@empty
|
|
<div class="text-xs text-gray-500">Nessun intervento registrato.</div>
|
|
@endforelse
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4 rounded-xl border bg-white p-4">
|
|
<div class="text-sm font-semibold">Storico note/messaggi</div>
|
|
<div class="mt-3 space-y-2">
|
|
@forelse($ticket->messages as $msg)
|
|
<div class="rounded-md border p-2 text-xs">
|
|
<div class="font-medium">{{ optional($msg->user)->name ?: 'Sistema' }}</div>
|
|
<div class="mt-1 whitespace-pre-wrap text-gray-700">{{ $msg->messaggio }}</div>
|
|
<div class="mt-1 text-gray-500">{{ optional($msg->created_at)->format('d/m/Y H:i') }}</div>
|
|
</div>
|
|
@empty
|
|
<div class="text-xs text-gray-500">Nessuna nota registrata.</div>
|
|
@endforelse
|
|
</div>
|
|
</div>
|
|
@endif
|
|
@endif
|
|
|
|
@if($activeTab === 'assicurazione')
|
|
@php
|
|
$ticket = $this->selectedTicket;
|
|
@endphp
|
|
@if(! $ticket)
|
|
<div class="mt-3 rounded-lg border border-amber-300 bg-amber-50 p-3 text-sm text-amber-800">
|
|
Nessun ticket selezionato. Apri un ticket e poi entra nella Tab 4 assicurazione.
|
|
</div>
|
|
@else
|
|
<div class="mt-3 rounded-lg border p-3">
|
|
<div class="flex flex-wrap items-center justify-between gap-2">
|
|
<div>
|
|
<div class="text-sm font-semibold">Tab 4 - Procedura assicurativa per ticket #{{ $ticket->id }}</div>
|
|
<div class="text-xs text-gray-600">{{ $ticket->titolo }} · {{ optional($ticket->stabile)->denominazione ?: '-' }}</div>
|
|
</div>
|
|
<div class="flex flex-wrap gap-2">
|
|
<a href="{{ $this->getInsuranceClaimsUrl() }}" class="inline-flex items-center rounded-md bg-slate-700 px-3 py-1.5 text-xs font-medium text-white hover:bg-slate-600">Elenco pratiche</a>
|
|
<button type="button" wire:click="$set('activeTab', 'scheda')" class="inline-flex items-center rounded-md bg-gray-700 px-3 py-1.5 text-xs font-medium text-white hover:bg-gray-600">Torna alla scheda ticket</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4 rounded-xl border bg-white p-4">
|
|
<div class="flex flex-wrap items-center justify-between gap-2">
|
|
<div>
|
|
<div class="text-sm font-semibold">Assicurazione e sinistro</div>
|
|
<div class="mt-1 text-xs text-gray-500">Apertura e aggiornamento pratica in area separata dal resto della lavorazione ticket.</div>
|
|
</div>
|
|
@if($ticket->insuranceClaim)
|
|
<span class="inline-flex rounded-full bg-emerald-100 px-2 py-1 text-xs font-medium text-emerald-700">
|
|
Sinistro {{ $ticket->insuranceClaim->status ?: 'aperto' }}
|
|
</span>
|
|
@endif
|
|
</div>
|
|
|
|
<div class="mt-3 grid gap-3 md:grid-cols-2">
|
|
<label class="block text-sm">
|
|
<span class="mb-1 block font-medium">Polizza stabile</span>
|
|
<select wire:model.defer="insurancePolicyId" class="w-full rounded-lg border-gray-300">
|
|
<option value="">Seleziona una polizza</option>
|
|
@foreach($this->insurancePolicyOptions as $policyId => $policyLabel)
|
|
<option value="{{ $policyId }}">{{ $policyLabel }}</option>
|
|
@endforeach
|
|
</select>
|
|
</label>
|
|
<label class="block text-sm">
|
|
<span class="mb-1 block font-medium">Riferimento polizza</span>
|
|
<input type="text" wire:model.defer="insurancePolicyReference" class="w-full rounded-lg border-gray-300" placeholder="Polizza o riferimento assicurativo" />
|
|
</label>
|
|
<label class="block text-sm">
|
|
<span class="mb-1 block font-medium">Numero sinistro</span>
|
|
<input type="text" wire:model.defer="insuranceClaimNumber" class="w-full rounded-lg border-gray-300" placeholder="Numero pratica/sinistro" />
|
|
</label>
|
|
<label class="block text-sm">
|
|
<span class="mb-1 block font-medium">Stato pratica</span>
|
|
<select wire:model.defer="insuranceStatus" class="w-full rounded-lg border-gray-300">
|
|
<option value="aperta">Aperta</option>
|
|
<option value="presa_in_carico">Presa in carico</option>
|
|
<option value="documenti_inviati">Documenti inviati</option>
|
|
<option value="in_valutazione">In valutazione</option>
|
|
<option value="in_perizia">In perizia</option>
|
|
<option value="chiusa">Chiusa</option>
|
|
</select>
|
|
</label>
|
|
<label class="block text-sm">
|
|
<span class="mb-1 block font-medium">Presa in carico il</span>
|
|
<input type="datetime-local" wire:model.defer="insuranceTakenInChargeAt" class="w-full rounded-lg border-gray-300" />
|
|
</label>
|
|
<label class="block text-sm">
|
|
<span class="mb-1 block font-medium">Email spedita il</span>
|
|
<input type="datetime-local" wire:model.defer="insuranceEmailSentAt" class="w-full rounded-lg border-gray-300" />
|
|
</label>
|
|
<label class="block text-sm">
|
|
<span class="mb-1 block font-medium">Parlato con assicuratore il</span>
|
|
<input type="datetime-local" wire:model.defer="insuranceInsurerContactedAt" class="w-full rounded-lg border-gray-300" />
|
|
</label>
|
|
<label class="block text-sm">
|
|
<span class="mb-1 block font-medium">Documenti inviati il</span>
|
|
<input type="datetime-local" wire:model.defer="insuranceDocumentsSentAt" class="w-full rounded-lg border-gray-300" />
|
|
</label>
|
|
<label class="block text-sm">
|
|
<span class="mb-1 block font-medium">Appuntamento / perizia</span>
|
|
<input type="datetime-local" wire:model.defer="insuranceAppointmentAt" class="w-full rounded-lg border-gray-300" />
|
|
</label>
|
|
<label class="block text-sm">
|
|
<span class="mb-1 block font-medium">Chiusa il</span>
|
|
<input type="datetime-local" wire:model.defer="insuranceClosedAt" class="w-full rounded-lg border-gray-300" />
|
|
</label>
|
|
<label class="block text-sm md:col-span-2">
|
|
<span class="mb-1 block font-medium">Prossima azione</span>
|
|
<input type="text" wire:model.defer="insuranceNextAction" class="w-full rounded-lg border-gray-300" placeholder="Richiamare il perito, inviare quietanza, attendere risposta, ecc." />
|
|
</label>
|
|
</div>
|
|
|
|
<label class="mt-3 block text-sm">
|
|
<span class="mb-1 block font-medium">Note sinistro</span>
|
|
<textarea rows="3" wire:model.defer="insuranceNotes" class="w-full rounded-lg border-gray-300" placeholder="Dettagli apertura o aggiornamento pratica"></textarea>
|
|
</label>
|
|
|
|
@if($ticket->insuranceClaim)
|
|
<div class="mt-3 rounded-lg border border-slate-200 bg-slate-50 p-3 text-xs text-slate-700">
|
|
Aperto il {{ optional($ticket->insuranceClaim->opened_at)->format('d/m/Y H:i') ?: '-' }}
|
|
@if($ticket->insuranceClaim->insurancePolicy)
|
|
· {{ $ticket->insuranceClaim->insurancePolicy->display_name }}
|
|
@endif
|
|
@if($ticket->insuranceClaim->claim_number)
|
|
· Sinistro {{ $ticket->insuranceClaim->claim_number }}
|
|
@endif
|
|
@if($ticket->insuranceClaim->policy_reference)
|
|
· Polizza {{ $ticket->insuranceClaim->policy_reference }}
|
|
@endif
|
|
@if(data_get($ticket->insuranceClaim->metadata, 'taken_in_charge_at'))
|
|
<div class="mt-1">Presa in carico: {{ \Carbon\Carbon::parse(data_get($ticket->insuranceClaim->metadata, 'taken_in_charge_at'))->format('d/m/Y H:i') }}</div>
|
|
@endif
|
|
@if(data_get($ticket->insuranceClaim->metadata, 'email_sent_at'))
|
|
<div>Email spedita: {{ \Carbon\Carbon::parse(data_get($ticket->insuranceClaim->metadata, 'email_sent_at'))->format('d/m/Y H:i') }}</div>
|
|
@endif
|
|
@if(data_get($ticket->insuranceClaim->metadata, 'insurer_contacted_at'))
|
|
<div>Parlato con assicuratore: {{ \Carbon\Carbon::parse(data_get($ticket->insuranceClaim->metadata, 'insurer_contacted_at'))->format('d/m/Y H:i') }}</div>
|
|
@endif
|
|
@if(data_get($ticket->insuranceClaim->metadata, 'documents_sent_at'))
|
|
<div>Documenti inviati: {{ \Carbon\Carbon::parse(data_get($ticket->insuranceClaim->metadata, 'documents_sent_at'))->format('d/m/Y H:i') }}</div>
|
|
@endif
|
|
@if(data_get($ticket->insuranceClaim->metadata, 'appointment_at'))
|
|
<div>Appuntamento/perizia: {{ \Carbon\Carbon::parse(data_get($ticket->insuranceClaim->metadata, 'appointment_at'))->format('d/m/Y H:i') }}</div>
|
|
@endif
|
|
@if(data_get($ticket->insuranceClaim->metadata, 'closed_at'))
|
|
<div>Chiusa il: {{ \Carbon\Carbon::parse(data_get($ticket->insuranceClaim->metadata, 'closed_at'))->format('d/m/Y H:i') }}</div>
|
|
@endif
|
|
@if(data_get($ticket->insuranceClaim->metadata, 'next_action'))
|
|
<div>Prossima azione: {{ data_get($ticket->insuranceClaim->metadata, 'next_action') }}</div>
|
|
@endif
|
|
</div>
|
|
@endif
|
|
|
|
<div class="mt-3 flex flex-wrap gap-2">
|
|
<x-filament::button color="warning" wire:click="salvaSinistroAssicurativo">Salva sinistro</x-filament::button>
|
|
<a href="{{ $this->getInsuranceClaimsUrl() }}" class="inline-flex items-center rounded-md bg-slate-700 px-3 py-1.5 text-xs font-medium text-white hover:bg-slate-600">Apri elenco pratiche</a>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
@if($attachmentPreview)
|
|
@php($attachmentPreviewUrl = (string) ($attachmentPreview['url'] ?? ''))
|
|
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4" x-data='{
|
|
zoom: 1,
|
|
pdfPage: 1,
|
|
iframeKey: 0,
|
|
pdfUrl: @js($attachmentPreviewUrl),
|
|
refreshPdf() { this.iframeKey++; },
|
|
zoomIn() { this.zoom = Math.min(this.zoom + 0.2, 4); },
|
|
zoomOut() { this.zoom = Math.max(this.zoom - 0.2, 0.5); },
|
|
resetZoom() { this.zoom = 1; },
|
|
nextPage() { this.pdfPage = this.pdfPage + 1; this.refreshPdf(); },
|
|
prevPage() { this.pdfPage = Math.max(this.pdfPage - 1, 1); this.refreshPdf(); },
|
|
printPdf() {
|
|
const frame = this.$refs.pdfFrame;
|
|
if (frame?.contentWindow) {
|
|
frame.contentWindow.focus();
|
|
frame.contentWindow.print();
|
|
} else if (this.pdfUrl) {
|
|
window.open(this.pdfUrl, "_blank");
|
|
}
|
|
}
|
|
}'>
|
|
<div class="h-[92vh] w-full max-w-[96vw] rounded-xl bg-white shadow-2xl">
|
|
<div class="flex items-center justify-between border-b px-4 py-3">
|
|
<div>
|
|
<div class="text-sm font-semibold">{{ $attachmentPreview['name'] ?? 'Allegato' }}</div>
|
|
@if(!empty($attachmentPreview['details']['primary_description']))
|
|
<div class="text-xs text-gray-500">{{ $attachmentPreview['details']['primary_description'] }}</div>
|
|
@endif
|
|
@if(!empty($attachmentPreview['details']['gps']) || !empty($attachmentPreview['details']['exif_datetime']) || !empty($attachmentPreview['details']['device']))
|
|
<div class="mt-1 flex flex-wrap gap-2 text-[11px] text-slate-600">
|
|
@if(!empty($attachmentPreview['details']['gps']))
|
|
<span>GPS {{ $attachmentPreview['details']['gps']['label'] }}</span>
|
|
@endif
|
|
@if(!empty($attachmentPreview['details']['exif_datetime']))
|
|
<span>EXIF {{ $attachmentPreview['details']['exif_datetime'] }}</span>
|
|
@endif
|
|
@if(!empty($attachmentPreview['details']['device']))
|
|
<span>{{ $attachmentPreview['details']['device'] }}</span>
|
|
@endif
|
|
</div>
|
|
@endif
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
@if($attachmentPreview['is_image'] ?? false)
|
|
<button type="button" x-on:click="zoomOut()" class="rounded-md bg-slate-100 px-2 py-1 text-xs hover:bg-slate-200">-</button>
|
|
<button type="button" x-on:click="resetZoom()" class="rounded-md bg-slate-100 px-2 py-1 text-xs hover:bg-slate-200">100%</button>
|
|
<button type="button" x-on:click="zoomIn()" class="rounded-md bg-slate-100 px-2 py-1 text-xs hover:bg-slate-200">+</button>
|
|
@elseif($attachmentPreview['is_pdf'] ?? false)
|
|
<button type="button" x-on:click="prevPage()" class="rounded-md bg-slate-100 px-2 py-1 text-xs hover:bg-slate-200">Pagina precedente</button>
|
|
<span class="text-xs text-slate-500">Pagina <span x-text="pdfPage"></span></span>
|
|
<button type="button" x-on:click="nextPage()" class="rounded-md bg-slate-100 px-2 py-1 text-xs hover:bg-slate-200">Pagina successiva</button>
|
|
<button type="button" x-on:click="printPdf()" class="rounded-md bg-slate-100 px-2 py-1 text-xs hover:bg-slate-200">Stampa PDF</button>
|
|
@endif
|
|
@if(!empty($attachmentPreview['details']['gps']))
|
|
<button type="button" wire:click="openAttachmentMapPreview({{ (int) ($attachmentPreview['id'] ?? 0) }})" class="rounded-md bg-emerald-100 px-2 py-1 text-xs text-emerald-800 hover:bg-emerald-200">Google Maps</button>
|
|
@endif
|
|
<a href="{{ $attachmentPreview['url'] }}" target="_blank" class="rounded-md bg-slate-100 px-2 py-1 text-xs hover:bg-slate-200">Apri in nuova scheda</a>
|
|
<button type="button" wire:click="closeAttachmentPreview" class="rounded-md bg-gray-100 px-2 py-1 text-xs hover:bg-gray-200">Chiudi</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="h-[calc(92vh-64px)] overflow-auto p-4">
|
|
@if(!empty($attachmentPreview['details']['full_description']))
|
|
<div class="mb-3 rounded-lg border border-slate-200 bg-slate-50 p-3 text-xs text-slate-700">
|
|
<div class="font-medium text-slate-900">Dettagli allegato</div>
|
|
<div class="mt-1 whitespace-pre-wrap">{{ $attachmentPreview['details']['full_description'] }}</div>
|
|
</div>
|
|
@endif
|
|
@if($attachmentPreview['is_image'] ?? false)
|
|
<div class="h-full overflow-auto rounded-lg bg-slate-50 p-4">
|
|
<div class="flex min-h-full min-w-full items-start justify-center">
|
|
<img src="{{ $attachmentPreview['url'] }}" alt="Anteprima allegato" class="max-w-none rounded border bg-white shadow" x-bind:style="`width: ${zoom * 100}%; height: auto;`" />
|
|
</div>
|
|
</div>
|
|
@elseif($attachmentPreview['is_pdf'] ?? false)
|
|
<iframe x-ref="pdfFrame" x-bind:key="iframeKey" x-bind:src="pdfUrl + '#toolbar=1&navpanes=0&scrollbar=1&view=FitH&zoom=page-width&page=' + pdfPage" class="h-[84vh] w-full rounded border"></iframe>
|
|
@else
|
|
<div class="rounded-md border bg-gray-50 p-4 text-sm text-gray-700">
|
|
Anteprima non disponibile per questo formato. Scarica o apri il file:
|
|
<a href="{{ $attachmentPreview['url'] }}" class="ml-1 text-primary-600 hover:underline">Apri file</a>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
@if($attachmentMapPreview)
|
|
<div class="fixed inset-0 z-[60] flex items-center justify-center bg-black/70 p-4">
|
|
<div class="h-[88vh] w-full max-w-5xl rounded-xl bg-white shadow-2xl">
|
|
<div class="flex items-center justify-between border-b px-4 py-3">
|
|
<div>
|
|
<div class="text-sm font-semibold">Mappa allegato</div>
|
|
<div class="text-xs text-slate-500">{{ $attachmentMapPreview['name'] ?? 'Allegato' }} · {{ $attachmentMapPreview['label'] ?? '' }}</div>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<a href="{{ $attachmentMapPreview['maps_url'] }}" target="_blank" rel="noopener noreferrer" class="rounded-md bg-emerald-100 px-2 py-1 text-xs text-emerald-800 hover:bg-emerald-200">Apri Google Maps</a>
|
|
<button type="button" wire:click="closeAttachmentMapPreview" class="rounded-md bg-gray-100 px-2 py-1 text-xs hover:bg-gray-200">Chiudi</button>
|
|
</div>
|
|
</div>
|
|
<div class="h-[calc(88vh-64px)] p-4">
|
|
<iframe src="{{ $attachmentMapPreview['embed_url'] }}" class="h-full w-full rounded-lg border" loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</x-filament-panels::page>
|