@fleetbase/fleetops-engine 0.6.46 → 0.6.48
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/routing-settings.hbs +71 -0
- package/addon/components/admin/routing-settings.js +124 -0
- package/addon/components/customer/orders.js +1 -1
- package/addon/components/display-place.hbs +1 -1
- package/addon/components/map/order-list-overlay/order.hbs +18 -3
- package/addon/components/map/toolbar.hbs +2 -2
- package/addon/components/map/toolbar.js +10 -2
- package/addon/components/orchestrator/phase-builder.hbs +28 -5
- package/addon/components/orchestrator/phase-builder.js +21 -1
- package/addon/components/orchestrator/resource-panel.hbs +19 -1
- package/addon/components/orchestrator/resource-panel.js +4 -0
- package/addon/components/orchestrator-workbench.js +3 -1
- package/addon/components/order/details/purchase-rate.hbs +19 -22
- package/addon/components/order/details/route.hbs +26 -1
- package/addon/components/order/details/route.js +50 -0
- package/addon/components/order/details/tracking.hbs +171 -27
- package/addon/components/order/details/tracking.js +457 -0
- package/addon/components/order/form/details.hbs +1 -2
- package/addon/components/order/form/details.js +6 -0
- package/addon/components/order/form/route.js +9 -1
- package/addon/components/order/kanban-card.hbs +18 -3
- package/addon/components/order-list-overlay/order.hbs +18 -3
- package/addon/components/order-progress-card.hbs +21 -6
- package/addon/components/order-tracking-lookup.hbs +7 -7
- package/addon/components/order-tracking-lookup.js +1 -1
- package/addon/components/route-list.hbs +78 -8
- package/addon/components/route-list.js +48 -6
- package/addon/components/tracking-stop-progress.hbs +45 -0
- package/addon/components/tracking-stop-progress.js +79 -0
- package/addon/controllers/settings/payments/index.js +1 -1
- package/addon/controllers/settings/routing.js +98 -1
- package/addon/helpers/format-duration.js +38 -0
- package/addon/routes/application.js +6 -0
- package/addon/services/order-list-overlay.js +2 -2
- package/addon/styles/fleetops-engine.css +733 -0
- package/addon/templates/settings/routing.hbs +60 -2
- package/app/components/tracking-stop-progress.js +1 -0
- package/app/helpers/format-duration.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +3 -3
- package/pnpm-workspace.yaml +7 -0
- package/server/config/fleetops.php +32 -11
- package/server/migrations/2026_05_13_000001_repair_customer_contact_user_types.php +86 -0
- package/server/seeders/VroomOrchestrationSeeder.php +402 -0
- package/server/src/Http/Controllers/Api/v1/OrchestrationController.php +64 -0
- package/server/src/Http/Controllers/Api/v1/OrderController.php +26 -26
- package/server/src/Http/Controllers/Api/v1/PayloadController.php +10 -10
- package/server/src/Http/Controllers/Api/v1/ServiceQuoteController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/ContactController.php +41 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +6 -42
- package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +69 -15
- package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +119 -12
- package/server/src/Http/Resources/v1/Index/Payload.php +2 -2
- package/server/src/Models/Contact.php +122 -13
- package/server/src/Models/Order.php +1 -1
- package/server/src/Models/Payload.php +8 -8
- package/server/src/Models/Place.php +0 -3
- package/server/src/Models/ServiceRate.php +1 -1
- package/server/src/Observers/ContactObserver.php +8 -0
- package/server/src/Orchestration/Engines/CapacityAllocationEngine.php +220 -0
- package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +330 -38
- package/server/src/Orchestration/Support/OrchestrationPayloadBuilder.php +259 -51
- package/server/src/Providers/FleetOpsServiceProvider.php +27 -0
- package/server/src/Support/LiveOrderQuery.php +75 -0
- package/server/src/Support/Metrics.php +15 -0
- package/server/src/Support/OSRM.php +11 -6
- package/server/src/Support/OrderTracker.php +7 -582
- package/server/src/Support/Utils.php +1 -1
- package/server/src/Tracking/Contracts/TrackingProviderInterface.php +19 -0
- package/server/src/Tracking/Providers/CalculatedTrackingProvider.php +65 -0
- package/server/src/Tracking/Providers/GoogleRoutesTrackingProvider.php +118 -0
- package/server/src/Tracking/Providers/OsrmTrackingProvider.php +71 -0
- package/server/src/Tracking/README.md +25 -0
- package/server/src/Tracking/Support/FakeTrackingProvider.php +42 -0
- package/server/src/Tracking/TrackingContext.php +63 -0
- package/server/src/Tracking/TrackingContextBuilder.php +204 -0
- package/server/src/Tracking/TrackingIntelligenceService.php +180 -0
- package/server/src/Tracking/TrackingOptions.php +49 -0
- package/server/src/Tracking/TrackingProviderCapabilities.php +30 -0
- package/server/src/Tracking/TrackingProviderManager.php +89 -0
- package/server/src/Tracking/TrackingProviderRegistry.php +35 -0
- package/server/src/Tracking/TrackingProviderResult.php +20 -0
- package/server/src/Tracking/TrackingStop.php +59 -0
- package/server/src/routes.php +11 -0
- package/server/tests/CapacityAllocationEngineTest.php +108 -0
- package/server/tests/LiveOrderQueryTest.php +34 -0
- package/server/tests/OrchestrationPayloadBuilderTest.php +132 -0
- package/server/tests/OrchestratorConsumableApiTest.php +74 -0
- package/server/tests/PingDriverEndpointTest.php +19 -0
- package/server/tests/TrackingIntelligenceTest.php +176 -0
- package/server/tests/VroomOrchestrationEngineTest.php +159 -0
- package/translations/en-us.yaml +4 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import 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';
|
|
4
5
|
import { task } from 'ember-concurrency';
|
|
5
6
|
|
|
6
7
|
export default class SettingsRoutingController extends Controller {
|
|
@@ -12,6 +13,19 @@ export default class SettingsRoutingController extends Controller {
|
|
|
12
13
|
@tracked displayEngine = 'osrm';
|
|
13
14
|
@tracked optimizationEngine = 'osrm';
|
|
14
15
|
@tracked routingUnit = 'km';
|
|
16
|
+
@tracked trackingProvider = 'google_routes';
|
|
17
|
+
@tracked trackingFallbacks = ['osrm', 'calculated'];
|
|
18
|
+
@tracked trackingTrafficEnabled = true;
|
|
19
|
+
@tracked trackingCacheTtlSeconds = 60;
|
|
20
|
+
@tracked trackingRouteCacheTtlSeconds = 600;
|
|
21
|
+
@tracked trackingStaleLocationThresholdSeconds = 300;
|
|
22
|
+
@tracked trackingDefaultVehicleSpeedKph = 35;
|
|
23
|
+
@tracked showTrackingAdvancedSettings = false;
|
|
24
|
+
@tracked trackingProviderOptions = [
|
|
25
|
+
{ value: 'google_routes', label: 'Google Routes' },
|
|
26
|
+
{ value: 'osrm', label: 'OSRM' },
|
|
27
|
+
{ value: 'calculated', label: 'Calculated' },
|
|
28
|
+
];
|
|
15
29
|
@tracked routingUnitOptions = [
|
|
16
30
|
{ label: 'Kilometers', value: 'km' },
|
|
17
31
|
{ label: 'Miles', value: 'mi' },
|
|
@@ -39,6 +53,8 @@ export default class SettingsRoutingController extends Controller {
|
|
|
39
53
|
optimization_engine: this.optimizationEngine,
|
|
40
54
|
unit: this.routingUnit,
|
|
41
55
|
});
|
|
56
|
+
const trackingSettings = this.trackingSettingsPayload;
|
|
57
|
+
yield this.fetch.post('fleet-ops/settings/tracking-settings', trackingSettings);
|
|
42
58
|
yield this.performAdditionalSaveTasks();
|
|
43
59
|
// Save in local memory too
|
|
44
60
|
this.currentUser.setOption('routing', {
|
|
@@ -47,7 +63,8 @@ export default class SettingsRoutingController extends Controller {
|
|
|
47
63
|
routing_optimization_engine: this.optimizationEngine,
|
|
48
64
|
unit: this.routingUnit,
|
|
49
65
|
});
|
|
50
|
-
this.
|
|
66
|
+
this.currentUser.setOption('tracking', trackingSettings);
|
|
67
|
+
this.notifications.success('Routing and tracking settings saved.');
|
|
51
68
|
} catch (error) {
|
|
52
69
|
this.notifications.serverError(error);
|
|
53
70
|
}
|
|
@@ -64,11 +81,91 @@ export default class SettingsRoutingController extends Controller {
|
|
|
64
81
|
this.displayEngine = display_engine ?? router ?? 'osrm';
|
|
65
82
|
this.optimizationEngine = optimization_engine ?? display_engine ?? router ?? 'osrm';
|
|
66
83
|
this.routingUnit = unit;
|
|
84
|
+
const trackingSettings = yield this.fetch.get('fleet-ops/settings/tracking-settings');
|
|
85
|
+
this.trackingProvider = trackingSettings.provider ?? 'google_routes';
|
|
86
|
+
this.trackingFallbacks = this.normalizeFallbacks(trackingSettings.fallbacks);
|
|
87
|
+
this.trackingTrafficEnabled = trackingSettings.traffic_enabled ?? true;
|
|
88
|
+
this.trackingCacheTtlSeconds = trackingSettings.cache_ttl_seconds ?? 60;
|
|
89
|
+
this.trackingRouteCacheTtlSeconds = trackingSettings.route_cache_ttl_seconds ?? 600;
|
|
90
|
+
this.trackingStaleLocationThresholdSeconds = trackingSettings.stale_location_threshold_seconds ?? 300;
|
|
91
|
+
this.trackingDefaultVehicleSpeedKph = trackingSettings.default_vehicle_speed_kph ?? 35;
|
|
92
|
+
this.trackingProviderOptions = this.normalizeProviderOptions(trackingSettings.providers ?? this.trackingProviderOptions);
|
|
67
93
|
} catch (error) {
|
|
68
94
|
this.notifications.serverError(error);
|
|
69
95
|
}
|
|
70
96
|
}
|
|
71
97
|
|
|
98
|
+
get selectedTrackingFallbackOptions() {
|
|
99
|
+
const selected = new Set(this.normalizeFallbacks(this.trackingFallbacks));
|
|
100
|
+
|
|
101
|
+
return this.trackingProviderOptions.filter((option) => selected.has(this.optionValue(option)));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
get trackingSettingsPayload() {
|
|
105
|
+
return {
|
|
106
|
+
provider: this.trackingProvider,
|
|
107
|
+
fallbacks: this.normalizeFallbacks(this.trackingFallbacks),
|
|
108
|
+
traffic_enabled: this.trackingTrafficEnabled,
|
|
109
|
+
cache_ttl_seconds: Number(this.trackingCacheTtlSeconds) || 60,
|
|
110
|
+
route_cache_ttl_seconds: Number(this.trackingRouteCacheTtlSeconds) || 600,
|
|
111
|
+
stale_location_threshold_seconds: Number(this.trackingStaleLocationThresholdSeconds) || 300,
|
|
112
|
+
default_vehicle_speed_kph: Number(this.trackingDefaultVehicleSpeedKph) || 35,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
normalizeFallbacks(fallbacks) {
|
|
117
|
+
if (Array.isArray(fallbacks)) {
|
|
118
|
+
return fallbacks
|
|
119
|
+
.map((fallback) => this.optionValue(fallback))
|
|
120
|
+
.map((fallback) => String(fallback).trim())
|
|
121
|
+
.filter(Boolean);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return String(fallbacks ?? '')
|
|
125
|
+
.split(',')
|
|
126
|
+
.map((fallback) => fallback.trim())
|
|
127
|
+
.filter(Boolean);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
normalizeProviderOptions(options = []) {
|
|
131
|
+
return options.map((option) => {
|
|
132
|
+
const value = this.optionValue(option);
|
|
133
|
+
const label = option?.label ?? option?.name ?? this.providerLabel(value);
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
...option,
|
|
137
|
+
key: option?.key ?? value,
|
|
138
|
+
name: option?.name ?? label,
|
|
139
|
+
value,
|
|
140
|
+
label,
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
providerLabel(value) {
|
|
146
|
+
if (value === 'osrm') {
|
|
147
|
+
return 'OSRM';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return String(value ?? '')
|
|
151
|
+
.replace(/[_-]+/g, ' ')
|
|
152
|
+
.replace(/\s+/g, ' ')
|
|
153
|
+
.trim()
|
|
154
|
+
.replace(/\b\w/g, (character) => character.toUpperCase());
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
optionValue(option) {
|
|
158
|
+
return typeof option === 'object' && option !== null ? (option.value ?? option.key) : option;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@action setTrackingFallbacks(options) {
|
|
162
|
+
this.trackingFallbacks = this.normalizeFallbacks(options);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
@action toggleTrackingAdvancedSettings() {
|
|
166
|
+
this.showTrackingAdvancedSettings = !this.showTrackingAdvancedSettings;
|
|
167
|
+
}
|
|
168
|
+
|
|
72
169
|
registerSaveTask(key, task) {
|
|
73
170
|
if (!key || typeof task?.perform !== 'function') {
|
|
74
171
|
return;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { helper } from '@ember/component/helper';
|
|
2
|
+
|
|
3
|
+
export function formatDurationValue(seconds) {
|
|
4
|
+
const value = Number(seconds);
|
|
5
|
+
|
|
6
|
+
if (!Number.isFinite(value)) {
|
|
7
|
+
return '0s';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const totalSeconds = Math.max(0, Math.ceil(value));
|
|
11
|
+
const days = Math.floor(totalSeconds / 86400);
|
|
12
|
+
const hours = Math.floor((totalSeconds % 86400) / 3600);
|
|
13
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
14
|
+
const remainingSeconds = totalSeconds % 60;
|
|
15
|
+
const parts = [];
|
|
16
|
+
|
|
17
|
+
if (days) {
|
|
18
|
+
parts.push(`${days}d`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (hours) {
|
|
22
|
+
parts.push(`${hours}h`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!days && minutes) {
|
|
26
|
+
parts.push(`${minutes}m`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!days && !hours && parts.length < 2 && remainingSeconds) {
|
|
30
|
+
parts.push(`${remainingSeconds}s`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return parts.length ? parts.join(' ') : '0s';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default helper(function formatDuration([seconds]) {
|
|
37
|
+
return formatDurationValue(seconds);
|
|
38
|
+
});
|
|
@@ -29,6 +29,7 @@ export default class ApplicationRoute extends Route {
|
|
|
29
29
|
|
|
30
30
|
await this.location.getUserLocation();
|
|
31
31
|
await this.#loadRoutingSettings();
|
|
32
|
+
await this.#loadTrackingSettings();
|
|
32
33
|
await this.#loadMapSettings();
|
|
33
34
|
}
|
|
34
35
|
|
|
@@ -37,6 +38,11 @@ export default class ApplicationRoute extends Route {
|
|
|
37
38
|
this.currentUser.setOption('routing', routingSetting);
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
async #loadTrackingSettings() {
|
|
42
|
+
const trackingSettings = await this.fetch.get('fleet-ops/settings/tracking-settings');
|
|
43
|
+
this.currentUser.setOption('tracking', trackingSettings);
|
|
44
|
+
}
|
|
45
|
+
|
|
40
46
|
async #loadMapSettings() {
|
|
41
47
|
await this.mapSettings.load();
|
|
42
48
|
}
|
|
@@ -185,12 +185,12 @@ export default class OrderListOverlayService extends Service {
|
|
|
185
185
|
|
|
186
186
|
#peekActiveOrders() {
|
|
187
187
|
// Consider an order "active" if it has an assigned driver and is not terminal
|
|
188
|
-
const TERMINAL = ['created', 'completed', 'canceled', 'expired'];
|
|
188
|
+
const TERMINAL = ['created', 'completed', 'canceled', 'expired', 'order_canceled', 'pending'];
|
|
189
189
|
return this.#peekOrders((order) => !!order.driver_assigned && !TERMINAL.includes(order.status));
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
#peekDriverActiveOrders(driver) {
|
|
193
|
-
const TERMINAL = ['created', 'completed', 'canceled', 'expired'];
|
|
193
|
+
const TERMINAL = ['created', 'completed', 'canceled', 'expired', 'order_canceled', 'pending'];
|
|
194
194
|
return this.#peekOrders((order) => !!order.driver_assigned && order.driver_assigned?.id === driver.id && !TERMINAL.includes(order.status));
|
|
195
195
|
}
|
|
196
196
|
|