@fleetbase/fleetops-engine 0.6.52 → 0.6.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/addon/components/device/details.hbs +106 -135
  2. package/addon/components/device/details.js +174 -1
  3. package/addon/components/device/panel-header.hbs +2 -2
  4. package/addon/components/layout/fleet-ops-sidebar/operations-monitor.hbs +201 -0
  5. package/addon/components/layout/fleet-ops-sidebar/operations-monitor.js +654 -0
  6. package/addon/components/layout/fleet-ops-sidebar.hbs +19 -233
  7. package/addon/components/layout/fleet-ops-sidebar.js +364 -395
  8. package/addon/components/maintenance-schedule/form.js +1 -0
  9. package/addon/components/order/form/details.hbs +1 -43
  10. package/addon/components/order/form/details.js +18 -53
  11. package/addon/components/order/form/orchestrator-constraints.hbs +40 -0
  12. package/addon/components/order/form/orchestrator-constraints.js +61 -0
  13. package/addon/components/order/form/payload.js +50 -2
  14. package/addon/components/order/form/route.js +15 -0
  15. package/addon/components/order/form/service-rate.hbs +62 -11
  16. package/addon/components/order/form/service-rate.js +124 -2
  17. package/addon/components/order/form.hbs +2 -0
  18. package/addon/components/telematic/details.hbs +7 -1
  19. package/addon/components/telematic/form.js +16 -8
  20. package/addon/components/work-order/details.hbs +5 -1
  21. package/addon/components/work-order/form.hbs +26 -4
  22. package/addon/components/work-order/form.js +5 -1
  23. package/addon/controllers/connectivity/devices/index/details/events.js +244 -1
  24. package/addon/controllers/connectivity/devices/index/details/sensors.js +199 -0
  25. package/addon/controllers/connectivity/devices/index/details/vehicle.js +74 -0
  26. package/addon/controllers/connectivity/devices/index/details.js +12 -0
  27. package/addon/controllers/connectivity/devices/index.js +4 -1
  28. package/addon/controllers/connectivity/telematics/details/attachments.js +220 -54
  29. package/addon/controllers/connectivity/telematics/details/devices.js +90 -41
  30. package/addon/controllers/connectivity/telematics/details/events.js +39 -11
  31. package/addon/controllers/connectivity/telematics/details/sensors.js +39 -19
  32. package/addon/controllers/maintenance/index.js +100 -0
  33. package/addon/controllers/maintenance/work-orders/index.js +18 -1
  34. package/addon/controllers/management/fuel-transactions/index.js +1 -1
  35. package/addon/controllers/management/index.js +88 -0
  36. package/addon/controllers/settings/index.js +142 -0
  37. package/addon/extension.js +21 -0
  38. package/addon/routes/analytics/index.js +3 -0
  39. package/addon/routes/connectivity/devices/index/details/events.js +37 -1
  40. package/addon/routes/connectivity/devices/index/details/sensors.js +35 -0
  41. package/addon/routes/connectivity/devices/index/details/vehicle.js +7 -0
  42. package/addon/routes/connectivity/devices/index.js +3 -0
  43. package/addon/routes/connectivity/telematics/details/attachments.js +72 -9
  44. package/addon/routes/connectivity/telematics/details/devices.js +4 -0
  45. package/addon/routes/connectivity/telematics/details/sensors.js +1 -0
  46. package/addon/routes/connectivity/telematics/details.js +1 -1
  47. package/addon/routes/connectivity/telematics/edit.js +1 -1
  48. package/addon/routes/maintenance/index.js +3 -0
  49. package/addon/routes/maintenance/work-orders/index.js +3 -0
  50. package/addon/routes/settings/index.js +3 -0
  51. package/addon/routes.js +2 -0
  52. package/addon/services/order-creation.js +54 -1
  53. package/addon/services/order-validation.js +3 -2
  54. package/addon/services/service-rate-actions.js +1 -0
  55. package/addon/styles/fleetops-engine.css +1821 -6
  56. package/addon/templates/analytics/index.hbs +14 -0
  57. package/addon/templates/analytics/reports/index.hbs +4 -4
  58. package/addon/templates/connectivity/devices/index/details/events.hbs +85 -1
  59. package/addon/templates/connectivity/devices/index/details/sensors.hbs +73 -0
  60. package/addon/templates/connectivity/devices/index/details/vehicle.hbs +84 -0
  61. package/addon/templates/connectivity/telematics/details/attachments.hbs +198 -121
  62. package/addon/templates/connectivity/telematics/details/devices.hbs +4 -1
  63. package/addon/templates/connectivity/telematics/details/events.hbs +4 -1
  64. package/addon/templates/connectivity/telematics/details/sensors.hbs +3 -0
  65. package/addon/templates/maintenance/index.hbs +113 -0
  66. package/addon/templates/management/fuel-transactions/index/details.hbs +1 -1
  67. package/addon/templates/management/index.hbs +109 -73
  68. package/addon/templates/management/vehicles/index/edit.hbs +1 -1
  69. package/addon/templates/operations/orders/index.hbs +1 -1
  70. package/addon/templates/operations/service-rates/index.hbs +1 -1
  71. package/addon/templates/settings/index.hbs +83 -0
  72. package/addon/templates/settings/routing.hbs +2 -2
  73. package/addon/utils/fleet-ops-options.js +38 -0
  74. package/app/components/order/form/orchestrator-constraints.js +1 -0
  75. package/app/controllers/connectivity/devices/index/details/sensors.js +1 -0
  76. package/app/controllers/connectivity/devices/index/details/vehicle.js +1 -0
  77. package/app/controllers/maintenance/index.js +1 -0
  78. package/app/controllers/management/index.js +1 -0
  79. package/app/controllers/settings/index.js +1 -0
  80. package/app/routes/analytics/index.js +1 -0
  81. package/app/routes/connectivity/devices/index/details/sensors.js +1 -0
  82. package/app/routes/connectivity/devices/index/details/vehicle.js +1 -0
  83. package/app/routes/maintenance/index.js +1 -0
  84. package/app/routes/settings/index.js +1 -0
  85. package/app/templates/analytics/index.js +1 -0
  86. package/app/templates/connectivity/devices/index/details/sensors.js +1 -0
  87. package/app/templates/connectivity/devices/index/details/vehicle.js +1 -0
  88. package/app/templates/maintenance/index.js +1 -0
  89. package/app/templates/management/index.js +1 -0
  90. package/app/templates/settings/index.js +1 -0
  91. package/composer.json +1 -1
  92. package/extension.json +1 -1
  93. package/package.json +3 -3
  94. package/server/migrations/2025_08_28_054930_create_work_orders_table.php +1 -0
  95. package/server/migrations/2026_06_15_000001_normalize_device_attachable_vehicle_morph_types.php +30 -0
  96. package/server/migrations/2026_06_18_000001_add_category_to_work_orders_table.php +32 -0
  97. package/server/seeders/Testing/MaintenanceSeeder.php +5 -4
  98. package/server/src/Console/Commands/FixInvalidPolymorphicRelationTypeNamespaces.php +1 -0
  99. package/server/src/Console/Commands/ProcessMaintenanceTriggers.php +1 -0
  100. package/server/src/Console/Commands/SyncTelematics.php +89 -0
  101. package/server/src/Exceptions/TelematicProviderException.php +13 -2
  102. package/server/src/Http/Controllers/Api/v1/OrderController.php +6 -21
  103. package/server/src/Http/Controllers/Internal/v1/DeviceController.php +112 -11
  104. package/server/src/Http/Controllers/Internal/v1/DeviceEventController.php +16 -5
  105. package/server/src/Http/Controllers/Internal/v1/FleetController.php +9 -0
  106. package/server/src/Http/Controllers/Internal/v1/HubController.php +300 -0
  107. package/server/src/Http/Controllers/Internal/v1/LiveController.php +177 -0
  108. package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +1 -0
  109. package/server/src/Http/Controllers/Internal/v1/OrderController.php +4 -32
  110. package/server/src/Http/Controllers/Internal/v1/SearchController.php +506 -0
  111. package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +4 -4
  112. package/server/src/Http/Controllers/Internal/v1/TelematicController.php +1 -1
  113. package/server/src/Http/Controllers/Internal/v1/VehicleController.php +94 -10
  114. package/server/src/Http/Filter/DeviceEventFilter.php +44 -0
  115. package/server/src/Http/Filter/DeviceFilter.php +131 -0
  116. package/server/src/Http/Filter/DriverFilter.php +6 -0
  117. package/server/src/Http/Filter/SensorFilter.php +125 -0
  118. package/server/src/Http/Filter/VehicleFilter.php +19 -0
  119. package/server/src/Http/Requests/CreateDriverRequest.php +14 -13
  120. package/server/src/Http/Requests/Internal/CreateDriverRequest.php +1 -0
  121. package/server/src/Http/Resources/v1/Driver.php +11 -2
  122. package/server/src/Http/Resources/v1/WorkOrder.php +1 -0
  123. package/server/src/Jobs/FinalizeApiOrderCreation.php +47 -0
  124. package/server/src/Jobs/FinalizeInternalOrderCreation.php +35 -0
  125. package/server/src/Jobs/NotifyBulkAssignedDriver.php +47 -0
  126. package/server/src/Jobs/SyncTelematicDevicesJob.php +181 -66
  127. package/server/src/Models/Device.php +15 -1
  128. package/server/src/Models/DeviceEvent.php +1 -1
  129. package/server/src/Models/Driver.php +17 -1
  130. package/server/src/Models/Sensor.php +15 -2
  131. package/server/src/Models/Telematic.php +1 -1
  132. package/server/src/Models/Vehicle.php +5 -1
  133. package/server/src/Models/WorkOrder.php +5 -2
  134. package/server/src/Observers/DriverObserver.php +3 -3
  135. package/server/src/Observers/FleetObserver.php +23 -0
  136. package/server/src/Observers/VehicleObserver.php +3 -3
  137. package/server/src/Providers/FleetOpsServiceProvider.php +12 -0
  138. package/server/src/Support/LiveCacheService.php +1 -1
  139. package/server/src/Support/Telematics/Providers/AfaqyProvider.php +188 -28
  140. package/server/src/Support/Telematics/Providers/SafeeProvider.php +44 -3
  141. package/server/src/Support/Telematics/TelematicService.php +330 -30
  142. package/server/src/routes.php +10 -0
  143. package/server/tests/DeviceFilterTest.php +36 -0
  144. package/server/tests/HubRoutesTest.php +62 -0
  145. package/server/tests/OperationsMonitorEndpointTest.php +38 -0
  146. package/server/tests/OrderQueuedClosureRegressionTest.php +36 -0
  147. package/server/tests/TelematicsHardeningTest.php +455 -3
  148. package/server/tests/VehicleDriverAssignmentTest.php +27 -0
  149. package/translations/ar-ae.yml +1 -1
  150. package/translations/en-us.yaml +1 -0
  151. package/translations/vi-vn.yaml +1 -1
@@ -0,0 +1,14 @@
1
+ <Layout::Section::Body class="overflow-y-scroll h-full" data-test-fleet-ops-analytics-hub>
2
+ <Dashboard
3
+ @defaultDashboardId="fleet-ops"
4
+ @defaultDashboardName="Fleet-Ops Dashboard"
5
+ @extension="fleet-ops"
6
+ @leftHeaderWrapperClass="fleet-ops-dashboard-title"
7
+ @rightHeaderWrapperClass="fleet-ops-dashboard-actions"
8
+ @createWrapperClass="fleet-ops-dashboard-create-wrapper"
9
+ @stickyHeader={{true}}
10
+ class="fleet-ops-dashboard-page"
11
+ />
12
+ <Spacer @height="300px" />
13
+ {{outlet}}
14
+ </Layout::Section::Body>
@@ -15,7 +15,7 @@
15
15
  "table/empty-state"
16
16
  icon="chart-line"
17
17
  title="No reports yet"
18
- description="Create a FleetOps report to start tracking operational trends, utilization, and performance over time."
18
+ description="Create a Fleet-Ops report to start tracking operational trends, utilization, and performance over time."
19
19
  filteredTitle="No reports match your search"
20
20
  filteredDescription="Adjust the search or filters to find an existing report."
21
21
  primaryText="New report"
@@ -24,9 +24,9 @@
24
24
  secondaryText="Refresh"
25
25
  secondaryIcon="refresh"
26
26
  secondaryAction=this.reportActions.refresh
27
- docsSlug="fleet-ops/analytics/overview"
28
- docsTitle="Analytics guide"
29
- docsText="Analytics guide"
27
+ docsSlug="fleet-ops/analytics/reports/overview"
28
+ docsTitle="Reports guide"
29
+ docsText="Reports guide"
30
30
  docsSource="fleet-ops-empty-reports"
31
31
  }}
32
32
  />
@@ -1 +1,85 @@
1
- {{outlet}}
1
+ <div class="flex flex-col gap-3 p-3">
2
+ <div class="flex flex-col gap-1">
3
+ <h2 class="text-base font-bold text-gray-900 dark:text-gray-100">Event Review + Triage</h2>
4
+ <p class="text-xs text-gray-500 dark:text-gray-300">Warning signals, diagnostics, and unprocessed telemetry.</p>
5
+ </div>
6
+
7
+ <section class="grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-4">
8
+ {{#each this.metrics as |metric|}}
9
+ <div class="fleet-ops-widget fleet-ops-kpi-tile fleetops-connectivity-kpi-tile {{metric.accentClass}} relative min-h-[82px] overflow-hidden rounded-md border bg-white shadow-sm dark:bg-gray-800">
10
+ <div class="flex h-full items-start justify-between gap-2 p-3">
11
+ <div class="min-w-0">
12
+ <div class="truncate text-[10px] font-bold uppercase text-gray-500 dark:text-gray-400">{{metric.label}}</div>
13
+ <div class="mt-1 text-xl font-extrabold leading-none text-gray-900 dark:text-gray-50">{{metric.value}}</div>
14
+ </div>
15
+ <div class="fleetops-connectivity-kpi-icon flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-md text-[13px]">
16
+ <FaIcon @icon={{metric.icon}} />
17
+ </div>
18
+ </div>
19
+ </div>
20
+ {{/each}}
21
+ </section>
22
+
23
+ {{#if this.hasHealthyEventState}}
24
+ <Telematic::TabEmptyState
25
+ @tone="success"
26
+ @icon="check"
27
+ @title="No warning or unprocessed events"
28
+ @message="Recent telemetry is reviewed and no warning-level events are currently visible for this device."
29
+ @primaryText="Refresh"
30
+ @primaryIcon="refresh"
31
+ @primaryAction={{this.refresh}}
32
+ />
33
+ {{/if}}
34
+
35
+ <section class="overflow-hidden rounded-md border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800">
36
+ <div class="fleetops-provider-connections-header border-b border-gray-200 px-3 py-2 dark:border-gray-700">
37
+ <div>
38
+ <h3 class="text-sm font-bold text-gray-900 dark:text-gray-100">Telemetry Events</h3>
39
+ <p class="mt-1 text-xs text-gray-500 dark:text-gray-300">Filter by type, severity, processed state, and date.</p>
40
+ </div>
41
+ <div class="fleetops-provider-connections-actions flex min-w-0 flex-wrap items-center gap-1 lg:ml-auto lg:flex-nowrap lg:justify-end">
42
+ <Layout::Resource::TabularActions
43
+ @title="Telemetry Events"
44
+ @searchQuery={{this.events_query}}
45
+ @onSearch={{perform this.searchTask}}
46
+ @columns={{this.columns}}
47
+ @page={{this.events_page}}
48
+ @onPageChange={{fn (mut this.events_page)}}
49
+ @actionButtons={{this.actionButtons}}
50
+ @bulkActions={{this.bulkActions}}
51
+ @controller={{this}}
52
+ @searchInputClass="fleetops-provider-connections-search"
53
+ @filterPickerButtonClass="fleetops-provider-connections-action-button"
54
+ @columnPickerButtonClass="fleetops-provider-connections-action-button"
55
+ @filterPickerSize="sm"
56
+ @columnPickerSize="sm"
57
+ />
58
+ </div>
59
+ </div>
60
+
61
+ <Layout::Resource::Tabular
62
+ @resource="device-event"
63
+ @title="Telemetry Events"
64
+ @data={{@model}}
65
+ @columns={{this.columns}}
66
+ @page={{this.events_page}}
67
+ @onPageChange={{fn (mut this.events_page)}}
68
+ @controller={{this}}
69
+ @withoutHeader={{true}}
70
+ @tableWrapperClass="no-table-extra-spacing"
71
+ @emptyStateComponent={{component
72
+ "table/empty-state"
73
+ variant="compact"
74
+ icon="bolt"
75
+ title="No telemetry events"
76
+ description="Events appear after this device reports provider activity."
77
+ filteredTitle="No events match your filters"
78
+ filteredDescription="Adjust the search, severity, or date filters to review other events."
79
+ primaryText="Refresh"
80
+ primaryIcon="refresh"
81
+ primaryAction=this.refresh
82
+ }}
83
+ />
84
+ </section>
85
+ </div>
@@ -0,0 +1,73 @@
1
+ <div class="flex flex-col gap-3 p-3">
2
+ <div class="flex flex-col gap-1">
3
+ <h2 class="text-base font-bold text-gray-900 dark:text-gray-100">Sensor Inventory + Health</h2>
4
+ <p class="text-xs text-gray-500 dark:text-gray-300">Readings, thresholds, and reporting health for this device.</p>
5
+ </div>
6
+
7
+ <section class="grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-4">
8
+ {{#each this.metrics as |metric|}}
9
+ <div class="fleet-ops-widget fleet-ops-kpi-tile fleetops-connectivity-kpi-tile {{metric.accentClass}} relative min-h-[82px] overflow-hidden rounded-md border bg-white shadow-sm dark:bg-gray-800">
10
+ <div class="flex h-full items-start justify-between gap-2 p-3">
11
+ <div class="min-w-0">
12
+ <div class="truncate text-[10px] font-bold uppercase text-gray-500 dark:text-gray-400">{{metric.label}}</div>
13
+ <div class="mt-1 text-xl font-extrabold leading-none text-gray-900 dark:text-gray-50">{{metric.value}}</div>
14
+ </div>
15
+ <div class="fleetops-connectivity-kpi-icon flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-md text-[13px]">
16
+ <FaIcon @icon={{metric.icon}} />
17
+ </div>
18
+ </div>
19
+ </div>
20
+ {{/each}}
21
+ </section>
22
+
23
+ <section class="overflow-hidden rounded-md border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800">
24
+ <div class="fleetops-provider-connections-header border-b border-gray-200 px-3 py-2 dark:border-gray-700">
25
+ <div>
26
+ <h3 class="text-sm font-bold text-gray-900 dark:text-gray-100">Device Sensors</h3>
27
+ <p class="mt-1 text-xs text-gray-500 dark:text-gray-300">Filter by type, status, and last reading.</p>
28
+ </div>
29
+ <div class="fleetops-provider-connections-actions flex min-w-0 flex-wrap items-center gap-2 lg:ml-auto lg:flex-nowrap lg:justify-end">
30
+ <Layout::Resource::TabularActions
31
+ @title="Device Sensors"
32
+ @searchQuery={{this.sensors_query}}
33
+ @onSearch={{perform this.searchTask}}
34
+ @columns={{this.columns}}
35
+ @page={{this.sensors_page}}
36
+ @onPageChange={{fn (mut this.sensors_page)}}
37
+ @actionButtons={{this.actionButtons}}
38
+ @bulkActions={{this.bulkActions}}
39
+ @controller={{this}}
40
+ @searchInputClass="fleetops-provider-connections-search"
41
+ @filterPickerButtonClass="fleetops-provider-connections-action-button"
42
+ @columnPickerButtonClass="fleetops-provider-connections-action-button"
43
+ @filterPickerSize="sm"
44
+ @columnPickerSize="sm"
45
+ />
46
+ </div>
47
+ </div>
48
+
49
+ <Layout::Resource::Tabular
50
+ @resource="sensor"
51
+ @title="Device Sensors"
52
+ @data={{@model}}
53
+ @columns={{this.columns}}
54
+ @page={{this.sensors_page}}
55
+ @onPageChange={{fn (mut this.sensors_page)}}
56
+ @controller={{this}}
57
+ @withoutHeader={{true}}
58
+ @tableWrapperClass="no-table-extra-spacing"
59
+ @emptyStateComponent={{component
60
+ "table/empty-state"
61
+ variant="compact"
62
+ icon="gauge-high"
63
+ title="No sensors connected"
64
+ description="Sensors appear here after provider telemetry links them to this device."
65
+ filteredTitle="No sensors match your filters"
66
+ filteredDescription="Adjust the search or filters to find another sensor."
67
+ primaryText="Refresh"
68
+ primaryIcon="refresh"
69
+ primaryAction=this.refresh
70
+ }}
71
+ />
72
+ </section>
73
+ </div>
@@ -0,0 +1,84 @@
1
+ <div class="flex flex-col gap-3 p-3">
2
+ <div class="flex items-start justify-between gap-3">
3
+ <div class="min-w-0">
4
+ <h2 class="text-base font-bold text-gray-900 dark:text-gray-100">Vehicle Attachment</h2>
5
+ <p class="mt-0.5 text-xs text-gray-500 dark:text-gray-300">Fleet context for telemetry from this device.</p>
6
+ </div>
7
+
8
+ {{#if this.hasVehicle}}
9
+ <div class="flex flex-nowrap items-center gap-2 whitespace-nowrap">
10
+ {{#if this.vehicle.id}}
11
+ <Button @icon="eye" @text="Open Vehicle" @size="xs" @onClick={{this.openVehicle}} />
12
+ {{/if}}
13
+ <Button @icon="shuffle" @text="Change Vehicle" @size="xs" @onClick={{this.attachToVehicle}} />
14
+ <Button @icon="unlink" @text="Detach" @size="xs" @type="danger" @onClick={{this.detachFromVehicle}} />
15
+ </div>
16
+ {{/if}}
17
+ </div>
18
+
19
+ {{#if this.hasVehicle}}
20
+ <section class="overflow-hidden rounded-md border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800">
21
+ <div class="flex gap-3 p-3">
22
+ <Image
23
+ src={{this.vehiclePhotoUrl}}
24
+ @fallbackSrc={{config "defaultValues.placeholderImage"}}
25
+ alt={{this.vehicleName}}
26
+ class="h-20 w-20 flex-shrink-0 rounded-md object-cover shadow-sm"
27
+ />
28
+
29
+ <div class="min-w-0 flex-1">
30
+ <div class="flex flex-wrap items-center gap-2">
31
+ <h3 class="truncate text-base font-bold text-gray-900 dark:text-gray-100">{{this.vehicleName}}</h3>
32
+ {{#if this.vehicleStatus}}
33
+ <Badge @status={{this.vehicleStatus}}>{{smart-humanize this.vehicleStatus}}</Badge>
34
+ {{/if}}
35
+ </div>
36
+
37
+ <div class="mt-1 grid grid-cols-1 gap-2 text-xs text-gray-500 dark:text-gray-300 sm:grid-cols-2 lg:grid-cols-4">
38
+ <div>
39
+ <div class="font-semibold uppercase text-gray-400 dark:text-gray-500">Plate</div>
40
+ <div class="truncate text-gray-800 dark:text-gray-100">{{n-a this.plateNumber}}</div>
41
+ </div>
42
+ <div>
43
+ <div class="font-semibold uppercase text-gray-400 dark:text-gray-500">VIN</div>
44
+ <div class="truncate text-gray-800 dark:text-gray-100">{{n-a this.vin}}</div>
45
+ </div>
46
+ <div>
47
+ <div class="font-semibold uppercase text-gray-400 dark:text-gray-500">Call Sign</div>
48
+ <div class="truncate text-gray-800 dark:text-gray-100">{{n-a this.callSign}}</div>
49
+ </div>
50
+ <div>
51
+ <div class="font-semibold uppercase text-gray-400 dark:text-gray-500">Driver</div>
52
+ <div class="truncate text-gray-800 dark:text-gray-100">{{n-a this.vehicleDriverName}}</div>
53
+ </div>
54
+ </div>
55
+
56
+ <div class="mt-3 grid grid-cols-1 gap-2 text-xs text-gray-500 dark:text-gray-300 sm:grid-cols-3">
57
+ <div>
58
+ <div class="font-semibold uppercase text-gray-400 dark:text-gray-500">Vehicle</div>
59
+ <div class="truncate text-gray-800 dark:text-gray-100">{{n-a this.yearMakeModel}}</div>
60
+ </div>
61
+ <div>
62
+ <div class="font-semibold uppercase text-gray-400 dark:text-gray-500">Attached Device</div>
63
+ <div class="truncate text-gray-800 dark:text-gray-100">{{n-a this.device.displayName}}</div>
64
+ </div>
65
+ <div>
66
+ <div class="font-semibold uppercase text-gray-400 dark:text-gray-500">Device Last Seen</div>
67
+ <div class="truncate text-gray-800 dark:text-gray-100">{{n-a (format-date-fns this.device.last_online_at "dd MMM yyyy, HH:mm")}}</div>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ </div>
72
+ </section>
73
+ {{else}}
74
+ <Telematic::TabEmptyState
75
+ @tone="warning"
76
+ @icon="link-slash"
77
+ @title="No vehicle attached"
78
+ @message="Attach this device to a vehicle so telemetry has fleet context."
79
+ @primaryText="Attach Vehicle"
80
+ @primaryIcon="link"
81
+ @primaryAction={{this.attachToVehicle}}
82
+ />
83
+ {{/if}}
84
+ </div>