traccarService = app(TraccarService::class); } public function boot(TraccarService $traccarService) { $this->traccarService = $traccarService; } protected function getTraccarService() { if (!$this->traccarService) { $this->traccarService = app(TraccarService::class); } return $this->traccarService; } protected $rules = [ 'name' => 'required|string|max:255', 'unique_id' => 'required|string|max:255|unique:devices,unique_id', 'imei' => 'nullable|string|max:255', 'phone' => 'nullable|string|max:255', 'model' => 'nullable|string|max:255', 'contact' => 'nullable|string|max:255', 'category' => 'required|string|max:255', 'protocol' => 'nullable|string|max:255', 'group_id' => 'nullable|exists:device_groups,id', 'driver_id' => 'nullable|exists:drivers,id', 'is_active' => 'boolean', 'deviceAttributes' => 'nullable|json', ]; public function updatingSearch() { $this->resetPage(); } public function updatingStatusFilter() { $this->resetPage(); } public function updatingGroupFilter() { $this->resetPage(); } public function createDevice() { $this->openCreateModal(); } public function openCreateModal() { $this->reset(['name', 'unique_id', 'imei', 'phone', 'model', 'contact', 'category', 'group_id', 'driver_id', 'is_active', 'deviceAttributes']); $this->editingDevice = false; $this->showForm = true; } public function editDevice($deviceId) { $device = Device::findOrFail($deviceId); if ($device->user_id !== Auth::id()) { abort(403); } $this->editingDevice = $device; $this->name = $device->name; $this->unique_id = $device->unique_id; $this->imei = $device->imei; $this->phone = $device->phone; $this->model = $device->model; $this->contact = $device->contact; $this->category = $device->category; $this->protocol = $device->protocol; $this->group_id = $device->group_id; $this->driver_id = $device->driver_id; $this->is_active = $device->is_active; $this->deviceAttributes = $device->attributes ? json_encode($device->attributes) : ''; $this->showForm = true; } public function saveDevice() { if ($this->editingDevice) { $this->rules['unique_id'] = 'required|string|max:255|unique:devices,unique_id,' . $this->editingDevice->id; } $this->validate(); try { // Prepare attributes for Traccar (must be an object, not null) $attributes = []; if ($this->deviceAttributes) { $decoded = json_decode($this->deviceAttributes, true); if (is_array($decoded)) { $attributes = $decoded; } } $deviceData = [ 'name' => $this->name, 'uniqueId' => $this->unique_id, 'phone' => $this->phone ?: null, 'model' => $this->model ?: null, 'contact' => $this->contact ?: null, 'category' => $this->category ?: 'default', 'attributes' => (object)$attributes, // Always send as object for Traccar ]; if ($this->editingDevice) { // Update existing device try { if ($this->editingDevice->traccar_device_id) { $this->getTraccarService()->updateDevice($this->editingDevice->traccar_device_id, $deviceData); } } catch (\Exception $e) { // Log Traccar error but continue with local update \Log::warning('Failed to update device in Traccar: ' . $e->getMessage()); } $this->editingDevice->update([ 'name' => $this->name, 'unique_id' => $this->unique_id, 'imei' => $this->imei, 'phone' => $this->phone, 'model' => $this->model, 'contact' => $this->contact, 'category' => $this->category, 'protocol' => $this->protocol, 'group_id' => $this->group_id ?: null, 'driver_id' => $this->driver_id ?: null, 'is_active' => $this->is_active, 'attributes' => $this->deviceAttributes ? json_decode($this->deviceAttributes, true) : null, ]); session()->flash('message', 'Device updated successfully' . ($this->editingDevice->traccar_device_id ? ' and synced with Traccar' : '')); $this->dispatch('device-updated'); } else { // Create new device - try Traccar first, then local $traccarDeviceId = null; $traccarSyncMessage = ''; try { $traccarDevice = $this->getTraccarService()->createDevice($deviceData); if (isset($traccarDevice['id']) && !empty($traccarDevice['id'])) { $traccarDeviceId = $traccarDevice['id']; $traccarSyncMessage = ' and automatically synced with Traccar'; } else { \Log::info('Traccar device creation returned empty response', ['response' => $traccarDevice]); $traccarSyncMessage = '. Traccar sync is available but returned empty response'; } } catch (\Exception $e) { // Log Traccar error but continue with local creation \Log::warning('Failed to create device in Traccar: ' . $e->getMessage()); $traccarSyncMessage = '. Traccar sync failed - device can be synced manually later'; } // Create device in local database $device = Device::create([ 'user_id' => Auth::id(), 'traccar_device_id' => $traccarDeviceId, 'name' => $this->name, 'unique_id' => $this->unique_id, 'imei' => $this->imei, 'phone' => $this->phone, 'model' => $this->model, 'contact' => $this->contact, 'category' => $this->category, 'protocol' => $this->protocol, 'status' => 'unknown', 'group_id' => $this->group_id ?: null, 'driver_id' => $this->driver_id ?: null, 'is_active' => $this->is_active, 'attributes' => $this->deviceAttributes ? json_decode($this->deviceAttributes, true) : null, ]); $message = 'Device "' . $this->name . '" created successfully' . $traccarSyncMessage; session()->flash('message', $message); $this->dispatch('device-created'); } $this->resetForm(); $this->showForm = false; } catch (\Exception $e) { $this->dispatch('device-error', ['message' => 'Failed to save device: ' . $e->getMessage()]); } } public function deleteDevice($deviceId) { $device = Device::findOrFail($deviceId); if ($device->user_id !== Auth::id()) { abort(403); } try { // Delete from Traccar if it exists if ($device->traccar_device_id) { try { $this->getTraccarService()->deleteDevice($device->traccar_device_id); } catch (\Exception $e) { // Log Traccar error but continue with local deletion \Log::warning('Failed to delete device from Traccar: ' . $e->getMessage()); } } $device->delete(); session()->flash('message', 'Device deleted successfully' . ($device->traccar_device_id ? ' and removed from Traccar' : '')); $this->dispatch('device-deleted'); } catch (\Exception $e) { session()->flash('error', 'Failed to delete device: ' . $e->getMessage()); } } public function syncWithTraccar() { try { $traccarDevices = $this->getTraccarService()->getDevices(); $user = Auth::user(); $syncedCount = 0; foreach ($traccarDevices as $traccarDevice) { Device::updateOrCreate( ['traccar_device_id' => $traccarDevice['id']], [ 'user_id' => $user->id, 'name' => $traccarDevice['name'], 'unique_id' => $traccarDevice['uniqueId'], 'phone' => $traccarDevice['phone'] ?? null, 'model' => $traccarDevice['model'] ?? null, 'contact' => $traccarDevice['contact'] ?? null, 'category' => $traccarDevice['category'] ?? 'default', 'attributes' => $traccarDevice['attributes'] ?? null, ] ); $syncedCount++; } session()->flash('message', "Successfully synced {$syncedCount} devices from Traccar"); $this->dispatch('devices-synced'); } catch (\Exception $e) { session()->flash('error', 'Failed to sync with Traccar: ' . $e->getMessage()); } } public function syncUnsyncedDevicesToTraccar() { try { $unsyncedDevices = Device::whereNull('traccar_device_id')->get(); $syncedCount = 0; $errorCount = 0; foreach ($unsyncedDevices as $device) { try { // Prepare attributes for Traccar $attributes = []; if ($device->attributes) { if (is_string($device->attributes)) { $decoded = json_decode($device->attributes, true); if (is_array($decoded)) { $attributes = $decoded; } } elseif (is_array($device->attributes)) { $attributes = $device->attributes; } } $deviceData = [ 'name' => $device->name, 'uniqueId' => $device->unique_id, 'phone' => $device->phone ?: null, 'model' => $device->model ?: null, 'contact' => $device->contact ?: null, 'category' => $device->category ?: 'default', 'attributes' => (object)$attributes, ]; $traccarDevice = $this->getTraccarService()->createDevice($deviceData); if (isset($traccarDevice['id']) && !empty($traccarDevice['id'])) { $device->update(['traccar_device_id' => $traccarDevice['id']]); $syncedCount++; } else { $errorCount++; } } catch (\Exception $e) { \Log::warning("Failed to sync device {$device->name} to Traccar: " . $e->getMessage()); $errorCount++; } } if ($syncedCount > 0) { session()->flash('message', "Successfully synced {$syncedCount} unsynced devices to Traccar" . ($errorCount > 0 ? ". {$errorCount} devices failed to sync." : "")); } else { session()->flash('error', "No devices were synced. " . ($errorCount > 0 ? "{$errorCount} devices failed." : "All devices are already synced.")); } $this->dispatch('devices-synced'); } catch (\Exception $e) { session()->flash('error', 'Failed to sync unsynced devices: ' . $e->getMessage()); } } public function cancelForm() { $this->resetForm(); $this->showForm = false; } private function resetForm() { $this->editingDevice = null; $this->name = ''; $this->unique_id = ''; $this->imei = ''; $this->phone = ''; $this->model = ''; $this->contact = ''; $this->category = 'default'; $this->protocol = ''; $this->group_id = ''; $this->driver_id = ''; $this->is_active = true; $this->deviceAttributes = ''; $this->resetValidation(); } public function render() { $query = Device::where('user_id', Auth::id()) ->with(['group', 'driver', 'currentPosition']); // Apply filters if ($this->search) { $query->where(function ($q) { $q->where('name', 'like', '%' . $this->search . '%') ->orWhere('unique_id', 'like', '%' . $this->search . '%') ->orWhere('imei', 'like', '%' . $this->search . '%'); }); } if ($this->statusFilter !== 'all') { $query->where('status', $this->statusFilter); } if ($this->groupFilter !== 'all') { $query->where('group_id', $this->groupFilter); } $devices = $query->orderBy('name')->paginate(10); $deviceGroups = DeviceGroup::active()->get(); $drivers = Driver::active()->get(); return view('livewire.device-management', [ 'devices' => $devices, 'deviceGroups' => $deviceGroups, 'drivers' => $drivers, ]); } public function getStatsProperty() { $userId = Auth::id(); return [ 'total_devices' => Device::where('user_id', $userId)->count(), 'active_devices' => Device::where('user_id', $userId)->where('is_active', true)->count(), 'online_devices' => Device::where('user_id', $userId)->where('status', 'online')->count(), 'with_traccar' => Device::where('user_id', $userId)->whereNotNull('traccar_device_id')->count(), ]; } }