@fleetbase/fleetops-engine 0.6.44 → 0.6.46
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/entity/form.hbs +24 -0
- package/addon/components/entity/form.js +30 -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/leaflet-draw-control.js +1 -1
- package/addon/components/maintenance-schedule/form.js +2 -1
- package/addon/components/map/drawer/driver-listing.js +5 -5
- package/addon/components/map/drawer/geofence-event-listing.js +5 -2
- 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/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 +78 -18
- package/addon/components/order/form/service-rate.hbs +1 -1
- 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/service-rate/details.hbs +6 -6
- package/addon/components/service-rate/form.hbs +28 -15
- package/addon/components/vehicle/card.hbs +4 -4
- package/addon/controllers/maintenance/schedules/index.js +2 -1
- package/addon/controllers/operations/orders/index/details/virtual.js +5 -0
- package/addon/controllers/operations/orders/index/details.js +113 -15
- package/addon/controllers/operations/orders/index/new.js +1 -1
- package/addon/controllers/operations/orders/index.js +8 -1
- package/addon/controllers/operations/service-rates/index/edit.js +1 -0
- package/addon/controllers/operations/service-rates/index/new.js +1 -0
- package/addon/controllers/settings/map.js +97 -0
- package/addon/controllers/settings/routing.js +47 -12
- package/addon/extension.js +11 -0
- package/addon/helpers/f-to-int.js +6 -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 +9 -3
- package/addon/routes/operations/orchestrator.js +13 -0
- package/addon/routes/operations/orders/index/details/virtual.js +1 -0
- package/addon/routes/operations/orders/index/details.js +47 -24
- 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/equipment-actions.js +4 -6
- 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 +21 -0
- package/addon/services/maintenance-actions.js +4 -6
- 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/part-actions.js +4 -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/services/service-rate-actions.js +8 -3
- package/addon/styles/fleetops-engine.css +555 -0
- package/addon/templates/management/vehicles/index.hbs +0 -1
- package/addon/templates/operations/orders/index/details/virtual.hbs +7 -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 +34 -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/serialize-payload.js +1 -1
- 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/operations/orders/index/details/virtual.js +1 -0
- package/app/controllers/settings/map.js +1 -0
- package/app/helpers/f-to-int.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 +3 -3
- package/server/src/Auth/Schemas/FleetOps.php +5 -0
- package/server/src/Http/Controllers/Api/v1/OrderController.php +36 -8
- package/server/src/Http/Controllers/Api/v1/PayloadController.php +14 -1
- package/server/src/Http/Controllers/Api/v1/PurchaseRateController.php +11 -3
- package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +2 -0
- package/server/src/Http/Controllers/Api/v1/ServiceQuoteController.php +7 -2
- package/server/src/Http/Controllers/Api/v1/ServiceRateController.php +6 -1
- 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 +61 -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/Requests/CreateServiceRateRequest.php +2 -2
- package/server/src/Http/Resources/v1/Index/Payload.php +9 -7
- package/server/src/Http/Resources/v1/Order.php +20 -2
- package/server/src/Http/Resources/v1/ServiceQuote.php +2 -2
- package/server/src/Models/Entity.php +7 -0
- package/server/src/Models/Order.php +81 -8
- 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 +125 -32
- package/server/src/Observers/PurchaseRateObserver.php +3 -5
- package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +59 -23
- package/server/src/Rules/ComputableAlgo.php +1 -3
- package/server/src/Support/Algo.php +87 -1
- package/server/src/Support/Utils.php +69 -0
- package/server/src/routes.php +4 -0
- package/translations/en-us.yaml +1937 -1916
|
@@ -1,5 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PositionPlaybackService
|
|
3
|
+
*
|
|
4
|
+
* Refactored to use the provider-agnostic MapManagerService for all
|
|
5
|
+
* marker operations. The Leaflet-specific `marker._map`, `marker.slideTo`,
|
|
6
|
+
* `marker.setRotationAngle`, and `map.panTo` calls are replaced with
|
|
7
|
+
* adapter calls. Backward-compat: if `this.marker` is still a raw Leaflet
|
|
8
|
+
* marker (set via the old `leafletLayer` option), the Leaflet path is used.
|
|
9
|
+
*/
|
|
1
10
|
import Service from '@ember/service';
|
|
2
11
|
import { tracked } from '@glimmer/tracking';
|
|
12
|
+
import { inject as service } from '@ember/service';
|
|
3
13
|
import { task, timeout } from 'ember-concurrency';
|
|
4
14
|
import { debug } from '@ember/debug';
|
|
5
15
|
import { isArray } from '@ember/array';
|
|
@@ -21,14 +31,18 @@ import { isArray } from '@ember/array';
|
|
|
21
31
|
* - Real-time replay: respects actual time intervals between positions
|
|
22
32
|
*/
|
|
23
33
|
export default class PositionPlaybackService extends Service {
|
|
34
|
+
@service mapManager;
|
|
35
|
+
|
|
24
36
|
@tracked isPlaying = false;
|
|
25
37
|
@tracked isPaused = false;
|
|
26
38
|
@tracked currentIndex = 0;
|
|
27
39
|
@tracked positions = [];
|
|
28
40
|
@tracked speed = 1;
|
|
29
41
|
@tracked marker = null;
|
|
42
|
+
@tracked markerId = null;
|
|
30
43
|
@tracked map = null;
|
|
31
44
|
@tracked callback = null;
|
|
45
|
+
@tracked lastLatLng = null;
|
|
32
46
|
|
|
33
47
|
/**
|
|
34
48
|
* Initialize replay session with positions and marker
|
|
@@ -43,12 +57,19 @@ export default class PositionPlaybackService extends Service {
|
|
|
43
57
|
*/
|
|
44
58
|
initialize(options = {}) {
|
|
45
59
|
const { subject, leafletLayer, positions = [], speed = 1, callback = null, map = null } = options;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
60
|
+
const subjectId = subject?.id ?? null;
|
|
61
|
+
const existingLeafletMarker = leafletLayer || subject?.leafletLayer || subject?._layer || subject?._marker;
|
|
62
|
+
|
|
63
|
+
if (subjectId && this.mapManager?.hasMarker(subjectId)) {
|
|
64
|
+
this.markerId = subjectId;
|
|
65
|
+
this.marker = null;
|
|
66
|
+
} else if (existingLeafletMarker) {
|
|
67
|
+
this.markerId = null;
|
|
68
|
+
this.marker = existingLeafletMarker;
|
|
69
|
+
} else {
|
|
70
|
+
this.markerId = null;
|
|
71
|
+
this.marker = null;
|
|
72
|
+
debug('[PositionPlayback] Warning: No marker found. Provide a subject with a registered marker or leafletLayer property.');
|
|
52
73
|
}
|
|
53
74
|
|
|
54
75
|
this.positions = positions;
|
|
@@ -58,8 +79,9 @@ export default class PositionPlaybackService extends Service {
|
|
|
58
79
|
this.currentIndex = 0;
|
|
59
80
|
this.isPlaying = false;
|
|
60
81
|
this.isPaused = false;
|
|
82
|
+
this.lastLatLng = this.#resolveCurrentLatLng();
|
|
61
83
|
|
|
62
|
-
debug(`[PositionPlayback] Initialized with ${positions.length} positions at ${speed}x speed`);
|
|
84
|
+
debug(`[PositionPlayback] Initialized with ${positions.length} positions at ${speed}x speed (markerId=${this.markerId ?? 'leaflet-compat'})`);
|
|
63
85
|
}
|
|
64
86
|
|
|
65
87
|
/**
|
|
@@ -173,35 +195,41 @@ export default class PositionPlaybackService extends Service {
|
|
|
173
195
|
const position = this.positions[index];
|
|
174
196
|
this.currentIndex = index;
|
|
175
197
|
|
|
176
|
-
|
|
198
|
+
const useAdapter = this.markerId && this.mapManager?.hasMarker(this.markerId);
|
|
199
|
+
|
|
200
|
+
if (!useAdapter && !this.marker) {
|
|
177
201
|
return;
|
|
178
202
|
}
|
|
179
203
|
|
|
180
|
-
// Update marker position without animation
|
|
181
204
|
const latLng = this.#getLatLngFromPosition(position);
|
|
182
205
|
if (latLng) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
this.
|
|
206
|
+
if (useAdapter) {
|
|
207
|
+
// Provider-agnostic path
|
|
208
|
+
if (Number.isFinite(position.heading) && position.heading !== -1) {
|
|
209
|
+
this.mapManager.setMarkerRotation(this.markerId, position.heading);
|
|
210
|
+
}
|
|
211
|
+
this.mapManager.updateMarkerPosition(this.markerId, latLng[0], latLng[1], true, 100);
|
|
212
|
+
this.mapManager.panTo(latLng[0], latLng[1]);
|
|
190
213
|
} else {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
214
|
+
// Leaflet backward-compat path
|
|
215
|
+
if (typeof this.marker.setRotationAngle === 'function' && Number.isFinite(position.heading) && position.heading !== -1) {
|
|
216
|
+
this.marker.setRotationAngle(position.heading);
|
|
217
|
+
}
|
|
218
|
+
if (typeof this.marker.slideTo === 'function') {
|
|
219
|
+
this.marker.slideTo(latLng, { duration: 100 });
|
|
220
|
+
} else {
|
|
221
|
+
this.marker.setLatLng(latLng);
|
|
222
|
+
requestAnimationFrame(() => {
|
|
223
|
+
if (typeof this.marker.setRotationAngle === 'function' && Number.isFinite(position.heading) && position.heading !== -1) {
|
|
224
|
+
this.marker.setRotationAngle(position.heading);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
if (this.map) {
|
|
229
|
+
this.map.panTo(latLng, { animate: true });
|
|
230
|
+
}
|
|
202
231
|
}
|
|
203
232
|
|
|
204
|
-
// Trigger callback
|
|
205
233
|
this.#triggerCallback(position, index, { animated: false });
|
|
206
234
|
}
|
|
207
235
|
|
|
@@ -236,9 +264,11 @@ export default class PositionPlaybackService extends Service {
|
|
|
236
264
|
this.stop();
|
|
237
265
|
this.positions = [];
|
|
238
266
|
this.marker = null;
|
|
267
|
+
this.markerId = null;
|
|
239
268
|
this.map = null;
|
|
240
269
|
this.callback = null;
|
|
241
270
|
this.speed = 1;
|
|
271
|
+
this.lastLatLng = null;
|
|
242
272
|
|
|
243
273
|
debug('[PositionPlayback] Reset complete');
|
|
244
274
|
}
|
|
@@ -260,9 +290,11 @@ export default class PositionPlaybackService extends Service {
|
|
|
260
290
|
continue;
|
|
261
291
|
}
|
|
262
292
|
|
|
263
|
-
//
|
|
264
|
-
const
|
|
265
|
-
|
|
293
|
+
// Resolve marker — prefer adapter, fall back to Leaflet
|
|
294
|
+
const useAdapter = this.markerId && this.mapManager?.hasMarker(this.markerId);
|
|
295
|
+
const marker = useAdapter ? null : this.marker;
|
|
296
|
+
|
|
297
|
+
if (!useAdapter && (!marker || !marker._map)) {
|
|
266
298
|
debug('[PositionPlayback] Marker not available or not on map');
|
|
267
299
|
this.currentIndex++;
|
|
268
300
|
continue;
|
|
@@ -276,26 +308,35 @@ export default class PositionPlaybackService extends Service {
|
|
|
276
308
|
continue;
|
|
277
309
|
}
|
|
278
310
|
|
|
279
|
-
// Calculate animation duration
|
|
280
|
-
const animationDuration = this.#calculateAnimationDuration(marker, latLng, position);
|
|
311
|
+
// Calculate animation duration
|
|
312
|
+
const animationDuration = useAdapter ? this.#calculateAnimationDurationFromAdapter(latLng, position) : this.#calculateAnimationDuration(marker, latLng, position);
|
|
281
313
|
|
|
282
314
|
try {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
315
|
+
if (useAdapter) {
|
|
316
|
+
// ── Provider-agnostic path ─────────────────────────────
|
|
317
|
+
if (Number.isFinite(position.heading) && position.heading !== -1) {
|
|
318
|
+
this.mapManager.setMarkerRotation(this.markerId, position.heading);
|
|
319
|
+
}
|
|
320
|
+
this.mapManager.updateMarkerPosition(this.markerId, latLng[0], latLng[1], true, animationDuration);
|
|
321
|
+
if (this.map || this.mapManager.isReady) {
|
|
322
|
+
this.mapManager.panTo(latLng[0], latLng[1]);
|
|
323
|
+
}
|
|
324
|
+
this.lastLatLng = latLng;
|
|
291
325
|
} else {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
326
|
+
// ── Leaflet backward-compat path ───────────────────────
|
|
327
|
+
if (typeof marker.setRotationAngle === 'function' && Number.isFinite(position.heading) && position.heading !== -1) {
|
|
328
|
+
marker.setRotationAngle(position.heading);
|
|
329
|
+
}
|
|
330
|
+
if (typeof marker.slideTo === 'function') {
|
|
331
|
+
marker.slideTo(latLng, { duration: animationDuration });
|
|
332
|
+
} else {
|
|
333
|
+
marker.setLatLng(latLng);
|
|
334
|
+
}
|
|
335
|
+
if (this.map) {
|
|
336
|
+
const targetLatLng = marker._slideToLatLng ?? marker.getLatLng();
|
|
337
|
+
this.map.panTo(targetLatLng, { animate: true });
|
|
338
|
+
}
|
|
339
|
+
this.lastLatLng = latLng;
|
|
299
340
|
}
|
|
300
341
|
|
|
301
342
|
// Trigger callback
|
|
@@ -444,23 +485,31 @@ export default class PositionPlaybackService extends Service {
|
|
|
444
485
|
const prev = marker.getLatLng();
|
|
445
486
|
const meters = map ? map.distance(prev, nextLatLng) : prev.distanceTo(nextLatLng);
|
|
446
487
|
|
|
447
|
-
// Get speed from position data (assume m/s)
|
|
448
488
|
let mps = Number.isFinite(position.speed) && position.speed > 0 ? position.speed : null;
|
|
489
|
+
if (mps && position.speed_unit === 'kmh') mps = mps / 3.6;
|
|
449
490
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
// Calculate base duration for animation
|
|
456
|
-
let baseDuration = mps ? (meters / mps) * 1000 : 500;
|
|
457
|
-
|
|
458
|
-
// For animation, we want it relatively quick regardless of playback speed
|
|
459
|
-
// The playback speed affects the delay between positions, not the animation speed
|
|
460
|
-
// Clamp between 100ms and 1000ms for smooth animation
|
|
461
|
-
const duration = Math.max(100, Math.min(baseDuration, 1000));
|
|
491
|
+
const baseDuration = mps ? (meters / mps) * 1000 : 500;
|
|
492
|
+
return Math.max(100, Math.min(baseDuration, 1000));
|
|
493
|
+
}
|
|
462
494
|
|
|
463
|
-
|
|
495
|
+
/**
|
|
496
|
+
* Calculate animation duration using the adapter (no direct Leaflet access).
|
|
497
|
+
*
|
|
498
|
+
* @private
|
|
499
|
+
* @param {Array} nextLatLng - [lat, lng]
|
|
500
|
+
* @param {Object} position
|
|
501
|
+
* @returns {number}
|
|
502
|
+
*/
|
|
503
|
+
#calculateAnimationDurationFromAdapter(nextLatLng, position) {
|
|
504
|
+
const currentLatLng = this.lastLatLng ?? this.#resolveCurrentLatLng();
|
|
505
|
+
let meters = 0;
|
|
506
|
+
if (currentLatLng) {
|
|
507
|
+
meters = this.mapManager.distanceBetween(currentLatLng[0], currentLatLng[1], nextLatLng[0], nextLatLng[1]);
|
|
508
|
+
}
|
|
509
|
+
let mps = Number.isFinite(position.speed) && position.speed > 0 ? position.speed : null;
|
|
510
|
+
if (mps && position.speed_unit === 'kmh') mps = mps / 3.6;
|
|
511
|
+
const baseDuration = mps ? (meters / mps) * 1000 : 500;
|
|
512
|
+
return Math.max(100, Math.min(baseDuration, 1000));
|
|
464
513
|
}
|
|
465
514
|
|
|
466
515
|
/**
|
|
@@ -483,4 +532,28 @@ export default class PositionPlaybackService extends Service {
|
|
|
483
532
|
});
|
|
484
533
|
}
|
|
485
534
|
}
|
|
535
|
+
|
|
536
|
+
#resolveCurrentLatLng() {
|
|
537
|
+
if (this.markerId && this.mapManager?.hasMarker?.(this.markerId)) {
|
|
538
|
+
const marker = this.mapManager.getMarker(this.markerId);
|
|
539
|
+
const position = marker?.position ?? marker?.getPosition?.();
|
|
540
|
+
|
|
541
|
+
if (position) {
|
|
542
|
+
const lat = typeof position.lat === 'function' ? position.lat() : position.lat;
|
|
543
|
+
const lng = typeof position.lng === 'function' ? position.lng() : position.lng;
|
|
544
|
+
if (Number.isFinite(lat) && Number.isFinite(lng)) {
|
|
545
|
+
return [lat, lng];
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if (this.marker?.getLatLng) {
|
|
551
|
+
const latlng = this.marker.getLatLng();
|
|
552
|
+
if (Number.isFinite(latlng?.lat) && Number.isFinite(latlng?.lng)) {
|
|
553
|
+
return [latlng.lat, latlng.lng];
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
486
559
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
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 RouteEngineRegistry {
|
|
6
|
+
@tracked engines = {};
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default class RouteEngineService extends Service {
|
|
10
|
+
@service universe;
|
|
11
|
+
@service currentUser;
|
|
12
|
+
registry = this.#initializeRegistry();
|
|
13
|
+
|
|
14
|
+
get availableServices() {
|
|
15
|
+
return Object.entries(this.registry.engines)
|
|
16
|
+
.filter(([, registration]) => registration?.capabilities?.display === true)
|
|
17
|
+
.map(([key, registration]) => ({
|
|
18
|
+
key,
|
|
19
|
+
name: registration?.engine?.name ?? key,
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
register(name, engine, capabilities = {}) {
|
|
24
|
+
const key = underscore(name);
|
|
25
|
+
const normalizedCapabilities = {
|
|
26
|
+
display: capabilities.display === true || typeof engine?.computeRoute === 'function',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
if (!normalizedCapabilities.display) {
|
|
30
|
+
throw new Error(`Cannot register "${name}" as a route engine without computeRoute() support`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
this.registry.engines = {
|
|
34
|
+
...this.registry.engines,
|
|
35
|
+
[key]: {
|
|
36
|
+
engine,
|
|
37
|
+
capabilities: normalizedCapabilities,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
unregister(name) {
|
|
43
|
+
const key = underscore(name);
|
|
44
|
+
const rest = { ...this.registry.engines };
|
|
45
|
+
delete rest[key];
|
|
46
|
+
this.registry.engines = rest;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get(name) {
|
|
50
|
+
const key = underscore(name ?? this.getDisplayEngine());
|
|
51
|
+
const registration = this.registry.engines[key] ?? this.registry.engines.osrm;
|
|
52
|
+
return registration?.engine ?? null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
getDisplayEngine(fallback = 'osrm') {
|
|
56
|
+
const routingSettings = this.currentUser.getOption('routing', {});
|
|
57
|
+
return routingSettings.routing_display_engine ?? routingSettings.router ?? fallback;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
getOptimizationEngine(fallback = null) {
|
|
61
|
+
const routingSettings = this.currentUser.getOption('routing', {});
|
|
62
|
+
return routingSettings.routing_optimization_engine ?? routingSettings.router ?? this.getDisplayEngine(fallback ?? 'osrm');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async compute(name, waypoints, options = {}) {
|
|
66
|
+
const engine = this.get(name);
|
|
67
|
+
if (!engine || typeof engine.computeRoute !== 'function') {
|
|
68
|
+
throw new Error(`No route engine registered as "${name}"`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return engine.computeRoute(waypoints, options);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
#initializeRegistry() {
|
|
75
|
+
const registry = 'registry:route-engines';
|
|
76
|
+
const application = this.universe.getApplicationInstance();
|
|
77
|
+
if (!application.hasRegistration(registry)) {
|
|
78
|
+
application.register(registry, new RouteEngineRegistry(), { instantiate: false });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return application.resolveRegistration(registry);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -312,6 +312,7 @@ export default class ServiceAreasService extends Service {
|
|
|
312
312
|
|
|
313
313
|
this.modalsManager.show('modals/map-layer-form', {
|
|
314
314
|
title: 'Create new Layer',
|
|
315
|
+
modalClass: 'flb-resource-modal',
|
|
315
316
|
acceptButtonText: 'Create',
|
|
316
317
|
acceptButtonIcon: 'magic',
|
|
317
318
|
declineButtonIcon: 'times',
|
|
@@ -319,7 +320,13 @@ export default class ServiceAreasService extends Service {
|
|
|
319
320
|
layerTypes: ['Service Area', 'Zone'],
|
|
320
321
|
selectedLayerType: 'Service Area',
|
|
321
322
|
serviceAreaTypes: this.serviceAreaTypes,
|
|
322
|
-
layerOptions: {
|
|
323
|
+
layerOptions: {
|
|
324
|
+
trigger_on_entry: false,
|
|
325
|
+
trigger_on_exit: false,
|
|
326
|
+
dwell_threshold_minutes: null,
|
|
327
|
+
speed_limit_kmh: null,
|
|
328
|
+
description: null,
|
|
329
|
+
},
|
|
323
330
|
confirm: (modal) => {
|
|
324
331
|
modal.startLoading();
|
|
325
332
|
|
|
@@ -6,13 +6,17 @@ import serializePayload from '../utils/serialize-payload';
|
|
|
6
6
|
export default class ServiceRateActionsService extends ResourceActionService {
|
|
7
7
|
modelNamePath = 'service_name';
|
|
8
8
|
|
|
9
|
+
get defaultCurrency() {
|
|
10
|
+
return this.currentUser?.company?.currency || this.currentUser.currency || 'USD';
|
|
11
|
+
}
|
|
12
|
+
|
|
9
13
|
constructor() {
|
|
10
14
|
super(...arguments);
|
|
11
15
|
this.initialize('service-rate', {
|
|
12
16
|
defaultAttributes: {
|
|
13
17
|
rate_calculation_method: 'per_meter',
|
|
14
18
|
per_meter_unit: 'm',
|
|
15
|
-
currency: this.
|
|
19
|
+
currency: this.defaultCurrency,
|
|
16
20
|
parcel_fees: this.#getDefaultParcelFees(),
|
|
17
21
|
},
|
|
18
22
|
});
|
|
@@ -118,12 +122,13 @@ export default class ServiceRateActionsService extends ResourceActionService {
|
|
|
118
122
|
|
|
119
123
|
const hasFacilitator = !isNone(order.facilitator);
|
|
120
124
|
const facilitatorServiceType = order.facilitator?.get('service_types.firstObject.key') ?? order.type;
|
|
125
|
+
const routeSummary = order.route?.summary ?? order.route?.details?.summary ?? {};
|
|
121
126
|
|
|
122
127
|
try {
|
|
123
128
|
const serviceQuotes = yield this.fetch.post('service-quotes/preliminary', {
|
|
124
129
|
payload: serializePayload(order.payload),
|
|
125
|
-
distance:
|
|
126
|
-
time:
|
|
130
|
+
distance: routeSummary.totalDistance,
|
|
131
|
+
time: routeSummary.totalTime,
|
|
127
132
|
service_type: hasFacilitator ? facilitatorServiceType : order.type,
|
|
128
133
|
facilitator: order.facilitator?.public_id,
|
|
129
134
|
scheduled_at: order.scheduled_at,
|