@fleetbase/fleetops-engine 0.6.56 → 0.6.57

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 (60) hide show
  1. package/addon/components/ai/create-order-preview.hbs +297 -0
  2. package/addon/components/ai/create-order-preview.js +362 -0
  3. package/addon/components/ai/route-preview-map.hbs +79 -0
  4. package/addon/components/ai/route-preview-map.js +527 -0
  5. package/addon/components/fleet/details.hbs +21 -8
  6. package/addon/components/fleet/details.js +55 -1
  7. package/addon/components/fleet/hierarchy-tree.hbs +137 -0
  8. package/addon/components/fleet/hierarchy-tree.js +528 -0
  9. package/addon/components/map/drawer/device-event-listing.js +1 -0
  10. package/addon/components/map/drawer/driver-listing.js +1 -0
  11. package/addon/components/map/drawer/place-listing.js +1 -0
  12. package/addon/components/map/drawer/vehicle-listing.js +1 -0
  13. package/addon/components/order/details.hbs +34 -1
  14. package/addon/components/order/form/details.hbs +4 -0
  15. package/addon/components/order/form.hbs +9 -1
  16. package/addon/components/vehicle/card.hbs +14 -2
  17. package/addon/components/vehicle/form.hbs +13 -1
  18. package/addon/controllers/settings/map.js +4 -0
  19. package/addon/extension.js +14 -0
  20. package/addon/routes/management/fleets/index/details.js +1 -1
  21. package/addon/services/map-adapter/google.js +56 -16
  22. package/addon/services/map-manager.js +12 -0
  23. package/addon/styles/fleetops-engine.css +487 -1
  24. package/addon/utils/map-drawer-dropdown-position.js +11 -13
  25. package/app/components/ai/create-order-preview.js +1 -0
  26. package/app/components/ai/route-preview-map.js +1 -0
  27. package/app/components/fleet/hierarchy-tree.js +1 -0
  28. package/app/services/route-engine.js +1 -0
  29. package/composer.json +1 -1
  30. package/extension.json +1 -1
  31. package/package.json +6 -4
  32. package/scripts/normalize-rgba-alpha.js +90 -0
  33. package/server/src/Http/Resources/v1/Fleet.php +8 -2
  34. package/server/src/Models/Driver.php +6 -0
  35. package/server/src/Models/Order.php +18 -6
  36. package/server/src/Models/Place.php +11 -3
  37. package/server/src/Providers/FleetOpsServiceProvider.php +26 -0
  38. package/server/src/Support/Ai/Capabilities/AbstractFleetOpsAICapability.php +88 -0
  39. package/server/src/Support/Ai/Capabilities/AssetStatusCapability.php +108 -0
  40. package/server/src/Support/Ai/Capabilities/ConsoleNavigationCapability.php +55 -0
  41. package/server/src/Support/Ai/Capabilities/CreateOrderPreviewCapability.php +508 -0
  42. package/server/src/Support/Ai/Capabilities/DocsHelpCapability.php +49 -0
  43. package/server/src/Support/Ai/Capabilities/ImportOrdersPreviewCapability.php +66 -0
  44. package/server/src/Support/Ai/Capabilities/OperationalQueryCapability.php +319 -0
  45. package/server/src/Support/Ai/Capabilities/OptimizeOrderRouteCapability.php +240 -0
  46. package/server/src/Support/Ai/Capabilities/OrderInsightsCapability.php +95 -0
  47. package/server/src/Support/Ai/Capabilities/SearchResourcesCapability.php +195 -0
  48. package/server/src/Support/Ai/FleetOpsAiQueryResources.php +150 -0
  49. package/server/src/Support/Utils.php +52 -0
  50. package/server/tests/AiCreateOrderPreviewCapabilityTest.php +131 -0
  51. package/server/tests/AiOperationalQueryCapabilityTest.php +75 -0
  52. package/server/tests/FleetHierarchyResourceTest.php +12 -0
  53. package/server/tests/OrderPayloadPlaceResolutionTest.php +53 -0
  54. package/server/tests/PointResolutionTest.php +63 -0
  55. package/addon/components/table/cell/dropdown/action-item.hbs +0 -31
  56. package/addon/components/table/cell/dropdown/action-item.js +0 -62
  57. package/addon/components/table/cell/dropdown.hbs +0 -28
  58. package/addon/components/table/cell/dropdown.js +0 -91
  59. package/app/components/table/cell/dropdown/action-item.js +0 -1
  60. package/app/components/table/cell/dropdown.js +0 -1
@@ -1,10 +1,16 @@
1
1
  <Layout::Resource::Card ...attributes as |Card|>
2
2
  <Card.header class="{{@headerClass}}">
3
+ <RegistryYield @registry="fleet-ops:component:vehicle:card:header:start" as |RegistryComponent|>
4
+ <RegistryComponent @order={{@resource}} @resource={{@resource}} />
5
+ </RegistryYield>
3
6
  <div class="font-semibold w-full truncate">{{or @resource.name @resource.yearMakeModel}}</div>
4
- <div class="text-gray-300 dark:text-gray-500 text-sm w-full truncate">{{or @resource.plate_number @resource.vin @resource.serial_number @resource.call_sign @resource.public_id}}</div>
7
+ <div class="text-gray-400 dark:text-gray-500 text-sm w-full truncate">{{or @resource.plate_number @resource.vin @resource.serial_number @resource.call_sign @resource.public_id}}</div>
5
8
  {{#if (has-block "header")}}
6
9
  {{yield to="header"}}
7
10
  {{/if}}
11
+ <RegistryYield @registry="fleet-ops:component:vehicle:card:header:end" as |RegistryComponent|>
12
+ <RegistryComponent @order={{@resource}} @resource={{@resource}} />
13
+ </RegistryYield>
8
14
  </Card.header>
9
15
  <Card.body class={{@bodyClass}}>
10
16
  <Image src={{@resource.photo_url}} class="card-img-lg" />
@@ -13,16 +19,22 @@
13
19
  {{/if}}
14
20
  </Card.body>
15
21
  <Card.footer class={{@footerClass}}>
22
+ <RegistryYield @registry="fleet-ops:component:vehicle:card:footer:start" as |RegistryComponent|>
23
+ <RegistryComponent @order={{@resource}} @resource={{@resource}} />
24
+ </RegistryYield>
16
25
  <div class="flex flex-row items-center space-x-1">
17
26
  <Button @icon="eye" @size="xs" @onClick={{fn this.vehicleActions.transition.view @resource}} />
18
27
  <Button @icon="pencil" @size="xs" @onClick={{fn this.vehicleActions.transition.edit @resource}} />
19
28
  <Button @icon="trash" @size="xs" @type="danger" @onClick={{fn this.vehicleActions.delete @resource}} />
20
29
  </div>
30
+ <RegistryYield @registry="fleet-ops:component:vehicle:card:footer:end" as |RegistryComponent|>
31
+ <RegistryComponent @order={{@resource}} @resource={{@resource}} />
32
+ </RegistryYield>
21
33
  {{#if (has-block "footer")}}
22
34
  {{yield to="footer"}}
23
35
  {{/if}}
24
36
  <div class="mt-1.5">
25
- <div class="text-gray-300 dark:text-gray-500 text-xs">Last Modified: {{@resource.updatedAt}}</div>
37
+ <div class="text-gray-400 dark:text-gray-500 text-xs">Last Modified: {{@resource.updatedAt}}</div>
26
38
  </div>
27
39
  </Card.footer>
28
40
  </Layout::Resource::Card>
@@ -1,4 +1,8 @@
1
1
  <div class="form-wrapper" ...attributes>
2
+ <RegistryYield @registry="fleet-ops:component:vehicle:form:start" as |RegistryComponent|>
3
+ <RegistryComponent @resource={{@resource}} @controller={{this.controller}} @permission={{get-write-permission @resource}} />
4
+ </RegistryYield>
5
+
2
6
  {{! DETAILS / IDENTIFICATION }}
3
7
  <ContentPanel @title={{t "common.details"}} @open={{true}} @wrapperClass="bordered-top">
4
8
  <div class="flex flex-row space-x-3 mb-3">
@@ -128,6 +132,10 @@
128
132
  </RegistryYield>
129
133
  </ContentPanel>
130
134
 
135
+ <RegistryYield @registry="fleet-ops:component:vehicle:form:after-details" as |RegistryComponent|>
136
+ <RegistryComponent @resource={{@resource}} @controller={{this.controller}} @permission={{get-write-permission @resource}} />
137
+ </RegistryYield>
138
+
131
139
  {{! MEASUREMENT & UNITS / ODOMETER }}
132
140
  <ContentPanel @title="Measurement & Units" @open={{true}} @wrapperClass="bordered-top">
133
141
  <div class="grid grid-cols-1 gap-2 lg:grid-cols-2 lg:gap-2 no-input-group-padding text-xs">
@@ -608,4 +616,8 @@
608
616
  <ContentPanel @title={{t "common.metadata"}} @open={{true}} @wrapperClass="bordered-top">
609
617
  <MetadataEditor @value={{@resource.meta}} @onChange={{fn (mut @resource.meta)}} />
610
618
  </ContentPanel>
611
- </div>
619
+
620
+ <RegistryYield @registry="fleet-ops:component:vehicle:form:end" as |RegistryComponent|>
621
+ <RegistryComponent @resource={{@resource}} @controller={{this.controller}} @permission={{get-write-permission @resource}} />
622
+ </RegistryYield>
623
+ </div>
@@ -90,6 +90,10 @@ export default class SettingsMapController extends Controller {
90
90
  this.mapManager.setActiveProvider(this.mapProvider);
91
91
  }
92
92
 
93
+ if (this.mapProvider === 'google' && typeof this.mapManager?.applyViewSettingsFromSettings === 'function') {
94
+ yield this.mapManager.applyViewSettingsFromSettings();
95
+ }
96
+
93
97
  this.notifications.success(this.intl.t('settings.map.settings-saved'));
94
98
  } catch (error) {
95
99
  this.notifications.serverError(error);
@@ -116,6 +116,7 @@ export default {
116
116
 
117
117
  // Create registries
118
118
  this.createRegistries(registryService);
119
+ registryService.registerRenderableComponent('ai:action-preview:fleet-ops.create_order', new ExtensionComponent('@fleetbase/fleetops-engine', 'ai/create-order-preview'));
119
120
 
120
121
  // // Register console home guidance
121
122
  // this.registerHomeComponents(registryService);
@@ -368,6 +369,13 @@ export default {
368
369
  'fleet-ops:component:place:form:details',
369
370
  'fleet-ops:component:vehicle:form',
370
371
  'fleet-ops:component:vehicle:form:details',
372
+ 'fleet-ops:component:vehicle:form:after-details',
373
+ 'fleet-ops:component:vehicle:form:start',
374
+ 'fleet-ops:component:vehicle:form:end',
375
+ 'fleet-ops:component:vehicle:card:header:start',
376
+ 'fleet-ops:component:vehicle:card:header:end',
377
+ 'fleet-ops:component:vehicle:card:footer:start',
378
+ 'fleet-ops:component:vehicle:card:footer:end',
371
379
  'fleet-ops:component:vendor:form:edit',
372
380
  'fleet-ops:component:vendor:form:edit:details',
373
381
  'fleet-ops:component:vendor:form:create',
@@ -392,6 +400,12 @@ export default {
392
400
  'fleet-ops:contextmenu:driver',
393
401
  'fleet-ops:component:order:details',
394
402
  'fleet-ops:component:order:form',
403
+ 'fleet-ops:component:order:form:start',
404
+ 'fleet-ops:component:order:form:end',
405
+ 'fleet-ops:component:order:form:details',
406
+ 'fleet-ops:component:order:form:details:start',
407
+ 'fleet-ops:component:order:form:details:end',
408
+ 'fleet-ops:component:order:form:details:after-details',
395
409
  'fleet-ops:component:order:form:payload:entity',
396
410
  'fleet-ops:component:order:form:payload:entity:form',
397
411
  'fleet-ops:template:settings:routing',
@@ -24,6 +24,6 @@ export default class ManagementFleetsIndexDetailsRoute extends Route {
24
24
  }
25
25
 
26
26
  model({ public_id }) {
27
- return this.store.queryRecord('fleet', { public_id, single: true, with: ['parent_fleet', 'service_area', 'zone'] });
27
+ return this.store.queryRecord('fleet', { public_id, single: true, with: ['parent_fleet', 'service_area', 'zone', 'subfleets', 'drivers', 'vehicles'] });
28
28
  }
29
29
  }
@@ -62,6 +62,16 @@ const DEFAULT_GOOGLE_MAP_STYLES = [
62
62
  },
63
63
  ];
64
64
 
65
+ function buildGoogleMapStyles({ showTransitLayer = false, googleOptions = {} } = {}) {
66
+ const baseStyles = showTransitLayer ? DEFAULT_GOOGLE_MAP_STYLES.filter((style) => style.featureType !== 'transit') : DEFAULT_GOOGLE_MAP_STYLES;
67
+
68
+ if (googleOptions.styles === undefined) {
69
+ return baseStyles;
70
+ }
71
+
72
+ return [...baseStyles, ...googleOptions.styles];
73
+ }
74
+
65
75
  function buildWaypointMarkerContent(label, color) {
66
76
  const wrapper = document.createElement('div');
67
77
  wrapper.className = 'fleetops-map-marker';
@@ -166,7 +176,10 @@ export default class GoogleMapsAdapter extends MapAdapterInterface {
166
176
  const { AdvancedMarkerElement } = await googleMaps.maps.importLibrary('marker');
167
177
  this._advancedMarkerElementClass = AdvancedMarkerElement;
168
178
  const mergedGoogleOptions = options.googleOptions ?? {};
169
- const mergedMapStyles = mergedGoogleOptions.styles === undefined ? DEFAULT_GOOGLE_MAP_STYLES : [...DEFAULT_GOOGLE_MAP_STYLES, ...mergedGoogleOptions.styles];
179
+ const mergedMapStyles = buildGoogleMapStyles({
180
+ showTransitLayer: options.showTransitLayer,
181
+ googleOptions: mergedGoogleOptions,
182
+ });
170
183
  const mapId = options.mapId ?? mergedGoogleOptions.mapId ?? this.#getConfig()?.googleMaps?.mapId ?? this.#getConfig()?.GOOGLE_MAPS_MAP_ID ?? null;
171
184
  this._supportsAdvancedMarkers = Boolean(mapId && mapId !== 'FLEETOPS_MAP');
172
185
 
@@ -182,7 +195,7 @@ export default class GoogleMapsAdapter extends MapAdapterInterface {
182
195
  styles: mergedMapStyles,
183
196
  });
184
197
 
185
- this.#syncGoogleLayers(options);
198
+ await this.applyViewSettings(options);
186
199
  await this.#initDrawingManager();
187
200
 
188
201
  debug('[GoogleMapsAdapter] Map initialized');
@@ -252,6 +265,47 @@ export default class GoogleMapsAdapter extends MapAdapterInterface {
252
265
  google.maps.event.trigger(this._map, 'resize');
253
266
  }
254
267
 
268
+ async applyViewSettings(options = {}) {
269
+ if (!this._map) return;
270
+
271
+ const googleMaps = window.google;
272
+ const { TrafficLayer, TransitLayer } = await googleMaps.maps.importLibrary('maps');
273
+ const mapTypeId = options.mapTypeId ?? googleMaps.maps.MapTypeId.ROADMAP;
274
+ const showTrafficLayer = Boolean(options.showTrafficLayer);
275
+ const showTransitLayer = Boolean(options.showTransitLayer);
276
+ const googleOptions = options.googleOptions ?? {};
277
+
278
+ this._map.setMapTypeId?.(mapTypeId);
279
+ this._map.setOptions?.({
280
+ styles: buildGoogleMapStyles({
281
+ showTransitLayer,
282
+ googleOptions,
283
+ }),
284
+ });
285
+
286
+ if (showTrafficLayer) {
287
+ if (!this._trafficLayer) {
288
+ this._trafficLayer = new TrafficLayer();
289
+ }
290
+ this._trafficLayer.setMap(this._map);
291
+ } else if (this._trafficLayer) {
292
+ this._trafficLayer.setMap(null);
293
+ this._trafficLayer = null;
294
+ }
295
+
296
+ if (showTransitLayer) {
297
+ if (!this._transitLayer) {
298
+ this._transitLayer = new TransitLayer();
299
+ }
300
+ this._transitLayer.setMap(this._map);
301
+ } else if (this._transitLayer) {
302
+ this._transitLayer.setMap(null);
303
+ this._transitLayer = null;
304
+ }
305
+
306
+ debug(`[GoogleMapsAdapter] Applied view settings: mapTypeId=${mapTypeId}, traffic=${showTrafficLayer}, transit=${showTransitLayer}`);
307
+ }
308
+
255
309
  // ─── Viewport ──────────────────────────────────────────────────────────────
256
310
 
257
311
  setCenter(lat, lng, zoom) {
@@ -1195,20 +1249,6 @@ export default class GoogleMapsAdapter extends MapAdapterInterface {
1195
1249
  }
1196
1250
  }
1197
1251
 
1198
- #syncGoogleLayers(options = {}) {
1199
- if (!this._map) return;
1200
-
1201
- if (options.showTrafficLayer) {
1202
- this._trafficLayer = new google.maps.TrafficLayer();
1203
- this._trafficLayer.setMap(this._map);
1204
- }
1205
-
1206
- if (options.showTransitLayer) {
1207
- this._transitLayer = new google.maps.TransitLayer();
1208
- this._transitLayer.setMap(this._map);
1209
- }
1210
- }
1211
-
1212
1252
  #ensureDrawToolbar() {
1213
1253
  if (this._drawControlEl || !this._map) return;
1214
1254
 
@@ -170,6 +170,18 @@ export default class MapManagerService extends Service {
170
170
  this.adapter?.invalidateSize();
171
171
  }
172
172
 
173
+ @action applyViewSettingsFromSettings() {
174
+ if (this.providerName !== 'google' || typeof this.adapter?.applyViewSettings !== 'function') {
175
+ return;
176
+ }
177
+
178
+ return this.adapter.applyViewSettings({
179
+ mapTypeId: this.mapSettings.googleMapsMapType,
180
+ showTrafficLayer: this.mapSettings.showGoogleMapsTrafficLayer,
181
+ showTransitLayer: this.mapSettings.showGoogleMapsTransitLayer,
182
+ });
183
+ }
184
+
173
185
  /**
174
186
  * Set the live map component reference.
175
187
  *