@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
@@ -28,7 +28,7 @@
28
28
  @hideColumnsPicker={{true}}
29
29
  />
30
30
  </Layout::Section::Header>
31
- <Layout::Section::Body class="overflow-y-scroll h-full">
31
+ <Layout::Section::Body class="overflow-y-scroll-i h-full">
32
32
  <Layout::Resource::CardsGrid
33
33
  @resource="equipment"
34
34
  @data={{@model}}
@@ -28,7 +28,7 @@
28
28
  @hideColumnsPicker={{true}}
29
29
  />
30
30
  </Layout::Section::Header>
31
- <Layout::Section::Body class="overflow-y-scroll h-full">
31
+ <Layout::Section::Body class="overflow-y-scroll-i h-full">
32
32
  <Layout::Resource::CardsGrid
33
33
  @resource="part"
34
34
  @data={{@model}}
@@ -16,7 +16,7 @@
16
16
  />
17
17
  </Layout::Section::Header>
18
18
 
19
- <Layout::Section::Body class="overflow-y-scroll h-full">
19
+ <Layout::Section::Body class="overflow-y-scroll-i h-full">
20
20
  <div class="p-4">
21
21
  <FullCalendar @events={{this.calendarEventSource}} @onEventClick={{this.onCalendarEventClick}} @onInit={{this.setCalendarApi}} />
22
22
  </div>
@@ -29,7 +29,7 @@
29
29
  />
30
30
  </Layout::Section::Header>
31
31
 
32
- <Layout::Section::Body class="overflow-y-scroll h-full">
32
+ <Layout::Section::Body class="overflow-y-scroll-i h-full">
33
33
  <Layout::Resource::CardsGrid
34
34
  @resource="vehicle"
35
35
  @data={{@model}}
@@ -1,6 +1,6 @@
1
1
  <Layout::Section::Header @title={{t "settings.avatar-management"}} />
2
2
 
3
- <Layout::Section::Body class="overflow-y-scroll h-full">
3
+ <Layout::Section::Body class="overflow-y-scroll-i h-full">
4
4
  <div class="container mx-auto h-screen">
5
5
  <div class="max-w-3xl my-10 mx-auto space-y-6">
6
6
  <AvatarManager />
@@ -1,6 +1,6 @@
1
1
  <Layout::Section::Header @title={{t "settings.custom-fields"}} />
2
2
 
3
- <Layout::Section::Body class="overflow-y-scroll h-full">
3
+ <Layout::Section::Body class="overflow-y-scroll-i h-full">
4
4
  <div class="container mx-auto h-screen">
5
5
  <div class="max-w-3xl my-10 mx-auto space-y-6">
6
6
  <CustomFieldsManager @subjects={{this.subjects}} />
@@ -10,7 +10,7 @@
10
10
  @permission="fleet-ops update map-settings"
11
11
  />
12
12
  </Layout::Section::Header>
13
- <Layout::Section::Body class="overflow-y-scroll h-full">
13
+ <Layout::Section::Body class="overflow-y-scroll-i h-full">
14
14
  <div class="container mx-auto h-screen">
15
15
  <div class="max-w-3xl my-10 mx-auto space-y-6">
16
16
 
@@ -1,6 +1,6 @@
1
1
  <Layout::Section::Header @title={{t "settings.navigator-app.navigator-app-settings"}} />
2
2
 
3
- <Layout::Section::Body class="overflow-y-scroll h-full">
3
+ <Layout::Section::Body class="overflow-y-scroll-i h-full">
4
4
  <div class="container mx-auto h-screen">
5
5
  <div class="max-w-3xl my-10 mx-auto space-y-6">
6
6
  <EntityFieldEditingSettings />
@@ -10,7 +10,7 @@
10
10
  />
11
11
  </Layout::Section::Header>
12
12
 
13
- <Layout::Section::Body class="overflow-y-scroll h-full">
13
+ <Layout::Section::Body class="overflow-y-scroll-i h-full">
14
14
  <div class="container mx-auto h-screen">
15
15
  <div class="max-w-3xl my-10 mx-auto space-y-6">
16
16
  <ContentPanel @title={{t "settings.notifications.configure-notifications"}} @open={{true}} @wrapperClass="bordered-classic">
@@ -9,7 +9,7 @@
9
9
  @isLoading={{or this.saveSettings.isRunning this.loadSettings.isRunning}}
10
10
  />
11
11
  </Layout::Section::Header>
12
- <Layout::Section::Body class="overflow-y-scroll h-full">
12
+ <Layout::Section::Body class="overflow-y-scroll-i h-full">
13
13
  <div class="container mx-auto h-screen">
14
14
  <div class="max-w-3xl my-10 mx-auto space-y-6">
15
15
  {{#if this.loadSettings.isRunning}}
@@ -11,7 +11,7 @@
11
11
  {{/if}}
12
12
  </Layout::Section::Header>
13
13
 
14
- <Layout::Section::Body class="overflow-y-scroll h-full">
14
+ <Layout::Section::Body class="overflow-y-scroll-i h-full">
15
15
  {{#if this.lookupStripeConnectAccount.isRunning}}
16
16
  <div class="flex items-center justify-center flex-1 p-4">
17
17
  <Spinner @loadingMessage={{t "settings.payments.loading-settings"}} @loadingMessageClass="ml-2 text-black dark:text-white" @wrapperClass="flex flex-row items-center" />
@@ -1,6 +1,6 @@
1
1
  <Layout::Section::Header @title={{t "payment.settings.onboard.header-title"}} />
2
2
 
3
- <Layout::Section::Body class="overflow-y-scroll h-full">
3
+ <Layout::Section::Body class="overflow-y-scroll-i h-full">
4
4
  <div class="container">
5
5
  <div class="max-w-3xl mx-auto mt-4">
6
6
  <div class="content">
@@ -1,4 +1,4 @@
1
- <Layout::Section::Header @title={{t "settings.routing.fleet-ops-routing-settings"}}>
1
+ <Layout::Section::Header @title="FleetOps Routing & Tracking">
2
2
  <Button
3
3
  @type="primary"
4
4
  @size="sm"
@@ -10,7 +10,7 @@
10
10
  />
11
11
  </Layout::Section::Header>
12
12
 
13
- <Layout::Section::Body class="overflow-y-scroll h-full">
13
+ <Layout::Section::Body class="overflow-y-scroll-i h-full">
14
14
  <div class="container mx-auto h-screen">
15
15
  <div class="max-w-3xl my-10 mx-auto space-y-6">
16
16
  <ContentPanel @title={{t "settings.routing.configure-routing"}} @open={{true}} @wrapperClass="bordered-classic">
@@ -48,10 +48,88 @@
48
48
  />
49
49
  </InputGroup>
50
50
  </ContentPanel>
51
+ <ContentPanel @title="Tracking Intelligence" @open={{true}} @wrapperClass="bordered-classic">
52
+ <InputGroup @name="Tracking Provider" @helpText="Choose the provider used for live tracking intelligence and ETA calculations.">
53
+ <Select
54
+ @value={{this.trackingProvider}}
55
+ @options={{this.trackingProviderOptions}}
56
+ @optionLabel="label"
57
+ @optionValue="value"
58
+ @onSelect={{fn (mut this.trackingProvider)}}
59
+ @placeholder="Select tracking provider"
60
+ class="w-full"
61
+ />
62
+ </InputGroup>
63
+ <InputGroup @name="Fallback Providers" @helpText="Providers used when the selected tracking provider is unavailable.">
64
+ <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
65
+ <PowerSelectMultiple
66
+ @searchEnabled={{true}}
67
+ @options={{this.trackingProviderOptions}}
68
+ @selected={{this.selectedTrackingFallbackOptions}}
69
+ @onChange={{this.setTrackingFallbacks}}
70
+ @placeholder="Select fallback providers"
71
+ @triggerClass="form-select form-input flex-1"
72
+ as |provider|
73
+ >
74
+ {{provider.label}}
75
+ </PowerSelectMultiple>
76
+ </div>
77
+ </InputGroup>
78
+ <div class="flex justify-end">
79
+ <Button
80
+ @type="default"
81
+ @size="xs"
82
+ @icon={{if this.showTrackingAdvancedSettings "chevron-up" "chevron-down"}}
83
+ @text="Advanced Settings"
84
+ @onClick={{this.toggleTrackingAdvancedSettings}}
85
+ />
86
+ </div>
87
+ {{#if this.showTrackingAdvancedSettings}}
88
+ <div class="mt-2 space-y-4 rounded-md border border-gray-200 bg-gray-50 p-3 dark:border-gray-700 dark:bg-gray-800">
89
+ <InputGroup @name="Traffic-Aware ETA" @helpText="Use provider traffic data when supported.">
90
+ <Toggle @isToggled={{this.trackingTrafficEnabled}} @onToggle={{fn (mut this.trackingTrafficEnabled)}} />
91
+ </InputGroup>
92
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
93
+ <InputGroup @name="Summary Cache TTL" @helpText="Seconds to cache the final tracking summary.">
94
+ <Input @type="number" @value={{this.trackingCacheTtlSeconds}} min="15" max="300" class="form-input w-full" />
95
+ </InputGroup>
96
+ <InputGroup @name="Route Cache TTL" @helpText="Seconds to cache provider route responses.">
97
+ <Input @type="number" @value={{this.trackingRouteCacheTtlSeconds}} min="60" max="1800" class="form-input w-full" />
98
+ </InputGroup>
99
+ <InputGroup @name="Stale Location Threshold" @helpText="Seconds before driver location is marked stale.">
100
+ <Input @type="number" @value={{this.trackingStaleLocationThresholdSeconds}} min="30" max="3600" class="form-input w-full" />
101
+ </InputGroup>
102
+ <InputGroup @name="Fallback Speed" @helpText="Kilometers per hour used by the calculated provider.">
103
+ <Input @type="number" @value={{this.trackingDefaultVehicleSpeedKph}} min="1" max="160" class="form-input w-full" />
104
+ </InputGroup>
105
+ </div>
106
+ </div>
107
+ {{/if}}
108
+ </ContentPanel>
109
+ <ContentPanel @title="Operational Alerts" @open={{true}} @wrapperClass="bordered-classic">
110
+ <InputGroup @name="Late Departures" @helpText="Notify when an order has not departed after the scheduled pickup window plus the configured grace period.">
111
+ <Toggle @isToggled={{this.lateDepartureAlertsEnabled}} @onToggle={{fn (mut this.lateDepartureAlertsEnabled)}} />
112
+ </InputGroup>
113
+ <InputGroup @name="Late Departure Grace Period" @helpText="Minutes after scheduled pickup before the order is considered late to depart.">
114
+ <Input @type="number" @value={{this.lateDepartureGracePeriodMinutes}} min="0" max="1440" class="form-input w-full" />
115
+ </InputGroup>
116
+ <InputGroup @name="Route Deviations" @helpText="Notify when tracked vehicle location drifts beyond the configured distance from the active route.">
117
+ <Toggle @isToggled={{this.routeDeviationAlertsEnabled}} @onToggle={{fn (mut this.routeDeviationAlertsEnabled)}} />
118
+ </InputGroup>
119
+ <InputGroup @name="Route Deviation Threshold" @helpText="Meters from the active route before a deviation alert is eligible.">
120
+ <Input @type="number" @value={{this.routeDeviationDistanceThresholdMeters}} min="0" max="100000" class="form-input w-full" />
121
+ </InputGroup>
122
+ <InputGroup @name="Prolonged Stoppages" @helpText="Notify when a vehicle remains stopped beyond the configured duration during an active trip.">
123
+ <Toggle @isToggled={{this.prolongedStoppageAlertsEnabled}} @onToggle={{fn (mut this.prolongedStoppageAlertsEnabled)}} />
124
+ </InputGroup>
125
+ <InputGroup @name="Prolonged Stoppage Duration" @helpText="Minutes stopped before a stoppage alert is eligible.">
126
+ <Input @type="number" @value={{this.prolongedStoppageDurationThresholdMinutes}} min="0" max="1440" class="form-input w-full" />
127
+ </InputGroup>
128
+ </ContentPanel>
51
129
  <RegistryYield @registry="fleet-ops:template:settings:routing" as |RegistryComponent|>
52
130
  <RegistryComponent @controller={{this}} />
53
131
  </RegistryYield>
54
132
  </div>
133
+ <Spacer @height="600px" />
55
134
  </div>
56
- <Spacer @height="600px" />
57
- </Layout::Section::Body>
135
+ </Layout::Section::Body>
@@ -9,7 +9,7 @@
9
9
  @isLoading={{or this.saveSettings.isRunning this.getSettings.isRunning}}
10
10
  />
11
11
  </Layout::Section::Header>
12
- <Layout::Section::Body class="overflow-y-scroll h-full">
12
+ <Layout::Section::Body class="overflow-y-scroll-i h-full">
13
13
  <div class="container mx-auto h-screen">
14
14
  <div class="max-w-3xl my-10 mx-auto space-y-6">
15
15
 
@@ -0,0 +1,171 @@
1
+ import { isArray } from '@ember/array';
2
+ import { dasherize } from '@ember/string';
3
+ import titleize from 'ember-cli-string-helpers/utils/titleize';
4
+
5
+ function resolveStatusClass(status) {
6
+ const statusKey = dasherize(status ?? 'default');
7
+
8
+ return `status-badge ${statusKey}-status-badge status-badge-${statusKey}`;
9
+ }
10
+
11
+ function titleizeStatus(status) {
12
+ if (!status) {
13
+ return '-';
14
+ }
15
+
16
+ return titleize(`${status}`.replace(/[_-]+/g, ' '));
17
+ }
18
+
19
+ function buildStatusBadge(status, label = status) {
20
+ return `
21
+ <div class="${resolveStatusClass(status)} shadow-none ml-auto">
22
+ <span class="rounded-full text-[9px] px-1.5 py-0.5">${titleizeStatus(label)}</span>
23
+ </div>`;
24
+ }
25
+
26
+ function buildMetaCell(label, value, valueClass = '') {
27
+ const shouldTruncate = valueClass.split(' ').includes('truncate');
28
+ const flowClass = shouldTruncate ? 'min-w-0 w-full' : 'whitespace-normal break-words';
29
+
30
+ return `
31
+ <div class="rounded bg-gray-900 shadow-md px-1.5 py-1 min-w-0">
32
+ <div class="text-[9px] font-semibold uppercase text-gray-400 leading-none">${label}</div>
33
+ <div class="text-[11px] text-white leading-tight ${flowClass} ${valueClass}">${value ?? '-'}</div>
34
+ </div>`;
35
+ }
36
+
37
+ function buildLiveMapCard(content, framed = false) {
38
+ const frameClasses = framed ? 'relative rounded-lg bg-gray-900 shadow-lg p-1.5 text-white' : '';
39
+ const closeButton = framed ? '<button type="button" class="fleetops-google-popover__close" data-fleetops-google-popover-close aria-label="Close">&times;</button>' : '';
40
+
41
+ return `<div class="w-[280px] max-w-[calc(100vw-2rem)] ${frameClasses}">${closeButton}${content}</div>`;
42
+ }
43
+
44
+ function resolveDriverStatus(driver) {
45
+ return driver.meta?.status_label ?? titleizeStatus(driver.status);
46
+ }
47
+
48
+ function resolveDriverLocation(driver) {
49
+ return driver.meta?.location_coordinates ?? resolveLocationCoordinates(driver.location);
50
+ }
51
+
52
+ function resolveDriverSpeed(driver) {
53
+ return driver.meta?.speed_label ?? `${driver.speed ?? '-'} km/h`;
54
+ }
55
+
56
+ function resolveDriverHeading(driver) {
57
+ return driver.meta?.heading_label ?? driver.heading ?? '-';
58
+ }
59
+
60
+ function resolveDriverId(driver) {
61
+ return driver.public_id ?? driver.id ?? '-';
62
+ }
63
+
64
+ function resolveVehicleNumber(vehicle) {
65
+ return vehicle.internal_id ?? vehicle.public_id ?? vehicle.plate_number ?? vehicle.serial_number ?? vehicle.vin ?? '-';
66
+ }
67
+
68
+ function resolveVehicleStatus(vehicle) {
69
+ return vehicle.meta?.status_label ?? vehicle.current_status ?? vehicle.status ?? '-';
70
+ }
71
+
72
+ function resolveVehicleLocation(vehicle) {
73
+ return vehicle.meta?.location_coordinates ?? resolveLocationCoordinates(vehicle.location);
74
+ }
75
+
76
+ function resolveVehicleSpeed(vehicle) {
77
+ return vehicle.meta?.speed_label ?? `${vehicle.speed ?? '-'} km/h`;
78
+ }
79
+
80
+ function resolveVehicleHeading(vehicle) {
81
+ return vehicle.meta?.heading_label ?? vehicle.heading ?? '-';
82
+ }
83
+
84
+ function resolveLocationCoordinates(location) {
85
+ if (!location) {
86
+ return '-';
87
+ }
88
+
89
+ if (location?.coordinates && isArray(location.coordinates)) {
90
+ const [lng, lat] = location.coordinates;
91
+ return Number.isFinite(lat) && Number.isFinite(lng) ? `${formatCoordinate(lat)} ${formatCoordinate(lng)}` : '-';
92
+ }
93
+
94
+ if (isArray(location) && location.length >= 2) {
95
+ const [lat, lng] = location;
96
+ return Number.isFinite(lat) && Number.isFinite(lng) ? `${formatCoordinate(lat)} ${formatCoordinate(lng)}` : '-';
97
+ }
98
+
99
+ return '-';
100
+ }
101
+
102
+ function formatCoordinate(coordinate) {
103
+ return `${Math.round(coordinate * 10000) / 10000}`;
104
+ }
105
+
106
+ export function buildDriverLiveMapContent(driver, framed = false) {
107
+ const status = resolveDriverStatus(driver);
108
+ const onlineClass = driver.online ? 'bg-green-400' : 'bg-red-500';
109
+
110
+ return buildLiveMapCard(
111
+ `
112
+ <div class="fleetops-google-hover-tooltip__title mb-1.5 flex items-center gap-1.5">
113
+ <span class="inline-block w-2 h-2 rounded-full ${onlineClass}"></span>
114
+ <span>${driver.name ?? '-'}</span>
115
+ ${buildStatusBadge(driver.status, status)}
116
+ </div>
117
+ <div class="grid grid-cols-2 gap-1">
118
+ ${buildMetaCell('ID', resolveDriverId(driver))}
119
+ ${buildMetaCell('Phone', driver.phone ?? '-')}
120
+ ${buildMetaCell('Vehicle', driver.vehicle_name ?? '-')}
121
+ ${buildMetaCell('Email', driver.email ?? '-', 'truncate')}
122
+ ${buildMetaCell('Order', driver.meta?.current_order_reference ?? '-')}
123
+ ${buildMetaCell('Speed', resolveDriverSpeed(driver))}
124
+ ${buildMetaCell('Heading', resolveDriverHeading(driver))}
125
+ ${buildMetaCell('Location', resolveDriverLocation(driver))}
126
+ </div>
127
+ `,
128
+ framed
129
+ );
130
+ }
131
+
132
+ export function buildVehicleLiveMapContent(vehicle, framed = false) {
133
+ const status = resolveVehicleStatus(vehicle);
134
+ const onlineClass = vehicle.online ? 'bg-green-400' : 'bg-red-500';
135
+
136
+ return buildLiveMapCard(
137
+ `
138
+ <div class="fleetops-google-hover-tooltip__title mb-1.5 flex items-center gap-1.5">
139
+ <span class="inline-block w-2 h-2 rounded-full ${onlineClass}"></span>
140
+ <span>${vehicle.displayName ?? '-'}</span>
141
+ ${buildStatusBadge(vehicle.status, status)}
142
+ </div>
143
+ <div class="grid grid-cols-2 gap-1">
144
+ ${buildMetaCell('Vehicle #', resolveVehicleNumber(vehicle))}
145
+ ${buildMetaCell('Driver', vehicle.driver_name ?? vehicle.driver?.name ?? '-')}
146
+ ${buildMetaCell('Order', vehicle.meta?.current_order_reference ?? '-')}
147
+ ${buildMetaCell('Speed', resolveVehicleSpeed(vehicle))}
148
+ ${buildMetaCell('Heading', resolveVehicleHeading(vehicle))}
149
+ ${buildMetaCell('Location', resolveVehicleLocation(vehicle))}
150
+ </div>
151
+ `,
152
+ framed
153
+ );
154
+ }
155
+
156
+ export function buildPlaceInfoWindowContent(place) {
157
+ return `
158
+ <div class="fleetops-google-popover fleetops-google-popover--compact">
159
+ <div class="fleetops-google-popover__body">
160
+ <div class="fleetops-google-popover__title">${place.name ?? place.address ?? '-'}</div>
161
+ <div class="fleetops-google-popover__meta">${place.address ?? ''}</div>
162
+ </div>
163
+ </div>`;
164
+ }
165
+
166
+ export function buildPlaceTooltipContent(place) {
167
+ return `
168
+ <div class="fleetops-google-hover-tooltip__title">${place.name ?? place.address ?? '-'}</div>
169
+ <div class="fleetops-google-hover-tooltip__meta">${place.address ?? ''}</div>
170
+ `;
171
+ }
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/tracking-stop-progress';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/widget/fuel-efficiency';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/widget/geofence-violations';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/widget/issues-insights';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/widget/kpi-active-orders';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/widget/kpi-aov';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/widget/kpi-distance';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/widget/kpi-drivers-online';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/widget/kpi-earnings';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/widget/kpi-open-issues';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/widget/kpi-tile';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/widget/live-fleet';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/widget/maintenance-overview';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/widget/on-time-delivery';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/widget/operations-pulse';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/widget/orders-by-status';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/widget/revenue-trend';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/widget/top-drivers';
@@ -0,0 +1 @@
1
+ export { default, formatDurationValue } from '@fleetbase/fleetops-engine/helpers/format-duration';
@@ -0,0 +1 @@
1
+ export * from '@fleetbase/fleetops-engine/utils/live-map-card-content';
package/composer.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fleetbase/fleetops-api",
3
- "version": "0.6.47",
3
+ "version": "0.6.49",
4
4
  "description": "Fleet & Transport Management Extension for Fleetbase",
5
5
  "keywords": [
6
6
  "fleetbase-extension",
package/extension.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Fleet-Ops",
3
- "version": "0.6.47",
3
+ "version": "0.6.49",
4
4
  "description": "Fleet & Transport Management Extension for Fleetbase",
5
5
  "repository": "https://github.com/fleetbase/fleetops",
6
6
  "license": "AGPL-3.0-or-later",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fleetbase/fleetops-engine",
3
- "version": "0.6.47",
3
+ "version": "0.6.49",
4
4
  "description": "Fleet & Transport Management Extension for Fleetbase",
5
5
  "fleetbase": {
6
6
  "route": "fleet-ops"
@@ -43,8 +43,8 @@
43
43
  "dependencies": {
44
44
  "@babel/core": "^7.23.2",
45
45
  "@fleetbase/ember-core": "^0.3.19",
46
- "@fleetbase/ember-ui": "^0.3.29",
47
- "@fleetbase/fleetops-data": "^0.1.32",
46
+ "@fleetbase/ember-ui": "^0.3.30",
47
+ "@fleetbase/fleetops-data": "^0.1.33",
48
48
  "@fleetbase/leaflet-routing-machine": "^3.2.17",
49
49
  "@fortawesome/ember-fontawesome": "^2.0.0",
50
50
  "@fortawesome/fontawesome-svg-core": "6.4.0",
@@ -5,3 +5,4 @@ allowBuilds:
5
5
  '@fortawesome/free-solid-svg-icons': false
6
6
  core-js: false
7
7
  fsevents: false
8
+ minimumReleaseAge: 0
@@ -3,7 +3,7 @@
3
3
  /**
4
4
  * -------------------------------------------
5
5
  * FleetOps API Configuration
6
- * -------------------------------------------
6
+ * -------------------------------------------.
7
7
  */
8
8
  return [
9
9
  /*
@@ -14,17 +14,17 @@ return [
14
14
  'api' => [
15
15
  'version' => '0.0.1',
16
16
  'routing' => [
17
- 'prefix' => null,
18
- 'internal_prefix' => 'int'
19
- ]
17
+ 'prefix' => null,
18
+ 'internal_prefix' => 'int',
19
+ ],
20
20
  ],
21
21
  'connection' => [
22
- 'db' => env('DB_CONNECTION', 'mysql')
22
+ 'db' => env('DB_CONNECTION', 'mysql'),
23
23
  ],
24
24
 
25
25
  /*
26
26
  |--------------------------------------------------------------------------
27
- | Facilitator Fee - This is a percentage fee the system admin takes when
27
+ | Facilitator Fee - This is a percentage fee the system admin takes when
28
28
  | facilitating any payments in the system.
29
29
  | Example: if `10` then 10% fee will be taken on all payments.
30
30
  |--------------------------------------------------------------------------
@@ -37,7 +37,7 @@ return [
37
37
  |--------------------------------------------------------------------------
38
38
  */
39
39
  'osrm' => [
40
- 'host' => env('OSRM_HOST', 'https://router.project-osrm.org')
40
+ 'host' => env('OSRM_HOST', 'https://router.project-osrm.org'),
41
41
  ],
42
42
 
43
43
  /*
@@ -61,12 +61,33 @@ return [
61
61
 
62
62
  /*
63
63
  |--------------------------------------------------------------------------
64
- | Distance Matrix Calculator
64
+ | Distance Matrix Calculator
65
65
  | Options: "calculate", "google", "osrm"
66
66
  |--------------------------------------------------------------------------
67
67
  */
68
68
  'distance_matrix' => [
69
- 'provider' => env('DISTANCE_MATRIX_PROVIDER', 'calculate')
69
+ 'provider' => env('DISTANCE_MATRIX_PROVIDER', 'calculate'),
70
+ ],
71
+
72
+ /*
73
+ |--------------------------------------------------------------------------
74
+ | Order Tracking Intelligence
75
+ |--------------------------------------------------------------------------
76
+ |
77
+ | Provider-neutral tracking data for active orders. The provider registry
78
+ | can be extended by third-party packages without editing FleetOps core.
79
+ |
80
+ | Built-in providers: "google_routes", "osrm", "calculated"
81
+ |--------------------------------------------------------------------------
82
+ */
83
+ 'tracking' => [
84
+ 'provider' => env('TRACKING_PROVIDER', 'google_routes'),
85
+ 'fallbacks' => array_filter(explode(',', env('TRACKING_PROVIDER_FALLBACKS', 'osrm,calculated'))),
86
+ 'traffic_enabled' => env('TRACKING_TRAFFIC_ENABLED', true),
87
+ 'cache_ttl_seconds' => env('TRACKING_CACHE_TTL_SECONDS', 60),
88
+ 'route_cache_ttl_seconds' => env('TRACKING_ROUTE_CACHE_TTL_SECONDS', 600),
89
+ 'stale_location_threshold_seconds' => env('TRACKING_STALE_LOCATION_THRESHOLD_SECONDS', 300),
90
+ 'default_vehicle_speed_kph' => env('TRACKING_DEFAULT_VEHICLE_SPEED_KPH', 35),
70
91
  ],
71
92
 
72
93
  /*
@@ -76,7 +97,7 @@ return [
76
97
  */
77
98
  'navigator' => [
78
99
  'bypass_verification_code' => env('SMS_AUTH_BYPASS_CODE', env('NAVIGATOR_BYPASS_VERIFICATION_CODE')),
79
- 'app_identifier' => env('NAVIGATOR_APP_IDENTIFIER', 'io.fleetbase.navigator')
100
+ 'app_identifier' => env('NAVIGATOR_APP_IDENTIFIER', 'io.fleetbase.navigator'),
80
101
  ],
81
102
 
82
103
  /*
@@ -189,5 +210,5 @@ return [
189
210
  |--------------------------------------------------------------------------
190
211
  */
191
212
  'versions' => ['2020-09-30', '2024-03-12'],
192
- 'version' => '2024-03-12',
213
+ 'version' => '2024-03-12',
193
214
  ];