@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
|
@@ -79,7 +79,7 @@ function waitForLeafletGlobal({ timeoutMs = 8000 } = {}) {
|
|
|
79
79
|
|
|
80
80
|
if (timeoutMs != null && Date.now() - startedAt >= timeoutMs) {
|
|
81
81
|
clearInterval(interval);
|
|
82
|
-
reject(new Error('[
|
|
82
|
+
reject(new Error('[Fleet-Ops Leaflet] Leaflet global is not available'));
|
|
83
83
|
}
|
|
84
84
|
}, 50);
|
|
85
85
|
});
|
|
@@ -105,7 +105,7 @@ function appendStylesheet(href) {
|
|
|
105
105
|
|
|
106
106
|
function loadScript(src, { timeoutMs = 8000, isReady = null } = {}) {
|
|
107
107
|
if (typeof document === 'undefined') {
|
|
108
|
-
return Promise.reject(new Error('[
|
|
108
|
+
return Promise.reject(new Error('[Fleet-Ops Leaflet] document is not available'));
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
const existing = findAssetElement('script', 'src', src);
|
|
@@ -137,7 +137,7 @@ function loadScript(src, { timeoutMs = 8000, isReady = null } = {}) {
|
|
|
137
137
|
|
|
138
138
|
const onError = () => {
|
|
139
139
|
cleanup();
|
|
140
|
-
reject(new Error(`[
|
|
140
|
+
reject(new Error(`[Fleet-Ops Leaflet] Failed to load ${src}`));
|
|
141
141
|
};
|
|
142
142
|
|
|
143
143
|
script.addEventListener('load', onLoad);
|
|
@@ -146,7 +146,7 @@ function loadScript(src, { timeoutMs = 8000, isReady = null } = {}) {
|
|
|
146
146
|
if (timeoutMs != null) {
|
|
147
147
|
timeoutId = setTimeout(() => {
|
|
148
148
|
cleanup();
|
|
149
|
-
reject(new Error(`[
|
|
149
|
+
reject(new Error(`[Fleet-Ops Leaflet] Timed out loading ${src}`));
|
|
150
150
|
}, timeoutMs);
|
|
151
151
|
}
|
|
152
152
|
|
|
@@ -202,7 +202,7 @@ export default function ensureLeafletPluginsReady(options = {}) {
|
|
|
202
202
|
.then(() => {
|
|
203
203
|
const readyLeaflet = normalizeLeafletGlobal();
|
|
204
204
|
if (!hasLeafletPluginsReady()) {
|
|
205
|
-
throw new Error('[
|
|
205
|
+
throw new Error('[Fleet-Ops Leaflet] Leaflet plugins loaded but required Draw/contextmenu APIs are missing');
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
window.fleetopsLeafletPluginsLoaded = true;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export default function calculateMapDrawerDropdownPosition(trigger, content) {
|
|
2
|
+
const dropdownRoot = trigger?.closest?.('.ember-basic-dropdown');
|
|
3
|
+
const drawerPanel = trigger?.closest?.('.next-drawer-panel');
|
|
4
|
+
|
|
5
|
+
if (!trigger || !dropdownRoot) {
|
|
6
|
+
return { style: {} };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const triggerRect = trigger.getBoundingClientRect();
|
|
10
|
+
const rootRect = dropdownRoot.getBoundingClientRect();
|
|
11
|
+
const drawerRect = drawerPanel?.getBoundingClientRect?.();
|
|
12
|
+
const contentRect = content?.getBoundingClientRect?.();
|
|
13
|
+
const contentWidth = contentRect?.width || 224;
|
|
14
|
+
// const contentHeight = contentRect?.height || 240;
|
|
15
|
+
const gap = 6;
|
|
16
|
+
|
|
17
|
+
const viewportRect = {
|
|
18
|
+
top: 0,
|
|
19
|
+
right: window.innerWidth,
|
|
20
|
+
bottom: window.innerHeight,
|
|
21
|
+
left: 0,
|
|
22
|
+
};
|
|
23
|
+
const boundaryRect = drawerRect ?? viewportRect;
|
|
24
|
+
const minLeft = boundaryRect.left - rootRect.left + gap;
|
|
25
|
+
const maxLeft = boundaryRect.right - rootRect.left - contentWidth - gap;
|
|
26
|
+
// const minTop = boundaryRect.top - rootRect.top + gap;
|
|
27
|
+
// const maxTop = boundaryRect.bottom - rootRect.top - contentHeight - gap;
|
|
28
|
+
|
|
29
|
+
const preferredLeft = triggerRect.left - rootRect.left - contentWidth - gap;
|
|
30
|
+
// const preferredTop = triggerRect.top - rootRect.top;
|
|
31
|
+
const left = clamp(preferredLeft, minLeft, Math.max(minLeft, maxLeft));
|
|
32
|
+
const top = 0; //clamp(preferredTop, minTop, Math.max(minTop, maxTop));
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
style: {
|
|
36
|
+
position: 'absolute',
|
|
37
|
+
left: `${left}px`,
|
|
38
|
+
top: `${top}px`,
|
|
39
|
+
marginTop: '0px',
|
|
40
|
+
zIndex: '10000',
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function clamp(value, min, max) {
|
|
46
|
+
return Math.min(Math.max(value, min), max);
|
|
47
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import getModelName from '@fleetbase/ember-core/utils/get-model-name';
|
|
2
|
+
import { isArray } from '@ember/array';
|
|
3
|
+
import { get } from '@ember/object';
|
|
4
|
+
|
|
5
|
+
export function buildTrackableOption(resource, modelName = getTrackableModelName(resource)) {
|
|
6
|
+
const deviceLabel = trackableDeviceLabel(resource, modelName);
|
|
7
|
+
const secondaryLabel = trackableSecondaryLabel(resource, modelName);
|
|
8
|
+
const primaryLabel = getValue(resource, 'name') ?? getValue(resource, 'displayName') ?? getValue(resource, 'display_name') ?? getValue(resource, 'public_id');
|
|
9
|
+
const trackableSearchText = [
|
|
10
|
+
primaryLabel,
|
|
11
|
+
secondaryLabel,
|
|
12
|
+
getValue(resource, 'public_id'),
|
|
13
|
+
getValue(resource, 'internal_id'),
|
|
14
|
+
getValue(resource, 'serial_number'),
|
|
15
|
+
getValue(resource, 'plate_number'),
|
|
16
|
+
getValue(resource, 'email'),
|
|
17
|
+
getValue(resource, 'vin'),
|
|
18
|
+
getValue(resource, 'call_sign'),
|
|
19
|
+
deviceLabel,
|
|
20
|
+
]
|
|
21
|
+
.filter(Boolean)
|
|
22
|
+
.join(' ');
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
resource,
|
|
26
|
+
modelName,
|
|
27
|
+
primaryLabel,
|
|
28
|
+
secondaryLabel,
|
|
29
|
+
deviceLabel,
|
|
30
|
+
trackableSearchText,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function trackableSecondaryLabel(resource, modelName = getTrackableModelName(resource)) {
|
|
35
|
+
if (modelName === 'driver') {
|
|
36
|
+
return getValue(resource, 'email') ?? getValue(resource, 'phone') ?? getValue(resource, 'vehicle_name') ?? getValue(resource, 'internal_id') ?? getValue(resource, 'public_id');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return getValue(resource, 'plate_number') ?? getValue(resource, 'serial_number') ?? getValue(resource, 'internal_id') ?? getValue(resource, 'vin') ?? getValue(resource, 'public_id');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function trackableDeviceLabel(resource, modelName = getTrackableModelName(resource)) {
|
|
43
|
+
const devices = trackableDevices(resource, modelName);
|
|
44
|
+
const labels = devices.map(deviceLabel).filter(Boolean);
|
|
45
|
+
|
|
46
|
+
if (labels.length === 0) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return labels.length === 1 ? `Device: ${labels[0]}` : `Devices: ${labels.join(', ')}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function trackableDevices(resource, modelName = getTrackableModelName(resource)) {
|
|
54
|
+
const devices = modelName === 'driver' ? getValue(resource, 'vehicle.devices') : getValue(resource, 'devices');
|
|
55
|
+
|
|
56
|
+
if (!devices) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (isArray(devices)) {
|
|
61
|
+
return devices;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (typeof devices.toArray === 'function') {
|
|
65
|
+
return devices.toArray();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function deviceLabel(device) {
|
|
72
|
+
return (
|
|
73
|
+
getValue(device, 'displayName') ??
|
|
74
|
+
getValue(device, 'display_name') ??
|
|
75
|
+
getValue(device, 'name') ??
|
|
76
|
+
getValue(device, 'device_id') ??
|
|
77
|
+
getValue(device, 'serial_number') ??
|
|
78
|
+
getValue(device, 'imei') ??
|
|
79
|
+
getValue(device, 'public_id')
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function getTrackableModelName(resource) {
|
|
84
|
+
return getModelName(resource) ?? getValue(resource, 'modelName') ?? resource?.constructor?.modelName;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function getValue(object, path) {
|
|
88
|
+
if (!object) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return get(object, path);
|
|
93
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/cell/attached-vehicle';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/cell/device-identity';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/cell/driver-identity';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/cell/equipment-identity';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/cell/part-identity';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/cell/telematic-device';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/cell/vehicle-identity';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<div class="flex flex-col gap-3 p-3" ...attributes>
|
|
2
|
+
<div class="flex items-start justify-between gap-3">
|
|
3
|
+
<div class="min-w-0">
|
|
4
|
+
<h2 class="text-base font-bold text-gray-900 dark:text-gray-100">Telemetry Events</h2>
|
|
5
|
+
<p class="mt-0.5 text-xs text-gray-500 dark:text-gray-300">Recent device events, warning signals, and processing state.</p>
|
|
6
|
+
</div>
|
|
7
|
+
<Button @icon="refresh" @size="xs" @onClick={{this.refreshEvents}} @isLoading={{this.loadEvents.isRunning}} />
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<Layout::Resource::Tabular
|
|
11
|
+
@resource="device-event"
|
|
12
|
+
@title="Telemetry Events"
|
|
13
|
+
@data={{this.events}}
|
|
14
|
+
@columns={{this.columns}}
|
|
15
|
+
@controller={{this}}
|
|
16
|
+
@pagination={{false}}
|
|
17
|
+
@withoutHeader={{true}}
|
|
18
|
+
@tableWrapperClass="no-table-extra-spacing"
|
|
19
|
+
@emptyStateComponent={{component
|
|
20
|
+
"table/empty-state"
|
|
21
|
+
variant="compact"
|
|
22
|
+
icon="bolt"
|
|
23
|
+
title="No telemetry events"
|
|
24
|
+
description="Events appear after this device reports provider activity."
|
|
25
|
+
primaryText="Refresh"
|
|
26
|
+
primaryIcon="refresh"
|
|
27
|
+
primaryAction=this.refreshEvents
|
|
28
|
+
}}
|
|
29
|
+
/>
|
|
30
|
+
</div>
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { action } from '@ember/object';
|
|
3
|
+
import { inject as service } from '@ember/service';
|
|
4
|
+
import { tracked } from '@glimmer/tracking';
|
|
5
|
+
import { task } from 'ember-concurrency';
|
|
6
|
+
|
|
7
|
+
const severityOptions = [
|
|
8
|
+
{ label: 'Info', value: 'info' },
|
|
9
|
+
{ label: 'Warning', value: 'warning' },
|
|
10
|
+
{ label: 'Error', value: 'error' },
|
|
11
|
+
{ label: 'Critical', value: 'critical' },
|
|
12
|
+
{ label: 'High', value: 'high' },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
function toRecentList(records) {
|
|
16
|
+
const list = Array.from(records ?? []);
|
|
17
|
+
|
|
18
|
+
list.meta = {
|
|
19
|
+
current_page: 1,
|
|
20
|
+
last_page: 1,
|
|
21
|
+
per_page: list.length,
|
|
22
|
+
total: list.length,
|
|
23
|
+
from: list.length > 0 ? 1 : 0,
|
|
24
|
+
to: list.length,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return list;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default class DevicePanelTabsEventsComponent extends Component {
|
|
31
|
+
@service deviceEventActions;
|
|
32
|
+
@service store;
|
|
33
|
+
|
|
34
|
+
@tracked events = toRecentList();
|
|
35
|
+
|
|
36
|
+
constructor() {
|
|
37
|
+
super(...arguments);
|
|
38
|
+
this.loadEvents.perform();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
get device() {
|
|
42
|
+
return this.args.resource ?? this.args.model;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
get columns() {
|
|
46
|
+
return [
|
|
47
|
+
{
|
|
48
|
+
sticky: true,
|
|
49
|
+
label: 'Event',
|
|
50
|
+
valuePath: 'event_type',
|
|
51
|
+
cellComponent: 'table/cell/anchor',
|
|
52
|
+
action: this.deviceEventActions.panel?.view ?? this.deviceEventActions.transition.view,
|
|
53
|
+
permission: 'fleet-ops view device-event',
|
|
54
|
+
resizable: true,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
label: 'Severity',
|
|
58
|
+
valuePath: 'severity',
|
|
59
|
+
cellComponent: 'table/cell/status',
|
|
60
|
+
filterOptions: severityOptions,
|
|
61
|
+
resizable: true,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
label: 'Message',
|
|
65
|
+
valuePath: 'message',
|
|
66
|
+
resizable: true,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
label: 'Code',
|
|
70
|
+
valuePath: 'code',
|
|
71
|
+
resizable: true,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
label: 'Processed',
|
|
75
|
+
valuePath: 'processedAt',
|
|
76
|
+
sortParam: 'processed_at',
|
|
77
|
+
resizable: true,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
label: 'Occurred',
|
|
81
|
+
valuePath: 'occurredAt',
|
|
82
|
+
sortParam: 'occurred_at',
|
|
83
|
+
resizable: true,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
label: '',
|
|
87
|
+
cellComponent: 'table/cell/dropdown',
|
|
88
|
+
ddButtonText: false,
|
|
89
|
+
ddButtonIcon: 'ellipsis-h',
|
|
90
|
+
ddButtonIconPrefix: 'fas',
|
|
91
|
+
wrapperClass: 'flex items-center justify-end mx-2',
|
|
92
|
+
sticky: 'right',
|
|
93
|
+
width: 60,
|
|
94
|
+
actions: [
|
|
95
|
+
{
|
|
96
|
+
label: 'View event',
|
|
97
|
+
fn: this.deviceEventActions.panel?.view ?? this.deviceEventActions.transition.view,
|
|
98
|
+
permission: 'fleet-ops view device-event',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
label: 'Mark processed',
|
|
102
|
+
fn: this.markProcessed,
|
|
103
|
+
permission: 'fleet-ops update device-event',
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@action async markProcessed(deviceEvent) {
|
|
111
|
+
await this.deviceEventActions.markProcessed(deviceEvent);
|
|
112
|
+
await this.loadEvents.perform();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@action refreshEvents() {
|
|
116
|
+
return this.loadEvents.perform();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@task *loadEvents() {
|
|
120
|
+
if (!this.device?.id) {
|
|
121
|
+
this.events = toRecentList();
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const events = yield this.store.query('device-event', { device_uuid: this.device.id, limit: 10, sort: '-created_at' });
|
|
126
|
+
this.events = toRecentList(events);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<div class="flex flex-col gap-3 p-3" ...attributes>
|
|
2
|
+
<div class="flex items-start justify-between gap-3">
|
|
3
|
+
<div class="min-w-0">
|
|
4
|
+
<h2 class="text-base font-bold text-gray-900 dark:text-gray-100">Sensor Inventory</h2>
|
|
5
|
+
<p class="mt-0.5 text-xs text-gray-500 dark:text-gray-300">Recent sensor readings and health for this device.</p>
|
|
6
|
+
</div>
|
|
7
|
+
<Button @icon="refresh" @size="xs" @onClick={{this.refreshSensors}} @isLoading={{this.loadSensors.isRunning}} />
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<Layout::Resource::Tabular
|
|
11
|
+
@resource="sensor"
|
|
12
|
+
@title="Device Sensors"
|
|
13
|
+
@data={{this.sensors}}
|
|
14
|
+
@columns={{this.columns}}
|
|
15
|
+
@controller={{this}}
|
|
16
|
+
@pagination={{false}}
|
|
17
|
+
@withoutHeader={{true}}
|
|
18
|
+
@tableWrapperClass="no-table-extra-spacing"
|
|
19
|
+
@emptyStateComponent={{component
|
|
20
|
+
"table/empty-state"
|
|
21
|
+
variant="compact"
|
|
22
|
+
icon="gauge-high"
|
|
23
|
+
title="No sensors connected"
|
|
24
|
+
description="Sensors appear here after provider telemetry links them to this device."
|
|
25
|
+
primaryText="Refresh"
|
|
26
|
+
primaryIcon="refresh"
|
|
27
|
+
primaryAction=this.refreshSensors
|
|
28
|
+
}}
|
|
29
|
+
/>
|
|
30
|
+
</div>
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { action } from '@ember/object';
|
|
3
|
+
import { inject as service } from '@ember/service';
|
|
4
|
+
import { tracked } from '@glimmer/tracking';
|
|
5
|
+
import { task } from 'ember-concurrency';
|
|
6
|
+
|
|
7
|
+
function toRecentList(records) {
|
|
8
|
+
const list = Array.from(records ?? []);
|
|
9
|
+
|
|
10
|
+
list.meta = {
|
|
11
|
+
current_page: 1,
|
|
12
|
+
last_page: 1,
|
|
13
|
+
per_page: list.length,
|
|
14
|
+
total: list.length,
|
|
15
|
+
from: list.length > 0 ? 1 : 0,
|
|
16
|
+
to: list.length,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
return list;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default class DevicePanelTabsSensorsComponent extends Component {
|
|
23
|
+
@service sensorActions;
|
|
24
|
+
@service store;
|
|
25
|
+
|
|
26
|
+
@tracked sensors = toRecentList();
|
|
27
|
+
|
|
28
|
+
constructor() {
|
|
29
|
+
super(...arguments);
|
|
30
|
+
this.loadSensors.perform();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get device() {
|
|
34
|
+
return this.args.resource ?? this.args.model;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get columns() {
|
|
38
|
+
return [
|
|
39
|
+
{
|
|
40
|
+
sticky: true,
|
|
41
|
+
label: 'Sensor',
|
|
42
|
+
valuePath: 'name',
|
|
43
|
+
cellComponent: 'table/cell/anchor',
|
|
44
|
+
action: this.sensorActions.panel?.view ?? this.sensorActions.transition.view,
|
|
45
|
+
permission: 'fleet-ops view sensor',
|
|
46
|
+
resizable: true,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
label: 'Type',
|
|
50
|
+
valuePath: 'type',
|
|
51
|
+
cellComponent: 'table/cell/base',
|
|
52
|
+
humanize: true,
|
|
53
|
+
resizable: true,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
label: 'Value',
|
|
57
|
+
valuePath: 'last_value',
|
|
58
|
+
resizable: true,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
label: 'Unit',
|
|
62
|
+
valuePath: 'unit',
|
|
63
|
+
resizable: true,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
label: 'Status',
|
|
67
|
+
valuePath: 'status',
|
|
68
|
+
cellComponent: 'table/cell/status',
|
|
69
|
+
resizable: true,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
label: 'Last Reading',
|
|
73
|
+
valuePath: 'lastReadingAt',
|
|
74
|
+
sortParam: 'last_reading_at',
|
|
75
|
+
resizable: true,
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@action refreshSensors() {
|
|
81
|
+
return this.loadSensors.perform();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@task *loadSensors() {
|
|
85
|
+
if (!this.device?.id) {
|
|
86
|
+
this.sensors = toRecentList();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const sensors = yield this.store.query('sensor', { device_uuid: this.device.id, limit: 10, sort: '-updated_at' });
|
|
91
|
+
this.sensors = toRecentList(sensors);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<div class="flex flex-col gap-3 p-3" ...attributes>
|
|
2
|
+
<div class="flex items-start justify-between gap-3">
|
|
3
|
+
<div class="min-w-0">
|
|
4
|
+
<h2 class="text-base font-bold text-gray-900 dark:text-gray-100">Vehicle Attachment</h2>
|
|
5
|
+
<p class="mt-0.5 text-xs text-gray-500 dark:text-gray-300">Fleet context for telemetry from this device.</p>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="flex flex-wrap items-center justify-end gap-2">
|
|
8
|
+
{{#if this.hasVehicle}}
|
|
9
|
+
{{#if this.canOpenVehicle}}
|
|
10
|
+
<Button @icon="eye" @text="Open Vehicle" @size="xs" @onClick={{this.openVehicle}} />
|
|
11
|
+
{{/if}}
|
|
12
|
+
{{#if this.canLocateVehicle}}
|
|
13
|
+
<Button @icon="location-dot" @text="Locate" @size="xs" @onClick={{this.locateVehicle}} />
|
|
14
|
+
{{/if}}
|
|
15
|
+
<Button @icon="shuffle" @text="Change Vehicle" @size="xs" @onClick={{this.attachToVehicle}} />
|
|
16
|
+
<Button @icon="unlink" @text="Detach" @size="xs" @type="danger" @onClick={{this.detachFromVehicle}} />
|
|
17
|
+
{{else}}
|
|
18
|
+
<Button @icon="link" @text="Attach Vehicle" @size="xs" @onClick={{this.attachToVehicle}} />
|
|
19
|
+
{{/if}}
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
{{#if this.hasVehicle}}
|
|
24
|
+
<section class="overflow-hidden rounded-md border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
25
|
+
<div class="flex gap-3 p-3">
|
|
26
|
+
<Image
|
|
27
|
+
src={{this.vehiclePhotoUrl}}
|
|
28
|
+
@fallbackSrc={{config "defaultValues.vehicleAvatar"}}
|
|
29
|
+
alt={{this.vehicleName}}
|
|
30
|
+
class="h-16 w-16 flex-shrink-0 rounded-md object-cover shadow-sm"
|
|
31
|
+
/>
|
|
32
|
+
<div class="min-w-0 flex-1">
|
|
33
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
34
|
+
<h3 class="truncate text-base font-bold text-gray-900 dark:text-gray-100">{{this.vehicleName}}</h3>
|
|
35
|
+
{{#if this.vehicleStatus}}
|
|
36
|
+
<Badge @status={{this.vehicleStatus}} @size="xs">{{smart-humanize this.vehicleStatus}}</Badge>
|
|
37
|
+
{{/if}}
|
|
38
|
+
</div>
|
|
39
|
+
<div class="mt-1 text-xs text-gray-500 dark:text-gray-300">{{n-a this.vehicleSubtitle}}</div>
|
|
40
|
+
<div class="mt-3 grid grid-cols-1 gap-2 text-xs sm:grid-cols-3">
|
|
41
|
+
<div>
|
|
42
|
+
<div class="font-semibold uppercase text-gray-400 dark:text-gray-500">Driver</div>
|
|
43
|
+
<div class="truncate text-gray-800 dark:text-gray-100">{{n-a this.vehicleDriverName}}</div>
|
|
44
|
+
</div>
|
|
45
|
+
<div>
|
|
46
|
+
<div class="font-semibold uppercase text-gray-400 dark:text-gray-500">Attached Device</div>
|
|
47
|
+
<div class="truncate text-gray-800 dark:text-gray-100">{{n-a this.device.displayName this.device.name}}</div>
|
|
48
|
+
</div>
|
|
49
|
+
<div>
|
|
50
|
+
<div class="font-semibold uppercase text-gray-400 dark:text-gray-500">Device Last Seen</div>
|
|
51
|
+
<div class="truncate text-gray-800 dark:text-gray-100">{{n-a (format-date-fns this.device.last_online_at "dd MMM yyyy, HH:mm")}}</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</section>
|
|
57
|
+
{{else}}
|
|
58
|
+
<Telematic::TabEmptyState
|
|
59
|
+
@tone="warning"
|
|
60
|
+
@icon="link-slash"
|
|
61
|
+
@title="No vehicle attached"
|
|
62
|
+
@message="Attach this device to a vehicle so telemetry has fleet context."
|
|
63
|
+
@primaryText="Attach Vehicle"
|
|
64
|
+
@primaryIcon="link"
|
|
65
|
+
@primaryAction={{this.attachToVehicle}}
|
|
66
|
+
/>
|
|
67
|
+
{{/if}}
|
|
68
|
+
</div>
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { action } from '@ember/object';
|
|
3
|
+
import { inject as service } from '@ember/service';
|
|
4
|
+
|
|
5
|
+
export default class DevicePanelTabsVehicleComponent extends Component {
|
|
6
|
+
@service deviceActions;
|
|
7
|
+
@service hostRouter;
|
|
8
|
+
@service mapManager;
|
|
9
|
+
@service vehicleActions;
|
|
10
|
+
|
|
11
|
+
get device() {
|
|
12
|
+
return this.args.resource ?? this.args.model;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
get vehicle() {
|
|
16
|
+
return this.device?.attachable;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get vehicleName() {
|
|
20
|
+
return this.device?.attached_to_name ?? this.vehicle?.displayName ?? this.vehicle?.display_name ?? this.vehicle?.name;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get vehicleSubtitle() {
|
|
24
|
+
return this.vehicle?.plate_number ?? this.vehicle?.call_sign ?? this.vehicle?.vin ?? this.vehicle?.public_id ?? this.device?.attachable_uuid;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get vehiclePhotoUrl() {
|
|
28
|
+
return this.vehicle?.photo_url ?? this.vehicle?.avatar_url;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get vehicleStatus() {
|
|
32
|
+
return this.vehicle?.status ?? (this.vehicle?.online ? 'online' : null);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get vehicleDriverName() {
|
|
36
|
+
return this.vehicle?.driver?.displayName ?? this.vehicle?.driver?.display_name ?? this.vehicle?.driver?.name ?? this.vehicle?.driver_name;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get hasVehicle() {
|
|
40
|
+
return Boolean(this.vehicleName || this.device?.attachable_uuid);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get canOpenVehicle() {
|
|
44
|
+
return Boolean(this.vehicle?.id);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get canLocateVehicle() {
|
|
48
|
+
return Boolean(this.vehicle?.id && (this.vehicle?.location || this.vehicle?.last_position));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@action attachToVehicle() {
|
|
52
|
+
return this.deviceActions.attachToVehicle(this.device, { callback: () => this.device?.reload?.() });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@action detachFromVehicle() {
|
|
56
|
+
return this.deviceActions.detachFromVehicle(this.device, { callback: () => this.device?.reload?.() });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@action openVehicle() {
|
|
60
|
+
if (this.vehicle?.id) {
|
|
61
|
+
return this.vehicleActions.panel?.view
|
|
62
|
+
? this.vehicleActions.panel.view(this.vehicle)
|
|
63
|
+
: this.hostRouter.transitionTo('console.fleet-ops.management.vehicles.index.details', this.vehicle);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@action async locateVehicle() {
|
|
68
|
+
if (!this.vehicle?.id) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
await this.transitionToLiveMap();
|
|
73
|
+
await this.mapManager.waitForMap({ timeoutMs: 8000 });
|
|
74
|
+
|
|
75
|
+
this.mapManager.focusResource(this.vehicle, 16, {
|
|
76
|
+
paddingBottomRight: [300, 200],
|
|
77
|
+
moveend: () => {
|
|
78
|
+
this.vehicleActions.panel?.view?.(this.vehicle, { closeOnTransition: true });
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async transitionToLiveMap() {
|
|
84
|
+
try {
|
|
85
|
+
await this.hostRouter.transitionTo('console.fleet-ops.operations.orders.index', { queryParams: { layout: 'map' } });
|
|
86
|
+
} catch (_) {
|
|
87
|
+
// Keep locate usable if another transition is already active.
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|