@fleetbase/fleetops-engine 0.6.64 → 0.6.65

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 (284) hide show
  1. package/RELEASE.md +7 -5
  2. package/addon/components/attachment-list.hbs +65 -0
  3. package/addon/components/attachment-list.js +8 -0
  4. package/addon/components/cell/attached-vehicle.hbs +1 -1
  5. package/addon/components/cell/attached-vehicle.js +3 -3
  6. package/addon/components/cell/driver-identity.js +4 -4
  7. package/addon/components/cell/driver-name.hbs +1 -1
  8. package/addon/components/cell/translated-value.hbs +9 -0
  9. package/addon/components/cell/translated-value.js +57 -0
  10. package/addon/components/contact/card.hbs +58 -0
  11. package/addon/components/contact/card.js +19 -0
  12. package/addon/components/contact/form.hbs +1 -1
  13. package/addon/components/contact/panel-header.hbs +1 -1
  14. package/addon/components/customer/form.hbs +2 -2
  15. package/addon/components/device/manager.hbs +26 -28
  16. package/addon/components/device/manager.js +28 -2
  17. package/addon/components/device/panel-tabs/vehicle.hbs +13 -13
  18. package/addon/components/device/panel-tabs/vehicle.js +8 -0
  19. package/addon/components/driver/card.hbs +24 -13
  20. package/addon/components/driver/card.js +20 -6
  21. package/addon/components/driver/form.hbs +3 -3
  22. package/addon/components/driver/panel-header.hbs +2 -2
  23. package/addon/components/driver/pill.hbs +2 -2
  24. package/addon/components/equipment/form.js +11 -4
  25. package/addon/components/equipment/pill.hbs +20 -0
  26. package/addon/components/equipment/pill.js +3 -0
  27. package/addon/components/filter/multi-model.hbs +19 -0
  28. package/addon/components/filter/multi-model.js +90 -0
  29. package/addon/components/fleet/driver-listing.hbs +1 -1
  30. package/addon/components/fleet/vehicle-listing.hbs +1 -1
  31. package/addon/components/fleet-panel/driver-listing.hbs +1 -1
  32. package/addon/components/fleet-panel/vehicle-listing.hbs +1 -1
  33. package/addon/components/layout/fleet-ops-sidebar.js +9 -7
  34. package/addon/components/maintenance/form.js +12 -4
  35. package/addon/components/maintenance-schedule/form.js +13 -7
  36. package/addon/components/map/leaflet-live-map.hbs +52 -0
  37. package/addon/components/map/order-list-overlay/order.hbs +4 -4
  38. package/addon/components/modals/attach-equipment.hbs +22 -0
  39. package/addon/components/modals/attach-equipment.js +3 -0
  40. package/addon/components/modals/attach-telematic-device.hbs +13 -8
  41. package/addon/components/modals/attach-telematic-device.js +11 -1
  42. package/addon/components/modals/attach-trailer.hbs +30 -0
  43. package/addon/components/modals/attach-trailer.js +3 -0
  44. package/addon/components/modals/contact-form.hbs +2 -2
  45. package/addon/components/modals/convert-customer-to-vendor.hbs +1 -1
  46. package/addon/components/modals/driver-form.hbs +2 -2
  47. package/addon/components/modals/select-trailer.hbs +35 -0
  48. package/addon/components/modals/select-trailer.js +3 -0
  49. package/addon/components/modals/vehicle-form.hbs +2 -2
  50. package/addon/components/orchestrator/resource-panel.hbs +2 -2
  51. package/addon/components/order/customer-avatar-stack.hbs +2 -2
  52. package/addon/components/order/details/detail.hbs +4 -4
  53. package/addon/components/order/details/payload.hbs +2 -2
  54. package/addon/components/order/kanban-card.hbs +4 -4
  55. package/addon/components/order-list-overlay/order.hbs +4 -4
  56. package/addon/components/trailer/card.hbs +68 -0
  57. package/addon/components/trailer/card.js +22 -0
  58. package/addon/components/trailer/details/connections.hbs +57 -0
  59. package/addon/components/trailer/details/connections.js +59 -0
  60. package/addon/components/trailer/details/equipment.hbs +25 -0
  61. package/addon/components/trailer/details/equipment.js +52 -0
  62. package/addon/components/trailer/details/maintenance-history.hbs +47 -0
  63. package/addon/components/trailer/details/maintenance-history.js +40 -0
  64. package/addon/components/trailer/details/schedules.hbs +45 -0
  65. package/addon/components/trailer/details/schedules.js +40 -0
  66. package/addon/components/trailer/details/work-orders.hbs +47 -0
  67. package/addon/components/trailer/details/work-orders.js +40 -0
  68. package/addon/components/trailer/details.hbs +399 -0
  69. package/addon/components/trailer/details.js +118 -0
  70. package/addon/components/trailer/form.hbs +447 -0
  71. package/addon/components/trailer/form.js +165 -0
  72. package/addon/components/trailer/panel-header.hbs +45 -0
  73. package/addon/components/trailer/panel-header.js +3 -0
  74. package/addon/components/trailer/pill.hbs +21 -0
  75. package/addon/components/trailer/pill.js +3 -0
  76. package/addon/components/vehicle/card.hbs +42 -11
  77. package/addon/components/vehicle/card.js +31 -0
  78. package/addon/components/vehicle/details/equipment.hbs +25 -0
  79. package/addon/components/vehicle/details/equipment.js +55 -0
  80. package/addon/components/vehicle/details/trailers.hbs +35 -0
  81. package/addon/components/vehicle/details/trailers.js +55 -0
  82. package/addon/components/vehicle/form.hbs +2 -2
  83. package/addon/components/vehicle/panel-header.hbs +2 -2
  84. package/addon/components/vehicle/pill.hbs +2 -2
  85. package/addon/components/vendor/card.hbs +58 -0
  86. package/addon/components/vendor/card.js +6 -0
  87. package/addon/components/vendor/panel-header.hbs +2 -2
  88. package/addon/components/vendor/personnel-manager.hbs +2 -2
  89. package/addon/components/work-order/form.js +12 -4
  90. package/addon/controllers/connectivity/devices/index/details/vehicle.js +11 -0
  91. package/addon/controllers/connectivity/devices/index/details.js +3 -3
  92. package/addon/controllers/connectivity/devices/index.js +1 -0
  93. package/addon/controllers/connectivity/telematics/details/attachments.js +28 -47
  94. package/addon/controllers/connectivity/telematics/details/devices.js +1 -0
  95. package/addon/controllers/management/contacts/customers.js +4 -0
  96. package/addon/controllers/management/contacts/index.js +32 -0
  97. package/addon/controllers/management/trailers/index/details.js +119 -0
  98. package/addon/controllers/management/trailers/index/edit.js +67 -0
  99. package/addon/controllers/management/trailers/index/new.js +43 -0
  100. package/addon/controllers/management/trailers/index.js +599 -0
  101. package/addon/controllers/management/vehicles/index/details.js +16 -0
  102. package/addon/controllers/management/vehicles/index.js +78 -4
  103. package/addon/controllers/management/vendors/index.js +32 -0
  104. package/addon/controllers/operations/orders/index.js +3 -1
  105. package/addon/controllers/settings/custom-fields.js +8 -0
  106. package/addon/extension.js +7 -0
  107. package/addon/helpers/placeholder-image.js +6 -0
  108. package/addon/helpers/resource-image.js +10 -0
  109. package/addon/helpers/trailer-placeholder-image.js +6 -0
  110. package/addon/routes/management/trailers/index/details/connections.js +2 -0
  111. package/addon/routes/management/trailers/index/details/devices.js +2 -0
  112. package/addon/routes/management/trailers/index/details/equipment.js +2 -0
  113. package/addon/routes/management/trailers/index/details/index.js +2 -0
  114. package/addon/routes/management/trailers/index/details/maintenance-history.js +2 -0
  115. package/addon/routes/management/trailers/index/details/positions.js +2 -0
  116. package/addon/routes/management/trailers/index/details/schedules.js +2 -0
  117. package/addon/routes/management/trailers/index/details/work-orders.js +2 -0
  118. package/addon/routes/management/trailers/index/details.js +30 -0
  119. package/addon/routes/management/trailers/index/edit.js +23 -0
  120. package/addon/routes/management/trailers/index/new.js +14 -0
  121. package/addon/routes/management/trailers/index.js +42 -0
  122. package/addon/routes/management/vehicles/index/details/trailers.js +2 -0
  123. package/addon/routes/management/vehicles/index.js +7 -1
  124. package/addon/routes.js +17 -0
  125. package/addon/services/device-actions.js +26 -16
  126. package/addon/services/trailer-actions.js +394 -0
  127. package/addon/services/vehicle-actions.js +111 -0
  128. package/addon/styles/fleetops-engine.css +349 -0
  129. package/addon/templates/connectivity/devices/index/details/vehicle.hbs +19 -19
  130. package/addon/templates/connectivity/telematics/details/attachments.hbs +15 -15
  131. package/addon/templates/management/contacts/customers.hbs +48 -29
  132. package/addon/templates/management/contacts/index.hbs +52 -33
  133. package/addon/templates/management/trailers/index/details/connections.hbs +1 -0
  134. package/addon/templates/management/trailers/index/details/devices.hbs +1 -0
  135. package/addon/templates/management/trailers/index/details/equipment.hbs +1 -0
  136. package/addon/templates/management/trailers/index/details/index.hbs +1 -0
  137. package/addon/templates/management/trailers/index/details/maintenance-history.hbs +1 -0
  138. package/addon/templates/management/trailers/index/details/positions.hbs +1 -0
  139. package/addon/templates/management/trailers/index/details/schedules.hbs +1 -0
  140. package/addon/templates/management/trailers/index/details/work-orders.hbs +1 -0
  141. package/addon/templates/management/trailers/index/details.hbs +15 -0
  142. package/addon/templates/management/trailers/index/edit.hbs +12 -0
  143. package/addon/templates/management/trailers/index/new.hbs +11 -0
  144. package/addon/templates/management/trailers/index.hbs +67 -0
  145. package/addon/templates/management/vehicles/index/details/equipment.hbs +1 -1
  146. package/addon/templates/management/vehicles/index/details/trailers.hbs +1 -0
  147. package/addon/templates/management/vehicles/index/details.hbs +1 -1
  148. package/addon/templates/management/vendors/index.hbs +66 -32
  149. package/addon/utils/device-table-columns.js +10 -6
  150. package/addon/utils/live-map-card-content.js +56 -0
  151. package/addon/utils/placeholder-images.js +71 -0
  152. package/addon/utils/trailer-placeholder-image.js +11 -0
  153. package/app/components/attachment-list.js +1 -0
  154. package/app/components/cell/translated-value.js +1 -0
  155. package/app/components/contact/card.js +1 -0
  156. package/app/components/equipment/pill.js +1 -0
  157. package/app/components/filter/multi-model.js +1 -0
  158. package/app/components/modals/attach-equipment.js +1 -0
  159. package/app/components/modals/attach-trailer.js +1 -0
  160. package/app/components/modals/select-trailer.js +1 -0
  161. package/app/components/trailer/card.js +1 -0
  162. package/app/components/trailer/details/connections.js +1 -0
  163. package/app/components/trailer/details/equipment.js +1 -0
  164. package/app/components/trailer/details/maintenance-history.js +1 -0
  165. package/app/components/trailer/details/schedules.js +1 -0
  166. package/app/components/trailer/details/work-orders.js +1 -0
  167. package/app/components/trailer/details.js +1 -0
  168. package/app/components/trailer/form.js +1 -0
  169. package/app/components/trailer/panel-header.js +1 -0
  170. package/app/components/trailer/pill.js +1 -0
  171. package/app/components/vehicle/details/equipment.js +1 -0
  172. package/app/components/vehicle/details/trailers.js +1 -0
  173. package/app/components/vendor/card.js +1 -0
  174. package/app/controllers/management/trailers/index/details.js +1 -0
  175. package/app/controllers/management/trailers/index/edit.js +1 -0
  176. package/app/controllers/management/trailers/index/new.js +1 -0
  177. package/app/controllers/management/trailers/index.js +1 -0
  178. package/app/helpers/placeholder-image.js +1 -0
  179. package/app/helpers/resource-image.js +1 -0
  180. package/app/helpers/trailer-placeholder-image.js +1 -0
  181. package/app/routes/management/trailers/index/details/connections.js +1 -0
  182. package/app/routes/management/trailers/index/details/devices.js +1 -0
  183. package/app/routes/management/trailers/index/details/equipment.js +1 -0
  184. package/app/routes/management/trailers/index/details/index.js +1 -0
  185. package/app/routes/management/trailers/index/details/maintenance-history.js +1 -0
  186. package/app/routes/management/trailers/index/details/positions.js +1 -0
  187. package/app/routes/management/trailers/index/details/schedules.js +1 -0
  188. package/app/routes/management/trailers/index/details/work-orders.js +1 -0
  189. package/app/routes/management/trailers/index/details.js +1 -0
  190. package/app/routes/management/trailers/index/edit.js +1 -0
  191. package/app/routes/management/trailers/index/new.js +1 -0
  192. package/app/routes/management/trailers/index.js +1 -0
  193. package/app/routes/management/vehicles/index/details/trailers.js +1 -0
  194. package/app/services/trailer-actions.js +1 -0
  195. package/app/templates/management/trailers/index/details/connections.js +1 -0
  196. package/app/templates/management/trailers/index/details/devices.js +1 -0
  197. package/app/templates/management/trailers/index/details/equipment.js +1 -0
  198. package/app/templates/management/trailers/index/details/index.js +1 -0
  199. package/app/templates/management/trailers/index/details/maintenance-history.js +1 -0
  200. package/app/templates/management/trailers/index/details/positions.js +1 -0
  201. package/app/templates/management/trailers/index/details/schedules.js +1 -0
  202. package/app/templates/management/trailers/index/details/work-orders.js +1 -0
  203. package/app/templates/management/trailers/index/details.js +1 -0
  204. package/app/templates/management/trailers/index/edit.js +1 -0
  205. package/app/templates/management/trailers/index/new.js +1 -0
  206. package/app/templates/management/trailers/index.js +1 -0
  207. package/app/templates/management/vehicles/index/details/trailers.js +1 -0
  208. package/composer.json +1 -1
  209. package/extension.json +1 -1
  210. package/package.json +1 -1
  211. package/server/migrations/2026_09_03_000001_add_first_class_trailer_columns_to_assets_table.php +58 -0
  212. package/server/migrations/2026_09_03_000002_create_asset_connections_table.php +43 -0
  213. package/server/migrations/2026_09_03_000003_create_device_installations_table.php +33 -0
  214. package/server/src/Auth/Schemas/FleetOps.php +7 -0
  215. package/server/src/Events/TrailerLocationChanged.php +70 -0
  216. package/server/src/Exceptions/DeviceAlreadyAttachedException.php +18 -0
  217. package/server/src/Exports/TrailerExport.php +31 -0
  218. package/server/src/Http/Controllers/Api/v1/Concerns/ResolvesFleetOpsApiResources.php +8 -2
  219. package/server/src/Http/Controllers/Api/v1/DeviceController.php +22 -15
  220. package/server/src/Http/Controllers/Api/v1/EquipmentController.php +47 -0
  221. package/server/src/Http/Controllers/Api/v1/TrailerController.php +362 -0
  222. package/server/src/Http/Controllers/Api/v1/VehicleController.php +1 -0
  223. package/server/src/Http/Controllers/Internal/v1/DeviceController.php +3 -0
  224. package/server/src/Http/Controllers/Internal/v1/LiveController.php +1 -1
  225. package/server/src/Http/Controllers/Internal/v1/SearchController.php +16 -0
  226. package/server/src/Http/Controllers/Internal/v1/TrailerController.php +360 -0
  227. package/server/src/Http/Controllers/Internal/v1/VehicleController.php +74 -0
  228. package/server/src/Http/Filter/EquipmentFilter.php +51 -0
  229. package/server/src/Http/Filter/OrderFilter.php +15 -0
  230. package/server/src/Http/Filter/TrailerFilter.php +269 -0
  231. package/server/src/Http/Filter/VehicleFilter.php +70 -3
  232. package/server/src/Http/Requests/CreateDeviceRequest.php +1 -1
  233. package/server/src/Http/Requests/CreateEquipmentRequest.php +1 -1
  234. package/server/src/Http/Requests/CreateTrailerRequest.php +54 -0
  235. package/server/src/Http/Requests/UpdateTrailerRequest.php +7 -0
  236. package/server/src/Http/Resources/v1/AssetConnection.php +90 -0
  237. package/server/src/Http/Resources/v1/Device.php +3 -1
  238. package/server/src/Http/Resources/v1/Equipment.php +3 -1
  239. package/server/src/Http/Resources/v1/Index/Vehicle.php +10 -0
  240. package/server/src/Http/Resources/v1/Trailer.php +199 -0
  241. package/server/src/Http/Resources/v1/Vehicle.php +2 -0
  242. package/server/src/Http/Resources/v1/VehicleWithoutDriver.php +3 -0
  243. package/server/src/Imports/TrailerImport.php +33 -0
  244. package/server/src/Models/Asset.php +39 -10
  245. package/server/src/Models/AssetConnection.php +43 -0
  246. package/server/src/Models/Device.php +39 -8
  247. package/server/src/Models/DeviceInstallation.php +27 -0
  248. package/server/src/Models/Equipment.php +2 -0
  249. package/server/src/Models/Trailer.php +181 -0
  250. package/server/src/Models/Vehicle.php +13 -0
  251. package/server/src/Providers/FleetOpsServiceProvider.php +4 -2
  252. package/server/src/Support/Ai/FleetOpsAiQueryResources.php +22 -0
  253. package/server/src/Support/Reporting/FleetOpsReportSchema.php +38 -0
  254. package/server/src/Support/Telematics/TelematicService.php +35 -0
  255. package/server/src/Support/TrailerSummary.php +35 -0
  256. package/server/src/routes.php +29 -0
  257. package/server/tests/CompactResourceSerializationTest.php +36 -0
  258. package/server/tests/ControllerFilterContractsTest.php +29 -0
  259. package/server/tests/Feature/Http/Api/DeviceControllerContractsTest.php +30 -13
  260. package/server/tests/Feature/Http/Api/TrailerControllerLifecycleTest.php +640 -0
  261. package/server/tests/Feature/Http/Internal/DeviceControllerContractsTest.php +20 -5
  262. package/server/tests/Feature/Http/Internal/LiveControllerEndpointsTest.php +19 -0
  263. package/server/tests/ModelAccessorContractsTest.php +10 -0
  264. package/server/tests/OrderFilterExecutionTest.php +5 -0
  265. package/server/tests/PublicApiControllerInputContractsTest.php +11 -0
  266. package/server/tests/PublicFleetOpsRestEndpointsTest.php +15 -3
  267. package/server/tests/ReportSchemaContractsTest.php +4 -0
  268. package/server/tests/TelematicsHardeningTest.php +4 -2
  269. package/server/tests/TrailerSupportCoverageTest.php +524 -0
  270. package/server/tests/Unit/Http/Resources/IndexVehicleResourceTest.php +35 -0
  271. package/server/tests/Unit/Models/EquipmentTest.php +22 -0
  272. package/server/tests/Unit/Models/ModelLookupFallbackSeamsTest.php +7 -3
  273. package/server/tests/Unit/Models/TrailerTest.php +204 -0
  274. package/server/tests/Unit/Support/Telematics/TelematicServiceTelemetryTest.php +93 -0
  275. package/server/tests/VehicleControllerHelperContractsTest.php +14 -0
  276. package/translations/ar-ae.yml +396 -0
  277. package/translations/bg-bg.yaml +424 -0
  278. package/translations/en-us.yaml +423 -0
  279. package/translations/es-pa.yaml +425 -0
  280. package/translations/fr-fr.yaml +424 -0
  281. package/translations/mn-mn.yaml +424 -0
  282. package/translations/pt-br.yaml +424 -0
  283. package/translations/ru-ru.yaml +424 -0
  284. package/translations/vi-vn.yaml +435 -0
@@ -0,0 +1,22 @@
1
+ <Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
2
+ <div class="modal-body-container">
3
+ <InputGroup @name={{t "common.select-field" field=(t "resource.equipment")}} @helpText={{t "trailer.help.select-equipment"}}>
4
+ <ModelSelect
5
+ @modelName="equipment"
6
+ @selectedModel={{@options.selectedEquipment}}
7
+ @query={{hash attachment_state="detached"}}
8
+ @placeholder={{t "trailer.placeholders.equipment"}}
9
+ @triggerClass="form-select form-input"
10
+ @infiniteScroll={{true}}
11
+ @renderInPlace={{true}}
12
+ @onChange={{fn (mut @options.selectedEquipment)}}
13
+ as |equipment|
14
+ >
15
+ <div class="flex flex-col">
16
+ <span class="text-sm font-semibold">{{or equipment.name equipment.public_id}}</span>
17
+ <span class="text-xs text-gray-500 dark:text-gray-400">{{n-a (or equipment.type equipment.serial_number equipment.code)}}</span>
18
+ </div>
19
+ </ModelSelect>
20
+ </InputGroup>
21
+ </div>
22
+ </Modal::Default>
@@ -0,0 +1,3 @@
1
+ import Component from '@glimmer/component';
2
+
3
+ export default class ModalsAttachEquipmentComponent extends Component {}
@@ -1,25 +1,30 @@
1
1
  <Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
2
2
  <div class="modal-body-container">
3
3
  <div class="mb-4 rounded-md border border-gray-200 bg-gray-50 p-3 dark:border-gray-700 dark:bg-gray-800">
4
- <div class="text-xs font-semibold uppercase text-gray-500 dark:text-gray-400">Synced device</div>
4
+ <div class="text-xs font-semibold uppercase text-gray-500 dark:text-gray-400">{{t "device.attachment.synced-device"}}</div>
5
5
  <div class="mt-2">
6
6
  <Device::Pill @device={{@options.device}} />
7
7
  </div>
8
8
  </div>
9
9
 
10
- <InputGroup @name={{t "common.select-field" field=(t "resource.vehicle")}} @value={{@options.selectedVehicle}}>
10
+ <InputGroup @name={{t "device.attachment.asset-type"}}>
11
+ <PowerSelect @options={{this.assetTypeOptions}} @selected={{@options.selectedAssetType}} @placeholder={{t "device.attachment.select-asset-type"}} @triggerClass="form-select form-input" @onChange={{fn (mut @options.selectedAssetType)}} as |option|>{{option.label}}</PowerSelect>
12
+ </InputGroup>
13
+ {{#if @options.selectedAssetType}}
14
+ <InputGroup @name={{t "common.select-field" field=@options.selectedAssetType.label}} @value={{@options.selectedAsset}}>
11
15
  <ModelSelect
12
- @modelName="vehicle"
13
- @selectedModel={{@options.selectedVehicle}}
14
- @placeholder={{t "common.select-field" field=(t "resource.vehicle")}}
16
+ @modelName={{@options.selectedAssetType.model}}
17
+ @selectedModel={{@options.selectedAsset}}
18
+ @placeholder={{t "common.select-field" field=@options.selectedAssetType.label}}
15
19
  @triggerClass="form-select form-input"
16
20
  @infiniteScroll={{true}}
17
21
  @renderInPlace={{true}}
18
- @onChange={{fn (mut @options.selectedVehicle)}}
19
- as |vehicle|
22
+ @onChange={{fn (mut @options.selectedAsset)}}
23
+ as |asset|
20
24
  >
21
- <Vehicle::Pill @resource={{vehicle}} />
25
+ {{or asset.displayName asset.display_name asset.name asset.public_id}}
22
26
  </ModelSelect>
23
27
  </InputGroup>
28
+ {{/if}}
24
29
  </div>
25
30
  </Modal::Default>
@@ -1,3 +1,13 @@
1
1
  import Component from '@glimmer/component';
2
+ import { inject as service } from '@ember/service';
2
3
 
3
- export default class ModalsAttachTelematicDeviceComponent extends Component {}
4
+ export default class ModalsAttachTelematicDeviceComponent extends Component {
5
+ @service intl;
6
+
7
+ get assetTypeOptions() {
8
+ return [
9
+ { value: 'fleet-ops:vehicle', model: 'vehicle', label: this.intl.t('resource.vehicle') },
10
+ { value: 'fleet-ops:trailer', model: 'trailer', label: this.intl.t('resource.trailer') },
11
+ ];
12
+ }
13
+ }
@@ -0,0 +1,30 @@
1
+ <Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
2
+ <div class="modal-body-container">
3
+ {{#if @options.trailer}}
4
+ <div class="mb-4 rounded-md border border-gray-200 bg-gray-50 p-3 dark:border-gray-700 dark:bg-gray-800">
5
+ <div class="text-xs font-semibold uppercase text-gray-500 dark:text-gray-400">{{t "resource.trailer"}}</div>
6
+ <div class="mt-1 text-sm font-semibold text-gray-900 dark:text-gray-100">{{@options.trailer.displayName}}</div>
7
+ <div class="text-xs text-gray-500 dark:text-gray-400">{{n-a (or @options.trailer.plate_number @options.trailer.code @options.trailer.public_id)}}</div>
8
+ </div>
9
+ {{/if}}
10
+
11
+ <InputGroup @name={{t "trailer.fields.vehicle"}} @helpText={{t "trailer.help.attach-vehicle"}}>
12
+ <ModelSelect
13
+ @modelName="vehicle"
14
+ @selectedModel={{@options.selectedVehicle}}
15
+ @placeholder={{t "trailer.placeholders.vehicle"}}
16
+ @triggerClass="form-select form-input"
17
+ @infiniteScroll={{true}}
18
+ @renderInPlace={{true}}
19
+ @onChange={{fn (mut @options.selectedVehicle)}}
20
+ as |vehicle|
21
+ >
22
+ <Vehicle::Pill @vehicle={{vehicle}} />
23
+ </ModelSelect>
24
+ </InputGroup>
25
+
26
+ <InputGroup @name={{t "trailer.fields.position"}} @helpText={{t "trailer.help.position"}}>
27
+ <Input @type="number" @value={{@options.position}} min="1" step="1" class="w-full form-input" placeholder={{t "trailer.placeholders.position"}} />
28
+ </InputGroup>
29
+ </div>
30
+ </Modal::Default>
@@ -0,0 +1,3 @@
1
+ import Component from '@glimmer/component';
2
+
3
+ export default class ModalsAttachTrailerComponent extends Component {}
@@ -3,8 +3,8 @@
3
3
  <div class="flex">
4
4
  <div class="w-32 mb-6 mr-6">
5
5
  <Image
6
- src={{@options.contact.photo_url}}
7
- @fallbackSrc={{config "defaultValues.contactImage"}}
6
+ src={{resource-image @options.contact.photo_url "contact"}}
7
+ @fallbackSrc={{(placeholder-image "contact")}}
8
8
  alt={{@options.contact.name}}
9
9
  height="128"
10
10
  width="128"
@@ -11,7 +11,7 @@
11
11
 
12
12
  <InputGroup @name="Primary Personnel" @wrapperClass="mb-0i">
13
13
  <div class="flex items-start rounded-md border border-gray-200 bg-gray-50 px-3 py-2 dark:border-gray-700 dark:bg-gray-800">
14
- <Image src={{this.customer.photo_url}} @fallbackSrc={{config "defaultValues.contactImage"}} alt={{this.customer.name}} class="mr-2 h-6 w-6 rounded-md" />
14
+ <Image src={{resource-image this.customer.photo_url "customer"}} @fallbackSrc={{(placeholder-image "customer")}} alt={{this.customer.name}} class="mr-2 h-6 w-6 rounded-md" />
15
15
  <div class="min-w-0">
16
16
  <div class="leading-4 text-sm truncate font-semibold dark:text-gray-100">{{this.customer.name}}</div>
17
17
  <div class="truncate text-sm text-gray-500 dark:text-gray-400">{{n-a this.customer.email}}</div>
@@ -3,8 +3,8 @@
3
3
  <div class="flex">
4
4
  <div class="w-32 mb-6 mr-6">
5
5
  <Image
6
- src={{@options.driver.photo_url}}
7
- @fallbackSrc={{config "defaultValues.driverImage"}}
6
+ src={{resource-image @options.driver.photo_url "driver"}}
7
+ @fallbackSrc={{(placeholder-image "driver")}}
8
8
  alt={{@options.driver.name}}
9
9
  height="128"
10
10
  width="128"
@@ -0,0 +1,35 @@
1
+ <Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
2
+ <div class="modal-body-container">
3
+ {{#if @options.vehicle}}
4
+ <div class="mb-4 rounded-md border border-gray-200 bg-gray-50 p-3 dark:border-gray-700 dark:bg-gray-800">
5
+ <div class="text-xs font-semibold uppercase text-gray-500 dark:text-gray-400">{{t "resource.vehicle"}}</div>
6
+ <div class="mt-2">
7
+ <Vehicle::Pill @vehicle={{@options.vehicle}} />
8
+ </div>
9
+ </div>
10
+ {{/if}}
11
+
12
+ <InputGroup @name={{t "resource.trailer"}} @helpText={{t "trailer.help.select-trailer"}}>
13
+ <ModelSelect
14
+ @modelName="trailer"
15
+ @selectedModel={{@options.selectedTrailer}}
16
+ @query={{hash attachment_state="detached"}}
17
+ @placeholder={{t "trailer.placeholders.trailer"}}
18
+ @triggerClass="form-select form-input"
19
+ @infiniteScroll={{true}}
20
+ @renderInPlace={{true}}
21
+ @onChange={{fn (mut @options.selectedTrailer)}}
22
+ as |trailer|
23
+ >
24
+ <div class="flex flex-col">
25
+ <span class="text-sm font-semibold">{{trailer.displayName}}</span>
26
+ <span class="text-xs text-gray-500 dark:text-gray-400">{{n-a (or trailer.plate_number trailer.code trailer.public_id)}}</span>
27
+ </div>
28
+ </ModelSelect>
29
+ </InputGroup>
30
+
31
+ <InputGroup @name={{t "trailer.fields.position"}} @helpText={{t "trailer.help.position"}}>
32
+ <Input @type="number" @value={{@options.position}} min="1" step="1" class="w-full form-input" placeholder={{t "trailer.placeholders.position"}} />
33
+ </InputGroup>
34
+ </div>
35
+ </Modal::Default>
@@ -0,0 +1,3 @@
1
+ import Component from '@glimmer/component';
2
+
3
+ export default class ModalsSelectTrailerComponent extends Component {}
@@ -3,8 +3,8 @@
3
3
  <div class="flex">
4
4
  <div class="w-32 mb-6 mr-6">
5
5
  <Image
6
- src={{@options.vehicle.photo_url}}
7
- @fallbackSrc={{config "defaultValues.vehicleImage"}}
6
+ src={{resource-image @options.vehicle.photo_url "vehicle"}}
7
+ @fallbackSrc={{(placeholder-image "vehicle")}}
8
8
  alt={{@options.vehicle.name}}
9
9
  height="128"
10
10
  width="128"
@@ -232,10 +232,10 @@
232
232
  {{! Avatar — online dot top-right }}
233
233
  <div class="relative flex-shrink-0 mt-0.5">
234
234
  <img
235
- src={{driver.photo_url}}
235
+ src={{resource-image driver.photo_url "driver"}}
236
236
  alt={{or driver.name "Driver"}}
237
237
  class="w-9 h-9 rounded-full object-cover bg-gray-200 dark:bg-gray-700"
238
- {{fallback-img-src (config "defaultValues.driverImage")}}
238
+ {{fallback-img-src (placeholder-image "driver")}}
239
239
  />
240
240
  <span
241
241
  class="absolute top-0 left-0 block w-2.5 h-2.5 rounded-full border-2 border-white dark:border-gray-900 {{if driver.online 'bg-green-500' 'bg-gray-400'}}"
@@ -2,8 +2,8 @@
2
2
  {{#each this.entries as |entry idx|}}
3
3
  <div class="relative group {{if (gt idx 0) this.overlapClass}}" {{set-z-index (add 1 idx)}}>
4
4
  <Image
5
- src={{avatar-url entry.photoUrl}}
6
- @fallbackSrc={{config "defaultValues.contactImage"}}
5
+ src={{resource-image entry.photoUrl "customer"}}
6
+ @fallbackSrc={{(placeholder-image "customer")}}
7
7
  alt={{or entry.name "Unknown customer"}}
8
8
  class="{{this.sizeClass}} rounded-full ring-2 ring-gray-800 dark:ring-gray-700 shadow transition-shadow hover:shadow-md focus:shadow-md"
9
9
  role="img"
@@ -44,8 +44,8 @@
44
44
  <div class="flex flex-row space-x-2">
45
45
  <div>
46
46
  <Image
47
- src={{avatar-url @resource.customer.photo_url}}
48
- @fallbackSrc={{config "defaultValues.contactImage"}}
47
+ src={{resource-image @resource.customer.photo_url "customer"}}
48
+ @fallbackSrc={{(placeholder-image "customer")}}
49
49
  width="28"
50
50
  height="28"
51
51
  class="w-7 h-7 rounded-full ring-2 ring-gray-800 dark:ring-gray-700 shadow transition-shadow hover:shadow-md focus:shadow-md"
@@ -66,8 +66,8 @@
66
66
  <div class="flex flex-row space-x-2">
67
67
  <div>
68
68
  <Image
69
- src={{avatar-url @resource.facilitator.photo_url}}
70
- @fallbackSrc={{config "defaultValues.contactImage"}}
69
+ src={{resource-image @resource.facilitator.photo_url "vendor"}}
70
+ @fallbackSrc={{(placeholder-image "vendor")}}
71
71
  width="28"
72
72
  height="28"
73
73
  class="w-7 h-7 rounded-full ring-2 ring-gray-800 dark:ring-gray-700 shadow transition-shadow hover:shadow-md focus:shadow-md"
@@ -10,8 +10,8 @@
10
10
  <div class="flex flex-row">
11
11
  <div>
12
12
  <Image
13
- src={{avatar-url group.waypoint.customer.photo_url}}
14
- @fallbackSrc={{config "defaultValues.contactImage"}}
13
+ src={{resource-image group.waypoint.customer.photo_url "customer"}}
14
+ @fallbackSrc={{(placeholder-image "customer")}}
15
15
  class="w-8 h-8 rounded-md shadow-sm mr-2"
16
16
  alt={{group.waypoint.customer.name}}
17
17
  />
@@ -64,8 +64,8 @@
64
64
  <div class="flex flex-row space-x-2">
65
65
  <div class="resource-assigned-photo relative">
66
66
  <Image
67
- src={{avatar-url @card.customer.photo_url}}
68
- @fallbackSrc={{config "defaultValues.contactImage"}}
67
+ src={{resource-image @card.customer.photo_url "customer"}}
68
+ @fallbackSrc={{(placeholder-image "customer")}}
69
69
  class="w-5 h-5 rounded-lg shadow-sm"
70
70
  alt={{@card.customer.name}}
71
71
  />
@@ -81,8 +81,8 @@
81
81
  <div class="flex flex-row space-x-2">
82
82
  <div class="resource-assigned-photo relative">
83
83
  <Image
84
- src={{avatar-url @card.driver_assigned.photo_url}}
85
- @fallbackSrc={{config "defaultValues.driverImage"}}
84
+ src={{resource-image @card.driver_assigned.photo_url "driver"}}
85
+ @fallbackSrc={{(placeholder-image "driver")}}
86
86
  class="w-5 h-5 rounded-lg shadow-sm"
87
87
  alt={{@card.driver_assigned.name}}
88
88
  />
@@ -74,8 +74,8 @@
74
74
  <div class="resource-assigned">
75
75
  <div class="resource-assigned-photo">
76
76
  <Image
77
- src={{avatar-url @order.customer.photo_url}}
78
- @fallbackSrc={{config "defaultValues.contactImage"}}
77
+ src={{resource-image @order.customer.photo_url "customer"}}
78
+ @fallbackSrc={{(placeholder-image "customer")}}
79
79
  width="32"
80
80
  height="32"
81
81
  class="w-8 h-8 rounded-lg shadow-sm mr-2"
@@ -93,8 +93,8 @@
93
93
  <div class="resource-assigned">
94
94
  <div class="resource-assigned-photo">
95
95
  <Image
96
- src={{avatar-url @order.driver_assigned.photo_url}}
97
- @fallbackSrc={{config "defaultValues.driverImage"}}
96
+ src={{resource-image @order.driver_assigned.photo_url "driver"}}
97
+ @fallbackSrc={{(placeholder-image "driver")}}
98
98
  width="32"
99
99
  height="32"
100
100
  class="w-8 h-8 rounded-lg shadow-sm mr-2"
@@ -0,0 +1,68 @@
1
+ <Layout::Resource::Card class="fleetops-resource-card trailer-card" ...attributes as |Card|>
2
+ <Card.header class={{@headerClass}}>
3
+ <RegistryYield @registry="fleet-ops:component:trailer:card:header:start" as |RegistryComponent|>
4
+ <RegistryComponent @resource={{@resource}} />
5
+ </RegistryYield>
6
+ <div class="fleetops-card-title">
7
+ <div class="font-semibold flex-1 truncate">{{@resource.displayName}}</div>
8
+ <FaIcon @icon="circle" @size="xs" class="fleetops-card-title-indicator {{if @resource.online 'text-green-500' 'text-gray-400'}}" />
9
+ </div>
10
+ <div class="text-gray-400 dark:text-gray-500 text-sm w-full truncate">
11
+ {{#if @resource.type}}{{t (concat "trailer.types." @resource.type)}}{{/if}}
12
+ {{#if (or @resource.plate_number @resource.vin @resource.code)}}<span class="mx-1">·</span>{{or @resource.plate_number @resource.vin @resource.code}}{{/if}}
13
+ </div>
14
+ {{#if (has-block "header")}}
15
+ {{yield to="header"}}
16
+ {{/if}}
17
+ <RegistryYield @registry="fleet-ops:component:trailer:card:header:end" as |RegistryComponent|>
18
+ <RegistryComponent @resource={{@resource}} />
19
+ </RegistryYield>
20
+ </Card.header>
21
+ <Card.body class="{{@bodyClass}} h-40 overflow-hidden">
22
+ <Image src={{resource-image @resource.photo_url "trailer"}} @fallbackSrc={{(placeholder-image "trailer")}} alt={{@resource.displayName}} class="fleetops-card-image fleetops-card-image--cover" />
23
+ {{#if (has-block "body")}}
24
+ {{yield to="body"}}
25
+ {{/if}}
26
+ </Card.body>
27
+ <Card.footer class={{@footerClass}}>
28
+ <RegistryYield @registry="fleet-ops:component:trailer:card:footer:start" as |RegistryComponent|>
29
+ <RegistryComponent @resource={{@resource}} />
30
+ </RegistryYield>
31
+ <div class="flex flex-row items-center justify-between w-full">
32
+ <Badge @status={{or @resource.status "available"}} @size="xs" @text={{t (concat "trailer.statuses." (or @resource.status "available"))}} />
33
+ <div class="flex flex-row items-center space-x-1">
34
+ <Button @icon="eye" @size="xs" @helpText={{t "common.view"}} @onClick={{fn this.trailerActions.transition.view @resource}} @permission="fleet-ops view trailer" />
35
+ <Button @icon="pencil" @size="xs" @helpText={{t "common.edit"}} @onClick={{fn this.trailerActions.transition.edit @resource}} @permission="fleet-ops update trailer" />
36
+ <Button @icon="trash" @size="xs" @type="danger" @helpText={{t "common.delete"}} @onClick={{fn this.trailerActions.delete @resource}} @permission="fleet-ops delete trailer" />
37
+ </div>
38
+ </div>
39
+ <RegistryYield @registry="fleet-ops:component:trailer:card:footer:end" as |RegistryComponent|>
40
+ <RegistryComponent @resource={{@resource}} />
41
+ </RegistryYield>
42
+ {{#if (has-block "footer")}}
43
+ {{yield to="footer"}}
44
+ {{/if}}
45
+ <div class="fleetops-card-meta-list">
46
+ <div class="fleetops-card-meta">
47
+ <FaIcon @icon="car" @prefix="fas" />
48
+ <span class="fleetops-card-meta-value">
49
+ {{#if @resource.isAttached}}
50
+ {{#if @resource.current_vehicle}}
51
+ <a href="javascript:;" class="fleetops-card-meta-link" {{on "click" this.viewVehicle}}>{{n-a @resource.current_vehicle_name}}</a>
52
+ {{else}}
53
+ {{n-a @resource.current_vehicle_name}}
54
+ {{/if}}
55
+ {{else}}
56
+ {{t "trailer.attachment.no-vehicle"}}
57
+ {{/if}}
58
+ </span>
59
+ </div>
60
+ <div class="fleetops-card-meta">
61
+ <FaIcon @icon="satellite-dish" @prefix="fas" />
62
+ <span class="fleetops-card-meta-value">{{t (concat "trailer.connectivity." (or @resource.connectivity_status "never_connected"))}}</span>
63
+ </div>
64
+ <div class="fleetops-card-meta">{{t "common.last-modified"}}: {{@resource.updatedAt}}</div>
65
+ </div>
66
+ </Card.footer>
67
+ </Layout::Resource::Card>
68
+ {{yield}}
@@ -0,0 +1,22 @@
1
+ import Component from '@glimmer/component';
2
+ import { inject as service } from '@ember/service';
3
+ import { action } from '@ember/object';
4
+
5
+ export default class TrailerCardComponent extends Component {
6
+ @service trailerActions;
7
+ @service vehicleActions;
8
+
9
+ @action viewVehicle() {
10
+ const vehicle = this.args.resource?.current_vehicle;
11
+
12
+ if (!vehicle) {
13
+ return;
14
+ }
15
+
16
+ if (this.vehicleActions.panel?.view) {
17
+ return this.vehicleActions.panel.view(vehicle);
18
+ }
19
+
20
+ return this.vehicleActions.transition.view(vehicle);
21
+ }
22
+ }
@@ -0,0 +1,57 @@
1
+ <AttachmentList
2
+ @title={{t "trailer.tabs.connections"}}
3
+ @description={{t "trailer.connections.description"}}
4
+ @actionText={{if this.isAttached (t "trailer.actions.detach-vehicle") (t "trailer.actions.attach-vehicle")}}
5
+ @actionIcon={{if this.isAttached "unlink" "link"}}
6
+ @actionType={{if this.isAttached "danger" "primary"}}
7
+ @onAction={{if this.isAttached this.detachVehicle this.attachVehicle}}
8
+ @actionPermission={{if this.isAttached "fleet-ops detach-vehicle-for trailer" "fleet-ops attach-vehicle-for trailer"}}
9
+ @hideEmptyAction={{this.isAttached}}
10
+ @isLoading={{this.loadConnections.isRunning}}
11
+ @items={{this.connections}}
12
+ @emptyIcon="link-slash"
13
+ @emptyTitle={{t "trailer.empty.connections"}}
14
+ @emptyDescription={{t "trailer.empty.connections-description"}}
15
+ class="trailer-details-connections"
16
+ >
17
+ <:item as |connection|>
18
+ <div class="fleetops-towing-entry {{if connection.isActive 'fleetops-towing-entry--active'}}">
19
+ <div class="fleetops-towing-entry-vehicle">
20
+ {{#if connection.vehicle}}
21
+ <Vehicle::Pill @vehicle={{connection.vehicle}} @onClick={{fn this.viewVehicle connection.vehicle}} @noTooltip={{true}} />
22
+ {{else}}
23
+ <span class="text-sm text-gray-400 dark:text-gray-500">{{t "trailer.attachment.no-vehicle"}}</span>
24
+ {{/if}}
25
+ </div>
26
+ <div class="fleetops-towing-entry-timeline">
27
+ <div class="fleetops-towing-entry-timeline-row">
28
+ <FaIcon @icon="link" class="text-green-500 dark:text-green-400" />
29
+ <span>{{t "trailer.connections.connected" date=(format-date-fns connection.connected_at "dd MMM yyyy, HH:mm")}}</span>
30
+ </div>
31
+ <div class="fleetops-towing-entry-timeline-row">
32
+ {{#if connection.disconnected_at}}
33
+ <FaIcon @icon="link-slash" class="text-gray-400 dark:text-gray-500" />
34
+ <span>{{t "trailer.connections.disconnected" date=(format-date-fns connection.disconnected_at "dd MMM yyyy, HH:mm")}}</span>
35
+ {{else}}
36
+ <FaIcon @icon="circle" @size="2xs" class="text-green-500 dark:text-green-400 animate-pulse" />
37
+ <span class="text-green-600 dark:text-green-400 font-medium">{{t "trailer.connections.still-attached"}}</span>
38
+ {{/if}}
39
+ </div>
40
+ <div class="fleetops-attachment-list-item-meta">
41
+ {{#if connection.position}}
42
+ <span class="fleetops-attachment-list-item-meta-entry">{{t "trailer.attachment.position" position=connection.position}}</span>
43
+ {{/if}}
44
+ {{#if connection.duration}}
45
+ <span class="fleetops-attachment-list-item-meta-entry"><FaIcon @icon="clock" class="mr-1" />{{connection.duration}}</span>
46
+ {{/if}}
47
+ {{#if connection.source}}
48
+ <span class="fleetops-attachment-list-item-meta-entry">{{smart-humanize connection.source}}</span>
49
+ {{/if}}
50
+ </div>
51
+ </div>
52
+ </div>
53
+ </:item>
54
+ <:itemActions as |connection|>
55
+ <Badge @status={{if connection.isActive "attached" "detached"}} @size="xs" @icon={{if connection.isActive "link" "link-slash"}} @text={{t (concat "trailer.attachment." (if connection.isActive "attached" "detached"))}} />
56
+ </:itemActions>
57
+ </AttachmentList>
@@ -0,0 +1,59 @@
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';
5
+ import { task } from 'ember-concurrency';
6
+
7
+ /**
8
+ * Effective-dated towing history for a trailer, newest first. The active
9
+ * connection is highlighted and can be ended from here.
10
+ */
11
+ export default class TrailerDetailsConnectionsComponent extends Component {
12
+ @service store;
13
+ @service notifications;
14
+ @service trailerActions;
15
+ @service vehicleActions;
16
+ @tracked connections = [];
17
+
18
+ get trailer() {
19
+ return this.args.resource;
20
+ }
21
+
22
+ get isAttached() {
23
+ return this.trailer?.isAttached ?? this.trailer?.attachment_state === 'attached';
24
+ }
25
+
26
+ constructor() {
27
+ super(...arguments);
28
+ this.loadConnections.perform();
29
+ }
30
+
31
+ @task *loadConnections() {
32
+ try {
33
+ const trailer = yield this.store.queryRecord('trailer', { public_id: this.trailer.public_id ?? this.trailer.id, single: true, with: ['connections.vehicle'] });
34
+ this.connections = Array.from(trailer?.connections ?? []);
35
+ } catch (error) {
36
+ this.notifications.serverError(error);
37
+ }
38
+ }
39
+
40
+ @action viewVehicle(vehicle) {
41
+ if (!vehicle) {
42
+ return;
43
+ }
44
+
45
+ if (this.vehicleActions.panel?.view) {
46
+ return this.vehicleActions.panel.view(vehicle);
47
+ }
48
+
49
+ return this.vehicleActions.transition.view(vehicle);
50
+ }
51
+
52
+ @action attachVehicle() {
53
+ return this.trailerActions.attachVehicle(this.trailer, { callback: () => this.loadConnections.perform() });
54
+ }
55
+
56
+ @action detachVehicle() {
57
+ return this.trailerActions.detachVehicle(this.trailer, { callback: () => this.loadConnections.perform() });
58
+ }
59
+ }
@@ -0,0 +1,25 @@
1
+ <AttachmentList
2
+ @title={{t "trailer.tabs.equipment"}}
3
+ @description={{t "trailer.equipment.description"}}
4
+ @actionText={{t "trailer.actions.attach-equipment"}}
5
+ @actionIcon="plus"
6
+ @onAction={{this.attach}}
7
+ @actionPermission="fleet-ops attach-equipment-for trailer"
8
+ @isLoading={{this.loadEquipment.isRunning}}
9
+ @items={{this.equipment}}
10
+ @emptyIcon="toolbox"
11
+ @emptyTitle={{t "trailer.empty.equipment"}}
12
+ @emptyDescription={{t "trailer.empty.equipment-description"}}
13
+ class="trailer-details-equipment"
14
+ >
15
+ <:item as |item|>
16
+ <Equipment::Pill @equipment={{item}} @onClick={{fn this.view item}} />
17
+ {{#if item.status}}
18
+ <Badge @status={{item.status}} @size="xs" class="fleetops-attachment-list-item-badge" />
19
+ {{/if}}
20
+ </:item>
21
+ <:itemActions as |item|>
22
+ <Button @icon="eye" @size="xs" @helpText={{t "common.view"}} @onClick={{fn this.view item}} @permission="fleet-ops view equipment" />
23
+ <Button @icon="unlink" @size="xs" @type="danger" @helpText={{t "trailer.actions.detach-equipment"}} @onClick={{fn this.detach item}} @permission="fleet-ops detach-equipment-for trailer" />
24
+ </:itemActions>
25
+ </AttachmentList>
@@ -0,0 +1,52 @@
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';
5
+ import { task } from 'ember-concurrency';
6
+
7
+ export default class TrailerDetailsEquipmentComponent extends Component {
8
+ @service store;
9
+ @service notifications;
10
+ @service equipmentActions;
11
+ @service trailerActions;
12
+ @tracked equipment = [];
13
+
14
+ get trailer() {
15
+ return this.args.resource;
16
+ }
17
+
18
+ constructor() {
19
+ super(...arguments);
20
+ this.loadEquipment.perform();
21
+ }
22
+
23
+ @task *loadEquipment() {
24
+ try {
25
+ const equipment = yield this.store.query('equipment', {
26
+ equipable_type: 'fleet-ops:trailer',
27
+ equipable: this.trailer.public_id ?? this.trailer.id,
28
+ sort: '-created_at',
29
+ });
30
+
31
+ this.equipment = Array.from(equipment ?? []);
32
+ } catch (error) {
33
+ this.notifications.serverError(error);
34
+ }
35
+ }
36
+
37
+ @action attach() {
38
+ return this.trailerActions.attachEquipment(this.trailer, { callback: () => this.loadEquipment.perform() });
39
+ }
40
+
41
+ @action detach(equipment) {
42
+ return this.trailerActions.detachEquipment(this.trailer, equipment, { callback: () => this.loadEquipment.perform() });
43
+ }
44
+
45
+ @action view(equipment) {
46
+ if (this.equipmentActions.panel?.view) {
47
+ return this.equipmentActions.panel.view(equipment);
48
+ }
49
+
50
+ return this.equipmentActions.transition.view(equipment);
51
+ }
52
+ }