@fleetbase/fleetops-engine 0.4.3 → 0.4.5

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 (116) hide show
  1. package/addon/components/contact-form-panel.hbs +1 -0
  2. package/addon/components/driver-panel.hbs +1 -0
  3. package/addon/components/edit-order-route-panel.hbs +183 -0
  4. package/addon/components/edit-order-route-panel.js +265 -0
  5. package/addon/components/fleet-form-panel.hbs +1 -0
  6. package/addon/components/fleet-panel.hbs +1 -0
  7. package/addon/components/fleet-panel.js +3 -3
  8. package/addon/components/fuel-report-form-panel.hbs +1 -0
  9. package/addon/components/fuel-report-panel.hbs +1 -0
  10. package/addon/components/issue-form-panel.hbs +1 -0
  11. package/addon/components/issue-panel.hbs +1 -0
  12. package/addon/components/map-container/toolbar.hbs +2 -2
  13. package/addon/components/modals/order-route-form.hbs +1 -1
  14. package/addon/components/place-form-panel.hbs +1 -0
  15. package/addon/components/place-panel.hbs +1 -0
  16. package/addon/components/vehicle-form-panel.hbs +1 -0
  17. package/addon/components/vehicle-panel.hbs +1 -0
  18. package/addon/components/vendor-form-panel.hbs +1 -0
  19. package/addon/components/vendor-panel.hbs +1 -0
  20. package/addon/components/widget/fleet-ops-key-metrics.hbs +19 -0
  21. package/addon/components/widget/fleet-ops-key-metrics.js +85 -0
  22. package/addon/controllers/operations/orders/index/new.js +66 -22
  23. package/addon/controllers/operations/orders/index/view.js +131 -126
  24. package/addon/controllers/operations/orders/index.js +1 -1
  25. package/addon/engine.js +18 -12
  26. package/addon/routes/operations/orders/index/view.js +1 -1
  27. package/addon/services/context-panel.js +6 -0
  28. package/addon/styles/fleetops-engine.css +7 -2
  29. package/addon/templates/operations/orders/index/new.hbs +390 -58
  30. package/addon/templates/operations/orders/index/view.hbs +135 -105
  31. package/addon/templates/operations/service-rates/index/new.hbs +3 -1
  32. package/app/components/widget/{issues.js → fleet-ops-key-metrics.js} +1 -1
  33. package/composer.json +6 -6
  34. package/extension.json +1 -1
  35. package/package.json +5 -6
  36. package/server/src/Casts/MultiPolygon.php +3 -3
  37. package/server/src/Casts/Point.php +2 -2
  38. package/server/src/Casts/Polygon.php +3 -3
  39. package/server/src/Console/Commands/DispatchAdhocOrders.php +1 -1
  40. package/server/src/Events/DriverSimulatedLocationChanged.php +1 -1
  41. package/server/src/Flow/Activity.php +12 -0
  42. package/server/src/Flow/Config.php +7 -0
  43. package/server/src/Flow/Logic.php +7 -0
  44. package/server/src/Http/Controllers/Api/v1/DriverController.php +61 -21
  45. package/server/src/Http/Controllers/Api/v1/OrderController.php +5 -3
  46. package/server/src/Http/Controllers/Api/v1/PlaceController.php +1 -1
  47. package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +1 -1
  48. package/server/src/Http/Controllers/Api/v1/TrackingStatusController.php +1 -1
  49. package/server/src/Http/Controllers/Api/v1/ZoneController.php +1 -1
  50. package/server/src/Http/Controllers/Internal/v1/DriverController.php +17 -4
  51. package/server/src/Http/Controllers/Internal/v1/GeocoderController.php +2 -2
  52. package/server/src/Http/Controllers/Internal/v1/MetricsController.php +6 -50
  53. package/server/src/Http/Controllers/Internal/v1/OrderController.php +13 -2
  54. package/server/src/Http/Controllers/Internal/v1/PlaceController.php +1 -1
  55. package/server/src/Http/Filter/PlaceFilter.php +1 -1
  56. package/server/src/Http/Resources/v1/Driver.php +1 -1
  57. package/server/src/Http/Resources/v1/FuelReport.php +1 -1
  58. package/server/src/Http/Resources/v1/Issue.php +1 -1
  59. package/server/src/Http/Resources/v1/Order.php +3 -0
  60. package/server/src/Http/Resources/v1/Payload.php +1 -0
  61. package/server/src/Http/Resources/v1/Place.php +1 -1
  62. package/server/src/Http/Resources/v1/PurchaseRate.php +2 -1
  63. package/server/src/Http/Resources/v1/ServiceQuote.php +1 -0
  64. package/server/src/Http/Resources/v1/ServiceQuoteItem.php +29 -0
  65. package/server/src/Http/Resources/v1/TrackingStatus.php +1 -1
  66. package/server/src/Http/Resources/v1/Vehicle.php +1 -1
  67. package/server/src/Http/Resources/v1/VehicleWithoutDriver.php +1 -1
  68. package/server/src/Http/Resources/v1/Waypoint.php +2 -4
  69. package/server/src/Integrations/Lalamove/LalamoveDeliveryStop.php +2 -2
  70. package/server/src/Jobs/SimulateWaypointReached.php +2 -2
  71. package/server/src/Models/Contact.php +6 -18
  72. package/server/src/Models/Driver.php +8 -20
  73. package/server/src/Models/Fleet.php +6 -25
  74. package/server/src/Models/FuelReport.php +1 -1
  75. package/server/src/Models/Issue.php +1 -1
  76. package/server/src/Models/Order.php +31 -19
  77. package/server/src/Models/Payload.php +7 -7
  78. package/server/src/Models/Place.php +16 -16
  79. package/server/src/Models/Position.php +1 -1
  80. package/server/src/Models/ServiceArea.php +14 -14
  81. package/server/src/Models/TrackingNumber.php +1 -1
  82. package/server/src/Models/TrackingStatus.php +1 -1
  83. package/server/src/Models/Vehicle.php +7 -19
  84. package/server/src/Models/VehicleDeviceEvent.php +1 -1
  85. package/server/src/Models/Vendor.php +8 -25
  86. package/server/src/Models/Waypoint.php +6 -6
  87. package/server/src/Models/Zone.php +8 -8
  88. package/server/src/Observers/DriverObserver.php +7 -2
  89. package/server/src/Observers/TrackingNumberObserver.php +1 -1
  90. package/server/src/Rules/ResolvablePoint.php +1 -1
  91. package/server/src/Support/Encoding/Polyline.php +1 -1
  92. package/server/src/Support/OSRM.php +1 -1
  93. package/server/src/Support/Utils.php +60 -60
  94. package/server/src/Traits/HasTrackingNumber.php +3 -3
  95. package/server/src/routes.php +2 -4
  96. package/translations/en-us.yaml +10 -2
  97. package/addon/components/widget/fleet-ops-quickstart.hbs +0 -40
  98. package/addon/components/widget/fleet-ops-quickstart.js +0 -59
  99. package/addon/components/widget/issues.hbs +0 -1
  100. package/addon/components/widget/key-metrics.hbs +0 -121
  101. package/addon/components/widget/key-metrics.js +0 -46
  102. package/addon/components/widget/live-order-map.hbs +0 -59
  103. package/addon/components/widget/live-order-map.js +0 -11
  104. package/addon/components/widget/recent-orders.hbs +0 -35
  105. package/addon/components/widget/recent-orders.js +0 -24
  106. package/addon/components/widget/transaction-earnings.hbs +0 -1
  107. package/addon/components/widget/transactions.hbs +0 -36
  108. package/addon/components/widget/transactions.js +0 -29
  109. package/addon/components/widget/weekly-orders.hbs +0 -1
  110. package/addon/components/widget/weekly-orders.js +0 -3
  111. package/app/components/widget/key-metrics.js +0 -1
  112. package/app/components/widget/live-order-map.js +0 -1
  113. package/app/components/widget/recent-orders.js +0 -1
  114. package/app/components/widget/transaction-earnings.js +0 -1
  115. package/app/components/widget/transactions.js +0 -1
  116. package/app/components/widget/weekly-orders.js +0 -1
@@ -103,5 +103,6 @@
103
103
  </div>
104
104
  </InputGroup>
105
105
  </div>
106
+ <Spacer @height="300px" />
106
107
  </Overlay::Body>
107
108
  </Overlay>
@@ -79,5 +79,6 @@
79
79
  <div class="tab-content tab-{{this.tab.slug}}">
80
80
  {{component this.tab.component driver=this.driver tabOptions=this.tab options=this.tab.componentParams}}
81
81
  </div>
82
+ <Spacer @height="300px" />
82
83
  </Overlay::Body>
83
84
  </Overlay>
@@ -0,0 +1,183 @@
1
+ <Overlay
2
+ @onLoad={{this.setOverlayContext}}
3
+ @onOpen={{this.onOpen}}
4
+ @onClose={{this.onClose}}
5
+ @onToggle={{this.onToggle}}
6
+ @position="right"
7
+ @noBackdrop={{true}}
8
+ @fullHeight={{true}}
9
+ @isResizeble={{or this.isResizable @isResizable}}
10
+ @width={{or this.width @width "570px"}}
11
+ >
12
+ <Overlay::Header @title={{concat "Edit order " this.order.public_id " route"}}>
13
+ <Button @type="primary" @icon="check" @text="Save" @helpText="Save order route changes" @onClick={{this.onSave}} @wrapperClass="mr-2" />
14
+ <Button @type="default" @icon="times" @helpText={{t "fleet-ops.common.cancel"}} @onClick={{this.onPressCancel}} />
15
+ </Overlay::Header>
16
+
17
+ <Overlay::Body @increaseInnerBodyHeightBy={{1000}}>
18
+ <div class="p-4">
19
+ <div class="flex flex-row justify-between mb-6">
20
+ <div class="flex">
21
+ <Toggle @isToggled={{this.order.payload.isMultiDrop}} @onToggle={{this.toggleMultiDropOrder}} @label={{t "fleet-ops.component.modals.order-route-form.toggle"}} />
22
+ </div>
23
+ <div class="flex flex-1 justify-end space-x-2">
24
+ {{#if this.order.payload.isMultiDrop}}
25
+ {{!-- <Button @type="magic" @icon="magic" @text={{t "fleet-ops.component.modals.order-route-form.optimize"}} @size="sm" @onClick={{fn this.optimizeRoute}} @helpText={{t "fleet-ops.component.modals.order-route-form.optimize-text"}} @disabled={{lt this.order.payload.waypoints.length 3}} @isLoading={{this.isOptimizingRoute}} /> --}}
26
+ <Button
27
+ @icon="map-marked-alt"
28
+ @text={{t "fleet-ops.component.modals.order-route-form.add"}}
29
+ @size="sm"
30
+ @onClick={{this.addWaypoint}}
31
+ @helpText={{t "fleet-ops.component.modals.order-route-form.add-text"}}
32
+ />
33
+ {{/if}}
34
+ </div>
35
+ </div>
36
+
37
+ {{#if this.order.payload.isMultiDrop}}
38
+ <DragSortList class="overflow-visible" @items={{this.order.payload.waypoints}} @dragEndAction={{this.sortWaypoints}} as |waypoint index|>
39
+ <div id={{concat "waypoint_" (add index 1)}} class="py-1 mb-3">
40
+ <div class="flex items-center justify-between">
41
+ <div class="flex items-center justify-between cursor-move">
42
+ <FaIcon @icon="grip-lines" @prefix="fas" class="mr-3 text-gray-100" />
43
+ <label class="waypoint-label dark:text-gray-100 text-sm">
44
+ {{waypoint-label (add index 1)}}
45
+ </label>
46
+ </div>
47
+
48
+ <div class="flex-1 px-4">
49
+ <ModelSelect
50
+ @modelName="place"
51
+ @selectedModel={{waypoint}}
52
+ @placeholder={{concat (t "fleet-ops.operations.orders.index.new.select-waypoint-placeholder") " " (add index 1)}}
53
+ @triggerClass="form-select form-input truncate max-w-300px"
54
+ @infiniteScroll={{false}}
55
+ @customSearchEndpoint="places/search"
56
+ @query={{hash geo=true latitude=this.latitude longitude=this.longitude}}
57
+ @renderInPlace={{true}}
58
+ @onChange={{fn this.setWaypointPlace index}}
59
+ as |model|
60
+ >
61
+ {{model.address}}
62
+ </ModelSelect>
63
+
64
+ {{#unless waypoint.isNew}}
65
+ {{#if waypoint.hasInvalidCoordinates}}
66
+ <div class="leading-5 text-sm text-red-400 mt-2">
67
+ <FaIcon @icon="exclamation-triangle" class="mr-1" />
68
+ {{t "fleet-ops.component.modals.order-route-form.invalid"}}
69
+ </div>
70
+ {{/if}}
71
+ {{/unless}}
72
+ </div>
73
+
74
+ <div class="flex items-center">
75
+ <Button class="mr-2" @icon="edit" @size="sm" @onClick={{fn this.editPlace waypoint}} />
76
+ <Button @type="danger" @icon="trash" @iconPrefix="fas" @size="sm" @onClick={{fn this.removeWaypoint waypoint}} />
77
+ </div>
78
+ </div>
79
+ </div>
80
+ </DragSortList>
81
+ {{else}}
82
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-3 text-xs dark:text-gray-100">
83
+ <div class="input-group">
84
+ <div class="flex items-center justify-between">
85
+ <label>{{t "fleet-ops.component.modals.order-route-form.pickup"}}</label>
86
+
87
+ {{#if this.order.payload.pickup}}
88
+ <a href="javascript:;" {{on "click" (fn this.editPlace this.order.payload.pickup)}}>{{t "fleet-ops.component.modals.order-route-form.edit-address"}}</a>
89
+ {{/if}}
90
+ </div>
91
+
92
+ <ModelSelect
93
+ @modelName="place"
94
+ @selectedModel={{this.order.payload.pickup}}
95
+ @placeholder={{t "fleet-ops.component.modals.order-route-form.select-pickup"}}
96
+ @triggerClass="form-select form-input"
97
+ @infiniteScroll={{false}}
98
+ @customSearchEndpoint="places/search"
99
+ @query={{hash geo=true latitude=this.latitude longitude=this.longitude}}
100
+ @renderInPlace={{true}}
101
+ @onChange={{fn this.setPayloadPlace "pickup"}}
102
+ as |model|
103
+ >
104
+ {{model.address}}
105
+ </ModelSelect>
106
+
107
+ {{#if this.order.payload.pickup.hasInvalidCoordinates}}
108
+ <div class="leading-5 text-sm text-red-400 mt-2">
109
+ <FaIcon @icon="exclamation-triangle" class="mr-1" />
110
+ {{t "fleet-ops.component.modals.order-route-form.invalid"}}
111
+ </div>
112
+ {{/if}}
113
+ </div>
114
+
115
+ <div class="input-group">
116
+ <div class="flex items-center justify-between">
117
+ <label>{{t "fleet-ops.component.modals.order-route-form.dropoff"}}</label>
118
+
119
+ {{#if this.order.payload.dropoff}}
120
+ <a href="javascript:;" {{on "click" (fn this.editPlace this.order.payload.dropoff)}}>{{t "fleet-ops.component.modals.order-route-form.edit-address"}}</a>
121
+ {{/if}}
122
+ </div>
123
+
124
+ <ModelSelect
125
+ @modelName="place"
126
+ @selectedModel={{this.order.payload.dropoff}}
127
+ @placeholder={{t "fleet-ops.component.modals.order-route-form.select-dropoff"}}
128
+ @triggerClass="form-select form-input"
129
+ @infiniteScroll={{false}}
130
+ @customSearchEndpoint="places/search"
131
+ @query={{hash geo=true latitude=this.latitude longitude=this.longitude}}
132
+ @renderInPlace={{true}}
133
+ @onChange={{fn this.setPayloadPlace "dropoff"}}
134
+ as |model|
135
+ >
136
+ {{model.address}}
137
+ </ModelSelect>
138
+
139
+ {{#if this.order.payload.dropoff.hasInvalidCoordinates}}
140
+ <div class="leading-5 text-sm text-red-400 mt-2">
141
+ <FaIcon @icon="exclamation-triangle" class="mr-1" />
142
+ {{t "fleet-ops.component.modals.order-route-form.invalid"}}
143
+ </div>
144
+ {{/if}}
145
+ </div>
146
+
147
+ <div class="input-group">
148
+ <div class="flex items-center justify-between">
149
+ <label>{{t "fleet-ops.component.modals.order-route-form.return"}}</label>
150
+
151
+ {{#if this.order.payload.return}}
152
+ <a href="javascript:;" {{on "click" (fn this.editPlace this.order.payload.return)}}>{{t "fleet-ops.component.modals.order-route-form.edit-address"}}</a>
153
+ {{/if}}
154
+ </div>
155
+
156
+ <ModelSelect
157
+ @modelName="place"
158
+ @selectedModel={{this.order.payload.return}}
159
+ @placeholder={{t "fleet-ops.component.modals.order-route-form.select-return"}}
160
+ @triggerClass="form-select form-input"
161
+ @infiniteScroll={{false}}
162
+ @customSearchEndpoint="places/search"
163
+ @query={{hash geo=true latitude=this.latitude longitude=this.longitude}}
164
+ @renderInPlace={{true}}
165
+ @onChange={{fn this.setPayloadPlace "return"}}
166
+ as |model|
167
+ >
168
+ {{model.address}}
169
+ </ModelSelect>
170
+
171
+ {{#if this.order.payload.return.hasInvalidCoordinates}}
172
+ <div class="leading-5 text-sm text-red-400 mt-2">
173
+ <FaIcon @icon="exclamation-triangle" class="mr-1" />
174
+ {{t "fleet-ops.component.modals.order-route-form.invalid"}}
175
+ </div>
176
+ {{/if}}
177
+ </div>
178
+ </div>
179
+ {{/if}}
180
+ </div>
181
+ <Spacer @height="300px" />
182
+ </Overlay::Body>
183
+ </Overlay>
@@ -0,0 +1,265 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { action } from '@ember/object';
4
+ import { inject as service } from '@ember/service';
5
+ import { isArray } from '@ember/array';
6
+ import contextComponentCallback from '../utils/context-component-callback';
7
+ import applyContextComponentArguments from '../utils/apply-context-component-arguments';
8
+ import getRoutingHost from '@fleetbase/ember-core/utils/get-routing-host';
9
+ import Point from '@fleetbase/fleetops-data/utils/geojson/point';
10
+ import findClosestWaypoint from '@fleetbase/ember-core/utils/find-closest-waypoint';
11
+
12
+ export default class EditOrderRoutePanelComponent extends Component {
13
+ /**
14
+ * Fetch service.
15
+ *
16
+ * @type {Service}
17
+ */
18
+ @service fetch;
19
+
20
+ /**
21
+ * Ember data store service.
22
+ *
23
+ * @type {Service}
24
+ */
25
+ @service store;
26
+
27
+ /**
28
+ * Service for managing routing within the host app.
29
+ *
30
+ * @type {Service}
31
+ */
32
+ @service hostRouter;
33
+
34
+ /**
35
+ * Service for managing the modals.
36
+ *
37
+ * @type {Service}
38
+ */
39
+ @service modalsManager;
40
+
41
+ /**
42
+ * Service for managing the context panel.
43
+ *
44
+ * @type {Service}
45
+ */
46
+ @service contextPanel;
47
+
48
+ /**
49
+ * Service for internationalization.
50
+ *
51
+ * @type {Service}
52
+ */
53
+ @service intl;
54
+
55
+ /**
56
+ * Service for notifications
57
+ *
58
+ * @type {Service}
59
+ */
60
+ @service notifications;
61
+
62
+ /**
63
+ * The orderwhich route is being edited.
64
+ *
65
+ * @type {OrderModel}
66
+ * @tracked
67
+ */
68
+ @tracked order;
69
+
70
+ /**
71
+ * Initializes the vehicle panel component.
72
+ */
73
+ constructor() {
74
+ super(...arguments);
75
+ this.order = this.args.order;
76
+ applyContextComponentArguments(this);
77
+ }
78
+
79
+ /**
80
+ * Sets the overlay context.
81
+ *
82
+ * @action
83
+ * @param {OverlayContextObject} overlayContext
84
+ */
85
+ @action setOverlayContext(overlayContext) {
86
+ this.context = overlayContext;
87
+ contextComponentCallback(this, 'onLoad', ...arguments);
88
+ }
89
+
90
+ /**
91
+ * Handles save action for the order route.
92
+ *
93
+ * @method
94
+ * @action
95
+ */
96
+ @action onSave() {
97
+ const isActionOverrided = contextComponentCallback(this, 'onSave', this.order);
98
+
99
+ if (!isActionOverrided) {
100
+ return this.order
101
+ .save()
102
+ .then((order) => {
103
+ this.notifications.success(this.intl.t('fleet-ops.operations.orders.index.view.update-success', { orderId: order.public_id }));
104
+ contextComponentCallback(this, 'onAfterSave', order);
105
+ })
106
+ .catch((error) => {
107
+ this.notifications.serverError(error);
108
+ });
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Handles the cancel action.
114
+ *
115
+ * @method
116
+ * @action
117
+ * @returns {Boolean} Indicates whether the cancel action was overridden.
118
+ */
119
+ @action onPressCancel() {
120
+ return contextComponentCallback(this, 'onPressCancel', this.order);
121
+ }
122
+
123
+ @action async editPlace(place) {
124
+ await this.modalsManager.done();
125
+
126
+ this.contextPanel.focus(place, 'editing', {
127
+ args: {
128
+ onClose: () => {
129
+ this.editOrderRoute(this.order);
130
+ },
131
+ },
132
+ });
133
+ }
134
+
135
+ @action toggleMultiDropOrder(isMultiDrop) {
136
+ const { pickup, dropoff } = this.order.payload;
137
+
138
+ if (isMultiDrop) {
139
+ // if pickup move it to multipdrop
140
+ if (pickup) {
141
+ this.addWaypointFromExistingPlace(pickup);
142
+ }
143
+
144
+ // if pickup move it to multipdrop
145
+ if (dropoff) {
146
+ this.addWaypointFromExistingPlace(dropoff);
147
+ }
148
+
149
+ this.order.payload.setProperties({
150
+ pickup_uuid: null,
151
+ dropoff_uuid: null,
152
+ });
153
+ } else {
154
+ const waypoint = this.store.createRecord('waypoint');
155
+ this.order.payload.waypoints.pushObject(waypoint);
156
+
157
+ this.order.payload.setProperties({
158
+ pickup_uuid: null,
159
+ dropoff_uuid: null,
160
+ pickup: null,
161
+ dropoff: null,
162
+ });
163
+ }
164
+
165
+ contextComponentCallback(this, 'onRouteChanged');
166
+ }
167
+
168
+ addWaypointFromExistingPlace(place) {
169
+ const waypoint = this.store.createRecord('waypoint', { place, location: place.location });
170
+ this.order.payload.waypoints.pushObject(waypoint);
171
+
172
+ // fire callback
173
+ contextComponentCallback(this, 'onWaypointAdded', waypoint);
174
+ }
175
+
176
+ @action removeWaypoint(waypoint) {
177
+ this.order.payload.waypoints.removeObject(waypoint);
178
+
179
+ // fire callback
180
+ contextComponentCallback(this, 'onWaypointRemoved', waypoint);
181
+ contextComponentCallback(this, 'onRouteChanged');
182
+ }
183
+
184
+ @action addWaypoint() {
185
+ const location = new Point(0, 0);
186
+ const place = this.store.createRecord('place', { location });
187
+ const waypoint = this.store.createRecord('waypoint', { place, location });
188
+
189
+ this.order.payload.waypoints.pushObject(waypoint);
190
+ // fire callback
191
+ contextComponentCallback(this, 'onWaypointAdded', waypoint);
192
+ }
193
+
194
+ @action setWaypointPlace(index, place) {
195
+ if (isArray(this.order.payload.waypoints) && !this.order.payload.waypoints.objectAt(index)) {
196
+ return;
197
+ }
198
+
199
+ this.order.payload.waypoints.objectAt(index).setProperties({
200
+ name: place.name,
201
+ place_uuid: place.id,
202
+ location: place.location,
203
+ place,
204
+ });
205
+
206
+ // fire callback waypoint place selected
207
+ contextComponentCallback(this, 'onWaypointPlaceSelected', place);
208
+ contextComponentCallback(this, 'onRouteChanged');
209
+ }
210
+
211
+ @action setPayloadPlace(prop, place) {
212
+ this.order.payload.set(prop, place);
213
+
214
+ // fire callback
215
+ contextComponentCallback(this, 'onPlaceSelected', place);
216
+ contextComponentCallback(this, 'onRouteChanged');
217
+ }
218
+
219
+ @action async optimizeRoute() {
220
+ this.isOptimizingRoute = true;
221
+
222
+ const coordinates = this.order.payload.payloadCoordinates;
223
+ const routingHost = getRoutingHost(this.order.payload, this.order.payload.waypoints);
224
+ let sortedWaypoints = [];
225
+
226
+ const response = await this.fetch.routing(coordinates, { source: 'any', destination: 'any', annotations: true }, { host: routingHost }).catch(() => {
227
+ this.notifications.error(this.intl.t('fleet-ops.operations.orders.index.view.route-error'));
228
+ this.isOptimizingRoute = false;
229
+ });
230
+
231
+ if (response && response.code === 'Ok') {
232
+ if (response.waypoints && isArray(response.waypoints)) {
233
+ const responseWaypoints = response.waypoints.sortBy('waypoint_index');
234
+
235
+ for (let i = 0; i < responseWaypoints.length; i++) {
236
+ const optimizedWaypoint = responseWaypoints.objectAt(i);
237
+ const optimizedWaypointLongitude = optimizedWaypoint.location.firstObject;
238
+ const optimizedWaypointLatitude = optimizedWaypoint.location.lastObject;
239
+ const waypointModel = findClosestWaypoint(optimizedWaypointLatitude, optimizedWaypointLongitude, this.order.payload.waypoints);
240
+
241
+ sortedWaypoints.pushObject(waypointModel);
242
+ }
243
+
244
+ this.order.payload.waypoints = sortedWaypoints;
245
+ }
246
+ } else {
247
+ this.notifications.error(this.intl.t('fleet-ops.operations.orders.index.view.route-error'));
248
+ }
249
+
250
+ this.isOptimizingRoute = false;
251
+ }
252
+
253
+ @action sortWaypoints({ sourceList, sourceIndex, targetList, targetIndex }) {
254
+ if (sourceList === targetList && sourceIndex === targetIndex) {
255
+ return;
256
+ }
257
+
258
+ const item = sourceList.objectAt(sourceIndex);
259
+
260
+ sourceList.removeAt(sourceIndex);
261
+ targetList.insertAt(targetIndex, item);
262
+
263
+ contextComponentCallback(this, 'onRouteChanged');
264
+ }
265
+ }
@@ -99,5 +99,6 @@
99
99
  </div>
100
100
  </div>
101
101
  {{yield}}
102
+ <Spacer @height="300px" />
102
103
  </Overlay::Body>
103
104
  </Overlay>
@@ -20,5 +20,6 @@
20
20
  <div class="tab-content tab-{{this.tab.slug}}">
21
21
  {{component this.tab.component fleet=this.fleet tabOptions=this.tab options=this.tab.componentParams}}
22
22
  </div>
23
+ <Spacer @height="300px" />
23
24
  </Overlay::Body>
24
25
  </Overlay>
@@ -61,12 +61,12 @@ export default class FleetPanelComponent extends Component {
61
61
  @tracked tab;
62
62
 
63
63
  /**
64
- * The vehicle being displayed or edited.
64
+ * The fleet being displayed or edited.
65
65
  *
66
- * @type {VehicleModel}
66
+ * @type {FleetModel}
67
67
  * @tracked
68
68
  */
69
- @tracked vehicle;
69
+ @tracked fleet;
70
70
 
71
71
  /**
72
72
  * Returns the array of tabs available for the panel.
@@ -116,5 +116,6 @@
116
116
  <CoordinatesInput @value={{this.fuelReport.location}} @onChange={{fn (mut this.fuelReport.location)}} />
117
117
  </InputGroup>
118
118
  </div>
119
+ <Spacer @height="300px" />
119
120
  </Overlay::Body>
120
121
  </Overlay>
@@ -29,5 +29,6 @@
29
29
  <div class="tab-content tab-{{this.tab.slug}}">
30
30
  {{component this.tab.component fuelReport=this.fuelReport tabOptions=this.tab options=this.tab.componentParams}}
31
31
  </div>
32
+ <Spacer @height="300px" />
32
33
  </Overlay::Body>
33
34
  </Overlay>
@@ -172,5 +172,6 @@
172
172
  <CoordinatesInput @value={{this.issue.location}} @onChange={{fn (mut this.issue.location)}} />
173
173
  </InputGroup>
174
174
  </div>
175
+ <Spacer @height="300px" />
175
176
  </Overlay::Body>
176
177
  </Overlay>
@@ -29,5 +29,6 @@
29
29
  <div class="tab-content tab-{{this.tab.slug}}">
30
30
  {{component this.tab.component issue=this.issue tabOptions=this.tab options=this.tab.componentParams}}
31
31
  </div>
32
+ <Spacer @height="300px" />
32
33
  </Overlay::Body>
33
34
  </Overlay>
@@ -33,14 +33,14 @@
33
33
  </div>
34
34
  <FaIcon @icon="layer-group" />
35
35
  <Attach::Tooltip @class="clean" @animation="scale" @placement="right">
36
- <InputInfo @text={{t "fleet-ops.component.map-container.toolbar.view"}} @iconClass="hidden" class="py-1 px-2 bg-gray-900 -mt-1" />
36
+ <InputInfo @text={{t "fleet-ops.component.map-container.toolbar.view-order"}} @iconClass="hidden" class="py-1 px-2 bg-gray-900 -mt-1" />
37
37
  </Attach::Tooltip>
38
38
  </button>
39
39
  <BasicDropdown id="map-toolbar-service-areas-button" @horizontalPosition="right" @calculatePosition={{this.calculatePosition}} @renderInPlace={{true}} as |dd|>
40
40
  <dd.Trigger class="toolbar-button">
41
41
  <FaIcon @icon="map-marked-alt" />
42
42
  <Attach::Tooltip @class="clean" @animation="scale" @placement="right">
43
- <InputInfo @text={{t "fleet-ops.component.map-container.toolbar.view"}} @iconClass="hidden" class="{{if dd.isOpen 'hidden'}} py-1 px-2 bg-gray-900 -mt-1" />
43
+ <InputInfo @text={{t "fleet-ops.component.map-container.toolbar.view-service-area"}} @iconClass="hidden" class="{{if dd.isOpen 'hidden'}} py-1 px-2 bg-gray-900 -mt-1" />
44
44
  </Attach::Tooltip>
45
45
  </dd.Trigger>
46
46
  <dd.Content>
@@ -2,7 +2,7 @@
2
2
  <div class="modal-body-container">
3
3
  <div class="flex flex-row justify-between mb-6">
4
4
  <div class="flex">
5
- <ShortToggle @isToggled={{@options.order.payload.isMultiDrop}} @onToggle={{@options.toggleMultiDropOrder}} @label={{t "fleet-ops.component.modals.order-route-form.toggle"}} />
5
+ <Toggle @isToggled={{@options.order.payload.isMultiDrop}} @onToggle={{@options.toggleMultiDropOrder}} @label={{t "fleet-ops.component.modals.order-route-form.toggle"}} />
6
6
  </div>
7
7
  <div class="flex flex-1 justify-end space-x-2">
8
8
  {{#if @options.order.payload.isMultiDrop}}
@@ -56,5 +56,6 @@
56
56
  <PhoneInput @value={{this.place.phone}} @autocomplete="nope" @onInput={{this.phone}} class="form-input w-full" />
57
57
  </InputGroup>
58
58
  </div>
59
+ <Spacer @height="300px" />
59
60
  </Overlay::Body>
60
61
  </Overlay>
@@ -29,5 +29,6 @@
29
29
  <div class="tab-content tab-{{this.tab.slug}}">
30
30
  {{component this.tab.component place=this.place tabOptions=this.tab options=this.tab.componentParams}}
31
31
  </div>
32
+ <Spacer @height="300px" />
32
33
  </Overlay::Body>
33
34
  </Overlay>
@@ -180,5 +180,6 @@
180
180
  </div>
181
181
  </ContentPanel>
182
182
  </div>
183
+ <Spacer @height="300px" />
183
184
  </Overlay::Body>
184
185
  </Overlay>
@@ -74,5 +74,6 @@
74
74
  <div class="tab-content tab-{{this.tab.slug}}">
75
75
  {{component this.tab.component vehicle=this.vehicle tabOptions=this.tab options=this.tab.componentParams}}
76
76
  </div>
77
+ <Spacer @height="300px" />
77
78
  </Overlay::Body>
78
79
  </Overlay>
@@ -87,5 +87,6 @@
87
87
  @onVendorChanged={{this.onVendorChanged}}
88
88
  />
89
89
  {{/if}}
90
+ <Spacer @height="300px" />
90
91
  </Overlay::Body>
91
92
  </Overlay>
@@ -71,5 +71,6 @@
71
71
  <div class="tab-content tab-{{this.tab.slug}}">
72
72
  {{component this.tab.component vendor=this.vendor tabOptions=this.tab params=this.tab.componentParams}}
73
73
  </div>
74
+ <Spacer @height="300px" />
74
75
  </Overlay::Body>
75
76
  </Overlay>
@@ -0,0 +1,19 @@
1
+ <div class="fleet-ops-key-metrics" ...attributes>
2
+ <div class="flex flex-col">
3
+ <div class="flex flex-row items-center justify-between px-4 py-2 border dark:border-gray-700 border-gray-200 dark:bg-gray-800 bg-gray-50 rounded-lg shadow-sm">
4
+ <span class="text-base font-bold text-black dark:text-gray-100">{{t "fleet-ops.component.widget.key-metrics.title"}}</span>
5
+ </div>
6
+
7
+ <div class="mt-4">
8
+ {{#if this.getDashboardMetrics.isRunning}}
9
+ <Spinner />
10
+ {{else}}
11
+ <div class="grid grid-cols-2 lg:grid-cols-12 gap-4">
12
+ {{#each-in this.metrics as |title options|}}
13
+ <Dashboard::Count @title={{smart-humanize title}} @options={{options}} />
14
+ {{/each-in}}
15
+ </div>
16
+ {{/if}}
17
+ </div>
18
+ </div>
19
+ </div>