@fleetbase/fleetops-engine 0.3.8 → 0.4.0

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 (58) hide show
  1. package/addon/components/contact-form-panel.hbs +4 -1
  2. package/addon/components/contact-panel.hbs +1 -1
  3. package/addon/components/driver-form-panel.hbs +5 -12
  4. package/addon/components/driver-panel.hbs +4 -1
  5. package/addon/components/fleet-form-panel.hbs +4 -1
  6. package/addon/components/fleet-listing-panel.hbs +2 -2
  7. package/addon/components/fleet-panel.hbs +1 -1
  8. package/addon/components/fuel-report-form-panel.hbs +4 -1
  9. package/addon/components/fuel-report-panel.hbs +4 -1
  10. package/addon/components/issue-form-panel.hbs +4 -1
  11. package/addon/components/issue-panel.hbs +4 -1
  12. package/addon/components/layout/fleet-ops-sidebar/driver-listing.hbs +5 -0
  13. package/addon/components/layout/fleet-ops-sidebar/driver-listing.js +122 -0
  14. package/addon/components/layout/fleet-ops-sidebar/fleet-listing.hbs +2 -2
  15. package/addon/components/layout/fleet-ops-sidebar/fleet-listing.js +69 -49
  16. package/addon/components/layout/fleet-ops-sidebar.hbs +1 -1
  17. package/addon/components/layout/fleet-ops-sidebar.js +4 -3
  18. package/addon/components/live-map.hbs +90 -9
  19. package/addon/components/live-map.js +28 -11
  20. package/addon/components/map-container/toolbar/zones-panel.js +16 -13
  21. package/addon/components/place-form-panel.hbs +4 -1
  22. package/addon/components/place-panel.hbs +4 -1
  23. package/addon/components/settings-window.hbs +1 -1
  24. package/addon/components/vehicle-form-panel.hbs +4 -1
  25. package/addon/components/vehicle-panel.hbs +4 -1
  26. package/addon/components/vendor-form-panel.hbs +4 -1
  27. package/addon/components/vendor-panel.hbs +4 -1
  28. package/addon/controllers/management/drivers/index.js +10 -90
  29. package/addon/controllers/management/vehicles/index.js +11 -21
  30. package/addon/controllers/operations/orders/index/new.js +5 -5
  31. package/addon/controllers/operations/orders/index/view.js +9 -13
  32. package/addon/helpers/format-point.js +5 -2
  33. package/addon/helpers/point-to-coordinates.js +6 -2
  34. package/addon/services/context-panel.js +23 -18
  35. package/addon/services/driver-actions.js +107 -0
  36. package/addon/services/vehicle-actions.js +42 -0
  37. package/addon/styles/fleetops-engine.css +12 -2
  38. package/addon/templates/operations/orders/index/new.hbs +3 -3
  39. package/addon/utils/fly-to-leaflet-layer.js +7 -0
  40. package/app/components/layout/fleet-ops-sidebar/driver-listing.js +1 -0
  41. package/app/services/driver-actions.js +1 -0
  42. package/app/services/vehicle-actions.js +1 -0
  43. package/composer.json +2 -2
  44. package/extension.json +1 -1
  45. package/package.json +4 -4
  46. package/server/src/Console/Commands/FixDriverCompanies.php +65 -0
  47. package/server/src/Exceptions/IntegratedVendorException.php +44 -1
  48. package/server/src/Http/Controllers/Api/v1/DriverController.php +43 -34
  49. package/server/src/Http/Controllers/Internal/v1/DriverController.php +42 -5
  50. package/server/src/Http/Controllers/Internal/v1/LiveController.php +3 -10
  51. package/server/src/Http/Controllers/Internal/v1/NavigatorController.php +1 -1
  52. package/server/src/Http/Resources/v1/Driver.php +1 -1
  53. package/server/src/Providers/FleetOpsServiceProvider.php +1 -0
  54. package/server/src/Support/Flow.php +19 -2
  55. package/addon/helpers/extract-coordinates.js +0 -6
  56. package/addon/utils/extract-coordinates.js +0 -1
  57. package/app/helpers/extract-coordinates.js +0 -1
  58. package/app/utils/extract-coordinates.js +0 -1
@@ -1,10 +1,13 @@
1
1
  <Overlay
2
2
  @onLoad={{this.setOverlayContext}}
3
+ @onOpen={{this.onOpen}}
4
+ @onClose={{this.onClose}}
5
+ @onToggle={{this.onToggle}}
3
6
  @position="right"
4
7
  @noBackdrop={{true}}
5
8
  @fullHeight={{true}}
6
9
  @isResizeble={{or this.isResizable @isResizable}}
7
- @width={{or this.width @width "600px"}}
10
+ @width={{or this.width @width "570px"}}
8
11
  >
9
12
  <Overlay::Header @hideLeftSection={{true}} @actionsWrapperClass="flex-1 flex-col py-3" class="h-auto-i min-h-[127px]">
10
13
  <div class="flex flex-row items-center justify-between w-full mb-4">
@@ -1,4 +1,4 @@
1
- <Overlay @onLoad={{this.setOverlayContext}} @position="right" @noBackdrop={{true}} @fullHeight={{true}} @isResizable={{or this.isResizable @isResizable}} @width={{or this.width @width "600px"}}>
1
+ <Overlay @onLoad={{this.setOverlayContext}} @onOpen={{this.onOpen}} @onClose={{this.onClose}} @onToggle={{this.onToggle}} @position="right" @noBackdrop={{true}} @fullHeight={{true}} @isResizable={{or this.isResizable @isResizable}} @width={{or this.width @width "570px"}}>
2
2
  <Overlay::Header @hideLeftSection={{true}} @actionsWrapperClass="flex-1 flex-col py-3" class="h-auto-i min-h-[127px]">
3
3
  <div class="flex flex-row items-center justify-between w-full mb-4">
4
4
  <div class="flex flex-1 space-x-2">
@@ -1,4 +1,4 @@
1
- <Overlay @onLoad={{this.setOverlayContext}} @position="right" @noBackdrop={{true}} @fullHeight={{true}} @isResizeble={{or this.isResizable @isResizable}} @width={{or this.width @width "600px"}}>
1
+ <Overlay @onLoad={{this.setOverlayContext}} @onOpen={{this.onOpen}} @onClose={{this.onClose}} @onToggle={{this.onToggle}} @position="right" @noBackdrop={{true}} @fullHeight={{true}} @isResizeble={{or this.isResizable @isResizable}} @width={{or this.width @width "570px"}}>
2
2
  <Overlay::Header @hideLeftSection={{true}} @actionsWrapperClass="flex-1 flex-col py-3" class="h-auto-i min-h-[127px]">
3
3
  <div class="flex flex-row items-center justify-between w-full mb-4">
4
4
  <div class="flex flex-1 space-x-2">
@@ -65,7 +65,7 @@
65
65
  <InputGroup @name="Name" @wrapperClass="col-span-2">
66
66
  <Input @value={{this.driver.name}} @type="text" class="w-full form-input" placeholder="Name" />
67
67
  </InputGroup>
68
-
68
+
69
69
  <InputGroup @name="Internal ID">
70
70
  <Input @value={{this.driver.internal_id}} @type="text" class="w-full form-input" placeholder="Internal ID" />
71
71
  </InputGroup>
@@ -109,16 +109,9 @@
109
109
  </PowerSelect>
110
110
  </div>
111
111
  </InputGroup>
112
- <InputGroup @name="Coordinates" @wrapperClass="col-span-2">
113
- <CoordinatesInput
114
- @value={{this.driver.location}}
115
- @onChange={{this.onCoordinatesChanged}}
116
- @onGeocode={{this.onAutocomplete}}
117
- @onUpdatedFromMap={{this.onCoordinatesChanged}}
118
- @onInit={{this.setCoordinatesInput}}
119
- @renderInPlace={{false}}
120
- />
121
- </InputGroup>
112
+ <InputGroup @name="Coordinates" @wrapperClass="col-span-2">
113
+ <CoordinatesInput @value={{this.driver.location}} @onChange={{this.onCoordinatesChanged}} @onGeocode={{this.onAutocomplete}} @onUpdatedFromMap={{this.onCoordinatesChanged}} @onInit={{this.setCoordinatesInput}} @renderInPlace={{false}} />
114
+ </InputGroup>
122
115
  </div>
123
116
  </div>
124
117
  </Overlay::Body>
@@ -1,10 +1,13 @@
1
1
  <Overlay
2
2
  @onLoad={{this.setOverlayContext}}
3
+ @onOpen={{this.onOpen}}
4
+ @onClose={{this.onClose}}
5
+ @onToggle={{this.onToggle}}
3
6
  @position="right"
4
7
  @noBackdrop={{true}}
5
8
  @fullHeight={{true}}
6
9
  @isResizeble={{or this.isResizable @isResizable}}
7
- @width={{or this.width @width "600px"}}
10
+ @width={{or this.width @width "570px"}}
8
11
  >
9
12
  <Overlay::Header @hideLeftSection={{true}} @actionsWrapperClass="flex-1 flex-col py-3" class="h-auto-i min-h-[127px]">
10
13
  <div class="flex flex-row items-center justify-between w-full mb-4">
@@ -1,10 +1,13 @@
1
1
  <Overlay
2
2
  @onLoad={{this.setOverlayContext}}
3
+ @onOpen={{this.onOpen}}
4
+ @onClose={{this.onClose}}
5
+ @onToggle={{this.onToggle}}
3
6
  @position="right"
4
7
  @noBackdrop={{true}}
5
8
  @fullHeight={{true}}
6
9
  @isResizeble={{or this.isResizable @isResizable}}
7
- @width={{or this.width @width "600px"}}
10
+ @width={{or this.width @width "570px"}}
8
11
  >
9
12
  <Overlay::Header
10
13
  @title={{if this.fleet.public_id this.fleet.name "New Fleet"}}
@@ -1,8 +1,8 @@
1
1
  <Layout::Sidebar::Panel @open={{or @open true}} @title={{this.fleet.name}} @titleStatusRightText={{concat this.fleet.vehicles_online_count "/" this.fleet.vehicles_count}} @titleStatusRight="info" @hideRightTitleStatusDot={{true}} @titleStatusRightContainerClass="-mr-2" @wrapperClass="next-fleet-summary-fleet fleet-depth-{{@depth}}">
2
2
  {{#each this.fleet.subfleets as |subFleet|}}
3
- <FleetListingPanel @fleet={{subFleet}} @onVehicleClicked={{this.onVehicleClicked}} @depth={{add @depth 1}} />
3
+ <FleetListingPanel @fleet={{subFleet}} @onVehicleClicked={{this.onVehicleClicked}} @depth={{add @depth 1}} @itemDropdownButton={{@itemDropdownButton}} @itemDropdownButtonActions={{@itemDropdownButtonActions}} />
4
4
  {{/each}}
5
5
  {{#each this.fleet.vehicles as |vehicle|}}
6
- <Layout::Sidebar::Item @icon="circle" @iconSize="2xs" @iconClass={{if vehicle.online "text-green-500" "text-yellow-200"}} @onClick={{fn this.onVehicleClicked vehicle}} @rightSideComponent={{@itemRightSideComponent}} @rightSideComponentContext={{@itemRightSideComponentContext}} @rightSideStatus={{@itemRightSideStatus}} @rightSideStatusText={{@itemRightSideStatusText}} @hideRightSideStatusDot={{@itemHideRightSideStatusDot}} @rightSideStatusClass={{@itemRightSideStatusClass}} @disableRightSideStatusHumanize={{@itemDisableRightSideStatusHumanize}} @dropdownButton={{@itemDropdownButton}} @dropdownButtonActions={{@itemDropdownButtonActions}} @dropdownButtonIcon={{@itemDropdownButtonIcon}} @dropdownButtonIconPrefix={{@itemDropdownButtonIconPrefix}} @dropdownButtonText={{@itemDropdownButtonText}} @dropdownButtonCalculatePosition={{@itemDropdownButtonCalculatePosition}} @dropdownButtonRenderInPlace={{@itemDropdownButtonRenderInPlace}} @dropdownButtonMenuLabel={{@itemDropdownButtonMenuLabel}} @dropdownButtonWrapperClass={{@itemDropdownButtonWrapperClass}} @dropdownButtonTriggerClass={{@itemDropdownButtonTriggerClass}} @registerDropdownButtonAPI={{@itemRegisterDropdownButtonAPI}}>{{vehicle.displayName}}</Layout::Sidebar::Item>
6
+ <Layout::Sidebar::Item @icon="circle" @iconSize="2xs" @iconClass={{if vehicle.online "text-green-500" "text-yellow-200"}} @onClick={{fn this.onVehicleClicked vehicle}} @rightSideComponent={{@itemRightSideComponent}} @rightSideComponentContext={{@itemRightSideComponentContext}} @rightSideStatus={{@itemRightSideStatus}} @rightSideStatusText={{@itemRightSideStatusText}} @hideRightSideStatusDot={{@itemHideRightSideStatusDot}} @rightSideStatusClass={{@itemRightSideStatusClass}} @disableRightSideStatusHumanize={{@itemDisableRightSideStatusHumanize}} @dropdownButton={{@itemDropdownButton}} @dropdownButtonActions={{@itemDropdownButtonActions}} @dropdownContext={{vehicle}} @dropdownButtonIcon={{@itemDropdownButtonIcon}} @dropdownButtonIconPrefix={{@itemDropdownButtonIconPrefix}} @dropdownButtonText={{@itemDropdownButtonText}} @dropdownButtonCalculatePosition={{@itemDropdownButtonCalculatePosition}} @dropdownButtonRenderInPlace={{@itemDropdownButtonRenderInPlace}} @dropdownButtonMenuLabel={{vehicle.displayName}} @dropdownButtonWrapperClass={{@itemDropdownButtonWrapperClass}} @dropdownButtonTriggerClass={{@itemDropdownButtonTriggerClass}} @registerDropdownButtonAPI={{@itemRegisterDropdownButtonAPI}}>{{vehicle.displayName}}</Layout::Sidebar::Item>
7
7
  {{/each}}
8
8
  </Layout::Sidebar::Panel>
@@ -1,4 +1,4 @@
1
- <Overlay @onLoad={{this.setOverlayContext}} @position="right" @noBackdrop={{true}} @fullHeight={{true}} @isResizeble={{or this.isResizable @isResizable}} @width={{or this.width @width "600px"}}>
1
+ <Overlay @onLoad={{this.setOverlayContext}} @onOpen={{this.onOpen}} @onClose={{this.onClose}} @onToggle={{this.onToggle}} @position="right" @noBackdrop={{true}} @fullHeight={{true}} @isResizeble={{or this.isResizable @isResizable}} @width={{or this.width @width "570px"}}>
2
2
  <Overlay::Header @title={{this.fleet.name}} @status={{this.fleet.public_id}} @disableBadgeHumanize={{true}} @hideStatusDot={{true}} @createdAt={{this.fleet.createdAt}}>
3
3
  <Button @type="default" @icon="pen" @helpText="Edit fleet" @onClick={{this.onEdit}} @wrapperClass="mr-2" />
4
4
  <Button @type="default" @icon="times" @helpText={{if this.fleet.id "Cancel edit fleet" "Cancel new fleet"}} @onClick={{this.onPressCancel}} />
@@ -1,10 +1,13 @@
1
1
  <Overlay
2
2
  @onLoad={{this.setOverlayContext}}
3
+ @onOpen={{this.onOpen}}
4
+ @onClose={{this.onClose}}
5
+ @onToggle={{this.onToggle}}
3
6
  @position="right"
4
7
  @noBackdrop={{true}}
5
8
  @fullHeight={{true}}
6
9
  @isResizeble={{or this.isResizable @isResizable}}
7
- @width={{or this.width @width "600px"}}
10
+ @width={{or this.width @width "570px"}}
8
11
  >
9
12
  <Overlay::Header
10
13
  @title={{if this.fuelReport.public_id this.fuelReport.name "New Fuel Report"}}
@@ -1,10 +1,13 @@
1
1
  <Overlay
2
2
  @onLoad={{this.setOverlayContext}}
3
+ @onOpen={{this.onOpen}}
4
+ @onClose={{this.onClose}}
5
+ @onToggle={{this.onToggle}}
3
6
  @position="right"
4
7
  @noBackdrop={{true}}
5
8
  @fullHeight={{true}}
6
9
  @isResizable={{or this.isResizable @isResizable}}
7
- @width={{or this.width @width "600px"}}
10
+ @width={{or this.width @width "570px"}}
8
11
  >
9
12
  <Overlay::Header @title="Fuel Report Details" @status={{this.fuelReport.public_id}} @hideStatusDot={{true}} @createdAt={{this.fuelReport.createdAt}}>
10
13
  <Button @type="default" @icon="pen" @helpText="Edit fuel report" @onClick={{this.onEdit}} @wrapperClass="mr-2" />
@@ -1,10 +1,13 @@
1
1
  <Overlay
2
2
  @onLoad={{this.setOverlayContext}}
3
+ @onOpen={{this.onOpen}}
4
+ @onClose={{this.onClose}}
5
+ @onToggle={{this.onToggle}}
3
6
  @position="right"
4
7
  @noBackdrop={{true}}
5
8
  @fullHeight={{true}}
6
9
  @isResizable={{or this.isResizable @isResizable}}
7
- @width={{or this.width @width "600px"}}
10
+ @width={{or this.width @width "570px"}}
8
11
  >
9
12
  <Overlay::Header
10
13
  @title={{if this.issue.public_id this.issue.name "New Issue"}}
@@ -1,10 +1,13 @@
1
1
  <Overlay
2
2
  @onLoad={{this.setOverlayContext}}
3
+ @onOpen={{this.onOpen}}
4
+ @onClose={{this.onClose}}
5
+ @onToggle={{this.onToggle}}
3
6
  @position="right"
4
7
  @noBackdrop={{true}}
5
8
  @fullHeight={{true}}
6
9
  @isResizable={{or this.isResizable @isResizable}}
7
- @width={{or this.width @width "600px"}}
10
+ @width={{or this.width @width "570px"}}
8
11
  >
9
12
  <Overlay::Header @title="Issue Details" @status={{this.issue.public_id}} @hideStatusDot={{true}} @createdAt={{this.issue.createdAt}}>
10
13
  <Button @type="default" @icon="pen" @helpText="Edit Issue" @onClick={{this.onEdit}} @wrapperClass="mr-2" />
@@ -0,0 +1,5 @@
1
+ <Layout::Sidebar::Panel @open={{true}} @title={{@title}} @titleIcon={{@icon}} @hideCaret={{true}} @toggleOnCaretOnly={{true}} @onClickPanelTitle={{this.transitionToRoute}} @wrapperClass="next-driver-listing">
2
+ {{#each this.drivers as |driver|}}
3
+ <Layout::Sidebar::Item @icon="circle" @iconSize="2xs" @iconClass={{if driver.online "text-green-500" "text-yellow-200"}} @onClick={{fn this.onDriverClicked driver}} class="driver-nav-item" @dropdownButton={{true}} @dropdownButtonActions={{this.dropdownButtonActions}} @dropdownContext={{driver}} @dropdownButtonIcon={{@itemDropdownButtonIcon}} @dropdownButtonIconPrefix={{@itemDropdownButtonIconPrefix}} @dropdownButtonText={{@itemDropdownButtonText}} @dropdownButtonCalculatePosition={{@itemDropdownButtonCalculatePosition}} @dropdownButtonRenderInPlace={{true}} @dropdownButtonMenuLabel={{driver.name}} @dropdownButtonWrapperClass={{@itemDropdownButtonWrapperClass}} @dropdownButtonTriggerClass={{@itemDropdownButtonTriggerClass}} @registerDropdownButtonAPI={{@itemRegisterDropdownButtonAPI}} @rightSideComponent={{@itemRightSideComponent}} @rightSideComponentContext={{@itemRightSideComponentContext}} @rightSideStatus={{@itemRightSideStatus}} @rightSideStatusText={{@itemRightSideStatusText}} @hideRightSideStatusDot={{@itemHideRightSideStatusDot}} @rightSideStatusClass={{@itemRightSideStatusClass}} @disableRightSideStatusHumanize={{@itemDisableRightSideStatusHumanize}}>{{driver.name}}</Layout::Sidebar::Item>
4
+ {{/each}}
5
+ </Layout::Sidebar::Panel>
@@ -0,0 +1,122 @@
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 { later } from '@ember/runloop';
6
+ import { task } from 'ember-concurrency-decorators';
7
+
8
+ export default class LayoutFleetOpsSidebarDriverListingComponent extends Component {
9
+ @service store;
10
+ @service universe;
11
+ @service contextPanel;
12
+ @service driverActions;
13
+ @service hostRouter;
14
+ @tracked drivers = [];
15
+
16
+ constructor() {
17
+ super(...arguments);
18
+ this.fetchDrivers.perform();
19
+ }
20
+
21
+ dropdownButtonActions = [
22
+ {
23
+ label: 'View driver details...',
24
+ onClick: (driver) => {
25
+ this.contextPanel.focus(driver);
26
+ },
27
+ },
28
+ {
29
+ label: 'Edit driver details...',
30
+ onClick: (driver) => {
31
+ this.contextPanel.focus(driver, 'editing');
32
+ },
33
+ },
34
+ {
35
+ separator: true,
36
+ },
37
+ {
38
+ label: 'Assign order to driver...',
39
+ onClick: (driver) => {
40
+ this.driverActions.assignOrder(driver);
41
+ },
42
+ },
43
+ {
44
+ label: 'Assign vehicle to driver...',
45
+ onClick: (driver) => {
46
+ this.driverActions.assignVehicle(driver);
47
+ },
48
+ },
49
+ {
50
+ label: 'Locate driver on map...',
51
+ onClick: (driver) => {
52
+ // If currently on the operations dashboard focus driver on the map
53
+ if (typeof this.hostRouter.currentRouteName === 'string' && this.hostRouter.currentRouteName.startsWith('console.fleet-ops.operations.orders')) {
54
+ return this.onDriverClicked(driver);
55
+ }
56
+
57
+ this.driverActions.locate(driver);
58
+ },
59
+ },
60
+ {
61
+ separator: true,
62
+ },
63
+ {
64
+ label: 'Delete driver...',
65
+ onClick: (driver) => {
66
+ this.driverActions.delete(driver);
67
+ },
68
+ },
69
+ ];
70
+
71
+ @action transitionToRoute(toggleApiContext) {
72
+ if (typeof this.args.route === 'string') {
73
+ if (typeof this.hostRouter.currentRouteName === 'string' && this.hostRouter.currentRouteName.startsWith('console.fleet-ops.management.fleets.index')) {
74
+ if (typeof toggleApiContext.toggle === 'function') {
75
+ toggleApiContext.toggle();
76
+ }
77
+ }
78
+
79
+ this.hostRouter.transitionTo(this.args.route);
80
+ }
81
+ }
82
+
83
+ @action onDriverClicked(driver) {
84
+ // Transition to dashboard/map display
85
+ return this.hostRouter.transitionTo('console.fleet-ops.operations.orders.index', { queryParams: { layout: 'map' } }).then(() => {
86
+ // Focus vehicle on live map
87
+ this.focusDriverOnMap(driver);
88
+
89
+ // Fire callback
90
+ if (typeof this.args.onFocusDriver === 'function') {
91
+ this.args.onFocusDriver(driver);
92
+ }
93
+ });
94
+ }
95
+
96
+ focusDriverOnMap(driver) {
97
+ const liveMap = this.universe.get('component:fleet-ops:live-map');
98
+
99
+ if (liveMap) {
100
+ if (liveMap.contextPanel) {
101
+ liveMap.contextPanel.clear();
102
+ }
103
+
104
+ liveMap.showAll();
105
+ liveMap.focusLayerByRecord(driver, 16, {
106
+ onAfterFocusWithRecord: function () {
107
+ later(
108
+ this,
109
+ () => {
110
+ liveMap.onDriverClicked(driver);
111
+ },
112
+ 1200
113
+ );
114
+ },
115
+ });
116
+ }
117
+ }
118
+
119
+ @task *fetchDrivers() {
120
+ this.drivers = yield this.store.query('driver', { limit: 20 });
121
+ }
122
+ }
@@ -1,5 +1,5 @@
1
- <Layout::Sidebar::Panel @open={{true}} @titleIcon={{@icon}} @title="Fleets" @wrapperClass="next-fleet-summary">
1
+ <Layout::Sidebar::Panel @open={{true}} @title={{@title}} @titleIcon={{@icon}} @hideCaret={{true}} @toggleOnCaretOnly={{true}} @onClickPanelTitle={{this.transitionToRoute}} @wrapperClass="next-fleet-summary">
2
2
  {{#each this.fleets as |fleet|}}
3
- <FleetListingPanel @fleet={{fleet}} @onVehicleClicked={{this.onFocusVehicle}} @depth={{1}} @open={{true}} />
3
+ <FleetListingPanel @fleet={{fleet}} @onVehicleClicked={{this.onVehicleClicked}} @depth={{1}} @open={{true}} @itemDropdownButton={{true}} @itemDropdownButtonActions={{this.dropdownButtonActions}} />
4
4
  {{/each}}
5
5
  </Layout::Sidebar::Panel>
@@ -2,62 +2,98 @@ import Component from '@glimmer/component';
2
2
  import { tracked } from '@glimmer/tracking';
3
3
  import { inject as service } from '@ember/service';
4
4
  import { action } from '@ember/object';
5
- import { isArray } from '@ember/array';
6
5
  import { later } from '@ember/runloop';
6
+ import { task } from 'ember-concurrency-decorators';
7
7
 
8
8
  export default class LayoutFleetOpsSidebarFleetListingComponent extends Component {
9
9
  @service store;
10
- @service notifications;
11
10
  @service universe;
11
+ @service contextPanel;
12
+ @service vehicleActions;
12
13
  @service hostRouter;
13
14
  @tracked fleets = [];
14
- @tracked isLoading = false;
15
15
 
16
16
  constructor() {
17
17
  super(...arguments);
18
- this.loadFleetsWithVehicles();
19
- this.listenForFleetChanges();
18
+ this.fetchFleets.perform();
19
+ this.listenForChanges();
20
20
  }
21
21
 
22
- @action transitionHome() {
23
- return this.hostRouter.transitionTo('console.fleet-ops.operations.orders.index', { queryParams: { layout: 'map' } });
24
- }
22
+ dropdownButtonActions = [
23
+ {
24
+ label: 'View vehicle details...',
25
+ onClick: (vehicle) => {
26
+ this.contextPanel.focus(vehicle);
27
+ },
28
+ },
29
+ {
30
+ label: 'Edit vehicle...',
31
+ onClick: (vehicle) => {
32
+ this.contextPanel.focus(vehicle, 'editing');
33
+ },
34
+ },
35
+ {
36
+ label: 'Locate vehicle...',
37
+ onClick: (vehicle) => {
38
+ // If currently on the operations dashboard focus driver on the map
39
+ if (typeof this.hostRouter.currentRouteName === 'string' && this.hostRouter.currentRouteName.startsWith('console.fleet-ops.operations.orders')) {
40
+ return this.onVehicleClicked(vehicle);
41
+ }
42
+
43
+ this.vehicleActions.locate(vehicle);
44
+ },
45
+ },
46
+ {
47
+ separator: true,
48
+ },
49
+ {
50
+ label: 'Delete vehicle...',
51
+ onClick: (vehicle) => {
52
+ this.vehicleActions.delete(vehicle);
53
+ },
54
+ },
55
+ ];
25
56
 
26
- @action async onFocusVehicle(vehicle) {
27
- const { onFocusVehicle } = this.args;
28
- // const { currentRouteName } = this.hostRouter;
29
- // const isOperationsRouteActive = typeof currentRouteName === 'string' && currentRouteName.includes('fleet-ops.operations.orders');
57
+ @action transitionToRoute(toggleApiContext) {
58
+ if (typeof this.args.route === 'string') {
59
+ if (typeof this.hostRouter.currentRouteName === 'string' && this.hostRouter.currentRouteName.startsWith('console.fleet-ops.management.fleets.index')) {
60
+ if (typeof toggleApiContext.toggle === 'function') {
61
+ toggleApiContext.toggle();
62
+ }
63
+ }
30
64
 
31
- // transition to dashboard/map display
32
- try {
33
- await this.hostRouter.transitionTo('console.fleet-ops.operations.orders.index', { queryParams: { layout: 'map' } });
34
- } catch (error) {
35
- // silent
65
+ this.hostRouter.transitionTo(this.args.route);
36
66
  }
67
+ }
37
68
 
38
- // focus vehicle on live map
39
- this.focusVehicleOnLiveMap(vehicle);
69
+ @action onVehicleClicked(vehicle) {
70
+ // Transition to dashboard/map display
71
+ return this.hostRouter.transitionTo('console.fleet-ops.operations.orders.index', { queryParams: { layout: 'map' } }).then(() => {
72
+ // Focus vehicle on live map
73
+ this.focusVehicleOnMap(vehicle);
40
74
 
41
- if (typeof onFocusVehicle === 'function') {
42
- onFocusVehicle(vehicle);
43
- }
75
+ // Fire callback
76
+ if (typeof this.args.onFocusVehicle === 'function') {
77
+ this.args.onFocusVehicle(vehicle);
78
+ }
79
+ });
44
80
  }
45
81
 
46
- @action focusVehicleOnLiveMap(vehicle) {
47
- const FleetOpsLiveMap = this.universe.get('FleetOpsLiveMap');
82
+ @action focusVehicleOnMap(vehicle) {
83
+ const liveMap = this.universe.get('component:fleet-ops:live-map');
48
84
 
49
- if (FleetOpsLiveMap) {
50
- if (FleetOpsLiveMap.contextPanel) {
51
- FleetOpsLiveMap.contextPanel.clear();
85
+ if (liveMap) {
86
+ if (liveMap.contextPanel) {
87
+ liveMap.contextPanel.clear();
52
88
  }
53
89
 
54
- FleetOpsLiveMap.showAll();
55
- FleetOpsLiveMap.focusLayerByRecord(vehicle, 16, {
90
+ liveMap.showAll();
91
+ liveMap.focusLayerByRecord(vehicle, 16, {
56
92
  onAfterFocusWithRecord: function () {
57
93
  later(
58
94
  this,
59
95
  () => {
60
- FleetOpsLiveMap.onVehicleClicked(vehicle);
96
+ liveMap.onVehicleClicked(vehicle);
61
97
  },
62
98
  600 * 2
63
99
  );
@@ -66,7 +102,7 @@ export default class LayoutFleetOpsSidebarFleetListingComponent extends Componen
66
102
  }
67
103
  }
68
104
 
69
- listenForFleetChanges() {
105
+ listenForChanges() {
70
106
  // when a vehicle is assigned/ or unassigned reload
71
107
  this.universe.on('fleet.vehicle.assigned', () => {
72
108
  this.loadFleetsWithVehicles();
@@ -78,23 +114,7 @@ export default class LayoutFleetOpsSidebarFleetListingComponent extends Componen
78
114
  });
79
115
  }
80
116
 
81
- loadFleetsWithVehicles() {
82
- this.isLoading = true;
83
- this.store
84
- .query('fleet', { with: ['vehicles', 'subfleets'], parents_only: true })
85
- .then((fleets) => {
86
- this.fleets = fleets.toArray().map(this.resolveFleetVehicles);
87
- })
88
- .catch((error) => {
89
- this.notifications.serverError(error);
90
- })
91
- .finally(() => {
92
- this.isLoading = false;
93
- });
94
- }
95
-
96
- @action resolveFleetVehicles(fleet) {
97
- fleet.vehicles = isArray(fleet.vehicles) ? fleet.vehicles.toArray() : [];
98
- return fleet;
117
+ @task *fetchFleets() {
118
+ this.fleets = yield this.store.query('fleet', { with: ['vehicles', 'subfleets'], parents_only: true });
99
119
  }
100
120
  }
@@ -12,7 +12,7 @@
12
12
  <Layout::Sidebar::Panel @open={{menuPanel.open}} @title={{menuPanel.title}}>
13
13
  {{#each menuPanel.items as |menuItem|}}
14
14
  {{#if menuItem.renderComponentInPlace}}
15
- {{component menuItem.component menuItem=menuItem title=@menuItem.title route=(concat this.routePrefix menuItem.route) icon=menuItem.icon rightSideComponent=menuItem.rightSideComponent rightSideComponentContext=menuItem.rightSideComponentContext rightSideStatus=menuItem.rightSideStatus rightSideStatusText=menuItem.rightSideStatusText hideRightSideStatusDot=menuItem.hideRightSideStatusDot rightSideStatusClass=menuItem.rightSideStatusClass disableRightSideStatusHumanize=menuItem.disableRightSideStatusHumanize dropdownButton=menuItem.dropdownButton dropdownButtonActions=menuItem.dropdownButtonActions dropdownButtonIcon=menuItem.dropdownButtonIcon dropdownButtonIconPrefix=menuItem.dropdownButtonIconPrefix dropdownButtonText=menuItem.dropdownButtonText dropdownButtonCalculatePosition=menuItem.dropdownButtonCalculatePosition dropdownButtonRenderInPlace=menuItem.dropdownButtonRenderInPlace dropdownButtonMenuLabel=menuItem.dropdownButtonMenuLabel dropdownButtonWrapperClass=menuItem.dropdownButtonWrapperClass dropdownButtonTriggerClass=menuItem.dropdownButtonTriggerClass registerDropdownButtonAPI=menuItem.registerDropdownButtonAPI}}
15
+ {{component menuItem.component menuItem=menuItem title=menuItem.title route=(concat this.routePrefix menuItem.route) icon=menuItem.icon rightSideComponent=menuItem.rightSideComponent rightSideComponentContext=menuItem.rightSideComponentContext rightSideStatus=menuItem.rightSideStatus rightSideStatusText=menuItem.rightSideStatusText hideRightSideStatusDot=menuItem.hideRightSideStatusDot rightSideStatusClass=menuItem.rightSideStatusClass disableRightSideStatusHumanize=menuItem.disableRightSideStatusHumanize dropdownButton=menuItem.dropdownButton dropdownButtonActions=menuItem.dropdownButtonActions dropdownButtonIcon=menuItem.dropdownButtonIcon dropdownButtonIconPrefix=menuItem.dropdownButtonIconPrefix dropdownButtonText=menuItem.dropdownButtonText dropdownButtonCalculatePosition=menuItem.dropdownButtonCalculatePosition dropdownButtonRenderInPlace=menuItem.dropdownButtonRenderInPlace dropdownButtonMenuLabel=menuItem.dropdownButtonMenuLabel dropdownButtonWrapperClass=menuItem.dropdownButtonWrapperClass dropdownButtonTriggerClass=menuItem.dropdownButtonTriggerClass registerDropdownButtonAPI=menuItem.registerDropdownButtonAPI}}
16
16
  {{else}}
17
17
  <Layout::Sidebar::Item @route={{concat this.routePrefix menuItem.route}} @icon={{menuItem.icon}} @rightSideComponent={{menuItem.rightSideComponent}} @rightSideComponentContext={{menuItem.rightSideComponentContext}} @rightSideStatus={{menuItem.rightSideStatus}} @rightSideStatusText={{menuItem.rightSideStatusText}} @hideRightSideStatusDot={{menuItem.hideRightSideStatusDot}} @rightSideStatusClass={{menuItem.rightSideStatusClass}} @disableRightSideStatusHumanize={{menuItem.disableRightSideStatusHumanize}} @dropdownButton={{menuItem.dropdownButton}} @dropdownButtonActions={{menuItem.dropdownButtonActions}} @dropdownButtonIcon={{menuItem.dropdownButtonIcon}} @dropdownButtonIconPrefix={{menuItem.dropdownButtonIconPrefix}} @dropdownButtonText={{menuItem.dropdownButtonText}} @dropdownButtonCalculatePosition={{menuItem.dropdownButtonCalculatePosition}} @dropdownButtonRenderInPlace={{menuItem.dropdownButtonRenderInPlace}} @dropdownButtonMenuLabel={{menuItem.dropdownButtonMenuLabel}} @dropdownButtonWrapperClass={{menuItem.dropdownButtonWrapperClass}} @dropdownButtonTriggerClass={{menuItem.dropdownButtonTriggerClass}} @registerDropdownButtonAPI={{menuItem.registerDropdownButtonAPI}}>{{menuItem.title}}</Layout::Sidebar::Item>
18
18
  {{/if}}
@@ -3,7 +3,8 @@ import { tracked } from '@glimmer/tracking';
3
3
  import { inject as service } from '@ember/service';
4
4
  import { action } from '@ember/object';
5
5
  import { isArray } from '@ember/array';
6
- // import FleetListingComponent from './fleet-ops-sidebar/fleet-listing';
6
+ import FleetListingComponent from './fleet-ops-sidebar/fleet-listing';
7
+ import DriverListingComponent from './fleet-ops-sidebar/driver-listing';
7
8
 
8
9
  /**
9
10
  * LayoutFleetOpsSidebarComponent
@@ -41,9 +42,9 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
41
42
  ];
42
43
 
43
44
  const resourcesItems = [
44
- { title: 'Drivers', icon: 'id-card', route: 'management.drivers' },
45
+ { title: 'Drivers', icon: 'id-card', route: 'management.drivers', renderComponentInPlace: true, component: DriverListingComponent },
45
46
  { title: 'Vehicles', icon: 'truck', route: 'management.vehicles' },
46
- { title: 'Fleets', icon: 'user-group', route: 'management.fleets' },
47
+ { title: 'Fleets', icon: 'user-group', route: 'management.fleets', renderComponentInPlace: true, component: FleetListingComponent },
47
48
  { title: 'Vendors', icon: 'warehouse', route: 'management.vendors' },
48
49
  { title: 'Contacts', icon: 'address-book', route: 'management.contacts' },
49
50
  { title: 'Places', icon: 'location-dot', route: 'management.places' },