gps_system/resources/views/livewire/events-and-alerts.blade.php
sackey 6b878bb0a0
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
Initial commit
2025-09-12 16:19:56 +00:00

390 lines
22 KiB
PHP

<div class="space-y-6" wire:key="events-alerts-component">
{{-- Page Header --}}
<div class="flex items-center justify-between">
<div>
<flux:heading size="lg">Events & Alerts</flux:heading>
<flux:subheading>Monitor and manage GPS tracking events</flux:subheading>
</div>
<div class="flex space-x-3">
<flux:button wire:click="refreshEvents" variant="ghost" size="sm" icon="arrow-path">
Refresh
</flux:button>
<flux:button wire:click="markAllAsRead" variant="outline" size="sm" icon="check-circle">
Mark All Read
</flux:button>
</div>
</div>
{{-- Stats Cards --}}
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
<div class="bg-red-50 border border-red-200 rounded-lg p-4">
<div class="flex items-center">
<div class="flex-shrink-0">
<div class="w-8 h-8 bg-red-500 rounded-full flex items-center justify-center">
<flux:icon name="exclamation-triangle" class="w-4 h-4 text-white" />
</div>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-red-600">Critical Alerts</p>
<p class="text-lg font-semibold text-red-900">{{ $this->criticalCount }}</p>
</div>
</div>
</div>
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
<div class="flex items-center">
<div class="flex-shrink-0">
<div class="w-8 h-8 bg-yellow-500 rounded-full flex items-center justify-center">
<flux:icon name="exclamation-circle" class="w-4 h-4 text-white" />
</div>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-yellow-600">Warnings</p>
<p class="text-lg font-semibold text-yellow-900">{{ $this->warningCount }}</p>
</div>
</div>
</div>
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4">
<div class="flex items-center">
<div class="flex-shrink-0">
<div class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center">
<flux:icon name="information-circle" class="w-4 h-4 text-white" />
</div>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-blue-600">Info Events</p>
<p class="text-lg font-semibold text-blue-900">{{ $this->infoCount }}</p>
</div>
</div>
</div>
<div class="bg-gray-50 border border-gray-200 rounded-lg p-4">
<div class="flex items-center">
<div class="flex-shrink-0">
<div class="w-8 h-8 bg-gray-500 rounded-full flex items-center justify-center">
<flux:icon name="bell" class="w-4 h-4 text-white" />
</div>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-gray-600">Total Events</p>
<p class="text-lg font-semibold text-gray-900">{{ $this->totalCount }}</p>
</div>
</div>
</div>
</div>
{{-- Filters --}}
<div class="bg-white shadow rounded-lg border border-zinc-200">
<div class="px-4 py-5 sm:p-6">
<div class="grid grid-cols-1 md:grid-cols-6 gap-4">
<div>
<flux:field>
<flux:label>Event Type</flux:label>
<flux:select wire:model.live="filterType" size="sm">
<option value="">All Types</option>
<option value="geofenceEnter">Geofence Enter</option>
<option value="geofenceExit">Geofence Exit</option>
<option value="alarm">Alarm</option>
<option value="ignitionOn">Ignition On</option>
<option value="ignitionOff">Ignition Off</option>
<option value="maintenance">Maintenance</option>
<option value="textMessage">Text Message</option>
<option value="driverChanged">Driver Changed</option>
</flux:select>
</flux:field>
</div>
<div>
<flux:field>
<flux:label>Status</flux:label>
<flux:select wire:model.live="filterStatus" size="sm">
<option value="">All Status</option>
<option value="unread">Unread</option>
<option value="read">Acknowledged</option>
</flux:select>
</flux:field>
</div>
<div>
<flux:field>
<flux:label>Device</flux:label>
<flux:select wire:model.live="filterDevice" size="sm">
<option value="">All Devices</option>
@foreach($devices as $device)
<option value="{{ $device->id }}">{{ $device->name }}</option>
@endforeach
</flux:select>
</flux:field>
</div>
<div>
<flux:field>
<flux:label>Date From</flux:label>
<flux:input wire:model.live="filterDateFrom" type="date" size="sm" />
</flux:field>
</div>
<div>
<flux:field>
<flux:label>Date To</flux:label>
<flux:input wire:model.live="filterDateTo" type="date" size="sm" />
</flux:field>
</div>
<div>
<flux:field>
<flux:label>Status</flux:label>
<flux:select wire:model.live="filterStatus" size="sm">
<option value="">All Status</option>
<option value="unread">Unread</option>
<option value="read">Read</option>
<option value="acknowledged">Acknowledged</option>
</flux:select>
</flux:field>
</div>
</div>
<div class="mt-4 flex justify-between">
<flux:button wire:click="clearFilters" variant="ghost" size="sm">
Clear Filters
</flux:button>
<div class="text-sm text-gray-500">
Showing {{ $events->count() }} of {{ $events->total() }} events
</div>
</div>
</div>
</div>
{{-- Events List --}}
<div class="bg-white shadow rounded-lg border border-zinc-200">
<div class="px-4 py-5 sm:p-6">
<flux:heading size="base" class="mb-4">Recent Events</flux:heading>
@if($events->count() > 0)
<div class="space-y-4">
@foreach($events as $event)
<div class="border rounded-lg p-4 {{ $event->acknowledged ? 'bg-gray-50' : 'bg-white' }} hover:shadow-md transition-shadow">
<div class="flex items-start justify-between">
<div class="flex-1">
<div class="flex items-center space-x-3 mb-2">
{{-- Event Icon --}}
<div class="flex-shrink-0">
@php
$eventSeverity = $this->getEventSeverity($event->type);
$iconClass = match($eventSeverity) {
'critical' => 'bg-red-500 text-white',
'warning' => 'bg-yellow-500 text-white',
'info' => 'bg-blue-500 text-white',
default => 'bg-gray-500 text-white'
};
$icon = match($event->type) {
'geofenceEnter', 'geofenceExit' => 'map-pin',
'alarm' => 'exclamation-triangle',
'ignitionOn', 'ignitionOff' => 'key',
'maintenance' => 'wrench-screwdriver',
'textMessage' => 'chat-bubble-left',
'driverChanged' => 'user',
default => 'bell'
};
@endphp
<div class="w-8 h-8 rounded-full flex items-center justify-center {{ $iconClass }}">
<flux:icon name="{{ $icon }}" class="w-4 h-4" />
</div>
</div>
{{-- Event Title --}}
<div class="flex-1">
<h4 class="font-medium text-gray-900">
{{ ucfirst(str_replace(['geofence', 'ignition'], ['Geofence ', 'Ignition '], $event->type)) }}
@if(!$event->acknowledged)
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800 ml-2">
New
</span>
@endif
</h4>
<p class="text-sm text-gray-600 mt-1">{{ $event->device->name }} - {{ $event->event_time->format('M j, Y H:i') }}</p>
</div>
{{-- Device & Time --}}
<div class="text-right">
<p class="text-sm font-medium text-gray-900">{{ $event->device->name ?? 'Unknown Device' }}</p>
<p class="text-xs text-gray-500">{{ $event->event_time->diffForHumans() }}</p>
<p class="text-xs text-gray-400">{{ $event->event_time->format('M j, Y H:i') }}</p>
</div>
</div>
{{-- Additional Event Details --}}
@if($event->geofence || $event->position)
<div class="ml-11 mt-2 text-sm text-gray-600">
@if($event->geofence)
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs bg-gray-100 text-gray-800 mr-2">
<flux:icon name="map-pin" class="w-3 h-3 mr-1" />
{{ $event->geofence->name }}
</span>
@endif
@if($event->position)
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs bg-gray-100 text-gray-800">
<flux:icon name="globe-alt" class="w-3 h-3 mr-1" />
{{ number_format($event->position->latitude, 6) }}, {{ number_format($event->position->longitude, 6) }}
</span>
@endif
</div>
@endif
</div>
{{-- Actions --}}
<div class="flex space-x-2 ml-4">
@if(!$event->acknowledged)
<flux:button wire:click="acknowledgeEvent({{ $event->id }})" variant="ghost" size="xs" icon="check">
</flux:button>
@endif
<flux:button wire:click="showEventDetails({{ $event->id }})" variant="ghost" size="xs" icon="information-circle">
</flux:button>
@if($event->position)
<flux:button wire:click="showOnMap({{ $event->position->latitude }}, {{ $event->position->longitude }})" variant="ghost" size="xs" icon="map">
</flux:button>
@endif
</div>
</div>
</div>
@endforeach
</div>
{{-- Pagination --}}
<div class="mt-6">
{{ $events->links() }}
</div>
@else
<div class="text-center py-12">
<flux:icon name="bell-slash" class="w-12 h-12 text-gray-400 mx-auto mb-4" />
<h3 class="text-lg font-medium text-gray-900 mb-2">No events found</h3>
<p class="text-gray-500">
@if($this->hasActiveFilters())
Try adjusting your filters to see more events.
@else
Events will appear here when devices generate alerts or notifications.
@endif
</p>
</div>
@endif
</div>
</div>
{{-- Event Details Modal --}}
@if($selectedEvent)
<div class="fixed inset-0 z-50 overflow-y-auto" wire:key="event-modal-{{ $selectedEvent->id }}">
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 opacity-75" wire:click="closeEventDetails"></div>
</div>
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="flex items-start">
<div class="flex-shrink-0">
@php
$eventSeverity = $this->getEventSeverity($selectedEvent->type);
$iconClass = match($eventSeverity) {
'critical' => 'bg-red-500 text-white',
'warning' => 'bg-yellow-500 text-white',
'info' => 'bg-blue-500 text-white',
default => 'bg-gray-500 text-white'
};
@endphp
<div class="w-10 h-10 rounded-full flex items-center justify-center {{ $iconClass }}">
<flux:icon name="bell" class="w-5 h-5" />
</div>
</div>
<div class="ml-4 flex-1">
<flux:heading size="lg" class="mb-2">Event Details</flux:heading>
<div class="space-y-3">
<div>
<span class="text-sm font-medium text-gray-600">Type:</span>
<span class="ml-2 text-sm text-gray-900">{{ ucfirst($selectedEvent->type) }}</span>
</div>
<div>
<span class="text-sm font-medium text-gray-600">Severity:</span>
<span class="ml-2 inline-flex items-center px-2 py-1 rounded-full text-xs font-medium
{{ $eventSeverity === 'critical' ? 'bg-red-100 text-red-800' :
($eventSeverity === 'warning' ? 'bg-yellow-100 text-yellow-800' : 'bg-blue-100 text-blue-800') }}">
{{ ucfirst($eventSeverity) }}
</span>
</div>
<div>
<span class="text-sm font-medium text-gray-600">Device:</span>
<span class="ml-2 text-sm text-gray-900">{{ $selectedEvent->device->name ?? 'Unknown' }}</span>
</div>
<div>
<span class="text-sm font-medium text-gray-600">Time:</span>
<span class="ml-2 text-sm text-gray-900">{{ $selectedEvent->event_time->format('M j, Y H:i:s') }}</span>
</div>
@if($selectedEvent->geofence)
<div>
<span class="text-sm font-medium text-gray-600">Geofence:</span>
<span class="ml-2 text-sm text-gray-900">{{ $selectedEvent->geofence->name }}</span>
</div>
@endif
@if($selectedEvent->position)
<div>
<span class="text-sm font-medium text-gray-600">Location:</span>
<span class="ml-2 text-sm text-gray-900">
{{ number_format($selectedEvent->position->latitude, 6) }},
{{ number_format($selectedEvent->position->longitude, 6) }}
</span>
</div>
@endif
<div>
<span class="text-sm font-medium text-gray-600">Message:</span>
<p class="mt-1 text-sm text-gray-900">{{ $selectedEvent->message }}</p>
</div>
@if($selectedEvent->attributes && count($selectedEvent->attributes) > 0)
<div>
<span class="text-sm font-medium text-gray-600">Additional Data:</span>
<div class="mt-1 text-xs text-gray-700 bg-gray-50 rounded p-2">
<pre>{{ json_encode($selectedEvent->attributes, JSON_PRETTY_PRINT) }}</pre>
</div>
</div>
@endif
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
@if(!$selectedEvent->acknowledged)
<flux:button wire:click="acknowledgeEvent({{ $selectedEvent->id }})" variant="primary" class="sm:ml-3">
Acknowledge
</flux:button>
@endif
@if($selectedEvent->position)
<flux:button wire:click="showOnMap({{ $selectedEvent->position->latitude }}, {{ $selectedEvent->position->longitude }})" variant="outline" class="sm:ml-3">
Show on Map
</flux:button>
@endif
<flux:button wire:click="closeEventDetails" variant="ghost">
Close
</flux:button>
</div>
</div>
</div>
</div>
@endif
<style>
.transition-shadow {
transition: box-shadow 0.15s ease-in-out;
}
</style>
</div>