@fleetbase/fleetops-engine 0.2.8 → 0.3.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 (88) hide show
  1. package/addon/components/context-panel.hbs +5 -0
  2. package/addon/components/context-panel.js +6 -0
  3. package/addon/components/driver-card-mini.js +34 -13
  4. package/addon/components/driver-form-panel.hbs +23 -23
  5. package/addon/components/driver-form-panel.js +96 -13
  6. package/addon/components/driver-panel.hbs +12 -12
  7. package/addon/components/driver-panel.js +125 -21
  8. package/addon/components/fleet-vehicle-listing.hbs +1 -1
  9. package/addon/components/live-map.hbs +20 -9
  10. package/addon/components/live-map.js +1255 -353
  11. package/addon/components/map-container/toolbar/visibility-control-panel.hbs +66 -0
  12. package/addon/components/map-container/toolbar/visibility-control-panel.js +74 -0
  13. package/addon/components/map-container/toolbar/zones-panel.hbs +10 -10
  14. package/addon/components/map-container/toolbar/zones-panel.js +90 -61
  15. package/addon/components/map-container/toolbar.hbs +12 -7
  16. package/addon/components/modals/vehicle-form.hbs +35 -35
  17. package/addon/components/settings-window.hbs +1 -1
  18. package/addon/components/vehicle-form-panel.hbs +25 -26
  19. package/addon/components/vehicle-form-panel.js +95 -12
  20. package/addon/components/vehicle-panel.hbs +12 -13
  21. package/addon/components/vehicle-panel.js +130 -20
  22. package/addon/controllers/management/drivers/index/details.js +53 -0
  23. package/addon/controllers/management/drivers/index/edit.js +77 -46
  24. package/addon/controllers/management/drivers/index/new.js +39 -79
  25. package/addon/controllers/management/drivers/index.js +17 -221
  26. package/addon/controllers/management/fleets/index/edit.js +1 -1
  27. package/addon/controllers/management/fleets/index/new.js +1 -1
  28. package/addon/controllers/management/fuel-reports/index.js +2 -2
  29. package/addon/controllers/management/vehicles/index/details.js +49 -0
  30. package/addon/controllers/management/vehicles/index/edit.js +104 -1
  31. package/addon/controllers/management/vehicles/index/new.js +71 -0
  32. package/addon/controllers/operations/orders/index/new.js +4 -6
  33. package/addon/controllers/operations/orders/index/view.js +22 -14
  34. package/addon/controllers/operations/orders/index.js +124 -50
  35. package/addon/controllers/operations/service-rates/index/edit.js +1 -1
  36. package/addon/controllers/operations/service-rates/index/new.js +1 -1
  37. package/addon/engine.js +6 -0
  38. package/addon/helpers/is-visibility-control-visible.js +18 -0
  39. package/addon/routes/application.js +1 -1
  40. package/addon/routes/home.js +1 -1
  41. package/addon/routes/management/drivers/index/details.js +5 -0
  42. package/addon/routes/operations/orders/index/view.js +4 -2
  43. package/addon/routes/operations/orders/index.js +5 -1
  44. package/addon/routes/operations/service-rates/index.js +1 -1
  45. package/addon/routes/operations/zones/index.js +1 -1
  46. package/addon/services/context-panel.js +173 -0
  47. package/addon/services/leaflet-contextmenu-manager.js +311 -0
  48. package/addon/services/leaflet-map-manager.js +61 -0
  49. package/addon/services/service-areas.js +242 -32
  50. package/addon/templates/application.hbs +2 -1
  51. package/addon/templates/management/drivers/index/details.hbs +1 -1
  52. package/addon/templates/management/drivers/index/edit.hbs +1 -1
  53. package/addon/templates/management/drivers/index/new.hbs +1 -1
  54. package/addon/templates/management/vehicles/index/details.hbs +1 -1
  55. package/addon/templates/management/vehicles/index/edit.hbs +1 -1
  56. package/addon/templates/management/vehicles/index/new.hbs +1 -1
  57. package/addon/templates/operations/orders/index/view.hbs +2 -2
  58. package/addon/templates/operations/orders/index.hbs +1 -1
  59. package/addon/utils/apply-context-component-arguments.js +31 -0
  60. package/addon/utils/context-component-callback.js +16 -0
  61. package/addon/utils/find-leaflet-layer.js +13 -0
  62. package/addon/utils/fly-to-leaflet-layer.js +25 -0
  63. package/addon/utils/get-leaflet-layer-by-id.js +12 -0
  64. package/addon/utils/layer-can-bind-contextmenu.js +3 -0
  65. package/app/components/context-panel.js +1 -0
  66. package/app/components/map-container/toolbar/visibility-control-panel.js +1 -0
  67. package/app/helpers/is-visibility-control-visible.js +1 -0
  68. package/app/services/context-panel.js +1 -0
  69. package/app/services/leaflet-contextmenu-manager.js +1 -0
  70. package/app/services/leaflet-map-manager.js +1 -0
  71. package/app/utils/apply-context-component-arguments.js +1 -0
  72. package/app/utils/context-component-callback.js +1 -0
  73. package/app/utils/find-leaflet-layer.js +1 -0
  74. package/app/utils/fly-to-leaflet-layer.js +1 -0
  75. package/app/utils/get-leaflet-layer-by-id.js +1 -0
  76. package/app/utils/layer-can-bind-contextmenu.js +1 -0
  77. package/composer.json +11 -1
  78. package/extension.json +1 -1
  79. package/package.json +3 -3
  80. package/server/migrations/2023_09_14_074512_update_vehicles_table.php +89 -88
  81. package/server/migrations/2023_10_25_093014_create_vehicle_device_events_table.php +56 -0
  82. package/server/src/Http/Controllers/Internal/v1/LiveController.php +7 -4
  83. package/server/src/Http/Filter/VehicleFilter.php +7 -0
  84. package/server/src/Http/Resources/v1/Vehicle.php +52 -39
  85. package/server/src/Models/VehicleDevice.php +17 -3
  86. package/server/src/Models/VehicleDeviceEvent.php +90 -0
  87. package/addon/services/map-manager.js +0 -3
  88. package/app/services/map-manager.js +0 -1
@@ -0,0 +1,5 @@
1
+ {{#if this.contextPanel.currentContextRegistry}}
2
+ {{#let this.contextPanel.currentContext this.contextPanel.currentContextRegistry this.contextPanel.currentContextComponentArguments as |model registry dynamicArgs|}}
3
+ {{component registry.component context=model dynamicArgs=dynamicArgs onPressCancel=this.contextPanel.clear options=this.contextPanel.contextOptions}}
4
+ {{/let}}
5
+ {{/if}}
@@ -0,0 +1,6 @@
1
+ import Component from '@glimmer/component';
2
+ import { inject as service } from '@ember/service';
3
+
4
+ export default class ContextPanelComponent extends Component {
5
+ @service contextPanel;
6
+ }
@@ -1,25 +1,46 @@
1
1
  import Component from '@glimmer/component';
2
- import { inject as controller } from '@ember/controller';
3
2
  import { inject as service } from '@ember/service';
4
3
  import { action } from '@ember/object';
5
4
 
5
+ /**
6
+ * DriverCardMiniComponent
7
+ *
8
+ * A mini-component responsible for displaying a specific driver's card.
9
+ *
10
+ * @extends Component
11
+ *
12
+ * @property {Service} store - Ember's store service.
13
+ * @property {Service} fetch - Service for fetching data.
14
+ * @property {Service} contextPanel - Service for context panel operations.
15
+ */
6
16
  export default class DriverCardMiniComponent extends Component {
7
- @controller('management.drivers.index') drivers;
17
+ /**
18
+ * Ember's store service.
19
+ * @type {Service}
20
+ */
8
21
  @service store;
9
22
 
10
- toModel(record, modelName) {
11
- // set id
12
- if (!record.id && record.uuid) {
13
- record.id = record.uuid;
14
- }
23
+ /**
24
+ * Fetch service.
25
+ * @type {Service}
26
+ */
27
+ @service fetch;
15
28
 
16
- const normalized = this.store.normalize(modelName, record);
17
- return this.store.push(normalized);
18
- }
29
+ /**
30
+ * ContextPanel service.
31
+ * @type {Service}
32
+ */
33
+ @service contextPanel;
19
34
 
35
+ /**
36
+ * View the details of a specific driver.
37
+ *
38
+ * @param {Object} record - The driver record to view.
39
+ * @action
40
+ * @returns {Promise} Resolves when the driver's details are focused in the context panel.
41
+ */
20
42
  @action viewDriver(record) {
21
- const driver = this.toModel(record, 'driver');
22
-
23
- return this.drivers.viewDriver(driver);
43
+ const driver = this.fetch.jsonToModel(record, 'driver');
44
+ return this.contextPanel.focus(driver);
24
45
  }
25
46
  }
@@ -1,43 +1,43 @@
1
- <Overlay @position="right" @noBackdrop={{true}} @fullHeight={{true}} @width="700">
1
+ <Overlay @onLoad={{this.setOverlayContext}} @position="right" @noBackdrop={{true}} @fullHeight={{true}} @isResizeble={{or this.isResizable @isResizable}} @width={{or this.width @width "600px"}}>
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">
5
- {{#if @driver.id}}
6
- <Button @type="default" @icon="id-card" @helpText="View driver details" @onClick={{this.viewDetails}} />
5
+ {{#if this.driver.id}}
6
+ <Button @type="default" @icon="id-card" @helpText="View driver details" @onClick={{this.onViewDetails}} />
7
7
  {{/if}}
8
8
  </div>
9
9
  <div class="flex flex-1 justify-end">
10
10
  <div class="mr-2">
11
- <Button @icon={{if @driver.id "save" "check"}} @type="primary" @text={{if @driver.id "Save Driver" "Create Driver"}} @onClick={{this.save}} />
11
+ <Button @icon={{if this.driver.id "save" "check"}} @type="primary" @text={{if this.driver.id "Save Driver" "Create Driver"}} @onClick={{this.save}} />
12
12
  </div>
13
- <Button @type="default" @icon="times" @helpText={{if @driver.id "Cancel edit driver" "Cancel new driver"}} @onClick={{@onPressCancel}} />
13
+ <Button @type="default" @icon="times" @helpText={{if this.driver.id "Cancel edit driver" "Cancel new driver"}} @onClick={{this.onPressCancel}} />
14
14
  </div>
15
15
  </div>
16
16
  <div class="flex flex-row justify-between w-full">
17
17
  <div class="flex flex-col flex-1 w-3/4">
18
18
  <div class="flex flex-row">
19
19
  <div class="w-14 flex items-center justify-start">
20
- <Image src={{@driver.photo_url}} @fallbackSrc={{config "defaultValues.driverImage"}} alt={{@driver.name}} height="48" width="48" class="h-12 w-12 rounded-lg shadow-sm" />
20
+ <Image src={{this.driver.photo_url}} @fallbackSrc={{config "defaultValues.driverImage"}} alt={{this.driver.name}} height="48" width="48" class="h-12 w-12 rounded-lg shadow-sm" />
21
21
  <Attach::Tooltip @class="clean" @animation="scale" @placement={{"top"}}>
22
- <InputInfo @text={{@driver.public_id}} />
22
+ <InputInfo @text={{this.driver.public_id}} />
23
23
  </Attach::Tooltip>
24
24
  </div>
25
25
  <div class="flex flex-col pt-2">
26
26
  <h1 class="text-gray-900 dark:text-white text-2xl">
27
- {{#if @driver.id}}
28
- {{@driver.name}}
27
+ {{#if this.driver.id}}
28
+ {{this.driver.name}}
29
29
  {{else}}
30
- {{#if @driver.name}}
31
- {{@driver.name}}
30
+ {{#if this.driver.name}}
31
+ {{this.driver.name}}
32
32
  {{else}}
33
33
  <span>New Driver</span>
34
34
  {{/if}}
35
35
  {{/if}}
36
36
  </h1>
37
37
  <div class="-mt-1">
38
- {{#if @driver.vehicle}}
38
+ {{#if this.driver.vehicle}}
39
39
  <div class="flex flex-row items-center">
40
- <span class="text-sm dark:text-gray-500 text-gray-700 mr-3">{{@driver.vehicle.displayName}}</span>
40
+ <span class="text-sm dark:text-gray-500 text-gray-700 mr-3">{{this.driver.vehicle.displayName}}</span>
41
41
  </div>
42
42
  {{else}}
43
43
  <div class="flex flex-row items-center">
@@ -49,7 +49,7 @@
49
49
  </div>
50
50
  </div>
51
51
  <div class="flex justify-end w-1/4">
52
- <Badge @status={{@driver.status}} />
52
+ <Badge @status={{this.driver.status}} />
53
53
  </div>
54
54
  </div>
55
55
  </Overlay::Header>
@@ -61,7 +61,7 @@
61
61
  <label>
62
62
  Name
63
63
  </label>
64
- <Input @value={{@driver.name}} @type="text" class="w-full form-input" placeholder="Name" />
64
+ <Input @value={{this.driver.name}} @type="text" class="w-full form-input" placeholder="Name" />
65
65
  </div>
66
66
  </div>
67
67
 
@@ -70,35 +70,35 @@
70
70
  <label>
71
71
  Internal ID
72
72
  </label>
73
- <Input @value={{@driver.internal_id}} @type="text" class="w-full form-input" placeholder="Internal ID" />
73
+ <Input @value={{this.driver.internal_id}} @type="text" class="w-full form-input" placeholder="Internal ID" />
74
74
  </div>
75
75
 
76
76
  <div class="input-group">
77
77
  <label>
78
78
  Drivers License
79
79
  </label>
80
- <Input @value={{@driver.drivers_license_number}} @type="text" class="w-full form-input" placeholder="Drivers License" />
80
+ <Input @value={{this.driver.drivers_license_number}} @type="text" class="w-full form-input" placeholder="Drivers License" />
81
81
  </div>
82
82
 
83
83
  <div class="input-group">
84
84
  <label>
85
85
  Email
86
86
  </label>
87
- <Input @value={{@driver.email}} @type="text" class="w-full form-input" placeholder="Email" />
87
+ <Input @value={{this.driver.email}} @type="text" class="w-full form-input" placeholder="Email" />
88
88
  </div>
89
89
 
90
90
  <div class="input-group">
91
91
  <label>
92
92
  Phone
93
93
  </label>
94
- <PhoneInput @value={{@driver.phone}} @onInput={{fn (mut @driver.phone)}} class="form-input w-full" />
94
+ <PhoneInput @value={{this.driver.phone}} @onInput={{fn (mut this.driver.phone)}} class="form-input w-full" />
95
95
  </div>
96
96
 
97
97
  <div class="input-group">
98
98
  <label>
99
99
  Vendor
100
100
  </label>
101
- <ModelSelect @modelName="vendor" @selectedModel={{@driver.vendor}} @placeholder="Select Vendor" @triggerClass="form-select form-input" @infiniteScroll={{false}} @renderInPlace={{true}} @onChange={{fn (mut @driver.vendor)}} as |model|>
101
+ <ModelSelect @modelName="vendor" @selectedModel={{this.driver.vendor}} @placeholder="Select Vendor" @triggerClass="form-select form-input" @infiniteScroll={{false}} @renderInPlace={{true}} @onChange={{fn (mut this.driver.vendor)}} as |model|>
102
102
  {{model.name}}
103
103
  </ModelSelect>
104
104
  </div>
@@ -107,7 +107,7 @@
107
107
  <label>
108
108
  Vehicle
109
109
  </label>
110
- <ModelSelect @modelName="vehicle" @selectedModel={{@driver.vehicle}} @placeholder="Select Vehicle" @triggerClass="form-select form-input" @infiniteScroll={{false}} @renderInPlace={{true}} @onChange={{fn (mut @driver.vehicle)}} as |model|>
110
+ <ModelSelect @modelName="vehicle" @selectedModel={{this.driver.vehicle}} @placeholder="Select Vehicle" @triggerClass="form-select form-input" @infiniteScroll={{false}} @renderInPlace={{true}} @onChange={{fn (mut this.driver.vehicle)}} as |model|>
111
111
  {{model.display_name}}
112
112
  </ModelSelect>
113
113
  </div>
@@ -116,14 +116,14 @@
116
116
  <label>
117
117
  City
118
118
  </label>
119
- <Input @value={{@driver.city}} @type="text" class="w-full form-input" placeholder="City" />
119
+ <Input @value={{this.driver.city}} @type="text" class="w-full form-input" placeholder="City" />
120
120
  </div>
121
121
 
122
122
  <div class="input-group">
123
123
  <label>
124
124
  Country
125
125
  </label>
126
- <CountrySelect class="w-full form-input form-select form-datalist" @value={{@driver.country}} @onChange={{fn (mut @driver.country)}} placeholder="Country" />
126
+ <CountrySelect class="w-full form-input form-select form-datalist" @value={{this.driver.country}} @onChange={{fn (mut this.driver.country)}} placeholder="Country" />
127
127
  </div>
128
128
  </div>
129
129
  </div>
@@ -1,39 +1,122 @@
1
1
  import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
2
3
  import { inject as service } from '@ember/service';
3
4
  import { action } from '@ember/object';
5
+ import contextComponentCallback from '../utils/context-component-callback';
6
+ import applyContextComponentArguments from '../utils/apply-context-component-arguments';
4
7
 
5
8
  export default class DriverFormPanelComponent extends Component {
9
+ /**
10
+ * @service store
11
+ */
6
12
  @service store;
13
+
14
+ /**
15
+ * @service notifications
16
+ */
7
17
  @service notifications;
18
+
19
+ /**
20
+ * @service hostRouter
21
+ */
8
22
  @service hostRouter;
23
+
24
+ /**
25
+ * @service loader
26
+ */
9
27
  @service loader;
10
28
 
29
+ /**
30
+ * @service contextPanel
31
+ */
32
+ @service contextPanel;
33
+
34
+ /**
35
+ * Overlay context.
36
+ * @type {any}
37
+ */
38
+ @tracked context;
39
+
40
+ /**
41
+ * Indicates whether the component is in a loading state.
42
+ * @type {boolean}
43
+ */
44
+ @tracked isLoading = false;
45
+
46
+ /**
47
+ * Constructs the component and applies initial state.
48
+ */
49
+ constructor() {
50
+ super(...arguments);
51
+ this.driver = this.args.driver;
52
+ applyContextComponentArguments(this);
53
+ }
54
+
55
+ /**
56
+ * Sets the overlay context.
57
+ *
58
+ * @action
59
+ * @param {OverlayContextObject} overlayContext
60
+ */
61
+ @action setOverlayContext(overlayContext) {
62
+ this.context = overlayContext;
63
+ contextComponentCallback(this, 'onLoad', ...arguments);
64
+ }
65
+
66
+ /**
67
+ * Saves the driver changes.
68
+ *
69
+ * @action
70
+ * @returns {Promise<any>}
71
+ */
11
72
  @action save() {
12
- const { driver, onAfterSave } = this.args;
73
+ const { driver } = this;
74
+
75
+ this.loader.showLoader('.next-content-overlay-panel-container', { loadingMessage: 'Saving driver...', preserveTargetPosition: true });
76
+ this.isLoading = true;
13
77
 
14
- this.loader.showLoader('.overlay-inner-content', 'Saving driver...');
78
+ contextComponentCallback(this, 'onBeforeSave', driver);
15
79
 
16
80
  try {
17
81
  return driver
18
82
  .save()
19
- .then((vehicle) => {
83
+ .then((driver) => {
20
84
  this.notifications.success(`Driver (${driver.name}) saved successfully.`);
21
-
22
- if (typeof onAfterSave === 'function') {
23
- onAfterSave(vehicle);
24
- }
85
+ contextComponentCallback(this, 'onAfterSave', driver);
86
+ })
87
+ .catch((error) => {
88
+ this.notifications.serverError(error);
25
89
  })
26
- .catch(this.notifications.serverError)
27
90
  .finally(() => {
28
- this.loader.removeLoader();
91
+ this.loader.removeLoader('.next-content-overlay-panel-container ');
92
+ this.isLoading = false;
29
93
  });
30
94
  } catch (error) {
31
- this.loader.removeLoader();
95
+ this.loader.removeLoader('.next-content-overlay-panel-container ');
96
+ this.isLoading = false;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * View the details of the driver.
102
+ *
103
+ * @action
104
+ */
105
+ @action onViewDetails() {
106
+ const isActionOverrided = contextComponentCallback(this, 'onViewDetails', this.driver);
107
+
108
+ if (!isActionOverrided) {
109
+ this.contextPanel.focus(this.driver, 'viewing');
32
110
  }
33
111
  }
34
112
 
35
- @action viewDetails() {
36
- const { driver } = this.args;
37
- return this.hostRouter.transitionTo('console.fleet-ops.management.drivers.index.details', driver.public_id);
113
+ /**
114
+ * Handles cancel button press.
115
+ *
116
+ * @action
117
+ * @returns {any}
118
+ */
119
+ @action onPressCancel() {
120
+ return contextComponentCallback(this, 'onPressCancel', this.driver);
38
121
  }
39
122
  }
@@ -1,29 +1,29 @@
1
- <Overlay @position="right" @noBackdrop={{true}} @fullHeight={{true}} @width="700">
1
+ <Overlay @onLoad={{this.setOverlayContext}} @position="right" @noBackdrop={{true}} @fullHeight={{true}} @isResizeble={{or this.isResizable @isResizable}} @width={{or this.width @width "600px"}}>
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">
5
5
  {{!-- <Button @type="default" @icon="location-arrow" @helpText="Locate driver" @onClick={{@onPressCancel}} /> --}}
6
- <Button @type="default" @icon="pen" @helpText="Edit driver" @onClick={{this.editDriver}} />
6
+ <Button @type="default" @icon="pen" @helpText="Edit driver" @onClick={{this.onEdit}} />
7
7
  </div>
8
8
  <div class="flex flex-1 justify-end">
9
- <Button @type="default" @icon="times" @helpText="Close driver details" @onClick={{@onPressCancel}} />
9
+ <Button @type="default" @icon="times" @helpText="Close driver details" @onClick={{this.onPressCancel}} />
10
10
  </div>
11
11
  </div>
12
12
  <div class="flex flex-row justify-between w-full">
13
13
  <div class="flex flex-col flex-1 w-3/4">
14
14
  <div class="flex flex-row">
15
15
  <div class="w-14 flex items-center justify-start">
16
- <Image src={{@driver.photo_url}} @fallbackSrc={{config "defaultValues.driverImage"}} alt={{@driver.name}} height="48" width="48" class="h-12 w-12 rounded-lg shadow-sm" />
16
+ <Image src={{this.driver.photo_url}} @fallbackSrc={{config "defaultValues.driverImage"}} alt={{this.driver.name}} height="48" width="48" class="h-12 w-12 rounded-lg shadow-sm" />
17
17
  <Attach::Tooltip @class="clean" @animation="scale" @placement={{"top"}}>
18
- <InputInfo @text={{@driver.public_id}} />
18
+ <InputInfo @text={{this.driver.public_id}} />
19
19
  </Attach::Tooltip>
20
20
  </div>
21
21
  <div class="flex flex-col">
22
- <h1 class="text-gray-900 dark:text-white text-2xl">{{@driver.name}}</h1>
22
+ <h1 class="text-gray-900 dark:text-white text-2xl">{{this.driver.name}}</h1>
23
23
  <div class="-mt-1">
24
- {{#if @driver.vehicle}}
24
+ {{#if this.driver.vehicle}}
25
25
  <div class="flex flex-row items-center">
26
- <span class="text-sm dark:text-gray-500 text-gray-700 mr-3">{{@driver.vehicle.displayName}}</span>
26
+ <span class="text-sm dark:text-gray-500 text-gray-700 mr-3">{{this.driver.vehicle.displayName}}</span>
27
27
  </div>
28
28
  {{else}}
29
29
  <div class="flex flex-row items-center">
@@ -36,7 +36,7 @@
36
36
  </div>
37
37
  </div>
38
38
  <div class="flex justify-end w-1/4">
39
- <Badge @status={{@driver.status}} />
39
+ <Badge @status={{this.driver.status}} />
40
40
  </div>
41
41
  </div>
42
42
  </Overlay::Header>
@@ -45,7 +45,7 @@
45
45
  <div class="ui-tabs mt-4">
46
46
  <nav>
47
47
  {{#each this.tabs as |tab|}}
48
- <a href="javascript:;" class="ui-tab {{if (eq this.currentTab tab.slug) 'active' }}" {{on "click" (fn this.changeTab tab.slug)}}>
48
+ <a href="javascript:;" class="ui-tab {{if (eq this.tab.slug tab.slug) 'active' }}" {{on "click" (fn this.onTabChanged tab.slug)}}>
49
49
  <FaIcon @icon={{tab.icon}} class="mr-1" />
50
50
  <span>{{tab.title}}</span>
51
51
  </a>
@@ -53,8 +53,8 @@
53
53
  </nav>
54
54
  </div>
55
55
  </div>
56
- <div class="tab-content tab-{{this.currentTab}}">
57
- {{component this.tab.component driver=@driver tabOptions=this.tab params=this.tab.componentParams}}
56
+ <div class="tab-content tab-{{this.tab.slug}}">
57
+ {{component this.tab.component driver=this.driver tabOptions=this.tab params=this.tab.componentParams}}
58
58
  </div>
59
59
  </Overlay::Body>
60
60
  </Overlay>
@@ -1,21 +1,76 @@
1
1
  import Component from '@glimmer/component';
2
2
  import { tracked } from '@glimmer/tracking';
3
- import { action, computed } from '@ember/object';
3
+ import { action } from '@ember/object';
4
4
  import { inject as service } from '@ember/service';
5
5
  import { isArray } from '@ember/array';
6
6
  import DriverPanelDetailComponent from './driver-panel/details';
7
+ import contextComponentCallback from '../utils/context-component-callback';
8
+ import applyContextComponentArguments from '../utils/apply-context-component-arguments';
7
9
 
8
10
  export default class DriverPanelComponent extends Component {
11
+ /**
12
+ * Service for fetching data.
13
+ *
14
+ * @type {Service}
15
+ */
9
16
  @service fetch;
17
+
18
+ /**
19
+ * Service for managing modals.
20
+ *
21
+ * @type {Service}
22
+ */
10
23
  @service modalsManager;
24
+
25
+ /**
26
+ * Universe service for managing global data and settings.
27
+ *
28
+ * @type {Service}
29
+ */
11
30
  @service universe;
31
+
32
+ /**
33
+ * Ember data store service.
34
+ *
35
+ * @type {Service}
36
+ */
12
37
  @service store;
38
+
39
+ /**
40
+ * Service for managing routing within the host app.
41
+ *
42
+ * @type {Service}
43
+ */
13
44
  @service hostRouter;
14
- @tracked currentTab;
15
- @tracked devices = [];
16
- @tracked deviceApi = {};
17
- @tracked vehicle;
18
45
 
46
+ /**
47
+ * Service for managing the context panel.
48
+ *
49
+ * @type {Service}
50
+ */
51
+ @service contextPanel;
52
+
53
+ /**
54
+ * The current active tab.
55
+ *
56
+ * @type {Object}
57
+ * @tracked
58
+ */
59
+ @tracked tab;
60
+
61
+ /**
62
+ * The driver being displayed or edited.
63
+ *
64
+ * @type {DriverModel}
65
+ * @tracked
66
+ */
67
+ @tracked driver;
68
+
69
+ /**
70
+ * Returns the array of tabs available for the panel.
71
+ *
72
+ * @type {Array}
73
+ */
19
74
  get tabs() {
20
75
  const registeredTabs = this.universe.getMenuItemsFromRegistry('component:driver-panel');
21
76
  // this.universe._createMenuItem('Tracking', null, { icon: 'satellite-dish', component: DriverPanelTrackingComponent }),
@@ -28,30 +83,79 @@ export default class DriverPanelComponent extends Component {
28
83
  return defaultTabs;
29
84
  }
30
85
 
31
- @computed('currentTab', 'tabs') get tab() {
32
- if (this.currentTab) {
33
- return this.tabs.find(({ slug }) => slug === this.currentTab);
34
- }
86
+ /**
87
+ * Initializes the driver panel component.
88
+ */
89
+ constructor() {
90
+ super(...arguments);
91
+ this.driver = this.args.driver;
92
+ this.tab = this.getTabUsingSlug(this.args.tab);
93
+ applyContextComponentArguments(this);
94
+ }
35
95
 
36
- return null;
96
+ /**
97
+ * Sets the overlay context.
98
+ *
99
+ * @action
100
+ * @param {OverlayContextObject} overlayContext
101
+ */
102
+ @action setOverlayContext(overlayContext) {
103
+ this.context = overlayContext;
104
+ contextComponentCallback(this, 'onLoad', ...arguments);
37
105
  }
38
106
 
39
- constructor() {
40
- super(...arguments);
41
- this.vehicle = this.args.vehicle;
42
- this.changeTab(this.args.tab || 'details');
107
+ /**
108
+ * Handles changing the active tab.
109
+ *
110
+ * @method
111
+ * @param {String} tab - The new tab to switch to.
112
+ * @action
113
+ */
114
+ @action onTabChanged(tab) {
115
+ this.tab = this.getTabUsingSlug(tab);
116
+ contextComponentCallback(this, 'onTabChanged', tab);
43
117
  }
44
118
 
45
- @action async changeTab(tab) {
46
- this.currentTab = tab;
119
+ /**
120
+ * Handles edit action for the driver.
121
+ *
122
+ * @method
123
+ * @action
124
+ */
125
+ @action onEdit() {
126
+ const isActionOverrided = contextComponentCallback(this, 'onEdit', this.driver);
47
127
 
48
- if (typeof this.args.onTabChanged === 'function') {
49
- this.args.onTabChanged(tab);
128
+ if (!isActionOverrided) {
129
+ this.contextPanel.focus(this.driver, 'editing', {
130
+ onAfterSave: () => {
131
+ this.contextPanel.clear();
132
+ },
133
+ });
50
134
  }
51
135
  }
52
136
 
53
- @action editDriver() {
54
- const { driver } = this.args;
55
- return this.hostRouter.transitionTo('console.fleet-ops.management.drivers.index.edit', driver.public_id);
137
+ /**
138
+ * Handles the cancel action.
139
+ *
140
+ * @method
141
+ * @action
142
+ * @returns {Boolean} Indicates whether the cancel action was overridden.
143
+ */
144
+ @action onPressCancel() {
145
+ return contextComponentCallback(this, 'onPressCancel', this.driver);
146
+ }
147
+
148
+ /**
149
+ * Finds and returns a tab based on its slug.
150
+ *
151
+ * @param {String} tabSlug - The slug of the tab.
152
+ * @returns {Object|null} The found tab or null.
153
+ */
154
+ getTabUsingSlug(tabSlug) {
155
+ if (tabSlug) {
156
+ return this.tabs.find(({ slug }) => slug === tabSlug);
157
+ }
158
+
159
+ return this.tabs[0];
56
160
  }
57
161
  }
@@ -13,7 +13,7 @@
13
13
  </div>
14
14
  <div class="w-56">
15
15
  {{#if @onAddVehicle}}
16
- <ModelSelect @modelName="vehicle" @selectedModel={{@options.selectedVehicle}} @placeholder="Add vehicle to fleet" @triggerClass="form-select form-input form-select-sm form-input-sm" @infiniteScroll={{false}} @renderInPlace={{true}} @onChange={{this.onAdd}} as |model|>
16
+ <ModelSelect @modelName="vehicle" @query={{hash assignedFleet=false}} @selectedModel={{@options.selectedVehicle}} @placeholder="Add vehicle to fleet" @triggerClass="form-select form-input form-select-sm form-input-sm" @infiniteScroll={{false}} @renderInPlace={{true}} @onChange={{this.onAdd}} as |model|>
17
17
  {{model.display_name}}
18
18
  </ModelSelect>
19
19
  {{/if}}