272 lines
20 KiB
PHP
272 lines
20 KiB
PHP
<x-filament-panels::page>
|
|
<div class="space-y-6">
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="flex items-center justify-between gap-2">
|
|
<div>
|
|
<h2 class="text-base font-semibold">Gestione Post-it chiamate</h2>
|
|
<p class="text-sm text-gray-500">Vista dedicata allo storico e alle conversioni in ticket.</p>
|
|
</div>
|
|
<a href="{{ $this->getPostItInserimentoUrl() }}" 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 Post-it</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rounded-xl border bg-white p-3">
|
|
<div class="flex flex-wrap gap-2">
|
|
<button type="button" wire:click="$set('activeTab', 'storico')" class="rounded-md px-3 py-1.5 text-xs font-medium {{ $activeTab === 'storico' ? 'bg-indigo-700 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200' }}">
|
|
Storico Post-it
|
|
</button>
|
|
<button type="button" wire:click="$set('activeTab', 'smdr')" class="rounded-md px-3 py-1.5 text-xs font-medium {{ $activeTab === 'smdr' ? 'bg-indigo-700 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200' }}">
|
|
Chiamate Tecniche SMDR
|
|
</button>
|
|
<button type="button" wire:click="$set('activeTab', 'csta')" class="rounded-md px-3 py-1.5 text-xs font-medium {{ $activeTab === 'csta' ? 'bg-indigo-700 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200' }}">
|
|
CTI / CSTA e Click-to-Call
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
@if(! $this->isPostItTableReady())
|
|
<div class="rounded-xl border border-amber-300 bg-amber-50 p-4 text-sm text-amber-800">
|
|
Tabella chiamate non pronta. Esegui le migrazioni per usare la gestione Post-it.
|
|
</div>
|
|
@else
|
|
@if($activeTab === 'storico')
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<h2 class="mb-3 text-base font-semibold">Storico chiamate recenti</h2>
|
|
|
|
<div class="space-y-3">
|
|
@forelse($this->recenti as $riga)
|
|
<div @class([
|
|
'rounded-lg border p-3',
|
|
'border-emerald-400 bg-emerald-50/60 ring-1 ring-emerald-200' => (int) ($this->focusPostItId ?? 0) === (int) $riga->id,
|
|
])>
|
|
<div class="flex flex-wrap items-center justify-between gap-2">
|
|
<div>
|
|
<div class="font-medium">{{ $riga->oggetto ?: 'Senza oggetto' }}</div>
|
|
@if((int) ($this->focusPostItId ?? 0) === (int) $riga->id)
|
|
<div class="mt-1 text-[11px] font-semibold uppercase tracking-wide text-emerald-700">Post-it appena creato</div>
|
|
@endif
|
|
<div class="text-sm text-gray-600">
|
|
{{ $riga->nome_chiamante ?: 'Chiamante non identificato' }}
|
|
@if($riga->telefono)
|
|
- {{ $riga->telefono }}
|
|
@php($rubricaNome = $this->getRubricaNomeByPhone((string) $riga->telefono))
|
|
@if($rubricaNome)
|
|
- <span class="font-medium text-indigo-700">{{ $rubricaNome }}</span>
|
|
@endif
|
|
@php($rubricaUrl = $this->getRubricaUrlByPhone((string) $riga->telefono))
|
|
@if($rubricaUrl)
|
|
- <a href="{{ $rubricaUrl }}" class="text-indigo-700 hover:underline">Apri scheda rubrica</a>
|
|
@endif
|
|
@endif
|
|
@if($riga->stabile)
|
|
- {{ $riga->stabile->denominazione ?: ('Stabile #' . $riga->stabile->id) }}
|
|
@endif
|
|
</div>
|
|
</div>
|
|
<div class="text-xs text-gray-500">{{ optional($riga->chiamata_il)->format('d/m/Y H:i') }}</div>
|
|
</div>
|
|
|
|
<div class="mt-2 text-sm text-gray-700 whitespace-pre-wrap">{{ $riga->nota }}</div>
|
|
|
|
<div class="mt-3 flex flex-wrap gap-2">
|
|
<span class="rounded-md bg-gray-100 px-2 py-1 text-xs">{{ $riga->priorita }}</span>
|
|
<span class="rounded-md bg-blue-100 px-2 py-1 text-xs text-blue-800">{{ $riga->stato }}</span>
|
|
<span class="rounded-md bg-amber-100 px-2 py-1 text-xs text-amber-800">{{ str_replace('_', ' ', (string) $riga->direzione) }}</span>
|
|
@if($riga->esito)
|
|
<span class="rounded-md bg-emerald-100 px-2 py-1 text-xs text-emerald-800">{{ $riga->esito }}</span>
|
|
@endif
|
|
|
|
@if(!$riga->ticket_id && $riga->stato !== 'chiusa')
|
|
<div class="flex flex-wrap items-center gap-2 rounded-md border bg-slate-50 px-2 py-1">
|
|
<input type="text" wire:model.defer="conversioneNote.{{ (int) $riga->id }}" class="w-56 rounded-md border-gray-300 text-xs" placeholder="Nota libera prima del ticket (opzionale)" />
|
|
<x-filament::button size="sm" color="primary" wire:click="convertiInTicket({{ $riga->id }})">Converti in ticket</x-filament::button>
|
|
</div>
|
|
@endif
|
|
|
|
@if($riga->stato !== 'chiusa')
|
|
<x-filament::button size="sm" color="gray" wire:click="chiudiPostIt({{ $riga->id }})">Chiudi</x-filament::button>
|
|
@endif
|
|
|
|
@php($ticketClosed = $riga->ticket && in_array((string) $riga->ticket->stato, ['Chiuso', 'Risolto'], true))
|
|
@if($riga->stato === 'chiusa' || $ticketClosed)
|
|
<div class="flex flex-wrap items-center gap-2 rounded-md border bg-slate-50 px-2 py-1">
|
|
<input type="text" wire:model.defer="riaperturaNote.{{ (int) $riga->id }}" class="w-56 rounded-md border-gray-300 text-xs" placeholder="Nota riapertura (opzionale)" />
|
|
<x-filament::button size="sm" color="warning" wire:click="riapriPostIt({{ (int) $riga->id }})">Riapri post-it/ticket</x-filament::button>
|
|
</div>
|
|
@endif
|
|
|
|
@if($riga->ticket_id)
|
|
<a href="{{ \App\Filament\Pages\Supporto\TicketGestione::getUrl(panel: 'admin-filament') }}#ticket-{{ (int) $riga->ticket_id }}" class="inline-flex items-center rounded-md border px-3 py-1 text-xs">Apri Ticket (#{{ $riga->ticket_id }})</a>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@empty
|
|
<div class="rounded-lg border border-dashed p-4 text-sm text-gray-500">Nessuna chiamata registrata.</div>
|
|
@endforelse
|
|
</div>
|
|
</div>
|
|
@elseif(in_array($activeTab, ['smdr', 'csta'], true))
|
|
<div class="rounded-xl border bg-white p-4">
|
|
@php($isCstaTab = $activeTab === 'csta')
|
|
<h2 class="mb-1 text-base font-semibold">{{ $isCstaTab ? 'Elenco tecnico CTI / CSTA' : 'Elenco tecnico chiamate SMDR' }}</h2>
|
|
<p class="mb-3 text-xs text-gray-500">
|
|
{{ $isCstaTab
|
|
? 'Vista test PBX per eventi CSTA, provider, interno coinvolto e richieste click-to-call. Il provider viene letto dai metadati così il cruscotto resta riusabile anche con FreePBX o altri centralini.'
|
|
: 'Qui vedi in colonne i dati disponibili dal centralino via SMDR: interno, linea/trunk, numero, durata, costo, utente assegnato, stabile e raw line.' }}
|
|
</p>
|
|
|
|
@if($isCstaTab)
|
|
<div class="mb-4 rounded-lg border border-blue-200 bg-blue-50 p-3 text-xs text-blue-900">
|
|
<div class="font-semibold">Controllo chiamate PBX</div>
|
|
<div class="mt-1">Da questo tab puoi verificare gli eventi CSTA e registrare richieste click-to-call verso il PBX dell'interno assegnato. La UI non e vincolata a Panasonic: il provider e esposto come metadato.</div>
|
|
</div>
|
|
|
|
<div class="mb-4 rounded-lg border border-slate-200 bg-slate-50 p-3">
|
|
<div class="mb-2 flex items-center justify-between gap-2">
|
|
<div class="text-xs font-semibold uppercase tracking-wide text-slate-600">Ultime richieste click-to-call</div>
|
|
<div class="text-[11px] text-slate-500">Fonte: coda locale PBX</div>
|
|
</div>
|
|
<div class="overflow-x-auto">
|
|
<table class="min-w-full border text-[11px]">
|
|
<thead class="bg-white">
|
|
<tr>
|
|
<th class="border px-2 py-1 text-left">Richiesta</th>
|
|
<th class="border px-2 py-1 text-left">Interno</th>
|
|
<th class="border px-2 py-1 text-left">Numero</th>
|
|
<th class="border px-2 py-1 text-left">Stato</th>
|
|
<th class="border px-2 py-1 text-left">Provider</th>
|
|
<th class="border px-2 py-1 text-left">Quando</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@forelse($this->recentClickToCallRequests as $req)
|
|
<tr>
|
|
<td class="border px-2 py-1">#{{ $req->id }}</td>
|
|
<td class="border px-2 py-1">{{ $req->source_extension ?: '-' }}</td>
|
|
<td class="border px-2 py-1">{{ $req->target_number ?: '-' }}</td>
|
|
<td class="border px-2 py-1">{{ $req->status ?: '-' }}</td>
|
|
<td class="border px-2 py-1">{{ strtoupper(str_replace('_', ' ', (string) data_get($req->metadata, 'provider', data_get($req->metadata, 'source_channel', '-')))) }}</td>
|
|
<td class="border px-2 py-1">{{ optional($req->requested_at)->format('d/m/Y H:i:s') ?: optional($req->created_at)->format('d/m/Y H:i:s') }}</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="6" class="border px-2 py-3 text-center text-slate-500">Nessuna richiesta click-to-call registrata per il contesto utente corrente.</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="mb-4 grid grid-cols-1 gap-3 rounded-lg border border-slate-200 bg-slate-50 p-3 md:grid-cols-4">
|
|
<div>
|
|
<label class="mb-1 block text-[11px] font-semibold uppercase tracking-wide text-slate-600">Vista chiamate</label>
|
|
<select wire:model.live="tecnicoCallView" class="w-full rounded-md border-gray-300 text-xs">
|
|
<option value="esterne">Solo esterne</option>
|
|
<option value="tutte">Tutte</option>
|
|
<option value="interne">Solo interne</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-[11px] font-semibold uppercase tracking-wide text-slate-600">Ambito numero</label>
|
|
<select wire:model.live="tecnicoScopeFilter" class="w-full rounded-md border-gray-300 text-xs">
|
|
<option value="tutti">Tutti</option>
|
|
<option value="studio">Solo numeri studio gestiti</option>
|
|
<option value="interno">Solo interni collaboratori</option>
|
|
<option value="esterno">Solo numeri esterni</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-[11px] font-semibold uppercase tracking-wide text-slate-600">Linea o gruppo</label>
|
|
<input type="text" wire:model.live.debounce.300ms="tecnicoLineFilter" class="w-full rounded-md border-gray-300 text-xs" placeholder="Es. 0003, 603, GRP00603" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-[11px] font-semibold uppercase tracking-wide text-slate-600">Direzione</label>
|
|
<select wire:model.live="tecnicoDirectionFilter" class="w-full rounded-md border-gray-300 text-xs">
|
|
<option value="tutte">Tutte</option>
|
|
<option value="inbound">Inbound</option>
|
|
<option value="outbound">Outbound</option>
|
|
<option value="internal">Internal</option>
|
|
</select>
|
|
</div>
|
|
<div class="md:col-span-2">
|
|
<label class="mb-1 block text-[11px] font-semibold uppercase tracking-wide text-slate-600">Ricerca</label>
|
|
<input type="text" wire:model.live.debounce.300ms="tecnicoSearch" class="w-full rounded-md border-gray-300 text-xs" placeholder="Numero, interno, testo riga, nominativo tecnico" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="overflow-x-auto">
|
|
<table class="min-w-full border text-xs">
|
|
<thead class="bg-gray-50">
|
|
<tr>
|
|
<th class="border px-2 py-1 text-left">ID</th>
|
|
<th class="border px-2 py-1 text-left">Data/Ora</th>
|
|
<th class="border px-2 py-1 text-left">Provider</th>
|
|
<th class="border px-2 py-1 text-left">Direzione</th>
|
|
<th class="border px-2 py-1 text-left">Ambito</th>
|
|
<th class="border px-2 py-1 text-left">Interno</th>
|
|
<th class="border px-2 py-1 text-left">Linea / Evento</th>
|
|
<th class="border px-2 py-1 text-left">Numero</th>
|
|
<th class="border px-2 py-1 text-left">Nominativo</th>
|
|
<th class="border px-2 py-1 text-left">Durata</th>
|
|
<th class="border px-2 py-1 text-left">Costo</th>
|
|
<th class="border px-2 py-1 text-left">Utente</th>
|
|
<th class="border px-2 py-1 text-left">Stabile</th>
|
|
<th class="border px-2 py-1 text-left">Post-it</th>
|
|
<th class="border px-2 py-1 text-left">Raw</th>
|
|
<th class="border px-2 py-1 text-left">Azioni</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@forelse($this->chiamateTecniche as $m)
|
|
@php($smdr = (array) data_get($m->metadata, 'smdr', []))
|
|
@php($cost = trim((string) (($smdr['cost_currency'] ?? '') . ' ' . ($smdr['cost_amount'] ?? ''))))
|
|
@php($duration = (string) (($smdr['duration_raw'] ?? '') ?: ($smdr['ring_duration_raw'] ?? '')))
|
|
@php($phoneValue = (string) ($m->phone_number ?: ($smdr['dial_number'] ?? '')))
|
|
@php($rubricaNomeTech = $this->getTecnicoNominativo($m))
|
|
<tr>
|
|
<td class="border px-2 py-1">{{ $m->id }}</td>
|
|
<td class="border px-2 py-1">{{ optional($m->received_at)->format('d/m/Y H:i:s') }}</td>
|
|
<td class="border px-2 py-1">{{ $this->getTecnicoProviderLabel($m) }}</td>
|
|
<td class="border px-2 py-1">{{ $m->direction }}</td>
|
|
<td class="border px-2 py-1">{{ $this->getTecnicoScopeLabel($m) }}</td>
|
|
<td class="border px-2 py-1">{{ $this->getTecnicoInternoLabel($m) }}</td>
|
|
<td class="border px-2 py-1">{{ $this->getTecnicoLineaLabel($m) }}</td>
|
|
<td class="border px-2 py-1">{{ (string) ($m->phone_number ?: ($smdr['dial_number'] ?? '-')) }}</td>
|
|
<td class="border px-2 py-1">{{ $rubricaNomeTech ?: '-' }}</td>
|
|
<td class="border px-2 py-1">{{ $duration !== '' ? $duration : '-' }}</td>
|
|
<td class="border px-2 py-1">{{ $cost !== '' ? $cost : '-' }}</td>
|
|
<td class="border px-2 py-1">{{ $m->assignedUser?->name ?: '-' }}</td>
|
|
<td class="border px-2 py-1">{{ $m->stabile?->denominazione ?: ($m->stabile_id ? ('#' . $m->stabile_id) : '-') }}</td>
|
|
<td class="border px-2 py-1">{{ $m->post_it_id ? ('#' . $m->post_it_id) : '-' }}</td>
|
|
<td class="border px-2 py-1 max-w-[340px] truncate" title="{{ $m->message_text }}">{{ $m->message_text }}</td>
|
|
<td class="border px-2 py-1">
|
|
@php($rubricaUrlTech = $this->getRubricaUrlByPhone($phoneValue))
|
|
@if($rubricaUrlTech && ! $this->isInternalSmdrMessage($m))
|
|
<a href="{{ $rubricaUrlTech }}" class="inline-flex items-center rounded-md border px-2 py-1 text-[11px] text-indigo-700 hover:bg-indigo-50">Apri Rubrica</a>
|
|
@endif
|
|
@if($activeTab === 'csta')
|
|
<x-filament::button size="xs" color="info" wire:click="richiediClickToCallDaMessaggio({{ (int) $m->id }})">Click-to-call</x-filament::button>
|
|
@endif
|
|
@if(!$m->post_it_id)
|
|
<x-filament::button size="xs" wire:click="creaPostItDaMessaggio({{ (int) $m->id }})">Crea Post-it</x-filament::button>
|
|
@else
|
|
<span class="text-gray-500">Collegato</span>
|
|
@endif
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="16" class="border px-2 py-3 text-center text-gray-500">{{ $isCstaTab ? 'Nessun evento CSTA disponibile.' : 'Nessun evento SMDR disponibile.' }}</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
@endif
|
|
</div>
|
|
</x-filament-panels::page>
|