@fleetbase/fleetops-engine 0.3.2 → 0.3.4

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 (137) hide show
  1. package/addon/components/admin/visibility-controls.js +1 -0
  2. package/addon/components/contact-form-panel.hbs +28 -6
  3. package/addon/components/driver-form-panel.hbs +10 -0
  4. package/addon/components/driver-form-panel.js +51 -0
  5. package/addon/components/driver-panel/details.hbs +1 -5
  6. package/addon/components/driver-panel.hbs +21 -3
  7. package/addon/components/fleet-form-panel.hbs +60 -6
  8. package/addon/components/fleet-panel/details.hbs +1 -1
  9. package/addon/components/fuel-report-form-panel.hbs +84 -8
  10. package/addon/components/fuel-report-form-panel.js +6 -0
  11. package/addon/components/fuel-report-panel/details.hbs +4 -0
  12. package/addon/components/issue-form-panel.hbs +99 -11
  13. package/addon/components/issue-form-panel.js +5 -13
  14. package/addon/components/layout/fleet-ops-sidebar.js +10 -1
  15. package/addon/components/live-map-drawer/driver-listing.hbs +6 -0
  16. package/addon/components/live-map-drawer/driver-listing.js +240 -0
  17. package/addon/components/live-map-drawer/place-listing.hbs +6 -0
  18. package/addon/components/live-map-drawer/place-listing.js +204 -0
  19. package/addon/components/live-map-drawer/vehicle-listing.hbs +6 -0
  20. package/addon/components/live-map-drawer/vehicle-listing.js +216 -0
  21. package/addon/components/live-map-drawer.hbs +17 -0
  22. package/addon/components/live-map-drawer.js +104 -0
  23. package/addon/components/live-map.hbs +5 -1
  24. package/addon/components/live-map.js +107 -15
  25. package/addon/components/map-container/toolbar.hbs +6 -0
  26. package/addon/components/modals/driver-assign-vehicle.hbs +2 -11
  27. package/addon/components/modals/driver-assign-vendor.hbs +1 -9
  28. package/addon/components/modals/fuel-report-details.hbs +4 -0
  29. package/addon/components/modals/order-event.hbs +1 -1
  30. package/addon/components/order/schedule-card.hbs +35 -7
  31. package/addon/components/order/schedule-card.js +163 -6
  32. package/addon/components/place-form-panel.hbs +14 -7
  33. package/addon/components/place-form-panel.js +71 -0
  34. package/addon/components/place-panel/details.hbs +11 -21
  35. package/addon/components/vehicle-form-panel.hbs +67 -7
  36. package/addon/components/vehicle-form-panel.js +37 -0
  37. package/addon/components/vehicle-panel/details.hbs +9 -5
  38. package/addon/components/vehicle-panel.hbs +18 -4
  39. package/addon/components/vendor-form-panel/create-form.hbs +72 -11
  40. package/addon/components/vendor-form-panel/edit-form.hbs +1 -1
  41. package/addon/components/vendor-form-panel.hbs +70 -30
  42. package/addon/components/vendor-form-panel.js +4 -40
  43. package/addon/components/vendor-panel/details.hbs +2 -2
  44. package/addon/components/vendor-panel.hbs +16 -2
  45. package/addon/controllers/management/contacts/index.js +42 -3
  46. package/addon/controllers/management/fleets/index.js +12 -23
  47. package/addon/controllers/management/fuel-reports/index/new.js +1 -1
  48. package/addon/controllers/management/fuel-reports/index.js +73 -20
  49. package/addon/controllers/management/issues/index/new.js +2 -2
  50. package/addon/controllers/management/issues/index.js +65 -45
  51. package/addon/controllers/management/places/index.js +59 -18
  52. package/addon/controllers/management/vehicles/index.js +67 -8
  53. package/addon/controllers/management/vendors/index.js +63 -8
  54. package/addon/controllers/operations/orders/index.js +70 -15
  55. package/addon/controllers/operations/scheduler/index.js +45 -30
  56. package/addon/routes/management/contacts/index.js +8 -0
  57. package/addon/routes/management/fleets/index.js +8 -2
  58. package/addon/routes/management/fuel-reports/index.js +6 -0
  59. package/addon/routes/management/issues/index.js +9 -1
  60. package/addon/routes/management/places/index.js +11 -1
  61. package/addon/routes/management/vehicles/index.js +3 -3
  62. package/addon/routes/management/vendors/index.js +8 -0
  63. package/addon/routes/operations/scheduler/index.js +26 -1
  64. package/addon/styles/fleetops-engine.css +28 -0
  65. package/addon/templates/operations/orders/index.hbs +2 -2
  66. package/addon/templates/operations/scheduler/index.hbs +33 -9
  67. package/addon/utils/create-full-calendar-event-from-order.js +8 -0
  68. package/addon/utils/get-issue-categories.js +30 -0
  69. package/addon/utils/get-issue-types.js +3 -0
  70. package/addon/utils/get-vendor-status-options.js +3 -0
  71. package/addon/utils/get-vendor-type-options.js +41 -0
  72. package/app/components/live-map-drawer/driver-listing.js +1 -0
  73. package/app/components/live-map-drawer/place-listing.js +1 -0
  74. package/app/components/live-map-drawer/vehicle-listing.js +1 -0
  75. package/app/components/live-map-drawer.js +1 -0
  76. package/app/utils/create-full-calendar-event-from-order.js +1 -0
  77. package/app/utils/get-issue-categories.js +1 -0
  78. package/app/utils/get-issue-types.js +1 -0
  79. package/app/utils/get-vendor-categories.js +1 -0
  80. package/app/utils/get-vendor-status-options.js +1 -0
  81. package/app/utils/get-vendor-type-options.js +1 -0
  82. package/app/utils/get-vendor-types.js +1 -0
  83. package/composer.json +1 -1
  84. package/extension.json +1 -1
  85. package/package.json +4 -4
  86. package/server/migrations/2023_11_09_041807_add_status_column_to_fuel_reports_table.php +32 -0
  87. package/server/migrations/2023_11_15_062814_add_comments_column_and_resolved_at_column_to_vehicle_device_events_table.php +33 -0
  88. package/server/src/Http/Controllers/Api/v1/DriverController.php +2 -2
  89. package/server/src/Http/Controllers/Api/v1/VehicleController.php +2 -2
  90. package/server/src/Http/Controllers/Internal/v1/DriverController.php +5 -13
  91. package/server/src/Http/Controllers/Internal/v1/OrderController.php +1 -3
  92. package/server/src/Http/Filter/ContactFilter.php +10 -0
  93. package/server/src/Http/Filter/FleetFilter.php +8 -6
  94. package/server/src/Http/Filter/FuelReportFilter.php +51 -0
  95. package/server/src/Http/Filter/IssueFilter.php +61 -5
  96. package/server/src/Http/Filter/PlaceFilter.php +48 -6
  97. package/server/src/Http/Filter/VehicleFilter.php +21 -6
  98. package/server/src/Http/Filter/VendorFilter.php +11 -8
  99. package/server/src/Http/Middleware/TransformLocationMiddleware.php +1 -1
  100. package/server/src/Http/Resources/Internal/v1/IntegratedVendorFacilitator.php +17 -18
  101. package/server/src/Http/Resources/v1/Contact.php +16 -19
  102. package/server/src/Http/Resources/v1/CurrentJob.php +13 -16
  103. package/server/src/Http/Resources/v1/Driver.php +59 -57
  104. package/server/src/Http/Resources/v1/Entity.php +30 -33
  105. package/server/src/Http/Resources/v1/FuelReport.php +16 -14
  106. package/server/src/Http/Resources/v1/Order.php +1 -1
  107. package/server/src/Http/Resources/v1/Payload.php +21 -24
  108. package/server/src/Http/Resources/v1/Place.php +27 -30
  109. package/server/src/Http/Resources/v1/Proof.php +13 -16
  110. package/server/src/Http/Resources/v1/PurchaseRate.php +14 -17
  111. package/server/src/Http/Resources/v1/ServiceArea.php +13 -16
  112. package/server/src/Http/Resources/v1/ServiceQuote.php +15 -18
  113. package/server/src/Http/Resources/v1/ServiceRate.php +33 -36
  114. package/server/src/Http/Resources/v1/ServiceRateFee.php +14 -17
  115. package/server/src/Http/Resources/v1/ServiceRateParcelFee.php +9 -12
  116. package/server/src/Http/Resources/v1/TrackingNumber.php +18 -21
  117. package/server/src/Http/Resources/v1/TrackingStatus.php +18 -21
  118. package/server/src/Http/Resources/v1/Vehicle.php +31 -34
  119. package/server/src/Http/Resources/v1/VehicleDevice.php +23 -25
  120. package/server/src/Http/Resources/v1/VehicleWithoutDriver.php +38 -32
  121. package/server/src/Http/Resources/v1/Vendor.php +31 -26
  122. package/server/src/Http/Resources/v1/Zone.php +14 -17
  123. package/server/src/Models/Driver.php +7 -1
  124. package/server/src/Models/FuelReport.php +2 -1
  125. package/server/src/Models/IntegratedVendor.php +6 -1
  126. package/server/src/Models/Order.php +24 -2
  127. package/server/src/Models/Place.php +2 -2
  128. package/server/src/Models/VehicleDevice.php +1 -1
  129. package/server/src/Models/VehicleDeviceEvent.php +2 -1
  130. package/server/src/Models/Vendor.php +1 -1
  131. package/server/src/Observers/DriverObserver.php +15 -0
  132. package/server/src/Observers/FleetObserver.php +19 -0
  133. package/server/src/Providers/FleetOpsServiceProvider.php +1 -0
  134. package/addon/components/full-calendar/draggable.js +0 -1
  135. package/addon/components/full-calendar.js +0 -1
  136. package/app/components/full-calendar/draggable.js +0 -1
  137. package/app/components/full-calendar.js +0 -1
@@ -6,6 +6,8 @@ import { isArray } from '@ember/array';
6
6
  import getWithDefault from '@fleetbase/ember-core/utils/get-with-default';
7
7
  import contextComponentCallback from '../utils/context-component-callback';
8
8
  import applyContextComponentArguments from '../utils/apply-context-component-arguments';
9
+ import getIssueTypes from '../utils/get-issue-types';
10
+ import getIssueCategories from '../utils/get-issue-categories';
9
11
 
10
12
  export default class IssueFormPanelComponent extends Component {
11
13
  /**
@@ -55,24 +57,14 @@ export default class IssueFormPanelComponent extends Component {
55
57
  *
56
58
  * @var {String}
57
59
  */
58
- @tracked issueTypes = ['vehicle', 'driver', 'route', 'payload-cargo', 'software-technical', 'operational', 'customer', 'security', 'environmental-sustainability'];
60
+ @tracked issueTypes = getIssueTypes();
59
61
 
60
62
  /**
61
63
  * The subcategories for issue types.
62
64
  *
63
65
  * @var {Object}
64
66
  */
65
- @tracked issueCategoriesByType = {
66
- vehicle: ['Mechanical Problems', 'Cosmetic Damages', 'Tire Issues', 'Electronics and Instruments', 'Maintenance Alerts', 'Fuel Efficiency Issues'],
67
- driver: ['Behavior Concerns', 'Documentation', 'Time Management', 'Communication', 'Training Needs', 'Health and Safety Violations'],
68
- route: ['Inefficient Routes', 'Safety Concerns', 'Blocked Routes', 'Environmental Considerations', 'Unfavorable Weather Conditions'],
69
- 'payload-cargo': ['Damaged Goods', 'Misplaced Goods', 'Documentation Issues', 'Temperature-Sensitive Goods', 'Incorrect Cargo Loading'],
70
- 'software-technical': ['Bugs', 'UI/UX Concerns', 'Integration Failures', 'Performance', 'Feature Requests', 'Security Vulnerabilities'],
71
- operational: ['Compliance', 'Resource Allocation', 'Cost Overruns', 'Communication', 'Vendor Management Issues'],
72
- customer: ['Service Quality', 'Billing Discrepancies', 'Communication Breakdown', 'Feedback and Suggestions', 'Order Errors'],
73
- security: ['Unauthorized Access', 'Data Concerns', 'Physical Security', 'Data Integrity Issues'],
74
- 'environmental-sustainability': ['Fuel Consumption', 'Carbon Footprint', 'Waste Management', 'Green Initiatives Opportunities'],
75
- };
67
+ @tracked issueCategoriesByType = getIssueCategories({ fullObject: true });
76
68
 
77
69
  /**
78
70
  * Selectable issue categories.
@@ -101,7 +93,7 @@ export default class IssueFormPanelComponent extends Component {
101
93
  constructor() {
102
94
  super(...arguments);
103
95
  this.issue = this.args.issue;
104
- this.issueCategories = getWithDefault(this.issueCategoriesByType, this.issue.type, []);
96
+ this.issueCategories = getWithDefault(this.issueCategoriesByType, getWithDefault(this.issue, 'type', 'operational'), []);
105
97
  applyContextComponentArguments(this);
106
98
  }
107
99
 
@@ -34,10 +34,11 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
34
34
  const operationsItems = [
35
35
  { title: 'Dashboard', icon: 'home', route: 'operations.orders' },
36
36
  { title: 'Service Rates', icon: 'file-invoice-dollar', route: 'operations.service-rates' },
37
+ { title: 'Scheduler', icon: 'calendar-day', route: 'operations.scheduler' },
37
38
  ];
38
39
 
39
40
  const resourcesItems = [
40
- { title: 'Drivers', icon: 'route', route: 'management.drivers' },
41
+ { title: 'Drivers', icon: 'id-card', route: 'management.drivers' },
41
42
  { title: 'Vehicles', icon: 'truck', route: 'management.vehicles' },
42
43
  { title: 'Fleets', icon: 'user-group', route: 'management.fleets' },
43
44
  { title: 'Vendors', icon: 'warehouse', route: 'management.vendors' },
@@ -99,6 +100,14 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
99
100
  return true;
100
101
  }
101
102
 
103
+ // Check if the route exists in the settings
104
+ const routeExists = visibilitySettings.some((visibilityControl) => (exactMatch ? visibilityControl.route === routePrefix : visibilityControl.route.startsWith(routePrefix)));
105
+
106
+ // If the route doesn't exist in the settings, return true (visible by default)
107
+ if (!routeExists) {
108
+ return true;
109
+ }
110
+
102
111
  return visibilitySettings.some((visibilityControl) => {
103
112
  const match = exactMatch ? visibilityControl.route === routePrefix : visibilityControl.route.startsWith(routePrefix);
104
113
  return match && visibilityControl.visible;
@@ -0,0 +1,6 @@
1
+ <div class="flex flex-row items-center px-5 py-2 border-b border-gray-200 dark:border-gray-700">
2
+ <div>
3
+ <Input @value={{this.query}} {{on "input" this.performSearch}} @type="text" class="form-input form-input-sm w-60" placeholder="Filter drivers by keyword..." />
4
+ </div>
5
+ </div>
6
+ <Table @rows={{this.drivers}} @columns={{this.columns}} @onSetup={{fn (mut this.table)}} />
@@ -0,0 +1,240 @@
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 { isBlank } from '@ember/utils';
6
+ import { later } from '@ember/runloop';
7
+ import getWithDefault from '@fleetbase/ember-core/utils/get-with-default';
8
+
9
+ /**
10
+ * Represents a live map drawer driver listing component.
11
+ * This component is responsible for displaying and interacting with a list of drivers on a live map.
12
+ *
13
+ * @extends Component
14
+ */
15
+ export default class LiveMapDrawerDriverListingComponent extends Component {
16
+ /**
17
+ * Service for managing context panels within the application.
18
+ * @service
19
+ */
20
+ @service contextPanel;
21
+
22
+ /**
23
+ * Service for triggering notifications.
24
+ * @service
25
+ */
26
+ @service notifications;
27
+
28
+ /**
29
+ * Service for triggering notifications.
30
+ * @service
31
+ */
32
+ @service hostRouter;
33
+
34
+ /**
35
+ * Service for CRUD operations.
36
+ * @service
37
+ */
38
+ @service crud;
39
+
40
+ /**
41
+ * The list of drivers to display, tracked for reactivity.
42
+ * @tracked
43
+ */
44
+ @tracked drivers = [];
45
+
46
+ /**
47
+ * The internal list of drivers used for searching, tracked for reactivity.
48
+ * @tracked
49
+ */
50
+ @tracked _drivers = [];
51
+
52
+ /**
53
+ * The current search query, tracked for reactivity.
54
+ * @tracked
55
+ */
56
+ @tracked query = '';
57
+
58
+ /**
59
+ * The table component reference, tracked for reactivity.
60
+ * @tracked
61
+ */
62
+ @tracked table = null;
63
+
64
+ /**
65
+ * The configuration for table columns including details like label, valuePath, and cellComponent,
66
+ * tracked for reactivity.
67
+ * @tracked
68
+ */
69
+ @tracked columns = [
70
+ {
71
+ label: 'Driver',
72
+ valuePath: 'name',
73
+ photoPath: 'photo_url',
74
+ width: '100px',
75
+ cellComponent: 'cell/driver-name',
76
+ onClick: this.focus,
77
+ },
78
+ {
79
+ label: 'Location',
80
+ valuePath: 'location',
81
+ width: '80px',
82
+ cellComponent: 'table/cell/point',
83
+ onClick: this.locate,
84
+ },
85
+ {
86
+ label: 'Current Job',
87
+ valuePath: 'current_job_id',
88
+ width: '80px',
89
+ cellComponent: 'table/cell/anchor',
90
+ onClick: this.job,
91
+ },
92
+ {
93
+ label: 'Status',
94
+ valuePath: 'status',
95
+ cellComponent: 'table/cell/status',
96
+ width: '60px',
97
+ },
98
+ {
99
+ label: 'Last Seen',
100
+ valuePath: 'updatedAgo',
101
+ width: '60px',
102
+ },
103
+ {
104
+ label: '',
105
+ cellComponent: 'table/cell/dropdown',
106
+ ddButtonText: false,
107
+ ddButtonIcon: 'ellipsis-h',
108
+ ddButtonIconPrefix: 'fas',
109
+ ddMenuLabel: 'Actions',
110
+ cellClassNames: 'overflow-visible',
111
+ wrapperClass: 'flex items-center justify-end mx-2',
112
+ width: '90px',
113
+ actions: [
114
+ {
115
+ label: 'View driver details...',
116
+ fn: this.focus,
117
+ },
118
+ {
119
+ label: 'Edit driver...',
120
+ fn: (driver) => {
121
+ return this.focus(driver, 'editing');
122
+ },
123
+ },
124
+ {
125
+ label: 'Locate driver...',
126
+ fn: this.locate,
127
+ },
128
+ {
129
+ label: 'Delete driver...',
130
+ fn: this.delete,
131
+ },
132
+ ],
133
+ sortable: false,
134
+ filterable: false,
135
+ resizable: false,
136
+ searchable: false,
137
+ },
138
+ ];
139
+
140
+ /**
141
+ * Initializes the component with drivers passed in from `this.args` and sets up the live map reference.
142
+ */
143
+ constructor() {
144
+ super(...arguments);
145
+ this.drivers = getWithDefault(this.args, 'drivers', []);
146
+ this._drivers = [...this.drivers];
147
+ this.liveMap = this.args.liveMap;
148
+ }
149
+
150
+ /**
151
+ * Filters the drivers list based on a query.
152
+ *
153
+ * @param {string} query - The query string to filter the drivers list.
154
+ */
155
+ search(query) {
156
+ if (typeof query !== 'string' && !isBlank(query)) {
157
+ return;
158
+ }
159
+
160
+ this.drivers = [
161
+ ...this._drivers.filter((driver) => {
162
+ return typeof driver.name === 'string' && driver.name.toLowerCase().includes(query.toLowerCase());
163
+ }),
164
+ ];
165
+ }
166
+
167
+ /**
168
+ * Action to perform a search based on the input event's value.
169
+ *
170
+ * @param {Event} event - The input event containing the search value.
171
+ */
172
+ @action performSearch({ target: { value } }) {
173
+ this.search(value);
174
+ }
175
+
176
+ /**
177
+ * Action to focus on a driver in the live map and context panel.
178
+ *
179
+ * @param {object} driver - The driver object to focus on.
180
+ * @param {string} intent - The intent for focusing, default is 'viewing'.
181
+ */
182
+ @action focus(driver, intent = 'viewing') {
183
+ if (this.liveMap) {
184
+ this.liveMap.focusLayerByRecord(driver, 16, {
185
+ onAfterFocusWithRecord: () => {
186
+ later(
187
+ this,
188
+ () => {
189
+ this.contextPanel.focus(driver, intent);
190
+ },
191
+ 600 * 2
192
+ );
193
+ },
194
+ });
195
+ } else {
196
+ this.contextPanel.focus(driver, intent);
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Action to locate a driver on the live map.
202
+ *
203
+ * @param {object} driver - The driver object to locate.
204
+ */
205
+ @action locate(driver) {
206
+ if (this.liveMap) {
207
+ this.liveMap.focusLayerByRecord(driver, 18);
208
+ } else {
209
+ this.notifications.warning('Unable to locate driver.');
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Transitino to view the drivers current job
215
+ *
216
+ * @param {DriverModel} driver
217
+ * @memberof LiveMapDrawerDriverListingComponent
218
+ */
219
+ @action job(driver) {
220
+ if (driver.current_job_id) {
221
+ this.hostRouter.transitionTo('console.fleet-ops.operations.orders.index.view', driver.current_job_id);
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Action to delete a driver from the list and perform cleanup.
227
+ *
228
+ * @param {object} driver - The driver object to delete.
229
+ * @param {object} options - Additional options for the delete operation.
230
+ */
231
+ @action delete(driver, options = {}) {
232
+ this.crud.delete(driver, {
233
+ onSuccess: () => {
234
+ this._drivers.removeObject(driver);
235
+ this.drivers.removeObject(driver);
236
+ },
237
+ ...options,
238
+ });
239
+ }
240
+ }
@@ -0,0 +1,6 @@
1
+ <div class="flex flex-row items-center px-5 py-2 border-b border-gray-200 dark:border-gray-700">
2
+ <div>
3
+ <Input @value={{this.query}} {{on "input" this.performSearch}} @type="text" class="form-input form-input-sm w-60" placeholder="Filter places by keyword..." />
4
+ </div>
5
+ </div>
6
+ <Table @rows={{this.places}} @columns={{this.columns}} @onSetup={{fn (mut this.table)}} />
@@ -0,0 +1,204 @@
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 { isBlank } from '@ember/utils';
6
+ import { later } from '@ember/runloop';
7
+ import getWithDefault from '@fleetbase/ember-core/utils/get-with-default';
8
+
9
+ /**
10
+ * Represents a live map drawer place listing component.
11
+ * This component is responsible for displaying and interacting with a list of places on a live map.
12
+ *
13
+ * @extends Component
14
+ */
15
+ export default class LiveMapDrawerPlaceListingComponent extends Component {
16
+ /**
17
+ * Service for managing context panels within the application.
18
+ * @service
19
+ */
20
+ @service contextPanel;
21
+
22
+ /**
23
+ * Service for triggering notifications.
24
+ * @service
25
+ */
26
+ @service notifications;
27
+
28
+ /**
29
+ * Service for CRUD operations.
30
+ * @service
31
+ */
32
+ @service crud;
33
+
34
+ /**
35
+ * The list of places to display, tracked for reactivity.
36
+ * @tracked
37
+ */
38
+ @tracked places = [];
39
+
40
+ /**
41
+ * The internal list of places used for searching, tracked for reactivity.
42
+ * @tracked
43
+ */
44
+ @tracked _places = [];
45
+
46
+ /**
47
+ * The current search query, tracked for reactivity.
48
+ * @tracked
49
+ */
50
+ @tracked query = '';
51
+
52
+ /**
53
+ * The table component reference, tracked for reactivity.
54
+ * @tracked
55
+ */
56
+ @tracked table = null;
57
+
58
+ /**
59
+ * The configuration for table columns including details like label, valuePath, and cellComponent,
60
+ * tracked for reactivity.
61
+ * @tracked
62
+ */
63
+ @tracked columns = [
64
+ {
65
+ label: 'Place',
66
+ valuePath: 'address',
67
+ width: '200px',
68
+ cellComponent: 'table/cell/anchor',
69
+ onClick: this.focus,
70
+ showOnlineIndicator: true,
71
+ },
72
+ {
73
+ label: 'Location',
74
+ valuePath: 'location',
75
+ width: '80px',
76
+ cellComponent: 'table/cell/point',
77
+ onClick: this.locate,
78
+ },
79
+ {
80
+ label: '',
81
+ cellComponent: 'table/cell/dropdown',
82
+ ddButtonText: false,
83
+ ddButtonIcon: 'ellipsis-h',
84
+ ddButtonIconPrefix: 'fas',
85
+ ddMenuLabel: 'Actions',
86
+ cellClassNames: 'overflow-visible',
87
+ wrapperClass: 'flex items-center justify-end mx-2',
88
+ width: '90px',
89
+ actions: [
90
+ {
91
+ label: 'View place details...',
92
+ fn: this.focus,
93
+ },
94
+ {
95
+ label: 'Edit place...',
96
+ fn: (place) => {
97
+ return this.focus(place, 'editing');
98
+ },
99
+ },
100
+ {
101
+ label: 'Locate place...',
102
+ fn: this.locate,
103
+ },
104
+ {
105
+ label: 'Delete place...',
106
+ fn: this.delete,
107
+ },
108
+ ],
109
+ sortable: false,
110
+ filterable: false,
111
+ resizable: false,
112
+ searchable: false,
113
+ },
114
+ ];
115
+
116
+ /**
117
+ * Initializes the component with places passed in from `this.args` and sets up the live map reference.
118
+ */
119
+ constructor() {
120
+ super(...arguments);
121
+ this.places = getWithDefault(this.args, 'places', []);
122
+ this._places = [...this.places];
123
+ this.liveMap = this.args.liveMap;
124
+ }
125
+
126
+ /**
127
+ * Filters the places list based on a query.
128
+ *
129
+ * @param {string} query - The query string to filter the places list.
130
+ */
131
+ search(query) {
132
+ if (typeof query !== 'string' && !isBlank(query)) {
133
+ return;
134
+ }
135
+
136
+ this.places = [
137
+ ...this._places.filter((place) => {
138
+ return typeof place.address === 'string' && place.address.toLowerCase().includes(query.toLowerCase());
139
+ }),
140
+ ];
141
+ }
142
+
143
+ /**
144
+ * Action to perform a search based on the input event's value.
145
+ *
146
+ * @param {Event} event - The input event containing the search value.
147
+ */
148
+ @action performSearch({ target: { value } }) {
149
+ this.search(value);
150
+ }
151
+
152
+ /**
153
+ * Action to focus on a place in the live map and context panel.
154
+ *
155
+ * @param {object} place - The place object to focus on.
156
+ * @param {string} intent - The intent for focusing, default is 'viewing'.
157
+ */
158
+ @action focus(place, intent = 'viewing') {
159
+ if (this.liveMap) {
160
+ this.liveMap.focusLayerByRecord(place, 17, {
161
+ onAfterFocusWithRecord: () => {
162
+ later(
163
+ this,
164
+ () => {
165
+ this.contextPanel.focus(place, intent);
166
+ },
167
+ 600 * 2
168
+ );
169
+ },
170
+ });
171
+ } else {
172
+ this.contextPanel.focus(place, intent);
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Action to locate a place on the live map.
178
+ *
179
+ * @param {object} place - The place object to locate.
180
+ */
181
+ @action locate(place) {
182
+ if (this.liveMap) {
183
+ this.liveMap.focusLayerByRecord(place, 18);
184
+ } else {
185
+ this.notifications.warning('Unable to locate place.');
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Action to delete a place from the list and perform cleanup.
191
+ *
192
+ * @param {object} place - The place object to delete.
193
+ * @param {object} options - Additional options for the delete operation.
194
+ */
195
+ @action delete(place, options = {}) {
196
+ this.crud.delete(place, {
197
+ onSuccess: () => {
198
+ this._places.removeObject(place);
199
+ this.places.removeObject(place);
200
+ },
201
+ ...options,
202
+ });
203
+ }
204
+ }
@@ -0,0 +1,6 @@
1
+ <div class="flex flex-row items-center px-5 py-2 border-b border-gray-200 dark:border-gray-700">
2
+ <div>
3
+ <Input @value={{this.query}} {{on "input" this.performSearch}} @type="text" class="form-input form-input-sm w-60" placeholder="Filter vehicles by keyword..." />
4
+ </div>
5
+ </div>
6
+ <Table @rows={{this.vehicles}} @columns={{this.columns}} @onSetup={{fn (mut this.table)}} />