@fleetbase/fleetops-engine 0.6.38 → 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 (429) 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.js +6 -0
  155. package/addon/controllers/operations/scheduler/fleet-schedule.js +341 -0
  156. package/addon/controllers/operations/scheduler/index.js +799 -275
  157. package/addon/controllers/operations/scheduler.js +21 -0
  158. package/addon/controllers/settings/orchestrator.js +70 -0
  159. package/addon/controllers/settings/scheduling.js +155 -0
  160. package/addon/extension.js +19 -0
  161. package/addon/instance-initializers/register-vroom-allocation.js +27 -0
  162. package/addon/models/maintenance-schedule.js +61 -0
  163. package/addon/routes/maintenance/equipment/index/details.js +27 -1
  164. package/addon/routes/maintenance/equipment/index/edit.js +27 -1
  165. package/addon/routes/maintenance/maintenances/index/details/index.js +3 -0
  166. package/addon/routes/maintenance/maintenances/index/details.js +29 -0
  167. package/addon/routes/maintenance/maintenances/index/edit.js +29 -0
  168. package/addon/routes/maintenance/maintenances/index/new.js +3 -0
  169. package/addon/routes/maintenance/maintenances/index.js +23 -0
  170. package/addon/routes/maintenance/maintenances.js +3 -0
  171. package/addon/routes/maintenance/parts/index/details.js +27 -1
  172. package/addon/routes/maintenance/parts/index/edit.js +27 -1
  173. package/addon/routes/maintenance/schedules/index/details/index.js +2 -0
  174. package/addon/routes/maintenance/schedules/index/details/work-orders.js +11 -0
  175. package/addon/routes/maintenance/schedules/index/details.js +25 -0
  176. package/addon/routes/maintenance/schedules/index/edit.js +25 -0
  177. package/addon/routes/maintenance/schedules/index/new.js +2 -0
  178. package/addon/routes/maintenance/schedules/index.js +21 -0
  179. package/addon/routes/maintenance/schedules.js +2 -0
  180. package/addon/routes/maintenance/work-orders/index/details.js +27 -1
  181. package/addon/routes/maintenance/work-orders/index/edit.js +27 -1
  182. package/addon/routes/management/vehicles/index/details/maintenance-history.js +3 -0
  183. package/addon/routes/management/vehicles/index/details/schedules.js +3 -0
  184. package/addon/routes/management/vehicles/index/details/work-orders.js +3 -0
  185. package/addon/routes/operations/orchestrator.js +23 -0
  186. package/addon/routes/operations/scheduler/fleet-schedule.js +28 -0
  187. package/addon/routes/operations/scheduler/index.js +48 -26
  188. package/addon/routes/operations/scheduler.js +14 -1
  189. package/addon/routes/settings/orchestrator.js +27 -0
  190. package/addon/routes/settings/scheduling.js +3 -0
  191. package/addon/routes.js +31 -1
  192. package/addon/services/driver-actions.js +40 -7
  193. package/addon/services/driver-scheduling.js +4 -1
  194. package/addon/services/equipment-actions.js +15 -5
  195. package/addon/services/leaflet-map-manager.js +14 -6
  196. package/addon/services/maintenance-actions.js +17 -14
  197. package/addon/services/maintenance-schedule-actions.js +118 -0
  198. package/addon/services/orchestration-engine-interface.js +49 -0
  199. package/addon/services/orchestration-engine.js +74 -0
  200. package/addon/services/order-actions.js +15 -0
  201. package/addon/services/order-allocation.js +116 -0
  202. package/addon/services/part-actions.js +12 -2
  203. package/addon/services/scheduling.js +316 -0
  204. package/addon/services/vehicle-actions.js +70 -7
  205. package/addon/services/vroom-allocation-engine.js +45 -0
  206. package/addon/services/work-order-actions.js +80 -0
  207. package/addon/styles/fleetops-engine.css +1658 -0
  208. package/addon/templates/analytics/reports/index/edit.hbs +1 -1
  209. package/addon/templates/analytics/reports/index/new.hbs +1 -1
  210. package/addon/templates/application.hbs +6 -1
  211. package/addon/templates/connectivity/devices/index/details/events.hbs +0 -1
  212. package/addon/templates/connectivity/devices.hbs +0 -1
  213. package/addon/templates/connectivity/events/index/details.hbs +0 -1
  214. package/addon/templates/connectivity/events.hbs +0 -1
  215. package/addon/templates/connectivity/sensors.hbs +0 -1
  216. package/addon/templates/connectivity/telematics/index/details/devices.hbs +0 -1
  217. package/addon/templates/connectivity/telematics/index/details/events.hbs +0 -1
  218. package/addon/templates/connectivity/telematics/index/details/sensors.hbs +0 -1
  219. package/addon/templates/connectivity/telematics.hbs +0 -1
  220. package/addon/templates/connectivity/tracking.hbs +0 -1
  221. package/addon/templates/connectivity.hbs +0 -1
  222. package/addon/templates/maintenance/equipment/index/details/index.hbs +1 -2
  223. package/addon/templates/maintenance/equipment/index/details.hbs +15 -2
  224. package/addon/templates/maintenance/equipment/index/edit.hbs +12 -2
  225. package/addon/templates/maintenance/equipment/index/new.hbs +1 -2
  226. package/addon/templates/maintenance/equipment/index.hbs +48 -13
  227. package/addon/templates/maintenance/equipment.hbs +0 -1
  228. package/addon/templates/maintenance/maintenances/index/details/index.hbs +1 -0
  229. package/addon/templates/maintenance/maintenances/index/details.hbs +15 -0
  230. package/addon/templates/maintenance/maintenances/index/edit.hbs +12 -0
  231. package/addon/templates/maintenance/maintenances/index/new.hbs +11 -0
  232. package/addon/templates/maintenance/maintenances/index.hbs +14 -0
  233. package/addon/templates/maintenance/maintenances.hbs +1 -0
  234. package/addon/templates/maintenance/parts/index/details/index.hbs +1 -2
  235. package/addon/templates/maintenance/parts/index/details.hbs +15 -2
  236. package/addon/templates/maintenance/parts/index/edit.hbs +12 -2
  237. package/addon/templates/maintenance/parts/index/new.hbs +1 -2
  238. package/addon/templates/maintenance/parts/index.hbs +48 -13
  239. package/addon/templates/maintenance/parts.hbs +0 -1
  240. package/addon/templates/maintenance/schedules/index/details/index.hbs +1 -0
  241. package/addon/templates/maintenance/schedules/index/details/work-orders.hbs +39 -0
  242. package/addon/templates/maintenance/schedules/index/details.hbs +14 -0
  243. package/addon/templates/maintenance/schedules/index/edit.hbs +12 -0
  244. package/addon/templates/maintenance/schedules/index/new.hbs +11 -0
  245. package/addon/templates/maintenance/schedules/index.hbs +40 -0
  246. package/addon/templates/maintenance/schedules.hbs +1 -0
  247. package/addon/templates/maintenance/work-orders/index/details.hbs +2 -1
  248. package/addon/templates/maintenance/work-orders/index/edit.hbs +2 -4
  249. package/addon/templates/maintenance/work-orders/index/new.hbs +1 -2
  250. package/addon/templates/maintenance/work-orders.hbs +0 -1
  251. package/addon/templates/management/contacts/customers/edit.hbs +1 -2
  252. package/addon/templates/management/contacts/customers/new.hbs +1 -2
  253. package/addon/templates/management/contacts/customers.hbs +1 -1
  254. package/addon/templates/management/contacts/index/edit.hbs +1 -2
  255. package/addon/templates/management/contacts/index/new.hbs +1 -2
  256. package/addon/templates/management/drivers/index/details/orders.hbs +0 -1
  257. package/addon/templates/management/drivers/index/edit.hbs +1 -2
  258. package/addon/templates/management/drivers/index/new.hbs +1 -2
  259. package/addon/templates/management/fleets/index/edit.hbs +1 -2
  260. package/addon/templates/management/fleets/index/new.hbs +1 -2
  261. package/addon/templates/management/fleets/index.hbs +1 -2
  262. package/addon/templates/management/fuel-reports/index/edit.hbs +1 -2
  263. package/addon/templates/management/fuel-reports/index/new.hbs +1 -2
  264. package/addon/templates/management/fuel-reports/index.hbs +1 -2
  265. package/addon/templates/management/issues/index/edit.hbs +1 -2
  266. package/addon/templates/management/issues/index/new.hbs +1 -2
  267. package/addon/templates/management/issues/index.hbs +1 -2
  268. package/addon/templates/management/places/index/details/activity.hbs +0 -1
  269. package/addon/templates/management/places/index/details/comments.hbs +0 -1
  270. package/addon/templates/management/places/index/details/documents.hbs +0 -1
  271. package/addon/templates/management/places/index/details/map.hbs +0 -1
  272. package/addon/templates/management/places/index/details/operations.hbs +0 -1
  273. package/addon/templates/management/places/index/details/performance.hbs +0 -1
  274. package/addon/templates/management/places/index/details/rules.hbs +0 -1
  275. package/addon/templates/management/vehicles/index/details/equipment.hbs +0 -1
  276. package/addon/templates/management/vehicles/index/details/maintenance-history.hbs +2 -0
  277. package/addon/templates/management/vehicles/index/details/schedules.hbs +2 -0
  278. package/addon/templates/management/vehicles/index/details/work-orders.hbs +2 -0
  279. package/addon/templates/management/vehicles/index/details.hbs +1 -1
  280. package/addon/templates/management/vehicles/index/edit.hbs +1 -2
  281. package/addon/templates/management/vehicles/index/new.hbs +1 -2
  282. package/addon/templates/management/vendors/index/edit.hbs +1 -2
  283. package/addon/templates/management/vendors/index/new.hbs +1 -2
  284. package/addon/templates/management/vendors/index.hbs +1 -2
  285. package/addon/templates/management/vendors/integrated.hbs +1 -2
  286. package/addon/templates/operations/orchestrator.hbs +1 -0
  287. package/addon/templates/operations/orders/index.hbs +6 -1
  288. package/addon/templates/operations/scheduler/fleet-schedule.hbs +41 -0
  289. package/addon/templates/operations/scheduler/index.hbs +147 -88
  290. package/addon/templates/operations/scheduler.hbs +7 -1
  291. package/addon/templates/settings/avatars.hbs +1 -1
  292. package/addon/templates/settings/orchestrator.hbs +65 -0
  293. package/addon/templates/settings/payments/index.hbs +1 -5
  294. package/addon/templates/settings/scheduling.hbs +82 -0
  295. package/addon/utils/create-full-calendar-event-from-order.js +52 -14
  296. package/addon/utils/create-full-calendar-event-from-schedule-item.js +50 -0
  297. package/addon/utils/fleet-ops-options.js +254 -0
  298. package/addon/utils/route-colors.js +99 -0
  299. package/addon/utils/to-calendar-date.js +70 -0
  300. package/app/components/driver/schedule.js +1 -0
  301. package/app/components/maintenance/cost-panel.js +1 -0
  302. package/app/components/maintenance/panel-header.js +1 -0
  303. package/app/components/maintenance-schedule/details.js +1 -0
  304. package/app/components/maintenance-schedule/form.js +1 -0
  305. package/app/components/modals/add-driver-shift.js +1 -0
  306. package/app/components/modals/bulk-assign-orders.js +1 -0
  307. package/app/components/modals/driver-shift.js +1 -0
  308. package/app/components/modals/orchestrator-import.js +1 -0
  309. package/app/components/modals/scheduling-conflict.js +1 -0
  310. package/app/components/modals/send-work-order.js +1 -0
  311. package/app/components/modals/set-driver-availability.js +1 -0
  312. package/app/components/orchestrator/card-fields-settings.js +1 -0
  313. package/app/components/orchestrator/order-pool.js +1 -0
  314. package/app/components/orchestrator/phase-builder.js +1 -0
  315. package/app/components/orchestrator/plan-viewer.js +1 -0
  316. package/app/components/orchestrator/resource-panel.js +1 -0
  317. package/app/components/orchestrator-workbench.js +1 -0
  318. package/app/components/vehicle/details/maintenance-history.js +1 -0
  319. package/app/components/vehicle/details/schedules.js +1 -0
  320. package/app/components/vehicle/details/work-orders.js +1 -0
  321. package/app/controllers/operations/orchestrator.js +1 -0
  322. package/app/controllers/settings/orchestrator.js +1 -0
  323. package/app/controllers/settings/scheduling.js +1 -0
  324. package/app/routes/operations/orchestrator.js +1 -0
  325. package/app/routes/settings/orchestrator.js +1 -0
  326. package/app/routes/settings/scheduling.js +1 -0
  327. package/app/services/maintenance-schedule-actions.js +1 -0
  328. package/app/services/orchestration-engine-interface.js +1 -0
  329. package/app/services/orchestration-engine.js +1 -0
  330. package/app/services/order-allocation.js +1 -0
  331. package/app/services/scheduling.js +1 -0
  332. package/app/services/vroom-allocation-engine.js +1 -0
  333. package/app/templates/settings/scheduling.js +1 -0
  334. package/app/utils/create-full-calendar-event-from-schedule-item.js +1 -0
  335. package/app/utils/route-colors.js +1 -0
  336. package/composer.json +5 -3
  337. package/extension.json +1 -1
  338. package/package.json +6 -5
  339. package/server/config/fleetops.php +20 -1
  340. package/server/migrations/2025_08_28_054927_create_parts_table.php +2 -2
  341. package/server/migrations/2025_08_28_054932_add_public_id_to_maintenance_tables.php +45 -0
  342. package/server/migrations/2025_09_01_000001_create_maintenance_schedules_table.php +88 -0
  343. package/server/migrations/2026_04_01_000001_fix_monetary_columns_in_parts_table.php +48 -0
  344. package/server/migrations/2026_04_01_000003_add_photo_uuid_to_equipment_and_parts_tables.php +61 -0
  345. package/server/migrations/2026_04_01_000004_add_public_id_to_equipments_table.php +38 -0
  346. package/server/migrations/2026_04_01_000005_add_missing_columns_to_parts_table.php +67 -0
  347. package/server/migrations/2026_04_04_000001_add_reminder_offsets_to_maintenance_schedules.php +44 -0
  348. package/server/migrations/2026_04_08_000001_add_orchestrator_columns_to_vehicles_table.php +58 -0
  349. package/server/migrations/2026_04_08_000002_add_orchestrator_columns_to_drivers_table.php +41 -0
  350. package/server/migrations/2026_04_08_000003_add_orchestrator_columns_to_orders_table.php +38 -0
  351. package/server/migrations/2026_04_08_000004_add_orchestrator_columns_to_payloads_table.php +41 -0
  352. package/server/migrations/2026_04_08_000005_add_orchestrator_columns_to_waypoints_table.php +38 -0
  353. package/server/migrations/2026_04_09_000001_create_manifests_table.php +48 -0
  354. package/server/migrations/2026_04_09_000002_create_manifest_stops_table.php +48 -0
  355. package/server/migrations/2026_04_09_000003_add_manifest_uuid_to_orders_table.php +28 -0
  356. package/server/migrations/2026_04_13_000001_add_pod_notes_columns_to_waypoints_table.php +39 -0
  357. package/server/resources/views/mail/maintenance-schedule-reminder.blade.php +59 -0
  358. package/server/resources/views/mail/work-order-dispatched.blade.php +67 -0
  359. package/server/src/Auth/Schemas/FleetOps.php +44 -0
  360. package/server/src/Console/Commands/ProcessMaintenanceTriggers.php +150 -0
  361. package/server/src/Console/Commands/SendMaintenanceReminders.php +128 -0
  362. package/server/src/Http/Controllers/Internal/v1/DriverController.php +1 -0
  363. package/server/src/Http/Controllers/Internal/v1/EquipmentController.php +27 -0
  364. package/server/src/Http/Controllers/Internal/v1/LiveController.php +9 -2
  365. package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +165 -0
  366. package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +304 -0
  367. package/server/src/Http/Controllers/Internal/v1/ManifestController.php +138 -0
  368. package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +975 -0
  369. package/server/src/Http/Controllers/Internal/v1/OrderController.php +42 -0
  370. package/server/src/Http/Controllers/Internal/v1/PartController.php +27 -0
  371. package/server/src/Http/Controllers/Internal/v1/SettingController.php +118 -0
  372. package/server/src/Http/Controllers/Internal/v1/Traits/DriverSchedulingTrait.php +214 -0
  373. package/server/src/Http/Controllers/Internal/v1/WorkOrderController.php +68 -0
  374. package/server/src/Http/Resources/v1/Driver.php +1 -0
  375. package/server/src/Http/Resources/v1/Maintenance.php +138 -0
  376. package/server/src/Http/Resources/v1/MaintenanceSchedule.php +137 -0
  377. package/server/src/Http/Resources/v1/Orchestrator/Order.php +116 -0
  378. package/server/src/Http/Resources/v1/Order.php +7 -4
  379. package/server/src/Http/Resources/v1/Waypoint.php +7 -0
  380. package/server/src/Http/Resources/v1/WorkOrder.php +136 -0
  381. package/server/src/Imports/EquipmentImport.php +32 -0
  382. package/server/src/Imports/MaintenanceImport.php +32 -0
  383. package/server/src/Imports/MaintenanceScheduleImport.php +32 -0
  384. package/server/src/Imports/PartImport.php +32 -0
  385. package/server/src/Imports/WorkOrderImport.php +32 -0
  386. package/server/src/Jobs/ProcessAllocationJob.php +119 -0
  387. package/server/src/Listeners/HandleDeliveryCompletion.php +47 -0
  388. package/server/src/Listeners/NotifyDriverOnShiftChange.php +63 -0
  389. package/server/src/Mail/MaintenanceScheduleReminder.php +68 -0
  390. package/server/src/Mail/WorkOrderDispatched.php +58 -0
  391. package/server/src/Models/Asset.php +2 -2
  392. package/server/src/Models/Device.php +1 -1
  393. package/server/src/Models/Driver.php +82 -4
  394. package/server/src/Models/Equipment.php +62 -2
  395. package/server/src/Models/Maintenance.php +127 -9
  396. package/server/src/Models/MaintenanceSchedule.php +353 -0
  397. package/server/src/Models/Manifest.php +214 -0
  398. package/server/src/Models/ManifestStop.php +162 -0
  399. package/server/src/Models/Order.php +70 -0
  400. package/server/src/Models/OrderConfig.php +5 -2
  401. package/server/src/Models/Part.php +69 -3
  402. package/server/src/Models/Payload.php +7 -2
  403. package/server/src/Models/Place.php +1 -1
  404. package/server/src/Models/Sensor.php +1 -1
  405. package/server/src/Models/ServiceQuote.php +1 -1
  406. package/server/src/Models/Vehicle.php +20 -1
  407. package/server/src/Models/Warranty.php +1 -1
  408. package/server/src/Models/Waypoint.php +7 -1
  409. package/server/src/Models/WorkOrder.php +122 -12
  410. package/server/src/Notifications/DriverShiftChanged.php +110 -0
  411. package/server/src/Observers/WorkOrderObserver.php +107 -0
  412. package/server/src/Orchestration/Contracts/OrchestrationEngineInterface.php +63 -0
  413. package/server/src/Orchestration/Engines/DriverAssignmentEngine.php +265 -0
  414. package/server/src/Orchestration/Engines/GreedyOrchestrationEngine.php +155 -0
  415. package/server/src/Orchestration/Engines/RouteSequencingEngine.php +272 -0
  416. package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +192 -0
  417. package/server/src/Orchestration/OrchestrationEngineRegistry.php +83 -0
  418. package/server/src/Orchestration/Support/OrchestrationPayloadBuilder.php +290 -0
  419. package/server/src/Providers/EventServiceProvider.php +7 -1
  420. package/server/src/Providers/FleetOpsServiceProvider.php +42 -15
  421. package/server/src/routes.php +65 -4
  422. package/translations/ar-ae.yml +44 -12
  423. package/translations/bg-bg.yaml +51 -10
  424. package/translations/en-us.yaml +444 -1
  425. package/translations/fr-fr.yaml +51 -10
  426. package/translations/mn-mn.yaml +51 -10
  427. package/translations/pt-br.yaml +51 -10
  428. package/translations/ru-ru.yaml +51 -10
  429. package/translations/vi-vn.yaml +48 -12
@@ -0,0 +1,162 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { action } from '@ember/object';
4
+ import { inject as service } from '@ember/service';
5
+
6
+ /**
7
+ * Orchestrator::PhaseBuilder
8
+ *
9
+ * Allows dispatchers to compose a multi-phase orchestration run.
10
+ * Each phase is a discrete step: a mode (Assign Vehicles, Assign Drivers,
11
+ * Optimize Routes) with its own constraint options and resource/order filters.
12
+ *
13
+ * Phases are executed in sequence. The dispatcher can run all phases at once
14
+ * or step through them manually.
15
+ *
16
+ * @arg phases - Array of phase objects (managed externally)
17
+ * @arg availableEngines - Array of { id, name } engine options
18
+ * @arg onPhasesChange - Action(phases) — called when phases array changes
19
+ * @arg onRunPhases - Action(phases) — called when "Run" is clicked
20
+ */
21
+ export default class OrchestratorPhaseBuilderComponent extends Component {
22
+ @service intl;
23
+
24
+ /** Index of the phase currently being edited in the editor panel. */
25
+ @tracked editingIndex = null;
26
+
27
+ /** Draft copy of the phase being edited. */
28
+ @tracked draftPhase = null;
29
+
30
+ get modeOptions() {
31
+ return [
32
+ { value: 'assign_vehicles', label: this.intl.t('orchestrator.mode-assign-vehicles') },
33
+ { value: 'assign_drivers', label: this.intl.t('orchestrator.mode-assign-drivers') },
34
+ { value: 'optimize_routes', label: this.intl.t('orchestrator.mode-optimize-routes') },
35
+ { value: 'allocate', label: this.intl.t('orchestrator.mode-allocate') },
36
+ ];
37
+ }
38
+
39
+ get orderStatusOptions() {
40
+ return [
41
+ { value: 'created', label: this.intl.t('orchestrator.status-created') },
42
+ { value: 'dispatched', label: this.intl.t('orchestrator.status-dispatched') },
43
+ { value: 'started', label: this.intl.t('orchestrator.status-started') },
44
+ ];
45
+ }
46
+
47
+ _defaultPhase(mode = 'assign_vehicles') {
48
+ return {
49
+ id: crypto.randomUUID(),
50
+ mode,
51
+ label: this.intl.t(`orchestrator.mode-${mode.replace(/_/g, '-')}`),
52
+ engine: 'greedy',
53
+ orderStatuses: ['created'],
54
+ balanceWorkload: false,
55
+ respectSkills: true,
56
+ respectCapacity: true,
57
+ returnToDepot: false,
58
+ autoCommit: false,
59
+ };
60
+ }
61
+
62
+ @action addPhase() {
63
+ const newPhase = this._defaultPhase('assign_vehicles');
64
+ const phases = [...(this.args.phases ?? []), newPhase];
65
+ this.args.onPhasesChange?.(phases);
66
+ this.editingIndex = phases.length - 1;
67
+ this.draftPhase = { ...newPhase };
68
+ }
69
+
70
+ @action removePhase(index) {
71
+ const phases = (this.args.phases ?? []).filter((_, i) => i !== index);
72
+ this.args.onPhasesChange?.(phases);
73
+ if (this.editingIndex === index) {
74
+ this.editingIndex = null;
75
+ this.draftPhase = null;
76
+ } else if (this.editingIndex > index) {
77
+ this.editingIndex = this.editingIndex - 1;
78
+ }
79
+ }
80
+
81
+ @action movePhaseUp(index) {
82
+ if (index === 0) return;
83
+ const phases = [...(this.args.phases ?? [])];
84
+ [phases[index - 1], phases[index]] = [phases[index], phases[index - 1]];
85
+ this.args.onPhasesChange?.(phases);
86
+ if (this.editingIndex === index) this.editingIndex = index - 1;
87
+ else if (this.editingIndex === index - 1) this.editingIndex = index;
88
+ }
89
+
90
+ @action movePhaseDown(index) {
91
+ const phases = this.args.phases ?? [];
92
+ if (index >= phases.length - 1) return;
93
+ const updated = [...phases];
94
+ [updated[index], updated[index + 1]] = [updated[index + 1], updated[index]];
95
+ this.args.onPhasesChange?.(updated);
96
+ if (this.editingIndex === index) this.editingIndex = index + 1;
97
+ else if (this.editingIndex === index + 1) this.editingIndex = index;
98
+ }
99
+
100
+ @action editPhase(index) {
101
+ this.editingIndex = index;
102
+ this.draftPhase = { ...(this.args.phases ?? [])[index] };
103
+ }
104
+
105
+ @action closePhasEditor() {
106
+ this.editingIndex = null;
107
+ this.draftPhase = null;
108
+ }
109
+
110
+ @action saveDraftPhase() {
111
+ if (this.editingIndex === null || !this.draftPhase) return;
112
+ const phases = [...(this.args.phases ?? [])];
113
+ phases[this.editingIndex] = { ...this.draftPhase };
114
+ this.args.onPhasesChange?.(phases);
115
+ this.editingIndex = null;
116
+ this.draftPhase = null;
117
+ }
118
+
119
+ @action setDraftMode(mode) {
120
+ this.draftPhase = {
121
+ ...this.draftPhase,
122
+ mode,
123
+ label: this.intl.t(`orchestrator.mode-${mode.replace(/_/g, '-')}`),
124
+ };
125
+ }
126
+
127
+ @action setDraftEngine(engine) {
128
+ this.draftPhase = { ...this.draftPhase, engine };
129
+ }
130
+
131
+ @action toggleDraftOrderStatus(status) {
132
+ const current = this.draftPhase.orderStatuses ?? [];
133
+ const updated = current.includes(status) ? current.filter((s) => s !== status) : [...current, status];
134
+ this.draftPhase = { ...this.draftPhase, orderStatuses: updated };
135
+ }
136
+
137
+ @action setDraftOption(key, value) {
138
+ this.draftPhase = { ...this.draftPhase, [key]: value };
139
+ }
140
+
141
+ @action setDraftLabel(event) {
142
+ this.draftPhase = { ...this.draftPhase, label: event.target.value };
143
+ }
144
+
145
+ get isEditing() {
146
+ return this.editingIndex !== null && this.draftPhase !== null;
147
+ }
148
+
149
+ modeLabel(mode) {
150
+ return this.modeOptions.find((m) => m.value === mode)?.label ?? mode;
151
+ }
152
+
153
+ modeIcon(mode) {
154
+ const icons = {
155
+ assign_vehicles: 'truck',
156
+ assign_drivers: 'user',
157
+ optimize_routes: 'route',
158
+ allocate: 'bolt',
159
+ };
160
+ return icons[mode] ?? 'cog';
161
+ }
162
+ }
@@ -0,0 +1,278 @@
1
+ {{! Orchestrator::PlanViewer — Post-run route cards with stop sequences and EventCalendar timeline }}
2
+ <div class="plan-viewer flex flex-col h-full overflow-hidden">
3
+
4
+ {{! ── Header: summary + tab switcher ── }}
5
+ <div class="px-3 py-2 border-b dark:border-gray-700 flex-shrink-0">
6
+ <div class="flex items-center justify-between mb-2">
7
+ <span class="text-xs font-medium uppercase tracking-wide dark:text-gray-400">
8
+ {{t "orchestrator.proposed-routes"}}
9
+ ({{@planByVehicle.length}})
10
+ </span>
11
+ {{#if (eq this.activeTab "routes")}}
12
+ <button type="button" class="text-xs text-blue-400 hover:text-blue-300 transition-colors" {{on "click" this.toggleExpandAll}}>
13
+ {{if this.allExpanded (t "orchestrator.collapse-all") (t "orchestrator.expand-all")}}
14
+ </button>
15
+ {{else if (eq this.activeTab "timeline")}}
16
+ {{#if this.timelineDateRange}}
17
+ <span class="text-xs text-gray-400 dark:text-gray-500">{{this.timelineDateRange}}</span>
18
+ {{/if}}
19
+ {{/if}}
20
+ </div>
21
+ {{! Tab bar }}
22
+ <div class="flex gap-1">
23
+ <button
24
+ type="button"
25
+ class="px-2.5 py-1 rounded text-xs font-medium transition-colors
26
+ {{if (eq this.activeTab 'routes') 'bg-indigo-600 text-white' 'text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700'}}"
27
+ {{on "click" (fn this.setTab "routes")}}
28
+ >
29
+ <FaIcon @icon="route" @size="xs" class="mr-1" />{{t "orchestrator.tab-routes"}}
30
+ </button>
31
+ <button
32
+ type="button"
33
+ class="px-2.5 py-1 rounded text-xs font-medium transition-colors
34
+ {{if (eq this.activeTab 'timeline') 'bg-indigo-600 text-white' 'text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700'}}"
35
+ {{on "click" (fn this.setTab "timeline")}}
36
+ >
37
+ <FaIcon @icon="calendar-alt" @size="xs" class="mr-1" />{{t "orchestrator.tab-timeline"}}
38
+ </button>
39
+ </div>
40
+ </div>
41
+
42
+ {{! Unassigned warning banner }}
43
+ {{#if this.hasUnassigned}}
44
+ <div class="mx-3 mt-2 px-3 py-2 rounded bg-yellow-900/30 border border-yellow-700 text-yellow-400 text-xs flex-shrink-0">
45
+ <FaIcon @icon="exclamation-triangle" class="mr-1" />
46
+ {{t "orchestrator.unassigned-warning" count=@unassignedAfterRun.length}}
47
+ </div>
48
+ {{/if}}
49
+
50
+ {{! Run message banner }}
51
+ {{#if @runMessage}}
52
+ <div class="flex items-start gap-2 mx-3 mt-2 px-3 py-2 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-700 rounded flex-shrink-0">
53
+ <FaIcon @icon="exclamation-triangle" class="mt-0.5 flex-shrink-0 text-amber-500" @size="sm" />
54
+ <p class="flex-1 text-xs text-amber-800 dark:text-amber-200">{{@runMessage}}</p>
55
+ <button type="button" class="flex-shrink-0 text-amber-400 hover:text-amber-600" {{on "click" @onDismissMessage}}>
56
+ <FaIcon @icon="times" @size="xs" />
57
+ </button>
58
+ </div>
59
+ {{/if}}
60
+
61
+ {{! ── ROUTES TAB ── }}
62
+ {{#if (eq this.activeTab "routes")}}
63
+ <div class="flex-1 overflow-y-auto">
64
+ {{#each @planByVehicle as |group|}}
65
+ {{! vehicle-route-card }}
66
+ <div
67
+ class="vehicle-route-card mx-3 my-2 rounded border dark:border-gray-600 dark:bg-gray-800 overflow-hidden"
68
+ data-vehicle-id={{group.vehicle.public_id}}
69
+ {{on "dragover" @onDragOver}}
70
+ {{on "drop" (fn @onDropOnVehicle group.vehicle.public_id group.driver.public_id)}}
71
+ >
72
+ {{! Vehicle / driver header — click to expand/collapse
73
+ When a driver phase ran (@hasDriverPhase), show driver as the primary
74
+ label with vehicle as the secondary sub-label. Otherwise show vehicle
75
+ as primary with driver as sub-label (legacy behaviour). }}
76
+ <div
77
+ class="flex items-center px-3 py-2 border-b dark:border-gray-700 cursor-pointer"
78
+ style={{html-safe (concat "border-left: 3px solid " group.routeColor)}}
79
+ {{on "click" (fn this.toggleCard group.vehicle.public_id)}}
80
+ >
81
+ {{#if (and @hasDriverPhase group.driver)}}
82
+ <FaIcon @icon="user" class="mr-2 text-green-400 flex-shrink-0" />
83
+ {{else}}
84
+ <FaIcon @icon="truck" class="mr-2 text-blue-400 flex-shrink-0" />
85
+ {{/if}}
86
+ <div class="flex-1 min-w-0">
87
+ {{#if (and @hasDriverPhase group.driver)}}
88
+ {{! Driver-phase run: driver is primary, vehicle is secondary }}
89
+ <div class="text-xs font-semibold dark:text-white truncate">{{group.driver.name}}</div>
90
+ <div class="text-xs text-gray-400 truncate">{{group.vehicle.display_name}}</div>
91
+ {{else}}
92
+ {{! Vehicle-only run: vehicle is primary, driver (if any) is secondary }}
93
+ <div class="text-xs font-semibold dark:text-white truncate">{{group.vehicle.display_name}}</div>
94
+ {{#if group.driver}}
95
+ <div class="text-xs text-gray-400 truncate">{{group.driver.name}}</div>
96
+ {{/if}}
97
+ {{/if}}
98
+ </div>
99
+ <div class="flex items-center space-x-1 flex-shrink-0">
100
+ <span class="text-xs text-gray-400">{{group.orders.length}} {{t "orchestrator.stops"}}</span>
101
+ {{#if group.summary.duration}}
102
+ <span class="text-xs text-gray-500">·</span>
103
+ <span class="text-xs text-gray-400">{{@formatDuration group.summary.duration}}</span>
104
+ {{/if}}
105
+ <FaIcon @icon={{if (this.isExpanded group.vehicle.public_id) "chevron-up" "chevron-down"}} @size="xs" class="text-gray-500 ml-1" />
106
+ </div>
107
+ </div>
108
+
109
+ {{! Expanded stop list }}
110
+ {{#if (this.isExpanded group.vehicle.public_id)}}
111
+ <div class="vehicle-drop-zone" data-vehicle-id={{group.vehicle.public_id}} data-driver-id={{group.driver.public_id}}>
112
+ {{#each group.orders as |item idx|}}
113
+ {{! Order block — draggable unit }}
114
+ <div
115
+ class="assigned-stop cursor-grab {{if item._overridden 'border-l-2 border-orange-500' ''}}"
116
+ draggable="true"
117
+ data-order-id={{item.order.public_id}}
118
+ {{on "dragstart" (fn @onAssignedOrderDragStart item.order)}}
119
+ >
120
+ {{! Order reference header — tracking number + type badge }}
121
+ <div class="flex items-center gap-1.5 px-3 pt-2 pb-1 border-b dark:border-gray-700/60">
122
+ <FaIcon @icon="hashtag" @size="xs" class="text-gray-500 flex-shrink-0" />
123
+ <span class="text-xs font-mono font-semibold dark:text-gray-300 truncate">
124
+ {{or item.order.tracking item.order.public_id}}
125
+ </span>
126
+ {{#if item.order.type}}
127
+ <span class="ml-auto text-xs text-gray-500 capitalize truncate">{{item.order.type}}</span>
128
+ {{/if}}
129
+ </div>
130
+
131
+ {{! Per-stop rows — one row per _labelledStop }}
132
+ {{#if item._labelledStops.length}}
133
+ {{#each item._labelledStops as |stop stopIdx|}}
134
+ <div
135
+ class="flex items-start gap-2 px-3 py-2
136
+ {{if (gt stopIdx 0) 'border-t dark:border-gray-700/60' ''}}
137
+ {{if item._overridden 'bg-orange-900/10' 'dark:bg-gray-800 hover:dark:bg-gray-750'}}"
138
+ >
139
+
140
+ {{! Letter badge }}
141
+ <span
142
+ class="mt-0.5 w-5 h-5 rounded-full flex-shrink-0 flex items-center justify-center text-white font-bold"
143
+ style={{html-safe (concat "font-size:0.6rem; background-color:" group.routeColor)}}
144
+ >{{stop.label}}</span>
145
+
146
+ {{! Stop content }}
147
+ <div class="flex-1 min-w-0">
148
+ {{! Address }}
149
+ <div class="text-xs font-medium dark:text-white truncate">{{stop.address}}</div>
150
+
151
+ {{! Tags row: Pickup/Dropoff, POD }}
152
+ <div class="flex flex-wrap items-center gap-1 mt-0.5">
153
+ {{#if (eq stop.stopType "pickup")}}
154
+ <span class="inline-flex items-center gap-0.5 px-1.5 py-0.5 rounded text-blue-400 bg-blue-900/30 text-xs leading-none">
155
+ <FaIcon @icon="arrow-up" @size="xs" />
156
+ {{t "orchestrator.stop-type-pickup"}}
157
+ </span>
158
+ {{else if (eq stop.stopType "dropoff")}}
159
+ <span class="inline-flex items-center gap-0.5 px-1.5 py-0.5 rounded text-green-400 bg-green-900/30 text-xs leading-none">
160
+ <FaIcon @icon="arrow-down" @size="xs" />
161
+ {{t "orchestrator.stop-type-dropoff"}}
162
+ </span>
163
+ {{/if}}
164
+ {{#if stop.requiresPod}}
165
+ <span class="inline-flex items-center gap-0.5 px-1.5 py-0.5 rounded text-purple-400 bg-purple-900/30 text-xs leading-none">
166
+ <FaIcon @icon="file-signature" @size="xs" />
167
+ {{t "orchestrator.pod-required"}}
168
+ </span>
169
+ {{else}}
170
+ <span class="inline-flex items-center gap-0.5 px-1.5 py-0.5 rounded text-gray-500 bg-gray-700/40 text-xs leading-none">
171
+ <FaIcon @icon="times" @size="xs" />
172
+ {{t "orchestrator.no-pod"}}
173
+ </span>
174
+ {{/if}}
175
+ </div>
176
+
177
+ {{! Time window }}
178
+ {{#if (or stop.timeWindowStart stop.timeWindowEnd)}}
179
+ <div class="flex items-center gap-1 mt-0.5 text-xs text-gray-400">
180
+ <FaIcon @icon="clock" @size="xs" class="flex-shrink-0" />
181
+ <span class="tabular-nums">{{@formatTimeWindow stop.timeWindowStart stop.timeWindowEnd}}</span>
182
+ </div>
183
+ {{/if}}
184
+
185
+ {{! Notes }}
186
+ {{#if stop.notes}}
187
+ <div class="flex items-start gap-1 mt-0.5 text-xs text-gray-400">
188
+ <FaIcon @icon="sticky-note" @size="xs" class="flex-shrink-0 mt-0.5" />
189
+ <span class="break-words">{{stop.notes}}</span>
190
+ </div>
191
+ {{/if}}
192
+ </div>
193
+
194
+ {{! Arrival time (OSRM-estimated) }}
195
+ {{#if item.arrival}}
196
+ <span class="flex-shrink-0 text-xs text-gray-500 tabular-nums mt-0.5">{{@formatUnixTime item.arrival}}</span>
197
+ {{/if}}
198
+
199
+ {{! Override indicator }}
200
+ {{#if item._overridden}}
201
+ <FaIcon @icon="pen" @size="xs" class="text-orange-400 flex-shrink-0 mt-0.5" />
202
+ {{/if}}
203
+ </div>
204
+ {{/each}}
205
+ {{else}}
206
+ {{! Fallback: no _labelledStops — show order tracking number only }}
207
+ <div class="flex items-center gap-2 px-3 py-2 dark:bg-gray-800">
208
+ <span
209
+ class="w-5 h-5 rounded-full flex-shrink-0 flex items-center justify-center text-white font-bold"
210
+ style={{html-safe (concat "font-size:0.6rem; background-color:" group.routeColor)}}
211
+ >{{@getStopLabel idx}}</span>
212
+ <span class="text-xs dark:text-white truncate">{{or item.order.tracking item.order.public_id}}</span>
213
+ </div>
214
+ {{/if}}
215
+ </div>
216
+ {{/each}}
217
+ {{#unless group.orders.length}}
218
+ <div class="py-3 text-center text-xs text-gray-500">
219
+ {{t "orchestrator.drop-orders-here"}}
220
+ </div>
221
+ {{/unless}}
222
+ </div>
223
+ {{! Route summary footer }}
224
+ {{#if (or group.summary.distance group.summary.duration)}}
225
+ <div class="px-3 py-1.5 border-t dark:border-gray-700 flex items-center space-x-3 text-xs text-gray-500">
226
+ {{#if group.summary.distance}}
227
+ <span><FaIcon @icon="road" @size="xs" class="mr-0.5" />{{@formatDistance group.summary.distance}}</span>
228
+ {{/if}}
229
+ {{#if group.summary.duration}}
230
+ <span><FaIcon @icon="clock" @size="xs" class="mr-0.5" />{{@formatDuration group.summary.duration}}</span>
231
+ {{/if}}
232
+ </div>
233
+ {{/if}}
234
+ {{/if}}
235
+ </div>
236
+ {{else}}
237
+ <div class="flex flex-col items-center justify-center py-12 text-gray-400 text-xs">
238
+ <FaIcon @icon="route" @size="2x" class="mb-2 opacity-40" />
239
+ {{t "orchestrator.no-plan"}}
240
+ </div>
241
+ {{/each}}
242
+ </div>
243
+ {{/if}}
244
+
245
+ {{! ── TIMELINE TAB ── }}
246
+ {{! Uses the same EventCalendar component as the Scheduler (resourceTimelineWeek view).
247
+ Each vehicle is a resource row; each planned stop is an event block showing
248
+ sequence number, tracking number, scheduled time, pickup -> dropoff address,
249
+ and customer name. Colors match the route colors used on the map. }}
250
+ {{#if (eq this.activeTab "timeline")}}
251
+ <div id="fleet-ops-orchestrator-plan-timeline" class="flex-1 overflow-hidden">
252
+ {{#if this.calendarEvents.length}}
253
+ <EventCalendar
254
+ @view="resourceTimelineWeek"
255
+ @date={{this.timelineDate}}
256
+ @events={{this.calendarEvents}}
257
+ @resources={{this.calendarResources}}
258
+ @editable={{false}}
259
+ @slotMinTime="00:00:00"
260
+ @slotMaxTime="24:00:00"
261
+ @slotWidth={{120}}
262
+ @height="100%"
263
+ @headerToolbar={{this.calendarHeaderToolbar}}
264
+ @resourceLabelContent={{this.renderResourceLabel}}
265
+ @eventContent={{this.renderEventContent}}
266
+ @onCalendarReady={{this.setCalendarApi}}
267
+ @options={{this.calendarOptions}}
268
+ />
269
+ {{else}}
270
+ <div class="flex flex-col items-center justify-center py-12 text-gray-400 text-xs">
271
+ <FaIcon @icon="calendar-alt" @size="2x" class="mb-2 opacity-40" />
272
+ {{t "orchestrator.no-plan"}}
273
+ </div>
274
+ {{/if}}
275
+ </div>
276
+ {{/if}}
277
+
278
+ </div>