@fleetbase/fleetops-engine 0.6.21 → 0.6.22
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/device/card.hbs +1 -0
- package/addon/components/device/card.js +3 -0
- package/addon/components/device/manager.hbs +29 -0
- package/addon/components/device/manager.js +95 -0
- package/addon/components/device/pill.hbs +16 -0
- package/addon/components/device/pill.js +3 -0
- package/addon/components/driver/details.hbs +4 -0
- package/addon/components/driver/details.js +19 -1
- package/addon/components/driver/form.hbs +13 -2
- package/addon/components/driver/pill.hbs +17 -0
- package/addon/components/driver/pill.js +3 -0
- package/addon/components/map/drawer/device-event-listing.hbs +6 -0
- package/addon/components/map/drawer/position-listing.hbs +35 -19
- package/addon/components/map/drawer/position-listing.js +230 -64
- package/addon/components/modals/attach-device.hbs +18 -0
- package/addon/components/modals/attach-device.js +3 -0
- package/addon/components/order/details/detail.hbs +2 -54
- package/addon/components/order/details/detail.js +1 -0
- package/addon/components/order/pill.hbs +34 -0
- package/addon/components/order/pill.js +3 -0
- package/addon/components/positions-replay.hbs +26 -20
- package/addon/components/positions-replay.js +100 -63
- package/addon/components/telematic/form.hbs +4 -4
- package/addon/components/vehicle/card.hbs +1 -1
- package/addon/components/vehicle/details.hbs +4 -0
- package/addon/components/vehicle/details.js +19 -1
- package/addon/components/vehicle/form.hbs +4 -0
- package/addon/components/vehicle/pill.hbs +34 -0
- package/addon/components/vehicle/pill.js +3 -0
- package/addon/controllers/management/vehicles/index/details.js +5 -0
- package/addon/routes/management/drivers/index/details/positions.js +3 -0
- package/addon/routes.js +3 -0
- package/addon/services/position-playback.js +486 -0
- package/addon/services/resource-metadata.js +46 -0
- package/addon/templates/management/drivers/index/details/positions.hbs +2 -0
- package/addon/templates/management/vehicles/index/details/devices.hbs +1 -2
- package/app/components/device/card.js +1 -0
- package/app/components/device/manager.js +1 -0
- package/app/components/device/pill.js +1 -0
- package/app/components/driver/pill.js +1 -0
- package/app/components/modals/attach-device.js +1 -0
- package/app/components/order/pill.js +1 -0
- package/app/components/vehicle/pill.js +1 -0
- package/app/routes/management/drivers/index/details/positions.js +1 -0
- package/app/services/position-playback.js +1 -0
- package/app/services/resource-metadata.js +1 -0
- package/app/templates/management/drivers/index/details/positions.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +2 -2
- package/server/src/Http/Controllers/{Internal/v1/TelematicWebhookController.php → TelematicWebhookController.php} +1 -2
- package/server/src/Http/Resources/v1/Position.php +1 -1
- package/server/src/Models/Asset.php +10 -8
- package/server/src/Models/Device.php +11 -6
- package/server/src/Models/DeviceEvent.php +26 -3
- package/server/src/Models/Maintenance.php +15 -12
- package/server/src/Models/Part.php +2 -0
- package/server/src/Models/Position.php +10 -0
- package/server/src/Models/TrackingNumber.php +3 -1
- package/server/src/Models/WorkOrder.php +8 -5
- package/server/src/routes.php +12 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{yield}}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<div class="fleetops-device-manager" ...attributes>
|
|
2
|
+
<div class="flex flex-row items-center justify-between mb-4 px-3 pt-2">
|
|
3
|
+
<div></div>
|
|
4
|
+
<div>
|
|
5
|
+
<Button @icon="plus" @text="Attach device" @onClick={{this.addDevice}} @disabled={{not @resource}} />
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
<div>
|
|
9
|
+
{{#each this.devices as |device|}}
|
|
10
|
+
<div class="flex flex-row justify-between px-3 py-4 border-t border-gray-200 dark:border-gray-700">
|
|
11
|
+
<Device::Pill @device={{device}} />
|
|
12
|
+
<div>
|
|
13
|
+
<Button @type="danger" @icon="times" @text="Detach" @size="xs" @onClick={{fn this.removeDevice device}} />
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
{{else}}
|
|
17
|
+
<div class="empty-state bg-white dark:bg-gray-800 rounded-lg shadow-sm p-12 text-center">
|
|
18
|
+
<FaIcon @icon="hard-drive" @size="2x" class="text-gray-400 mb-2" />
|
|
19
|
+
<h3 class="text-base font-semibold text-gray-700 dark:text-gray-300">
|
|
20
|
+
No Devices Attached
|
|
21
|
+
</h3>
|
|
22
|
+
<p class="text-sm text-gray-500 dark:text-gray-400">
|
|
23
|
+
Click Attach device above to start receiving telematic data for this
|
|
24
|
+
{{get-model-name @resource}}
|
|
25
|
+
</p>
|
|
26
|
+
</div>
|
|
27
|
+
{{/each}}
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { tracked } from '@glimmer/tracking';
|
|
3
|
+
import { action, get } from '@ember/object';
|
|
4
|
+
import { inject as service } from '@ember/service';
|
|
5
|
+
import { debug } from '@ember/debug';
|
|
6
|
+
import { task } from 'ember-concurrency';
|
|
7
|
+
import getModelName from '@fleetbase/ember-core/utils/get-model-name';
|
|
8
|
+
|
|
9
|
+
export default class DeviceManagerComponent extends Component {
|
|
10
|
+
@service store;
|
|
11
|
+
@service modalsManager;
|
|
12
|
+
@service notifications;
|
|
13
|
+
@tracked devices = [];
|
|
14
|
+
|
|
15
|
+
get resourceName() {
|
|
16
|
+
const record = this.args.resource;
|
|
17
|
+
if (!record) return 'resource';
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
get(record, this.args.namePath ?? 'name') ??
|
|
21
|
+
get(record, 'display_name') ??
|
|
22
|
+
get(record, 'displayName') ??
|
|
23
|
+
get(record, 'tracking') ??
|
|
24
|
+
get(record, 'public_id') ??
|
|
25
|
+
getModelName(record)
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
constructor() {
|
|
30
|
+
super(...arguments);
|
|
31
|
+
this.loadDevices.perform();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@action addDevice() {
|
|
35
|
+
this.modalsManager.show('modals/attach-device', {
|
|
36
|
+
title: 'Select device to attach',
|
|
37
|
+
acceptButtonText: 'Confirm & Attach Device',
|
|
38
|
+
selectedDevice: null,
|
|
39
|
+
confirm: async (modal) => {
|
|
40
|
+
const selectedDevice = modal.getOption('selectedDevice');
|
|
41
|
+
if (!selectedDevice) return;
|
|
42
|
+
|
|
43
|
+
selectedDevice.setProperties({
|
|
44
|
+
attachable_uuid: this.args.resource.id,
|
|
45
|
+
attachable_type: `fleet-ops:${getModelName(this.args.resource)}`,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
modal.startLoading();
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
await selectedDevice.save();
|
|
52
|
+
await this.loadDevices.perform();
|
|
53
|
+
this.notifications.success('Device attached successfully.');
|
|
54
|
+
modal.done();
|
|
55
|
+
} catch (err) {
|
|
56
|
+
this.notifications.serverError(err);
|
|
57
|
+
modal.stopLoading();
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@action removeDevice(device) {
|
|
64
|
+
this.modalsManager.confirm({
|
|
65
|
+
title: `Are you sure you want to detach this device (${device.name}) from (${this.resourceName})?`,
|
|
66
|
+
body: `Removing this device will stop all telematic updates and events for this ${getModelName(this.args.resource)}.`,
|
|
67
|
+
confirm: async (modal) => {
|
|
68
|
+
modal.startLoading();
|
|
69
|
+
|
|
70
|
+
device.setProperties({ attachable_uuid: null, attachable_type: null });
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
await device.save();
|
|
74
|
+
await this.loadDevices.perform();
|
|
75
|
+
this.notifications.success('Device removed.');
|
|
76
|
+
modal.done();
|
|
77
|
+
} catch (error) {
|
|
78
|
+
this.notifications.serverError(error);
|
|
79
|
+
modal.stopLoading();
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@task *loadDevices() {
|
|
86
|
+
if (!this.args.resource) return;
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const devices = yield this.store.query('device', { attachable_uuid: this.args.resource.id });
|
|
90
|
+
this.devices = devices;
|
|
91
|
+
} catch (err) {
|
|
92
|
+
debug('Unable to load devices: ' + err.message);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{{#let (or @device @resource) as |resource|}}
|
|
2
|
+
<Pill
|
|
3
|
+
@resource={{resource}}
|
|
4
|
+
@imageSrc={{resource.photo_url}}
|
|
5
|
+
@subtitle={{or resource.device_id resource.serial_number resource.imei resource.public_id}}
|
|
6
|
+
@showOnlineIndicator={{true}}
|
|
7
|
+
@onClick={{@onClick}}
|
|
8
|
+
@anchorClass={{@anchorClass}}
|
|
9
|
+
@imageClass={{@imageClass}}
|
|
10
|
+
@imageWrapperClass={{@imageWrapperClass}}
|
|
11
|
+
@contentWrapperClass={{@contentWrapperClass}}
|
|
12
|
+
@titleClass={{@titleClass}}
|
|
13
|
+
@subtitleClass={{@subtitleClass}}
|
|
14
|
+
...attributes
|
|
15
|
+
/>
|
|
16
|
+
{{/let}}
|
|
@@ -53,4 +53,8 @@
|
|
|
53
53
|
</ContentPanel>
|
|
54
54
|
|
|
55
55
|
<CustomField::Yield @subject={{@resource}} @viewMode={{true}} @wrapperClass="bordered-top" />
|
|
56
|
+
|
|
57
|
+
<ContentPanel @title={{t "common.metadata"}} @open={{true}} @actionButtons={{this.metadataButtons}} @wrapperClass="bordered-top" @panelBodyWrapperClass={{unless (is-object-empty @resource.meta) "px-0i" ""}}>
|
|
58
|
+
<MetadataViewer @metadata={{@resource.meta}} />
|
|
59
|
+
</ContentPanel>
|
|
56
60
|
</div>
|
|
@@ -1,3 +1,21 @@
|
|
|
1
1
|
import Component from '@glimmer/component';
|
|
2
|
+
import { inject as service } from '@ember/service';
|
|
2
3
|
|
|
3
|
-
export default class DriverDetailsComponent extends Component {
|
|
4
|
+
export default class DriverDetailsComponent extends Component {
|
|
5
|
+
@service resourceMetadata;
|
|
6
|
+
|
|
7
|
+
get metadataButtons() {
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
type: 'default',
|
|
11
|
+
text: 'Edit',
|
|
12
|
+
icon: 'pencil',
|
|
13
|
+
iconPrefix: 'fas',
|
|
14
|
+
permission: 'fleet-ops update driver',
|
|
15
|
+
onClick: () => {
|
|
16
|
+
this.resourceMetadata.edit(this.args.resource);
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -51,7 +51,14 @@
|
|
|
51
51
|
<ContentPanel @title={{t "driver.fields.driver-details"}} @open={{true}} @wrapperClass="bordered-top">
|
|
52
52
|
<div class="flex flex-row space-x-3 mb-3">
|
|
53
53
|
<div class="flex items-center justify-start rounded-full">
|
|
54
|
-
<Image
|
|
54
|
+
<Image
|
|
55
|
+
src={{@resource.photo_url}}
|
|
56
|
+
@fallbackSrc={{config "defaultValues.driverImage"}}
|
|
57
|
+
alt={{@resource.name}}
|
|
58
|
+
height="48"
|
|
59
|
+
width="48"
|
|
60
|
+
class="h-14 w-14 rounded-full shadow-sm"
|
|
61
|
+
/>
|
|
55
62
|
<Attach::Tooltip @class="clean" @animation="scale" @placement="top">
|
|
56
63
|
<InputInfo @text={{t "common.upload-new-photo"}} />
|
|
57
64
|
</Attach::Tooltip>
|
|
@@ -133,7 +140,7 @@
|
|
|
133
140
|
</InputGroup>
|
|
134
141
|
|
|
135
142
|
<InputGroup @name={{t "common.status"}}>
|
|
136
|
-
|
|
143
|
+
<div class="fleetbase-model-select fleetbase-power-select ember-model-select">
|
|
137
144
|
<PowerSelect
|
|
138
145
|
@options={{get-fleet-ops-options "driverStatuses"}}
|
|
139
146
|
@selected={{find-by "value" @resource.status (get-fleet-ops-options "driverStatuses")}}
|
|
@@ -168,4 +175,8 @@
|
|
|
168
175
|
<ContentPanel @title={{t "avatar-picker.avatar"}} @open={{true}} @wrapperClass="bordered-top">
|
|
169
176
|
<AvatarPicker @model={{@resource}} @defaultAvatar={{config "defaultValues.driverAvatar"}} />
|
|
170
177
|
</ContentPanel>
|
|
178
|
+
|
|
179
|
+
<ContentPanel @title={{t "common.metadata"}} @open={{true}} @wrapperClass="bordered-top">
|
|
180
|
+
<MetadataEditor @value={{@resource.meta}} @onChange={{fn (mut @resource.meta)}} />
|
|
181
|
+
</ContentPanel>
|
|
171
182
|
</div>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{{#let (or @driver @resource) as |resource|}}
|
|
2
|
+
<Pill
|
|
3
|
+
@resource={{resource}}
|
|
4
|
+
@imageSrc={{avatar-url resource.photo_url}}
|
|
5
|
+
@subtitle={{n-a resource.phone "No Phone"}}
|
|
6
|
+
@fallbackImageType="driverImage"
|
|
7
|
+
@showOnlineIndicator={{true}}
|
|
8
|
+
@onClick={{@onClick}}
|
|
9
|
+
@anchorClass={{@anchorClass}}
|
|
10
|
+
@imageClass={{@imageClass}}
|
|
11
|
+
@imageWrapperClass={{@imageWrapperClass}}
|
|
12
|
+
@contentWrapperClass={{@contentWrapperClass}}
|
|
13
|
+
@titleClass={{@titleClass}}
|
|
14
|
+
@subtitleClass={{@subtitleClass}}
|
|
15
|
+
...attributes
|
|
16
|
+
/>
|
|
17
|
+
{{/let}}
|
|
@@ -10,20 +10,24 @@
|
|
|
10
10
|
@triggerClass="form-select form-input form-input-sm w-48"
|
|
11
11
|
as |option|
|
|
12
12
|
>
|
|
13
|
-
<div class="flex flex-row items-
|
|
13
|
+
<div class="flex flex-row items-start space-x-2">
|
|
14
14
|
<div class="inline-block">
|
|
15
|
-
|
|
15
|
+
{{#if (eq (get-model-name option) "driver")}}
|
|
16
|
+
<Image src={{option.photo_url}} @fallbackSrc={{config "defaultValues.driverAvatar"}} class="w-4 h-4" />
|
|
17
|
+
{{else}}
|
|
18
|
+
<Image src={{option.avatar_url}} @fallbackSrc={{config "defaultValues.vehicleAvatar"}} class="w-4 h-4" />
|
|
19
|
+
{{/if}}
|
|
16
20
|
</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>
|
|
21
|
+
<div class="inline-block text-sm truncate">
|
|
22
|
+
<div class="font-semibold text-sm normalize-in-trigger">{{or option.name option.displayName option.public_id}}</div>
|
|
23
|
+
<div class="hide-from-trigger text-xs">{{or option.email option.serial_number option.plate_number option.internal_id}}</div>
|
|
20
24
|
</div>
|
|
21
25
|
</div>
|
|
22
26
|
</PowerSelect>
|
|
23
27
|
</div>
|
|
24
28
|
</div>
|
|
25
29
|
|
|
26
|
-
<div class="w-48">
|
|
30
|
+
<div class="w-48 {{if this.isReplaying 'hidden' ''}}">
|
|
27
31
|
<DatePicker
|
|
28
32
|
@value={{this.dateFilter}}
|
|
29
33
|
@range={{true}}
|
|
@@ -33,23 +37,35 @@
|
|
|
33
37
|
class="w-full form-input form-input-sm"
|
|
34
38
|
/>
|
|
35
39
|
</div>
|
|
40
|
+
|
|
41
|
+
{{#if this.loadPositions.isRunning}}
|
|
42
|
+
<div>
|
|
43
|
+
<Spinner />
|
|
44
|
+
</div>
|
|
45
|
+
{{/if}}
|
|
36
46
|
</div>
|
|
37
47
|
|
|
38
48
|
<div class="replay-controls">
|
|
39
49
|
<div class="flex items-center justify-between space-x-2">
|
|
40
50
|
<div class="flex items-center space-x-2">
|
|
41
|
-
<Button @type="danger" @text="Stop" @icon="stop" @size="xs" @onClick={{this.stopReplay}} />
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
<Button @type="danger" @text="Stop" @icon="stop" @size="xs" @disabled={{not (or this.isReplaying this.isPaused)}} @onClick={{this.stopReplay}} />
|
|
52
|
+
|
|
53
|
+
{{#if (or this.isReplaying this.isPaused)}}
|
|
54
|
+
{{#if this.isReplaying}}
|
|
55
|
+
<Button @type="warning" @text="Pause" @icon="pause" @size="xs" @onClick={{this.pauseReplay}} />
|
|
56
|
+
{{else}}
|
|
57
|
+
<Button @type="success" @text="Resume" @icon="play" @size="xs" @onClick={{this.startReplay}} />
|
|
58
|
+
{{/if}}
|
|
59
|
+
{{else}}
|
|
60
|
+
<Button @type="success" @text="Play" @icon="play" @size="xs" @disabled={{not this.hasPositions}} @onClick={{this.startReplay}} />
|
|
61
|
+
{{/if}}
|
|
62
|
+
|
|
63
|
+
<div class="step-controls flex items-center space-x-1 border-l border-gray-300 dark:border-gray-700 pl-2 ml-2">
|
|
64
|
+
<Button @type="default" @icon="backward-step" @size="xs" @disabled={{not this.hasPositions}} @onClick={{this.stepBackward}} title="Step backward" />
|
|
65
|
+
<Button @type="default" @icon="forward-step" @size="xs" @disabled={{not this.hasPositions}} @onClick={{this.stepForward}} title="Step forward" />
|
|
66
|
+
</div>
|
|
51
67
|
|
|
52
|
-
<div class="speed-control">
|
|
68
|
+
<div class="speed-control border-l border-gray-300 dark:border-gray-700 pl-2 ml-2">
|
|
53
69
|
<label class="text-xs uppercase tracking-wide font-medium text-gray-700 dark:text-gray-400 mr-1">
|
|
54
70
|
Speed:
|
|
55
71
|
</label>
|
|
@@ -64,7 +80,7 @@
|
|
|
64
80
|
</div>
|
|
65
81
|
</div>
|
|
66
82
|
|
|
67
|
-
{{#if this.isReplaying}}
|
|
83
|
+
{{#if (or this.isReplaying this.isPaused)}}
|
|
68
84
|
<div class="replay-progress flex items-center space-x-1">
|
|
69
85
|
<span class="text-sm text-gray-600 dark:text-gray-400">
|
|
70
86
|
{{this.currentReplayIndex}}/{{this.totalPositions}}
|
|
@@ -81,4 +97,4 @@
|
|
|
81
97
|
</div>
|
|
82
98
|
</div>
|
|
83
99
|
<Table @rows={{this.positions}} @columns={{this.columns}} />
|
|
84
|
-
<Spacer @height="
|
|
100
|
+
<Spacer @height="400px" />
|