@fleetbase/fleetops-engine 0.6.48 → 0.6.50

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 (266) hide show
  1. package/addon/components/customer/create-order-form.hbs +2 -2
  2. package/addon/components/customer/form.hbs +2 -2
  3. package/addon/components/customer/order-form.hbs +2 -2
  4. package/addon/components/customer/orders.hbs +2 -2
  5. package/addon/components/display-place.hbs +2 -2
  6. package/addon/components/driver/details.hbs +5 -1
  7. package/addon/components/driver/form.hbs +11 -1
  8. package/addon/components/fleet-listing-panel.hbs +2 -2
  9. package/addon/components/fleet-listing-panel.js +10 -0
  10. package/addon/components/map/google-live-map.js +18 -75
  11. package/addon/components/map/leaflet-live-map.hbs +187 -37
  12. package/addon/components/map/leaflet-live-map.js +184 -20
  13. package/addon/components/modals/order-change-destination.hbs +46 -0
  14. package/addon/components/modals/order-change-destination.js +106 -0
  15. package/addon/components/modals/stop-activity.hbs +28 -0
  16. package/addon/components/modals/stop-activity.js +52 -0
  17. package/addon/components/modals/update-order-activity.hbs +45 -3
  18. package/addon/components/modals/update-order-activity.js +143 -5
  19. package/addon/components/orchestrator/phase-builder.hbs +21 -0
  20. package/addon/components/orchestrator/phase-builder.js +13 -0
  21. package/addon/components/orchestrator/resource-panel.hbs +0 -10
  22. package/addon/components/orchestrator/resource-panel.js +2 -4
  23. package/addon/components/orchestrator-workbench.js +1 -0
  24. package/addon/components/order/activity-list.hbs +2 -2
  25. package/addon/components/order/activity-timeline.hbs +31 -29
  26. package/addon/components/order/details/activity.hbs +3 -3
  27. package/addon/components/order/details/activity.js +11 -0
  28. package/addon/components/order/details/documents.js +2 -0
  29. package/addon/components/order/details/purchase-rate.hbs +2 -2
  30. package/addon/components/order/details/route.hbs +11 -3
  31. package/addon/components/order/details/route.js +91 -14
  32. package/addon/components/order/details/tracking.hbs +101 -72
  33. package/addon/components/order/details/tracking.js +149 -2
  34. package/addon/components/order/form/details.hbs +3 -3
  35. package/addon/components/order/form/documents.js +11 -4
  36. package/addon/components/order/form/route.hbs +108 -104
  37. package/addon/components/order/form/route.js +37 -1
  38. package/addon/components/order/form/service-rate.hbs +2 -2
  39. package/addon/components/order/form.hbs +2 -2
  40. package/addon/components/order-config-manager/activity-flow.hbs +4 -1
  41. package/addon/components/order-config-manager/activity-flow.js +178 -12
  42. package/addon/components/place/address.js +1 -1
  43. package/addon/components/route-list.hbs +17 -17
  44. package/addon/components/route-list.js +42 -10
  45. package/addon/components/service-rate/details.hbs +41 -0
  46. package/addon/components/service-rate/form.hbs +118 -0
  47. package/addon/components/service-rate/form.js +55 -0
  48. package/addon/components/vehicle/form.js +1 -1
  49. package/addon/components/vehicle/pill.hbs +1 -1
  50. package/addon/components/widget/fleet-ops-key-metrics.hbs +19 -17
  51. package/addon/components/widget/fleet-ops-key-metrics.js +26 -0
  52. package/addon/components/widget/fuel-efficiency.hbs +26 -0
  53. package/addon/components/widget/fuel-efficiency.js +43 -0
  54. package/addon/components/widget/geofence-violations.hbs +44 -0
  55. package/addon/components/widget/geofence-violations.js +55 -0
  56. package/addon/components/widget/issues-insights.hbs +43 -0
  57. package/addon/components/widget/issues-insights.js +62 -0
  58. package/addon/components/widget/kpi-active-orders.hbs +10 -0
  59. package/addon/components/widget/kpi-active-orders.js +5 -0
  60. package/addon/components/widget/kpi-aov.hbs +10 -0
  61. package/addon/components/widget/kpi-aov.js +5 -0
  62. package/addon/components/widget/kpi-distance.hbs +10 -0
  63. package/addon/components/widget/kpi-distance.js +5 -0
  64. package/addon/components/widget/kpi-drivers-online.hbs +10 -0
  65. package/addon/components/widget/kpi-drivers-online.js +5 -0
  66. package/addon/components/widget/kpi-earnings.hbs +10 -0
  67. package/addon/components/widget/kpi-earnings.js +5 -0
  68. package/addon/components/widget/kpi-open-issues.hbs +11 -0
  69. package/addon/components/widget/kpi-open-issues.js +5 -0
  70. package/addon/components/widget/kpi-tile.hbs +59 -0
  71. package/addon/components/widget/kpi-tile.js +168 -0
  72. package/addon/components/widget/live-fleet.hbs +122 -0
  73. package/addon/components/widget/live-fleet.js +365 -0
  74. package/addon/components/widget/maintenance-overview.hbs +45 -0
  75. package/addon/components/widget/maintenance-overview.js +40 -0
  76. package/addon/components/widget/on-time-delivery.hbs +27 -0
  77. package/addon/components/widget/on-time-delivery.js +73 -0
  78. package/addon/components/widget/operations-pulse.hbs +51 -0
  79. package/addon/components/widget/operations-pulse.js +173 -0
  80. package/addon/components/widget/orders-by-status.hbs +26 -0
  81. package/addon/components/widget/orders-by-status.js +54 -0
  82. package/addon/components/widget/revenue-trend.hbs +34 -0
  83. package/addon/components/widget/revenue-trend.js +67 -0
  84. package/addon/components/widget/top-drivers.hbs +44 -0
  85. package/addon/components/widget/top-drivers.js +54 -0
  86. package/addon/controllers/management/vehicles/index/new.js +1 -1
  87. package/addon/controllers/operations/orders/index/details.js +4 -4
  88. package/addon/controllers/operations/orders/index.js +14 -0
  89. package/addon/controllers/settings/routing.js +26 -0
  90. package/addon/extension.js +172 -3
  91. package/addon/helpers/place-address-html.js +2 -2
  92. package/addon/routes/operations/service-rates/index.js +1 -1
  93. package/addon/services/geofence.js +163 -19
  94. package/addon/services/leaflet-contextmenu-manager.js +27 -0
  95. package/addon/services/map-adapter/google.js +13 -2
  96. package/addon/services/vehicle-actions.js +1 -1
  97. package/addon/styles/fleetops-engine.css +205 -88
  98. package/addon/templates/maintenance/equipment/index.hbs +1 -1
  99. package/addon/templates/maintenance/parts/index.hbs +1 -1
  100. package/addon/templates/maintenance/schedules/index.hbs +1 -1
  101. package/addon/templates/management/vehicles/index.hbs +1 -1
  102. package/addon/templates/settings/avatars.hbs +1 -1
  103. package/addon/templates/settings/custom-fields.hbs +1 -1
  104. package/addon/templates/settings/map.hbs +1 -1
  105. package/addon/templates/settings/navigator-app.hbs +1 -1
  106. package/addon/templates/settings/notifications.hbs +1 -1
  107. package/addon/templates/settings/orchestrator.hbs +1 -1
  108. package/addon/templates/settings/payments/index.hbs +1 -1
  109. package/addon/templates/settings/payments/onboard.hbs +1 -1
  110. package/addon/templates/settings/routing.hbs +22 -2
  111. package/addon/templates/settings/scheduling.hbs +1 -1
  112. package/addon/utils/fleet-ops-options.js +2 -1
  113. package/addon/utils/live-map-card-content.js +171 -0
  114. package/addon/utils/normalize-order-config-flow.js +41 -0
  115. package/addon/utils/place-address-html.js +3 -2
  116. package/addon/utils/route-visualization.js +54 -7
  117. package/app/components/modals/order-change-destination.js +1 -0
  118. package/app/components/modals/stop-activity.js +1 -0
  119. package/app/components/widget/fuel-efficiency.js +1 -0
  120. package/app/components/widget/geofence-violations.js +1 -0
  121. package/app/components/widget/issues-insights.js +1 -0
  122. package/app/components/widget/kpi-active-orders.js +1 -0
  123. package/app/components/widget/kpi-aov.js +1 -0
  124. package/app/components/widget/kpi-distance.js +1 -0
  125. package/app/components/widget/kpi-drivers-online.js +1 -0
  126. package/app/components/widget/kpi-earnings.js +1 -0
  127. package/app/components/widget/kpi-open-issues.js +1 -0
  128. package/app/components/widget/kpi-tile.js +1 -0
  129. package/app/components/widget/live-fleet.js +1 -0
  130. package/app/components/widget/maintenance-overview.js +1 -0
  131. package/app/components/widget/on-time-delivery.js +1 -0
  132. package/app/components/widget/operations-pulse.js +1 -0
  133. package/app/components/widget/orders-by-status.js +1 -0
  134. package/app/components/widget/revenue-trend.js +1 -0
  135. package/app/components/widget/top-drivers.js +1 -0
  136. package/app/utils/live-map-card-content.js +1 -0
  137. package/composer.json +1 -1
  138. package/extension.json +1 -1
  139. package/package.json +3 -3
  140. package/pnpm-workspace.yaml +1 -0
  141. package/server/config/telematics.php +111 -1
  142. package/server/migrations/2026_05_18_000001_add_license_expiry_to_drivers_table.php +25 -0
  143. package/server/migrations/2026_05_21_000001_add_geographic_rules_to_service_rate_fees_table.php +37 -0
  144. package/server/migrations/2026_05_25_000001_add_endpoint_tracking_refs_to_payloads_table.php +50 -0
  145. package/server/migrations/2026_05_27_000001_default_driver_vehicle_statuses_to_available.php +48 -0
  146. package/server/resources/views/labels/default.php +57 -49
  147. package/server/resources/views/labels/waypoint-label.php +27 -20
  148. package/server/seeders/Testing/Concerns/SeedsTestingData.php +183 -0
  149. package/server/seeders/Testing/ConnectivitySeeder.php +213 -0
  150. package/server/seeders/Testing/FleetSeeder.php +341 -0
  151. package/server/seeders/Testing/MaintenanceSeeder.php +238 -0
  152. package/server/seeders/Testing/NetworkSeeder.php +250 -0
  153. package/server/seeders/Testing/OrdersSeeder.php +198 -0
  154. package/server/seeders/Testing/TestingSeeder.php +40 -0
  155. package/server/seeders/{VroomOrchestrationSeeder.php → Testing/VroomOrchestrationSeeder.php} +29 -22
  156. package/server/src/Console/Commands/AuditCustomerUserConflicts.php +75 -0
  157. package/server/src/Console/Commands/ProcessOperationalAlerts.php +279 -0
  158. package/server/src/Exceptions/CustomerUserConflictException.php +13 -0
  159. package/server/src/Http/Controllers/Api/v1/ContactController.php +17 -2
  160. package/server/src/Http/Controllers/Api/v1/CustomerController.php +876 -0
  161. package/server/src/Http/Controllers/Api/v1/DriverController.php +1 -1
  162. package/server/src/Http/Controllers/Api/v1/OrderConfigController.php +54 -0
  163. package/server/src/Http/Controllers/Api/v1/OrderController.php +98 -70
  164. package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +2 -2
  165. package/server/src/Http/Controllers/Api/v1/VehicleController.php +20 -3
  166. package/server/src/Http/Controllers/Internal/v1/AnalyticsController.php +119 -0
  167. package/server/src/Http/Controllers/Internal/v1/ContactController.php +19 -0
  168. package/server/src/Http/Controllers/Internal/v1/LiveController.php +101 -66
  169. package/server/src/Http/Controllers/Internal/v1/MetricsController.php +82 -3
  170. package/server/src/Http/Controllers/Internal/v1/OrderController.php +441 -32
  171. package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +1 -1
  172. package/server/src/Http/Controllers/Internal/v1/SettingController.php +46 -1
  173. package/server/src/Http/Controllers/Internal/v1/VehicleController.php +54 -1
  174. package/server/src/Http/Middleware/AuthenticateCustomerToken.php +34 -0
  175. package/server/src/Http/Requests/CreateCustomerOrderRequest.php +63 -0
  176. package/server/src/Http/Requests/CreateCustomerRequest.php +48 -0
  177. package/server/src/Http/Requests/CreateDriverRequest.php +1 -1
  178. package/server/src/Http/Requests/CreateVehicleRequest.php +25 -1
  179. package/server/src/Http/Requests/Internal/CreateDriverRequest.php +1 -1
  180. package/server/src/Http/Requests/VerifyCreateCustomerRequest.php +29 -0
  181. package/server/src/Http/Resources/Internal/v1/OrderConfig.php +41 -0
  182. package/server/src/Http/Resources/v1/Customer.php +79 -0
  183. package/server/src/Http/Resources/v1/Driver.php +2 -0
  184. package/server/src/Http/Resources/v1/Fleet.php +4 -0
  185. package/server/src/Http/Resources/v1/Index/Driver.php +48 -0
  186. package/server/src/Http/Resources/v1/Index/Vehicle.php +74 -1
  187. package/server/src/Http/Resources/v1/OrderConfig.php +74 -0
  188. package/server/src/Http/Resources/v1/Payload.php +23 -20
  189. package/server/src/Http/Resources/v1/ServiceArea.php +33 -19
  190. package/server/src/Http/Resources/v1/ServiceRateFee.php +7 -0
  191. package/server/src/Listeners/HandleOrderDispatched.php +2 -2
  192. package/server/src/Models/Contact.php +92 -15
  193. package/server/src/Models/Customer.php +64 -0
  194. package/server/src/Models/Driver.php +14 -3
  195. package/server/src/Models/Entity.php +2 -2
  196. package/server/src/Models/Order.php +29 -4
  197. package/server/src/Models/OrderConfig.php +14 -11
  198. package/server/src/Models/Payload.php +17 -1
  199. package/server/src/Models/ServiceRate.php +273 -1
  200. package/server/src/Models/ServiceRateFee.php +39 -9
  201. package/server/src/Models/TrackingNumber.php +8 -2
  202. package/server/src/Models/Vehicle.php +18 -2
  203. package/server/src/Models/Waypoint.php +2 -2
  204. package/server/src/Notifications/LateDeparture.php +48 -0
  205. package/server/src/Notifications/ProlongedStoppage.php +48 -0
  206. package/server/src/Notifications/RouteDeviation.php +48 -0
  207. package/server/src/Observers/ContactObserver.php +2 -0
  208. package/server/src/Observers/ServiceRateObserver.php +6 -6
  209. package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +29 -2
  210. package/server/src/Providers/FleetOpsServiceProvider.php +6 -0
  211. package/server/src/Providers/NotificationServiceProvider.php +3 -0
  212. package/server/src/Support/Analytics/AbstractAnalytics.php +77 -0
  213. package/server/src/Support/Analytics/FuelEfficiency.php +83 -0
  214. package/server/src/Support/Analytics/GeofenceViolations.php +63 -0
  215. package/server/src/Support/Analytics/IssuesInsights.php +67 -0
  216. package/server/src/Support/Analytics/LiveFleet.php +127 -0
  217. package/server/src/Support/Analytics/MaintenanceOverview.php +70 -0
  218. package/server/src/Support/Analytics/OnTimeDelivery.php +75 -0
  219. package/server/src/Support/Analytics/OperationsPulse.php +86 -0
  220. package/server/src/Support/Analytics/OrdersByStatus.php +64 -0
  221. package/server/src/Support/Analytics/RevenueTrend.php +78 -0
  222. package/server/src/Support/Analytics/TopDrivers.php +92 -0
  223. package/server/src/Support/CustomerAuth.php +98 -0
  224. package/server/src/Support/LabelPdf.php +52 -0
  225. package/server/src/Support/Metrics/AbstractMetric.php +170 -0
  226. package/server/src/Support/Metrics/ActiveLiveOrdersMetric.php +28 -0
  227. package/server/src/Support/Metrics/AvgOrderValueMetric.php +53 -0
  228. package/server/src/Support/Metrics/DriversOnlineMetric.php +30 -0
  229. package/server/src/Support/Metrics/EarningsMetric.php +30 -0
  230. package/server/src/Support/Metrics/FuelCostsMetric.php +30 -0
  231. package/server/src/Support/Metrics/MoneyMetric.php +30 -0
  232. package/server/src/Support/Metrics/OpenIssuesMetric.php +35 -0
  233. package/server/src/Support/Metrics/OrdersCanceledMetric.php +35 -0
  234. package/server/src/Support/Metrics/OrdersCompletedMetric.php +35 -0
  235. package/server/src/Support/Metrics/OrdersInProgressMetric.php +51 -0
  236. package/server/src/Support/Metrics/OrdersScheduledMetric.php +37 -0
  237. package/server/src/Support/Metrics/Registry.php +46 -0
  238. package/server/src/Support/Metrics/ResolvedIssuesMetric.php +35 -0
  239. package/server/src/Support/Metrics/TotalCustomersMetric.php +29 -0
  240. package/server/src/Support/Metrics/TotalDistanceTraveledMetric.php +35 -0
  241. package/server/src/Support/Metrics/TotalDriversMetric.php +28 -0
  242. package/server/src/Support/Metrics/TotalTimeTraveledMetric.php +35 -0
  243. package/server/src/Support/Metrics.php +46 -249
  244. package/server/src/Support/ResolvesOrderServiceStops.php +512 -0
  245. package/server/src/Support/Telematics/Providers/AfaqyProvider.php +290 -0
  246. package/server/src/Support/Telematics/Providers/SafeeProvider.php +333 -0
  247. package/server/src/Tracking/TrackingContextBuilder.php +41 -38
  248. package/server/src/Tracking/TrackingIntelligenceService.php +109 -9
  249. package/server/src/Tracking/TrackingStop.php +13 -11
  250. package/server/src/routes.php +50 -0
  251. package/server/tests/AnalyticsRoutesTest.php +41 -0
  252. package/server/tests/CapacityAllocationEngineTest.php +10 -10
  253. package/server/tests/CustomerContactIdentitySafetyTest.php +54 -0
  254. package/server/tests/CustomerEndpointTest.php +199 -0
  255. package/server/tests/DriverVehicleStatusDefaultTest.php +39 -0
  256. package/server/tests/LabelPdfRenderingTest.php +23 -0
  257. package/server/tests/LiveControllerViewportTest.php +60 -0
  258. package/server/tests/MetricsRegistryTest.php +84 -0
  259. package/server/tests/OrchestrationPayloadBuilderTest.php +2 -2
  260. package/server/tests/OrderConfigEndpointTest.php +86 -0
  261. package/server/tests/OrderFileAttachmentTest.php +20 -0
  262. package/server/tests/ServiceAreaZoneApiShapeTest.php +34 -0
  263. package/server/tests/VehicleDriverAssignmentTest.php +18 -0
  264. package/server/tests/VroomOrchestrationEngineTest.php +91 -0
  265. package/translations/en-us.yaml +38 -0
  266. package/server/migrations/2026_05_13_000001_repair_customer_contact_user_types.php +0 -86
@@ -335,7 +335,7 @@
335
335
  {{#each serviceQuote.items as |item|}}
336
336
  <tr>
337
337
  <td>
338
- <span class="font-semibold">{{smart-humanize item.details}}</span>
338
+ <span class="font-semibold">{{item.details}}</span>
339
339
  </td>
340
340
  <td>
341
341
  <span>{{format-currency item.amount serviceQuote.currency}}</span>
@@ -449,4 +449,4 @@
449
449
 
450
450
  <Spacer @height="300px" />
451
451
  </Overlay::Body>
452
- </Overlay>
452
+ </Overlay>
@@ -6,7 +6,7 @@
6
6
  @modelName="user"
7
7
  @selectedModel={{@resource.user}}
8
8
  @placeholder={{t "customer.fields.select-user"}}
9
- @query={{hash doesnt_have_contact=true}}
9
+ @query={{hash doesnt_have_contact=true is_customer=true}}
10
10
  @triggerClass="form-select form-input"
11
11
  @infiniteScroll={{false}}
12
12
  @renderInPlace={{true}}
@@ -141,4 +141,4 @@
141
141
  <RegistryYield @registry="fleet-ops:component:customer:form" as |RegistryComponent|>
142
142
  <RegistryComponent @resource={{@resource}} @controller={{@controller}} @permission={{get-write-permission @resource}} />
143
143
  </RegistryYield>
144
- </div>
144
+ </div>
@@ -351,7 +351,7 @@
351
351
  {{#each serviceQuote.items as |item|}}
352
352
  <tr>
353
353
  <td>
354
- <span class="font-semibold">{{smart-humanize item.details}}</span>
354
+ <span class="font-semibold">{{item.details}}</span>
355
355
  </td>
356
356
  <td>
357
357
  <span>{{format-currency item.amount serviceQuote.currency}}</span>
@@ -473,4 +473,4 @@
473
473
 
474
474
  <Spacer @height="300px" />
475
475
  </Overlay::Body>
476
- </Overlay>
476
+ </Overlay>
@@ -274,7 +274,7 @@
274
274
  {{#each order.purchase_rate.service_quote.items as |item|}}
275
275
  <tr>
276
276
  <td>
277
- <span class="font-semibold">{{smart-humanize item.details}}</span>
277
+ <span class="font-semibold">{{item.details}}</span>
278
278
  </td>
279
279
  <td>
280
280
  <div class="flex flex-row items-center justify-end">{{format-currency
@@ -320,4 +320,4 @@
320
320
  </div>
321
321
  <div id="customer-portal-content-wormhole"></div>
322
322
  {{/let}}
323
- </div>
323
+ </div>
@@ -31,7 +31,7 @@
31
31
  <div class="next-dd-menu-seperator"></div>
32
32
  {{#each-in @waypointActions as |actionId action|}}
33
33
  <div class="px-1">
34
- <a href="javascript:;" class="next-dd-item" disabled={{cannot "fleet-ops view order"}} {{on "click" (dropdown-fn dd action.fn this.place)}}>
34
+ <a href="javascript:;" class="next-dd-item" disabled={{cannot "fleet-ops view order"}} {{on "click" (dropdown-fn dd action.fn (or @waypointActionContext this.place))}}>
35
35
  {{action.label}}
36
36
  </a>
37
37
  </div>
@@ -65,4 +65,4 @@
65
65
  {{/if}}
66
66
  </address>
67
67
  {{/if}}
68
- </div>
68
+ </div>
@@ -30,6 +30,10 @@
30
30
  <div class="field-name">{{t "driver.fields.driver-license"}}</div>
31
31
  <div class="field-value">{{n-a @resource.drivers_license_number}}</div>
32
32
  </div>
33
+ <div class="field-info-container">
34
+ <div class="field-name">{{t "driver.fields.license-expiry"}}</div>
35
+ <div class="field-value">{{n-a @resource.license_expiry}}</div>
36
+ </div>
33
37
  <div class="field-info-container">
34
38
  <div class="field-name">{{t "resource.vendor"}}</div>
35
39
  <div class="field-value">{{n-a @resource.vendor_name}}</div>
@@ -92,4 +96,4 @@
92
96
  >
93
97
  <MetadataViewer @metadata={{@resource.meta}} />
94
98
  </ContentPanel>
95
- </div>
99
+ </div>
@@ -93,6 +93,16 @@
93
93
  />
94
94
  </InputGroup>
95
95
 
96
+ <InputGroup @name={{t "driver.fields.license-expiry"}}>
97
+ <DatePicker
98
+ @value={{@resource.license_expiry}}
99
+ @onChange={{fn (mut @resource.license_expiry)}}
100
+ @placeholder={{t "driver.fields.license-expiry"}}
101
+ @disabled={{cannot-write @resource}}
102
+ class="w-full form-input"
103
+ />
104
+ </InputGroup>
105
+
96
106
  <InputGroup @name={{t "driver.fields.vendor"}}>
97
107
  <ModelSelect
98
108
  @modelName="vendor"
@@ -221,4 +231,4 @@
221
231
  <ContentPanel @title={{t "common.metadata"}} @open={{true}} @wrapperClass="bordered-top">
222
232
  <MetadataEditor @value={{@resource.meta}} @onChange={{fn (mut @resource.meta)}} />
223
233
  </ContentPanel>
224
- </div>
234
+ </div>
@@ -36,13 +36,13 @@
36
36
  @dropdownButtonIcon={{@itemDropdownButtonIcon}}
37
37
  @dropdownButtonIconPrefix={{@itemDropdownButtonIconPrefix}}
38
38
  @dropdownButtonText={{@itemDropdownButtonText}}
39
- @dropdownButtonCalculatePosition={{@itemDropdownButtonCalculatePosition}}
39
+ @dropdownButtonCalculatePosition={{this.calculateDropdownItemPosition}}
40
40
  @dropdownButtonMenuLabel={{vehicle.displayName}}
41
41
  @dropdownButtonWrapperClass={{@itemDropdownButtonWrapperClass}}
42
42
  @dropdownButtonTriggerClass={{@itemDropdownButtonTriggerClass}}
43
43
  @registerDropdownButtonAPI={{@itemRegisterDropdownButtonAPI}}
44
- @permission="fleet-ops view vehicle"
45
44
  @dropdownButtonRenderInPlace={{false}}
45
+ @permission="fleet-ops view vehicle"
46
46
  >{{vehicle.displayName}}</Layout::Sidebar::Item>
47
47
  {{/each}}
48
48
  </Layout::Sidebar::Panel>
@@ -31,4 +31,14 @@ export default class FleetListingPanelComponent extends Component {
31
31
  this.args.onVehicleClicked(vehicle);
32
32
  }
33
33
  }
34
+
35
+ @action calculateDropdownItemPosition(trigger) {
36
+ let { top, left, width } = trigger.getBoundingClientRect();
37
+ let style = {
38
+ left: 11 + left + width,
39
+ top: top + 2,
40
+ };
41
+
42
+ return { style };
43
+ }
34
44
  }
@@ -6,73 +6,7 @@ import { action } from '@ember/object';
6
6
  import { isArray } from '@ember/array';
7
7
  import { debug } from '@ember/debug';
8
8
  import { guidFor } from '@ember/object/internals';
9
-
10
- function buildDriverInfoWindowContent(driver) {
11
- return `
12
- <div class="fleetops-google-popover fleetops-google-popover--compact">
13
- <div class="fleetops-google-popover__row">
14
- <img src="${driver.photoUrl ?? ''}" alt="${driver.name ?? ''}" class="fleetops-google-popover__avatar" />
15
- <div class="fleetops-google-popover__body">
16
- <div class="fleetops-google-popover__title">${driver.name ?? '-'}</div>
17
- <div class="fleetops-google-popover__meta">Phone: ${driver.phone ?? '-'}</div>
18
- <div class="fleetops-google-popover__meta">Vehicle: ${driver.vehicle_name ?? '-'}</div>
19
- <div class="fleetops-google-popover__meta">Status:
20
- <span class="${driver.online ? 'text-green-500' : 'text-red-400'}">${driver.online ? 'Online' : 'Offline'}</span>
21
- </div>
22
- </div>
23
- </div>
24
- </div>`;
25
- }
26
-
27
- function buildDriverTooltipContent(driver) {
28
- return `
29
- <div class="fleetops-google-hover-tooltip__title">${driver.name ?? '-'}</div>
30
- <div class="fleetops-google-hover-tooltip__meta ${driver.online ? 'text-green-400' : 'text-red-400'}">${driver.online ? 'Online' : 'Offline'}</div>
31
- `;
32
- }
33
-
34
- function buildVehicleInfoWindowContent(vehicle) {
35
- return `
36
- <div class="fleetops-google-popover">
37
- <div class="fleetops-google-popover__row">
38
- <img src="${vehicle.photo_url ?? ''}" alt="${vehicle.display_name ?? ''}" class="fleetops-google-popover__vehicle" />
39
- <div class="fleetops-google-popover__body">
40
- <div class="fleetops-google-popover__title">${vehicle.displayName ?? '-'}</div>
41
- <div class="fleetops-google-popover__meta">ID: ${vehicle.public_id ?? '-'}</div>
42
- <div class="fleetops-google-popover__meta">Serial: ${vehicle.serial_number ?? vehicle.vin ?? '-'}</div>
43
- <div class="fleetops-google-popover__meta">Driver: ${vehicle.driver_name ?? '-'}</div>
44
- <div class="fleetops-google-popover__meta">Status:
45
- <span class="${vehicle.online ? 'text-green-500' : 'text-red-400'}">${vehicle.online ? 'Online' : 'Offline'}</span>
46
- </div>
47
- </div>
48
- </div>
49
- </div>`;
50
- }
51
-
52
- function buildVehicleTooltipContent(vehicle) {
53
- return `
54
- <div class="fleetops-google-hover-tooltip__title">${vehicle.displayName ?? '-'}</div>
55
- <div class="fleetops-google-hover-tooltip__meta">ID: ${vehicle.public_id ?? '-'}</div>
56
- <div class="fleetops-google-hover-tooltip__meta ${vehicle.online ? 'text-green-400' : 'text-red-400'}">${vehicle.online ? 'Online' : 'Offline'}</div>
57
- `;
58
- }
59
-
60
- function buildPlaceInfoWindowContent(place) {
61
- return `
62
- <div class="fleetops-google-popover fleetops-google-popover--compact">
63
- <div class="fleetops-google-popover__body">
64
- <div class="fleetops-google-popover__title">${place.name ?? place.address ?? '-'}</div>
65
- <div class="fleetops-google-popover__meta">${place.address ?? ''}</div>
66
- </div>
67
- </div>`;
68
- }
69
-
70
- function buildPlaceTooltipContent(place) {
71
- return `
72
- <div class="fleetops-google-hover-tooltip__title">${place.name ?? place.address ?? '-'}</div>
73
- <div class="fleetops-google-hover-tooltip__meta">${place.address ?? ''}</div>
74
- `;
75
- }
9
+ import { buildDriverLiveMapContent, buildPlaceInfoWindowContent, buildPlaceTooltipContent, buildVehicleLiveMapContent } from '../../utils/live-map-card-content';
76
10
 
77
11
  export default class MapGoogleLiveMapComponent extends Component {
78
12
  @service mapManager;
@@ -100,6 +34,7 @@ export default class MapGoogleLiveMapComponent extends Component {
100
34
 
101
35
  this.map = map;
102
36
  this.mapManager.on('moveend', () => this.args.onViewportChanged?.());
37
+ this.mapManager.on('click', () => this._closeAllInfoWindows());
103
38
  this.mapManager.on('rightclick', (event) => this._showMapContextMenu(event));
104
39
  this.args.onLoad?.({ target: map });
105
40
  this.syncResources();
@@ -125,12 +60,12 @@ export default class MapGoogleLiveMapComponent extends Component {
125
60
  if (!marker) {
126
61
  const createdMarker = await this.mapManager.addMarker(driver.id, coords.lat, coords.lng, {
127
62
  iconUrl: driver.vehicle_avatar ?? '/engines-dist/images/driver-marker.png',
128
- iconSize: [24, 24],
63
+ iconSize: [20, 20],
129
64
  title: driver.name,
130
- tooltip: buildDriverTooltipContent(driver),
65
+ tooltip: buildDriverLiveMapContent(driver),
131
66
  tooltipOptions: { html: true },
132
67
  rotationAngle: driver.heading,
133
- onClick: () => this.#openInfoWindow(`driver:${driver.id}`, buildDriverInfoWindowContent(driver), driver.id, () => this.args.onDriverClicked?.(driver)),
68
+ onClick: () => this.#openInfoWindow(`driver:${driver.id}`, buildDriverLiveMapContent(driver, true), driver.id, () => this.args.onDriverClicked?.(driver)),
134
69
  onRightClick: (event) => this._showDriverContextMenu(driver, event),
135
70
  });
136
71
 
@@ -155,12 +90,12 @@ export default class MapGoogleLiveMapComponent extends Component {
155
90
  if (!marker) {
156
91
  const createdMarker = await this.mapManager.addMarker(vehicle.id, coords.lat, coords.lng, {
157
92
  iconUrl: vehicle.avatar_url ?? '/engines-dist/images/vehicle-marker.png',
158
- iconSize: [24, 24],
93
+ iconSize: [20, 20],
159
94
  title: vehicle.displayName,
160
- tooltip: buildVehicleTooltipContent(vehicle),
95
+ tooltip: buildVehicleLiveMapContent(vehicle),
161
96
  tooltipOptions: { html: true },
162
97
  rotationAngle: vehicle.heading,
163
- onClick: () => this.#openInfoWindow(`vehicle:${vehicle.id}`, buildVehicleInfoWindowContent(vehicle), vehicle.id, () => this.args.onVehicleClicked?.(vehicle)),
98
+ onClick: () => this.#openInfoWindow(`vehicle:${vehicle.id}`, buildVehicleLiveMapContent(vehicle, true), vehicle.id, () => this.args.onVehicleClicked?.(vehicle)),
164
99
  onRightClick: (event) => this._showVehicleContextMenu(vehicle, event),
165
100
  });
166
101
 
@@ -308,6 +243,10 @@ export default class MapGoogleLiveMapComponent extends Component {
308
243
  if (!infoWindow) {
309
244
  infoWindow = new google.maps.InfoWindow({ content });
310
245
  this._infoWindows.set(infoId, infoWindow);
246
+
247
+ infoWindow.addListener('domready', () => {
248
+ document.querySelector('[data-fleetops-google-popover-close]')?.addEventListener('click', () => infoWindow.close(), { once: true });
249
+ });
311
250
  } else {
312
251
  infoWindow.setContent(content);
313
252
  }
@@ -337,15 +276,19 @@ export default class MapGoogleLiveMapComponent extends Component {
337
276
  }
338
277
 
339
278
  _showServiceAreaContextMenu(serviceArea, event) {
340
- const items = this.mapManager.getComposedContextMenuItems(`service-area:${serviceArea.public_id}`);
279
+ const items = this.mapManager.getComposedContextMenuItems(this._resourceContextKey('service-area', serviceArea));
341
280
  this.mapManager.showContextMenu(event, items);
342
281
  }
343
282
 
344
283
  _showZoneContextMenu(zone, event) {
345
- const items = this.mapManager.getComposedContextMenuItems(`zone:${zone.public_id}`);
284
+ const items = this.mapManager.getComposedContextMenuItems(this._resourceContextKey('zone', zone));
346
285
  this.mapManager.showContextMenu(event, items);
347
286
  }
348
287
 
288
+ _resourceContextKey(type, resource) {
289
+ return `${type}:${resource?.public_id ?? resource?.id}`;
290
+ }
291
+
349
292
  _closeAllInfoWindows() {
350
293
  this._infoWindows.forEach((iw) => iw.close());
351
294
  this.mapManager.closeContextMenu();
@@ -13,7 +13,7 @@
13
13
  through the provider-agnostic MapManagerService.
14
14
  }}
15
15
 
16
- <div class="live-map-component {{@wrapperClass}}">
16
+ <div class="live-map-component {{@wrapperClass}}" {{did-update this.syncServiceAreaContextMenus this.serviceAreaActions.serviceAreas this.serviceAreaActions.serviceAreas.length}}>
17
17
 
18
18
  {{#if this.shouldUseGoogleMaps}}
19
19
  {{! ─── Google Maps branch ──────────────────────────────────────────── }}
@@ -83,27 +83,108 @@
83
83
  as |marker|
84
84
  >
85
85
  <marker.popup @maxWidth="500" @minWidth="225">
86
- <div class="flex flex-row">
87
- <div class="w-14">
88
- <img src={{driver.photoUrl}} alt={{driver.name}} class="rounded-md w-12 h-12 shadow-sm" />
86
+ <div class="w-[280px] max-w-[calc(100vw-2rem)] rounded-lg bg-gray-900 shadow-lg p-1.5 text-white">
87
+ <div class="flex items-center gap-1.5 text-xs font-semibold mb-1.5">
88
+ <span class="inline-block w-2 h-2 rounded-full {{if driver.online 'bg-green-400' 'bg-red-500'}}"></span>
89
+ <span>{{driver.name}}</span>
90
+ <div
91
+ class="status-badge
92
+ {{if driver.status (dasherize driver.status) 'default'}}-status-badge status-badge-{{if driver.status (dasherize driver.status) 'default'}}
93
+ shadow-none ml-auto"
94
+ >
95
+ <span class="rounded-full text-[9px] px-1.5 py-0.5">{{n-a driver.meta.status_label (smart-humanize driver.status)}}</span>
96
+ </div>
89
97
  </div>
90
- <div class="flex-1">
91
- <div class="text-xs font-semibold">{{driver.name}}</div>
92
- <div class="text-xs">{{t "common.phone"}}: {{n-a driver.phone}}</div>
93
- <div class="text-xs">{{t "resource.vehicle"}}: {{n-a driver.vehicle_name}}</div>
94
- <div class="text-xs">{{t "common.status"}}:
95
- <span class="{{if driver.online 'text-green-500' 'text-red-400'}}">{{if driver.online "Online" "Offline"}}</span></div>
96
- <div class="text-xs truncate">{{t "common.pos"}}: {{point-coordinates driver.location}}</div>
98
+ <div class="grid grid-cols-2 gap-1">
99
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
100
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">ID</div>
101
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.public_id driver.id}}</div>
102
+ </div>
103
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
104
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Phone</div>
105
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.phone}}</div>
106
+ </div>
107
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
108
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Vehicle</div>
109
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.vehicle_name}}</div>
110
+ </div>
111
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
112
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Email</div>
113
+ <div class="text-[11px] text-white leading-tight truncate min-w-0 w-full">{{n-a driver.email}}</div>
114
+ </div>
115
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
116
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Order</div>
117
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.meta.current_order_reference}}</div>
118
+ </div>
119
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
120
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Speed</div>
121
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.meta.speed_label}}</div>
122
+ </div>
123
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
124
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Heading</div>
125
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.meta.heading_label}}</div>
126
+ </div>
127
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
128
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Location</div>
129
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a
130
+ driver.meta.location_coordinates
131
+ (point-coordinates driver.location)
132
+ }}</div>
133
+ </div>
97
134
  </div>
98
135
  </div>
99
136
  </marker.popup>
100
137
  <marker.tooltip @permanent={{false}} @sticky={{true}}>
101
- <div class="flex items-center space-x-1">
102
- <div class="text-xs font-semibold">{{driver.name}}</div>
103
- <div>•</div>
104
- <div class="text-xs {{if driver.online 'text-green-500' 'text-red-400'}}">{{if driver.online "Online" "Offline"}}</div>
138
+ <div class="w-[280px] max-w-[calc(100vw-2rem)]">
139
+ <div class="flex items-center gap-1.5 text-xs font-semibold mb-1.5">
140
+ <span class="inline-block w-2 h-2 rounded-full {{if driver.online 'bg-green-400' 'bg-red-500'}}"></span>
141
+ <span>{{driver.name}}</span>
142
+ <div
143
+ class="status-badge
144
+ {{if driver.status (dasherize driver.status) 'default'}}-status-badge status-badge-{{if driver.status (dasherize driver.status) 'default'}}
145
+ shadow-none ml-auto"
146
+ >
147
+ <span class="rounded-full text-[9px] px-1.5 py-0.5">{{n-a driver.meta.status_label (smart-humanize driver.status)}}</span>
148
+ </div>
149
+ </div>
150
+ <div class="grid grid-cols-2 gap-1">
151
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
152
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">ID</div>
153
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.public_id driver.id}}</div>
154
+ </div>
155
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
156
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Phone</div>
157
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.phone}}</div>
158
+ </div>
159
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
160
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Vehicle</div>
161
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.vehicle_name}}</div>
162
+ </div>
163
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
164
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Email</div>
165
+ <div class="text-[11px] text-white leading-tight truncate min-w-0 w-full">{{n-a driver.email}}</div>
166
+ </div>
167
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
168
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Order</div>
169
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.meta.current_order_reference}}</div>
170
+ </div>
171
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
172
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Speed</div>
173
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.meta.speed_label}}</div>
174
+ </div>
175
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
176
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Heading</div>
177
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a driver.meta.heading_label}}</div>
178
+ </div>
179
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
180
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Location</div>
181
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a
182
+ driver.meta.location_coordinates
183
+ (point-coordinates driver.location)
184
+ }}</div>
185
+ </div>
186
+ </div>
105
187
  </div>
106
- <div class="text-xs truncate"><FaIcon @icon="location-dot" @size="xs" class="mr-0.5" />{{point-coordinates driver.location}}</div>
107
188
  </marker.tooltip>
108
189
  </layers.tracking-marker>
109
190
  {{/each}}
@@ -121,29 +202,98 @@
121
202
  as |marker|
122
203
  >
123
204
  <marker.popup @permanent={{false}} @sticky={{true}}>
124
- <div class="flex flex-row">
125
- <div class="w-14 mr-2">
126
- <img src={{vehicle.photo_url}} alt={{vehicle.display_name}} class="rounded-md w-14 h-12 shadow-sm" />
205
+ <div class="w-[280px] max-w-[calc(100vw-2rem)] rounded-lg bg-gray-900 shadow-lg p-1.5 text-white">
206
+ <div class="flex items-center gap-1.5 text-xs font-semibold mb-1.5">
207
+ <span class="inline-block w-2 h-2 rounded-full {{if vehicle.online 'bg-green-400' 'bg-red-500'}}"></span>
208
+ <span>{{vehicle.displayName}}</span>
209
+ <div
210
+ class="status-badge
211
+ {{if vehicle.status (dasherize vehicle.status) 'default'}}-status-badge status-badge-{{if vehicle.status (dasherize vehicle.status) 'default'}}
212
+ shadow-none ml-auto"
213
+ >
214
+ <span class="rounded-full text-[9px] px-1.5 py-0.5">{{n-a vehicle.meta.status_label}}</span>
215
+ </div>
127
216
  </div>
128
- <div class="flex-1">
129
- <div class="text-xs font-semibold">{{vehicle.displayName}}</div>
130
- <div class="text-xs">ID: {{n-a vehicle.public_id}}</div>
131
- <div class="text-xs">Serial No: {{n-a vehicle.serial_number vehicle.vin vehicle.internal_id vehicle.id}}</div>
132
- <div class="text-xs">Driver: {{n-a vehicle.driver_name}}</div>
133
- <div class="text-xs">Status: <span class="{{if vehicle.online 'text-green-500' 'text-red-400'}}">{{if vehicle.online "Online" "Offline"}}</span></div>
134
- <div class="text-xs truncate">Pos: {{point-coordinates vehicle.location}}</div>
217
+ <div class="grid grid-cols-2 gap-1">
218
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
219
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Vehicle #</div>
220
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a
221
+ vehicle.internal_id
222
+ vehicle.public_id
223
+ vehicle.plate_number
224
+ vehicle.serial_number
225
+ vehicle.vin
226
+ }}</div>
227
+ </div>
228
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
229
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Driver</div>
230
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.driver_name}}</div>
231
+ </div>
232
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
233
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Order</div>
234
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.meta.current_order_reference}}</div>
235
+ </div>
236
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
237
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Speed</div>
238
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.meta.speed_label}}</div>
239
+ </div>
240
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
241
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Heading</div>
242
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.meta.heading_label}}</div>
243
+ </div>
244
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
245
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Location</div>
246
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.meta.location_coordinates}}</div>
247
+ </div>
135
248
  </div>
136
249
  </div>
137
250
  </marker.popup>
138
251
  <marker.tooltip @permanent={{false}} @sticky={{true}}>
139
- <div class="flex items-center space-x-1">
140
- <div class="text-xs font-semibold">{{vehicle.displayName}}</div>
141
- <div>•</div>
142
- <div class="text-xs {{if vehicle.online 'text-green-500' 'text-red-400'}}">{{if vehicle.online "Online" "Offline"}}</div>
252
+ <div class="w-[280px] max-w-[calc(100vw-2rem)]">
253
+ <div class="flex items-center gap-1.5 text-xs font-semibold mb-1.5">
254
+ <span class="inline-block w-2 h-2 rounded-full {{if vehicle.online 'bg-green-400' 'bg-red-500'}}"></span>
255
+ <span>{{vehicle.displayName}}</span>
256
+ <div
257
+ class="status-badge
258
+ {{if vehicle.status (dasherize vehicle.status) 'default'}}-status-badge status-badge-{{if vehicle.status (dasherize vehicle.status) 'default'}}
259
+ shadow-none ml-auto"
260
+ >
261
+ <span class="rounded-full text-[9px] px-1.5 py-0.5">{{n-a vehicle.meta.status_label}}</span>
262
+ </div>
263
+ </div>
264
+ <div class="grid grid-cols-2 gap-1">
265
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
266
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Vehicle #</div>
267
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a
268
+ vehicle.internal_id
269
+ vehicle.public_id
270
+ vehicle.plate_number
271
+ vehicle.serial_number
272
+ vehicle.vin
273
+ }}</div>
274
+ </div>
275
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
276
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Driver</div>
277
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.driver_name}}</div>
278
+ </div>
279
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
280
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Order</div>
281
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.meta.current_order_reference}}</div>
282
+ </div>
283
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
284
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Speed</div>
285
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.meta.speed_label}}</div>
286
+ </div>
287
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
288
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Heading</div>
289
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.meta.heading_label}}</div>
290
+ </div>
291
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
292
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">Location</div>
293
+ <div class="text-[11px] text-white leading-tight whitespace-normal break-words">{{n-a vehicle.meta.location_coordinates}}</div>
294
+ </div>
295
+ </div>
143
296
  </div>
144
- <div class="text-xs">ID: {{n-a vehicle.public_id}}</div>
145
- <div class="text-xs">Serial No: {{or vehicle.serial_number vehicle.vin vehicle.internal_id vehicle.id "-"}}</div>
146
- <div class="text-xs truncate"><FaIcon @icon="location-dot" @size="xs" class="mr-0.5" />{{point-coordinates vehicle.location}}</div>
147
297
  </marker.tooltip>
148
298
  </layers.tracking-marker>
149
299
  {{/each}}
@@ -175,8 +325,8 @@
175
325
  @id={{serviceArea.id}}
176
326
  @record={{serviceArea}}
177
327
  @locations={{unwrap-coordinates serviceArea.leafletCoordinates}}
178
- @fillColor={{serviceArea.color}}
179
- @color={{serviceArea.stroke_color}}
328
+ @fillColor={{or serviceArea.color "#3388ff"}}
329
+ @color={{or serviceArea.stroke_color serviceArea.color "#3388ff"}}
180
330
  @onAdd={{fn this.trigger "onServiceAreaLayerAdded" serviceArea}}
181
331
  as |polygon|
182
332
  >
@@ -188,8 +338,8 @@
188
338
  @id={{zone.id}}
189
339
  @record={{zone}}
190
340
  @locations={{unwrap-coordinates zone.leafletCoordinates}}
191
- @fillColor={{zone.color}}
192
- @color={{zone.stroke_color}}
341
+ @fillColor={{or zone.color "#3388ff"}}
342
+ @color={{or zone.stroke_color zone.color "#3388ff"}}
193
343
  @onAdd={{fn this.trigger "onZoneLayerAdd" zone}}
194
344
  as |polygon|
195
345
  >