@fleetbase/fleetops-engine 0.6.52 → 0.6.53

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 (60) hide show
  1. package/addon/components/layout/fleet-ops-sidebar/operations-monitor.hbs +201 -0
  2. package/addon/components/layout/fleet-ops-sidebar/operations-monitor.js +589 -0
  3. package/addon/components/layout/fleet-ops-sidebar.hbs +19 -233
  4. package/addon/components/layout/fleet-ops-sidebar.js +287 -394
  5. package/addon/components/maintenance-schedule/form.js +1 -0
  6. package/addon/components/order/form/payload.js +3 -1
  7. package/addon/controllers/connectivity/devices/index.js +4 -1
  8. package/addon/controllers/maintenance/index.js +100 -0
  9. package/addon/controllers/management/fuel-transactions/index.js +1 -1
  10. package/addon/controllers/management/index.js +88 -0
  11. package/addon/controllers/settings/index.js +142 -0
  12. package/addon/extension.js +21 -0
  13. package/addon/routes/analytics/index.js +3 -0
  14. package/addon/routes/connectivity/devices/index.js +3 -0
  15. package/addon/routes/maintenance/index.js +3 -0
  16. package/addon/routes/maintenance/work-orders/index.js +2 -0
  17. package/addon/routes/settings/index.js +3 -0
  18. package/addon/styles/fleetops-engine.css +1801 -2
  19. package/addon/templates/analytics/index.hbs +14 -0
  20. package/addon/templates/analytics/reports/index.hbs +4 -4
  21. package/addon/templates/maintenance/index.hbs +113 -0
  22. package/addon/templates/management/fuel-transactions/index/details.hbs +1 -1
  23. package/addon/templates/management/index.hbs +109 -73
  24. package/addon/templates/operations/orders/index.hbs +1 -1
  25. package/addon/templates/operations/service-rates/index.hbs +1 -1
  26. package/addon/templates/settings/index.hbs +83 -0
  27. package/addon/templates/settings/routing.hbs +2 -2
  28. package/app/controllers/maintenance/index.js +1 -0
  29. package/app/controllers/management/index.js +1 -0
  30. package/app/controllers/settings/index.js +1 -0
  31. package/app/routes/analytics/index.js +1 -0
  32. package/app/routes/maintenance/index.js +1 -0
  33. package/app/routes/settings/index.js +1 -0
  34. package/app/templates/analytics/index.js +1 -0
  35. package/app/templates/maintenance/index.js +1 -0
  36. package/app/templates/management/index.js +1 -0
  37. package/app/templates/settings/index.js +1 -0
  38. package/composer.json +1 -1
  39. package/extension.json +1 -1
  40. package/package.json +3 -3
  41. package/server/migrations/2026_06_15_000001_normalize_device_attachable_vehicle_morph_types.php +30 -0
  42. package/server/src/Console/Commands/FixInvalidPolymorphicRelationTypeNamespaces.php +1 -0
  43. package/server/src/Http/Controllers/Internal/v1/DeviceController.php +75 -11
  44. package/server/src/Http/Controllers/Internal/v1/HubController.php +300 -0
  45. package/server/src/Http/Controllers/Internal/v1/SearchController.php +506 -0
  46. package/server/src/Http/Controllers/Internal/v1/TelematicController.php +1 -1
  47. package/server/src/Http/Controllers/Internal/v1/VehicleController.php +94 -10
  48. package/server/src/Http/Filter/DeviceFilter.php +74 -0
  49. package/server/src/Http/Filter/DriverFilter.php +6 -0
  50. package/server/src/Http/Filter/VehicleFilter.php +6 -0
  51. package/server/src/Jobs/SyncTelematicDevicesJob.php +82 -24
  52. package/server/src/Models/Device.php +1 -1
  53. package/server/src/Models/Driver.php +1 -1
  54. package/server/src/Providers/FleetOpsServiceProvider.php +10 -0
  55. package/server/src/Support/Telematics/Providers/AfaqyProvider.php +51 -13
  56. package/server/src/routes.php +9 -0
  57. package/server/tests/HubRoutesTest.php +62 -0
  58. package/server/tests/TelematicsHardeningTest.php +94 -3
  59. package/translations/ar-ae.yml +1 -1
  60. package/translations/vi-vn.yaml +1 -1
@@ -37,6 +37,7 @@ const MODEL_TO_TYPE = {
37
37
  const ASSIGNEE_MODEL_TO_TYPE = {
38
38
  'facilitator-vendor': 'fleet-ops:vendor',
39
39
  'facilitator-contact': 'fleet-ops:contact',
40
+ 'facilitator-driver': 'fleet-ops:driver',
40
41
  'facilitator-integrated-vendor': 'fleet-ops:vendor',
41
42
  vendor: 'fleet-ops:vendor',
42
43
  contact: 'fleet-ops:contact',
@@ -69,6 +69,8 @@ export default class OrderFormPayloadComponent extends Component {
69
69
  }
70
70
 
71
71
  @action editEntity(entity) {
72
- this.entityActions.modal.edit(entity);
72
+ this.entityActions.modal.edit(entity, {
73
+ confirm: (modal) => modal.done(),
74
+ });
73
75
  }
74
76
  }
@@ -9,12 +9,15 @@ export default class ConnectivityDevicesIndexController extends Controller {
9
9
  @service intl;
10
10
 
11
11
  /** query params */
12
- @tracked queryParams = ['name', 'page', 'limit', 'sort', 'query', 'public_id', 'created_at', 'updated_at'];
12
+ @tracked queryParams = ['name', 'status', 'attachment_state', 'telematic', 'page', 'limit', 'sort', 'query', 'public_id', 'created_at', 'updated_at'];
13
13
  @tracked page = 1;
14
14
  @tracked limit;
15
15
  @tracked sort = '-created_at';
16
16
  @tracked public_id;
17
17
  @tracked name;
18
+ @tracked status;
19
+ @tracked attachment_state;
20
+ @tracked telematic;
18
21
 
19
22
  /** action buttons */
20
23
  @tracked actionButtons = [
@@ -0,0 +1,100 @@
1
+ import Controller from '@ember/controller';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { inject as service } from '@ember/service';
4
+ import { action } from '@ember/object';
5
+ import { task } from 'ember-concurrency';
6
+
7
+ export default class MaintenanceIndexController extends Controller {
8
+ @service fetch;
9
+ @service docsPanel;
10
+
11
+ @tracked hub = null;
12
+
13
+ constructor() {
14
+ super(...arguments);
15
+ this.loadHub.perform();
16
+ }
17
+
18
+ get kpis() {
19
+ return this.hub?.kpis ?? this.loadingKpis;
20
+ }
21
+
22
+ get actions() {
23
+ return (this.hub?.actions ?? []).map((action) => this.normalizeAction(action));
24
+ }
25
+
26
+ get sections() {
27
+ return this.hub?.sections ?? [];
28
+ }
29
+
30
+ get docs() {
31
+ return (this.hub?.docs ?? []).map((doc) => ({
32
+ ...doc,
33
+ description: doc.description ?? this.docDescriptions[doc.slug] ?? '',
34
+ }));
35
+ }
36
+
37
+ docDescriptions = {
38
+ 'fleet-ops/maintenance/schedules/overview': 'Plan recurring service windows and convert due schedules into work orders.',
39
+ 'fleet-ops/maintenance/work-orders/overview': 'Coordinate assigned maintenance work, vendors, due dates, and completion.',
40
+ 'fleet-ops/maintenance/equipment/overview': 'Track serviceable equipment that participates in maintenance operations.',
41
+ 'fleet-ops/maintenance/parts/overview': 'Manage parts inventory and restocking signals used by maintenance teams.',
42
+ };
43
+
44
+ normalizeAction(action = {}) {
45
+ const query = action.query && typeof action.query === 'object' && !Array.isArray(action.query) ? action.query : {};
46
+
47
+ return {
48
+ ...action,
49
+ query,
50
+ };
51
+ }
52
+
53
+ get loadingKpis() {
54
+ return [
55
+ {
56
+ key: 'overdue_schedules',
57
+ label: 'Overdue Schedules',
58
+ value: '...',
59
+ caption: 'Loading recurring service risk.',
60
+ tone: 'rose',
61
+ icon: 'calendar-xmark',
62
+ route: 'maintenance.schedules',
63
+ },
64
+ {
65
+ key: 'upcoming_schedules',
66
+ label: 'Due This Week',
67
+ value: '...',
68
+ caption: 'Loading upcoming service windows.',
69
+ tone: 'blue',
70
+ icon: 'calendar-day',
71
+ route: 'maintenance.schedules',
72
+ },
73
+ {
74
+ key: 'open_work_orders',
75
+ label: 'Open Work Orders',
76
+ value: '...',
77
+ caption: 'Loading active maintenance work.',
78
+ tone: 'amber',
79
+ icon: 'clipboard-list',
80
+ route: 'maintenance.work-orders',
81
+ },
82
+ { key: 'low_stock_parts', label: 'Low Stock Parts', value: '...', caption: 'Loading parts inventory signals.', tone: 'amber', icon: 'cog', route: 'maintenance.parts' },
83
+ ];
84
+ }
85
+
86
+ @action openDocs(link) {
87
+ if (!link?.slug) {
88
+ return;
89
+ }
90
+
91
+ return this.docsPanel.open(link.slug, {
92
+ title: link.title ?? link.label,
93
+ source: 'fleet-ops-maintenance-hub',
94
+ });
95
+ }
96
+
97
+ @task *loadHub() {
98
+ this.hub = yield this.fetch.get('fleet-ops/hubs/maintenance');
99
+ }
100
+ }
@@ -40,7 +40,7 @@ export default class ManagementFuelTransactionsIndexController extends Controlle
40
40
 
41
41
  get emptyStateMessage() {
42
42
  if (this.sync_status === 'unmatched') {
43
- return 'FleetOps did not find imported provider bills that still need vehicle or trip matching.';
43
+ return 'Fleet-Ops did not find imported provider bills that still need vehicle or trip matching.';
44
44
  }
45
45
 
46
46
  if (this.connection) {
@@ -0,0 +1,88 @@
1
+ import Controller from '@ember/controller';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { inject as service } from '@ember/service';
4
+ import { action } from '@ember/object';
5
+ import { task } from 'ember-concurrency';
6
+
7
+ export default class ManagementIndexController extends Controller {
8
+ @service fetch;
9
+ @service docsPanel;
10
+
11
+ @tracked hub = null;
12
+
13
+ constructor() {
14
+ super(...arguments);
15
+ this.loadHub.perform();
16
+ }
17
+
18
+ get kpis() {
19
+ return (this.hub?.kpis ?? this.loadingKpis).map((kpi) => ({
20
+ ...kpi,
21
+ actionLabel: kpi.actionLabel ?? this.kpiActionLabels[kpi.key] ?? `Open ${kpi.label}`,
22
+ }));
23
+ }
24
+
25
+ get actions() {
26
+ return (this.hub?.actions ?? []).map((action) => this.normalizeAction(action));
27
+ }
28
+
29
+ get sections() {
30
+ return this.hub?.sections ?? [];
31
+ }
32
+
33
+ get docs() {
34
+ return (this.hub?.docs ?? []).map((doc) => ({
35
+ ...doc,
36
+ description: doc.description ?? this.docDescriptions[doc.slug] ?? '',
37
+ }));
38
+ }
39
+
40
+ docDescriptions = {
41
+ 'fleet-ops/resources/drivers/overview': 'Create driver profiles, app access, assignment context, and operating status.',
42
+ 'fleet-ops/resources/vehicles/overview': 'Track fleet assets, assignment readiness, and vehicle operating details.',
43
+ 'fleet-ops/resources/fleets/overview': 'Group drivers and vehicles by team, region, or service coverage.',
44
+ 'fleet-ops/resources/contacts/overview': 'Manage people and organizations used across orders and operational records.',
45
+ 'fleet-ops/resources/places/overview': 'Manage reusable pickup, dropoff, hub, and facility locations.',
46
+ 'fleet-ops/resources/issues/overview': 'Track incidents, service exceptions, and operational follow-up.',
47
+ };
48
+
49
+ kpiActionLabels = {
50
+ drivers: 'Manage Drivers',
51
+ vehicles: 'Track Vehicles',
52
+ issues: 'Review Issues',
53
+ fuel_records: 'Review Fuel Records',
54
+ };
55
+
56
+ normalizeAction(action = {}) {
57
+ const query = action.query && typeof action.query === 'object' && !Array.isArray(action.query) ? action.query : {};
58
+
59
+ return {
60
+ ...action,
61
+ query,
62
+ };
63
+ }
64
+
65
+ get loadingKpis() {
66
+ return [
67
+ { key: 'drivers', label: 'Drivers', value: '...', caption: 'Loading resource readiness.', tone: 'blue', icon: 'id-card', route: 'management.drivers' },
68
+ { key: 'vehicles', label: 'Vehicles', value: '...', caption: 'Loading fleet asset coverage.', tone: 'green', icon: 'truck', route: 'management.vehicles' },
69
+ { key: 'issues', label: 'Open Issues', value: '...', caption: 'Loading exception health.', tone: 'rose', icon: 'triangle-exclamation', route: 'management.issues' },
70
+ { key: 'fuel_records', label: 'Fuel Records', value: '...', caption: 'Loading fuel activity.', tone: 'amber', icon: 'gas-pump', route: 'management.fuel-reports' },
71
+ ];
72
+ }
73
+
74
+ @action openDocs(link) {
75
+ if (!link?.slug) {
76
+ return;
77
+ }
78
+
79
+ return this.docsPanel.open(link.slug, {
80
+ title: link.title ?? link.label,
81
+ source: 'fleet-ops-resources-hub',
82
+ });
83
+ }
84
+
85
+ @task *loadHub() {
86
+ this.hub = yield this.fetch.get('fleet-ops/hubs/resources');
87
+ }
88
+ }
@@ -0,0 +1,142 @@
1
+ import Controller from '@ember/controller';
2
+ import { inject as service } from '@ember/service';
3
+ import { action } from '@ember/object';
4
+
5
+ export default class SettingsIndexController extends Controller {
6
+ @service docsPanel;
7
+
8
+ groups = [
9
+ {
10
+ title: 'Driver And Map Experience',
11
+ description: 'Tune what dispatchers and drivers see while work is moving.',
12
+ links: [
13
+ { label: 'Navigator App', route: 'settings.navigator-app', icon: 'location-arrow', description: 'Driver app behavior and workflow defaults.' },
14
+ { label: 'Map', route: 'settings.map', icon: 'map', description: 'Map providers, defaults, and display behavior.' },
15
+ ],
16
+ },
17
+ {
18
+ title: 'Dispatch Automation',
19
+ description: 'Shape route planning, assignment, and scheduling behavior.',
20
+ links: [
21
+ { label: 'Routing', route: 'settings.routing', icon: 'route', description: 'Routing engines and routing defaults.' },
22
+ { label: 'Orchestrator', route: 'settings.orchestrator', icon: 'circle-nodes', description: 'Dispatch orchestration and assignment behavior.' },
23
+ { label: 'Scheduling', route: 'settings.scheduling', icon: 'calendar-days', description: 'Schedule planning and operational timing rules.' },
24
+ ],
25
+ },
26
+ {
27
+ title: 'Business And Data',
28
+ description: 'Keep commerce, metadata, and visual conventions aligned.',
29
+ links: [
30
+ { label: 'Payments', route: 'settings.payments', icon: 'cash-register', description: 'Payment setup for operational commerce workflows.' },
31
+ { label: 'Custom Fields', route: 'settings.custom-fields', icon: 'pen-to-square', description: 'Operational metadata fields for Fleet-Ops records.' },
32
+ { label: 'Avatars', route: 'settings.avatars', icon: 'icons', description: 'Visual assets for driver, vehicle, and map displays.' },
33
+ ],
34
+ },
35
+ {
36
+ title: 'Communication',
37
+ description: 'Control operational alerts and notification behavior.',
38
+ links: [{ label: 'Notifications', route: 'settings.notifications', icon: 'bell', description: 'Operational status alerts and notification rules.' }],
39
+ },
40
+ ];
41
+
42
+ actions = [
43
+ {
44
+ label: 'Start with driver experience',
45
+ description: 'Navigator App and Map settings should be reviewed before operators rely on live dispatch defaults.',
46
+ icon: 'location-arrow',
47
+ route: 'settings.navigator-app',
48
+ tone: 'info',
49
+ },
50
+ {
51
+ label: 'Review automation rules',
52
+ description: 'Routing, orchestration, and scheduling settings affect assignment and planning behavior.',
53
+ icon: 'route',
54
+ route: 'settings.routing',
55
+ tone: 'warning',
56
+ },
57
+ {
58
+ label: 'Keep forms focused',
59
+ description: 'Add custom fields after core workflows are stable so operational screens stay easy to scan.',
60
+ icon: 'pen-to-square',
61
+ route: 'settings.custom-fields',
62
+ tone: 'success',
63
+ },
64
+ ];
65
+
66
+ docs = [
67
+ {
68
+ label: 'Navigator App',
69
+ icon: 'location-arrow',
70
+ slug: 'fleet-ops/settings/navigator-app',
71
+ title: 'Navigator App settings',
72
+ description: 'Configure driver app behavior, onboarding, and live workflow defaults.',
73
+ },
74
+ {
75
+ label: 'Map',
76
+ icon: 'map',
77
+ slug: 'fleet-ops/settings/map',
78
+ title: 'Map settings',
79
+ description: 'Set map providers, display defaults, and operator map behavior.',
80
+ },
81
+ {
82
+ label: 'Payments',
83
+ icon: 'cash-register',
84
+ slug: 'fleet-ops/settings/payments',
85
+ title: 'Payments guide',
86
+ description: 'Connect payment providers and payment behavior for Fleet-Ops workflows.',
87
+ },
88
+ {
89
+ label: 'Notifications',
90
+ icon: 'bell',
91
+ slug: 'fleet-ops/settings/notifications',
92
+ title: 'Notification settings',
93
+ description: 'Tune operational alerts sent to dispatchers, customers, and drivers.',
94
+ },
95
+ {
96
+ label: 'Routing',
97
+ icon: 'route',
98
+ slug: 'fleet-ops/settings/routing',
99
+ title: 'Routing settings',
100
+ description: 'Configure routing engines, tracking defaults, and distance units.',
101
+ },
102
+ {
103
+ label: 'Orchestrator',
104
+ icon: 'circle-nodes',
105
+ slug: 'fleet-ops/settings/orchestrator',
106
+ title: 'Orchestrator settings',
107
+ description: 'Select dispatch automation engines and assignment behavior.',
108
+ },
109
+ {
110
+ label: 'Scheduling',
111
+ icon: 'calendar-days',
112
+ slug: 'fleet-ops/settings/scheduling',
113
+ title: 'Scheduling settings',
114
+ description: 'Manage schedule templates and timing rules for planned work.',
115
+ },
116
+ {
117
+ label: 'Custom Fields',
118
+ icon: 'pen-to-square',
119
+ slug: 'fleet-ops/settings/custom-fields',
120
+ title: 'Custom field settings',
121
+ description: 'Add structured operational metadata to Fleet-Ops records.',
122
+ },
123
+ {
124
+ label: 'Avatars',
125
+ icon: 'icons',
126
+ slug: 'fleet-ops/settings/avatars',
127
+ title: 'Avatar settings',
128
+ description: 'Manage visual markers used across maps and operational screens.',
129
+ },
130
+ ];
131
+
132
+ @action openDocs(link) {
133
+ if (!link?.slug) {
134
+ return;
135
+ }
136
+
137
+ return this.docsPanel.open(link.slug, {
138
+ title: link.title ?? link.label,
139
+ source: 'fleet-ops-settings-hub',
140
+ });
141
+ }
142
+ }
@@ -321,7 +321,28 @@ export default {
321
321
  }),
322
322
  ];
323
323
 
324
+ const defaultWidgets = widgets
325
+ .filter((widget) => (typeof widget.isDefault === 'function' ? widget.isDefault() : widget.default === true))
326
+ .map((widget) => {
327
+ const definition = typeof widget.toObject === 'function' ? widget.toObject() : widget;
328
+
329
+ return {
330
+ ...definition,
331
+ widgetId: definition.widgetId ?? definition.id,
332
+ };
333
+ });
334
+
324
335
  widgetService.registerWidgets('dashboard', widgets);
336
+
337
+ if (typeof widgetService.registerDashboard === 'function') {
338
+ widgetService.registerDashboard('fleet-ops');
339
+ }
340
+
341
+ widgetService.registerWidgets('fleet-ops', widgets);
342
+
343
+ if (typeof widgetService.registerDefaultWidgets === 'function') {
344
+ widgetService.registerDefaultWidgets('fleet-ops', defaultWidgets);
345
+ }
325
346
  },
326
347
 
327
348
  registerHomeComponents(registryService) {
@@ -0,0 +1,3 @@
1
+ import Route from '@ember/routing/route';
2
+
3
+ export default class AnalyticsIndexRoute extends Route {}
@@ -11,6 +11,9 @@ export default class ConnectivityDevicesIndexRoute extends Route {
11
11
  query: { refreshModel: true },
12
12
  name: { refreshModel: true },
13
13
  public_id: { refreshModel: true },
14
+ status: { refreshModel: true },
15
+ attachment_state: { refreshModel: true },
16
+ telematic: { refreshModel: true },
14
17
  created_at: { refreshModel: true },
15
18
  updated_at: { refreshModel: true },
16
19
  };
@@ -0,0 +1,3 @@
1
+ import Route from '@ember/routing/route';
2
+
3
+ export default class MaintenanceIndexRoute extends Route {}
@@ -11,6 +11,8 @@ export default class MaintenanceWorkOrdersIndexRoute extends Route {
11
11
  query: { refreshModel: true },
12
12
  name: { refreshModel: true },
13
13
  public_id: { refreshModel: true },
14
+ status: { refreshModel: true },
15
+ priority: { refreshModel: true },
14
16
  created_at: { refreshModel: true },
15
17
  updated_at: { refreshModel: true },
16
18
  };
@@ -0,0 +1,3 @@
1
+ import Route from '@ember/routing/route';
2
+
3
+ export default class SettingsIndexRoute extends Route {}