@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
@@ -3,9 +3,80 @@
3
3
  Configure system-wide routing engine defaults here. Organizations can optionally override these values from their own routing settings.
4
4
  </InfoBlock>
5
5
 
6
+ <ContentPanel @title="Tracking Defaults" @open={{true}} @wrapperClass="bordered-classic">
7
+ <InputGroup @name="Default Tracking Provider" @helpText="Default provider for organization-level tracking intelligence unless an organization overrides it.">
8
+ <Select
9
+ class="w-full"
10
+ @value={{this.trackingProvider}}
11
+ @options={{this.trackingProviderOptions}}
12
+ @optionLabel="label"
13
+ @optionValue="value"
14
+ @placeholder="Select default tracking provider"
15
+ @onSelect={{fn (mut this.trackingProvider)}}
16
+ />
17
+ </InputGroup>
18
+ <InputGroup @name="Default Fallback Providers" @helpText="Providers used when the selected tracking provider is unavailable.">
19
+ <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
20
+ <PowerSelectMultiple
21
+ @searchEnabled={{true}}
22
+ @options={{this.trackingProviderOptions}}
23
+ @selected={{this.selectedTrackingFallbackOptions}}
24
+ @onChange={{this.setTrackingFallbacks}}
25
+ @placeholder="Select default fallback providers"
26
+ @triggerClass="form-select form-input form-input-sm flex-1"
27
+ as |provider|
28
+ >
29
+ {{provider.label}}
30
+ </PowerSelectMultiple>
31
+ </div>
32
+ </InputGroup>
33
+ <div class="flex justify-end">
34
+ <Button
35
+ @type="default"
36
+ @size="xs"
37
+ @icon={{if this.showTrackingAdvancedSettings "chevron-up" "chevron-down"}}
38
+ @text="Advanced Settings"
39
+ @onClick={{this.toggleTrackingAdvancedSettings}}
40
+ />
41
+ </div>
42
+ {{#if this.showTrackingAdvancedSettings}}
43
+ <div class="mt-4 space-y-4 rounded-md border border-gray-200 bg-gray-50 p-3 dark:border-gray-700 dark:bg-gray-800">
44
+ <InputGroup @name="Traffic-Aware ETA" @helpText="Use traffic-aware provider calculations when supported.">
45
+ <Toggle @isToggled={{this.trackingTrafficEnabled}} @onToggle={{fn (mut this.trackingTrafficEnabled)}} />
46
+ </InputGroup>
47
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
48
+ <InputGroup @name="Summary Cache TTL">
49
+ <Input @type="number" @value={{this.trackingCacheTtlSeconds}} min="15" max="300" class="form-input w-full" />
50
+ </InputGroup>
51
+ <InputGroup @name="Route Cache TTL">
52
+ <Input @type="number" @value={{this.trackingRouteCacheTtlSeconds}} min="60" max="1800" class="form-input w-full" />
53
+ </InputGroup>
54
+ <InputGroup @name="Stale Location Threshold">
55
+ <Input @type="number" @value={{this.trackingStaleLocationThresholdSeconds}} min="30" max="3600" class="form-input w-full" />
56
+ </InputGroup>
57
+ <InputGroup @name="Fallback Speed">
58
+ <Input @type="number" @value={{this.trackingDefaultVehicleSpeedKph}} min="1" max="160" class="form-input w-full" />
59
+ </InputGroup>
60
+ </div>
61
+ </div>
62
+ {{/if}}
63
+ </ContentPanel>
64
+
6
65
  <RegistryYield @registry="fleet-ops:component:admin:routing-settings" as |RegistryComponent|>
7
66
  <div>
8
67
  <RegistryComponent />
9
68
  </div>
10
69
  </RegistryYield>
11
70
  </div>
71
+
72
+ <EmberWormhole @to="next-view-section-subheader-actions">
73
+ <Button
74
+ @type="primary"
75
+ @size="sm"
76
+ @icon="save"
77
+ @text="Save Changes"
78
+ @onClick={{perform this.saveSettings}}
79
+ @isLoading={{or this.saveSettings.isRunning this.loadSettings.isRunning}}
80
+ @disabled={{this.saveSettings.isRunning}}
81
+ />
82
+ </EmberWormhole>
@@ -0,0 +1,124 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { inject as service } from '@ember/service';
4
+ import { action } from '@ember/object';
5
+ import { task } from 'ember-concurrency';
6
+
7
+ export default class AdminRoutingSettingsComponent extends Component {
8
+ @service fetch;
9
+ @service notifications;
10
+ @tracked trackingProvider = 'google_routes';
11
+ @tracked trackingFallbacks = ['osrm', 'calculated'];
12
+ @tracked trackingTrafficEnabled = true;
13
+ @tracked trackingCacheTtlSeconds = 60;
14
+ @tracked trackingRouteCacheTtlSeconds = 600;
15
+ @tracked trackingStaleLocationThresholdSeconds = 300;
16
+ @tracked trackingDefaultVehicleSpeedKph = 35;
17
+ @tracked showTrackingAdvancedSettings = false;
18
+ @tracked trackingProviderOptions = [
19
+ { value: 'google_routes', label: 'Google Routes' },
20
+ { value: 'osrm', label: 'OSRM' },
21
+ { value: 'calculated', label: 'Calculated' },
22
+ ];
23
+
24
+ constructor() {
25
+ super(...arguments);
26
+ this.loadSettings.perform();
27
+ }
28
+
29
+ @task *loadSettings() {
30
+ try {
31
+ const settings = yield this.fetch.get('fleet-ops/settings/admin-tracking-settings');
32
+ this.trackingProvider = settings.provider ?? 'google_routes';
33
+ this.trackingFallbacks = this.normalizeFallbacks(settings.fallbacks);
34
+ this.trackingTrafficEnabled = settings.traffic_enabled ?? true;
35
+ this.trackingCacheTtlSeconds = settings.cache_ttl_seconds ?? 60;
36
+ this.trackingRouteCacheTtlSeconds = settings.route_cache_ttl_seconds ?? 600;
37
+ this.trackingStaleLocationThresholdSeconds = settings.stale_location_threshold_seconds ?? 300;
38
+ this.trackingDefaultVehicleSpeedKph = settings.default_vehicle_speed_kph ?? 35;
39
+ this.trackingProviderOptions = this.normalizeProviderOptions(settings.providers ?? this.trackingProviderOptions);
40
+ } catch (error) {
41
+ this.notifications.serverError(error);
42
+ }
43
+ }
44
+
45
+ @task *saveSettings() {
46
+ try {
47
+ yield this.fetch.post('fleet-ops/settings/admin-tracking-settings', this.trackingSettingsPayload);
48
+ this.notifications.success('Tracking defaults saved.');
49
+ } catch (error) {
50
+ this.notifications.serverError(error);
51
+ }
52
+ }
53
+
54
+ get trackingSettingsPayload() {
55
+ return {
56
+ provider: this.trackingProvider,
57
+ fallbacks: this.normalizeFallbacks(this.trackingFallbacks),
58
+ traffic_enabled: this.trackingTrafficEnabled,
59
+ cache_ttl_seconds: Number(this.trackingCacheTtlSeconds) || 60,
60
+ route_cache_ttl_seconds: Number(this.trackingRouteCacheTtlSeconds) || 600,
61
+ stale_location_threshold_seconds: Number(this.trackingStaleLocationThresholdSeconds) || 300,
62
+ default_vehicle_speed_kph: Number(this.trackingDefaultVehicleSpeedKph) || 35,
63
+ };
64
+ }
65
+
66
+ normalizeFallbacks(fallbacks) {
67
+ if (Array.isArray(fallbacks)) {
68
+ return fallbacks
69
+ .map((fallback) => this.optionValue(fallback))
70
+ .map((fallback) => String(fallback).trim())
71
+ .filter(Boolean);
72
+ }
73
+
74
+ return String(fallbacks ?? '')
75
+ .split(',')
76
+ .map((fallback) => fallback.trim())
77
+ .filter(Boolean);
78
+ }
79
+
80
+ get selectedTrackingFallbackOptions() {
81
+ const selected = new Set(this.normalizeFallbacks(this.trackingFallbacks));
82
+
83
+ return this.trackingProviderOptions.filter((option) => selected.has(this.optionValue(option)));
84
+ }
85
+
86
+ normalizeProviderOptions(options = []) {
87
+ return options.map((option) => {
88
+ const value = this.optionValue(option);
89
+ const label = option?.label ?? option?.name ?? this.providerLabel(value);
90
+
91
+ return {
92
+ ...option,
93
+ key: option?.key ?? value,
94
+ name: option?.name ?? label,
95
+ value,
96
+ label,
97
+ };
98
+ });
99
+ }
100
+
101
+ providerLabel(value) {
102
+ if (value === 'osrm') {
103
+ return 'OSRM';
104
+ }
105
+
106
+ return String(value ?? '')
107
+ .replace(/[_-]+/g, ' ')
108
+ .replace(/\s+/g, ' ')
109
+ .trim()
110
+ .replace(/\b\w/g, (character) => character.toUpperCase());
111
+ }
112
+
113
+ optionValue(option) {
114
+ return typeof option === 'object' && option !== null ? (option.value ?? option.key) : option;
115
+ }
116
+
117
+ @action setTrackingFallbacks(options) {
118
+ this.trackingFallbacks = this.normalizeFallbacks(options);
119
+ }
120
+
121
+ @action toggleTrackingAdvancedSettings() {
122
+ this.showTrackingAdvancedSettings = !this.showTrackingAdvancedSettings;
123
+ }
124
+ }
@@ -138,7 +138,7 @@ export default class CustomerOrdersComponent extends Component {
138
138
  // start loading order tracking activity
139
139
  order.loadTrackingActivity();
140
140
  this.urlSearchParams.addParamToCurrentUrl('order', order.public_id);
141
- const driverCurrentLocation = order.get('tracker_data.driver_current_location');
141
+ const driverCurrentLocation = order.get('tracker_data.driver.location');
142
142
  if (driverCurrentLocation) {
143
143
  this.latitude = driverCurrentLocation.coordinates[1];
144
144
  this.longitude = driverCurrentLocation.coordinates[0];
@@ -10,7 +10,7 @@
10
10
  </span>
11
11
  </div>
12
12
  {{else}}
13
- {{#if (and @waypointActions (not @hideBadges))}}
13
+ {{#if @waypointActions}}
14
14
  <div class="flex flex-row item-center space-x-2 {{if (or this.place.status_code @eta) 'mb-2'}}">
15
15
  {{#unless @hideBadges}}
16
16
  {{#if this.place.status_code}}
@@ -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
  }