From c079737b5bac2138abb381b9a65210dddc59b8fe Mon Sep 17 00:00:00 2001 From: michele Date: Wed, 8 Apr 2026 19:57:14 +0000 Subject: [PATCH] Ticket visibility and dashboard cleanup --- CHANGELOG.md | 3 + .../Pages/Supporto/TicketGestione.php | 139 ++++++++++++++++++ .../Widgets/GoogleWorkspaceOverview.php | 12 +- .../Filament/AdminFilamentPanelProvider.php | 2 - resources/css/filament/admin/theme.css | 14 ++ .../sidebar-avvisi-urgenze.blade.php | 13 +- .../components/topbar-icons.blade.php | 14 +- .../pages/supporto/dashboard.blade.php | 2 - .../pages/supporto/ticket-gestione.blade.php | 89 ++++++++++- .../pages/supporto/ticket-mobile.blade.php | 12 +- 10 files changed, 275 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 313157d..1f9f268 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ # Changelog ## [Unreleased] - Initial open-source release prep. +- Removed active-stabile filtering from topbar avvisi/urgenze counters and from the Google ticket-operativi box so cross-stabile operational tickets stay visible. +- Removed the Google Workspace widget from the main/support dashboard, leaving the full panel only under Impostazioni > Google. +- Tightened Filament sidebar navigation spacing between main menu groups for denser top-level navigation. - Added immediate local preview for Ticket Mobile photos and attachments before final send on phone. - Split insurance handling out of Ticket Gestione into dedicated Tab 4 and added centralized Supporto claims list. - Fixed supplier ticket-detail access by resolving both intervention-id and ticket-id safely in supplier context. diff --git a/app/Filament/Pages/Supporto/TicketGestione.php b/app/Filament/Pages/Supporto/TicketGestione.php index a9ef040..f8bded0 100644 --- a/app/Filament/Pages/Supporto/TicketGestione.php +++ b/app/Filament/Pages/Supporto/TicketGestione.php @@ -114,6 +114,9 @@ class TicketGestione extends Page /** @var array|null */ public ?array $attachmentPreview = null; + /** @var array|null */ + public ?array $attachmentMapPreview = null; + /** @var \Illuminate\Support\Collection */ public $tickets; @@ -320,6 +323,7 @@ public function openAttachmentPreview(int $attachmentId): void $isImage = $attachment->isImage(); $isPdf = $attachment->isPdf(); + $details = $this->getAttachmentDetails($attachment); $this->attachmentPreview = [ 'id' => (int) $attachment->id, @@ -329,12 +333,58 @@ public function openAttachmentPreview(int $attachmentId): void 'is_image' => $isImage, 'is_pdf' => $isPdf, 'mime' => $mime, + 'details' => $details, + ]; + } + + public function openAttachmentMapPreview(int $attachmentId): void + { + $ticket = $this->selectedTicket; + if (! $ticket) { + return; + } + + $attachment = $ticket->attachments->firstWhere('id', $attachmentId); + if (! $attachment instanceof TicketAttachment) { + return; + } + + $details = $this->getAttachmentDetails($attachment); + $gps = $details['gps'] ?? []; + $lat = $gps['lat'] ?? null; + $lng = $gps['lng'] ?? null; + + if (! is_numeric($lat) || ! is_numeric($lng)) { + Notification::make()->title('Coordinate GPS non disponibili per questo allegato')->warning()->send(); + return; + } + + $this->attachmentMapPreview = [ + 'attachment_id' => (int) $attachment->id, + 'name' => (string) ($attachment->original_file_name ?? 'Allegato'), + 'label' => (string) ($gps['label'] ?? (number_format((float) $lat, 5, '.', '') . ', ' . number_format((float) $lng, 5, '.', ''))), + 'maps_url' => (string) ($details['maps_url'] ?? ('https://maps.google.com/?q=' . $lat . ',' . $lng)), + 'embed_url' => (string) ($details['maps_embed_url'] ?? ('https://maps.google.com/maps?q=' . $lat . ',' . $lng . '&z=17&output=embed')), ]; } public function closeAttachmentPreview(): void { $this->attachmentPreview = null; + $this->attachmentMapPreview = null; + } + + public function closeAttachmentMapPreview(): void + { + $this->attachmentMapPreview = null; + } + + /** + * @return array + */ + public function getAttachmentDetails(TicketAttachment $attachment): array + { + return $this->parseAttachmentDescription((string) ($attachment->description ?? '')); } public function getSelectedTicketProperty(): ?Ticket @@ -1276,6 +1326,95 @@ private function syncTicketAttachmentsArchive(Ticket $ticket): void } } + /** + * @return array + */ + private function parseAttachmentDescription(string $description): array + { + $parts = preg_split('/\s*\|\s*/', trim($description)) ?: []; + + $details = [ + 'primary_description' => '', + 'gps' => null, + 'maps_url' => null, + 'maps_embed_url' => null, + 'exif_datetime' => null, + 'device' => null, + 'file_label' => null, + 'original_label' => null, + 'other' => [], + 'full_description' => trim($description), + ]; + + foreach ($parts as $part) { + $part = trim((string) $part); + if ($part === '') { + continue; + } + + if (str_starts_with($part, 'GPS:')) { + $gpsValue = trim((string) Str::after($part, 'GPS:')); + if (preg_match('/^\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*$/', $gpsValue, $matches) === 1) { + $lat = (float) $matches[1]; + $lng = (float) $matches[2]; + + $details['gps'] = [ + 'lat' => $lat, + 'lng' => $lng, + 'label' => number_format($lat, 5, '.', '') . ', ' . number_format($lng, 5, '.', ''), + ]; + $details['maps_embed_url'] = 'https://maps.google.com/maps?q=' . $lat . ',' . $lng . '&z=17&output=embed'; + } + + continue; + } + + if (str_starts_with($part, 'Maps:')) { + $details['maps_url'] = trim((string) Str::after($part, 'Maps:')); + continue; + } + + if (str_starts_with($part, 'EXIF:')) { + $details['exif_datetime'] = trim((string) Str::after($part, 'EXIF:')); + continue; + } + + if (str_starts_with($part, 'Device:')) { + $details['device'] = trim((string) Str::after($part, 'Device:')); + continue; + } + + if (str_starts_with($part, 'File:')) { + $details['file_label'] = trim((string) Str::after($part, 'File:')); + continue; + } + + if (str_starts_with($part, 'Orig:')) { + $details['original_label'] = trim((string) Str::after($part, 'Orig:')); + continue; + } + + if ($details['primary_description'] === '') { + $details['primary_description'] = $part; + continue; + } + + $details['other'][] = $part; + } + + if ($details['maps_url'] === null && is_array($details['gps'])) { + $details['maps_url'] = 'https://maps.google.com/?q=' . $details['gps']['lat'] . ',' . $details['gps']['lng']; + } + + if ($details['primary_description'] === '') { + $details['primary_description'] = $details['full_description'] !== '' + ? $details['full_description'] + : 'Allegato ticket'; + } + + return $details; + } + private function archiveTicketAttachmentDocument(Ticket $ticket, TicketAttachment $attachment): void { if (! Schema::hasTable('documenti')) { diff --git a/app/Filament/Widgets/GoogleWorkspaceOverview.php b/app/Filament/Widgets/GoogleWorkspaceOverview.php index 28d8307..30deeb7 100644 --- a/app/Filament/Widgets/GoogleWorkspaceOverview.php +++ b/app/Filament/Widgets/GoogleWorkspaceOverview.php @@ -673,15 +673,21 @@ private function loadOpenTickets(): void return; } - $stabileId = StabileContext::resolveActiveStabileId($user); - if (! $stabileId) { + $stabileIds = StabileContext::accessibleStabili($user) + ->pluck('id') + ->map(fn($value) => (int) $value) + ->filter(fn(int $value) => $value > 0) + ->values() + ->all(); + + if ($stabileIds === []) { $this->openTickets = []; return; } $items = Ticket::query() ->with('stabile:id,denominazione') - ->where('stabile_id', $stabileId) + ->whereIn('stabile_id', $stabileIds) ->aperti() ->orderByDesc('created_at') ->limit(8) diff --git a/app/Providers/Filament/AdminFilamentPanelProvider.php b/app/Providers/Filament/AdminFilamentPanelProvider.php index c409f58..26bf5af 100644 --- a/app/Providers/Filament/AdminFilamentPanelProvider.php +++ b/app/Providers/Filament/AdminFilamentPanelProvider.php @@ -10,7 +10,6 @@ use App\Filament\Pages\Gescon\RubricaUniversaleArchivio; use App\Filament\Pages\Supporto\SupportoDashboard; use App\Filament\Pages\Supporto\TicketMobile; -use App\Filament\Widgets\GoogleScadenziarioOverview; use App\Filament\Widgets\TicketMobileOverview; use App\Models\Fornitore; use App\Models\FornitoreDipendente; @@ -116,7 +115,6 @@ public function panel(Panel $panel): Panel ->discoverWidgets(in: app_path('Filament/Widgets'), for : 'App\\Filament\\Widgets') ->widgets([ AccountWidget::class, - GoogleScadenziarioOverview::class, TicketMobileOverview::class, ]) ->middleware([ diff --git a/resources/css/filament/admin/theme.css b/resources/css/filament/admin/theme.css index 463cc65..57d948f 100644 --- a/resources/css/filament/admin/theme.css +++ b/resources/css/filament/admin/theme.css @@ -35,4 +35,18 @@ .netgescon-footer { width: 100% !important; flex: 0 0 auto; margin-top: auto; +} + +.fi-sidebar-nav, +.fi-sidebar-nav-groups { + row-gap: calc(var(--spacing) * 4); +} + +.fi-sidebar-group-btn, +.fi-sidebar-group-dropdown-trigger-btn { + padding-block: calc(var(--spacing) * 1.25); +} + +.fi-sidebar-group-label { + line-height: calc(var(--spacing) * 5); } \ No newline at end of file diff --git a/resources/views/filament/components/sidebar-avvisi-urgenze.blade.php b/resources/views/filament/components/sidebar-avvisi-urgenze.blade.php index ed09d4c..01fffc8 100644 --- a/resources/views/filament/components/sidebar-avvisi-urgenze.blade.php +++ b/resources/views/filament/components/sidebar-avvisi-urgenze.blade.php @@ -1,17 +1,22 @@ @php $user = \Illuminate\Support\Facades\Auth::user(); - $stabileId = null; + $stabileIds = []; if ($user instanceof \App\Models\User) { - $stabileId = \App\Support\StabileContext::resolveActiveStabileId($user); + $stabileIds = \App\Support\StabileContext::accessibleStabili($user) + ->pluck('id') + ->map(fn($value) => (int) $value) + ->filter(fn(int $value) => $value > 0) + ->values() + ->all(); } $avvisiCount = 0; $urgenzeCount = 0; - if ($stabileId) { + if ($stabileIds !== []) { $baseQuery = \App\Models\Ticket::query() - ->where('stabile_id', $stabileId) + ->whereIn('stabile_id', $stabileIds) ->whereIn('stato', ['Aperto', 'Preso in Carico', 'In Lavorazione']); $urgenzeCount = (clone $baseQuery)->where('priorita', 'Urgente')->count(); diff --git a/resources/views/filament/components/topbar-icons.blade.php b/resources/views/filament/components/topbar-icons.blade.php index 6ef5dbf..e7f8b42 100644 --- a/resources/views/filament/components/topbar-icons.blade.php +++ b/resources/views/filament/components/topbar-icons.blade.php @@ -5,11 +5,17 @@ $avvisi = 0; if ($user instanceof \App\Models\User) { - $stabileId = \App\Support\StabileContext::resolveActiveStabileId($user); - if ($stabileId) { + $stabileIds = \App\Support\StabileContext::accessibleStabili($user) + ->pluck('id') + ->map(fn($value) => (int) $value) + ->filter(fn(int $value) => $value > 0) + ->values() + ->all(); + + if ($stabileIds !== []) { $base = \App\Models\Ticket::query() - ->where('stabile_id', $stabileId) - ->where('stato', 'Aperto'); + ->whereIn('stabile_id', $stabileIds) + ->whereIn('stato', ['Aperto', 'Preso in Carico', 'In Lavorazione']); $urgenti = (clone $base)->where('priorita', 'Urgente')->count(); $avvisi = (clone $base)->where('priorita', '!=', 'Urgente')->count(); diff --git a/resources/views/filament/pages/supporto/dashboard.blade.php b/resources/views/filament/pages/supporto/dashboard.blade.php index 45be049..265d569 100644 --- a/resources/views/filament/pages/supporto/dashboard.blade.php +++ b/resources/views/filament/pages/supporto/dashboard.blade.php @@ -16,7 +16,5 @@ - - @livewire(\App\Filament\Widgets\GoogleScadenziarioOverview::class) diff --git a/resources/views/filament/pages/supporto/ticket-gestione.blade.php b/resources/views/filament/pages/supporto/ticket-gestione.blade.php index e89a84d..f5753f2 100644 --- a/resources/views/filament/pages/supporto/ticket-gestione.blade.php +++ b/resources/views/filament/pages/supporto/ticket-gestione.blade.php @@ -332,6 +332,7 @@
@forelse($ticket->attachments as $a) + @php($details = $this->getAttachmentDetails($a))
@if($a->isImage()) @@ -347,10 +348,48 @@
{{ $a->original_file_name }}
-
{{ $a->description ?: 'Nessuna descrizione' }}
+
{{ $details['primary_description'] ?: 'Nessuna descrizione' }}
+
+ @if(!empty($details['gps'])) + GPS + @endif + @if(!empty($details['exif_datetime'])) + EXIF + @endif + @if(!empty($details['device'])) + {{ $details['device'] }} + @endif +
+ @if(!empty($details['gps']) || !empty($details['exif_datetime']) || !empty($details['file_label']) || !empty($details['original_label']) || !empty($details['other'])) +
+ @if(!empty($details['gps'])) +
Coordinate: {{ $details['gps']['label'] }}
+ @endif + @if(!empty($details['exif_datetime'])) +
Data EXIF: {{ $details['exif_datetime'] }}
+ @endif + @if(!empty($details['device'])) +
Dispositivo: {{ $details['device'] }}
+ @endif + @if(!empty($details['file_label'])) +
Nome file: {{ $details['file_label'] }}
+ @endif + @if(!empty($details['original_label'])) +
Origine: {{ $details['original_label'] }}
+ @endif + @foreach($details['other'] as $otherPart) +
{{ $otherPart }}
+ @endforeach +
+ @endif
{{ optional($a->created_at)->format('d/m/Y H:i') }}
- +
+ + @if(!empty($details['gps'])) + + @endif +
@empty
Nessun allegato.
@@ -545,8 +584,21 @@
{{ $attachmentPreview['name'] ?? 'Allegato' }}
- @if(!empty($attachmentPreview['description'])) -
{{ $attachmentPreview['description'] }}
+ @if(!empty($attachmentPreview['details']['primary_description'])) +
{{ $attachmentPreview['details']['primary_description'] }}
+ @endif + @if(!empty($attachmentPreview['details']['gps']) || !empty($attachmentPreview['details']['exif_datetime']) || !empty($attachmentPreview['details']['device'])) +
+ @if(!empty($attachmentPreview['details']['gps'])) + GPS {{ $attachmentPreview['details']['gps']['label'] }} + @endif + @if(!empty($attachmentPreview['details']['exif_datetime'])) + EXIF {{ $attachmentPreview['details']['exif_datetime'] }} + @endif + @if(!empty($attachmentPreview['details']['device'])) + {{ $attachmentPreview['details']['device'] }} + @endif +
@endif
@@ -560,12 +612,21 @@ @endif + @if(!empty($attachmentPreview['details']['gps'])) + + @endif Apri in nuova scheda
+ @if(!empty($attachmentPreview['details']['full_description'])) +
+
Dettagli allegato
+
{{ $attachmentPreview['details']['full_description'] }}
+
+ @endif @if($attachmentPreview['is_image'] ?? false)
@@ -584,4 +645,24 @@
@endif + + @if($attachmentMapPreview) +
+
+
+
+
Mappa allegato
+
{{ $attachmentMapPreview['name'] ?? 'Allegato' }} ยท {{ $attachmentMapPreview['label'] ?? '' }}
+
+
+ Apri Google Maps + +
+
+
+ +
+
+
+ @endif diff --git a/resources/views/filament/pages/supporto/ticket-mobile.blade.php b/resources/views/filament/pages/supporto/ticket-mobile.blade.php index a6ea3cb..f70d922 100644 --- a/resources/views/filament/pages/supporto/ticket-mobile.blade.php +++ b/resources/views/filament/pages/supporto/ticket-mobile.blade.php @@ -364,7 +364,7 @@
Anteprima immediata sul telefono
-
Le foto e gli allegati selezionati si vedono subito qui, prima dell'accodamento definitivo nella bozza ticket.
+
Le foto e gli allegati selezionati si vedono subito qui mentre entrano in coda. Nella griglia sotto ogni miniatura resta nello stesso box della sua descrizione.