fix: separa lavagna chiamate e filtra interni

This commit is contained in:
michele 2026-04-04 13:47:15 +00:00
parent 236aef53cd
commit ab7b804d08
2 changed files with 139 additions and 88 deletions

View File

@ -68,8 +68,8 @@ public function mount(): void
$focus = (int) request()->query('focus_post_it', 0);
$this->focusPostItId = $focus > 0 ? $focus : null;
if (request()->query('tab') === 'storico') {
$this->activeTab = 'storico';
if (in_array((string) request()->query('tab'), ['storico', 'lavagna'], true)) {
$this->activeTab = (string) request()->query('tab');
}
}
@ -232,45 +232,67 @@ public function getRecentiProperty()
$query->orderByRaw('CASE WHEN id = ? THEN 0 ELSE 1 END', [$this->focusPostItId]);
}
return $query->limit(50)->get();
return $query->limit(80)->get();
} catch (QueryException) {
return collect();
}
}
public function getRecentiVisibiliProperty()
{
return $this->recenti
->filter(fn(ChiamataPostIt $postIt): bool => ! $this->isInternalPostIt($postIt))
->values();
}
public function getRecentiRaggruppatiProperty()
{
$groups = [];
foreach ($this->recenti as $postIt) {
foreach ($this->recentiVisibili as $postIt) {
if ($this->isInternalPostIt($postIt)) {
continue;
}
$groupKey = $this->buildGroupedPostItKey($postIt);
$lastIndex = count($groups) - 1;
if ($lastIndex >= 0 && $groups[$lastIndex]['key'] === $groupKey) {
$groups[$lastIndex]['items'][] = $postIt;
$groups[$lastIndex]['count']++;
$groups[$lastIndex]['last_at'] = optional($postIt->chiamata_il)->format('d/m/Y H:i') ?: '-';
if (isset($groups[$groupKey])) {
$groups[$groupKey]['items'][] = $postIt;
$groups[$groupKey]['count']++;
$groups[$groupKey]['last_at'] = optional($postIt->chiamata_il)->format('d/m/Y H:i') ?: '-';
$groups[$groupKey]['latest'] = $this->resolveLatestGroupedPostIt($groups[$groupKey]['items']);
$groups[$groupKey]['caller_label'] = $this->resolveGroupedCallerLabel($groups[$groupKey]['items']);
continue;
}
$groups[] = [
$groups[$groupKey] = [
'key' => $groupKey,
'items' => [$postIt],
'count' => 1,
'latest' => $postIt,
'caller_label' => $postIt->nome_chiamante ?: 'Chiamante non identificato',
'phone' => (string) ($postIt->telefono ?? ''),
'route_label' => $this->resolvePostItRouteLabel($postIt),
'first_at' => optional($postIt->chiamata_il)->format('d/m/Y H:i') ?: '-',
'last_at' => optional($postIt->chiamata_il)->format('d/m/Y H:i') ?: '-',
];
}
return collect($groups);
return collect(array_values($groups))
->map(function (array $group): array {
usort($group['items'], function (ChiamataPostIt $left, ChiamataPostIt $right): int {
return ($right->chiamata_il?->getTimestamp() ?? 0) <=> ($left->chiamata_il?->getTimestamp() ?? 0);
});
$group['latest'] = $this->resolveLatestGroupedPostIt($group['items']);
$oldest = collect($group['items'])
->sortBy(fn(ChiamataPostIt $item) => $item->chiamata_il?->getTimestamp() ?? 0)
->first();
$group['first_at'] = optional($oldest?->chiamata_il)->format('d/m/Y H:i') ?: '-';
$group['last_at'] = optional($group['latest']->chiamata_il)->format('d/m/Y H:i') ?: '-';
return $group;
})
->sortByDesc(fn(array $group) => $group['latest']->chiamata_il?->getTimestamp() ?? 0)
->values();
}
public function getChiamateTecnicheProperty()
@ -563,28 +585,12 @@ private function buildTicketDescriptionFromPostIt(int $postItId, ChiamataPostIt
private function buildGroupedPostItKey(ChiamataPostIt $postIt): string
{
$phone = preg_replace('/\D+/', '', (string) ($postIt->telefono ?? '')) ?: '';
$caller = mb_strtolower(trim((string) ($postIt->nome_chiamante ?? '')));
$route = mb_strtolower($this->resolvePostItRouteLabel($postIt));
return implode('|', [$phone, $caller, $route, (string) $postIt->direzione]);
}
private function resolvePostItRouteLabel(ChiamataPostIt $postIt): string
{
$message = $this->resolveMessageFromPostIt($postIt);
if ($message instanceof CommunicationMessage) {
$line = trim((string) ($message->target_extension ?: data_get($message->metadata, 'smdr.co', '')));
if ($line !== '') {
return $line;
}
$phone = $this->normalizeGroupedPhone((string) ($postIt->telefono ?? ''));
if ($phone !== '') {
return 'phone:' . $phone;
}
if (preg_match('/linea\s+([^\n]+)/i', (string) ($postIt->oggetto ?? ''), $matches) === 1) {
return trim((string) $matches[1]);
}
return '-';
return 'caller:' . mb_strtolower(trim((string) ($postIt->nome_chiamante ?? 'sconosciuto')));
}
private function isInternalPostIt(ChiamataPostIt $postIt): bool
@ -614,6 +620,38 @@ private function resolveMessageFromPostIt(ChiamataPostIt $postIt): ?Communicatio
return $message;
}
private function normalizeGroupedPhone(string $phone): string
{
return preg_replace('/\D+/', '', $phone) ?: '';
}
/**
* @param array<int,ChiamataPostIt> $items
*/
private function resolveLatestGroupedPostIt(array $items): ChiamataPostIt
{
usort($items, function (ChiamataPostIt $left, ChiamataPostIt $right): int {
return ($right->chiamata_il?->getTimestamp() ?? 0) <=> ($left->chiamata_il?->getTimestamp() ?? 0);
});
return $items[0];
}
/**
* @param array<int,ChiamataPostIt> $items
*/
private function resolveGroupedCallerLabel(array $items): string
{
foreach ($items as $item) {
$label = trim((string) ($item->nome_chiamante ?? ''));
if ($label !== '') {
return $label;
}
}
return 'Chiamante non identificato';
}
public function richiediClickToCallDaMessaggio(int $messageId): void
{
$user = Auth::user();

View File

@ -15,6 +15,9 @@
<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', 'lavagna')" class="rounded-md px-3 py-1.5 text-xs font-medium {{ $activeTab === 'lavagna' ? 'bg-amber-600 text-white' : 'bg-amber-50 text-amber-900 hover:bg-amber-100' }}">
Lavagna chiamate
</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>
@ -33,62 +36,8 @@
<div class="rounded-xl border bg-white p-4">
<h2 class="mb-3 text-base font-semibold">Storico chiamate recenti</h2>
<div class="mb-5">
<div class="mb-2 flex items-center justify-between gap-2">
<div>
<div class="text-sm font-semibold text-slate-800">Box chiamate raggruppate</div>
<div class="text-xs text-slate-500">Le comunicazioni interne non compaiono qui. Le chiamate consecutive dello stesso numero restano nello stesso box finche non interviene un altro chiamante.</div>
</div>
</div>
<div class="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
@forelse($this->recentiRaggruppati as $gruppo)
@php($box = $gruppo['latest'])
<div class="rounded-xl border border-slate-200 bg-slate-50 p-4">
<div class="flex items-start justify-between gap-3">
<div>
<div class="font-semibold text-slate-900">{{ $gruppo['caller_label'] }}</div>
<div class="text-sm text-slate-600">
{{ $gruppo['phone'] !== '' ? $gruppo['phone'] : 'Numero non disponibile' }}
@if($gruppo['route_label'] !== '-')
· verso {{ $gruppo['route_label'] }}
@endif
</div>
</div>
<span class="rounded-full bg-indigo-100 px-2.5 py-1 text-xs font-semibold text-indigo-800">{{ $gruppo['count'] }} tentativi</span>
</div>
<div class="mt-3 text-xs text-slate-500">{{ $gruppo['last_at'] }}{{ $gruppo['count'] > 1 ? ' · gruppo consecutivo' : '' }}</div>
<div class="mt-2 text-sm text-slate-700">{{ $box->oggetto ?: 'Senza oggetto' }}</div>
<div class="mt-3 flex flex-wrap gap-2 text-[11px]">
<span class="rounded-md bg-white px-2 py-1 text-slate-700">{{ $box->stato }}</span>
@if($box->assegnazione_tipo)
<span class="rounded-md bg-amber-100 px-2 py-1 text-amber-800">{{ ucfirst(str_replace('_', ' ', (string) $box->assegnazione_tipo)) }}</span>
@endif
@if($box->assegnatoAUser)
<span class="rounded-md bg-emerald-100 px-2 py-1 text-emerald-800">{{ $box->assegnatoAUser->name }}</span>
@endif
@if($box->assegnatoAFornitore)
<span class="rounded-md bg-emerald-100 px-2 py-1 text-emerald-800">{{ $box->assegnatoAFornitore->ragione_sociale ?: trim(($box->assegnatoAFornitore->nome ?? '') . ' ' . ($box->assegnatoAFornitore->cognome ?? '')) }}</span>
@endif
</div>
<div class="mt-4 flex flex-wrap gap-2">
<a href="{{ $this->getPostItPageUrl((int) $box->id) }}" 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">Apri Post-it</a>
@if($box->ticket_id)
<a href="{{ \App\Filament\Pages\Supporto\TicketGestione::getUrl(panel: 'admin-filament') }}#ticket-{{ (int) $box->ticket_id }}" class="inline-flex items-center rounded-md border px-3 py-1.5 text-xs">Apri ticket</a>
@endif
</div>
</div>
@empty
<div class="rounded-lg border border-dashed p-4 text-sm text-gray-500 md:col-span-2 xl:col-span-3">Nessuna chiamata esterna raggruppabile disponibile.</div>
@endforelse
</div>
</div>
<div class="space-y-3">
@forelse($this->recenti as $riga)
@forelse($this->recentiVisibili 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,
@ -159,6 +108,70 @@
@endforelse
</div>
</div>
@elseif($activeTab === 'lavagna')
<div class="rounded-xl border border-amber-200 bg-amber-50/40 p-4">
<div class="mb-4">
<h2 class="text-base font-semibold text-amber-900">Lavagna chiamate raggruppate</h2>
<p class="text-sm text-amber-800">Qui vedi i Post-it raggruppati per numero chiamante. Le chiamate interne restano nel database ma non vengono visualizzate.</p>
</div>
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
@forelse($this->recentiRaggruppati as $gruppo)
@php($box = $gruppo['latest'])
<article class="rounded-2xl border border-amber-200 bg-[#fff4b8] p-4 shadow-sm">
<div class="flex items-start justify-between gap-3">
<div>
<div class="text-sm font-semibold uppercase tracking-wide text-amber-900">{{ $box->oggetto ?: 'Post-it chiamate' }}</div>
<div class="mt-1 text-sm text-amber-950">
{{ $gruppo['caller_label'] }}
@if($gruppo['phone'] !== '')
- {{ $gruppo['phone'] }}
@endif
</div>
@php($rubricaUrlGrouped = $this->getRubricaUrlByPhone((string) $gruppo['phone']))
@if($rubricaUrlGrouped)
<a href="{{ $rubricaUrlGrouped }}" class="mt-1 inline-flex text-xs font-medium text-indigo-700 hover:underline">Apri scheda rubrica</a>
@endif
</div>
<span class="rounded-full bg-amber-200 px-2.5 py-1 text-xs font-semibold text-amber-900">{{ $gruppo['count'] }} chiamate</span>
</div>
<div class="mt-3 text-xs text-amber-900">
Aperto dal {{ $gruppo['first_at'] }} al {{ $gruppo['last_at'] }}
</div>
<div class="mt-4 space-y-3 border-t border-amber-300 pt-3">
@foreach($gruppo['items'] as $item)
<div class="rounded-xl border border-amber-300 bg-amber-50/70 p-3">
<div class="flex items-start justify-between gap-3">
<div>
<div class="text-sm font-medium text-amber-950">{{ $item->oggetto ?: 'Senza oggetto' }}</div>
<div class="text-xs text-amber-800">{{ optional($item->chiamata_il)->format('d/m/Y H:i') }}</div>
</div>
<span class="rounded-md bg-white/70 px-2 py-1 text-[11px] text-amber-900">{{ $item->stato }}</span>
</div>
@if($item->stabile)
<div class="mt-1 text-xs text-amber-900">{{ $item->stabile->denominazione ?: ('Stabile #' . $item->stabile->id) }}</div>
@endif
<div class="mt-2 whitespace-pre-wrap text-xs text-amber-950">{{ $item->nota }}</div>
</div>
@endforeach
</div>
<div class="mt-4 flex flex-wrap gap-2">
<a href="{{ $this->getPostItPageUrl((int) $box->id) }}" 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 Post-it</a>
@if($box->ticket_id)
<a href="{{ \App\Filament\Pages\Supporto\TicketGestione::getUrl(panel: 'admin-filament') }}#ticket-{{ (int) $box->ticket_id }}" class="inline-flex items-center rounded-md border border-amber-400 bg-white/70 px-3 py-1.5 text-xs text-amber-900">Apri ticket</a>
@endif
</div>
</article>
@empty
<div class="rounded-lg border border-dashed border-amber-300 bg-white/70 p-4 text-sm text-amber-900 md:col-span-2 xl:col-span-3">Nessuna chiamata esterna disponibile per la lavagna.</div>
@endforelse
</div>
</div>
@elseif(in_array($activeTab, ['smdr', 'csta'], true))
<div class="rounded-xl border bg-white p-4">
@php($isCstaTab = $activeTab === 'csta')