@fleetbase/fleetops-engine 0.6.70 → 0.6.71

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 (144) hide show
  1. package/RELEASE.md +21 -47
  2. package/addon/components/order/details/activity.hbs +8 -6
  3. package/addon/components/order/details/activity.js +9 -0
  4. package/addon/components/order/details.hbs +29 -0
  5. package/addon/components/order/details.js +26 -0
  6. package/addon/components/order/form/details.hbs +30 -4
  7. package/addon/components/order/form/details.js +46 -0
  8. package/addon/components/order/form/route.hbs +4 -2
  9. package/addon/components/order/form/route.js +11 -0
  10. package/addon/components/order/form.hbs +12 -0
  11. package/addon/components/order/form.js +21 -1
  12. package/addon/components/order/kanban.js +72 -0
  13. package/addon/components/order-config-manager/activity-flow.js +45 -10
  14. package/addon/components/order-config-manager/lifecycle.hbs +250 -0
  15. package/addon/components/order-config-manager/lifecycle.js +358 -0
  16. package/addon/components/order-config-manager.js +5 -1
  17. package/addon/controllers/analytics/reports/index.js +10 -1
  18. package/addon/controllers/connectivity/devices/index/details.js +1 -0
  19. package/addon/controllers/connectivity/devices/index.js +8 -2
  20. package/addon/controllers/connectivity/events/index.js +31 -1
  21. package/addon/controllers/connectivity/sensors/index/details.js +1 -1
  22. package/addon/controllers/connectivity/sensors/index.js +37 -1
  23. package/addon/controllers/maintenance/equipment/index/details.js +2 -2
  24. package/addon/controllers/maintenance/equipment/index.js +28 -9
  25. package/addon/controllers/maintenance/inspection-forms/index.js +32 -12
  26. package/addon/controllers/maintenance/inspection-submissions/index/details.js +2 -1
  27. package/addon/controllers/maintenance/inspection-submissions/index.js +49 -9
  28. package/addon/controllers/maintenance/maintenances/index/details.js +2 -0
  29. package/addon/controllers/maintenance/maintenances/index.js +20 -1
  30. package/addon/controllers/maintenance/parts/index/details.js +2 -2
  31. package/addon/controllers/maintenance/parts/index.js +28 -8
  32. package/addon/controllers/maintenance/schedules/index.js +33 -4
  33. package/addon/controllers/maintenance/work-orders/index/details.js +3 -3
  34. package/addon/controllers/maintenance/work-orders/index.js +39 -8
  35. package/addon/controllers/management/contacts/customers.js +13 -0
  36. package/addon/controllers/management/contacts/index.js +43 -1
  37. package/addon/controllers/management/drivers/index/details.js +1 -0
  38. package/addon/controllers/management/drivers/index.js +32 -2
  39. package/addon/controllers/management/fleets/index/details.js +1 -0
  40. package/addon/controllers/management/fleets/index.js +39 -1
  41. package/addon/controllers/management/fuel-reports/index.js +22 -2
  42. package/addon/controllers/management/fuel-transactions/index.js +43 -7
  43. package/addon/controllers/management/issues/index/details.js +1 -0
  44. package/addon/controllers/management/issues/index.js +22 -2
  45. package/addon/controllers/management/places/index.js +36 -1
  46. package/addon/controllers/management/trailers/index/details.js +1 -0
  47. package/addon/controllers/management/trailers/index.js +50 -2
  48. package/addon/controllers/management/vehicles/index/details.js +1 -0
  49. package/addon/controllers/management/vehicles/index.js +33 -2
  50. package/addon/controllers/management/vendors/index.js +26 -2
  51. package/addon/controllers/operations/orders/index/details.js +31 -4
  52. package/addon/controllers/operations/orders/index.js +59 -4
  53. package/addon/controllers/operations/service-rates/index.js +15 -1
  54. package/addon/extension.js +32 -0
  55. package/addon/routes/operations/orders/index/details.js +1 -0
  56. package/addon/services/contact-actions.js +1 -0
  57. package/addon/services/customer-actions.js +2 -1
  58. package/addon/services/device-actions.js +1 -0
  59. package/addon/services/device-event-actions.js +1 -0
  60. package/addon/services/driver-actions.js +4 -13
  61. package/addon/services/equipment-actions.js +1 -0
  62. package/addon/services/fleet-actions.js +1 -0
  63. package/addon/services/fuel-report-actions.js +1 -0
  64. package/addon/services/inspection-form-actions.js +1 -0
  65. package/addon/services/inspection-submission-actions.js +1 -0
  66. package/addon/services/integrated-vendor-actions.js +1 -0
  67. package/addon/services/issue-actions.js +1 -0
  68. package/addon/services/maintenance-actions.js +1 -0
  69. package/addon/services/maintenance-schedule-actions.js +2 -1
  70. package/addon/services/order-actions.js +7 -0
  71. package/addon/services/order-presentation.js +129 -0
  72. package/addon/services/part-actions.js +1 -0
  73. package/addon/services/place-actions.js +1 -0
  74. package/addon/services/sensor-actions.js +1 -0
  75. package/addon/services/service-area-actions.js +1 -0
  76. package/addon/services/service-rate-actions.js +1 -0
  77. package/addon/services/telematic-actions.js +1 -0
  78. package/addon/services/trailer-actions.js +4 -14
  79. package/addon/services/vehicle-actions.js +4 -13
  80. package/addon/services/vendor-actions.js +1 -0
  81. package/addon/services/work-order-actions.js +1 -0
  82. package/addon/services/zone-actions.js +1 -0
  83. package/addon/templates/analytics/reports/index/details.hbs +1 -0
  84. package/addon/templates/analytics/reports/index.hbs +1 -0
  85. package/addon/templates/connectivity/devices/index/details.hbs +1 -0
  86. package/addon/templates/connectivity/devices/index.hbs +1 -0
  87. package/addon/templates/connectivity/events/index.hbs +1 -0
  88. package/addon/templates/connectivity/sensors/index/details.hbs +1 -0
  89. package/addon/templates/connectivity/sensors/index.hbs +1 -0
  90. package/addon/templates/maintenance/equipment/index/details.hbs +1 -0
  91. package/addon/templates/maintenance/equipment/index.hbs +2 -0
  92. package/addon/templates/maintenance/inspection-forms/index/details.hbs +1 -0
  93. package/addon/templates/maintenance/inspection-forms/index.hbs +1 -0
  94. package/addon/templates/maintenance/inspection-submissions/index/details.hbs +1 -0
  95. package/addon/templates/maintenance/inspection-submissions/index.hbs +1 -0
  96. package/addon/templates/maintenance/maintenances/index/details.hbs +1 -0
  97. package/addon/templates/maintenance/maintenances/index.hbs +1 -0
  98. package/addon/templates/maintenance/parts/index/details.hbs +1 -0
  99. package/addon/templates/maintenance/parts/index.hbs +2 -0
  100. package/addon/templates/maintenance/schedules/index/details.hbs +1 -0
  101. package/addon/templates/maintenance/schedules/index.hbs +2 -0
  102. package/addon/templates/maintenance/work-orders/index/details.hbs +1 -0
  103. package/addon/templates/maintenance/work-orders/index.hbs +1 -0
  104. package/addon/templates/management/contacts/customers/details.hbs +1 -0
  105. package/addon/templates/management/contacts/customers.hbs +1 -0
  106. package/addon/templates/management/contacts/index/details.hbs +1 -0
  107. package/addon/templates/management/contacts/index.hbs +1 -0
  108. package/addon/templates/management/contacts.hbs +1 -0
  109. package/addon/templates/management/drivers/index/details.hbs +1 -0
  110. package/addon/templates/management/drivers/index.hbs +2 -0
  111. package/addon/templates/management/fleets/index/details.hbs +1 -0
  112. package/addon/templates/management/fleets/index.hbs +1 -0
  113. package/addon/templates/management/fuel-reports/index/details.hbs +1 -0
  114. package/addon/templates/management/fuel-reports/index.hbs +1 -0
  115. package/addon/templates/management/fuel-transactions/index.hbs +1 -0
  116. package/addon/templates/management/issues/index/details.hbs +1 -0
  117. package/addon/templates/management/issues/index.hbs +1 -0
  118. package/addon/templates/management/places/index/details.hbs +1 -0
  119. package/addon/templates/management/places/index.hbs +1 -0
  120. package/addon/templates/management/trailers/index/details.hbs +1 -0
  121. package/addon/templates/management/trailers/index.hbs +2 -0
  122. package/addon/templates/management/vehicles/index/details.hbs +1 -0
  123. package/addon/templates/management/vehicles/index.hbs +2 -0
  124. package/addon/templates/management/vendors/index/details.hbs +1 -0
  125. package/addon/templates/management/vendors/index.hbs +2 -0
  126. package/addon/templates/management/vendors/integrated.hbs +1 -0
  127. package/addon/templates/operations/orders/index/details.hbs +1 -0
  128. package/addon/templates/operations/orders/index.hbs +1 -0
  129. package/addon/templates/operations/service-rates/index/details.hbs +1 -0
  130. package/addon/templates/operations/service-rates/index.hbs +1 -0
  131. package/addon/utils/normalize-order-config-flow.js +6 -1
  132. package/app/services/order-presentation.js +1 -0
  133. package/composer.json +1 -1
  134. package/extension.json +1 -1
  135. package/package.json +3 -3
  136. package/server/src/Http/Controllers/Internal/v1/OrderController.php +34 -4
  137. package/server/src/Models/Order.php +35 -3
  138. package/server/src/Models/OrderConfig.php +129 -0
  139. package/server/src/Models/TrackingNumber.php +8 -4
  140. package/server/tests/Feature/Http/Internal/OrderControllerActivityFlowsTest.php +72 -0
  141. package/server/tests/Feature/Http/Internal/OrderControllerCreateRecordTest.php +26 -0
  142. package/server/tests/OrderConfigLifecycleTest.php +120 -0
  143. package/server/tests/Unit/Events/OrderDispatchFailedTest.php +2 -1
  144. package/translations/en-us.yaml +60 -0
@@ -0,0 +1,358 @@
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 { isArray } from '@ember/array';
6
+ import { task } from 'ember-concurrency';
7
+ import { underscore } from '@ember/string';
8
+ import generateUUID from '@fleetbase/ember-core/utils/generate-uuid';
9
+ import normalizeOrderConfigFlow, { getOrderConfigFlowRootCode } from '../../utils/normalize-order-config-flow';
10
+
11
+ /** Keys of `meta.lifecycle` understood by the server (see OrderConfig::lifecycle()). */
12
+ const DEFAULTS = { initial: null, completed: null, canceled: null, terminal: [], dispatch: true, strict_transitions: false };
13
+
14
+ const NEW_ACTIVITY = { status: '', code: '', details: '', color: '#1f2937' };
15
+
16
+ /**
17
+ * Manages an order config's configured lifecycle (`meta.lifecycle`): the activity new orders start
18
+ * at, the activities that complete, cancel and end an order, whether orders may be dispatched and
19
+ * whether transitions must follow the flow. Without a configured lifecycle the default dispatch
20
+ * lifecycle applies.
21
+ *
22
+ * The activities themselves (the config's `flow`) can be defined here too, so a lifecycle can be
23
+ * built from scratch without the graph editor: add or remove activities, set their labels and the
24
+ * activities each one can move to. Flow and lifecycle are saved together.
25
+ *
26
+ * @extends Component
27
+ */
28
+ export default class OrderConfigManagerLifecycleComponent extends Component {
29
+ @service modalsManager;
30
+ @service notifications;
31
+ @service abilities;
32
+ @service intl;
33
+
34
+ /** The config being managed. */
35
+ @tracked config;
36
+
37
+ /** Working copy of `meta.lifecycle`, or null when the config has none. */
38
+ @tracked lifecycle = null;
39
+
40
+ /** Last saved lifecycle, to tell whether there is anything to save. */
41
+ @tracked saved = null;
42
+
43
+ /** Working copy of the config's flow, keyed by activity code, with next activities as codes. */
44
+ @tracked flow = {};
45
+
46
+ /** Flow as last saved, serialized, to tell whether there is anything to save. */
47
+ @tracked savedFlow = '{}';
48
+
49
+ /** The activity being added. */
50
+ @tracked newActivity = { ...NEW_ACTIVITY };
51
+
52
+ constructor(owner, { config, configManagerContext }) {
53
+ super(...arguments);
54
+ this.changeConfig(config);
55
+
56
+ configManagerContext.on('onConfigChanged', (newConfig) => {
57
+ this.changeConfig(newConfig);
58
+ });
59
+ }
60
+
61
+ get canEdit() {
62
+ return this.abilities.can('fleet-ops update order-config') && !this.config?.core_service;
63
+ }
64
+
65
+ get enabled() {
66
+ return this.lifecycle !== null;
67
+ }
68
+
69
+ /** Activities of the flow as select options. */
70
+ get activities() {
71
+ return Object.values(this.flow).map((activity) => ({ code: activity.code, label: activity.status || activity.code }));
72
+ }
73
+
74
+ /** Activities with their next activities resolved, for the editor. */
75
+ get activityRows() {
76
+ return Object.values(this.flow).map((activity) => ({
77
+ ...activity,
78
+ next: (activity.activities ?? []).map((code) => this.option(code)).filter(Boolean),
79
+ isInitial: this.lifecycle?.initial === activity.code,
80
+ }));
81
+ }
82
+
83
+ get hasActivities() {
84
+ return Object.keys(this.flow).length > 0;
85
+ }
86
+
87
+ get newActivityCode() {
88
+ return underscore((this.newActivity.code || this.newActivity.status || '').trim());
89
+ }
90
+
91
+ get canAddActivity() {
92
+ const code = this.newActivityCode;
93
+ return this.canEdit && this.newActivity.status.trim() !== '' && code !== '' && !this.flow[code];
94
+ }
95
+
96
+ get newActivityCodeTaken() {
97
+ const code = this.newActivityCode;
98
+ return code !== '' && Boolean(this.flow[code]);
99
+ }
100
+
101
+ get selectedInitial() {
102
+ return this.option(this.lifecycle?.initial);
103
+ }
104
+
105
+ get selectedCompleted() {
106
+ return this.option(this.lifecycle?.completed);
107
+ }
108
+
109
+ get selectedCanceled() {
110
+ return this.option(this.lifecycle?.canceled);
111
+ }
112
+
113
+ get selectedTerminal() {
114
+ return (this.lifecycle?.terminal ?? []).map((code) => this.option(code)).filter(Boolean);
115
+ }
116
+
117
+ get isDirty() {
118
+ return JSON.stringify(this.lifecycle) !== JSON.stringify(this.saved) || this.serializeFlow() !== this.savedFlow;
119
+ }
120
+
121
+ /** Problems that make the lifecycle unusable server-side. */
122
+ get problems() {
123
+ if (!this.lifecycle) {
124
+ return [];
125
+ }
126
+
127
+ const problems = [];
128
+ const codes = this.activities.map((activity) => activity.code);
129
+ if (codes.length === 0) {
130
+ problems.push(this.intl.t('order-config-manager.lifecycle.problem-no-activities'));
131
+ }
132
+ if (!this.lifecycle.initial) {
133
+ problems.push(this.intl.t('order-config-manager.lifecycle.problem-initial-required'));
134
+ }
135
+
136
+ const referenced = [this.lifecycle.initial, this.lifecycle.completed, this.lifecycle.canceled, ...(this.lifecycle.terminal ?? [])].filter(Boolean);
137
+ for (const code of new Set(referenced)) {
138
+ if (!codes.includes(code)) {
139
+ problems.push(this.intl.t('order-config-manager.lifecycle.problem-missing-activity', { code }));
140
+ }
141
+ }
142
+
143
+ return problems;
144
+ }
145
+
146
+ get canSave() {
147
+ return this.canEdit && this.isDirty && this.problems.length === 0;
148
+ }
149
+
150
+ /** Arrow function so templates can call it as a helper: (this.option code). */
151
+ option = (code) => {
152
+ return code ? (this.activities.find((activity) => activity.code === code) ?? { code, label: code }) : null;
153
+ };
154
+
155
+ changeConfig(config) {
156
+ this.config = config;
157
+ this.saved = this.normalize(config?.meta?.lifecycle);
158
+ this.lifecycle = this.normalize(config?.meta?.lifecycle);
159
+ this.flow = this.readFlow(config?.flow);
160
+ this.savedFlow = this.serializeFlow();
161
+ this.newActivity = { ...NEW_ACTIVITY };
162
+ }
163
+
164
+ /** The stored flow as plain activity objects with next activities as codes. */
165
+ readFlow(flow) {
166
+ const normalized = normalizeOrderConfigFlow(flow ?? {});
167
+ const result = {};
168
+ Object.values(normalized).forEach((activity, index) => {
169
+ if (!activity || typeof activity !== 'object') {
170
+ return;
171
+ }
172
+ const code = activity.code ?? activity.key;
173
+ if (!code) {
174
+ return;
175
+ }
176
+ result[code] = {
177
+ ...activity,
178
+ code,
179
+ key: activity.key ?? code,
180
+ sequence: activity.sequence ?? index,
181
+ activities: (isArray(activity.activities) ? activity.activities : []).map((next) => (typeof next === 'string' ? next : (next?.code ?? next?.key))).filter(Boolean),
182
+ };
183
+ });
184
+ return result;
185
+ }
186
+
187
+ serializeFlow() {
188
+ const serialized = {};
189
+ Object.values(this.flow).forEach((activity) => {
190
+ // Graph-only state never belongs in the stored flow.
191
+ // eslint-disable-next-line no-unused-vars
192
+ const { node, id, parentId, _internalModel, ...stored } = activity;
193
+ serialized[activity.code] = { ...stored, activities: [...(activity.activities ?? [])] };
194
+ });
195
+ return JSON.stringify(serialized);
196
+ }
197
+
198
+ @action setNewActivityField(field, event) {
199
+ this.newActivity = { ...this.newActivity, [field]: event.target.value };
200
+ }
201
+
202
+ @action addActivity() {
203
+ if (!this.canAddActivity) {
204
+ return;
205
+ }
206
+
207
+ const code = this.newActivityCode;
208
+ const activity = {
209
+ key: code,
210
+ code,
211
+ status: this.newActivity.status.trim(),
212
+ details: this.newActivity.details.trim(),
213
+ color: this.newActivity.color || '#1f2937',
214
+ sequence: Object.keys(this.flow).length,
215
+ activities: [],
216
+ events: [],
217
+ logic: [],
218
+ actions: [],
219
+ entities: [],
220
+ complete: false,
221
+ require_pod: false,
222
+ pod_method: 'scan',
223
+ options: {},
224
+ internalId: generateUUID(),
225
+ };
226
+
227
+ this.flow = { ...this.flow, [code]: activity };
228
+ if (this.lifecycle && !this.lifecycle.initial) {
229
+ this.lifecycle = { ...this.lifecycle, initial: code };
230
+ }
231
+ this.newActivity = { ...NEW_ACTIVITY };
232
+ }
233
+
234
+ @action removeActivity(code) {
235
+ const flow = { ...this.flow };
236
+ delete flow[code];
237
+ Object.keys(flow).forEach((key) => {
238
+ flow[key] = { ...flow[key], activities: flow[key].activities.filter((next) => next !== code) };
239
+ });
240
+ this.flow = flow;
241
+
242
+ if (this.lifecycle) {
243
+ const without = (value) => (value === code ? null : value);
244
+ this.lifecycle = {
245
+ ...this.lifecycle,
246
+ initial: without(this.lifecycle.initial),
247
+ completed: without(this.lifecycle.completed),
248
+ canceled: without(this.lifecycle.canceled),
249
+ terminal: this.lifecycle.terminal.filter((item) => item !== code),
250
+ };
251
+ }
252
+ }
253
+
254
+ @action setActivityField(code, field, event) {
255
+ this.flow = { ...this.flow, [code]: { ...this.flow[code], [field]: event.target.value } };
256
+ }
257
+
258
+ @action setNextActivities(code, selected) {
259
+ this.flow = { ...this.flow, [code]: { ...this.flow[code], activities: (selected ?? []).map((activity) => activity.code) } };
260
+ }
261
+
262
+ /** Drops the default dispatch activities so a lifecycle can be designed from nothing. */
263
+ @action startBlankFlow() {
264
+ this.modalsManager.confirm({
265
+ title: this.intl.t('order-config-manager.lifecycle.blank-flow-title'),
266
+ body: this.intl.t('order-config-manager.lifecycle.blank-flow-body'),
267
+ acceptButtonText: this.intl.t('order-config-manager.lifecycle.blank-flow-confirm'),
268
+ confirm: (modal) => {
269
+ this.flow = {};
270
+ if (this.lifecycle) {
271
+ this.lifecycle = { ...this.lifecycle, initial: null, completed: null, canceled: null, terminal: [] };
272
+ }
273
+ modal.done();
274
+ },
275
+ });
276
+ }
277
+
278
+ normalize(lifecycle) {
279
+ if (!lifecycle || typeof lifecycle !== 'object' || Object.keys(lifecycle).length === 0) {
280
+ return null;
281
+ }
282
+
283
+ return {
284
+ ...DEFAULTS,
285
+ ...lifecycle,
286
+ terminal: isArray(lifecycle.terminal) ? [...lifecycle.terminal] : [],
287
+ dispatch: !['false', '0', 0, false].includes(lifecycle.dispatch),
288
+ strict_transitions: Boolean(lifecycle.strict_transitions),
289
+ };
290
+ }
291
+
292
+ @action toggleEnabled(enabled) {
293
+ if (enabled) {
294
+ const flow = normalizeOrderConfigFlow(this.config?.flow ?? {});
295
+ this.lifecycle = this.saved ? { ...this.saved, terminal: [...this.saved.terminal] } : { ...DEFAULTS, terminal: [], initial: getOrderConfigFlowRootCode(flow) ?? null };
296
+ return;
297
+ }
298
+
299
+ if (this.saved) {
300
+ this.modalsManager.confirm({
301
+ title: this.intl.t('order-config-manager.lifecycle.remove-title'),
302
+ body: this.intl.t('order-config-manager.lifecycle.remove-body'),
303
+ acceptButtonText: this.intl.t('order-config-manager.lifecycle.remove-confirm'),
304
+ confirm: async (modal) => {
305
+ modal.startLoading();
306
+ this.lifecycle = null;
307
+ await this.save.perform();
308
+ modal.done();
309
+ },
310
+ decline: (modal) => {
311
+ modal.done();
312
+ },
313
+ });
314
+ return;
315
+ }
316
+
317
+ this.lifecycle = null;
318
+ }
319
+
320
+ @action setActivity(key, activity) {
321
+ this.lifecycle = { ...this.lifecycle, [key]: activity?.code ?? null };
322
+ }
323
+
324
+ @action setTerminal(activities) {
325
+ this.lifecycle = { ...this.lifecycle, terminal: (activities ?? []).map((activity) => activity.code) };
326
+ }
327
+
328
+ @action setFlag(key, value) {
329
+ this.lifecycle = { ...this.lifecycle, [key]: Boolean(value) };
330
+ }
331
+
332
+ @task *save() {
333
+ const meta = { ...(this.config.meta ?? {}) };
334
+ if (this.lifecycle) {
335
+ meta.lifecycle = { ...this.lifecycle };
336
+ } else {
337
+ delete meta.lifecycle;
338
+ }
339
+
340
+ try {
341
+ this.config.set('meta', meta);
342
+ this.config.set('flow', JSON.parse(this.serializeFlow()));
343
+ yield this.config.save();
344
+ this.saved = this.normalize(this.lifecycle);
345
+ this.lifecycle = this.normalize(this.lifecycle);
346
+ this.savedFlow = this.serializeFlow();
347
+ this.notifications.success(this.intl.t('order-config-manager.lifecycle.saved', { orderConfigName: this.config.name }));
348
+ if (typeof this.args.onConfigUpdated === 'function') {
349
+ this.args.onConfigUpdated(this.config);
350
+ }
351
+ } catch (error) {
352
+ this.config.rollbackAttributes();
353
+ this.lifecycle = this.normalize(this.config.meta?.lifecycle);
354
+ this.flow = this.readFlow(this.config.flow);
355
+ this.notifications.serverError(error);
356
+ }
357
+ }
358
+ }
@@ -14,6 +14,7 @@ import applyContextComponentArguments from '@fleetbase/ember-core/utils/apply-co
14
14
  import OrderConfigManagerDetailsComponent from './order-config-manager/details';
15
15
  import OrderConfigManagerCustomFieldsComponent from './order-config-manager/custom-fields';
16
16
  import OrderConfigManagerActivityFlowComponent from './order-config-manager/activity-flow';
17
+ import OrderConfigManagerLifecycleComponent from './order-config-manager/lifecycle';
17
18
  import OrderConfigManagerEntitiesComponent from './order-config-manager/entities';
18
19
  import findActiveTab from '../utils/find-active-tab';
19
20
 
@@ -45,6 +46,7 @@ export default class OrderConfigManagerComponent extends Component {
45
46
  this.universe._createMenuItem(this.intl.t('order-config-manager.tabs.details'), null, { icon: 'circle-info', component: OrderConfigManagerDetailsComponent }),
46
47
  this.universe._createMenuItem(this.intl.t('order-config-manager.tabs.custom-fields'), null, { icon: 'rectangle-list', component: OrderConfigManagerCustomFieldsComponent }),
47
48
  this.universe._createMenuItem(this.intl.t('order-config-manager.tabs.activity-flow'), null, { icon: 'diagram-project', component: OrderConfigManagerActivityFlowComponent }),
49
+ this.universe._createMenuItem(this.intl.t('order-config-manager.tabs.lifecycle'), null, { icon: 'arrows-spin', component: OrderConfigManagerLifecycleComponent }),
48
50
  this.universe._createMenuItem(this.intl.t('order-config-manager.tabs.entities'), null, { icon: 'boxes-packing', component: OrderConfigManagerEntitiesComponent }),
49
51
  ];
50
52
 
@@ -106,7 +108,9 @@ export default class OrderConfigManagerComponent extends Component {
106
108
  */
107
109
  @task *loadOrderConfigs(options = {}) {
108
110
  try {
109
- const configs = yield this.store.findAll('order-config').then(Array.from);
111
+ // Always fetch: `findAll` otherwise resolves with whatever subset of configs the store
112
+ // already holds (e.g. from an order's embedded config) and only refreshes in the background.
113
+ const configs = yield this.store.findAll('order-config', { reload: true }).then(Array.from);
110
114
  let currentConfig;
111
115
  if (isArray(configs) && configs.length > 0) {
112
116
  currentConfig = configs.find((c) => c.id === this.args.orderConfig);
@@ -8,7 +8,7 @@ export default class AnalyticsReportsIndexController extends Controller {
8
8
  @service intl;
9
9
 
10
10
  /** query params */
11
- @tracked queryParams = ['page', 'limit', 'sort', 'query', 'public_id', 'name', 'created_at'];
11
+ @tracked queryParams = this.reportActions.queryParamsFor(['page', 'limit', 'sort', 'query', 'public_id', 'name', 'created_at']);
12
12
  @tracked page = 1;
13
13
  @tracked limit;
14
14
  @tracked sort = '-created_at';
@@ -20,11 +20,13 @@ export default class AnalyticsReportsIndexController extends Controller {
20
20
  get actionButtons() {
21
21
  return [
22
22
  {
23
+ id: 'refresh',
23
24
  icon: 'refresh',
24
25
  onClick: this.reportActions.refresh,
25
26
  helpText: this.intl.t('common.refresh'),
26
27
  },
27
28
  {
29
+ id: 'create',
28
30
  text: this.intl.t('common.new'),
29
31
  type: 'primary',
30
32
  icon: 'plus',
@@ -37,6 +39,7 @@ export default class AnalyticsReportsIndexController extends Controller {
37
39
  get bulkActions() {
38
40
  return [
39
41
  {
42
+ id: 'bulk-delete',
40
43
  label: 'Delete selected...',
41
44
  class: 'text-red-500',
42
45
  fn: this.reportActions.bulkDelete,
@@ -48,6 +51,7 @@ export default class AnalyticsReportsIndexController extends Controller {
48
51
  get columns() {
49
52
  return [
50
53
  {
54
+ id: 'title',
51
55
  sticky: true,
52
56
  label: 'Title',
53
57
  valuePath: 'title',
@@ -59,6 +63,7 @@ export default class AnalyticsReportsIndexController extends Controller {
59
63
  filterComponent: 'filter/string',
60
64
  },
61
65
  {
66
+ id: 'public-id',
62
67
  label: 'ID',
63
68
  valuePath: 'public_id',
64
69
  cellComponent: 'click-to-copy',
@@ -69,6 +74,7 @@ export default class AnalyticsReportsIndexController extends Controller {
69
74
  filterComponent: 'filter/string',
70
75
  },
71
76
  {
77
+ id: 'row-actions',
72
78
  label: '',
73
79
  cellComponent: 'table/cell/dropdown',
74
80
  ddButtonText: false,
@@ -81,11 +87,13 @@ export default class AnalyticsReportsIndexController extends Controller {
81
87
  width: 60,
82
88
  actions: [
83
89
  {
90
+ id: 'view',
84
91
  label: 'View report...',
85
92
  fn: this.reportActions.transition.view,
86
93
  permission: 'iam view report',
87
94
  },
88
95
  {
96
+ id: 'edit',
89
97
  label: 'Edit report...',
90
98
  fn: this.reportActions.transition.edit,
91
99
  permission: 'iam update report',
@@ -94,6 +102,7 @@ export default class AnalyticsReportsIndexController extends Controller {
94
102
  separator: true,
95
103
  },
96
104
  {
105
+ id: 'delete',
97
106
  label: 'Delete report...',
98
107
  fn: this.reportActions.delete,
99
108
  permission: 'iam delete report',
@@ -39,6 +39,7 @@ export default class ConnectivityDevicesIndexDetailsController extends Controlle
39
39
  permission: 'fleet-ops update device',
40
40
  },
41
41
  {
42
+ id: 'more',
42
43
  icon: 'ellipsis-h',
43
44
  iconPrefix: 'fas',
44
45
  renderInPlace: true,
@@ -17,7 +17,7 @@ export default class ConnectivityDevicesIndexController extends Controller {
17
17
  @service trailerActions;
18
18
 
19
19
  /** query params */
20
- @tracked queryParams = [
20
+ @tracked queryParams = this.deviceActions.queryParamsFor([
21
21
  'name',
22
22
  'status',
23
23
  'attachment_state',
@@ -36,7 +36,7 @@ export default class ConnectivityDevicesIndexController extends Controller {
36
36
  'public_id',
37
37
  'created_at',
38
38
  'updated_at',
39
- ];
39
+ ]);
40
40
  @tracked page = 1;
41
41
  @tracked limit;
42
42
  @tracked sort = '-created_at';
@@ -56,11 +56,13 @@ export default class ConnectivityDevicesIndexController extends Controller {
56
56
  /** action buttons */
57
57
  @tracked actionButtons = [
58
58
  {
59
+ id: 'refresh',
59
60
  icon: 'refresh',
60
61
  onClick: this.deviceActions.refresh,
61
62
  helpText: this.intl.t('common.refresh'),
62
63
  },
63
64
  {
65
+ id: 'create',
64
66
  text: this.intl.t('common.new'),
65
67
  type: 'primary',
66
68
  icon: 'plus',
@@ -68,6 +70,7 @@ export default class ConnectivityDevicesIndexController extends Controller {
68
70
  permission: 'fleet-ops create device',
69
71
  },
70
72
  {
73
+ id: 'import',
71
74
  text: this.intl.t('common.import'),
72
75
  type: 'magic',
73
76
  icon: 'upload',
@@ -75,6 +78,7 @@ export default class ConnectivityDevicesIndexController extends Controller {
75
78
  permission: 'fleet-ops import device',
76
79
  },
77
80
  {
81
+ id: 'export',
78
82
  text: this.intl.t('common.export'),
79
83
  icon: 'long-arrow-up',
80
84
  iconClass: 'rotate-icon-45',
@@ -87,6 +91,7 @@ export default class ConnectivityDevicesIndexController extends Controller {
87
91
  /** bulk action buttons */
88
92
  @tracked bulkActions = [
89
93
  {
94
+ id: 'bulk-delete',
90
95
  label: 'Delete selected...',
91
96
  class: 'text-red-500',
92
97
  fn: this.deviceActions.bulkDelete,
@@ -111,6 +116,7 @@ export default class ConnectivityDevicesIndexController extends Controller {
111
116
  separator: true,
112
117
  },
113
118
  {
119
+ id: 'delete',
114
120
  label: this.intl.t('common.delete-resource', { resource: this.intl.t('resource.device') }),
115
121
  fn: this.deviceActions.delete,
116
122
  permission: 'fleet-ops delete device',
@@ -78,7 +78,20 @@ export default class ConnectivityEventsIndexController extends Controller {
78
78
  @service telematicActions;
79
79
 
80
80
  /** query params */
81
- @tracked queryParams = ['page', 'limit', 'sort', 'query', 'telematic', 'device', 'event_type', 'severity', 'processed', 'occurred_at', 'created_at', 'updated_at'];
81
+ @tracked queryParams = this.deviceEventActions.queryParamsFor([
82
+ 'page',
83
+ 'limit',
84
+ 'sort',
85
+ 'query',
86
+ 'telematic',
87
+ 'device',
88
+ 'event_type',
89
+ 'severity',
90
+ 'processed',
91
+ 'occurred_at',
92
+ 'created_at',
93
+ 'updated_at',
94
+ ]);
82
95
  @tracked page = 1;
83
96
  @tracked limit;
84
97
  @tracked sort = '-created_at';
@@ -95,6 +108,7 @@ export default class ConnectivityEventsIndexController extends Controller {
95
108
  /** action buttons */
96
109
  @tracked actionButtons = [
97
110
  {
111
+ id: 'refresh',
98
112
  icon: 'refresh',
99
113
  onClick: this.deviceEventActions.refresh,
100
114
  helpText: this.intl.t('common.refresh'),
@@ -107,6 +121,7 @@ export default class ConnectivityEventsIndexController extends Controller {
107
121
  /** columns */
108
122
  @tracked columns = [
109
123
  {
124
+ id: 'event-type',
110
125
  sticky: true,
111
126
  label: 'Event',
112
127
  valuePath: 'event_type',
@@ -121,6 +136,7 @@ export default class ConnectivityEventsIndexController extends Controller {
121
136
  filterComponent: 'filter/string',
122
137
  },
123
138
  {
139
+ id: 'device-display-name',
124
140
  label: 'Device',
125
141
  valuePath: 'device.displayName',
126
142
  cellComponent: 'cell/device-identity',
@@ -137,6 +153,7 @@ export default class ConnectivityEventsIndexController extends Controller {
137
153
  modelNamePath: 'displayName',
138
154
  },
139
155
  {
156
+ id: 'provider',
140
157
  label: 'Provider',
141
158
  valuePath: 'provider',
142
159
  cellComponent: 'cell/telematic-identity',
@@ -152,6 +169,7 @@ export default class ConnectivityEventsIndexController extends Controller {
152
169
  model: 'telematic',
153
170
  },
154
171
  {
172
+ id: 'severity',
155
173
  label: 'Severity',
156
174
  valuePath: 'severity',
157
175
  cellComponent: 'table/cell/status',
@@ -165,12 +183,14 @@ export default class ConnectivityEventsIndexController extends Controller {
165
183
  filterOptionValue: 'value',
166
184
  },
167
185
  {
186
+ id: 'message',
168
187
  label: 'Message',
169
188
  valuePath: 'message',
170
189
  resizable: true,
171
190
  sortable: false,
172
191
  },
173
192
  {
193
+ id: 'code',
174
194
  label: 'Code',
175
195
  valuePath: 'code',
176
196
  resizable: true,
@@ -180,6 +200,7 @@ export default class ConnectivityEventsIndexController extends Controller {
180
200
  filterComponent: 'filter/string',
181
201
  },
182
202
  {
203
+ id: 'ident',
183
204
  label: 'IDENT',
184
205
  valuePath: 'ident',
185
206
  hidden: true,
@@ -187,6 +208,7 @@ export default class ConnectivityEventsIndexController extends Controller {
187
208
  sortable: true,
188
209
  },
189
210
  {
211
+ id: 'protocol',
190
212
  label: 'Protocol',
191
213
  valuePath: 'protocol',
192
214
  hidden: true,
@@ -194,6 +216,7 @@ export default class ConnectivityEventsIndexController extends Controller {
194
216
  sortable: true,
195
217
  },
196
218
  {
219
+ id: 'state',
197
220
  label: 'State',
198
221
  valuePath: 'state',
199
222
  hidden: true,
@@ -201,6 +224,7 @@ export default class ConnectivityEventsIndexController extends Controller {
201
224
  sortable: true,
202
225
  },
203
226
  {
227
+ id: 'processed-at',
204
228
  label: 'Processed',
205
229
  valuePath: 'processedAt',
206
230
  sortParam: 'processed_at',
@@ -214,6 +238,7 @@ export default class ConnectivityEventsIndexController extends Controller {
214
238
  filterOptionValue: 'value',
215
239
  },
216
240
  {
241
+ id: 'occurred-at',
217
242
  label: 'Occurred',
218
243
  valuePath: 'occurredAt',
219
244
  sortParam: 'occurred_at',
@@ -224,6 +249,7 @@ export default class ConnectivityEventsIndexController extends Controller {
224
249
  filterComponent: 'filter/date',
225
250
  },
226
251
  {
252
+ id: 'created-at',
227
253
  label: this.intl.t('column.created-at'),
228
254
  valuePath: 'createdAt',
229
255
  sortParam: 'created_at',
@@ -234,6 +260,7 @@ export default class ConnectivityEventsIndexController extends Controller {
234
260
  filterComponent: 'filter/date',
235
261
  },
236
262
  {
263
+ id: 'updated-at',
237
264
  label: this.intl.t('column.updated-at'),
238
265
  valuePath: 'updatedAt',
239
266
  sortParam: 'updated_at',
@@ -245,6 +272,7 @@ export default class ConnectivityEventsIndexController extends Controller {
245
272
  hidden: true,
246
273
  },
247
274
  {
275
+ id: 'row-actions',
248
276
  label: '',
249
277
  cellComponent: 'table/cell/dropdown',
250
278
  ddButtonText: false,
@@ -257,11 +285,13 @@ export default class ConnectivityEventsIndexController extends Controller {
257
285
  width: 60,
258
286
  actions: [
259
287
  {
288
+ id: 'view',
260
289
  label: this.intl.t('common.view-resource', { resource: this.intl.t('resource.device-event') }),
261
290
  fn: this.deviceEventActions.transition.view,
262
291
  permission: 'fleet-ops view device-event',
263
292
  },
264
293
  {
294
+ id: 'mark-processed',
265
295
  label: 'Mark processed',
266
296
  fn: this.markProcessed,
267
297
  permission: 'fleet-ops update device-event',
@@ -7,7 +7,7 @@ export default class ConnectivitySensorsIndexDetailsController extends Controlle
7
7
  @service hostRouter;
8
8
 
9
9
  get tabs() {
10
- const registeredTabs = this.menuService.getMenuItems('fleet-ops:component:place:details');
10
+ const registeredTabs = this.menuService.getMenuItems('fleet-ops:component:sensor:details');
11
11
  return [
12
12
  {
13
13
  route: 'connectivity.sensors.index.details.index',