@fleetbase/fleetops-engine 0.6.37 → 0.6.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. package/addon/components/activity/form.hbs +2 -12
  2. package/addon/components/activity/logic-builder.hbs +1 -1
  3. package/addon/components/custom-entity/form.hbs +4 -20
  4. package/addon/components/device/form.hbs +3 -15
  5. package/addon/components/device/panel-header.hbs +13 -2
  6. package/addon/components/driver/details.hbs +33 -2
  7. package/addon/components/driver/form.hbs +42 -0
  8. package/addon/components/driver/panel-header.hbs +8 -1
  9. package/addon/components/driver/schedule.hbs +115 -76
  10. package/addon/components/driver/schedule.js +349 -157
  11. package/addon/components/driver-onboard-settings.hbs +2 -8
  12. package/addon/components/entity-field-editing-settings.hbs +2 -5
  13. package/addon/components/equipment/card.hbs +49 -0
  14. package/addon/components/equipment/card.js +6 -0
  15. package/addon/components/equipment/details.hbs +83 -44
  16. package/addon/components/equipment/form.hbs +111 -41
  17. package/addon/components/equipment/form.js +78 -10
  18. package/addon/components/equipment/panel-header.hbs +36 -0
  19. package/addon/components/equipment/panel-header.js +2 -0
  20. package/addon/components/fleet/driver-listing.hbs +3 -1
  21. package/addon/components/fleet/vehicle-listing.hbs +4 -7
  22. package/addon/components/fleet-panel/vehicle-listing.hbs +1 -6
  23. package/addon/components/fuel-report/form.hbs +1 -5
  24. package/addon/components/layout/fleet-ops-sidebar.hbs +40 -36
  25. package/addon/components/layout/fleet-ops-sidebar.js +61 -10
  26. package/addon/components/maintenance/cost-panel.hbs +176 -0
  27. package/addon/components/maintenance/cost-panel.js +241 -0
  28. package/addon/components/maintenance/details.hbs +123 -60
  29. package/addon/components/maintenance/form.hbs +138 -78
  30. package/addon/components/maintenance/form.js +131 -6
  31. package/addon/components/maintenance/panel-header.hbs +31 -0
  32. package/addon/components/maintenance/panel-header.js +2 -0
  33. package/addon/components/maintenance-schedule/details.hbs +260 -0
  34. package/addon/components/maintenance-schedule/details.js +158 -0
  35. package/addon/components/maintenance-schedule/form.hbs +287 -0
  36. package/addon/components/maintenance-schedule/form.js +199 -0
  37. package/addon/components/map/container.hbs +1 -1
  38. package/addon/components/map/drawer/device-event-listing.hbs +1 -1
  39. package/addon/components/map/drawer/driver-listing.hbs +1 -6
  40. package/addon/components/map/drawer/place-listing.hbs +1 -6
  41. package/addon/components/map/drawer/vehicle-listing.hbs +1 -6
  42. package/addon/components/map/drawer.hbs +8 -1
  43. package/addon/components/map/leaflet-live-map.hbs +2 -1
  44. package/addon/components/map/toolbar/visibility-control-panel.hbs +1 -1
  45. package/addon/components/map/toolbar.hbs +4 -29
  46. package/addon/components/modals/add-driver-shift.hbs +155 -0
  47. package/addon/components/modals/add-driver-shift.js +210 -0
  48. package/addon/components/modals/bulk-assign-orders.hbs +67 -0
  49. package/addon/components/modals/driver-shift.hbs +43 -0
  50. package/addon/components/modals/driver-shift.js +56 -0
  51. package/addon/components/modals/entity-form.hbs +5 -28
  52. package/addon/components/modals/orchestrator-import.hbs +351 -0
  53. package/addon/components/modals/orchestrator-import.js +807 -0
  54. package/addon/components/modals/order-config-new-status.hbs +1 -5
  55. package/addon/components/modals/scheduling-conflict.hbs +47 -0
  56. package/addon/components/modals/scheduling-conflict.js +53 -0
  57. package/addon/components/modals/send-work-order.hbs +91 -0
  58. package/addon/components/modals/send-work-order.js +3 -0
  59. package/addon/components/modals/service-area-form.hbs +1 -6
  60. package/addon/components/modals/set-driver-availability.hbs +50 -0
  61. package/addon/components/modals/set-driver-availability.js +57 -0
  62. package/addon/components/modals/update-order-activity.hbs +13 -9
  63. package/addon/components/modals/user-form.hbs +1 -5
  64. package/addon/components/modals/vehicle-form.hbs +17 -102
  65. package/addon/components/modals/vendor-form.hbs +15 -82
  66. package/addon/components/orchestrator/card-fields-settings.hbs +76 -0
  67. package/addon/components/orchestrator/card-fields-settings.js +134 -0
  68. package/addon/components/orchestrator/order-pool.hbs +264 -0
  69. package/addon/components/orchestrator/order-pool.js +394 -0
  70. package/addon/components/orchestrator/phase-builder.hbs +162 -0
  71. package/addon/components/orchestrator/phase-builder.js +162 -0
  72. package/addon/components/orchestrator/plan-viewer.hbs +278 -0
  73. package/addon/components/orchestrator/plan-viewer.js +342 -0
  74. package/addon/components/orchestrator/resource-panel.hbs +301 -0
  75. package/addon/components/orchestrator/resource-panel.js +106 -0
  76. package/addon/components/orchestrator-workbench.hbs +318 -0
  77. package/addon/components/orchestrator-workbench.js +1087 -0
  78. package/addon/components/order/details/custom-fields.hbs +10 -1
  79. package/addon/components/order/details/detail.hbs +37 -2
  80. package/addon/components/order/details/detail.js +0 -1
  81. package/addon/components/order/details/integrated-vendor-details.hbs +4 -4
  82. package/addon/components/order/details/notes.hbs +1 -7
  83. package/addon/components/order/details/route.hbs +1 -5
  84. package/addon/components/order/form/details.hbs +44 -10
  85. package/addon/components/order/form/details.js +57 -0
  86. package/addon/components/order/form/notes.hbs +1 -7
  87. package/addon/components/order/form/payload.hbs +1 -7
  88. package/addon/components/order/form/route.hbs +3 -15
  89. package/addon/components/order/header.hbs +1 -7
  90. package/addon/components/order/route-editor.hbs +4 -25
  91. package/addon/components/order/schedule-card.hbs +102 -95
  92. package/addon/components/order/schedule-card.js +8 -3
  93. package/addon/components/order-config-manager/details.hbs +2 -10
  94. package/addon/components/order-config-manager/entities.hbs +1 -7
  95. package/addon/components/order-config-manager.hbs +1 -6
  96. package/addon/components/part/card.hbs +49 -0
  97. package/addon/components/part/card.js +6 -0
  98. package/addon/components/part/details.hbs +102 -56
  99. package/addon/components/part/form.hbs +131 -56
  100. package/addon/components/part/form.js +78 -11
  101. package/addon/components/part/panel-header.hbs +36 -0
  102. package/addon/components/part/panel-header.js +2 -0
  103. package/addon/components/place/form.hbs +1 -7
  104. package/addon/components/sensor/details.hbs +1 -1
  105. package/addon/components/sensor/form.hbs +5 -3
  106. package/addon/components/sensor/panel-header.hbs +8 -1
  107. package/addon/components/service-area/form.hbs +1 -6
  108. package/addon/components/service-rate/form.hbs +12 -60
  109. package/addon/components/telematic/form.hbs +6 -2
  110. package/addon/components/vehicle/details/maintenance-history.hbs +42 -0
  111. package/addon/components/vehicle/details/maintenance-history.js +32 -0
  112. package/addon/components/vehicle/details/schedules.hbs +40 -0
  113. package/addon/components/vehicle/details/schedules.js +32 -0
  114. package/addon/components/vehicle/details/work-orders.hbs +42 -0
  115. package/addon/components/vehicle/details/work-orders.js +32 -0
  116. package/addon/components/vehicle/details.hbs +30 -0
  117. package/addon/components/vehicle/form.hbs +39 -0
  118. package/addon/components/vehicle/panel-header.hbs +19 -49
  119. package/addon/components/warranty/details.hbs +3 -2
  120. package/addon/components/warranty/form.hbs +3 -17
  121. package/addon/components/work-order/details.hbs +135 -40
  122. package/addon/components/work-order/form.hbs +178 -45
  123. package/addon/components/work-order/form.js +197 -4
  124. package/addon/components/work-order/panel-header.hbs +31 -0
  125. package/addon/components/work-order/panel-header.js +2 -0
  126. package/addon/controllers/connectivity/devices/index/details.js +1 -1
  127. package/addon/controllers/maintenance/equipment/index/details/index.js +0 -1
  128. package/addon/controllers/maintenance/equipment/index/details.js +36 -1
  129. package/addon/controllers/maintenance/equipment/index/edit.js +56 -1
  130. package/addon/controllers/maintenance/equipment/index/new.js +32 -1
  131. package/addon/controllers/maintenance/equipment/index.js +127 -113
  132. package/addon/controllers/maintenance/maintenances/index/details/index.js +3 -0
  133. package/addon/controllers/maintenance/maintenances/index/details.js +54 -0
  134. package/addon/controllers/maintenance/maintenances/index/edit.js +68 -0
  135. package/addon/controllers/maintenance/maintenances/index/new.js +34 -0
  136. package/addon/controllers/maintenance/maintenances/index.js +191 -0
  137. package/addon/controllers/maintenance/parts/index/details/index.js +0 -1
  138. package/addon/controllers/maintenance/parts/index/details.js +36 -1
  139. package/addon/controllers/maintenance/parts/index/edit.js +56 -1
  140. package/addon/controllers/maintenance/parts/index/new.js +32 -1
  141. package/addon/controllers/maintenance/parts/index.js +135 -113
  142. package/addon/controllers/maintenance/schedules/index/details.js +115 -0
  143. package/addon/controllers/maintenance/schedules/index/edit.js +41 -0
  144. package/addon/controllers/maintenance/schedules/index/new.js +33 -0
  145. package/addon/controllers/maintenance/schedules/index.js +280 -0
  146. package/addon/controllers/maintenance/work-orders/index/details.js +41 -1
  147. package/addon/controllers/maintenance/work-orders/index/edit.js +67 -1
  148. package/addon/controllers/maintenance/work-orders/index/new.js +43 -1
  149. package/addon/controllers/maintenance/work-orders/index.js +105 -113
  150. package/addon/controllers/management/drivers/index/details.js +6 -1
  151. package/addon/controllers/management/vehicles/index/details.js +65 -0
  152. package/addon/controllers/management/vehicles/index.js +18 -0
  153. package/addon/controllers/operations/orchestrator.js +10 -0
  154. package/addon/controllers/operations/orders/index/details.js +5 -0
  155. package/addon/controllers/operations/orders/index.js +6 -0
  156. package/addon/controllers/operations/scheduler/fleet-schedule.js +341 -0
  157. package/addon/controllers/operations/scheduler/index.js +799 -275
  158. package/addon/controllers/operations/scheduler.js +21 -0
  159. package/addon/controllers/settings/orchestrator.js +70 -0
  160. package/addon/controllers/settings/scheduling.js +155 -0
  161. package/addon/extension.js +74 -1
  162. package/addon/instance-initializers/register-vroom-allocation.js +27 -0
  163. package/addon/models/maintenance-schedule.js +61 -0
  164. package/addon/routes/maintenance/equipment/index/details.js +27 -1
  165. package/addon/routes/maintenance/equipment/index/edit.js +27 -1
  166. package/addon/routes/maintenance/maintenances/index/details/index.js +3 -0
  167. package/addon/routes/maintenance/maintenances/index/details.js +29 -0
  168. package/addon/routes/maintenance/maintenances/index/edit.js +29 -0
  169. package/addon/routes/maintenance/maintenances/index/new.js +3 -0
  170. package/addon/routes/maintenance/maintenances/index.js +23 -0
  171. package/addon/routes/maintenance/maintenances.js +3 -0
  172. package/addon/routes/maintenance/parts/index/details.js +27 -1
  173. package/addon/routes/maintenance/parts/index/edit.js +27 -1
  174. package/addon/routes/maintenance/schedules/index/details/index.js +2 -0
  175. package/addon/routes/maintenance/schedules/index/details/work-orders.js +11 -0
  176. package/addon/routes/maintenance/schedules/index/details.js +25 -0
  177. package/addon/routes/maintenance/schedules/index/edit.js +25 -0
  178. package/addon/routes/maintenance/schedules/index/new.js +2 -0
  179. package/addon/routes/maintenance/schedules/index.js +21 -0
  180. package/addon/routes/maintenance/schedules.js +2 -0
  181. package/addon/routes/maintenance/work-orders/index/details.js +27 -1
  182. package/addon/routes/maintenance/work-orders/index/edit.js +27 -1
  183. package/addon/routes/management/vehicles/index/details/maintenance-history.js +3 -0
  184. package/addon/routes/management/vehicles/index/details/schedules.js +3 -0
  185. package/addon/routes/management/vehicles/index/details/work-orders.js +3 -0
  186. package/addon/routes/operations/orchestrator.js +23 -0
  187. package/addon/routes/operations/orders/index/details/virtual.js +23 -0
  188. package/addon/routes/operations/scheduler/fleet-schedule.js +28 -0
  189. package/addon/routes/operations/scheduler/index.js +48 -26
  190. package/addon/routes/operations/scheduler.js +14 -1
  191. package/addon/routes/settings/orchestrator.js +27 -0
  192. package/addon/routes/settings/scheduling.js +3 -0
  193. package/addon/routes.js +33 -1
  194. package/addon/services/driver-actions.js +40 -7
  195. package/addon/services/driver-scheduling.js +4 -1
  196. package/addon/services/equipment-actions.js +15 -5
  197. package/addon/services/leaflet-map-manager.js +14 -6
  198. package/addon/services/maintenance-actions.js +17 -14
  199. package/addon/services/maintenance-schedule-actions.js +118 -0
  200. package/addon/services/orchestration-engine-interface.js +49 -0
  201. package/addon/services/orchestration-engine.js +74 -0
  202. package/addon/services/order-actions.js +15 -0
  203. package/addon/services/order-allocation.js +116 -0
  204. package/addon/services/part-actions.js +12 -2
  205. package/addon/services/scheduling.js +316 -0
  206. package/addon/services/vehicle-actions.js +70 -7
  207. package/addon/services/vroom-allocation-engine.js +45 -0
  208. package/addon/services/work-order-actions.js +80 -0
  209. package/addon/styles/fleetops-engine.css +1658 -0
  210. package/addon/templates/analytics/reports/index/edit.hbs +1 -1
  211. package/addon/templates/analytics/reports/index/new.hbs +1 -1
  212. package/addon/templates/application.hbs +6 -1
  213. package/addon/templates/connectivity/devices/index/details/events.hbs +0 -1
  214. package/addon/templates/connectivity/devices.hbs +0 -1
  215. package/addon/templates/connectivity/events/index/details.hbs +0 -1
  216. package/addon/templates/connectivity/events.hbs +0 -1
  217. package/addon/templates/connectivity/sensors.hbs +0 -1
  218. package/addon/templates/connectivity/telematics/index/details/devices.hbs +0 -1
  219. package/addon/templates/connectivity/telematics/index/details/events.hbs +0 -1
  220. package/addon/templates/connectivity/telematics/index/details/sensors.hbs +0 -1
  221. package/addon/templates/connectivity/telematics.hbs +0 -1
  222. package/addon/templates/connectivity/tracking.hbs +0 -1
  223. package/addon/templates/connectivity.hbs +0 -1
  224. package/addon/templates/maintenance/equipment/index/details/index.hbs +1 -2
  225. package/addon/templates/maintenance/equipment/index/details.hbs +15 -2
  226. package/addon/templates/maintenance/equipment/index/edit.hbs +12 -2
  227. package/addon/templates/maintenance/equipment/index/new.hbs +1 -2
  228. package/addon/templates/maintenance/equipment/index.hbs +48 -13
  229. package/addon/templates/maintenance/equipment.hbs +0 -1
  230. package/addon/templates/maintenance/maintenances/index/details/index.hbs +1 -0
  231. package/addon/templates/maintenance/maintenances/index/details.hbs +15 -0
  232. package/addon/templates/maintenance/maintenances/index/edit.hbs +12 -0
  233. package/addon/templates/maintenance/maintenances/index/new.hbs +11 -0
  234. package/addon/templates/maintenance/maintenances/index.hbs +14 -0
  235. package/addon/templates/maintenance/maintenances.hbs +1 -0
  236. package/addon/templates/maintenance/parts/index/details/index.hbs +1 -2
  237. package/addon/templates/maintenance/parts/index/details.hbs +15 -2
  238. package/addon/templates/maintenance/parts/index/edit.hbs +12 -2
  239. package/addon/templates/maintenance/parts/index/new.hbs +1 -2
  240. package/addon/templates/maintenance/parts/index.hbs +48 -13
  241. package/addon/templates/maintenance/parts.hbs +0 -1
  242. package/addon/templates/maintenance/schedules/index/details/index.hbs +1 -0
  243. package/addon/templates/maintenance/schedules/index/details/work-orders.hbs +39 -0
  244. package/addon/templates/maintenance/schedules/index/details.hbs +14 -0
  245. package/addon/templates/maintenance/schedules/index/edit.hbs +12 -0
  246. package/addon/templates/maintenance/schedules/index/new.hbs +11 -0
  247. package/addon/templates/maintenance/schedules/index.hbs +40 -0
  248. package/addon/templates/maintenance/schedules.hbs +1 -0
  249. package/addon/templates/maintenance/work-orders/index/details.hbs +2 -1
  250. package/addon/templates/maintenance/work-orders/index/edit.hbs +2 -4
  251. package/addon/templates/maintenance/work-orders/index/new.hbs +1 -2
  252. package/addon/templates/maintenance/work-orders.hbs +0 -1
  253. package/addon/templates/management/contacts/customers/edit.hbs +1 -2
  254. package/addon/templates/management/contacts/customers/new.hbs +1 -2
  255. package/addon/templates/management/contacts/customers.hbs +1 -1
  256. package/addon/templates/management/contacts/index/edit.hbs +1 -2
  257. package/addon/templates/management/contacts/index/new.hbs +1 -2
  258. package/addon/templates/management/drivers/index/details/orders.hbs +0 -1
  259. package/addon/templates/management/drivers/index/edit.hbs +1 -2
  260. package/addon/templates/management/drivers/index/new.hbs +1 -2
  261. package/addon/templates/management/fleets/index/edit.hbs +1 -2
  262. package/addon/templates/management/fleets/index/new.hbs +1 -2
  263. package/addon/templates/management/fleets/index.hbs +1 -2
  264. package/addon/templates/management/fuel-reports/index/edit.hbs +1 -2
  265. package/addon/templates/management/fuel-reports/index/new.hbs +1 -2
  266. package/addon/templates/management/fuel-reports/index.hbs +1 -2
  267. package/addon/templates/management/issues/index/edit.hbs +1 -2
  268. package/addon/templates/management/issues/index/new.hbs +1 -2
  269. package/addon/templates/management/issues/index.hbs +1 -2
  270. package/addon/templates/management/places/index/details/activity.hbs +0 -1
  271. package/addon/templates/management/places/index/details/comments.hbs +0 -1
  272. package/addon/templates/management/places/index/details/documents.hbs +0 -1
  273. package/addon/templates/management/places/index/details/map.hbs +0 -1
  274. package/addon/templates/management/places/index/details/operations.hbs +0 -1
  275. package/addon/templates/management/places/index/details/performance.hbs +0 -1
  276. package/addon/templates/management/places/index/details/rules.hbs +0 -1
  277. package/addon/templates/management/vehicles/index/details/equipment.hbs +0 -1
  278. package/addon/templates/management/vehicles/index/details/maintenance-history.hbs +2 -0
  279. package/addon/templates/management/vehicles/index/details/schedules.hbs +2 -0
  280. package/addon/templates/management/vehicles/index/details/work-orders.hbs +2 -0
  281. package/addon/templates/management/vehicles/index/details.hbs +1 -1
  282. package/addon/templates/management/vehicles/index/edit.hbs +1 -2
  283. package/addon/templates/management/vehicles/index/new.hbs +1 -2
  284. package/addon/templates/management/vendors/index/edit.hbs +1 -2
  285. package/addon/templates/management/vendors/index/new.hbs +1 -2
  286. package/addon/templates/management/vendors/index.hbs +1 -2
  287. package/addon/templates/management/vendors/integrated.hbs +1 -2
  288. package/addon/templates/operations/orchestrator.hbs +1 -0
  289. package/addon/templates/operations/orders/index/details/virtual.hbs +1 -0
  290. package/addon/templates/operations/orders/index.hbs +6 -1
  291. package/addon/templates/operations/scheduler/fleet-schedule.hbs +41 -0
  292. package/addon/templates/operations/scheduler/index.hbs +147 -88
  293. package/addon/templates/operations/scheduler.hbs +7 -1
  294. package/addon/templates/settings/avatars.hbs +1 -1
  295. package/addon/templates/settings/orchestrator.hbs +65 -0
  296. package/addon/templates/settings/payments/index.hbs +1 -5
  297. package/addon/templates/settings/scheduling.hbs +82 -0
  298. package/addon/utils/create-full-calendar-event-from-order.js +52 -14
  299. package/addon/utils/create-full-calendar-event-from-schedule-item.js +50 -0
  300. package/addon/utils/fleet-ops-options.js +254 -0
  301. package/addon/utils/route-colors.js +99 -0
  302. package/addon/utils/to-calendar-date.js +70 -0
  303. package/app/components/driver/schedule.js +1 -0
  304. package/app/components/maintenance/cost-panel.js +1 -0
  305. package/app/components/maintenance/panel-header.js +1 -0
  306. package/app/components/maintenance-schedule/details.js +1 -0
  307. package/app/components/maintenance-schedule/form.js +1 -0
  308. package/app/components/modals/add-driver-shift.js +1 -0
  309. package/app/components/modals/bulk-assign-orders.js +1 -0
  310. package/app/components/modals/driver-shift.js +1 -0
  311. package/app/components/modals/orchestrator-import.js +1 -0
  312. package/app/components/modals/scheduling-conflict.js +1 -0
  313. package/app/components/modals/send-work-order.js +1 -0
  314. package/app/components/modals/set-driver-availability.js +1 -0
  315. package/app/components/orchestrator/card-fields-settings.js +1 -0
  316. package/app/components/orchestrator/order-pool.js +1 -0
  317. package/app/components/orchestrator/phase-builder.js +1 -0
  318. package/app/components/orchestrator/plan-viewer.js +1 -0
  319. package/app/components/orchestrator/resource-panel.js +1 -0
  320. package/app/components/orchestrator-workbench.js +1 -0
  321. package/app/components/vehicle/details/maintenance-history.js +1 -0
  322. package/app/components/vehicle/details/schedules.js +1 -0
  323. package/app/components/vehicle/details/work-orders.js +1 -0
  324. package/app/controllers/operations/orchestrator.js +1 -0
  325. package/app/controllers/settings/orchestrator.js +1 -0
  326. package/app/controllers/settings/scheduling.js +1 -0
  327. package/app/routes/operations/orchestrator.js +1 -0
  328. package/app/routes/operations/orders/index/details/virtual.js +1 -0
  329. package/app/routes/settings/orchestrator.js +1 -0
  330. package/app/routes/settings/scheduling.js +1 -0
  331. package/app/services/maintenance-schedule-actions.js +1 -0
  332. package/app/services/orchestration-engine-interface.js +1 -0
  333. package/app/services/orchestration-engine.js +1 -0
  334. package/app/services/order-allocation.js +1 -0
  335. package/app/services/scheduling.js +1 -0
  336. package/app/services/vroom-allocation-engine.js +1 -0
  337. package/app/templates/operations/orders/index/details/virtual.js +1 -0
  338. package/app/templates/settings/scheduling.js +1 -0
  339. package/app/utils/create-full-calendar-event-from-schedule-item.js +1 -0
  340. package/app/utils/route-colors.js +1 -0
  341. package/composer.json +5 -3
  342. package/extension.json +1 -1
  343. package/package.json +6 -5
  344. package/server/config/fleetops.php +20 -1
  345. package/server/migrations/2025_08_28_054927_create_parts_table.php +2 -2
  346. package/server/migrations/2025_08_28_054932_add_public_id_to_maintenance_tables.php +45 -0
  347. package/server/migrations/2025_09_01_000001_create_maintenance_schedules_table.php +88 -0
  348. package/server/migrations/2026_04_01_000001_fix_monetary_columns_in_parts_table.php +48 -0
  349. package/server/migrations/2026_04_01_000003_add_photo_uuid_to_equipment_and_parts_tables.php +61 -0
  350. package/server/migrations/2026_04_01_000004_add_public_id_to_equipments_table.php +38 -0
  351. package/server/migrations/2026_04_01_000005_add_missing_columns_to_parts_table.php +67 -0
  352. package/server/migrations/2026_04_04_000001_add_reminder_offsets_to_maintenance_schedules.php +44 -0
  353. package/server/migrations/2026_04_08_000001_add_orchestrator_columns_to_vehicles_table.php +58 -0
  354. package/server/migrations/2026_04_08_000002_add_orchestrator_columns_to_drivers_table.php +41 -0
  355. package/server/migrations/2026_04_08_000003_add_orchestrator_columns_to_orders_table.php +38 -0
  356. package/server/migrations/2026_04_08_000004_add_orchestrator_columns_to_payloads_table.php +41 -0
  357. package/server/migrations/2026_04_08_000005_add_orchestrator_columns_to_waypoints_table.php +38 -0
  358. package/server/migrations/2026_04_09_000001_create_manifests_table.php +48 -0
  359. package/server/migrations/2026_04_09_000002_create_manifest_stops_table.php +48 -0
  360. package/server/migrations/2026_04_09_000003_add_manifest_uuid_to_orders_table.php +28 -0
  361. package/server/migrations/2026_04_13_000001_add_pod_notes_columns_to_waypoints_table.php +39 -0
  362. package/server/resources/views/mail/maintenance-schedule-reminder.blade.php +59 -0
  363. package/server/resources/views/mail/work-order-dispatched.blade.php +67 -0
  364. package/server/src/Auth/Schemas/FleetOps.php +44 -0
  365. package/server/src/Console/Commands/ProcessMaintenanceTriggers.php +150 -0
  366. package/server/src/Console/Commands/SendMaintenanceReminders.php +128 -0
  367. package/server/src/Http/Controllers/Internal/v1/DriverController.php +1 -0
  368. package/server/src/Http/Controllers/Internal/v1/EquipmentController.php +27 -0
  369. package/server/src/Http/Controllers/Internal/v1/LiveController.php +9 -2
  370. package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +165 -0
  371. package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +304 -0
  372. package/server/src/Http/Controllers/Internal/v1/ManifestController.php +138 -0
  373. package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +975 -0
  374. package/server/src/Http/Controllers/Internal/v1/OrderController.php +42 -0
  375. package/server/src/Http/Controllers/Internal/v1/PartController.php +27 -0
  376. package/server/src/Http/Controllers/Internal/v1/SettingController.php +118 -0
  377. package/server/src/Http/Controllers/Internal/v1/Traits/DriverSchedulingTrait.php +214 -0
  378. package/server/src/Http/Controllers/Internal/v1/WorkOrderController.php +68 -0
  379. package/server/src/Http/Resources/v1/Driver.php +1 -0
  380. package/server/src/Http/Resources/v1/Maintenance.php +138 -0
  381. package/server/src/Http/Resources/v1/MaintenanceSchedule.php +137 -0
  382. package/server/src/Http/Resources/v1/Orchestrator/Order.php +116 -0
  383. package/server/src/Http/Resources/v1/Order.php +7 -4
  384. package/server/src/Http/Resources/v1/Waypoint.php +7 -0
  385. package/server/src/Http/Resources/v1/WorkOrder.php +136 -0
  386. package/server/src/Imports/EquipmentImport.php +32 -0
  387. package/server/src/Imports/MaintenanceImport.php +32 -0
  388. package/server/src/Imports/MaintenanceScheduleImport.php +32 -0
  389. package/server/src/Imports/PartImport.php +32 -0
  390. package/server/src/Imports/WorkOrderImport.php +32 -0
  391. package/server/src/Jobs/ProcessAllocationJob.php +119 -0
  392. package/server/src/Listeners/HandleDeliveryCompletion.php +47 -0
  393. package/server/src/Listeners/NotifyDriverOnShiftChange.php +63 -0
  394. package/server/src/Mail/MaintenanceScheduleReminder.php +68 -0
  395. package/server/src/Mail/WorkOrderDispatched.php +58 -0
  396. package/server/src/Models/Asset.php +2 -2
  397. package/server/src/Models/Device.php +1 -1
  398. package/server/src/Models/Driver.php +82 -4
  399. package/server/src/Models/Equipment.php +62 -2
  400. package/server/src/Models/Maintenance.php +127 -9
  401. package/server/src/Models/MaintenanceSchedule.php +353 -0
  402. package/server/src/Models/Manifest.php +214 -0
  403. package/server/src/Models/ManifestStop.php +162 -0
  404. package/server/src/Models/Order.php +70 -0
  405. package/server/src/Models/OrderConfig.php +5 -2
  406. package/server/src/Models/Part.php +69 -3
  407. package/server/src/Models/Payload.php +7 -2
  408. package/server/src/Models/Place.php +1 -1
  409. package/server/src/Models/Sensor.php +1 -1
  410. package/server/src/Models/ServiceQuote.php +1 -1
  411. package/server/src/Models/Vehicle.php +20 -1
  412. package/server/src/Models/Warranty.php +1 -1
  413. package/server/src/Models/Waypoint.php +7 -1
  414. package/server/src/Models/WorkOrder.php +122 -12
  415. package/server/src/Notifications/DriverShiftChanged.php +110 -0
  416. package/server/src/Observers/WorkOrderObserver.php +107 -0
  417. package/server/src/Orchestration/Contracts/OrchestrationEngineInterface.php +63 -0
  418. package/server/src/Orchestration/Engines/DriverAssignmentEngine.php +265 -0
  419. package/server/src/Orchestration/Engines/GreedyOrchestrationEngine.php +155 -0
  420. package/server/src/Orchestration/Engines/RouteSequencingEngine.php +272 -0
  421. package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +192 -0
  422. package/server/src/Orchestration/OrchestrationEngineRegistry.php +83 -0
  423. package/server/src/Orchestration/Support/OrchestrationPayloadBuilder.php +290 -0
  424. package/server/src/Providers/EventServiceProvider.php +7 -1
  425. package/server/src/Providers/FleetOpsServiceProvider.php +42 -15
  426. package/server/src/routes.php +65 -4
  427. package/translations/ar-ae.yml +44 -12
  428. package/translations/bg-bg.yaml +51 -10
  429. package/translations/en-us.yaml +444 -1
  430. package/translations/fr-fr.yaml +51 -10
  431. package/translations/mn-mn.yaml +51 -10
  432. package/translations/pt-br.yaml +51 -10
  433. package/translations/ru-ru.yaml +51 -10
  434. package/translations/vi-vn.yaml +48 -12
@@ -58,11 +58,7 @@
58
58
  <InputGroup
59
59
  @name={{smart-humanize param.key}}
60
60
  @value={{mut (get @options.integratedVendor.credentials param.key)}}
61
- @helpText={{concat
62
- (t "modals.vendor-form.optionally")
63
- (smart-humanize param.key)
64
- (t "modals.vendor-form.configure-vendor")
65
- }}
61
+ @helpText={{concat (t "modals.vendor-form.optionally") (smart-humanize param.key) (t "modals.vendor-form.configure-vendor")}}
66
62
  />
67
63
  {{/each}}
68
64
  </div>
@@ -85,11 +81,7 @@
85
81
  <InputGroup
86
82
  @name={{smart-humanize param.key}}
87
83
  @value={{mut (get @options.integratedVendor.options param.key)}}
88
- @helpText={{concat
89
- (t "modals.vendor-form.optionally")
90
- (smart-humanize param.key)
91
- (t "modals.vendor-form.configure-vendor")
92
- }}
84
+ @helpText={{concat (t "modals.vendor-form.optionally") (smart-humanize param.key) (t "modals.vendor-form.configure-vendor")}}
93
85
  />
94
86
  {{/if}}
95
87
  {{/each}}
@@ -112,21 +104,9 @@
112
104
  {{/if}}
113
105
  </button>
114
106
  {{#if @options.showAdvancedOptions}}
115
- <InputGroup
116
- @name={{t "modals.vendor-form.custom-host"}}
117
- @value={{@options.integratedVendor.host}}
118
- @helpText={{t "modals.vendor-form.host-text"}}
119
- />
120
- <InputGroup
121
- @name={{t "modals.vendor-form.custom-namespace"}}
122
- @value={{@options.integratedVendor.namespace}}
123
- @helpText={{t "modals.vendor-form.namespace-text"}}
124
- />
125
- <InputGroup
126
- @name={{t "modals.vendor-form.webhook"}}
127
- @value={{@options.integratedVendor.webhook_url}}
128
- @helpText={{t "modals.vendor-form.webhook-text"}}
129
- />
107
+ <InputGroup @name={{t "modals.vendor-form.custom-host"}} @value={{@options.integratedVendor.host}} @helpText={{t "modals.vendor-form.host-text"}} />
108
+ <InputGroup @name={{t "modals.vendor-form.custom-namespace"}} @value={{@options.integratedVendor.namespace}} @helpText={{t "modals.vendor-form.namespace-text"}} />
109
+ <InputGroup @name={{t "modals.vendor-form.webhook"}} @value={{@options.integratedVendor.webhook_url}} @helpText={{t "modals.vendor-form.webhook-text"}} />
130
110
  {{/if}}
131
111
  </div>
132
112
  {{/if}}
@@ -147,21 +127,11 @@
147
127
  <InputGroup
148
128
  @name={{smart-humanize param.key}}
149
129
  @value={{mut (get @options.vendor.credentials param.key)}}
150
- @helpText={{concat
151
- (t "modals.vendor-form.optionally")
152
- (smart-humanize param.key)
153
- (t "modals.vendor-form.configure-vendor")
154
- }}
130
+ @helpText={{concat (t "modals.vendor-form.optionally") (smart-humanize param.key) (t "modals.vendor-form.configure-vendor")}}
155
131
  />
156
132
  {{/each}}
157
133
  <div class="my-4">
158
- <Button
159
- @text={{t "modals.vendor-form.cancel-credentials"}}
160
- @type="danger"
161
- @size="sm"
162
- @icon="times"
163
- @onClick={{@options.toggleCredentialsReset}}
164
- />
134
+ <Button @text={{t "modals.vendor-form.cancel-credentials"}} @type="danger" @size="sm" @icon="times" @onClick={{@options.toggleCredentialsReset}} />
165
135
  </div>
166
136
  {{else}}
167
137
  <div>
@@ -175,11 +145,7 @@
175
145
  @name={{smart-humanize param.key}}
176
146
  @value="*********"
177
147
  @disabled="true"
178
- @helpText={{concat
179
- (t "modals.vendor-form.optionally")
180
- (smart-humanize param.key)
181
- (t "modals.vendor-form.configure-vendor")
182
- }}
148
+ @helpText={{concat (t "modals.vendor-form.optionally") (smart-humanize param.key) (t "modals.vendor-form.configure-vendor")}}
183
149
  />
184
150
  {{/each}}
185
151
  {{/if}}
@@ -203,11 +169,7 @@
203
169
  <InputGroup
204
170
  @name={{smart-humanize param.key}}
205
171
  @value={{mut (get @options.vendor.options param.key)}}
206
- @helpText={{concat
207
- (t "modals.vendor-form.optionally")
208
- (smart-humanize param.key)
209
- (t "modals.vendor-form.configure-vendor")
210
- }}
172
+ @helpText={{concat (t "modals.vendor-form.optionally") (smart-humanize param.key) (t "modals.vendor-form.configure-vendor")}}
211
173
  />
212
174
  {{/if}}
213
175
  {{/each}}
@@ -224,46 +186,17 @@
224
186
  {{/if}}
225
187
  </button>
226
188
  {{#if @options.showAdvancedOptions}}
227
- <InputGroup
228
- @name={{t "modals.vendor-form.custom-host"}}
229
- @value={{@options.vendor.host}}
230
- @helpText={{t "modals.vendor-form.host-text"}}
231
- />
232
- <InputGroup
233
- @name={{t "modals.vendor-form.custom-namespace"}}
234
- @value={{@options.vendor.namespace}}
235
- @helpText={{t "modals.vendor-form.namespace-text"}}
236
- />
237
- <InputGroup
238
- @name={{t "modals.vendor-form.webhook"}}
239
- @value={{@options.vendor.webhook_url}}
240
- @helpText={{t "modals.vendor-form.webhook-text"}}
241
- />
189
+ <InputGroup @name={{t "modals.vendor-form.custom-host"}} @value={{@options.vendor.host}} @helpText={{t "modals.vendor-form.host-text"}} />
190
+ <InputGroup @name={{t "modals.vendor-form.custom-namespace"}} @value={{@options.vendor.namespace}} @helpText={{t "modals.vendor-form.namespace-text"}} />
191
+ <InputGroup @name={{t "modals.vendor-form.webhook"}} @value={{@options.vendor.webhook_url}} @helpText={{t "modals.vendor-form.webhook-text"}} />
242
192
  {{/if}}
243
193
  </div>
244
194
  {{else}}
245
195
  <div class="grid lg:grid-cols-2 grid-cols-1 gap-2 text-xs dark:text-gray-100">
246
196
  <InputGroup @name={{t "common.name"}} @value={{@options.vendor.name}} @wrapperClass="mb-0" @helpText={{t "modals.vendor-form.name-text"}} />
247
- <InputGroup
248
- @name={{t "common.email"}}
249
- @type="email"
250
- @value={{@options.vendor.email}}
251
- @wrapperClass="mb-0"
252
- @helpText={{t "modals.vendor-form.email-text"}}
253
- />
254
- <InputGroup
255
- @name={{t "common.phone"}}
256
- @type="tel"
257
- @value={{@options.vendor.phone}}
258
- @wrapperClass="mb-0"
259
- @helpText={{t "modals.vendor-form.phone-text"}}
260
- />
261
- <InputGroup
262
- @name={{t "common.website"}}
263
- @value={{@options.vendor.website}}
264
- @wrapperClass="mb-0"
265
- @helpText={{t "modals.vendor-form.website-text"}}
266
- />
197
+ <InputGroup @name={{t "common.email"}} @type="email" @value={{@options.vendor.email}} @wrapperClass="mb-0" @helpText={{t "modals.vendor-form.email-text"}} />
198
+ <InputGroup @name={{t "common.phone"}} @type="tel" @value={{@options.vendor.phone}} @wrapperClass="mb-0" @helpText={{t "modals.vendor-form.phone-text"}} />
199
+ <InputGroup @name={{t "common.website"}} @value={{@options.vendor.website}} @wrapperClass="mb-0" @helpText={{t "modals.vendor-form.website-text"}} />
267
200
  <InputGroup @wrapperClass="mb-0 col-span-2">
268
201
  <div class="flex items-start justify-between">
269
202
  <label>
@@ -0,0 +1,76 @@
1
+ {{! Orchestrator::CardFieldsSettings — Configure which fields show on order cards }}
2
+ <div class="card-fields-settings" ...attributes>
3
+ {{#if this.loadData.isRunning}}
4
+ <div class="flex justify-center py-8"><Spinner /></div>
5
+ {{else}}
6
+ <div class="card-fields-settings-container {{@wrapperClass}}">
7
+ {{! Standard fields }}
8
+ <ContentPanel @title={{t "orchestrator.standard-fields"}} @open={{true}} @wrapperClass="bordered-top">
9
+ <p class="text-xs text-gray-500 dark:text-gray-400 mb-3">{{t "orchestrator.standard-fields-description"}}</p>
10
+ <div class="grid grid-cols-2 gap-2">
11
+ {{#each this.standardFieldOptions as |field|}}
12
+ <label class="flex items-center gap-2 cursor-pointer">
13
+ <input
14
+ type="checkbox"
15
+ class="form-checkbox h-3.5 w-3.5 rounded text-indigo-600"
16
+ checked={{this.isStandardSelected field.key}}
17
+ {{on "change" (fn this.toggleStandardField field.key)}}
18
+ />
19
+ <span class="text-xs text-gray-700 dark:text-gray-300">{{field.label}}</span>
20
+ </label>
21
+ {{/each}}
22
+ </div>
23
+ </ContentPanel>
24
+
25
+ {{! Per-order-config custom fields.
26
+ Backend returns { id, uuid, name, key, fields: [{key, label, type, required}] }
27
+ We use config.uuid as the settings key so it matches
28
+ order.order_config_uuid on the card renderer. }}
29
+ {{! Backend already excludes configs with no custom fields }}
30
+ {{#each this.orderConfigs as |config|}}
31
+ <ContentPanel @title={{config.name}} @open={{true}} @wrapperClass="bordered-top">
32
+ <p class="text-xs text-gray-500 dark:text-gray-400 mb-3">
33
+ {{t "orchestrator.config-fields-description" name=config.name}}
34
+ </p>
35
+ <div class="grid grid-cols-2 gap-2">
36
+ {{#each config.fields as |field|}}
37
+ <label class="flex items-center gap-2 cursor-pointer">
38
+ <input
39
+ type="checkbox"
40
+ class="form-checkbox h-3.5 w-3.5 rounded text-indigo-600"
41
+ checked={{this.isConfigFieldSelected config.uuid field.key}}
42
+ {{on "change" (fn this.toggleConfigField config.uuid field.key)}}
43
+ />
44
+ <span class="text-xs text-gray-700 dark:text-gray-300" title={{field.key}}>{{field.label}}</span>
45
+ </label>
46
+ {{/each}}
47
+ </div>
48
+ </ContentPanel>
49
+ {{/each}}
50
+
51
+ {{! Meta fields — only shown when the company has orders with meta keys }}
52
+ {{#if this.availableMetaKeys.length}}
53
+ <ContentPanel @title={{t "orchestrator.meta-fields"}} @open={{true}} @wrapperClass="bordered-top">
54
+ <p class="text-xs text-gray-500 dark:text-gray-400 mb-3">{{t "orchestrator.meta-fields-description"}}</p>
55
+ <div class="grid grid-cols-2 gap-2">
56
+ {{#each this.availableMetaKeys as |key|}}
57
+ <label class="flex items-center gap-2 cursor-pointer">
58
+ <input
59
+ type="checkbox"
60
+ class="form-checkbox h-3.5 w-3.5 rounded text-indigo-600"
61
+ checked={{this.isMetaKeySelected key}}
62
+ {{on "change" (fn this.toggleMetaKey key)}}
63
+ />
64
+ <span class="text-xs font-mono text-gray-600 dark:text-gray-400 truncate">{{key}}</span>
65
+ </label>
66
+ {{/each}}
67
+ </div>
68
+ </ContentPanel>
69
+ {{/if}}
70
+ </div>
71
+ {{! Save button }}
72
+ <div class="px-3 py-2 border-t dark:border-gray-700 flex-shrink-0 bg-gray-50 dark:bg-gray-800">
73
+ <Button @text={{t "orchestrator.save-card-fields"}} @type="primary" @size="sm" @isLoading={{this.saveSettings.isRunning}} @onClick={{perform this.saveSettings}} />
74
+ </div>
75
+ {{/if}}
76
+ </div>
@@ -0,0 +1,134 @@
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
+ import { task } from 'ember-concurrency';
6
+
7
+ /**
8
+ * Orchestrator::CardFieldsSettings
9
+ *
10
+ * Settings panel for configuring which fields appear on order cards in the
11
+ * workbench. Fields are grouped by order config so the dispatcher can see
12
+ * exactly which custom fields belong to which order type.
13
+ *
14
+ * Saves to company settings key: fleetops.orchestrator_card_fields
15
+ * Shape: { standard: string[], byConfig: { [configUuid]: string[] }, meta: string[] }
16
+ *
17
+ * @arg onSaved - Optional action called after settings are saved
18
+ */
19
+ export default class OrchestratorCardFieldsSettingsComponent extends Component {
20
+ @service fetch;
21
+ @service notifications;
22
+ @service intl;
23
+
24
+ /** Available order configs with their custom field definitions. */
25
+ @tracked orderConfigs = [];
26
+
27
+ /** Available meta keys discovered from recent orders. */
28
+ @tracked availableMetaKeys = [];
29
+
30
+ /** Current saved settings. */
31
+ @tracked settings = {
32
+ standard: ['pickup', 'dropoff', 'scheduled_at', 'customer', 'driver_assigned', 'vehicle_assigned', 'created_at'],
33
+ byConfig: {},
34
+ meta: [],
35
+ };
36
+
37
+ @tracked isLoaded = false;
38
+
39
+ constructor() {
40
+ super(...arguments);
41
+ this.loadData.perform();
42
+ }
43
+
44
+ get standardFieldOptions() {
45
+ return [
46
+ { key: 'tracking', label: this.intl.t('orchestrator.field-tracking') },
47
+ { key: 'status', label: this.intl.t('orchestrator.field-status') },
48
+ { key: 'scheduled_at', label: this.intl.t('orchestrator.field-scheduled-at') },
49
+ { key: 'customer', label: this.intl.t('orchestrator.field-customer') },
50
+ { key: 'type', label: this.intl.t('orchestrator.field-type') },
51
+ { key: 'notes', label: this.intl.t('orchestrator.field-notes') },
52
+ { key: 'priority', label: this.intl.t('orchestrator.field-priority') },
53
+ { key: 'dropoff', label: this.intl.t('orchestrator.dropoff') },
54
+ { key: 'pickup', label: this.intl.t('orchestrator.pickup') },
55
+ { key: 'driver_assigned', label: this.intl.t('orchestrator.driver-assigned') },
56
+ { key: 'vehicle_assigned', label: this.intl.t('orchestrator.vehicle-assigned') },
57
+ { key: 'created_at', label: this.intl.t('orchestrator.created') },
58
+ ];
59
+ }
60
+
61
+ @task *loadData() {
62
+ try {
63
+ const [configsResult, settingsResult] = yield Promise.all([
64
+ this.fetch.get('fleet-ops/orchestrator/order-config-fields'),
65
+ this.fetch.get('fleet-ops/settings/orchestrator-card-fields').catch(() => null),
66
+ ]);
67
+
68
+ // Backend returns { configs: [...], meta_keys: [...] }
69
+ this.orderConfigs = configsResult?.configs ?? [];
70
+ this.availableMetaKeys = configsResult?.meta_keys ?? [];
71
+
72
+ if (settingsResult?.settings) {
73
+ this.settings = {
74
+ standard: settingsResult.settings.standard ?? this.settings.standard,
75
+ byConfig: settingsResult.settings.byConfig ?? {},
76
+ meta: settingsResult.settings.meta ?? [],
77
+ };
78
+ }
79
+ } catch (error) {
80
+ this.notifications.serverError(error);
81
+ } finally {
82
+ this.isLoaded = true;
83
+ }
84
+ }
85
+
86
+ @task *saveSettings() {
87
+ try {
88
+ yield this.fetch.post('fleet-ops/settings/orchestrator-card-fields', {
89
+ settings: this.settings,
90
+ });
91
+ this.notifications.success(this.intl.t('orchestrator.card-fields-saved'));
92
+ this.args.onSaved?.();
93
+ } catch (error) {
94
+ this.notifications.serverError(error);
95
+ }
96
+ }
97
+
98
+ @action toggleStandardField(key) {
99
+ const current = this.settings.standard ?? [];
100
+ const updated = current.includes(key) ? current.filter((k) => k !== key) : [...current, key];
101
+ this.settings = { ...this.settings, standard: updated };
102
+ }
103
+
104
+ // configUuid here is the real UUID (not public_id) so it matches order.order_config_uuid
105
+ @action toggleConfigField(configUuid, fieldKey) {
106
+ const byConfig = { ...(this.settings.byConfig ?? {}) };
107
+ const current = byConfig[configUuid] ?? [];
108
+ byConfig[configUuid] = current.includes(fieldKey) ? current.filter((k) => k !== fieldKey) : [...current, fieldKey];
109
+ this.settings = { ...this.settings, byConfig };
110
+ }
111
+
112
+ @action toggleMetaKey(key) {
113
+ const current = this.settings.meta ?? [];
114
+ const updated = current.includes(key) ? current.filter((k) => k !== key) : [...current, key];
115
+ this.settings = { ...this.settings, meta: updated };
116
+ }
117
+
118
+ /**
119
+ * These three methods are called with arguments from HBS so they must be
120
+ * decorated with @action to satisfy Glimmer's template invocation rules.
121
+ */
122
+ @action isStandardSelected(key) {
123
+ return (this.settings.standard ?? []).includes(key);
124
+ }
125
+
126
+ // configUuid here is the real UUID (not public_id) so it matches order.order_config_uuid
127
+ @action isConfigFieldSelected(configUuid, fieldKey) {
128
+ return (this.settings.byConfig?.[configUuid] ?? []).includes(fieldKey);
129
+ }
130
+
131
+ @action isMetaKeySelected(key) {
132
+ return (this.settings.meta ?? []).includes(key);
133
+ }
134
+ }
@@ -0,0 +1,264 @@
1
+ {{! Orchestrator::OrderPool — left panel of the workbench }}
2
+ <div class="order-pool flex flex-col h-full overflow-hidden">
3
+
4
+ {{! Search + count }}
5
+ <div class="px-3 py-2 border-b dark:border-gray-700 flex items-center space-x-2 flex-shrink-0">
6
+ <div class="relative flex-1">
7
+ <span class="absolute inset-y-0 left-0 flex items-center justify-center pointer-events-none text-gray-400 w-8">
8
+ <FaIcon @icon="magnifying-glass" @size="xs" class="block align-baseline" />
9
+ </span>
10
+ <input type="text" class="form-input form-input-sm w-full pl-7i" placeholder={{t "orchestrator.search-orders"}} value={{this.orderSearch}} {{on "input" this.onSearchInput}} />
11
+ </div>
12
+ <span class="text-xs text-gray-400 flex-shrink-0 tabular-nums">
13
+ {{this.filteredOrders.length}}/{{@orders.length}}
14
+ </span>
15
+ </div>
16
+
17
+ {{! Quick filter chips }}
18
+ <div class="px-3 py-1.5 border-b dark:border-gray-700 flex items-center gap-1 flex-wrap flex-shrink-0">
19
+ <button
20
+ type="button"
21
+ class="px-2 py-0.5 rounded text-xs transition-colors
22
+ {{if (eq this.orderFilter 'all') 'bg-indigo-500 text-white' 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'}}"
23
+ {{on "click" (fn this.setFilter "all")}}
24
+ >{{t "orchestrator.filter-all"}}</button>
25
+ <button
26
+ type="button"
27
+ class="px-2 py-0.5 rounded text-xs transition-colors
28
+ {{if (eq this.orderFilter 'scheduled') 'bg-indigo-500 text-white' 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'}}"
29
+ {{on "click" (fn this.setFilter "scheduled")}}
30
+ >{{t "orchestrator.filter-scheduled"}}</button>
31
+ <button
32
+ type="button"
33
+ class="px-2 py-0.5 rounded text-xs transition-colors
34
+ {{if (eq this.orderFilter 'today') 'bg-indigo-500 text-white' 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'}}"
35
+ {{on "click" (fn this.setFilter "today")}}
36
+ >{{t "orchestrator.filter-today"}}</button>
37
+ <button
38
+ type="button"
39
+ class="px-2 py-0.5 rounded text-xs transition-colors
40
+ {{if (eq this.orderFilter 'urgent') 'bg-indigo-500 text-white' 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'}}"
41
+ {{on "click" (fn this.setFilter "urgent")}}
42
+ >{{t "orchestrator.filter-urgent"}}</button>
43
+ <button
44
+ type="button"
45
+ class="px-2 py-0.5 rounded text-xs transition-colors
46
+ {{if (eq this.orderFilter 'unassigned') 'bg-indigo-500 text-white' 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'}}"
47
+ {{on "click" (fn this.setFilter "unassigned")}}
48
+ >{{t "orchestrator.filter-unassigned"}}</button>
49
+ <button
50
+ type="button"
51
+ class="px-2 py-0.5 rounded text-xs transition-colors
52
+ {{if (eq this.orderFilter 'imported') 'bg-indigo-500 text-white' 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'}}"
53
+ {{on "click" (fn this.setFilter "imported")}}
54
+ >{{t "orchestrator.filter-imported"}}</button>
55
+ </div>
56
+
57
+ {{! Advanced Filters toggle — on its own row }}
58
+ <div class="px-3 py-1 border-b dark:border-gray-700 flex items-center flex-shrink-0">
59
+ <button
60
+ type="button"
61
+ class="px-2 py-0.5 rounded text-xs transition-colors flex items-center gap-1
62
+ {{if this.showAdvanced 'bg-indigo-500 text-white' 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'}}
63
+ {{if this.hasAdvancedFilters 'ring-1 ring-indigo-400' ''}}"
64
+ {{on "click" this.toggleAdvanced}}
65
+ >
66
+ <FaIcon @icon="sliders" @size="xs" />
67
+ {{t "orchestrator.advanced-filters"}}
68
+ {{#if this.hasAdvancedFilters}}
69
+ <span class="w-1.5 h-1.5 rounded-full bg-yellow-400 inline-block"></span>
70
+ {{/if}}
71
+ </button>
72
+ </div>
73
+
74
+ {{! Advanced filter panel }}
75
+ {{#if this.showAdvanced}}
76
+ <div class="px-3 py-2 border-b dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50 flex-shrink-0 space-y-2">
77
+ <div class="grid grid-cols-2 gap-2">
78
+ {{! Country }}
79
+ <div class="input-group">
80
+ <label class="input-label text-xs">{{t "orchestrator.filter-country"}}</label>
81
+ <CountrySelect
82
+ class="form-input form-input-sm w-full"
83
+ @value={{this.advancedCountry}}
84
+ @onChange={{this.setAdvancedCountry}}
85
+ @placeholder={{t "orchestrator.filter-country-placeholder"}}
86
+ />
87
+ </div>
88
+ {{! Order Type }}
89
+ <div class="input-group">
90
+ <label class="input-label text-xs">{{t "orchestrator.filter-type"}}</label>
91
+ <select class="form-select form-input-sm w-full" {{on "change" this.setAdvancedType}}>
92
+ <option value="">{{t "orchestrator.filter-type-placeholder"}}</option>
93
+ {{#each this.availableTypes as |type|}}
94
+ <option value={{type}} selected={{eq type this.advancedType}}>{{type}}</option>
95
+ {{/each}}
96
+ </select>
97
+ </div>
98
+ {{! Status }}
99
+ <div class="input-group">
100
+ <label class="input-label text-xs">{{t "orchestrator.filter-status"}}</label>
101
+ <select class="form-select form-input-sm w-full" {{on "change" this.setAdvancedStatus}}>
102
+ <option value="">{{t "orchestrator.filter-status-placeholder"}}</option>
103
+ {{#each this.availableStatuses as |status|}}
104
+ <option value={{status}} selected={{eq status this.advancedStatus}}>{{status}}</option>
105
+ {{/each}}
106
+ </select>
107
+ </div>
108
+ {{! Scheduled Date }}
109
+ <div class="input-group">
110
+ <label class="input-label text-xs">{{t "orchestrator.filter-date"}}</label>
111
+ <input type="date" class="form-input form-input-sm w-full" value={{this.advancedDate}} {{on "change" this.setAdvancedDate}} />
112
+ </div>
113
+ </div>
114
+ {{#if this.hasAdvancedFilters}}
115
+ <button type="button" class="text-xs text-indigo-400 hover:text-indigo-300" {{on "click" this.clearAdvancedFilters}}>
116
+ <FaIcon @icon="xmark" @size="xs" class="mr-0.5" />{{t "orchestrator.clear-filters"}}
117
+ </button>
118
+ {{/if}}
119
+ </div>
120
+ {{/if}}
121
+
122
+ {{! Order list }}
123
+ <div class="flex-1 overflow-y-auto space-y-2">
124
+ {{#if @isLoading}}
125
+ <div class="flex justify-center py-8"><Spinner /></div>
126
+ {{else if this.filteredOrders.length}}
127
+ {{! Single collapse/expand-all toggle }}
128
+ <div class="px-3 pt-1.5 pb-0.5 flex items-center flex-shrink-0">
129
+ <button type="button" class="text-xs text-gray-400 hover:text-indigo-400 transition-colors flex items-center gap-1" {{on "click" this.toggleAllWaypoints}}>
130
+ <FaIcon @icon={{if this.allWaypointsCollapsed "chevron-down" "chevron-up"}} @size="xs" />
131
+ {{if this.allWaypointsCollapsed (t "orchestrator.expand-all") (t "orchestrator.collapse-all")}}
132
+ </button>
133
+ </div>
134
+ {{#each this.filteredOrders as |order|}}
135
+ <div
136
+ class="order-pool-card mx-2 my-1 rounded border overflow-hidden select-none
137
+ {{if
138
+ (includes order.public_id this.selectedOrderIdsArray)
139
+ 'ring-2 ring-indigo-500 border-indigo-400 dark:border-indigo-500 cursor-grab'
140
+ 'border-gray-200 dark:border-gray-700 cursor-pointer hover:border-indigo-400 dark:hover:border-indigo-500 hover:ring-1 hover:ring-indigo-400'
141
+ }}"
142
+ draggable="true"
143
+ data-order-id={{order.public_id}}
144
+ {{on "dragstart" (fn @onDragStart order)}}
145
+ {{on "click" (fn @onToggleSelection order)}}
146
+ >
147
+ {{! Header: checkbox + tracking number + priority/status badges + collapse toggle }}
148
+ <div class="flex items-center gap-1.5 px-2 py-1.5 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
149
+ <input
150
+ type="checkbox"
151
+ class="form-checkbox h-3.5 w-3.5 rounded text-indigo-600 border-gray-300 dark:border-gray-600 cursor-pointer flex-shrink-0"
152
+ checked={{includes order.public_id this.selectedOrderIdsArray}}
153
+ {{on "change" (fn @onToggleSelection order)}}
154
+ {{on "click" this.stopPropagation}}
155
+ />
156
+ <span class="flex-1 min-w-0 truncate text-xs font-semibold text-gray-800 dark:text-gray-100">
157
+ {{or order.tracking order.public_id}}
158
+ </span>
159
+ <div class="flex-shrink-0 flex items-center gap-1">
160
+ {{#if order.orchestrator_priority}}
161
+ <Badge @status={{this.priorityBadgeStatus order.orchestrator_priority}}>P{{order.orchestrator_priority}}</Badge>
162
+ {{/if}}
163
+ <Badge @status={{order.status}} />
164
+ {{! Waypoint collapse toggle }}
165
+ <button
166
+ type="button"
167
+ class="ml-0.5 w-5 h-5 flex items-center justify-center rounded text-gray-400 hover:text-indigo-400 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors flex-shrink-0"
168
+ title={{if (this.isWaypointCollapsed order.public_id) (t "orchestrator.expand-route") (t "orchestrator.collapse-route")}}
169
+ {{on "click" (fn this.toggleWaypointCollapse order.public_id)}}
170
+ >
171
+ <FaIcon @icon={{if (this.isWaypointCollapsed order.public_id) "chevron-down" "chevron-up"}} @size="xs" />
172
+ </button>
173
+ </div>
174
+ </div>
175
+
176
+ {{! Card body — hidden when collapsed }}
177
+ {{#unless (this.isWaypointCollapsed order.public_id)}}
178
+ <div class="bg-white dark:bg-gray-900 px-2 py-1.5">
179
+ <dl class="grid grid-cols-[auto_1fr] gap-x-2 gap-y-0.5">
180
+ {{#each (this.resolvedCardFields order) as |field|}}
181
+ <dt class="flex items-center gap-1 text-xs text-gray-500 dark:text-gray-400 whitespace-nowrap">
182
+ {{#if field.icon}}
183
+ <FaIcon @icon={{field.icon}} @size="xs" class="w-3 text-center {{if field.iconClass field.iconClass ''}}" />
184
+ {{/if}}
185
+ {{field.label}}
186
+ </dt>
187
+ <dd class="truncate text-right text-xs {{if field.highlight field.highlight 'text-gray-700 dark:text-gray-300'}}">
188
+ {{or field.value "-"}}
189
+ </dd>
190
+ {{/each}}
191
+ </dl>
192
+
193
+ {{! Inline route stop list }}
194
+ {{#let (this.orderRouteStops order) as |stops|}}
195
+ {{#if stops.length}}
196
+ <div class="mt-1.5 border-t border-gray-100 dark:border-gray-700 pt-1">
197
+ {{! Route header with toggle }}
198
+ <button
199
+ type="button"
200
+ class="w-full flex items-center justify-between text-xs text-gray-500 dark:text-gray-400 hover:text-indigo-500 dark:hover:text-indigo-400 transition-colors"
201
+ {{on "click" (fn this.toggleRouteCollapse order.public_id)}}
202
+ >
203
+ <span class="flex items-center gap-1">
204
+ <FaIcon @icon="route" @size="xs" class="text-indigo-400" />
205
+ <span class="font-medium">{{t "orchestrator.route"}} &middot; {{stops.length}} {{t "orchestrator.stops"}}</span>
206
+ </span>
207
+ <FaIcon @icon={{if (this.isRouteCollapsed order.public_id) "chevron-down" "chevron-up"}} @size="xs" />
208
+ </button>
209
+ {{! Stop list }}
210
+ {{#unless (this.isRouteCollapsed order.public_id)}}
211
+ <ol class="mt-1 space-y-1.5">
212
+ {{#each stops as |stop|}}
213
+ <li class="flex items-start gap-1.5 text-xs">
214
+ <span
215
+ class="flex-shrink-0 w-4 h-4 rounded-full bg-indigo-100 dark:bg-indigo-900 text-indigo-600 dark:text-indigo-300 text-[0.6rem] font-bold flex items-center justify-center leading-none"
216
+ >
217
+ {{stop.label}}
218
+ </span>
219
+ <span class="text-gray-600 dark:text-gray-300 leading-4 line-clamp-2">{{stop.address}}</span>
220
+ </li>
221
+ {{/each}}
222
+ </ol>
223
+ {{/unless}}
224
+ </div>
225
+ {{/if}}
226
+ {{/let}}
227
+
228
+ {{#if order.required_skills.length}}
229
+ <div class="flex flex-wrap gap-0.5 mt-1">
230
+ {{#each order.required_skills as |skill|}}
231
+ <span class="skill-tag">{{skill}}</span>
232
+ {{/each}}
233
+ </div>
234
+ {{/if}}
235
+ </div>
236
+ {{/unless}}
237
+ </div>
238
+ {{/each}}
239
+ {{else}}
240
+ <div class="flex flex-col items-center justify-center py-12 text-gray-400 text-xs">
241
+ <FaIcon @icon="check-circle" @size="2x" class="mb-2 text-green-400" />
242
+ {{t "orchestrator.no-unassigned-orders"}}
243
+ </div>
244
+ {{/if}}
245
+ <Spacer @height="200px" />
246
+ </div>
247
+
248
+ {{! Bulk action bar + import }}
249
+ <div class="px-3 py-2 border-t dark:border-gray-700 flex items-center justify-between flex-shrink-0">
250
+ <Button @icon="file-import" @text={{t "orchestrator.import-orders"}} @type="default" @size="xs" @onClick={{@onOpenImport}} />
251
+ {{#if this.selectedOrderIdsArray.length}}
252
+ <div class="flex items-center gap-1.5">
253
+ <span class="text-xs text-blue-400">
254
+ {{this.selectedOrderIdsArray.length}}
255
+ {{t "orchestrator.selected"}}
256
+ </span>
257
+ <button type="button" class="text-xs text-gray-400 hover:text-white" {{on "click" @onClearSelection}}>
258
+ {{t "orchestrator.clear-selection"}}
259
+ </button>
260
+ </div>
261
+ {{/if}}
262
+ </div>
263
+
264
+ </div>