@fleetbase/fleetops-engine 0.6.54 → 0.6.56
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/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/details.js +1 -1
- 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/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/equipment/form.js +8 -1
- package/addon/components/layout/fleet-ops-sidebar.hbs +1 -1
- package/addon/components/layout/fleet-ops-sidebar.js +20 -6
- 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/telematic/details.hbs +1 -1
- package/addon/components/telematic/form.hbs +2 -2
- package/addon/components/telematic/settings.hbs +1 -1
- 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/events.js +1 -1
- package/addon/controllers/connectivity/devices/index/details/vehicle.js +50 -2
- package/addon/controllers/connectivity/devices/index.js +144 -129
- package/addon/controllers/connectivity/events/details.js +70 -0
- package/addon/controllers/connectivity/events/index.js +187 -11
- package/addon/controllers/connectivity/sensors/index.js +160 -14
- package/addon/controllers/connectivity/telematics/details/devices.js +126 -134
- package/addon/controllers/connectivity/telematics/details/events.js +1 -1
- package/addon/controllers/connectivity/telematics/details/sensors.js +4 -0
- 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/controllers/settings/map.js +15 -0
- package/addon/instance-initializers/register-vroom-allocation.js +1 -1
- 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 +28 -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/routes/management/vehicles/index/details.js +3 -1
- 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/map-adapter/google.js +25 -0
- package/addon/services/map-adapter/leaflet.js +1 -1
- package/addon/services/map-adapter-interface.js +1 -1
- package/addon/services/map-manager.js +4 -1
- package/addon/services/map-settings.js +15 -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 +9 -1
- package/addon/templates/connectivity/devices/index/details/vehicle.hbs +66 -2
- package/addon/templates/connectivity/events/details.hbs +40 -0
- package/addon/templates/management/vendors/index/details.hbs +2 -2
- package/addon/templates/settings/map.hbs +38 -9
- 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/leaflet-plugin-loader.js +5 -5
- package/addon/utils/map-drawer-dropdown-position.js +47 -0
- package/addon/utils/route-colors.js +1 -1
- 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/device-event/details.hbs +192 -0
- package/app/components/device-event/details.js +1 -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/migrations/2026_06_23_000001_delete_bad_afaqy_telematics_sensors.php +34 -0
- package/server/src/Console/Commands/SyncTelematics.php +4 -4
- 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 +36 -1
- package/server/src/Http/Controllers/Internal/v1/DeviceEventController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/EquipmentController.php +17 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +1 -1
- 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/ServiceRateController.php +82 -13
- 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/DeviceEventFilter.php +21 -0
- package/server/src/Http/Filter/DeviceFilter.php +50 -4
- 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/FuelTransaction.php +69 -0
- package/server/src/Http/Resources/v1/Index/Vehicle.php +19 -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/Device.php +2 -0
- package/server/src/Models/DeviceEvent.php +81 -0
- package/server/src/Models/Equipment.php +55 -0
- package/server/src/Models/Order.php +2 -1
- package/server/src/Observers/PurchaseRateObserver.php +2 -1
- 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 +105 -5
- 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 +567 -47
- package/server/src/Support/Telematics/Providers/SamsaraProvider.php +122 -14
- package/server/src/Support/Telematics/TelematicService.php +28 -9
- package/server/src/routes.php +67 -15
- package/server/tests/DeviceFilterTest.php +9 -0
- package/server/tests/DriverVehicleStatusDefaultTest.php +19 -2
- package/server/tests/EquipmentAssignmentMorphTypeTest.php +35 -0
- package/server/tests/ExportCoverageTest.php +79 -0
- package/server/tests/MetricsRegistryTest.php +45 -5
- package/server/tests/PublicFleetOpsRestEndpointsTest.php +191 -0
- package/server/tests/ServiceRateRouteValidationTest.php +101 -0
- package/server/tests/TelematicsHardeningTest.php +873 -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 +0 -1
|
@@ -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',
|
|
@@ -19,6 +19,12 @@ export default class SettingsMapController extends Controller {
|
|
|
19
19
|
* @var {Array}
|
|
20
20
|
*/
|
|
21
21
|
@tracked mapProviderOptions = MAP_PROVIDER_OPTIONS;
|
|
22
|
+
@tracked googleMapsMapTypeOptions = [
|
|
23
|
+
{ label: 'Roadmap', value: 'roadmap' },
|
|
24
|
+
{ label: 'Satellite', value: 'satellite' },
|
|
25
|
+
{ label: 'Hybrid', value: 'hybrid' },
|
|
26
|
+
{ label: 'Terrain', value: 'terrain' },
|
|
27
|
+
];
|
|
22
28
|
|
|
23
29
|
/**
|
|
24
30
|
* The currently-selected map provider key (e.g. 'leaflet' or 'google').
|
|
@@ -27,6 +33,9 @@ export default class SettingsMapController extends Controller {
|
|
|
27
33
|
* @var {String}
|
|
28
34
|
*/
|
|
29
35
|
@tracked mapProvider = 'leaflet';
|
|
36
|
+
@tracked googleMapsMapType = 'roadmap';
|
|
37
|
+
@tracked showGoogleMapsTrafficLayer = false;
|
|
38
|
+
@tracked showGoogleMapsTransitLayer = false;
|
|
30
39
|
|
|
31
40
|
/**
|
|
32
41
|
* Whether settings have been loaded from the server.
|
|
@@ -68,6 +77,9 @@ export default class SettingsMapController extends Controller {
|
|
|
68
77
|
@task *saveSettings() {
|
|
69
78
|
const settings = {
|
|
70
79
|
mapProvider: this.mapProvider,
|
|
80
|
+
googleMapsMapType: this.googleMapsMapType,
|
|
81
|
+
showGoogleMapsTrafficLayer: this.showGoogleMapsTrafficLayer,
|
|
82
|
+
showGoogleMapsTransitLayer: this.showGoogleMapsTransitLayer,
|
|
71
83
|
};
|
|
72
84
|
|
|
73
85
|
try {
|
|
@@ -89,6 +101,9 @@ export default class SettingsMapController extends Controller {
|
|
|
89
101
|
const response = yield this.mapSettings.load({ force: true });
|
|
90
102
|
|
|
91
103
|
this.mapProvider = response?.mapProvider ?? 'leaflet';
|
|
104
|
+
this.googleMapsMapType = response?.googleMapsMapType ?? 'roadmap';
|
|
105
|
+
this.showGoogleMapsTrafficLayer = Boolean(response?.showGoogleMapsTrafficLayer);
|
|
106
|
+
this.showGoogleMapsTransitLayer = Boolean(response?.showGoogleMapsTransitLayer);
|
|
92
107
|
this.settingsLoaded = true;
|
|
93
108
|
} catch (error) {
|
|
94
109
|
this.notifications.serverError(error);
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Third-party orchestration engines follow the same pattern:
|
|
11
11
|
* 1. Create a service extending OrchestrationEngineInterfaceService
|
|
12
12
|
* 2. Create an instance initializer that calls orchestrationEngine.register()
|
|
13
|
-
* 3. The engine will appear in the
|
|
13
|
+
* 3. The engine will appear in the Fleet-Ops orchestrator settings dropdown
|
|
14
14
|
*/
|
|
15
15
|
export function initialize(appInstance) {
|
|
16
16
|
const orchestrationEngine = appInstance.lookup('service:orchestration-engine');
|
|
@@ -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
|
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import Route from '@ember/routing/route';
|
|
2
|
+
import { action } from '@ember/object';
|
|
3
|
+
import { inject as service } from '@ember/service';
|
|
4
|
+
|
|
5
|
+
export default class ConnectivityEventsDetailsRoute extends Route {
|
|
6
|
+
@service abilities;
|
|
7
|
+
@service hostRouter;
|
|
8
|
+
@service intl;
|
|
9
|
+
@service notifications;
|
|
10
|
+
@service store;
|
|
11
|
+
|
|
12
|
+
@action error(error) {
|
|
13
|
+
this.notifications.serverError(error);
|
|
14
|
+
|
|
15
|
+
return this.hostRouter.transitionTo('console.fleet-ops.connectivity.events.index');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
beforeModel() {
|
|
19
|
+
if (this.abilities.cannot('fleet-ops view device-event')) {
|
|
20
|
+
this.notifications.warning(this.intl.t('common.unauthorized-access'));
|
|
21
|
+
return this.hostRouter.transitionTo('console.fleet-ops.connectivity.events.index');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
model({ public_id }) {
|
|
26
|
+
return this.store.findRecord('device-event', public_id);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -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 = {
|
|
@@ -108,6 +108,12 @@ export default class GoogleMapsAdapter extends MapAdapterInterface {
|
|
|
108
108
|
/** @type {google.maps.ImageMapType|null} */
|
|
109
109
|
_customTileLayer = null;
|
|
110
110
|
|
|
111
|
+
/** @type {google.maps.TrafficLayer|null} */
|
|
112
|
+
_trafficLayer = null;
|
|
113
|
+
|
|
114
|
+
/** @type {google.maps.TransitLayer|null} */
|
|
115
|
+
_transitLayer = null;
|
|
116
|
+
|
|
111
117
|
/** @type {Map<string, Function[]>} Normalized event name → array of [handler, gmListener] pairs */
|
|
112
118
|
_eventListeners = new Map();
|
|
113
119
|
|
|
@@ -176,6 +182,7 @@ export default class GoogleMapsAdapter extends MapAdapterInterface {
|
|
|
176
182
|
styles: mergedMapStyles,
|
|
177
183
|
});
|
|
178
184
|
|
|
185
|
+
this.#syncGoogleLayers(options);
|
|
179
186
|
await this.#initDrawingManager();
|
|
180
187
|
|
|
181
188
|
debug('[GoogleMapsAdapter] Map initialized');
|
|
@@ -231,6 +238,10 @@ export default class GoogleMapsAdapter extends MapAdapterInterface {
|
|
|
231
238
|
this._tooltipOverlayView?.setMap?.(null);
|
|
232
239
|
this._tooltipOverlayView = null;
|
|
233
240
|
this._supportsAdvancedMarkers = false;
|
|
241
|
+
this._trafficLayer?.setMap(null);
|
|
242
|
+
this._trafficLayer = null;
|
|
243
|
+
this._transitLayer?.setMap(null);
|
|
244
|
+
this._transitLayer = null;
|
|
234
245
|
|
|
235
246
|
this._map = null;
|
|
236
247
|
debug('[GoogleMapsAdapter] Map destroyed');
|
|
@@ -1184,6 +1195,20 @@ export default class GoogleMapsAdapter extends MapAdapterInterface {
|
|
|
1184
1195
|
}
|
|
1185
1196
|
}
|
|
1186
1197
|
|
|
1198
|
+
#syncGoogleLayers(options = {}) {
|
|
1199
|
+
if (!this._map) return;
|
|
1200
|
+
|
|
1201
|
+
if (options.showTrafficLayer) {
|
|
1202
|
+
this._trafficLayer = new google.maps.TrafficLayer();
|
|
1203
|
+
this._trafficLayer.setMap(this._map);
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
if (options.showTransitLayer) {
|
|
1207
|
+
this._transitLayer = new google.maps.TransitLayer();
|
|
1208
|
+
this._transitLayer.setMap(this._map);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1187
1212
|
#ensureDrawToolbar() {
|
|
1188
1213
|
if (this._drawControlEl || !this._map) return;
|
|
1189
1214
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Implements the MapAdapterInterface using the Leaflet library (via ember-leaflet).
|
|
5
5
|
* This adapter wraps all existing Leaflet-specific logic, preserving 100% of
|
|
6
|
-
* the original
|
|
6
|
+
* the original Fleet-Ops map functionality while conforming to the provider-agnostic
|
|
7
7
|
* interface.
|
|
8
8
|
*
|
|
9
9
|
* All Leaflet-specific plugins are supported:
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* MapAdapterInterface
|
|
3
3
|
*
|
|
4
4
|
* Abstract base class that defines the standardized contract all map provider
|
|
5
|
-
* adapters must implement. This enables
|
|
5
|
+
* adapters must implement. This enables Fleet-Ops to support multiple map
|
|
6
6
|
* providers (Leaflet, Google Maps, Mapbox, etc.) without changing any
|
|
7
7
|
* application-level business logic.
|
|
8
8
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MapManagerService
|
|
3
3
|
*
|
|
4
|
-
* Provider-agnostic central map service for
|
|
4
|
+
* Provider-agnostic central map service for Fleet-Ops. Replaces the
|
|
5
5
|
* Leaflet-specific `leaflet-map-manager` as the single point of contact
|
|
6
6
|
* for all map operations across the application.
|
|
7
7
|
*
|
|
@@ -139,6 +139,9 @@ export default class MapManagerService extends Service {
|
|
|
139
139
|
...options,
|
|
140
140
|
apiKey: options.apiKey ?? this.mapSettings.googleMapsApiKey,
|
|
141
141
|
mapId: options.mapId ?? this.mapSettings.googleMapsMapId,
|
|
142
|
+
mapTypeId: options.mapTypeId ?? this.mapSettings.googleMapsMapType,
|
|
143
|
+
showTrafficLayer: options.showTrafficLayer ?? this.mapSettings.showGoogleMapsTrafficLayer,
|
|
144
|
+
showTransitLayer: options.showTransitLayer ?? this.mapSettings.showGoogleMapsTransitLayer,
|
|
142
145
|
});
|
|
143
146
|
|
|
144
147
|
return Promise.resolve(map).then((nativeMap) => {
|
|
@@ -6,6 +6,9 @@ const DEFAULT_SETTINGS = {
|
|
|
6
6
|
mapProvider: 'leaflet',
|
|
7
7
|
googleMapsApiKey: '',
|
|
8
8
|
googleMapsMapId: '',
|
|
9
|
+
googleMapsMapType: 'roadmap',
|
|
10
|
+
showGoogleMapsTrafficLayer: false,
|
|
11
|
+
showGoogleMapsTransitLayer: false,
|
|
9
12
|
};
|
|
10
13
|
|
|
11
14
|
export default class MapSettingsService extends Service {
|
|
@@ -26,6 +29,18 @@ export default class MapSettingsService extends Service {
|
|
|
26
29
|
return this.settings.googleMapsMapId ?? '';
|
|
27
30
|
}
|
|
28
31
|
|
|
32
|
+
get googleMapsMapType() {
|
|
33
|
+
return this.settings.googleMapsMapType ?? 'roadmap';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get showGoogleMapsTrafficLayer() {
|
|
37
|
+
return Boolean(this.settings.showGoogleMapsTrafficLayer);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get showGoogleMapsTransitLayer() {
|
|
41
|
+
return Boolean(this.settings.showGoogleMapsTransitLayer);
|
|
42
|
+
}
|
|
43
|
+
|
|
29
44
|
get isGoogleMaps() {
|
|
30
45
|
return this.mapProvider === 'google';
|
|
31
46
|
}
|
|
@@ -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
|
|