@fleetbase/fleetops-engine 0.6.21 → 0.6.23

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.
Files changed (67) hide show
  1. package/addon/components/device/card.hbs +1 -0
  2. package/addon/components/device/card.js +3 -0
  3. package/addon/components/device/manager.hbs +29 -0
  4. package/addon/components/device/manager.js +95 -0
  5. package/addon/components/device/pill.hbs +16 -0
  6. package/addon/components/device/pill.js +3 -0
  7. package/addon/components/driver/details.hbs +4 -0
  8. package/addon/components/driver/details.js +19 -1
  9. package/addon/components/driver/form.hbs +13 -2
  10. package/addon/components/driver/pill.hbs +17 -0
  11. package/addon/components/driver/pill.js +3 -0
  12. package/addon/components/map/drawer/device-event-listing.hbs +6 -0
  13. package/addon/components/map/drawer/position-listing.hbs +35 -19
  14. package/addon/components/map/drawer/position-listing.js +230 -64
  15. package/addon/components/modals/attach-device.hbs +18 -0
  16. package/addon/components/modals/attach-device.js +3 -0
  17. package/addon/components/order/details/detail.hbs +2 -54
  18. package/addon/components/order/details/detail.js +1 -0
  19. package/addon/components/order/pill.hbs +34 -0
  20. package/addon/components/order/pill.js +3 -0
  21. package/addon/components/positions-replay.hbs +26 -20
  22. package/addon/components/positions-replay.js +100 -63
  23. package/addon/components/telematic/form.hbs +4 -4
  24. package/addon/components/vehicle/card.hbs +1 -1
  25. package/addon/components/vehicle/details.hbs +4 -0
  26. package/addon/components/vehicle/details.js +19 -1
  27. package/addon/components/vehicle/form.hbs +4 -0
  28. package/addon/components/vehicle/pill.hbs +34 -0
  29. package/addon/components/vehicle/pill.js +3 -0
  30. package/addon/controllers/management/vehicles/index/details.js +5 -0
  31. package/addon/routes/management/drivers/index/details/positions.js +3 -0
  32. package/addon/routes.js +3 -0
  33. package/addon/services/position-playback.js +486 -0
  34. package/addon/services/resource-metadata.js +46 -0
  35. package/addon/templates/management/drivers/index/details/positions.hbs +2 -0
  36. package/addon/templates/management/vehicles/index/details/devices.hbs +1 -2
  37. package/app/components/device/card.js +1 -0
  38. package/app/components/device/manager.js +1 -0
  39. package/app/components/device/pill.js +1 -0
  40. package/app/components/driver/pill.js +1 -0
  41. package/app/components/modals/attach-device.js +1 -0
  42. package/app/components/order/pill.js +1 -0
  43. package/app/components/vehicle/pill.js +1 -0
  44. package/app/routes/management/drivers/index/details/positions.js +1 -0
  45. package/app/services/position-playback.js +1 -0
  46. package/app/services/resource-metadata.js +1 -0
  47. package/app/templates/management/drivers/index/details/positions.js +1 -0
  48. package/composer.json +1 -1
  49. package/extension.json +1 -1
  50. package/package.json +2 -2
  51. package/server/src/Console/Commands/DispatchAdhocOrders.php +47 -75
  52. package/server/src/Console/Commands/FixInvalidPolymorphicRelationTypeNamespaces.php +1 -1
  53. package/server/src/Console/Commands/TrackOrderDistanceAndTime.php +109 -65
  54. package/server/src/Http/Controllers/{Internal/v1/TelematicWebhookController.php → TelematicWebhookController.php} +1 -2
  55. package/server/src/Http/Resources/v1/Position.php +1 -1
  56. package/server/src/Models/Asset.php +10 -8
  57. package/server/src/Models/Device.php +11 -6
  58. package/server/src/Models/DeviceEvent.php +26 -3
  59. package/server/src/Models/Maintenance.php +15 -12
  60. package/server/src/Models/Order.php +20 -8
  61. package/server/src/Models/Part.php +2 -0
  62. package/server/src/Models/Place.php +4 -5
  63. package/server/src/Models/Position.php +10 -0
  64. package/server/src/Models/TrackingNumber.php +3 -1
  65. package/server/src/Models/WorkOrder.php +8 -5
  66. package/server/src/Providers/FleetOpsServiceProvider.php +1 -1
  67. package/server/src/routes.php +12 -0
@@ -0,0 +1 @@
1
+ {{yield}}
@@ -0,0 +1,3 @@
1
+ import Component from '@glimmer/component';
2
+
3
+ export default class DeviceCardComponent extends Component {}
@@ -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}}
@@ -0,0 +1,3 @@
1
+ import Component from '@glimmer/component';
2
+
3
+ export default class DevicePillComponent extends Component {}
@@ -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 src={{@resource.photo_url}} @fallbackSrc={{config "defaultValues.driverImage"}} alt={{@resource.name}} height="48" width="48" class="h-14 w-14 rounded-full shadow-sm" />
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
- <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
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}}
@@ -0,0 +1,3 @@
1
+ import Component from '@glimmer/component';
2
+
3
+ export default class DriverPillComponent extends Component {}
@@ -52,6 +52,12 @@
52
52
  class="w-full form-input form-input-sm w-48"
53
53
  />
54
54
  </div>
55
+
56
+ {{#if this.loadEvents.isRunning}}
57
+ <div>
58
+ <Spinner />
59
+ </div>
60
+ {{/if}}
55
61
  </div>
56
62
  </div>
57
63
  <Table @rows={{this.events}} @columns={{this.columns}} />
@@ -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-center space-x-2">
13
+ <div class="flex flex-row items-start space-x-2">
14
14
  <div class="inline-block">
15
- <Image src={{or option.avatar_url option.photo_url}} class="w-4 h-4" />
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
- <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
+ <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="200px" />
100
+ <Spacer @height="400px" />