@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
@@ -3,12 +3,16 @@
3
3
  namespace Fleetbase\FleetOps\Http\Controllers\Internal\v1;
4
4
 
5
5
  use Fleetbase\FleetOps\Jobs\DispatchTelematicsRetentionJobs;
6
+ use Fleetbase\FleetOps\Orchestration\Support\OrchestratorSettings;
6
7
  use Fleetbase\FleetOps\Support\Telematics\Retention\RetentionPolicy;
7
8
  use Fleetbase\FleetOps\Support\Telematics\Telemetry\Queue;
9
+ use Fleetbase\FleetOps\Support\TrackingPage\TrackingPageConfig;
10
+ use Fleetbase\FleetOps\Support\Utils;
8
11
  use Fleetbase\FleetOps\Tracking\TrackingProviderRegistry;
9
12
  use Fleetbase\FleetOps\Traits\AuthorizesMethods;
10
13
  use Fleetbase\Http\Controllers\Controller;
11
14
  use Fleetbase\Models\Setting;
15
+ use Fleetbase\Services\SmsService;
12
16
  use Fleetbase\Support\Auth;
13
17
  use Fleetbase\Support\NotificationRegistry;
14
18
  use Illuminate\Database\MySqlConnection;
@@ -47,6 +51,11 @@ class SettingController extends Controller
47
51
  'saveTelematicsSettings' => 'update telematics-settings',
48
52
  'getTelematicsStorageUsage' => 'admin',
49
53
  'runTelematicsRetention' => 'admin',
54
+ 'getTrackingPageSettings' => 'view tracking-page-settings',
55
+ 'saveTrackingPageSettings' => 'update tracking-page-settings',
56
+ 'validateTrackingPageSlug' => 'update tracking-page-settings',
57
+ 'getAdminTrackingPageSettings' => 'admin',
58
+ 'saveAdminTrackingPageSettings' => 'admin',
50
59
  ]);
51
60
  }
52
61
 
@@ -387,6 +396,10 @@ class SettingController extends Controller
387
396
  'googleMapsMapType' => 'roadmap',
388
397
  'showGoogleMapsTrafficLayer' => false,
389
398
  'showGoogleMapsTransitLayer' => false,
399
+ 'defaultMapLatitude' => null,
400
+ 'defaultMapLongitude' => null,
401
+ 'defaultMapZoom' => null,
402
+ 'fitMapToVehicles' => false,
390
403
  ];
391
404
 
392
405
  $systemMapSettings = $this->lookupSetting('fleet-ops.map-settings', []);
@@ -400,6 +413,12 @@ class SettingController extends Controller
400
413
  $mapSettings['googleMapsApiKey'] = $this->googleMapsApiKey();
401
414
  $mapSettings['googleMapsMapId'] = data_get($systemMapSettings, 'googleMapsMapId', '');
402
415
 
416
+ // Last-resort starting point for the live map when the company has no
417
+ // configured default view and the browser cannot locate the user: the
418
+ // centre of the company's country, so a fresh account opens in the
419
+ // right part of the world instead of a hardcoded city.
420
+ $mapSettings['companyMapCenter'] = $this->companyMapCenter();
421
+
403
422
  return response()->json($mapSettings);
404
423
  }
405
424
 
@@ -421,6 +440,9 @@ class SettingController extends Controller
421
440
  // from the payload in case a client accidentally sends it.
422
441
  unset($settings['googleMapsApiKey']);
423
442
 
443
+ // Derived server-side on every read, never persisted.
444
+ unset($settings['companyMapCenter']);
445
+
424
446
  // Validate provider value
425
447
  $settings = $this->normalizeCompanyMapSettings($settings);
426
448
 
@@ -479,9 +501,99 @@ class SettingController extends Controller
479
501
  'googleMapsMapType' => $googleMapsMapType,
480
502
  'showGoogleMapsTrafficLayer' => filter_var(data_get($settings, 'showGoogleMapsTrafficLayer', false), FILTER_VALIDATE_BOOLEAN),
481
503
  'showGoogleMapsTransitLayer' => filter_var(data_get($settings, 'showGoogleMapsTransitLayer', false), FILTER_VALIDATE_BOOLEAN),
504
+ ...$this->normalizeDefaultMapView($settings),
505
+ ];
506
+ }
507
+
508
+ /**
509
+ * Normalize the company's default live map view. A centre is only kept as
510
+ * a valid latitude/longitude pair — half a coordinate would send the map
511
+ * to the equator or the meridian — and the zoom is clamped to the range
512
+ * both map providers support.
513
+ */
514
+ protected function normalizeDefaultMapView(array $settings): array
515
+ {
516
+ $latitude = $this->normalizeCoordinate(data_get($settings, 'defaultMapLatitude'), 90);
517
+ $longitude = $this->normalizeCoordinate(data_get($settings, 'defaultMapLongitude'), 180);
518
+ $hasCenter = $latitude !== null && $longitude !== null;
519
+ $zoom = filter_var(data_get($settings, 'defaultMapZoom'), FILTER_VALIDATE_INT);
520
+
521
+ return [
522
+ 'defaultMapLatitude' => $hasCenter ? $latitude : null,
523
+ 'defaultMapLongitude' => $hasCenter ? $longitude : null,
524
+ 'defaultMapZoom' => $zoom === false ? null : max(1, min(20, $zoom)),
525
+ 'fitMapToVehicles' => filter_var(data_get($settings, 'fitMapToVehicles', false), FILTER_VALIDATE_BOOLEAN),
482
526
  ];
483
527
  }
484
528
 
529
+ protected function normalizeCoordinate($value, float $limit): ?float
530
+ {
531
+ if ($value === null || $value === '' || !is_numeric($value)) {
532
+ return null;
533
+ }
534
+
535
+ $value = (float) $value;
536
+
537
+ return abs($value) <= $limit ? $value : null;
538
+ }
539
+
540
+ /**
541
+ * Resolve the centre of the current company's country, from its country
542
+ * code or, when none was recorded, the country of its timezone.
543
+ */
544
+ protected function companyMapCenter(): ?array
545
+ {
546
+ $company = $this->currentCompany();
547
+ $country = $this->normalizeCountryCode(data_get($company, 'country')) ?? $this->countryFromTimezone(data_get($company, 'timezone'));
548
+ if ($country === null) {
549
+ return null;
550
+ }
551
+
552
+ $coordinates = data_get($this->countryData($country), 'coordinates', []);
553
+ $latitude = $this->normalizeCoordinate(data_get($coordinates, 'latitude'), 90);
554
+ $longitude = $this->normalizeCoordinate(data_get($coordinates, 'longitude'), 180);
555
+ if ($latitude === null || $longitude === null || ($latitude == 0 && $longitude == 0)) {
556
+ return null;
557
+ }
558
+
559
+ return [
560
+ 'country' => $country,
561
+ 'latitude' => $latitude,
562
+ 'longitude' => $longitude,
563
+ ];
564
+ }
565
+
566
+ protected function normalizeCountryCode($country): ?string
567
+ {
568
+ return is_string($country) && preg_match('/^[A-Za-z]{2}$/', $country) ? strtoupper($country) : null;
569
+ }
570
+
571
+ protected function countryFromTimezone($timezone): ?string
572
+ {
573
+ if (!is_string($timezone) || $timezone === '') {
574
+ return null;
575
+ }
576
+
577
+ try {
578
+ $location = (new \DateTimeZone($timezone))->getLocation();
579
+ } catch (\Throwable $e) {
580
+ return null;
581
+ }
582
+
583
+ // UTC and other non-geographic zones report the country code "??".
584
+ return $this->normalizeCountryCode(data_get($location, 'country_code'));
585
+ }
586
+
587
+ protected function countryData(string $country): ?array
588
+ {
589
+ try {
590
+ return Utils::getCountryData($country);
591
+ } catch (\Throwable $e) {
592
+ // The countries package throws for codes it does not know.
593
+ return null;
594
+ }
595
+ }
596
+
485
597
  /**
486
598
  * Sanitize a Leaflet XYZ tile URL template — only http(s) URLs are accepted,
487
599
  * anything else is discarded so the frontend falls back to the default tiles.
@@ -547,14 +659,7 @@ class SettingController extends Controller
547
659
  */
548
660
  public function getOrchestratorSettings()
549
661
  {
550
- $defaults = [
551
- 'allocation_engine' => 'vroom',
552
- 'auto_allocate_on_create' => false,
553
- 'auto_reallocate_on_complete' => false,
554
- 'max_travel_time_seconds' => 3600,
555
- 'balance_workload' => false,
556
- ];
557
- $settings = $this->lookupFromCompanySetting('fleet-ops.allocation-settings', $defaults);
662
+ $settings = $this->lookupFromCompanySetting(OrchestratorSettings::KEY, OrchestratorSettings::defaults());
558
663
 
559
664
  return response()->json($settings);
560
665
  }
@@ -567,13 +672,13 @@ class SettingController extends Controller
567
672
  public function saveOrchestratorSettings(Request $request)
568
673
  {
569
674
  $settings = [
570
- 'allocation_engine' => $request->input('allocation_engine', 'vroom'),
675
+ 'allocation_engine' => $request->input('allocation_engine', OrchestratorSettings::DEFAULT_ENGINE),
571
676
  'auto_allocate_on_create' => (bool) $request->input('auto_allocate_on_create', false),
572
677
  'auto_reallocate_on_complete' => (bool) $request->input('auto_reallocate_on_complete', false),
573
678
  'max_travel_time_seconds' => (int) $request->input('max_travel_time_seconds', 3600),
574
679
  'balance_workload' => (bool) $request->input('balance_workload', false),
575
680
  ];
576
- $this->configureCompanySetting('fleet-ops.allocation-settings', $settings);
681
+ $this->configureCompanySetting(OrchestratorSettings::KEY, $settings);
577
682
 
578
683
  return response()->json($settings);
579
684
  }
@@ -1074,6 +1179,194 @@ class SettingController extends Controller
1074
1179
  return $statistics;
1075
1180
  }
1076
1181
 
1182
+ /**
1183
+ * The company's customer tracking page settings, with what the settings screen needs to
1184
+ * explain them: whether the slug is usable, whether SMS can be sent, and whether the
1185
+ * customer portal is there for sign-in.
1186
+ *
1187
+ * @return \Illuminate\Http\JsonResponse
1188
+ */
1189
+ public function getTrackingPageSettings()
1190
+ {
1191
+ return response()->json($this->trackingPageView($this->trackingPageConfig()));
1192
+ }
1193
+
1194
+ /**
1195
+ * Save the company's tracking page settings.
1196
+ *
1197
+ * The slug is indexed so the public page can find the company in one read. A slug that
1198
+ * is invalid, reserved or taken can't be saved while the organization page is on; while
1199
+ * it is off, the slug is kept but not indexed.
1200
+ *
1201
+ * @return \Illuminate\Http\JsonResponse
1202
+ */
1203
+ public function saveTrackingPageSettings(Request $request)
1204
+ {
1205
+ $previous = $this->trackingPageConfig();
1206
+ $config = TrackingPageConfig::sanitize((array) $request->input('trackingPage', []), $this->trackingPageAdminConfig(), $this->trackingPageCompanyName());
1207
+ $slug = $config['org_page']['slug'];
1208
+ $validation = $this->trackingPageSlugValidation($slug);
1209
+
1210
+ if ($config['org_page']['enabled'] && !$validation['valid']) {
1211
+ return response()->error($validation['message'], 422);
1212
+ }
1213
+
1214
+ if ($validation['valid']) {
1215
+ $previousSlug = $previous['org_page']['slug'];
1216
+ if ($previousSlug !== $slug && $this->trackingPageSlugOwner($previousSlug) === $this->trackingPageCompanyUuid()) {
1217
+ $this->configureSetting(TrackingPageConfig::SLUG_INDEX_PREFIX . $previousSlug, null);
1218
+ }
1219
+
1220
+ $this->configureSetting(TrackingPageConfig::SLUG_INDEX_PREFIX . $slug, $this->trackingPageCompanyUuid());
1221
+ }
1222
+
1223
+ $this->configureCompanySetting(TrackingPageConfig::SETTING_KEY, $config);
1224
+
1225
+ return response()->json($this->trackingPageView($config));
1226
+ }
1227
+
1228
+ /**
1229
+ * Check a slug while it is typed.
1230
+ *
1231
+ * @return \Illuminate\Http\JsonResponse
1232
+ */
1233
+ public function validateTrackingPageSlug(Request $request)
1234
+ {
1235
+ $slug = strtolower(trim((string) $request->input('slug', '')));
1236
+
1237
+ return response()->json(array_merge(['slug' => $slug], $this->trackingPageSlugValidation($slug)));
1238
+ }
1239
+
1240
+ /**
1241
+ * Instance-wide defaults for the generic tracking page.
1242
+ *
1243
+ * @return \Illuminate\Http\JsonResponse
1244
+ */
1245
+ public function getAdminTrackingPageSettings()
1246
+ {
1247
+ return response()->json($this->trackingPageAdminConfig());
1248
+ }
1249
+
1250
+ /**
1251
+ * Save the instance-wide defaults for the generic tracking page.
1252
+ *
1253
+ * @return \Illuminate\Http\JsonResponse
1254
+ */
1255
+ public function saveAdminTrackingPageSettings(Request $request)
1256
+ {
1257
+ $config = TrackingPageConfig::sanitizeAdmin((array) $request->input('trackingPage', []));
1258
+ $this->configureSetting(TrackingPageConfig::ADMIN_SETTING_KEY, $config);
1259
+
1260
+ return response()->json($config);
1261
+ }
1262
+
1263
+ protected function trackingPageConfig(): array
1264
+ {
1265
+ return TrackingPageConfig::sanitize(
1266
+ (array) $this->lookupCompanySetting(TrackingPageConfig::SETTING_KEY, []),
1267
+ $this->trackingPageAdminConfig(),
1268
+ $this->trackingPageCompanyName()
1269
+ );
1270
+ }
1271
+
1272
+ protected function trackingPageAdminConfig(): array
1273
+ {
1274
+ return TrackingPageConfig::sanitizeAdmin((array) $this->lookupSetting(TrackingPageConfig::ADMIN_SETTING_KEY, []));
1275
+ }
1276
+
1277
+ /**
1278
+ * The saved config plus the read-only facts the settings screen shows beside it.
1279
+ */
1280
+ protected function trackingPageView(array $config): array
1281
+ {
1282
+ $accent = $config['branding']['accent'];
1283
+
1284
+ return array_merge($config, [
1285
+ 'slug_validation' => $this->trackingPageSlugValidation($config['org_page']['slug']),
1286
+ 'accent_ink' => TrackingPageConfig::inkFor($accent),
1287
+ 'accent_contrast' => TrackingPageConfig::contrastRatio($accent, TrackingPageConfig::inkFor($accent)),
1288
+ 'sms_available' => $this->smsProviderConfigured(),
1289
+ 'customer_portal_installed' => $this->customerPortalInstalled(),
1290
+ 'admin' => $this->trackingPageAdminConfig(),
1291
+ ]);
1292
+ }
1293
+
1294
+ /**
1295
+ * @return array{valid: bool, code: string, message: string}
1296
+ */
1297
+ protected function trackingPageSlugValidation(string $slug): array
1298
+ {
1299
+ $error = TrackingPageConfig::slugError($slug);
1300
+ if ($error === 'invalid') {
1301
+ return ['valid' => false, 'code' => 'invalid', 'message' => 'Use 3 to 40 lowercase letters, numbers and single hyphens, starting and ending with a letter or number.'];
1302
+ }
1303
+
1304
+ if ($error === 'reserved') {
1305
+ return ['valid' => false, 'code' => 'reserved', 'message' => 'This address is reserved. Please choose another.'];
1306
+ }
1307
+
1308
+ $owner = $this->trackingPageSlugOwner($slug);
1309
+ if ($owner !== null && $owner !== $this->trackingPageCompanyUuid()) {
1310
+ return ['valid' => false, 'code' => 'taken', 'message' => 'Another organization already uses this address.'];
1311
+ }
1312
+
1313
+ return ['valid' => true, 'code' => 'available', 'message' => 'This address is available.'];
1314
+ }
1315
+
1316
+ protected function trackingPageSlugOwner(string $slug): ?string
1317
+ {
1318
+ $owner = $this->lookupSetting(TrackingPageConfig::SLUG_INDEX_PREFIX . $slug);
1319
+
1320
+ return is_string($owner) && $owner !== '' ? $owner : null;
1321
+ }
1322
+
1323
+ protected function trackingPageCompanyUuid(): ?string
1324
+ {
1325
+ return data_get($this->currentCompany(), 'uuid');
1326
+ }
1327
+
1328
+ protected function trackingPageCompanyName(): ?string
1329
+ {
1330
+ return data_get($this->currentCompany(), 'name');
1331
+ }
1332
+
1333
+ /**
1334
+ * Whether the instance can send SMS. Twilio is listed as always available, so it counts
1335
+ * only with credentials; other providers report their own configuration.
1336
+ */
1337
+ protected function smsProviderConfigured(): bool
1338
+ {
1339
+ if (filled(config('services.twilio.sid')) && filled(config('services.twilio.token'))) {
1340
+ return true;
1341
+ }
1342
+
1343
+ return collect($this->smsProviders())
1344
+ ->except(SmsService::PROVIDER_TWILIO)
1345
+ ->contains(fn ($provider) => (bool) data_get($provider, 'available'));
1346
+ }
1347
+
1348
+ protected function smsProviders(): array
1349
+ {
1350
+ // @codeCoverageIgnoreStart
1351
+ // Reads the live SMS provider configuration; tests replace this seam.
1352
+ return (new SmsService())->getAvailableProviders();
1353
+ // @codeCoverageIgnoreEnd
1354
+ }
1355
+
1356
+ protected function customerPortalInstalled(): bool
1357
+ {
1358
+ return collect($this->installedFleetbaseExtensions())
1359
+ ->contains(fn ($package) => data_get($package, 'name') === 'fleetbase/customer-portal-api');
1360
+ }
1361
+
1362
+ protected function installedFleetbaseExtensions(): array
1363
+ {
1364
+ // @codeCoverageIgnoreStart
1365
+ // Reads the installed composer packages; tests replace this seam.
1366
+ return Utils::getInstalledFleetbaseExtensions();
1367
+ // @codeCoverageIgnoreEnd
1368
+ }
1369
+
1077
1370
  protected function dispatchTelematicsPrune(): void
1078
1371
  {
1079
1372
  Queue::dispatch(new DispatchTelematicsRetentionJobs());
@@ -166,6 +166,7 @@ class TrailerController extends FleetOpsController
166
166
  'updated_by_uuid' => session('user'),
167
167
  'updated_at' => now(),
168
168
  ]));
169
+ AssetConnection::invalidateApiCacheManually(session('company'));
169
170
 
170
171
  return response()->json([
171
172
  'status' => 'ok',
@@ -186,6 +186,7 @@ class VehicleController extends FleetOpsController
186
186
  'updated_at' => now(),
187
187
  ]);
188
188
  });
189
+ Order::invalidateApiCacheManually(session('company'));
189
190
 
190
191
  return response()->json([
191
192
  'status' => 'ok',