@fleetbase/fleetops-engine 0.6.37 → 0.6.39

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 (434) hide show
  1. package/addon/components/activity/form.hbs +2 -12
  2. package/addon/components/activity/logic-builder.hbs +1 -1
  3. package/addon/components/custom-entity/form.hbs +4 -20
  4. package/addon/components/device/form.hbs +3 -15
  5. package/addon/components/device/panel-header.hbs +13 -2
  6. package/addon/components/driver/details.hbs +33 -2
  7. package/addon/components/driver/form.hbs +42 -0
  8. package/addon/components/driver/panel-header.hbs +8 -1
  9. package/addon/components/driver/schedule.hbs +115 -76
  10. package/addon/components/driver/schedule.js +349 -157
  11. package/addon/components/driver-onboard-settings.hbs +2 -8
  12. package/addon/components/entity-field-editing-settings.hbs +2 -5
  13. package/addon/components/equipment/card.hbs +49 -0
  14. package/addon/components/equipment/card.js +6 -0
  15. package/addon/components/equipment/details.hbs +83 -44
  16. package/addon/components/equipment/form.hbs +111 -41
  17. package/addon/components/equipment/form.js +78 -10
  18. package/addon/components/equipment/panel-header.hbs +36 -0
  19. package/addon/components/equipment/panel-header.js +2 -0
  20. package/addon/components/fleet/driver-listing.hbs +3 -1
  21. package/addon/components/fleet/vehicle-listing.hbs +4 -7
  22. package/addon/components/fleet-panel/vehicle-listing.hbs +1 -6
  23. package/addon/components/fuel-report/form.hbs +1 -5
  24. package/addon/components/layout/fleet-ops-sidebar.hbs +40 -36
  25. package/addon/components/layout/fleet-ops-sidebar.js +61 -10
  26. package/addon/components/maintenance/cost-panel.hbs +176 -0
  27. package/addon/components/maintenance/cost-panel.js +241 -0
  28. package/addon/components/maintenance/details.hbs +123 -60
  29. package/addon/components/maintenance/form.hbs +138 -78
  30. package/addon/components/maintenance/form.js +131 -6
  31. package/addon/components/maintenance/panel-header.hbs +31 -0
  32. package/addon/components/maintenance/panel-header.js +2 -0
  33. package/addon/components/maintenance-schedule/details.hbs +260 -0
  34. package/addon/components/maintenance-schedule/details.js +158 -0
  35. package/addon/components/maintenance-schedule/form.hbs +287 -0
  36. package/addon/components/maintenance-schedule/form.js +199 -0
  37. package/addon/components/map/container.hbs +1 -1
  38. package/addon/components/map/drawer/device-event-listing.hbs +1 -1
  39. package/addon/components/map/drawer/driver-listing.hbs +1 -6
  40. package/addon/components/map/drawer/place-listing.hbs +1 -6
  41. package/addon/components/map/drawer/vehicle-listing.hbs +1 -6
  42. package/addon/components/map/drawer.hbs +8 -1
  43. package/addon/components/map/leaflet-live-map.hbs +2 -1
  44. package/addon/components/map/toolbar/visibility-control-panel.hbs +1 -1
  45. package/addon/components/map/toolbar.hbs +4 -29
  46. package/addon/components/modals/add-driver-shift.hbs +155 -0
  47. package/addon/components/modals/add-driver-shift.js +210 -0
  48. package/addon/components/modals/bulk-assign-orders.hbs +67 -0
  49. package/addon/components/modals/driver-shift.hbs +43 -0
  50. package/addon/components/modals/driver-shift.js +56 -0
  51. package/addon/components/modals/entity-form.hbs +5 -28
  52. package/addon/components/modals/orchestrator-import.hbs +351 -0
  53. package/addon/components/modals/orchestrator-import.js +807 -0
  54. package/addon/components/modals/order-config-new-status.hbs +1 -5
  55. package/addon/components/modals/scheduling-conflict.hbs +47 -0
  56. package/addon/components/modals/scheduling-conflict.js +53 -0
  57. package/addon/components/modals/send-work-order.hbs +91 -0
  58. package/addon/components/modals/send-work-order.js +3 -0
  59. package/addon/components/modals/service-area-form.hbs +1 -6
  60. package/addon/components/modals/set-driver-availability.hbs +50 -0
  61. package/addon/components/modals/set-driver-availability.js +57 -0
  62. package/addon/components/modals/update-order-activity.hbs +13 -9
  63. package/addon/components/modals/user-form.hbs +1 -5
  64. package/addon/components/modals/vehicle-form.hbs +17 -102
  65. package/addon/components/modals/vendor-form.hbs +15 -82
  66. package/addon/components/orchestrator/card-fields-settings.hbs +76 -0
  67. package/addon/components/orchestrator/card-fields-settings.js +134 -0
  68. package/addon/components/orchestrator/order-pool.hbs +264 -0
  69. package/addon/components/orchestrator/order-pool.js +394 -0
  70. package/addon/components/orchestrator/phase-builder.hbs +162 -0
  71. package/addon/components/orchestrator/phase-builder.js +162 -0
  72. package/addon/components/orchestrator/plan-viewer.hbs +278 -0
  73. package/addon/components/orchestrator/plan-viewer.js +342 -0
  74. package/addon/components/orchestrator/resource-panel.hbs +301 -0
  75. package/addon/components/orchestrator/resource-panel.js +106 -0
  76. package/addon/components/orchestrator-workbench.hbs +318 -0
  77. package/addon/components/orchestrator-workbench.js +1087 -0
  78. package/addon/components/order/details/custom-fields.hbs +10 -1
  79. package/addon/components/order/details/detail.hbs +37 -2
  80. package/addon/components/order/details/detail.js +0 -1
  81. package/addon/components/order/details/integrated-vendor-details.hbs +4 -4
  82. package/addon/components/order/details/notes.hbs +1 -7
  83. package/addon/components/order/details/route.hbs +1 -5
  84. package/addon/components/order/form/details.hbs +44 -10
  85. package/addon/components/order/form/details.js +57 -0
  86. package/addon/components/order/form/notes.hbs +1 -7
  87. package/addon/components/order/form/payload.hbs +1 -7
  88. package/addon/components/order/form/route.hbs +3 -15
  89. package/addon/components/order/header.hbs +1 -7
  90. package/addon/components/order/route-editor.hbs +4 -25
  91. package/addon/components/order/schedule-card.hbs +102 -95
  92. package/addon/components/order/schedule-card.js +8 -3
  93. package/addon/components/order-config-manager/details.hbs +2 -10
  94. package/addon/components/order-config-manager/entities.hbs +1 -7
  95. package/addon/components/order-config-manager.hbs +1 -6
  96. package/addon/components/part/card.hbs +49 -0
  97. package/addon/components/part/card.js +6 -0
  98. package/addon/components/part/details.hbs +102 -56
  99. package/addon/components/part/form.hbs +131 -56
  100. package/addon/components/part/form.js +78 -11
  101. package/addon/components/part/panel-header.hbs +36 -0
  102. package/addon/components/part/panel-header.js +2 -0
  103. package/addon/components/place/form.hbs +1 -7
  104. package/addon/components/sensor/details.hbs +1 -1
  105. package/addon/components/sensor/form.hbs +5 -3
  106. package/addon/components/sensor/panel-header.hbs +8 -1
  107. package/addon/components/service-area/form.hbs +1 -6
  108. package/addon/components/service-rate/form.hbs +12 -60
  109. package/addon/components/telematic/form.hbs +6 -2
  110. package/addon/components/vehicle/details/maintenance-history.hbs +42 -0
  111. package/addon/components/vehicle/details/maintenance-history.js +32 -0
  112. package/addon/components/vehicle/details/schedules.hbs +40 -0
  113. package/addon/components/vehicle/details/schedules.js +32 -0
  114. package/addon/components/vehicle/details/work-orders.hbs +42 -0
  115. package/addon/components/vehicle/details/work-orders.js +32 -0
  116. package/addon/components/vehicle/details.hbs +30 -0
  117. package/addon/components/vehicle/form.hbs +39 -0
  118. package/addon/components/vehicle/panel-header.hbs +19 -49
  119. package/addon/components/warranty/details.hbs +3 -2
  120. package/addon/components/warranty/form.hbs +3 -17
  121. package/addon/components/work-order/details.hbs +135 -40
  122. package/addon/components/work-order/form.hbs +178 -45
  123. package/addon/components/work-order/form.js +197 -4
  124. package/addon/components/work-order/panel-header.hbs +31 -0
  125. package/addon/components/work-order/panel-header.js +2 -0
  126. package/addon/controllers/connectivity/devices/index/details.js +1 -1
  127. package/addon/controllers/maintenance/equipment/index/details/index.js +0 -1
  128. package/addon/controllers/maintenance/equipment/index/details.js +36 -1
  129. package/addon/controllers/maintenance/equipment/index/edit.js +56 -1
  130. package/addon/controllers/maintenance/equipment/index/new.js +32 -1
  131. package/addon/controllers/maintenance/equipment/index.js +127 -113
  132. package/addon/controllers/maintenance/maintenances/index/details/index.js +3 -0
  133. package/addon/controllers/maintenance/maintenances/index/details.js +54 -0
  134. package/addon/controllers/maintenance/maintenances/index/edit.js +68 -0
  135. package/addon/controllers/maintenance/maintenances/index/new.js +34 -0
  136. package/addon/controllers/maintenance/maintenances/index.js +191 -0
  137. package/addon/controllers/maintenance/parts/index/details/index.js +0 -1
  138. package/addon/controllers/maintenance/parts/index/details.js +36 -1
  139. package/addon/controllers/maintenance/parts/index/edit.js +56 -1
  140. package/addon/controllers/maintenance/parts/index/new.js +32 -1
  141. package/addon/controllers/maintenance/parts/index.js +135 -113
  142. package/addon/controllers/maintenance/schedules/index/details.js +115 -0
  143. package/addon/controllers/maintenance/schedules/index/edit.js +41 -0
  144. package/addon/controllers/maintenance/schedules/index/new.js +33 -0
  145. package/addon/controllers/maintenance/schedules/index.js +280 -0
  146. package/addon/controllers/maintenance/work-orders/index/details.js +41 -1
  147. package/addon/controllers/maintenance/work-orders/index/edit.js +67 -1
  148. package/addon/controllers/maintenance/work-orders/index/new.js +43 -1
  149. package/addon/controllers/maintenance/work-orders/index.js +105 -113
  150. package/addon/controllers/management/drivers/index/details.js +6 -1
  151. package/addon/controllers/management/vehicles/index/details.js +65 -0
  152. package/addon/controllers/management/vehicles/index.js +18 -0
  153. package/addon/controllers/operations/orchestrator.js +10 -0
  154. package/addon/controllers/operations/orders/index/details.js +5 -0
  155. package/addon/controllers/operations/orders/index.js +6 -0
  156. package/addon/controllers/operations/scheduler/fleet-schedule.js +341 -0
  157. package/addon/controllers/operations/scheduler/index.js +799 -275
  158. package/addon/controllers/operations/scheduler.js +21 -0
  159. package/addon/controllers/settings/orchestrator.js +70 -0
  160. package/addon/controllers/settings/scheduling.js +155 -0
  161. package/addon/extension.js +74 -1
  162. package/addon/instance-initializers/register-vroom-allocation.js +27 -0
  163. package/addon/models/maintenance-schedule.js +61 -0
  164. package/addon/routes/maintenance/equipment/index/details.js +27 -1
  165. package/addon/routes/maintenance/equipment/index/edit.js +27 -1
  166. package/addon/routes/maintenance/maintenances/index/details/index.js +3 -0
  167. package/addon/routes/maintenance/maintenances/index/details.js +29 -0
  168. package/addon/routes/maintenance/maintenances/index/edit.js +29 -0
  169. package/addon/routes/maintenance/maintenances/index/new.js +3 -0
  170. package/addon/routes/maintenance/maintenances/index.js +23 -0
  171. package/addon/routes/maintenance/maintenances.js +3 -0
  172. package/addon/routes/maintenance/parts/index/details.js +27 -1
  173. package/addon/routes/maintenance/parts/index/edit.js +27 -1
  174. package/addon/routes/maintenance/schedules/index/details/index.js +2 -0
  175. package/addon/routes/maintenance/schedules/index/details/work-orders.js +11 -0
  176. package/addon/routes/maintenance/schedules/index/details.js +25 -0
  177. package/addon/routes/maintenance/schedules/index/edit.js +25 -0
  178. package/addon/routes/maintenance/schedules/index/new.js +2 -0
  179. package/addon/routes/maintenance/schedules/index.js +21 -0
  180. package/addon/routes/maintenance/schedules.js +2 -0
  181. package/addon/routes/maintenance/work-orders/index/details.js +27 -1
  182. package/addon/routes/maintenance/work-orders/index/edit.js +27 -1
  183. package/addon/routes/management/vehicles/index/details/maintenance-history.js +3 -0
  184. package/addon/routes/management/vehicles/index/details/schedules.js +3 -0
  185. package/addon/routes/management/vehicles/index/details/work-orders.js +3 -0
  186. package/addon/routes/operations/orchestrator.js +23 -0
  187. package/addon/routes/operations/orders/index/details/virtual.js +23 -0
  188. package/addon/routes/operations/scheduler/fleet-schedule.js +28 -0
  189. package/addon/routes/operations/scheduler/index.js +48 -26
  190. package/addon/routes/operations/scheduler.js +14 -1
  191. package/addon/routes/settings/orchestrator.js +27 -0
  192. package/addon/routes/settings/scheduling.js +3 -0
  193. package/addon/routes.js +33 -1
  194. package/addon/services/driver-actions.js +40 -7
  195. package/addon/services/driver-scheduling.js +4 -1
  196. package/addon/services/equipment-actions.js +15 -5
  197. package/addon/services/leaflet-map-manager.js +14 -6
  198. package/addon/services/maintenance-actions.js +17 -14
  199. package/addon/services/maintenance-schedule-actions.js +118 -0
  200. package/addon/services/orchestration-engine-interface.js +49 -0
  201. package/addon/services/orchestration-engine.js +74 -0
  202. package/addon/services/order-actions.js +15 -0
  203. package/addon/services/order-allocation.js +116 -0
  204. package/addon/services/part-actions.js +12 -2
  205. package/addon/services/scheduling.js +316 -0
  206. package/addon/services/vehicle-actions.js +70 -7
  207. package/addon/services/vroom-allocation-engine.js +45 -0
  208. package/addon/services/work-order-actions.js +80 -0
  209. package/addon/styles/fleetops-engine.css +1658 -0
  210. package/addon/templates/analytics/reports/index/edit.hbs +1 -1
  211. package/addon/templates/analytics/reports/index/new.hbs +1 -1
  212. package/addon/templates/application.hbs +6 -1
  213. package/addon/templates/connectivity/devices/index/details/events.hbs +0 -1
  214. package/addon/templates/connectivity/devices.hbs +0 -1
  215. package/addon/templates/connectivity/events/index/details.hbs +0 -1
  216. package/addon/templates/connectivity/events.hbs +0 -1
  217. package/addon/templates/connectivity/sensors.hbs +0 -1
  218. package/addon/templates/connectivity/telematics/index/details/devices.hbs +0 -1
  219. package/addon/templates/connectivity/telematics/index/details/events.hbs +0 -1
  220. package/addon/templates/connectivity/telematics/index/details/sensors.hbs +0 -1
  221. package/addon/templates/connectivity/telematics.hbs +0 -1
  222. package/addon/templates/connectivity/tracking.hbs +0 -1
  223. package/addon/templates/connectivity.hbs +0 -1
  224. package/addon/templates/maintenance/equipment/index/details/index.hbs +1 -2
  225. package/addon/templates/maintenance/equipment/index/details.hbs +15 -2
  226. package/addon/templates/maintenance/equipment/index/edit.hbs +12 -2
  227. package/addon/templates/maintenance/equipment/index/new.hbs +1 -2
  228. package/addon/templates/maintenance/equipment/index.hbs +48 -13
  229. package/addon/templates/maintenance/equipment.hbs +0 -1
  230. package/addon/templates/maintenance/maintenances/index/details/index.hbs +1 -0
  231. package/addon/templates/maintenance/maintenances/index/details.hbs +15 -0
  232. package/addon/templates/maintenance/maintenances/index/edit.hbs +12 -0
  233. package/addon/templates/maintenance/maintenances/index/new.hbs +11 -0
  234. package/addon/templates/maintenance/maintenances/index.hbs +14 -0
  235. package/addon/templates/maintenance/maintenances.hbs +1 -0
  236. package/addon/templates/maintenance/parts/index/details/index.hbs +1 -2
  237. package/addon/templates/maintenance/parts/index/details.hbs +15 -2
  238. package/addon/templates/maintenance/parts/index/edit.hbs +12 -2
  239. package/addon/templates/maintenance/parts/index/new.hbs +1 -2
  240. package/addon/templates/maintenance/parts/index.hbs +48 -13
  241. package/addon/templates/maintenance/parts.hbs +0 -1
  242. package/addon/templates/maintenance/schedules/index/details/index.hbs +1 -0
  243. package/addon/templates/maintenance/schedules/index/details/work-orders.hbs +39 -0
  244. package/addon/templates/maintenance/schedules/index/details.hbs +14 -0
  245. package/addon/templates/maintenance/schedules/index/edit.hbs +12 -0
  246. package/addon/templates/maintenance/schedules/index/new.hbs +11 -0
  247. package/addon/templates/maintenance/schedules/index.hbs +40 -0
  248. package/addon/templates/maintenance/schedules.hbs +1 -0
  249. package/addon/templates/maintenance/work-orders/index/details.hbs +2 -1
  250. package/addon/templates/maintenance/work-orders/index/edit.hbs +2 -4
  251. package/addon/templates/maintenance/work-orders/index/new.hbs +1 -2
  252. package/addon/templates/maintenance/work-orders.hbs +0 -1
  253. package/addon/templates/management/contacts/customers/edit.hbs +1 -2
  254. package/addon/templates/management/contacts/customers/new.hbs +1 -2
  255. package/addon/templates/management/contacts/customers.hbs +1 -1
  256. package/addon/templates/management/contacts/index/edit.hbs +1 -2
  257. package/addon/templates/management/contacts/index/new.hbs +1 -2
  258. package/addon/templates/management/drivers/index/details/orders.hbs +0 -1
  259. package/addon/templates/management/drivers/index/edit.hbs +1 -2
  260. package/addon/templates/management/drivers/index/new.hbs +1 -2
  261. package/addon/templates/management/fleets/index/edit.hbs +1 -2
  262. package/addon/templates/management/fleets/index/new.hbs +1 -2
  263. package/addon/templates/management/fleets/index.hbs +1 -2
  264. package/addon/templates/management/fuel-reports/index/edit.hbs +1 -2
  265. package/addon/templates/management/fuel-reports/index/new.hbs +1 -2
  266. package/addon/templates/management/fuel-reports/index.hbs +1 -2
  267. package/addon/templates/management/issues/index/edit.hbs +1 -2
  268. package/addon/templates/management/issues/index/new.hbs +1 -2
  269. package/addon/templates/management/issues/index.hbs +1 -2
  270. package/addon/templates/management/places/index/details/activity.hbs +0 -1
  271. package/addon/templates/management/places/index/details/comments.hbs +0 -1
  272. package/addon/templates/management/places/index/details/documents.hbs +0 -1
  273. package/addon/templates/management/places/index/details/map.hbs +0 -1
  274. package/addon/templates/management/places/index/details/operations.hbs +0 -1
  275. package/addon/templates/management/places/index/details/performance.hbs +0 -1
  276. package/addon/templates/management/places/index/details/rules.hbs +0 -1
  277. package/addon/templates/management/vehicles/index/details/equipment.hbs +0 -1
  278. package/addon/templates/management/vehicles/index/details/maintenance-history.hbs +2 -0
  279. package/addon/templates/management/vehicles/index/details/schedules.hbs +2 -0
  280. package/addon/templates/management/vehicles/index/details/work-orders.hbs +2 -0
  281. package/addon/templates/management/vehicles/index/details.hbs +1 -1
  282. package/addon/templates/management/vehicles/index/edit.hbs +1 -2
  283. package/addon/templates/management/vehicles/index/new.hbs +1 -2
  284. package/addon/templates/management/vendors/index/edit.hbs +1 -2
  285. package/addon/templates/management/vendors/index/new.hbs +1 -2
  286. package/addon/templates/management/vendors/index.hbs +1 -2
  287. package/addon/templates/management/vendors/integrated.hbs +1 -2
  288. package/addon/templates/operations/orchestrator.hbs +1 -0
  289. package/addon/templates/operations/orders/index/details/virtual.hbs +1 -0
  290. package/addon/templates/operations/orders/index.hbs +6 -1
  291. package/addon/templates/operations/scheduler/fleet-schedule.hbs +41 -0
  292. package/addon/templates/operations/scheduler/index.hbs +147 -88
  293. package/addon/templates/operations/scheduler.hbs +7 -1
  294. package/addon/templates/settings/avatars.hbs +1 -1
  295. package/addon/templates/settings/orchestrator.hbs +65 -0
  296. package/addon/templates/settings/payments/index.hbs +1 -5
  297. package/addon/templates/settings/scheduling.hbs +82 -0
  298. package/addon/utils/create-full-calendar-event-from-order.js +52 -14
  299. package/addon/utils/create-full-calendar-event-from-schedule-item.js +50 -0
  300. package/addon/utils/fleet-ops-options.js +254 -0
  301. package/addon/utils/route-colors.js +99 -0
  302. package/addon/utils/to-calendar-date.js +70 -0
  303. package/app/components/driver/schedule.js +1 -0
  304. package/app/components/maintenance/cost-panel.js +1 -0
  305. package/app/components/maintenance/panel-header.js +1 -0
  306. package/app/components/maintenance-schedule/details.js +1 -0
  307. package/app/components/maintenance-schedule/form.js +1 -0
  308. package/app/components/modals/add-driver-shift.js +1 -0
  309. package/app/components/modals/bulk-assign-orders.js +1 -0
  310. package/app/components/modals/driver-shift.js +1 -0
  311. package/app/components/modals/orchestrator-import.js +1 -0
  312. package/app/components/modals/scheduling-conflict.js +1 -0
  313. package/app/components/modals/send-work-order.js +1 -0
  314. package/app/components/modals/set-driver-availability.js +1 -0
  315. package/app/components/orchestrator/card-fields-settings.js +1 -0
  316. package/app/components/orchestrator/order-pool.js +1 -0
  317. package/app/components/orchestrator/phase-builder.js +1 -0
  318. package/app/components/orchestrator/plan-viewer.js +1 -0
  319. package/app/components/orchestrator/resource-panel.js +1 -0
  320. package/app/components/orchestrator-workbench.js +1 -0
  321. package/app/components/vehicle/details/maintenance-history.js +1 -0
  322. package/app/components/vehicle/details/schedules.js +1 -0
  323. package/app/components/vehicle/details/work-orders.js +1 -0
  324. package/app/controllers/operations/orchestrator.js +1 -0
  325. package/app/controllers/settings/orchestrator.js +1 -0
  326. package/app/controllers/settings/scheduling.js +1 -0
  327. package/app/routes/operations/orchestrator.js +1 -0
  328. package/app/routes/operations/orders/index/details/virtual.js +1 -0
  329. package/app/routes/settings/orchestrator.js +1 -0
  330. package/app/routes/settings/scheduling.js +1 -0
  331. package/app/services/maintenance-schedule-actions.js +1 -0
  332. package/app/services/orchestration-engine-interface.js +1 -0
  333. package/app/services/orchestration-engine.js +1 -0
  334. package/app/services/order-allocation.js +1 -0
  335. package/app/services/scheduling.js +1 -0
  336. package/app/services/vroom-allocation-engine.js +1 -0
  337. package/app/templates/operations/orders/index/details/virtual.js +1 -0
  338. package/app/templates/settings/scheduling.js +1 -0
  339. package/app/utils/create-full-calendar-event-from-schedule-item.js +1 -0
  340. package/app/utils/route-colors.js +1 -0
  341. package/composer.json +5 -3
  342. package/extension.json +1 -1
  343. package/package.json +6 -5
  344. package/server/config/fleetops.php +20 -1
  345. package/server/migrations/2025_08_28_054927_create_parts_table.php +2 -2
  346. package/server/migrations/2025_08_28_054932_add_public_id_to_maintenance_tables.php +45 -0
  347. package/server/migrations/2025_09_01_000001_create_maintenance_schedules_table.php +88 -0
  348. package/server/migrations/2026_04_01_000001_fix_monetary_columns_in_parts_table.php +48 -0
  349. package/server/migrations/2026_04_01_000003_add_photo_uuid_to_equipment_and_parts_tables.php +61 -0
  350. package/server/migrations/2026_04_01_000004_add_public_id_to_equipments_table.php +38 -0
  351. package/server/migrations/2026_04_01_000005_add_missing_columns_to_parts_table.php +67 -0
  352. package/server/migrations/2026_04_04_000001_add_reminder_offsets_to_maintenance_schedules.php +44 -0
  353. package/server/migrations/2026_04_08_000001_add_orchestrator_columns_to_vehicles_table.php +58 -0
  354. package/server/migrations/2026_04_08_000002_add_orchestrator_columns_to_drivers_table.php +41 -0
  355. package/server/migrations/2026_04_08_000003_add_orchestrator_columns_to_orders_table.php +38 -0
  356. package/server/migrations/2026_04_08_000004_add_orchestrator_columns_to_payloads_table.php +41 -0
  357. package/server/migrations/2026_04_08_000005_add_orchestrator_columns_to_waypoints_table.php +38 -0
  358. package/server/migrations/2026_04_09_000001_create_manifests_table.php +48 -0
  359. package/server/migrations/2026_04_09_000002_create_manifest_stops_table.php +48 -0
  360. package/server/migrations/2026_04_09_000003_add_manifest_uuid_to_orders_table.php +28 -0
  361. package/server/migrations/2026_04_13_000001_add_pod_notes_columns_to_waypoints_table.php +39 -0
  362. package/server/resources/views/mail/maintenance-schedule-reminder.blade.php +59 -0
  363. package/server/resources/views/mail/work-order-dispatched.blade.php +67 -0
  364. package/server/src/Auth/Schemas/FleetOps.php +44 -0
  365. package/server/src/Console/Commands/ProcessMaintenanceTriggers.php +150 -0
  366. package/server/src/Console/Commands/SendMaintenanceReminders.php +128 -0
  367. package/server/src/Http/Controllers/Internal/v1/DriverController.php +1 -0
  368. package/server/src/Http/Controllers/Internal/v1/EquipmentController.php +27 -0
  369. package/server/src/Http/Controllers/Internal/v1/LiveController.php +9 -2
  370. package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +165 -0
  371. package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +304 -0
  372. package/server/src/Http/Controllers/Internal/v1/ManifestController.php +138 -0
  373. package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +975 -0
  374. package/server/src/Http/Controllers/Internal/v1/OrderController.php +42 -0
  375. package/server/src/Http/Controllers/Internal/v1/PartController.php +27 -0
  376. package/server/src/Http/Controllers/Internal/v1/SettingController.php +118 -0
  377. package/server/src/Http/Controllers/Internal/v1/Traits/DriverSchedulingTrait.php +214 -0
  378. package/server/src/Http/Controllers/Internal/v1/WorkOrderController.php +68 -0
  379. package/server/src/Http/Resources/v1/Driver.php +1 -0
  380. package/server/src/Http/Resources/v1/Maintenance.php +138 -0
  381. package/server/src/Http/Resources/v1/MaintenanceSchedule.php +137 -0
  382. package/server/src/Http/Resources/v1/Orchestrator/Order.php +116 -0
  383. package/server/src/Http/Resources/v1/Order.php +7 -4
  384. package/server/src/Http/Resources/v1/Waypoint.php +7 -0
  385. package/server/src/Http/Resources/v1/WorkOrder.php +136 -0
  386. package/server/src/Imports/EquipmentImport.php +32 -0
  387. package/server/src/Imports/MaintenanceImport.php +32 -0
  388. package/server/src/Imports/MaintenanceScheduleImport.php +32 -0
  389. package/server/src/Imports/PartImport.php +32 -0
  390. package/server/src/Imports/WorkOrderImport.php +32 -0
  391. package/server/src/Jobs/ProcessAllocationJob.php +119 -0
  392. package/server/src/Listeners/HandleDeliveryCompletion.php +47 -0
  393. package/server/src/Listeners/NotifyDriverOnShiftChange.php +63 -0
  394. package/server/src/Mail/MaintenanceScheduleReminder.php +68 -0
  395. package/server/src/Mail/WorkOrderDispatched.php +58 -0
  396. package/server/src/Models/Asset.php +2 -2
  397. package/server/src/Models/Device.php +1 -1
  398. package/server/src/Models/Driver.php +82 -4
  399. package/server/src/Models/Equipment.php +62 -2
  400. package/server/src/Models/Maintenance.php +127 -9
  401. package/server/src/Models/MaintenanceSchedule.php +353 -0
  402. package/server/src/Models/Manifest.php +214 -0
  403. package/server/src/Models/ManifestStop.php +162 -0
  404. package/server/src/Models/Order.php +70 -0
  405. package/server/src/Models/OrderConfig.php +5 -2
  406. package/server/src/Models/Part.php +69 -3
  407. package/server/src/Models/Payload.php +7 -2
  408. package/server/src/Models/Place.php +1 -1
  409. package/server/src/Models/Sensor.php +1 -1
  410. package/server/src/Models/ServiceQuote.php +1 -1
  411. package/server/src/Models/Vehicle.php +20 -1
  412. package/server/src/Models/Warranty.php +1 -1
  413. package/server/src/Models/Waypoint.php +7 -1
  414. package/server/src/Models/WorkOrder.php +122 -12
  415. package/server/src/Notifications/DriverShiftChanged.php +110 -0
  416. package/server/src/Observers/WorkOrderObserver.php +107 -0
  417. package/server/src/Orchestration/Contracts/OrchestrationEngineInterface.php +63 -0
  418. package/server/src/Orchestration/Engines/DriverAssignmentEngine.php +265 -0
  419. package/server/src/Orchestration/Engines/GreedyOrchestrationEngine.php +155 -0
  420. package/server/src/Orchestration/Engines/RouteSequencingEngine.php +272 -0
  421. package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +192 -0
  422. package/server/src/Orchestration/OrchestrationEngineRegistry.php +83 -0
  423. package/server/src/Orchestration/Support/OrchestrationPayloadBuilder.php +290 -0
  424. package/server/src/Providers/EventServiceProvider.php +7 -1
  425. package/server/src/Providers/FleetOpsServiceProvider.php +42 -15
  426. package/server/src/routes.php +65 -4
  427. package/translations/ar-ae.yml +44 -12
  428. package/translations/bg-bg.yaml +51 -10
  429. package/translations/en-us.yaml +444 -1
  430. package/translations/fr-fr.yaml +51 -10
  431. package/translations/mn-mn.yaml +51 -10
  432. package/translations/pt-br.yaml +51 -10
  433. package/translations/ru-ru.yaml +51 -10
  434. package/translations/vi-vn.yaml +48 -12
@@ -0,0 +1,82 @@
1
+ <Layout::Section::Header @title={{t "settings.scheduling.scheduling-settings"}}>
2
+ <Button
3
+ @type="primary"
4
+ @size="sm"
5
+ @icon="save"
6
+ @text={{t "common.save-changes"}}
7
+ @onClick={{perform this.saveSettings}}
8
+ @disabled={{this.saveSettings.isRunning}}
9
+ @isLoading={{or this.saveSettings.isRunning this.getSettings.isRunning}}
10
+ />
11
+ </Layout::Section::Header>
12
+ <Layout::Section::Body class="overflow-y-scroll h-full">
13
+ <div class="container mx-auto h-screen">
14
+ <div class="max-w-3xl my-10 mx-auto space-y-6">
15
+
16
+ {{! ── Materialisation ──────────────────────────────────────────── }}
17
+ <ContentPanel @title={{t "settings.scheduling.materialisation"}} @open={{true}} @wrapperClass="bordered-classic">
18
+ <InputGroup @name={{t "settings.scheduling.horizon-days"}} @helpText={{t "settings.scheduling.horizon-days-help"}}>
19
+ <Input @type="number" @value={{this.horizonDays}} min="7" max="365" class="w-full form-input" />
20
+ </InputGroup>
21
+ <InputGroup @name={{t "settings.scheduling.default-shift-duration"}} @helpText={{t "settings.scheduling.default-shift-duration-help"}}>
22
+ <Input @type="number" @value={{this.defaultShiftDuration}} min="1" max="24" class="w-full form-input" />
23
+ </InputGroup>
24
+ </ContentPanel>
25
+
26
+ {{! ── Hours of Service Limits ──────────────────────────────────── }}
27
+ <ContentPanel @title={{t "settings.scheduling.hours-of-service"}} @open={{true}} @wrapperClass="bordered-classic">
28
+ <InputGroup @name={{t "settings.scheduling.hos-daily-limit"}} @helpText={{t "settings.scheduling.hos-daily-limit-help"}}>
29
+ <Input @type="number" @value={{this.hosDailyLimit}} min="1" max="24" class="w-full form-input" />
30
+ </InputGroup>
31
+ <InputGroup @name={{t "settings.scheduling.hos-weekly-limit"}} @helpText={{t "settings.scheduling.hos-weekly-limit-help"}}>
32
+ <Input @type="number" @value={{this.hosWeeklyLimit}} min="1" max="168" class="w-full form-input" />
33
+ </InputGroup>
34
+ </ContentPanel>
35
+
36
+ {{! ── Behaviour ────────────────────────────────────────────────── }}
37
+ <ContentPanel @title={{t "settings.scheduling.behaviour"}} @open={{true}} @wrapperClass="bordered-classic">
38
+ <InputGroup @name={{t "settings.scheduling.auto-activate-schedule"}} @helpText={{t "settings.scheduling.auto-activate-schedule-help"}}>
39
+ <Toggle @isToggled={{this.autoActivateSchedule}} @onToggle={{fn (mut this.autoActivateSchedule)}} />
40
+ </InputGroup>
41
+ <InputGroup @name={{t "settings.scheduling.notify-drivers"}} @helpText={{t "settings.scheduling.notify-drivers-help"}}>
42
+ <Toggle @isToggled={{this.notifyDriversOnShiftChange}} @onToggle={{fn (mut this.notifyDriversOnShiftChange)}} />
43
+ </InputGroup>
44
+ </ContentPanel>
45
+
46
+ {{! ── Reusable Schedule Templates ─────────────────────────────── }}
47
+ <ContentPanel @title={{t "settings.scheduling.schedule-templates"}} @open={{true}} @wrapperClass="bordered-classic" @actionButtons={{this.templateActionButtons}}>
48
+ {{#if this.loadTemplates.isRunning}}
49
+ <div class="flex items-center justify-center py-8">
50
+ <Spinner />
51
+ </div>
52
+ {{else if this.scheduleTemplates.length}}
53
+ <div class="divide-y divide-gray-100 dark:divide-gray-700">
54
+ {{#each this.scheduleTemplates as |template|}}
55
+ <div class="flex items-center justify-between py-2.5 px-1">
56
+ <div class="flex items-center gap-2 min-w-0">
57
+ <span class="inline-block w-2.5 h-2.5 rounded-full flex-shrink-0" style={{html-safe (concat "background-color: " template.color)}}></span>
58
+ <span class="text-sm font-medium text-gray-800 dark:text-gray-100 truncate">
59
+ {{template.name}}
60
+ </span>
61
+ <span class="text-xs text-gray-400 dark:text-gray-500 truncate hidden sm:block">
62
+ {{template.rrule}}
63
+ </span>
64
+ </div>
65
+ <div class="flex items-center gap-1 flex-shrink-0">
66
+ <Button @icon="pencil" @size="xs" @type="link" @onClick={{fn this.editTemplate template}} />
67
+ <Button @icon="trash" @size="xs" @type="link" class="text-red-400 hover:text-red-600" @onClick={{fn this.deleteTemplate template}} />
68
+ </div>
69
+ </div>
70
+ {{/each}}
71
+ </div>
72
+ {{else}}
73
+ <div class="text-center py-8 text-sm text-gray-400 dark:text-gray-500">
74
+ {{t "settings.scheduling.no-templates"}}
75
+ </div>
76
+ {{/if}}
77
+ </ContentPanel>
78
+
79
+ </div>
80
+ </div>
81
+ <Spacer @height="600px" />
82
+ </Layout::Section::Body>
@@ -1,4 +1,15 @@
1
1
  import { get } from '@ember/object';
2
+ import { addMinutes } from 'date-fns';
3
+ import toCalendarDate from './to-calendar-date';
4
+
5
+ const STATUS_COLORS = {
6
+ created: '#6366f1',
7
+ dispatched: '#3b82f6',
8
+ active: '#22c55e',
9
+ completed: '#9ca3af',
10
+ cancelled: '#ef4444',
11
+ expired: '#f97316',
12
+ };
2
13
 
3
14
  export function createOrderEventTitle(order) {
4
15
  return get(order, 'tracking') ?? get(order, 'public_id');
@@ -9,28 +20,55 @@ export function createOrderEventDescription(order) {
9
20
  const driverAssignedName = get(order, 'driver_assigned.name');
10
21
  const vehicleAssignedName = get(order, 'driver_assigned.vehicle_name');
11
22
  const destination = get(order, 'pickupName');
12
-
13
- let title = [];
23
+ const parts = [];
14
24
  if (driverAssignedName) {
15
- title.push(`${driverAssignedName} @ ${scheduledAtTime}`);
16
- title.push(vehicleAssignedName);
17
- title.push(`to ${destination}`);
25
+ parts.push(driverAssignedName + ' @ ' + scheduledAtTime);
26
+ if (vehicleAssignedName) parts.push(vehicleAssignedName);
27
+ if (destination) parts.push('to ' + destination);
18
28
  } else {
19
- title.push(`${scheduledAtTime} to ${destination}`);
20
- title.push(vehicleAssignedName);
29
+ if (scheduledAtTime) parts.push(scheduledAtTime);
30
+ if (destination) parts.push('to ' + destination);
31
+ if (vehicleAssignedName) parts.push(vehicleAssignedName);
21
32
  }
22
-
23
- return title.filter(Boolean).join('\n');
33
+ return parts.filter(Boolean).join('\n');
24
34
  }
25
35
 
26
- export default function createFullCalendarEventFromOrder(order) {
36
+ /**
37
+ * Converts an order Ember Data record into a calendar event object for
38
+ * @event-calendar/core.
39
+ *
40
+ * @event-calendar/core has no timezone support — it reads the browser-local
41
+ * fields of any Date passed as `start`/`end` and positions the event at that
42
+ * wall-clock time. To display events in the company timezone regardless of
43
+ * the browser's timezone, we pass a "fake local" Date whose local fields equal
44
+ * the company wall-clock time. See `to-calendar-date.js` for details.
45
+ *
46
+ * @param {Model} order Ember Data `order` record.
47
+ * @param {string} timezone IANA timezone string for the company, e.g. 'Asia/Singapore'.
48
+ * @returns {Object} Calendar event object.
49
+ */
50
+ export default function createFullCalendarEventFromOrder(order, timezone) {
51
+ const scheduledAt = get(order, 'scheduled_at');
52
+ const duration = get(order, 'estimated_duration') ?? 60;
53
+
54
+ // Convert the UTC scheduled_at to a wall-clock Date in the company timezone.
55
+ const startUtc = scheduledAt ? new Date(scheduledAt) : null;
56
+ const start = startUtc ? toCalendarDate(startUtc, timezone) : null;
57
+ const end = start ? toCalendarDate(addMinutes(startUtc, duration), timezone) : null;
58
+
59
+ const status = get(order, 'status') ?? 'created';
60
+ const color = STATUS_COLORS[status] ?? STATUS_COLORS.created;
27
61
  return {
28
- id: order.id,
62
+ id: get(order, 'id'),
63
+ resourceId: get(order, 'driver_assigned_uuid') ?? null,
29
64
  title: createOrderEventTitle(order),
30
65
  description: createOrderEventDescription(order),
31
- start: order.scheduled_at,
32
- displayEventTime: false,
33
- allDay: true,
66
+ start,
67
+ end,
34
68
  display: 'block',
69
+ backgroundColor: color,
70
+ borderColor: color,
71
+ textColor: '#ffffff',
72
+ extendedProps: { order, status, type: 'order' },
35
73
  };
36
74
  }
@@ -0,0 +1,50 @@
1
+ import { get } from '@ember/object';
2
+ import toCalendarDate from './to-calendar-date';
3
+
4
+ /**
5
+ * createFullCalendarEventFromScheduleItem
6
+ *
7
+ * Transforms a ScheduleItem Ember Data record into a calendar event object for
8
+ * @event-calendar/core. Used to render driver shift windows as background
9
+ * blocks on the resource timeline.
10
+ *
11
+ * @event-calendar/core has no timezone support — it reads the browser-local
12
+ * fields of any Date passed as `start`/`end` and positions the event at that
13
+ * wall-clock time. To display shifts in the company timezone regardless of
14
+ * the browser's timezone, we pass a "fake local" Date whose local fields equal
15
+ * the company wall-clock time. See `to-calendar-date.js` for details.
16
+ *
17
+ * @param {Model} scheduleItem A `schedule-item` Ember Data record.
18
+ * @param {Model} driver The associated `driver` Ember Data record.
19
+ * @param {string} timezone IANA timezone string for the company, e.g. 'Asia/Singapore'.
20
+ * @param {Object} [overrides] Optional calendar property overrides (e.g. display, backgroundColor).
21
+ * @returns {Object} Calendar event object.
22
+ */
23
+ export default function createFullCalendarEventFromScheduleItem(scheduleItem, driver, timezone, overrides = {}) {
24
+ const driverName = get(driver, 'name') ?? 'Unknown Driver';
25
+ const title = get(scheduleItem, 'title') || `${driverName} — Shift`;
26
+
27
+ const startRaw = get(scheduleItem, 'start_at');
28
+ const endRaw = get(scheduleItem, 'end_at');
29
+
30
+ // Convert the UTC start/end to wall-clock Dates in the company timezone.
31
+ const start = startRaw ? toCalendarDate(new Date(startRaw), timezone) : null;
32
+ const end = endRaw ? toCalendarDate(new Date(endRaw), timezone) : null;
33
+
34
+ return {
35
+ id: `shift-${get(scheduleItem, 'id')}`,
36
+ resourceId: get(scheduleItem, 'assignee_uuid') ?? get(driver, 'id'),
37
+ title,
38
+ start,
39
+ end,
40
+ display: 'background',
41
+ backgroundColor: 'rgba(99, 102, 241, 0.08)',
42
+ borderColor: 'rgba(99, 102, 241, 0.25)',
43
+ extendedProps: {
44
+ scheduleItem,
45
+ driver,
46
+ type: 'shift',
47
+ },
48
+ ...overrides,
49
+ };
50
+ }
@@ -569,6 +569,247 @@ export const transmissionTypes = [
569
569
 
570
570
  export const odometerUnits = [...distanceUnits, { label: 'Hours', value: 'hours', description: 'Unit of time measurement, commonly used worldwide.' }];
571
571
 
572
+ // ─── Orchestrator: Driver & Vehicle Skills ────────────────────────────────────
573
+
574
+ /**
575
+ * driverSkills
576
+ * Comprehensive list of driver qualifications and certifications used by the
577
+ * Orchestrator to match orders to appropriately skilled drivers.
578
+ */
579
+ export const driverSkills = [
580
+ // Licences & Certifications
581
+ { label: 'Car Licence (B)', value: 'licence_b', description: 'Standard passenger car licence.', group: 'licence' },
582
+ { label: 'Light Truck Licence (C)', value: 'licence_c', description: 'Licence for rigid trucks up to 8 tonnes GVM.', group: 'licence' },
583
+ { label: 'Medium Rigid (MR)', value: 'licence_mr', description: 'Two-axle rigid vehicle up to 8 tonnes.', group: 'licence' },
584
+ { label: 'Heavy Rigid (HR)', value: 'licence_hr', description: 'Three or more axle rigid vehicle.', group: 'licence' },
585
+ { label: 'Heavy Combination (HC)', value: 'licence_hc', description: 'Prime mover + semi-trailer combination.', group: 'licence' },
586
+ { label: 'Multi-Combination (MC)', value: 'licence_mc', description: 'B-double, road train, or multi-trailer combination.', group: 'licence' },
587
+ { label: 'Motorcycle Licence (A)', value: 'licence_a', description: 'Motorcycle or scooter delivery licence.', group: 'licence' },
588
+ { label: 'Forklift Licence', value: 'licence_forklift', description: 'Certified forklift operator.', group: 'licence' },
589
+ { label: 'Dangerous Goods (DG)', value: 'cert_dg', description: 'Certified to transport dangerous goods under ADG/IMDG codes.', group: 'certification' },
590
+ { label: 'Hazmat (US DOT)', value: 'cert_hazmat', description: 'US DOT hazardous materials endorsement.', group: 'certification' },
591
+ { label: 'ADR Hazmat (EU)', value: 'cert_adr', description: 'European ADR dangerous goods driver certificate.', group: 'certification' },
592
+ { label: 'Refrigerated Transport', value: 'cert_reefer', description: 'Trained in cold-chain and temperature-controlled transport.', group: 'certification' },
593
+ { label: 'Oversize / Overweight', value: 'cert_oversize', description: 'Certified for oversize or overweight load operations.', group: 'certification' },
594
+ { label: 'Passenger Transport', value: 'cert_passenger', description: 'Authorised to carry fare-paying passengers.', group: 'certification' },
595
+ { label: 'First Aid', value: 'cert_first_aid', description: 'Current first aid certificate.', group: 'certification' },
596
+ { label: 'Defensive Driving', value: 'cert_defensive_driving', description: 'Advanced defensive driving qualification.', group: 'certification' },
597
+ // Operational Skills
598
+ { label: 'Hand-truck / Trolley', value: 'skill_hand_truck', description: 'Proficient with hand-truck and trolley equipment.', group: 'operational' },
599
+ { label: 'Tail-lift Operation', value: 'skill_tail_lift', description: 'Trained to operate hydraulic tail-lift.', group: 'operational' },
600
+ { label: 'Crane / HIAB', value: 'skill_hiab', description: 'Certified to operate truck-mounted crane/HIAB.', group: 'operational' },
601
+ { label: 'Pallet Jack', value: 'skill_pallet_jack', description: 'Manual or electric pallet jack operation.', group: 'operational' },
602
+ { label: 'Pump-out / Tanker', value: 'skill_tanker', description: 'Trained in tanker loading, unloading, and pump operation.', group: 'operational' },
603
+ { label: 'Livestock Handling', value: 'skill_livestock', description: 'Experienced with live animal transport and handling.', group: 'operational' },
604
+ { label: 'Fragile / White Goods', value: 'skill_fragile', description: 'Trained in handling fragile or high-value goods.', group: 'operational' },
605
+ { label: 'Pharmaceutical / Medical', value: 'skill_pharma', description: 'Experienced with pharmaceutical and medical supply chain requirements.', group: 'operational' },
606
+ { label: 'Cash-in-Transit', value: 'skill_cit', description: 'Cleared and trained for cash-in-transit operations.', group: 'operational' },
607
+ { label: 'Contactless / Unattended Delivery', value: 'skill_contactless', description: 'Proficient in unattended or safe-drop delivery procedures.', group: 'operational' },
608
+ // Language & Customer Service
609
+ { label: 'Customer-facing', value: 'skill_customer_facing', description: 'Experienced in direct customer interaction and service.', group: 'soft_skills' },
610
+ { label: 'Multilingual', value: 'skill_multilingual', description: 'Fluent in more than one language.', group: 'soft_skills' },
611
+ { label: 'Digital / App Proficient', value: 'skill_digital', description: 'Comfortable using mobile apps and digital POD tools.', group: 'soft_skills' },
612
+ ];
613
+
614
+ /**
615
+ * vehicleSkills
616
+ * Equipment and capability flags for vehicles used by the Orchestrator to
617
+ * match vehicles to orders requiring specific handling.
618
+ */
619
+ export const vehicleSkills = [
620
+ // Cargo Handling Equipment
621
+ { label: 'Tail Lift', value: 'tail_lift', description: 'Hydraulic tail-lift for loading/unloading without a dock.', group: 'equipment' },
622
+ { label: 'Crane / HIAB', value: 'hiab', description: 'Truck-mounted crane for self-loading.', group: 'equipment' },
623
+ { label: 'Pallet Racking', value: 'pallet_racking', description: 'Internal pallet racking system.', group: 'equipment' },
624
+ { label: 'Roller Bed / Conveyor Floor', value: 'roller_bed', description: 'Motorised roller floor for easy cargo movement.', group: 'equipment' },
625
+ { label: 'Curtainsider', value: 'curtainsider', description: 'Curtain-sided trailer for easy side-loading.', group: 'equipment' },
626
+ { label: 'Flatbed / Step-deck', value: 'flatbed', description: 'Open flatbed or step-deck for oversized loads.', group: 'equipment' },
627
+ { label: 'Refrigerated (Reefer)', value: 'refrigerated', description: 'Active refrigeration unit for cold-chain cargo.', group: 'temperature' },
628
+ { label: 'Chilled (2–8 °C)', value: 'chilled', description: 'Maintains chilled temperature range 2–8 °C.', group: 'temperature' },
629
+ { label: 'Frozen (< −18 °C)', value: 'frozen', description: 'Maintains frozen temperature below −18 °C.', group: 'temperature' },
630
+ { label: 'Ambient / Insulated', value: 'insulated', description: 'Insulated body without active cooling.', group: 'temperature' },
631
+ { label: 'Heated / Warm', value: 'heated', description: 'Heated cargo space for temperature-sensitive goods in cold climates.', group: 'temperature' },
632
+ // Compliance & Certification
633
+ { label: 'Dangerous Goods Approved', value: 'dg_approved', description: 'Vehicle certified and placarded for dangerous goods.', group: 'compliance' },
634
+ { label: 'ADR Certified (EU)', value: 'adr_certified', description: 'European ADR dangerous goods vehicle certification.', group: 'compliance' },
635
+ { label: 'Oversize Permit', value: 'oversize_permit', description: 'Holds current oversize/overweight load permit.', group: 'compliance' },
636
+ { label: 'Food Grade', value: 'food_grade', description: 'Cargo area meets food safety/hygiene standards.', group: 'compliance' },
637
+ { label: 'Pharmaceutical Grade', value: 'pharma_grade', description: 'Meets GDP/pharmaceutical transport standards.', group: 'compliance' },
638
+ { label: 'Cash-in-Transit Secure', value: 'cit_secure', description: 'Armoured or security-fitted for cash-in-transit.', group: 'compliance' },
639
+ // Connectivity & Telematics
640
+ { label: 'GPS / Live Tracking', value: 'gps_tracking', description: 'Fitted with live GPS tracking device.', group: 'telematics' },
641
+ { label: 'Temperature Logger', value: 'temp_logger', description: 'Continuous temperature data logging.', group: 'telematics' },
642
+ { label: 'Dash Camera', value: 'dash_cam', description: 'Forward-facing dash camera installed.', group: 'telematics' },
643
+ { label: 'ELD / Tachograph', value: 'eld', description: 'Electronic logging device or digital tachograph fitted.', group: 'telematics' },
644
+ ];
645
+
646
+ // ─── Orchestrator: Capacity & Dimension Options ───────────────────────────────
647
+
648
+ /**
649
+ * capacityDimensions
650
+ * The multi-dimensional capacity axes supported by the Orchestrator and VROOM.
651
+ * Each dimension can be independently constrained on vehicles and required by orders.
652
+ */
653
+ export const capacityDimensions = [
654
+ { label: 'Weight (kg)', value: 'weight_kg', unit: 'kg', description: 'Gross cargo weight in kilograms.', group: 'weight' },
655
+ { label: 'Weight (lb)', value: 'weight_lb', unit: 'lb', description: 'Gross cargo weight in pounds.', group: 'weight' },
656
+ { label: 'Volume (m³)', value: 'volume_m3', unit: 'm³', description: 'Total cargo volume in cubic metres.', group: 'volume' },
657
+ { label: 'Volume (ft³)', value: 'volume_ft3', unit: 'ft³', description: 'Total cargo volume in cubic feet.', group: 'volume' },
658
+ { label: 'Pallets', value: 'pallets', unit: 'pallets', description: 'Number of standard EUR/AU pallets.', group: 'units' },
659
+ { label: 'Parcels / Packages', value: 'parcels', unit: 'parcels', description: 'Count of individual parcels or packages.', group: 'units' },
660
+ { label: 'Cartons / Cases', value: 'cartons', unit: 'cartons', description: 'Count of cartons or cases.', group: 'units' },
661
+ { label: 'Linear Metres (LDM)', value: 'ldm', unit: 'LDM', description: 'Loading metres — floor space occupied by cargo.', group: 'length' },
662
+ { label: 'Floor Area (m²)', value: 'floor_area_m2', unit: 'm²', description: 'Cargo floor area in square metres.', group: 'length' },
663
+ { label: 'Seats / Passengers', value: 'seats', unit: 'seats', description: 'Passenger seat capacity (for passenger transport).', group: 'units' },
664
+ ];
665
+
666
+ /**
667
+ * vehicleCapacityProfiles
668
+ * Common pre-defined capacity profiles for quick assignment to vehicles.
669
+ */
670
+ export const vehicleCapacityProfiles = [
671
+ { label: 'Motorbike / Scooter', value: 'moto', description: 'Up to 20 kg, 0.05 m³, 5 parcels.', weight_kg: 20, volume_m3: 0.05, parcels: 5 },
672
+ { label: 'Small Car / Hatchback', value: 'small_car', description: 'Up to 150 kg, 0.3 m³, 20 parcels.', weight_kg: 150, volume_m3: 0.3, parcels: 20 },
673
+ { label: 'Cargo Van (Small)', value: 'small_van', description: 'Up to 800 kg, 5 m³, 100 parcels.', weight_kg: 800, volume_m3: 5, parcels: 100 },
674
+ { label: 'Cargo Van (Large)', value: 'large_van', description: 'Up to 1,500 kg, 10 m³, 200 parcels.', weight_kg: 1500, volume_m3: 10, parcels: 200 },
675
+ { label: 'Light Truck (3.5t)', value: 'light_truck', description: 'Up to 3,500 kg, 20 m³, 6 pallets.', weight_kg: 3500, volume_m3: 20, pallets: 6 },
676
+ { label: 'Medium Truck (7.5t)', value: 'medium_truck', description: 'Up to 7,500 kg, 40 m³, 14 pallets.', weight_kg: 7500, volume_m3: 40, pallets: 14 },
677
+ { label: 'Heavy Truck (13.5t)', value: 'heavy_truck', description: 'Up to 13,500 kg, 60 m³, 22 pallets.', weight_kg: 13500, volume_m3: 60, pallets: 22 },
678
+ { label: 'Semi-Trailer (26t)', value: 'semi_trailer', description: 'Up to 26,000 kg, 90 m³, 33 pallets.', weight_kg: 26000, volume_m3: 90, pallets: 33 },
679
+ { label: 'B-Double (42.5t)', value: 'b_double', description: 'Up to 42,500 kg, 150 m³, 54 pallets.', weight_kg: 42500, volume_m3: 150, pallets: 54 },
680
+ { label: 'Refrigerated Van', value: 'reefer_van', description: 'Up to 1,200 kg, 8 m³, cold-chain.', weight_kg: 1200, volume_m3: 8 },
681
+ { label: 'Refrigerated Truck (10t)', value: 'reefer_truck', description: 'Up to 10,000 kg, 50 m³, cold-chain.', weight_kg: 10000, volume_m3: 50 },
682
+ ];
683
+
684
+ // ─── Orchestrator: Order Priority Options ─────────────────────────────────────
685
+
686
+ /**
687
+ * orderPriorityLevels
688
+ * Routing priority levels for orders. Higher priority orders are served first
689
+ * by the optimization engine when time windows conflict.
690
+ */
691
+ export const orderPriorityLevels = [
692
+ { label: 'Critical', value: 100, description: 'Must be served first — SLA breach imminent or emergency delivery.', color: 'red' },
693
+ { label: 'High', value: 75, description: 'High-priority order; serve before standard orders.', color: 'orange' },
694
+ { label: 'Standard', value: 50, description: 'Normal priority — default for all orders.', color: 'blue' },
695
+ { label: 'Low', value: 25, description: 'Low-priority order; serve after higher-priority stops.', color: 'gray' },
696
+ { label: 'Flexible', value: 10, description: 'No strict time requirement; fill remaining capacity.', color: 'green' },
697
+ ];
698
+
699
+ // ─── Orchestrator: Time Window Presets ────────────────────────────────────────
700
+
701
+ /**
702
+ * timeWindowPresets
703
+ * Common delivery/pickup time window presets for quick assignment.
704
+ * Times are stored as HH:MM strings; the backend converts to Unix timestamps.
705
+ */
706
+ export const timeWindowPresets = [
707
+ { label: 'Early Morning (06:00–09:00)', value: 'early_morning', start: '06:00', end: '09:00', description: 'Pre-business-hours delivery window.' },
708
+ { label: 'Morning (08:00–12:00)', value: 'morning', start: '08:00', end: '12:00', description: 'Standard morning delivery window.' },
709
+ { label: 'Midday (11:00–14:00)', value: 'midday', start: '11:00', end: '14:00', description: 'Lunchtime delivery window.' },
710
+ { label: 'Afternoon (12:00–17:00)', value: 'afternoon', start: '12:00', end: '17:00', description: 'Standard afternoon delivery window.' },
711
+ { label: 'Business Hours (09:00–17:00)', value: 'business_hours', start: '09:00', end: '17:00', description: 'Full business-hours window.' },
712
+ { label: 'Evening (17:00–21:00)', value: 'evening', start: '17:00', end: '21:00', description: 'After-hours residential delivery.' },
713
+ { label: 'Night (21:00–06:00)', value: 'night', start: '21:00', end: '06:00', description: 'Overnight or graveyard-shift delivery.' },
714
+ { label: 'AM Only (Before 12:00)', value: 'am_only', start: '00:00', end: '12:00', description: 'Must be delivered before noon.' },
715
+ { label: 'PM Only (After 12:00)', value: 'pm_only', start: '12:00', end: '23:59', description: 'Must be delivered after noon.' },
716
+ { label: 'Anytime', value: 'anytime', start: '00:00', end: '23:59', description: 'No time restriction — deliver any time.' },
717
+ ];
718
+
719
+ // ─── Orchestrator: Route Optimization Constraint Options ──────────────────────
720
+
721
+ /**
722
+ * optimizationObjectives
723
+ * The primary objective the optimization engine should minimise/maximise.
724
+ */
725
+ export const optimizationObjectives = [
726
+ { label: 'Minimize Total Distance', value: 'min_distance', description: 'Produce routes with the shortest total travel distance.', icon: 'route' },
727
+ { label: 'Minimize Total Duration', value: 'min_duration', description: 'Produce routes with the shortest total travel time.', icon: 'clock' },
728
+ { label: 'Minimize Vehicles Used', value: 'min_vehicles', description: 'Use as few vehicles as possible while serving all orders.', icon: 'truck' },
729
+ { label: 'Balance Workload', value: 'balance_workload', description: 'Distribute stops evenly across all available drivers.', icon: 'scale' },
730
+ { label: 'Maximize On-Time Delivery', value: 'max_on_time', description: 'Prioritise serving stops within their time windows.', icon: 'check-circle' },
731
+ { label: 'Minimize Overtime', value: 'min_overtime', description: 'Keep driver working hours within contracted limits.', icon: 'user-clock' },
732
+ { label: 'Minimize Fuel / Emissions', value: 'min_emissions', description: 'Optimize for lowest fuel consumption and CO₂ output.', icon: 'leaf' },
733
+ ];
734
+
735
+ /**
736
+ * routingConstraintOptions
737
+ * Boolean constraint toggles exposed in the Orchestrator settings and run panel.
738
+ */
739
+ export const routingConstraintOptions = [
740
+ { label: 'Respect Time Windows', value: 'respect_time_windows', description: 'Enforce delivery/pickup time windows as hard constraints.' },
741
+ { label: 'Respect Vehicle Capacity', value: 'respect_capacity', description: 'Do not exceed vehicle weight, volume, or unit capacity.' },
742
+ { label: 'Respect Driver Skills', value: 'respect_skills', description: 'Only assign orders to drivers/vehicles with matching skills.' },
743
+ { label: 'Respect Max Tasks per Route', value: 'respect_max_tasks', description: 'Do not exceed the maximum number of stops per vehicle.' },
744
+ { label: 'Respect Max Travel Time', value: 'respect_max_travel_time', description: 'Do not exceed the maximum driving time per vehicle/driver.' },
745
+ { label: 'Respect Max Distance', value: 'respect_max_distance', description: 'Do not exceed the maximum distance per vehicle/driver.' },
746
+ { label: 'Allow Unassigned Orders', value: 'allow_unassigned', description: 'Permit orders to remain unassigned if no feasible vehicle exists.' },
747
+ { label: 'Pickup Before Delivery', value: 'pickup_before_delivery', description: 'Enforce pickup-before-dropoff sequencing for P&D orders.' },
748
+ { label: 'Return to Depot', value: 'return_to_depot', description: 'Require vehicles to return to their start depot after the last stop.' },
749
+ { label: 'Avoid Highways', value: 'avoid_highways', description: 'Route via non-highway roads where possible.' },
750
+ { label: 'Avoid Tolls', value: 'avoid_tolls', description: 'Route to avoid toll roads where possible.' },
751
+ { label: 'Avoid Ferries', value: 'avoid_ferries', description: 'Route to avoid ferry crossings where possible.' },
752
+ ];
753
+
754
+ /**
755
+ * serviceTimePresets
756
+ * Common service time (dwell time) presets for waypoints.
757
+ * Represents the expected time in seconds to complete the task at a stop.
758
+ */
759
+ export const serviceTimePresets = [
760
+ { label: '2 minutes', value: 120, description: 'Quick drop — parcel left at door.' },
761
+ { label: '5 minutes', value: 300, description: 'Standard residential drop-off.' },
762
+ { label: '10 minutes', value: 600, description: 'Standard commercial delivery.' },
763
+ { label: '15 minutes', value: 900, description: 'Delivery with signature/POD.' },
764
+ { label: '20 minutes', value: 1200, description: 'Delivery with unloading assistance.' },
765
+ { label: '30 minutes', value: 1800, description: 'Larger delivery or installation.' },
766
+ { label: '45 minutes', value: 2700, description: 'Complex delivery or site inspection.' },
767
+ { label: '1 hour', value: 3600, description: 'Full hour on-site service.' },
768
+ { label: '2 hours', value: 7200, description: 'Extended on-site service or installation.' },
769
+ ];
770
+
771
+ // ─── Orchestrator: Import Column Mapping Options ──────────────────────────────
772
+
773
+ /**
774
+ * importColumnMappings
775
+ * Canonical column names used by the Orchestrator order import flow.
776
+ * Each entry defines the internal field name, a human-readable label, whether
777
+ * it is required, and example values to guide column mapping.
778
+ */
779
+ export const importColumnMappings = [
780
+ // Order Identity
781
+ { field: 'reference', label: 'Order Reference / ID', required: false, example: 'ORD-001', group: 'order' },
782
+ { field: 'type', label: 'Order Type', required: false, example: 'delivery', group: 'order' },
783
+ { field: 'notes', label: 'Notes / Instructions', required: false, example: 'Leave at back door', group: 'order' },
784
+ { field: 'priority', label: 'Priority', required: false, example: '75', group: 'order' },
785
+ // Pickup
786
+ { field: 'pickup_name', label: 'Pickup Contact Name', required: false, example: 'Warehouse A', group: 'pickup' },
787
+ { field: 'pickup_address', label: 'Pickup Address', required: true, example: '123 Main St, Sydney NSW 2000', group: 'pickup' },
788
+ { field: 'pickup_lat', label: 'Pickup Latitude', required: false, example: '-33.8688', group: 'pickup' },
789
+ { field: 'pickup_lng', label: 'Pickup Longitude', required: false, example: '151.2093', group: 'pickup' },
790
+ { field: 'pickup_time_window_start', label: 'Pickup Window Start', required: false, example: '08:00', group: 'pickup' },
791
+ { field: 'pickup_time_window_end', label: 'Pickup Window End', required: false, example: '12:00', group: 'pickup' },
792
+ { field: 'pickup_service_time', label: 'Pickup Service Time (min)', required: false, example: '10', group: 'pickup' },
793
+ // Dropoff
794
+ { field: 'dropoff_name', label: 'Dropoff Contact Name', required: false, example: 'John Smith', group: 'dropoff' },
795
+ { field: 'dropoff_address', label: 'Dropoff Address', required: true, example: '456 Queen St, Melbourne VIC 3000', group: 'dropoff' },
796
+ { field: 'dropoff_lat', label: 'Dropoff Latitude', required: false, example: '-37.8136', group: 'dropoff' },
797
+ { field: 'dropoff_lng', label: 'Dropoff Longitude', required: false, example: '144.9631', group: 'dropoff' },
798
+ { field: 'dropoff_time_window_start', label: 'Delivery Window Start', required: false, example: '12:00', group: 'dropoff' },
799
+ { field: 'dropoff_time_window_end', label: 'Delivery Window End', required: false, example: '17:00', group: 'dropoff' },
800
+ { field: 'dropoff_service_time', label: 'Delivery Service Time (min)', required: false, example: '5', group: 'dropoff' },
801
+ // Cargo / Capacity
802
+ { field: 'weight_kg', label: 'Weight (kg)', required: false, example: '25', group: 'cargo' },
803
+ { field: 'volume_m3', label: 'Volume (m³)', required: false, example: '0.5', group: 'cargo' },
804
+ { field: 'pallets', label: 'Pallets', required: false, example: '2', group: 'cargo' },
805
+ { field: 'parcels', label: 'Parcels / Packages', required: false, example: '10', group: 'cargo' },
806
+ { field: 'required_skills', label: 'Required Skills (comma-separated)', required: false, example: 'refrigerated,tail_lift', group: 'cargo' },
807
+ // Customer
808
+ { field: 'customer_name', label: 'Customer Name', required: false, example: 'Acme Corp', group: 'customer' },
809
+ { field: 'customer_phone', label: 'Customer Phone', required: false, example: '+61400000000', group: 'customer' },
810
+ { field: 'customer_email', label: 'Customer Email', required: false, example: 'customer@example.com', group: 'customer' },
811
+ ];
812
+
572
813
  export default function fleetOpsOptions(key) {
573
814
  const allOptions = {
574
815
  driverTypes,
@@ -615,6 +856,19 @@ export default function fleetOpsOptions(key) {
615
856
  transmissionTypes,
616
857
  odometerUnits,
617
858
  measurementSystems,
859
+ // Orchestrator options
860
+ driverSkills,
861
+ driverSkillsPowerGroups: toPowerSelectGroups(driverSkills),
862
+ vehicleSkills,
863
+ vehicleSkillsPowerGroups: toPowerSelectGroups(vehicleSkills),
864
+ capacityDimensions,
865
+ vehicleCapacityProfiles,
866
+ orderPriorityLevels,
867
+ timeWindowPresets,
868
+ optimizationObjectives,
869
+ routingConstraintOptions,
870
+ serviceTimePresets,
871
+ importColumnMappings,
618
872
  };
619
873
 
620
874
  return allOptions[key] ?? [];
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Route Colors Utility
3
+ *
4
+ * Provides deterministic color assignment, color manipulation helpers,
5
+ * and status-based route line styling for the FleetOps map visualization.
6
+ */
7
+
8
+ /**
9
+ * The canonical route color palette used across the live map and order detail views.
10
+ * Colors are chosen for high contrast against both light and dark CartoDB tile layers.
11
+ */
12
+ export const ROUTE_COLOR_PALETTE = ['#0EA5E9', '#8B5CF6', '#F59E0B', '#10B981', '#F97316', '#EC4899', '#06B6D4', '#EF4444', '#84CC16', '#6366F1'];
13
+
14
+ /**
15
+ * Darken a hex color by a given integer amount (0-255 per channel).
16
+ *
17
+ * @param {string} hex - A CSS hex color string, e.g. "#0EA5E9"
18
+ * @param {number} amount - How much to subtract from each RGB channel
19
+ * @returns {string} Darkened hex color string
20
+ */
21
+ export function darkenColor(hex, amount = 40) {
22
+ const clean = hex.replace('#', '');
23
+ const num = parseInt(clean, 16);
24
+ const r = Math.max(0, (num >> 16) - amount);
25
+ const g = Math.max(0, ((num >> 8) & 0xff) - amount);
26
+ const b = Math.max(0, (num & 0xff) - amount);
27
+ return `#${[r, g, b].map((v) => v.toString(16).padStart(2, '0')).join('')}`;
28
+ }
29
+
30
+ /**
31
+ * Derive a deterministic color from a string identifier (e.g. order public_id).
32
+ * The same ID will always produce the same color from the palette.
33
+ *
34
+ * @param {string} id - Any string identifier
35
+ * @returns {string} A hex color string from ROUTE_COLOR_PALETTE
36
+ */
37
+ export function colorForId(id = '') {
38
+ let hash = 0;
39
+ for (let i = 0; i < id.length; i++) {
40
+ hash = (hash << 5) - hash + id.charCodeAt(i);
41
+ hash |= 0; // Convert to 32-bit integer
42
+ }
43
+ return ROUTE_COLOR_PALETTE[Math.abs(hash) % ROUTE_COLOR_PALETTE.length];
44
+ }
45
+
46
+ /**
47
+ * Return a Leaflet Path options `styles` array for a route polyline
48
+ * based on the order status. Uses a two-layer "cased" approach:
49
+ * a darker, heavier outline beneath a brighter, thinner main line.
50
+ *
51
+ * @param {string} status - The order status string
52
+ * @param {string} color - The base hex color for this route
53
+ * @returns {Array<Object>} Array of Leaflet Path options objects
54
+ */
55
+ export function routeStyleForStatus(status, color) {
56
+ const darker = darkenColor(color, 40);
57
+
58
+ switch (status) {
59
+ case 'pending':
60
+ return [{ color: '#9CA3AF', weight: 5, opacity: 0.65, dashArray: '8 14', lineCap: 'round', lineJoin: 'round' }];
61
+
62
+ case 'canceled':
63
+ return [{ color: '#EF4444', weight: 4, opacity: 0.4, dashArray: '6 10', lineCap: 'round', lineJoin: 'round' }];
64
+
65
+ case 'completed':
66
+ return [
67
+ { color: darkenColor('#14B8A6', 30), weight: 7, opacity: 0.5, lineCap: 'round', lineJoin: 'round' },
68
+ { color: '#14B8A6', weight: 4, opacity: 0.5, lineCap: 'round', lineJoin: 'round' },
69
+ ];
70
+
71
+ case 'in_progress':
72
+ case 'dispatched':
73
+ default:
74
+ return [
75
+ { color: darker, weight: 9, opacity: 0.9, lineCap: 'round', lineJoin: 'round' },
76
+ { color: color, weight: 5, opacity: 1.0, lineCap: 'round', lineJoin: 'round' },
77
+ ];
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Build a Leaflet `L.divIcon` HTML string for a numbered/typed waypoint marker.
83
+ *
84
+ * @param {string|number} label - The label to display inside the circle (e.g. "P", "D", "2")
85
+ * @param {string} bgColor - CSS background color for the circle
86
+ * @returns {string} HTML string suitable for use in `L.divIcon({ html: ... })`
87
+ */
88
+ export function waypointIconHtml(label, bgColor) {
89
+ return `<div style="
90
+ width:32px;height:32px;
91
+ background:${bgColor};
92
+ border:2.5px solid #fff;
93
+ border-radius:50%;
94
+ display:flex;align-items:center;justify-content:center;
95
+ color:#fff;font-weight:700;font-size:13px;
96
+ box-shadow:0 2px 8px rgba(0,0,0,0.45);
97
+ font-family:ui-sans-serif,system-ui,sans-serif;
98
+ ">${label}</div>`;
99
+ }