@fleetbase/fleetops-engine 0.6.70 → 0.6.72

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 (510) hide show
  1. package/RELEASE.md +18 -47
  2. package/addon/components/admin/tracking-page-settings.hbs +56 -0
  3. package/addon/components/admin/tracking-page-settings.js +67 -0
  4. package/addon/components/device/form.hbs +2 -2
  5. package/addon/components/driver/details.hbs +1 -1
  6. package/addon/components/driver/form.hbs +1 -1
  7. package/addon/components/equipment/form.hbs +1 -1
  8. package/addon/components/layout/fleet-ops-sidebar.js +11 -3
  9. package/addon/components/maintenance/form.hbs +3 -3
  10. package/addon/components/map/google-live-map.js +2 -2
  11. package/addon/components/map/leaflet-live-map.js +45 -32
  12. package/addon/components/orchestrator/phase-builder.js +2 -1
  13. package/addon/components/orchestrator-workbench.hbs +7 -1
  14. package/addon/components/orchestrator-workbench.js +16 -2
  15. package/addon/components/order/details/activity.hbs +8 -6
  16. package/addon/components/order/details/activity.js +9 -0
  17. package/addon/components/order/details.hbs +29 -0
  18. package/addon/components/order/details.js +26 -0
  19. package/addon/components/order/form/details.hbs +30 -4
  20. package/addon/components/order/form/details.js +46 -0
  21. package/addon/components/order/form/route.hbs +5 -3
  22. package/addon/components/order/form/route.js +24 -1
  23. package/addon/components/order/form.hbs +12 -0
  24. package/addon/components/order/form.js +21 -1
  25. package/addon/components/order/kanban.js +72 -0
  26. package/addon/components/order-config-manager/activity-flow.js +45 -10
  27. package/addon/components/order-config-manager/lifecycle.hbs +250 -0
  28. package/addon/components/order-config-manager/lifecycle.js +358 -0
  29. package/addon/components/order-config-manager.js +5 -1
  30. package/addon/components/service-rate/details.hbs +1 -1
  31. package/addon/components/service-rate/form.hbs +1 -1
  32. package/addon/components/tracking-page/full-view.hbs +276 -0
  33. package/addon/components/tracking-page/full-view.js +474 -0
  34. package/addon/components/tracking-page/locale-selector.js +30 -0
  35. package/addon/components/tracking-page/map.hbs +44 -0
  36. package/addon/components/tracking-page/map.js +328 -0
  37. package/addon/components/tracking-page/order-list.hbs +35 -0
  38. package/addon/components/tracking-page/public-view.hbs +60 -0
  39. package/addon/components/tracking-page/settings-preview.hbs +21 -0
  40. package/addon/components/tracking-page/settings-preview.js +23 -0
  41. package/addon/components/tracking-page/verify-panel.hbs +109 -0
  42. package/addon/components/tracking-page.hbs +142 -0
  43. package/addon/components/tracking-page.js +564 -0
  44. package/addon/components/trailer/form.hbs +2 -2
  45. package/addon/components/vehicle/form.hbs +3 -3
  46. package/addon/components/warranty/form.hbs +2 -2
  47. package/addon/components/widget/live-fleet.js +54 -6
  48. package/addon/components/widget/proof-of-delivery.hbs +49 -0
  49. package/addon/components/widget/proof-of-delivery.js +62 -0
  50. package/addon/components/work-order/form.hbs +3 -3
  51. package/addon/controllers/analytics/reports/index.js +10 -1
  52. package/addon/controllers/connectivity/devices/index/details.js +1 -0
  53. package/addon/controllers/connectivity/devices/index.js +8 -2
  54. package/addon/controllers/connectivity/events/index.js +31 -1
  55. package/addon/controllers/connectivity/sensors/index/details.js +1 -1
  56. package/addon/controllers/connectivity/sensors/index.js +37 -1
  57. package/addon/controllers/maintenance/equipment/index/details.js +2 -2
  58. package/addon/controllers/maintenance/equipment/index.js +28 -9
  59. package/addon/controllers/maintenance/inspection-forms/index.js +32 -12
  60. package/addon/controllers/maintenance/inspection-submissions/index/details.js +2 -1
  61. package/addon/controllers/maintenance/inspection-submissions/index.js +49 -9
  62. package/addon/controllers/maintenance/maintenances/index/details.js +2 -0
  63. package/addon/controllers/maintenance/maintenances/index.js +20 -1
  64. package/addon/controllers/maintenance/parts/index/details.js +2 -2
  65. package/addon/controllers/maintenance/parts/index.js +28 -8
  66. package/addon/controllers/maintenance/schedules/index.js +33 -4
  67. package/addon/controllers/maintenance/work-orders/index/details.js +3 -3
  68. package/addon/controllers/maintenance/work-orders/index.js +39 -8
  69. package/addon/controllers/management/contacts/customers.js +13 -0
  70. package/addon/controllers/management/contacts/index.js +43 -1
  71. package/addon/controllers/management/drivers/index/details.js +1 -0
  72. package/addon/controllers/management/drivers/index.js +32 -2
  73. package/addon/controllers/management/fleets/index/details.js +1 -0
  74. package/addon/controllers/management/fleets/index.js +39 -1
  75. package/addon/controllers/management/fuel-reports/index.js +22 -2
  76. package/addon/controllers/management/fuel-transactions/index.js +43 -7
  77. package/addon/controllers/management/issues/index/details.js +1 -0
  78. package/addon/controllers/management/issues/index.js +22 -2
  79. package/addon/controllers/management/places/index.js +36 -1
  80. package/addon/controllers/management/trailers/index/details.js +1 -0
  81. package/addon/controllers/management/trailers/index.js +50 -2
  82. package/addon/controllers/management/vehicles/index/details.js +1 -0
  83. package/addon/controllers/management/vehicles/index.js +33 -2
  84. package/addon/controllers/management/vendors/index.js +26 -2
  85. package/addon/controllers/operations/orders/index/details.js +50 -7
  86. package/addon/controllers/operations/orders/index.js +59 -4
  87. package/addon/controllers/operations/scheduler/index.js +45 -14
  88. package/addon/controllers/operations/service-rates/index.js +15 -1
  89. package/addon/controllers/settings/index.js +6 -0
  90. package/addon/controllers/settings/map.js +75 -0
  91. package/addon/controllers/settings/orchestrator.js +8 -6
  92. package/addon/controllers/settings/tracking-page.js +212 -0
  93. package/addon/extension.js +97 -1
  94. package/addon/helpers/parcel-box-image.js +16 -0
  95. package/addon/routes/operations/orders/index/details.js +1 -0
  96. package/addon/routes/operations/orders/index/new.js +5 -4
  97. package/addon/routes/operations/scheduler/index.js +1 -1
  98. package/addon/routes/settings/tracking-page.js +31 -0
  99. package/addon/routes.js +1 -0
  100. package/addon/services/contact-actions.js +1 -0
  101. package/addon/services/customer-actions.js +2 -1
  102. package/addon/services/device-actions.js +1 -0
  103. package/addon/services/device-event-actions.js +1 -0
  104. package/addon/services/driver-actions.js +4 -13
  105. package/addon/services/equipment-actions.js +1 -0
  106. package/addon/services/fleet-actions.js +1 -0
  107. package/addon/services/fuel-report-actions.js +1 -0
  108. package/addon/services/inspection-form-actions.js +1 -0
  109. package/addon/services/inspection-submission-actions.js +1 -0
  110. package/addon/services/integrated-vendor-actions.js +1 -0
  111. package/addon/services/issue-actions.js +1 -0
  112. package/addon/services/location.js +69 -21
  113. package/addon/services/maintenance-actions.js +1 -0
  114. package/addon/services/maintenance-schedule-actions.js +2 -1
  115. package/addon/services/map-settings.js +28 -0
  116. package/addon/services/order-actions.js +7 -0
  117. package/addon/services/order-allocation.js +1 -1
  118. package/addon/services/order-presentation.js +129 -0
  119. package/addon/services/part-actions.js +1 -0
  120. package/addon/services/place-actions.js +1 -0
  121. package/addon/services/sensor-actions.js +1 -0
  122. package/addon/services/service-area-actions.js +1 -0
  123. package/addon/services/service-rate-actions.js +1 -0
  124. package/addon/services/telematic-actions.js +1 -0
  125. package/addon/services/trailer-actions.js +4 -14
  126. package/addon/services/vehicle-actions.js +4 -13
  127. package/addon/services/vendor-actions.js +1 -0
  128. package/addon/services/work-order-actions.js +1 -0
  129. package/addon/services/zone-actions.js +1 -0
  130. package/addon/styles/fleetops-engine.css +1391 -0
  131. package/addon/templates/analytics/reports/index/details.hbs +1 -0
  132. package/addon/templates/analytics/reports/index.hbs +1 -0
  133. package/addon/templates/connectivity/devices/index/details.hbs +1 -0
  134. package/addon/templates/connectivity/devices/index.hbs +1 -0
  135. package/addon/templates/connectivity/events/index.hbs +1 -0
  136. package/addon/templates/connectivity/sensors/index/details.hbs +1 -0
  137. package/addon/templates/connectivity/sensors/index.hbs +1 -0
  138. package/addon/templates/maintenance/equipment/index/details.hbs +1 -0
  139. package/addon/templates/maintenance/equipment/index.hbs +2 -0
  140. package/addon/templates/maintenance/inspection-forms/index/details.hbs +1 -0
  141. package/addon/templates/maintenance/inspection-forms/index.hbs +1 -0
  142. package/addon/templates/maintenance/inspection-submissions/index/details.hbs +1 -0
  143. package/addon/templates/maintenance/inspection-submissions/index.hbs +1 -0
  144. package/addon/templates/maintenance/maintenances/index/details.hbs +1 -0
  145. package/addon/templates/maintenance/maintenances/index.hbs +1 -0
  146. package/addon/templates/maintenance/parts/index/details.hbs +1 -0
  147. package/addon/templates/maintenance/parts/index.hbs +2 -0
  148. package/addon/templates/maintenance/schedules/index/details.hbs +1 -0
  149. package/addon/templates/maintenance/schedules/index.hbs +2 -0
  150. package/addon/templates/maintenance/work-orders/index/details.hbs +1 -0
  151. package/addon/templates/maintenance/work-orders/index.hbs +1 -0
  152. package/addon/templates/management/contacts/customers/details.hbs +1 -0
  153. package/addon/templates/management/contacts/customers.hbs +1 -0
  154. package/addon/templates/management/contacts/index/details.hbs +1 -0
  155. package/addon/templates/management/contacts/index.hbs +1 -0
  156. package/addon/templates/management/contacts.hbs +1 -0
  157. package/addon/templates/management/drivers/index/details.hbs +1 -0
  158. package/addon/templates/management/drivers/index.hbs +2 -0
  159. package/addon/templates/management/fleets/index/details.hbs +1 -0
  160. package/addon/templates/management/fleets/index.hbs +1 -0
  161. package/addon/templates/management/fuel-reports/index/details.hbs +1 -0
  162. package/addon/templates/management/fuel-reports/index.hbs +1 -0
  163. package/addon/templates/management/fuel-transactions/index.hbs +1 -0
  164. package/addon/templates/management/issues/index/details.hbs +1 -0
  165. package/addon/templates/management/issues/index.hbs +1 -0
  166. package/addon/templates/management/places/index/details.hbs +1 -0
  167. package/addon/templates/management/places/index.hbs +1 -0
  168. package/addon/templates/management/trailers/index/details.hbs +1 -0
  169. package/addon/templates/management/trailers/index.hbs +2 -0
  170. package/addon/templates/management/vehicles/index/details.hbs +1 -0
  171. package/addon/templates/management/vehicles/index.hbs +2 -0
  172. package/addon/templates/management/vendors/index/details.hbs +1 -0
  173. package/addon/templates/management/vendors/index.hbs +2 -0
  174. package/addon/templates/management/vendors/integrated.hbs +1 -0
  175. package/addon/templates/operations/orders/index/details.hbs +1 -0
  176. package/addon/templates/operations/orders/index.hbs +1 -0
  177. package/addon/templates/operations/service-rates/index/details.hbs +1 -0
  178. package/addon/templates/operations/service-rates/index.hbs +1 -0
  179. package/addon/templates/settings/map.hbs +46 -0
  180. package/addon/templates/settings/tracking-page.hbs +318 -0
  181. package/addon/utils/identity-cell-resource.js +2 -56
  182. package/addon/utils/map-default-view.js +132 -0
  183. package/addon/utils/normalize-order-config-flow.js +6 -1
  184. package/addon/utils/placeholder-images.js +4 -69
  185. package/addon/utils/resource-descriptors/assets.js +2 -57
  186. package/addon/utils/resource-descriptors/helpers.js +23 -122
  187. package/addon/utils/resource-descriptors/operations.js +2 -61
  188. package/addon/utils/resource-descriptors/people.js +3 -142
  189. package/addon/utils/resource-descriptors/shared.js +18 -0
  190. package/addon/utils/tracking-live-source.js +123 -0
  191. package/addon/utils/tracking-page-theme.js +123 -0
  192. package/app/components/admin/tracking-page-settings.js +1 -0
  193. package/app/components/tracking-page/full-view.js +1 -0
  194. package/app/components/tracking-page/locale-selector.js +1 -0
  195. package/app/components/tracking-page/map.js +1 -0
  196. package/app/components/tracking-page/order-list.js +1 -0
  197. package/app/components/tracking-page/public-view.js +1 -0
  198. package/app/components/tracking-page/settings-preview.js +1 -0
  199. package/app/components/tracking-page/verify-panel.js +1 -0
  200. package/app/components/tracking-page.js +1 -0
  201. package/app/components/widget/proof-of-delivery.js +1 -0
  202. package/app/controllers/settings/tracking-page.js +1 -0
  203. package/app/helpers/parcel-box-image.js +1 -0
  204. package/app/routes/settings/tracking-page.js +1 -0
  205. package/app/services/order-presentation.js +1 -0
  206. package/app/templates/settings/tracking-page.js +1 -0
  207. package/app/utils/map-default-view.js +1 -0
  208. package/app/utils/tracking-live-source.js +1 -0
  209. package/app/utils/tracking-page-theme.js +1 -0
  210. package/composer.json +2 -2
  211. package/extension.json +1 -1
  212. package/package.json +4 -4
  213. package/scripts/pest-bootstrap.php +4 -0
  214. package/server/config/fleetops.php +4 -0
  215. package/server/migrations/2026_10_08_000001_create_tracking_sessions_tables.php +51 -0
  216. package/server/migrations/2026_10_10_000001_add_trashed_index_to_device_events.php +40 -0
  217. package/server/migrations/2026_10_10_000002_add_trashed_index_to_positions.php +40 -0
  218. package/server/migrations/2026_10_12_000001_add_deleted_at_to_tracking_sessions_tables.php +32 -0
  219. package/server/resources/views/labels/default.php +2 -2
  220. package/server/resources/views/labels/entity-label.php +2 -2
  221. package/server/resources/views/labels/waypoint-label.php +2 -2
  222. package/server/src/Auth/Schemas/FleetOps.php +5 -0
  223. package/server/src/Console/Commands/ProcessOperationalAlerts.php +1 -0
  224. package/server/src/Console/Commands/PruneTelematicsData.php +101 -26
  225. package/server/src/Events/OrderChanged.php +257 -0
  226. package/server/src/Events/OrderDriverUnassigned.php +68 -0
  227. package/server/src/Exceptions/OrderEditLockedException.php +32 -0
  228. package/server/src/Expansions/CompanyExpansion.php +3 -2
  229. package/server/src/Expansions/UserExpansion.php +12 -10
  230. package/server/src/Http/Controllers/Api/v1/CustomerController.php +108 -18
  231. package/server/src/Http/Controllers/Api/v1/DriverController.php +95 -10
  232. package/server/src/Http/Controllers/Api/v1/EntityController.php +66 -0
  233. package/server/src/Http/Controllers/Api/v1/FleetController.php +10 -4
  234. package/server/src/Http/Controllers/Api/v1/OrderController.php +139 -36
  235. package/server/src/Http/Controllers/Api/v1/PayloadController.php +76 -8
  236. package/server/src/Http/Controllers/Api/v1/PlaceController.php +3 -3
  237. package/server/src/Http/Controllers/Api/v1/TrackingNumberController.php +6 -32
  238. package/server/src/Http/Controllers/Api/v1/VehicleController.php +5 -0
  239. package/server/src/Http/Controllers/Internal/v1/ContactController.php +3 -0
  240. package/server/src/Http/Controllers/Internal/v1/DriverController.php +10 -0
  241. package/server/src/Http/Controllers/Internal/v1/FleetController.php +16 -2
  242. package/server/src/Http/Controllers/Internal/v1/InspectionSubmissionController.php +13 -8
  243. package/server/src/Http/Controllers/Internal/v1/IssueController.php +2 -0
  244. package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +4 -2
  245. package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +93 -11
  246. package/server/src/Http/Controllers/Internal/v1/OrderController.php +118 -28
  247. package/server/src/Http/Controllers/Internal/v1/PositionController.php +22 -0
  248. package/server/src/Http/Controllers/Internal/v1/ProofController.php +55 -4
  249. package/server/src/Http/Controllers/Internal/v1/SettingController.php +303 -10
  250. package/server/src/Http/Controllers/Internal/v1/TrailerController.php +1 -0
  251. package/server/src/Http/Controllers/Internal/v1/VehicleController.php +1 -0
  252. package/server/src/Http/Controllers/Public/TrackingController.php +518 -0
  253. package/server/src/Http/Filter/ContactFilter.php +1 -1
  254. package/server/src/Http/Filter/InspectionFormFilter.php +23 -0
  255. package/server/src/Http/Filter/InspectionSubmissionFilter.php +23 -0
  256. package/server/src/Http/Filter/MaintenanceFilter.php +23 -0
  257. package/server/src/Http/Filter/OrderFilter.php +39 -4
  258. package/server/src/Http/Filter/ProofFilter.php +18 -0
  259. package/server/src/Http/Filter/RouteFilter.php +18 -0
  260. package/server/src/Http/Filter/TelematicFilter.php +23 -0
  261. package/server/src/Http/Filter/WarrantyFilter.php +23 -0
  262. package/server/src/Http/Requests/CancelOrderRequest.php +2 -1
  263. package/server/src/Http/Resources/v1/Concerns/ResolvesPublicRelationFields.php +14 -1
  264. package/server/src/Http/Resources/v1/Index/Customer.php +18 -0
  265. package/server/src/Http/Resources/v1/Index/Order.php +11 -2
  266. package/server/src/Http/Resources/v1/PublicOrderTracking.php +153 -0
  267. package/server/src/Http/Resources/v1/TrackingNumber.php +8 -34
  268. package/server/src/Http/Resources/v1/Zone.php +31 -23
  269. package/server/src/Jobs/FinalizeApiOrderCreation.php +3 -1
  270. package/server/src/Jobs/ProcessAllocationJob.php +16 -6
  271. package/server/src/Jobs/PublishTrackingUpdate.php +50 -0
  272. package/server/src/Listeners/HandleGeofenceEntered.php +31 -15
  273. package/server/src/Listeners/HandleOrderCanceled.php +9 -1
  274. package/server/src/Listeners/HandleUserRemovedFromCompany.php +2 -1
  275. package/server/src/Listeners/PublishTrackingUpdates.php +27 -0
  276. package/server/src/Mail/CustomerCredentialsMail.php +4 -2
  277. package/server/src/Models/AssetConnection.php +3 -0
  278. package/server/src/Models/Contact.php +3 -0
  279. package/server/src/Models/Device.php +28 -0
  280. package/server/src/Models/DeviceEvent.php +7 -0
  281. package/server/src/Models/Driver.php +20 -7
  282. package/server/src/Models/Entity.php +12 -4
  283. package/server/src/Models/Fleet.php +11 -32
  284. package/server/src/Models/FleetDriver.php +3 -0
  285. package/server/src/Models/FleetVehicle.php +3 -0
  286. package/server/src/Models/FuelReport.php +5 -13
  287. package/server/src/Models/Issue.php +4 -16
  288. package/server/src/Models/Order.php +234 -11
  289. package/server/src/Models/OrderConfig.php +129 -0
  290. package/server/src/Models/Payload.php +227 -35
  291. package/server/src/Models/Place.php +7 -1
  292. package/server/src/Models/ServiceArea.php +7 -0
  293. package/server/src/Models/ServiceRate.php +7 -17
  294. package/server/src/Models/TrackingNumber.php +109 -6
  295. package/server/src/Models/TrackingSession.php +33 -0
  296. package/server/src/Models/TrackingSessionGrant.php +22 -0
  297. package/server/src/Models/TrackingStatus.php +6 -0
  298. package/server/src/Models/Vehicle.php +21 -2
  299. package/server/src/Models/Vendor.php +7 -0
  300. package/server/src/Models/Waypoint.php +13 -4
  301. package/server/src/Notifications/OrderAssigned.php +2 -1
  302. package/server/src/Notifications/OrderCanceled.php +7 -1
  303. package/server/src/Notifications/OrderChanged.php +87 -0
  304. package/server/src/Notifications/OrderCompleted.php +2 -1
  305. package/server/src/Notifications/OrderDispatchFailed.php +2 -1
  306. package/server/src/Notifications/OrderDispatched.php +2 -1
  307. package/server/src/Notifications/OrderFailed.php +2 -1
  308. package/server/src/Notifications/OrderUnassigned.php +66 -0
  309. package/server/src/Notifications/WaypointCompleted.php +2 -1
  310. package/server/src/Observers/CompanyUserObserver.php +8 -7
  311. package/server/src/Observers/DriverObserver.php +6 -1
  312. package/server/src/Observers/OrderObserver.php +141 -1
  313. package/server/src/Observers/TrackingNumberObserver.php +18 -6
  314. package/server/src/Observers/UserObserver.php +2 -1
  315. package/server/src/Observers/VehicleObserver.php +8 -3
  316. package/server/src/Orchestration/Engines/RouteSequencingEngine.php +21 -3
  317. package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +38 -8
  318. package/server/src/Orchestration/OrchestrationEngineRegistry.php +61 -1
  319. package/server/src/Orchestration/Support/OrchestratorSettings.php +61 -0
  320. package/server/src/Orchestration/Support/RouteMetrics.php +165 -0
  321. package/server/src/Providers/EventServiceProvider.php +11 -0
  322. package/server/src/Providers/FleetOpsServiceProvider.php +13 -0
  323. package/server/src/Support/ApiCacheThrottle.php +76 -0
  324. package/server/src/Support/LiveOrderQuery.php +3 -1
  325. package/server/src/Support/OrderEdit.php +365 -0
  326. package/server/src/Support/OrderEditPolicy.php +292 -0
  327. package/server/src/Support/ProfileAccountManager.php +54 -1
  328. package/server/src/Support/ResolvesOrderServiceStops.php +5 -0
  329. package/server/src/Support/SocketChannels.php +346 -0
  330. package/server/src/Support/Telematics/TelematicService.php +7 -2
  331. package/server/src/Support/Telematics/Telemetry/Inbox.php +8 -1
  332. package/server/src/Support/TrackingChannel.php +55 -0
  333. package/server/src/Support/TrackingCode.php +242 -0
  334. package/server/src/Support/TrackingPage/TrackingAccess.php +356 -0
  335. package/server/src/Support/TrackingPage/TrackingPageConfig.php +264 -0
  336. package/server/src/Support/TrackingPage/TrackingRecipient.php +111 -0
  337. package/server/src/Support/TrackingPage/TrackingResolver.php +201 -0
  338. package/server/src/Support/TrackingPage/TrackingTarget.php +40 -0
  339. package/server/src/Support/TrackingPage/TrackingView.php +543 -0
  340. package/server/src/Support/TrackingPublisher.php +261 -0
  341. package/server/src/Support/TrackingScope.php +182 -0
  342. package/server/src/Support/TrackingUpdate.php +402 -0
  343. package/server/src/Support/Utils.php +15 -3
  344. package/server/src/Traits/HasTrackingNumber.php +2 -0
  345. package/server/src/routes.php +39 -7
  346. package/server/tests/ApiCustomerControllerContractsTest.php +54 -7
  347. package/server/tests/ApiDriverPasswordContractsTest.php +25 -21
  348. package/server/tests/ApiEntityControllerContractsTest.php +84 -0
  349. package/server/tests/ApiOrderControllerContractsTest.php +245 -0
  350. package/server/tests/ApiPayloadControllerContractsTest.php +121 -0
  351. package/server/tests/ApiPlaceControllerContractsTest.php +1 -1
  352. package/server/tests/ApiTrackingNumberControllerContractsTest.php +6 -9
  353. package/server/tests/CompactResourceSerializationTest.php +23 -59
  354. package/server/tests/ControllerFilterContractsTest.php +1 -1
  355. package/server/tests/CustomerContactIdentitySafetyTest.php +1 -1
  356. package/server/tests/EventContractsTest.php +26 -60
  357. package/server/tests/ExpansionContractsTest.php +15 -2
  358. package/server/tests/Feature/Http/Api/CustomerControllerHelperSeamsTest.php +29 -3
  359. package/server/tests/Feature/Http/Api/DriverControllerAuthFlowsTest.php +23 -0
  360. package/server/tests/Feature/Http/Api/OrderControllerNearbyFiltersTest.php +16 -16
  361. package/server/tests/Feature/Http/Api/OrderControllerQueryFiltersTest.php +41 -14
  362. package/server/tests/Feature/Http/Api/TrackingNumberControllerHelpersTest.php +57 -22
  363. package/server/tests/Feature/Http/Internal/DriverControllerCreateRecordTest.php +23 -9
  364. package/server/tests/Feature/Http/Internal/OrchestrationControllerContractsTest.php +171 -1
  365. package/server/tests/Feature/Http/Internal/OrderControllerActivityFlowsTest.php +72 -0
  366. package/server/tests/Feature/Http/Internal/OrderControllerContractsTest.php +110 -14
  367. package/server/tests/Feature/Http/Internal/OrderControllerCreateRecordTest.php +26 -0
  368. package/server/tests/Feature/Http/Internal/OrderControllerHelpersTest.php +7 -5
  369. package/server/tests/Feature/Http/Internal/PositionControllerReplayChannelTest.php +135 -0
  370. package/server/tests/Feature/Http/Internal/ProofControllerContractsTest.php +3 -3
  371. package/server/tests/Feature/Http/Internal/ProofControllerRecentTest.php +170 -0
  372. package/server/tests/Feature/Http/Public/TrackingControllerTest.php +495 -0
  373. package/server/tests/IndexCustomerPhotoTest.php +151 -0
  374. package/server/tests/ModelAccessorContractsTest.php +4 -3
  375. package/server/tests/NotificationAndMailContractsTest.php +5 -8
  376. package/server/tests/ObserverContractsTest.php +72 -10
  377. package/server/tests/OrderActivityFlowRegressionTest.php +3 -2
  378. package/server/tests/OrderConfigLifecycleTest.php +120 -0
  379. package/server/tests/OrderFilterExecutionTest.php +167 -2
  380. package/server/tests/ProcessAllocationJobContractsTest.php +20 -0
  381. package/server/tests/PublicOrderTrackingResourceTest.php +221 -0
  382. package/server/tests/QueueJobContractsTest.php +2 -1
  383. package/server/tests/RequestContractsTest.php +1 -1
  384. package/server/tests/RouteRegistrationExecutionTest.php +26 -0
  385. package/server/tests/RouteSequencingEngineTest.php +17 -0
  386. package/server/tests/SettingControllerContractsTest.php +110 -0
  387. package/server/tests/Support/TrackingPageDatabase.php +200 -0
  388. package/server/tests/TenantScopingTest.php +206 -0
  389. package/server/tests/TrackingPageSettingsTest.php +343 -0
  390. package/server/tests/Unit/Console/PruneTelematicsDataCommandTest.php +51 -0
  391. package/server/tests/Unit/Events/OrderDispatchFailedTest.php +2 -1
  392. package/server/tests/Unit/Http/Filter/ContactFilterAddressTest.php +61 -0
  393. package/server/tests/Unit/Http/Resources/IndexOrderResourceTest.php +30 -0
  394. package/server/tests/Unit/Jobs/FinalizeApiOrderCreationDispatchGuardTest.php +266 -0
  395. package/server/tests/Unit/Listeners/HandleGeofenceEnteredTest.php +182 -22
  396. package/server/tests/Unit/Listeners/HandleUserRemovedFromCompanyTest.php +5 -0
  397. package/server/tests/Unit/Models/DriverAssignVehicleTest.php +81 -0
  398. package/server/tests/Unit/Models/FleetTest.php +4 -3
  399. package/server/tests/Unit/Models/OrderCancelReasonTest.php +143 -0
  400. package/server/tests/Unit/Models/OrderPayloadConfigAndDistanceTest.php +60 -0
  401. package/server/tests/Unit/Models/OrderPayloadRouteTest.php +4 -2
  402. package/server/tests/Unit/Models/PayloadTest.php +18 -4
  403. package/server/tests/Unit/Models/ServiceRatePersistenceAndServicabilityTest.php +2 -2
  404. package/server/tests/Unit/Models/TrackingNumberTest.php +67 -6
  405. package/server/tests/Unit/Models/VehicleTest.php +16 -2
  406. package/server/tests/Unit/Models/WaypointTest.php +4 -3
  407. package/server/tests/Unit/Notifications/OrderChangeNotificationsTest.php +59 -0
  408. package/server/tests/Unit/Notifications/TerminalOrderNotificationsTest.php +3 -3
  409. package/server/tests/Unit/Observers/DriverDeletionCleanupTest.php +158 -0
  410. package/server/tests/Unit/Observers/FleetOpsCompanyUserAndServiceAreaObserversTest.php +20 -0
  411. package/server/tests/Unit/Observers/ObserverListenerSeamsTest.php +3 -3
  412. package/server/tests/Unit/Observers/OrderAssignmentObserverTest.php +100 -0
  413. package/server/tests/Unit/Observers/OrderDeleteCascadeTest.php +213 -0
  414. package/server/tests/Unit/Observers/VehicleObserverDeleteTest.php +67 -0
  415. package/server/tests/Unit/Orchestration/EngineSelectionAndRouteMetricsTest.php +336 -0
  416. package/server/tests/Unit/Orchestration/Engines/VroomOrchestrationEngineLifecycleTest.php +8 -1
  417. package/server/tests/Unit/Support/ApiCacheDependenciesTest.php +347 -0
  418. package/server/tests/Unit/Support/OrderEditPolicyTest.php +156 -0
  419. package/server/tests/Unit/Support/OrderOperationsTest.php +514 -0
  420. package/server/tests/Unit/Support/OsrmProofAndIssueFilterTest.php +4 -3
  421. package/server/tests/Unit/Support/ProfileAccountManagerTest.php +141 -0
  422. package/server/tests/Unit/Support/QuerySeamsTest.php +5 -1
  423. package/server/tests/Unit/Support/SocketChannelsTest.php +396 -0
  424. package/server/tests/Unit/Support/TrackingChannelTest.php +232 -0
  425. package/server/tests/Unit/Support/TrackingCodeTest.php +186 -0
  426. package/server/tests/Unit/Support/TrackingPage/TrackingAccessTest.php +200 -0
  427. package/server/tests/Unit/Support/TrackingPage/TrackingRecipientTest.php +51 -0
  428. package/server/tests/Unit/Support/TrackingPage/TrackingResolverTest.php +181 -0
  429. package/server/tests/Unit/Support/TrackingPage/TrackingViewTest.php +479 -0
  430. package/server/tests/Unit/Support/TrackingPublisherTest.php +425 -0
  431. package/server/tests/Unit/Support/TrackingUpdateTest.php +363 -0
  432. package/server/tests/Unit/Support/UtilsAdditionalTest.php +9 -0
  433. package/server/tests/Unit/Support/UtilsGeoMatrixAndVendorTest.php +25 -0
  434. package/server/tests/VehicleResourceContractsTest.php +46 -0
  435. package/server/tests/VroomOrchestrationEngineTest.php +34 -1
  436. package/server/tests/ZoneGeofenceSettingsResourceTest.php +121 -0
  437. package/translations/ar-ae.yml +274 -0
  438. package/translations/bg-bg.yaml +274 -0
  439. package/translations/en-us.yaml +347 -0
  440. package/translations/es-pa.yaml +274 -0
  441. package/translations/fr-fr.yaml +274 -0
  442. package/translations/mn-mn.yaml +274 -0
  443. package/translations/pt-br.yaml +274 -0
  444. package/translations/ru-ru.yaml +276 -2
  445. package/translations/uk-ua.yaml +275 -1
  446. package/translations/vi-vn.yaml +274 -0
  447. package/addon/components/cell/contact-identity.hbs +0 -1
  448. package/addon/components/cell/customer-identity.hbs +0 -1
  449. package/addon/components/cell/driver-identity.hbs +0 -1
  450. package/addon/components/cell/fleet-identity.hbs +0 -1
  451. package/addon/components/cell/order-identity.hbs +0 -1
  452. package/addon/components/cell/place-identity.hbs +0 -1
  453. package/addon/components/cell/vehicle-identity.hbs +0 -1
  454. package/addon/components/cell/vendor-identity.hbs +0 -1
  455. package/addon/components/contact/pill.hbs +0 -24
  456. package/addon/components/contact/summary.hbs +0 -1
  457. package/addon/components/customer/pill.hbs +0 -24
  458. package/addon/components/customer/summary.hbs +0 -1
  459. package/addon/components/driver/pill.hbs +0 -24
  460. package/addon/components/driver/summary.hbs +0 -1
  461. package/addon/components/fleet/pill.hbs +0 -24
  462. package/addon/components/fleet/summary.hbs +0 -1
  463. package/addon/components/order/pill.hbs +0 -24
  464. package/addon/components/order/summary.hbs +0 -1
  465. package/addon/components/place/pill.hbs +0 -24
  466. package/addon/components/place/summary.hbs +0 -1
  467. package/addon/components/select-option/contact.hbs +0 -12
  468. package/addon/components/select-option/customer.hbs +0 -12
  469. package/addon/components/select-option/driver.hbs +0 -12
  470. package/addon/components/select-option/fleet.hbs +0 -12
  471. package/addon/components/select-option/order.hbs +0 -12
  472. package/addon/components/select-option/place.hbs +0 -12
  473. package/addon/components/select-option/vehicle.hbs +0 -12
  474. package/addon/components/select-option/vendor.hbs +0 -12
  475. package/addon/components/vehicle/pill.hbs +0 -24
  476. package/addon/components/vehicle/summary.hbs +0 -1
  477. package/addon/components/vendor/pill.hbs +0 -24
  478. package/addon/components/vendor/summary.hbs +0 -1
  479. package/app/components/cell/contact-identity.js +0 -1
  480. package/app/components/cell/customer-identity.js +0 -1
  481. package/app/components/cell/driver-identity.js +0 -1
  482. package/app/components/cell/fleet-identity.js +0 -1
  483. package/app/components/cell/order-identity.js +0 -1
  484. package/app/components/cell/place-identity.js +0 -1
  485. package/app/components/cell/vehicle-identity.js +0 -1
  486. package/app/components/cell/vendor-identity.js +0 -1
  487. package/app/components/contact/pill.js +0 -1
  488. package/app/components/contact/summary.js +0 -1
  489. package/app/components/customer/pill.js +0 -1
  490. package/app/components/customer/summary.js +0 -1
  491. package/app/components/driver/pill.js +0 -1
  492. package/app/components/driver/summary.js +0 -1
  493. package/app/components/fleet/pill.js +0 -1
  494. package/app/components/fleet/summary.js +0 -1
  495. package/app/components/order/pill.js +0 -1
  496. package/app/components/order/summary.js +0 -1
  497. package/app/components/place/pill.js +0 -1
  498. package/app/components/place/summary.js +0 -1
  499. package/app/components/select-option/contact.js +0 -1
  500. package/app/components/select-option/customer.js +0 -1
  501. package/app/components/select-option/driver.js +0 -1
  502. package/app/components/select-option/fleet.js +0 -1
  503. package/app/components/select-option/order.js +0 -1
  504. package/app/components/select-option/place.js +0 -1
  505. package/app/components/select-option/vehicle.js +0 -1
  506. package/app/components/select-option/vendor.js +0 -1
  507. package/app/components/vehicle/pill.js +0 -1
  508. package/app/components/vehicle/summary.js +0 -1
  509. package/app/components/vendor/pill.js +0 -1
  510. package/app/components/vendor/summary.js +0 -1
package/RELEASE.md CHANGED
@@ -1,58 +1,29 @@
1
- > v0.6.70 ~ "Telematics data retention"
1
+ > v0.6.72 ~ "Customer tracking page settings, safer public tracking and structured label codes"
2
2
 
3
3
  ---
4
- ## What's New
5
- - **Telematics Data settings.** A new Fleet-Ops → Settings → Telematics Data page lets each organization decide how long telemetry is kept: device events, raw event payloads, positions, processed and quarantined delivery envelopes, and sync run diagnostics. Administrators set system-wide defaults from the admin console (Fleet-Ops Config → Telematics Data). A value of 0 keeps rows forever.
6
- - **Scheduled retention sweep.** `fleetops:prune-telematics-data` runs every fifteen minutes and applies each organization's policy in bounded batches, so a large backlog is drained gradually without stalling ingestion. Defaults: events 30 days, raw payloads stripped after 7 days, positions 90 days, processed deliveries 24 hours, quarantined deliveries 7 days, sync runs 7 days.
7
- - **Storage usage and on-demand cleanup.** The settings page shows rows, oldest row and estimated size per table for your organization, and "Run cleanup now" queues an immediate retention run.
4
+ ## Highlights
8
5
 
9
- ---
10
- ## Fixes
11
- - Device events no longer store the raw provider unit twice. `payload` keeps the raw unit; `meta` holds only the normalized block (speed, heading, odometer, ignition, fuel level, timing). New event rows are roughly half the size.
12
- - Telemetry-driven saves (device, event, position, vehicle, sensors) no longer write to the activity log on every poll. Organizations that want them can enable "Log telemetry activity" in Telematics Data settings. Manual edits are logged as before.
13
- - Delivery inbox and sync run retention moved from the drain command into the retention sweep; `fleetops:drain-telematic-inbox` now only recovers deliveries and finishes interrupted runs.
14
-
15
- ---
16
- ## Upgrade notes
17
- - New indexes on `device_events (company_uuid, created_at)`, `positions (company_uuid, created_at)` and `telematic_sync_runs (telematic_uuid, updated_at)`; on very large tables run `php artisan migrate` in a maintenance window.
18
- - Retention is enabled by default. Existing rows older than the defaults are removed over successive runs after upgrading; set the values to 0 before upgrading if you need to keep history indefinitely.
19
- - Deleting rows frees space inside the database files, not on disk. Run `OPTIMIZE TABLE` off-peak to return space to the operating system.
20
- - Run `php artisan fleetbase:create-permissions` to register the `telematics-settings` permission and the Telematics Settings Manager policy and role.
6
+ - **Tracking Page settings.** A new Fleet-Ops → Settings → Tracking Page lets each organization configure the customer tracking page:
7
+ - its own page at `/t/{slug}`, with a live address check;
8
+ - whether its numbers can be tracked on the shared page;
9
+ - branding: display name, accent colour with a live contrast check, an optional dark-mode accent, support contacts, "Powered by Fleetbase", and languages;
10
+ - how customers verify (text message or email codes, session length, customer sign-in);
11
+ - what verified customers see.
21
12
 
22
- ---
23
- ## Testing
24
- - Backend tests cover the retention policy layering and clamping, the prune command (per-company policies, compaction, inbox tables via connections, orphans, batch caps, dry runs, filters, locking), the settings and storage usage endpoints, the index migrations, the on-demand job, and the meta and activity-log changes to ingestion.
25
- - Ember tests cover the settings route guard, the settings controller, and the admin defaults component.
26
- > v0.6.70 ~ "Order reporting and a cleaner default dashboard"
27
-
28
- ---
29
- ## What's New
30
- - **Report on orders, their items and tracking.** The report builder can answer questions like "Which products sold the most this month?" and "What did orders total this month?".
31
- - Orders expose their ID and Internal ID, and relationships for tracking (tracking number, region, latest status), Order Config, Customer Vendor, Created By, Purchase Rate › Service Quote and Payload Return.
32
- - `Payload` exposes its items with name, SKU, price, dimensions, metadata and destination. Select an item column for one row per item, or group by one to summarise per product.
33
- - Summary columns count orders distinctly, so they stay correct when item rows are selected. Distance, duration and transaction amount have totals and averages.
34
- - `meta` has no fixed shape, so no column assumes a key in it. Read keys with a computed column, for example `CAST(JSON_UNQUOTE(JSON_EXTRACT(payload.entities.meta, '$.quantity')) AS DECIMAL(15,2))`.
35
- - Column labels drop the table name ("Type", not "Order Type"). Distance and duration are labelled in meters and seconds, and money columns are marked "(minor units)".
36
- - **A cleaner default dashboard.** Fleet-Ops widgets declare their place on the default dashboard: Radar first, then Active Orders and Drivers Online, a full-width Live Fleet map, and Revenue Trend, Top Drivers and Maintenance side by side.
37
- - **Live Fleet widget cards match the live map.** Clicking a driver or vehicle on the widget map shows the same card as **Operations › Live Map**.
13
+ Administrators set the shared page's defaults under Fleet-Ops Config → Tracking Page. The new tracking pages themselves arrive in a later release; these settings are what they will read.
14
+ - **Safer public order lookup.** `GET int/v1/fleet-ops/lookup` is rate limited per address and returns a minimal resource: tracking number, status, timeline, ETA, stop coordinates and items. It no longer returns notes, meta, internal ids, files, rates or customer details. The driver's location is included only while the order is under way. Unknown and malformed tracking numbers get the same answer.
15
+ - **Structured label codes.** Tracking-number QR codes now carry a tracking URL holding the tracking number and the owner's public id, which a phone camera opens as the tracking page. Barcodes use Code 128. Every scan endpoint still accepts the uuid codes already printed on parcels.
16
+ - **Fix: signatures on private S3 buckets.** Signatures were not stored on buckets that reject ACLs; they are now written without one and served through signed URLs. Legacy absolute avatar URLs on vehicles, drivers and places are re-signed when read.
17
+ - **Fix: the customer credentials email** linked to an unrouted page; it now links to `/customer-access/{slug}`.
18
+ - **Fix: parcel box images** on the service rate form and details showed broken images in production builds.
38
19
 
39
20
  ---
40
- ## Fixes
41
- - Custom field values on fuel reports and service areas are saved. They were dropped because the save hooks read the snake_case payload root, while the console sends `fuelReport` and `serviceArea`.
42
- - The report schema no longer references 13 columns and join keys that don't exist: driver name, email and phone and the driver's vehicle, the vehicle's driver, and the fuel report cost, odometer and date. Transaction summaries sum `transaction.amount`.
43
- - Every report table, and the item join, leaves soft-deleted rows out on core-api v1.6.64 and later.
44
-
45
- ---
46
- ## Dependencies
47
- - Order items, JSON totals and soft deletes in reports need `fleetbase/core-api` v1.6.64. The schema still registers on older versions.
48
- - The report builder and the default dashboard order ship in `@fleetbase/ember-ui` v0.4.4.
49
-
50
- ---
51
- ## Testing
52
- - Every declared report column and join key was checked against the database schema, and order reports were run against real storefront orders on MySQL in strict mode.
53
- - Tests cover the report schema at 100% line coverage, the Live Fleet map cards and the default dashboard order.
21
+ ## Upgrading
22
+ - Run `php artisan fleetbase:create-permissions` to register the new `tracking-page-settings` permission.
23
+ - Private-bucket signature storage pairs with fleetbase/core-api's `File::signStoredUrl()`; on older core-api releases stored avatar URLs pass through unchanged.
54
24
 
55
25
  ---
56
26
  ## Need help?
57
27
  - [GitHub Discussions](https://github.com/fleetbase/fleetbase/discussions)
58
28
  - [Discord](https://discord.gg/HnTqQ6zAVn)
29
+ ---
@@ -0,0 +1,56 @@
1
+ <ContentPanel @title="Generic Tracking Page" @open={{true}} @wrapperClass="bordered-classic">
2
+ <div class="space-y-5">
3
+ <div class="flex items-start justify-between gap-4">
4
+ <div>
5
+ <div class="text-sm font-semibold">Enable the generic tracking page</div>
6
+ <div class="text-xs text-gray-600 dark:text-gray-300">Serves /~/track for every organization that allows it. Organizations can also run their own page from their Fleet-Ops settings.</div>
7
+ </div>
8
+ <Toggle @isToggled={{this.genericEnabled}} @onToggle={{fn (mut this.genericEnabled)}} />
9
+ </div>
10
+ <div>
11
+ <label for="admin-tracking-page-name" class="block text-sm font-semibold mb-1">Display name</label>
12
+ <input id="admin-tracking-page-name" type="text" class="form-input w-full" value={{this.displayName}} placeholder="Fleetbase" {{on "input" (fn this.setValue "displayName")}} />
13
+ <p class="mt-1 text-xs text-gray-600 dark:text-gray-300">Shown before a lookup. The logo comes from the instance branding.</p>
14
+ </div>
15
+ <div>
16
+ <label for="admin-tracking-page-accent" class="block text-sm font-semibold mb-1">Accent colour</label>
17
+ <div class="flex items-center gap-3">
18
+ <input id="admin-tracking-page-accent" type="color" class="h-10 w-14 rounded-md border border-gray-300 dark:border-gray-700 bg-transparent cursor-pointer" value={{this.accent}} {{on "input" (fn this.setValue "accent")}} />
19
+ <span class="font-mono text-sm">{{this.accent}}</span>
20
+ </div>
21
+ </div>
22
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
23
+ <div>
24
+ <label for="admin-tracking-page-phone" class="block text-sm font-semibold mb-1">Support phone</label>
25
+ <input id="admin-tracking-page-phone" type="tel" class="form-input w-full" value={{this.supportPhone}} placeholder="+1 555 010 0100" {{on "input" (fn this.setValue "supportPhone")}} />
26
+ </div>
27
+ <div>
28
+ <label for="admin-tracking-page-email" class="block text-sm font-semibold mb-1">Support email</label>
29
+ <input id="admin-tracking-page-email" type="email" class="form-input w-full" value={{this.supportEmail}} placeholder="support@example.com" {{on "input" (fn this.setValue "supportEmail")}} />
30
+ </div>
31
+ <div>
32
+ <label for="admin-tracking-page-website" class="block text-sm font-semibold mb-1">Website</label>
33
+ <input id="admin-tracking-page-website" type="url" class="form-input w-full" value={{this.website}} placeholder="https://example.com" {{on "input" (fn this.setValue "website")}} />
34
+ </div>
35
+ </div>
36
+ <div class="flex items-start justify-between gap-4">
37
+ <div>
38
+ <div class="text-sm font-semibold">Show "Powered by Fleetbase"</div>
39
+ <div class="text-xs text-gray-600 dark:text-gray-300">The default for organizations that haven't chosen.</div>
40
+ </div>
41
+ <Toggle @isToggled={{this.poweredBy}} @onToggle={{fn (mut this.poweredBy)}} />
42
+ </div>
43
+ </div>
44
+ </ContentPanel>
45
+
46
+ <EmberWormhole @to="next-view-section-subheader-actions">
47
+ <Button
48
+ @type="primary"
49
+ @size="sm"
50
+ @icon="save"
51
+ @text="Save Changes"
52
+ @onClick={{perform this.saveSettings}}
53
+ @isLoading={{or this.saveSettings.isRunning this.loadSettings.isRunning}}
54
+ @disabled={{this.saveSettings.isRunning}}
55
+ />
56
+ </EmberWormhole>
@@ -0,0 +1,67 @@
1
+ import Component from '@glimmer/component';
2
+ import { inject as service } from '@ember/service';
3
+ import { tracked } from '@glimmer/tracking';
4
+ import { action } from '@ember/object';
5
+ import { task } from 'ember-concurrency';
6
+
7
+ /**
8
+ * Instance-wide defaults for the generic customer tracking page: whether it is on, and the
9
+ * branding it shows before a lookup (and for companies without their own).
10
+ */
11
+ export default class AdminTrackingPageSettingsComponent extends Component {
12
+ @service fetch;
13
+ @service notifications;
14
+ @tracked genericEnabled = true;
15
+ @tracked displayName = '';
16
+ @tracked accent = '#1F5FA8';
17
+ @tracked supportPhone = '';
18
+ @tracked supportEmail = '';
19
+ @tracked website = '';
20
+ @tracked poweredBy = true;
21
+
22
+ constructor() {
23
+ super(...arguments);
24
+ this.loadSettings.perform();
25
+ }
26
+
27
+ @task *loadSettings() {
28
+ try {
29
+ const settings = yield this.fetch.get('fleet-ops/settings/admin-tracking-page-settings');
30
+ this.genericEnabled = settings?.generic_page?.enabled ?? true;
31
+ this.displayName = settings?.branding?.display_name ?? '';
32
+ this.accent = settings?.branding?.accent ?? '#1F5FA8';
33
+ this.supportPhone = settings?.branding?.support_phone ?? '';
34
+ this.supportEmail = settings?.branding?.support_email ?? '';
35
+ this.website = settings?.branding?.website ?? '';
36
+ this.poweredBy = settings?.branding?.powered_by ?? true;
37
+ } catch (error) {
38
+ this.notifications.serverError(error);
39
+ }
40
+ }
41
+
42
+ @action setValue(property, event) {
43
+ this[property] = event.target.value;
44
+ }
45
+
46
+ @task *saveSettings() {
47
+ try {
48
+ yield this.fetch.post('fleet-ops/settings/admin-tracking-page-settings', {
49
+ trackingPage: {
50
+ generic_page: { enabled: this.genericEnabled },
51
+ branding: {
52
+ display_name: this.displayName,
53
+ accent: this.accent,
54
+ support_phone: this.supportPhone,
55
+ support_email: this.supportEmail,
56
+ website: this.website,
57
+ powered_by: this.poweredBy,
58
+ },
59
+ },
60
+ });
61
+
62
+ this.notifications.success('Tracking page defaults saved.');
63
+ } catch (error) {
64
+ this.notifications.serverError(error);
65
+ }
66
+ }
67
+ }
@@ -137,7 +137,7 @@
137
137
 
138
138
  <InputGroup @name={{t "device.fields.installation-date"}}>
139
139
  <DatePicker
140
- @value={{@resource.installation_date}}
140
+ @value={{format-date-fns @resource.installation_date "yyyy-MM-dd"}}
141
141
  @onChange={{fn (mut @resource.installation_date)}}
142
142
  @placeholder={{t "device.fields.installation-date-placeholder"}}
143
143
  @disabled={{cannot-write @resource}}
@@ -147,7 +147,7 @@
147
147
 
148
148
  <InputGroup @name={{t "device.fields.last-maintenance-date"}}>
149
149
  <DatePicker
150
- @value={{@resource.last_maintenance_date}}
150
+ @value={{format-date-fns @resource.last_maintenance_date "yyyy-MM-dd"}}
151
151
  @onChange={{fn (mut @resource.last_maintenance_date)}}
152
152
  @placeholder={{t "device.fields.last-maintenance-date-placeholder"}}
153
153
  @disabled={{cannot-write @resource}}
@@ -27,7 +27,7 @@
27
27
  </div>
28
28
  <div class="field-info-container md:col-span-3">
29
29
  <div class="field-name">{{t "driver.fields.license-expiry"}}</div>
30
- <div class="field-value">{{n-a (format-date-fns @resource.license_expiry)}}</div>
30
+ <div class="field-value">{{n-a (format-date-fns @resource.license_expiry "yyyy-MM-dd")}}</div>
31
31
  </div>
32
32
  <div class="field-info-container">
33
33
  <div class="field-name">{{t "resource.vehicle"}}</div>
@@ -66,7 +66,7 @@
66
66
 
67
67
  <InputGroup @name={{t "driver.fields.license-expiry"}}>
68
68
  <DatePicker
69
- @value={{@resource.license_expiry}}
69
+ @value={{format-date-fns @resource.license_expiry "yyyy-MM-dd"}}
70
70
  @onChange={{fn (mut @resource.license_expiry)}}
71
71
  @placeholder={{t "driver.fields.license-expiry"}}
72
72
  @disabled={{cannot-write @resource}}
@@ -159,7 +159,7 @@
159
159
  </InputGroup>
160
160
  <InputGroup @name="Purchase Date">
161
161
  <DatePicker
162
- @value={{or (format-date-fns @resource.purchased_at "dd-MM-yyyy") null}}
162
+ @value={{or (format-date-fns @resource.purchased_at "yyyy-MM-dd") null}}
163
163
  @onChange={{fn (mut @resource.purchased_at)}}
164
164
  @placeholder="DD-MM-YYYY"
165
165
  class="filter-date-input w-full flex-1"
@@ -208,6 +208,13 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
208
208
  this.createItem('menu.routing', 'route', 'settings.routing', 'fleet-ops view routing-settings', 'fleet-ops see routing-settings'),
209
209
  this.createItem('menu.orchestrator', 'circle-nodes', 'settings.orchestrator', 'fleet-ops view routing-settings', 'fleet-ops see routing-settings'),
210
210
  this.createItem('menu.scheduling', 'calendar-days', 'settings.scheduling', 'fleet-ops view scheduling-settings', 'fleet-ops see scheduling-settings'),
211
+ this.createItem(
212
+ 'tracking-page.settings.menu',
213
+ 'magnifying-glass-location',
214
+ 'settings.tracking-page',
215
+ 'fleet-ops view tracking-page-settings',
216
+ 'fleet-ops see tracking-page-settings'
217
+ ),
211
218
  this.createItem('menu.telematics-settings', 'satellite-dish', 'settings.telematics', 'fleet-ops view telematics-settings', 'fleet-ops see telematics-settings'),
212
219
  this.createItem('menu.custom-fields', 'pen-to-square', 'settings.custom-fields', 'fleet-ops list custom-field', 'fleet-ops see custom-field'),
213
220
  this.createItem('menu.avatars', 'icons', 'settings.avatars', 'fleet-ops list avatar', 'fleet-ops see avatar'),
@@ -406,9 +413,10 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
406
413
  'settings.routing': 5,
407
414
  'settings.orchestrator': 6,
408
415
  'settings.scheduling': 7,
409
- 'settings.telematics': 8,
410
- 'settings.custom-fields': 9,
411
- 'settings.avatars': 10,
416
+ 'settings.tracking-page': 8,
417
+ 'settings.telematics': 9,
418
+ 'settings.custom-fields': 10,
419
+ 'settings.avatars': 11,
412
420
  };
413
421
 
414
422
  return priorities[route] ?? 0;
@@ -185,7 +185,7 @@
185
185
  </div>
186
186
  <InputGroup @name="Scheduled At">
187
187
  <DatePicker
188
- @value={{or (format-date-fns @resource.scheduled_at "dd-MM-yyyy") null}}
188
+ @value={{or (format-date-fns @resource.scheduled_at "yyyy-MM-dd") null}}
189
189
  @onChange={{fn (mut @resource.scheduled_at)}}
190
190
  @placeholder="DD-MM-YYYY"
191
191
  class="filter-date-input w-full flex-1"
@@ -193,7 +193,7 @@
193
193
  </InputGroup>
194
194
  <InputGroup @name="Started At">
195
195
  <DatePicker
196
- @value={{or (format-date-fns @resource.started_at "dd-MM-yyyy") null}}
196
+ @value={{or (format-date-fns @resource.started_at "yyyy-MM-dd") null}}
197
197
  @onChange={{fn (mut @resource.started_at)}}
198
198
  @placeholder="DD-MM-YYYY"
199
199
  class="filter-date-input w-full flex-1"
@@ -201,7 +201,7 @@
201
201
  </InputGroup>
202
202
  <InputGroup @name="Completed At">
203
203
  <DatePicker
204
- @value={{or (format-date-fns @resource.completed_at "dd-MM-yyyy") null}}
204
+ @value={{or (format-date-fns @resource.completed_at "yyyy-MM-dd") null}}
205
205
  @onChange={{fn (mut @resource.completed_at)}}
206
206
  @placeholder="DD-MM-YYYY"
207
207
  class="filter-date-input w-full flex-1"
@@ -59,7 +59,7 @@ export default class MapGoogleLiveMapComponent extends Component {
59
59
  const marker = this.mapManager.getMarker(driver.id);
60
60
  if (!marker) {
61
61
  const createdMarker = await this.mapManager.addMarker(driver.id, coords.lat, coords.lng, {
62
- iconUrl: driver.vehicle_avatar ?? '/engines-dist/images/driver-marker.png',
62
+ iconUrl: driver.vehicle_avatar ?? '/engines-dist/images/map-marker.png',
63
63
  iconSize: [20, 20],
64
64
  title: driver.name,
65
65
  tooltip: buildDriverLiveMapContent(driver),
@@ -89,7 +89,7 @@ export default class MapGoogleLiveMapComponent extends Component {
89
89
  const marker = this.mapManager.getMarker(vehicle.id);
90
90
  if (!marker) {
91
91
  const createdMarker = await this.mapManager.addMarker(vehicle.id, coords.lat, coords.lng, {
92
- iconUrl: vehicle.avatar_url ?? '/engines-dist/images/vehicle-marker.png',
92
+ iconUrl: vehicle.avatar_url ?? '/engines-dist/images/map-marker.png',
93
93
  iconSize: [20, 20],
94
94
  title: vehicle.displayName,
95
95
  tooltip: buildVehicleLiveMapContent(vehicle),
@@ -12,6 +12,7 @@ import { task, timeout } from 'ember-concurrency';
12
12
  import { next } from '@ember/runloop';
13
13
  import getModelName from '@fleetbase/ember-core/utils/get-model-name';
14
14
  import ensureLeafletPluginsReady, { hasLeafletPluginsReady } from '../../utils/leaflet-plugin-loader';
15
+ import { FIT_VEHICLES_MAX_ZOOM, isZoom, positionedBounds } from '../../utils/map-default-view';
15
16
 
16
17
  export default class MapLeafletLiveMapComponent extends Component {
17
18
  liveViewportReloadDebounceMs = 300;
@@ -243,9 +244,45 @@ export default class MapLeafletLiveMapComponent extends Component {
243
244
  this.rebuildMapContextMenu();
244
245
  }
245
246
 
247
+ /**
248
+ * When the company chose "Fit to my vehicles", frame every positioned
249
+ * vehicle before the first viewport-filtered load. With no positioned
250
+ * vehicles the map stays on the default view resolved by the location
251
+ * service.
252
+ *
253
+ * @return {Boolean} whether the view was fitted
254
+ */
255
+ @task *fitToPositionedVehicles() {
256
+ if (!this.mapSettings.fitMapToVehicles || this.abilities.cannot('fleet-ops list vehicle')) {
257
+ return false;
258
+ }
259
+
260
+ try {
261
+ const vehicles = yield this.fetch.get('fleet-ops/live/vehicles', { limit: this.liveViewportResourceLimit });
262
+ const bounds = positionedBounds(vehicles);
263
+ if (!bounds) {
264
+ return false;
265
+ }
266
+
267
+ const options = { maxZoom: FIT_VEHICLES_MAX_ZOOM, padding: [40, 40], animate: false };
268
+ if (typeof this.mapManager.fitBounds === 'function' && this.mapManager.adapter) {
269
+ this.mapManager.fitBounds(bounds, options);
270
+ } else {
271
+ this.map?.fitBounds?.(bounds, options);
272
+ }
273
+
274
+ return true;
275
+ } catch (err) {
276
+ debug('Failed to fit live map to vehicles: ' + err.message);
277
+ return false;
278
+ }
279
+ }
280
+
246
281
  /** load resources and wait for stuff here and trigger map ready **/
247
282
  @task *load() {
248
283
  try {
284
+ yield this.fitToPositionedVehicles.perform();
285
+
249
286
  // Get initial map bounds for spatial filtering (provider-agnostic)
250
287
  const mapBounds = this.mapManager.getBounds?.() ?? (this.map ? this.map.getBounds() : null);
251
288
  const bounds = this.#serializeBoundsForRequest(mapBounds);
@@ -360,6 +397,11 @@ export default class MapLeafletLiveMapComponent extends Component {
360
397
  if (typeof zoom === 'number' && !isNaN(zoom) && zoom >= 1 && zoom <= 20) {
361
398
  return zoom;
362
399
  }
400
+ // The company's default view (or a country centre) suggests its own zoom
401
+ const locationZoom = this.location.getZoom?.();
402
+ if (isZoom(locationZoom)) {
403
+ return locationZoom;
404
+ }
363
405
  // Return default zoom of 14 if invalid
364
406
  return 14;
365
407
  }
@@ -428,6 +470,9 @@ export default class MapLeafletLiveMapComponent extends Component {
428
470
  if (coordinates && typeof coordinates.latitude === 'number' && typeof coordinates.longitude === 'number') {
429
471
  this.latitude = coordinates.latitude;
430
472
  this.longitude = coordinates.longitude;
473
+ if (isZoom(coordinates.zoom) && !isZoom(this.args.zoom)) {
474
+ this.zoom = coordinates.zoom;
475
+ }
431
476
 
432
477
  // Update map position if map is loaded
433
478
  if (this.map && this.map.setView) {
@@ -860,36 +905,4 @@ export default class MapLeafletLiveMapComponent extends Component {
860
905
 
861
906
  return contextmenuRegistry;
862
907
  }
863
-
864
- /**
865
- * Safely gets a valid latitude value with fallback to default
866
- * @returns {number} Valid latitude value
867
- */
868
- #getValidLatitude() {
869
- const lat = this.location.getLatitude();
870
-
871
- // Validate latitude is a number and within valid range (-90 to 90)
872
- if (typeof lat === 'number' && !isNaN(lat) && lat >= -90 && lat <= 90) {
873
- return lat;
874
- }
875
-
876
- // Fallback to default Singapore latitude
877
- return 1.369;
878
- }
879
-
880
- /**
881
- * Safely gets a valid longitude value with fallback to default
882
- * @returns {number} Valid longitude value
883
- */
884
- #getValidLongitude() {
885
- const lng = this.location.getLongitude();
886
-
887
- // Validate longitude is a number and within valid range (-180 to 180)
888
- if (typeof lng === 'number' && !isNaN(lng) && lng >= -180 && lng <= 180) {
889
- return lng;
890
- }
891
-
892
- // Fallback to default Singapore longitude
893
- return 103.8864;
894
- }
895
908
  }
@@ -15,6 +15,7 @@ import { inject as service } from '@ember/service';
15
15
  *
16
16
  * @arg phases - Array of phase objects (managed externally)
17
17
  * @arg availableEngines - Array of { id, name } engine options
18
+ * @arg defaultEngine - Engine id selected in Orchestrator settings; new phases start with it
18
19
  * @arg onPhasesChange - Action(phases) — called when phases array changes
19
20
  * @arg onRunPhases - Action(phases) — called when "Run" is clicked
20
21
  */
@@ -49,7 +50,7 @@ export default class OrchestratorPhaseBuilderComponent extends Component {
49
50
  id: crypto.randomUUID(),
50
51
  mode,
51
52
  label: this.intl.t(`orchestrator.mode-${mode.replace(/_/g, '-')}`),
52
- engine: 'greedy',
53
+ engine: this.args.defaultEngine ?? 'greedy',
53
54
  allocationStrategy: 'route_aware',
54
55
  vehiclePacking: 'minimize_vehicles',
55
56
  orderStatuses: ['created'],
@@ -59,7 +59,13 @@
59
59
  {{! ── Phase builder panel (collapsible, below toolbar) ── }}
60
60
  {{#if this.showPhaseBuilder}}
61
61
  <div class="workbench-phase-panel border-b dark:border-gray-700 bg-white dark:bg-gray-900 flex-shrink-0 h-80">
62
- <Orchestrator::PhaseBuilder @phases={{this.phases}} @availableEngines={{this.availableEngines}} @onPhasesChange={{this.onPhasesChange}} @onRunPhases={{this.onRunPhases}} />
62
+ <Orchestrator::PhaseBuilder
63
+ @phases={{this.phases}}
64
+ @availableEngines={{this.availableEngines}}
65
+ @defaultEngine={{this.defaultEngine}}
66
+ @onPhasesChange={{this.onPhasesChange}}
67
+ @onRunPhases={{this.onRunPhases}}
68
+ />
63
69
  </div>
64
70
  {{/if}}
65
71
 
@@ -49,6 +49,8 @@ export default class OrchestratorWorkbenchComponent extends Component {
49
49
  @tracked availableVehicles = [];
50
50
  @tracked availableDrivers = [];
51
51
  @tracked availableEngines = [];
52
+ /** The engine selected in Orchestrator settings; new phases start with it. */
53
+ @tracked defaultEngine = null;
52
54
 
53
55
  // ── Plan state ────────────────────────────────────────────────────────────
54
56
 
@@ -204,6 +206,12 @@ export default class OrchestratorWorkbenchComponent extends Component {
204
206
  } catch {
205
207
  this.availableEngines = [{ id: 'greedy', name: 'Greedy (built-in)' }];
206
208
  }
209
+ try {
210
+ const settings = yield this.fetch.get('fleet-ops/settings/orchestrator-settings');
211
+ this.defaultEngine = settings?.allocation_engine ?? null;
212
+ } catch {
213
+ this.defaultEngine = null;
214
+ }
207
215
  }
208
216
 
209
217
  @task *loadCardFields() {
@@ -270,7 +278,8 @@ export default class OrchestratorWorkbenchComponent extends Component {
270
278
  mode: phase.mode,
271
279
  order_statuses: phase.orderStatuses ?? ['created'],
272
280
  options: {
273
- engine: phase.engine ?? 'greedy',
281
+ // Without an explicit engine the server runs the one selected in Orchestrator settings.
282
+ engine: phase.engine ?? null,
274
283
  allocation_strategy: phase.allocationStrategy ?? 'route_aware',
275
284
  vehicle_packing: phase.vehiclePacking ?? 'minimize_vehicles',
276
285
  balance_workload: phase.balanceWorkload ?? false,
@@ -311,6 +320,11 @@ export default class OrchestratorWorkbenchComponent extends Component {
311
320
  if (result.message) {
312
321
  this.orchestratorRunMessage = result.message;
313
322
  }
323
+ // The server substituted another engine (e.g. VROOM unavailable) — say so.
324
+ if (result.warning) {
325
+ this.orchestratorRunMessage = result.warning;
326
+ this.notifications.warning(result.warning, { autoClear: true, clearDuration: 10000 });
327
+ }
314
328
  // If the run returned zero assignments, surface the error/message to
315
329
  // the user immediately — otherwise the right panel stays blank with
316
330
  // no feedback (PlanViewer is only rendered when hasProposedPlan).
@@ -401,7 +415,7 @@ export default class OrchestratorWorkbenchComponent extends Component {
401
415
  id: 'legacy',
402
416
  mode: 'allocate',
403
417
  label: 'Allocate',
404
- engine: 'greedy',
418
+ engine: null,
405
419
  allocationStrategy: 'route_aware',
406
420
  orderStatuses: ['created'],
407
421
  balanceWorkload: false,
@@ -5,9 +5,11 @@
5
5
  @isLoading={{or @isLoadingActivity this.loadActivity.isRunning @isLoading}}
6
6
  @wrapperClass="bordered-top"
7
7
  >
8
- {{#if (eq this.layout "timeline")}}
9
- <Order::ActivityTimeline @resource={{@resource}} @activity={{this.parentOrderActivity}} />
10
- {{else}}
11
- <Order::ActivityList @resource={{@resource}} @activity={{this.parentOrderActivity}} />
12
- {{/if}}
13
- </ContentPanel>
8
+ <div {{did-update this.onStatusChanged @resource.status}}>
9
+ {{#if (eq this.layout "timeline")}}
10
+ <Order::ActivityTimeline @resource={{@resource}} @activity={{this.parentOrderActivity}} />
11
+ {{else}}
12
+ <Order::ActivityList @resource={{@resource}} @activity={{this.parentOrderActivity}} />
13
+ {{/if}}
14
+ </div>
15
+ </ContentPanel>
@@ -1,6 +1,7 @@
1
1
  import Component from '@glimmer/component';
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 OrderDetailsActivityComponent extends Component {
@@ -64,6 +65,14 @@ export default class OrderDetailsActivityComponent extends Component {
64
65
  ];
65
66
  }
66
67
 
68
+ /**
69
+ * A status change recorded outside this panel (a configured lifecycle advancing on save, an
70
+ * extension action, a realtime update) means new tracking statuses exist server-side.
71
+ */
72
+ @action onStatusChanged() {
73
+ this.loadActivity.perform();
74
+ }
75
+
67
76
  @task *loadActivity() {
68
77
  if (!this.args.resource) {
69
78
  return;
@@ -44,6 +44,35 @@
44
44
  )
45
45
  )
46
46
  }}
47
+ {{else if this.profile}}
48
+ {{#each this.profileSections key="key" as |section|}}
49
+ {{#if section.native}}
50
+ {{#let
51
+ (component
52
+ section.componentName
53
+ resource=@resource
54
+ onChange=section.onChange
55
+ isLoading=@isLoading
56
+ isLoadingActivity=@isLoadingActivity
57
+ reloadToken=this.effectiveProofReloadToken
58
+ )
59
+ as |NativeSection|
60
+ }}
61
+ <NativeSection />
62
+ {{/let}}
63
+ {{else}}
64
+ <LazyEngineComponent @component={{section.component}} as |ProfileSection|>
65
+ <ProfileSection
66
+ @resource={{@resource}}
67
+ @controller={{@controller}}
68
+ @permission={{get-write-permission @resource}}
69
+ @isLoading={{@isLoading}}
70
+ @profile={{this.profile}}
71
+ @onChange={{@onDetailsChanged}}
72
+ />
73
+ </LazyEngineComponent>
74
+ {{/if}}
75
+ {{/each}}
47
76
  {{else}}
48
77
  <RegistryYield @registry="fleet-ops:component:order:details:start" as |RegistryComponent|>
49
78
  <RegistryComponent @resource={{@resource}} @controller={{@controller}} @permission={{get-write-permission @resource}} @isLoading={{@isLoading}} />