@fleetbase/fleetops-engine 0.6.47 → 0.6.49

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 (223) hide show
  1. package/addon/components/admin/routing-settings.hbs +71 -0
  2. package/addon/components/admin/routing-settings.js +124 -0
  3. package/addon/components/customer/orders.js +1 -1
  4. package/addon/components/display-place.hbs +1 -1
  5. package/addon/components/driver/details.hbs +5 -1
  6. package/addon/components/driver/form.hbs +11 -1
  7. package/addon/components/fleet-listing-panel.hbs +2 -2
  8. package/addon/components/fleet-listing-panel.js +10 -0
  9. package/addon/components/map/google-live-map.js +12 -73
  10. package/addon/components/map/leaflet-live-map.hbs +149 -33
  11. package/addon/components/map/order-list-overlay/order.hbs +18 -3
  12. package/addon/components/map/toolbar.hbs +2 -2
  13. package/addon/components/map/toolbar.js +10 -2
  14. package/addon/components/orchestrator/phase-builder.hbs +49 -5
  15. package/addon/components/orchestrator/phase-builder.js +34 -1
  16. package/addon/components/orchestrator/resource-panel.hbs +19 -1
  17. package/addon/components/orchestrator/resource-panel.js +4 -0
  18. package/addon/components/orchestrator-workbench.js +4 -1
  19. package/addon/components/order/details/purchase-rate.hbs +19 -22
  20. package/addon/components/order/details/route.hbs +26 -1
  21. package/addon/components/order/details/route.js +50 -0
  22. package/addon/components/order/details/tracking.hbs +171 -27
  23. package/addon/components/order/details/tracking.js +457 -0
  24. package/addon/components/order/form.hbs +2 -2
  25. package/addon/components/order/kanban-card.hbs +18 -3
  26. package/addon/components/order-list-overlay/order.hbs +18 -3
  27. package/addon/components/order-progress-card.hbs +21 -6
  28. package/addon/components/order-tracking-lookup.hbs +7 -7
  29. package/addon/components/order-tracking-lookup.js +1 -1
  30. package/addon/components/route-list.hbs +78 -8
  31. package/addon/components/route-list.js +48 -6
  32. package/addon/components/tracking-stop-progress.hbs +45 -0
  33. package/addon/components/tracking-stop-progress.js +79 -0
  34. package/addon/components/widget/fleet-ops-key-metrics.hbs +19 -17
  35. package/addon/components/widget/fleet-ops-key-metrics.js +26 -0
  36. package/addon/components/widget/fuel-efficiency.hbs +26 -0
  37. package/addon/components/widget/fuel-efficiency.js +43 -0
  38. package/addon/components/widget/geofence-violations.hbs +44 -0
  39. package/addon/components/widget/geofence-violations.js +55 -0
  40. package/addon/components/widget/issues-insights.hbs +43 -0
  41. package/addon/components/widget/issues-insights.js +62 -0
  42. package/addon/components/widget/kpi-active-orders.hbs +10 -0
  43. package/addon/components/widget/kpi-active-orders.js +5 -0
  44. package/addon/components/widget/kpi-aov.hbs +10 -0
  45. package/addon/components/widget/kpi-aov.js +5 -0
  46. package/addon/components/widget/kpi-distance.hbs +10 -0
  47. package/addon/components/widget/kpi-distance.js +5 -0
  48. package/addon/components/widget/kpi-drivers-online.hbs +10 -0
  49. package/addon/components/widget/kpi-drivers-online.js +5 -0
  50. package/addon/components/widget/kpi-earnings.hbs +10 -0
  51. package/addon/components/widget/kpi-earnings.js +5 -0
  52. package/addon/components/widget/kpi-open-issues.hbs +11 -0
  53. package/addon/components/widget/kpi-open-issues.js +5 -0
  54. package/addon/components/widget/kpi-tile.hbs +59 -0
  55. package/addon/components/widget/kpi-tile.js +168 -0
  56. package/addon/components/widget/live-fleet.hbs +122 -0
  57. package/addon/components/widget/live-fleet.js +365 -0
  58. package/addon/components/widget/maintenance-overview.hbs +45 -0
  59. package/addon/components/widget/maintenance-overview.js +40 -0
  60. package/addon/components/widget/on-time-delivery.hbs +27 -0
  61. package/addon/components/widget/on-time-delivery.js +73 -0
  62. package/addon/components/widget/operations-pulse.hbs +51 -0
  63. package/addon/components/widget/operations-pulse.js +173 -0
  64. package/addon/components/widget/orders-by-status.hbs +26 -0
  65. package/addon/components/widget/orders-by-status.js +54 -0
  66. package/addon/components/widget/revenue-trend.hbs +34 -0
  67. package/addon/components/widget/revenue-trend.js +67 -0
  68. package/addon/components/widget/top-drivers.hbs +44 -0
  69. package/addon/components/widget/top-drivers.js +54 -0
  70. package/addon/controllers/settings/routing.js +124 -1
  71. package/addon/extension.js +172 -3
  72. package/addon/helpers/format-duration.js +38 -0
  73. package/addon/routes/application.js +6 -0
  74. package/addon/services/map-adapter/google.js +13 -2
  75. package/addon/services/order-list-overlay.js +2 -2
  76. package/addon/styles/fleetops-engine.css +806 -46
  77. package/addon/templates/maintenance/equipment/index.hbs +1 -1
  78. package/addon/templates/maintenance/parts/index.hbs +1 -1
  79. package/addon/templates/maintenance/schedules/index.hbs +1 -1
  80. package/addon/templates/management/vehicles/index.hbs +1 -1
  81. package/addon/templates/settings/avatars.hbs +1 -1
  82. package/addon/templates/settings/custom-fields.hbs +1 -1
  83. package/addon/templates/settings/map.hbs +1 -1
  84. package/addon/templates/settings/navigator-app.hbs +1 -1
  85. package/addon/templates/settings/notifications.hbs +1 -1
  86. package/addon/templates/settings/orchestrator.hbs +1 -1
  87. package/addon/templates/settings/payments/index.hbs +1 -1
  88. package/addon/templates/settings/payments/onboard.hbs +1 -1
  89. package/addon/templates/settings/routing.hbs +82 -4
  90. package/addon/templates/settings/scheduling.hbs +1 -1
  91. package/addon/utils/live-map-card-content.js +171 -0
  92. package/app/components/tracking-stop-progress.js +1 -0
  93. package/app/components/widget/fuel-efficiency.js +1 -0
  94. package/app/components/widget/geofence-violations.js +1 -0
  95. package/app/components/widget/issues-insights.js +1 -0
  96. package/app/components/widget/kpi-active-orders.js +1 -0
  97. package/app/components/widget/kpi-aov.js +1 -0
  98. package/app/components/widget/kpi-distance.js +1 -0
  99. package/app/components/widget/kpi-drivers-online.js +1 -0
  100. package/app/components/widget/kpi-earnings.js +1 -0
  101. package/app/components/widget/kpi-open-issues.js +1 -0
  102. package/app/components/widget/kpi-tile.js +1 -0
  103. package/app/components/widget/live-fleet.js +1 -0
  104. package/app/components/widget/maintenance-overview.js +1 -0
  105. package/app/components/widget/on-time-delivery.js +1 -0
  106. package/app/components/widget/operations-pulse.js +1 -0
  107. package/app/components/widget/orders-by-status.js +1 -0
  108. package/app/components/widget/revenue-trend.js +1 -0
  109. package/app/components/widget/top-drivers.js +1 -0
  110. package/app/helpers/format-duration.js +1 -0
  111. package/app/utils/live-map-card-content.js +1 -0
  112. package/composer.json +1 -1
  113. package/extension.json +1 -1
  114. package/package.json +3 -3
  115. package/pnpm-workspace.yaml +1 -0
  116. package/server/config/fleetops.php +32 -11
  117. package/server/config/telematics.php +111 -1
  118. package/server/migrations/2026_05_13_000001_repair_customer_contact_user_types.php +86 -0
  119. package/server/migrations/2026_05_18_000001_add_license_expiry_to_drivers_table.php +25 -0
  120. package/server/seeders/Testing/Concerns/SeedsTestingData.php +183 -0
  121. package/server/seeders/Testing/ConnectivitySeeder.php +212 -0
  122. package/server/seeders/Testing/FleetSeeder.php +341 -0
  123. package/server/seeders/Testing/MaintenanceSeeder.php +238 -0
  124. package/server/seeders/Testing/NetworkSeeder.php +250 -0
  125. package/server/seeders/Testing/OrdersSeeder.php +214 -0
  126. package/server/seeders/Testing/TestingSeeder.php +40 -0
  127. package/server/seeders/Testing/VroomOrchestrationSeeder.php +409 -0
  128. package/server/src/Console/Commands/ProcessOperationalAlerts.php +279 -0
  129. package/server/src/Http/Controllers/Api/v1/OrchestrationController.php +64 -0
  130. package/server/src/Http/Controllers/Api/v1/OrderController.php +26 -26
  131. package/server/src/Http/Controllers/Api/v1/PayloadController.php +10 -10
  132. package/server/src/Http/Controllers/Api/v1/ServiceQuoteController.php +1 -1
  133. package/server/src/Http/Controllers/Internal/v1/AnalyticsController.php +119 -0
  134. package/server/src/Http/Controllers/Internal/v1/ContactController.php +41 -0
  135. package/server/src/Http/Controllers/Internal/v1/LiveController.php +6 -42
  136. package/server/src/Http/Controllers/Internal/v1/MetricsController.php +82 -3
  137. package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +1 -1
  138. package/server/src/Http/Controllers/Internal/v1/OrderController.php +52 -21
  139. package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +1 -1
  140. package/server/src/Http/Controllers/Internal/v1/SettingController.php +164 -12
  141. package/server/src/Http/Resources/v1/Driver.php +2 -0
  142. package/server/src/Http/Resources/v1/Fleet.php +4 -0
  143. package/server/src/Http/Resources/v1/Index/Driver.php +48 -0
  144. package/server/src/Http/Resources/v1/Index/Payload.php +2 -2
  145. package/server/src/Http/Resources/v1/Index/Vehicle.php +74 -1
  146. package/server/src/Models/Contact.php +122 -13
  147. package/server/src/Models/Driver.php +2 -0
  148. package/server/src/Models/Order.php +1 -1
  149. package/server/src/Models/OrderConfig.php +14 -11
  150. package/server/src/Models/Payload.php +8 -8
  151. package/server/src/Models/Place.php +0 -3
  152. package/server/src/Models/ServiceRate.php +1 -1
  153. package/server/src/Notifications/LateDeparture.php +48 -0
  154. package/server/src/Notifications/ProlongedStoppage.php +48 -0
  155. package/server/src/Notifications/RouteDeviation.php +48 -0
  156. package/server/src/Observers/ContactObserver.php +8 -0
  157. package/server/src/Orchestration/Engines/CapacityAllocationEngine.php +220 -0
  158. package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +357 -38
  159. package/server/src/Orchestration/Support/OrchestrationPayloadBuilder.php +259 -51
  160. package/server/src/Providers/FleetOpsServiceProvider.php +32 -0
  161. package/server/src/Providers/NotificationServiceProvider.php +3 -0
  162. package/server/src/Support/Analytics/AbstractAnalytics.php +77 -0
  163. package/server/src/Support/Analytics/FuelEfficiency.php +83 -0
  164. package/server/src/Support/Analytics/GeofenceViolations.php +63 -0
  165. package/server/src/Support/Analytics/IssuesInsights.php +67 -0
  166. package/server/src/Support/Analytics/LiveFleet.php +127 -0
  167. package/server/src/Support/Analytics/MaintenanceOverview.php +70 -0
  168. package/server/src/Support/Analytics/OnTimeDelivery.php +75 -0
  169. package/server/src/Support/Analytics/OperationsPulse.php +86 -0
  170. package/server/src/Support/Analytics/OrdersByStatus.php +64 -0
  171. package/server/src/Support/Analytics/RevenueTrend.php +79 -0
  172. package/server/src/Support/Analytics/TopDrivers.php +92 -0
  173. package/server/src/Support/LiveOrderQuery.php +75 -0
  174. package/server/src/Support/Metrics/AbstractMetric.php +170 -0
  175. package/server/src/Support/Metrics/ActiveLiveOrdersMetric.php +28 -0
  176. package/server/src/Support/Metrics/AvgOrderValueMetric.php +53 -0
  177. package/server/src/Support/Metrics/DriversOnlineMetric.php +30 -0
  178. package/server/src/Support/Metrics/EarningsMetric.php +30 -0
  179. package/server/src/Support/Metrics/FuelCostsMetric.php +30 -0
  180. package/server/src/Support/Metrics/MoneyMetric.php +30 -0
  181. package/server/src/Support/Metrics/OpenIssuesMetric.php +35 -0
  182. package/server/src/Support/Metrics/OrdersCanceledMetric.php +35 -0
  183. package/server/src/Support/Metrics/OrdersCompletedMetric.php +35 -0
  184. package/server/src/Support/Metrics/OrdersInProgressMetric.php +51 -0
  185. package/server/src/Support/Metrics/OrdersScheduledMetric.php +37 -0
  186. package/server/src/Support/Metrics/Registry.php +46 -0
  187. package/server/src/Support/Metrics/ResolvedIssuesMetric.php +35 -0
  188. package/server/src/Support/Metrics/TotalCustomersMetric.php +29 -0
  189. package/server/src/Support/Metrics/TotalDistanceTraveledMetric.php +35 -0
  190. package/server/src/Support/Metrics/TotalDriversMetric.php +28 -0
  191. package/server/src/Support/Metrics/TotalTimeTraveledMetric.php +35 -0
  192. package/server/src/Support/Metrics.php +46 -234
  193. package/server/src/Support/OSRM.php +11 -6
  194. package/server/src/Support/OrderTracker.php +7 -582
  195. package/server/src/Support/Telematics/Providers/AfaqyProvider.php +290 -0
  196. package/server/src/Support/Telematics/Providers/SafeeProvider.php +333 -0
  197. package/server/src/Support/Utils.php +1 -1
  198. package/server/src/Tracking/Contracts/TrackingProviderInterface.php +19 -0
  199. package/server/src/Tracking/Providers/CalculatedTrackingProvider.php +65 -0
  200. package/server/src/Tracking/Providers/GoogleRoutesTrackingProvider.php +118 -0
  201. package/server/src/Tracking/Providers/OsrmTrackingProvider.php +71 -0
  202. package/server/src/Tracking/README.md +25 -0
  203. package/server/src/Tracking/Support/FakeTrackingProvider.php +42 -0
  204. package/server/src/Tracking/TrackingContext.php +63 -0
  205. package/server/src/Tracking/TrackingContextBuilder.php +204 -0
  206. package/server/src/Tracking/TrackingIntelligenceService.php +180 -0
  207. package/server/src/Tracking/TrackingOptions.php +49 -0
  208. package/server/src/Tracking/TrackingProviderCapabilities.php +30 -0
  209. package/server/src/Tracking/TrackingProviderManager.php +89 -0
  210. package/server/src/Tracking/TrackingProviderRegistry.php +35 -0
  211. package/server/src/Tracking/TrackingProviderResult.php +20 -0
  212. package/server/src/Tracking/TrackingStop.php +59 -0
  213. package/server/src/routes.php +27 -0
  214. package/server/tests/AnalyticsRoutesTest.php +41 -0
  215. package/server/tests/CapacityAllocationEngineTest.php +108 -0
  216. package/server/tests/LiveOrderQueryTest.php +34 -0
  217. package/server/tests/MetricsRegistryTest.php +88 -0
  218. package/server/tests/OrchestrationPayloadBuilderTest.php +132 -0
  219. package/server/tests/OrchestratorConsumableApiTest.php +74 -0
  220. package/server/tests/PingDriverEndpointTest.php +19 -0
  221. package/server/tests/TrackingIntelligenceTest.php +176 -0
  222. package/server/tests/VroomOrchestrationEngineTest.php +250 -0
  223. package/translations/en-us.yaml +41 -0
@@ -128,13 +128,182 @@ export default {
128
128
 
129
129
  registerWidgets(widgetService) {
130
130
  const widgets = [
131
+ // Legacy monolithic 13-tile widget — kept registered for one release as
132
+ // users have it pinned to existing dashboards. The new KPI tile widgets
133
+ // below supersede it. Scheduled for removal in the next major.
131
134
  new Widget({
132
135
  id: 'fleet-ops-key-metrics-widget',
133
- name: 'Fleet-Ops Metrics',
134
- description: 'Key metrics from Fleet-Ops.',
136
+ name: 'Fleet-Ops Metrics (Legacy)',
137
+ description: 'DEPRECATED — replaced by individual KPI tile widgets. Will be removed in the next major release.',
135
138
  icon: 'truck',
136
139
  component: new ExtensionComponent('@fleetbase/fleetops-engine', 'widget/fleet-ops-key-metrics'),
137
- grid_options: { w: 12, h: 12, minW: 8, minH: 12 },
140
+ grid_options: { w: 12, h: 8, minW: 8, minH: 6 },
141
+ category: 'Legacy',
142
+ default: false,
143
+ }),
144
+
145
+ // Small KPI tile widgets — S-tier, each wraps <Widget::KpiTile> with a
146
+ // fixed slug pointing at GET /fleet-ops/metrics/{slug}.
147
+ new Widget({
148
+ id: 'fleet-ops-kpi-earnings-widget',
149
+ name: 'Earnings (30d)',
150
+ description: 'Total earnings over the last 30 days with trend.',
151
+ icon: 'sack-dollar',
152
+ component: new ExtensionComponent('@fleetbase/fleetops-engine', 'widget/kpi-earnings'),
153
+ grid_options: { w: 3, h: 4, minW: 3, minH: 4 },
154
+ category: 'KPI Tiles',
155
+ default: true,
156
+ }),
157
+ new Widget({
158
+ id: 'fleet-ops-kpi-aov-widget',
159
+ name: 'Avg Order Value (30d)',
160
+ description: 'Average revenue per completed order with trend.',
161
+ icon: 'receipt',
162
+ component: new ExtensionComponent('@fleetbase/fleetops-engine', 'widget/kpi-aov'),
163
+ grid_options: { w: 3, h: 4, minW: 3, minH: 4 },
164
+ category: 'KPI Tiles',
165
+ default: true,
166
+ }),
167
+ new Widget({
168
+ id: 'fleet-ops-kpi-distance-widget',
169
+ name: 'Distance Travelled (30d)',
170
+ description: 'Total kilometres delivered over the last 30 days.',
171
+ icon: 'route',
172
+ component: new ExtensionComponent('@fleetbase/fleetops-engine', 'widget/kpi-distance'),
173
+ grid_options: { w: 3, h: 4, minW: 3, minH: 4 },
174
+ category: 'KPI Tiles',
175
+ default: false,
176
+ }),
177
+ new Widget({
178
+ id: 'fleet-ops-kpi-active-orders-widget',
179
+ name: 'Active Orders',
180
+ description: 'Live count of orders currently in flight.',
181
+ icon: 'bolt',
182
+ component: new ExtensionComponent('@fleetbase/fleetops-engine', 'widget/kpi-active-orders'),
183
+ grid_options: { w: 3, h: 4, minW: 3, minH: 4 },
184
+ category: 'KPI Tiles',
185
+ default: true,
186
+ }),
187
+ new Widget({
188
+ id: 'fleet-ops-kpi-drivers-online-widget',
189
+ name: 'Drivers Online',
190
+ description: 'Live count of drivers currently active on a job.',
191
+ icon: 'id-card',
192
+ component: new ExtensionComponent('@fleetbase/fleetops-engine', 'widget/kpi-drivers-online'),
193
+ grid_options: { w: 3, h: 4, minW: 3, minH: 4 },
194
+ category: 'KPI Tiles',
195
+ default: true,
196
+ }),
197
+ new Widget({
198
+ id: 'fleet-ops-kpi-open-issues-widget',
199
+ name: 'Open Issues',
200
+ description: 'Pending issues across the fleet (lower is better).',
201
+ icon: 'triangle-exclamation',
202
+ component: new ExtensionComponent('@fleetbase/fleetops-engine', 'widget/kpi-open-issues'),
203
+ grid_options: { w: 3, h: 4, minW: 3, minH: 4 },
204
+ category: 'KPI Tiles',
205
+ default: false,
206
+ }),
207
+
208
+ // Analytics widgets — composed visualizations powered by /fleet-ops/analytics/*.
209
+ new Widget({
210
+ id: 'fleet-ops-operations-pulse-widget',
211
+ name: 'Operations Pulse',
212
+ description: 'Live operational snapshot with day-over-day deltas.',
213
+ icon: 'wave-pulse',
214
+ component: new ExtensionComponent('@fleetbase/fleetops-engine', 'widget/operations-pulse'),
215
+ grid_options: { w: 6, h: 6, minW: 5, minH: 5 },
216
+ category: 'Analytics',
217
+ default: false,
218
+ }),
219
+ new Widget({
220
+ id: 'fleet-ops-revenue-trend-widget',
221
+ name: 'Revenue Trend',
222
+ description: 'Revenue over time with period comparison.',
223
+ icon: 'chart-line',
224
+ component: new ExtensionComponent('@fleetbase/fleetops-engine', 'widget/revenue-trend'),
225
+ grid_options: { w: 6, h: 6, minW: 5, minH: 5 },
226
+ category: 'Analytics',
227
+ default: true,
228
+ }),
229
+ new Widget({
230
+ id: 'fleet-ops-orders-by-status-widget',
231
+ name: 'Order Volume by Status',
232
+ description: 'Daily stacked bars of order counts by status.',
233
+ icon: 'chart-column',
234
+ component: new ExtensionComponent('@fleetbase/fleetops-engine', 'widget/orders-by-status'),
235
+ grid_options: { w: 6, h: 6, minW: 5, minH: 5 },
236
+ category: 'Analytics',
237
+ default: false,
238
+ }),
239
+ new Widget({
240
+ id: 'fleet-ops-on-time-delivery-widget',
241
+ name: 'On-Time Delivery',
242
+ description: 'Percentage of deliveries completed within the SLA window.',
243
+ icon: 'clock',
244
+ component: new ExtensionComponent('@fleetbase/fleetops-engine', 'widget/on-time-delivery'),
245
+ grid_options: { w: 3, h: 4, minW: 3, minH: 4 },
246
+ category: 'Analytics',
247
+ default: false,
248
+ }),
249
+ new Widget({
250
+ id: 'fleet-ops-top-drivers-widget',
251
+ name: 'Top Drivers',
252
+ description: 'Driver leaderboard sortable by orders, on-time %, or distance.',
253
+ icon: 'medal',
254
+ component: new ExtensionComponent('@fleetbase/fleetops-engine', 'widget/top-drivers'),
255
+ grid_options: { w: 6, h: 6, minW: 5, minH: 5 },
256
+ category: 'Analytics',
257
+ default: false,
258
+ }),
259
+ new Widget({
260
+ id: 'fleet-ops-fuel-efficiency-widget',
261
+ name: 'Fuel Cost & Efficiency',
262
+ description: 'Weekly fuel cost and cost-per-km trend.',
263
+ icon: 'gas-pump',
264
+ component: new ExtensionComponent('@fleetbase/fleetops-engine', 'widget/fuel-efficiency'),
265
+ grid_options: { w: 6, h: 6, minW: 5, minH: 5 },
266
+ category: 'Analytics',
267
+ default: false,
268
+ }),
269
+ new Widget({
270
+ id: 'fleet-ops-issues-insights-widget',
271
+ name: 'Issues Insights',
272
+ description: 'Open/resolved issues, category breakdown, and average resolution time.',
273
+ icon: 'triangle-exclamation',
274
+ component: new ExtensionComponent('@fleetbase/fleetops-engine', 'widget/issues-insights'),
275
+ grid_options: { w: 6, h: 6, minW: 5, minH: 5 },
276
+ category: 'Analytics',
277
+ default: false,
278
+ }),
279
+ new Widget({
280
+ id: 'fleet-ops-maintenance-overview-widget',
281
+ name: 'Maintenance Overview',
282
+ description: 'Overdue, scheduled, and YTD maintenance spend.',
283
+ icon: 'wrench',
284
+ component: new ExtensionComponent('@fleetbase/fleetops-engine', 'widget/maintenance-overview'),
285
+ grid_options: { w: 6, h: 6, minW: 5, minH: 5 },
286
+ category: 'Analytics',
287
+ default: true,
288
+ }),
289
+ new Widget({
290
+ id: 'fleet-ops-geofence-violations-widget',
291
+ name: 'Geofence Violations',
292
+ description: 'Dwell-time outliers and geofence event hotspots.',
293
+ icon: 'location-crosshairs',
294
+ component: new ExtensionComponent('@fleetbase/fleetops-engine', 'widget/geofence-violations'),
295
+ grid_options: { w: 6, h: 6, minW: 5, minH: 5 },
296
+ category: 'Analytics',
297
+ default: false,
298
+ }),
299
+ new Widget({
300
+ id: 'fleet-ops-live-fleet-widget',
301
+ name: 'Live Fleet Map',
302
+ description: 'Real-time driver positions and active routes.',
303
+ icon: 'map-location-dot',
304
+ component: new ExtensionComponent('@fleetbase/fleetops-engine', 'widget/live-fleet'),
305
+ grid_options: { w: 12, h: 10, minW: 8, minH: 8 },
306
+ category: 'Maps',
138
307
  default: true,
139
308
  }),
140
309
  ];
@@ -0,0 +1,38 @@
1
+ import { helper } from '@ember/component/helper';
2
+
3
+ export function formatDurationValue(seconds) {
4
+ const value = Number(seconds);
5
+
6
+ if (!Number.isFinite(value)) {
7
+ return '0s';
8
+ }
9
+
10
+ const totalSeconds = Math.max(0, Math.ceil(value));
11
+ const days = Math.floor(totalSeconds / 86400);
12
+ const hours = Math.floor((totalSeconds % 86400) / 3600);
13
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
14
+ const remainingSeconds = totalSeconds % 60;
15
+ const parts = [];
16
+
17
+ if (days) {
18
+ parts.push(`${days}d`);
19
+ }
20
+
21
+ if (hours) {
22
+ parts.push(`${hours}h`);
23
+ }
24
+
25
+ if (!days && minutes) {
26
+ parts.push(`${minutes}m`);
27
+ }
28
+
29
+ if (!days && !hours && parts.length < 2 && remainingSeconds) {
30
+ parts.push(`${remainingSeconds}s`);
31
+ }
32
+
33
+ return parts.length ? parts.join(' ') : '0s';
34
+ }
35
+
36
+ export default helper(function formatDuration([seconds]) {
37
+ return formatDurationValue(seconds);
38
+ });
@@ -29,6 +29,7 @@ export default class ApplicationRoute extends Route {
29
29
 
30
30
  await this.location.getUserLocation();
31
31
  await this.#loadRoutingSettings();
32
+ await this.#loadTrackingSettings();
32
33
  await this.#loadMapSettings();
33
34
  }
34
35
 
@@ -37,6 +38,11 @@ export default class ApplicationRoute extends Route {
37
38
  this.currentUser.setOption('routing', routingSetting);
38
39
  }
39
40
 
41
+ async #loadTrackingSettings() {
42
+ const trackingSettings = await this.fetch.get('fleet-ops/settings/tracking-settings');
43
+ this.currentUser.setOption('tracking', trackingSettings);
44
+ }
45
+
40
46
  async #loadMapSettings() {
41
47
  await this.mapSettings.load();
42
48
  }
@@ -342,14 +342,19 @@ export default class GoogleMapsAdapter extends MapAdapterInterface {
342
342
  const img = document.createElement('img');
343
343
  img.src = options.iconUrl;
344
344
  const size = options.iconSize ?? [24, 24];
345
+ img.width = size[0];
346
+ img.height = size[1];
345
347
  img.style.width = `${size[0]}px`;
346
348
  img.style.height = `${size[1]}px`;
349
+ img.style.maxWidth = 'none';
350
+ img.style.maxHeight = 'none';
351
+ img.style.objectFit = 'contain';
347
352
  img.style.display = 'block';
348
353
  img.alt = options.alt ?? options.title ?? '';
349
354
  // Wrapper div allows rotation transforms without affecting the anchor
350
355
  const wrapper = document.createElement('div');
351
356
  wrapper.className = 'fleetops-map-marker';
352
- wrapper.style.cssText = 'transform-origin: center center; display: block;';
357
+ wrapper.style.cssText = `width: ${size[0]}px; height: ${size[1]}px; max-width: ${size[0]}px; max-height: ${size[1]}px; line-height: 0; transform-origin: center center; display: block; overflow: visible;`;
353
358
  wrapper.appendChild(img);
354
359
  content = wrapper;
355
360
  }
@@ -1051,6 +1056,7 @@ export default class GoogleMapsAdapter extends MapAdapterInterface {
1051
1056
  content: buildWaypointMarkerContent(customOptions.waypointLabel, customOptions.waypointColor ?? '#2563eb'),
1052
1057
  iconSize: customOptions.iconSize ?? [32, 32],
1053
1058
  iconAnchor: customOptions.iconAnchor ?? [16, 16],
1059
+ zIndexOffset: customOptions.zIndexOffset ?? 100000,
1054
1060
  };
1055
1061
  }
1056
1062
 
@@ -2079,7 +2085,7 @@ export default class GoogleMapsAdapter extends MapAdapterInterface {
2079
2085
  map: this._map,
2080
2086
  position,
2081
2087
  title: options.title ?? options.tooltip ?? '',
2082
- zIndex: options.zIndexOffset ?? 0,
2088
+ zIndex: options.zIndexOffset ?? (options.waypointLabel ? 100000 : 0),
2083
2089
  };
2084
2090
 
2085
2091
  if (options.waypointLabel) {
@@ -2088,12 +2094,17 @@ export default class GoogleMapsAdapter extends MapAdapterInterface {
2088
2094
  scaledSize: new google.maps.Size(34, 34),
2089
2095
  anchor: new google.maps.Point(17, 17),
2090
2096
  };
2097
+ markerOptions.optimized = false;
2091
2098
  } else if (options.iconUrl) {
2092
2099
  const size = options.iconSize ?? [24, 24];
2093
2100
  markerOptions.icon = {
2094
2101
  url: options.iconUrl,
2102
+ size: new google.maps.Size(size[0], size[1]),
2095
2103
  scaledSize: new google.maps.Size(size[0], size[1]),
2104
+ origin: new google.maps.Point(0, 0),
2105
+ anchor: new google.maps.Point(size[0] / 2, size[1] / 2),
2096
2106
  };
2107
+ markerOptions.optimized = false;
2097
2108
  }
2098
2109
 
2099
2110
  const marker = new google.maps.Marker(markerOptions);
@@ -185,12 +185,12 @@ export default class OrderListOverlayService extends Service {
185
185
 
186
186
  #peekActiveOrders() {
187
187
  // Consider an order "active" if it has an assigned driver and is not terminal
188
- const TERMINAL = ['created', 'completed', 'canceled', 'expired'];
188
+ const TERMINAL = ['created', 'completed', 'canceled', 'expired', 'order_canceled', 'pending'];
189
189
  return this.#peekOrders((order) => !!order.driver_assigned && !TERMINAL.includes(order.status));
190
190
  }
191
191
 
192
192
  #peekDriverActiveOrders(driver) {
193
- const TERMINAL = ['created', 'completed', 'canceled', 'expired'];
193
+ const TERMINAL = ['created', 'completed', 'canceled', 'expired', 'order_canceled', 'pending'];
194
194
  return this.#peekOrders((order) => !!order.driver_assigned && order.driver_assigned?.id === driver.id && !TERMINAL.includes(order.status));
195
195
  }
196
196