@fleetbase/fleetops-engine 0.6.12 → 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 (42) 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/live-map.js +6 -8
  4. package/addon/components/order-config/fields-editor.js +1 -1
  5. package/addon/components/route-optimization-engine-select-button.hbs +25 -0
  6. package/addon/components/route-optimization-engine-select-button.js +13 -0
  7. package/addon/controllers/operations/orders/index/new.js +52 -150
  8. package/addon/controllers/operations/orders/index/view.js +8 -9
  9. package/addon/controllers/settings/routing.js +47 -0
  10. package/addon/engine.js +27 -0
  11. package/addon/routes/application.js +8 -0
  12. package/addon/routes/operations/orders/index/view.js +12 -3
  13. package/addon/routes/settings/routing.js +3 -0
  14. package/addon/routes.js +1 -0
  15. package/addon/services/leaflet-router-control.js +64 -0
  16. package/addon/services/osrm.js +46 -0
  17. package/addon/services/route-optimization-interface.js +9 -0
  18. package/addon/services/route-optimization.js +67 -0
  19. package/addon/templates/operations/orders/index/new.hbs +21 -16
  20. package/addon/templates/settings/routing.hbs +32 -0
  21. package/app/components/route-optimization-engine-select-button.js +1 -0
  22. package/app/controllers/settings/routing.js +1 -0
  23. package/app/routes/settings/routing.js +1 -0
  24. package/app/services/leaflet-router-control.js +1 -0
  25. package/app/services/osrm.js +1 -0
  26. package/app/services/route-optimization-interface.js +1 -0
  27. package/app/services/route-optimization.js +1 -0
  28. package/app/templates/settings/routing.js +1 -0
  29. package/composer.json +1 -1
  30. package/extension.json +1 -1
  31. package/package.json +2 -2
  32. package/server/config/api.php +10 -1
  33. package/server/src/Auth/Schemas/FleetOps.php +5 -0
  34. package/server/src/Events/EntityActivityChanged.php +118 -0
  35. package/server/src/Events/EntityCompleted.php +118 -0
  36. package/server/src/Http/Controllers/Internal/v1/OrderController.php +1 -1
  37. package/server/src/Http/Controllers/Internal/v1/SettingController.php +30 -0
  38. package/server/src/Models/Order.php +6 -0
  39. package/server/src/Models/Payload.php +19 -8
  40. package/server/src/Models/Place.php +49 -13
  41. package/server/src/routes.php +2 -0
  42. 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 = []) => ({
@@ -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;
@@ -873,13 +791,12 @@ export default class OperationsOrdersIndexNewController extends BaseController {
873
791
  const canPreviewRoute = this.routePreviewArray.length > 0;
874
792
 
875
793
  if (canPreviewRoute) {
876
- const routingHost = getRoutingHost(payload, waypoints);
877
- const router = new OSRMv1({
878
- serviceUrl: `${routingHost}/route/v1`,
879
- profile: 'driving',
880
- });
794
+ const routingService = this.currentUser.getOption('routing', { router: 'osrm' }).router;
795
+ const { router, formatter } = this.leafletRouterControl.get(routingService);
881
796
 
882
797
  this.previewRouteControl = new RoutingControl({
798
+ router,
799
+ formatter,
883
800
  waypoints: this.routePreviewCoordinates,
884
801
  alternativeClassName: 'hidden',
885
802
  addWaypoints: false,
@@ -892,7 +809,6 @@ export default class OperationsOrdersIndexNewController extends BaseController {
892
809
  iconAnchor: [12, 41],
893
810
  }),
894
811
  },
895
- router,
896
812
  }).addTo(leafletMap);
897
813
 
898
814
  this.previewRouteControl.on('routesfound', (event) => {
@@ -939,68 +855,54 @@ export default class OperationsOrdersIndexNewController extends BaseController {
939
855
  }
940
856
  }
941
857
 
942
- @task *optimizeRoute() {
943
- this.isOptimizingRoute = true;
944
-
945
- // Build the coordinate list we’ll send to OSRM
946
- const driverAssigned = this.order.driver_assigned;
947
- const driverPosition = driverAssigned?.location?.coordinates; // [lon,lat] | undefined
948
- const originalCoords = this.getCoordinatesFromPayload(); // [[lon,lat], …]
949
- const coordinates = driverPosition ? [driverPosition, ...originalCoords] : [...originalCoords];
950
- const hasDriverStart = Boolean(driverPosition);
951
- const source = 'first';
952
- const destination = 'any';
953
- const roundtrip = false; // don’t loop back
954
- const routingHost = getRoutingHost(this.payload, this.waypoints);
955
-
956
- // Call the OSRM /trip service
858
+ @task *optimizeRouteWithService(service) {
957
859
  try {
958
- const response = yield this.fetch.routing(coordinates, { source, destination, roundtrip, annotations: true }, { host: routingHost });
959
-
960
- if (response?.code !== 'Ok') {
961
- throw new Error(`OSRM error: ${response?.code}`);
962
- }
963
-
964
- // Pair each OSRM waypoint with its Waypoint model
965
- const modelsByInputIndex = hasDriverStart ? [null, ...this.waypoints] : this.waypoints;
966
-
967
- const pairs = response.waypoints.map((wp, idx) => ({
968
- model: modelsByInputIndex[idx], // Ember model or null (driver)
969
- wp,
970
- }));
971
-
972
- // Drop the driver start if present
973
- const payloadPairs = hasDriverStart ? pairs.slice(1) : pairs;
974
-
975
- // Sort by the optimised order
976
- payloadPairs.sort((a, b) => a.wp.waypoint_index - b.wp.waypoint_index);
977
-
978
- // Extract the Ember models (null-safe)
979
- 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
+ }
980
872
 
981
- // Update map layers & UI
982
- this.removeRoutingControlPreview();
983
- this.removeOptimizedRoute(this.leafletMap);
984
- 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
+ });
985
882
 
986
- const trip = response.trips?.[0];
987
- 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
+ }
988
888
 
989
- this.waypoints = sortedWaypoints;
990
- this.setOptimizedRoute(route, trip, response.waypoints);
991
- this.previewDraftOrderRoute(this.payload, this.waypoints, this.isMultipleDropoffOrder);
992
- this.updatePayloadCoordinates();
889
+ handleRouteOptimization({ sortedWaypoints, route, trip, result }) {
890
+ // Update map layers & UI
891
+ this.removeRoutingControlPreview();
892
+ this.removeOptimizedRoute(this.leafletMap);
893
+ this.clearLayers();
993
894
 
994
- 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();
995
902
 
996
- if (this.isUsingIntegratedVendor) {
997
- this.getQuotes();
998
- }
999
- } catch (err) {
1000
- debug('Error optimizing route', err);
1001
- this.notifications.error(this.intl.t('fleet-ops.operations.orders.index.new.route-error'));
1002
- } finally {
1003
- this.isOptimizingRoute = false;
903
+ this.order.set('is_route_optimized', true);
904
+ if (this.isUsingIntegratedVendor) {
905
+ this.getQuotes();
1004
906
  }
1005
907
  }
1006
908
 
@@ -6,8 +6,7 @@ import { action, computed } from '@ember/object';
6
6
  import { later } from '@ember/runloop';
7
7
  import { not, notEmpty, alias } from '@ember/object/computed';
8
8
  import { task } 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
 
12
11
  export default class OperationsOrdersIndexViewController extends BaseController {
13
12
  @controller('operations.orders.index') ordersController;
@@ -24,6 +23,7 @@ export default class OperationsOrdersIndexViewController extends BaseController
24
23
  @service socket;
25
24
  @service universe;
26
25
  @service contextPanel;
26
+ @service leafletRouterControl;
27
27
 
28
28
  @tracked isLoadingAdditionalData = false;
29
29
  @tracked isWaypointsCollapsed;
@@ -276,6 +276,7 @@ export default class OperationsOrdersIndexViewController extends BaseController
276
276
  }
277
277
 
278
278
  this.forceRemoveRoutePreview();
279
+ this.routeControl = null;
279
280
  resolve(true);
280
281
  });
281
282
  }
@@ -372,9 +373,8 @@ export default class OperationsOrdersIndexViewController extends BaseController
372
373
  const leafletMap = this.leafletMap;
373
374
  const payload = this.model.payload;
374
375
  const waypoints = this.getPayloadCoordinates(payload);
375
- const routingHost = getRoutingHost(payload, this.getPayloadWaypointsAsArray());
376
376
 
377
- if (!waypoints || waypoints.length < 2 || !leafletMap) {
377
+ if (!waypoints || waypoints.length < 2 || !leafletMap || this.routeControl) {
378
378
  return;
379
379
  }
380
380
 
@@ -382,12 +382,12 @@ export default class OperationsOrdersIndexViewController extends BaseController
382
382
  leafletMap.stop();
383
383
  leafletMap.flyTo(waypoints.firstObject);
384
384
 
385
- const router = new OSRMv1({
386
- serviceUrl: `${routingHost}/route/v1`,
387
- profile: 'driving',
388
- });
385
+ const routingService = this.currentUser.getOption('routing', { router: 'osrm' }).router;
386
+ const { router, formatter } = this.leafletRouterControl.get(routingService);
389
387
 
390
388
  this.routeControl = new RoutingControl({
389
+ router,
390
+ formatter,
391
391
  waypoints,
392
392
  markerOptions: {
393
393
  icon: L.icon({
@@ -400,7 +400,6 @@ export default class OperationsOrdersIndexViewController extends BaseController
400
400
  },
401
401
  alternativeClassName: 'hidden',
402
402
  addWaypoints: false,
403
- router,
404
403
  }).addTo(leafletMap);
405
404
 
406
405
  this.routeControl.on('routesfound', (event) => {
@@ -0,0 +1,47 @@
1
+ import Controller from '@ember/controller';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { inject as service } from '@ember/service';
4
+ import { task } from 'ember-concurrency';
5
+
6
+ export default class SettingsRoutingController extends Controller {
7
+ @service fetch;
8
+ @service notifications;
9
+ @service currentUser;
10
+ @service leafletRouterControl;
11
+ @tracked routerService = 'osrm';
12
+
13
+ constructor() {
14
+ super(...arguments);
15
+ this.getSettings.perform();
16
+ }
17
+
18
+ /**
19
+ * Save routing settings.
20
+ *
21
+ * @memberof SettingsRoutingController
22
+ */
23
+ @task *saveSettings() {
24
+ try {
25
+ yield this.fetch.post('fleet-ops/settings/routing-settings', { router: this.routerService });
26
+ // Save in local memory too
27
+ this.currentUser.setOption('routing', { router: this.routerService });
28
+ this.notifications.success('Routing setting saved.');
29
+ } catch (error) {
30
+ this.notifications.serverError(error);
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Get routing settings.
36
+ *
37
+ * @memberof SettingsRoutingController
38
+ */
39
+ @task *getSettings() {
40
+ try {
41
+ const { router } = yield this.fetch.get('fleet-ops/settings/routing-settings');
42
+ this.routerService = router;
43
+ } catch (error) {
44
+ this.notifications.serverError(error);
45
+ }
46
+ }
47
+ }
package/addon/engine.js CHANGED
@@ -9,6 +9,9 @@ import AdminAvatarManagementComponent from './components/admin/avatar-management
9
9
  import CustomerOrdersComponent from './components/customer/orders';
10
10
  import CustomerAdminSettingsComponent from './components/customer/admin-settings';
11
11
  import OrderTrackingLookupComponent from './components/order-tracking-lookup';
12
+ import { RouterControl } from './services/leaflet-router-control';
13
+ import { OSRMv1 } from '@fleetbase/leaflet-routing-machine';
14
+ import getRoutingHost from '@fleetbase/ember-core/utils/get-routing-host';
12
15
 
13
16
  const { modulePrefix } = config;
14
17
  const externalRoutes = ['console', 'extensions'];
@@ -57,6 +60,29 @@ export default class FleetOpsEngine extends Engine {
57
60
  },
58
61
  });
59
62
 
63
+ // Register OSRM as route optimization service
64
+ const routeOptimization = app.lookup('service:route-optimization');
65
+ const osrm = app.lookup('service:osrm');
66
+ if (routeOptimization && osrm) {
67
+ routeOptimization.register('osrm', osrm);
68
+ }
69
+
70
+ // Register OSRM as Routing Controler
71
+ const leafletRouterControl = app.lookup('service:leaflet-router-control');
72
+ if (leafletRouterControl) {
73
+ const routingHost = getRoutingHost();
74
+ leafletRouterControl.register(
75
+ 'osrm',
76
+ new RouterControl({
77
+ name: 'OSRM',
78
+ router: new OSRMv1({
79
+ serviceUrl: `${routingHost}/route/v1`,
80
+ profile: 'driving',
81
+ }),
82
+ })
83
+ );
84
+ }
85
+
60
86
  // widgets for registry
61
87
  const KeyMetricsWidgetDefinition = {
62
88
  widgetId: 'fleet-ops-key-metrics-widget',
@@ -106,6 +132,7 @@ export default class FleetOpsEngine extends Engine {
106
132
  'fleet-ops:template:operations:orders:view',
107
133
  'fleet-ops:template:operations:orders:new',
108
134
  'fleet-ops:template:operations:orders:new:entities-input',
135
+ 'fleet-ops:template:operations:orders:new:entities-input:entity',
109
136
  ]);
110
137
 
111
138
  universe.afterBoot(function (universe) {
@@ -10,6 +10,8 @@ export default class ApplicationRoute extends Route {
10
10
  @service abilities;
11
11
  @service hostRouter;
12
12
  @service notifications;
13
+ @service fetch;
14
+ @service currentUser;
13
15
 
14
16
  @action loading(transition) {
15
17
  const resourceName = getResourceNameFromTransition(transition, { humanize: true });
@@ -25,5 +27,11 @@ export default class ApplicationRoute extends Route {
25
27
  }
26
28
 
27
29
  this.location.getUserLocation();
30
+ this.#loadRoutingSettings();
31
+ }
32
+
33
+ async #loadRoutingSettings() {
34
+ const routingSetting = await this.fetch.get('fleet-ops/settings/routing-settings');
35
+ this.currentUser.setOption('routing', routingSetting);
28
36
  }
29
37
  }
@@ -12,10 +12,18 @@ export default class OperationsOrdersIndexViewRoute extends Route {
12
12
  @service abilities;
13
13
  @service intl;
14
14
 
15
- @action willTransition() {
16
- if (this.controller) {
15
+ @action willTransition(transition) {
16
+ // if we're coming _from_ this route and going _to_ a different one, then reset
17
+ let fromName = transition.from?.name;
18
+ let toName = transition.to.name;
19
+
20
+ // transition.from is null on initial page load, so guard that
21
+ if (fromName && fromName !== toName) {
17
22
  this.controller.resetView();
18
23
  }
24
+
25
+ // bubble the event on up so Ember can finish the transition
26
+ return true;
19
27
  }
20
28
 
21
29
  @action error(error) {
@@ -75,13 +83,14 @@ export default class OperationsOrdersIndexViewRoute extends Route {
75
83
  // Only reload if the order has a status change stemming from an updated event OR
76
84
  // if a waypoint has been completed which will trigger `order.completed`
77
85
  const statusChanged = event === 'order.updated' && data.status !== model.status;
78
- const shouldReload = ['order.completed', 'waypoint.activity', 'order.created'].includes(event);
86
+ const shouldReload = ['order.completed', 'waypoint.activity', 'entity.activity', 'order.created'].includes(event);
79
87
  if (statusChanged || shouldReload) {
80
88
  this.refresh();
81
89
 
82
90
  // reload the controller stuff as well
83
91
  if (this.controller) {
84
92
  this.controller.loadOrderRelations.perform(model);
93
+ this.controller.displayOrderRoute();
85
94
  }
86
95
  }
87
96
 
@@ -0,0 +1,3 @@
1
+ import Route from '@ember/routing/route';
2
+
3
+ export default class SettingsRoutingRoute extends Route {}
package/addon/routes.js CHANGED
@@ -4,6 +4,7 @@ export default buildRoutes(function () {
4
4
  this.route('settings', function () {
5
5
  this.route('navigator-app');
6
6
  this.route('notifications');
7
+ this.route('routing');
7
8
  this.route('payments', function () {
8
9
  this.route('index', { path: '/' });
9
10
  this.route('onboard');