gps_system/resources/views/livewire/device-management.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

367 lines
19 KiB
PHP

<div class="space-y-6" wire:key="device-management">
{{-- Header --}}
<div class="flex items-center justify-between">
<div>
<flux:heading size="lg">Device Management</flux:heading>
<flux:subheading>Manage your GPS tracking devices and Traccar integration</flux:subheading>
</div>
<div class="flex space-x-3">
<flux:button wire:click="syncWithTraccar" variant="outline" size="sm" icon="arrow-path">
Sync with Traccar
</flux:button>
<flux:button wire:click="syncUnsyncedDevicesToTraccar" variant="outline" size="sm" icon="cloud-arrow-up">
Sync Unsynced to Traccar
</flux:button>
<flux:button wire:click="createDevice" variant="primary" size="sm" icon="plus">
Add Device
</flux:button>
</div>
</div>
{{-- Flash Messages --}}
@if (session()->has('message'))
<div class="bg-green-100 dark:bg-green-900 border border-green-400 dark:border-green-600 text-green-700 dark:text-green-200 px-4 py-3 rounded relative" role="alert">
<span class="block sm:inline">{{ session('message') }}</span>
</div>
@endif
@if (session()->has('error'))
<div class="bg-red-100 dark:bg-red-900 border border-red-400 dark:border-red-600 text-red-700 dark:text-red-200 px-4 py-3 rounded relative" role="alert">
<span class="block sm:inline">{{ session('error') }}</span>
</div>
@endif
{{-- Stats Cards --}}
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<flux:card>
<div class="text-center">
<div class="text-2xl font-bold text-blue-600">{{ $this->stats['total_devices'] }}</div>
<div class="text-sm text-gray-600 dark:text-gray-400">Total Devices</div>
</div>
</flux:card>
<flux:card>
<div class="text-center">
<div class="text-2xl font-bold text-green-600">{{ $this->stats['active_devices'] }}</div>
<div class="text-sm text-gray-600 dark:text-gray-400">Active Devices</div>
</div>
</flux:card>
<flux:card>
<div class="text-center">
<div class="text-2xl font-bold text-orange-600">{{ $this->stats['online_devices'] }}</div>
<div class="text-sm text-gray-600 dark:text-gray-400">Online Now</div>
</div>
</flux:card>
<flux:card>
<div class="text-center">
<div class="text-2xl font-bold text-purple-600">{{ $this->stats['with_traccar'] }}</div>
<div class="text-sm text-gray-600 dark:text-gray-400">Traccar Synced</div>
</div>
</flux:card>
</div>
{{-- Filters --}}
<flux:card>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<flux:field>
<flux:label>Search</flux:label>
<flux:input
wire:model.live="search"
placeholder="Search devices..."
icon="magnifying-glass"
/>
</flux:field>
<flux:field>
<flux:label>Status</flux:label>
<flux:select wire:model.live="statusFilter">
<option value="all">All Status</option>
<option value="online">Online</option>
<option value="offline">Offline</option>
<option value="unknown">Unknown</option>
</flux:select>
</flux:field>
<flux:field>
<flux:label>Group</flux:label>
<flux:select wire:model.live="groupFilter">
<option value="all">All Groups</option>
@foreach($deviceGroups as $group)
<option value="{{ $group->id }}">{{ $group->name }}</option>
@endforeach
</flux:select>
</flux:field>
</div>
</flux:card>
{{-- Device Form Modal --}}
<flux:modal wire:model.live="showForm" class="md:w-2xl">
<div class="space-y-6">
<div>
<flux:heading size="lg">
{{ $editingDevice ? 'Edit Device' : 'Add New Device' }}
</flux:heading>
<flux:subheading>
{{ $editingDevice ? 'Update device information and Traccar integration' : 'Add a new GPS tracking device with Traccar integration' }}
</flux:subheading>
</div>
<form wire:submit="saveDevice" class="space-y-6">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<flux:field>
<flux:label>Device Name *</flux:label>
<flux:input wire:model="name" required />
<flux:error name="name" />
</flux:field>
<flux:field>
<flux:label>Unique ID *</flux:label>
<flux:input wire:model="unique_id" required />
<flux:error name="unique_id" />
</flux:field>
<flux:field>
<flux:label>IMEI</flux:label>
<flux:input wire:model="imei" />
<flux:error name="imei" />
</flux:field>
<flux:field>
<flux:label>Phone Number</flux:label>
<flux:input wire:model="phone" />
<flux:error name="phone" />
</flux:field>
<flux:field>
<flux:label>Model</flux:label>
<flux:input wire:model="model" />
<flux:error name="model" />
</flux:field>
<flux:field>
<flux:label>Contact</flux:label>
<flux:input wire:model="contact" />
<flux:error name="contact" />
</flux:field>
<flux:field>
<flux:label>Protocol</flux:label>
<flux:input wire:model="protocol" placeholder="e.g., osmand, gt06, h02" />
<flux:error name="protocol" />
</flux:field>
<flux:field>
<flux:label>Category</flux:label>
<flux:select wire:model="category">
<option value="default">Default</option>
<option value="car">Car</option>
<option value="truck">Truck</option>
<option value="motorcycle">Motorcycle</option>
<option value="person">Person</option>
<option value="animal">Animal</option>
</flux:select>
<flux:error name="category" />
</flux:field>
<flux:field>
<flux:label>Group</flux:label>
<flux:select wire:model="group_id">
<option value="">No Group</option>
@foreach($deviceGroups as $group)
<option value="{{ $group->id }}">{{ $group->name }}</option>
@endforeach
</flux:select>
<flux:error name="group_id" />
</flux:field>
<flux:field>
<flux:label>Driver</flux:label>
<flux:select wire:model="driver_id">
<option value="">No Driver</option>
@foreach($drivers as $driver)
<option value="{{ $driver->id }}">{{ $driver->name }}</option>
@endforeach
</flux:select>
<flux:error name="driver_id" />
</flux:field>
<flux:field class="md:col-span-2">
<flux:checkbox wire:model="is_active">
Device is active
</flux:checkbox>
<flux:error name="is_active" />
</flux:field>
</div>
<flux:field>
<flux:label>Attributes (JSON)</flux:label>
<flux:textarea wire:model="deviceAttributes" rows="3" placeholder='{"key": "value"}' />
<flux:error name="deviceAttributes" />
</flux:field>
<div class="flex gap-2">
<flux:spacer />
<flux:modal.close>
<flux:button variant="ghost">Cancel</flux:button>
</flux:modal.close>
<flux:button type="submit" variant="primary">
{{ $editingDevice ? 'Update Device' : 'Create Device' }}
</flux:button>
</div>
</form>
</div>
</flux:modal> {{-- Devices Table --}}
<flux:card>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead class="bg-gray-50 dark:bg-gray-700">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
Device
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
Status
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
Location
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
Last Update
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
Group/Driver
</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
Actions
</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
@forelse($devices as $device)
<tr>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center space-x-3">
<div class="flex-shrink-0">
<div class="h-10 w-10 rounded-full bg-gray-100 dark:bg-gray-700 flex items-center justify-center">
<flux:icon.device-phone-mobile class="h-5 w-5 text-gray-600 dark:text-gray-400" />
</div>
</div>
<div>
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">{{ $device->name }}</div>
<div class="text-sm text-gray-500 dark:text-gray-400">{{ $device->unique_id }}</div>
@if($device->imei)
<div class="text-xs text-gray-400 dark:text-gray-500">IMEI: {{ $device->imei }}</div>
@endif
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="inline-flex items-center px-2 py-1 text-xs font-semibold rounded-full
@if($device->status === 'online') bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200
@elseif($device->status === 'offline') bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200
@else bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200 @endif">
{{ ucfirst($device->status) }}
</span>
@if($device->is_active)
<div class="mt-1">
<span class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200">
Active
</span>
</div>
@else
<div class="mt-1">
<span class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200">
Inactive
</span>
</div>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
@if($device->currentPosition)
<div>{{ $device->currentPosition->address ?? 'Unknown' }}</div>
<div class="text-xs text-gray-500 dark:text-gray-400">{{ $device->currentPosition->getCoordinates() }}</div>
@else
<span class="text-gray-400 dark:text-gray-500">No position</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
@if($device->last_update)
{{ $device->last_update->diffForHumans() }}
@else
Never
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
<div class="space-y-1">
@if($device->group)
<div class="text-sm text-blue-600 dark:text-blue-400">
<flux:icon.folder class="h-3 w-3 inline mr-1" />
{{ $device->group->name }}
</div>
@endif
@if($device->driver)
<div class="text-sm text-green-600 dark:text-green-400">
<flux:icon.user class="h-3 w-3 inline mr-1" />
{{ $device->driver->name }}
</div>
@endif
@if($device->protocol)
<div class="text-xs text-gray-500 dark:text-gray-400">{{ $device->protocol }}</div>
@endif
@if($device->traccar_device_id)
<div class="text-xs text-purple-600 dark:text-purple-400">
<flux:icon.link class="h-3 w-3 inline mr-1" />
Traccar Synced
</div>
@else
<div class="text-xs text-gray-400 dark:text-gray-500">
<flux:icon.exclamation-triangle class="h-3 w-3 inline mr-1" />
Local Only
</div>
@endif
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium space-x-2">
<flux:button
wire:click="editDevice({{ $device->id }})"
variant="outline"
size="sm">
<flux:icon.pencil class="size-4" />
</flux:button>
<flux:button
wire:click="deleteDevice({{ $device->id }})"
wire:confirm="Are you sure you want to delete this device? This will also remove it from Traccar."
variant="danger"
size="sm">
<flux:icon.trash class="size-4" />
</flux:button>
</td>
</tr>
@empty
<tr>
<td colspan="6" class="px-6 py-12 text-center">
<flux:icon.device-phone-mobile class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500" />
<h3 class="mt-2 text-sm font-medium text-gray-900 dark:text-gray-100">No devices found</h3>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Get started by adding your first device or syncing with Traccar.</p>
<div class="mt-6 flex justify-center space-x-3">
<flux:button wire:click="createDevice" icon="plus">
Add Device
</flux:button>
<flux:button wire:click="syncWithTraccar" variant="outline" icon="arrow-path">
Sync with Traccar
</flux:button>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($devices->hasPages())
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700">
{{ $devices->links() }}
</div>
@endif
</flux:card>
</div>