@fleetbase/fleetops-engine 0.6.55 → 0.6.57
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.
- package/addon/components/admin/map-settings.hbs +1 -1
- package/addon/components/ai/create-order-preview.hbs +297 -0
- package/addon/components/ai/create-order-preview.js +362 -0
- package/addon/components/ai/route-preview-map.hbs +79 -0
- package/addon/components/ai/route-preview-map.js +527 -0
- package/addon/components/device/details.js +1 -1
- package/addon/components/device-event/details.hbs +192 -0
- package/addon/components/device-event/details.js +151 -0
- package/addon/components/driver/schedule.js +1 -1
- package/addon/components/fleet/details.hbs +21 -8
- package/addon/components/fleet/details.js +55 -1
- package/addon/components/fleet/hierarchy-tree.hbs +137 -0
- package/addon/components/fleet/hierarchy-tree.js +528 -0
- package/addon/components/layout/fleet-ops-sidebar.js +15 -9
- package/addon/components/map/drawer/device-event-listing.js +1 -0
- package/addon/components/map/drawer/driver-listing.js +1 -0
- package/addon/components/map/drawer/place-listing.js +1 -0
- package/addon/components/map/drawer/vehicle-listing.js +1 -0
- package/addon/components/order/details.hbs +34 -1
- package/addon/components/order/form/details.hbs +4 -0
- package/addon/components/order/form.hbs +9 -1
- package/addon/components/telematic/details.hbs +1 -1
- package/addon/components/telematic/form.hbs +2 -2
- package/addon/components/telematic/settings.hbs +1 -1
- package/addon/components/vehicle/card.hbs +14 -2
- package/addon/components/vehicle/form.hbs +13 -1
- package/addon/controllers/connectivity/devices/index/details/events.js +1 -1
- package/addon/controllers/connectivity/devices/index/details/vehicle.js +2 -2
- package/addon/controllers/connectivity/events/details.js +68 -1
- package/addon/controllers/connectivity/sensors/index.js +4 -0
- package/addon/controllers/connectivity/telematics/details/events.js +1 -1
- package/addon/controllers/connectivity/telematics/details/sensors.js +4 -0
- package/addon/controllers/settings/map.js +19 -0
- package/addon/extension.js +14 -0
- package/addon/instance-initializers/register-vroom-allocation.js +1 -1
- package/addon/routes/connectivity/events/details.js +26 -1
- package/addon/routes/management/fleets/index/details.js +1 -1
- package/addon/routes/management/vehicles/index/details.js +3 -1
- package/addon/services/map-adapter/google.js +66 -1
- package/addon/services/map-adapter/leaflet.js +1 -1
- package/addon/services/map-adapter-interface.js +1 -1
- package/addon/services/map-manager.js +16 -1
- package/addon/services/map-settings.js +15 -0
- package/addon/styles/fleetops-engine.css +487 -1
- package/addon/templates/connectivity/devices/index/details/vehicle.hbs +2 -1
- package/addon/templates/connectivity/events/details.hbs +40 -1
- package/addon/templates/settings/map.hbs +38 -9
- package/addon/utils/leaflet-plugin-loader.js +5 -5
- package/addon/utils/map-drawer-dropdown-position.js +11 -13
- package/addon/utils/route-colors.js +1 -1
- package/app/components/ai/create-order-preview.js +1 -0
- package/app/components/ai/route-preview-map.js +1 -0
- package/app/components/device-event/details.hbs +192 -0
- package/app/components/device-event/details.js +1 -0
- package/app/components/fleet/hierarchy-tree.js +1 -0
- package/app/services/route-engine.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +6 -4
- package/scripts/normalize-rgba-alpha.js +90 -0
- package/server/migrations/2026_06_23_000001_delete_bad_afaqy_telematics_sensors.php +34 -0
- package/server/src/Exports/ContactExport.php +12 -5
- package/server/src/Exports/DeviceExport.php +92 -0
- package/server/src/Exports/DriverExport.php +28 -3
- package/server/src/Exports/EquipmentExport.php +94 -0
- package/server/src/Exports/FleetExport.php +22 -7
- package/server/src/Exports/FuelReportExport.php +16 -1
- package/server/src/Exports/IssueExport.php +25 -3
- package/server/src/Exports/MaintenanceExport.php +106 -0
- package/server/src/Exports/MaintenanceScheduleExport.php +99 -0
- package/server/src/Exports/OrderExport.php +24 -5
- package/server/src/Exports/PartExport.php +99 -0
- package/server/src/Exports/PlaceExport.php +33 -7
- package/server/src/Exports/SensorExport.php +94 -0
- package/server/src/Exports/ServiceAreaExport.php +31 -7
- package/server/src/Exports/ServiceRateExport.php +41 -1
- package/server/src/Exports/TelematicExport.php +90 -0
- package/server/src/Exports/VehicleExport.php +274 -8
- package/server/src/Exports/VendorExport.php +15 -2
- package/server/src/Exports/WorkOrderExport.php +92 -0
- package/server/src/Http/Controllers/Api/v1/Concerns/ResolvesFleetOpsApiResources.php +176 -0
- package/server/src/Http/Controllers/Api/v1/DeviceController.php +225 -0
- package/server/src/Http/Controllers/Api/v1/EquipmentController.php +114 -0
- package/server/src/Http/Controllers/Api/v1/FuelTransactionController.php +185 -0
- package/server/src/Http/Controllers/Api/v1/PartController.php +120 -0
- package/server/src/Http/Controllers/Api/v1/SensorController.php +132 -0
- package/server/src/Http/Controllers/Api/v1/WorkOrderController.php +161 -0
- package/server/src/Http/Controllers/Internal/v1/DeviceController.php +18 -0
- package/server/src/Http/Controllers/Internal/v1/EquipmentController.php +17 -0
- package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +17 -0
- package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +17 -0
- package/server/src/Http/Controllers/Internal/v1/PartController.php +17 -0
- package/server/src/Http/Controllers/Internal/v1/SensorController.php +18 -0
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +29 -5
- package/server/src/Http/Controllers/Internal/v1/TelematicController.php +18 -0
- package/server/src/Http/Controllers/Internal/v1/WorkOrderController.php +17 -0
- package/server/src/Http/Filter/DeviceFilter.php +33 -10
- package/server/src/Http/Filter/EquipmentFilter.php +23 -0
- package/server/src/Http/Filter/FuelProviderTransactionFilter.php +57 -2
- package/server/src/Http/Filter/PartFilter.php +47 -0
- package/server/src/Http/Filter/SensorFilter.php +34 -6
- package/server/src/Http/Filter/WorkOrderFilter.php +23 -0
- package/server/src/Http/Requests/CreateDeviceRequest.php +51 -0
- package/server/src/Http/Requests/CreateEquipmentRequest.php +35 -0
- package/server/src/Http/Requests/CreateFuelTransactionRequest.php +50 -0
- package/server/src/Http/Requests/CreatePartRequest.php +40 -0
- package/server/src/Http/Requests/CreateSensorRequest.php +47 -0
- package/server/src/Http/Requests/CreateWorkOrderRequest.php +42 -0
- package/server/src/Http/Requests/UpdateDeviceRequest.php +7 -0
- package/server/src/Http/Requests/UpdateEquipmentRequest.php +7 -0
- package/server/src/Http/Requests/UpdateFuelTransactionRequest.php +7 -0
- package/server/src/Http/Requests/UpdatePartRequest.php +7 -0
- package/server/src/Http/Requests/UpdateSensorRequest.php +7 -0
- package/server/src/Http/Requests/UpdateWorkOrderRequest.php +7 -0
- package/server/src/Http/Resources/v1/Device.php +72 -0
- package/server/src/Http/Resources/v1/Equipment.php +57 -0
- package/server/src/Http/Resources/v1/Fleet.php +8 -2
- package/server/src/Http/Resources/v1/FuelTransaction.php +69 -0
- package/server/src/Http/Resources/v1/Part.php +64 -0
- package/server/src/Http/Resources/v1/Sensor.php +68 -0
- package/server/src/Jobs/SyncTelematicDevicesJob.php +194 -38
- package/server/src/Models/Driver.php +6 -0
- package/server/src/Models/Order.php +20 -7
- package/server/src/Models/Place.php +11 -3
- package/server/src/Observers/PurchaseRateObserver.php +2 -1
- package/server/src/Providers/FleetOpsServiceProvider.php +26 -0
- package/server/src/Support/Ai/Capabilities/AbstractFleetOpsAICapability.php +88 -0
- package/server/src/Support/Ai/Capabilities/AssetStatusCapability.php +108 -0
- package/server/src/Support/Ai/Capabilities/ConsoleNavigationCapability.php +55 -0
- package/server/src/Support/Ai/Capabilities/CreateOrderPreviewCapability.php +508 -0
- package/server/src/Support/Ai/Capabilities/DocsHelpCapability.php +49 -0
- package/server/src/Support/Ai/Capabilities/ImportOrdersPreviewCapability.php +66 -0
- package/server/src/Support/Ai/Capabilities/OperationalQueryCapability.php +319 -0
- package/server/src/Support/Ai/Capabilities/OptimizeOrderRouteCapability.php +240 -0
- package/server/src/Support/Ai/Capabilities/OrderInsightsCapability.php +95 -0
- package/server/src/Support/Ai/Capabilities/SearchResourcesCapability.php +195 -0
- package/server/src/Support/Ai/FleetOpsAiQueryResources.php +150 -0
- package/server/src/Support/Analytics/RevenueTrend.php +3 -8
- package/server/src/Support/Metrics/ActiveRevenueQuery.php +121 -0
- package/server/src/Support/Metrics/AvgOrderValueMetric.php +1 -7
- package/server/src/Support/Metrics/EarningsMetric.php +1 -10
- package/server/src/Support/Telematics/Providers/AfaqyProvider.php +104 -5
- package/server/src/Support/Telematics/Providers/SafeeProvider.php +550 -54
- package/server/src/Support/Telematics/TelematicService.php +28 -9
- package/server/src/Support/Utils.php +52 -0
- package/server/src/routes.php +67 -14
- package/server/tests/AiCreateOrderPreviewCapabilityTest.php +131 -0
- package/server/tests/AiOperationalQueryCapabilityTest.php +75 -0
- package/server/tests/ExportCoverageTest.php +79 -0
- package/server/tests/FleetHierarchyResourceTest.php +12 -0
- package/server/tests/MetricsRegistryTest.php +45 -5
- package/server/tests/OrderPayloadPlaceResolutionTest.php +53 -0
- package/server/tests/PointResolutionTest.php +63 -0
- package/server/tests/PublicFleetOpsRestEndpointsTest.php +191 -0
- package/server/tests/TelematicsHardeningTest.php +636 -1
- package/addon/components/table/cell/dropdown/action-item.hbs +0 -31
- package/addon/components/table/cell/dropdown/action-item.js +0 -62
- package/addon/components/table/cell/dropdown.hbs +0 -28
- package/addon/components/table/cell/dropdown.js +0 -91
- package/app/components/table/cell/dropdown/action-item.js +0 -1
- package/app/components/table/cell/dropdown.js +0 -1
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
<div class="flex flex-col gap-3 p-3" ...attributes>
|
|
2
|
+
<section class="overflow-hidden rounded-md border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
3
|
+
<div class="flex flex-col gap-3 p-3 lg:flex-row lg:items-start lg:justify-between">
|
|
4
|
+
<div class="min-w-0">
|
|
5
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
6
|
+
<h2 class="truncate text-base font-bold text-gray-900 dark:text-gray-100">{{smart-humanize this.eventType}}</h2>
|
|
7
|
+
<Badge @status={{this.severity}} @hideStatusDot={{true}}>{{smart-humanize this.severity}}</Badge>
|
|
8
|
+
<Badge @status={{this.processedStatus}} @hideStatusDot={{true}}>{{this.processedLabel}}</Badge>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<p class="mt-2 max-w-4xl text-sm leading-5 text-gray-700 dark:text-gray-200">{{n-a this.message}}</p>
|
|
12
|
+
|
|
13
|
+
<div class="mt-2 flex flex-wrap gap-2 text-xs text-gray-500 dark:text-gray-300">
|
|
14
|
+
<span>Occurred: {{n-a (format-date-fns this.occurredAt "dd MMM yyyy, HH:mm")}}</span>
|
|
15
|
+
<span>Processed: {{n-a (format-date-fns this.processedAt "dd MMM yyyy, HH:mm")}}</span>
|
|
16
|
+
<span>Public ID: {{n-a @resource.public_id}}</span>
|
|
17
|
+
<span>UUID: {{n-a @resource.id}}</span>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</section>
|
|
22
|
+
|
|
23
|
+
<section class="grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-4">
|
|
24
|
+
{{#each this.metrics as |metric|}}
|
|
25
|
+
<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">
|
|
26
|
+
<div class="flex h-full items-start justify-between gap-2 p-3">
|
|
27
|
+
<div class="min-w-0">
|
|
28
|
+
<div class="truncate text-[10px] font-bold uppercase text-gray-500 dark:text-gray-400">{{metric.label}}</div>
|
|
29
|
+
<div class="mt-1 truncate text-lg font-extrabold leading-none text-gray-900 dark:text-gray-50">{{n-a metric.value}}</div>
|
|
30
|
+
{{#if metric.meta}}
|
|
31
|
+
<div class="mt-1 truncate text-xs text-gray-500 dark:text-gray-300">{{metric.meta}}</div>
|
|
32
|
+
{{/if}}
|
|
33
|
+
</div>
|
|
34
|
+
<div class="fleetops-connectivity-kpi-icon flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-md text-[13px]">
|
|
35
|
+
<FaIcon @icon={{metric.icon}} />
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
{{/each}}
|
|
40
|
+
</section>
|
|
41
|
+
|
|
42
|
+
<section class="grid grid-cols-1 gap-3 xl:grid-cols-2">
|
|
43
|
+
<div class="overflow-hidden rounded-md border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
44
|
+
<div class="border-b border-gray-200 px-3 py-2 dark:border-gray-700">
|
|
45
|
+
<h3 class="text-sm font-bold text-gray-900 dark:text-gray-100">Event Details</h3>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="grid grid-cols-1 gap-2 p-3 sm:grid-cols-2 lg:grid-cols-3">
|
|
48
|
+
<div class="field-info-container">
|
|
49
|
+
<div class="field-name">Event Type</div>
|
|
50
|
+
<div class="field-value">{{n-a (smart-humanize this.eventType)}}</div>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="field-info-container">
|
|
53
|
+
<div class="field-name">Severity</div>
|
|
54
|
+
<div class="field-value"><Badge @status={{this.severity}}>{{smart-humanize this.severity}}</Badge></div>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="field-info-container">
|
|
57
|
+
<div class="field-name">State</div>
|
|
58
|
+
<div class="field-value">{{n-a (smart-humanize @resource.state)}}</div>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="field-info-container">
|
|
61
|
+
<div class="field-name">Code</div>
|
|
62
|
+
<div class="field-value">{{n-a @resource.code}}</div>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="field-info-container">
|
|
65
|
+
<div class="field-name">Reason</div>
|
|
66
|
+
<div class="field-value">{{n-a @resource.reason}}</div>
|
|
67
|
+
</div>
|
|
68
|
+
<div class="field-info-container">
|
|
69
|
+
<div class="field-name">Mileage</div>
|
|
70
|
+
<div class="field-value">{{n-a @resource.mileage}}</div>
|
|
71
|
+
</div>
|
|
72
|
+
<div class="field-info-container">
|
|
73
|
+
<div class="field-name">Age</div>
|
|
74
|
+
<div class="field-value">{{#if this.ageMinutes}}{{this.ageMinutes}} minutes{{else}}{{n-a null}}{{/if}}</div>
|
|
75
|
+
</div>
|
|
76
|
+
<div class="field-info-container">
|
|
77
|
+
<div class="field-name">Processing Delay</div>
|
|
78
|
+
<div class="field-value">{{#if this.processingDelay}}{{this.processingDelay}} minutes{{else}}{{n-a null}}{{/if}}</div>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="field-info-container sm:col-span-2 lg:col-span-3">
|
|
81
|
+
<div class="field-name">Comment</div>
|
|
82
|
+
<div class="field-value whitespace-pre-line">{{n-a @resource.comment}}</div>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<div class="overflow-hidden rounded-md border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
88
|
+
<div class="border-b border-gray-200 px-3 py-2 dark:border-gray-700">
|
|
89
|
+
<h3 class="text-sm font-bold text-gray-900 dark:text-gray-100">Device & Provider</h3>
|
|
90
|
+
</div>
|
|
91
|
+
<div class="grid grid-cols-1 gap-2 p-3 sm:grid-cols-2 lg:grid-cols-3">
|
|
92
|
+
<div class="field-info-container">
|
|
93
|
+
<div class="field-name">Device</div>
|
|
94
|
+
<div class="field-value">
|
|
95
|
+
{{#if this.deviceRouteModel}}
|
|
96
|
+
<button
|
|
97
|
+
type="button"
|
|
98
|
+
class="text-blue-600 hover:text-blue-700 hover:underline dark:text-blue-300 dark:hover:text-blue-200"
|
|
99
|
+
data-test-device-event-device-link
|
|
100
|
+
{{on "click" this.openDevice}}
|
|
101
|
+
>
|
|
102
|
+
{{n-a this.deviceName}}
|
|
103
|
+
</button>
|
|
104
|
+
{{else}}
|
|
105
|
+
{{n-a this.deviceName}}
|
|
106
|
+
{{/if}}
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
<div class="field-info-container">
|
|
110
|
+
<div class="field-name">Device UUID</div>
|
|
111
|
+
<div class="field-value">{{n-a @resource.device_uuid}}</div>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="field-info-container">
|
|
114
|
+
<div class="field-name">Device ID</div>
|
|
115
|
+
<div class="field-value">{{n-a @resource.device_id}}</div>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="field-info-container">
|
|
118
|
+
<div class="field-name">IMEI</div>
|
|
119
|
+
<div class="field-value">{{n-a @resource.device_imei}}</div>
|
|
120
|
+
</div>
|
|
121
|
+
<div class="field-info-container">
|
|
122
|
+
<div class="field-name">Serial Number</div>
|
|
123
|
+
<div class="field-value">{{n-a @resource.device_serial_number}}</div>
|
|
124
|
+
</div>
|
|
125
|
+
<div class="field-info-container">
|
|
126
|
+
<div class="field-name">IDENT</div>
|
|
127
|
+
<div class="field-value">{{n-a @resource.ident}}</div>
|
|
128
|
+
</div>
|
|
129
|
+
<div class="field-info-container">
|
|
130
|
+
<div class="field-name">Provider</div>
|
|
131
|
+
<div class="field-value">
|
|
132
|
+
{{#if this.telematicRouteModel}}
|
|
133
|
+
<button
|
|
134
|
+
type="button"
|
|
135
|
+
class="text-blue-600 hover:text-blue-700 hover:underline dark:text-blue-300 dark:hover:text-blue-200"
|
|
136
|
+
data-test-device-event-telematic-link
|
|
137
|
+
{{on "click" this.openTelematic}}
|
|
138
|
+
>
|
|
139
|
+
{{n-a this.providerLabel}}
|
|
140
|
+
</button>
|
|
141
|
+
{{else}}
|
|
142
|
+
{{n-a this.providerLabel}}
|
|
143
|
+
{{/if}}
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
<div class="field-info-container">
|
|
147
|
+
<div class="field-name">Protocol</div>
|
|
148
|
+
<div class="field-value">{{n-a @resource.protocol}}</div>
|
|
149
|
+
</div>
|
|
150
|
+
<div class="field-info-container">
|
|
151
|
+
<div class="field-name">Telematic UUID</div>
|
|
152
|
+
<div class="field-value">{{n-a @resource.telematic_uuid}}</div>
|
|
153
|
+
</div>
|
|
154
|
+
<div class="field-info-container">
|
|
155
|
+
<div class="field-name">Device Status</div>
|
|
156
|
+
<div class="field-value" data-test-device-event-device-status>
|
|
157
|
+
{{#if this.deviceStatus}}
|
|
158
|
+
<Badge @status={{this.deviceStatus}}>{{smart-humanize this.deviceStatus}}</Badge>
|
|
159
|
+
{{else}}
|
|
160
|
+
{{n-a null}}
|
|
161
|
+
{{/if}}
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
</section>
|
|
167
|
+
|
|
168
|
+
<section class="grid grid-cols-1 gap-3 xl:grid-cols-3">
|
|
169
|
+
<div class="overflow-hidden rounded-md border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
170
|
+
<div class="border-b border-gray-200 px-3 py-2 dark:border-gray-700">
|
|
171
|
+
<h3 class="text-sm font-bold text-gray-900 dark:text-gray-100">Payload</h3>
|
|
172
|
+
</div>
|
|
173
|
+
<pre class="max-h-80 overflow-auto bg-gray-900 p-3 text-[11px] leading-5 text-gray-100">{{n-a this.payloadJson}}</pre>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div class="overflow-hidden rounded-md border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
177
|
+
<div class="border-b border-gray-200 px-3 py-2 dark:border-gray-700">
|
|
178
|
+
<h3 class="text-sm font-bold text-gray-900 dark:text-gray-100">Data</h3>
|
|
179
|
+
</div>
|
|
180
|
+
<pre class="max-h-80 overflow-auto bg-gray-900 p-3 text-[11px] leading-5 text-gray-100">{{n-a this.dataJson}}</pre>
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
<div class="overflow-hidden rounded-md border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
184
|
+
<div class="border-b border-gray-200 px-3 py-2 dark:border-gray-700">
|
|
185
|
+
<h3 class="text-sm font-bold text-gray-900 dark:text-gray-100">Meta</h3>
|
|
186
|
+
</div>
|
|
187
|
+
<pre class="max-h-80 overflow-auto bg-gray-900 p-3 text-[11px] leading-5 text-gray-100">{{n-a this.metaJson}}</pre>
|
|
188
|
+
</div>
|
|
189
|
+
</section>
|
|
190
|
+
|
|
191
|
+
<CustomField::Yield @subject={{@resource}} @viewMode={{true}} @wrapperClass="bordered-top" />
|
|
192
|
+
</div>
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { action, get } from '@ember/object';
|
|
3
|
+
import { inject as service } from '@ember/service';
|
|
4
|
+
|
|
5
|
+
const warningSeverities = ['warning', 'error', 'critical', 'high'];
|
|
6
|
+
|
|
7
|
+
function firstPresent(...values) {
|
|
8
|
+
return values.find((value) => value !== undefined && value !== null && value !== '');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function prettyJson(value) {
|
|
12
|
+
if (!value) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (typeof value === 'string') {
|
|
17
|
+
try {
|
|
18
|
+
return JSON.stringify(JSON.parse(value), null, 2);
|
|
19
|
+
} catch {
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return JSON.stringify(value, null, 2);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default class DeviceEventDetailsComponent extends Component {
|
|
28
|
+
@service hostRouter;
|
|
29
|
+
|
|
30
|
+
get resource() {
|
|
31
|
+
return this.args.resource ?? {};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get eventType() {
|
|
35
|
+
return firstPresent(get(this.resource, 'event_type'), get(this.resource, 'eventType'), get(this.resource, 'type'));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get severity() {
|
|
39
|
+
return firstPresent(get(this.resource, 'severity'), 'info');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get isProcessed() {
|
|
43
|
+
return Boolean(firstPresent(get(this.resource, 'processed_at'), get(this.resource, 'processedAt')));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get processedStatus() {
|
|
47
|
+
return this.isProcessed ? 'success' : 'warning';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get processedLabel() {
|
|
51
|
+
return this.isProcessed ? 'Processed' : 'Unprocessed';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get severityAccentClass() {
|
|
55
|
+
return warningSeverities.includes(String(this.severity ?? '').toLowerCase()) ? 'fleetops-connectivity-kpi-accent-amber' : 'fleetops-connectivity-kpi-accent-green';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get message() {
|
|
59
|
+
return get(this.resource, 'message');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get occurredAt() {
|
|
63
|
+
return firstPresent(get(this.resource, 'occurred_at'), get(this.resource, 'occurredAt'), get(this.resource, 'created_at'), get(this.resource, 'createdAt'));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get processedAt() {
|
|
67
|
+
return firstPresent(get(this.resource, 'processed_at'), get(this.resource, 'processedAt'));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get processingDelay() {
|
|
71
|
+
return firstPresent(get(this.resource, 'processing_delay_minutes'), get(this.resource, 'processingDelayMinutes'));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
get ageMinutes() {
|
|
75
|
+
return firstPresent(get(this.resource, 'age_minutes'), get(this.resource, 'ageMinutes'));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get deviceName() {
|
|
79
|
+
return firstPresent(
|
|
80
|
+
get(this.resource, 'device.displayName'),
|
|
81
|
+
get(this.resource, 'device.display_name'),
|
|
82
|
+
get(this.resource, 'device.name'),
|
|
83
|
+
get(this.resource, 'device_name'),
|
|
84
|
+
get(this.resource, 'device_id')
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
get deviceIdentifier() {
|
|
89
|
+
return firstPresent(
|
|
90
|
+
get(this.resource, 'device_id'),
|
|
91
|
+
get(this.resource, 'device.device_id'),
|
|
92
|
+
get(this.resource, 'device_imei'),
|
|
93
|
+
get(this.resource, 'device_serial_number'),
|
|
94
|
+
get(this.resource, 'ident')
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
get deviceRouteModel() {
|
|
99
|
+
return firstPresent(get(this.resource, 'device.id'), get(this.resource, 'device.public_id'), get(this.resource, 'device_uuid'));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
get providerLabel() {
|
|
103
|
+
return firstPresent(get(this.resource, 'provider_descriptor.label'), get(this.resource, 'telematic.name'), get(this.resource, 'telematic_name'), get(this.resource, 'provider'));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
get telematicRouteModel() {
|
|
107
|
+
return firstPresent(get(this.resource, 'telematic.id'), get(this.resource, 'telematic.public_id'), get(this.resource, 'telematic_uuid'));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
get deviceStatus() {
|
|
111
|
+
return firstPresent(get(this.resource, 'device.status'), get(this.resource, 'device_status'), get(this.resource, 'device_connection_status'));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
get metrics() {
|
|
115
|
+
return [
|
|
116
|
+
{ label: 'Severity', value: this.severity, icon: 'triangle-exclamation', accentClass: this.severityAccentClass },
|
|
117
|
+
{
|
|
118
|
+
label: 'Processing',
|
|
119
|
+
value: this.processedLabel,
|
|
120
|
+
icon: this.isProcessed ? 'check' : 'clock',
|
|
121
|
+
accentClass: this.isProcessed ? 'fleetops-connectivity-kpi-accent-green' : 'fleetops-connectivity-kpi-accent-rose',
|
|
122
|
+
},
|
|
123
|
+
{ label: 'Device', value: this.deviceName, meta: this.deviceIdentifier, icon: 'microchip', accentClass: 'fleetops-connectivity-kpi-accent-blue' },
|
|
124
|
+
{ label: 'Provider', value: this.providerLabel, icon: 'satellite-dish', accentClass: 'fleetops-connectivity-kpi-accent-green' },
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
get payloadJson() {
|
|
129
|
+
return prettyJson(get(this.resource, 'payload'));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
get dataJson() {
|
|
133
|
+
return prettyJson(get(this.resource, 'data'));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
get metaJson() {
|
|
137
|
+
return prettyJson(get(this.resource, 'meta'));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@action openDevice() {
|
|
141
|
+
if (this.deviceRouteModel) {
|
|
142
|
+
return this.hostRouter.transitionTo('console.fleet-ops.connectivity.devices.index.details', this.deviceRouteModel);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
@action openTelematic() {
|
|
147
|
+
if (this.telematicRouteModel) {
|
|
148
|
+
return this.hostRouter.transitionTo('console.fleet-ops.connectivity.telematics.details', this.telematicRouteModel);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -219,7 +219,7 @@ export default class DriverScheduleComponent extends Component {
|
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
/**
|
|
222
|
-
* Load HOS (Hours of Service) status from the
|
|
222
|
+
* Load HOS (Hours of Service) status from the Fleet-Ops driver endpoint.
|
|
223
223
|
* Best-effort — if the endpoint is not yet implemented hosStatus stays null.
|
|
224
224
|
*/
|
|
225
225
|
@task *loadHOSStatus() {
|
|
@@ -3,30 +3,43 @@
|
|
|
3
3
|
<div class="grid grid-cols-2 gap-2 text-xs dark:text-gray-100">
|
|
4
4
|
<div class="field-info-container">
|
|
5
5
|
<div class="field-name">{{t "common.name"}}</div>
|
|
6
|
-
<div class="field-value">{{n-a
|
|
6
|
+
<div class="field-value">{{n-a this.resource.name}}</div>
|
|
7
7
|
</div>
|
|
8
8
|
<div class="field-info-container">
|
|
9
9
|
<div class="field-name">{{t "resource.service-area"}}</div>
|
|
10
|
-
<div class="field-value">{{n-a
|
|
10
|
+
<div class="field-value">{{n-a this.resource.service_area.name}}</div>
|
|
11
11
|
</div>
|
|
12
12
|
<div class="field-info-container">
|
|
13
13
|
<div class="field-name">{{t "resource.zone"}}</div>
|
|
14
|
-
<div class="field-value">{{n-a
|
|
14
|
+
<div class="field-value">{{n-a this.resource.zone.name}}</div>
|
|
15
15
|
</div>
|
|
16
16
|
<div class="field-info-container">
|
|
17
17
|
<div class="field-name">{{t "fleet.fields.task"}}</div>
|
|
18
|
-
<div class="field-value">{{n-a
|
|
18
|
+
<div class="field-value">{{n-a this.resource.task}}</div>
|
|
19
19
|
</div>
|
|
20
20
|
<div class="field-info-container">
|
|
21
21
|
<div class="field-name">{{t "fleet.fields.active-manpower"}}</div>
|
|
22
|
-
<div class="field-value">{{
|
|
22
|
+
<div class="field-value">{{this.driversOnlineCount}} of {{this.driversCount}} Online</div>
|
|
23
23
|
</div>
|
|
24
24
|
<div class="field-info-container">
|
|
25
25
|
<div class="field-name">{{t "common.date-created"}}</div>
|
|
26
|
-
<div class="field-value">{{
|
|
26
|
+
<div class="field-value">{{this.resource.createdAtShort}}</div>
|
|
27
27
|
</div>
|
|
28
28
|
</div>
|
|
29
29
|
</ContentPanel>
|
|
30
30
|
|
|
31
|
-
<
|
|
32
|
-
|
|
31
|
+
<ContentPanel @title="Hierarchy" @open={{true}} @wrapperClass="bordered-top" @panelBodyWrapperClass="p-0i">
|
|
32
|
+
<Fleet::HierarchyTree
|
|
33
|
+
@fleet={{this.resource}}
|
|
34
|
+
@onViewFleet={{this.viewFleet}}
|
|
35
|
+
@onViewDriver={{this.viewDriver}}
|
|
36
|
+
@onViewVehicle={{this.viewVehicle}}
|
|
37
|
+
@onAssignDriver={{this.assignDriver}}
|
|
38
|
+
@onAssignVehicle={{this.assignVehicle}}
|
|
39
|
+
/>
|
|
40
|
+
</ContentPanel>
|
|
41
|
+
|
|
42
|
+
<CustomField::Yield @subject={{this.resource}} @viewMode={{true}} @wrapperClass="bordered-top" />
|
|
43
|
+
|
|
44
|
+
<Spacer @height="200px" />
|
|
45
|
+
</div>
|
|
@@ -1,3 +1,57 @@
|
|
|
1
1
|
import Component from '@glimmer/component';
|
|
2
|
+
import { inject as service } from '@ember/service';
|
|
3
|
+
import { action } from '@ember/object';
|
|
2
4
|
|
|
3
|
-
export default class FleetDetailsComponent extends Component {
|
|
5
|
+
export default class FleetDetailsComponent extends Component {
|
|
6
|
+
@service fleetActions;
|
|
7
|
+
@service driverActions;
|
|
8
|
+
@service vehicleActions;
|
|
9
|
+
|
|
10
|
+
get resource() {
|
|
11
|
+
return this.args.resource ?? this.args.fleet;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
get drivers() {
|
|
15
|
+
return this.resourceArray(this.resource?.drivers);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get driversCount() {
|
|
19
|
+
return Number(this.resource?.drivers_count ?? this.drivers.length ?? 0);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get driversOnlineCount() {
|
|
23
|
+
return Number(this.resource?.drivers_online_count ?? this.drivers.filter((driver) => driver?.online).length ?? 0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
resourceArray(resources) {
|
|
27
|
+
if (!resources) {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (typeof resources.toArray === 'function') {
|
|
32
|
+
return resources.toArray();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return Array.isArray(resources) ? resources : [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@action viewFleet(fleet) {
|
|
39
|
+
return this.fleetActions.panel.view(fleet);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@action viewDriver(driver) {
|
|
43
|
+
return this.driverActions.panel.view(driver);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@action viewVehicle(vehicle) {
|
|
47
|
+
return this.vehicleActions.panel.view(vehicle);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@action assignDriver(fleet) {
|
|
51
|
+
return this.fleetActions.assignDriver(fleet);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@action assignVehicle(fleet) {
|
|
55
|
+
return this.fleetActions.assignVehicle(fleet);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<div class="fleet-hierarchy-tree" data-test-fleet-hierarchy-tree {{did-insert this.loadVisibleFleetResources}} ...attributes>
|
|
2
|
+
<div class="flex flex-col gap-2 border-b border-gray-200 bg-gray-50 px-2.5 pb-2 dark:border-gray-700 dark:bg-gray-800">
|
|
3
|
+
<div class="flex flex-col gap-2 md:flex-row md:items-center md:justify-between">
|
|
4
|
+
<div class="flex min-w-0 flex-1 items-center gap-1.5 rounded border border-gray-200 bg-white px-2 py-1 text-gray-400 dark:border-gray-700 dark:bg-gray-900">
|
|
5
|
+
<FaIcon @icon="magnifying-glass" @size="xs" />
|
|
6
|
+
<input
|
|
7
|
+
type="search"
|
|
8
|
+
value={{this.query}}
|
|
9
|
+
placeholder="Search hierarchy..."
|
|
10
|
+
class="min-w-0 flex-1 appearance-none rounded-md border-0 bg-transparent p-0 text-xs leading-5 text-gray-700 outline-none shadow-none ring-0 focus:border-transparent focus:outline-none focus:ring-0 dark:text-gray-100"
|
|
11
|
+
data-test-fleet-hierarchy-search
|
|
12
|
+
{{on "input" this.updateQuery}}
|
|
13
|
+
/>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="flex shrink-0 items-center gap-1 rounded bg-gray-100 p-1 dark:bg-gray-900" data-test-fleet-hierarchy-filters>
|
|
16
|
+
{{#each this.filters as |filter|}}
|
|
17
|
+
<button
|
|
18
|
+
type="button"
|
|
19
|
+
class="rounded px-2 py-1 text-xs font-medium {{if (eq this.activeFilter filter.id) 'bg-white text-gray-900 shadow-sm dark:bg-gray-700 dark:text-white' 'text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white'}}"
|
|
20
|
+
data-test-fleet-hierarchy-filter={{filter.id}}
|
|
21
|
+
{{on "click" (fn this.setFilter filter.id)}}
|
|
22
|
+
>
|
|
23
|
+
{{filter.label}}
|
|
24
|
+
</button>
|
|
25
|
+
{{/each}}
|
|
26
|
+
</div>
|
|
27
|
+
<div class="flex shrink-0 items-center gap-1">
|
|
28
|
+
<Button
|
|
29
|
+
@type="default"
|
|
30
|
+
@size="xs"
|
|
31
|
+
@icon={{this.expandCollapseIcon}}
|
|
32
|
+
@helpText={{this.expandCollapseHelpText}}
|
|
33
|
+
@onClick={{this.toggleExpandCollapseAll}}
|
|
34
|
+
data-test-fleet-hierarchy-expand-toggle
|
|
35
|
+
/>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<div class="fleet-hierarchy-tree-list bg-white dark:bg-gray-900" aria-label="Fleet hierarchy">
|
|
41
|
+
{{#if this.visibleRows.length}}
|
|
42
|
+
{{#each this.visibleRows as |row|}}
|
|
43
|
+
{{#if (eq row.type "fleet")}}
|
|
44
|
+
<div
|
|
45
|
+
class="fleet-hierarchy-tree-row {{row.depthClass}} group flex min-w-0 items-start gap-2 border-b border-gray-100 px-3 py-2 text-sm text-gray-700 hover:bg-gray-50 dark:border-gray-800 dark:text-gray-200 dark:hover:bg-gray-800"
|
|
46
|
+
data-test-fleet-hierarchy-row="fleet"
|
|
47
|
+
>
|
|
48
|
+
<button
|
|
49
|
+
type="button"
|
|
50
|
+
class="flex min-w-0 flex-1 items-start gap-2 text-left"
|
|
51
|
+
aria-expanded={{if (this.fleetHasChildren row.fleet) (if (this.isFleetExpanded row.fleet) "true" "false")}}
|
|
52
|
+
{{on "click" (fn this.toggleFleet row.fleet)}}
|
|
53
|
+
>
|
|
54
|
+
<span class="mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded bg-gray-100 text-gray-400 dark:bg-gray-800" aria-hidden="true">
|
|
55
|
+
{{#if (this.fleetHasChildren row.fleet)}}
|
|
56
|
+
<FaIcon @icon={{if (this.isFleetExpanded row.fleet) "chevron-down" "chevron-right"}} @size="xs" />
|
|
57
|
+
{{else}}
|
|
58
|
+
<FaIcon @icon="user-group" @size="xs" />
|
|
59
|
+
{{/if}}
|
|
60
|
+
</span>
|
|
61
|
+
<span class="min-w-0 flex-1">
|
|
62
|
+
<span class="block truncate font-semibold leading-5" title={{this.displayName row.fleet}}>{{this.displayName row.fleet}}</span>
|
|
63
|
+
<span class="block truncate text-xs leading-4 text-gray-400">{{this.fleetSubtitle row.fleet}}</span>
|
|
64
|
+
</span>
|
|
65
|
+
</button>
|
|
66
|
+
<div class="flex shrink-0 items-center gap-1">
|
|
67
|
+
<span class="hidden rounded bg-gray-100 px-1.5 py-0.5 text-xs text-gray-500 dark:bg-gray-800 dark:text-gray-400 md:inline-flex">
|
|
68
|
+
{{this.fleetSubfleetCount row.fleet}} subfleets
|
|
69
|
+
</span>
|
|
70
|
+
<DropdownButton
|
|
71
|
+
@icon="ellipsis-h"
|
|
72
|
+
@iconPrefix="fas"
|
|
73
|
+
@size="xs"
|
|
74
|
+
@buttonClass="fleetops-btn-xxs"
|
|
75
|
+
@contentClass="dropdown-menu"
|
|
76
|
+
@horizontalPosition="left"
|
|
77
|
+
@calculatePosition={{this.calculateDropdownLeftPosition}}
|
|
78
|
+
as |dd|
|
|
79
|
+
>
|
|
80
|
+
<div class="next-dd-menu mx-0 mt-1" role="menu">
|
|
81
|
+
<a href="javascript:;" class="next-dd-item" {{on "click" (dropdown-fn dd this.viewFleet row.fleet)}}>View details</a>
|
|
82
|
+
<div class="next-dd-menu-seperator"></div>
|
|
83
|
+
<a href="javascript:;" class="next-dd-item" {{on "click" (dropdown-fn dd this.assignDriver row.fleet)}}>Assign driver</a>
|
|
84
|
+
<a href="javascript:;" class="next-dd-item" {{on "click" (dropdown-fn dd this.assignVehicle row.fleet)}}>Assign vehicle</a>
|
|
85
|
+
</div>
|
|
86
|
+
</DropdownButton>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
{{else if (eq row.type "driver")}}
|
|
90
|
+
<button
|
|
91
|
+
type="button"
|
|
92
|
+
class="fleet-hierarchy-tree-row {{row.depthClass}} flex w-full min-w-0 items-start gap-2 border-b border-gray-100 px-3 py-1.5 text-left text-xs text-gray-600 hover:bg-gray-50 dark:border-gray-800 dark:text-gray-300 dark:hover:bg-gray-800"
|
|
93
|
+
data-test-fleet-hierarchy-row="driver"
|
|
94
|
+
{{on "click" (fn this.viewDriver row.driver)}}
|
|
95
|
+
>
|
|
96
|
+
<span class="mt-1 h-1.5 w-1.5 shrink-0 rounded-full {{if row.driver.online 'bg-green-500' 'bg-yellow-300'}}" aria-hidden="true"></span>
|
|
97
|
+
<span class="min-w-0 flex-1">
|
|
98
|
+
<span class="block truncate font-medium leading-4" title={{this.displayName row.driver}}>{{this.displayName row.driver}}</span>
|
|
99
|
+
<span class="block truncate text-xs leading-4 text-gray-400">Driver - {{this.driverSubtitle row.driver}}</span>
|
|
100
|
+
</span>
|
|
101
|
+
</button>
|
|
102
|
+
{{else}}
|
|
103
|
+
<button
|
|
104
|
+
type="button"
|
|
105
|
+
class="fleet-hierarchy-tree-row {{row.depthClass}} flex w-full min-w-0 items-start gap-2 border-b border-gray-100 px-3 py-1.5 text-left text-xs text-gray-600 hover:bg-gray-50 dark:border-gray-800 dark:text-gray-300 dark:hover:bg-gray-800"
|
|
106
|
+
data-test-fleet-hierarchy-row="vehicle"
|
|
107
|
+
{{on "click" (fn this.viewVehicle row.vehicle)}}
|
|
108
|
+
>
|
|
109
|
+
<span class="mt-1 h-1.5 w-1.5 shrink-0 rounded-full {{if row.vehicle.online 'bg-green-500' 'bg-yellow-300'}}" aria-hidden="true"></span>
|
|
110
|
+
<span class="min-w-0 flex-1">
|
|
111
|
+
<span class="block truncate font-medium leading-4" title={{this.displayName row.vehicle}}>{{this.displayName row.vehicle}}</span>
|
|
112
|
+
<span class="block truncate text-xs leading-4 text-gray-400">Vehicle - {{this.vehicleSubtitle row.vehicle}}</span>
|
|
113
|
+
</span>
|
|
114
|
+
</button>
|
|
115
|
+
{{/if}}
|
|
116
|
+
{{/each}}
|
|
117
|
+
{{else}}
|
|
118
|
+
<div class="flex flex-col items-center justify-center gap-2 p-6 text-center" data-test-fleet-hierarchy-empty>
|
|
119
|
+
<span class="flex h-8 w-8 items-center justify-center rounded bg-gray-100 text-gray-400 dark:bg-gray-800">
|
|
120
|
+
<FaIcon @icon={{if this.hasControlsApplied "magnifying-glass" "diagram-project"}} @size="sm" />
|
|
121
|
+
</span>
|
|
122
|
+
<div>
|
|
123
|
+
<div class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{this.emptyTitle}}</div>
|
|
124
|
+
<div class="mt-1 text-xs leading-5 text-gray-500 dark:text-gray-400">{{this.emptyDescription}}</div>
|
|
125
|
+
</div>
|
|
126
|
+
{{#if this.hasControlsApplied}}
|
|
127
|
+
<Button @type="default" @size="xs" @icon="times" @text="Clear filters" @onClick={{this.clearFilters}} data-test-fleet-hierarchy-clear-filters />
|
|
128
|
+
{{else}}
|
|
129
|
+
<div class="flex items-center gap-1">
|
|
130
|
+
<Button @type="default" @size="xs" @icon="user-plus" @text="Assign driver" @onClick={{fn this.assignDriver this.rootFleet}} />
|
|
131
|
+
<Button @type="default" @size="xs" @icon="car" @text="Assign vehicle" @onClick={{fn this.assignVehicle this.rootFleet}} />
|
|
132
|
+
</div>
|
|
133
|
+
{{/if}}
|
|
134
|
+
</div>
|
|
135
|
+
{{/if}}
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|