@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
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
<div class="flex flex-col gap-3 p-3" ...attributes>
|
|
2
|
+
<section class="overflow-hidden rounded-md border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
3
|
+
<div class="flex flex-col gap-3 p-3 lg:flex-row lg:items-start lg:justify-between">
|
|
4
|
+
<div class="min-w-0">
|
|
5
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
6
|
+
<h2 class="truncate text-base font-bold text-gray-900 dark:text-gray-100">{{smart-humanize this.eventType}}</h2>
|
|
7
|
+
<Badge @status={{this.severity}} @hideStatusDot={{true}}>{{smart-humanize this.severity}}</Badge>
|
|
8
|
+
<Badge @status={{this.processedStatus}} @hideStatusDot={{true}}>{{this.processedLabel}}</Badge>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<p class="mt-2 max-w-4xl text-sm leading-5 text-gray-700 dark:text-gray-200">{{n-a this.message}}</p>
|
|
12
|
+
|
|
13
|
+
<div class="mt-2 flex flex-wrap gap-2 text-xs text-gray-500 dark:text-gray-300">
|
|
14
|
+
<span>Occurred: {{n-a (format-date-fns this.occurredAt "dd MMM yyyy, HH:mm")}}</span>
|
|
15
|
+
<span>Processed: {{n-a (format-date-fns this.processedAt "dd MMM yyyy, HH:mm")}}</span>
|
|
16
|
+
<span>Public ID: {{n-a @resource.public_id}}</span>
|
|
17
|
+
<span>UUID: {{n-a @resource.id}}</span>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</section>
|
|
22
|
+
|
|
23
|
+
<section class="grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-4">
|
|
24
|
+
{{#each this.metrics as |metric|}}
|
|
25
|
+
<div class="fleet-ops-widget fleet-ops-kpi-tile fleetops-connectivity-kpi-tile {{metric.accentClass}} relative min-h-[82px] overflow-hidden rounded-md border bg-white shadow-sm dark:bg-gray-800">
|
|
26
|
+
<div class="flex h-full items-start justify-between gap-2 p-3">
|
|
27
|
+
<div class="min-w-0">
|
|
28
|
+
<div class="truncate text-[10px] font-bold uppercase text-gray-500 dark:text-gray-400">{{metric.label}}</div>
|
|
29
|
+
<div class="mt-1 truncate text-lg font-extrabold leading-none text-gray-900 dark:text-gray-50">{{n-a metric.value}}</div>
|
|
30
|
+
{{#if metric.meta}}
|
|
31
|
+
<div class="mt-1 truncate text-xs text-gray-500 dark:text-gray-300">{{metric.meta}}</div>
|
|
32
|
+
{{/if}}
|
|
33
|
+
</div>
|
|
34
|
+
<div class="fleetops-connectivity-kpi-icon flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-md text-[13px]">
|
|
35
|
+
<FaIcon @icon={{metric.icon}} />
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
{{/each}}
|
|
40
|
+
</section>
|
|
41
|
+
|
|
42
|
+
<section class="grid grid-cols-1 gap-3 xl:grid-cols-2">
|
|
43
|
+
<div class="overflow-hidden rounded-md border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
44
|
+
<div class="border-b border-gray-200 px-3 py-2 dark:border-gray-700">
|
|
45
|
+
<h3 class="text-sm font-bold text-gray-900 dark:text-gray-100">Event Details</h3>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="grid grid-cols-1 gap-2 p-3 sm:grid-cols-2 lg:grid-cols-3">
|
|
48
|
+
<div class="field-info-container">
|
|
49
|
+
<div class="field-name">Event Type</div>
|
|
50
|
+
<div class="field-value">{{n-a (smart-humanize this.eventType)}}</div>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="field-info-container">
|
|
53
|
+
<div class="field-name">Severity</div>
|
|
54
|
+
<div class="field-value"><Badge @status={{this.severity}}>{{smart-humanize this.severity}}</Badge></div>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="field-info-container">
|
|
57
|
+
<div class="field-name">State</div>
|
|
58
|
+
<div class="field-value">{{n-a (smart-humanize @resource.state)}}</div>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="field-info-container">
|
|
61
|
+
<div class="field-name">Code</div>
|
|
62
|
+
<div class="field-value">{{n-a @resource.code}}</div>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="field-info-container">
|
|
65
|
+
<div class="field-name">Reason</div>
|
|
66
|
+
<div class="field-value">{{n-a @resource.reason}}</div>
|
|
67
|
+
</div>
|
|
68
|
+
<div class="field-info-container">
|
|
69
|
+
<div class="field-name">Mileage</div>
|
|
70
|
+
<div class="field-value">{{n-a @resource.mileage}}</div>
|
|
71
|
+
</div>
|
|
72
|
+
<div class="field-info-container">
|
|
73
|
+
<div class="field-name">Age</div>
|
|
74
|
+
<div class="field-value">{{#if this.ageMinutes}}{{this.ageMinutes}} minutes{{else}}{{n-a null}}{{/if}}</div>
|
|
75
|
+
</div>
|
|
76
|
+
<div class="field-info-container">
|
|
77
|
+
<div class="field-name">Processing Delay</div>
|
|
78
|
+
<div class="field-value">{{#if this.processingDelay}}{{this.processingDelay}} minutes{{else}}{{n-a null}}{{/if}}</div>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="field-info-container sm:col-span-2 lg:col-span-3">
|
|
81
|
+
<div class="field-name">Comment</div>
|
|
82
|
+
<div class="field-value whitespace-pre-line">{{n-a @resource.comment}}</div>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<div class="overflow-hidden rounded-md border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
88
|
+
<div class="border-b border-gray-200 px-3 py-2 dark:border-gray-700">
|
|
89
|
+
<h3 class="text-sm font-bold text-gray-900 dark:text-gray-100">Device & Provider</h3>
|
|
90
|
+
</div>
|
|
91
|
+
<div class="grid grid-cols-1 gap-2 p-3 sm:grid-cols-2 lg:grid-cols-3">
|
|
92
|
+
<div class="field-info-container">
|
|
93
|
+
<div class="field-name">Device</div>
|
|
94
|
+
<div class="field-value">
|
|
95
|
+
{{#if this.deviceRouteModel}}
|
|
96
|
+
<button
|
|
97
|
+
type="button"
|
|
98
|
+
class="text-blue-600 hover:text-blue-700 hover:underline dark:text-blue-300 dark:hover:text-blue-200"
|
|
99
|
+
data-test-device-event-device-link
|
|
100
|
+
{{on "click" this.openDevice}}
|
|
101
|
+
>
|
|
102
|
+
{{n-a this.deviceName}}
|
|
103
|
+
</button>
|
|
104
|
+
{{else}}
|
|
105
|
+
{{n-a this.deviceName}}
|
|
106
|
+
{{/if}}
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
<div class="field-info-container">
|
|
110
|
+
<div class="field-name">Device UUID</div>
|
|
111
|
+
<div class="field-value">{{n-a @resource.device_uuid}}</div>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="field-info-container">
|
|
114
|
+
<div class="field-name">Device ID</div>
|
|
115
|
+
<div class="field-value">{{n-a @resource.device_id}}</div>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="field-info-container">
|
|
118
|
+
<div class="field-name">IMEI</div>
|
|
119
|
+
<div class="field-value">{{n-a @resource.device_imei}}</div>
|
|
120
|
+
</div>
|
|
121
|
+
<div class="field-info-container">
|
|
122
|
+
<div class="field-name">Serial Number</div>
|
|
123
|
+
<div class="field-value">{{n-a @resource.device_serial_number}}</div>
|
|
124
|
+
</div>
|
|
125
|
+
<div class="field-info-container">
|
|
126
|
+
<div class="field-name">IDENT</div>
|
|
127
|
+
<div class="field-value">{{n-a @resource.ident}}</div>
|
|
128
|
+
</div>
|
|
129
|
+
<div class="field-info-container">
|
|
130
|
+
<div class="field-name">Provider</div>
|
|
131
|
+
<div class="field-value">
|
|
132
|
+
{{#if this.telematicRouteModel}}
|
|
133
|
+
<button
|
|
134
|
+
type="button"
|
|
135
|
+
class="text-blue-600 hover:text-blue-700 hover:underline dark:text-blue-300 dark:hover:text-blue-200"
|
|
136
|
+
data-test-device-event-telematic-link
|
|
137
|
+
{{on "click" this.openTelematic}}
|
|
138
|
+
>
|
|
139
|
+
{{n-a this.providerLabel}}
|
|
140
|
+
</button>
|
|
141
|
+
{{else}}
|
|
142
|
+
{{n-a this.providerLabel}}
|
|
143
|
+
{{/if}}
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
<div class="field-info-container">
|
|
147
|
+
<div class="field-name">Protocol</div>
|
|
148
|
+
<div class="field-value">{{n-a @resource.protocol}}</div>
|
|
149
|
+
</div>
|
|
150
|
+
<div class="field-info-container">
|
|
151
|
+
<div class="field-name">Telematic UUID</div>
|
|
152
|
+
<div class="field-value">{{n-a @resource.telematic_uuid}}</div>
|
|
153
|
+
</div>
|
|
154
|
+
<div class="field-info-container">
|
|
155
|
+
<div class="field-name">Device Status</div>
|
|
156
|
+
<div class="field-value" data-test-device-event-device-status>
|
|
157
|
+
{{#if this.deviceStatus}}
|
|
158
|
+
<Badge @status={{this.deviceStatus}}>{{smart-humanize this.deviceStatus}}</Badge>
|
|
159
|
+
{{else}}
|
|
160
|
+
{{n-a null}}
|
|
161
|
+
{{/if}}
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
</section>
|
|
167
|
+
|
|
168
|
+
<section class="grid grid-cols-1 gap-3 xl:grid-cols-3">
|
|
169
|
+
<div class="overflow-hidden rounded-md border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
170
|
+
<div class="border-b border-gray-200 px-3 py-2 dark:border-gray-700">
|
|
171
|
+
<h3 class="text-sm font-bold text-gray-900 dark:text-gray-100">Payload</h3>
|
|
172
|
+
</div>
|
|
173
|
+
<pre class="max-h-80 overflow-auto bg-gray-900 p-3 text-[11px] leading-5 text-gray-100">{{n-a this.payloadJson}}</pre>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div class="overflow-hidden rounded-md border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
177
|
+
<div class="border-b border-gray-200 px-3 py-2 dark:border-gray-700">
|
|
178
|
+
<h3 class="text-sm font-bold text-gray-900 dark:text-gray-100">Data</h3>
|
|
179
|
+
</div>
|
|
180
|
+
<pre class="max-h-80 overflow-auto bg-gray-900 p-3 text-[11px] leading-5 text-gray-100">{{n-a this.dataJson}}</pre>
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
<div class="overflow-hidden rounded-md border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
184
|
+
<div class="border-b border-gray-200 px-3 py-2 dark:border-gray-700">
|
|
185
|
+
<h3 class="text-sm font-bold text-gray-900 dark:text-gray-100">Meta</h3>
|
|
186
|
+
</div>
|
|
187
|
+
<pre class="max-h-80 overflow-auto bg-gray-900 p-3 text-[11px] leading-5 text-gray-100">{{n-a this.metaJson}}</pre>
|
|
188
|
+
</div>
|
|
189
|
+
</section>
|
|
190
|
+
|
|
191
|
+
<CustomField::Yield @subject={{@resource}} @viewMode={{true}} @wrapperClass="bordered-top" />
|
|
192
|
+
</div>
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { action, get } from '@ember/object';
|
|
3
|
+
import { inject as service } from '@ember/service';
|
|
4
|
+
|
|
5
|
+
const warningSeverities = ['warning', 'error', 'critical', 'high'];
|
|
6
|
+
|
|
7
|
+
function firstPresent(...values) {
|
|
8
|
+
return values.find((value) => value !== undefined && value !== null && value !== '');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function prettyJson(value) {
|
|
12
|
+
if (!value) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (typeof value === 'string') {
|
|
17
|
+
try {
|
|
18
|
+
return JSON.stringify(JSON.parse(value), null, 2);
|
|
19
|
+
} catch {
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return JSON.stringify(value, null, 2);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default class DeviceEventDetailsComponent extends Component {
|
|
28
|
+
@service hostRouter;
|
|
29
|
+
|
|
30
|
+
get resource() {
|
|
31
|
+
return this.args.resource ?? {};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get eventType() {
|
|
35
|
+
return firstPresent(get(this.resource, 'event_type'), get(this.resource, 'eventType'), get(this.resource, 'type'));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get severity() {
|
|
39
|
+
return firstPresent(get(this.resource, 'severity'), 'info');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get isProcessed() {
|
|
43
|
+
return Boolean(firstPresent(get(this.resource, 'processed_at'), get(this.resource, 'processedAt')));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get processedStatus() {
|
|
47
|
+
return this.isProcessed ? 'success' : 'warning';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get processedLabel() {
|
|
51
|
+
return this.isProcessed ? 'Processed' : 'Unprocessed';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get severityAccentClass() {
|
|
55
|
+
return warningSeverities.includes(String(this.severity ?? '').toLowerCase()) ? 'fleetops-connectivity-kpi-accent-amber' : 'fleetops-connectivity-kpi-accent-green';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get message() {
|
|
59
|
+
return get(this.resource, 'message');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get occurredAt() {
|
|
63
|
+
return firstPresent(get(this.resource, 'occurred_at'), get(this.resource, 'occurredAt'), get(this.resource, 'created_at'), get(this.resource, 'createdAt'));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get processedAt() {
|
|
67
|
+
return firstPresent(get(this.resource, 'processed_at'), get(this.resource, 'processedAt'));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get processingDelay() {
|
|
71
|
+
return firstPresent(get(this.resource, 'processing_delay_minutes'), get(this.resource, 'processingDelayMinutes'));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
get ageMinutes() {
|
|
75
|
+
return firstPresent(get(this.resource, 'age_minutes'), get(this.resource, 'ageMinutes'));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get deviceName() {
|
|
79
|
+
return firstPresent(
|
|
80
|
+
get(this.resource, 'device.displayName'),
|
|
81
|
+
get(this.resource, 'device.display_name'),
|
|
82
|
+
get(this.resource, 'device.name'),
|
|
83
|
+
get(this.resource, 'device_name'),
|
|
84
|
+
get(this.resource, 'device_id')
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
get deviceIdentifier() {
|
|
89
|
+
return firstPresent(
|
|
90
|
+
get(this.resource, 'device_id'),
|
|
91
|
+
get(this.resource, 'device.device_id'),
|
|
92
|
+
get(this.resource, 'device_imei'),
|
|
93
|
+
get(this.resource, 'device_serial_number'),
|
|
94
|
+
get(this.resource, 'ident')
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
get deviceRouteModel() {
|
|
99
|
+
return firstPresent(get(this.resource, 'device.id'), get(this.resource, 'device.public_id'), get(this.resource, 'device_uuid'));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
get providerLabel() {
|
|
103
|
+
return firstPresent(get(this.resource, 'provider_descriptor.label'), get(this.resource, 'telematic.name'), get(this.resource, 'telematic_name'), get(this.resource, 'provider'));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
get telematicRouteModel() {
|
|
107
|
+
return firstPresent(get(this.resource, 'telematic.id'), get(this.resource, 'telematic.public_id'), get(this.resource, 'telematic_uuid'));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
get deviceStatus() {
|
|
111
|
+
return firstPresent(get(this.resource, 'device.status'), get(this.resource, 'device_status'), get(this.resource, 'device_connection_status'));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
get metrics() {
|
|
115
|
+
return [
|
|
116
|
+
{ label: 'Severity', value: this.severity, icon: 'triangle-exclamation', accentClass: this.severityAccentClass },
|
|
117
|
+
{
|
|
118
|
+
label: 'Processing',
|
|
119
|
+
value: this.processedLabel,
|
|
120
|
+
icon: this.isProcessed ? 'check' : 'clock',
|
|
121
|
+
accentClass: this.isProcessed ? 'fleetops-connectivity-kpi-accent-green' : 'fleetops-connectivity-kpi-accent-rose',
|
|
122
|
+
},
|
|
123
|
+
{ label: 'Device', value: this.deviceName, meta: this.deviceIdentifier, icon: 'microchip', accentClass: 'fleetops-connectivity-kpi-accent-blue' },
|
|
124
|
+
{ label: 'Provider', value: this.providerLabel, icon: 'satellite-dish', accentClass: 'fleetops-connectivity-kpi-accent-green' },
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
get payloadJson() {
|
|
129
|
+
return prettyJson(get(this.resource, 'payload'));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
get dataJson() {
|
|
133
|
+
return prettyJson(get(this.resource, 'data'));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
get metaJson() {
|
|
137
|
+
return prettyJson(get(this.resource, 'meta'));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@action openDevice() {
|
|
141
|
+
if (this.deviceRouteModel) {
|
|
142
|
+
return this.hostRouter.transitionTo('console.fleet-ops.connectivity.devices.index.details', this.deviceRouteModel);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
@action openTelematic() {
|
|
147
|
+
if (this.telematicRouteModel) {
|
|
148
|
+
return this.hostRouter.transitionTo('console.fleet-ops.connectivity.telematics.details', this.telematicRouteModel);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -219,7 +219,7 @@ export default class DriverScheduleComponent extends Component {
|
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
/**
|
|
222
|
-
* Load HOS (Hours of Service) status from the
|
|
222
|
+
* Load HOS (Hours of Service) status from the Fleet-Ops driver endpoint.
|
|
223
223
|
* Best-effort — if the endpoint is not yet implemented hosStatus stays null.
|
|
224
224
|
*/
|
|
225
225
|
@task *loadHOSStatus() {
|
|
@@ -11,9 +11,16 @@ import { task } from 'ember-concurrency';
|
|
|
11
11
|
const TYPE_TO_MODEL = {
|
|
12
12
|
'fleet-ops:vehicle': 'vehicle',
|
|
13
13
|
'fleet-ops:driver': 'driver',
|
|
14
|
+
'Fleetbase\\FleetOps\\Models\\Vehicle': 'vehicle',
|
|
15
|
+
'Fleetbase\\FleetOps\\Models\\Driver': 'driver',
|
|
14
16
|
'fleet-ops:equipment': 'equipment',
|
|
15
17
|
};
|
|
16
18
|
|
|
19
|
+
const TYPE_TO_OPTION_VALUE = {
|
|
20
|
+
'Fleetbase\\FleetOps\\Models\\Vehicle': 'fleet-ops:vehicle',
|
|
21
|
+
'Fleetbase\\FleetOps\\Models\\Driver': 'fleet-ops:driver',
|
|
22
|
+
};
|
|
23
|
+
|
|
17
24
|
export default class EquipmentFormComponent extends Component {
|
|
18
25
|
@service fetch;
|
|
19
26
|
@service currentUser;
|
|
@@ -45,7 +52,7 @@ export default class EquipmentFormComponent extends Component {
|
|
|
45
52
|
const { resource } = args;
|
|
46
53
|
if (resource?.equipable_type) {
|
|
47
54
|
this.equipableModelName = TYPE_TO_MODEL[resource.equipable_type] ?? null;
|
|
48
|
-
this.selectedEquipableType = this.equipableTypeOptions.find((o) => o.value === resource.equipable_type) ?? null;
|
|
55
|
+
this.selectedEquipableType = this.equipableTypeOptions.find((o) => o.value === (TYPE_TO_OPTION_VALUE[resource.equipable_type] ?? resource.equipable_type)) ?? null;
|
|
49
56
|
}
|
|
50
57
|
}
|
|
51
58
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
@primaryAction={{this.createOrderAction}}
|
|
4
4
|
@searchPlaceholder="Search Fleet-Ops..."
|
|
5
5
|
@searchProvider={{this.searchNavigation}}
|
|
6
|
-
@
|
|
6
|
+
@shouldSyncInitialActiveParent={{this.shouldSyncInitialActiveParent}}
|
|
7
7
|
class="fleet-ops-sidebar-navigator"
|
|
8
8
|
>
|
|
9
9
|
<:footer as |state|>
|