@fleetbase/fleetops-engine 0.6.50 → 0.6.51

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 (159) hide show
  1. package/README.md +185 -67
  2. package/addon/components/contact/panel-header.hbs +11 -48
  3. package/addon/components/customer/details.hbs +1 -2
  4. package/addon/components/customer/form.hbs +63 -51
  5. package/addon/components/customer/form.js +27 -1
  6. package/addon/components/customer/orders.hbs +48 -0
  7. package/addon/components/customer/orders.js +67 -5
  8. package/addon/components/driver/form.hbs +1 -1
  9. package/addon/components/home/getting-started-guidance.hbs +113 -0
  10. package/addon/components/home/getting-started-guidance.js +107 -0
  11. package/addon/components/issue/details/comments.hbs +3 -0
  12. package/addon/components/issue/details/comments.js +3 -0
  13. package/addon/components/issue/details/correspondence.hbs +9 -0
  14. package/addon/components/issue/details/correspondence.js +83 -0
  15. package/addon/components/issue/details/documents.hbs +69 -0
  16. package/addon/components/issue/details/documents.js +85 -0
  17. package/addon/components/issue/details.hbs +220 -59
  18. package/addon/components/issue/details.js +245 -0
  19. package/addon/components/issue/form.hbs +19 -1
  20. package/addon/components/issue/panel-header.hbs +43 -0
  21. package/addon/components/issue/panel-header.js +22 -0
  22. package/addon/components/issue/timeline.hbs +40 -0
  23. package/addon/components/issue/timeline.js +45 -0
  24. package/addon/components/leaflet-tracking-marker.js +3 -0
  25. package/addon/components/map/drawer/position-listing.js +3 -0
  26. package/addon/components/map/leaflet-live-map.hbs +2 -2
  27. package/addon/components/map/leaflet-live-map.js +17 -0
  28. package/addon/components/map/order-list-overlay.hbs +4 -4
  29. package/addon/components/modals/convert-customer-to-vendor.hbs +30 -0
  30. package/addon/components/modals/convert-customer-to-vendor.js +55 -0
  31. package/addon/components/modals/issue-assign.hbs +20 -0
  32. package/addon/components/modals/issue-assign.js +49 -0
  33. package/addon/components/modals/issue-close.hbs +30 -0
  34. package/addon/components/modals/issue-close.js +73 -0
  35. package/addon/components/modals/issue-status.hbs +20 -0
  36. package/addon/components/modals/issue-status.js +43 -0
  37. package/addon/components/modals/reset-customer-credentials.js +12 -6
  38. package/addon/components/modals/update-order-activity.hbs +51 -17
  39. package/addon/components/modals/update-order-activity.js +59 -5
  40. package/addon/components/order/details/activity.js +2 -2
  41. package/addon/components/order/details/proof.hbs +40 -38
  42. package/addon/components/order/details/proof.js +31 -4
  43. package/addon/components/order/details.hbs +5 -5
  44. package/addon/components/order/details.js +19 -1
  45. package/addon/components/order/header.hbs +1 -1
  46. package/addon/components/route-list.hbs +0 -12
  47. package/addon/components/service-rate/details.hbs +1 -1
  48. package/addon/components/service-rate/form.hbs +1 -1
  49. package/addon/components/service-rate/form.js +4 -0
  50. package/addon/components/vendor/personnel-manager.hbs +166 -0
  51. package/addon/components/vendor/personnel-manager.js +143 -0
  52. package/addon/components/widget/live-fleet.hbs +1 -0
  53. package/addon/components/widget/live-fleet.js +2 -7
  54. package/addon/components/widget/revenue-trend.hbs +1 -1
  55. package/addon/components/widget/revenue-trend.js +75 -2
  56. package/addon/controllers/management/contacts/customers/details.js +31 -13
  57. package/addon/controllers/management/contacts/customers.js +16 -5
  58. package/addon/controllers/management/contacts/index/details.js +31 -13
  59. package/addon/controllers/management/contacts/index.js +7 -0
  60. package/addon/controllers/management/issues/index/details.js +52 -6
  61. package/addon/controllers/management/vendors/index/details.js +4 -0
  62. package/addon/controllers/operations/orders/index/details.js +11 -3
  63. package/addon/controllers/operations/orders/index.js +1 -1
  64. package/addon/extension.js +19 -12
  65. package/addon/helpers/format-duration.js +1 -38
  66. package/addon/initializers/load-leaflet-assets.js +5 -6
  67. package/addon/initializers/patch-ember-leaflet-tooltip-layer.js +51 -0
  68. package/addon/routes/management/issues/index/details.js +1 -1
  69. package/addon/routes.js +1 -0
  70. package/addon/services/contact-actions.js +231 -1
  71. package/addon/services/getting-started.js +44 -0
  72. package/addon/services/issue-actions.js +71 -0
  73. package/addon/services/leaflet-map-manager.js +2 -0
  74. package/addon/services/map-adapter/leaflet.js +19 -0
  75. package/addon/services/service-areas.js +3 -0
  76. package/addon/styles/fleetops-engine.css +1036 -0
  77. package/addon/templates/management/contacts/customers/details.hbs +1 -1
  78. package/addon/templates/management/contacts/index/details.hbs +1 -1
  79. package/addon/templates/management/contacts.hbs +2 -2
  80. package/addon/templates/management/issues/index/details/index.hbs +3 -1
  81. package/addon/templates/management/issues/index/details.hbs +3 -2
  82. package/addon/templates/management/vendors/index/details/personnel.hbs +1 -2
  83. package/addon/templates/operations/orders/index/details/index.hbs +2 -2
  84. package/addon/templates/operations/orders/index.hbs +1 -1
  85. package/addon/templates/operations/service-rates/index/edit.hbs +1 -0
  86. package/addon/templates/operations/service-rates/index/new.hbs +1 -0
  87. package/addon/templates/settings/payments/onboard.hbs +8 -8
  88. package/addon/utils/leaflet-draw-namespace-guard.js +22 -0
  89. package/addon/utils/leaflet-plugin-loader.js +219 -0
  90. package/addon/utils/setup-customer-portal.js +4 -64
  91. package/app/components/home/getting-started-guidance.js +1 -0
  92. package/app/components/issue/details/comments.js +1 -0
  93. package/app/components/issue/details/correspondence.js +1 -0
  94. package/app/components/issue/details/documents.js +1 -0
  95. package/app/components/issue/panel-header.js +1 -0
  96. package/app/components/modals/convert-customer-to-vendor.js +1 -0
  97. package/app/components/modals/issue-assign.js +1 -0
  98. package/app/components/modals/issue-close.js +1 -0
  99. package/app/components/modals/issue-status.js +1 -0
  100. package/app/components/vendor/personnel-manager.js +1 -0
  101. package/app/helpers/format-duration.js +1 -1
  102. package/app/services/getting-started.js +1 -0
  103. package/app/utils/leaflet-draw-namespace-guard.js +1 -0
  104. package/app/utils/leaflet-plugin-loader.js +1 -0
  105. package/composer.json +1 -1
  106. package/extension.json +1 -1
  107. package/package.json +5 -5
  108. package/server/migrations/2026_05_31_000001_add_portal_fields_to_vendor_personnels_table.php +30 -0
  109. package/server/migrations/2026_06_03_000000_ensure_tags_column_on_issues_table.php +31 -0
  110. package/server/migrations/2026_06_03_000001_add_order_uuid_to_issues_table.php +25 -0
  111. package/server/migrations/2026_06_04_000001_backfill_order_transaction_subjects.php +39 -0
  112. package/server/migrations/2026_06_04_000002_backfill_order_transaction_directions.php +24 -0
  113. package/server/resources/views/mail/customer-credentials.blade.php +16 -13
  114. package/server/src/Expansions/UserFilterExpansion.php +28 -0
  115. package/server/src/Http/Controllers/Api/v1/IssueController.php +2 -0
  116. package/server/src/Http/Controllers/Api/v1/OrderController.php +26 -25
  117. package/server/src/Http/Controllers/Api/v1/PlaceController.php +10 -58
  118. package/server/src/Http/Controllers/Internal/v1/ContactController.php +135 -0
  119. package/server/src/Http/Controllers/Internal/v1/CustomerController.php +116 -6
  120. package/server/src/Http/Controllers/Internal/v1/GettingStartedController.php +17 -0
  121. package/server/src/Http/Controllers/Internal/v1/IssueController.php +251 -0
  122. package/server/src/Http/Controllers/Internal/v1/OrderController.php +172 -97
  123. package/server/src/Http/Controllers/Internal/v1/PlaceController.php +10 -68
  124. package/server/src/Http/Controllers/Internal/v1/VendorController.php +89 -0
  125. package/server/src/Http/Requests/CreateIssueRequest.php +4 -0
  126. package/server/src/Http/Requests/UpdateIssueRequest.php +4 -0
  127. package/server/src/Http/Resources/v1/Index/Order.php +12 -9
  128. package/server/src/Http/Resources/v1/Issue.php +25 -0
  129. package/server/src/Http/Resources/v1/Order.php +20 -18
  130. package/server/src/Mail/CustomerCredentialsMail.php +5 -6
  131. package/server/src/Models/Contact.php +30 -51
  132. package/server/src/Models/Issue.php +47 -6
  133. package/server/src/Models/Order.php +33 -6
  134. package/server/src/Models/ServiceArea.php +1 -1
  135. package/server/src/Models/VendorPersonnel.php +10 -1
  136. package/server/src/Models/Zone.php +1 -1
  137. package/server/src/Observers/PurchaseRateObserver.php +20 -0
  138. package/server/src/Observers/ZoneObserver.php +56 -0
  139. package/server/src/Providers/FleetOpsServiceProvider.php +1 -0
  140. package/server/src/Support/Analytics/TopDrivers.php +9 -1
  141. package/server/src/Support/GettingStarted.php +144 -0
  142. package/server/src/Support/PlaceSearch.php +282 -0
  143. package/server/src/Support/ResolvesOrderServiceStops.php +22 -12
  144. package/server/src/routes.php +33 -1
  145. package/server/tests/AnalyticsRoutesTest.php +8 -0
  146. package/server/tests/CustomerContactIdentitySafetyTest.php +41 -0
  147. package/server/tests/DriverVehicleStatusDefaultTest.php +2 -2
  148. package/server/tests/GettingStartedStatusTest.php +21 -0
  149. package/server/tests/OrderActivityFlowRegressionTest.php +116 -0
  150. package/server/tests/ServiceAreaZoneApiShapeTest.php +44 -0
  151. package/translations/ar-ae.yml +9 -0
  152. package/translations/bg-bg.yaml +9 -0
  153. package/translations/en-us.yaml +11 -0
  154. package/translations/es-pa.yaml +9 -0
  155. package/translations/fr-fr.yaml +9 -0
  156. package/translations/mn-mn.yaml +9 -0
  157. package/translations/pt-br.yaml +9 -0
  158. package/translations/ru-ru.yaml +9 -0
  159. package/translations/vi-vn.yaml +9 -0
@@ -2,6 +2,7 @@ import ResourceActionService from '@fleetbase/ember-core/services/resource-actio
2
2
  import { inject as service } from '@ember/service';
3
3
  import { action } from '@ember/object';
4
4
  import { format } from 'date-fns';
5
+ import { issueStatuses } from '../utils/fleet-ops-options';
5
6
 
6
7
  export default class IssueActionsService extends ResourceActionService {
7
8
  @service driverActions;
@@ -92,6 +93,76 @@ export default class IssueActionsService extends ResourceActionService {
92
93
  },
93
94
  };
94
95
 
96
+ get closedStatuses() {
97
+ return ['closed', 'resolved', 'completed'];
98
+ }
99
+
100
+ openStatusModal(issue, options = {}) {
101
+ return this.modalsManager.show('modals/issue-status', {
102
+ issue,
103
+ statusOptions: issueStatuses,
104
+ onSaved: options.onSaved,
105
+ });
106
+ }
107
+
108
+ openAssignModal(issue, options = {}) {
109
+ return this.modalsManager.show('modals/issue-assign', {
110
+ issue,
111
+ onSaved: options.onSaved,
112
+ });
113
+ }
114
+
115
+ openCloseIssueModal(issue, options = {}) {
116
+ return this.modalsManager.show('modals/issue-close', {
117
+ issue,
118
+ onSaved: options.onSaved,
119
+ });
120
+ }
121
+
122
+ confirmReopenIssue(issue, options = {}) {
123
+ this.modalsManager.confirm({
124
+ title: `Re-open ${issue.public_id || 'issue'}?`,
125
+ body: 'This will move the issue back into an active re-opened state and clear the active resolution date.',
126
+ acceptButtonText: 'Re-open Issue',
127
+ acceptButtonIcon: 'rotate-left',
128
+ confirm: async (modal) => {
129
+ modal.startLoading();
130
+
131
+ try {
132
+ const meta = issue.meta ?? {};
133
+ const reopenHistory = Array.isArray(meta.reopen_history) ? meta.reopen_history : [];
134
+ const user = this.currentUser.user;
135
+
136
+ issue.status = 're_opened';
137
+ issue.resolved_at = null;
138
+ issue.meta = {
139
+ ...meta,
140
+ reopen_history: [
141
+ ...reopenHistory,
142
+ {
143
+ reopened_at: new Date().toISOString(),
144
+ reopened_by_uuid: user?.id,
145
+ reopened_by_name: user?.name,
146
+ },
147
+ ],
148
+ };
149
+
150
+ await issue.save();
151
+ this.notifications.success('Issue re-opened.');
152
+
153
+ if (typeof options.onSaved === 'function') {
154
+ await options.onSaved(issue);
155
+ }
156
+
157
+ modal.done();
158
+ } catch (error) {
159
+ this.notifications.serverError(error);
160
+ modal.stopLoading();
161
+ }
162
+ },
163
+ });
164
+ }
165
+
95
166
  @action async viewDriver(issue) {
96
167
  const driver = await issue.loadDriver();
97
168
  if (driver) {
@@ -7,6 +7,7 @@ import { renderCompleted, waitForInsertedAndSized } from '@fleetbase/ember-ui/ut
7
7
  import { Control as RoutingControl } from '@fleetbase/leaflet-routing-machine';
8
8
  import { getLayerById, findLayer, flyToLayer } from '../utils/leaflet';
9
9
  import isUuid from '@fleetbase/ember-core/utils/is-uuid';
10
+ import ensureLeafletPluginsReady from '../utils/leaflet-plugin-loader';
10
11
 
11
12
  export default class LeafletMapManagerService extends Service {
12
13
  @service leafletRoutingControl;
@@ -92,6 +93,7 @@ export default class LeafletMapManagerService extends Service {
92
93
  }
93
94
 
94
95
  async ensureInteractive({ timeoutMs = 8000 } = {}) {
96
+ await ensureLeafletPluginsReady({ timeoutMs });
95
97
  await this.whenMapLoaded({ timeoutMs });
96
98
 
97
99
  // always ask Leaflet for the live container
@@ -21,9 +21,16 @@ import { isNone } from '@ember/utils';
21
21
  import { next } from '@ember/runloop';
22
22
  import { createGeoJsonFromLayer } from '../../utils/leaflet-to-geojson';
23
23
  import { waypointIconHtml } from '../../utils/route-colors';
24
+ import ensureLeafletDrawEditNamespace from '../../utils/leaflet-draw-namespace-guard';
25
+ import ensureLeafletPluginsReady from '../../utils/leaflet-plugin-loader';
24
26
 
25
27
  const L = window.leaflet || window.L;
26
28
 
29
+ function ensureLeafletConstructorNamespace() {
30
+ ensureLeafletDrawEditNamespace(L);
31
+ ensureLeafletDrawEditNamespace();
32
+ }
33
+
27
34
  export default class LeafletAdapter extends MapAdapterInterface {
28
35
  // ─── Internal State ────────────────────────────────────────────────────────
29
36
 
@@ -65,6 +72,8 @@ export default class LeafletAdapter extends MapAdapterInterface {
65
72
  return this._map;
66
73
  }
67
74
 
75
+ ensureLeafletConstructorNamespace();
76
+
68
77
  const mapOptions = {
69
78
  center: [options.lat ?? 1.3521, options.lng ?? 103.8198],
70
79
  zoom: options.zoom ?? 12,
@@ -113,6 +122,11 @@ export default class LeafletAdapter extends MapAdapterInterface {
113
122
  this._map?.invalidateSize(false);
114
123
  }
115
124
 
125
+ async ensureInteractive({ timeoutMs = 8000, map = null } = {}) {
126
+ await ensureLeafletPluginsReady({ timeoutMs });
127
+ return map ?? this._map;
128
+ }
129
+
116
130
  // ─── Viewport ──────────────────────────────────────────────────────────────
117
131
 
118
132
  setCenter(lat, lng, zoom) {
@@ -183,6 +197,7 @@ export default class LeafletAdapter extends MapAdapterInterface {
183
197
 
184
198
  addMarker(id, lat, lng, options = {}) {
185
199
  if (!this._map) return null;
200
+ ensureLeafletConstructorNamespace();
186
201
 
187
202
  const iconOptions = options.iconUrl
188
203
  ? L.icon({
@@ -258,6 +273,7 @@ export default class LeafletAdapter extends MapAdapterInterface {
258
273
 
259
274
  addPolygon(id, coordinates, options = {}) {
260
275
  if (!this._map) return null;
276
+ ensureLeafletConstructorNamespace();
261
277
  const polygon = L.polygon(coordinates, {
262
278
  color: options.color ?? '#3388ff',
263
279
  fillColor: options.fillColor ?? options.color ?? '#3388ff',
@@ -276,6 +292,7 @@ export default class LeafletAdapter extends MapAdapterInterface {
276
292
 
277
293
  addPolyline(id, coordinates, options = {}) {
278
294
  if (!this._map) return null;
295
+ ensureLeafletConstructorNamespace();
279
296
  const polyline = L.polyline(coordinates, {
280
297
  color: options.color ?? '#3388ff',
281
298
  weight: options.weight ?? 3,
@@ -289,6 +306,7 @@ export default class LeafletAdapter extends MapAdapterInterface {
289
306
 
290
307
  addCircle(id, lat, lng, radiusMeters, options = {}) {
291
308
  if (!this._map) return null;
309
+ ensureLeafletConstructorNamespace();
292
310
  const circle = L.circle([lat, lng], {
293
311
  radius: radiusMeters,
294
312
  color: options.color ?? '#3388ff',
@@ -671,6 +689,7 @@ export default class LeafletAdapter extends MapAdapterInterface {
671
689
  return Promise.reject(new Error('LeafletAdapter editPolygon: layer has no coordinates'));
672
690
  }
673
691
 
692
+ ensureLeafletConstructorNamespace();
674
693
  const proxy = L.polygon(latlngs, {
675
694
  color: originalLayer.options?.color || '#3388ff',
676
695
  weight: 3,
@@ -10,6 +10,7 @@ import MultiPolygon from '@fleetbase/fleetops-data/utils/geojson/multi-polygon';
10
10
  import Polygon from '@fleetbase/fleetops-data/utils/geojson/polygon';
11
11
  import FeatureCollection from '@fleetbase/fleetops-data/utils/geojson/feature-collection';
12
12
  import wrapCoordinates from '../utils/leaflet-wrap-coordinates';
13
+ import ensureLeafletDrawEditNamespace from '../utils/leaflet-draw-namespace-guard';
13
14
 
14
15
  const L = window.L;
15
16
  export default class ServiceAreasService extends Service {
@@ -194,6 +195,8 @@ export default class ServiceAreasService extends Service {
194
195
 
195
196
  // If you need a Leaflet polygon, you might need to convert back to LatLng arrays.
196
197
  // Here we assume Leaflet can work with [lng, lat] arrays, or you convert them as needed.
198
+ ensureLeafletDrawEditNamespace(L);
199
+ ensureLeafletDrawEditNamespace();
197
200
  const polygon = L.polygon(latLngs, options);
198
201
  return polygon;
199
202
  }