@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,394 @@
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::OrderPool
8
+ *
9
+ * Left panel of the Orchestrator Workbench. Displays the filterable,
10
+ * searchable list of orders available for allocation. Handles order
11
+ * selection (checkbox + click) and drag initiation.
12
+ *
13
+ * @arg orders - Full array of available orders
14
+ * @arg selectedOrderIds - Set of selected order public_ids
15
+ * @arg isLoading - Whether data is loading
16
+ * @arg cardFields - Configured card fields { standard: [], byConfig: {}, meta: [] }
17
+ * @arg onToggleSelection - Action(order) — toggle order selection
18
+ * @arg onClearSelection - Action — clear all selections
19
+ * @arg onDragStart - Action(order, event) — drag start handler
20
+ * @arg onOpenImport - Action — open import modal
21
+ */
22
+
23
+ /**
24
+ * Default standard fields shown when no card-field settings have been saved.
25
+ */
26
+ const DEFAULT_FIELDS = [
27
+ { key: 'pickup', icon: 'location-crosshairs', iconClass: 'text-green-400' },
28
+ { key: 'dropoff', icon: 'location-dot', iconClass: 'text-red-400' },
29
+ { key: 'scheduled_at', icon: 'clock', highlight: 'text-blue-500 dark:text-blue-400' },
30
+ { key: 'customer', icon: 'user' },
31
+ { key: 'driver_assigned', icon: 'id-badge' },
32
+ { key: 'vehicle_assigned', icon: 'truck' },
33
+ { key: 'created_at', icon: 'calendar-plus', highlight: 'text-gray-500 dark:text-gray-400' },
34
+ ];
35
+
36
+ /** Icon/highlight metadata for known standard field keys. */
37
+ const FIELD_META = {
38
+ tracking: { icon: 'hashtag' },
39
+ status: { icon: 'circle-dot' },
40
+ scheduled_at: { icon: 'clock', highlight: 'text-blue-500 dark:text-blue-400' },
41
+ customer: { icon: 'user' },
42
+ type: { icon: 'tag' },
43
+ notes: { icon: 'note-sticky' },
44
+ priority: { icon: 'flag' },
45
+ dropoff: { icon: 'location-dot', iconClass: 'text-red-400' },
46
+ pickup: { icon: 'location-crosshairs', iconClass: 'text-green-400' },
47
+ driver_assigned: { icon: 'id-badge' },
48
+ vehicle_assigned: { icon: 'truck' },
49
+ created_at: { icon: 'calendar-plus', highlight: 'text-gray-500 dark:text-gray-400' },
50
+ };
51
+
52
+ export default class OrchestratorOrderPoolComponent extends Component {
53
+ @service intl;
54
+
55
+ // ── Quick filter chip ─────────────────────────────────────────────────
56
+ @tracked orderSearch = '';
57
+ @tracked orderFilter = 'all';
58
+
59
+ // ── Card body collapse state (hides entire card body) ─────────────────
60
+ @tracked collapsedOrderIds = new Set();
61
+ // ── Inline route collapse state (hides route stop list inside card) ───
62
+ @tracked collapsedRouteIds = new Set();
63
+
64
+ // ── Advanced filter panel ─────────────────────────────────────────────────
65
+ @tracked showAdvanced = false;
66
+ @tracked advancedCountry = null;
67
+ @tracked advancedType = null;
68
+ @tracked advancedStatus = null;
69
+ @tracked advancedDate = null;
70
+
71
+ // ── Quick filter ──────────────────────────────────────────────────────────
72
+
73
+ @action onSearchInput(event) {
74
+ this.orderSearch = event.target.value;
75
+ }
76
+
77
+ @action setFilter(filter) {
78
+ this.orderFilter = filter;
79
+ }
80
+
81
+ @action stopPropagation(event) {
82
+ event?.stopPropagation();
83
+ }
84
+
85
+ // ── Waypoint collapse actions ─────────────────────────────────────────
86
+
87
+ @action toggleWaypointCollapse(orderId, event) {
88
+ event?.stopPropagation();
89
+ const next = new Set(this.collapsedOrderIds);
90
+ if (next.has(orderId)) {
91
+ next.delete(orderId);
92
+ } else {
93
+ next.add(orderId);
94
+ }
95
+ this.collapsedOrderIds = next;
96
+ }
97
+
98
+ get allWaypointsCollapsed() {
99
+ const orders = this.filteredOrders ?? [];
100
+ return orders.length > 0 && orders.every((o) => this.collapsedOrderIds.has(o.public_id));
101
+ }
102
+
103
+ @action toggleAllWaypoints() {
104
+ if (this.allWaypointsCollapsed) {
105
+ this.collapsedOrderIds = new Set();
106
+ } else {
107
+ const ids = (this.filteredOrders ?? []).map((o) => o.public_id).filter(Boolean);
108
+ this.collapsedOrderIds = new Set(ids);
109
+ }
110
+ }
111
+
112
+ @action collapseAllWaypoints() {
113
+ const ids = (this.filteredOrders ?? []).map((o) => o.public_id).filter(Boolean);
114
+ this.collapsedOrderIds = new Set(ids);
115
+ }
116
+
117
+ @action expandAllWaypoints() {
118
+ this.collapsedOrderIds = new Set();
119
+ }
120
+
121
+ @action isWaypointCollapsed(orderId) {
122
+ return this.collapsedOrderIds.has(orderId);
123
+ }
124
+
125
+ // ── Inline route collapse actions ────────────────────────────────────
126
+
127
+ @action toggleRouteCollapse(orderId, event) {
128
+ event?.stopPropagation();
129
+ const next = new Set(this.collapsedRouteIds);
130
+ if (next.has(orderId)) {
131
+ next.delete(orderId);
132
+ } else {
133
+ next.add(orderId);
134
+ }
135
+ this.collapsedRouteIds = next;
136
+ }
137
+
138
+ @action isRouteCollapsed(orderId) {
139
+ return this.collapsedRouteIds.has(orderId);
140
+ }
141
+
142
+ /**
143
+ * orderRouteStops — returns an array of { label, address } objects
144
+ * representing the ordered route stops for an order, using A/B/C labels.
145
+ */
146
+ @action orderRouteStops(order) {
147
+ const stops = [];
148
+ const payload = order.payload;
149
+
150
+ if (!payload) {
151
+ if (order.pickup_name) stops.push(order.pickup_name);
152
+ if (order.dropoff_name) stops.push(order.dropoff_name);
153
+ return stops.map((address, i) => ({ label: String.fromCharCode(65 + i), address }));
154
+ }
155
+
156
+ if (payload.pickup) {
157
+ stops.push(payload.pickup.address ?? payload.pickup.street1 ?? payload.pickup.name ?? '—');
158
+ }
159
+
160
+ const waypoints = typeof payload.waypoints?.toArray === 'function' ? payload.waypoints.toArray() : Array.isArray(payload.waypoints) ? payload.waypoints : [];
161
+
162
+ for (const wp of waypoints) {
163
+ const place = wp.place ?? wp;
164
+ stops.push(place.address ?? place.street1 ?? place.name ?? '—');
165
+ }
166
+
167
+ if (payload.dropoff) {
168
+ stops.push(payload.dropoff.address ?? payload.dropoff.street1 ?? payload.dropoff.name ?? '—');
169
+ }
170
+
171
+ if (stops.length === 0) {
172
+ if (order.pickup_name) stops.push(order.pickup_name);
173
+ if (order.dropoff_name) stops.push(order.dropoff_name);
174
+ }
175
+
176
+ return stops.map((address, i) => ({ label: String.fromCharCode(65 + i), address }));
177
+ }
178
+
179
+ // ── Advanced filter panel ─────────────────────────────────────────────────
180
+
181
+ @action toggleAdvanced() {
182
+ this.showAdvanced = !this.showAdvanced;
183
+ }
184
+
185
+ @action setAdvancedCountry(value) {
186
+ this.advancedCountry = value || null;
187
+ }
188
+
189
+ @action setAdvancedType(event) {
190
+ this.advancedType = event.target.value || null;
191
+ }
192
+
193
+ @action setAdvancedStatus(event) {
194
+ this.advancedStatus = event.target.value || null;
195
+ }
196
+
197
+ @action setAdvancedDate(event) {
198
+ this.advancedDate = event.target.value || null;
199
+ }
200
+
201
+ @action clearAdvancedFilters() {
202
+ this.advancedCountry = null;
203
+ this.advancedType = null;
204
+ this.advancedStatus = null;
205
+ this.advancedDate = null;
206
+ }
207
+
208
+ get hasAdvancedFilters() {
209
+ return !!(this.advancedCountry || this.advancedType || this.advancedStatus || this.advancedDate);
210
+ }
211
+
212
+ /** Unique order types derived from the current order pool. */
213
+ get availableTypes() {
214
+ const orders = this.args.orders ?? [];
215
+ const types = [...new Set(orders.map((o) => o.type).filter(Boolean))].sort();
216
+ return types;
217
+ }
218
+
219
+ /** Unique order statuses derived from the current order pool. */
220
+ get availableStatuses() {
221
+ const orders = this.args.orders ?? [];
222
+ const statuses = [...new Set(orders.map((o) => o.status).filter(Boolean))].sort();
223
+ return statuses;
224
+ }
225
+
226
+ // ── Filtered orders ───────────────────────────────────────────────────────
227
+
228
+ get filteredOrders() {
229
+ let orders = this.args.orders ?? [];
230
+
231
+ // ── Text search ───────────────────────────────────────────────────────
232
+ if (this.orderSearch) {
233
+ const q = this.orderSearch.toLowerCase();
234
+ orders = orders.filter(
235
+ (o) =>
236
+ o.tracking?.toLowerCase().includes(q) ||
237
+ o.public_id?.toLowerCase().includes(q) ||
238
+ o.payload?.dropoff?.address?.toLowerCase().includes(q) ||
239
+ o.payload?.pickup?.address?.toLowerCase().includes(q)
240
+ );
241
+ }
242
+
243
+ // ── Quick filter chip ─────────────────────────────────────────────────
244
+ if (this.orderFilter === 'scheduled') {
245
+ orders = orders.filter((o) => !!o.scheduled_at);
246
+ } else if (this.orderFilter === 'urgent') {
247
+ orders = orders.filter((o) => (o.orchestrator_priority ?? 0) >= 75);
248
+ } else if (this.orderFilter === 'today') {
249
+ const today = new Date().toDateString();
250
+ orders = orders.filter((o) => o.scheduled_at && new Date(o.scheduled_at).toDateString() === today);
251
+ } else if (this.orderFilter === 'unassigned') {
252
+ orders = orders.filter((o) => !o.vehicle_assigned_uuid && !o.driver_assigned_uuid);
253
+ } else if (this.orderFilter === 'imported') {
254
+ orders = orders.filter((o) => o.meta?.imported_via_orchestrator);
255
+ }
256
+
257
+ // ── Advanced filters ──────────────────────────────────────────────────
258
+ if (this.advancedCountry) {
259
+ const country = this.advancedCountry.toLowerCase();
260
+ orders = orders.filter((o) => {
261
+ const pickupCountry = (o.payload?.pickup?.country ?? '').toLowerCase();
262
+ const dropoffCountry = (o.payload?.dropoff?.country ?? '').toLowerCase();
263
+ const pickupAddr = (o.payload?.pickup?.address ?? '').toLowerCase();
264
+ return pickupCountry === country || dropoffCountry === country || pickupAddr.includes(country);
265
+ });
266
+ }
267
+
268
+ if (this.advancedType) {
269
+ orders = orders.filter((o) => o.type === this.advancedType);
270
+ }
271
+
272
+ if (this.advancedStatus) {
273
+ orders = orders.filter((o) => o.status === this.advancedStatus);
274
+ }
275
+
276
+ if (this.advancedDate) {
277
+ const filterDate = new Date(this.advancedDate).toDateString();
278
+ orders = orders.filter((o) => o.scheduled_at && new Date(o.scheduled_at).toDateString() === filterDate);
279
+ }
280
+
281
+ return orders;
282
+ }
283
+
284
+ get selectedOrderIdsArray() {
285
+ return [...(this.args.selectedOrderIds ?? new Set())];
286
+ }
287
+
288
+ // ── Card field resolution ─────────────────────────────────────────────────
289
+
290
+ /**
291
+ * resolvedCardFields — @action so Glimmer allows it to be invoked with
292
+ * arguments from HBS: {{#each (this.resolvedCardFields order) as |field|}}
293
+ */
294
+ @action resolvedCardFields(order) {
295
+ const cardFields = this.args.cardFields;
296
+
297
+ if (!cardFields) {
298
+ return DEFAULT_FIELDS.map(({ key, icon, iconClass, highlight }) => {
299
+ const { value, label } = this._resolveStandardFieldFull(order, key);
300
+ if (!value) return null;
301
+ return { label, value, icon, iconClass, highlight };
302
+ }).filter(Boolean);
303
+ }
304
+
305
+ const fields = [];
306
+
307
+ for (const key of cardFields.standard ?? []) {
308
+ const { value, label } = this._resolveStandardFieldFull(order, key);
309
+ if (!value) continue;
310
+ const meta = FIELD_META[key] ?? {};
311
+ fields.push({ label, value, ...meta });
312
+ }
313
+
314
+ const configUuid = order.order_config_uuid;
315
+ if (configUuid && cardFields.byConfig?.[configUuid]) {
316
+ for (const fieldKey of cardFields.byConfig[configUuid]) {
317
+ let label = fieldKey;
318
+ let value = null;
319
+
320
+ const cfvCollection = order.custom_field_values ?? order.customFieldValues ?? [];
321
+ const cfvArray = typeof cfvCollection?.toArray === 'function' ? cfvCollection.toArray() : Array.isArray(cfvCollection) ? cfvCollection : [];
322
+
323
+ for (const cfv of cfvArray) {
324
+ const cf = cfv.custom_field ?? (typeof cfv.customField?.get === 'function' ? cfv.customField : null) ?? cfv.customField ?? null;
325
+
326
+ const cfName = cf?.name ?? cf?.get?.('name') ?? '';
327
+ const cfLabel = cf?.label ?? cf?.get?.('label') ?? '';
328
+
329
+ if (cfName !== fieldKey && cfLabel !== fieldKey) continue;
330
+
331
+ const rawVal = cfv.value ?? null;
332
+ if (rawVal !== null && rawVal !== undefined) {
333
+ label = cfLabel || cfName || fieldKey;
334
+ value = typeof rawVal === 'object' ? JSON.stringify(rawVal) : String(rawVal);
335
+ }
336
+ break;
337
+ }
338
+
339
+ if (value !== null && value !== '') {
340
+ fields.push({ label, value });
341
+ }
342
+ }
343
+ }
344
+
345
+ for (const key of cardFields.meta ?? []) {
346
+ const val = order.meta?.[key];
347
+ if (val !== undefined && val !== null) {
348
+ fields.push({ label: key, value: String(val) });
349
+ }
350
+ }
351
+
352
+ return fields;
353
+ }
354
+
355
+ @action priorityBadgeStatus(priority) {
356
+ if (priority >= 75) return 'error';
357
+ if (priority >= 50) return 'warning';
358
+ return 'info';
359
+ }
360
+
361
+ _resolveStandardFieldFull(order, key) {
362
+ const t = (k) => {
363
+ try {
364
+ return this.intl.t(`orchestrator.${k}`);
365
+ } catch {
366
+ return k;
367
+ }
368
+ };
369
+ const map = {
370
+ tracking: { label: t('field-tracking'), value: order.tracking ?? order.public_id },
371
+ status: { label: t('field-status'), value: order.status },
372
+ scheduled_at: { label: t('scheduled'), value: order.scheduled_at ? this._formatDate(order.scheduled_at) : null },
373
+ customer: { label: t('customer'), value: order.customer?.name },
374
+ type: { label: t('field-type'), value: order.type },
375
+ notes: { label: t('field-notes'), value: order.notes },
376
+ priority: { label: t('field-priority'), value: order.orchestrator_priority != null ? String(order.orchestrator_priority) : null },
377
+ dropoff: { label: t('dropoff'), value: order.payload?.dropoff?.address ?? order.dropoff_name },
378
+ pickup: { label: t('pickup'), value: order.payload?.pickup?.address ?? order.pickup_name },
379
+ driver_assigned: { label: t('driver-assigned'), value: order.driver_assigned?.name },
380
+ vehicle_assigned: { label: t('vehicle-assigned'), value: order.vehicle_assigned?.display_name ?? order.vehicle_assigned?.name },
381
+ created_at: { label: t('created'), value: order.created_at ? this._formatDate(order.created_at) : null },
382
+ };
383
+ return map[key] ?? { label: key, value: order[key] != null ? String(order[key]) : null };
384
+ }
385
+
386
+ _formatDate(date) {
387
+ if (!date) return null;
388
+ try {
389
+ return new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }).format(new Date(date));
390
+ } catch {
391
+ return String(date);
392
+ }
393
+ }
394
+ }
@@ -0,0 +1,162 @@
1
+ {{! Orchestrator::PhaseBuilder — Compose a multi-phase orchestration run }}
2
+ <div class="phase-builder flex flex-col h-full">
3
+
4
+ {{! Header }}
5
+ <div class="px-3 py-2 border-b dark:border-gray-700 flex items-center justify-between flex-shrink-0 bg-gray-50 dark:bg-gray-800">
6
+ <div>
7
+ <h3 class="text-xs font-semibold text-gray-800 dark:text-gray-100">{{t "orchestrator.phases"}}</h3>
8
+ <p class="text-xs text-gray-400 dark:text-gray-500">{{t "orchestrator.phases-description"}}</p>
9
+ </div>
10
+ <Button @icon="plus" @text={{t "orchestrator.add-phase"}} @type="default" @size="xs" @onClick={{this.addPhase}} />
11
+ </div>
12
+
13
+ <div class="flex flex-1 overflow-hidden">
14
+
15
+ {{! Phase list }}
16
+ <div class="w-48 border-r dark:border-gray-700 flex flex-col overflow-y-auto flex-shrink-0">
17
+ {{#if @phases.length}}
18
+ {{#each @phases as |phase index|}}
19
+ <div
20
+ class="flex items-center gap-2 px-2 py-2 border-b dark:border-gray-700 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors
21
+ {{if (eq this.editingIndex index) 'bg-indigo-50 dark:bg-indigo-900/20 border-l-2 border-l-indigo-500' ''}}"
22
+ {{on "click" (fn this.editPhase index)}}
23
+ >
24
+ <div class="w-5 h-5 rounded-full bg-indigo-100 dark:bg-indigo-900/40 flex items-center justify-center flex-shrink-0">
25
+ <FaIcon @icon={{this.modeIcon phase.mode}} class="text-indigo-600 dark:text-indigo-400" @size="xs" />
26
+ </div>
27
+ <div class="flex-1 min-w-0">
28
+ <p class="text-xs font-medium text-gray-800 dark:text-gray-100 truncate">{{phase.label}}</p>
29
+ <p class="text-2xs text-gray-400 truncate">{{t "orchestrator.phase-n" n=(add index 1)}}</p>
30
+ </div>
31
+ <div class="flex flex-col gap-0.5 flex-shrink-0">
32
+ <button type="button" class="text-gray-300 hover:text-gray-500 dark:hover:text-gray-200" {{on "click" (fn this.movePhaseUp index)}} title="Move up">
33
+ <FaIcon @icon="chevron-up" @size="xs" />
34
+ </button>
35
+ <button type="button" class="text-gray-300 hover:text-gray-500 dark:hover:text-gray-200" {{on "click" (fn this.movePhaseDown index)}} title="Move down">
36
+ <FaIcon @icon="chevron-down" @size="xs" />
37
+ </button>
38
+ </div>
39
+ </div>
40
+ {{/each}}
41
+ {{else}}
42
+ <div class="flex flex-col items-center justify-center flex-1 py-8 text-gray-400 dark:text-gray-600">
43
+ <FaIcon @icon="list-ol" class="mb-2 text-xl" />
44
+ <p class="text-xs text-center px-2">{{t "orchestrator.no-phases"}}</p>
45
+ </div>
46
+ {{/if}}
47
+ </div>
48
+
49
+ {{! Phase editor }}
50
+ <div class="flex-1 overflow-y-auto">
51
+ {{#if this.isEditing}}
52
+ <div class="p-3 space-y-3">
53
+
54
+ {{! Phase label }}
55
+ <InputGroup @name={{t "orchestrator.phase-label"}}>
56
+ <Input @type="text" @value={{this.draftPhase.label}} class="form-input form-input-sm w-full" {{on "input" this.setDraftLabel}} />
57
+ </InputGroup>
58
+
59
+ {{! Mode selection }}
60
+ <InputGroup @name={{t "orchestrator.mode"}}>
61
+ <div class="grid grid-cols-2 gap-1">
62
+ {{#each this.modeOptions as |opt|}}
63
+ <button
64
+ type="button"
65
+ class="flex items-center gap-1.5 px-2 py-1.5 rounded border text-xs transition-colors
66
+ {{if
67
+ (eq this.draftPhase.mode opt.value)
68
+ 'bg-indigo-50 dark:bg-indigo-900/30 border-indigo-400 dark:border-indigo-600 text-indigo-700 dark:text-indigo-300'
69
+ 'border-gray-200 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:border-gray-300 dark:hover:border-gray-500'
70
+ }}"
71
+ {{on "click" (fn this.setDraftMode opt.value)}}
72
+ >
73
+ <FaIcon @icon={{this.modeIcon opt.value}} @size="xs" />
74
+ {{opt.label}}
75
+ </button>
76
+ {{/each}}
77
+ </div>
78
+ </InputGroup>
79
+
80
+ {{! Order statuses to include }}
81
+ <InputGroup @name={{t "orchestrator.include-order-statuses"}}>
82
+ <div class="flex flex-wrap gap-1">
83
+ {{#each this.orderStatusOptions as |opt|}}
84
+ <button
85
+ type="button"
86
+ class="px-1.5 py-0.5 rounded text-xs border transition-colors
87
+ {{if
88
+ (includes opt.value this.draftPhase.orderStatuses)
89
+ 'bg-indigo-100 dark:bg-indigo-900/40 border-indigo-400 text-indigo-700 dark:text-indigo-300'
90
+ 'border-gray-200 dark:border-gray-600 text-gray-500 dark:text-gray-400'
91
+ }}"
92
+ {{on "click" (fn this.toggleDraftOrderStatus opt.value)}}
93
+ >
94
+ {{opt.label}}
95
+ </button>
96
+ {{/each}}
97
+ </div>
98
+ </InputGroup>
99
+
100
+ {{! Engine }}
101
+ <InputGroup @name={{t "orchestrator.engine"}}>
102
+ <Select
103
+ @value={{this.draftPhase.engine}}
104
+ @options={{@availableEngines}}
105
+ @optionLabel="name"
106
+ @optionValue="id"
107
+ @onSelect={{this.setDraftEngine}}
108
+ class="form-input-sm w-full"
109
+ />
110
+ </InputGroup>
111
+
112
+ {{! Constraint toggles }}
113
+ <InputGroup @name={{t "orchestrator.constraints"}}>
114
+ <div class="space-y-1.5">
115
+ <div class="flex items-center justify-between">
116
+ <span class="text-xs text-gray-600 dark:text-gray-400">{{t "orchestrator.respect-capacity"}}</span>
117
+ <Toggle @isToggled={{this.draftPhase.respectCapacity}} @onToggle={{fn this.setDraftOption "respectCapacity"}} />
118
+ </div>
119
+ <div class="flex items-center justify-between">
120
+ <span class="text-xs text-gray-600 dark:text-gray-400">{{t "orchestrator.respect-skills"}}</span>
121
+ <Toggle @isToggled={{this.draftPhase.respectSkills}} @onToggle={{fn this.setDraftOption "respectSkills"}} />
122
+ </div>
123
+ <div class="flex items-center justify-between">
124
+ <span class="text-xs text-gray-600 dark:text-gray-400">{{t "orchestrator.balance-workload"}}</span>
125
+ <Toggle @isToggled={{this.draftPhase.balanceWorkload}} @onToggle={{fn this.setDraftOption "balanceWorkload"}} />
126
+ </div>
127
+ <div class="flex items-center justify-between">
128
+ <span class="text-xs text-gray-600 dark:text-gray-400">{{t "orchestrator.return-to-depot"}}</span>
129
+ <Toggle @isToggled={{this.draftPhase.returnToDepot}} @onToggle={{fn this.setDraftOption "returnToDepot"}} />
130
+ </div>
131
+ <div class="flex items-center justify-between">
132
+ <span class="text-xs text-gray-600 dark:text-gray-400">{{t "orchestrator.auto-commit"}}</span>
133
+ <Toggle @isToggled={{this.draftPhase.autoCommit}} @onToggle={{fn this.setDraftOption "autoCommit"}} />
134
+ </div>
135
+ </div>
136
+ </InputGroup>
137
+
138
+ {{! Save / Cancel / Delete }}
139
+ <div class="flex items-center gap-2 pt-1 border-t dark:border-gray-700">
140
+ <Button @text={{t "orchestrator.save-phase"}} @type="primary" @size="xs" @onClick={{this.saveDraftPhase}} />
141
+ <Button @text={{t "orchestrator.cancel"}} @type="default" @size="xs" @onClick={{this.closePhasEditor}} />
142
+ <div class="flex-1"></div>
143
+ <Button @icon="trash" @type="danger" @size="xs" @onClick={{fn this.removePhase this.editingIndex}} />
144
+ </div>
145
+ </div>
146
+ {{else}}
147
+ <div class="flex flex-col items-center justify-center h-full py-12 text-gray-400 dark:text-gray-600">
148
+ <FaIcon @icon="hand-pointer" class="mb-2 text-2xl" />
149
+ <p class="text-xs">{{t "orchestrator.select-phase-to-edit"}}</p>
150
+ </div>
151
+ {{/if}}
152
+ </div>
153
+ </div>
154
+
155
+ {{! Run all phases button }}
156
+ {{#if @phases.length}}
157
+ <div class="px-3 py-2 border-t dark:border-gray-700 flex-shrink-0 bg-gray-50 dark:bg-gray-800">
158
+ <Button @icon="bolt" @text={{t "orchestrator.run-phases" count=@phases.length}} @type="primary" @size="sm" @onClick={{fn @onRunPhases @phases}} />
159
+ </div>
160
+ {{/if}}
161
+
162
+ </div>