@fleetbase/fleetops-engine 0.6.38 → 0.6.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (429) hide show
  1. package/addon/components/activity/form.hbs +2 -12
  2. package/addon/components/activity/logic-builder.hbs +1 -1
  3. package/addon/components/custom-entity/form.hbs +4 -20
  4. package/addon/components/device/form.hbs +3 -15
  5. package/addon/components/device/panel-header.hbs +13 -2
  6. package/addon/components/driver/details.hbs +33 -2
  7. package/addon/components/driver/form.hbs +42 -0
  8. package/addon/components/driver/panel-header.hbs +8 -1
  9. package/addon/components/driver/schedule.hbs +115 -76
  10. package/addon/components/driver/schedule.js +349 -157
  11. package/addon/components/driver-onboard-settings.hbs +2 -8
  12. package/addon/components/entity-field-editing-settings.hbs +2 -5
  13. package/addon/components/equipment/card.hbs +49 -0
  14. package/addon/components/equipment/card.js +6 -0
  15. package/addon/components/equipment/details.hbs +83 -44
  16. package/addon/components/equipment/form.hbs +111 -41
  17. package/addon/components/equipment/form.js +78 -10
  18. package/addon/components/equipment/panel-header.hbs +36 -0
  19. package/addon/components/equipment/panel-header.js +2 -0
  20. package/addon/components/fleet/driver-listing.hbs +3 -1
  21. package/addon/components/fleet/vehicle-listing.hbs +4 -7
  22. package/addon/components/fleet-panel/vehicle-listing.hbs +1 -6
  23. package/addon/components/fuel-report/form.hbs +1 -5
  24. package/addon/components/layout/fleet-ops-sidebar.hbs +40 -36
  25. package/addon/components/layout/fleet-ops-sidebar.js +61 -10
  26. package/addon/components/maintenance/cost-panel.hbs +176 -0
  27. package/addon/components/maintenance/cost-panel.js +241 -0
  28. package/addon/components/maintenance/details.hbs +123 -60
  29. package/addon/components/maintenance/form.hbs +138 -78
  30. package/addon/components/maintenance/form.js +131 -6
  31. package/addon/components/maintenance/panel-header.hbs +31 -0
  32. package/addon/components/maintenance/panel-header.js +2 -0
  33. package/addon/components/maintenance-schedule/details.hbs +260 -0
  34. package/addon/components/maintenance-schedule/details.js +158 -0
  35. package/addon/components/maintenance-schedule/form.hbs +287 -0
  36. package/addon/components/maintenance-schedule/form.js +199 -0
  37. package/addon/components/map/container.hbs +1 -1
  38. package/addon/components/map/drawer/device-event-listing.hbs +1 -1
  39. package/addon/components/map/drawer/driver-listing.hbs +1 -6
  40. package/addon/components/map/drawer/place-listing.hbs +1 -6
  41. package/addon/components/map/drawer/vehicle-listing.hbs +1 -6
  42. package/addon/components/map/drawer.hbs +8 -1
  43. package/addon/components/map/leaflet-live-map.hbs +2 -1
  44. package/addon/components/map/toolbar/visibility-control-panel.hbs +1 -1
  45. package/addon/components/map/toolbar.hbs +4 -29
  46. package/addon/components/modals/add-driver-shift.hbs +155 -0
  47. package/addon/components/modals/add-driver-shift.js +210 -0
  48. package/addon/components/modals/bulk-assign-orders.hbs +67 -0
  49. package/addon/components/modals/driver-shift.hbs +43 -0
  50. package/addon/components/modals/driver-shift.js +56 -0
  51. package/addon/components/modals/entity-form.hbs +5 -28
  52. package/addon/components/modals/orchestrator-import.hbs +351 -0
  53. package/addon/components/modals/orchestrator-import.js +807 -0
  54. package/addon/components/modals/order-config-new-status.hbs +1 -5
  55. package/addon/components/modals/scheduling-conflict.hbs +47 -0
  56. package/addon/components/modals/scheduling-conflict.js +53 -0
  57. package/addon/components/modals/send-work-order.hbs +91 -0
  58. package/addon/components/modals/send-work-order.js +3 -0
  59. package/addon/components/modals/service-area-form.hbs +1 -6
  60. package/addon/components/modals/set-driver-availability.hbs +50 -0
  61. package/addon/components/modals/set-driver-availability.js +57 -0
  62. package/addon/components/modals/update-order-activity.hbs +13 -9
  63. package/addon/components/modals/user-form.hbs +1 -5
  64. package/addon/components/modals/vehicle-form.hbs +17 -102
  65. package/addon/components/modals/vendor-form.hbs +15 -82
  66. package/addon/components/orchestrator/card-fields-settings.hbs +76 -0
  67. package/addon/components/orchestrator/card-fields-settings.js +134 -0
  68. package/addon/components/orchestrator/order-pool.hbs +264 -0
  69. package/addon/components/orchestrator/order-pool.js +394 -0
  70. package/addon/components/orchestrator/phase-builder.hbs +162 -0
  71. package/addon/components/orchestrator/phase-builder.js +162 -0
  72. package/addon/components/orchestrator/plan-viewer.hbs +278 -0
  73. package/addon/components/orchestrator/plan-viewer.js +342 -0
  74. package/addon/components/orchestrator/resource-panel.hbs +301 -0
  75. package/addon/components/orchestrator/resource-panel.js +106 -0
  76. package/addon/components/orchestrator-workbench.hbs +318 -0
  77. package/addon/components/orchestrator-workbench.js +1087 -0
  78. package/addon/components/order/details/custom-fields.hbs +10 -1
  79. package/addon/components/order/details/detail.hbs +37 -2
  80. package/addon/components/order/details/detail.js +0 -1
  81. package/addon/components/order/details/integrated-vendor-details.hbs +4 -4
  82. package/addon/components/order/details/notes.hbs +1 -7
  83. package/addon/components/order/details/route.hbs +1 -5
  84. package/addon/components/order/form/details.hbs +44 -10
  85. package/addon/components/order/form/details.js +57 -0
  86. package/addon/components/order/form/notes.hbs +1 -7
  87. package/addon/components/order/form/payload.hbs +1 -7
  88. package/addon/components/order/form/route.hbs +3 -15
  89. package/addon/components/order/header.hbs +1 -7
  90. package/addon/components/order/route-editor.hbs +4 -25
  91. package/addon/components/order/schedule-card.hbs +102 -95
  92. package/addon/components/order/schedule-card.js +8 -3
  93. package/addon/components/order-config-manager/details.hbs +2 -10
  94. package/addon/components/order-config-manager/entities.hbs +1 -7
  95. package/addon/components/order-config-manager.hbs +1 -6
  96. package/addon/components/part/card.hbs +49 -0
  97. package/addon/components/part/card.js +6 -0
  98. package/addon/components/part/details.hbs +102 -56
  99. package/addon/components/part/form.hbs +131 -56
  100. package/addon/components/part/form.js +78 -11
  101. package/addon/components/part/panel-header.hbs +36 -0
  102. package/addon/components/part/panel-header.js +2 -0
  103. package/addon/components/place/form.hbs +1 -7
  104. package/addon/components/sensor/details.hbs +1 -1
  105. package/addon/components/sensor/form.hbs +5 -3
  106. package/addon/components/sensor/panel-header.hbs +8 -1
  107. package/addon/components/service-area/form.hbs +1 -6
  108. package/addon/components/service-rate/form.hbs +12 -60
  109. package/addon/components/telematic/form.hbs +6 -2
  110. package/addon/components/vehicle/details/maintenance-history.hbs +42 -0
  111. package/addon/components/vehicle/details/maintenance-history.js +32 -0
  112. package/addon/components/vehicle/details/schedules.hbs +40 -0
  113. package/addon/components/vehicle/details/schedules.js +32 -0
  114. package/addon/components/vehicle/details/work-orders.hbs +42 -0
  115. package/addon/components/vehicle/details/work-orders.js +32 -0
  116. package/addon/components/vehicle/details.hbs +30 -0
  117. package/addon/components/vehicle/form.hbs +39 -0
  118. package/addon/components/vehicle/panel-header.hbs +19 -49
  119. package/addon/components/warranty/details.hbs +3 -2
  120. package/addon/components/warranty/form.hbs +3 -17
  121. package/addon/components/work-order/details.hbs +135 -40
  122. package/addon/components/work-order/form.hbs +178 -45
  123. package/addon/components/work-order/form.js +197 -4
  124. package/addon/components/work-order/panel-header.hbs +31 -0
  125. package/addon/components/work-order/panel-header.js +2 -0
  126. package/addon/controllers/connectivity/devices/index/details.js +1 -1
  127. package/addon/controllers/maintenance/equipment/index/details/index.js +0 -1
  128. package/addon/controllers/maintenance/equipment/index/details.js +36 -1
  129. package/addon/controllers/maintenance/equipment/index/edit.js +56 -1
  130. package/addon/controllers/maintenance/equipment/index/new.js +32 -1
  131. package/addon/controllers/maintenance/equipment/index.js +127 -113
  132. package/addon/controllers/maintenance/maintenances/index/details/index.js +3 -0
  133. package/addon/controllers/maintenance/maintenances/index/details.js +54 -0
  134. package/addon/controllers/maintenance/maintenances/index/edit.js +68 -0
  135. package/addon/controllers/maintenance/maintenances/index/new.js +34 -0
  136. package/addon/controllers/maintenance/maintenances/index.js +191 -0
  137. package/addon/controllers/maintenance/parts/index/details/index.js +0 -1
  138. package/addon/controllers/maintenance/parts/index/details.js +36 -1
  139. package/addon/controllers/maintenance/parts/index/edit.js +56 -1
  140. package/addon/controllers/maintenance/parts/index/new.js +32 -1
  141. package/addon/controllers/maintenance/parts/index.js +135 -113
  142. package/addon/controllers/maintenance/schedules/index/details.js +115 -0
  143. package/addon/controllers/maintenance/schedules/index/edit.js +41 -0
  144. package/addon/controllers/maintenance/schedules/index/new.js +33 -0
  145. package/addon/controllers/maintenance/schedules/index.js +280 -0
  146. package/addon/controllers/maintenance/work-orders/index/details.js +41 -1
  147. package/addon/controllers/maintenance/work-orders/index/edit.js +67 -1
  148. package/addon/controllers/maintenance/work-orders/index/new.js +43 -1
  149. package/addon/controllers/maintenance/work-orders/index.js +105 -113
  150. package/addon/controllers/management/drivers/index/details.js +6 -1
  151. package/addon/controllers/management/vehicles/index/details.js +65 -0
  152. package/addon/controllers/management/vehicles/index.js +18 -0
  153. package/addon/controllers/operations/orchestrator.js +10 -0
  154. package/addon/controllers/operations/orders/index.js +6 -0
  155. package/addon/controllers/operations/scheduler/fleet-schedule.js +341 -0
  156. package/addon/controllers/operations/scheduler/index.js +799 -275
  157. package/addon/controllers/operations/scheduler.js +21 -0
  158. package/addon/controllers/settings/orchestrator.js +70 -0
  159. package/addon/controllers/settings/scheduling.js +155 -0
  160. package/addon/extension.js +19 -0
  161. package/addon/instance-initializers/register-vroom-allocation.js +27 -0
  162. package/addon/models/maintenance-schedule.js +61 -0
  163. package/addon/routes/maintenance/equipment/index/details.js +27 -1
  164. package/addon/routes/maintenance/equipment/index/edit.js +27 -1
  165. package/addon/routes/maintenance/maintenances/index/details/index.js +3 -0
  166. package/addon/routes/maintenance/maintenances/index/details.js +29 -0
  167. package/addon/routes/maintenance/maintenances/index/edit.js +29 -0
  168. package/addon/routes/maintenance/maintenances/index/new.js +3 -0
  169. package/addon/routes/maintenance/maintenances/index.js +23 -0
  170. package/addon/routes/maintenance/maintenances.js +3 -0
  171. package/addon/routes/maintenance/parts/index/details.js +27 -1
  172. package/addon/routes/maintenance/parts/index/edit.js +27 -1
  173. package/addon/routes/maintenance/schedules/index/details/index.js +2 -0
  174. package/addon/routes/maintenance/schedules/index/details/work-orders.js +11 -0
  175. package/addon/routes/maintenance/schedules/index/details.js +25 -0
  176. package/addon/routes/maintenance/schedules/index/edit.js +25 -0
  177. package/addon/routes/maintenance/schedules/index/new.js +2 -0
  178. package/addon/routes/maintenance/schedules/index.js +21 -0
  179. package/addon/routes/maintenance/schedules.js +2 -0
  180. package/addon/routes/maintenance/work-orders/index/details.js +27 -1
  181. package/addon/routes/maintenance/work-orders/index/edit.js +27 -1
  182. package/addon/routes/management/vehicles/index/details/maintenance-history.js +3 -0
  183. package/addon/routes/management/vehicles/index/details/schedules.js +3 -0
  184. package/addon/routes/management/vehicles/index/details/work-orders.js +3 -0
  185. package/addon/routes/operations/orchestrator.js +23 -0
  186. package/addon/routes/operations/scheduler/fleet-schedule.js +28 -0
  187. package/addon/routes/operations/scheduler/index.js +48 -26
  188. package/addon/routes/operations/scheduler.js +14 -1
  189. package/addon/routes/settings/orchestrator.js +27 -0
  190. package/addon/routes/settings/scheduling.js +3 -0
  191. package/addon/routes.js +31 -1
  192. package/addon/services/driver-actions.js +40 -7
  193. package/addon/services/driver-scheduling.js +4 -1
  194. package/addon/services/equipment-actions.js +15 -5
  195. package/addon/services/leaflet-map-manager.js +14 -6
  196. package/addon/services/maintenance-actions.js +17 -14
  197. package/addon/services/maintenance-schedule-actions.js +118 -0
  198. package/addon/services/orchestration-engine-interface.js +49 -0
  199. package/addon/services/orchestration-engine.js +74 -0
  200. package/addon/services/order-actions.js +15 -0
  201. package/addon/services/order-allocation.js +116 -0
  202. package/addon/services/part-actions.js +12 -2
  203. package/addon/services/scheduling.js +316 -0
  204. package/addon/services/vehicle-actions.js +70 -7
  205. package/addon/services/vroom-allocation-engine.js +45 -0
  206. package/addon/services/work-order-actions.js +80 -0
  207. package/addon/styles/fleetops-engine.css +1658 -0
  208. package/addon/templates/analytics/reports/index/edit.hbs +1 -1
  209. package/addon/templates/analytics/reports/index/new.hbs +1 -1
  210. package/addon/templates/application.hbs +6 -1
  211. package/addon/templates/connectivity/devices/index/details/events.hbs +0 -1
  212. package/addon/templates/connectivity/devices.hbs +0 -1
  213. package/addon/templates/connectivity/events/index/details.hbs +0 -1
  214. package/addon/templates/connectivity/events.hbs +0 -1
  215. package/addon/templates/connectivity/sensors.hbs +0 -1
  216. package/addon/templates/connectivity/telematics/index/details/devices.hbs +0 -1
  217. package/addon/templates/connectivity/telematics/index/details/events.hbs +0 -1
  218. package/addon/templates/connectivity/telematics/index/details/sensors.hbs +0 -1
  219. package/addon/templates/connectivity/telematics.hbs +0 -1
  220. package/addon/templates/connectivity/tracking.hbs +0 -1
  221. package/addon/templates/connectivity.hbs +0 -1
  222. package/addon/templates/maintenance/equipment/index/details/index.hbs +1 -2
  223. package/addon/templates/maintenance/equipment/index/details.hbs +15 -2
  224. package/addon/templates/maintenance/equipment/index/edit.hbs +12 -2
  225. package/addon/templates/maintenance/equipment/index/new.hbs +1 -2
  226. package/addon/templates/maintenance/equipment/index.hbs +48 -13
  227. package/addon/templates/maintenance/equipment.hbs +0 -1
  228. package/addon/templates/maintenance/maintenances/index/details/index.hbs +1 -0
  229. package/addon/templates/maintenance/maintenances/index/details.hbs +15 -0
  230. package/addon/templates/maintenance/maintenances/index/edit.hbs +12 -0
  231. package/addon/templates/maintenance/maintenances/index/new.hbs +11 -0
  232. package/addon/templates/maintenance/maintenances/index.hbs +14 -0
  233. package/addon/templates/maintenance/maintenances.hbs +1 -0
  234. package/addon/templates/maintenance/parts/index/details/index.hbs +1 -2
  235. package/addon/templates/maintenance/parts/index/details.hbs +15 -2
  236. package/addon/templates/maintenance/parts/index/edit.hbs +12 -2
  237. package/addon/templates/maintenance/parts/index/new.hbs +1 -2
  238. package/addon/templates/maintenance/parts/index.hbs +48 -13
  239. package/addon/templates/maintenance/parts.hbs +0 -1
  240. package/addon/templates/maintenance/schedules/index/details/index.hbs +1 -0
  241. package/addon/templates/maintenance/schedules/index/details/work-orders.hbs +39 -0
  242. package/addon/templates/maintenance/schedules/index/details.hbs +14 -0
  243. package/addon/templates/maintenance/schedules/index/edit.hbs +12 -0
  244. package/addon/templates/maintenance/schedules/index/new.hbs +11 -0
  245. package/addon/templates/maintenance/schedules/index.hbs +40 -0
  246. package/addon/templates/maintenance/schedules.hbs +1 -0
  247. package/addon/templates/maintenance/work-orders/index/details.hbs +2 -1
  248. package/addon/templates/maintenance/work-orders/index/edit.hbs +2 -4
  249. package/addon/templates/maintenance/work-orders/index/new.hbs +1 -2
  250. package/addon/templates/maintenance/work-orders.hbs +0 -1
  251. package/addon/templates/management/contacts/customers/edit.hbs +1 -2
  252. package/addon/templates/management/contacts/customers/new.hbs +1 -2
  253. package/addon/templates/management/contacts/customers.hbs +1 -1
  254. package/addon/templates/management/contacts/index/edit.hbs +1 -2
  255. package/addon/templates/management/contacts/index/new.hbs +1 -2
  256. package/addon/templates/management/drivers/index/details/orders.hbs +0 -1
  257. package/addon/templates/management/drivers/index/edit.hbs +1 -2
  258. package/addon/templates/management/drivers/index/new.hbs +1 -2
  259. package/addon/templates/management/fleets/index/edit.hbs +1 -2
  260. package/addon/templates/management/fleets/index/new.hbs +1 -2
  261. package/addon/templates/management/fleets/index.hbs +1 -2
  262. package/addon/templates/management/fuel-reports/index/edit.hbs +1 -2
  263. package/addon/templates/management/fuel-reports/index/new.hbs +1 -2
  264. package/addon/templates/management/fuel-reports/index.hbs +1 -2
  265. package/addon/templates/management/issues/index/edit.hbs +1 -2
  266. package/addon/templates/management/issues/index/new.hbs +1 -2
  267. package/addon/templates/management/issues/index.hbs +1 -2
  268. package/addon/templates/management/places/index/details/activity.hbs +0 -1
  269. package/addon/templates/management/places/index/details/comments.hbs +0 -1
  270. package/addon/templates/management/places/index/details/documents.hbs +0 -1
  271. package/addon/templates/management/places/index/details/map.hbs +0 -1
  272. package/addon/templates/management/places/index/details/operations.hbs +0 -1
  273. package/addon/templates/management/places/index/details/performance.hbs +0 -1
  274. package/addon/templates/management/places/index/details/rules.hbs +0 -1
  275. package/addon/templates/management/vehicles/index/details/equipment.hbs +0 -1
  276. package/addon/templates/management/vehicles/index/details/maintenance-history.hbs +2 -0
  277. package/addon/templates/management/vehicles/index/details/schedules.hbs +2 -0
  278. package/addon/templates/management/vehicles/index/details/work-orders.hbs +2 -0
  279. package/addon/templates/management/vehicles/index/details.hbs +1 -1
  280. package/addon/templates/management/vehicles/index/edit.hbs +1 -2
  281. package/addon/templates/management/vehicles/index/new.hbs +1 -2
  282. package/addon/templates/management/vendors/index/edit.hbs +1 -2
  283. package/addon/templates/management/vendors/index/new.hbs +1 -2
  284. package/addon/templates/management/vendors/index.hbs +1 -2
  285. package/addon/templates/management/vendors/integrated.hbs +1 -2
  286. package/addon/templates/operations/orchestrator.hbs +1 -0
  287. package/addon/templates/operations/orders/index.hbs +6 -1
  288. package/addon/templates/operations/scheduler/fleet-schedule.hbs +41 -0
  289. package/addon/templates/operations/scheduler/index.hbs +147 -88
  290. package/addon/templates/operations/scheduler.hbs +7 -1
  291. package/addon/templates/settings/avatars.hbs +1 -1
  292. package/addon/templates/settings/orchestrator.hbs +65 -0
  293. package/addon/templates/settings/payments/index.hbs +1 -5
  294. package/addon/templates/settings/scheduling.hbs +82 -0
  295. package/addon/utils/create-full-calendar-event-from-order.js +52 -14
  296. package/addon/utils/create-full-calendar-event-from-schedule-item.js +50 -0
  297. package/addon/utils/fleet-ops-options.js +254 -0
  298. package/addon/utils/route-colors.js +99 -0
  299. package/addon/utils/to-calendar-date.js +70 -0
  300. package/app/components/driver/schedule.js +1 -0
  301. package/app/components/maintenance/cost-panel.js +1 -0
  302. package/app/components/maintenance/panel-header.js +1 -0
  303. package/app/components/maintenance-schedule/details.js +1 -0
  304. package/app/components/maintenance-schedule/form.js +1 -0
  305. package/app/components/modals/add-driver-shift.js +1 -0
  306. package/app/components/modals/bulk-assign-orders.js +1 -0
  307. package/app/components/modals/driver-shift.js +1 -0
  308. package/app/components/modals/orchestrator-import.js +1 -0
  309. package/app/components/modals/scheduling-conflict.js +1 -0
  310. package/app/components/modals/send-work-order.js +1 -0
  311. package/app/components/modals/set-driver-availability.js +1 -0
  312. package/app/components/orchestrator/card-fields-settings.js +1 -0
  313. package/app/components/orchestrator/order-pool.js +1 -0
  314. package/app/components/orchestrator/phase-builder.js +1 -0
  315. package/app/components/orchestrator/plan-viewer.js +1 -0
  316. package/app/components/orchestrator/resource-panel.js +1 -0
  317. package/app/components/orchestrator-workbench.js +1 -0
  318. package/app/components/vehicle/details/maintenance-history.js +1 -0
  319. package/app/components/vehicle/details/schedules.js +1 -0
  320. package/app/components/vehicle/details/work-orders.js +1 -0
  321. package/app/controllers/operations/orchestrator.js +1 -0
  322. package/app/controllers/settings/orchestrator.js +1 -0
  323. package/app/controllers/settings/scheduling.js +1 -0
  324. package/app/routes/operations/orchestrator.js +1 -0
  325. package/app/routes/settings/orchestrator.js +1 -0
  326. package/app/routes/settings/scheduling.js +1 -0
  327. package/app/services/maintenance-schedule-actions.js +1 -0
  328. package/app/services/orchestration-engine-interface.js +1 -0
  329. package/app/services/orchestration-engine.js +1 -0
  330. package/app/services/order-allocation.js +1 -0
  331. package/app/services/scheduling.js +1 -0
  332. package/app/services/vroom-allocation-engine.js +1 -0
  333. package/app/templates/settings/scheduling.js +1 -0
  334. package/app/utils/create-full-calendar-event-from-schedule-item.js +1 -0
  335. package/app/utils/route-colors.js +1 -0
  336. package/composer.json +5 -3
  337. package/extension.json +1 -1
  338. package/package.json +6 -5
  339. package/server/config/fleetops.php +20 -1
  340. package/server/migrations/2025_08_28_054927_create_parts_table.php +2 -2
  341. package/server/migrations/2025_08_28_054932_add_public_id_to_maintenance_tables.php +45 -0
  342. package/server/migrations/2025_09_01_000001_create_maintenance_schedules_table.php +88 -0
  343. package/server/migrations/2026_04_01_000001_fix_monetary_columns_in_parts_table.php +48 -0
  344. package/server/migrations/2026_04_01_000003_add_photo_uuid_to_equipment_and_parts_tables.php +61 -0
  345. package/server/migrations/2026_04_01_000004_add_public_id_to_equipments_table.php +38 -0
  346. package/server/migrations/2026_04_01_000005_add_missing_columns_to_parts_table.php +67 -0
  347. package/server/migrations/2026_04_04_000001_add_reminder_offsets_to_maintenance_schedules.php +44 -0
  348. package/server/migrations/2026_04_08_000001_add_orchestrator_columns_to_vehicles_table.php +58 -0
  349. package/server/migrations/2026_04_08_000002_add_orchestrator_columns_to_drivers_table.php +41 -0
  350. package/server/migrations/2026_04_08_000003_add_orchestrator_columns_to_orders_table.php +38 -0
  351. package/server/migrations/2026_04_08_000004_add_orchestrator_columns_to_payloads_table.php +41 -0
  352. package/server/migrations/2026_04_08_000005_add_orchestrator_columns_to_waypoints_table.php +38 -0
  353. package/server/migrations/2026_04_09_000001_create_manifests_table.php +48 -0
  354. package/server/migrations/2026_04_09_000002_create_manifest_stops_table.php +48 -0
  355. package/server/migrations/2026_04_09_000003_add_manifest_uuid_to_orders_table.php +28 -0
  356. package/server/migrations/2026_04_13_000001_add_pod_notes_columns_to_waypoints_table.php +39 -0
  357. package/server/resources/views/mail/maintenance-schedule-reminder.blade.php +59 -0
  358. package/server/resources/views/mail/work-order-dispatched.blade.php +67 -0
  359. package/server/src/Auth/Schemas/FleetOps.php +44 -0
  360. package/server/src/Console/Commands/ProcessMaintenanceTriggers.php +150 -0
  361. package/server/src/Console/Commands/SendMaintenanceReminders.php +128 -0
  362. package/server/src/Http/Controllers/Internal/v1/DriverController.php +1 -0
  363. package/server/src/Http/Controllers/Internal/v1/EquipmentController.php +27 -0
  364. package/server/src/Http/Controllers/Internal/v1/LiveController.php +9 -2
  365. package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +165 -0
  366. package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +304 -0
  367. package/server/src/Http/Controllers/Internal/v1/ManifestController.php +138 -0
  368. package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +975 -0
  369. package/server/src/Http/Controllers/Internal/v1/OrderController.php +42 -0
  370. package/server/src/Http/Controllers/Internal/v1/PartController.php +27 -0
  371. package/server/src/Http/Controllers/Internal/v1/SettingController.php +118 -0
  372. package/server/src/Http/Controllers/Internal/v1/Traits/DriverSchedulingTrait.php +214 -0
  373. package/server/src/Http/Controllers/Internal/v1/WorkOrderController.php +68 -0
  374. package/server/src/Http/Resources/v1/Driver.php +1 -0
  375. package/server/src/Http/Resources/v1/Maintenance.php +138 -0
  376. package/server/src/Http/Resources/v1/MaintenanceSchedule.php +137 -0
  377. package/server/src/Http/Resources/v1/Orchestrator/Order.php +116 -0
  378. package/server/src/Http/Resources/v1/Order.php +7 -4
  379. package/server/src/Http/Resources/v1/Waypoint.php +7 -0
  380. package/server/src/Http/Resources/v1/WorkOrder.php +136 -0
  381. package/server/src/Imports/EquipmentImport.php +32 -0
  382. package/server/src/Imports/MaintenanceImport.php +32 -0
  383. package/server/src/Imports/MaintenanceScheduleImport.php +32 -0
  384. package/server/src/Imports/PartImport.php +32 -0
  385. package/server/src/Imports/WorkOrderImport.php +32 -0
  386. package/server/src/Jobs/ProcessAllocationJob.php +119 -0
  387. package/server/src/Listeners/HandleDeliveryCompletion.php +47 -0
  388. package/server/src/Listeners/NotifyDriverOnShiftChange.php +63 -0
  389. package/server/src/Mail/MaintenanceScheduleReminder.php +68 -0
  390. package/server/src/Mail/WorkOrderDispatched.php +58 -0
  391. package/server/src/Models/Asset.php +2 -2
  392. package/server/src/Models/Device.php +1 -1
  393. package/server/src/Models/Driver.php +82 -4
  394. package/server/src/Models/Equipment.php +62 -2
  395. package/server/src/Models/Maintenance.php +127 -9
  396. package/server/src/Models/MaintenanceSchedule.php +353 -0
  397. package/server/src/Models/Manifest.php +214 -0
  398. package/server/src/Models/ManifestStop.php +162 -0
  399. package/server/src/Models/Order.php +70 -0
  400. package/server/src/Models/OrderConfig.php +5 -2
  401. package/server/src/Models/Part.php +69 -3
  402. package/server/src/Models/Payload.php +7 -2
  403. package/server/src/Models/Place.php +1 -1
  404. package/server/src/Models/Sensor.php +1 -1
  405. package/server/src/Models/ServiceQuote.php +1 -1
  406. package/server/src/Models/Vehicle.php +20 -1
  407. package/server/src/Models/Warranty.php +1 -1
  408. package/server/src/Models/Waypoint.php +7 -1
  409. package/server/src/Models/WorkOrder.php +122 -12
  410. package/server/src/Notifications/DriverShiftChanged.php +110 -0
  411. package/server/src/Observers/WorkOrderObserver.php +107 -0
  412. package/server/src/Orchestration/Contracts/OrchestrationEngineInterface.php +63 -0
  413. package/server/src/Orchestration/Engines/DriverAssignmentEngine.php +265 -0
  414. package/server/src/Orchestration/Engines/GreedyOrchestrationEngine.php +155 -0
  415. package/server/src/Orchestration/Engines/RouteSequencingEngine.php +272 -0
  416. package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +192 -0
  417. package/server/src/Orchestration/OrchestrationEngineRegistry.php +83 -0
  418. package/server/src/Orchestration/Support/OrchestrationPayloadBuilder.php +290 -0
  419. package/server/src/Providers/EventServiceProvider.php +7 -1
  420. package/server/src/Providers/FleetOpsServiceProvider.php +42 -15
  421. package/server/src/routes.php +65 -4
  422. package/translations/ar-ae.yml +44 -12
  423. package/translations/bg-bg.yaml +51 -10
  424. package/translations/en-us.yaml +444 -1
  425. package/translations/fr-fr.yaml +51 -10
  426. package/translations/mn-mn.yaml +51 -10
  427. package/translations/pt-br.yaml +51 -10
  428. package/translations/ru-ru.yaml +51 -10
  429. package/translations/vi-vn.yaml +48 -12
@@ -0,0 +1,49 @@
1
+ <Layout::Resource::Card ...attributes as |Card|>
2
+ <Card.header class="{{@headerClass}} truncate">
3
+ <div class="font-semibold truncate">{{or @resource.name @resource.public_id}}</div>
4
+ <div class="text-gray-300 dark:text-gray-500 text-sm truncate">{{or @resource.serial_number @resource.code @resource.public_id}}</div>
5
+ {{#if (has-block "header")}}
6
+ {{yield to="header"}}
7
+ {{/if}}
8
+ </Card.header>
9
+ <Card.body class={{@bodyClass}}>
10
+ <Image src={{@resource.photo_url}} @fallbackSrc={{config "defaultValues.equipmentImage"}} class="card-img-lg" />
11
+ <div class="mt-2 space-y-1 text-xs text-gray-500 dark:text-gray-400">
12
+ {{#if @resource.type}}
13
+ <div class="flex items-center space-x-1">
14
+ <FaIcon @icon="tag" @size="xs" class="text-gray-400" />
15
+ <span>{{humanize @resource.type}}</span>
16
+ </div>
17
+ {{/if}}
18
+ {{#if @resource.status}}
19
+ <div class="flex items-center space-x-1">
20
+ <FaIcon @icon="circle" @size="xs" class="text-gray-400" />
21
+ <span>{{humanize @resource.status}}</span>
22
+ </div>
23
+ {{/if}}
24
+ {{#if @resource.year}}
25
+ <div class="flex items-center space-x-1">
26
+ <FaIcon @icon="calendar" @size="xs" class="text-gray-400" />
27
+ <span>{{@resource.year}}</span>
28
+ </div>
29
+ {{/if}}
30
+ </div>
31
+ {{#if (has-block "body")}}
32
+ {{yield to="body"}}
33
+ {{/if}}
34
+ </Card.body>
35
+ <Card.footer class={{@footerClass}}>
36
+ <div class="flex flex-row items-center space-x-1">
37
+ <Button @icon="eye" @size="xs" @onClick={{fn this.equipmentActions.transition.view @resource}} />
38
+ <Button @icon="pencil" @size="xs" @onClick={{fn this.equipmentActions.transition.edit @resource}} />
39
+ <Button @icon="trash" @size="xs" @type="danger" @onClick={{fn this.equipmentActions.delete @resource}} />
40
+ </div>
41
+ {{#if (has-block "footer")}}
42
+ {{yield to="footer"}}
43
+ {{/if}}
44
+ <div class="mt-1">
45
+ <div class="text-gray-300 dark:text-gray-500 text-xs">Last Modified: {{@resource.updatedAt}}</div>
46
+ </div>
47
+ </Card.footer>
48
+ </Layout::Resource::Card>
49
+ {{yield}}
@@ -0,0 +1,6 @@
1
+ import Component from '@glimmer/component';
2
+ import { inject as service } from '@ember/service';
3
+
4
+ export default class EquipmentCardComponent extends Component {
5
+ @service equipmentActions;
6
+ }
@@ -1,63 +1,75 @@
1
- <div class="details-wrapper" ...attributes>
2
- <ContentPanel @title="Details" @open={{true}} @wrapperClass="bordered-top">
3
- <div class="grid grid-cols-1 lg:grid-cols-3 gap-2">
1
+ <div class="next-content-panel-wrapper" ...attributes>
2
+
3
+ {{! ===== OVERVIEW ===== }}
4
+ <ContentPanel @title="Overview" @open={{true}} @wrapperClass="bordered-top">
5
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-2 text-xs dark:text-gray-100">
6
+
7
+ {{! — Photo — }}
8
+ {{#if (or @resource.photo_url (config "defaultValues.placeholderImage"))}}
9
+ <div class="col-span-2 flex items-start gap-3 pb-2">
10
+ <div class="border border-dashed border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-600 rounded-lg flex items-center justify-center w-16 h-16 flex-shrink-0">
11
+ <Image
12
+ alt={{@resource.name}}
13
+ src={{or @resource.photo_url (config "defaultValues.placeholderImage")}}
14
+ @fallbackSrc={{config "defaultValues.placeholderImage"}}
15
+ class="w-12 h-12 object-contain"
16
+ />
17
+ </div>
18
+ <div>
19
+ <p class="font-semibold text-sm text-gray-800 dark:text-gray-100">{{n-a @resource.name}}</p>
20
+ <p class="text-[11px] text-gray-400 dark:text-gray-500 font-mono">{{n-a @resource.public_id}}</p>
21
+ </div>
22
+ </div>
23
+ {{/if}}
24
+
25
+ {{! — Identity & Classification — }}
26
+ <div class="col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-1">
27
+ Identity & Classification
28
+ </div>
4
29
  <div class="field-info-container">
5
- <div class="field-name">Name</div>
6
- <div class="field-value">{{n-a @resource.name}}</div>
30
+ <div class="field-name">ID</div>
31
+ <ClickToCopy @value={{@resource.public_id}} class="field-value font-mono">{{n-a @resource.public_id}}</ClickToCopy>
7
32
  </div>
8
-
9
33
  <div class="field-info-container">
10
34
  <div class="field-name">Code</div>
11
- <div class="field-value">{{n-a @resource.code}}</div>
35
+ <ClickToCopy @value={{@resource.code}} class="field-value font-mono">{{n-a @resource.code}}</ClickToCopy>
36
+ </div>
37
+ <div class="field-info-container">
38
+ <div class="field-name">Name</div>
39
+ <div class="field-value">{{n-a @resource.name}}</div>
12
40
  </div>
13
-
14
41
  <div class="field-info-container">
15
42
  <div class="field-name">Type</div>
16
- <div class="field-value">{{smart-humanize @resource.type}}</div>
43
+ <div class="field-value">{{n-a (smart-humanize @resource.type)}}</div>
17
44
  </div>
18
-
19
45
  <div class="field-info-container">
20
46
  <div class="field-name">Status</div>
21
47
  <div class="field-value">
22
48
  <Badge @status={{@resource.status}}>{{smart-humanize @resource.status}}</Badge>
23
49
  </div>
24
50
  </div>
25
-
26
51
  <div class="field-info-container">
27
52
  <div class="field-name">Serial Number</div>
28
- <div class="field-value">{{n-a @resource.serial_number}}</div>
53
+ <div class="field-value font-mono">{{n-a @resource.serial_number}}</div>
29
54
  </div>
30
55
 
56
+ {{! — Make & Model — }}
57
+ <div class="col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-3">
58
+ Make & Model
59
+ </div>
31
60
  <div class="field-info-container">
32
61
  <div class="field-name">Manufacturer</div>
33
62
  <div class="field-value">{{n-a @resource.manufacturer}}</div>
34
63
  </div>
35
-
36
64
  <div class="field-info-container">
37
65
  <div class="field-name">Model</div>
38
66
  <div class="field-value">{{n-a @resource.model}}</div>
39
67
  </div>
40
68
 
41
- <div class="field-info-container">
42
- <div class="field-name">Purchased At</div>
43
- <div class="field-value">{{format-date @resource.purchased_at}}</div>
44
- </div>
45
-
46
- <div class="field-info-container">
47
- <div class="field-name">Purchase Price</div>
48
- <div class="field-value">{{format-currency @resource.purchase_price}}</div>
69
+ {{! — Assignment — }}
70
+ <div class="col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-3">
71
+ Assignment
49
72
  </div>
50
-
51
- <div class="field-info-container">
52
- <div class="field-name">Warranty</div>
53
- <div class="field-value">{{n-a @resource.warranty_name}}</div>
54
- </div>
55
-
56
- <div class="field-info-container">
57
- <div class="field-name">Equipped To</div>
58
- <div class="field-value">{{n-a @resource.equipped_to_name}}</div>
59
- </div>
60
-
61
73
  <div class="field-info-container">
62
74
  <div class="field-name">Equipped Status</div>
63
75
  <div class="field-value">
@@ -68,28 +80,55 @@
68
80
  {{/if}}
69
81
  </div>
70
82
  </div>
83
+ {{#if @resource.is_equipped}}
84
+ <div class="field-info-container">
85
+ <div class="field-name">Equipped To</div>
86
+ <div class="field-value">{{n-a @resource.equipped_to_name}}</div>
87
+ </div>
88
+ {{/if}}
89
+ </div>
90
+ </ContentPanel>
71
91
 
92
+ {{! ===== FINANCIALS ===== }}
93
+ <ContentPanel @title="Financials" @open={{true}} @wrapperClass="bordered-top">
94
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-2 text-xs dark:text-gray-100">
95
+ <div class="field-info-container">
96
+ <div class="field-name">Purchase Price</div>
97
+ <div class="field-value">{{format-currency @resource.purchase_price @resource.currency}}</div>
98
+ </div>
99
+ <div class="field-info-container">
100
+ <div class="field-name">Purchased At</div>
101
+ <div class="field-value">{{n-a (format-date-fns @resource.purchased_at "dd MMM yyyy")}}</div>
102
+ </div>
72
103
  <div class="field-info-container">
73
104
  <div class="field-name">Age</div>
74
105
  <div class="field-value">{{n-a @resource.age_in_days}} days</div>
75
106
  </div>
76
-
77
107
  <div class="field-info-container">
78
108
  <div class="field-name">Depreciated Value</div>
79
- <div class="field-value">{{format-currency @resource.depreciated_value}}</div>
109
+ <div class="field-value">{{format-currency @resource.depreciated_value @resource.currency}}</div>
110
+ </div>
111
+ <div class="field-info-container">
112
+ <div class="field-name">Currency</div>
113
+ <div class="field-value">{{n-a @resource.currency}}</div>
80
114
  </div>
81
115
  </div>
82
116
  </ContentPanel>
83
117
 
84
- <CustomField::Yield @subject={{@resource}} @viewMode={{true}} @wrapperClass="bordered-top" />
85
-
86
- <ContentPanel @title="Photo" @open={{true}} @wrapperClass="bordered-top">
87
- <div class="flex px-1">
88
- <div class="border border-dashed border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-600 rounded-lg flex items-center justify-center">
89
- <div class="flex items-center justify-center h-20 w-24">
90
- <Image alt={{@resource.name}} src={{or @resource.photo_url (config "defaultValues.equipmentImage")}} @fallbackSrc={{config "defaultValues.equipmentImage"}} class="w-12 h-12" />
118
+ {{! ===== WARRANTY ===== }}
119
+ {{#if @resource.warranty_name}}
120
+ <ContentPanel @title="Warranty" @open={{true}} @wrapperClass="bordered-top">
121
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-2 text-xs dark:text-gray-100">
122
+ <div class="field-info-container">
123
+ <div class="field-name">Warranty</div>
124
+ <div class="field-value">{{n-a @resource.warranty_name}}</div>
91
125
  </div>
92
126
  </div>
93
- </div>
94
- </ContentPanel>
95
- </div>
127
+ </ContentPanel>
128
+ {{/if}}
129
+
130
+ {{! ===== CUSTOM FIELDS ===== }}
131
+ <CustomField::Yield @subject={{@resource}} @viewMode={{true}} @wrapperClass="bordered-top" />
132
+
133
+ <Spacer @height="200px" />
134
+ </div>
@@ -1,10 +1,20 @@
1
- <div class="form-wrapper" ...attributes>
2
- <ContentPanel @title="Equipment Details" @open={{true}} @wrapperClass="bordered-top">
1
+ <div class="equipment-form" ...attributes>
2
+
3
+ {{! ===== IDENTIFICATION (includes photo, matching vehicle/form convention) ===== }}
4
+ <ContentPanel @title="Identification" @open={{true}} @wrapperClass="bordered-top">
5
+ {{! Photo row — identical structure to vehicle/form UploadButton pattern }}
3
6
  <div class="flex flex-row space-x-3 mb-3">
4
7
  <div class="flex items-center justify-start rounded-full">
5
- <Image src={{@resource.photo_url}} @fallbackSrc={{config "defaultValues.equipmentImage"}} alt={{@resource.name}} height="48" width="48" class="h-14 w-14 rounded-full shadow-sm" />
8
+ <Image
9
+ src={{@resource.photo_url}}
10
+ @fallbackSrc={{config "defaultValues.placeholderImage"}}
11
+ alt={{@resource.name}}
12
+ height="48"
13
+ width="48"
14
+ class="h-14 w-14 rounded-full shadow-sm"
15
+ />
6
16
  <Attach::Tooltip @class="clean" @animation="scale" @placement="top">
7
- <InputInfo @text="Upload new photo" />
17
+ <InputInfo @text="Upload a new photo" />
8
18
  </Attach::Tooltip>
9
19
  </div>
10
20
  <div>
@@ -14,23 +24,40 @@
14
24
  @onFileAdded={{perform this.handlePhotoUpload}}
15
25
  @icon="upload"
16
26
  @size="xs"
17
- @buttonText={{t "common.upload-image"}}
27
+ @buttonText="Upload Image"
18
28
  @disabled={{cannot-write @resource}}
19
29
  />
20
30
  <div class="px-1">
21
- <span class="text-gray-300 dark:text-gray-600 text-xs">{{t "common.upload-image-supported"}}</span>
31
+ <span class="text-gray-300 dark:text-gray-600 text-xs">Supports JPG, PNG, GIF up to 10 MB</span>
22
32
  </div>
23
33
  </div>
24
34
  </div>
35
+
25
36
  <div class="grid grid-cols-1 gap-2 lg:grid-cols-2 lg:gap-2 no-input-group-padding text-xs">
26
- <InputGroup @name="Name" @wrapperClass="col-span-2">
27
- <Input @value={{@resource.name}} @type="text" class="w-full form-input" placeholder="Name" disabled={{cannot-write @resource}} />
37
+ {{! Equipment Identification }}
38
+ <div class="col-span-1 lg:col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-1">
39
+ Equipment Details
40
+ </div>
41
+ <InputGroup @name="Name">
42
+ <Input @value={{@resource.name}} @type="text" class="w-full form-input" placeholder="Equipment name" disabled={{cannot-write @resource}} />
28
43
  </InputGroup>
29
-
30
44
  <InputGroup @name="Code">
31
- <Input @value={{@resource.code}} @type="text" class="w-full form-input" placeholder="Equipment Code" disabled={{cannot-write @resource}} />
45
+ <Input @value={{@resource.code}} @type="text" class="w-full form-input" placeholder="Internal code or reference" disabled={{cannot-write @resource}} />
46
+ </InputGroup>
47
+ <InputGroup @name="Serial Number">
48
+ <Input @value={{@resource.serial_number}} @type="text" class="w-full form-input" placeholder="Manufacturer serial number" disabled={{cannot-write @resource}} />
49
+ </InputGroup>
50
+ <InputGroup @name="Manufacturer">
51
+ <Input @value={{@resource.manufacturer}} @type="text" class="w-full form-input" placeholder="Manufacturer name" disabled={{cannot-write @resource}} />
52
+ </InputGroup>
53
+ <InputGroup @name="Model">
54
+ <Input @value={{@resource.model}} @type="text" class="w-full form-input" placeholder="Model designation" disabled={{cannot-write @resource}} />
32
55
  </InputGroup>
33
56
 
57
+ {{! Type & Status }}
58
+ <div class="col-span-1 lg:col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-3">
59
+ Type & Status
60
+ </div>
34
61
  <InputGroup @name="Type">
35
62
  <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
36
63
  <PowerSelect
@@ -46,7 +73,6 @@
46
73
  </PowerSelect>
47
74
  </div>
48
75
  </InputGroup>
49
-
50
76
  <InputGroup @name="Status">
51
77
  <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
52
78
  <PowerSelect
@@ -62,45 +88,85 @@
62
88
  </PowerSelect>
63
89
  </div>
64
90
  </InputGroup>
91
+ </div>
92
+ </ContentPanel>
65
93
 
66
- <InputGroup @name="Serial Number">
67
- <Input @value={{@resource.serial_number}} @type="text" class="w-full form-input" placeholder="Serial Number" disabled={{cannot-write @resource}} />
68
- </InputGroup>
69
-
70
- <InputGroup @name="Manufacturer">
71
- <Input @value={{@resource.manufacturer}} @type="text" class="w-full form-input" placeholder="Manufacturer" disabled={{cannot-write @resource}} />
72
- </InputGroup>
73
-
74
- <InputGroup @name="Model">
75
- <Input @value={{@resource.model}} @type="text" class="w-full form-input" placeholder="Model" disabled={{cannot-write @resource}} />
94
+ {{! ===== ASSIGNMENT ===== }}
95
+ <ContentPanel @title="Assignment" @open={{true}} @wrapperClass="bordered-top">
96
+ <div class="grid grid-cols-1 gap-2 lg:grid-cols-2 lg:gap-2 no-input-group-padding text-xs">
97
+ <div class="col-span-1 lg:col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-1">
98
+ Equipped To
99
+ </div>
100
+ <InputGroup @name="Asset Type">
101
+ <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
102
+ <PowerSelect
103
+ @options={{this.equipableTypeOptions}}
104
+ @selected={{this.selectedEquipableType}}
105
+ @onChange={{this.onEquipableTypeChange}}
106
+ @placeholder="Select asset type"
107
+ @triggerClass="form-select form-input"
108
+ @disabled={{cannot-write @resource}}
109
+ as |option|
110
+ >
111
+ {{option.label}}
112
+ </PowerSelect>
113
+ </div>
76
114
  </InputGroup>
115
+ {{#if this.equipableModelName}}
116
+ <InputGroup @name="Asset">
117
+ <ModelSelect
118
+ @modelName={{this.equipableModelName}}
119
+ @selectedModel={{@resource.equipable}}
120
+ @placeholder="Select asset to equip on"
121
+ @triggerClass="form-select form-input"
122
+ @infiniteScroll={{false}}
123
+ @renderInPlace={{true}}
124
+ @onChange={{this.assignEquipable}}
125
+ @disabled={{cannot-write @resource}}
126
+ as |model|
127
+ >
128
+ {{or model.name model.public_id}}
129
+ </ModelSelect>
130
+ </InputGroup>
131
+ {{/if}}
132
+ </div>
133
+ </ContentPanel>
77
134
 
78
- <InputGroup @name="Purchased At">
79
- <DatePicker
80
- @value={{@resource.purchased_at}}
81
- @onChange={{fn (mut @resource.purchased_at)}}
82
- @placeholder="Select Purchase Date"
83
- @disabled={{cannot-write @resource}}
84
- class="w-full form-input"
85
- />
135
+ {{! ===== PURCHASE & WARRANTY ===== }}
136
+ <ContentPanel @title="Purchase & Warranty" @open={{true}} @wrapperClass="bordered-top">
137
+ <div class="grid grid-cols-1 gap-2 lg:grid-cols-2 lg:gap-2 no-input-group-padding text-xs">
138
+ <div class="col-span-1 lg:col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-1">
139
+ Purchase Information
140
+ </div>
141
+ <InputGroup @name="Currency">
142
+ <CurrencySelect @currency={{@resource.currency}} @onCurrencyChange={{fn (mut @resource.currency)}} @triggerClass="w-full form-select" @disabled={{cannot-write @resource}} />
86
143
  </InputGroup>
87
-
88
144
  <InputGroup @name="Purchase Price">
89
145
  <MoneyInput
90
146
  class="w-full"
91
147
  @value={{@resource.purchase_price}}
92
148
  @currency={{or @resource.currency "USD"}}
93
- @canSelectCurrency={{true}}
94
- @onCurrencyChange={{fn (mut @resource.currency)}}
149
+ @onChange={{fn (mut @resource.purchase_price)}}
95
150
  @disabled={{cannot-write @resource}}
96
151
  />
97
152
  </InputGroup>
153
+ <InputGroup @name="Purchase Date">
154
+ <DatePicker
155
+ @value={{or (format-date-fns @resource.purchased_at "dd-MM-yyyy") null}}
156
+ @onChange={{fn (mut @resource.purchased_at)}}
157
+ @placeholder="DD-MM-YYYY"
158
+ class="filter-date-input w-full flex-1"
159
+ />
160
+ </InputGroup>
98
161
 
99
- <InputGroup @name="Warranty">
162
+ <div class="col-span-1 lg:col-span-2 text-[11px] uppercase tracking-wide text-gray-500 font-semibold mt-3">
163
+ Warranty
164
+ </div>
165
+ <InputGroup @name="Warranty" @wrapperClass="col-span-2">
100
166
  <ModelSelect
101
167
  @modelName="warranty"
102
168
  @selectedModel={{@resource.warranty}}
103
- @placeholder="Select Warranty"
169
+ @placeholder="Select Warranty (optional)"
104
170
  @triggerClass="form-select form-input"
105
171
  @infiniteScroll={{false}}
106
172
  @renderInPlace={{true}}
@@ -109,19 +175,23 @@
109
175
  @disabled={{cannot-write @resource}}
110
176
  as |model|
111
177
  >
112
- {{model.policy_number}} - {{model.provider}}
178
+ {{model.policy_number}}
179
+ -
180
+ {{model.provider}}
113
181
  </ModelSelect>
114
182
  </InputGroup>
115
-
116
- <RegistryYield @registry="fleet-ops:component:equipment:form:details" as |RegistryComponent|>
117
- <RegistryComponent @resource={{@resource}} @controller={{@controller}} @permission={{get-write-permission @resource}} />
118
- </RegistryYield>
119
183
  </div>
120
184
  </ContentPanel>
121
185
 
186
+ {{! ===== REGISTRY & METADATA ===== }}
187
+ <RegistryYield @registry="fleet-ops:component:equipment:form:details" as |RegistryComponent|>
188
+ <RegistryComponent @resource={{@resource}} @controller={{@controller}} @permission={{get-write-permission @resource}} />
189
+ </RegistryYield>
122
190
  <CustomField::Yield @subject={{@resource}} @wrapperClass="bordered-top" />
123
-
124
191
  <RegistryYield @registry="fleet-ops:component:equipment:form" as |RegistryComponent|>
125
192
  <RegistryComponent @resource={{@resource}} @controller={{@controller}} @permission={{get-write-permission @resource}} />
126
193
  </RegistryYield>
127
- </div>
194
+ <ContentPanel @title="Metadata" @open={{false}} @wrapperClass="bordered-top">
195
+ <MetadataEditor @value={{@resource.meta}} @onChange={{fn (mut @resource.meta)}} />
196
+ </ContentPanel>
197
+ </div>
@@ -1,27 +1,95 @@
1
1
  import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { inject as service } from '@ember/service';
4
+ import { action } from '@ember/object';
2
5
  import { task } from 'ember-concurrency';
3
6
 
7
+ /**
8
+ * Maps a user-facing polymorphic type string to the Ember Data model name
9
+ * used by ModelSelect.
10
+ */
11
+ const TYPE_TO_MODEL = {
12
+ 'fleet-ops:vehicle': 'vehicle',
13
+ 'fleet-ops:driver': 'driver',
14
+ 'fleet-ops:equipment': 'equipment',
15
+ };
16
+
4
17
  export default class EquipmentFormComponent extends Component {
18
+ @service fetch;
19
+ @service currentUser;
20
+ @service notifications;
21
+
22
+ /** Equipment type options. */
23
+ equipmentTypeOptions = ['ppe', 'refrigeration_unit', 'tool', 'liftgate', 'ramp', 'container', 'pallet_jack', 'forklift', 'safety_equipment', 'communication_device', 'other'];
24
+
25
+ /** Status options for equipment. */
26
+ statusOptions = ['available', 'in_use', 'maintenance', 'retired', 'lost', 'damaged'];
27
+
5
28
  /**
6
- * Equipment type options
29
+ * Polymorphic equipable type options — the asset this equipment is attached to.
30
+ * Each entry has a `value` (stored on the model) and a `label` (displayed in the UI).
7
31
  */
8
- equipmentTypeOptions = ['ppe', 'refrigeration_unit', 'tool', 'liftgate', 'ramp', 'container', 'pallet_jack', 'forklift', 'safety_equipment', 'communication_device', 'other'];
32
+ equipableTypeOptions = [
33
+ { value: 'fleet-ops:vehicle', label: 'Vehicle' },
34
+ { value: 'fleet-ops:driver', label: 'Driver' },
35
+ ];
36
+
37
+ /** Derived Ember Data model name for the currently selected equipable type. */
38
+ @tracked equipableModelName = null;
39
+
40
+ /** The currently selected equipable type option object (drives the PowerSelect trigger label). */
41
+ @tracked selectedEquipableType = null;
42
+
43
+ constructor(owner, args) {
44
+ super(owner, args);
45
+ const { resource } = args;
46
+ if (resource?.equipable_type) {
47
+ this.equipableModelName = TYPE_TO_MODEL[resource.equipable_type] ?? null;
48
+ this.selectedEquipableType = this.equipableTypeOptions.find((o) => o.value === resource.equipable_type) ?? null;
49
+ }
50
+ }
9
51
 
10
52
  /**
11
- * Status options for equipment
53
+ * Handles a change to the equipable type selector. Resets the equipable
54
+ * relationship so a stale association is not persisted.
12
55
  */
13
- statusOptions = ['available', 'in_use', 'maintenance', 'retired', 'lost', 'damaged'];
56
+ @action onEquipableTypeChange(option) {
57
+ this.selectedEquipableType = option;
58
+ this.args.resource.equipable_type = option.value;
59
+ this.args.resource.equipable_uuid = null;
60
+ this.args.resource.equipable = null;
61
+ this.equipableModelName = TYPE_TO_MODEL[option.value] ?? null;
62
+ }
63
+
64
+ /** Assigns the selected equipable model to the resource. */
65
+ @action assignEquipable(model) {
66
+ this.args.resource.equipable = model;
67
+ this.args.resource.equipable_uuid = model?.id ?? null;
68
+ }
14
69
 
15
70
  /**
16
- * Task to handle photo upload
71
+ * Handles photo upload using the Fleetbase fetch service upload pattern.
17
72
  */
18
73
  @task *handlePhotoUpload(file) {
19
74
  try {
20
- const response = yield file.upload(this.args.resource);
21
- this.args.resource.photo_uuid = response.uuid;
22
- this.args.resource.photo_url = response.url;
23
- } catch (error) {
24
- console.error('Photo upload failed:', error);
75
+ yield this.fetch.uploadFile.perform(
76
+ file,
77
+ {
78
+ path: `uploads/${this.currentUser.companyId}/equipment/${this.args.resource.id}`,
79
+ subject_uuid: this.args.resource.id,
80
+ subject_type: 'fleet-ops:equipment',
81
+ type: 'equipment_photo',
82
+ },
83
+ (uploadedFile) => {
84
+ this.args.resource.setProperties({
85
+ photo_uuid: uploadedFile.id,
86
+ photo_url: uploadedFile.url,
87
+ photo: uploadedFile,
88
+ });
89
+ }
90
+ );
91
+ } catch (err) {
92
+ this.notifications.error('Unable to upload photo: ' + err.message);
25
93
  }
26
94
  }
27
95
  }
@@ -0,0 +1,36 @@
1
+ <div class="px-4 py-2">
2
+ <div class="flex flex-1 flex-row items-start justify-between">
3
+ <div class="flex flex-row space-x-3">
4
+ <div class="flex items-start justify-start rounded-full">
5
+ <Image
6
+ src={{@resource.photo_url}}
7
+ @fallbackSrc={{config "defaultValues.placeholderImage"}}
8
+ alt={{@resource.name}}
9
+ height="48"
10
+ width="48"
11
+ class="h-14 w-14 rounded-full shadow-sm"
12
+ />
13
+ </div>
14
+ <div class="flex flex-col">
15
+ <div class="flex flex-row items-center space-x-2">
16
+ <h1 class="text-gray-900 dark:text-white font-semibold text-lg">{{@resource.name}}</h1>
17
+ <Badge @status={{@resource.status}}>{{smart-humanize @resource.status}}</Badge>
18
+ </div>
19
+ <div class="text-xs text-gray-400 dark:text-gray-500">{{smart-humanize @resource.type}}</div>
20
+ <div class="text-xs text-gray-400 dark:text-gray-500">{{n-a @resource.serial_number}}</div>
21
+ </div>
22
+ </div>
23
+ <div class="next-view-header-right">
24
+ <Layout::Resource::Panel::HeaderActions
25
+ @resource={{@resource}}
26
+ @saveTask={{@saveTask}}
27
+ @saveOptions={{@saveOptions}}
28
+ @saveDisabled={{@saveDisabled}}
29
+ @pojoResource={{@pojoResource}}
30
+ @authSchema={{@authSchema}}
31
+ @actionButtons={{@actionButtons}}
32
+ @onPressCancel={{@onPressCancel}}
33
+ />
34
+ </div>
35
+ </div>
36
+ </div>
@@ -0,0 +1,2 @@
1
+ import Component from '@glimmer/component';
2
+ export default class EquipmentPanelHeaderComponent extends Component {}
@@ -46,7 +46,9 @@
46
46
  </div>
47
47
  {{/if}}
48
48
  {{#each this.drivers as |driver|}}
49
- <div class="flex flex-row items-center justify-between text-xs py-2 px-3 text-gray-800 border-b border-gray-200 dark:border-gray-700 dark:text-gray-100 bg-gray-50 dark:bg-gray-800">
49
+ <div
50
+ class="flex flex-row items-center justify-between text-xs py-2 px-3 text-gray-800 border-b border-gray-200 dark:border-gray-700 dark:text-gray-100 bg-gray-50 dark:bg-gray-800"
51
+ >
50
52
  <div class="flex flex-row items-center">
51
53
  {{#if this.selectable}}
52
54
  <div class="flex items-center mr-2">
@@ -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.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.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">
@@ -51,7 +46,9 @@
51
46
  </div>
52
47
  {{/if}}
53
48
  {{#each this.vehicles as |vehicle|}}
54
- <div class="flex flex-row items-center justify-between text-xs py-2 px-3 text-gray-800 border-b border-gray-200 dark:border-gray-700 dark:text-gray-100 bg-gray-50 dark:bg-gray-800">
49
+ <div
50
+ class="flex flex-row items-center justify-between text-xs py-2 px-3 text-gray-800 border-b border-gray-200 dark:border-gray-700 dark:text-gray-100 bg-gray-50 dark:bg-gray-800"
51
+ >
55
52
  <div class="flex flex-row items-center">
56
53
  {{#if this.selectable}}
57
54
  <div class="flex items-center mr-2">