@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.
- package/addon/components/device/details.hbs +106 -135
- package/addon/components/device/details.js +174 -1
- package/addon/components/device/panel-header.hbs +2 -2
- package/addon/components/layout/fleet-ops-sidebar/operations-monitor.hbs +201 -0
- package/addon/components/layout/fleet-ops-sidebar/operations-monitor.js +654 -0
- package/addon/components/layout/fleet-ops-sidebar.hbs +19 -233
- package/addon/components/layout/fleet-ops-sidebar.js +364 -395
- package/addon/components/maintenance-schedule/form.js +1 -0
- package/addon/components/order/form/details.hbs +1 -43
- package/addon/components/order/form/details.js +18 -53
- package/addon/components/order/form/orchestrator-constraints.hbs +40 -0
- package/addon/components/order/form/orchestrator-constraints.js +61 -0
- package/addon/components/order/form/payload.js +50 -2
- package/addon/components/order/form/route.js +15 -0
- package/addon/components/order/form/service-rate.hbs +62 -11
- package/addon/components/order/form/service-rate.js +124 -2
- package/addon/components/order/form.hbs +2 -0
- package/addon/components/telematic/details.hbs +7 -1
- package/addon/components/telematic/form.js +16 -8
- package/addon/components/work-order/details.hbs +5 -1
- package/addon/components/work-order/form.hbs +26 -4
- package/addon/components/work-order/form.js +5 -1
- package/addon/controllers/connectivity/devices/index/details/events.js +244 -1
- package/addon/controllers/connectivity/devices/index/details/sensors.js +199 -0
- package/addon/controllers/connectivity/devices/index/details/vehicle.js +74 -0
- package/addon/controllers/connectivity/devices/index/details.js +12 -0
- package/addon/controllers/connectivity/devices/index.js +4 -1
- package/addon/controllers/connectivity/telematics/details/attachments.js +220 -54
- package/addon/controllers/connectivity/telematics/details/devices.js +90 -41
- package/addon/controllers/connectivity/telematics/details/events.js +39 -11
- package/addon/controllers/connectivity/telematics/details/sensors.js +39 -19
- package/addon/controllers/maintenance/index.js +100 -0
- package/addon/controllers/maintenance/work-orders/index.js +18 -1
- package/addon/controllers/management/fuel-transactions/index.js +1 -1
- package/addon/controllers/management/index.js +88 -0
- package/addon/controllers/settings/index.js +142 -0
- package/addon/extension.js +21 -0
- package/addon/routes/analytics/index.js +3 -0
- package/addon/routes/connectivity/devices/index/details/events.js +37 -1
- package/addon/routes/connectivity/devices/index/details/sensors.js +35 -0
- package/addon/routes/connectivity/devices/index/details/vehicle.js +7 -0
- package/addon/routes/connectivity/devices/index.js +3 -0
- package/addon/routes/connectivity/telematics/details/attachments.js +72 -9
- package/addon/routes/connectivity/telematics/details/devices.js +4 -0
- package/addon/routes/connectivity/telematics/details/sensors.js +1 -0
- package/addon/routes/connectivity/telematics/details.js +1 -1
- package/addon/routes/connectivity/telematics/edit.js +1 -1
- package/addon/routes/maintenance/index.js +3 -0
- package/addon/routes/maintenance/work-orders/index.js +3 -0
- package/addon/routes/settings/index.js +3 -0
- package/addon/routes.js +2 -0
- package/addon/services/order-creation.js +54 -1
- package/addon/services/order-validation.js +3 -2
- package/addon/services/service-rate-actions.js +1 -0
- package/addon/styles/fleetops-engine.css +1821 -6
- package/addon/templates/analytics/index.hbs +14 -0
- package/addon/templates/analytics/reports/index.hbs +4 -4
- package/addon/templates/connectivity/devices/index/details/events.hbs +85 -1
- package/addon/templates/connectivity/devices/index/details/sensors.hbs +73 -0
- package/addon/templates/connectivity/devices/index/details/vehicle.hbs +84 -0
- package/addon/templates/connectivity/telematics/details/attachments.hbs +198 -121
- package/addon/templates/connectivity/telematics/details/devices.hbs +4 -1
- package/addon/templates/connectivity/telematics/details/events.hbs +4 -1
- package/addon/templates/connectivity/telematics/details/sensors.hbs +3 -0
- package/addon/templates/maintenance/index.hbs +113 -0
- package/addon/templates/management/fuel-transactions/index/details.hbs +1 -1
- package/addon/templates/management/index.hbs +109 -73
- package/addon/templates/management/vehicles/index/edit.hbs +1 -1
- package/addon/templates/operations/orders/index.hbs +1 -1
- package/addon/templates/operations/service-rates/index.hbs +1 -1
- package/addon/templates/settings/index.hbs +83 -0
- package/addon/templates/settings/routing.hbs +2 -2
- package/addon/utils/fleet-ops-options.js +38 -0
- package/app/components/order/form/orchestrator-constraints.js +1 -0
- package/app/controllers/connectivity/devices/index/details/sensors.js +1 -0
- package/app/controllers/connectivity/devices/index/details/vehicle.js +1 -0
- package/app/controllers/maintenance/index.js +1 -0
- package/app/controllers/management/index.js +1 -0
- package/app/controllers/settings/index.js +1 -0
- package/app/routes/analytics/index.js +1 -0
- package/app/routes/connectivity/devices/index/details/sensors.js +1 -0
- package/app/routes/connectivity/devices/index/details/vehicle.js +1 -0
- package/app/routes/maintenance/index.js +1 -0
- package/app/routes/settings/index.js +1 -0
- package/app/templates/analytics/index.js +1 -0
- package/app/templates/connectivity/devices/index/details/sensors.js +1 -0
- package/app/templates/connectivity/devices/index/details/vehicle.js +1 -0
- package/app/templates/maintenance/index.js +1 -0
- package/app/templates/management/index.js +1 -0
- package/app/templates/settings/index.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +3 -3
- package/server/migrations/2025_08_28_054930_create_work_orders_table.php +1 -0
- package/server/migrations/2026_06_15_000001_normalize_device_attachable_vehicle_morph_types.php +30 -0
- package/server/migrations/2026_06_18_000001_add_category_to_work_orders_table.php +32 -0
- package/server/seeders/Testing/MaintenanceSeeder.php +5 -4
- package/server/src/Console/Commands/FixInvalidPolymorphicRelationTypeNamespaces.php +1 -0
- package/server/src/Console/Commands/ProcessMaintenanceTriggers.php +1 -0
- package/server/src/Console/Commands/SyncTelematics.php +89 -0
- package/server/src/Exceptions/TelematicProviderException.php +13 -2
- package/server/src/Http/Controllers/Api/v1/OrderController.php +6 -21
- package/server/src/Http/Controllers/Internal/v1/DeviceController.php +112 -11
- package/server/src/Http/Controllers/Internal/v1/DeviceEventController.php +16 -5
- package/server/src/Http/Controllers/Internal/v1/FleetController.php +9 -0
- package/server/src/Http/Controllers/Internal/v1/HubController.php +300 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +177 -0
- package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +1 -0
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +4 -32
- package/server/src/Http/Controllers/Internal/v1/SearchController.php +506 -0
- package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +4 -4
- package/server/src/Http/Controllers/Internal/v1/TelematicController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/VehicleController.php +94 -10
- package/server/src/Http/Filter/DeviceEventFilter.php +44 -0
- package/server/src/Http/Filter/DeviceFilter.php +131 -0
- package/server/src/Http/Filter/DriverFilter.php +6 -0
- package/server/src/Http/Filter/SensorFilter.php +125 -0
- package/server/src/Http/Filter/VehicleFilter.php +19 -0
- package/server/src/Http/Requests/CreateDriverRequest.php +14 -13
- package/server/src/Http/Requests/Internal/CreateDriverRequest.php +1 -0
- package/server/src/Http/Resources/v1/Driver.php +11 -2
- package/server/src/Http/Resources/v1/WorkOrder.php +1 -0
- package/server/src/Jobs/FinalizeApiOrderCreation.php +47 -0
- package/server/src/Jobs/FinalizeInternalOrderCreation.php +35 -0
- package/server/src/Jobs/NotifyBulkAssignedDriver.php +47 -0
- package/server/src/Jobs/SyncTelematicDevicesJob.php +181 -66
- package/server/src/Models/Device.php +15 -1
- package/server/src/Models/DeviceEvent.php +1 -1
- package/server/src/Models/Driver.php +17 -1
- package/server/src/Models/Sensor.php +15 -2
- package/server/src/Models/Telematic.php +1 -1
- package/server/src/Models/Vehicle.php +5 -1
- package/server/src/Models/WorkOrder.php +5 -2
- package/server/src/Observers/DriverObserver.php +3 -3
- package/server/src/Observers/FleetObserver.php +23 -0
- package/server/src/Observers/VehicleObserver.php +3 -3
- package/server/src/Providers/FleetOpsServiceProvider.php +12 -0
- package/server/src/Support/LiveCacheService.php +1 -1
- package/server/src/Support/Telematics/Providers/AfaqyProvider.php +188 -28
- package/server/src/Support/Telematics/Providers/SafeeProvider.php +44 -3
- package/server/src/Support/Telematics/TelematicService.php +330 -30
- package/server/src/routes.php +10 -0
- package/server/tests/DeviceFilterTest.php +36 -0
- package/server/tests/HubRoutesTest.php +62 -0
- package/server/tests/OperationsMonitorEndpointTest.php +38 -0
- package/server/tests/OrderQueuedClosureRegressionTest.php +36 -0
- package/server/tests/TelematicsHardeningTest.php +455 -3
- package/server/tests/VehicleDriverAssignmentTest.php +27 -0
- package/translations/ar-ae.yml +1 -1
- package/translations/en-us.yaml +1 -0
- package/translations/vi-vn.yaml +1 -1
|
@@ -5,78 +5,136 @@ import { tracked } from '@glimmer/tracking';
|
|
|
5
5
|
|
|
6
6
|
export default class ConnectivityTelematicsDetailsAttachmentsController extends Controller {
|
|
7
7
|
@service deviceActions;
|
|
8
|
+
@service fetch;
|
|
8
9
|
@service hostRouter;
|
|
9
10
|
@service intl;
|
|
10
11
|
@service modalsManager;
|
|
11
12
|
@service notifications;
|
|
12
13
|
|
|
13
|
-
@tracked queryParams = ['query', 'status', '
|
|
14
|
+
@tracked queryParams = ['query', 'status', 'vehicle', 'sort'];
|
|
14
15
|
@tracked telematic;
|
|
15
16
|
@tracked query;
|
|
16
17
|
@tracked status;
|
|
17
|
-
@tracked attachment_state;
|
|
18
18
|
@tracked vehicle;
|
|
19
19
|
@tracked sort = '-updated_at';
|
|
20
|
+
@tracked selectedDevice = null;
|
|
21
|
+
@tracked selectedVehicle = null;
|
|
22
|
+
@tracked isRefreshing = false;
|
|
20
23
|
|
|
21
24
|
get syncedDevices() {
|
|
22
|
-
return Array.from(this.model ?? []);
|
|
25
|
+
return Array.from(this.model?.devices ?? this.model ?? []);
|
|
23
26
|
}
|
|
24
27
|
|
|
25
|
-
get
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
.toLowerCase();
|
|
29
|
-
|
|
30
|
-
return this.syncedDevices.filter((device) => {
|
|
31
|
-
const matchesQuery =
|
|
32
|
-
!query ||
|
|
33
|
-
[device.displayName, device.name, device.device_id, device.serial_number, device.imei, device.attached_to_name, device.status, device.connection_status]
|
|
34
|
-
.filter(Boolean)
|
|
35
|
-
.some((value) => String(value).toLowerCase().includes(query));
|
|
28
|
+
get loadError() {
|
|
29
|
+
return this.model?.error;
|
|
30
|
+
}
|
|
36
31
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const matchesVehicle = !this.vehicle || device.attachable_uuid === this.vehicle;
|
|
32
|
+
get totalSyncedDevices() {
|
|
33
|
+
return this.model?.meta?.total ?? this.syncedDevices.length;
|
|
34
|
+
}
|
|
41
35
|
|
|
42
|
-
|
|
43
|
-
|
|
36
|
+
get devices() {
|
|
37
|
+
return [...this.unattachedDevices, ...this.attachedDevices];
|
|
44
38
|
}
|
|
45
39
|
|
|
46
40
|
get unattachedDevices() {
|
|
47
|
-
return this.
|
|
41
|
+
return this.syncedDevices.filter((device) => !device.attachable_uuid && this.deviceMatchesQuery(device) && this.deviceMatchesStatus(device));
|
|
48
42
|
}
|
|
49
43
|
|
|
50
44
|
get attachedDevices() {
|
|
51
|
-
return this.
|
|
45
|
+
return this.syncedDevices.filter((device) => device.attachable_uuid && this.deviceMatchesQuery(device) && this.deviceMatchesStatus(device) && this.deviceMatchesVehicle(device));
|
|
52
46
|
}
|
|
53
47
|
|
|
54
48
|
get onlineDevicesCount() {
|
|
55
|
-
return this.
|
|
49
|
+
return this.syncedDevices.filter((device) => device.is_online || device.connection_status === 'online' || device.status === 'online' || device.status === 'active').length;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get attachedDevicesCount() {
|
|
53
|
+
return this.syncedDevices.filter((device) => device.attachable_uuid).length;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get unattachedDevicesCount() {
|
|
57
|
+
return this.syncedDevices.filter((device) => !device.attachable_uuid).length;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get mappedVehiclesCount() {
|
|
61
|
+
const vehicleIds = new Set(this.syncedDevices.filter((device) => device.attachable_uuid).map((device) => device.attachable_uuid));
|
|
62
|
+
|
|
63
|
+
return vehicleIds.size;
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
get vehicleGroups() {
|
|
59
67
|
const groups = new Map();
|
|
60
68
|
|
|
61
69
|
for (const device of this.attachedDevices) {
|
|
62
|
-
|
|
70
|
+
this.addDeviceToVehicleGroup(groups, device);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return this.sortVehicleGroups(groups);
|
|
74
|
+
}
|
|
63
75
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
id: key,
|
|
67
|
-
name: device.attached_to_name ?? 'Unknown vehicle',
|
|
68
|
-
devices: [],
|
|
69
|
-
});
|
|
70
|
-
}
|
|
76
|
+
addDeviceToVehicleGroup(groups, device) {
|
|
77
|
+
const key = device.attachable_uuid;
|
|
71
78
|
|
|
72
|
-
|
|
79
|
+
if (!groups.has(key)) {
|
|
80
|
+
const vehicle = device.attachable;
|
|
81
|
+
|
|
82
|
+
groups.set(key, {
|
|
83
|
+
id: key,
|
|
84
|
+
name: this.getDeviceVehicleName(device),
|
|
85
|
+
vehicle,
|
|
86
|
+
devices: [],
|
|
87
|
+
});
|
|
73
88
|
}
|
|
74
89
|
|
|
90
|
+
groups.get(key).devices.push(device);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
sortVehicleGroups(groups) {
|
|
75
94
|
return Array.from(groups.values()).sort((a, b) => String(a.name).localeCompare(String(b.name)));
|
|
76
95
|
}
|
|
77
96
|
|
|
97
|
+
get normalizedQuery() {
|
|
98
|
+
return String(this.query ?? '')
|
|
99
|
+
.trim()
|
|
100
|
+
.toLowerCase();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
deviceMatchesQuery(device) {
|
|
104
|
+
const query = this.normalizedQuery;
|
|
105
|
+
|
|
106
|
+
if (!query) {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return [
|
|
111
|
+
device.displayName,
|
|
112
|
+
device.name,
|
|
113
|
+
device.device_id,
|
|
114
|
+
device.serial_number,
|
|
115
|
+
device.imei,
|
|
116
|
+
device.public_id,
|
|
117
|
+
device.attached_to_name,
|
|
118
|
+
device.attachable?.displayName,
|
|
119
|
+
device.attachable?.display_name,
|
|
120
|
+
device.attachable?.name,
|
|
121
|
+
device.status,
|
|
122
|
+
device.connection_status,
|
|
123
|
+
]
|
|
124
|
+
.filter(Boolean)
|
|
125
|
+
.some((value) => String(value).toLowerCase().includes(query));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
deviceMatchesStatus(device) {
|
|
129
|
+
return !this.status || device.status === this.status || device.connection_status === this.status;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
deviceMatchesVehicle(device) {
|
|
133
|
+
return !this.vehicle || device.attachable_uuid === this.vehicle;
|
|
134
|
+
}
|
|
135
|
+
|
|
78
136
|
get hasActiveFilters() {
|
|
79
|
-
return Boolean(this.query || this.status || this.
|
|
137
|
+
return Boolean(this.query || this.status || this.vehicle);
|
|
80
138
|
}
|
|
81
139
|
|
|
82
140
|
get hasSyncedDevices() {
|
|
@@ -96,6 +154,10 @@ export default class ConnectivityTelematicsDetailsAttachmentsController extends
|
|
|
96
154
|
}
|
|
97
155
|
|
|
98
156
|
get emptyStateVariant() {
|
|
157
|
+
if (this.loadError) {
|
|
158
|
+
return 'error';
|
|
159
|
+
}
|
|
160
|
+
|
|
99
161
|
if (!this.hasSyncedDevices) {
|
|
100
162
|
return 'not_synced';
|
|
101
163
|
}
|
|
@@ -117,6 +179,19 @@ export default class ConnectivityTelematicsDetailsAttachmentsController extends
|
|
|
117
179
|
|
|
118
180
|
get emptyStateContent() {
|
|
119
181
|
switch (this.emptyStateVariant) {
|
|
182
|
+
case 'error':
|
|
183
|
+
return {
|
|
184
|
+
tone: 'danger',
|
|
185
|
+
icon: 'triangle-exclamation',
|
|
186
|
+
title: 'Unable to load device attachments',
|
|
187
|
+
message: 'Fleetbase could not load the full provider device list. Refresh the tab or review the connection logs.',
|
|
188
|
+
primaryText: 'Refresh',
|
|
189
|
+
primaryIcon: 'refresh',
|
|
190
|
+
primaryAction: this.refresh,
|
|
191
|
+
secondaryText: 'Go to Logs',
|
|
192
|
+
secondaryIcon: 'history',
|
|
193
|
+
secondaryAction: this.goToLogs,
|
|
194
|
+
};
|
|
120
195
|
case 'not_synced':
|
|
121
196
|
return {
|
|
122
197
|
tone: 'warning',
|
|
@@ -158,36 +233,96 @@ export default class ConnectivityTelematicsDetailsAttachmentsController extends
|
|
|
158
233
|
|
|
159
234
|
get metrics() {
|
|
160
235
|
return [
|
|
161
|
-
{ label: '
|
|
162
|
-
{ label: '
|
|
163
|
-
{ label: '
|
|
236
|
+
{ label: 'Synced devices', value: this.totalSyncedDevices, icon: 'microchip', accentClass: 'fleetops-connectivity-kpi-accent-blue' },
|
|
237
|
+
{ label: 'Vehicles mapped', value: this.mappedVehiclesCount, icon: 'truck', accentClass: 'fleetops-connectivity-kpi-accent-blue' },
|
|
238
|
+
{ label: 'Attached devices', value: this.attachedDevicesCount, icon: 'link', accentClass: 'fleetops-connectivity-kpi-accent-green' },
|
|
239
|
+
{ label: 'Unattached devices', value: this.unattachedDevicesCount, icon: 'link-slash', accentClass: 'fleetops-connectivity-kpi-accent-amber' },
|
|
164
240
|
{ label: 'Online devices', value: this.onlineDevicesCount, icon: 'signal', accentClass: 'fleetops-connectivity-kpi-accent-green' },
|
|
165
241
|
];
|
|
166
242
|
}
|
|
167
243
|
|
|
168
|
-
|
|
169
|
-
this.
|
|
244
|
+
get visibleDevicesCount() {
|
|
245
|
+
return this.devices.length;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
get filteredUnattachedCount() {
|
|
249
|
+
return this.unattachedDevices.length;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
get filteredAttachedCount() {
|
|
253
|
+
return this.attachedDevices.length;
|
|
170
254
|
}
|
|
171
255
|
|
|
172
|
-
|
|
173
|
-
this.
|
|
256
|
+
get selectedDeviceName() {
|
|
257
|
+
return this.selectedDevice?.displayName ?? this.selectedDevice?.name ?? this.selectedDevice?.device_id;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
@action updateQuery(event) {
|
|
261
|
+
this.query = event.target.value;
|
|
174
262
|
}
|
|
175
263
|
|
|
176
264
|
@action updateStatus(event) {
|
|
177
265
|
this.status = event.target.value || null;
|
|
178
266
|
}
|
|
179
267
|
|
|
268
|
+
@action updateVehicle(valueOrEvent) {
|
|
269
|
+
const value = valueOrEvent?.target ? valueOrEvent.target.value : valueOrEvent;
|
|
270
|
+
|
|
271
|
+
this.vehicle = value || null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
@action updateSelectedVehicle(vehicle) {
|
|
275
|
+
this.selectedVehicle = vehicle;
|
|
276
|
+
this.vehicle = vehicle?.id ?? null;
|
|
277
|
+
}
|
|
278
|
+
|
|
180
279
|
@action clearFilters() {
|
|
181
280
|
this.query = null;
|
|
182
281
|
this.status = null;
|
|
183
|
-
this.attachment_state = null;
|
|
184
282
|
this.vehicle = null;
|
|
283
|
+
this.selectedVehicle = null;
|
|
185
284
|
}
|
|
186
285
|
|
|
187
286
|
@action goToDevices() {
|
|
188
287
|
return this.hostRouter.transitionTo('console.fleet-ops.connectivity.telematics.details.devices', this.telematic);
|
|
189
288
|
}
|
|
190
289
|
|
|
290
|
+
@action goToLogs() {
|
|
291
|
+
return this.hostRouter.transitionTo('console.fleet-ops.connectivity.telematics.details.logs', this.telematic);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
@action async refresh() {
|
|
295
|
+
this.isRefreshing = true;
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
return await this.hostRouter.refresh();
|
|
299
|
+
} finally {
|
|
300
|
+
this.isRefreshing = false;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
@action selectUnattachedDevice(device) {
|
|
305
|
+
if (this.selectedDevice === device) {
|
|
306
|
+
this.selectedDevice = null;
|
|
307
|
+
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
this.selectedDevice = device;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
@action clearSelectedDevice() {
|
|
315
|
+
this.selectedDevice = null;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
@action attachSelectedDeviceToGroup(group) {
|
|
319
|
+
if (!this.selectedDevice) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return this.attachDeviceToVehicle(this.selectedDevice, group.vehicle ?? { id: group.id, name: group.name });
|
|
324
|
+
}
|
|
325
|
+
|
|
191
326
|
@action openAttachDeviceModal(device) {
|
|
192
327
|
this.modalsManager.show('modals/attach-telematic-device', {
|
|
193
328
|
title: this.intl.t('device.prompts.attach-device-to-vehicle-title', { deviceName: device.displayName ?? device.name ?? device.device_id ?? this.intl.t('resource.device') }),
|
|
@@ -200,20 +335,12 @@ export default class ConnectivityTelematicsDetailsAttachmentsController extends
|
|
|
200
335
|
return;
|
|
201
336
|
}
|
|
202
337
|
|
|
203
|
-
device.setProperties({
|
|
204
|
-
attachable_uuid: selectedVehicle.id,
|
|
205
|
-
attachable_type: 'fleet-ops:vehicle',
|
|
206
|
-
});
|
|
207
|
-
|
|
208
338
|
modal.startLoading();
|
|
209
339
|
|
|
210
340
|
try {
|
|
211
|
-
await
|
|
212
|
-
this.notifications.success(this.intl.t('device.prompts.attach-to-vehicle-success'));
|
|
213
|
-
await this.hostRouter.refresh();
|
|
341
|
+
await this.attachDeviceToVehicle(device, selectedVehicle);
|
|
214
342
|
modal.done();
|
|
215
343
|
} catch (error) {
|
|
216
|
-
device.rollbackAttributes();
|
|
217
344
|
this.notifications.serverError(error);
|
|
218
345
|
modal.stopLoading();
|
|
219
346
|
}
|
|
@@ -221,6 +348,14 @@ export default class ConnectivityTelematicsDetailsAttachmentsController extends
|
|
|
221
348
|
});
|
|
222
349
|
}
|
|
223
350
|
|
|
351
|
+
async attachDeviceToVehicle(device, selectedVehicle) {
|
|
352
|
+
const response = await this.fetch.post(`devices/${device.id}/attach`, { vehicle: selectedVehicle.id });
|
|
353
|
+
|
|
354
|
+
this.applyDeviceAttachment(device, selectedVehicle, response?.device);
|
|
355
|
+
this.selectedDevice = null;
|
|
356
|
+
this.notifications.success(this.intl.t('device.prompts.attach-to-vehicle-success'));
|
|
357
|
+
}
|
|
358
|
+
|
|
224
359
|
@action detachDevice(device) {
|
|
225
360
|
const deviceName = device.displayName ?? device.name ?? device.device_id ?? this.intl.t('resource.device');
|
|
226
361
|
|
|
@@ -229,19 +364,50 @@ export default class ConnectivityTelematicsDetailsAttachmentsController extends
|
|
|
229
364
|
body: this.intl.t('device.prompts.detach-telematic-device-body', { deviceName }),
|
|
230
365
|
confirm: async (modal) => {
|
|
231
366
|
modal.startLoading();
|
|
232
|
-
device.setProperties({ attachable_uuid: null, attachable_type: null });
|
|
233
367
|
|
|
234
368
|
try {
|
|
235
|
-
await device.
|
|
369
|
+
const response = await this.fetch.post(`devices/${device.id}/detach`);
|
|
370
|
+
|
|
371
|
+
this.applyDeviceDetachment(device, response?.device);
|
|
236
372
|
this.notifications.success(this.intl.t('device.prompts.detach-from-vehicle-success'));
|
|
237
|
-
await this.hostRouter.refresh();
|
|
238
373
|
modal.done();
|
|
239
374
|
} catch (error) {
|
|
240
|
-
device.rollbackAttributes();
|
|
241
375
|
this.notifications.serverError(error);
|
|
242
376
|
modal.stopLoading();
|
|
243
377
|
}
|
|
244
378
|
},
|
|
245
379
|
});
|
|
246
380
|
}
|
|
381
|
+
|
|
382
|
+
getDeviceVehicleName(device) {
|
|
383
|
+
return device.attached_to_name ?? device.attachable?.display_name ?? device.attachable?.name ?? 'Unknown vehicle';
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
applyDeviceAttachment(device, selectedVehicle, serverDevice = {}) {
|
|
387
|
+
this.updateDevice(device, {
|
|
388
|
+
attachable_uuid: serverDevice.attachable_uuid ?? selectedVehicle.id,
|
|
389
|
+
attachable_type: serverDevice.attachable_type ?? 'fleet-ops:vehicle',
|
|
390
|
+
attached_to_name: serverDevice.attached_to_name ?? selectedVehicle.displayName ?? selectedVehicle.display_name ?? selectedVehicle.name,
|
|
391
|
+
attachable: serverDevice.attachable ?? selectedVehicle,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
applyDeviceDetachment(device, serverDevice = {}) {
|
|
396
|
+
this.updateDevice(device, {
|
|
397
|
+
attachable_uuid: serverDevice.attachable_uuid ?? null,
|
|
398
|
+
attachable_type: serverDevice.attachable_type ?? null,
|
|
399
|
+
attached_to_name: serverDevice.attached_to_name ?? null,
|
|
400
|
+
attachable: serverDevice.attachable ?? null,
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
updateDevice(device, properties) {
|
|
405
|
+
if (typeof device.setProperties === 'function') {
|
|
406
|
+
device.setProperties(properties);
|
|
407
|
+
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
Object.assign(device, properties);
|
|
412
|
+
}
|
|
247
413
|
}
|
|
@@ -3,7 +3,14 @@ import { action } from '@ember/object';
|
|
|
3
3
|
import { inject as service } from '@ember/service';
|
|
4
4
|
import { tracked } from '@glimmer/tracking';
|
|
5
5
|
import { task } from 'ember-concurrency';
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
const connectionStatusOptions = [
|
|
8
|
+
{ label: 'Online', value: 'online' },
|
|
9
|
+
{ label: 'Recently Offline', value: 'recently_offline' },
|
|
10
|
+
{ label: 'Offline', value: 'offline' },
|
|
11
|
+
{ label: 'Long Offline', value: 'long_offline' },
|
|
12
|
+
{ label: 'Never Connected', value: 'never_connected' },
|
|
13
|
+
];
|
|
7
14
|
|
|
8
15
|
export default class ConnectivityTelematicsDetailsDevicesController extends Controller {
|
|
9
16
|
@service deviceActions;
|
|
@@ -13,7 +20,7 @@ export default class ConnectivityTelematicsDetailsDevicesController extends Cont
|
|
|
13
20
|
@service modalsManager;
|
|
14
21
|
@service notifications;
|
|
15
22
|
|
|
16
|
-
@tracked queryParams = ['page', 'limit', 'sort', 'query', 'status', 'provider', 'attachment_state', 'device_id'];
|
|
23
|
+
@tracked queryParams = ['page', 'limit', 'sort', 'query', 'status', 'provider', 'attachment_state', 'vehicle', 'connection_status', 'device_id', 'last_online_at', 'updated_at'];
|
|
17
24
|
@tracked telematic;
|
|
18
25
|
@tracked page = 1;
|
|
19
26
|
@tracked limit;
|
|
@@ -22,7 +29,11 @@ export default class ConnectivityTelematicsDetailsDevicesController extends Cont
|
|
|
22
29
|
@tracked status;
|
|
23
30
|
@tracked provider;
|
|
24
31
|
@tracked attachment_state;
|
|
32
|
+
@tracked vehicle;
|
|
33
|
+
@tracked connection_status;
|
|
25
34
|
@tracked device_id;
|
|
35
|
+
@tracked last_online_at;
|
|
36
|
+
@tracked updated_at;
|
|
26
37
|
|
|
27
38
|
get devices() {
|
|
28
39
|
return Array.from(this.model ?? []);
|
|
@@ -45,7 +56,9 @@ export default class ConnectivityTelematicsDetailsDevicesController extends Cont
|
|
|
45
56
|
}
|
|
46
57
|
|
|
47
58
|
get hasActiveFilters() {
|
|
48
|
-
return Boolean(
|
|
59
|
+
return Boolean(
|
|
60
|
+
this.query || this.status || this.provider || this.attachment_state || this.vehicle || this.connection_status || this.device_id || this.last_online_at || this.updated_at
|
|
61
|
+
);
|
|
49
62
|
}
|
|
50
63
|
|
|
51
64
|
get hasDevices() {
|
|
@@ -145,35 +158,39 @@ export default class ConnectivityTelematicsDetailsDevicesController extends Cont
|
|
|
145
158
|
];
|
|
146
159
|
}
|
|
147
160
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
161
|
+
get actionButtons() {
|
|
162
|
+
return [
|
|
163
|
+
{
|
|
164
|
+
icon: 'refresh',
|
|
165
|
+
size: 'sm',
|
|
166
|
+
onClick: this.refresh,
|
|
167
|
+
helpText: this.intl.t('common.refresh'),
|
|
168
|
+
wrapperClass: 'fleetops-telematics-action-button',
|
|
169
|
+
isLoading: this.refreshTask.isRunning,
|
|
170
|
+
disabled: this.refreshTask.isRunning,
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
icon: 'ellipsis-h',
|
|
174
|
+
prefix: 'fas',
|
|
175
|
+
helpText: 'Actions',
|
|
176
|
+
type: 'primary',
|
|
177
|
+
size: 'sm',
|
|
178
|
+
triggerClass: 'fleetops-telematics-action-button',
|
|
179
|
+
items: [
|
|
180
|
+
{
|
|
181
|
+
icon: 'satellite-dish',
|
|
182
|
+
text: 'Sync Devices',
|
|
183
|
+
onClick: this.startDeviceSync,
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
icon: 'link',
|
|
187
|
+
text: 'Attach Unassigned',
|
|
188
|
+
onClick: this.openAttachmentsForUnassigned,
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
];
|
|
193
|
+
}
|
|
177
194
|
|
|
178
195
|
@tracked bulkActions = [];
|
|
179
196
|
|
|
@@ -207,12 +224,11 @@ export default class ConnectivityTelematicsDetailsDevicesController extends Cont
|
|
|
207
224
|
resizable: true,
|
|
208
225
|
sortable: true,
|
|
209
226
|
filterable: true,
|
|
210
|
-
filterParam: '
|
|
211
|
-
filterComponent: 'filter/
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
],
|
|
227
|
+
filterParam: 'vehicle',
|
|
228
|
+
filterComponent: 'filter/model',
|
|
229
|
+
filterComponentPlaceholder: 'Select vehicle',
|
|
230
|
+
model: 'vehicle',
|
|
231
|
+
modelNamePath: 'displayName',
|
|
216
232
|
},
|
|
217
233
|
{
|
|
218
234
|
label: 'Connection',
|
|
@@ -221,9 +237,11 @@ export default class ConnectivityTelematicsDetailsDevicesController extends Cont
|
|
|
221
237
|
resizable: true,
|
|
222
238
|
sortable: true,
|
|
223
239
|
filterable: true,
|
|
224
|
-
filterParam: '
|
|
240
|
+
filterParam: 'connection_status',
|
|
225
241
|
filterComponent: 'filter/multi-option',
|
|
226
|
-
filterOptions:
|
|
242
|
+
filterOptions: connectionStatusOptions,
|
|
243
|
+
filterOptionLabel: 'label',
|
|
244
|
+
filterOptionValue: 'value',
|
|
227
245
|
},
|
|
228
246
|
{
|
|
229
247
|
label: 'Last Seen',
|
|
@@ -231,6 +249,25 @@ export default class ConnectivityTelematicsDetailsDevicesController extends Cont
|
|
|
231
249
|
sortParam: 'last_online_at',
|
|
232
250
|
resizable: true,
|
|
233
251
|
sortable: true,
|
|
252
|
+
filterable: true,
|
|
253
|
+
filterParam: 'last_online_at',
|
|
254
|
+
filterComponent: 'filter/date',
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
label: 'Attachment',
|
|
258
|
+
valuePath: 'attachable_uuid',
|
|
259
|
+
hidden: true,
|
|
260
|
+
resizable: true,
|
|
261
|
+
sortable: false,
|
|
262
|
+
filterable: true,
|
|
263
|
+
filterParam: 'attachment_state',
|
|
264
|
+
filterComponent: 'filter/select',
|
|
265
|
+
filterOptions: [
|
|
266
|
+
{ label: 'Attached', value: 'attached' },
|
|
267
|
+
{ label: 'Unattached', value: 'unattached' },
|
|
268
|
+
],
|
|
269
|
+
filterOptionLabel: 'label',
|
|
270
|
+
filterOptionValue: 'value',
|
|
234
271
|
},
|
|
235
272
|
{
|
|
236
273
|
label: this.intl.t('column.updated-at'),
|
|
@@ -284,7 +321,11 @@ export default class ConnectivityTelematicsDetailsDevicesController extends Cont
|
|
|
284
321
|
}
|
|
285
322
|
|
|
286
323
|
@action refresh() {
|
|
287
|
-
|
|
324
|
+
if (this.refreshTask.isRunning) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return this.refreshTask.perform();
|
|
288
329
|
}
|
|
289
330
|
|
|
290
331
|
@action clearFilters() {
|
|
@@ -292,7 +333,11 @@ export default class ConnectivityTelematicsDetailsDevicesController extends Cont
|
|
|
292
333
|
this.status = null;
|
|
293
334
|
this.provider = null;
|
|
294
335
|
this.attachment_state = null;
|
|
336
|
+
this.vehicle = null;
|
|
337
|
+
this.connection_status = null;
|
|
295
338
|
this.device_id = null;
|
|
339
|
+
this.last_online_at = null;
|
|
340
|
+
this.updated_at = null;
|
|
296
341
|
this.page = 1;
|
|
297
342
|
}
|
|
298
343
|
|
|
@@ -356,6 +401,10 @@ export default class ConnectivityTelematicsDetailsDevicesController extends Cont
|
|
|
356
401
|
return this.syncDevices.perform();
|
|
357
402
|
}
|
|
358
403
|
|
|
404
|
+
@task *refreshTask() {
|
|
405
|
+
yield this.hostRouter.refresh();
|
|
406
|
+
}
|
|
407
|
+
|
|
359
408
|
@task *syncDevices() {
|
|
360
409
|
try {
|
|
361
410
|
yield this.fetch.post(`telematics/${this.telematic.id}/discover`);
|