@fleetbase/fleetops-engine 0.6.53 → 0.6.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addon/components/admin/routing-settings.hbs +2 -1
- package/addon/components/cell/attached-vehicle.hbs +10 -0
- package/addon/components/cell/attached-vehicle.js +58 -0
- package/addon/components/cell/device-identity.hbs +24 -0
- package/addon/components/cell/device-identity.js +129 -0
- package/addon/components/cell/driver-identity.hbs +33 -0
- package/addon/components/cell/driver-identity.js +143 -0
- package/addon/components/cell/equipment-identity.hbs +5 -0
- package/addon/components/cell/equipment-identity.js +48 -0
- package/addon/components/cell/part-identity.hbs +5 -0
- package/addon/components/cell/part-identity.js +63 -0
- package/addon/components/cell/telematic-device.hbs +38 -0
- package/addon/components/cell/telematic-device.js +44 -0
- package/addon/components/cell/telematic-provider.hbs +33 -16
- package/addon/components/cell/telematic-provider.js +50 -8
- package/addon/components/cell/vehicle-identity.hbs +33 -0
- package/addon/components/cell/vehicle-identity.js +138 -0
- package/addon/components/device/details.hbs +106 -135
- package/addon/components/device/details.js +174 -1
- package/addon/components/device/form.hbs +2 -2
- package/addon/components/device/form.js +7 -0
- package/addon/components/device/panel-header.hbs +46 -21
- package/addon/components/device/panel-header.js +49 -1
- package/addon/components/device/panel-tabs/events.hbs +30 -0
- package/addon/components/device/panel-tabs/events.js +128 -0
- package/addon/components/device/panel-tabs/sensors.hbs +30 -0
- package/addon/components/device/panel-tabs/sensors.js +93 -0
- package/addon/components/device/panel-tabs/vehicle.hbs +68 -0
- package/addon/components/device/panel-tabs/vehicle.js +90 -0
- package/addon/components/equipment/form.js +8 -1
- package/addon/components/layout/fleet-ops-sidebar/operations-monitor.js +76 -11
- package/addon/components/layout/fleet-ops-sidebar.hbs +1 -1
- package/addon/components/layout/fleet-ops-sidebar.js +101 -17
- 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/order/form/details.hbs +1 -43
- package/addon/components/order/form/details.js +18 -53
- package/addon/components/order/form/orchestrator-constraints.hbs +40 -0
- package/addon/components/order/form/orchestrator-constraints.js +61 -0
- package/addon/components/order/form/payload.js +48 -2
- package/addon/components/order/form/route.js +15 -0
- package/addon/components/order/form/service-rate.hbs +62 -11
- package/addon/components/order/form/service-rate.js +124 -2
- package/addon/components/order/form.hbs +2 -0
- package/addon/components/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 +7 -1
- package/addon/components/telematic/form.js +16 -8
- package/addon/components/vendor/panel-header.hbs +82 -0
- package/addon/components/vendor/panel-header.js +48 -0
- package/addon/components/work-order/details.hbs +5 -1
- package/addon/components/work-order/form.hbs +26 -4
- package/addon/components/work-order/form.js +5 -1
- package/addon/controllers/connectivity/devices/index/details/events.js +244 -1
- package/addon/controllers/connectivity/devices/index/details/sensors.js +199 -0
- package/addon/controllers/connectivity/devices/index/details/vehicle.js +122 -0
- package/addon/controllers/connectivity/devices/index/details.js +12 -0
- package/addon/controllers/connectivity/devices/index.js +144 -129
- package/addon/controllers/connectivity/events/details.js +3 -0
- package/addon/controllers/connectivity/events/index.js +187 -11
- package/addon/controllers/connectivity/sensors/index.js +156 -14
- package/addon/controllers/connectivity/telematics/details/attachments.js +220 -54
- package/addon/controllers/connectivity/telematics/details/devices.js +170 -129
- package/addon/controllers/connectivity/telematics/details/events.js +39 -11
- package/addon/controllers/connectivity/telematics/details/sensors.js +39 -19
- package/addon/controllers/maintenance/equipment/index.js +1 -2
- package/addon/controllers/maintenance/parts/index.js +1 -2
- package/addon/controllers/maintenance/work-orders/index.js +18 -1
- package/addon/controllers/management/drivers/index.js +82 -42
- package/addon/controllers/management/fuel-reports/index.js +63 -4
- package/addon/controllers/management/issues/index.js +63 -4
- package/addon/controllers/management/vehicles/index.js +33 -6
- package/addon/routes/connectivity/devices/index/details/events.js +37 -1
- package/addon/routes/connectivity/devices/index/details/sensors.js +35 -0
- package/addon/routes/connectivity/devices/index/details/vehicle.js +29 -0
- package/addon/routes/connectivity/devices/index.js +7 -0
- package/addon/routes/connectivity/events/details.js +3 -0
- package/addon/routes/connectivity/events/index.js +6 -2
- package/addon/routes/connectivity/sensors/index.js +7 -2
- package/addon/routes/connectivity/telematics/details/attachments.js +72 -9
- package/addon/routes/connectivity/telematics/details/devices.js +6 -0
- package/addon/routes/connectivity/telematics/details/sensors.js +1 -0
- package/addon/routes/connectivity/telematics/details.js +1 -1
- package/addon/routes/connectivity/telematics/edit.js +1 -1
- package/addon/routes/maintenance/work-orders/index.js +1 -0
- package/addon/routes.js +2 -0
- package/addon/services/device-actions.js +30 -0
- package/addon/services/device-event-actions.js +1 -1
- package/addon/services/equipment-actions.js +1 -0
- package/addon/services/order-creation.js +54 -1
- package/addon/services/order-validation.js +3 -2
- package/addon/services/sensor-actions.js +3 -0
- package/addon/services/service-rate-actions.js +40 -1
- package/addon/services/vehicle-actions.js +24 -12
- package/addon/services/vendor-actions.js +3 -0
- package/addon/styles/fleetops-engine.css +28 -4
- package/addon/templates/connectivity/devices/index/details/events.hbs +85 -1
- package/addon/templates/connectivity/devices/index/details/sensors.hbs +73 -0
- package/addon/templates/connectivity/devices/index/details/vehicle.hbs +147 -0
- package/addon/templates/connectivity/telematics/details/attachments.hbs +198 -121
- package/addon/templates/connectivity/telematics/details/devices.hbs +4 -1
- package/addon/templates/connectivity/telematics/details/events.hbs +4 -1
- package/addon/templates/connectivity/telematics/details/sensors.hbs +3 -0
- package/addon/templates/management/vehicles/index/edit.hbs +1 -1
- package/addon/templates/management/vendors/index/details.hbs +2 -2
- package/addon/templates/settings/notifications.hbs +2 -1
- package/addon/templates/settings/routing.hbs +1 -0
- package/addon/utils/device-table-columns.js +288 -0
- package/addon/utils/fleet-ops-options.js +38 -0
- package/addon/utils/identity-cell-resource.js +20 -0
- package/addon/utils/map-drawer-dropdown-position.js +47 -0
- package/addon/utils/trackable-option.js +93 -0
- package/app/components/cell/attached-vehicle.js +1 -0
- package/app/components/cell/device-identity.js +1 -0
- package/app/components/cell/driver-identity.js +1 -0
- package/app/components/cell/equipment-identity.js +1 -0
- package/app/components/cell/part-identity.js +1 -0
- package/app/components/cell/telematic-device.js +1 -0
- package/app/components/cell/vehicle-identity.js +1 -0
- package/app/components/device/panel-tabs/events.hbs +30 -0
- package/app/components/device/panel-tabs/events.js +128 -0
- package/app/components/device/panel-tabs/sensors.hbs +30 -0
- package/app/components/device/panel-tabs/sensors.js +93 -0
- package/app/components/device/panel-tabs/vehicle.hbs +68 -0
- package/app/components/device/panel-tabs/vehicle.js +90 -0
- package/app/components/order/form/orchestrator-constraints.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/devices/index/details/sensors.js +1 -0
- package/app/controllers/connectivity/devices/index/details/vehicle.js +1 -0
- package/app/controllers/connectivity/events/{index/details.js → details.js} +1 -1
- package/app/routes/connectivity/devices/index/details/sensors.js +1 -0
- package/app/routes/connectivity/devices/index/details/vehicle.js +1 -0
- package/app/routes/connectivity/events/{index/details.js → details.js} +1 -1
- package/app/templates/connectivity/devices/index/details/sensors.js +1 -0
- package/app/templates/connectivity/devices/index/details/vehicle.js +1 -0
- package/app/templates/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/2025_08_28_054930_create_work_orders_table.php +1 -0
- package/server/migrations/2026_06_18_000001_add_category_to_work_orders_table.php +32 -0
- package/server/seeders/Testing/MaintenanceSeeder.php +5 -4
- package/server/src/Console/Commands/ProcessMaintenanceTriggers.php +1 -0
- package/server/src/Console/Commands/SyncTelematics.php +89 -0
- package/server/src/Exceptions/TelematicProviderException.php +13 -2
- package/server/src/Http/Controllers/Api/v1/OrderController.php +6 -21
- package/server/src/Http/Controllers/Internal/v1/DeviceController.php +55 -1
- package/server/src/Http/Controllers/Internal/v1/DeviceEventController.php +17 -6
- package/server/src/Http/Controllers/Internal/v1/FleetController.php +9 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +178 -1
- package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +1 -0
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +4 -32
- package/server/src/Http/Controllers/Internal/v1/SearchController.php +2 -2
- package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +4 -4
- package/server/src/Http/Controllers/Internal/v1/ServiceRateController.php +82 -13
- package/server/src/Http/Filter/DeviceEventFilter.php +65 -0
- package/server/src/Http/Filter/DeviceFilter.php +80 -0
- package/server/src/Http/Filter/SensorFilter.php +125 -0
- package/server/src/Http/Filter/VehicleFilter.php +13 -0
- package/server/src/Http/Requests/CreateDriverRequest.php +14 -13
- package/server/src/Http/Requests/Internal/CreateDriverRequest.php +1 -0
- package/server/src/Http/Resources/v1/Driver.php +11 -2
- package/server/src/Http/Resources/v1/Index/Vehicle.php +19 -0
- package/server/src/Http/Resources/v1/WorkOrder.php +1 -0
- package/server/src/Jobs/FinalizeApiOrderCreation.php +47 -0
- package/server/src/Jobs/FinalizeInternalOrderCreation.php +35 -0
- package/server/src/Jobs/NotifyBulkAssignedDriver.php +47 -0
- package/server/src/Jobs/SyncTelematicDevicesJob.php +180 -123
- package/server/src/Models/Device.php +17 -1
- package/server/src/Models/DeviceEvent.php +82 -1
- package/server/src/Models/Driver.php +16 -0
- package/server/src/Models/Equipment.php +55 -0
- package/server/src/Models/Sensor.php +15 -2
- package/server/src/Models/Telematic.php +1 -1
- package/server/src/Models/Vehicle.php +5 -1
- package/server/src/Models/WorkOrder.php +5 -2
- package/server/src/Observers/DriverObserver.php +3 -3
- package/server/src/Observers/FleetObserver.php +23 -0
- package/server/src/Observers/VehicleObserver.php +3 -3
- package/server/src/Providers/FleetOpsServiceProvider.php +2 -0
- package/server/src/Support/LiveCacheService.php +1 -1
- package/server/src/Support/Telematics/Providers/AfaqyProvider.php +143 -20
- 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 +70 -5
- package/server/src/Support/Telematics/Providers/SamsaraProvider.php +122 -14
- package/server/src/Support/Telematics/TelematicService.php +330 -30
- package/server/src/routes.php +1 -1
- package/server/tests/DeviceFilterTest.php +45 -0
- package/server/tests/DriverVehicleStatusDefaultTest.php +19 -2
- package/server/tests/EquipmentAssignmentMorphTypeTest.php +35 -0
- package/server/tests/OperationsMonitorEndpointTest.php +38 -0
- package/server/tests/OrderQueuedClosureRegressionTest.php +36 -0
- package/server/tests/ServiceRateRouteValidationTest.php +101 -0
- package/server/tests/TelematicsHardeningTest.php +598 -6
- package/server/tests/VehicleDriverAssignmentTest.php +27 -0
- package/translations/en-us.yaml +1 -0
- package/addon/controllers/connectivity/events/index/details.js +0 -3
- package/addon/routes/connectivity/events/index/details.js +0 -3
- /package/addon/templates/connectivity/events/{index/details.hbs → details.hbs} +0 -0
|
@@ -12,7 +12,13 @@ export default class ConnectivityTelematicsDetailsDevicesRoute extends Route {
|
|
|
12
12
|
status: { refreshModel: true },
|
|
13
13
|
provider: { refreshModel: true },
|
|
14
14
|
attachment_state: { refreshModel: true },
|
|
15
|
+
vehicle: { refreshModel: true },
|
|
16
|
+
connection_status: { refreshModel: true },
|
|
15
17
|
device_id: { refreshModel: true },
|
|
18
|
+
type: { refreshModel: true },
|
|
19
|
+
serial_number: { refreshModel: true },
|
|
20
|
+
last_online_at: { refreshModel: true },
|
|
21
|
+
updated_at: { refreshModel: true },
|
|
16
22
|
};
|
|
17
23
|
|
|
18
24
|
model(params) {
|
|
@@ -11,6 +11,7 @@ export default class MaintenanceWorkOrdersIndexRoute extends Route {
|
|
|
11
11
|
query: { refreshModel: true },
|
|
12
12
|
name: { refreshModel: true },
|
|
13
13
|
public_id: { refreshModel: true },
|
|
14
|
+
category: { refreshModel: true },
|
|
14
15
|
status: { refreshModel: true },
|
|
15
16
|
priority: { refreshModel: true },
|
|
16
17
|
created_at: { refreshModel: true },
|
package/addon/routes.js
CHANGED
|
@@ -184,6 +184,8 @@ export default buildRoutes(function () {
|
|
|
184
184
|
this.route('edit', { path: '/edit/:public_id' });
|
|
185
185
|
this.route('details', { path: '/:public_id' }, function () {
|
|
186
186
|
this.route('index', { path: '/' });
|
|
187
|
+
this.route('vehicle');
|
|
188
|
+
this.route('sensors');
|
|
187
189
|
this.route('events');
|
|
188
190
|
this.route('virtual', { path: '/:slug' });
|
|
189
191
|
});
|
|
@@ -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 = {
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import Service, { inject as service } from '@ember/service';
|
|
2
2
|
import { tracked } from '@glimmer/tracking';
|
|
3
3
|
import { next } from '@ember/runloop';
|
|
4
|
+
import Evented from '@ember/object/evented';
|
|
4
5
|
|
|
5
|
-
export
|
|
6
|
+
export const SERVICE_QUOTE_REFRESH_REQUESTED = 'service-quote-refresh-requested';
|
|
7
|
+
|
|
8
|
+
export default class OrderCreationService extends Service.extend(Evented) {
|
|
6
9
|
@service orderActions;
|
|
7
10
|
@tracked context;
|
|
8
11
|
@tracked order;
|
|
9
12
|
@tracked cfManager;
|
|
13
|
+
@tracked serviceQuoteOverrides = {};
|
|
14
|
+
orderValidationRules = {};
|
|
10
15
|
|
|
11
16
|
newOrder(attrs = {}) {
|
|
12
17
|
const order = this.orderActions.createNewInstance(attrs);
|
|
@@ -34,4 +39,52 @@ export default class OrderCreationService extends Service {
|
|
|
34
39
|
removeContext(key) {
|
|
35
40
|
delete this.context[key];
|
|
36
41
|
}
|
|
42
|
+
|
|
43
|
+
requestServiceQuoteRefresh(reason, order = this.order) {
|
|
44
|
+
this.trigger(SERVICE_QUOTE_REFRESH_REQUESTED, {
|
|
45
|
+
reason,
|
|
46
|
+
order,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
setServiceQuoteOverride(key, config = {}) {
|
|
51
|
+
this.serviceQuoteOverrides = {
|
|
52
|
+
...this.serviceQuoteOverrides,
|
|
53
|
+
[key]: {
|
|
54
|
+
...config,
|
|
55
|
+
key,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
getServiceQuoteOverride() {
|
|
61
|
+
return Object.values(this.serviceQuoteOverrides ?? {}).find((override) => override?.mode === 'locked') ?? null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
clearServiceQuoteOverride(key) {
|
|
65
|
+
const overrides = { ...(this.serviceQuoteOverrides ?? {}) };
|
|
66
|
+
delete overrides[key];
|
|
67
|
+
this.serviceQuoteOverrides = overrides;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
setOrderValidationRule(key, fn) {
|
|
71
|
+
if (typeof fn !== 'function') {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
this.orderValidationRules = {
|
|
76
|
+
...(this.orderValidationRules ?? {}),
|
|
77
|
+
[key]: fn,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
clearOrderValidationRule(key) {
|
|
82
|
+
const rules = { ...(this.orderValidationRules ?? {}) };
|
|
83
|
+
delete rules[key];
|
|
84
|
+
this.orderValidationRules = rules;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
validateOrderRules(order, cfManager = null) {
|
|
88
|
+
return Object.values(this.orderValidationRules ?? {}).every((fn) => fn(order, cfManager) !== false);
|
|
89
|
+
}
|
|
37
90
|
}
|
|
@@ -24,12 +24,13 @@ export default class OrderValidationService extends Service {
|
|
|
24
24
|
const hasPickup = isNotEmpty(order.payload.pickup);
|
|
25
25
|
const hasDropoff = isNotEmpty(order.payload.dropoff);
|
|
26
26
|
const hasValidCustomFields = cfManager ? this.isCustomFieldsValid(cfManager) : true;
|
|
27
|
+
const hasValidExtensionRules = this.orderCreation.validateOrderRules(order, cfManager);
|
|
27
28
|
|
|
28
29
|
if (hasWaypoints) {
|
|
29
|
-
return hasOrderConfig && hasOrderType && hasValidCustomFields;
|
|
30
|
+
return hasOrderConfig && hasOrderType && hasValidCustomFields && hasValidExtensionRules;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
return hasPickup && hasDropoff && hasOrderConfig && hasOrderType && hasValidCustomFields;
|
|
33
|
+
return hasPickup && hasDropoff && hasOrderConfig && hasOrderType && hasValidCustomFields && hasValidExtensionRules;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
validationFails(order, cfManager) {
|
|
@@ -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
|
|
|
@@ -134,6 +172,7 @@ export default class ServiceRateActionsService extends ResourceActionService {
|
|
|
134
172
|
scheduled_at: order.scheduled_at,
|
|
135
173
|
is_route_optimized: order.optimized,
|
|
136
174
|
service: serviceRate.id,
|
|
175
|
+
meta: order.meta,
|
|
137
176
|
});
|
|
138
177
|
|
|
139
178
|
return serviceQuotes;
|
|
@@ -66,6 +66,26 @@ export default class VehicleActionsService extends ResourceActionService {
|
|
|
66
66
|
this.initialize('vehicle', { status: 'available' });
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
async resolveVehicleResource(vehicle) {
|
|
70
|
+
if (typeof vehicle?.then === 'function') {
|
|
71
|
+
return await vehicle;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (typeof vehicle?.loadResource === 'function') {
|
|
75
|
+
return (await vehicle.loadResource()) ?? vehicle;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return vehicle;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async reloadIndexResource(vehicle) {
|
|
82
|
+
if (vehicle?.meta?._index_resource) {
|
|
83
|
+
await vehicle.reload();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return vehicle;
|
|
87
|
+
}
|
|
88
|
+
|
|
69
89
|
transition = {
|
|
70
90
|
view: (vehicle) => this.transitionTo('management.vehicles.index.details', vehicle),
|
|
71
91
|
edit: (vehicle) => this.transitionTo('management.vehicles.index.edit', vehicle),
|
|
@@ -87,9 +107,7 @@ export default class VehicleActionsService extends ResourceActionService {
|
|
|
87
107
|
});
|
|
88
108
|
},
|
|
89
109
|
edit: async (vehicle, options = {}) => {
|
|
90
|
-
|
|
91
|
-
await vehicle.reload();
|
|
92
|
-
}
|
|
110
|
+
vehicle = await this.reloadIndexResource(await this.resolveVehicleResource(vehicle));
|
|
93
111
|
|
|
94
112
|
return this.resourceContextPanel.open({
|
|
95
113
|
content: 'vehicle/form',
|
|
@@ -109,9 +127,7 @@ export default class VehicleActionsService extends ResourceActionService {
|
|
|
109
127
|
});
|
|
110
128
|
},
|
|
111
129
|
view: async (vehicle, options = {}) => {
|
|
112
|
-
|
|
113
|
-
await vehicle.reload();
|
|
114
|
-
}
|
|
130
|
+
vehicle = await this.reloadIndexResource(await this.resolveVehicleResource(vehicle));
|
|
115
131
|
|
|
116
132
|
return this.resourceContextPanel.open({
|
|
117
133
|
vehicle,
|
|
@@ -144,9 +160,7 @@ export default class VehicleActionsService extends ResourceActionService {
|
|
|
144
160
|
});
|
|
145
161
|
},
|
|
146
162
|
edit: async (vehicle, options = {}, saveOptions = {}) => {
|
|
147
|
-
|
|
148
|
-
await vehicle.reload();
|
|
149
|
-
}
|
|
163
|
+
vehicle = await this.reloadIndexResource(await this.resolveVehicleResource(vehicle));
|
|
150
164
|
|
|
151
165
|
return this.modalsManager.show('modals/resource', {
|
|
152
166
|
resource: vehicle,
|
|
@@ -159,9 +173,7 @@ export default class VehicleActionsService extends ResourceActionService {
|
|
|
159
173
|
});
|
|
160
174
|
},
|
|
161
175
|
view: async (vehicle, options = {}) => {
|
|
162
|
-
|
|
163
|
-
await vehicle.reload();
|
|
164
|
-
}
|
|
176
|
+
vehicle = await this.reloadIndexResource(await this.resolveVehicleResource(vehicle));
|
|
165
177
|
|
|
166
178
|
return this.modalsManager.show('modals/resource', {
|
|
167
179
|
resource: vehicle,
|
|
@@ -43,8 +43,11 @@ export default class VendorActionsService extends ResourceActionService {
|
|
|
43
43
|
view: (vendor) => {
|
|
44
44
|
return this.resourceContextPanel.open({
|
|
45
45
|
vendor,
|
|
46
|
+
header: 'vendor/panel-header',
|
|
46
47
|
tabs: [
|
|
47
48
|
{
|
|
49
|
+
key: 'overview',
|
|
50
|
+
id: 'overview',
|
|
48
51
|
label: this.intl.t('common.overview'),
|
|
49
52
|
component: 'vendor/details',
|
|
50
53
|
},
|
|
@@ -235,8 +235,6 @@ body[data-theme='dark'] .fleetops-connectivity-kpi-accent-rose .fleetops-connect
|
|
|
235
235
|
|
|
236
236
|
.fleetops-provider-connections-actions .fleetops-provider-connections-search {
|
|
237
237
|
min-width: 16rem;
|
|
238
|
-
height: 2.25rem;
|
|
239
|
-
min-height: 2.25rem;
|
|
240
238
|
}
|
|
241
239
|
|
|
242
240
|
.fleetops-provider-connections-header {
|
|
@@ -257,8 +255,6 @@ body[data-theme='dark'] .fleetops-connectivity-kpi-accent-rose .fleetops-connect
|
|
|
257
255
|
.fleetops-provider-connections-actions .fleetops-telematics-action-button,
|
|
258
256
|
.fleetops-provider-connections-actions button {
|
|
259
257
|
flex-shrink: 0;
|
|
260
|
-
height: 2.25rem;
|
|
261
|
-
min-height: 2.25rem;
|
|
262
258
|
white-space: nowrap;
|
|
263
259
|
}
|
|
264
260
|
|
|
@@ -266,6 +262,26 @@ body[data-theme='dark'] .fleetops-connectivity-kpi-accent-rose .fleetops-connect
|
|
|
266
262
|
white-space: nowrap;
|
|
267
263
|
}
|
|
268
264
|
|
|
265
|
+
.fleetops-attachments-vehicle-filter-trigger.ember-power-select-trigger {
|
|
266
|
+
display: flex;
|
|
267
|
+
align-items: center;
|
|
268
|
+
min-height: 2rem;
|
|
269
|
+
padding-right: 1.75rem;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.fleetops-attachments-vehicle-filter-trigger.ember-power-select-trigger .ember-power-select-placeholder,
|
|
273
|
+
.fleetops-attachments-vehicle-filter-trigger.ember-power-select-trigger .ember-power-select-selected-item {
|
|
274
|
+
min-width: 0;
|
|
275
|
+
overflow: hidden;
|
|
276
|
+
text-overflow: ellipsis;
|
|
277
|
+
white-space: nowrap;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.fleetops-attachments-vehicle-filter-trigger.ember-power-select-trigger .ember-power-select-status-icon {
|
|
281
|
+
display: block;
|
|
282
|
+
right: 0.65rem;
|
|
283
|
+
}
|
|
284
|
+
|
|
269
285
|
.status-badge.long-offline-status-badge > span,
|
|
270
286
|
.status-badge.long_offline-status-badge > span {
|
|
271
287
|
background-color: #854d0e;
|
|
@@ -278,6 +294,14 @@ body[data-theme='dark'] .fleetops-connectivity-kpi-accent-rose .fleetops-connect
|
|
|
278
294
|
color: #fde047;
|
|
279
295
|
}
|
|
280
296
|
|
|
297
|
+
.fleetops-device-status-badge.status-badge > span {
|
|
298
|
+
padding: 0 0.25rem !important;
|
|
299
|
+
border-radius: 0.125rem !important;
|
|
300
|
+
font-size: 0.5625rem !important;
|
|
301
|
+
line-height: 0.75rem !important;
|
|
302
|
+
font-weight: 700;
|
|
303
|
+
}
|
|
304
|
+
|
|
281
305
|
.fleetops-provider-connections-panel .table-wrapper table tbody tr td,
|
|
282
306
|
.fleetops-provider-connections-panel .next-table-wrapper table tbody tr td {
|
|
283
307
|
vertical-align: top;
|
|
@@ -1 +1,85 @@
|
|
|
1
|
-
|
|
1
|
+
<div class="flex flex-col gap-3 p-3">
|
|
2
|
+
<div class="flex flex-col gap-1">
|
|
3
|
+
<h2 class="text-base font-bold text-gray-900 dark:text-gray-100">Event Review + Triage</h2>
|
|
4
|
+
<p class="text-xs text-gray-500 dark:text-gray-300">Warning signals, diagnostics, and unprocessed telemetry.</p>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<section class="grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-4">
|
|
8
|
+
{{#each this.metrics as |metric|}}
|
|
9
|
+
<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">
|
|
10
|
+
<div class="flex h-full items-start justify-between gap-2 p-3">
|
|
11
|
+
<div class="min-w-0">
|
|
12
|
+
<div class="truncate text-[10px] font-bold uppercase text-gray-500 dark:text-gray-400">{{metric.label}}</div>
|
|
13
|
+
<div class="mt-1 text-xl font-extrabold leading-none text-gray-900 dark:text-gray-50">{{metric.value}}</div>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="fleetops-connectivity-kpi-icon flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-md text-[13px]">
|
|
16
|
+
<FaIcon @icon={{metric.icon}} />
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
{{/each}}
|
|
21
|
+
</section>
|
|
22
|
+
|
|
23
|
+
{{#if this.hasHealthyEventState}}
|
|
24
|
+
<Telematic::TabEmptyState
|
|
25
|
+
@tone="success"
|
|
26
|
+
@icon="check"
|
|
27
|
+
@title="No warning or unprocessed events"
|
|
28
|
+
@message="Recent telemetry is reviewed and no warning-level events are currently visible for this device."
|
|
29
|
+
@primaryText="Refresh"
|
|
30
|
+
@primaryIcon="refresh"
|
|
31
|
+
@primaryAction={{this.refresh}}
|
|
32
|
+
/>
|
|
33
|
+
{{/if}}
|
|
34
|
+
|
|
35
|
+
<section class="overflow-hidden rounded-md border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
36
|
+
<div class="fleetops-provider-connections-header border-b border-gray-200 px-3 py-2 dark:border-gray-700">
|
|
37
|
+
<div>
|
|
38
|
+
<h3 class="text-sm font-bold text-gray-900 dark:text-gray-100">Telemetry Events</h3>
|
|
39
|
+
<p class="mt-1 text-xs text-gray-500 dark:text-gray-300">Filter by type, severity, processed state, and date.</p>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="fleetops-provider-connections-actions flex min-w-0 flex-wrap items-center gap-1 lg:ml-auto lg:flex-nowrap lg:justify-end">
|
|
42
|
+
<Layout::Resource::TabularActions
|
|
43
|
+
@title="Telemetry Events"
|
|
44
|
+
@searchQuery={{this.events_query}}
|
|
45
|
+
@onSearch={{perform this.searchTask}}
|
|
46
|
+
@columns={{this.columns}}
|
|
47
|
+
@page={{this.events_page}}
|
|
48
|
+
@onPageChange={{fn (mut this.events_page)}}
|
|
49
|
+
@actionButtons={{this.actionButtons}}
|
|
50
|
+
@bulkActions={{this.bulkActions}}
|
|
51
|
+
@controller={{this}}
|
|
52
|
+
@searchInputClass="fleetops-provider-connections-search"
|
|
53
|
+
@filterPickerButtonClass="fleetops-provider-connections-action-button"
|
|
54
|
+
@columnPickerButtonClass="fleetops-provider-connections-action-button"
|
|
55
|
+
@filterPickerSize="sm"
|
|
56
|
+
@columnPickerSize="sm"
|
|
57
|
+
/>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<Layout::Resource::Tabular
|
|
62
|
+
@resource="device-event"
|
|
63
|
+
@title="Telemetry Events"
|
|
64
|
+
@data={{@model}}
|
|
65
|
+
@columns={{this.columns}}
|
|
66
|
+
@page={{this.events_page}}
|
|
67
|
+
@onPageChange={{fn (mut this.events_page)}}
|
|
68
|
+
@controller={{this}}
|
|
69
|
+
@withoutHeader={{true}}
|
|
70
|
+
@tableWrapperClass="no-table-extra-spacing"
|
|
71
|
+
@emptyStateComponent={{component
|
|
72
|
+
"table/empty-state"
|
|
73
|
+
variant="compact"
|
|
74
|
+
icon="bolt"
|
|
75
|
+
title="No telemetry events"
|
|
76
|
+
description="Events appear after this device reports provider activity."
|
|
77
|
+
filteredTitle="No events match your filters"
|
|
78
|
+
filteredDescription="Adjust the search, severity, or date filters to review other events."
|
|
79
|
+
primaryText="Refresh"
|
|
80
|
+
primaryIcon="refresh"
|
|
81
|
+
primaryAction=this.refresh
|
|
82
|
+
}}
|
|
83
|
+
/>
|
|
84
|
+
</section>
|
|
85
|
+
</div>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<div class="flex flex-col gap-3 p-3">
|
|
2
|
+
<div class="flex flex-col gap-1">
|
|
3
|
+
<h2 class="text-base font-bold text-gray-900 dark:text-gray-100">Sensor Inventory + Health</h2>
|
|
4
|
+
<p class="text-xs text-gray-500 dark:text-gray-300">Readings, thresholds, and reporting health for this device.</p>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<section class="grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-4">
|
|
8
|
+
{{#each this.metrics as |metric|}}
|
|
9
|
+
<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">
|
|
10
|
+
<div class="flex h-full items-start justify-between gap-2 p-3">
|
|
11
|
+
<div class="min-w-0">
|
|
12
|
+
<div class="truncate text-[10px] font-bold uppercase text-gray-500 dark:text-gray-400">{{metric.label}}</div>
|
|
13
|
+
<div class="mt-1 text-xl font-extrabold leading-none text-gray-900 dark:text-gray-50">{{metric.value}}</div>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="fleetops-connectivity-kpi-icon flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-md text-[13px]">
|
|
16
|
+
<FaIcon @icon={{metric.icon}} />
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
{{/each}}
|
|
21
|
+
</section>
|
|
22
|
+
|
|
23
|
+
<section class="overflow-hidden rounded-md border border-gray-200 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
24
|
+
<div class="fleetops-provider-connections-header border-b border-gray-200 px-3 py-2 dark:border-gray-700">
|
|
25
|
+
<div>
|
|
26
|
+
<h3 class="text-sm font-bold text-gray-900 dark:text-gray-100">Device Sensors</h3>
|
|
27
|
+
<p class="mt-1 text-xs text-gray-500 dark:text-gray-300">Filter by type, status, and last reading.</p>
|
|
28
|
+
</div>
|
|
29
|
+
<div class="fleetops-provider-connections-actions flex min-w-0 flex-wrap items-center gap-2 lg:ml-auto lg:flex-nowrap lg:justify-end">
|
|
30
|
+
<Layout::Resource::TabularActions
|
|
31
|
+
@title="Device Sensors"
|
|
32
|
+
@searchQuery={{this.sensors_query}}
|
|
33
|
+
@onSearch={{perform this.searchTask}}
|
|
34
|
+
@columns={{this.columns}}
|
|
35
|
+
@page={{this.sensors_page}}
|
|
36
|
+
@onPageChange={{fn (mut this.sensors_page)}}
|
|
37
|
+
@actionButtons={{this.actionButtons}}
|
|
38
|
+
@bulkActions={{this.bulkActions}}
|
|
39
|
+
@controller={{this}}
|
|
40
|
+
@searchInputClass="fleetops-provider-connections-search"
|
|
41
|
+
@filterPickerButtonClass="fleetops-provider-connections-action-button"
|
|
42
|
+
@columnPickerButtonClass="fleetops-provider-connections-action-button"
|
|
43
|
+
@filterPickerSize="sm"
|
|
44
|
+
@columnPickerSize="sm"
|
|
45
|
+
/>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<Layout::Resource::Tabular
|
|
50
|
+
@resource="sensor"
|
|
51
|
+
@title="Device Sensors"
|
|
52
|
+
@data={{@model}}
|
|
53
|
+
@columns={{this.columns}}
|
|
54
|
+
@page={{this.sensors_page}}
|
|
55
|
+
@onPageChange={{fn (mut this.sensors_page)}}
|
|
56
|
+
@controller={{this}}
|
|
57
|
+
@withoutHeader={{true}}
|
|
58
|
+
@tableWrapperClass="no-table-extra-spacing"
|
|
59
|
+
@emptyStateComponent={{component
|
|
60
|
+
"table/empty-state"
|
|
61
|
+
variant="compact"
|
|
62
|
+
icon="gauge-high"
|
|
63
|
+
title="No sensors connected"
|
|
64
|
+
description="Sensors appear here after provider telemetry links them to this device."
|
|
65
|
+
filteredTitle="No sensors match your filters"
|
|
66
|
+
filteredDescription="Adjust the search or filters to find another sensor."
|
|
67
|
+
primaryText="Refresh"
|
|
68
|
+
primaryIcon="refresh"
|
|
69
|
+
primaryAction=this.refresh
|
|
70
|
+
}}
|
|
71
|
+
/>
|
|
72
|
+
</section>
|
|
73
|
+
</div>
|