traccarService = $traccarService; } public function mount() { $this->loadNotificationSettings(); } public function loadNotificationSettings() { $user = Auth::user(); $preferences = NotificationPreference::where('user_id', $user->id)->first(); if ($preferences) { $this->emailNotifications = $preferences->email_enabled; $this->pushNotifications = $preferences->push_enabled; $this->smsNotifications = $preferences->sms_enabled; $this->notificationTypes = $preferences->event_types ?? []; } } public function saveNotificationSettings() { try { $user = Auth::user(); NotificationPreference::updateOrCreate( ['user_id' => $user->id], [ 'email_enabled' => $this->emailNotifications, 'push_enabled' => $this->pushNotifications, 'sms_enabled' => $this->smsNotifications, 'event_types' => $this->notificationTypes, ] ); $this->showNotificationSettings = false; session()->flash('success', 'Notification settings saved successfully!'); } catch (\Exception $e) { session()->flash('error', 'Failed to save notification settings: ' . $e->getMessage()); } } public function viewEventDetails($eventId) { $this->selectedEvent = Event::with(['device', 'geofence'])->findOrFail($eventId); } public function closeEventDetails() { $this->selectedEvent = null; } public function markAsRead($eventId) { try { $event = Event::findOrFail($eventId); $event->update(['acknowledged' => true, 'acknowledged_at' => now()]); session()->flash('success', 'Event marked as read'); } catch (\Exception $e) { session()->flash('error', 'Failed to mark event as read: ' . $e->getMessage()); } } public function syncEvents() { try { // Get events from Traccar $traccarEvents = $this->traccarService->getEvents(); if ($traccarEvents) { foreach ($traccarEvents as $traccarEvent) { $device = Device::where('traccar_id', $traccarEvent['deviceId'])->first(); $geofence = null; if (isset($traccarEvent['geofenceId'])) { $geofence = Geofence::where('traccar_id', $traccarEvent['geofenceId'])->first(); } if ($device) { Event::updateOrCreate( ['traccar_id' => $traccarEvent['id']], [ 'device_id' => $device->id, 'geofence_id' => $geofence?->id, 'event_type' => $traccarEvent['type'], 'event_time' => Carbon::parse($traccarEvent['eventTime']), 'position_id' => null, // Would need to match position 'attributes' => $traccarEvent['attributes'] ?? null, 'acknowledged' => false, ] ); } } } session()->flash('success', 'Events synchronized successfully!'); } catch (\Exception $e) { session()->flash('error', 'Failed to sync events: ' . $e->getMessage()); } } public function getEventIcon($eventType) { return match ($eventType) { 'deviceOnline' => 'signal', 'deviceOffline' => 'signal-slash', 'deviceOverspeed' => 'exclamation-triangle', 'deviceFuelDrop' => 'fire', 'geofenceEnter' => 'map-pin', 'geofenceExit' => 'map-pin', 'alarm' => 'bell', 'ignitionOn' => 'key', 'ignitionOff' => 'key', 'maintenance' => 'wrench', 'textMessage' => 'chat-bubble-left', 'driverChanged' => 'user', default => 'information-circle' }; } public function getEventColor($eventType) { return match ($eventType) { 'deviceOnline' => 'text-green-600 dark:text-green-400', 'deviceOffline' => 'text-red-600 dark:text-red-400', 'deviceOverspeed' => 'text-orange-600 dark:text-orange-400', 'deviceFuelDrop' => 'text-red-600 dark:text-red-400', 'geofenceEnter' => 'text-blue-600 dark:text-blue-400', 'geofenceExit' => 'text-purple-600 dark:text-purple-400', 'alarm' => 'text-red-600 dark:text-red-400', 'ignitionOn' => 'text-green-600 dark:text-green-400', 'ignitionOff' => 'text-gray-600 dark:text-gray-400', 'maintenance' => 'text-yellow-600 dark:text-yellow-400', 'textMessage' => 'text-blue-600 dark:text-blue-400', 'driverChanged' => 'text-indigo-600 dark:text-indigo-400', default => 'text-gray-600 dark:text-gray-400' }; } public function refreshEvents() { $this->resetPage(); session()->flash('success', 'Events refreshed!'); } public function markAllAsRead() { try { Event::whereHas('device', function($q) { $q->where('user_id', Auth::id()); }) ->where('acknowledged', false) ->update(['acknowledged' => true, 'acknowledged_at' => now()]); session()->flash('success', 'All events marked as read'); } catch (\Exception $e) { session()->flash('error', 'Failed to mark all events as read: ' . $e->getMessage()); } } public function acknowledgeEvent($eventId) { try { $event = Event::findOrFail($eventId); $event->update([ 'acknowledged' => true, 'acknowledged_at' => now(), 'acknowledged_by' => Auth::id() ]); session()->flash('success', 'Event acknowledged'); } catch (\Exception $e) { session()->flash('error', 'Failed to acknowledge event: ' . $e->getMessage()); } } public function showEventDetails($eventId) { $this->selectedEvent = Event::with(['device', 'geofence', 'position'])->findOrFail($eventId); } public function showOnMap($latitude, $longitude) { // Emit event to show location on map $this->dispatch('showLocationOnMap', ['lat' => $latitude, 'lng' => $longitude]); session()->flash('info', "Location: {$latitude}, {$longitude}"); } public function clearFilters() { $this->filterType = ''; $this->filterDevice = ''; $this->filterDateFrom = ''; $this->filterDateTo = ''; $this->filterStatus = ''; $this->filterAcknowledged = ''; $this->resetPage(); } public function hasActiveFilters() { return !empty($this->filterType) || !empty($this->filterDevice) || !empty($this->filterDateFrom) || !empty($this->filterDateTo) || !empty($this->filterStatus) || !empty($this->filterAcknowledged); } // Computed properties for stats public function getCriticalCountProperty() { $criticalTypes = ['alarm', 'deviceOffline', 'deviceOverspeed', 'panic', 'accident', 'deviceFuelDrop']; return Event::whereHas('device', function($q) { $q->where('user_id', Auth::id()); }) ->whereIn('type', $criticalTypes) ->where('event_time', '>=', now()->subDays(30)) ->count(); } public function getWarningCountProperty() { $warningTypes = ['maintenance', 'batteryLow', 'deviceMoving', 'deviceStopped', 'geofenceExit', 'ignitionOff']; return Event::whereHas('device', function($q) { $q->where('user_id', Auth::id()); }) ->whereIn('type', $warningTypes) ->where('event_time', '>=', now()->subDays(30)) ->count(); } public function getInfoCountProperty() { $infoTypes = ['deviceOnline', 'geofenceEnter', 'ignitionOn', 'driverChanged', 'textMessage']; return Event::whereHas('device', function($q) { $q->where('user_id', Auth::id()); }) ->whereIn('type', $infoTypes) ->where('event_time', '>=', now()->subDays(30)) ->count(); } public function getTotalCountProperty() { return Event::whereHas('device', function($q) { $q->where('user_id', Auth::id()); }) ->where('event_time', '>=', now()->subDays(30)) ->count(); } public function getEventSeverity($eventType) { $criticalTypes = ['alarm', 'deviceOffline', 'deviceOverspeed', 'panic', 'accident', 'deviceFuelDrop']; $warningTypes = ['maintenance', 'batteryLow', 'deviceMoving', 'deviceStopped', 'geofenceExit', 'ignitionOff']; if (in_array($eventType, $criticalTypes)) { return 'critical'; } elseif (in_array($eventType, $warningTypes)) { return 'warning'; } else { return 'info'; } } public function render() { // Build query with filters $query = Event::with(['device', 'geofence', 'position']) ->whereHas('device', function($q) { $q->where('user_id', Auth::id()); }); // Apply new filters if ($this->filterType) { $query->where('type', $this->filterType); } if ($this->filterDevice) { $query->where('device_id', $this->filterDevice); } if ($this->filterDateFrom) { $query->where('event_time', '>=', $this->filterDateFrom); } if ($this->filterDateTo) { $query->where('event_time', '<=', $this->filterDateTo); } if ($this->filterStatus) { if ($this->filterStatus === 'unread') { $query->where('acknowledged', false); } elseif ($this->filterStatus === 'read') { $query->where('acknowledged', true); } } if ($this->filterAcknowledged) { if ($this->filterAcknowledged === 'yes') { $query->where('acknowledged', true); } elseif ($this->filterAcknowledged === 'no') { $query->where('acknowledged', false); } } // Apply legacy search filter if ($this->search) { $query->where(function($q) { $q->whereHas('device', function($deviceQuery) { $deviceQuery->where('name', 'like', '%' . $this->search . '%'); })->orWhere('type', 'like', '%' . $this->search . '%'); }); } // Apply legacy filters for backward compatibility if ($this->deviceFilter !== 'all') { $query->where('device_id', $this->deviceFilter); } if ($this->eventTypeFilter !== 'all') { $query->where('type', $this->eventTypeFilter); } if ($this->dateRange !== 'all') { $query->where('event_time', '>=', now()->subDays((int)$this->dateRange)); } $events = $query->orderBy('event_time', 'desc')->paginate(20); $deviceQuery = Device::where('user_id', Auth::id()); // Check if device groups have users relationship (safely handle missing relationship) if (method_exists(\App\Models\DeviceGroup::class, 'users')) { $deviceQuery->orWhereHas('group.users', function($subQuery) { $subQuery->where('user_id', Auth::id()); }); } $devices = $deviceQuery->get(); $eventTypes = Event::whereHas('device', function($q) { $q->where('user_id', Auth::id()); }) ->distinct() ->pluck('type') ->sort(); $unreadCount = Event::whereHas('device', function($q) { $q->where('user_id', Auth::id()); }) ->where('acknowledged', false) ->count(); return view('livewire.events-and-alerts', [ 'events' => $events, 'devices' => $devices, 'eventTypes' => $eventTypes, 'unreadCount' => $unreadCount ]); } }