@fleetbase/fleetops-engine 0.6.68 → 0.6.70

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 (294) hide show
  1. package/RELEASE.md +44 -9
  2. package/addon/components/admin/telematics-settings.hbs +143 -0
  3. package/addon/components/admin/telematics-settings.js +145 -0
  4. package/addon/components/contact/form.hbs +13 -2
  5. package/addon/components/customer/details.hbs +17 -17
  6. package/addon/components/customer/form.hbs +13 -2
  7. package/addon/components/customer/form.js +0 -56
  8. package/addon/components/driver/details.hbs +13 -19
  9. package/addon/components/driver/form.hbs +20 -35
  10. package/addon/components/driver/form.js +0 -58
  11. package/addon/components/fuel-integration/hub.hbs +1 -1
  12. package/addon/components/layout/fleet-ops-sidebar.js +37 -17
  13. package/addon/components/map/leaflet-live-map.hbs +4 -242
  14. package/addon/components/map/leaflet-live-map.js +2 -1
  15. package/addon/components/map/marker-card/driver.hbs +57 -0
  16. package/addon/components/map/marker-card/driver.js +3 -0
  17. package/addon/components/map/marker-card/vehicle.hbs +78 -0
  18. package/addon/components/map/marker-card/vehicle.js +3 -0
  19. package/addon/components/modals/driver-form.hbs +2 -2
  20. package/addon/components/modals/reset-profile-credentials.hbs +23 -0
  21. package/addon/components/modals/reset-profile-credentials.js +70 -0
  22. package/addon/components/profile-identity-hint.hbs +13 -0
  23. package/addon/components/profile-identity-hint.js +49 -0
  24. package/addon/components/telematic/hub.hbs +1 -1
  25. package/addon/components/widget/live-fleet.hbs +8 -40
  26. package/addon/controllers/analytics/reports/index.js +5 -0
  27. package/addon/controllers/connectivity/devices/index.js +4 -0
  28. package/addon/controllers/connectivity/fuel-providers/index.js +6 -4
  29. package/addon/controllers/connectivity/sensors/index.js +4 -0
  30. package/addon/controllers/connectivity/telematics/index.js +4 -0
  31. package/addon/controllers/maintenance/equipment/index.js +11 -4
  32. package/addon/controllers/maintenance/inspection-forms/index.js +2 -2
  33. package/addon/controllers/maintenance/inspection-submissions/index.js +8 -2
  34. package/addon/controllers/maintenance/maintenances/index.js +4 -0
  35. package/addon/controllers/maintenance/parts/index.js +11 -4
  36. package/addon/controllers/maintenance/schedules/index.js +17 -4
  37. package/addon/controllers/maintenance/work-orders/index.js +11 -4
  38. package/addon/controllers/management/contacts/index.js +4 -0
  39. package/addon/controllers/management/drivers/index.js +8 -0
  40. package/addon/controllers/management/fleets/index.js +4 -0
  41. package/addon/controllers/management/fuel-reports/index.js +4 -0
  42. package/addon/controllers/management/fuel-transactions/index.js +9 -7
  43. package/addon/controllers/management/issues/index.js +4 -0
  44. package/addon/controllers/management/places/index.js +4 -0
  45. package/addon/controllers/management/vehicles/index.js +4 -0
  46. package/addon/controllers/management/vendors/index.js +4 -0
  47. package/addon/controllers/operations/orders/index/details.js +30 -1
  48. package/addon/controllers/operations/orders/index.js +7 -0
  49. package/addon/controllers/operations/service-rates/index.js +3 -0
  50. package/addon/controllers/settings/index.js +13 -0
  51. package/addon/controllers/settings/telematics.js +113 -0
  52. package/addon/extension.js +42 -8
  53. package/addon/routes/analytics/index.js +14 -1
  54. package/addon/routes/analytics/reports/index/details.js +1 -1
  55. package/addon/routes/analytics/reports/index/edit.js +1 -1
  56. package/addon/routes/analytics/reports/index/new.js +14 -1
  57. package/addon/routes/analytics/reports.js +14 -1
  58. package/addon/routes/connectivity/devices/index/new.js +14 -1
  59. package/addon/routes/connectivity/devices.js +14 -1
  60. package/addon/routes/connectivity/events.js +14 -1
  61. package/addon/routes/connectivity/fuel-providers/details/transactions.js +12 -0
  62. package/addon/routes/connectivity/fuel-providers/details.js +9 -0
  63. package/addon/routes/connectivity/fuel-providers/edit.js +9 -0
  64. package/addon/routes/connectivity/fuel-providers/new.js +14 -1
  65. package/addon/routes/connectivity/fuel-providers.js +14 -1
  66. package/addon/routes/connectivity/sensors/index/new.js +14 -1
  67. package/addon/routes/connectivity/sensors.js +14 -1
  68. package/addon/routes/connectivity/telematics/new.js +14 -1
  69. package/addon/routes/connectivity/telematics.js +14 -1
  70. package/addon/routes/connectivity/tracking.js +14 -1
  71. package/addon/routes/maintenance/equipment/index/details.js +2 -2
  72. package/addon/routes/maintenance/equipment/index/edit.js +2 -2
  73. package/addon/routes/maintenance/equipment/index/new.js +14 -1
  74. package/addon/routes/maintenance/equipment.js +14 -1
  75. package/addon/routes/maintenance/index.js +14 -1
  76. package/addon/routes/maintenance/inspection-forms/index/details.js +10 -1
  77. package/addon/routes/maintenance/inspection-forms/index/edit.js +10 -1
  78. package/addon/routes/maintenance/inspection-forms/index/new.js +14 -1
  79. package/addon/routes/maintenance/inspection-forms.js +14 -1
  80. package/addon/routes/maintenance/inspection-submissions/index/details.js +10 -1
  81. package/addon/routes/maintenance/inspection-submissions/index/edit.js +10 -1
  82. package/addon/routes/maintenance/inspection-submissions/index/new.js +14 -1
  83. package/addon/routes/maintenance/inspection-submissions.js +14 -1
  84. package/addon/routes/maintenance/maintenances/index/details.js +2 -2
  85. package/addon/routes/maintenance/maintenances/index/edit.js +2 -2
  86. package/addon/routes/maintenance/maintenances/index/new.js +14 -1
  87. package/addon/routes/maintenance/maintenances.js +14 -1
  88. package/addon/routes/maintenance/parts/index/details.js +2 -2
  89. package/addon/routes/maintenance/parts/index/edit.js +2 -2
  90. package/addon/routes/maintenance/parts/index/new.js +14 -1
  91. package/addon/routes/maintenance/parts.js +14 -1
  92. package/addon/routes/maintenance/schedules/index/details.js +2 -2
  93. package/addon/routes/maintenance/schedules/index/edit.js +2 -2
  94. package/addon/routes/maintenance/schedules/index/new.js +15 -1
  95. package/addon/routes/maintenance/schedules.js +15 -1
  96. package/addon/routes/maintenance/work-orders/index/details.js +2 -2
  97. package/addon/routes/maintenance/work-orders/index/edit.js +2 -2
  98. package/addon/routes/maintenance/work-orders/index/new.js +14 -1
  99. package/addon/routes/maintenance/work-orders.js +14 -1
  100. package/addon/routes/management/fuel-transactions/index/details.js +9 -0
  101. package/addon/routes/management/fuel-transactions.js +14 -1
  102. package/addon/routes/management/index.js +13 -0
  103. package/addon/routes/management/trailers.js +16 -0
  104. package/addon/routes/management/vendors/integrated/details.js +14 -1
  105. package/addon/routes/management/vendors/integrated/edit.js +14 -1
  106. package/addon/routes/management/vendors/integrated/new.js +14 -1
  107. package/addon/routes/management/vendors/integrated.js +14 -1
  108. package/addon/routes/operations/orders.js +40 -1
  109. package/addon/routes/operations/routes/index/details.js +14 -1
  110. package/addon/routes/operations/routes/index/new.js +13 -0
  111. package/addon/routes/operations/routes.js +14 -1
  112. package/addon/routes/operations/service-rates/index/new.js +11 -0
  113. package/addon/routes/operations/service-rates.js +14 -1
  114. package/addon/routes/settings/avatars.js +14 -1
  115. package/addon/routes/settings/custom-fields.js +14 -1
  116. package/addon/routes/settings/map.js +14 -1
  117. package/addon/routes/settings/notifications.js +11 -0
  118. package/addon/routes/settings/orchestrator.js +1 -1
  119. package/addon/routes/settings/payments/index.js +1 -1
  120. package/addon/routes/settings/routing.js +14 -1
  121. package/addon/routes/settings/scheduling.js +14 -1
  122. package/addon/routes/settings/telematics.js +21 -0
  123. package/addon/routes/virtual.js +11 -0
  124. package/addon/routes.js +1 -0
  125. package/addon/services/contact-actions.js +50 -89
  126. package/addon/services/driver-actions.js +98 -0
  127. package/addon/templates/settings/telematics.hbs +70 -0
  128. package/addon/utils/profile-login-actions.js +172 -0
  129. package/app/components/admin/telematics-settings.js +1 -0
  130. package/app/components/{modals/reset-customer-credentials.js → map/marker-card/driver.js} +1 -1
  131. package/app/components/map/marker-card/vehicle.js +1 -0
  132. package/app/components/modals/reset-profile-credentials.js +1 -0
  133. package/app/components/profile-identity-hint.js +1 -0
  134. package/app/controllers/settings/telematics.js +1 -0
  135. package/app/routes/settings/telematics.js +1 -0
  136. package/app/templates/settings/telematics.js +1 -0
  137. package/composer.json +2 -2
  138. package/docs/AFAQY.md +1 -1
  139. package/docs/TELEMATICS_QUEUES.md +1 -1
  140. package/docs/TELEMETRY_ARCHITECTURE.md +72 -1
  141. package/extension.json +1 -1
  142. package/package.json +3 -3
  143. package/scripts/pest-bootstrap.php +12 -0
  144. package/server/config/telemetry.php +12 -0
  145. package/server/migrations/2026_09_21_000001_convert_profile_only_users_to_managed_accounts.php +132 -0
  146. package/server/migrations/2026_09_23_000001_add_retention_index_to_device_events.php +40 -0
  147. package/server/migrations/2026_09_23_000002_add_retention_index_to_positions.php +40 -0
  148. package/server/migrations/2026_09_23_000003_add_retention_index_to_telematic_sync_runs.php +40 -0
  149. package/server/resources/views/mail/driver-credentials.blade.php +21 -0
  150. package/server/src/Auth/Schemas/FleetOps.php +104 -5
  151. package/server/src/Console/Commands/DrainTelematicInbox.php +3 -13
  152. package/server/src/Console/Commands/FixCustomerCompanies.php +1 -1
  153. package/server/src/Console/Commands/FixDriverCompanies.php +1 -1
  154. package/server/src/Console/Commands/ProcessOperationalAlerts.php +3 -3
  155. package/server/src/Console/Commands/PruneTelematicsData.php +318 -0
  156. package/server/src/Exceptions/ProfileIdentityConflictException.php +34 -0
  157. package/server/src/Http/Controllers/Api/v1/CustomerController.php +14 -0
  158. package/server/src/Http/Controllers/Api/v1/DriverController.php +56 -39
  159. package/server/src/Http/Controllers/Api/v1/OrchestrationController.php +9 -0
  160. package/server/src/Http/Controllers/FleetOpsController.php +21 -0
  161. package/server/src/Http/Controllers/Internal/v1/AnalyticsController.php +11 -0
  162. package/server/src/Http/Controllers/Internal/v1/ContactController.php +16 -17
  163. package/server/src/Http/Controllers/Internal/v1/CustomerController.php +54 -4
  164. package/server/src/Http/Controllers/Internal/v1/DeviceController.php +13 -0
  165. package/server/src/Http/Controllers/Internal/v1/DeviceEventController.php +11 -0
  166. package/server/src/Http/Controllers/Internal/v1/DriverController.php +204 -162
  167. package/server/src/Http/Controllers/Internal/v1/FleetOpsLookupController.php +47 -0
  168. package/server/src/Http/Controllers/Internal/v1/FuelProviderConnectionController.php +13 -0
  169. package/server/src/Http/Controllers/Internal/v1/FuelProviderTransactionController.php +15 -0
  170. package/server/src/Http/Controllers/Internal/v1/FuelReportController.php +9 -3
  171. package/server/src/Http/Controllers/Internal/v1/InspectionFormController.php +15 -0
  172. package/server/src/Http/Controllers/Internal/v1/LiveController.php +16 -0
  173. package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +15 -0
  174. package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +15 -0
  175. package/server/src/Http/Controllers/Internal/v1/ManifestController.php +15 -0
  176. package/server/src/Http/Controllers/Internal/v1/MetricsController.php +11 -0
  177. package/server/src/Http/Controllers/Internal/v1/NavigatorController.php +42 -1
  178. package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +12 -0
  179. package/server/src/Http/Controllers/Internal/v1/OrderController.php +178 -27
  180. package/server/src/Http/Controllers/Internal/v1/PaymentController.php +13 -0
  181. package/server/src/Http/Controllers/Internal/v1/RadarController.php +19 -0
  182. package/server/src/Http/Controllers/Internal/v1/ServiceAreaController.php +9 -3
  183. package/server/src/Http/Controllers/Internal/v1/SettingController.php +465 -14
  184. package/server/src/Http/Controllers/Internal/v1/TelematicController.php +21 -0
  185. package/server/src/Http/Controllers/Internal/v1/TrailerController.php +21 -0
  186. package/server/src/Http/Controllers/Internal/v1/VehicleController.php +21 -0
  187. package/server/src/Http/Controllers/Internal/v1/VendorController.php +17 -0
  188. package/server/src/Http/Controllers/Internal/v1/WorkOrderController.php +11 -0
  189. package/server/src/Http/Requests/CreateDriverRequest.php +6 -2
  190. package/server/src/Http/Requests/Internal/CreateDriverRequest.php +9 -18
  191. package/server/src/Http/Resources/v1/Contact.php +3 -0
  192. package/server/src/Http/Resources/v1/Driver.php +3 -0
  193. package/server/src/Jobs/DispatchTelematicsRetentionJobs.php +70 -0
  194. package/server/src/Jobs/PruneTelematicsDataJob.php +74 -0
  195. package/server/src/Listeners/NotifyDriverOnShiftChange.php +3 -3
  196. package/server/src/Mail/CustomerCredentialsMail.php +1 -1
  197. package/server/src/Mail/DriverCredentialsMail.php +55 -0
  198. package/server/src/Models/Contact.php +55 -90
  199. package/server/src/Models/Sensor.php +3 -5
  200. package/server/src/Observers/ContactObserver.php +2 -22
  201. package/server/src/Observers/DriverObserver.php +5 -6
  202. package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +9 -1
  203. package/server/src/Providers/FleetOpsServiceProvider.php +18 -0
  204. package/server/src/Support/Ai/Capabilities/AbstractFleetOpsAICapability.php +62 -5
  205. package/server/src/Support/Ai/Capabilities/AssetStatusCapability.php +24 -5
  206. package/server/src/Support/Ai/Capabilities/CreateOrderPreviewCapability.php +6 -2
  207. package/server/src/Support/Ai/Capabilities/OrderInsightsCapability.php +40 -7
  208. package/server/src/Support/Ai/Capabilities/SearchResourcesCapability.php +86 -24
  209. package/server/src/Support/Ai/FleetOpsAiConsoleCommands.php +122 -0
  210. package/server/src/Support/Ai/Tools/CreateOrderTool.php +86 -0
  211. package/server/src/Support/Ai/Tools/ImportOrdersTool.php +53 -0
  212. package/server/src/Support/Ai/Tools/OptimizeOrderRouteTool.php +70 -0
  213. package/server/src/Support/Ai/Tools/SearchResourcesTool.php +73 -0
  214. package/server/src/Support/Analytics/LiveFleet.php +18 -2
  215. package/server/src/Support/Authorization.php +103 -0
  216. package/server/src/Support/Database/TableIndexes.php +66 -0
  217. package/server/src/Support/OrderTracker.php +2 -2
  218. package/server/src/Support/ProfileAccountManager.php +405 -0
  219. package/server/src/Support/Reporting/FleetOpsReportSchema.php +476 -116
  220. package/server/src/Support/Telematics/Retention/RetentionPolicy.php +300 -0
  221. package/server/src/Support/Telematics/Retention/TelemetryActivity.php +40 -0
  222. package/server/src/Support/Telematics/TelematicService.php +12 -2
  223. package/server/src/Support/Telematics/Telemetry/Ingestor.php +4 -2
  224. package/server/src/Tracking/TrackingIntelligenceService.php +1 -1
  225. package/server/src/Tracking/TrackingOptions.php +2 -2
  226. package/server/src/Traits/AuthorizesMethods.php +37 -0
  227. package/server/src/routes.php +14 -1
  228. package/server/tests/AiOperationalQueryCapabilityTest.php +1 -0
  229. package/server/tests/AnalyticsRoutesTest.php +29 -2
  230. package/server/tests/ApiCustomerControllerContractsTest.php +21 -0
  231. package/server/tests/ApiDriverControllerContractsTest.php +102 -20
  232. package/server/tests/CommandContractsTest.php +4 -4
  233. package/server/tests/ControllerHelperContractsTest.php +3 -11
  234. package/server/tests/Feature/Http/AfaqyRealtimeIngestionTest.php +21 -1
  235. package/server/tests/Feature/Http/Api/DriverControllerAuthFlowsTest.php +36 -0
  236. package/server/tests/Feature/Http/Api/DriverControllerSessionHelpersTest.php +19 -6
  237. package/server/tests/Feature/Http/Api/ProfileOnlyUserConversionMigrationTest.php +201 -0
  238. package/server/tests/Feature/Http/Api/TelematicsRetentionIndexMigrationTest.php +115 -0
  239. package/server/tests/Feature/Http/Internal/ContactControllerEndpointsTest.php +6 -4
  240. package/server/tests/Feature/Http/Internal/CustomerControllerCredentialsTest.php +6 -2
  241. package/server/tests/Feature/Http/Internal/DriverControllerContractsTest.php +23 -0
  242. package/server/tests/Feature/Http/Internal/DriverControllerCreateRecordTest.php +42 -17
  243. package/server/tests/Feature/Http/Internal/DriverControllerExistingUserAdoptionTest.php +168 -277
  244. package/server/tests/Feature/Http/Internal/DriverControllerLoginManagementTest.php +716 -0
  245. package/server/tests/Feature/Http/Internal/OrderControllerContractsTest.php +10 -3
  246. package/server/tests/Feature/Http/Internal/OrderControllerImportFromFilesTest.php +18 -0
  247. package/server/tests/Feature/Http/Internal/OrderControllerTenantScopingTest.php +411 -0
  248. package/server/tests/Feature/Http/Internal/OrderControllerUpstreamNotFoundTest.php +8 -20
  249. package/server/tests/Feature/Http/Internal/SettingControllerHelpersTest.php +71 -0
  250. package/server/tests/Feature/Http/Internal/TelematicsStorageDeadlinesTest.php +119 -0
  251. package/server/tests/InternalCustomerControllerContractsTest.php +32 -1
  252. package/server/tests/InternalImportExportControllerContractsTest.php +5 -8
  253. package/server/tests/ModelAccessorContractsTest.php +82 -20
  254. package/server/tests/NavigatorControllerContractsTest.php +36 -4
  255. package/server/tests/NotificationAndMailContractsTest.php +31 -0
  256. package/server/tests/ObserverContractsTest.php +28 -3
  257. package/server/tests/OrchestratorConsumableApiTest.php +42 -0
  258. package/server/tests/ProviderContractsTest.php +10 -0
  259. package/server/tests/ReportSchemaContractsTest.php +120 -6
  260. package/server/tests/RequestContractsTest.php +19 -11
  261. package/server/tests/SettingControllerContractsTest.php +429 -18
  262. package/server/tests/SmallControllerContractsTest.php +8 -5
  263. package/server/tests/Support/PruneTelematicsDataProbe.php +55 -0
  264. package/server/tests/SupportJobAndAiCoverageTest.php +3 -3
  265. package/server/tests/TelematicControllerContractsTest.php +22 -4
  266. package/server/tests/Unit/Console/Commands/ProcessOperationalAlertsCommandTest.php +2 -2
  267. package/server/tests/Unit/Console/PruneTelematicsDataCommandTest.php +353 -0
  268. package/server/tests/Unit/Jobs/DispatchTelematicsRetentionJobsTest.php +114 -0
  269. package/server/tests/Unit/Jobs/PruneTelematicsDataJobTest.php +60 -0
  270. package/server/tests/Unit/Models/ContactCustomerUserTest.php +90 -14
  271. package/server/tests/Unit/Models/ContactTest.php +48 -5
  272. package/server/tests/Unit/Models/SensorTest.php +2 -2
  273. package/server/tests/Unit/Models/SmallModelSurfacesTest.php +31 -32
  274. package/server/tests/Unit/Observers/ObserverListenerSeamsTest.php +7 -2
  275. package/server/tests/Unit/Orchestration/VroomEngineTransportAndSettingsTest.php +20 -0
  276. package/server/tests/Unit/Providers/FleetOpsAiRegistrationTest.php +15 -2
  277. package/server/tests/Unit/Support/Ai/AssetStatusCapabilityQueriesTest.php +42 -0
  278. package/server/tests/Unit/Support/Ai/Capabilities/OrderInsightsCapabilityTest.php +19 -1
  279. package/server/tests/Unit/Support/Ai/Capabilities/SearchResourcesCapabilityTest.php +85 -1
  280. package/server/tests/Unit/Support/Ai/FleetOpsAiConsoleCommandsTest.php +31 -0
  281. package/server/tests/Unit/Support/Ai/Tools/FleetOpsAiToolsTest.php +246 -0
  282. package/server/tests/Unit/Support/AiCapabilityPermissionsTest.php +3 -3
  283. package/server/tests/Unit/Support/Analytics/LiveFleetTest.php +6 -0
  284. package/server/tests/Unit/Support/AuthorizationTest.php +142 -0
  285. package/server/tests/Unit/Support/MaintainableAndOperationalAlertsTest.php +12 -1
  286. package/server/tests/Unit/Support/MoreQuerySeamsTest.php +11 -2
  287. package/server/tests/Unit/Support/ProfileAccountManagerTest.php +643 -0
  288. package/server/tests/Unit/Support/Telematics/Retention/RetentionPolicyTest.php +190 -0
  289. package/server/tests/Unit/Support/Telematics/Retention/TelemetryActivityTest.php +68 -0
  290. package/server/tests/Unit/Tracking/TrackingOptionsCompanySettingsTest.php +109 -0
  291. package/translations/en-us.yaml +85 -2
  292. package/translations/uk-ua.yaml +3475 -0
  293. package/addon/components/modals/reset-customer-credentials.hbs +0 -18
  294. package/addon/components/modals/reset-customer-credentials.js +0 -55
@@ -0,0 +1,78 @@
1
+ {{!
2
+ The vehicle card the fleet-ops live map shows in a marker's popup and hover tooltip.
3
+ Shared with the dashboard's Live Fleet widget so both look the same. @vehicle is a vehicle
4
+ model or its index resource payload (the same attributes, plain JSON).
5
+ @variant: "popup" (default, a dark panel) or "tooltip" (inherits the tooltip's panel).
6
+ }}
7
+ <div class="w-[280px] max-w-[calc(100vw-2rem)] {{unless (eq @variant 'tooltip') 'rounded-lg bg-gray-900 shadow-lg p-1.5 text-white'}}" ...attributes>
8
+ <div class="flex items-center gap-1.5 text-xs font-semibold mb-1.5">
9
+ <span class="inline-block w-2 h-2 rounded-full {{if @vehicle.online 'bg-green-400' 'bg-red-500'}}"></span>
10
+ <span>{{or @vehicle.displayName @vehicle.display_name @vehicle.name}}</span>
11
+ <div
12
+ class="status-badge
13
+ {{if @vehicle.status (dasherize @vehicle.status) 'default'}}-status-badge status-badge-{{if @vehicle.status (dasherize @vehicle.status) 'default'}}
14
+ shadow-none ml-auto"
15
+ >
16
+ <span class="rounded-full text-[9px] px-1.5 py-0.5">{{n-a @vehicle.meta.status_label}}</span>
17
+ </div>
18
+ </div>
19
+ <div class="grid grid-cols-2 gap-1">
20
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
21
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Vehicle #</div>
22
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a
23
+ @vehicle.internal_id
24
+ @vehicle.public_id
25
+ @vehicle.plate_number
26
+ @vehicle.serial_number
27
+ @vehicle.vin
28
+ }}</div>
29
+ </div>
30
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
31
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Driver</div>
32
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a @vehicle.driver_name}}</div>
33
+ </div>
34
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
35
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">{{t "resource.trailers"}}</div>
36
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">
37
+ {{#each @vehicle.trailers as |trailer|}}
38
+ <div class="flex items-center gap-1 min-w-0">
39
+ <span class="inline-block w-1.5 h-1.5 rounded-full shrink-0 {{if trailer.online 'bg-green-400' 'bg-gray-500'}}"></span>
40
+ <span class="truncate">{{or trailer.displayName trailer.display_name trailer.name trailer.public_id}}</span>
41
+ </div>
42
+ {{else}}
43
+ -
44
+ {{/each}}
45
+ </div>
46
+ </div>
47
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
48
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">{{t "resource.devices"}}</div>
49
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">
50
+ {{#each @vehicle.devices as |device|}}
51
+ <div class="flex items-center gap-1 min-w-0">
52
+ <span class="inline-block w-1.5 h-1.5 rounded-full shrink-0 {{if device.online 'bg-green-400' 'bg-gray-500'}}"></span>
53
+ <span class="truncate">{{or device.displayName device.display_name device.name device.device_id device.public_id}}</span>
54
+ </div>
55
+ {{else}}
56
+ -
57
+ {{/each}}
58
+ </div>
59
+ </div>
60
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
61
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Order</div>
62
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a @vehicle.meta.current_order_reference}}</div>
63
+ </div>
64
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
65
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Speed</div>
66
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a @vehicle.meta.speed_label}}</div>
67
+ </div>
68
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
69
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Heading</div>
70
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a @vehicle.meta.heading_label}}</div>
71
+ </div>
72
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
73
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Location</div>
74
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a @vehicle.meta.location_coordinates}}</div>
75
+ </div>
76
+ </div>
77
+ </div>
78
+
@@ -0,0 +1,3 @@
1
+ import templateOnly from '@ember/component/template-only';
2
+
3
+ export default templateOnly();
@@ -41,14 +41,14 @@
41
41
  <label>
42
42
  {{t "common.email"}}
43
43
  </label>
44
- <Input @value={{@options.driver.email}} @type="text" class="w-full form-input" placeholder={{t "common.email"}} />
44
+ <Input @value={{@options.driver.email}} @type="text" class="w-full form-input" placeholder={{t "common.email"}} disabled={{@options.driver.is_staff_linked}} />
45
45
  </div>
46
46
 
47
47
  <div class="input-group">
48
48
  <label>
49
49
  {{t "common.phone"}}
50
50
  </label>
51
- <PhoneInput @value={{@options.driver.phone}} @onInput={{fn (mut @options.driver.phone)}} class="form-input w-full" />
51
+ <PhoneInput @value={{@options.driver.phone}} @onInput={{fn (mut @options.driver.phone)}} class="form-input w-full" disabled={{@options.driver.is_staff_linked}} />
52
52
  </div>
53
53
 
54
54
  <div class="input-group">
@@ -0,0 +1,23 @@
1
+ <Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
2
+ <div class="px-4 text-gray-900 dark:text-gray-50">
3
+ <div class="mb-1 text-base font-semibold">
4
+ {{t "profile-account.prompts.reset-password-heading" name=this.profile.name}}
5
+ </div>
6
+ <div class="text-sm mb-4">{{t "profile-account.prompts.reset-password-body"}}</div>
7
+ <InputGroup>
8
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
9
+ <InputGroup @name={{t "profile-account.prompts.new-password"}} @type="password" @value={{this.password}} @wrapperClass="mb-0i" />
10
+ <InputGroup @name={{t "profile-account.prompts.confirm-new-password"}} @type="password" @value={{this.confirmPassword}} @wrapperClass="mb-0i" />
11
+ </div>
12
+ </InputGroup>
13
+ <InputGroup>
14
+ <Checkbox
15
+ @value={{this.sendCredentials}}
16
+ @label={{t "profile-account.prompts.send-new-credentials"}}
17
+ @onToggle={{fn (mut this.sendCredentials)}}
18
+ @alignItems="center"
19
+ @labelClass="mb-0i"
20
+ />
21
+ </InputGroup>
22
+ </div>
23
+ </Modal::Default>
@@ -0,0 +1,70 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { inject as service } from '@ember/service';
4
+ import { INTERNAL_NAMESPACE, updateProfileFromResponse } from '../../utils/profile-login-actions';
5
+
6
+ /**
7
+ * Resets the login password of a driver, contact or customer profile.
8
+ *
9
+ * Options:
10
+ * - `profile`: the driver or contact record
11
+ * - `endpoint`: the reset credentials endpoint (internal namespace)
12
+ * - `payload`: extra body params, or a function returning them
13
+ * - `onPasswordResetComplete(response)`: optional callback
14
+ */
15
+ export default class ModalsResetProfileCredentialsComponent extends Component {
16
+ @service fetch;
17
+ @service intl;
18
+ @service notifications;
19
+ @tracked options = {};
20
+ @tracked password;
21
+ @tracked confirmPassword;
22
+ @tracked sendCredentials = true;
23
+ @tracked profile;
24
+
25
+ constructor(owner, { options }) {
26
+ super(...arguments);
27
+ this.profile = options.profile;
28
+ this.options = options;
29
+ this.setupOptions();
30
+ }
31
+
32
+ get extraPayload() {
33
+ const { payload } = this.options;
34
+ return (typeof payload === 'function' ? payload(this.profile) : payload) ?? {};
35
+ }
36
+
37
+ setupOptions() {
38
+ this.options.title = this.options.title ?? this.intl.t('profile-account.prompts.reset-password-title');
39
+ this.options.acceptButtonText = this.options.acceptButtonText ?? this.intl.t('profile-account.prompts.reset-password-accept');
40
+ this.options.declineButtonHidden = true;
41
+ this.options.confirm = async (modal) => {
42
+ modal.startLoading();
43
+
44
+ try {
45
+ const response = await this.fetch.post(
46
+ this.options.endpoint,
47
+ {
48
+ ...this.extraPayload,
49
+ password: this.password,
50
+ password_confirmation: this.confirmPassword,
51
+ send_credentials: this.sendCredentials,
52
+ },
53
+ { namespace: INTERNAL_NAMESPACE }
54
+ );
55
+
56
+ updateProfileFromResponse(this.profile, response);
57
+ this.notifications.success(this.intl.t('profile-account.prompts.reset-password-success'));
58
+
59
+ if (typeof this.options.onPasswordResetComplete === 'function') {
60
+ this.options.onPasswordResetComplete(response);
61
+ }
62
+
63
+ modal.done();
64
+ } catch (error) {
65
+ this.notifications.serverError(error);
66
+ modal.stopLoading();
67
+ }
68
+ };
69
+ }
70
+ }
@@ -0,0 +1,13 @@
1
+ <div class="profile-identity-hint text-xs" {{did-insert this.lookupIdentity}} {{did-update this.lookupIdentity @email @phone}} ...attributes>
2
+ {{#if this.conflict}}
3
+ <div class="flex items-center space-x-1 text-yellow-600 dark:text-yellow-400" data-test-profile-identity-conflict>
4
+ <FaIcon @icon="triangle-exclamation" @size="xs" />
5
+ <span>{{this.conflict}}</span>
6
+ </div>
7
+ {{else if this.staff}}
8
+ <div class="flex items-center space-x-1 text-blue-600 dark:text-blue-400" data-test-profile-identity-staff>
9
+ <FaIcon @icon="circle-info" @size="xs" />
10
+ <span>{{t "profile-account.will-link-to-staff" name=(or this.staff.name this.staff.email this.staff.phone)}}</span>
11
+ </div>
12
+ {{/if}}
13
+ </div>
@@ -0,0 +1,49 @@
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, timeout } from 'ember-concurrency';
6
+
7
+ /**
8
+ * Inline hint shown while creating a driver, contact or customer profile:
9
+ * tells the user when the entered email/phone belongs to an existing team
10
+ * member (the profile will be linked to that account) or cannot be used.
11
+ *
12
+ * Args: `@email`, `@phone`, `@type` ('driver' | 'customer' | 'contact'),
13
+ * `@ignore` (optional profile id).
14
+ */
15
+ export default class ProfileIdentityHintComponent extends Component {
16
+ @service fetch;
17
+ @tracked staff = null;
18
+ @tracked conflict = null;
19
+
20
+ @action lookupIdentity() {
21
+ this.lookup.perform();
22
+ }
23
+
24
+ @task({ restartable: true }) *lookup() {
25
+ const { email, phone, type, ignore } = this.args;
26
+
27
+ if (!email && !phone) {
28
+ this.staff = null;
29
+ this.conflict = null;
30
+ return;
31
+ }
32
+
33
+ yield timeout(400);
34
+
35
+ const query = { type };
36
+ if (email) query.email = email;
37
+ if (phone) query.phone = phone;
38
+ if (ignore) query.ignore = ignore;
39
+
40
+ try {
41
+ const response = yield this.fetch.get('fleet-ops/lookup/profile-identity', query, { namespace: 'int/v1' });
42
+ this.staff = response?.staff ?? null;
43
+ this.conflict = response?.conflict ?? null;
44
+ } catch {
45
+ this.staff = null;
46
+ this.conflict = null;
47
+ }
48
+ }
49
+ }
@@ -15,7 +15,7 @@
15
15
  </div>
16
16
  <div class="flex flex-shrink-0 items-center gap-2 whitespace-nowrap">
17
17
  <Button @icon="refresh" @size="sm" @helpText={{t "common.reload"}} @onClick={{this.refresh}} />
18
- <Button @icon="plus" @text="Connect Provider" @type="primary" @size="sm" @onClick={{this.telematicActions.transition.create}} />
18
+ <Button @icon="plus" @text="Connect Provider" @type="primary" @size="sm" @permission="fleet-ops create telematic" @onClick={{this.telematicActions.transition.create}} />
19
19
  </div>
20
20
  </div>
21
21
  </section>
@@ -28,7 +28,8 @@
28
28
 
29
29
  {{#if this.load.isIdle}}
30
30
  {{! Drivers — fleetops' rotating tracking-marker matches the operational
31
- live-map's look and animates on heading changes. }}
31
+ live-map's look and animates on heading changes. The popup and tooltip
32
+ are the live map's own cards, fed from each marker's index-resource card. }}
32
33
  {{#each this.drivers as |driver|}}
33
34
  <layers.tracking-marker
34
35
  @id={{driver.uuid}}
@@ -39,28 +40,11 @@
39
40
  @draggable={{false}}
40
41
  as |marker|
41
42
  >
42
- <marker.popup @maxWidth="240" @minWidth="180">
43
- <div class="flex flex-row gap-2">
44
- <div class="w-9 h-9 flex-shrink-0">
45
- <img src={{or driver.avatar_url driver.vehicle_avatar}} alt={{driver.name}} class="rounded-md w-9 h-9 object-cover shadow-sm" />
46
- </div>
47
- <div class="flex-1 min-w-0">
48
- <div class="text-xs font-semibold truncate">{{or driver.name "Driver"}}</div>
49
- <div class="text-[11px]">
50
- {{#if driver.current_order_uuid}}
51
- <span class="text-blue-500"><FaIcon @icon="bolt" class="mr-1" />On order</span>
52
- {{else if driver.online}}
53
- <span class="text-green-500"><FaIcon @icon="circle" @size="2xs" class="mr-1" />Online</span>
54
- {{else}}
55
- <span class="text-gray-400"><FaIcon @icon="circle" @size="2xs" class="mr-1" />Offline</span>
56
- {{/if}}
57
- </div>
58
- <div class="text-[10px] text-gray-400 mt-0.5 truncate">{{driver.lat}}, {{driver.lng}}</div>
59
- </div>
60
- </div>
43
+ <marker.popup @maxWidth="500" @minWidth="225">
44
+ <Map::MarkerCard::Driver @driver={{or driver.card driver}} />
61
45
  </marker.popup>
62
46
  <marker.tooltip @permanent={{false}} @sticky={{true}} @direction="top">
63
- <div class="text-[11px] font-semibold">{{or driver.name "Driver"}}</div>
47
+ <Map::MarkerCard::Driver @driver={{or driver.card driver}} @variant="tooltip" />
64
48
  </marker.tooltip>
65
49
  </layers.tracking-marker>
66
50
  {{/each}}
@@ -77,27 +61,11 @@
77
61
  @draggable={{false}}
78
62
  as |marker|
79
63
  >
80
- <marker.popup @maxWidth="240" @minWidth="180">
81
- <div class="flex flex-row gap-2">
82
- <div class="w-9 h-9 flex-shrink-0">
83
- <img src={{or vehicle.photo_url vehicle.avatar_url}} alt={{vehicle.name}} class="rounded-md w-9 h-9 object-cover shadow-sm" />
84
- </div>
85
- <div class="flex-1 min-w-0">
86
- <div class="text-xs font-semibold truncate">{{or vehicle.name "Vehicle"}}</div>
87
- {{#if vehicle.plate_number}}
88
- <div class="text-[10px] text-gray-500 truncate">{{vehicle.plate_number}}</div>
89
- {{/if}}
90
- {{#if vehicle.driver_name}}
91
- <div class="text-[11px] text-gray-600 dark:text-gray-300 truncate">
92
- <FaIcon @icon="id-card" class="mr-1 text-gray-400" />{{vehicle.driver_name}}
93
- </div>
94
- {{/if}}
95
- <div class="text-[10px] text-gray-400 mt-0.5 truncate">{{vehicle.lat}}, {{vehicle.lng}}</div>
96
- </div>
97
- </div>
64
+ <marker.popup @maxWidth="500" @minWidth="225">
65
+ <Map::MarkerCard::Vehicle @vehicle={{or vehicle.card vehicle}} />
98
66
  </marker.popup>
99
67
  <marker.tooltip @permanent={{false}} @sticky={{true}} @direction="top">
100
- <div class="text-[11px] font-semibold">{{or vehicle.name "Vehicle"}}</div>
68
+ <Map::MarkerCard::Vehicle @vehicle={{or vehicle.card vehicle}} @variant="tooltip" />
101
69
  </marker.tooltip>
102
70
  </layers.tracking-marker>
103
71
  {{/each}}
@@ -29,6 +29,7 @@ export default class AnalyticsReportsIndexController extends Controller {
29
29
  type: 'primary',
30
30
  icon: 'plus',
31
31
  onClick: this.reportActions.transition.create,
32
+ permission: 'iam create report',
32
33
  },
33
34
  ];
34
35
  }
@@ -39,6 +40,7 @@ export default class AnalyticsReportsIndexController extends Controller {
39
40
  label: 'Delete selected...',
40
41
  class: 'text-red-500',
41
42
  fn: this.reportActions.bulkDelete,
43
+ permission: 'iam delete report',
42
44
  },
43
45
  ];
44
46
  }
@@ -81,10 +83,12 @@ export default class AnalyticsReportsIndexController extends Controller {
81
83
  {
82
84
  label: 'View report...',
83
85
  fn: this.reportActions.transition.view,
86
+ permission: 'iam view report',
84
87
  },
85
88
  {
86
89
  label: 'Edit report...',
87
90
  fn: this.reportActions.transition.edit,
91
+ permission: 'iam update report',
88
92
  },
89
93
  {
90
94
  separator: true,
@@ -92,6 +96,7 @@ export default class AnalyticsReportsIndexController extends Controller {
92
96
  {
93
97
  label: 'Delete report...',
94
98
  fn: this.reportActions.delete,
99
+ permission: 'iam delete report',
95
100
  },
96
101
  ],
97
102
  sortable: false,
@@ -65,12 +65,14 @@ export default class ConnectivityDevicesIndexController extends Controller {
65
65
  type: 'primary',
66
66
  icon: 'plus',
67
67
  onClick: this.deviceActions.transition.create,
68
+ permission: 'fleet-ops create device',
68
69
  },
69
70
  {
70
71
  text: this.intl.t('common.import'),
71
72
  type: 'magic',
72
73
  icon: 'upload',
73
74
  onClick: this.deviceActions.import,
75
+ permission: 'fleet-ops import device',
74
76
  },
75
77
  {
76
78
  text: this.intl.t('common.export'),
@@ -78,6 +80,7 @@ export default class ConnectivityDevicesIndexController extends Controller {
78
80
  iconClass: 'rotate-icon-45',
79
81
  wrapperClass: 'hidden md:flex',
80
82
  onClick: this.deviceActions.export,
83
+ permission: 'fleet-ops export device',
81
84
  },
82
85
  ];
83
86
 
@@ -87,6 +90,7 @@ export default class ConnectivityDevicesIndexController extends Controller {
87
90
  label: 'Delete selected...',
88
91
  class: 'text-red-500',
89
92
  fn: this.deviceActions.bulkDelete,
93
+ permission: 'fleet-ops delete device',
90
94
  },
91
95
  ];
92
96
 
@@ -40,6 +40,7 @@ export default class ConnectivityFuelProvidersIndexController extends Controller
40
40
  text: 'Connect Integration',
41
41
  type: 'primary',
42
42
  onClick: () => this.fuelIntegrationActions.transition.create(),
43
+ permission: 'fleet-ops create fuel-provider-connection',
43
44
  },
44
45
  ];
45
46
  }
@@ -51,6 +52,7 @@ export default class ConnectivityFuelProvidersIndexController extends Controller
51
52
  {
52
53
  label: `Sync ${selected.length} selected`,
53
54
  fn: () => selected.forEach((connection) => this.syncConnection(connection)),
55
+ permission: 'fleet-ops sync fuel-provider-connection',
54
56
  },
55
57
  ];
56
58
  }
@@ -125,11 +127,11 @@ export default class ConnectivityFuelProvidersIndexController extends Controller
125
127
  sticky: 'right',
126
128
  width: 60,
127
129
  actions: [
128
- { label: 'Open Integration', fn: this.openConnection },
129
- { label: 'Edit Settings', fn: this.editConnection },
130
+ { label: 'Open Integration', fn: this.openConnection, permission: 'fleet-ops view fuel-provider-connection' },
131
+ { label: 'Edit Settings', fn: this.editConnection, permission: 'fleet-ops update fuel-provider-connection' },
130
132
  { separator: true },
131
- { label: 'Test Connection', fn: this.testConnection },
132
- { label: 'Sync Transactions', fn: this.syncConnection },
133
+ { label: 'Test Connection', fn: this.testConnection, permission: 'fleet-ops update fuel-provider-connection' },
134
+ { label: 'Sync Transactions', fn: this.syncConnection, permission: 'fleet-ops sync fuel-provider-connection' },
133
135
  ],
134
136
  sortable: false,
135
137
  filterable: false,
@@ -92,12 +92,14 @@ export default class ConnectivitySensorsIndexController extends Controller {
92
92
  type: 'primary',
93
93
  icon: 'plus',
94
94
  onClick: this.sensorActions.transition.create,
95
+ permission: 'fleet-ops create sensor',
95
96
  },
96
97
  {
97
98
  text: this.intl.t('common.import'),
98
99
  type: 'magic',
99
100
  icon: 'upload',
100
101
  onClick: this.sensorActions.import,
102
+ permission: 'fleet-ops import sensor',
101
103
  },
102
104
  {
103
105
  text: this.intl.t('common.export'),
@@ -105,6 +107,7 @@ export default class ConnectivitySensorsIndexController extends Controller {
105
107
  iconClass: 'rotate-icon-45',
106
108
  wrapperClass: 'hidden md:flex',
107
109
  onClick: this.sensorActions.export,
110
+ permission: 'fleet-ops export sensor',
108
111
  },
109
112
  ];
110
113
 
@@ -114,6 +117,7 @@ export default class ConnectivitySensorsIndexController extends Controller {
114
117
  label: 'Delete selected...',
115
118
  class: 'text-red-500',
116
119
  fn: this.sensorActions.bulkDelete,
120
+ permission: 'fleet-ops delete sensor',
117
121
  },
118
122
  ];
119
123
 
@@ -38,12 +38,14 @@ export default class ConnectivityTelematicsIndexController extends Controller {
38
38
  type: 'primary',
39
39
  icon: 'plus',
40
40
  onClick: this.telematicActions.transition.create,
41
+ permission: 'fleet-ops create telematic',
41
42
  },
42
43
  {
43
44
  text: this.intl.t('common.import'),
44
45
  type: 'magic',
45
46
  icon: 'upload',
46
47
  onClick: this.telematicActions.import,
48
+ permission: 'fleet-ops import telematic',
47
49
  },
48
50
  {
49
51
  text: this.intl.t('common.export'),
@@ -51,6 +53,7 @@ export default class ConnectivityTelematicsIndexController extends Controller {
51
53
  iconClass: 'rotate-icon-45',
52
54
  wrapperClass: 'hidden md:flex',
53
55
  onClick: this.telematicActions.export,
56
+ permission: 'fleet-ops export telematic',
54
57
  },
55
58
  ];
56
59
 
@@ -60,6 +63,7 @@ export default class ConnectivityTelematicsIndexController extends Controller {
60
63
  label: 'Delete selected...',
61
64
  class: 'text-red-500',
62
65
  fn: this.telematicActions.bulkDelete,
66
+ permission: 'fleet-ops delete telematic',
63
67
  },
64
68
  ];
65
69
 
@@ -46,14 +46,21 @@ export default class MaintenanceEquipmentIndexController extends Controller {
46
46
  helpText: 'Change the layout',
47
47
  },
48
48
  { icon: 'refresh', onClick: this.equipmentActions.refresh, helpText: this.intl.t('common.refresh') },
49
- { text: this.intl.t('common.new'), type: 'primary', icon: 'plus', onClick: this.equipmentActions.transition.create },
50
- { text: this.intl.t('common.import'), type: 'magic', icon: 'upload', onClick: this.equipmentActions.import },
51
- { text: this.intl.t('common.export'), icon: 'long-arrow-up', iconClass: 'rotate-icon-45', wrapperClass: 'hidden md:flex', onClick: this.equipmentActions.export },
49
+ { text: this.intl.t('common.new'), type: 'primary', icon: 'plus', onClick: this.equipmentActions.transition.create, permission: 'fleet-ops create equipment' },
50
+ { text: this.intl.t('common.import'), type: 'magic', icon: 'upload', onClick: this.equipmentActions.import, permission: 'fleet-ops import equipment' },
51
+ {
52
+ text: this.intl.t('common.export'),
53
+ icon: 'long-arrow-up',
54
+ iconClass: 'rotate-icon-45',
55
+ wrapperClass: 'hidden md:flex',
56
+ onClick: this.equipmentActions.export,
57
+ permission: 'fleet-ops export equipment',
58
+ },
52
59
  ];
53
60
  }
54
61
 
55
62
  get bulkActions() {
56
- return [{ label: 'Delete selected...', class: 'text-red-500', fn: this.equipmentActions.bulkDelete }];
63
+ return [{ label: 'Delete selected...', class: 'text-red-500', fn: this.equipmentActions.bulkDelete, permission: 'fleet-ops delete equipment' }];
57
64
  }
58
65
 
59
66
  get columns() {
@@ -17,12 +17,12 @@ export default class MaintenanceInspectionFormsIndexController extends Controlle
17
17
  get actionButtons() {
18
18
  return [
19
19
  { icon: 'refresh', onClick: this.inspectionFormActions.refresh, helpText: this.intl.t('common.refresh') },
20
- { text: this.intl.t('common.new'), type: 'primary', icon: 'plus', onClick: this.inspectionFormActions.transition.create },
20
+ { text: this.intl.t('common.new'), type: 'primary', icon: 'plus', onClick: this.inspectionFormActions.transition.create, permission: 'fleet-ops create inspection-form' },
21
21
  ];
22
22
  }
23
23
 
24
24
  get bulkActions() {
25
- return [{ label: 'Delete selected...', class: 'text-red-500', fn: this.inspectionFormActions.bulkDelete }];
25
+ return [{ label: 'Delete selected...', class: 'text-red-500', fn: this.inspectionFormActions.bulkDelete, permission: 'fleet-ops delete inspection-form' }];
26
26
  }
27
27
 
28
28
  get columns() {
@@ -23,12 +23,18 @@ export default class MaintenanceInspectionSubmissionsIndexController extends Con
23
23
  get actionButtons() {
24
24
  return [
25
25
  { icon: 'refresh', onClick: this.inspectionSubmissionActions.refresh, helpText: this.intl.t('common.refresh') },
26
- { text: this.intl.t('common.new'), type: 'primary', icon: 'plus', onClick: this.inspectionSubmissionActions.transition.create },
26
+ {
27
+ text: this.intl.t('common.new'),
28
+ type: 'primary',
29
+ icon: 'plus',
30
+ onClick: this.inspectionSubmissionActions.transition.create,
31
+ permission: 'fleet-ops create inspection-submission',
32
+ },
27
33
  ];
28
34
  }
29
35
 
30
36
  get bulkActions() {
31
- return [{ label: 'Delete selected...', class: 'text-red-500', fn: this.inspectionSubmissionActions.bulkDelete }];
37
+ return [{ label: 'Delete selected...', class: 'text-red-500', fn: this.inspectionSubmissionActions.bulkDelete, permission: 'fleet-ops delete inspection-submission' }];
32
38
  }
33
39
 
34
40
  get columns() {
@@ -31,12 +31,14 @@ export default class MaintenanceMaintenancesIndexController extends Controller {
31
31
  type: 'primary',
32
32
  icon: 'plus',
33
33
  onClick: this.maintenanceActions.transition.create,
34
+ permission: 'fleet-ops create maintenance',
34
35
  },
35
36
  {
36
37
  text: this.intl.t('common.import'),
37
38
  type: 'magic',
38
39
  icon: 'upload',
39
40
  onClick: this.maintenanceActions.import,
41
+ permission: 'fleet-ops import maintenance',
40
42
  },
41
43
  {
42
44
  text: this.intl.t('common.export'),
@@ -44,6 +46,7 @@ export default class MaintenanceMaintenancesIndexController extends Controller {
44
46
  iconClass: 'rotate-icon-45',
45
47
  wrapperClass: 'hidden md:flex',
46
48
  onClick: this.maintenanceActions.export,
49
+ permission: 'fleet-ops export maintenance',
47
50
  },
48
51
  ];
49
52
  }
@@ -55,6 +58,7 @@ export default class MaintenanceMaintenancesIndexController extends Controller {
55
58
  label: 'Delete selected...',
56
59
  class: 'text-red-500',
57
60
  fn: this.maintenanceActions.bulkDelete,
61
+ permission: 'fleet-ops delete maintenance',
58
62
  },
59
63
  ];
60
64
  }
@@ -46,14 +46,21 @@ export default class MaintenancePartsIndexController extends Controller {
46
46
  helpText: 'Change the layout',
47
47
  },
48
48
  { icon: 'refresh', onClick: this.partActions.refresh, helpText: this.intl.t('common.refresh') },
49
- { text: this.intl.t('common.new'), type: 'primary', icon: 'plus', onClick: this.partActions.transition.create },
50
- { text: this.intl.t('common.import'), type: 'magic', icon: 'upload', onClick: this.partActions.import },
51
- { text: this.intl.t('common.export'), icon: 'long-arrow-up', iconClass: 'rotate-icon-45', wrapperClass: 'hidden md:flex', onClick: this.partActions.export },
49
+ { text: this.intl.t('common.new'), type: 'primary', icon: 'plus', onClick: this.partActions.transition.create, permission: 'fleet-ops create part' },
50
+ { text: this.intl.t('common.import'), type: 'magic', icon: 'upload', onClick: this.partActions.import, permission: 'fleet-ops import part' },
51
+ {
52
+ text: this.intl.t('common.export'),
53
+ icon: 'long-arrow-up',
54
+ iconClass: 'rotate-icon-45',
55
+ wrapperClass: 'hidden md:flex',
56
+ onClick: this.partActions.export,
57
+ permission: 'fleet-ops export part',
58
+ },
52
59
  ];
53
60
  }
54
61
 
55
62
  get bulkActions() {
56
- return [{ label: 'Delete selected...', class: 'text-red-500', fn: this.partActions.bulkDelete }];
63
+ return [{ label: 'Delete selected...', class: 'text-red-500', fn: this.partActions.bulkDelete, permission: 'fleet-ops delete part' }];
57
64
  }
58
65
 
59
66
  get columns() {