@fleetbase/fleetops-engine 0.6.19 → 0.6.21
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/custom-entity/form.hbs +14 -14
- package/addon/components/device/details.hbs +92 -43
- package/addon/components/device/form.hbs +108 -60
- package/addon/components/device/form.js +36 -8
- package/addon/components/device/panel-header.hbs +32 -0
- package/addon/components/device/panel-header.js +3 -0
- package/addon/components/driver/form.hbs +1 -1
- package/addon/components/driver/form.js +49 -47
- package/addon/components/entity/form.hbs +7 -5
- package/addon/components/layout/fleet-ops-sidebar.js +12 -12
- package/addon/components/map/drawer/device-event-listing.hbs +58 -0
- package/addon/components/map/drawer/device-event-listing.js +181 -0
- package/addon/components/map/drawer/position-listing.hbs +84 -0
- package/addon/components/map/drawer/position-listing.js +289 -0
- package/addon/components/map/drawer.js +2 -0
- package/addon/components/map/leaflet-live-map.hbs +7 -2
- package/addon/components/order/details/payload.hbs +6 -4
- package/addon/components/order/details/payload.js +2 -0
- package/addon/components/order/kanban.hbs +12 -10
- package/addon/components/order/kanban.js +27 -3
- package/addon/components/order-config-manager/custom-fields.js +1 -1
- package/addon/components/positions-replay.hbs +333 -0
- package/addon/components/positions-replay.js +372 -0
- package/addon/components/sensor/details.hbs +64 -38
- package/addon/components/sensor/form.hbs +112 -63
- package/addon/components/sensor/form.js +36 -24
- package/addon/components/sensor/panel-header.hbs +32 -0
- package/addon/components/sensor/panel-header.js +3 -0
- package/addon/components/telematic/details.hbs +40 -16
- package/addon/components/telematic/form.hbs +63 -64
- package/addon/components/telematic/form.js +73 -4
- package/addon/components/vehicle/card.hbs +1 -1
- package/addon/controllers/analytics/reports/index/edit.js +1 -1
- package/addon/controllers/connectivity/devices/index/details.js +22 -1
- package/addon/controllers/connectivity/devices/index/edit.js +66 -1
- package/addon/controllers/connectivity/devices/index.js +51 -9
- package/addon/controllers/connectivity/events/index.js +65 -16
- package/addon/controllers/connectivity/sensors/index/details.js +22 -1
- package/addon/controllers/connectivity/sensors/index/edit.js +66 -1
- package/addon/controllers/connectivity/sensors/index.js +66 -6
- package/addon/controllers/connectivity/telematics/index/details.js +22 -1
- package/addon/controllers/connectivity/telematics/index/edit.js +66 -1
- package/addon/controllers/connectivity/telematics/index.js +20 -11
- package/addon/controllers/management/fleets/index/details.js +26 -21
- package/addon/controllers/management/fleets/index/edit.js +9 -6
- package/addon/controllers/management/vehicles/index/details.js +21 -13
- package/addon/controllers/operations/orders/index/new.js +4 -2
- package/addon/controllers/operations/orders/index.js +50 -45
- package/addon/controllers/settings/custom-fields.js +6 -0
- package/addon/helpers/get-fleet-ops-option-label.js +11 -0
- package/addon/routes/connectivity/devices/index/details.js +27 -1
- package/addon/routes/connectivity/devices/index/edit.js +27 -1
- package/addon/routes/connectivity/sensors/index/details.js +27 -1
- package/addon/routes/connectivity/sensors/index/edit.js +27 -1
- package/addon/routes/connectivity/telematics/index/details.js +27 -1
- package/addon/routes/connectivity/telematics/index/edit.js +27 -1
- package/addon/routes/management/vehicles/index/details/positions.js +3 -0
- package/addon/routes/operations/orders/index.js +0 -3
- package/addon/routes.js +1 -0
- package/addon/services/movement-tracker.js +81 -30
- package/addon/services/order-creation.js +4 -8
- package/addon/services/order-validation.js +3 -3
- package/addon/styles/fleetops-engine.css +192 -0
- package/addon/templates/connectivity/devices/index/details/index.hbs +2 -2
- package/addon/templates/connectivity/devices/index/details.hbs +15 -2
- package/addon/templates/connectivity/devices/index/edit.hbs +1 -1
- package/addon/templates/connectivity/events/index.hbs +1 -1
- package/addon/templates/connectivity/sensors/index/details/index.hbs +2 -2
- package/addon/templates/connectivity/sensors/index/details.hbs +15 -2
- package/addon/templates/connectivity/sensors/index/edit.hbs +1 -1
- package/addon/templates/connectivity/telematics/index/details/index.hbs +2 -2
- package/addon/templates/connectivity/telematics/index/details.hbs +14 -2
- package/addon/templates/connectivity/telematics/index/edit.hbs +1 -1
- package/addon/templates/management/vehicles/index/details/positions.hbs +1 -0
- package/addon/templates/operations/orders/index.hbs +26 -2
- package/addon/utils/fleet-ops-options.js +95 -0
- package/addon/utils/setup-customer-portal.js +7 -0
- package/app/components/device/panel-header.js +1 -0
- package/app/components/map/drawer/device-event-listing.js +1 -0
- package/app/components/map/drawer/position-listing.js +1 -0
- package/app/components/positions-replay.js +1 -0
- package/app/components/sensor/panel-header.js +1 -0
- package/app/helpers/get-fleet-ops-option-label.js +1 -0
- package/app/routes/management/vehicles/index/details/positions.js +1 -0
- package/app/templates/management/vehicles/index/details/positions.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +4 -4
- package/server/config/telematics.php +111 -0
- package/server/migrations/2025_10_27_000001_add_telematics_integration_fields.php +70 -0
- package/server/migrations/2025_10_27_171322_fix_device_column_names.php +107 -0
- package/server/migrations/2025_10_27_203023_add_company_uuid_to_device_events_table.php +28 -0
- package/server/src/Console/Commands/ReplayVehicleLocations.php +225 -0
- package/server/src/Contracts/TelematicProviderDescriptor.php +72 -0
- package/server/src/Contracts/TelematicProviderInterface.php +119 -0
- package/server/src/Exceptions/TelematicProviderException.php +14 -0
- package/server/src/Exceptions/TelematicRateLimitExceededException.php +12 -0
- package/server/src/Http/Controllers/Api/v1/DriverController.php +24 -14
- package/server/src/Http/Controllers/Api/v1/VehicleController.php +27 -7
- package/server/src/Http/Controllers/Internal/v1/DeviceController.php +22 -0
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +50 -68
- package/server/src/Http/Controllers/Internal/v1/PositionController.php +240 -0
- package/server/src/Http/Controllers/Internal/v1/SensorController.php +11 -0
- package/server/src/Http/Controllers/Internal/v1/TelematicController.php +141 -0
- package/server/src/Http/Controllers/Internal/v1/TelematicWebhookController.php +170 -0
- package/server/src/Http/Filter/DeviceEventFilter.php +68 -0
- package/server/src/Http/Filter/PositionFilter.php +35 -0
- package/server/src/Http/Resources/v1/Position.php +44 -0
- package/server/src/Jobs/ReplayPositions.php +64 -0
- package/server/src/Jobs/SendPositionReplay.php +65 -0
- package/server/src/Jobs/SyncTelematicDevicesJob.php +106 -0
- package/server/src/Jobs/TestTelematicConnectionJob.php +102 -0
- package/server/src/Models/Device.php +72 -10
- package/server/src/Models/DeviceEvent.php +7 -0
- package/server/src/Models/Driver.php +28 -1
- package/server/src/Models/Payload.php +11 -3
- package/server/src/Models/Place.php +9 -2
- package/server/src/Models/Position.php +17 -17
- package/server/src/Models/Sensor.php +78 -13
- package/server/src/Models/Telematic.php +116 -6
- package/server/src/Models/Vehicle.php +104 -1
- package/server/src/Providers/FleetOpsServiceProvider.php +2 -0
- package/server/src/Support/Telematics/Providers/AbstractProvider.php +151 -0
- package/server/src/Support/Telematics/Providers/FlespiProvider.php +182 -0
- package/server/src/Support/Telematics/Providers/GeotabProvider.php +181 -0
- package/server/src/Support/Telematics/Providers/SamsaraProvider.php +177 -0
- package/server/src/Support/Telematics/TelematicProviderRegistry.php +147 -0
- package/server/src/Support/Telematics/TelematicService.php +223 -0
- package/server/src/Support/Utils.php +1 -1
- package/server/src/routes.php +12 -1
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import Component from '@glimmer/component';
|
|
2
|
-
import { tracked } from '@glimmer/tracking';
|
|
3
2
|
import { inject as service } from '@ember/service';
|
|
4
3
|
import { task } from 'ember-concurrency';
|
|
5
4
|
|
|
@@ -9,55 +8,58 @@ export default class DriverFormComponent extends Component {
|
|
|
9
8
|
@service currentUser;
|
|
10
9
|
@service notifications;
|
|
11
10
|
@service modalsManager;
|
|
12
|
-
@tracked userAccountActionButtons = [
|
|
13
|
-
{
|
|
14
|
-
icon: 'user-plus',
|
|
15
|
-
size: 'xs',
|
|
16
|
-
permission: 'iam create user',
|
|
17
|
-
onClick: () => {
|
|
18
|
-
const user = this.store.createRecord('user', {
|
|
19
|
-
status: 'pending',
|
|
20
|
-
type: 'user',
|
|
21
|
-
});
|
|
22
11
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
type: 'user_photo',
|
|
35
|
-
},
|
|
36
|
-
(uploadedFile) => {
|
|
37
|
-
user.setProperties({
|
|
38
|
-
avatar_uuid: uploadedFile.id,
|
|
39
|
-
avatar_url: uploadedFile.url,
|
|
40
|
-
avatar: uploadedFile,
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
);
|
|
44
|
-
},
|
|
45
|
-
confirm: async (modal) => {
|
|
46
|
-
modal.startLoading();
|
|
12
|
+
get userAccountActionButtons() {
|
|
13
|
+
return [
|
|
14
|
+
{
|
|
15
|
+
icon: 'user-plus',
|
|
16
|
+
size: 'xs',
|
|
17
|
+
permission: 'iam create user',
|
|
18
|
+
onClick: () => {
|
|
19
|
+
const user = this.store.createRecord('user', {
|
|
20
|
+
status: 'pending',
|
|
21
|
+
type: 'user',
|
|
22
|
+
});
|
|
47
23
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
this.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
24
|
+
this.modalsManager.show('modals/user-form', {
|
|
25
|
+
title: 'Create a new user',
|
|
26
|
+
user,
|
|
27
|
+
formPermission: 'iam create user',
|
|
28
|
+
uploadNewPhoto: (file) => {
|
|
29
|
+
this.fetch.uploadFile.perform(
|
|
30
|
+
file,
|
|
31
|
+
{
|
|
32
|
+
path: `uploads/${this.currentUser.companyId}/users/${user.slug}`,
|
|
33
|
+
subject_uui: user.id,
|
|
34
|
+
subject_type: 'user',
|
|
35
|
+
type: 'user_photo',
|
|
36
|
+
},
|
|
37
|
+
(uploadedFile) => {
|
|
38
|
+
user.setProperties({
|
|
39
|
+
avatar_uuid: uploadedFile.id,
|
|
40
|
+
avatar_url: uploadedFile.url,
|
|
41
|
+
avatar: uploadedFile,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
},
|
|
46
|
+
confirm: async (modal) => {
|
|
47
|
+
modal.startLoading();
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
await user.save();
|
|
51
|
+
this.notifications.success('New user created successfully!');
|
|
52
|
+
modal.done();
|
|
53
|
+
} catch (error) {
|
|
54
|
+
this.notifications.serverError(error);
|
|
55
|
+
modal.stopLoading();
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
},
|
|
58
60
|
},
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
];
|
|
62
|
+
}
|
|
61
63
|
|
|
62
64
|
@task *handlePhotoUpload(file) {
|
|
63
65
|
try {
|
|
@@ -25,11 +25,7 @@
|
|
|
25
25
|
<InputGroup @name={{t "common.internal-id"}} @value={{@resource.internal_id}} @helpText={{t "modals.entity-form.id-text"}} />
|
|
26
26
|
<InputGroup @name={{t "modals.entity-form.sku"}} @value={{@resource.sku}} @helpText={{t "modals.entity-form.sku-text"}} />
|
|
27
27
|
<div></div>
|
|
28
|
-
<InputGroup
|
|
29
|
-
@name={{t "modals.entity-form.description"}}
|
|
30
|
-
@helpText={{t "modals.entity-form.description-text"}}
|
|
31
|
-
@wrapperClass="col-span-2"
|
|
32
|
-
>
|
|
28
|
+
<InputGroup @name={{t "modals.entity-form.description"}} @helpText={{t "modals.entity-form.description-text"}} @wrapperClass="col-span-2">
|
|
33
29
|
<Textarea @value={{@resource.description}} type="text" class="w-full form-input" placeholder={{t "modals.entity-form.description"}} />
|
|
34
30
|
</InputGroup>
|
|
35
31
|
</div>
|
|
@@ -83,4 +79,10 @@
|
|
|
83
79
|
</div>
|
|
84
80
|
</div>
|
|
85
81
|
</ContentPanel>
|
|
82
|
+
|
|
83
|
+
<CustomField::Yield @subject={{@resource}} @wrapperClass="bordered-top" />
|
|
84
|
+
|
|
85
|
+
<RegistryYield @registry="fleet-ops:component:entity:form" as |RegistryComponent|>
|
|
86
|
+
<RegistryComponent @resource={{@resource}} @controller={{this.controller}} @permission={{get-write-permission @resource}} />
|
|
87
|
+
</RegistryYield>
|
|
86
88
|
</div>
|
|
@@ -171,14 +171,14 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
|
|
|
171
171
|
permission: 'fleet-ops list device-event',
|
|
172
172
|
visible: this.abilities.can('fleet-ops see device-event'),
|
|
173
173
|
},
|
|
174
|
-
{
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
},
|
|
174
|
+
// {
|
|
175
|
+
// intl: 'menu.tracking',
|
|
176
|
+
// title: this.intl.t('menu.tracking'),
|
|
177
|
+
// icon: 'map-marked-alt',
|
|
178
|
+
// route: 'connectivity.tracking',
|
|
179
|
+
// permission: 'fleet-ops list device',
|
|
180
|
+
// visible: this.abilities.can('fleet-ops see device'),
|
|
181
|
+
// },
|
|
182
182
|
];
|
|
183
183
|
|
|
184
184
|
const maintenanceItems = [
|
|
@@ -284,10 +284,10 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
|
|
|
284
284
|
// open: this.appCache.get('fleet-ops:sidebar:maintenance:open', false),
|
|
285
285
|
// onToggle: (open) => this.appCache.set('fleet-ops:sidebar:maintenance:open', open),
|
|
286
286
|
// }),
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
287
|
+
createPanel('menu.connectivity', 'connectivity', connectivityItems, {
|
|
288
|
+
open: this.appCache.get('fleet-ops:sidebar:connectivity:open', false),
|
|
289
|
+
onToggle: (open) => this.appCache.set('fleet-ops:sidebar:connectivity:open', open),
|
|
290
|
+
}),
|
|
291
291
|
createPanel('menu.analytics', 'analytics', analyticsItems, {
|
|
292
292
|
open: this.appCache.get('fleet-ops:sidebar:analytics:open', false),
|
|
293
293
|
onToggle: (open) => this.appCache.set('fleet-ops:sidebar:analytics:open', open),
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<div class="flex flex-row items-center justify-between w-full px-5 py-2 border-b border-gray-200 dark:border-gray-700">
|
|
2
|
+
<div class="flex flex-row items-center space-x-2">
|
|
3
|
+
<div>
|
|
4
|
+
<ModelSelect
|
|
5
|
+
@modelName="telematic"
|
|
6
|
+
@selectedModel={{this.telematic}}
|
|
7
|
+
@placeholder="Filter by Telematic"
|
|
8
|
+
@triggerClass="form-select form-input form-input-sm w-48"
|
|
9
|
+
@infiniteScroll={{false}}
|
|
10
|
+
@renderInPlace={{true}}
|
|
11
|
+
@onChange={{this.onTelematicSelected}}
|
|
12
|
+
as |model|
|
|
13
|
+
>
|
|
14
|
+
<div class="space-x-2 text-sm">
|
|
15
|
+
<div class="inline-block align-top">
|
|
16
|
+
<div class="hide-from-trigger h-1.5 w-full" />
|
|
17
|
+
<Image src={{model.provider_descriptor.icon}} class="w-5 h-5" />
|
|
18
|
+
</div>
|
|
19
|
+
<div class="inline-block">
|
|
20
|
+
<div class="font-semibold normalize-in-trigger">{{or model.name model.public_id}}</div>
|
|
21
|
+
<div class="hide-from-trigger">{{n-a model.provider_descriptor.label (titleize model.provider)}}</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</ModelSelect>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div>
|
|
28
|
+
<ModelSelect
|
|
29
|
+
@modelName="device"
|
|
30
|
+
@selectedModel={{this.device}}
|
|
31
|
+
@placeholder="Filter by Device"
|
|
32
|
+
@triggerClass="form-select form-input form-input-sm w-48"
|
|
33
|
+
@infiniteScroll={{false}}
|
|
34
|
+
@renderInPlace={{true}}
|
|
35
|
+
@onChange={{this.onDeviceSelected}}
|
|
36
|
+
as |model|
|
|
37
|
+
>
|
|
38
|
+
<div class="text-sm">
|
|
39
|
+
<div class="font-semibold normalize-in-trigger">{{model.name}}</div>
|
|
40
|
+
<div class="hide-from-trigger">{{n-a model.serial_number}}</div>
|
|
41
|
+
</div>
|
|
42
|
+
</ModelSelect>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div class="w-48">
|
|
46
|
+
<DatePicker
|
|
47
|
+
@value={{this.dateFilter}}
|
|
48
|
+
@range={{true}}
|
|
49
|
+
@onSelect={{this.onDateRangeChanged}}
|
|
50
|
+
@autoClose={{true}}
|
|
51
|
+
@placeholder="Select date range"
|
|
52
|
+
class="w-full form-input form-input-sm w-48"
|
|
53
|
+
/>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
<Table @rows={{this.events}} @columns={{this.columns}} />
|
|
58
|
+
<Spacer @height="200px" />
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { tracked } from '@glimmer/tracking';
|
|
3
|
+
import { inject as service } from '@ember/service';
|
|
4
|
+
import { action } from '@ember/object';
|
|
5
|
+
import { task } from 'ember-concurrency';
|
|
6
|
+
import { isArray } from '@ember/array';
|
|
7
|
+
import { startOfWeek, endOfWeek, format } from 'date-fns';
|
|
8
|
+
|
|
9
|
+
export default class MapDrawerDeviceEventListingComponent extends Component {
|
|
10
|
+
@service store;
|
|
11
|
+
@service hostRouter;
|
|
12
|
+
@service notifications;
|
|
13
|
+
@service deviceEventActions;
|
|
14
|
+
@service deviceActions;
|
|
15
|
+
@service intl;
|
|
16
|
+
|
|
17
|
+
@tracked events = [];
|
|
18
|
+
@tracked telematic = null;
|
|
19
|
+
@tracked device = null;
|
|
20
|
+
@tracked dateFilter = [format(startOfWeek(new Date(), { weekStartsOn: 1 }), 'yyyy-MM-dd'), format(endOfWeek(new Date(), { weekStartsOn: 1 }), 'yyyy-MM-dd')];
|
|
21
|
+
|
|
22
|
+
get columns() {
|
|
23
|
+
return [
|
|
24
|
+
{
|
|
25
|
+
label: 'Event',
|
|
26
|
+
valuePath: 'event_type',
|
|
27
|
+
cellComponent: 'table/cell/anchor',
|
|
28
|
+
cellClassNames: 'uppercase',
|
|
29
|
+
action: this.deviceEventActions.panel.view,
|
|
30
|
+
permission: 'fleet-ops view device-event',
|
|
31
|
+
resizable: true,
|
|
32
|
+
sortable: true,
|
|
33
|
+
filterable: true,
|
|
34
|
+
filterParam: 'name',
|
|
35
|
+
filterComponent: 'filter/string',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
label: 'Device',
|
|
39
|
+
valuePath: 'device.displayName',
|
|
40
|
+
cellComponent: 'table/cell/anchor',
|
|
41
|
+
action: this.deviceActions.panel.view,
|
|
42
|
+
permission: 'fleet-ops view device',
|
|
43
|
+
resizable: true,
|
|
44
|
+
sortable: true,
|
|
45
|
+
filterable: true,
|
|
46
|
+
filterComponent: 'filter/model',
|
|
47
|
+
filterComponentPlaceholder: 'Select device',
|
|
48
|
+
filterParam: 'device',
|
|
49
|
+
model: 'device',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
label: 'Provider',
|
|
53
|
+
valuePath: 'provider',
|
|
54
|
+
resizable: true,
|
|
55
|
+
sortable: true,
|
|
56
|
+
filterable: true,
|
|
57
|
+
filterParam: 'provider',
|
|
58
|
+
filterComponent: 'filter/string',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
label: 'Severity',
|
|
62
|
+
valuePath: 'severity',
|
|
63
|
+
resizable: true,
|
|
64
|
+
sortable: true,
|
|
65
|
+
filterable: true,
|
|
66
|
+
filterParam: 'severity',
|
|
67
|
+
filterComponent: 'filter/string',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
label: 'IDENT',
|
|
71
|
+
valuePath: 'ident',
|
|
72
|
+
hidden: true,
|
|
73
|
+
resizable: true,
|
|
74
|
+
sortable: true,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
label: 'Protocol',
|
|
78
|
+
valuePath: 'protocol',
|
|
79
|
+
hidden: true,
|
|
80
|
+
resizable: true,
|
|
81
|
+
sortable: true,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
label: 'State',
|
|
85
|
+
valuePath: 'state',
|
|
86
|
+
hidden: true,
|
|
87
|
+
resizable: true,
|
|
88
|
+
sortable: true,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
label: 'Code',
|
|
92
|
+
valuePath: 'code',
|
|
93
|
+
resizable: true,
|
|
94
|
+
sortable: true,
|
|
95
|
+
filterable: true,
|
|
96
|
+
filterParam: 'code',
|
|
97
|
+
filterComponent: 'filter/string',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
label: this.intl.t('column.created-at'),
|
|
101
|
+
valuePath: 'createdAt',
|
|
102
|
+
sortParam: 'created_at',
|
|
103
|
+
width: '10%',
|
|
104
|
+
resizable: true,
|
|
105
|
+
sortable: true,
|
|
106
|
+
filterable: true,
|
|
107
|
+
filterComponent: 'filter/date',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
label: '',
|
|
111
|
+
cellComponent: 'table/cell/dropdown',
|
|
112
|
+
ddButtonText: false,
|
|
113
|
+
ddButtonIcon: 'ellipsis-h',
|
|
114
|
+
ddButtonIconPrefix: 'fas',
|
|
115
|
+
ddMenuLabel: this.intl.t('common.resource-actions', { resource: this.intl.t('resource.device-event') }),
|
|
116
|
+
cellClassNames: 'overflow-visible',
|
|
117
|
+
wrapperClass: 'flex items-center justify-end mx-2',
|
|
118
|
+
width: '10%',
|
|
119
|
+
actions: [
|
|
120
|
+
{
|
|
121
|
+
label: this.intl.t('common.view-resource', { resource: this.intl.t('resource.device-event') }),
|
|
122
|
+
fn: this.deviceEventActions.panel.view,
|
|
123
|
+
permission: 'fleet-ops view device-event',
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
sortable: false,
|
|
127
|
+
filterable: false,
|
|
128
|
+
resizable: false,
|
|
129
|
+
searchable: false,
|
|
130
|
+
},
|
|
131
|
+
];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
constructor() {
|
|
135
|
+
super(...arguments);
|
|
136
|
+
this.loadEvents.perform();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@action onDeviceSelected(device) {
|
|
140
|
+
this.device = device;
|
|
141
|
+
this.loadEvents.perform();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
@action onTelematicSelected(telematic) {
|
|
145
|
+
this.telematic = telematic;
|
|
146
|
+
this.loadEvents.perform();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
@action onDateRangeChanged({ formattedDate }) {
|
|
150
|
+
if (isArray(formattedDate) && formattedDate.length === 2) {
|
|
151
|
+
this.dateFilter = formattedDate;
|
|
152
|
+
this.loadEvents.perform();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
@task *loadEvents() {
|
|
157
|
+
try {
|
|
158
|
+
const params = {
|
|
159
|
+
limit: 900,
|
|
160
|
+
sort: 'created_at',
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
if (this.telematic) {
|
|
164
|
+
params.telematic = this.telematic.id;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (this.device) {
|
|
168
|
+
params.device = this.device.id;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (isArray(this.dateFilter) && this.dateFilter.length === 2) {
|
|
172
|
+
params.created_at = this.dateFilter.join(',');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const events = yield this.store.query('device-event', params);
|
|
176
|
+
this.positions = isArray(events) ? events : [];
|
|
177
|
+
} catch (error) {
|
|
178
|
+
this.notifications.serverError(error);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<div class="positions-replay-component flex flex-row items-center justify-between w-full px-5 py-2 border-b border-gray-200 dark:border-gray-700">
|
|
2
|
+
<div class="flex flex-row items-center space-x-2">
|
|
3
|
+
<div>
|
|
4
|
+
<div class="fleetbase-model-select fleetbase-power-select ember-model-select">
|
|
5
|
+
<PowerSelect
|
|
6
|
+
@options={{this.trackables}}
|
|
7
|
+
@selected={{this.resource}}
|
|
8
|
+
@onChange={{this.onResourceSelected}}
|
|
9
|
+
@placeholder={{t "common.select-field" field="trackable"}}
|
|
10
|
+
@triggerClass="form-select form-input form-input-sm w-48"
|
|
11
|
+
as |option|
|
|
12
|
+
>
|
|
13
|
+
<div class="flex flex-row items-center space-x-2">
|
|
14
|
+
<div class="inline-block">
|
|
15
|
+
<Image src={{or option.avatar_url option.photo_url}} class="w-4 h-4" />
|
|
16
|
+
</div>
|
|
17
|
+
<div class="inline-block text-sm">
|
|
18
|
+
<div class="font-semibold normalize-in-trigger">{{or option.name option.displayName option.public_id}}</div>
|
|
19
|
+
<div class="hide-from-trigger">{{or option.email option.serial_number option.plate_number option.internal_id}}</div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</PowerSelect>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div class="w-48">
|
|
27
|
+
<DatePicker
|
|
28
|
+
@value={{this.dateFilter}}
|
|
29
|
+
@range={{true}}
|
|
30
|
+
@onSelect={{this.onDateRangeChanged}}
|
|
31
|
+
@autoClose={{true}}
|
|
32
|
+
@placeholder="Select date range"
|
|
33
|
+
class="w-full form-input form-input-sm"
|
|
34
|
+
/>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<div class="replay-controls">
|
|
39
|
+
<div class="flex items-center justify-between space-x-2">
|
|
40
|
+
<div class="flex items-center space-x-2">
|
|
41
|
+
<Button @type="danger" @text="Stop" @icon="stop" @size="xs" @onClick={{this.stopReplay}} />
|
|
42
|
+
<Button
|
|
43
|
+
@type="success"
|
|
44
|
+
@text="Play"
|
|
45
|
+
@icon="play"
|
|
46
|
+
@size="xs"
|
|
47
|
+
@isLoading={{this.isReplaying}}
|
|
48
|
+
@disabled={{or this.replayPositions.isRunning (not this.hasPositions)}}
|
|
49
|
+
@onClick={{this.startReplay}}
|
|
50
|
+
/>
|
|
51
|
+
|
|
52
|
+
<div class="speed-control">
|
|
53
|
+
<label class="text-xs uppercase tracking-wide font-medium text-gray-700 dark:text-gray-400 mr-1">
|
|
54
|
+
Speed:
|
|
55
|
+
</label>
|
|
56
|
+
<Select
|
|
57
|
+
@value={{this.replaySpeed}}
|
|
58
|
+
@options={{this.speedOptions}}
|
|
59
|
+
@optionValue="value"
|
|
60
|
+
@optionLabel="label"
|
|
61
|
+
@onSelect={{this.onSpeedChanged}}
|
|
62
|
+
class="form-select speed-select"
|
|
63
|
+
/>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
{{#if this.isReplaying}}
|
|
68
|
+
<div class="replay-progress flex items-center space-x-1">
|
|
69
|
+
<span class="text-sm text-gray-600 dark:text-gray-400">
|
|
70
|
+
{{this.currentReplayIndex}}/{{this.totalPositions}}
|
|
71
|
+
</span>
|
|
72
|
+
<div class="progress-bar w-32 h-2 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
|
|
73
|
+
<div class="progress-fill h-full bg-blue-600 transition-all duration-300" style={{this.replayProgressWidth}}></div>
|
|
74
|
+
</div>
|
|
75
|
+
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
76
|
+
{{this.replayProgress}}%
|
|
77
|
+
</span>
|
|
78
|
+
</div>
|
|
79
|
+
{{/if}}
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
<Table @rows={{this.positions}} @columns={{this.columns}} />
|
|
84
|
+
<Spacer @height="200px" />
|