@fleetbase/fleetops-engine 0.6.47 → 0.6.49
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/driver/details.hbs +5 -1
- package/addon/components/driver/form.hbs +11 -1
- package/addon/components/fleet-listing-panel.hbs +2 -2
- package/addon/components/fleet-listing-panel.js +10 -0
- package/addon/components/map/google-live-map.js +12 -73
- package/addon/components/map/leaflet-live-map.hbs +149 -33
- 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 +49 -5
- package/addon/components/orchestrator/phase-builder.js +34 -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 +4 -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.hbs +2 -2
- 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/components/widget/fleet-ops-key-metrics.hbs +19 -17
- package/addon/components/widget/fleet-ops-key-metrics.js +26 -0
- package/addon/components/widget/fuel-efficiency.hbs +26 -0
- package/addon/components/widget/fuel-efficiency.js +43 -0
- package/addon/components/widget/geofence-violations.hbs +44 -0
- package/addon/components/widget/geofence-violations.js +55 -0
- package/addon/components/widget/issues-insights.hbs +43 -0
- package/addon/components/widget/issues-insights.js +62 -0
- package/addon/components/widget/kpi-active-orders.hbs +10 -0
- package/addon/components/widget/kpi-active-orders.js +5 -0
- package/addon/components/widget/kpi-aov.hbs +10 -0
- package/addon/components/widget/kpi-aov.js +5 -0
- package/addon/components/widget/kpi-distance.hbs +10 -0
- package/addon/components/widget/kpi-distance.js +5 -0
- package/addon/components/widget/kpi-drivers-online.hbs +10 -0
- package/addon/components/widget/kpi-drivers-online.js +5 -0
- package/addon/components/widget/kpi-earnings.hbs +10 -0
- package/addon/components/widget/kpi-earnings.js +5 -0
- package/addon/components/widget/kpi-open-issues.hbs +11 -0
- package/addon/components/widget/kpi-open-issues.js +5 -0
- package/addon/components/widget/kpi-tile.hbs +59 -0
- package/addon/components/widget/kpi-tile.js +168 -0
- package/addon/components/widget/live-fleet.hbs +122 -0
- package/addon/components/widget/live-fleet.js +365 -0
- package/addon/components/widget/maintenance-overview.hbs +45 -0
- package/addon/components/widget/maintenance-overview.js +40 -0
- package/addon/components/widget/on-time-delivery.hbs +27 -0
- package/addon/components/widget/on-time-delivery.js +73 -0
- package/addon/components/widget/operations-pulse.hbs +51 -0
- package/addon/components/widget/operations-pulse.js +173 -0
- package/addon/components/widget/orders-by-status.hbs +26 -0
- package/addon/components/widget/orders-by-status.js +54 -0
- package/addon/components/widget/revenue-trend.hbs +34 -0
- package/addon/components/widget/revenue-trend.js +67 -0
- package/addon/components/widget/top-drivers.hbs +44 -0
- package/addon/components/widget/top-drivers.js +54 -0
- package/addon/controllers/settings/routing.js +124 -1
- package/addon/extension.js +172 -3
- package/addon/helpers/format-duration.js +38 -0
- package/addon/routes/application.js +6 -0
- package/addon/services/map-adapter/google.js +13 -2
- package/addon/services/order-list-overlay.js +2 -2
- package/addon/styles/fleetops-engine.css +806 -46
- package/addon/templates/maintenance/equipment/index.hbs +1 -1
- package/addon/templates/maintenance/parts/index.hbs +1 -1
- package/addon/templates/maintenance/schedules/index.hbs +1 -1
- package/addon/templates/management/vehicles/index.hbs +1 -1
- package/addon/templates/settings/avatars.hbs +1 -1
- package/addon/templates/settings/custom-fields.hbs +1 -1
- package/addon/templates/settings/map.hbs +1 -1
- package/addon/templates/settings/navigator-app.hbs +1 -1
- package/addon/templates/settings/notifications.hbs +1 -1
- package/addon/templates/settings/orchestrator.hbs +1 -1
- package/addon/templates/settings/payments/index.hbs +1 -1
- package/addon/templates/settings/payments/onboard.hbs +1 -1
- package/addon/templates/settings/routing.hbs +82 -4
- package/addon/templates/settings/scheduling.hbs +1 -1
- package/addon/utils/live-map-card-content.js +171 -0
- package/app/components/tracking-stop-progress.js +1 -0
- package/app/components/widget/fuel-efficiency.js +1 -0
- package/app/components/widget/geofence-violations.js +1 -0
- package/app/components/widget/issues-insights.js +1 -0
- package/app/components/widget/kpi-active-orders.js +1 -0
- package/app/components/widget/kpi-aov.js +1 -0
- package/app/components/widget/kpi-distance.js +1 -0
- package/app/components/widget/kpi-drivers-online.js +1 -0
- package/app/components/widget/kpi-earnings.js +1 -0
- package/app/components/widget/kpi-open-issues.js +1 -0
- package/app/components/widget/kpi-tile.js +1 -0
- package/app/components/widget/live-fleet.js +1 -0
- package/app/components/widget/maintenance-overview.js +1 -0
- package/app/components/widget/on-time-delivery.js +1 -0
- package/app/components/widget/operations-pulse.js +1 -0
- package/app/components/widget/orders-by-status.js +1 -0
- package/app/components/widget/revenue-trend.js +1 -0
- package/app/components/widget/top-drivers.js +1 -0
- package/app/helpers/format-duration.js +1 -0
- package/app/utils/live-map-card-content.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +3 -3
- package/pnpm-workspace.yaml +1 -0
- package/server/config/fleetops.php +32 -11
- package/server/config/telematics.php +111 -1
- package/server/migrations/2026_05_13_000001_repair_customer_contact_user_types.php +86 -0
- package/server/migrations/2026_05_18_000001_add_license_expiry_to_drivers_table.php +25 -0
- package/server/seeders/Testing/Concerns/SeedsTestingData.php +183 -0
- package/server/seeders/Testing/ConnectivitySeeder.php +212 -0
- package/server/seeders/Testing/FleetSeeder.php +341 -0
- package/server/seeders/Testing/MaintenanceSeeder.php +238 -0
- package/server/seeders/Testing/NetworkSeeder.php +250 -0
- package/server/seeders/Testing/OrdersSeeder.php +214 -0
- package/server/seeders/Testing/TestingSeeder.php +40 -0
- package/server/seeders/Testing/VroomOrchestrationSeeder.php +409 -0
- package/server/src/Console/Commands/ProcessOperationalAlerts.php +279 -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/AnalyticsController.php +119 -0
- 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/MetricsController.php +82 -3
- package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +52 -21
- package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +164 -12
- package/server/src/Http/Resources/v1/Driver.php +2 -0
- package/server/src/Http/Resources/v1/Fleet.php +4 -0
- package/server/src/Http/Resources/v1/Index/Driver.php +48 -0
- package/server/src/Http/Resources/v1/Index/Payload.php +2 -2
- package/server/src/Http/Resources/v1/Index/Vehicle.php +74 -1
- package/server/src/Models/Contact.php +122 -13
- package/server/src/Models/Driver.php +2 -0
- package/server/src/Models/Order.php +1 -1
- package/server/src/Models/OrderConfig.php +14 -11
- 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/Notifications/LateDeparture.php +48 -0
- package/server/src/Notifications/ProlongedStoppage.php +48 -0
- package/server/src/Notifications/RouteDeviation.php +48 -0
- package/server/src/Observers/ContactObserver.php +8 -0
- package/server/src/Orchestration/Engines/CapacityAllocationEngine.php +220 -0
- package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +357 -38
- package/server/src/Orchestration/Support/OrchestrationPayloadBuilder.php +259 -51
- package/server/src/Providers/FleetOpsServiceProvider.php +32 -0
- package/server/src/Providers/NotificationServiceProvider.php +3 -0
- package/server/src/Support/Analytics/AbstractAnalytics.php +77 -0
- package/server/src/Support/Analytics/FuelEfficiency.php +83 -0
- package/server/src/Support/Analytics/GeofenceViolations.php +63 -0
- package/server/src/Support/Analytics/IssuesInsights.php +67 -0
- package/server/src/Support/Analytics/LiveFleet.php +127 -0
- package/server/src/Support/Analytics/MaintenanceOverview.php +70 -0
- package/server/src/Support/Analytics/OnTimeDelivery.php +75 -0
- package/server/src/Support/Analytics/OperationsPulse.php +86 -0
- package/server/src/Support/Analytics/OrdersByStatus.php +64 -0
- package/server/src/Support/Analytics/RevenueTrend.php +79 -0
- package/server/src/Support/Analytics/TopDrivers.php +92 -0
- package/server/src/Support/LiveOrderQuery.php +75 -0
- package/server/src/Support/Metrics/AbstractMetric.php +170 -0
- package/server/src/Support/Metrics/ActiveLiveOrdersMetric.php +28 -0
- package/server/src/Support/Metrics/AvgOrderValueMetric.php +53 -0
- package/server/src/Support/Metrics/DriversOnlineMetric.php +30 -0
- package/server/src/Support/Metrics/EarningsMetric.php +30 -0
- package/server/src/Support/Metrics/FuelCostsMetric.php +30 -0
- package/server/src/Support/Metrics/MoneyMetric.php +30 -0
- package/server/src/Support/Metrics/OpenIssuesMetric.php +35 -0
- package/server/src/Support/Metrics/OrdersCanceledMetric.php +35 -0
- package/server/src/Support/Metrics/OrdersCompletedMetric.php +35 -0
- package/server/src/Support/Metrics/OrdersInProgressMetric.php +51 -0
- package/server/src/Support/Metrics/OrdersScheduledMetric.php +37 -0
- package/server/src/Support/Metrics/Registry.php +46 -0
- package/server/src/Support/Metrics/ResolvedIssuesMetric.php +35 -0
- package/server/src/Support/Metrics/TotalCustomersMetric.php +29 -0
- package/server/src/Support/Metrics/TotalDistanceTraveledMetric.php +35 -0
- package/server/src/Support/Metrics/TotalDriversMetric.php +28 -0
- package/server/src/Support/Metrics/TotalTimeTraveledMetric.php +35 -0
- package/server/src/Support/Metrics.php +46 -234
- package/server/src/Support/OSRM.php +11 -6
- package/server/src/Support/OrderTracker.php +7 -582
- package/server/src/Support/Telematics/Providers/AfaqyProvider.php +290 -0
- package/server/src/Support/Telematics/Providers/SafeeProvider.php +333 -0
- 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 +27 -0
- package/server/tests/AnalyticsRoutesTest.php +41 -0
- package/server/tests/CapacityAllocationEngineTest.php +108 -0
- package/server/tests/LiveOrderQueryTest.php +34 -0
- package/server/tests/MetricsRegistryTest.php +88 -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 +250 -0
- package/translations/en-us.yaml +41 -0
|
@@ -83,27 +83,94 @@
|
|
|
83
83
|
as |marker|
|
|
84
84
|
>
|
|
85
85
|
<marker.popup @maxWidth="500" @minWidth="225">
|
|
86
|
-
<div class="
|
|
87
|
-
<div class="
|
|
88
|
-
<
|
|
86
|
+
<div class="w-[280px] max-w-[calc(100vw-2rem)] rounded-lg bg-gray-900 shadow-lg p-1.5 text-white">
|
|
87
|
+
<div class="flex items-center gap-1.5 text-xs font-semibold mb-1.5">
|
|
88
|
+
<span class="inline-block w-2 h-2 rounded-full {{if driver.online 'bg-green-400' 'bg-red-500'}}"></span>
|
|
89
|
+
<span>{{driver.name}}</span>
|
|
90
|
+
<div class="status-badge {{if driver.status (dasherize driver.status) 'default'}}-status-badge status-badge-{{if driver.status (dasherize driver.status) 'default'}} shadow-none ml-auto">
|
|
91
|
+
<span class="rounded-full text-[9px] px-1.5 py-0.5">{{n-a driver.meta.status_label (smart-humanize driver.status)}}</span>
|
|
92
|
+
</div>
|
|
89
93
|
</div>
|
|
90
|
-
<div class="
|
|
91
|
-
<div class="
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
<div class="grid grid-cols-2 gap-1">
|
|
95
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
96
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">ID</div>
|
|
97
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.public_id driver.id}}</div>
|
|
98
|
+
</div>
|
|
99
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
100
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Phone</div>
|
|
101
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.phone}}</div>
|
|
102
|
+
</div>
|
|
103
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
104
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Vehicle</div>
|
|
105
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.vehicle_name}}</div>
|
|
106
|
+
</div>
|
|
107
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
108
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Email</div>
|
|
109
|
+
<div class="text-[11px] text-white leading-tight truncate min-w-0 w-full">{{n-a driver.email}}</div>
|
|
110
|
+
</div>
|
|
111
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
112
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Order</div>
|
|
113
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.meta.current_order_reference}}</div>
|
|
114
|
+
</div>
|
|
115
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
116
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Speed</div>
|
|
117
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.meta.speed_label}}</div>
|
|
118
|
+
</div>
|
|
119
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
120
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Heading</div>
|
|
121
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.meta.heading_label}}</div>
|
|
122
|
+
</div>
|
|
123
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
124
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Location</div>
|
|
125
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.meta.location_coordinates (point-coordinates driver.location)}}</div>
|
|
126
|
+
</div>
|
|
97
127
|
</div>
|
|
98
128
|
</div>
|
|
99
129
|
</marker.popup>
|
|
100
130
|
<marker.tooltip @permanent={{false}} @sticky={{true}}>
|
|
101
|
-
<div class="
|
|
102
|
-
<div class="text-xs font-semibold">
|
|
103
|
-
|
|
104
|
-
|
|
131
|
+
<div class="w-[280px] max-w-[calc(100vw-2rem)]">
|
|
132
|
+
<div class="flex items-center gap-1.5 text-xs font-semibold mb-1.5">
|
|
133
|
+
<span class="inline-block w-2 h-2 rounded-full {{if driver.online 'bg-green-400' 'bg-red-500'}}"></span>
|
|
134
|
+
<span>{{driver.name}}</span>
|
|
135
|
+
<div class="status-badge {{if driver.status (dasherize driver.status) 'default'}}-status-badge status-badge-{{if driver.status (dasherize driver.status) 'default'}} shadow-none ml-auto">
|
|
136
|
+
<span class="rounded-full text-[9px] px-1.5 py-0.5">{{n-a driver.meta.status_label (smart-humanize driver.status)}}</span>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
<div class="grid grid-cols-2 gap-1">
|
|
140
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
141
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">ID</div>
|
|
142
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.public_id driver.id}}</div>
|
|
143
|
+
</div>
|
|
144
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
145
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Phone</div>
|
|
146
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.phone}}</div>
|
|
147
|
+
</div>
|
|
148
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
149
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Vehicle</div>
|
|
150
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.vehicle_name}}</div>
|
|
151
|
+
</div>
|
|
152
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
153
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Email</div>
|
|
154
|
+
<div class="text-[11px] text-white leading-tight truncate min-w-0 w-full">{{n-a driver.email}}</div>
|
|
155
|
+
</div>
|
|
156
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
157
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Order</div>
|
|
158
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.meta.current_order_reference}}</div>
|
|
159
|
+
</div>
|
|
160
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
161
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Speed</div>
|
|
162
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.meta.speed_label}}</div>
|
|
163
|
+
</div>
|
|
164
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
165
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Heading</div>
|
|
166
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.meta.heading_label}}</div>
|
|
167
|
+
</div>
|
|
168
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
169
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Location</div>
|
|
170
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.meta.location_coordinates (point-coordinates driver.location)}}</div>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
105
173
|
</div>
|
|
106
|
-
<div class="text-xs truncate"><FaIcon @icon="location-dot" @size="xs" class="mr-0.5" />{{point-coordinates driver.location}}</div>
|
|
107
174
|
</marker.tooltip>
|
|
108
175
|
</layers.tracking-marker>
|
|
109
176
|
{{/each}}
|
|
@@ -121,29 +188,78 @@
|
|
|
121
188
|
as |marker|
|
|
122
189
|
>
|
|
123
190
|
<marker.popup @permanent={{false}} @sticky={{true}}>
|
|
124
|
-
<div class="
|
|
125
|
-
<div class="
|
|
126
|
-
<
|
|
191
|
+
<div class="w-[280px] max-w-[calc(100vw-2rem)] rounded-lg bg-gray-900 shadow-lg p-1.5 text-white">
|
|
192
|
+
<div class="flex items-center gap-1.5 text-xs font-semibold mb-1.5">
|
|
193
|
+
<span class="inline-block w-2 h-2 rounded-full {{if vehicle.online 'bg-green-400' 'bg-red-500'}}"></span>
|
|
194
|
+
<span>{{vehicle.displayName}}</span>
|
|
195
|
+
<div class="status-badge {{if vehicle.status (dasherize vehicle.status) 'default'}}-status-badge status-badge-{{if vehicle.status (dasherize vehicle.status) 'default'}} shadow-none ml-auto">
|
|
196
|
+
<span class="rounded-full text-[9px] px-1.5 py-0.5">{{n-a vehicle.meta.status_label}}</span>
|
|
197
|
+
</div>
|
|
127
198
|
</div>
|
|
128
|
-
<div class="
|
|
129
|
-
<div class="
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
<div class="
|
|
134
|
-
|
|
199
|
+
<div class="grid grid-cols-2 gap-1">
|
|
200
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
201
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Vehicle #</div>
|
|
202
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.internal_id vehicle.public_id vehicle.plate_number vehicle.serial_number vehicle.vin}}</div>
|
|
203
|
+
</div>
|
|
204
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
205
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Driver</div>
|
|
206
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.driver_name}}</div>
|
|
207
|
+
</div>
|
|
208
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
209
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Order</div>
|
|
210
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.meta.current_order_reference}}</div>
|
|
211
|
+
</div>
|
|
212
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
213
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Speed</div>
|
|
214
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.meta.speed_label}}</div>
|
|
215
|
+
</div>
|
|
216
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
217
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Heading</div>
|
|
218
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.meta.heading_label}}</div>
|
|
219
|
+
</div>
|
|
220
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
221
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Location</div>
|
|
222
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.meta.location_coordinates}}</div>
|
|
223
|
+
</div>
|
|
135
224
|
</div>
|
|
136
225
|
</div>
|
|
137
226
|
</marker.popup>
|
|
138
227
|
<marker.tooltip @permanent={{false}} @sticky={{true}}>
|
|
139
|
-
<div class="
|
|
140
|
-
<div class="text-xs font-semibold">
|
|
141
|
-
|
|
142
|
-
|
|
228
|
+
<div class="w-[280px] max-w-[calc(100vw-2rem)]">
|
|
229
|
+
<div class="flex items-center gap-1.5 text-xs font-semibold mb-1.5">
|
|
230
|
+
<span class="inline-block w-2 h-2 rounded-full {{if vehicle.online 'bg-green-400' 'bg-red-500'}}"></span>
|
|
231
|
+
<span>{{vehicle.displayName}}</span>
|
|
232
|
+
<div class="status-badge {{if vehicle.status (dasherize vehicle.status) 'default'}}-status-badge status-badge-{{if vehicle.status (dasherize vehicle.status) 'default'}} shadow-none ml-auto">
|
|
233
|
+
<span class="rounded-full text-[9px] px-1.5 py-0.5">{{n-a vehicle.meta.status_label}}</span>
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
<div class="grid grid-cols-2 gap-1">
|
|
237
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
238
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Vehicle #</div>
|
|
239
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.internal_id vehicle.public_id vehicle.plate_number vehicle.serial_number vehicle.vin}}</div>
|
|
240
|
+
</div>
|
|
241
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
242
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Driver</div>
|
|
243
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.driver_name}}</div>
|
|
244
|
+
</div>
|
|
245
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
246
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Order</div>
|
|
247
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.meta.current_order_reference}}</div>
|
|
248
|
+
</div>
|
|
249
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
250
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Speed</div>
|
|
251
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.meta.speed_label}}</div>
|
|
252
|
+
</div>
|
|
253
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
254
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Heading</div>
|
|
255
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.meta.heading_label}}</div>
|
|
256
|
+
</div>
|
|
257
|
+
<div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
|
|
258
|
+
<div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Location</div>
|
|
259
|
+
<div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.meta.location_coordinates}}</div>
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
143
262
|
</div>
|
|
144
|
-
<div class="text-xs">ID: {{n-a vehicle.public_id}}</div>
|
|
145
|
-
<div class="text-xs">Serial No: {{or vehicle.serial_number vehicle.vin vehicle.internal_id vehicle.id "-"}}</div>
|
|
146
|
-
<div class="text-xs truncate"><FaIcon @icon="location-dot" @size="xs" class="mr-0.5" />{{point-coordinates vehicle.location}}</div>
|
|
147
263
|
</marker.tooltip>
|
|
148
264
|
</layers.tracking-marker>
|
|
149
265
|
{{/each}}
|
|
@@ -224,4 +340,4 @@
|
|
|
224
340
|
@liveMap={{this}}
|
|
225
341
|
@onDrawerReady={{this.setDrawerContext}}
|
|
226
342
|
/>
|
|
227
|
-
{{/if}} --}}
|
|
343
|
+
{{/if}} --}}
|
|
@@ -27,10 +27,25 @@
|
|
|
27
27
|
<div class="order-listing-row-progress">
|
|
28
28
|
<OrderProgressBar
|
|
29
29
|
@order={{@order}}
|
|
30
|
-
@progress={{@order.tracker_data.
|
|
31
|
-
@firstWaypointCompleted={{@order.tracker_data.
|
|
32
|
-
@lastWaypointCompleted={{@order.tracker_data.
|
|
30
|
+
@progress={{@order.tracker_data.progress.percentage}}
|
|
31
|
+
@firstWaypointCompleted={{gt @order.tracker_data.progress.completed_stops 0}}
|
|
32
|
+
@lastWaypointCompleted={{eq @order.tracker_data.progress.percentage 100}}
|
|
33
33
|
/>
|
|
34
|
+
{{#if @order.tracker_data.insights.is_location_stale}}
|
|
35
|
+
<div class="flex mt-1">
|
|
36
|
+
<Badge @status="warning" @hideStatusDot={{true}}>Stale GPS</Badge>
|
|
37
|
+
</div>
|
|
38
|
+
{{else if @order.tracker_data.fallback_provider}}
|
|
39
|
+
<div class="flex mt-1">
|
|
40
|
+
<Badge @status="warning" @hideStatusDot={{true}}>Fallback ETA</Badge>
|
|
41
|
+
</div>
|
|
42
|
+
{{else if @order.tracker_data.confidence}}
|
|
43
|
+
{{#unless (eq @order.tracker_data.confidence "high")}}
|
|
44
|
+
<div class="flex mt-1">
|
|
45
|
+
<Badge @status="warning" @hideStatusDot={{true}}>Low ETA Confidence</Badge>
|
|
46
|
+
</div>
|
|
47
|
+
{{/unless}}
|
|
48
|
+
{{/if}}
|
|
34
49
|
</div>
|
|
35
50
|
<div class="order-listing-row-body">
|
|
36
51
|
<div class="order-listing-row-body-address start">
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
<button type="button" id="map-toolbar-view-orders-button" class="toolbar-button relative {{if unauthorized 'unauthorized'}}" {{on "click" this.orderListOverlay.toggle}}>
|
|
43
43
|
<div class="active-orders-count absolute top-0 right-0 -mr-2.5 -mt-1">
|
|
44
44
|
<div class="relative flex w-5 h-5 rounded-full">
|
|
45
|
-
<div class="absolute inset-0 rounded-full w-5 h-5 text-xs bg-blue-500 opacity-75 {{if this.
|
|
46
|
-
<div class="relative inline-flex items-center justify-center rounded-full w-5 h-5 text-xs bg-blue-500 text-white z-10">{{this.
|
|
45
|
+
<div class="absolute inset-0 rounded-full w-5 h-5 text-xs bg-blue-500 opacity-75 {{if this.displayedActiveOrderCount 'animate-ping'}}"></div>
|
|
46
|
+
<div class="relative inline-flex items-center justify-center rounded-full w-5 h-5 text-xs bg-blue-500 text-white z-10">{{this.displayedActiveOrderCount}}</div>
|
|
47
47
|
</div>
|
|
48
48
|
</div>
|
|
49
49
|
<FaIcon @icon="layer-group" />
|
|
@@ -38,10 +38,18 @@ export default class MapToolbarComponent extends Component {
|
|
|
38
38
|
return this.mapManager.zoomOut?.();
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
get displayedActiveOrderCount() {
|
|
42
|
+
if (this.orderListOverlay.isOpen && this.orderListOverlay.loaded) {
|
|
43
|
+
return this.orderListOverlay.activeOrdersCount;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return this.activeOrderCount;
|
|
47
|
+
}
|
|
48
|
+
|
|
41
49
|
@task *getActiveOrderCount() {
|
|
42
50
|
try {
|
|
43
|
-
const count = yield this.fetch.get('fleet-ops/metrics', { discover: ['
|
|
44
|
-
this.activeOrderCount = count.
|
|
51
|
+
const count = yield this.fetch.get('fleet-ops/metrics', { discover: ['active_live_orders'] });
|
|
52
|
+
this.activeOrderCount = count.active_live_orders;
|
|
45
53
|
return count;
|
|
46
54
|
} catch (err) {
|
|
47
55
|
debug('Failed to get active order count: ' + err.message);
|
|
@@ -109,6 +109,48 @@
|
|
|
109
109
|
/>
|
|
110
110
|
</InputGroup>
|
|
111
111
|
|
|
112
|
+
{{#if this.shouldShowAllocationStrategy}}
|
|
113
|
+
<InputGroup @name={{t "orchestrator.allocation-strategy"}}>
|
|
114
|
+
<div class="grid grid-cols-2 gap-1">
|
|
115
|
+
{{#each this.allocationStrategyOptions as |opt|}}
|
|
116
|
+
<button
|
|
117
|
+
type="button"
|
|
118
|
+
class="flex items-center justify-center gap-1.5 px-2 py-1.5 rounded border text-xs transition-colors
|
|
119
|
+
{{if
|
|
120
|
+
(eq this.draftPhase.allocationStrategy opt.value)
|
|
121
|
+
'bg-indigo-50 dark:bg-indigo-900/30 border-indigo-400 dark:border-indigo-600 text-indigo-700 dark:text-indigo-300'
|
|
122
|
+
'border-gray-200 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:border-gray-300 dark:hover:border-gray-500'
|
|
123
|
+
}}"
|
|
124
|
+
{{on "click" (fn this.setDraftOption "allocationStrategy" opt.value)}}
|
|
125
|
+
>
|
|
126
|
+
{{opt.label}}
|
|
127
|
+
</button>
|
|
128
|
+
{{/each}}
|
|
129
|
+
</div>
|
|
130
|
+
</InputGroup>
|
|
131
|
+
{{/if}}
|
|
132
|
+
|
|
133
|
+
{{#if this.shouldShowVehiclePacking}}
|
|
134
|
+
<InputGroup @name={{t "orchestrator.vehicle-packing"}}>
|
|
135
|
+
<div class="grid grid-cols-2 gap-1">
|
|
136
|
+
{{#each this.vehiclePackingOptions as |opt|}}
|
|
137
|
+
<button
|
|
138
|
+
type="button"
|
|
139
|
+
class="flex items-center justify-center gap-1.5 px-2 py-1.5 rounded border text-xs transition-colors
|
|
140
|
+
{{if
|
|
141
|
+
(eq this.draftPhase.vehiclePacking opt.value)
|
|
142
|
+
'bg-indigo-50 dark:bg-indigo-900/30 border-indigo-400 dark:border-indigo-600 text-indigo-700 dark:text-indigo-300'
|
|
143
|
+
'border-gray-200 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:border-gray-300 dark:hover:border-gray-500'
|
|
144
|
+
}}"
|
|
145
|
+
{{on "click" (fn this.setDraftOption "vehiclePacking" opt.value)}}
|
|
146
|
+
>
|
|
147
|
+
{{opt.label}}
|
|
148
|
+
</button>
|
|
149
|
+
{{/each}}
|
|
150
|
+
</div>
|
|
151
|
+
</InputGroup>
|
|
152
|
+
{{/if}}
|
|
153
|
+
|
|
112
154
|
{{! Constraint toggles }}
|
|
113
155
|
<InputGroup @name={{t "orchestrator.constraints"}}>
|
|
114
156
|
<div class="space-y-1.5">
|
|
@@ -124,10 +166,12 @@
|
|
|
124
166
|
<span class="text-xs text-gray-600 dark:text-gray-400">{{t "orchestrator.balance-workload"}}</span>
|
|
125
167
|
<Toggle @isToggled={{this.draftPhase.balanceWorkload}} @onToggle={{fn this.setDraftOption "balanceWorkload"}} />
|
|
126
168
|
</div>
|
|
127
|
-
|
|
128
|
-
<
|
|
129
|
-
|
|
130
|
-
|
|
169
|
+
{{#if this.shouldShowRouteOptions}}
|
|
170
|
+
<div class="flex items-center justify-between">
|
|
171
|
+
<span class="text-xs text-gray-600 dark:text-gray-400">{{t "orchestrator.return-to-depot"}}</span>
|
|
172
|
+
<Toggle @isToggled={{this.draftPhase.returnToDepot}} @onToggle={{fn this.setDraftOption "returnToDepot"}} />
|
|
173
|
+
</div>
|
|
174
|
+
{{/if}}
|
|
131
175
|
<div class="flex items-center justify-between">
|
|
132
176
|
<span class="text-xs text-gray-600 dark:text-gray-400">{{t "orchestrator.auto-commit"}}</span>
|
|
133
177
|
<Toggle @isToggled={{this.draftPhase.autoCommit}} @onToggle={{fn this.setDraftOption "autoCommit"}} />
|
|
@@ -159,4 +203,4 @@
|
|
|
159
203
|
</div>
|
|
160
204
|
{{/if}}
|
|
161
205
|
|
|
162
|
-
</div>
|
|
206
|
+
</div>
|
|
@@ -50,6 +50,8 @@ export default class OrchestratorPhaseBuilderComponent extends Component {
|
|
|
50
50
|
mode,
|
|
51
51
|
label: this.intl.t(`orchestrator.mode-${mode.replace(/_/g, '-')}`),
|
|
52
52
|
engine: 'greedy',
|
|
53
|
+
allocationStrategy: 'route_aware',
|
|
54
|
+
vehiclePacking: 'minimize_vehicles',
|
|
53
55
|
orderStatuses: ['created'],
|
|
54
56
|
balanceWorkload: false,
|
|
55
57
|
respectSkills: true,
|
|
@@ -125,7 +127,12 @@ export default class OrchestratorPhaseBuilderComponent extends Component {
|
|
|
125
127
|
}
|
|
126
128
|
|
|
127
129
|
@action setDraftEngine(engine) {
|
|
128
|
-
this.draftPhase = {
|
|
130
|
+
this.draftPhase = {
|
|
131
|
+
...this.draftPhase,
|
|
132
|
+
engine,
|
|
133
|
+
allocationStrategy: engine === 'capacity' ? 'capacity_only' : (this.draftPhase.allocationStrategy ?? 'route_aware'),
|
|
134
|
+
vehiclePacking: this.draftPhase.vehiclePacking ?? 'minimize_vehicles',
|
|
135
|
+
};
|
|
129
136
|
}
|
|
130
137
|
|
|
131
138
|
@action toggleDraftOrderStatus(status) {
|
|
@@ -146,6 +153,32 @@ export default class OrchestratorPhaseBuilderComponent extends Component {
|
|
|
146
153
|
return this.editingIndex !== null && this.draftPhase !== null;
|
|
147
154
|
}
|
|
148
155
|
|
|
156
|
+
get allocationStrategyOptions() {
|
|
157
|
+
return [
|
|
158
|
+
{ value: 'route_aware', label: this.intl.t('orchestrator.allocation-strategy-route-aware') },
|
|
159
|
+
{ value: 'capacity_only', label: this.intl.t('orchestrator.allocation-strategy-capacity-only') },
|
|
160
|
+
];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
get vehiclePackingOptions() {
|
|
164
|
+
return [
|
|
165
|
+
{ value: 'minimize_vehicles', label: this.intl.t('orchestrator.vehicle-packing-minimize-vehicles') },
|
|
166
|
+
{ value: 'balanced', label: this.intl.t('orchestrator.vehicle-packing-balanced') },
|
|
167
|
+
];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
get shouldShowAllocationStrategy() {
|
|
171
|
+
return this.draftPhase?.mode === 'assign_vehicles' && this.draftPhase?.engine === 'vroom';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
get shouldShowVehiclePacking() {
|
|
175
|
+
return this.shouldShowAllocationStrategy && this.draftPhase?.allocationStrategy === 'capacity_only';
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
get shouldShowRouteOptions() {
|
|
179
|
+
return this.draftPhase?.engine !== 'capacity' && this.draftPhase?.allocationStrategy !== 'capacity_only';
|
|
180
|
+
}
|
|
181
|
+
|
|
149
182
|
modeLabel(mode) {
|
|
150
183
|
return this.modeOptions.find((m) => m.value === mode)?.label ?? mode;
|
|
151
184
|
}
|
|
@@ -187,6 +187,15 @@
|
|
|
187
187
|
<Badge @status={{vehicle.status}}>{{smart-humanize vehicle.status}}</Badge>
|
|
188
188
|
{{/if}}
|
|
189
189
|
</div>
|
|
190
|
+
{{! Position }}
|
|
191
|
+
<div
|
|
192
|
+
class="flex items-center gap-1 text-xs truncate
|
|
193
|
+
{{if (this.hasPosition vehicle) 'text-green-500 dark:text-green-300' 'text-amber-600 dark:text-amber-300'}}"
|
|
194
|
+
title={{if (this.hasPosition vehicle) (point-coordinates vehicle.location) (t "orchestrator.no-position")}}
|
|
195
|
+
>
|
|
196
|
+
<FaIcon @icon="location-dot" @size="xs" class="w-3 flex-shrink-0" />
|
|
197
|
+
<span class="truncate">{{#if (this.hasPosition vehicle)}}{{point-coordinates vehicle.location}}{{else}}{{t "orchestrator.no-position"}}{{/if}}</span>
|
|
198
|
+
</div>
|
|
190
199
|
{{! Assigned driver }}
|
|
191
200
|
<div class="flex items-center gap-1 text-xs text-gray-500 dark:text-gray-400">
|
|
192
201
|
<FaIcon @icon="id-card-alt" @size="xs" class="w-3 flex-shrink-0" />
|
|
@@ -245,6 +254,15 @@
|
|
|
245
254
|
{{! All fields — always shown, "-" when absent }}
|
|
246
255
|
<div class="flex-1 min-w-0 space-y-0.5">
|
|
247
256
|
<div class="text-xs font-semibold truncate dark:text-white">{{or driver.name "-"}}</div>
|
|
257
|
+
{{! Position }}
|
|
258
|
+
<div
|
|
259
|
+
class="flex items-center gap-1 text-xs truncate
|
|
260
|
+
{{if (this.hasPosition driver) 'text-green-500 dark:text-green-300' 'text-amber-600 dark:text-amber-300'}}"
|
|
261
|
+
title={{if (this.hasPosition driver) (point-coordinates driver.location) (t "orchestrator.no-position")}}
|
|
262
|
+
>
|
|
263
|
+
<FaIcon @icon="location-dot" @size="xs" class="w-3 flex-shrink-0" />
|
|
264
|
+
<span class="truncate">{{#if (this.hasPosition driver)}}{{point-coordinates driver.location}}{{else}}{{t "orchestrator.no-position"}}{{/if}}</span>
|
|
265
|
+
</div>
|
|
248
266
|
{{! Vehicle }}
|
|
249
267
|
<div class="flex items-center gap-1 text-xs text-gray-500 dark:text-gray-400">
|
|
250
268
|
<FaIcon @icon="truck" @size="xs" class="w-3 flex-shrink-0" />
|
|
@@ -298,4 +316,4 @@
|
|
|
298
316
|
</div>
|
|
299
317
|
{{/if}}
|
|
300
318
|
|
|
301
|
-
</div>
|
|
319
|
+
</div>
|
|
@@ -99,6 +99,10 @@ export default class OrchestratorResourcePanelComponent extends Component {
|
|
|
99
99
|
return [...(this.args.selectedDriverIds ?? new Set())];
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
@action hasPosition(resource) {
|
|
103
|
+
return resource?.hasInvalidCoordinates === false;
|
|
104
|
+
}
|
|
105
|
+
|
|
102
106
|
@action clearAllSelections() {
|
|
103
107
|
this.args.onClearVehicles?.();
|
|
104
108
|
this.args.onClearDrivers?.();
|
|
@@ -68,7 +68,7 @@ export default class OrchestratorWorkbenchComponent extends Component {
|
|
|
68
68
|
|
|
69
69
|
/**
|
|
70
70
|
* User-composed list of phases to execute in sequence.
|
|
71
|
-
* Each phase: { id, mode, label, engine, orderStatuses, balanceWorkload,
|
|
71
|
+
* Each phase: { id, mode, label, engine, allocationStrategy, orderStatuses, balanceWorkload,
|
|
72
72
|
* respectSkills, respectCapacity, returnToDepot, autoCommit }
|
|
73
73
|
*/
|
|
74
74
|
@tracked phases = [];
|
|
@@ -270,6 +270,8 @@ export default class OrchestratorWorkbenchComponent extends Component {
|
|
|
270
270
|
order_statuses: phase.orderStatuses ?? ['created'],
|
|
271
271
|
options: {
|
|
272
272
|
engine: phase.engine ?? 'greedy',
|
|
273
|
+
allocation_strategy: phase.allocationStrategy ?? 'route_aware',
|
|
274
|
+
vehicle_packing: phase.vehiclePacking ?? 'minimize_vehicles',
|
|
273
275
|
balance_workload: phase.balanceWorkload ?? false,
|
|
274
276
|
respect_skills: phase.respectSkills ?? true,
|
|
275
277
|
respect_capacity: phase.respectCapacity ?? true,
|
|
@@ -399,6 +401,7 @@ export default class OrchestratorWorkbenchComponent extends Component {
|
|
|
399
401
|
mode: 'allocate',
|
|
400
402
|
label: 'Allocate',
|
|
401
403
|
engine: 'greedy',
|
|
404
|
+
allocationStrategy: 'route_aware',
|
|
402
405
|
orderStatuses: ['created'],
|
|
403
406
|
balanceWorkload: false,
|
|
404
407
|
respectSkills: true,
|
|
@@ -1,38 +1,35 @@
|
|
|
1
1
|
{{#if @resource.purchase_rate}}
|
|
2
|
-
<ContentPanel @title={{t "order.fields.purchase-rate-panel-title"}} @isLoading={{@isLoading}} @open={{true}} @wrapperClass="bordered-top"
|
|
3
|
-
<div class="
|
|
4
|
-
<table
|
|
2
|
+
<ContentPanel @title={{t "order.fields.purchase-rate-panel-title"}} @isLoading={{@isLoading}} @open={{true}} @wrapperClass="bordered-top">
|
|
3
|
+
<div class="overflow-x-auto">
|
|
4
|
+
<table class="w-full text-xs">
|
|
5
5
|
<thead>
|
|
6
|
-
<tr>
|
|
7
|
-
<th class="text-left">{{t "order.fields.breakdown"}}</th>
|
|
8
|
-
<th>
|
|
9
|
-
<div class="flex flex-row items-center justify-end">{{@resource.purchase_rate.service_quote.currency}}</div>
|
|
10
|
-
</th>
|
|
6
|
+
<tr class="border-b border-gray-200 dark:border-gray-700 text-[11px] uppercase tracking-wide text-gray-500 font-semibold">
|
|
7
|
+
<th class="py-2 pr-2 text-left">{{t "order.fields.breakdown"}}</th>
|
|
8
|
+
<th class="py-2 pl-2 text-right w-32">{{@resource.purchase_rate.service_quote.currency}}</th>
|
|
11
9
|
</tr>
|
|
12
10
|
</thead>
|
|
13
11
|
<tbody>
|
|
14
12
|
{{#each @resource.purchase_rate.service_quote.items as |item|}}
|
|
15
|
-
<tr>
|
|
16
|
-
<td>
|
|
17
|
-
|
|
13
|
+
<tr class="border-b border-gray-100 dark:border-gray-800 last:border-0">
|
|
14
|
+
<td class="py-1 pr-2 text-gray-700 dark:text-gray-300">
|
|
15
|
+
{{smart-humanize item.details}}
|
|
18
16
|
</td>
|
|
19
|
-
<td>
|
|
20
|
-
|
|
17
|
+
<td class="py-1 pl-2 text-right font-medium text-gray-700 dark:text-gray-300 whitespace-nowrap">
|
|
18
|
+
{{format-currency item.amount @resource.purchase_rate.service_quote.currency}}
|
|
21
19
|
</td>
|
|
22
20
|
</tr>
|
|
23
21
|
{{/each}}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
</tbody>
|
|
23
|
+
<tfoot class="border-t border-gray-200 dark:border-gray-700">
|
|
24
|
+
<tr class="text-xs">
|
|
25
|
+
<td class="pt-2 pb-1 pr-2 text-right text-[11px] uppercase tracking-wide font-bold text-gray-800 dark:text-gray-100">
|
|
26
|
+
{{t "common.total"}}
|
|
27
27
|
</td>
|
|
28
|
-
<td class="
|
|
29
|
-
|
|
30
|
-
@resource.purchase_rate.service_quote.amount
|
|
31
|
-
@resource.purchase_rate.service_quote.currency
|
|
32
|
-
}}</div>
|
|
28
|
+
<td class="pt-2 pb-1 pl-2 text-right font-bold text-gray-800 dark:text-gray-100 whitespace-nowrap">
|
|
29
|
+
{{format-currency @resource.purchase_rate.service_quote.amount @resource.purchase_rate.service_quote.currency}}
|
|
33
30
|
</td>
|
|
34
31
|
</tr>
|
|
35
|
-
</
|
|
32
|
+
</tfoot>
|
|
36
33
|
</table>
|
|
37
34
|
</div>
|
|
38
35
|
</ContentPanel>
|
|
@@ -1,4 +1,29 @@
|
|
|
1
1
|
<ContentPanel @title={{t "order.fields.route-panel-title"}} @open={{true}} @actionButtons={{this.actionButtons}} @wrapperClass="bordered-top">
|
|
2
|
+
{{#if this.hasRouteSummaryLine}}
|
|
3
|
+
<div class="mb-3 flex items-center justify-between gap-3 text-xs">
|
|
4
|
+
<div class="font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">All Stops - {{this.routeStopsCount}}</div>
|
|
5
|
+
<div class="flex min-w-0 items-center gap-2 font-semibold text-gray-700 dark:text-gray-200">
|
|
6
|
+
<span>
|
|
7
|
+
{{#if this.hasTrackingDistance}}
|
|
8
|
+
{{format-meters this.trackerData.route.distance_m}}
|
|
9
|
+
{{else}}
|
|
10
|
+
-
|
|
11
|
+
{{/if}}
|
|
12
|
+
</span>
|
|
13
|
+
<span class="text-gray-300 dark:text-gray-600">-</span>
|
|
14
|
+
<span>
|
|
15
|
+
{{#if this.trackingDurationSeconds}}
|
|
16
|
+
{{format-duration this.trackingDurationSeconds}}
|
|
17
|
+
{{else if this.hasCompletionEta}}
|
|
18
|
+
{{format-date-fns this.trackerData.eta.completion_at "d MMM HH:mm"}}
|
|
19
|
+
{{else}}
|
|
20
|
+
-
|
|
21
|
+
{{/if}}
|
|
22
|
+
</span>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
{{/if}}
|
|
26
|
+
|
|
2
27
|
{{#if @resource.hasIntermediateWaypoints}}
|
|
3
28
|
<div>
|
|
4
29
|
<RouteList @order={{@resource}} @eta={{@resource.eta}} @waypointActions={{hash viewWaypointLabel=(hash label=(t "order.fields.get-label") fn=this.viewWaypointLabel)}} />
|
|
@@ -19,4 +44,4 @@
|
|
|
19
44
|
</div>
|
|
20
45
|
</div>
|
|
21
46
|
{{/if}}
|
|
22
|
-
</ContentPanel>
|
|
47
|
+
</ContentPanel>
|