@fleetbase/fleetops-engine 0.6.45 → 0.6.47
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.
- package/addon/components/admin/map-settings.hbs +26 -0
- package/addon/components/admin/map-settings.js +46 -0
- package/addon/components/admin/routing-settings.hbs +11 -0
- package/addon/components/cell/order-route-type.hbs +46 -0
- package/addon/components/cell/order-route-type.js +90 -0
- package/addon/components/cell/place-address.hbs +1 -0
- package/addon/components/display-place.hbs +45 -56
- package/addon/components/display-place.js +21 -0
- package/addon/components/layout/fleet-ops-sidebar/driver-listing.js +5 -5
- package/addon/components/layout/fleet-ops-sidebar/fleet-listing.js +5 -5
- package/addon/components/layout/fleet-ops-sidebar.js +8 -0
- package/addon/components/maintenance-schedule/form.js +2 -1
- package/addon/components/map/drawer/driver-listing.js +5 -5
- package/addon/components/map/drawer/place-listing.js +5 -5
- package/addon/components/map/drawer/position-listing.js +102 -21
- package/addon/components/map/drawer/vehicle-listing.js +5 -5
- package/addon/components/map/google-live-map.hbs +9 -0
- package/addon/components/map/google-live-map.js +445 -0
- package/addon/components/map/leaflet-live-map.hbs +186 -142
- package/addon/components/map/leaflet-live-map.js +121 -41
- package/addon/components/map/toolbar/visibility-control-panel.js +2 -2
- package/addon/components/map/toolbar/zones-panel.hbs +9 -9
- package/addon/components/map/toolbar.hbs +3 -3
- package/addon/components/map/toolbar.js +9 -1
- package/addon/components/modals/map-layer-form.hbs +132 -63
- package/addon/components/modals/orchestrator-import.hbs +4 -11
- package/addon/components/modals/orchestrator-import.js +17 -1
- package/addon/components/modals/update-order-activity.hbs +1 -1
- package/addon/components/orchestrator/order-pool.js +3 -2
- package/addon/components/orchestrator-workbench.js +29 -41
- package/addon/components/order/details/detail.js +2 -2
- package/addon/components/order/details/payload.hbs +5 -5
- package/addon/components/order/details/payload.js +18 -1
- package/addon/components/order/details/route.hbs +2 -2
- package/addon/components/order/details/route.js +76 -3
- package/addon/components/order/form/details.hbs +1 -2
- package/addon/components/order/form/details.js +6 -0
- package/addon/components/order/form/payload.hbs +33 -0
- package/addon/components/order/form/payload.js +1 -0
- package/addon/components/order/form/route.hbs +8 -8
- package/addon/components/order/form/route.js +86 -18
- package/addon/components/order/form.js +2 -2
- package/addon/components/order/route-editor.hbs +209 -147
- package/addon/components/order/route-editor.js +143 -102
- package/addon/components/place/address.hbs +1 -0
- package/addon/components/place/address.js +12 -0
- package/addon/components/route-list.hbs +43 -55
- package/addon/components/route-list.js +47 -0
- package/addon/components/route-optimization-wizard-panel.js +3 -4
- package/addon/components/vehicle/card.hbs +4 -4
- package/addon/controllers/maintenance/schedules/index.js +2 -1
- package/addon/controllers/operations/orders/index/details.js +110 -37
- package/addon/controllers/operations/orders/index/new.js +1 -1
- package/addon/controllers/operations/orders/index.js +8 -1
- package/addon/controllers/settings/map.js +97 -0
- package/addon/controllers/settings/payments/index.js +1 -1
- package/addon/controllers/settings/routing.js +47 -12
- package/addon/extension.js +11 -0
- package/addon/helpers/place-address-html.js +6 -0
- package/addon/instance-initializers/register-google-routes.js +17 -0
- package/addon/instance-initializers/register-osrm.js +5 -0
- package/addon/routes/application.js +6 -0
- package/addon/routes/operations/orchestrator.js +13 -0
- package/addon/routes/operations/orders/index/details.js +29 -22
- package/addon/routes/operations/orders/index/new.js +9 -0
- package/addon/routes/operations/orders/index.js +0 -1
- package/addon/routes/settings/map.js +3 -0
- package/addon/routes.js +1 -0
- package/addon/services/entity-actions.js +1 -1
- package/addon/services/geofence-event-bus.js +2 -1
- package/addon/services/geofence.js +204 -168
- package/addon/services/google-routes.js +300 -0
- package/addon/services/leaflet-draw-restriction.js +2 -1
- package/addon/services/leaflet-map-manager.js +17 -0
- package/addon/services/map-adapter/google.js +2205 -0
- package/addon/services/map-adapter/leaflet.js +1042 -0
- package/addon/services/map-adapter-interface.js +551 -0
- package/addon/services/map-manager.js +768 -0
- package/addon/services/map-settings.js +75 -0
- package/addon/services/movement-tracker.js +83 -32
- package/addon/services/order-actions.js +11 -1
- package/addon/services/osrm.js +79 -6
- package/addon/services/position-playback.js +135 -62
- package/addon/services/route-engine.js +83 -0
- package/addon/services/service-areas.js +8 -1
- package/addon/styles/fleetops-engine.css +555 -0
- package/addon/templates/management/vehicles/index.hbs +0 -1
- package/addon/templates/operations/orders/index/details.hbs +0 -1
- package/addon/templates/settings/map.hbs +53 -0
- package/addon/templates/settings/routing.hbs +15 -4
- package/addon/utils/fleet-ops-options.js +1 -0
- package/addon/utils/has-valid-coordinates.js +6 -0
- package/addon/utils/map-provider-options.js +6 -0
- package/addon/utils/order-route-editing.js +86 -0
- package/addon/utils/order-route-summary.js +20 -0
- package/addon/utils/place-address-html.js +41 -0
- package/addon/utils/route-colors.js +43 -12
- package/addon/utils/route-visualization.js +93 -0
- package/addon/utils/vendor-integration.js +2 -1
- package/app/components/admin/map-settings.js +1 -0
- package/app/components/admin/routing-settings.js +1 -0
- package/app/components/cell/order-route-type.js +1 -0
- package/app/components/cell/place-address.js +1 -0
- package/app/components/map/google-live-map.js +1 -0
- package/app/components/place/address.js +1 -0
- package/app/controllers/settings/map.js +1 -0
- package/app/helpers/place-address-html.js +1 -0
- package/app/routes/settings/map.js +1 -0
- package/app/services/map-adapter/google.js +1 -0
- package/app/services/map-adapter/leaflet.js +1 -0
- package/app/services/map-adapter-interface.js +1 -0
- package/app/services/map-manager.js +1 -0
- package/app/services/map-settings.js +1 -0
- package/app/templates/settings/map.js +1 -0
- package/app/utils/has-valid-coordinates.js +1 -0
- package/app/utils/order-route-editing.js +2 -0
- package/app/utils/order-route-summary.js +1 -0
- package/app/utils/place-address-html.js +1 -0
- package/app/utils/route-colors.js +1 -1
- package/app/utils/route-visualization.js +1 -0
- package/assets/images/drawing-controls-spritesheet.svg +156 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +4 -4
- package/pnpm-workspace.yaml +7 -0
- package/server/src/Auth/Schemas/FleetOps.php +5 -0
- package/server/src/Http/Controllers/Api/v1/OrderController.php +31 -3
- package/server/src/Http/Controllers/Api/v1/PayloadController.php +14 -1
- package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +2 -0
- package/server/src/Http/Controllers/Api/v1/ServiceQuoteController.php +3 -2
- package/server/src/Http/Controllers/Api/v1/ZoneController.php +2 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +2 -1
- package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +7 -4
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +90 -34
- package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +3 -2
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +106 -3
- package/server/src/Http/Filter/OrderFilter.php +2 -1
- package/server/src/Http/Resources/v1/Index/Payload.php +9 -7
- package/server/src/Http/Resources/v1/Order.php +20 -1
- package/server/src/Models/Order.php +42 -1
- package/server/src/Models/OrderConfig.php +15 -0
- package/server/src/Models/Payload.php +131 -27
- package/server/src/Models/Place.php +182 -48
- package/server/src/Models/ServiceRate.php +4 -3
- package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +59 -23
- package/server/src/Support/Utils.php +36 -2
- package/server/src/routes.php +4 -0
- package/translations/en-us.yaml +1937 -1916
|
@@ -1,22 +1,73 @@
|
|
|
1
1
|
import Controller, { inject as controller } from '@ember/controller';
|
|
2
2
|
import { tracked } from '@glimmer/tracking';
|
|
3
3
|
import { inject as service } from '@ember/service';
|
|
4
|
-
import { action } from '@ember/object';
|
|
5
4
|
import { isArray } from '@ember/array';
|
|
6
5
|
import { task } from 'ember-concurrency';
|
|
6
|
+
import { colorForId, routeColorForStatus, routeStyleForStatus } from '../../../../utils/route-colors';
|
|
7
|
+
import { buildRoutePointMarkerPresentation, buildRoutePointsFromPayload } from '../../../../utils/route-visualization';
|
|
7
8
|
|
|
8
9
|
export default class OperationsOrdersIndexDetailsController extends Controller {
|
|
9
10
|
@controller('operations.orders.index') index;
|
|
10
11
|
@service('universe/menu-service') menuService;
|
|
11
12
|
@service orderActions;
|
|
12
13
|
@service orderSocketEvents;
|
|
13
|
-
@service
|
|
14
|
+
@service mapManager;
|
|
14
15
|
@service leafletLayerVisibilityManager;
|
|
15
16
|
@service hostRouter;
|
|
16
17
|
@service universe;
|
|
17
18
|
@service sidebar;
|
|
18
19
|
@tracked routingControl;
|
|
19
20
|
@tracked routingCompleted = false;
|
|
21
|
+
@tracked realtimeOrderPublicId = null;
|
|
22
|
+
|
|
23
|
+
get routePoints() {
|
|
24
|
+
return buildRoutePointsFromPayload(this.model?.payload);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get routeMarkerFactory() {
|
|
28
|
+
const routeColor = colorForId(this.model.public_id ?? this.model.id ?? 'order-route');
|
|
29
|
+
|
|
30
|
+
return (_waypoint, index) => {
|
|
31
|
+
return buildRoutePointMarkerPresentation(this.routePoints[index], routeColor);
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get routeMarkerWaypoints() {
|
|
36
|
+
return this.routePoints.map(({ place }) => [place.latitude, place.longitude]);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get routeStatus() {
|
|
40
|
+
return this.model.status ?? 'created';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get routePolylineOptions() {
|
|
44
|
+
const color = routeColorForStatus(this.routeStatus);
|
|
45
|
+
const styles = routeStyleForStatus(this.routeStatus, color);
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
color,
|
|
49
|
+
weight: styles.at(-1)?.weight ?? 4,
|
|
50
|
+
opacity: styles.at(-1)?.opacity ?? 0.85,
|
|
51
|
+
styles,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get routeControlTag() {
|
|
56
|
+
return this.model?.public_id ? `order-details:${this.model.public_id}` : null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get routingControlOptions() {
|
|
60
|
+
return {
|
|
61
|
+
tag: this.routeControlTag,
|
|
62
|
+
color: this.routePolylineOptions.color,
|
|
63
|
+
status: this.routeStatus,
|
|
64
|
+
markerWaypoints: this.routeMarkerWaypoints,
|
|
65
|
+
polylineOptions: this.routePolylineOptions,
|
|
66
|
+
createMarker: this.routeMarkerFactory,
|
|
67
|
+
onRouteFound: this.handleRoutingComplete,
|
|
68
|
+
onRoutingError: this.handleRoutingComplete,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
20
71
|
|
|
21
72
|
get tabs() {
|
|
22
73
|
const registeredTabs = this.menuService.getMenuItems('fleet-ops:component:order:details');
|
|
@@ -104,56 +155,78 @@ export default class OperationsOrdersIndexDetailsController extends Controller {
|
|
|
104
155
|
}
|
|
105
156
|
|
|
106
157
|
@task *refresh() {
|
|
107
|
-
|
|
158
|
+
try {
|
|
159
|
+
yield this.hostRouter.refresh();
|
|
160
|
+
} catch (error) {
|
|
161
|
+
return error;
|
|
162
|
+
}
|
|
108
163
|
}
|
|
109
164
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
this.routingCompleted = false;
|
|
114
|
-
this.routingControl = await this.leafletMapManager.addRoutingControl(this.model.routeWaypoints, {
|
|
115
|
-
onRouteFound: () => {
|
|
116
|
-
this.routingCompleted = true;
|
|
117
|
-
},
|
|
118
|
-
onRoutingError: () => {
|
|
119
|
-
this.routingCompleted = true;
|
|
120
|
-
},
|
|
121
|
-
});
|
|
165
|
+
handleRoutingComplete = () => {
|
|
166
|
+
this.routingCompleted = true;
|
|
167
|
+
};
|
|
122
168
|
|
|
123
|
-
|
|
124
|
-
|
|
169
|
+
async setupRealtime() {
|
|
170
|
+
const currentOrderPublicId = this.model?.public_id;
|
|
171
|
+
if (!currentOrderPublicId) {
|
|
172
|
+
return;
|
|
125
173
|
}
|
|
126
174
|
|
|
127
|
-
|
|
128
|
-
|
|
175
|
+
if (this.realtimeOrderPublicId && this.realtimeOrderPublicId !== currentOrderPublicId) {
|
|
176
|
+
this.orderSocketEvents.stop({ public_id: this.realtimeOrderPublicId });
|
|
177
|
+
}
|
|
129
178
|
|
|
130
|
-
|
|
131
|
-
this.leafletLayerVisibilityManager.hideCategory('drivers');
|
|
132
|
-
this.leafletLayerVisibilityManager.showModelLayer(this.model.driver_assigned);
|
|
133
|
-
// Should we hide places & other vehicles?
|
|
179
|
+
this.realtimeOrderPublicId = currentOrderPublicId;
|
|
134
180
|
|
|
135
|
-
// Listen for this order events
|
|
136
181
|
this.orderSocketEvents.start(
|
|
137
182
|
this.model,
|
|
138
183
|
async (_msg, { reloadable }) => {
|
|
139
184
|
if (reloadable) {
|
|
140
185
|
await this.hostRouter.refresh();
|
|
141
|
-
this.routingCompleted = false;
|
|
142
|
-
this.routingControl = await this.leafletMapManager.replaceRoutingControl(this.model.routeWaypoints, this.routingControl, {
|
|
143
|
-
onRouteFound: () => {
|
|
144
|
-
this.routingCompleted = true;
|
|
145
|
-
},
|
|
146
|
-
onRoutingError: () => {
|
|
147
|
-
this.routingCompleted = true;
|
|
148
|
-
},
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
if (!this.routingControl) {
|
|
152
|
-
this.routingCompleted = true;
|
|
153
|
-
}
|
|
154
186
|
}
|
|
155
187
|
},
|
|
156
188
|
{ debounceMs: 250 }
|
|
157
189
|
);
|
|
158
190
|
}
|
|
191
|
+
|
|
192
|
+
async syncRoutingControl() {
|
|
193
|
+
this.routingCompleted = false;
|
|
194
|
+
this.teardownRoutingControls();
|
|
195
|
+
|
|
196
|
+
this.routingControl = await this.mapManager.addRoutingControl(this.model.routeWaypoints, this.routingControlOptions);
|
|
197
|
+
|
|
198
|
+
if (!this.routingControl) {
|
|
199
|
+
this.routingCompleted = true;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
syncMapContext() {
|
|
204
|
+
this.leafletLayerVisibilityManager.hideCategory('drivers');
|
|
205
|
+
this.leafletLayerVisibilityManager.showModelLayer(this.model.driver_assigned);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
teardownRoutingControls() {
|
|
209
|
+
if (this.routeControlTag) {
|
|
210
|
+
this.mapManager.clearRoutingControlsByTag(this.routeControlTag);
|
|
211
|
+
} else if (this.routingControl) {
|
|
212
|
+
this.mapManager.removeRoutingControl(this.routingControl);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
this.routingControl = undefined;
|
|
216
|
+
this.routingCompleted = false;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
teardownRealtime() {
|
|
220
|
+
if (this.realtimeOrderPublicId) {
|
|
221
|
+
this.orderSocketEvents.stop({ public_id: this.realtimeOrderPublicId });
|
|
222
|
+
this.realtimeOrderPublicId = null;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async setupDetailsSession() {
|
|
227
|
+
this.index.changeLayout('map');
|
|
228
|
+
await this.setupRealtime();
|
|
229
|
+
await this.syncRoutingControl();
|
|
230
|
+
this.syncMapContext();
|
|
231
|
+
}
|
|
159
232
|
}
|
|
@@ -20,6 +20,7 @@ export default class OperationsOrdersIndexNewController extends Controller {
|
|
|
20
20
|
@service orderCreation;
|
|
21
21
|
@service orderValidation;
|
|
22
22
|
@service events;
|
|
23
|
+
@service sidebar;
|
|
23
24
|
@tracked order = this.orderCreation.newOrder();
|
|
24
25
|
@tracked overlay;
|
|
25
26
|
|
|
@@ -92,7 +93,6 @@ export default class OperationsOrdersIndexNewController extends Controller {
|
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
@action setup() {
|
|
95
|
-
// Change to map layout
|
|
96
96
|
this.index.changeLayout('map');
|
|
97
97
|
}
|
|
98
98
|
|
|
@@ -142,6 +142,13 @@ export default class OperationsOrdersIndexController extends Controller {
|
|
|
142
142
|
filterable: true,
|
|
143
143
|
filterComponent: 'filter/string',
|
|
144
144
|
},
|
|
145
|
+
{
|
|
146
|
+
label: this.intl.t('column.route-type'),
|
|
147
|
+
valuePath: 'payload.waypoints.length',
|
|
148
|
+
cellComponent: 'cell/order-route-type',
|
|
149
|
+
cellClassNames: 'overflow-visible',
|
|
150
|
+
resizable: true,
|
|
151
|
+
},
|
|
145
152
|
{
|
|
146
153
|
label: this.intl.t('column.internal-id'),
|
|
147
154
|
valuePath: 'internal_id',
|
|
@@ -283,7 +290,7 @@ export default class OperationsOrdersIndexController extends Controller {
|
|
|
283
290
|
label: this.intl.t('column.type'),
|
|
284
291
|
valuePath: 'type',
|
|
285
292
|
resizable: true,
|
|
286
|
-
|
|
293
|
+
humanize: true,
|
|
287
294
|
sortable: true,
|
|
288
295
|
filterable: true,
|
|
289
296
|
filterComponent: 'filter/model',
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import Controller from '@ember/controller';
|
|
2
|
+
import { tracked } from '@glimmer/tracking';
|
|
3
|
+
import { inject as service } from '@ember/service';
|
|
4
|
+
import { action } from '@ember/object';
|
|
5
|
+
import { task } from 'ember-concurrency';
|
|
6
|
+
import MAP_PROVIDER_OPTIONS from '../../utils/map-provider-options';
|
|
7
|
+
|
|
8
|
+
export default class SettingsMapController extends Controller {
|
|
9
|
+
@service fetch;
|
|
10
|
+
@service notifications;
|
|
11
|
+
@service intl;
|
|
12
|
+
@service mapManager;
|
|
13
|
+
@service mapSettings;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Available map provider options for the provider selector.
|
|
17
|
+
*
|
|
18
|
+
* @memberof SettingsMapController
|
|
19
|
+
* @var {Array}
|
|
20
|
+
*/
|
|
21
|
+
@tracked mapProviderOptions = MAP_PROVIDER_OPTIONS;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The currently-selected map provider key (e.g. 'leaflet' or 'google').
|
|
25
|
+
*
|
|
26
|
+
* @memberof SettingsMapController
|
|
27
|
+
* @var {String}
|
|
28
|
+
*/
|
|
29
|
+
@tracked mapProvider = 'leaflet';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Whether settings have been loaded from the server.
|
|
33
|
+
*
|
|
34
|
+
* @memberof SettingsMapController
|
|
35
|
+
* @var {Boolean}
|
|
36
|
+
*/
|
|
37
|
+
@tracked settingsLoaded = false;
|
|
38
|
+
|
|
39
|
+
constructor() {
|
|
40
|
+
super(...arguments);
|
|
41
|
+
this.getSettings.perform();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ─── Computed getters ──────────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* True when the user has selected Google Maps as the provider.
|
|
48
|
+
*
|
|
49
|
+
* @memberof SettingsMapController
|
|
50
|
+
* @return {Boolean}
|
|
51
|
+
*/
|
|
52
|
+
get isGoogleMapsSelected() {
|
|
53
|
+
return this.mapProvider === 'google';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ─── Actions ───────────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Called when the user picks a different map provider from the Select.
|
|
60
|
+
*
|
|
61
|
+
* @param {Object|String} option The selected option object or raw value.
|
|
62
|
+
* @memberof SettingsMapController
|
|
63
|
+
*/
|
|
64
|
+
@action selectMapProvider(option) {
|
|
65
|
+
this.mapProvider = option?.value ?? option ?? 'leaflet';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@task *saveSettings() {
|
|
69
|
+
const settings = {
|
|
70
|
+
mapProvider: this.mapProvider,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const response = yield this.fetch.post('fleet-ops/settings/map', { settings });
|
|
75
|
+
this.mapSettings.applySettings(response);
|
|
76
|
+
|
|
77
|
+
if (this.mapManager && typeof this.mapManager.setActiveProvider === 'function') {
|
|
78
|
+
this.mapManager.setActiveProvider(this.mapProvider);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.notifications.success(this.intl.t('settings.map.settings-saved'));
|
|
82
|
+
} catch (error) {
|
|
83
|
+
this.notifications.serverError(error);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@task *getSettings() {
|
|
88
|
+
try {
|
|
89
|
+
const response = yield this.mapSettings.load({ force: true });
|
|
90
|
+
|
|
91
|
+
this.mapProvider = response?.mapProvider ?? 'leaflet';
|
|
92
|
+
this.settingsLoaded = true;
|
|
93
|
+
} catch (error) {
|
|
94
|
+
this.notifications.serverError(error);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -46,7 +46,7 @@ export default class SettingsPaymentsIndexController extends Controller {
|
|
|
46
46
|
];
|
|
47
47
|
|
|
48
48
|
get isStripeEnabled() {
|
|
49
|
-
return
|
|
49
|
+
return !isEmpty(config.stripe.publishableKey);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
@task *lookupStripeConnectAccount() {
|
|
@@ -7,14 +7,20 @@ export default class SettingsRoutingController extends Controller {
|
|
|
7
7
|
@service fetch;
|
|
8
8
|
@service notifications;
|
|
9
9
|
@service currentUser;
|
|
10
|
-
@service
|
|
11
|
-
@
|
|
10
|
+
@service routeEngine;
|
|
11
|
+
@service routeOptimization;
|
|
12
|
+
@tracked displayEngine = 'osrm';
|
|
13
|
+
@tracked optimizationEngine = 'osrm';
|
|
12
14
|
@tracked routingUnit = 'km';
|
|
13
15
|
@tracked routingUnitOptions = [
|
|
14
16
|
{ label: 'Kilometers', value: 'km' },
|
|
15
17
|
{ label: 'Miles', value: 'mi' },
|
|
16
18
|
];
|
|
17
|
-
|
|
19
|
+
saveTasks = new Map();
|
|
20
|
+
|
|
21
|
+
get hasEngineOverrides() {
|
|
22
|
+
return ['vroom', 'valhalla'].includes(this.displayEngine) || ['vroom', 'valhalla'].includes(this.optimizationEngine);
|
|
23
|
+
}
|
|
18
24
|
|
|
19
25
|
constructor() {
|
|
20
26
|
super(...arguments);
|
|
@@ -28,10 +34,19 @@ export default class SettingsRoutingController extends Controller {
|
|
|
28
34
|
*/
|
|
29
35
|
@task *saveSettings() {
|
|
30
36
|
try {
|
|
31
|
-
yield this.fetch.post('fleet-ops/settings/routing-settings', {
|
|
37
|
+
yield this.fetch.post('fleet-ops/settings/routing-settings', {
|
|
38
|
+
display_engine: this.displayEngine,
|
|
39
|
+
optimization_engine: this.optimizationEngine,
|
|
40
|
+
unit: this.routingUnit,
|
|
41
|
+
});
|
|
32
42
|
yield this.performAdditionalSaveTasks();
|
|
33
43
|
// Save in local memory too
|
|
34
|
-
this.currentUser.setOption('routing', {
|
|
44
|
+
this.currentUser.setOption('routing', {
|
|
45
|
+
router: this.displayEngine,
|
|
46
|
+
routing_display_engine: this.displayEngine,
|
|
47
|
+
routing_optimization_engine: this.optimizationEngine,
|
|
48
|
+
unit: this.routingUnit,
|
|
49
|
+
});
|
|
35
50
|
this.notifications.success('Routing setting saved.');
|
|
36
51
|
} catch (error) {
|
|
37
52
|
this.notifications.serverError(error);
|
|
@@ -45,23 +60,43 @@ export default class SettingsRoutingController extends Controller {
|
|
|
45
60
|
*/
|
|
46
61
|
@task *getSettings() {
|
|
47
62
|
try {
|
|
48
|
-
const { router, unit } = yield this.fetch.get('fleet-ops/settings/routing-settings');
|
|
49
|
-
this.
|
|
63
|
+
const { router, display_engine, optimization_engine, unit } = yield this.fetch.get('fleet-ops/settings/routing-settings');
|
|
64
|
+
this.displayEngine = display_engine ?? router ?? 'osrm';
|
|
65
|
+
this.optimizationEngine = optimization_engine ?? display_engine ?? router ?? 'osrm';
|
|
50
66
|
this.routingUnit = unit;
|
|
51
67
|
} catch (error) {
|
|
52
68
|
this.notifications.serverError(error);
|
|
53
69
|
}
|
|
54
70
|
}
|
|
55
71
|
|
|
56
|
-
registerSaveTask(task) {
|
|
57
|
-
|
|
72
|
+
registerSaveTask(key, task) {
|
|
73
|
+
if (!key || typeof task?.perform !== 'function') {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this.saveTasks.set(key, task);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
unregisterSaveTask(key) {
|
|
81
|
+
if (!key) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
this.saveTasks.delete(key);
|
|
58
86
|
}
|
|
59
87
|
|
|
60
88
|
async performAdditionalSaveTasks() {
|
|
61
|
-
for (
|
|
62
|
-
|
|
63
|
-
if (typeof task.perform === 'function') {
|
|
89
|
+
for (const task of this.saveTasks.values()) {
|
|
90
|
+
try {
|
|
64
91
|
await task.perform();
|
|
92
|
+
} catch (error) {
|
|
93
|
+
// Ignore explicit ember-concurrency task cancellations from components
|
|
94
|
+
// that may have unmounted during routing-engine selection changes.
|
|
95
|
+
if (typeof error?.name === 'string' && error.name.includes('TaskCancelation')) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
throw error;
|
|
65
100
|
}
|
|
66
101
|
}
|
|
67
102
|
return true;
|
package/addon/extension.js
CHANGED
|
@@ -73,6 +73,16 @@ export default {
|
|
|
73
73
|
menuService.registerAdminMenuPanel(
|
|
74
74
|
'Fleet-Ops Config',
|
|
75
75
|
[
|
|
76
|
+
new MenuItem({
|
|
77
|
+
title: 'Routing',
|
|
78
|
+
icon: 'route',
|
|
79
|
+
component: new ExtensionComponent('@fleetbase/fleetops-engine', 'admin/routing-settings'),
|
|
80
|
+
}),
|
|
81
|
+
new MenuItem({
|
|
82
|
+
title: 'Map',
|
|
83
|
+
icon: 'map',
|
|
84
|
+
component: new ExtensionComponent('@fleetbase/fleetops-engine', 'admin/map-settings'),
|
|
85
|
+
}),
|
|
76
86
|
new MenuItem({
|
|
77
87
|
title: 'Navigator App',
|
|
78
88
|
icon: 'location-arrow',
|
|
@@ -179,6 +189,7 @@ export default {
|
|
|
179
189
|
'fleet-ops:component:order:form:payload:entity:form',
|
|
180
190
|
'fleet-ops:template:settings:routing',
|
|
181
191
|
'fleet-ops:template:settings:orchestrator',
|
|
192
|
+
'fleet-ops:component:admin:routing-settings',
|
|
182
193
|
]);
|
|
183
194
|
},
|
|
184
195
|
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function initialize(owner) {
|
|
2
|
+
const routeOptimization = owner.lookup('service:route-optimization');
|
|
3
|
+
const routeEngine = owner.lookup('service:route-engine');
|
|
4
|
+
const googleRoutes = owner.lookup('service:google-routes');
|
|
5
|
+
|
|
6
|
+
if (routeOptimization && googleRoutes) {
|
|
7
|
+
routeOptimization.register('google', googleRoutes);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (routeEngine && googleRoutes) {
|
|
11
|
+
routeEngine.register('google', googleRoutes, { display: true });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default {
|
|
16
|
+
initialize,
|
|
17
|
+
};
|
|
@@ -5,11 +5,16 @@ import { OSRMv1 } from '@fleetbase/leaflet-routing-machine';
|
|
|
5
5
|
export function initialize(owner) {
|
|
6
6
|
// Register OSRM as route optimization service
|
|
7
7
|
const routeOptimization = owner.lookup('service:route-optimization');
|
|
8
|
+
const routeEngine = owner.lookup('service:route-engine');
|
|
8
9
|
const osrm = owner.lookup('service:osrm');
|
|
9
10
|
if (routeOptimization && osrm) {
|
|
10
11
|
routeOptimization.register('osrm', osrm);
|
|
11
12
|
}
|
|
12
13
|
|
|
14
|
+
if (routeEngine && osrm) {
|
|
15
|
+
routeEngine.register('osrm', osrm, { display: true });
|
|
16
|
+
}
|
|
17
|
+
|
|
13
18
|
// Register OSRM as Routing Controler
|
|
14
19
|
const leafletRoutingControl = owner.lookup('service:leaflet-routing-control');
|
|
15
20
|
if (leafletRoutingControl) {
|
|
@@ -12,6 +12,7 @@ export default class ApplicationRoute extends Route {
|
|
|
12
12
|
@service notifications;
|
|
13
13
|
@service fetch;
|
|
14
14
|
@service currentUser;
|
|
15
|
+
@service mapSettings;
|
|
15
16
|
|
|
16
17
|
@action loading(transition) {
|
|
17
18
|
const resource = getResourceNameFromTransition(transition, { humanize: true });
|
|
@@ -28,10 +29,15 @@ export default class ApplicationRoute extends Route {
|
|
|
28
29
|
|
|
29
30
|
await this.location.getUserLocation();
|
|
30
31
|
await this.#loadRoutingSettings();
|
|
32
|
+
await this.#loadMapSettings();
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
async #loadRoutingSettings() {
|
|
34
36
|
const routingSetting = await this.fetch.get('fleet-ops/settings/routing-settings');
|
|
35
37
|
this.currentUser.setOption('routing', routingSetting);
|
|
36
38
|
}
|
|
39
|
+
|
|
40
|
+
async #loadMapSettings() {
|
|
41
|
+
await this.mapSettings.load();
|
|
42
|
+
}
|
|
37
43
|
}
|
|
@@ -13,6 +13,19 @@ export default class OperationsAllocationRoute extends Route {
|
|
|
13
13
|
@service hostRouter;
|
|
14
14
|
@service abilities;
|
|
15
15
|
@service intl;
|
|
16
|
+
@service sidebar;
|
|
17
|
+
@service mapManager;
|
|
18
|
+
|
|
19
|
+
activate() {
|
|
20
|
+
this.sidebar.hide();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
deactivate() {
|
|
24
|
+
this.sidebar.show();
|
|
25
|
+
this.mapManager.setMapInstance?.(null);
|
|
26
|
+
this.mapManager.setLivemap?.(null);
|
|
27
|
+
this.mapManager.setActiveProvider?.(this.mapManager.getConfiguredProvider?.() ?? 'leaflet');
|
|
28
|
+
}
|
|
16
29
|
|
|
17
30
|
beforeModel() {
|
|
18
31
|
if (this.abilities.cannot('fleet-ops list order')) {
|
|
@@ -7,36 +7,27 @@ export default class OperationsOrdersIndexDetailsRoute extends Route {
|
|
|
7
7
|
@service store;
|
|
8
8
|
@service hostRouter;
|
|
9
9
|
@service orderSocketEvents;
|
|
10
|
-
@service
|
|
10
|
+
@service mapManager;
|
|
11
11
|
@service abilities;
|
|
12
|
-
@service universe;
|
|
13
12
|
@service intl;
|
|
13
|
+
@service sidebar;
|
|
14
14
|
|
|
15
15
|
@action willTransition(transition) {
|
|
16
16
|
const fromName = transition.from?.name;
|
|
17
17
|
const toName = transition.to?.name;
|
|
18
18
|
const orderDetailsRoute = 'console.fleet-ops.operations.orders.index.details';
|
|
19
|
+
const isRefreshWithinSameDetailsRoute = fromName?.startsWith(orderDetailsRoute) && toName?.startsWith(orderDetailsRoute) && fromName === toName;
|
|
20
|
+
|
|
21
|
+
if (isRefreshWithinSameDetailsRoute) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
19
24
|
|
|
20
25
|
// only cleanup when leaving the order details route tree entirely
|
|
21
26
|
const isLeavingOrderDetails = fromName?.startsWith(orderDetailsRoute) && !toName?.startsWith(orderDetailsRoute);
|
|
22
|
-
|
|
23
27
|
if (isLeavingOrderDetails) {
|
|
24
28
|
const controller = this.controllerFor('operations.orders.index.details');
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// Put back sidebar
|
|
28
|
-
if (this.universe.sidebarContext) {
|
|
29
|
-
this.universe.sidebarContext.show();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// stop listening for events
|
|
33
|
-
this.orderSocketEvents.stop(controller.model);
|
|
34
|
-
|
|
35
|
-
// cleanup guards
|
|
36
|
-
if (rc) {
|
|
37
|
-
this.leafletMapManager.removeRoutingControl(rc);
|
|
38
|
-
controller.routingControl = undefined;
|
|
39
|
-
}
|
|
29
|
+
controller.teardownRealtime();
|
|
30
|
+
controller.teardownRoutingControls();
|
|
40
31
|
}
|
|
41
32
|
|
|
42
33
|
return true;
|
|
@@ -47,6 +38,14 @@ export default class OperationsOrdersIndexDetailsRoute extends Route {
|
|
|
47
38
|
return this.hostRouter.transitionTo('console.fleet-ops.operations.orders.index');
|
|
48
39
|
}
|
|
49
40
|
|
|
41
|
+
activate() {
|
|
42
|
+
this.sidebar.hide();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
deactivate() {
|
|
46
|
+
this.sidebar.show();
|
|
47
|
+
}
|
|
48
|
+
|
|
50
49
|
beforeModel() {
|
|
51
50
|
if (this.abilities.cannot('fleet-ops view order')) {
|
|
52
51
|
this.notifications.warning(this.intl.t('common.unauthorized-access'));
|
|
@@ -75,10 +74,18 @@ export default class OperationsOrdersIndexDetailsRoute extends Route {
|
|
|
75
74
|
});
|
|
76
75
|
}
|
|
77
76
|
|
|
78
|
-
async
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
async setupController(controller, model) {
|
|
78
|
+
super.setupController(...arguments);
|
|
79
|
+
|
|
80
|
+
// Change layout to map
|
|
81
|
+
controller.index?.changeLayout?.('map');
|
|
82
|
+
|
|
83
|
+
// load activity and reload order if lightweight payload
|
|
84
|
+
await model.loadTrackingActivity();
|
|
85
|
+
if (model.meta?._index_resource) {
|
|
86
|
+
await model.reload();
|
|
82
87
|
}
|
|
88
|
+
|
|
89
|
+
await controller.setupDetailsSession();
|
|
83
90
|
}
|
|
84
91
|
}
|
|
@@ -7,6 +7,7 @@ export default class OperationsOrdersIndexNewRoute extends Route {
|
|
|
7
7
|
@service hostRouter;
|
|
8
8
|
@service abilities;
|
|
9
9
|
@service intl;
|
|
10
|
+
@service sidebar;
|
|
10
11
|
|
|
11
12
|
@action willTransition() {
|
|
12
13
|
if (this.controller) {
|
|
@@ -14,6 +15,14 @@ export default class OperationsOrdersIndexNewRoute extends Route {
|
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
activate() {
|
|
19
|
+
this.sidebar.hide();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
deactivate() {
|
|
23
|
+
this.sidebar.show();
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
beforeModel() {
|
|
18
27
|
if (this.abilities.cannot('fleet-ops create order')) {
|
|
19
28
|
this.notifications.warning(this.intl.t('common.unauthorized-access'));
|
package/addon/routes.js
CHANGED