@fleetbase/fleetops-engine 0.6.54 → 0.6.55
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/routing-settings.hbs +2 -1
- package/addon/components/cell/attached-vehicle.hbs +10 -0
- package/addon/components/cell/attached-vehicle.js +58 -0
- package/addon/components/cell/device-identity.hbs +24 -0
- package/addon/components/cell/device-identity.js +129 -0
- package/addon/components/cell/driver-identity.hbs +33 -0
- package/addon/components/cell/driver-identity.js +143 -0
- package/addon/components/cell/equipment-identity.hbs +5 -0
- package/addon/components/cell/equipment-identity.js +48 -0
- package/addon/components/cell/part-identity.hbs +5 -0
- package/addon/components/cell/part-identity.js +63 -0
- package/addon/components/cell/telematic-device.hbs +38 -0
- package/addon/components/cell/telematic-device.js +44 -0
- package/addon/components/cell/telematic-provider.hbs +33 -16
- package/addon/components/cell/telematic-provider.js +50 -8
- package/addon/components/cell/vehicle-identity.hbs +33 -0
- package/addon/components/cell/vehicle-identity.js +138 -0
- package/addon/components/device/form.hbs +2 -2
- package/addon/components/device/form.js +7 -0
- package/addon/components/device/panel-header.hbs +45 -20
- package/addon/components/device/panel-header.js +49 -1
- package/addon/components/device/panel-tabs/events.hbs +30 -0
- package/addon/components/device/panel-tabs/events.js +128 -0
- package/addon/components/device/panel-tabs/sensors.hbs +30 -0
- package/addon/components/device/panel-tabs/sensors.js +93 -0
- package/addon/components/device/panel-tabs/vehicle.hbs +68 -0
- package/addon/components/device/panel-tabs/vehicle.js +90 -0
- package/addon/components/equipment/form.js +8 -1
- package/addon/components/layout/fleet-ops-sidebar.hbs +1 -1
- package/addon/components/layout/fleet-ops-sidebar.js +8 -0
- package/addon/components/map/drawer/device-event-listing.js +2 -0
- package/addon/components/map/drawer/driver-listing.js +2 -0
- package/addon/components/map/drawer/place-listing.js +2 -0
- package/addon/components/map/drawer/position-listing.hbs +11 -6
- package/addon/components/map/drawer/position-listing.js +17 -4
- package/addon/components/map/drawer/vehicle-listing.js +2 -0
- package/addon/components/sensor/panel-header.hbs +47 -15
- package/addon/components/sensor/panel-header.js +59 -1
- package/addon/components/table/cell/dropdown/action-item.hbs +31 -0
- package/addon/components/table/cell/dropdown/action-item.js +62 -0
- package/addon/components/table/cell/dropdown.hbs +28 -0
- package/addon/components/table/cell/dropdown.js +91 -0
- package/addon/components/vendor/panel-header.hbs +82 -0
- package/addon/components/vendor/panel-header.js +48 -0
- package/addon/controllers/connectivity/devices/index/details/vehicle.js +49 -1
- package/addon/controllers/connectivity/devices/index.js +144 -129
- package/addon/controllers/connectivity/events/details.js +3 -0
- package/addon/controllers/connectivity/events/index.js +187 -11
- package/addon/controllers/connectivity/sensors/index.js +156 -14
- package/addon/controllers/connectivity/telematics/details/devices.js +126 -134
- package/addon/controllers/maintenance/equipment/index.js +1 -2
- package/addon/controllers/maintenance/parts/index.js +1 -2
- package/addon/controllers/management/drivers/index.js +82 -42
- package/addon/controllers/management/fuel-reports/index.js +63 -4
- package/addon/controllers/management/issues/index.js +63 -4
- package/addon/controllers/management/vehicles/index.js +33 -6
- package/addon/routes/connectivity/devices/index/details/vehicle.js +24 -2
- package/addon/routes/connectivity/devices/index.js +7 -0
- package/addon/routes/connectivity/events/details.js +3 -0
- package/addon/routes/connectivity/events/index.js +6 -2
- package/addon/routes/connectivity/sensors/index.js +7 -2
- package/addon/routes/connectivity/telematics/details/devices.js +2 -0
- package/addon/services/device-actions.js +30 -0
- package/addon/services/device-event-actions.js +1 -1
- package/addon/services/equipment-actions.js +1 -0
- package/addon/services/sensor-actions.js +3 -0
- package/addon/services/service-rate-actions.js +39 -1
- package/addon/services/vehicle-actions.js +24 -12
- package/addon/services/vendor-actions.js +3 -0
- package/addon/styles/fleetops-engine.css +8 -0
- package/addon/templates/connectivity/devices/index/details/vehicle.hbs +64 -1
- package/addon/templates/management/vendors/index/details.hbs +2 -2
- package/addon/templates/settings/notifications.hbs +2 -1
- package/addon/templates/settings/routing.hbs +1 -0
- package/addon/utils/device-table-columns.js +288 -0
- package/addon/utils/identity-cell-resource.js +20 -0
- package/addon/utils/map-drawer-dropdown-position.js +47 -0
- package/addon/utils/trackable-option.js +93 -0
- package/app/components/cell/attached-vehicle.js +1 -0
- package/app/components/cell/device-identity.js +1 -0
- package/app/components/cell/driver-identity.js +1 -0
- package/app/components/cell/equipment-identity.js +1 -0
- package/app/components/cell/part-identity.js +1 -0
- package/app/components/cell/telematic-device.js +1 -0
- package/app/components/cell/vehicle-identity.js +1 -0
- package/app/components/device/panel-tabs/events.hbs +30 -0
- package/app/components/device/panel-tabs/events.js +128 -0
- package/app/components/device/panel-tabs/sensors.hbs +30 -0
- package/app/components/device/panel-tabs/sensors.js +93 -0
- package/app/components/device/panel-tabs/vehicle.hbs +68 -0
- package/app/components/device/panel-tabs/vehicle.js +90 -0
- package/app/components/table/cell/dropdown/action-item.js +1 -0
- package/app/components/table/cell/dropdown.js +1 -0
- package/app/components/vendor/panel-header.js +1 -0
- package/app/controllers/connectivity/events/{index/details.js → details.js} +1 -1
- package/app/routes/connectivity/events/{index/details.js → details.js} +1 -1
- package/app/templates/connectivity/events/{index/details.js → details.js} +1 -1
- package/app/utils/map-drawer-dropdown-position.js +1 -0
- package/app/utils/trackable-option.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +4 -4
- package/server/src/Console/Commands/SyncTelematics.php +4 -4
- package/server/src/Http/Controllers/Internal/v1/DeviceController.php +18 -1
- package/server/src/Http/Controllers/Internal/v1/DeviceEventController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/ServiceRateController.php +82 -13
- package/server/src/Http/Filter/DeviceEventFilter.php +21 -0
- package/server/src/Http/Filter/DeviceFilter.php +24 -1
- package/server/src/Http/Resources/v1/Index/Vehicle.php +19 -0
- package/server/src/Models/Device.php +2 -0
- package/server/src/Models/DeviceEvent.php +81 -0
- package/server/src/Models/Equipment.php +55 -0
- package/server/src/Support/Telematics/Providers/AfaqyProvider.php +1 -0
- package/server/src/Support/Telematics/Providers/FlespiProvider.php +89 -15
- package/server/src/Support/Telematics/Providers/GeotabProvider.php +123 -35
- package/server/src/Support/Telematics/Providers/SafeeProvider.php +26 -2
- package/server/src/Support/Telematics/Providers/SamsaraProvider.php +122 -14
- package/server/src/routes.php +0 -1
- package/server/tests/DeviceFilterTest.php +9 -0
- package/server/tests/DriverVehicleStatusDefaultTest.php +19 -2
- package/server/tests/EquipmentAssignmentMorphTypeTest.php +35 -0
- package/server/tests/ServiceRateRouteValidationTest.php +101 -0
- package/server/tests/TelematicsHardeningTest.php +238 -7
- package/addon/controllers/connectivity/events/index/details.js +0 -3
- package/addon/routes/connectivity/events/index/details.js +0 -3
- /package/addon/templates/connectivity/events/{index/details.hbs → details.hbs} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Controller from '@ember/controller';
|
|
2
2
|
import { inject as service } from '@ember/service';
|
|
3
3
|
import { tracked } from '@glimmer/tracking';
|
|
4
|
-
import { action } from '@ember/object';
|
|
4
|
+
import { action, get } from '@ember/object';
|
|
5
5
|
|
|
6
6
|
export default class ManagementDriversIndexController extends Controller {
|
|
7
7
|
@service driverActions;
|
|
@@ -52,6 +52,22 @@ export default class ManagementDriversIndexController extends Controller {
|
|
|
52
52
|
return Boolean(driver?.vehicle_uuid || driver?.vehicle?.id || driver?.vehicle?.uuid || driver?.vehicle_name);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
async resolveAssignedVehicle(vehicle) {
|
|
56
|
+
if (!vehicle) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (typeof vehicle.then === 'function') {
|
|
61
|
+
return await vehicle;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (typeof vehicle.loadResource === 'function') {
|
|
65
|
+
return (await vehicle.loadResource()) ?? vehicle;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return vehicle;
|
|
69
|
+
}
|
|
70
|
+
|
|
55
71
|
/** action buttons */
|
|
56
72
|
get actionButtons() {
|
|
57
73
|
return [
|
|
@@ -102,7 +118,8 @@ export default class ManagementDriversIndexController extends Controller {
|
|
|
102
118
|
sticky: true,
|
|
103
119
|
label: this.intl.t('column.name'),
|
|
104
120
|
valuePath: 'name',
|
|
105
|
-
cellComponent: '
|
|
121
|
+
cellComponent: 'cell/driver-identity',
|
|
122
|
+
compact: true,
|
|
106
123
|
permission: 'fleet-ops view driver',
|
|
107
124
|
action: this.driverActions.transition.view,
|
|
108
125
|
resizable: true,
|
|
@@ -120,6 +137,69 @@ export default class ManagementDriversIndexController extends Controller {
|
|
|
120
137
|
hidden: false,
|
|
121
138
|
filterComponent: 'filter/string',
|
|
122
139
|
},
|
|
140
|
+
{
|
|
141
|
+
label: this.intl.t('column.phone'),
|
|
142
|
+
valuePath: 'phone',
|
|
143
|
+
cellComponent: 'table/cell/base',
|
|
144
|
+
resizable: true,
|
|
145
|
+
sortable: true,
|
|
146
|
+
filterable: true,
|
|
147
|
+
filterParam: 'phone',
|
|
148
|
+
filterComponent: 'filter/string',
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
label: this.intl.t('column.license'),
|
|
152
|
+
valuePath: 'drivers_license_number',
|
|
153
|
+
cellComponent: 'table/cell/base',
|
|
154
|
+
resizable: true,
|
|
155
|
+
sortable: true,
|
|
156
|
+
filterable: true,
|
|
157
|
+
filterComponent: 'filter/string',
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
label: this.intl.t('column.vehicle'),
|
|
161
|
+
cellComponent: 'cell/vehicle-identity',
|
|
162
|
+
compact: true,
|
|
163
|
+
permission: 'fleet-ops view vehicle',
|
|
164
|
+
action: async (vehicle) => {
|
|
165
|
+
try {
|
|
166
|
+
const resolvedVehicle = await this.resolveAssignedVehicle(vehicle);
|
|
167
|
+
if (resolvedVehicle) this.vehicleActions.panel.view(resolvedVehicle);
|
|
168
|
+
} catch (err) {
|
|
169
|
+
this.notifications.serverError(err);
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
valuePath: 'vehicle.display_name',
|
|
173
|
+
emptyText: '-',
|
|
174
|
+
showStatusBadge: true,
|
|
175
|
+
resourcePath: (driver) => {
|
|
176
|
+
const vehicle = get(driver, 'vehicle_assigned') ?? get(driver, 'vehicle');
|
|
177
|
+
|
|
178
|
+
if (vehicle) {
|
|
179
|
+
return vehicle;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const vehicleName = get(driver, 'vehicle_name');
|
|
183
|
+
|
|
184
|
+
if (vehicleName) {
|
|
185
|
+
return {
|
|
186
|
+
display_name: vehicleName,
|
|
187
|
+
name: vehicleName,
|
|
188
|
+
status: 'assigned',
|
|
189
|
+
loadResource: () => driver.loadVehicle?.(),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return null;
|
|
194
|
+
},
|
|
195
|
+
modelNamePath: 'display_name',
|
|
196
|
+
resizable: true,
|
|
197
|
+
filterable: true,
|
|
198
|
+
filterComponent: 'filter/model',
|
|
199
|
+
filterComponentPlaceholder: 'Select vehicle to filter by',
|
|
200
|
+
filterParam: 'vehicle',
|
|
201
|
+
model: 'vehicle',
|
|
202
|
+
},
|
|
123
203
|
{
|
|
124
204
|
label: this.intl.t('column.internal-id'),
|
|
125
205
|
valuePath: 'internal_id',
|
|
@@ -150,27 +230,6 @@ export default class ManagementDriversIndexController extends Controller {
|
|
|
150
230
|
filterParam: 'vendor',
|
|
151
231
|
model: 'vendor',
|
|
152
232
|
},
|
|
153
|
-
{
|
|
154
|
-
label: this.intl.t('column.vehicle'),
|
|
155
|
-
cellComponent: 'table/cell/anchor',
|
|
156
|
-
permission: 'fleet-ops view vehicle',
|
|
157
|
-
onClick: async (driver) => {
|
|
158
|
-
try {
|
|
159
|
-
const vehicle = await driver.loadVehicle();
|
|
160
|
-
if (vehicle) this.vehicleActions.panel.view(vehicle);
|
|
161
|
-
} catch (err) {
|
|
162
|
-
this.notifications.serverError(err);
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
valuePath: 'vehicle.display_name',
|
|
166
|
-
modelNamePath: 'display_name',
|
|
167
|
-
resizable: true,
|
|
168
|
-
filterable: true,
|
|
169
|
-
filterComponent: 'filter/model',
|
|
170
|
-
filterComponentPlaceholder: 'Select vehicle to filter by',
|
|
171
|
-
filterParam: 'vehicle',
|
|
172
|
-
model: 'vehicle',
|
|
173
|
-
},
|
|
174
233
|
{
|
|
175
234
|
label: this.intl.t('column.fleet'),
|
|
176
235
|
cellComponent: 'table/cell/link-list',
|
|
@@ -185,25 +244,6 @@ export default class ManagementDriversIndexController extends Controller {
|
|
|
185
244
|
filterParam: 'fleet',
|
|
186
245
|
model: 'fleet',
|
|
187
246
|
},
|
|
188
|
-
{
|
|
189
|
-
label: this.intl.t('column.license'),
|
|
190
|
-
valuePath: 'drivers_license_number',
|
|
191
|
-
cellComponent: 'table/cell/base',
|
|
192
|
-
resizable: true,
|
|
193
|
-
sortable: true,
|
|
194
|
-
filterable: true,
|
|
195
|
-
filterComponent: 'filter/string',
|
|
196
|
-
},
|
|
197
|
-
{
|
|
198
|
-
label: this.intl.t('column.phone'),
|
|
199
|
-
valuePath: 'phone',
|
|
200
|
-
cellComponent: 'table/cell/base',
|
|
201
|
-
resizable: true,
|
|
202
|
-
sortable: true,
|
|
203
|
-
filterable: true,
|
|
204
|
-
filterParam: 'phone',
|
|
205
|
-
filterComponent: 'filter/string',
|
|
206
|
-
},
|
|
207
247
|
{
|
|
208
248
|
label: this.intl.t('column.country'),
|
|
209
249
|
valuePath: 'country',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Controller from '@ember/controller';
|
|
2
|
+
import { get } from '@ember/object';
|
|
2
3
|
import { inject as service } from '@ember/service';
|
|
3
4
|
import { tracked } from '@glimmer/tracking';
|
|
4
5
|
|
|
@@ -113,9 +114,37 @@ export default class ManagementFuelReportsIndexController extends Controller {
|
|
|
113
114
|
{
|
|
114
115
|
label: this.intl.t('column.driver'),
|
|
115
116
|
valuePath: 'driver_name',
|
|
116
|
-
cellComponent: '
|
|
117
|
+
cellComponent: 'cell/driver-identity',
|
|
117
118
|
permission: 'fleet-ops view driver',
|
|
118
|
-
|
|
119
|
+
action: async (driver) => {
|
|
120
|
+
const resolvedDriver = driver?.loadResource ? await driver.loadResource() : driver;
|
|
121
|
+
|
|
122
|
+
if (resolvedDriver) {
|
|
123
|
+
this.fuelReportActions.driverActions.panel.view(resolvedDriver);
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
emptyText: '-',
|
|
127
|
+
showStatusBadge: true,
|
|
128
|
+
resourcePath: (fuelReport) => {
|
|
129
|
+
const driver = get(fuelReport, 'driver');
|
|
130
|
+
|
|
131
|
+
if (driver) {
|
|
132
|
+
return driver;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const driverName = get(fuelReport, 'driver_name');
|
|
136
|
+
|
|
137
|
+
if (driverName) {
|
|
138
|
+
return {
|
|
139
|
+
id: get(fuelReport, 'driver_uuid'),
|
|
140
|
+
name: driverName,
|
|
141
|
+
display_name: driverName,
|
|
142
|
+
loadResource: () => fuelReport.loadDriver?.(),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return null;
|
|
147
|
+
},
|
|
119
148
|
resizable: true,
|
|
120
149
|
sortable: true,
|
|
121
150
|
filterable: true,
|
|
@@ -127,9 +156,39 @@ export default class ManagementFuelReportsIndexController extends Controller {
|
|
|
127
156
|
{
|
|
128
157
|
label: this.intl.t('column.vehicle'),
|
|
129
158
|
valuePath: 'vehicle_name',
|
|
130
|
-
cellComponent: '
|
|
159
|
+
cellComponent: 'cell/vehicle-identity',
|
|
131
160
|
permission: 'fleet-ops view vehicle',
|
|
132
|
-
|
|
161
|
+
action: async (vehicle) => {
|
|
162
|
+
const resolvedVehicle = vehicle?.loadResource ? await vehicle.loadResource() : vehicle;
|
|
163
|
+
|
|
164
|
+
if (resolvedVehicle) {
|
|
165
|
+
this.fuelReportActions.vehicleActions.panel.view(resolvedVehicle);
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
emptyText: '-',
|
|
169
|
+
showStatusBadge: true,
|
|
170
|
+
resourcePath: (fuelReport) => {
|
|
171
|
+
const vehicle = get(fuelReport, 'vehicle');
|
|
172
|
+
|
|
173
|
+
if (vehicle) {
|
|
174
|
+
return vehicle;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const vehicleName = get(fuelReport, 'vehicle_name');
|
|
178
|
+
|
|
179
|
+
if (vehicleName) {
|
|
180
|
+
return {
|
|
181
|
+
id: get(fuelReport, 'vehicle_uuid'),
|
|
182
|
+
displayName: vehicleName,
|
|
183
|
+
display_name: vehicleName,
|
|
184
|
+
name: vehicleName,
|
|
185
|
+
vehicle_number: get(fuelReport, 'vehicle_uuid'),
|
|
186
|
+
loadResource: () => fuelReport.loadVehicle?.(),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return null;
|
|
191
|
+
},
|
|
133
192
|
resizable: true,
|
|
134
193
|
sortable: true,
|
|
135
194
|
filterable: true,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Controller from '@ember/controller';
|
|
2
|
+
import { get } from '@ember/object';
|
|
2
3
|
import { inject as service } from '@ember/service';
|
|
3
4
|
import { tracked } from '@glimmer/tracking';
|
|
4
5
|
import fleetOpsOptions from '../../../utils/fleet-ops-options';
|
|
@@ -162,9 +163,37 @@ export default class ManagementIssuesIndexController extends Controller {
|
|
|
162
163
|
{
|
|
163
164
|
label: this.intl.t('column.driver'),
|
|
164
165
|
valuePath: 'driver_name',
|
|
165
|
-
cellComponent: '
|
|
166
|
+
cellComponent: 'cell/driver-identity',
|
|
166
167
|
permission: 'fleet-ops view driver',
|
|
167
|
-
|
|
168
|
+
action: async (driver) => {
|
|
169
|
+
const resolvedDriver = driver?.loadResource ? await driver.loadResource() : driver;
|
|
170
|
+
|
|
171
|
+
if (resolvedDriver) {
|
|
172
|
+
this.issueActions.driverActions.panel.view(resolvedDriver);
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
emptyText: '-',
|
|
176
|
+
showStatusBadge: true,
|
|
177
|
+
resourcePath: (issue) => {
|
|
178
|
+
const driver = get(issue, 'driver');
|
|
179
|
+
|
|
180
|
+
if (driver) {
|
|
181
|
+
return driver;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const driverName = get(issue, 'driver_name');
|
|
185
|
+
|
|
186
|
+
if (driverName) {
|
|
187
|
+
return {
|
|
188
|
+
id: get(issue, 'driver_uuid'),
|
|
189
|
+
name: driverName,
|
|
190
|
+
display_name: driverName,
|
|
191
|
+
loadResource: () => issue.loadDriver?.(),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return null;
|
|
196
|
+
},
|
|
168
197
|
resizable: true,
|
|
169
198
|
sortable: true,
|
|
170
199
|
filterable: true,
|
|
@@ -176,9 +205,39 @@ export default class ManagementIssuesIndexController extends Controller {
|
|
|
176
205
|
{
|
|
177
206
|
label: this.intl.t('column.vehicle'),
|
|
178
207
|
valuePath: 'vehicle_name',
|
|
179
|
-
cellComponent: '
|
|
208
|
+
cellComponent: 'cell/vehicle-identity',
|
|
180
209
|
permission: 'fleet-ops view vehicle',
|
|
181
|
-
|
|
210
|
+
action: async (vehicle) => {
|
|
211
|
+
const resolvedVehicle = vehicle?.loadResource ? await vehicle.loadResource() : vehicle;
|
|
212
|
+
|
|
213
|
+
if (resolvedVehicle) {
|
|
214
|
+
this.issueActions.vehicleActions.panel.view(resolvedVehicle);
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
emptyText: '-',
|
|
218
|
+
showStatusBadge: true,
|
|
219
|
+
resourcePath: (issue) => {
|
|
220
|
+
const vehicle = get(issue, 'vehicle');
|
|
221
|
+
|
|
222
|
+
if (vehicle) {
|
|
223
|
+
return vehicle;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const vehicleName = get(issue, 'vehicle_name');
|
|
227
|
+
|
|
228
|
+
if (vehicleName) {
|
|
229
|
+
return {
|
|
230
|
+
id: get(issue, 'vehicle_uuid'),
|
|
231
|
+
displayName: vehicleName,
|
|
232
|
+
display_name: vehicleName,
|
|
233
|
+
name: vehicleName,
|
|
234
|
+
vehicle_number: get(issue, 'vehicle_uuid'),
|
|
235
|
+
loadResource: () => issue.loadVehicle?.(),
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return null;
|
|
240
|
+
},
|
|
182
241
|
resizable: true,
|
|
183
242
|
sortable: true,
|
|
184
243
|
filterable: true,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Controller from '@ember/controller';
|
|
2
|
-
import { action } from '@ember/object';
|
|
2
|
+
import { action, get } from '@ember/object';
|
|
3
3
|
import { inject as service } from '@ember/service';
|
|
4
4
|
import { tracked } from '@glimmer/tracking';
|
|
5
5
|
|
|
@@ -126,7 +126,8 @@ export default class ManagementVehiclesIndexController extends Controller {
|
|
|
126
126
|
label: this.intl.t('column.name'),
|
|
127
127
|
valuePath: 'displayName',
|
|
128
128
|
photoPath: 'avatar_url',
|
|
129
|
-
cellComponent: '
|
|
129
|
+
cellComponent: 'cell/vehicle-identity',
|
|
130
|
+
compact: true,
|
|
130
131
|
permission: 'fleet-ops view vehicle',
|
|
131
132
|
action: this.vehicleActions.transition.view,
|
|
132
133
|
resizable: true,
|
|
@@ -135,6 +136,7 @@ export default class ManagementVehiclesIndexController extends Controller {
|
|
|
135
136
|
filterComponent: 'filter/string',
|
|
136
137
|
filterParam: 'name',
|
|
137
138
|
showOnlineIndicator: true,
|
|
139
|
+
showStatus: false,
|
|
138
140
|
},
|
|
139
141
|
{
|
|
140
142
|
label: this.intl.t('column.plate-number'),
|
|
@@ -160,14 +162,39 @@ export default class ManagementVehiclesIndexController extends Controller {
|
|
|
160
162
|
},
|
|
161
163
|
{
|
|
162
164
|
label: this.intl.t('column.driver-assigned'),
|
|
163
|
-
cellComponent: '
|
|
165
|
+
cellComponent: 'cell/driver-identity',
|
|
166
|
+
compact: true,
|
|
167
|
+
assignedVehicleLabel: (_driver, vehicle) => get(vehicle, 'displayName') ?? get(vehicle, 'display_name') ?? get(vehicle, 'name'),
|
|
164
168
|
permission: 'fleet-ops view driver',
|
|
165
|
-
action: async (
|
|
166
|
-
const
|
|
169
|
+
action: async (driver) => {
|
|
170
|
+
const resolvedDriver = get(driver, 'id') ? driver : await driver?.loadResource?.();
|
|
167
171
|
|
|
168
|
-
|
|
172
|
+
if (resolvedDriver) {
|
|
173
|
+
return this.driverActions.panel.view(resolvedDriver);
|
|
174
|
+
}
|
|
169
175
|
},
|
|
170
176
|
valuePath: 'driver_name',
|
|
177
|
+
emptyText: '-',
|
|
178
|
+
resourcePath: (vehicle) => {
|
|
179
|
+
const driver = get(vehicle, 'driver');
|
|
180
|
+
|
|
181
|
+
if (driver) {
|
|
182
|
+
return driver;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const driverName = get(vehicle, 'driver_name');
|
|
186
|
+
|
|
187
|
+
if (driverName) {
|
|
188
|
+
return {
|
|
189
|
+
name: driverName,
|
|
190
|
+
display_name: driverName,
|
|
191
|
+
status: 'assigned',
|
|
192
|
+
loadResource: () => vehicle.loadDriver?.(),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return null;
|
|
197
|
+
},
|
|
171
198
|
resizable: true,
|
|
172
199
|
filterable: true,
|
|
173
200
|
filterComponent: 'filter/model',
|
|
@@ -1,7 +1,29 @@
|
|
|
1
1
|
import Route from '@ember/routing/route';
|
|
2
|
+
import { inject as service } from '@ember/service';
|
|
2
3
|
|
|
3
4
|
export default class ConnectivityDevicesIndexDetailsVehicleRoute extends Route {
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
@service store;
|
|
6
|
+
|
|
7
|
+
async model() {
|
|
8
|
+
const device = this.modelFor('connectivity.devices.index.details');
|
|
9
|
+
const vehicle = await Promise.resolve(device?.attachable);
|
|
10
|
+
|
|
11
|
+
if (!vehicle?.id) {
|
|
12
|
+
return { device, positions: [] };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let positions = [];
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
positions = await this.store.query('position', {
|
|
19
|
+
subject_uuid: vehicle.id,
|
|
20
|
+
sort: '-created_at',
|
|
21
|
+
limit: 5,
|
|
22
|
+
});
|
|
23
|
+
} catch (_) {
|
|
24
|
+
positions = [];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return { device, positions };
|
|
6
28
|
}
|
|
7
29
|
}
|
|
@@ -14,6 +14,13 @@ export default class ConnectivityDevicesIndexRoute extends Route {
|
|
|
14
14
|
status: { refreshModel: true },
|
|
15
15
|
attachment_state: { refreshModel: true },
|
|
16
16
|
telematic: { refreshModel: true },
|
|
17
|
+
provider: { refreshModel: true },
|
|
18
|
+
vehicle: { refreshModel: true },
|
|
19
|
+
connection_status: { refreshModel: true },
|
|
20
|
+
device_id: { refreshModel: true },
|
|
21
|
+
type: { refreshModel: true },
|
|
22
|
+
serial_number: { refreshModel: true },
|
|
23
|
+
last_online_at: { refreshModel: true },
|
|
17
24
|
created_at: { refreshModel: true },
|
|
18
25
|
updated_at: { refreshModel: true },
|
|
19
26
|
};
|
|
@@ -9,8 +9,12 @@ export default class ConnectivityEventsIndexRoute extends Route {
|
|
|
9
9
|
limit: { refreshModel: true },
|
|
10
10
|
sort: { refreshModel: true },
|
|
11
11
|
query: { refreshModel: true },
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
telematic: { refreshModel: true },
|
|
13
|
+
device: { refreshModel: true },
|
|
14
|
+
event_type: { refreshModel: true },
|
|
15
|
+
severity: { refreshModel: true },
|
|
16
|
+
processed: { refreshModel: true },
|
|
17
|
+
occurred_at: { refreshModel: true },
|
|
14
18
|
created_at: { refreshModel: true },
|
|
15
19
|
updated_at: { refreshModel: true },
|
|
16
20
|
};
|
|
@@ -9,8 +9,13 @@ export default class ConnectivitySensorsIndexRoute extends Route {
|
|
|
9
9
|
limit: { refreshModel: true },
|
|
10
10
|
sort: { refreshModel: true },
|
|
11
11
|
query: { refreshModel: true },
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
telematic: { refreshModel: true },
|
|
13
|
+
device: { refreshModel: true },
|
|
14
|
+
type: { refreshModel: true },
|
|
15
|
+
status: { refreshModel: true },
|
|
16
|
+
serial_number: { refreshModel: true },
|
|
17
|
+
imei: { refreshModel: true },
|
|
18
|
+
last_reading_at: { refreshModel: true },
|
|
14
19
|
created_at: { refreshModel: true },
|
|
15
20
|
updated_at: { refreshModel: true },
|
|
16
21
|
};
|
|
@@ -15,6 +15,8 @@ export default class ConnectivityTelematicsDetailsDevicesRoute extends Route {
|
|
|
15
15
|
vehicle: { refreshModel: true },
|
|
16
16
|
connection_status: { refreshModel: true },
|
|
17
17
|
device_id: { refreshModel: true },
|
|
18
|
+
type: { refreshModel: true },
|
|
19
|
+
serial_number: { refreshModel: true },
|
|
18
20
|
last_online_at: { refreshModel: true },
|
|
19
21
|
updated_at: { refreshModel: true },
|
|
20
22
|
};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import ResourceActionService, { inject as service } from '@fleetbase/ember-core/services/resource-action';
|
|
2
2
|
import { action } from '@ember/object';
|
|
3
|
+
import { isArray } from '@ember/array';
|
|
3
4
|
|
|
4
5
|
export default class DeviceActionsService extends ResourceActionService {
|
|
5
6
|
@service fetch;
|
|
7
|
+
@service('universe/menu-service') menuService;
|
|
6
8
|
|
|
7
9
|
constructor() {
|
|
8
10
|
super(...arguments);
|
|
@@ -37,13 +39,41 @@ export default class DeviceActionsService extends ResourceActionService {
|
|
|
37
39
|
});
|
|
38
40
|
},
|
|
39
41
|
view: (device) => {
|
|
42
|
+
if (!device?.id) {
|
|
43
|
+
return this.notifications?.warning?.(this.intl.t('common.invalid-resource'));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const registeredTabs = this.menuService?.getMenuItems?.('fleet-ops:component:device:details');
|
|
47
|
+
|
|
40
48
|
return this.resourceContextPanel.open({
|
|
41
49
|
device,
|
|
50
|
+
header: 'device/panel-header',
|
|
42
51
|
tabs: [
|
|
43
52
|
{
|
|
53
|
+
key: 'overview',
|
|
54
|
+
id: 'overview',
|
|
44
55
|
label: this.intl.t('common.overview'),
|
|
45
56
|
component: 'device/details',
|
|
46
57
|
},
|
|
58
|
+
{
|
|
59
|
+
key: 'vehicle',
|
|
60
|
+
id: 'vehicle',
|
|
61
|
+
label: this.intl.t('resource.vehicle'),
|
|
62
|
+
component: 'device/panel-tabs/vehicle',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
key: 'sensors',
|
|
66
|
+
id: 'sensors',
|
|
67
|
+
label: this.intl.t('resource.sensors'),
|
|
68
|
+
component: 'device/panel-tabs/sensors',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
key: 'events',
|
|
72
|
+
id: 'events',
|
|
73
|
+
label: this.intl.t('resource.device-events'),
|
|
74
|
+
component: 'device/panel-tabs/events',
|
|
75
|
+
},
|
|
76
|
+
...(isArray(registeredTabs) ? registeredTabs.filter((tab) => tab.component || tab.render) : []),
|
|
47
77
|
],
|
|
48
78
|
});
|
|
49
79
|
},
|
|
@@ -11,7 +11,7 @@ export default class DeviceEventActionsService extends ResourceActionService {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
transition = {
|
|
14
|
-
view: (deviceEvent) => this.transitionTo('connectivity.events.
|
|
14
|
+
view: (deviceEvent) => this.transitionTo('connectivity.events.details', deviceEvent),
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
panel = {
|
|
@@ -36,8 +36,11 @@ export default class SensorActionsService extends ResourceActionService {
|
|
|
36
36
|
view: (sensor) => {
|
|
37
37
|
return this.resourceContextPanel.open({
|
|
38
38
|
sensor,
|
|
39
|
+
header: 'sensor/panel-header',
|
|
39
40
|
tabs: [
|
|
40
41
|
{
|
|
42
|
+
key: 'overview',
|
|
43
|
+
id: 'overview',
|
|
41
44
|
label: this.intl.t('common.overview'),
|
|
42
45
|
component: 'sensor/details',
|
|
43
46
|
},
|
|
@@ -96,8 +96,15 @@ export default class ServiceRateActionsService extends ResourceActionService {
|
|
|
96
96
|
};
|
|
97
97
|
|
|
98
98
|
@task *queryServiceRatesForOrder(order, params = {}) {
|
|
99
|
+
const payloadCoordinates = order.payload?.payloadCoordinates ?? [];
|
|
100
|
+
const routeCoordinates = payloadCoordinates.map((coordinate) => this.#serializeRouteCoordinate(coordinate)).filter(Boolean);
|
|
101
|
+
|
|
102
|
+
if (routeCoordinates.length < 2) {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
|
|
99
106
|
const queryParams = {
|
|
100
|
-
coordinates:
|
|
107
|
+
coordinates: routeCoordinates.join(';'),
|
|
101
108
|
facilitator: order.facilitator?.get('isIntegratedVendor') ? order.facilitator.get('public_id') : null,
|
|
102
109
|
service_type: order.order_config?.get('key'),
|
|
103
110
|
...params,
|
|
@@ -117,6 +124,37 @@ export default class ServiceRateActionsService extends ResourceActionService {
|
|
|
117
124
|
}
|
|
118
125
|
}
|
|
119
126
|
|
|
127
|
+
#serializeRouteCoordinate(coordinate) {
|
|
128
|
+
if (Array.isArray(coordinate)) {
|
|
129
|
+
const [longitude, latitude] = coordinate;
|
|
130
|
+
|
|
131
|
+
if (!this.#isValidCoordinate(latitude) || !this.#isValidCoordinate(longitude)) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return `${latitude},${longitude}`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (typeof coordinate === 'string') {
|
|
139
|
+
const parts = coordinate
|
|
140
|
+
.split(',')
|
|
141
|
+
.map((part) => part.trim())
|
|
142
|
+
.filter(Boolean);
|
|
143
|
+
|
|
144
|
+
if (parts.length !== 2 || !parts.every((part) => this.#isValidCoordinate(part))) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return parts.join(',');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
#isValidCoordinate(value) {
|
|
155
|
+
return value !== null && value !== undefined && value !== '' && Number.isFinite(Number(value));
|
|
156
|
+
}
|
|
157
|
+
|
|
120
158
|
@task *getServiceQuotes(serviceRate, order) {
|
|
121
159
|
if (order.payload.payloadCoordinates.length < 2) return;
|
|
122
160
|
|