@fleetbase/fleetops-engine 0.6.38 → 0.6.40

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 (432) 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 +43 -38
  25. package/addon/components/layout/fleet-ops-sidebar.js +103 -24
  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 +1088 -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 +52 -0
  117. package/addon/components/vehicle/form.hbs +51 -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 +79 -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 +4 -2
  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 +53 -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 +38 -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/migrations/2026_04_14_000001_drop_redundant_capacity_columns_from_payloads_table.php +42 -0
  358. package/server/migrations/2026_04_14_000002_rename_capacity_columns_on_vehicles_table.php +48 -0
  359. package/server/resources/views/mail/maintenance-schedule-reminder.blade.php +59 -0
  360. package/server/resources/views/mail/work-order-dispatched.blade.php +67 -0
  361. package/server/src/Auth/Schemas/FleetOps.php +44 -0
  362. package/server/src/Console/Commands/ProcessMaintenanceTriggers.php +150 -0
  363. package/server/src/Console/Commands/SendMaintenanceReminders.php +128 -0
  364. package/server/src/Http/Controllers/Api/v1/OrderController.php +15 -2
  365. package/server/src/Http/Controllers/Api/v1/VehicleController.php +18 -2
  366. package/server/src/Http/Controllers/Internal/v1/DriverController.php +1 -0
  367. package/server/src/Http/Controllers/Internal/v1/EquipmentController.php +27 -0
  368. package/server/src/Http/Controllers/Internal/v1/LiveController.php +9 -2
  369. package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +165 -0
  370. package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +304 -0
  371. package/server/src/Http/Controllers/Internal/v1/ManifestController.php +138 -0
  372. package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +975 -0
  373. package/server/src/Http/Controllers/Internal/v1/OrderController.php +42 -0
  374. package/server/src/Http/Controllers/Internal/v1/PartController.php +27 -0
  375. package/server/src/Http/Controllers/Internal/v1/SettingController.php +118 -0
  376. package/server/src/Http/Controllers/Internal/v1/Traits/DriverSchedulingTrait.php +214 -0
  377. package/server/src/Http/Controllers/Internal/v1/WorkOrderController.php +68 -0
  378. package/server/src/Http/Resources/v1/Driver.php +1 -0
  379. package/server/src/Http/Resources/v1/Maintenance.php +138 -0
  380. package/server/src/Http/Resources/v1/MaintenanceSchedule.php +137 -0
  381. package/server/src/Http/Resources/v1/Orchestrator/Order.php +116 -0
  382. package/server/src/Http/Resources/v1/Order.php +7 -4
  383. package/server/src/Http/Resources/v1/Waypoint.php +7 -0
  384. package/server/src/Http/Resources/v1/WorkOrder.php +136 -0
  385. package/server/src/Imports/EquipmentImport.php +32 -0
  386. package/server/src/Imports/MaintenanceImport.php +32 -0
  387. package/server/src/Imports/MaintenanceScheduleImport.php +32 -0
  388. package/server/src/Imports/PartImport.php +32 -0
  389. package/server/src/Imports/WorkOrderImport.php +32 -0
  390. package/server/src/Jobs/ProcessAllocationJob.php +119 -0
  391. package/server/src/Listeners/HandleDeliveryCompletion.php +47 -0
  392. package/server/src/Listeners/NotifyDriverOnShiftChange.php +63 -0
  393. package/server/src/Mail/MaintenanceScheduleReminder.php +68 -0
  394. package/server/src/Mail/WorkOrderDispatched.php +58 -0
  395. package/server/src/Models/Asset.php +2 -2
  396. package/server/src/Models/Device.php +1 -1
  397. package/server/src/Models/Driver.php +82 -4
  398. package/server/src/Models/Equipment.php +62 -2
  399. package/server/src/Models/Maintenance.php +127 -9
  400. package/server/src/Models/MaintenanceSchedule.php +353 -0
  401. package/server/src/Models/Manifest.php +214 -0
  402. package/server/src/Models/ManifestStop.php +162 -0
  403. package/server/src/Models/Order.php +70 -0
  404. package/server/src/Models/OrderConfig.php +5 -2
  405. package/server/src/Models/Part.php +69 -3
  406. package/server/src/Models/Place.php +1 -1
  407. package/server/src/Models/Sensor.php +1 -1
  408. package/server/src/Models/ServiceQuote.php +1 -1
  409. package/server/src/Models/Vehicle.php +18 -1
  410. package/server/src/Models/Warranty.php +1 -1
  411. package/server/src/Models/Waypoint.php +7 -1
  412. package/server/src/Models/WorkOrder.php +122 -12
  413. package/server/src/Notifications/DriverShiftChanged.php +110 -0
  414. package/server/src/Observers/WorkOrderObserver.php +107 -0
  415. package/server/src/Orchestration/Contracts/OrchestrationEngineInterface.php +63 -0
  416. package/server/src/Orchestration/Engines/DriverAssignmentEngine.php +265 -0
  417. package/server/src/Orchestration/Engines/GreedyOrchestrationEngine.php +155 -0
  418. package/server/src/Orchestration/Engines/RouteSequencingEngine.php +272 -0
  419. package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +192 -0
  420. package/server/src/Orchestration/OrchestrationEngineRegistry.php +83 -0
  421. package/server/src/Orchestration/Support/OrchestrationPayloadBuilder.php +380 -0
  422. package/server/src/Providers/EventServiceProvider.php +7 -1
  423. package/server/src/Providers/FleetOpsServiceProvider.php +42 -15
  424. package/server/src/routes.php +65 -4
  425. package/translations/ar-ae.yml +44 -12
  426. package/translations/bg-bg.yaml +51 -10
  427. package/translations/en-us.yaml +444 -1
  428. package/translations/fr-fr.yaml +51 -10
  429. package/translations/mn-mn.yaml +51 -10
  430. package/translations/pt-br.yaml +51 -10
  431. package/translations/ru-ru.yaml +51 -10
  432. package/translations/vi-vn.yaml +48 -12
@@ -3,12 +3,7 @@
3
3
  <div class="mb-3 flex items-center justify-between">
4
4
  <div class="flex items-center">
5
5
  <div class="fleet-vehicle-listing-search">
6
- <Input
7
- @type="text"
8
- {{on "input" this.onInput}}
9
- placeholder={{t "fleet-panel.vehicle-listing.search-vehicle"}}
10
- class="w-64 form-input form-input-sm"
11
- />
6
+ <Input @type="text" {{on "input" this.onInput}} placeholder={{t "fleet-panel.vehicle-listing.search-vehicle"}} class="w-64 form-input form-input-sm" />
12
7
  </div>
13
8
  {{#if this.search.isRunning}}
14
9
  <div class="mx-2">
@@ -77,11 +77,7 @@
77
77
  @disabled={{cannot-write @resource}}
78
78
  />
79
79
  </InputGroup>
80
- <InputGroup
81
- @name={{t "fuel-report.fields.fuel-volume"}}
82
- @helpText={{t "fuel-report.fields.select-volume-help-text"}}
83
- @wrapperClass="w-full"
84
- >
80
+ <InputGroup @name={{t "fuel-report.fields.fuel-volume"}} @helpText={{t "fuel-report.fields.select-volume-help-text"}} @wrapperClass="w-full">
85
81
  <UnitInput
86
82
  class="w-full"
87
83
  @measurement="volume"
@@ -104,8 +104,9 @@
104
104
  }}
105
105
  {{else}}
106
106
  <Layout::Sidebar::Item
107
- @onClick={{menuItem.onClick}}
108
- @route={{concat this.routePrefix menuItem.route}}
107
+ @onClick={{if menuItem._virtual (fn this.universe.transitionMenuItem (concat this.routePrefix "virtual") menuItem) menuItem.onClick}}
108
+ @menuItem={{menuItem}}
109
+ @route={{if menuItem._virtual null (concat this.routePrefix menuItem.route)}}
109
110
  @icon={{menuItem.icon}}
110
111
  @iconComponent={{menuItem.iconComponent}}
111
112
  @iconComponentOptions={{menuItem.iconComponentOptions}}
@@ -135,44 +136,48 @@
135
136
  >{{if menuItem.intl (t menuItem.intl) menuItem.title}}</Layout::Sidebar::Item>
136
137
  {{/if}}
137
138
  {{/each}}
138
- {{#if (eq menuPanel.routePrefix "settings")}}
139
- {{#each this.universeSettingsMenuItems as |menuItem|}}
140
- {{#if menuItem.renderComponentInPlace}}
141
- {{component menuItem.component}}
142
- {{else}}
143
- <Layout::Sidebar::Item
144
- @onClick={{fn this.universe.transitionMenuItem (concat this.routePrefix "virtual") menuItem}}
145
- @menuItem={{menuItem}}
146
- @icon={{menuItem.icon}}
147
- @iconComponent={{menuItem.iconComponent}}
148
- @iconComponentOptions={{menuItem.iconComponentOptions}}
149
- @iconSize={{menuItem.iconSize}}
150
- @iconPrefix={{menuItem.iconPrefix}}
151
- @iconClass={{menuItem.iconClass}}
152
- @rightSideComponent={{menuItem.rightSideComponent}}
153
- @rightSideComponentContext={{menuItem.rightSideComponentContext}}
154
- @rightSideStatus={{menuItem.rightSideStatus}}
155
- @rightSideStatusText={{menuItem.rightSideStatusText}}
156
- @hideRightSideStatusDot={{menuItem.hideRightSideStatusDot}}
157
- @rightSideStatusClass={{menuItem.rightSideStatusClass}}
158
- @disableRightSideStatusHumanize={{menuItem.disableRightSideStatusHumanize}}
159
- @dropdownButton={{menuItem.dropdownButton}}
160
- @dropdownButtonActions={{menuItem.dropdownButtonActions}}
161
- @dropdownButtonIcon={{menuItem.dropdownButtonIcon}}
162
- @dropdownButtonIconPrefix={{menuItem.dropdownButtonIconPrefix}}
163
- @dropdownButtonText={{menuItem.dropdownButtonText}}
164
- @dropdownButtonCalculatePosition={{menuItem.dropdownButtonCalculatePosition}}
165
- @dropdownButtonRenderInPlace={{menuItem.dropdownButtonRenderInPlace}}
166
- @dropdownButtonMenuLabel={{menuItem.dropdownButtonMenuLabel}}
167
- @dropdownButtonWrapperClass={{menuItem.dropdownButtonWrapperClass}}
168
- @dropdownButtonTriggerClass={{menuItem.dropdownButtonTriggerClass}}
169
- @registerDropdownButtonAPI={{menuItem.registerDropdownButtonAPI}}
170
- @permission={{menuItem.permission}}
171
- @visible={{menuItem.visible}}
172
- >{{if menuItem.intl (t menuItem.intl) menuItem.title}}</Layout::Sidebar::Item>
139
+ {{!-- {{#let (array "operations" "management" "connectivity" "maintenance" "analytics" "settings") as |sectionsToRender|}}
140
+ {{#each sectionsToRender as |section|}}
141
+ {{#if (eq menuPanel.routePrefix section)}}
142
+ {{#each (get this (concat "universe" (capitalize section) "MenuItems")) as |menuItem|}}
143
+ {{#if menuItem.renderComponentInPlace}}
144
+ {{component menuItem.component}}
145
+ {{else}}
146
+ <Layout::Sidebar::Item
147
+ @onClick={{fn this.universe.transitionMenuItem (concat this.routePrefix "virtual") menuItem}}
148
+ @menuItem={{menuItem}}
149
+ @icon={{menuItem.icon}}
150
+ @iconComponent={{menuItem.iconComponent}}
151
+ @iconComponentOptions={{menuItem.iconComponentOptions}}
152
+ @iconSize={{menuItem.iconSize}}
153
+ @iconPrefix={{menuItem.iconPrefix}}
154
+ @iconClass={{menuItem.iconClass}}
155
+ @rightSideComponent={{menuItem.rightSideComponent}}
156
+ @rightSideComponentContext={{menuItem.rightSideComponentContext}}
157
+ @rightSideStatus={{menuItem.rightSideStatus}}
158
+ @rightSideStatusText={{menuItem.rightSideStatusText}}
159
+ @hideRightSideStatusDot={{menuItem.hideRightSideStatusDot}}
160
+ @rightSideStatusClass={{menuItem.rightSideStatusClass}}
161
+ @disableRightSideStatusHumanize={{menuItem.disableRightSideStatusHumanize}}
162
+ @dropdownButton={{menuItem.dropdownButton}}
163
+ @dropdownButtonActions={{menuItem.dropdownButtonActions}}
164
+ @dropdownButtonIcon={{menuItem.dropdownButtonIcon}}
165
+ @dropdownButtonIconPrefix={{menuItem.dropdownButtonIconPrefix}}
166
+ @dropdownButtonText={{menuItem.dropdownButtonText}}
167
+ @dropdownButtonCalculatePosition={{menuItem.dropdownButtonCalculatePosition}}
168
+ @dropdownButtonRenderInPlace={{menuItem.dropdownButtonRenderInPlace}}
169
+ @dropdownButtonMenuLabel={{menuItem.dropdownButtonMenuLabel}}
170
+ @dropdownButtonWrapperClass={{menuItem.dropdownButtonWrapperClass}}
171
+ @dropdownButtonTriggerClass={{menuItem.dropdownButtonTriggerClass}}
172
+ @registerDropdownButtonAPI={{menuItem.registerDropdownButtonAPI}}
173
+ @permission={{menuItem.permission}}
174
+ @visible={{menuItem.visible}}
175
+ >{{if menuItem.intl (t menuItem.intl) menuItem.title}}</Layout::Sidebar::Item>
176
+ {{/if}}
177
+ {{/each}}
173
178
  {{/if}}
174
179
  {{/each}}
175
- {{/if}}
180
+ {{/let}} --}}
176
181
  </Layout::Sidebar::Panel>
177
182
  {{/each}}
178
183
  {{#each this.universeMenuPanels as |menuPanel|}}
@@ -15,6 +15,11 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
15
15
  @tracked routePrefix = 'console.fleet-ops.';
16
16
  @tracked menuPanels = [];
17
17
  @tracked universeMenuItems = [];
18
+ @tracked universeOperationsMenuItems = [];
19
+ @tracked universeManagementMenuItems = [];
20
+ @tracked universeConnectivityMenuItems = [];
21
+ @tracked universeMaintenanceMenuItems = [];
22
+ @tracked universeAnalyticsMenuItems = [];
18
23
  @tracked universeSettingsMenuItems = [];
19
24
  @tracked universeMenuPanels = [];
20
25
 
@@ -28,6 +33,11 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
28
33
  const registeredMenuItems = this.menuService.getMenuItems('engine:fleet-ops');
29
34
  this.universeMenuPanels = this.menuService.getMenuPanels('engine:fleet-ops');
30
35
  this.universeMenuItems = registeredMenuItems.filter((menuItem) => menuItem.section === undefined);
36
+ this.universeOperationsMenuItems = registeredMenuItems.filter((menuItem) => menuItem.section === 'operations');
37
+ this.universeManagementMenuItems = registeredMenuItems.filter((menuItem) => menuItem.section === 'management');
38
+ this.universeMaintenanceMenuItems = registeredMenuItems.filter((menuItem) => menuItem.section === 'maintenance');
39
+ this.universeConnectivityMenuItems = registeredMenuItems.filter((menuItem) => menuItem.section === 'connectivity');
40
+ this.universeAnalyticsMenuItems = registeredMenuItems.filter((menuItem) => menuItem.section === 'analytics');
31
41
  this.universeSettingsMenuItems = registeredMenuItems.filter((menuItem) => menuItem.section === 'settings');
32
42
  }
33
43
 
@@ -35,6 +45,7 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
35
45
  createMenuPanels() {
36
46
  const operationsItems = [
37
47
  {
48
+ priority: 0,
38
49
  intl: 'menu.dashboard',
39
50
  title: this.intl.t('menu.dashboard'),
40
51
  icon: 'home',
@@ -43,14 +54,16 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
43
54
  visible: this.abilities.can('fleet-ops see order'),
44
55
  },
45
56
  {
46
- intl: 'menu.service-rates',
47
- title: this.intl.t('menu.service-rates'),
48
- icon: 'file-invoice-dollar',
49
- route: 'operations.service-rates',
50
- permission: 'fleet-ops list service-rate',
51
- visible: this.abilities.can('fleet-ops see service-rate'),
57
+ priority: 1,
58
+ intl: 'menu.orchestrator',
59
+ title: this.intl.t('menu.orchestrator'),
60
+ icon: 'circle-nodes',
61
+ route: 'operations.orchestrator',
62
+ permission: 'fleet-ops list order',
63
+ visible: this.abilities.can('fleet-ops see order'),
52
64
  },
53
65
  {
66
+ priority: 2,
54
67
  intl: 'menu.scheduler',
55
68
  title: this.intl.t('menu.scheduler'),
56
69
  icon: 'calendar-day',
@@ -59,6 +72,7 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
59
72
  visible: this.abilities.can('fleet-ops see order'),
60
73
  },
61
74
  {
75
+ priority: 3,
62
76
  intl: 'menu.order-config',
63
77
  title: this.intl.t('menu.order-config'),
64
78
  icon: 'diagram-project',
@@ -66,10 +80,21 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
66
80
  permission: 'fleet-ops list order-config',
67
81
  visible: this.abilities.can('fleet-ops see order-config'),
68
82
  },
69
- ];
83
+ {
84
+ priority: 4,
85
+ intl: 'menu.service-rates',
86
+ title: this.intl.t('menu.service-rates'),
87
+ icon: 'file-invoice-dollar',
88
+ route: 'operations.service-rates',
89
+ permission: 'fleet-ops list service-rate',
90
+ visible: this.abilities.can('fleet-ops see service-rate'),
91
+ },
92
+ ...(this.universeOperationsMenuItems ?? []).map((item) => ({ ...item, _virtual: true })),
93
+ ].sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
70
94
 
71
95
  const resourcesItems = [
72
96
  {
97
+ priority: 0,
73
98
  intl: 'menu.drivers',
74
99
  title: this.intl.t('menu.drivers'),
75
100
  icon: 'id-card',
@@ -80,6 +105,7 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
80
105
  visible: this.abilities.can('fleet-ops see driver'),
81
106
  },
82
107
  {
108
+ priority: 1,
83
109
  intl: 'menu.vehicles',
84
110
  title: this.intl.t('menu.vehicles'),
85
111
  icon: 'truck',
@@ -88,6 +114,7 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
88
114
  visible: this.abilities.can('fleet-ops see vehicle'),
89
115
  },
90
116
  {
117
+ priority: 2,
91
118
  intl: 'menu.fleets',
92
119
  title: this.intl.t('menu.fleets'),
93
120
  icon: 'user-group',
@@ -98,6 +125,7 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
98
125
  visible: this.abilities.can('fleet-ops see fleet'),
99
126
  },
100
127
  {
128
+ priority: 3,
101
129
  intl: 'menu.vendors',
102
130
  title: this.intl.t('menu.vendors'),
103
131
  icon: 'warehouse',
@@ -106,6 +134,7 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
106
134
  visible: this.abilities.can('fleet-ops see vendor'),
107
135
  },
108
136
  {
137
+ priority: 4,
109
138
  intl: 'menu.contacts',
110
139
  title: this.intl.t('menu.contacts'),
111
140
  icon: 'address-book',
@@ -114,6 +143,7 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
114
143
  visible: this.abilities.can('fleet-ops see contact'),
115
144
  },
116
145
  {
146
+ priority: 5,
117
147
  intl: 'menu.places',
118
148
  title: this.intl.t('menu.places'),
119
149
  icon: 'location-dot',
@@ -122,6 +152,7 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
122
152
  visible: this.abilities.can('fleet-ops see place'),
123
153
  },
124
154
  {
155
+ priority: 6,
125
156
  intl: 'menu.fuel-reports',
126
157
  title: this.intl.t('menu.fuel-reports'),
127
158
  icon: 'gas-pump',
@@ -130,6 +161,7 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
130
161
  visible: this.abilities.can('fleet-ops see fuel-report'),
131
162
  },
132
163
  {
164
+ priority: 7,
133
165
  intl: 'menu.issues',
134
166
  title: this.intl.t('menu.issues'),
135
167
  icon: 'triangle-exclamation',
@@ -137,10 +169,12 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
137
169
  permission: 'fleet-ops list issue',
138
170
  visible: this.abilities.can('fleet-ops see issue'),
139
171
  },
140
- ];
172
+ ...(this.universeManagementMenuItems ?? []).map((item) => ({ ...item, _virtual: true })),
173
+ ].sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
141
174
 
142
175
  const connectivityItems = [
143
176
  {
177
+ priority: 0,
144
178
  intl: 'menu.telematics',
145
179
  title: this.intl.t('menu.telematics'),
146
180
  icon: 'satellite-dish',
@@ -149,6 +183,7 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
149
183
  visible: this.abilities.can('fleet-ops see telematic'),
150
184
  },
151
185
  {
186
+ priority: 1,
152
187
  intl: 'menu.devices',
153
188
  title: this.intl.t('menu.devices'),
154
189
  icon: 'hard-drive',
@@ -157,6 +192,7 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
157
192
  visible: this.abilities.can('fleet-ops see device'),
158
193
  },
159
194
  {
195
+ priority: 2,
160
196
  intl: 'menu.sensors',
161
197
  title: this.intl.t('menu.sensors'),
162
198
  icon: 'temperature-full',
@@ -165,6 +201,7 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
165
201
  visible: this.abilities.can('fleet-ops see sensor'),
166
202
  },
167
203
  {
204
+ priority: 3,
168
205
  intl: 'menu.events',
169
206
  title: this.intl.t('menu.events'),
170
207
  icon: 'stream',
@@ -172,18 +209,21 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
172
209
  permission: 'fleet-ops list device-event',
173
210
  visible: this.abilities.can('fleet-ops see device-event'),
174
211
  },
175
- // {
176
- // intl: 'menu.tracking',
177
- // title: this.intl.t('menu.tracking'),
178
- // icon: 'map-marked-alt',
179
- // route: 'connectivity.tracking',
180
- // permission: 'fleet-ops list device',
181
- // visible: this.abilities.can('fleet-ops see device'),
182
- // },
183
- ];
212
+ ...(this.universeConnectivityMenuItems ?? []).map((item) => ({ ...item, _virtual: true })),
213
+ ].sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
184
214
 
185
215
  const maintenanceItems = [
186
216
  {
217
+ priority: 0,
218
+ intl: 'menu.schedules',
219
+ title: this.intl.t('menu.schedules'),
220
+ icon: 'calendar-alt',
221
+ route: 'maintenance.schedules',
222
+ permission: 'fleet-ops list maintenance-schedule',
223
+ visible: this.abilities.can('fleet-ops see maintenance-schedule'),
224
+ },
225
+ {
226
+ priority: 1,
187
227
  intl: 'menu.work-orders',
188
228
  title: this.intl.t('menu.work-orders'),
189
229
  icon: 'clipboard-list',
@@ -192,6 +232,16 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
192
232
  visible: this.abilities.can('fleet-ops see work-order'),
193
233
  },
194
234
  {
235
+ priority: 2,
236
+ intl: 'menu.maintenances',
237
+ title: this.intl.t('menu.maintenances'),
238
+ icon: 'history',
239
+ route: 'maintenance.maintenances',
240
+ permission: 'fleet-ops list maintenance',
241
+ visible: this.abilities.can('fleet-ops see maintenance'),
242
+ },
243
+ {
244
+ priority: 3,
195
245
  intl: 'menu.equipment',
196
246
  title: this.intl.t('menu.equipment'),
197
247
  icon: 'trailer',
@@ -200,6 +250,7 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
200
250
  visible: this.abilities.can('fleet-ops see equipment'),
201
251
  },
202
252
  {
253
+ priority: 4,
203
254
  intl: 'menu.parts',
204
255
  title: this.intl.t('menu.parts'),
205
256
  icon: 'cog',
@@ -207,10 +258,12 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
207
258
  permission: 'fleet-ops list part',
208
259
  visible: this.abilities.can('fleet-ops see part'),
209
260
  },
210
- ];
261
+ ...(this.universeMaintenanceMenuItems ?? []).map((item) => ({ ...item, _virtual: true })),
262
+ ].sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
211
263
 
212
264
  const analyticsItems = [
213
265
  {
266
+ priority: 0,
214
267
  intl: 'menu.reports',
215
268
  title: this.intl.t('menu.reports'),
216
269
  icon: 'file-import',
@@ -218,10 +271,12 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
218
271
  permission: 'iam list report',
219
272
  visible: this.abilities.can('fleet-ops see report'),
220
273
  },
221
- ];
274
+ ...(this.universeAnalyticsMenuItems ?? []).map((item) => ({ ...item, _virtual: true })),
275
+ ].sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
222
276
 
223
277
  const settingsItems = [
224
278
  {
279
+ priority: 0,
225
280
  intl: 'menu.navigator-app',
226
281
  title: this.intl.t('menu.navigator-app'),
227
282
  icon: 'location-arrow',
@@ -230,6 +285,7 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
230
285
  visible: this.abilities.can('fleet-ops see navigator-settings'),
231
286
  },
232
287
  {
288
+ priority: 1,
233
289
  intl: 'menu.payments',
234
290
  title: this.intl.t('menu.payments'),
235
291
  icon: 'cash-register',
@@ -238,6 +294,7 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
238
294
  visible: this.abilities.can('fleet-ops see payments'),
239
295
  },
240
296
  {
297
+ priority: 2,
241
298
  intl: 'menu.notifications',
242
299
  title: this.intl.t('menu.notifications'),
243
300
  icon: 'bell',
@@ -246,6 +303,7 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
246
303
  visible: this.abilities.can('fleet-ops see notification-settings'),
247
304
  },
248
305
  {
306
+ priority: 3,
249
307
  intl: 'menu.routing',
250
308
  title: this.intl.t('menu.routing'),
251
309
  icon: 'route',
@@ -254,6 +312,25 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
254
312
  visible: this.abilities.can('fleet-ops see routing-settings'),
255
313
  },
256
314
  {
315
+ priority: 4,
316
+ intl: 'menu.orchestrator',
317
+ title: this.intl.t('menu.orchestrator'),
318
+ icon: 'circle-nodes',
319
+ route: 'settings.orchestrator',
320
+ permission: 'fleet-ops view routing-settings',
321
+ visible: this.abilities.can('fleet-ops see routing-settings'),
322
+ },
323
+ {
324
+ priority: 5,
325
+ intl: 'menu.scheduling',
326
+ title: this.intl.t('menu.scheduling'),
327
+ icon: 'calendar-days',
328
+ route: 'settings.scheduling',
329
+ permission: 'fleet-ops view scheduling-settings',
330
+ visible: this.abilities.can('fleet-ops see scheduling-settings'),
331
+ },
332
+ {
333
+ priority: 6,
257
334
  intl: 'menu.custom-fields',
258
335
  title: this.intl.t('menu.custom-fields'),
259
336
  icon: 'rectangle-list',
@@ -262,6 +339,7 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
262
339
  visible: this.abilities.can('fleet-ops see custom-field'),
263
340
  },
264
341
  {
342
+ priority: 7,
265
343
  intl: 'menu.avatars',
266
344
  title: this.intl.t('menu.avatars'),
267
345
  icon: 'icons',
@@ -269,7 +347,8 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
269
347
  permission: 'fleet-ops view avatar',
270
348
  visible: this.abilities.can('fleet-ops see avatar'),
271
349
  },
272
- ];
350
+ ...(this.universeSettingsMenuItems ?? []).map((item) => ({ ...item, _virtual: true })),
351
+ ].sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
273
352
 
274
353
  const createPanel = (intl, routePrefix, items = [], options = {}) => ({
275
354
  intl,
@@ -289,10 +368,10 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
289
368
  open: this.appCache.get('fleet-ops:sidebar:management:open', true),
290
369
  onToggle: (open) => this.appCache.set('fleet-ops:sidebar:management:open', open),
291
370
  }),
292
- // createPanel('menu.maintenance', 'maintenance', maintenanceItems, {
293
- // open: this.appCache.get('fleet-ops:sidebar:maintenance:open', false),
294
- // onToggle: (open) => this.appCache.set('fleet-ops:sidebar:maintenance:open', open),
295
- // }),
371
+ createPanel('menu.maintenance', 'maintenance', maintenanceItems, {
372
+ open: this.appCache.get('fleet-ops:sidebar:maintenance:open', false),
373
+ onToggle: (open) => this.appCache.set('fleet-ops:sidebar:maintenance:open', open),
374
+ }),
296
375
  createPanel('menu.connectivity', 'connectivity', connectivityItems, {
297
376
  open: this.appCache.get('fleet-ops:sidebar:connectivity:open', false),
298
377
  onToggle: (open) => this.appCache.set('fleet-ops:sidebar:connectivity:open', open),
@@ -0,0 +1,176 @@
1
+ {{! ===== COST PANEL — invoice-style line items ===== }}
2
+ <ContentPanel @title="Costs" @open={{true}} @wrapperClass="bordered-top">
3
+
4
+ {{! ── Currency selector (single, shared by all money fields) ── }}
5
+ <div class="grid grid-cols-1 gap-2 lg:grid-cols-2 lg:gap-2 no-input-group-padding text-xs mb-2">
6
+ <InputGroup @name="Currency">
7
+ <CurrencySelect @currency={{@resource.currency}} @onCurrencyChange={{fn (mut @resource.currency)}} @triggerClass="w-full form-select" @disabled={{this.isDisabled}} />
8
+ </InputGroup>
9
+ </div>
10
+
11
+ {{! ── Labour & Tax inputs (always editable on the form) ── }}
12
+ <div class="grid grid-cols-1 gap-2 lg:grid-cols-2 lg:gap-2 no-input-group-padding text-xs mb-4">
13
+ <InputGroup @name="Labour Cost">
14
+ <MoneyInput class="w-full" @value={{@resource.labor_cost}} @currency={{this.currency}} @onChange={{fn (mut @resource.labor_cost)}} @disabled={{this.isDisabled}} />
15
+ </InputGroup>
16
+ <InputGroup @name="Tax">
17
+ <MoneyInput class="w-full" @value={{@resource.tax}} @currency={{this.currency}} @onChange={{fn (mut @resource.tax)}} @disabled={{this.isDisabled}} />
18
+ </InputGroup>
19
+ </div>
20
+
21
+ {{! ── Line items table ── }}
22
+ <div class="text-[11px] uppercase tracking-wide text-gray-500 dark:text-gray-400 font-semibold mb-2">
23
+ Parts &amp; Services
24
+ </div>
25
+
26
+ <table class="w-full text-xs border-collapse mb-2 table-fixed">
27
+ <colgroup>
28
+ <col class="w-[45%]" />
29
+ <col class="w-[10%]" />
30
+ <col class="w-[20%]" />
31
+ <col class="w-[15%]" />
32
+ {{#unless this.isDisabled}}
33
+ <col class="w-[10%]" />
34
+ {{/unless}}
35
+ </colgroup>
36
+ <thead>
37
+ <tr class="border-b border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 text-left">
38
+ <th class="pb-1 pr-2 font-medium">Description</th>
39
+ <th class="pb-1 pr-2 font-medium text-right">Qty</th>
40
+ <th class="pb-1 pr-2 font-medium text-right">Unit Cost</th>
41
+ <th class="pb-1 pr-2 font-medium text-right">Total</th>
42
+ {{#unless this.isDisabled}}
43
+ <th class="pb-1"></th>
44
+ {{/unless}}
45
+ </tr>
46
+ </thead>
47
+ <tbody>
48
+ {{#each this.lineItems as |item index|}}
49
+ {{#if (eq this.editingIndex index)}}
50
+ {{! ── Inline edit row ── }}
51
+ <tr class="border-b border-gray-100 dark:border-gray-800">
52
+ <td class="py-1 pr-2">
53
+ <Input @value={{this.draftDescription}} class="form-input w-full text-xs" placeholder="Description" />
54
+ </td>
55
+ <td class="py-1 pr-2">
56
+ <Input @value={{this.draftQuantity}} @type="number" class="form-input w-full text-xs text-right" min="0" />
57
+ </td>
58
+ <td class="py-1 pr-2">
59
+ <MoneyInput class="w-full" @value={{this.draftUnitCost}} @currency={{this.currency}} @onChange={{this.setDraftUnitCost}} />
60
+ </td>
61
+ <td class="py-1 pr-2 text-right text-gray-500 dark:text-gray-400">
62
+ {{format-currency this.draftLineTotal this.currency}}
63
+ </td>
64
+ <td class="py-1">
65
+ <div class="flex items-center space-x-1">
66
+ <Button @size="xs" @type="primary" @onClick={{this.saveEdit}} class="px-2 py-0.5">
67
+ Save
68
+ </Button>
69
+ <Button @size="xs" @type="default" @onClick={{this.cancelEdit}} class="px-2 py-0.5">
70
+ Cancel
71
+ </Button>
72
+ </div>
73
+ </td>
74
+ </tr>
75
+ {{else}}
76
+ {{! ── Read row ── }}
77
+ <tr class="border-b border-gray-100 dark:border-gray-800 hover:bg-gray-50 dark:hover:bg-gray-800/40 group">
78
+ <td class="py-1.5 pr-2 text-gray-800 dark:text-gray-200 truncate">{{item.description}}</td>
79
+ <td class="py-1.5 pr-2 text-right text-gray-600 dark:text-gray-400">{{item.quantity}}</td>
80
+ <td class="py-1.5 pr-2 text-right text-gray-600 dark:text-gray-400">
81
+ {{format-currency (this._toCents item.unit_cost) this.currency}}
82
+ </td>
83
+ <td class="py-1.5 pr-2 text-right font-medium text-gray-800 dark:text-gray-200">
84
+ {{format-currency (this.lineTotal item) this.currency}}
85
+ </td>
86
+ {{#unless this.isDisabled}}
87
+ <td class="py-1.5">
88
+ <div class="flex items-center space-x-1 opacity-0 group-hover:opacity-100 transition-opacity">
89
+ <button
90
+ type="button"
91
+ class="text-gray-400 hover:text-blue-500 dark:hover:text-blue-400 transition-colors"
92
+ title="Edit"
93
+ {{on "click" (fn this.startEdit index)}}
94
+ >
95
+ <FaIcon @icon="pencil" @size="xs" />
96
+ </button>
97
+ <button
98
+ type="button"
99
+ class="text-gray-400 hover:text-red-500 dark:hover:text-red-400 transition-colors"
100
+ title="Remove"
101
+ {{on "click" (fn this.removeLineItem index)}}
102
+ >
103
+ <FaIcon @icon="trash" @size="xs" />
104
+ </button>
105
+ </div>
106
+ </td>
107
+ {{/unless}}
108
+ </tr>
109
+ {{/if}}
110
+ {{/each}}
111
+
112
+ {{! ── Add new item row ── }}
113
+ {{#if this.isAddingItem}}
114
+ <tr class="border-b border-gray-100 dark:border-gray-800">
115
+ <td class="py-1 pr-2">
116
+ <Input @value={{this.draftDescription}} class="form-input w-full text-xs" placeholder="Description" />
117
+ </td>
118
+ <td class="py-1 pr-2">
119
+ <Input @value={{this.draftQuantity}} @type="number" class="form-input w-full text-xs text-right" min="0" />
120
+ </td>
121
+ <td class="py-1 pr-2">
122
+ <MoneyInput class="w-full" @value={{this.draftUnitCost}} @currency={{this.currency}} @onChange={{this.setDraftUnitCost}} />
123
+ </td>
124
+ <td class="py-1 pr-2 text-right text-gray-500 dark:text-gray-400">
125
+ {{format-currency this.draftLineTotal this.currency}}
126
+ </td>
127
+ <td class="py-1">
128
+ <div class="flex items-center space-x-1">
129
+ <Button @size="xs" @type="primary" @onClick={{this.addLineItem}} class="px-2 py-0.5">
130
+ Add
131
+ </Button>
132
+ <Button @size="xs" @type="default" @onClick={{this.cancelAdd}} class="px-2 py-0.5">
133
+ Cancel
134
+ </Button>
135
+ </div>
136
+ </td>
137
+ </tr>
138
+ {{/if}}
139
+ </tbody>
140
+ </table>
141
+
142
+ {{! ── Add item button ── }}
143
+ {{#unless this.isDisabled}}
144
+ {{#unless this.isAddingItem}}
145
+ <button
146
+ type="button"
147
+ class="flex items-center space-x-1 text-xs text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300 font-medium mb-3 transition-colors"
148
+ {{on "click" this.showAddRow}}
149
+ >
150
+ <FaIcon @icon="plus" @size="xs" />
151
+ <span>Add line item</span>
152
+ </button>
153
+ {{/unless}}
154
+ {{/unless}}
155
+
156
+ {{! ── Totals summary ── }}
157
+ <div class="border-t border-gray-200 dark:border-gray-700 pt-2 mt-1 space-y-1 text-xs">
158
+ <div class="flex justify-between text-gray-500 dark:text-gray-400">
159
+ <span>Labour</span>
160
+ <span>{{format-currency this.laborCost this.currency}}</span>
161
+ </div>
162
+ <div class="flex justify-between text-gray-500 dark:text-gray-400">
163
+ <span>Parts</span>
164
+ <span>{{format-currency this.partsCost this.currency}}</span>
165
+ </div>
166
+ <div class="flex justify-between text-gray-500 dark:text-gray-400">
167
+ <span>Tax</span>
168
+ <span>{{format-currency this.tax this.currency}}</span>
169
+ </div>
170
+ <div class="flex justify-between font-semibold text-gray-800 dark:text-gray-100 border-t border-gray-200 dark:border-gray-700 pt-1 mt-1">
171
+ <span>Total</span>
172
+ <span>{{format-currency this.totalCost this.currency}}</span>
173
+ </div>
174
+ </div>
175
+
176
+ </ContentPanel>