@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
@@ -1,21 +1,82 @@
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 VehiclePanelDetailComponent from './vehicle-panel/details';
7
+ import contextComponentCallback from '../utils/context-component-callback';
8
+ import applyContextComponentArguments from '../utils/apply-context-component-arguments';
7
9
 
10
+ /**
11
+ * Represents the vehicle panel component, handling vehicle information display and editing.
12
+ *
13
+ * @class VehiclePanelComponent
14
+ * @extends Component
15
+ */
8
16
  export default class VehiclePanelComponent extends Component {
17
+ /**
18
+ * Service for fetching data.
19
+ *
20
+ * @type {Service}
21
+ */
9
22
  @service fetch;
23
+
24
+ /**
25
+ * Service for managing modals.
26
+ *
27
+ * @type {Service}
28
+ */
10
29
  @service modalsManager;
30
+
31
+ /**
32
+ * Universe service for managing global data and settings.
33
+ *
34
+ * @type {Service}
35
+ */
11
36
  @service universe;
37
+
38
+ /**
39
+ * Ember data store service.
40
+ *
41
+ * @type {Service}
42
+ */
12
43
  @service store;
44
+
45
+ /**
46
+ * Service for managing routing within the host app.
47
+ *
48
+ * @type {Service}
49
+ */
13
50
  @service hostRouter;
14
- @tracked currentTab;
15
- @tracked devices = [];
16
- @tracked deviceApi = {};
51
+
52
+ /**
53
+ * Service for managing the context panel.
54
+ *
55
+ * @type {Service}
56
+ */
57
+ @service contextPanel;
58
+
59
+ /**
60
+ * The current active tab.
61
+ *
62
+ * @type {Object}
63
+ * @tracked
64
+ */
65
+ @tracked tab;
66
+
67
+ /**
68
+ * The vehicle being displayed or edited.
69
+ *
70
+ * @type {VehicleModel}
71
+ * @tracked
72
+ */
17
73
  @tracked vehicle;
18
74
 
75
+ /**
76
+ * Returns the array of tabs available for the panel.
77
+ *
78
+ * @type {Array}
79
+ */
19
80
  get tabs() {
20
81
  const registeredTabs = this.universe.getMenuItemsFromRegistry('component:vehicle-panel');
21
82
  // this.universe._createMenuItem('Tracking', null, { icon: 'satellite-dish', component: VehiclePanelTrackingComponent }),
@@ -28,30 +89,79 @@ export default class VehiclePanelComponent extends Component {
28
89
  return defaultTabs;
29
90
  }
30
91
 
31
- @computed('currentTab', 'tabs') get tab() {
32
- if (this.currentTab) {
33
- return this.tabs.find(({ slug }) => slug === this.currentTab);
34
- }
35
-
36
- return null;
37
- }
38
-
92
+ /**
93
+ * Initializes the vehicle panel component.
94
+ */
39
95
  constructor() {
40
96
  super(...arguments);
41
97
  this.vehicle = this.args.vehicle;
42
- this.changeTab(this.args.tab || 'details');
98
+ this.tab = this.getTabUsingSlug(this.args.tab);
99
+ applyContextComponentArguments(this);
100
+ }
101
+
102
+ /**
103
+ * Sets the overlay context.
104
+ *
105
+ * @action
106
+ * @param {OverlayContextObject} overlayContext
107
+ */
108
+ @action setOverlayContext(overlayContext) {
109
+ this.context = overlayContext;
110
+ contextComponentCallback(this, 'onLoad', ...arguments);
111
+ }
112
+
113
+ /**
114
+ * Handles changing the active tab.
115
+ *
116
+ * @method
117
+ * @param {String} tab - The new tab to switch to.
118
+ * @action
119
+ */
120
+ @action onTabChanged(tab) {
121
+ this.tab = this.getTabUsingSlug(tab);
122
+ contextComponentCallback(this, 'onTabChanged', tab);
43
123
  }
44
124
 
45
- @action async changeTab(tab) {
46
- this.currentTab = tab;
125
+ /**
126
+ * Handles edit action for the vehicle.
127
+ *
128
+ * @method
129
+ * @action
130
+ */
131
+ @action onEdit() {
132
+ const isActionOverrided = contextComponentCallback(this, 'onEdit', this.vehicle);
47
133
 
48
- if (typeof this.args.onTabChanged === 'function') {
49
- this.args.onTabChanged(tab);
134
+ if (!isActionOverrided) {
135
+ this.contextPanel.focus(this.vehicle, 'editing', {
136
+ onAfterSave: () => {
137
+ this.contextPanel.clear();
138
+ },
139
+ });
50
140
  }
51
141
  }
52
142
 
53
- @action editVehicle() {
54
- const { vehicle } = this.args;
55
- return this.hostRouter.transitionTo('console.fleet-ops.management.vehicles.index.edit', vehicle.public_id);
143
+ /**
144
+ * Handles the cancel action.
145
+ *
146
+ * @method
147
+ * @action
148
+ * @returns {Boolean} Indicates whether the cancel action was overridden.
149
+ */
150
+ @action onPressCancel() {
151
+ return contextComponentCallback(this, 'onPressCancel', this.vehicle);
152
+ }
153
+
154
+ /**
155
+ * Finds and returns a tab based on its slug.
156
+ *
157
+ * @param {String} tabSlug - The slug of the tab.
158
+ * @returns {Object|null} The found tab or null.
159
+ */
160
+ getTabUsingSlug(tabSlug) {
161
+ if (tabSlug) {
162
+ return this.tabs.find(({ slug }) => slug === tabSlug);
163
+ }
164
+
165
+ return this.tabs[0];
56
166
  }
57
167
  }
@@ -1,8 +1,61 @@
1
1
  import Controller from '@ember/controller';
2
+ import { tracked } from '@glimmer/tracking';
2
3
  import { action } from '@ember/object';
3
4
 
5
+ /**
6
+ * Controller for managing the details view within the "Management -> Drivers -> Index" route.
7
+ *
8
+ * @class ManagementDriversIndexDetailsController
9
+ * @extends Controller
10
+ */
4
11
  export default class ManagementDriverIndexDetailsController extends Controller {
12
+ /**
13
+ * The currently active view tab ('details' by default).
14
+ *
15
+ * @type {String}
16
+ * @tracked
17
+ */
18
+ @tracked view = 'details';
19
+
20
+ /**
21
+ * An array of query parameters to be serialized in the URL.
22
+ *
23
+ * @type {String[]}
24
+ * @tracked
25
+ */
26
+ @tracked queryParams = ['view'];
27
+
28
+ /**
29
+ * Transitions back to the "management.drivers.index" route.
30
+ *
31
+ * @method
32
+ * @action
33
+ * @returns {Transition} The transition object representing the route change.
34
+ */
5
35
  @action transitionBack() {
6
36
  return this.transitionToRoute('management.drivers.index');
7
37
  }
38
+
39
+ /**
40
+ * Transitions to the edit view for a specific vehicle.
41
+ *
42
+ * @method
43
+ * @param {DriverModel} driver - The vehicle to be edited.
44
+ * @action
45
+ * @returns {Transition} The transition object representing the route change.
46
+ */
47
+ @action onEdit(driver) {
48
+ return this.transitionToRoute('management.drivers.index.edit', driver);
49
+ }
50
+
51
+ /**
52
+ * Updates the active view tab.
53
+ *
54
+ * @method
55
+ * @param {String} tab - The name of the tab to activate.
56
+ * @action
57
+ */
58
+ @action onTabChanged(tab) {
59
+ this.view = tab;
60
+ }
8
61
  }
@@ -5,76 +5,107 @@ import { action } from '@ember/object';
5
5
 
6
6
  export default class ManagementDriversIndexEditController extends Controller {
7
7
  /**
8
- * True if updating service rate.
8
+ * Inject the `hostRouter` service
9
9
  *
10
- * @var {Boolean}
10
+ * @memberof ManagementDriversIndexEditController
11
11
  */
12
- @tracked isUpdatingDriver = false;
12
+ @service hostRouter;
13
+
13
14
  /**
14
- * Inject the `loader` service
15
+ * Inject the `hostRouter` service
15
16
  *
16
- * @var {Service}
17
+ * @memberof ManagementDriversIndexEditController
17
18
  */
18
- @service loader;
19
+ @service modalsManager;
20
+
19
21
  /**
20
- * Inject the `notifications` service
22
+ * The overlay component context.
21
23
  *
22
- * @var {Service}
24
+ * @memberof ManagementDriversIndexEditController
23
25
  */
24
- @service notifications;
26
+ @tracked overlay;
27
+
25
28
  /**
26
- * Inject the `hostRouter` service
29
+ * When exiting the overlay.
27
30
  *
28
- * @var {Service}
31
+ * @return {Transition}
32
+ * @memberof ManagementDriversIndexEditController
29
33
  */
30
- @service hostRouter;
34
+ @action transitionBack(driver) {
35
+ // check if driver record has been edited and prompt for confirmation
36
+ if (driver.hasDirtyAttributes) {
37
+ return this.confirmContinueWithUnsavedChanges(driver, {
38
+ confirm: () => {
39
+ driver.rollbackAttributes();
40
+ return this.transitionToRoute('management.drivers.index');
41
+ },
42
+ });
43
+ }
44
+
45
+ return this.transitionToRoute('management.drivers.index');
46
+ }
47
+
31
48
  /**
32
- * Inject the `currentUser` service
49
+ * Set the overlay component context object.
33
50
  *
34
- * @var {Service}
51
+ * @param {OverlayContext} overlay
52
+ * @memberof ManagementDriversIndexEditController
35
53
  */
36
- @service store;
54
+ @action setOverlayContext(overlay) {
55
+ this.overlay = overlay;
56
+ }
57
+
37
58
  /**
38
- * Updates the drivers to server
59
+ * When driver details button is clicked in overlay.
39
60
  *
40
- * @void
61
+ * @param {VehicleModel} driver
62
+ * @return {Promise}
63
+ * @memberof ManagementDriversIndexEditController
41
64
  */
42
- @action updateDriver() {
43
- const { driver } = this;
65
+ @action onViewDetails(driver) {
66
+ // check if driver record has been edited and prompt for confirmation
67
+ if (driver.hasDirtyAttributes) {
68
+ return this.confirmContinueWithUnsavedChanges(driver);
69
+ }
44
70
 
45
- this.isUpdatingDriver = true;
46
- this.loader.showLoader('.overlay-inner-content', 'Updating driver...');
71
+ return this.transitionToRoute('management.drivers.index.details', driver);
72
+ }
47
73
 
48
- try {
49
- return driver
50
- .save()
51
- .then((driver) => {
52
- return this.transitionToRoute('management.drivers.index').then(() => {
53
- this.notifications.success(`Driver '${driver.name}' updated`);
54
- this.resetForm();
55
- this.hostRouter.refresh();
56
- });
57
- })
58
- .catch(this.notifications.serverError)
59
- .finally(() => {
60
- this.isUpdatingDriver = false;
61
- this.loader.removeLoader();
62
- });
63
- } catch (error) {
64
- this.isUpdatingDriver = false;
65
- this.loader.removeLoader();
74
+ /**
75
+ * Trigger a route refresh and focus the new driver created.
76
+ *
77
+ * @param {VehicleModel} driver
78
+ * @return {Promise}
79
+ * @memberof ManagementDriversIndexEditController
80
+ */
81
+ @action onAfterSave(driver) {
82
+ if (this.overlay) {
83
+ this.overlay.close();
66
84
  }
67
- }
68
85
 
69
- @action transitionBack() {
70
- return this.transitionToRoute('management.drivers.index');
86
+ this.hostRouter.refresh();
87
+ return this.transitionToRoute('management.drivers.index.details', driver);
71
88
  }
89
+
72
90
  /**
73
- * Resets the driver form
91
+ * Prompts the user to confirm if they wish to continue with unsaved changes.
74
92
  *
75
- * @void
93
+ * @method
94
+ * @param {VehicleModel} driver - The driver object with unsaved changes.
95
+ * @param {Object} [options={}] - Additional options for configuring the modal.
96
+ * @returns {Promise} A promise that resolves when the user confirms, and transitions to a new route.
97
+ * @memberof ManagementDriversIndexEditController
76
98
  */
77
- @action resetForm() {
78
- this.driver = this.store.createRecord('driver');
99
+ confirmContinueWithUnsavedChanges(driver, options = {}) {
100
+ return this.modalsManager.confirm({
101
+ title: 'Continue Without Saving?',
102
+ body: 'Unsaved changes to this driver will be lost. Click continue to proceed.',
103
+ acceptButtonText: 'Continue without saving',
104
+ confirm: () => {
105
+ driver.rollbackAttributes();
106
+ return this.transitionToRoute('management.drivers.index.details', driver);
107
+ },
108
+ ...options,
109
+ });
79
110
  }
80
111
  }
@@ -1,128 +1,88 @@
1
- import Controller, { inject as controller } from '@ember/controller';
2
- import { inject as service } from '@ember/service';
1
+ import Controller from '@ember/controller';
3
2
  import { tracked } from '@glimmer/tracking';
3
+ import { inject as service } from '@ember/service';
4
4
  import { action } from '@ember/object';
5
- import generateSlug from '@fleetbase/ember-core/utils/generate-slug';
6
5
 
7
6
  export default class ManagementDriversIndexNewController extends Controller {
8
7
  /**
9
- * Inject the `management.drivers.index` controller
8
+ * Inject the `store` service
10
9
  *
11
- * @var {Controller}
12
- */
13
- @controller('management.drivers.index') index;
14
-
15
- /**
16
- * Inject the `currentUser` service
17
- *
18
- * @var {Service}
10
+ * @memberof ManagementDriversIndexNewController
19
11
  */
20
12
  @service store;
21
13
 
22
14
  /**
23
15
  * Inject the `hostRouter` service
24
16
  *
25
- * @var {Service}
17
+ * @memberof ManagementDriversIndexNewController
26
18
  */
27
19
  @service hostRouter;
28
20
 
29
21
  /**
30
- * Inject the `notifications` service
22
+ * Inject the `hostRouter` service
31
23
  *
32
- * @var {Service}
24
+ * @memberof ManagementDriversIndexNewController
33
25
  */
34
- @service notifications;
26
+ @service modalsManager;
35
27
 
36
28
  /**
37
- * Inject the `loader` service
29
+ * The overlay component context.
38
30
  *
39
- * @var {Service}
31
+ * @memberof ManagementDriversIndexNewController
40
32
  */
41
- @service loader;
33
+ @tracked overlay;
42
34
 
43
35
  /**
44
36
  * The driver being created.
45
37
  *
46
38
  * @var {DriverModel}
47
39
  */
48
- @tracked driver = this.store.createRecord('driver', {
49
- status: `active`,
50
- slug: generateSlug(),
51
- });
52
-
53
- /**
54
- * Different service types available, based on order type.
55
- *
56
- * @var {Array}
57
- */
58
- @tracked serviceTypes = [];
59
-
60
- /**
61
- * Service areas.
62
- *
63
- * @var {Array}
64
- */
65
- @tracked serviceAreas = [];
40
+ @tracked driver = this.store.createRecord('driver');
66
41
 
67
42
  /**
68
- * Zones.
43
+ * Set the overlay component context object.
69
44
  *
70
- * @var {Array}
45
+ * @param {OverlayContext} overlay
46
+ * @memberof ManagementDriversIndexNewController
71
47
  */
72
- @tracked zones = [];
48
+ @action setOverlayContext(overlay) {
49
+ this.overlay = overlay;
50
+ }
73
51
 
74
52
  /**
75
- * True if creating driver.
53
+ * When exiting the overlay.
76
54
  *
77
- * @var {Boolean}
55
+ * @return {Transition}
56
+ * @memberof ManagementDriversIndexNewController
78
57
  */
79
- @tracked isCreatingDriver = false;
58
+ @action transitionBack() {
59
+ return this.transitionToRoute('management.drivers.index');
60
+ }
80
61
 
81
62
  /**
82
- * Saves the driver to server
63
+ * Trigger a route refresh and focus the new driver created.
83
64
  *
84
- * @void
65
+ * @param {DriverModel} driver
66
+ * @return {Promise}
67
+ * @memberof ManagementDriversIndexNewController
85
68
  */
86
- @action createDriver() {
87
- const { driver } = this;
88
-
89
- this.isCreatingDriver = true;
90
- this.loader.showLoader('.overlay-inner-content', 'Creating driver...');
91
-
92
- try {
93
- return driver
94
- .save()
95
- .then((driver) => {
96
- return this.transitionToRoute('management.drivers.index').then(() => {
97
- this.notifications.success(`New Driver ${driver.name} Created`);
98
- this.resetForm();
99
- this.hostRouter.refresh();
100
- });
101
- })
102
- .catch((error) => {
103
- console.log(error);
104
- this.notifications.serverError(error);
105
- })
106
- .finally(() => {
107
- this.isCreatingDriver = false;
108
- this.loader.removeLoader();
109
- });
110
- } catch (error) {
111
- this.isCreatingDriver = false;
112
- this.loader.removeLoader();
69
+ @action onAfterSave(driver) {
70
+ if (this.overlay) {
71
+ this.overlay.close();
113
72
  }
73
+
74
+ this.hostRouter.refresh();
75
+ return this.transitionToRoute('management.drivers.index.details', driver).then(() => {
76
+ this.resetForm();
77
+ });
114
78
  }
115
79
 
116
80
  /**
117
- * Resets the driver form
81
+ * Resets the form with a new driver record
118
82
  *
119
- * @void
83
+ * @memberof ManagementDriversIndexNewController
120
84
  */
121
- @action resetForm() {
85
+ resetForm() {
122
86
  this.driver = this.store.createRecord('driver');
123
87
  }
124
-
125
- @action transitionBack() {
126
- return this.transitionToRoute('management.drivers.index');
127
- }
128
88
  }