@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 { 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;
@@ -243,39 +243,42 @@ export default class OperationsOrdersIndexViewController extends BaseController
243
243
  this.customFieldGroups = customFieldGroups;
244
244
  }
245
245
 
246
- @action resetView() {
247
- this.removeRoutingControlPreview();
246
+ @action async resetView() {
247
+ await this.removeRoutingControlPreview();
248
248
  this.resetInterface();
249
249
  }
250
250
 
251
251
  @action resetInterface() {
252
- const liveMap = this.leafletMap ? this.leafletMap.liveMap : null;
253
- if (liveMap) {
254
- liveMap.reload();
255
- liveMap.showAll();
252
+ if (this.leafletMap && this.leafletMap.liveMap) {
253
+ this.leafletMap.liveMap.showAll();
254
+ this.leafletMap.liveMap.reload();
256
255
  }
257
256
  }
258
257
 
259
258
  @action removeRoutingControlPreview() {
260
- const { leafletMap, routeControl } = this;
259
+ return new Promise((resolve) => {
260
+ const { leafletMap, routeControl } = this;
261
261
 
262
- if (routeControl instanceof RoutingControl) {
263
- try {
264
- routeControl.remove();
265
- } catch (e) {
266
- // silent
262
+ if (routeControl instanceof RoutingControl) {
263
+ try {
264
+ routeControl.remove();
265
+ } catch (e) {
266
+ // silent
267
+ }
267
268
  }
268
- }
269
269
 
270
- if (leafletMap instanceof L.Map) {
271
- try {
272
- leafletMap.removeControl(routeControl);
273
- } catch (e) {
274
- // silent
270
+ if (leafletMap instanceof L.Map) {
271
+ try {
272
+ leafletMap.removeControl(routeControl);
273
+ } catch (e) {
274
+ // silent
275
+ }
275
276
  }
276
- }
277
277
 
278
- this.forceRemoveRoutePreview();
278
+ this.forceRemoveRoutePreview();
279
+ this.routeControl = null;
280
+ resolve(true);
281
+ });
279
282
  }
280
283
 
281
284
  @action forceRemoveRoutePreview() {
@@ -370,9 +373,8 @@ export default class OperationsOrdersIndexViewController extends BaseController
370
373
  const leafletMap = this.leafletMap;
371
374
  const payload = this.model.payload;
372
375
  const waypoints = this.getPayloadCoordinates(payload);
373
- const routingHost = getRoutingHost(payload, this.getPayloadWaypointsAsArray());
374
376
 
375
- if (!waypoints || waypoints.length < 2 || !leafletMap) {
377
+ if (!waypoints || waypoints.length < 2 || !leafletMap || this.routeControl) {
376
378
  return;
377
379
  }
378
380
 
@@ -380,12 +382,12 @@ export default class OperationsOrdersIndexViewController extends BaseController
380
382
  leafletMap.stop();
381
383
  leafletMap.flyTo(waypoints.firstObject);
382
384
 
383
- const router = new OSRMv1({
384
- serviceUrl: `${routingHost}/route/v1`,
385
- profile: 'driving',
386
- });
385
+ const routingService = this.currentUser.getOption('routing', { router: 'osrm' }).router;
386
+ const { router, formatter } = this.leafletRouterControl.get(routingService);
387
387
 
388
388
  this.routeControl = new RoutingControl({
389
+ router,
390
+ formatter,
389
391
  waypoints,
390
392
  markerOptions: {
391
393
  icon: L.icon({
@@ -398,7 +400,6 @@ export default class OperationsOrdersIndexViewController extends BaseController
398
400
  },
399
401
  alternativeClassName: 'hidden',
400
402
  addWaypoints: false,
401
- router,
402
403
  }).addTo(leafletMap);
403
404
 
404
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');
@@ -0,0 +1,64 @@
1
+ import Service, { inject as service } from '@ember/service';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { underscore } from '@ember/string';
4
+
5
+ export class RouterControlRegistry {
6
+ @tracked routers = {};
7
+ }
8
+
9
+ export class RouterControl {
10
+ @tracked name;
11
+ @tracked router;
12
+ @tracked formatter;
13
+
14
+ constructor(init) {
15
+ const { name, router, formatter } = typeof init === 'function' ? init() : init;
16
+ this.name = name;
17
+ this.router = router;
18
+ this.formatter = formatter;
19
+ }
20
+ }
21
+
22
+ export default class LeafletRouterControlService extends Service {
23
+ @service universe;
24
+ registry = this.#initializeRegistry();
25
+
26
+ get availableEngines() {
27
+ return Object.keys(this.registry.routers).map(underscore);
28
+ }
29
+
30
+ get availableServices() {
31
+ return Object.entries(this.registry.routers).map(([key, control]) => ({
32
+ key,
33
+ name: control.name ?? key,
34
+ }));
35
+ }
36
+
37
+ register(name, getter) {
38
+ this.registry.routers = {
39
+ ...this.registry.routers,
40
+ [underscore(name)]: getter,
41
+ };
42
+ }
43
+
44
+ /* eslint-disable no-unused-vars */
45
+ unregister(name) {
46
+ let key = underscore(name);
47
+ let { [key]: _, ...rest } = this.registry.routers;
48
+ this.registry.routers = rest;
49
+ }
50
+
51
+ get(name) {
52
+ return this.registry.routers[underscore(name)];
53
+ }
54
+
55
+ #initializeRegistry() {
56
+ const registry = 'registry:router-controls';
57
+ const application = this.universe.getApplicationInstance();
58
+ if (!application.hasRegistration(registry)) {
59
+ application.register(registry, new RouterControlRegistry(), { instantiate: false });
60
+ }
61
+
62
+ return application.resolveRegistration(registry);
63
+ }
64
+ }
@@ -0,0 +1,46 @@
1
+ import RouteOptimizationInterfaceService from './route-optimization-interface';
2
+ import getRoutingHost from '@fleetbase/ember-core/utils/get-routing-host';
3
+ import polyline from '@fleetbase/ember-core/utils/polyline';
4
+ import { debug } from '@ember/debug';
5
+
6
+ export default class OsrmService extends RouteOptimizationInterfaceService {
7
+ name = 'OSRM';
8
+
9
+ async optimize({ order, payload, waypoints, coordinates: originalCoords }, options = {}) {
10
+ const driverAssigned = order.driver_assigned;
11
+ const driverPosition = driverAssigned?.location?.coordinates; // [lon,lat] | undefined
12
+ const coordinates = driverPosition ? [driverPosition, ...originalCoords] : [...originalCoords];
13
+ const hasDriverStart = Boolean(driverPosition);
14
+ const source = 'first';
15
+ const destination = 'any';
16
+ const roundtrip = false; // don’t loop back
17
+ const routingHost = getRoutingHost(payload, waypoints);
18
+
19
+ try {
20
+ const result = await this.fetch.routing(coordinates, { source, destination, roundtrip, annotations: true }, { host: routingHost, ...options });
21
+
22
+ // Pair each OSRM waypoint with its Waypoint model
23
+ const modelsByInputIndex = hasDriverStart ? [null, ...waypoints] : waypoints;
24
+ const pairs = result.waypoints.map((wp, idx) => ({
25
+ model: modelsByInputIndex[idx], // Ember model or null (driver)
26
+ wp,
27
+ }));
28
+
29
+ // Drop the driver start if present
30
+ const payloadPairs = hasDriverStart ? pairs.slice(1) : pairs;
31
+
32
+ // Sort by the optimised order
33
+ payloadPairs.sort((a, b) => a.wp.waypoint_index - b.wp.waypoint_index);
34
+
35
+ // Extract the Ember models (null-safe)
36
+ const sortedWaypoints = payloadPairs.map((p) => p.model).filter(Boolean);
37
+ const trip = result.trips?.[0];
38
+ const route = polyline.decode(trip.geometry);
39
+
40
+ return { sortedWaypoints, trip, route, result };
41
+ } catch (err) {
42
+ debug(`[OSRM] Error routing trip : ${err.message}`);
43
+ throw err;
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,9 @@
1
+ import Service, { inject as service } from '@ember/service';
2
+
3
+ export default class RouteOptimizationInterfaceService extends Service {
4
+ @service fetch;
5
+
6
+ optimize() {
7
+ throw new Error(`${this.constructor.name} must implement optimize(params, options)`);
8
+ }
9
+ }
@@ -0,0 +1,67 @@
1
+ import Service, { inject as service } from '@ember/service';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { underscore } from '@ember/string';
4
+
5
+ export class RouteOptimizationRegistry {
6
+ @tracked engines = {};
7
+ }
8
+
9
+ export default class RouteOptimizationService extends Service {
10
+ @service universe;
11
+ registry = this.#initializeRegistry();
12
+
13
+ get availableEngines() {
14
+ return Object.keys(this.registry.engines).map(underscore);
15
+ }
16
+
17
+ get availableServices() {
18
+ return Object.entries(this.registry.engines).map(([key, engine]) => ({
19
+ key,
20
+ name: engine.name ?? key,
21
+ }));
22
+ }
23
+
24
+ register(name, engine) {
25
+ if (typeof engine.optimize !== 'function') {
26
+ throw new Error(`Cannot register "${name}": missing optimize()`);
27
+ }
28
+
29
+ this.registry.engines = {
30
+ ...this.registry.engines,
31
+ [underscore(name)]: engine,
32
+ };
33
+ }
34
+
35
+ /* eslint-disable no-unused-vars */
36
+ unregister(name) {
37
+ let key = underscore(name);
38
+ let { [key]: _, ...rest } = this.registry.engines;
39
+ this.registry.engines = rest;
40
+ }
41
+
42
+ optimize(name, params = {}, options = {}) {
43
+ let engine = this.registry.engines[underscore(name)];
44
+ if (!engine) {
45
+ return Promise.reject(new Error(`No route optimization engine registered as "${name}"`));
46
+ }
47
+ return engine.optimize(params, options);
48
+ }
49
+
50
+ handler(name, context, data) {
51
+ let engine = this.registry.engines[underscore(name)];
52
+ if (!engine) {
53
+ throw new Error(`No route optimization engine registered as "${name}"`);
54
+ }
55
+ return engine.handler(context, data);
56
+ }
57
+
58
+ #initializeRegistry() {
59
+ const registry = 'registry:route-optimization-engines';
60
+ const application = this.universe.getApplicationInstance();
61
+ if (!application.hasRegistration(registry)) {
62
+ application.register(registry, new RouteOptimizationRegistry(), { instantiate: false });
63
+ }
64
+
65
+ return application.resolveRegistration(registry);
66
+ }
67
+ }
@@ -315,17 +315,21 @@
315
315
  </div>
316
316
  <div class="flex flex-1 justify-end space-x-2">
317
317
  {{#if this.isMultipleDropoffOrder}}
318
- <Button
319
- @type="magic"
320
- @icon="magic"
321
- @text={{t "fleet-ops.operations.orders.index.new.optimize-route"}}
322
- @size="sm"
323
- @onClick={{perform this.optimizeRoute}}
324
- @permission="fleet-ops optimize order"
325
- @helpText={{t "fleet-ops.operations.orders.index.new.optimize-route-help-text"}}
326
- @disabled={{lt this.waypoints.length 3}}
327
- @isLoading={{this.isOptimizingRoute}}
328
- />
318
+ {{#if this.hasRouteOptimizationEngines}}
319
+ <RouteOptimizationEngineSelectButton @onClick={{perform this.optimizeRouteWithService}} @isLoading={{this.optimizeRouteWithService.isRunning}} />
320
+ {{else}}
321
+ <Button
322
+ @type="magic"
323
+ @icon="magic"
324
+ @text={{t "fleet-ops.operations.orders.index.new.optimize-route"}}
325
+ @size="sm"
326
+ @onClick={{perform this.optimizeRoute}}
327
+ @permission="fleet-ops optimize order"
328
+ @helpText={{t "fleet-ops.operations.orders.index.new.optimize-route-help-text"}}
329
+ @disabled={{lt this.waypoints.length 3}}
330
+ @isLoading={{this.optimizeRoute.isRunning}}
331
+ />
332
+ {{/if}}
329
333
  <Button
330
334
  @icon="map-marked-alt"
331
335
  @text={{t "fleet-ops.operations.orders.index.new.add-waypoint"}}
@@ -630,11 +634,9 @@
630
634
  </div>
631
635
  </DropdownButton>
632
636
  {{/if}}
633
- {{#if this.renderableEntityInputComponents}}
634
- {{#each this.renderableEntityInputComponents as |renderableEntityInputComponent|}}
635
- {{component renderableEntityInputComponent order=this.order controller=this}}
636
- {{/each}}
637
- {{/if}}
637
+ <RegistryYield @registry="fleet-ops:template:operations:orders:new:entities-input" as |RegistryComponent|>
638
+ <RegistryComponent @order={{this.order}} @controller={{this}} />
639
+ </RegistryYield>
638
640
  </div>
639
641
  {{/if}}
640
642
  {{#if this.isCsvImportedOrder}}
@@ -709,6 +711,9 @@
709
711
  <div>
710
712
  <Input @value={{entity.sku}} @type="text" class="w-full form-input form-input-sm" placeholder={{t "fleet-ops.operations.orders.index.new.sku"}} />
711
713
  </div>
714
+ <RegistryYield @registry="fleet-ops:template:operations:orders:new:entities-input:entity" as |RegistryComponent|>
715
+ <RegistryComponent @entity={{entity}} @order={{this.order}} @controller={{this}} />
716
+ </RegistryYield>
712
717
  {{#if this.waypoints.length}}
713
718
  <Select
714
719
  @value={{entity.destination_uuid}}
@@ -0,0 +1,32 @@
1
+ <Layout::Section::Header @title={{t "fleet-ops.settings.routing.fleet-ops-routing-settings"}}>
2
+ <Button
3
+ @type="primary"
4
+ @size="sm"
5
+ @icon="save"
6
+ @text={{t "common.save-button-text"}}
7
+ @onClick={{perform this.saveSettings}}
8
+ @disabled={{this.saveSettings.isRunning}}
9
+ @isLoading={{or this.saveSettings.isRunning this.getSettings.isRunning}}
10
+ />
11
+ </Layout::Section::Header>
12
+
13
+ <Layout::Section::Body class="overflow-y-scroll h-full">
14
+ <div class="container mx-auto h-screen">
15
+ <div class="max-w-3xl my-10 mx-auto space-y-6">
16
+ <ContentPanel @title={{t "fleet-ops.settings.routing.configure-routing"}} @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
17
+ <InputGroup @name="Routing Service" @helpText="Select the service which is responsible for calculating and plotting routes on the map.">
18
+ <Select
19
+ @value={{this.routerService}}
20
+ @options={{this.leafletRouterControl.availableServices}}
21
+ @optionLabel="name"
22
+ @optionValue="key"
23
+ @onSelect={{fn (mut this.routerService)}}
24
+ @placeholder="Select routing service..."
25
+ class="w-full"
26
+ />
27
+ </InputGroup>
28
+ </ContentPanel>
29
+ </div>
30
+ </div>
31
+ <Spacer @height="600px" />
32
+ </Layout::Section::Body>
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/route-optimization-engine-select-button';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/controllers/settings/routing';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/routes/settings/routing';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/services/leaflet-router-control';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/services/osrm';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/services/route-optimization-interface';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/services/route-optimization';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/templates/settings/routing';
package/composer.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fleetbase/fleetops-api",
3
- "version": "0.6.11",
3
+ "version": "0.6.13",
4
4
  "description": "Fleet & Transport Management Extension for Fleetbase",
5
5
  "keywords": [
6
6
  "fleetbase-extension",
package/extension.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Fleet-Ops",
3
- "version": "0.6.11",
3
+ "version": "0.6.13",
4
4
  "description": "Fleet & Transport Management Extension for Fleetbase",
5
5
  "repository": "https://github.com/fleetbase/fleetops",
6
6
  "license": "AGPL-3.0-or-later",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fleetbase/fleetops-engine",
3
- "version": "0.6.11",
3
+ "version": "0.6.13",
4
4
  "description": "Fleet & Transport Management Extension for Fleetbase",
5
5
  "fleetbase": {
6
6
  "route": "fleet-ops"
@@ -45,7 +45,7 @@
45
45
  "@fleetbase/ember-core": "latest",
46
46
  "@fleetbase/ember-ui": "latest",
47
47
  "@fleetbase/fleetops-data": "latest",
48
- "@fleetbase/leaflet-routing-machine": "^3.2.16",
48
+ "@fleetbase/leaflet-routing-machine": "^3.2.17",
49
49
  "@fortawesome/ember-fontawesome": "^2.0.0",
50
50
  "@fortawesome/fontawesome-svg-core": "6.4.0",
51
51
  "@fortawesome/free-brands-svg-icons": "6.4.0",