@fleetbase/fleetops-engine 0.6.48 → 0.6.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (266) hide show
  1. package/addon/components/customer/create-order-form.hbs +2 -2
  2. package/addon/components/customer/form.hbs +2 -2
  3. package/addon/components/customer/order-form.hbs +2 -2
  4. package/addon/components/customer/orders.hbs +2 -2
  5. package/addon/components/display-place.hbs +2 -2
  6. package/addon/components/driver/details.hbs +5 -1
  7. package/addon/components/driver/form.hbs +11 -1
  8. package/addon/components/fleet-listing-panel.hbs +2 -2
  9. package/addon/components/fleet-listing-panel.js +10 -0
  10. package/addon/components/map/google-live-map.js +18 -75
  11. package/addon/components/map/leaflet-live-map.hbs +187 -37
  12. package/addon/components/map/leaflet-live-map.js +184 -20
  13. package/addon/components/modals/order-change-destination.hbs +46 -0
  14. package/addon/components/modals/order-change-destination.js +106 -0
  15. package/addon/components/modals/stop-activity.hbs +28 -0
  16. package/addon/components/modals/stop-activity.js +52 -0
  17. package/addon/components/modals/update-order-activity.hbs +45 -3
  18. package/addon/components/modals/update-order-activity.js +143 -5
  19. package/addon/components/orchestrator/phase-builder.hbs +21 -0
  20. package/addon/components/orchestrator/phase-builder.js +13 -0
  21. package/addon/components/orchestrator/resource-panel.hbs +0 -10
  22. package/addon/components/orchestrator/resource-panel.js +2 -4
  23. package/addon/components/orchestrator-workbench.js +1 -0
  24. package/addon/components/order/activity-list.hbs +2 -2
  25. package/addon/components/order/activity-timeline.hbs +31 -29
  26. package/addon/components/order/details/activity.hbs +3 -3
  27. package/addon/components/order/details/activity.js +11 -0
  28. package/addon/components/order/details/documents.js +2 -0
  29. package/addon/components/order/details/purchase-rate.hbs +2 -2
  30. package/addon/components/order/details/route.hbs +11 -3
  31. package/addon/components/order/details/route.js +91 -14
  32. package/addon/components/order/details/tracking.hbs +101 -72
  33. package/addon/components/order/details/tracking.js +149 -2
  34. package/addon/components/order/form/details.hbs +3 -3
  35. package/addon/components/order/form/documents.js +11 -4
  36. package/addon/components/order/form/route.hbs +108 -104
  37. package/addon/components/order/form/route.js +37 -1
  38. package/addon/components/order/form/service-rate.hbs +2 -2
  39. package/addon/components/order/form.hbs +2 -2
  40. package/addon/components/order-config-manager/activity-flow.hbs +4 -1
  41. package/addon/components/order-config-manager/activity-flow.js +178 -12
  42. package/addon/components/place/address.js +1 -1
  43. package/addon/components/route-list.hbs +17 -17
  44. package/addon/components/route-list.js +42 -10
  45. package/addon/components/service-rate/details.hbs +41 -0
  46. package/addon/components/service-rate/form.hbs +118 -0
  47. package/addon/components/service-rate/form.js +55 -0
  48. package/addon/components/vehicle/form.js +1 -1
  49. package/addon/components/vehicle/pill.hbs +1 -1
  50. package/addon/components/widget/fleet-ops-key-metrics.hbs +19 -17
  51. package/addon/components/widget/fleet-ops-key-metrics.js +26 -0
  52. package/addon/components/widget/fuel-efficiency.hbs +26 -0
  53. package/addon/components/widget/fuel-efficiency.js +43 -0
  54. package/addon/components/widget/geofence-violations.hbs +44 -0
  55. package/addon/components/widget/geofence-violations.js +55 -0
  56. package/addon/components/widget/issues-insights.hbs +43 -0
  57. package/addon/components/widget/issues-insights.js +62 -0
  58. package/addon/components/widget/kpi-active-orders.hbs +10 -0
  59. package/addon/components/widget/kpi-active-orders.js +5 -0
  60. package/addon/components/widget/kpi-aov.hbs +10 -0
  61. package/addon/components/widget/kpi-aov.js +5 -0
  62. package/addon/components/widget/kpi-distance.hbs +10 -0
  63. package/addon/components/widget/kpi-distance.js +5 -0
  64. package/addon/components/widget/kpi-drivers-online.hbs +10 -0
  65. package/addon/components/widget/kpi-drivers-online.js +5 -0
  66. package/addon/components/widget/kpi-earnings.hbs +10 -0
  67. package/addon/components/widget/kpi-earnings.js +5 -0
  68. package/addon/components/widget/kpi-open-issues.hbs +11 -0
  69. package/addon/components/widget/kpi-open-issues.js +5 -0
  70. package/addon/components/widget/kpi-tile.hbs +59 -0
  71. package/addon/components/widget/kpi-tile.js +168 -0
  72. package/addon/components/widget/live-fleet.hbs +122 -0
  73. package/addon/components/widget/live-fleet.js +365 -0
  74. package/addon/components/widget/maintenance-overview.hbs +45 -0
  75. package/addon/components/widget/maintenance-overview.js +40 -0
  76. package/addon/components/widget/on-time-delivery.hbs +27 -0
  77. package/addon/components/widget/on-time-delivery.js +73 -0
  78. package/addon/components/widget/operations-pulse.hbs +51 -0
  79. package/addon/components/widget/operations-pulse.js +173 -0
  80. package/addon/components/widget/orders-by-status.hbs +26 -0
  81. package/addon/components/widget/orders-by-status.js +54 -0
  82. package/addon/components/widget/revenue-trend.hbs +34 -0
  83. package/addon/components/widget/revenue-trend.js +67 -0
  84. package/addon/components/widget/top-drivers.hbs +44 -0
  85. package/addon/components/widget/top-drivers.js +54 -0
  86. package/addon/controllers/management/vehicles/index/new.js +1 -1
  87. package/addon/controllers/operations/orders/index/details.js +4 -4
  88. package/addon/controllers/operations/orders/index.js +14 -0
  89. package/addon/controllers/settings/routing.js +26 -0
  90. package/addon/extension.js +172 -3
  91. package/addon/helpers/place-address-html.js +2 -2
  92. package/addon/routes/operations/service-rates/index.js +1 -1
  93. package/addon/services/geofence.js +163 -19
  94. package/addon/services/leaflet-contextmenu-manager.js +27 -0
  95. package/addon/services/map-adapter/google.js +13 -2
  96. package/addon/services/vehicle-actions.js +1 -1
  97. package/addon/styles/fleetops-engine.css +205 -88
  98. package/addon/templates/maintenance/equipment/index.hbs +1 -1
  99. package/addon/templates/maintenance/parts/index.hbs +1 -1
  100. package/addon/templates/maintenance/schedules/index.hbs +1 -1
  101. package/addon/templates/management/vehicles/index.hbs +1 -1
  102. package/addon/templates/settings/avatars.hbs +1 -1
  103. package/addon/templates/settings/custom-fields.hbs +1 -1
  104. package/addon/templates/settings/map.hbs +1 -1
  105. package/addon/templates/settings/navigator-app.hbs +1 -1
  106. package/addon/templates/settings/notifications.hbs +1 -1
  107. package/addon/templates/settings/orchestrator.hbs +1 -1
  108. package/addon/templates/settings/payments/index.hbs +1 -1
  109. package/addon/templates/settings/payments/onboard.hbs +1 -1
  110. package/addon/templates/settings/routing.hbs +22 -2
  111. package/addon/templates/settings/scheduling.hbs +1 -1
  112. package/addon/utils/fleet-ops-options.js +2 -1
  113. package/addon/utils/live-map-card-content.js +171 -0
  114. package/addon/utils/normalize-order-config-flow.js +41 -0
  115. package/addon/utils/place-address-html.js +3 -2
  116. package/addon/utils/route-visualization.js +54 -7
  117. package/app/components/modals/order-change-destination.js +1 -0
  118. package/app/components/modals/stop-activity.js +1 -0
  119. package/app/components/widget/fuel-efficiency.js +1 -0
  120. package/app/components/widget/geofence-violations.js +1 -0
  121. package/app/components/widget/issues-insights.js +1 -0
  122. package/app/components/widget/kpi-active-orders.js +1 -0
  123. package/app/components/widget/kpi-aov.js +1 -0
  124. package/app/components/widget/kpi-distance.js +1 -0
  125. package/app/components/widget/kpi-drivers-online.js +1 -0
  126. package/app/components/widget/kpi-earnings.js +1 -0
  127. package/app/components/widget/kpi-open-issues.js +1 -0
  128. package/app/components/widget/kpi-tile.js +1 -0
  129. package/app/components/widget/live-fleet.js +1 -0
  130. package/app/components/widget/maintenance-overview.js +1 -0
  131. package/app/components/widget/on-time-delivery.js +1 -0
  132. package/app/components/widget/operations-pulse.js +1 -0
  133. package/app/components/widget/orders-by-status.js +1 -0
  134. package/app/components/widget/revenue-trend.js +1 -0
  135. package/app/components/widget/top-drivers.js +1 -0
  136. package/app/utils/live-map-card-content.js +1 -0
  137. package/composer.json +1 -1
  138. package/extension.json +1 -1
  139. package/package.json +3 -3
  140. package/pnpm-workspace.yaml +1 -0
  141. package/server/config/telematics.php +111 -1
  142. package/server/migrations/2026_05_18_000001_add_license_expiry_to_drivers_table.php +25 -0
  143. package/server/migrations/2026_05_21_000001_add_geographic_rules_to_service_rate_fees_table.php +37 -0
  144. package/server/migrations/2026_05_25_000001_add_endpoint_tracking_refs_to_payloads_table.php +50 -0
  145. package/server/migrations/2026_05_27_000001_default_driver_vehicle_statuses_to_available.php +48 -0
  146. package/server/resources/views/labels/default.php +57 -49
  147. package/server/resources/views/labels/waypoint-label.php +27 -20
  148. package/server/seeders/Testing/Concerns/SeedsTestingData.php +183 -0
  149. package/server/seeders/Testing/ConnectivitySeeder.php +213 -0
  150. package/server/seeders/Testing/FleetSeeder.php +341 -0
  151. package/server/seeders/Testing/MaintenanceSeeder.php +238 -0
  152. package/server/seeders/Testing/NetworkSeeder.php +250 -0
  153. package/server/seeders/Testing/OrdersSeeder.php +198 -0
  154. package/server/seeders/Testing/TestingSeeder.php +40 -0
  155. package/server/seeders/{VroomOrchestrationSeeder.php → Testing/VroomOrchestrationSeeder.php} +29 -22
  156. package/server/src/Console/Commands/AuditCustomerUserConflicts.php +75 -0
  157. package/server/src/Console/Commands/ProcessOperationalAlerts.php +279 -0
  158. package/server/src/Exceptions/CustomerUserConflictException.php +13 -0
  159. package/server/src/Http/Controllers/Api/v1/ContactController.php +17 -2
  160. package/server/src/Http/Controllers/Api/v1/CustomerController.php +876 -0
  161. package/server/src/Http/Controllers/Api/v1/DriverController.php +1 -1
  162. package/server/src/Http/Controllers/Api/v1/OrderConfigController.php +54 -0
  163. package/server/src/Http/Controllers/Api/v1/OrderController.php +98 -70
  164. package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +2 -2
  165. package/server/src/Http/Controllers/Api/v1/VehicleController.php +20 -3
  166. package/server/src/Http/Controllers/Internal/v1/AnalyticsController.php +119 -0
  167. package/server/src/Http/Controllers/Internal/v1/ContactController.php +19 -0
  168. package/server/src/Http/Controllers/Internal/v1/LiveController.php +101 -66
  169. package/server/src/Http/Controllers/Internal/v1/MetricsController.php +82 -3
  170. package/server/src/Http/Controllers/Internal/v1/OrderController.php +441 -32
  171. package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +1 -1
  172. package/server/src/Http/Controllers/Internal/v1/SettingController.php +46 -1
  173. package/server/src/Http/Controllers/Internal/v1/VehicleController.php +54 -1
  174. package/server/src/Http/Middleware/AuthenticateCustomerToken.php +34 -0
  175. package/server/src/Http/Requests/CreateCustomerOrderRequest.php +63 -0
  176. package/server/src/Http/Requests/CreateCustomerRequest.php +48 -0
  177. package/server/src/Http/Requests/CreateDriverRequest.php +1 -1
  178. package/server/src/Http/Requests/CreateVehicleRequest.php +25 -1
  179. package/server/src/Http/Requests/Internal/CreateDriverRequest.php +1 -1
  180. package/server/src/Http/Requests/VerifyCreateCustomerRequest.php +29 -0
  181. package/server/src/Http/Resources/Internal/v1/OrderConfig.php +41 -0
  182. package/server/src/Http/Resources/v1/Customer.php +79 -0
  183. package/server/src/Http/Resources/v1/Driver.php +2 -0
  184. package/server/src/Http/Resources/v1/Fleet.php +4 -0
  185. package/server/src/Http/Resources/v1/Index/Driver.php +48 -0
  186. package/server/src/Http/Resources/v1/Index/Vehicle.php +74 -1
  187. package/server/src/Http/Resources/v1/OrderConfig.php +74 -0
  188. package/server/src/Http/Resources/v1/Payload.php +23 -20
  189. package/server/src/Http/Resources/v1/ServiceArea.php +33 -19
  190. package/server/src/Http/Resources/v1/ServiceRateFee.php +7 -0
  191. package/server/src/Listeners/HandleOrderDispatched.php +2 -2
  192. package/server/src/Models/Contact.php +92 -15
  193. package/server/src/Models/Customer.php +64 -0
  194. package/server/src/Models/Driver.php +14 -3
  195. package/server/src/Models/Entity.php +2 -2
  196. package/server/src/Models/Order.php +29 -4
  197. package/server/src/Models/OrderConfig.php +14 -11
  198. package/server/src/Models/Payload.php +17 -1
  199. package/server/src/Models/ServiceRate.php +273 -1
  200. package/server/src/Models/ServiceRateFee.php +39 -9
  201. package/server/src/Models/TrackingNumber.php +8 -2
  202. package/server/src/Models/Vehicle.php +18 -2
  203. package/server/src/Models/Waypoint.php +2 -2
  204. package/server/src/Notifications/LateDeparture.php +48 -0
  205. package/server/src/Notifications/ProlongedStoppage.php +48 -0
  206. package/server/src/Notifications/RouteDeviation.php +48 -0
  207. package/server/src/Observers/ContactObserver.php +2 -0
  208. package/server/src/Observers/ServiceRateObserver.php +6 -6
  209. package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +29 -2
  210. package/server/src/Providers/FleetOpsServiceProvider.php +6 -0
  211. package/server/src/Providers/NotificationServiceProvider.php +3 -0
  212. package/server/src/Support/Analytics/AbstractAnalytics.php +77 -0
  213. package/server/src/Support/Analytics/FuelEfficiency.php +83 -0
  214. package/server/src/Support/Analytics/GeofenceViolations.php +63 -0
  215. package/server/src/Support/Analytics/IssuesInsights.php +67 -0
  216. package/server/src/Support/Analytics/LiveFleet.php +127 -0
  217. package/server/src/Support/Analytics/MaintenanceOverview.php +70 -0
  218. package/server/src/Support/Analytics/OnTimeDelivery.php +75 -0
  219. package/server/src/Support/Analytics/OperationsPulse.php +86 -0
  220. package/server/src/Support/Analytics/OrdersByStatus.php +64 -0
  221. package/server/src/Support/Analytics/RevenueTrend.php +78 -0
  222. package/server/src/Support/Analytics/TopDrivers.php +92 -0
  223. package/server/src/Support/CustomerAuth.php +98 -0
  224. package/server/src/Support/LabelPdf.php +52 -0
  225. package/server/src/Support/Metrics/AbstractMetric.php +170 -0
  226. package/server/src/Support/Metrics/ActiveLiveOrdersMetric.php +28 -0
  227. package/server/src/Support/Metrics/AvgOrderValueMetric.php +53 -0
  228. package/server/src/Support/Metrics/DriversOnlineMetric.php +30 -0
  229. package/server/src/Support/Metrics/EarningsMetric.php +30 -0
  230. package/server/src/Support/Metrics/FuelCostsMetric.php +30 -0
  231. package/server/src/Support/Metrics/MoneyMetric.php +30 -0
  232. package/server/src/Support/Metrics/OpenIssuesMetric.php +35 -0
  233. package/server/src/Support/Metrics/OrdersCanceledMetric.php +35 -0
  234. package/server/src/Support/Metrics/OrdersCompletedMetric.php +35 -0
  235. package/server/src/Support/Metrics/OrdersInProgressMetric.php +51 -0
  236. package/server/src/Support/Metrics/OrdersScheduledMetric.php +37 -0
  237. package/server/src/Support/Metrics/Registry.php +46 -0
  238. package/server/src/Support/Metrics/ResolvedIssuesMetric.php +35 -0
  239. package/server/src/Support/Metrics/TotalCustomersMetric.php +29 -0
  240. package/server/src/Support/Metrics/TotalDistanceTraveledMetric.php +35 -0
  241. package/server/src/Support/Metrics/TotalDriversMetric.php +28 -0
  242. package/server/src/Support/Metrics/TotalTimeTraveledMetric.php +35 -0
  243. package/server/src/Support/Metrics.php +46 -249
  244. package/server/src/Support/ResolvesOrderServiceStops.php +512 -0
  245. package/server/src/Support/Telematics/Providers/AfaqyProvider.php +290 -0
  246. package/server/src/Support/Telematics/Providers/SafeeProvider.php +333 -0
  247. package/server/src/Tracking/TrackingContextBuilder.php +41 -38
  248. package/server/src/Tracking/TrackingIntelligenceService.php +109 -9
  249. package/server/src/Tracking/TrackingStop.php +13 -11
  250. package/server/src/routes.php +50 -0
  251. package/server/tests/AnalyticsRoutesTest.php +41 -0
  252. package/server/tests/CapacityAllocationEngineTest.php +10 -10
  253. package/server/tests/CustomerContactIdentitySafetyTest.php +54 -0
  254. package/server/tests/CustomerEndpointTest.php +199 -0
  255. package/server/tests/DriverVehicleStatusDefaultTest.php +39 -0
  256. package/server/tests/LabelPdfRenderingTest.php +23 -0
  257. package/server/tests/LiveControllerViewportTest.php +60 -0
  258. package/server/tests/MetricsRegistryTest.php +84 -0
  259. package/server/tests/OrchestrationPayloadBuilderTest.php +2 -2
  260. package/server/tests/OrderConfigEndpointTest.php +86 -0
  261. package/server/tests/OrderFileAttachmentTest.php +20 -0
  262. package/server/tests/ServiceAreaZoneApiShapeTest.php +34 -0
  263. package/server/tests/VehicleDriverAssignmentTest.php +18 -0
  264. package/server/tests/VroomOrchestrationEngineTest.php +91 -0
  265. package/translations/en-us.yaml +38 -0
  266. package/server/migrations/2026_05_13_000001_repair_customer_contact_user_types.php +0 -86
@@ -45,93 +45,122 @@
45
45
  </div>
46
46
  {{/if}}
47
47
 
48
- <div class="tracking-intelligence__eta-grid">
49
- <div class="tracking-intelligence-cell tracking-intelligence-cell--accent">
50
- <div class="tracking-intelligence-cell__label">
51
- Smart adjusted ETA
52
- <span class="tracking-intelligence-tag">Live</span>
48
+ {{#if this.lifecycleMessage}}
49
+ <div class="tracking-intelligence-alert">
50
+ <div class="tracking-intelligence-alert__icon"><FaIcon @icon={{this.lifecycleMessageIcon}} @size="xs" /></div>
51
+ <div class="min-w-0">
52
+ <div class="tracking-intelligence-alert__title">{{this.lifecycleMessageTitle}}</div>
53
+ <div class="tracking-intelligence-alert__body">{{this.lifecycleMessage}}</div>
53
54
  </div>
54
- <div class="tracking-intelligence-cell__value">
55
- {{#if this.hasSmartAdjustedEta}}
56
- {{format-duration this.smartAdjustedEtaSeconds}}
57
- {{else}}
58
- {{this.smartAdjustedEtaUnavailableLabel}}
59
- {{/if}}
55
+ </div>
56
+ {{/if}}
57
+
58
+ {{#if this.showStartEta}}
59
+ <div class="tracking-intelligence__eta-grid">
60
+ <div class="tracking-intelligence-cell tracking-intelligence-cell--accent">
61
+ <div class="tracking-intelligence-cell__label">
62
+ Estimated start
63
+ <span class="tracking-intelligence-tag">Pre-start</span>
64
+ </div>
65
+ <div class="tracking-intelligence-cell__value">
66
+ {{#if this.hasStartEta}}
67
+ {{format-duration this.startEtaSeconds}}
68
+ {{else}}
69
+ Pending start
70
+ {{/if}}
71
+ </div>
72
+ <div class="tracking-intelligence-cell__sub">Based on the assigned driver route to the first stop</div>
60
73
  </div>
61
- <div class="tracking-intelligence-cell__sub">Based on provider route and driver signal</div>
62
74
  </div>
63
- <div class="tracking-intelligence-cell">
64
- <div class="tracking-intelligence-cell__label">Reported ETA</div>
65
- <div class="tracking-intelligence-cell__value {{if this.isReportedEtaUntrusted 'tracking-intelligence-cell__value--muted'}}">
66
- {{#if this.hasDisplayedReportedEta}}
67
- {{format-duration this.displayedReportedEtaSeconds}}
68
- {{else}}
69
- {{this.etaUnavailableLabel}}
70
- {{/if}}
75
+ {{else if this.showLiveEta}}
76
+ <div class="tracking-intelligence__eta-grid">
77
+ <div class="tracking-intelligence-cell tracking-intelligence-cell--accent">
78
+ <div class="tracking-intelligence-cell__label">
79
+ Smart adjusted ETA
80
+ <span class="tracking-intelligence-tag">Live</span>
81
+ </div>
82
+ <div class="tracking-intelligence-cell__value">
83
+ {{#if this.hasSmartAdjustedEta}}
84
+ {{format-duration this.smartAdjustedEtaSeconds}}
85
+ {{else}}
86
+ {{this.smartAdjustedEtaUnavailableLabel}}
87
+ {{/if}}
88
+ </div>
89
+ <div class="tracking-intelligence-cell__sub">Based on provider route and driver signal</div>
90
+ </div>
91
+ <div class="tracking-intelligence-cell">
92
+ <div class="tracking-intelligence-cell__label">Reported ETA</div>
93
+ <div class="tracking-intelligence-cell__value {{if this.isReportedEtaUntrusted 'tracking-intelligence-cell__value--muted'}}">
94
+ {{#if this.hasDisplayedReportedEta}}
95
+ {{format-duration this.displayedReportedEtaSeconds}}
96
+ {{else}}
97
+ {{this.etaUnavailableLabel}}
98
+ {{/if}}
99
+ </div>
100
+ <div class="tracking-intelligence-cell__sub {{if this.isReportedEtaUntrusted 'tracking-intelligence-cell__sub--warn'}}">{{this.reportedEtaWarning}}</div>
71
101
  </div>
72
- <div class="tracking-intelligence-cell__sub {{if this.isReportedEtaUntrusted 'tracking-intelligence-cell__sub--warn'}}">{{this.reportedEtaWarning}}</div>
73
102
  </div>
74
- </div>
75
103
 
76
- <div class="tracking-intelligence-confidence">
77
- <div class="tracking-intelligence-confidence__label">ETA confidence</div>
78
- <div class="tracking-intelligence-confidence__segments {{this.confidenceToneClass}}">
79
- {{#each this.confidenceSegments as |segment|}}
80
- <span class="{{if segment.lit 'is-lit'}}"></span>
81
- {{/each}}
104
+ <div class="tracking-intelligence-confidence">
105
+ <div class="tracking-intelligence-confidence__label">ETA confidence</div>
106
+ <div class="tracking-intelligence-confidence__segments {{this.confidenceToneClass}}">
107
+ {{#each this.confidenceSegments as |segment|}}
108
+ <span class="{{if segment.lit 'is-lit'}}"></span>
109
+ {{/each}}
110
+ </div>
111
+ <div class="tracking-intelligence-confidence__value">{{this.confidencePercent}}%</div>
112
+ </div>
113
+
114
+ <div class="tracking-intelligence-destination">
115
+ <div class="tracking-intelligence-destination__marker">{{this.activeStopMarkerLabel}}</div>
116
+ <div class="min-w-0 flex-1">
117
+ <div class="tracking-intelligence-destination__label">{{this.activeStopLabel}}</div>
118
+ <div class="tracking-intelligence-destination__address">{{n-a @resource.tracker_data.active_stop.address}}</div>
119
+ <div class="tracking-intelligence-destination__eta">
120
+ ETA:
121
+ <strong>
122
+ {{#if this.hasActiveEta}}
123
+ {{format-duration this.activeEtaSeconds}}
124
+ {{else}}
125
+ {{this.etaUnavailableLabel}}
126
+ {{/if}}
127
+ </strong>
128
+ </div>
129
+ </div>
82
130
  </div>
83
- <div class="tracking-intelligence-confidence__value">{{this.confidencePercent}}%</div>
84
- </div>
85
131
 
86
- <div class="tracking-intelligence-destination">
87
- <div class="tracking-intelligence-destination__marker">{{this.activeStopMarkerLabel}}</div>
88
- <div class="min-w-0 flex-1">
89
- <div class="tracking-intelligence-destination__label">{{this.activeStopLabel}}</div>
90
- <div class="tracking-intelligence-destination__address">{{n-a @resource.tracker_data.active_stop.address}}</div>
91
- <div class="tracking-intelligence-destination__eta">
92
- ETA:
132
+ <div class="tracking-intelligence-distance">
133
+ <div class="tracking-intelligence-distance__row">
134
+ <span>Reported total</span>
93
135
  <strong>
94
- {{#if this.hasActiveEta}}
95
- {{format-duration this.activeEtaSeconds}}
136
+ {{#if this.hasRemainingDistance}}
137
+ {{format-meters @resource.tracker_data.route.distance_m}}
96
138
  {{else}}
97
- {{this.etaUnavailableLabel}}
139
+ -
98
140
  {{/if}}
99
141
  </strong>
142
+ <div class="tracking-intelligence-distance__bar">
143
+ <span style={{this.totalProgressStyle}}></span>
144
+ </div>
100
145
  </div>
101
- </div>
102
- </div>
103
-
104
- <div class="tracking-intelligence-distance">
105
- <div class="tracking-intelligence-distance__row">
106
- <span>Reported total</span>
107
- <strong>
108
- {{#if this.hasRemainingDistance}}
109
- {{format-meters @resource.tracker_data.route.distance_m}}
110
- {{else}}
111
- -
112
- {{/if}}
113
- </strong>
114
- <div class="tracking-intelligence-distance__bar">
115
- <span style={{this.totalProgressStyle}}></span>
116
- </div>
117
- </div>
118
- <div class="tracking-intelligence-distance__row">
119
- <span>Between stops</span>
120
- <strong>
121
- {{#if this.hasCurrentLegDistance}}
122
- {{format-meters this.currentLeg.distance_m}}
123
- {{else}}
124
- -
125
- {{/if}}
126
- </strong>
127
- <div class="tracking-intelligence-distance__bar tracking-intelligence-distance__bar--secondary">
128
- <span style={{this.currentLegProgressStyle}}></span>
146
+ <div class="tracking-intelligence-distance__row">
147
+ <span>Between stops</span>
148
+ <strong>
149
+ {{#if this.hasCurrentLegDistance}}
150
+ {{format-meters this.currentLeg.distance_m}}
151
+ {{else}}
152
+ -
153
+ {{/if}}
154
+ </strong>
155
+ <div class="tracking-intelligence-distance__bar tracking-intelligence-distance__bar--secondary">
156
+ <span style={{this.currentLegProgressStyle}}></span>
157
+ </div>
129
158
  </div>
159
+ <div class="tracking-intelligence-distance__foot">Provider context:
160
+ {{this.providerLabel}}
161
+ route</div>
130
162
  </div>
131
- <div class="tracking-intelligence-distance__foot">Provider context:
132
- {{this.providerLabel}}
133
- route</div>
134
- </div>
163
+ {{/if}}
135
164
 
136
165
  <details class="tracking-intelligence-diagnostics">
137
166
  <summary>
@@ -34,15 +34,150 @@ export default class OrderDetailsTrackingComponent extends Component {
34
34
  return Boolean(this.trackerData);
35
35
  }
36
36
 
37
+ get lifecycle() {
38
+ return this.trackerData?.lifecycle ?? {};
39
+ }
40
+
41
+ get lifecycleMode() {
42
+ if (this.lifecycle.mode) {
43
+ return this.lifecycle.mode;
44
+ }
45
+
46
+ const status = this.statusCode;
47
+
48
+ if (['completed', 'canceled'].includes(status)) {
49
+ return status;
50
+ }
51
+
52
+ if (!this.hasAssignedDriver) {
53
+ return 'unassigned';
54
+ }
55
+
56
+ if (!this.hasOrderStarted && status === 'dispatched') {
57
+ return 'dispatched';
58
+ }
59
+
60
+ if (!this.hasOrderStarted) {
61
+ return 'pre_start';
62
+ }
63
+
64
+ return 'active';
65
+ }
66
+
67
+ get statusCode() {
68
+ return String(this.lifecycle.status ?? this.args.resource?.status ?? 'created').toLowerCase();
69
+ }
70
+
71
+ get hasOrderStarted() {
72
+ return Boolean(this.lifecycle.has_started ?? this.args.resource?.started ?? this.args.resource?.started_at ?? this.statusCode === 'started');
73
+ }
74
+
75
+ get showLiveEta() {
76
+ if (this.lifecycle.show_live_eta !== undefined) {
77
+ return Boolean(this.lifecycle.show_live_eta);
78
+ }
79
+
80
+ return this.hasOrderStarted && !this.isTerminalLifecycle;
81
+ }
82
+
83
+ get showStartEta() {
84
+ if (this.lifecycle.show_start_eta !== undefined) {
85
+ return Boolean(this.lifecycle.show_start_eta);
86
+ }
87
+
88
+ return this.lifecycleMode === 'dispatched' && this.hasStartEta;
89
+ }
90
+
91
+ get isTerminalLifecycle() {
92
+ return Boolean(this.lifecycle.is_terminal ?? ['completed', 'canceled'].includes(this.statusCode));
93
+ }
94
+
95
+ get isPreStartLifecycle() {
96
+ return ['created', 'pre_start'].includes(this.lifecycleMode);
97
+ }
98
+
99
+ get isDispatchedLifecycle() {
100
+ return this.lifecycleMode === 'dispatched';
101
+ }
102
+
103
+ get lifecycleMessage() {
104
+ if (this.lifecycleMode === 'unassigned') {
105
+ return null;
106
+ }
107
+
108
+ return this.lifecycle.message ?? this.defaultLifecycleMessage;
109
+ }
110
+
111
+ get defaultLifecycleMessage() {
112
+ if (this.statusCode === 'completed') {
113
+ return 'Order has been completed.';
114
+ }
115
+
116
+ if (this.statusCode === 'canceled') {
117
+ return 'Order has been canceled.';
118
+ }
119
+
120
+ if (this.isDispatchedLifecycle) {
121
+ return 'Order has been dispatched. Estimated start is based on the assigned driver route to the first stop.';
122
+ }
123
+
124
+ if (this.isPreStartLifecycle) {
125
+ return 'Live ETA will begin once the order is started.';
126
+ }
127
+
128
+ return null;
129
+ }
130
+
131
+ get lifecycleMessageTitle() {
132
+ switch (this.lifecycleMode) {
133
+ case 'completed':
134
+ return 'Order completed';
135
+ case 'canceled':
136
+ return 'Order canceled';
137
+ case 'dispatched':
138
+ return 'Order dispatched';
139
+ case 'pre_start':
140
+ case 'created':
141
+ return 'Tracking pending start';
142
+ default:
143
+ return null;
144
+ }
145
+ }
146
+
147
+ get lifecycleMessageIcon() {
148
+ switch (this.lifecycleMode) {
149
+ case 'completed':
150
+ return 'circle-check';
151
+ case 'canceled':
152
+ return 'ban';
153
+ case 'dispatched':
154
+ return 'route';
155
+ default:
156
+ return 'clock';
157
+ }
158
+ }
159
+
160
+ get startEtaSeconds() {
161
+ return this.trackerData?.eta?.start_seconds;
162
+ }
163
+
164
+ get hasStartEta() {
165
+ return this.startEtaSeconds !== null && this.startEtaSeconds !== undefined;
166
+ }
167
+
37
168
  get activeEtaSeconds() {
38
169
  return this.trackerData?.eta?.active_stop_seconds;
39
170
  }
40
171
 
41
172
  get hasActiveEta() {
42
- return this.activeEtaSeconds !== null && this.activeEtaSeconds !== undefined;
173
+ return this.showLiveEta && this.activeEtaSeconds !== null && this.activeEtaSeconds !== undefined;
43
174
  }
44
175
 
45
176
  get smartAdjustedEtaSeconds() {
177
+ if (!this.showLiveEta) {
178
+ return null;
179
+ }
180
+
46
181
  return (
47
182
  this.firstPositiveNumber(this.activeEtaSeconds) ??
48
183
  this.firstPositiveNumber(this.trackerData?.route?.duration_in_traffic_s) ??
@@ -69,7 +204,7 @@ export default class OrderDetailsTrackingComponent extends Component {
69
204
  }
70
205
 
71
206
  get hasCompletionEta() {
72
- return Boolean(this.trackerData?.eta?.completion_at);
207
+ return this.showLiveEta && Boolean(this.trackerData?.eta?.completion_at);
73
208
  }
74
209
 
75
210
  get hasRemainingDistance() {
@@ -249,6 +384,10 @@ export default class OrderDetailsTrackingComponent extends Component {
249
384
  }
250
385
 
251
386
  get displayedReportedEtaSeconds() {
387
+ if (!this.showLiveEta) {
388
+ return null;
389
+ }
390
+
252
391
  return (
253
392
  this.firstPositiveNumber(this.reportedEtaSeconds) ??
254
393
  this.firstPositiveNumber(this.activeEtaSeconds) ??
@@ -269,6 +408,10 @@ export default class OrderDetailsTrackingComponent extends Component {
269
408
  return false;
270
409
  }
271
410
 
411
+ if (!this.showLiveEta) {
412
+ return false;
413
+ }
414
+
272
415
  return !trackerData?.driver?.location || trackerData?.insights?.is_location_stale || trackerData.fallback_provider || (trackerData.confidence && trackerData.confidence !== 'high');
273
416
  }
274
417
 
@@ -402,6 +545,10 @@ export default class OrderDetailsTrackingComponent extends Component {
402
545
  return null;
403
546
  }
404
547
 
548
+ if (this.isTerminalLifecycle || this.isPreStartLifecycle || this.isDispatchedLifecycle) {
549
+ return null;
550
+ }
551
+
405
552
  if (!this.hasAssignedDriver) {
406
553
  return 'Assign a driver to start live tracking and improve ETA accuracy.';
407
554
  }
@@ -1,6 +1,6 @@
1
1
  <ContentPanel @title="Details" @open={{true}} @wrapperClass="bordered-top">
2
2
  <div class="grid grid-cols-1 lg:grid-cols-2 gap-2">
3
- <InputGroup @name={{t "order.fields.order-type"}} @helpText={{t "order.fields.order-type-help-text"}}>
3
+ <InputGroup @name={{t "order.fields.order-type"}} @helpText={{t "order.fields.order-type-help-text"}} @required={{true}}>
4
4
  <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
5
5
  <PowerSelect
6
6
  @options={{this.orderConfigActions.allOrderConfigs}}
@@ -160,7 +160,7 @@
160
160
  </InputGroup>
161
161
 
162
162
  {{#if (and @resource.pod_required (not @resource.facilitator.isIntegratedVendor))}}
163
- <InputGroup @name={{t "order.fields.proof-delivery"}} @helpText={{t "order.fields.proof-delivery-help-text"}}>
163
+ <InputGroup @name={{t "order.fields.proof-delivery"}} @helpText={{t "order.fields.proof-delivery-help-text"}} @required={{true}}>
164
164
  <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
165
165
  <PowerSelect
166
166
  @options={{get-fleet-ops-options "podOptions"}}
@@ -279,4 +279,4 @@
279
279
  </div>
280
280
  </ContentPanel>
281
281
 
282
- <Order::Form::CustomFields @resource={{@resource}} @customFields={{this.customFields}} />
282
+ <Order::Form::CustomFields @resource={{@resource}} @customFields={{this.customFields}} />
@@ -35,12 +35,19 @@ export default class OrderFormDocumentsComponent extends Component {
35
35
 
36
36
  try {
37
37
  this.uploadQueue.pushObject(file);
38
+ const uploadOptions = {
39
+ path: 'uploads/fleet-ops/order-files',
40
+ type: 'order_file',
41
+ };
42
+
43
+ if (!this.args.resource.isNew) {
44
+ uploadOptions.subject_uuid = this.args.resource.id;
45
+ uploadOptions.subject_type = 'fleet-ops:order';
46
+ }
47
+
38
48
  yield this.fetch.uploadFile.perform(
39
49
  file,
40
- {
41
- path: 'uploads/fleet-ops/order-files',
42
- type: 'order_file',
43
- },
50
+ uploadOptions,
44
51
  (uploadedFile) => {
45
52
  this.args.resource.files.pushObject(uploadedFile);
46
53
  this.uploadQueue.removeObject(file);