@fleetbase/fleetops-engine 0.6.11 → 0.6.13

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 (48) hide show
  1. package/addon/components/customer/orders.js +6 -8
  2. package/addon/components/layout/fleet-ops-sidebar.js +8 -0
  3. package/addon/components/leaflet-tracking-marker.js +19 -0
  4. package/addon/components/live-map.hbs +5 -0
  5. package/addon/components/live-map.js +6 -8
  6. package/addon/components/order-config/fields-editor.js +1 -1
  7. package/addon/components/route-optimization-engine-select-button.hbs +25 -0
  8. package/addon/components/route-optimization-engine-select-button.js +13 -0
  9. package/addon/controllers/operations/orders/index/new.js +78 -171
  10. package/addon/controllers/operations/orders/index/view.js +30 -29
  11. package/addon/controllers/settings/routing.js +47 -0
  12. package/addon/engine.js +27 -0
  13. package/addon/routes/application.js +8 -0
  14. package/addon/routes/operations/orders/index/view.js +12 -3
  15. package/addon/routes/settings/routing.js +3 -0
  16. package/addon/routes.js +1 -0
  17. package/addon/services/leaflet-router-control.js +64 -0
  18. package/addon/services/osrm.js +46 -0
  19. package/addon/services/route-optimization-interface.js +9 -0
  20. package/addon/services/route-optimization.js +67 -0
  21. package/addon/templates/operations/orders/index/new.hbs +21 -16
  22. package/addon/templates/settings/routing.hbs +32 -0
  23. package/app/components/route-optimization-engine-select-button.js +1 -0
  24. package/app/controllers/settings/routing.js +1 -0
  25. package/app/routes/settings/routing.js +1 -0
  26. package/app/services/leaflet-router-control.js +1 -0
  27. package/app/services/osrm.js +1 -0
  28. package/app/services/route-optimization-interface.js +1 -0
  29. package/app/services/route-optimization.js +1 -0
  30. package/app/templates/settings/routing.js +1 -0
  31. package/composer.json +1 -1
  32. package/extension.json +1 -1
  33. package/package.json +2 -2
  34. package/server/config/api.php +10 -1
  35. package/server/src/Auth/Schemas/FleetOps.php +5 -0
  36. package/server/src/Console/Commands/DebugOrderTracker.php +5 -5
  37. package/server/src/Events/EntityActivityChanged.php +118 -0
  38. package/server/src/Events/EntityCompleted.php +118 -0
  39. package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +3 -1
  40. package/server/src/Http/Controllers/Internal/v1/OrderController.php +16 -13
  41. package/server/src/Http/Controllers/Internal/v1/SettingController.php +31 -1
  42. package/server/src/Http/Resources/v1/Waypoint.php +1 -1
  43. package/server/src/Models/Order.php +44 -8
  44. package/server/src/Models/Payload.php +29 -10
  45. package/server/src/Models/Place.php +49 -13
  46. package/server/src/Support/Geocoding.php +0 -2
  47. package/server/src/routes.php +2 -0
  48. package/translations/en-us.yaml +5 -0
@@ -6,8 +6,7 @@ import { isArray } from '@ember/array';
6
6
  import { later } from '@ember/runloop';
7
7
  import { debug } from '@ember/debug';
8
8
  import { task, timeout } from 'ember-concurrency';
9
- import { OSRMv1, Control as RoutingControl } from '@fleetbase/leaflet-routing-machine';
10
- import getRoutingHost from '@fleetbase/ember-core/utils/get-routing-host';
9
+ import { Control as RoutingControl } from '@fleetbase/leaflet-routing-machine';
11
10
  import engineService from '@fleetbase/ember-core/decorators/engine-service';
12
11
  import registerComponent from '@fleetbase/ember-core/utils/register-component';
13
12
  import OrderProgressCardComponent from '../order-progress-card';
@@ -26,6 +25,7 @@ export default class CustomerOrdersComponent extends Component {
26
25
  @service modalsManager;
27
26
  @service customerSession;
28
27
  @service hostRouter;
28
+ @service leafletRouterControl;
29
29
  @engineService('@fleetbase/fleetops-engine') movementTracker;
30
30
  @engineService('@fleetbase/fleetops-engine') location;
31
31
  @tracked orders = [];
@@ -226,7 +226,6 @@ export default class CustomerOrdersComponent extends Component {
226
226
 
227
227
  @action displayOrderRoute() {
228
228
  const waypoints = this.getRouteCoordinatesFromOrder(this.selectedOrder);
229
- const routingHost = getRoutingHost();
230
229
  if (this.cannotRouteWaypoints(waypoints)) {
231
230
  return;
232
231
  }
@@ -240,15 +239,14 @@ export default class CustomerOrdersComponent extends Component {
240
239
  debug(`Leaflet Map Error: ${error.message}`);
241
240
  }
242
241
 
243
- const router = new OSRMv1({
244
- serviceUrl: `${routingHost}/route/v1`,
245
- profile: 'driving',
246
- });
242
+ const routingService = this.currentUser.getOption('routing', { router: 'osrm' }).router;
243
+ const { router, formatter } = this.leafletRouterControl.get(routingService);
247
244
 
248
245
  this.routeControl = new RoutingControl({
249
- fitSelectedRoutes: false,
250
246
  router,
247
+ formatter,
251
248
  waypoints,
249
+ fitSelectedRoutes: false,
252
250
  alternativeClassName: 'hidden',
253
251
  addWaypoints: false,
254
252
  markerOptions: {
@@ -162,6 +162,14 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
162
162
  permission: 'fleet-ops view notification-settings',
163
163
  visible: this.abilities.can('fleet-ops see notification-settings'),
164
164
  },
165
+ {
166
+ intl: 'fleet-ops.component.layout.fleet-ops-sidebar.routing',
167
+ title: this.intl.t('fleet-ops.component.layout.fleet-ops-sidebar.routing'),
168
+ icon: 'route',
169
+ route: 'settings.routing',
170
+ permission: 'fleet-ops view routing-settings',
171
+ visible: this.abilities.can('fleet-ops see routing-settings'),
172
+ },
165
173
  ];
166
174
 
167
175
  const createPanel = (intl, routePrefix, items = []) => ({
@@ -1,6 +1,16 @@
1
1
  import MarkerLayer from 'ember-leaflet/components/marker-layer';
2
2
  import { isArray } from '@ember/array';
3
3
 
4
+ const __draggingHotfix = (layer) => {
5
+ if (!layer.dragging) {
6
+ layer.dragging = {
7
+ enabled: () => false,
8
+ enable: () => (layer.options.draggable = true),
9
+ disable: () => (layer.options.draggable = false),
10
+ };
11
+ }
12
+ };
13
+
4
14
  const arrayFromLatLng = (latlng) => {
5
15
  if (isArray(latlng)) {
6
16
  return latlng;
@@ -52,6 +62,7 @@ L.TrackingMarker = L.Marker.extend({
52
62
  this._slideKeepAtCenter = false;
53
63
  this._slideDraggingWasAllowed = false;
54
64
  this._slideFrame = 0;
65
+ __draggingHotfix(this);
55
66
  },
56
67
 
57
68
  slideTo: function (latlng, options = {}) {
@@ -69,6 +80,7 @@ L.TrackingMarker = L.Marker.extend({
69
80
  this._nextPosition = arrayFromLatLng(latlng);
70
81
  this._slideKeepAtCenter = !!options.keepAtCenter;
71
82
  this._slideDraggingWasAllowed = this._slideDraggingWasAllowed !== undefined ? this._slideDraggingWasAllowed : this._map.dragging.enabled();
83
+ __draggingHotfix(this);
72
84
 
73
85
  if (this._slideKeepAtCenter) {
74
86
  this._map.dragging.disable();
@@ -245,6 +257,13 @@ export default class LeafletTrackingMarkerComponent extends MarkerLayer {
245
257
  */
246
258
  rotationAngle = 0;
247
259
 
260
+ /**
261
+ * Default value for the draggable prop.
262
+ *
263
+ * @memberof LeafletTrackingMarkerComponent
264
+ */
265
+ draggable = false;
266
+
248
267
  getOption(key, defaultValue = null) {
249
268
  const value = this.options[key] ?? this[key];
250
269
  if (value === undefined) {
@@ -38,6 +38,7 @@
38
38
  @icon={{icon iconUrl=driver.vehicle_avatar iconSize=(array 24 24)}}
39
39
  @onAdd={{fn this.triggerAction "onDriverAdded" driver}}
40
40
  @onClick={{fn this.triggerAction "onDriverClicked" driver}}
41
+ @draggable={{false}}
41
42
  as |marker|
42
43
  >
43
44
  <marker.popup @maxWidth="500" @minWidth="225">
@@ -77,6 +78,7 @@
77
78
  @icon={{icon iconUrl=driver.vehicle_avatar iconSize=(array 24 24)}}
78
79
  @onAdd={{fn this.triggerAction "onDriverAdded" driver}}
79
80
  @onClick={{fn this.triggerAction "onDriverClicked" driver}}
81
+ @draggable={{false}}
80
82
  as |marker|
81
83
  >
82
84
  <marker.popup @maxWidth="500" @minWidth="225">
@@ -114,6 +116,7 @@
114
116
  @icon={{icon iconUrl=vehicle.avatar_url iconSize=(array 24 24)}}
115
117
  @onAdd={{fn this.triggerAction "onVehicleAdded" vehicle}}
116
118
  @onClick={{fn this.triggerAction "onVehicleClicked" vehicle}}
119
+ @draggable={{false}}
117
120
  as |marker|
118
121
  >
119
122
  <marker.popup @permanent={{false}} @sticky={{true}}>
@@ -150,6 +153,7 @@
150
153
  @icon={{icon iconUrl=vehicle.avatar_url iconSize=(array 24 24)}}
151
154
  @onAdd={{fn this.triggerAction "onVehicleAdded" vehicle}}
152
155
  @onClick={{fn this.triggerAction "onVehicleClicked" vehicle}}
156
+ @draggable={{false}}
153
157
  as |marker|
154
158
  >
155
159
  <marker.popup @permanent={{false}} @sticky={{true}}>
@@ -189,6 +193,7 @@
189
193
  @alt={{place.address}}
190
194
  @onAdd={{fn this.triggerAction "onPlaceAdded" place}}
191
195
  @onClick={{fn this.triggerAction "onPlaceClicked" place}}
196
+ @draggable={{false}}
192
197
  as |marker|
193
198
  >
194
199
  <marker.popup>
@@ -9,8 +9,7 @@ import { later } from '@ember/runloop';
9
9
  import { debug } from '@ember/debug';
10
10
  import { allSettled } from 'rsvp';
11
11
  import { task } from 'ember-concurrency';
12
- import { OSRMv1, Control as RoutingControl } from '@fleetbase/leaflet-routing-machine';
13
- import getRoutingHost from '@fleetbase/ember-core/utils/get-routing-host';
12
+ import { Control as RoutingControl } from '@fleetbase/leaflet-routing-machine';
14
13
  import getWithDefault from '@fleetbase/ember-core/utils/get-with-default';
15
14
 
16
15
  /**
@@ -37,6 +36,7 @@ export default class LiveMapComponent extends Component {
37
36
  @service contextPanel;
38
37
  @service leafletMapManager;
39
38
  @service leafletContextmenuManager;
39
+ @service leafletRouterControl;
40
40
  @service theme;
41
41
 
42
42
  /**
@@ -1126,7 +1126,6 @@ export default class LiveMapComponent extends Component {
1126
1126
 
1127
1127
  // create order route preview
1128
1128
  const waypoints = this.getRouteCoordinatesFromOrder(order);
1129
- const routingHost = getRoutingHost();
1130
1129
  if (this.cannotRouteWaypoints(waypoints)) {
1131
1130
  return;
1132
1131
  }
@@ -1140,15 +1139,14 @@ export default class LiveMapComponent extends Component {
1140
1139
  debug(`Leaflet Map Error: ${error.message}`);
1141
1140
  }
1142
1141
 
1143
- const router = new OSRMv1({
1144
- serviceUrl: `${routingHost}/route/v1`,
1145
- profile: 'driving',
1146
- });
1142
+ const routingService = this.currentUser.getOption('routing', { router: 'osrm' }).router;
1143
+ const { router, formatter } = this.leafletRouterControl.get(routingService);
1147
1144
 
1148
1145
  this.routeControl = new RoutingControl({
1149
- fitSelectedRoutes: false,
1150
1146
  router,
1147
+ formatter,
1151
1148
  waypoints,
1149
+ fitSelectedRoutes: false,
1152
1150
  alternativeClassName: 'hidden',
1153
1151
  addWaypoints: false,
1154
1152
  markerOptions: {
@@ -197,7 +197,7 @@ export default class OrderConfigFieldsEditorComponent extends Component {
197
197
  }
198
198
  }
199
199
 
200
- /* eslint no-unused-vars: "off" */
200
+ /* eslint-disable no-unused-vars */
201
201
  @action sortMetaFieldOptions(metaField, el, target) {
202
202
  // const { fields } = this;
203
203
  // const { index } = el.dataset;
@@ -0,0 +1,25 @@
1
+ <DropdownButton
2
+ @text="Optimize Route"
3
+ @icon="caret-down"
4
+ @type="magic"
5
+ @size="sm"
6
+ @iconPrefix="fas"
7
+ @triggerClass="hidden md:flex"
8
+ @helpText="Optimize route using an integrated optimization engine"
9
+ @disabled={{@disabled}}
10
+ @isLoading={{@isLoading}}
11
+ @renderInPlace={{true}}
12
+ as |dd|
13
+ >
14
+ <div class="next-dd-menu" aria-labelledby="user-menu">
15
+ <div class="px-1">
16
+ {{#each this.routeOptimization.availableServices as |service|}}
17
+ <a href="javascript:;" class="next-dd-item" {{on "click" (dropdown-fn dd this.handleClick service)}}>
18
+ <div >
19
+ <div class="text-sm">{{service.name}}</div>
20
+ </div>
21
+ </a>
22
+ {{/each}}
23
+ </div>
24
+ </div>
25
+ </DropdownButton>
@@ -0,0 +1,13 @@
1
+ import Component from '@glimmer/component';
2
+ import { inject as service } from '@ember/service';
3
+ import { action } from '@ember/object';
4
+
5
+ export default class RouteOptimizationEngineSelectButtonComponent extends Component {
6
+ @service routeOptimization;
7
+
8
+ @action handleClick({ key }) {
9
+ if (typeof this.args.onClick === 'function') {
10
+ this.args.onClick(key);
11
+ }
12
+ }
13
+ }
@@ -9,11 +9,9 @@ import { isBlank } from '@ember/utils';
9
9
  import { dasherize } from '@ember/string';
10
10
  import { next } from '@ember/runloop';
11
11
  import { task } from 'ember-concurrency';
12
- import { OSRMv1, Control as RoutingControl } from '@fleetbase/leaflet-routing-machine';
12
+ import { Control as RoutingControl } from '@fleetbase/leaflet-routing-machine';
13
13
  import { debug } from '@ember/debug';
14
- import polyline from '@fleetbase/ember-core/utils/polyline';
15
14
  import isNotEmpty from '@fleetbase/ember-core/utils/is-not-empty';
16
- import getRoutingHost from '@fleetbase/ember-core/utils/get-routing-host';
17
15
  import getWithDefault from '@fleetbase/ember-core/utils/get-with-default';
18
16
  import isModel from '@fleetbase/ember-core/utils/is-model';
19
17
 
@@ -31,95 +29,22 @@ L.Bounds.prototype.intersects = function (bounds) {
31
29
  export default class OperationsOrdersIndexNewController extends BaseController {
32
30
  @controller('operations.orders.index') ordersController;
33
31
 
34
- /**
35
- * Inject the `modalsManager` service
36
- *
37
- * @var {Service}
38
- */
39
32
  @service modalsManager;
40
-
41
- /**
42
- * Inject the `notifications` service
43
- *
44
- * @var {Service}
45
- */
46
33
  @service notifications;
47
-
48
- /**
49
- * Inject the `loader` service
50
- *
51
- * @var {Service}
52
- */
53
34
  @service loader;
54
-
55
- /**
56
- * Inject the `currentUser` service
57
- *
58
- * @var {Service}
59
- */
60
35
  @service currentUser;
61
-
62
- /**
63
- * Inject the `hostRouter` service
64
- *
65
- * @var {Service}
66
- */
67
36
  @service hostRouter;
68
-
69
- /**
70
- * Inject the `fileQueue` service
71
- *
72
- * @var {Service}
73
- */
74
37
  @service fileQueue;
75
-
76
- /**
77
- * Inject the `intl` service
78
- *
79
- * @var {Service}
80
- */
81
38
  @service intl;
82
-
83
- /**
84
- * Inject the `fetch` service
85
- *
86
- * @var {Service}
87
- */
88
39
  @service fetch;
89
-
90
- /**
91
- * Inject the `store` service
92
- *
93
- * @var {Service}
94
- */
95
40
  @service store;
96
-
97
- /**
98
- * Inject the `contextPanel` service
99
- *
100
- * @var {Service}
101
- */
102
41
  @service contextPanel;
103
-
104
- /**
105
- * Inject the `universe` service
106
- *
107
- * @var {Service}
108
- */
109
42
  @service universe;
43
+ @service routeOptimization;
44
+ @service leafletRouterControl;
45
+ @service osrm;
110
46
 
111
- /**
112
- * Create an OrderModel instance.
113
- *
114
- * @var {OrderModel}
115
- */
116
47
  @tracked order = this.store.createRecord('order', { meta: [] });
117
-
118
- /**
119
- * Create an PayloadModel instance.
120
- *
121
- * @var {OrderModel}
122
- */
123
48
  @tracked payload = this.store.createRecord('payload');
124
49
  @tracked driversQuery = {};
125
50
  @tracked vehiclesQuery = {};
@@ -139,7 +64,6 @@ export default class OperationsOrdersIndexNewController extends BaseController {
139
64
  @tracked isCreatingOrder = false;
140
65
  @tracked isMultipleDropoffOrder = false;
141
66
  @tracked isViewingRoutePreview = false;
142
- @tracked isOptimizingRoute = false;
143
67
  @tracked optimizedRouteMarkers = [];
144
68
  @tracked optimizedRoutePolyline;
145
69
  @tracked isFetchingQuotes = false;
@@ -191,14 +115,8 @@ export default class OperationsOrdersIndexNewController extends BaseController {
191
115
  'application/x-tar',
192
116
  ];
193
117
 
194
- get renderableComponents() {
195
- const renderableComponents = this.universe.getRenderableComponentsFromRegistry('fleet-ops:template:operations:orders:new');
196
- return renderableComponents;
197
- }
198
-
199
- get renderableEntityInputComponents() {
200
- const renderableComponents = this.universe.getRenderableComponentsFromRegistry('fleet-ops:template:operations:orders:new:entities-input');
201
- return renderableComponents;
118
+ get hasRouteOptimizationEngines() {
119
+ return this.routeOptimization.availableEngines.length > 1;
202
120
  }
203
121
 
204
122
  @not('isServicable') isNotServicable;
@@ -677,7 +595,7 @@ export default class OperationsOrdersIndexNewController extends BaseController {
677
595
 
678
596
  @action resetInterface() {
679
597
  if (this.leafletMap && this.leafletMap.liveMap) {
680
- this.leafletMap.liveMap.show(['drivers', 'vehicles', 'routes']);
598
+ this.leafletMap.liveMap.showAll();
681
599
  }
682
600
  }
683
601
 
@@ -709,31 +627,35 @@ export default class OperationsOrdersIndexNewController extends BaseController {
709
627
  }
710
628
 
711
629
  @action removeRoutingControlPreview() {
712
- const leafletMap = this.leafletMap;
713
- const previewRouteControl = this.previewRouteControl;
630
+ return new Promise((resolve) => {
631
+ const leafletMap = this.leafletMap;
632
+ const previewRouteControl = this.previewRouteControl;
714
633
 
715
- let removed = false;
634
+ let removed = false;
716
635
 
717
- if (leafletMap && previewRouteControl instanceof RoutingControl) {
718
- try {
719
- previewRouteControl.remove();
720
- removed = true;
721
- } catch (e) {
722
- // silent
723
- }
724
-
725
- if (!removed) {
636
+ if (leafletMap && previewRouteControl instanceof RoutingControl) {
726
637
  try {
727
- leafletMap.removeControl(previewRouteControl);
638
+ previewRouteControl.remove();
639
+ removed = true;
728
640
  } catch (e) {
729
641
  // silent
730
642
  }
643
+
644
+ if (!removed) {
645
+ try {
646
+ leafletMap.removeControl(previewRouteControl);
647
+ } catch (e) {
648
+ // silent
649
+ }
650
+ }
731
651
  }
732
- }
733
652
 
734
- if (!removed) {
735
- this.forceRemoveRoutePreview();
736
- }
653
+ if (!removed) {
654
+ this.forceRemoveRoutePreview();
655
+ }
656
+
657
+ resolve(true);
658
+ });
737
659
  }
738
660
 
739
661
  @action forceRemoveRoutePreview() {
@@ -869,13 +791,12 @@ export default class OperationsOrdersIndexNewController extends BaseController {
869
791
  const canPreviewRoute = this.routePreviewArray.length > 0;
870
792
 
871
793
  if (canPreviewRoute) {
872
- const routingHost = getRoutingHost(payload, waypoints);
873
- const router = new OSRMv1({
874
- serviceUrl: `${routingHost}/route/v1`,
875
- profile: 'driving',
876
- });
794
+ const routingService = this.currentUser.getOption('routing', { router: 'osrm' }).router;
795
+ const { router, formatter } = this.leafletRouterControl.get(routingService);
877
796
 
878
797
  this.previewRouteControl = new RoutingControl({
798
+ router,
799
+ formatter,
879
800
  waypoints: this.routePreviewCoordinates,
880
801
  alternativeClassName: 'hidden',
881
802
  addWaypoints: false,
@@ -888,7 +809,6 @@ export default class OperationsOrdersIndexNewController extends BaseController {
888
809
  iconAnchor: [12, 41],
889
810
  }),
890
811
  },
891
- router,
892
812
  }).addTo(leafletMap);
893
813
 
894
814
  this.previewRouteControl.on('routesfound', (event) => {
@@ -935,68 +855,54 @@ export default class OperationsOrdersIndexNewController extends BaseController {
935
855
  }
936
856
  }
937
857
 
938
- @task *optimizeRoute() {
939
- this.isOptimizingRoute = true;
940
-
941
- // Build the coordinate list we’ll send to OSRM
942
- const driverAssigned = this.order.driver_assigned;
943
- const driverPosition = driverAssigned?.location?.coordinates; // [lon,lat] | undefined
944
- const originalCoords = this.getCoordinatesFromPayload(); // [[lon,lat], …]
945
- const coordinates = driverPosition ? [driverPosition, ...originalCoords] : [...originalCoords];
946
- const hasDriverStart = Boolean(driverPosition);
947
- const source = 'first';
948
- const destination = 'any';
949
- const roundtrip = false; // don’t loop back
950
- const routingHost = getRoutingHost(this.payload, this.waypoints);
951
-
952
- // Call the OSRM /trip service
858
+ @task *optimizeRouteWithService(service) {
953
859
  try {
954
- const response = yield this.fetch.routing(coordinates, { source, destination, roundtrip, annotations: true }, { host: routingHost });
955
-
956
- if (response?.code !== 'Ok') {
957
- throw new Error(`OSRM error: ${response?.code}`);
958
- }
959
-
960
- // Pair each OSRM waypoint with its Waypoint model
961
- const modelsByInputIndex = hasDriverStart ? [null, ...this.waypoints] : this.waypoints;
962
-
963
- const pairs = response.waypoints.map((wp, idx) => ({
964
- model: modelsByInputIndex[idx], // Ember model or null (driver)
965
- wp,
966
- }));
967
-
968
- // Drop the driver start if present
969
- const payloadPairs = hasDriverStart ? pairs.slice(1) : pairs;
970
-
971
- // Sort by the optimised order
972
- payloadPairs.sort((a, b) => a.wp.waypoint_index - b.wp.waypoint_index);
973
-
974
- // Extract the Ember models (null-safe)
975
- const sortedWaypoints = payloadPairs.map((p) => p.model).filter(Boolean);
860
+ const result = yield this.routeOptimization.optimize(service, {
861
+ context: 'create_order',
862
+ order: this.order,
863
+ payload: this.payload,
864
+ waypoints: this.waypoints,
865
+ coordinates: this.getCoordinatesFromPayload(),
866
+ });
867
+ this.handleRouteOptimization(result);
868
+ } catch (err) {
869
+ this.notifications.error(err.message ?? this.intl.t('fleet-ops.operations.orders.index.new.route-error'));
870
+ }
871
+ }
976
872
 
977
- // Update map layers & UI
978
- this.removeRoutingControlPreview();
979
- this.removeOptimizedRoute(this.leafletMap);
980
- this.clearLayers();
873
+ @task *optimizeRoute() {
874
+ try {
875
+ const result = yield this.osrm.optimize({
876
+ context: 'create_order',
877
+ order: this.order,
878
+ payload: this.payload,
879
+ waypoints: this.waypoints,
880
+ coordinates: this.getCoordinatesFromPayload(),
881
+ });
981
882
 
982
- const trip = response.trips?.[0];
983
- const route = polyline.decode(trip.geometry); // [[lat,lon], …]
883
+ this.handleRouteOptimization(result);
884
+ } catch (err) {
885
+ this.notifications.error(this.intl.t('fleet-ops.operations.orders.index.new.route-error'));
886
+ }
887
+ }
984
888
 
985
- this.waypoints = sortedWaypoints;
986
- this.setOptimizedRoute(route, trip, response.waypoints);
987
- this.previewDraftOrderRoute(this.payload, this.waypoints, this.isMultipleDropoffOrder);
988
- this.updatePayloadCoordinates();
889
+ handleRouteOptimization({ sortedWaypoints, route, trip, result }) {
890
+ // Update map layers & UI
891
+ this.removeRoutingControlPreview();
892
+ this.removeOptimizedRoute(this.leafletMap);
893
+ this.clearLayers();
989
894
 
990
- this.order.set('is_route_optimized', true);
895
+ // Update controller state
896
+ this.waypoints = sortedWaypoints;
897
+ if (route) {
898
+ this.setOptimizedRoute(route, trip, result.waypoints);
899
+ }
900
+ this.previewDraftOrderRoute(this.payload, this.waypoints, this.isMultipleDropoffOrder);
901
+ this.updatePayloadCoordinates();
991
902
 
992
- if (this.isUsingIntegratedVendor) {
993
- this.getQuotes();
994
- }
995
- } catch (err) {
996
- debug('Error optimizing route', err);
997
- this.notifications.error(this.intl.t('fleet-ops.operations.orders.index.new.route-error'));
998
- } finally {
999
- this.isOptimizingRoute = false;
903
+ this.order.set('is_route_optimized', true);
904
+ if (this.isUsingIntegratedVendor) {
905
+ this.getQuotes();
1000
906
  }
1001
907
  }
1002
908
 
@@ -1060,7 +966,7 @@ export default class OperationsOrdersIndexNewController extends BaseController {
1060
966
  }
1061
967
  }
1062
968
 
1063
- @action resetForm() {
969
+ @action async resetForm() {
1064
970
  const order = this.store.createRecord('order', { meta: [] });
1065
971
  const payload = this.store.createRecord('payload');
1066
972
  const driversQuery = {};
@@ -1080,8 +986,6 @@ export default class OperationsOrdersIndexNewController extends BaseController {
1080
986
  const customFields = [];
1081
987
  const customFieldValues = {};
1082
988
 
1083
- this.removeRoutingControlPreview();
1084
- this.removeOptimizedRoute();
1085
989
  this.setProperties({
1086
990
  order,
1087
991
  payload,
@@ -1102,6 +1006,9 @@ export default class OperationsOrdersIndexNewController extends BaseController {
1102
1006
  customFields,
1103
1007
  customFieldValues,
1104
1008
  });
1009
+
1010
+ await this.removeRoutingControlPreview();
1011
+ this.removeOptimizedRoute();
1105
1012
  this.resetInterface();
1106
1013
  }
1107
1014