@fleetbase/fleetops-engine 0.6.28 → 0.6.30

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 (92) hide show
  1. package/addon/components/customer/create-order-form.hbs +1 -1
  2. package/addon/components/customer/order-form.hbs +34 -34
  3. package/addon/components/customer/orders.hbs +2 -2
  4. package/addon/components/display-place.hbs +1 -1
  5. package/addon/components/driver/pill.hbs +16 -17
  6. package/addon/components/driver/pill.js +5 -1
  7. package/addon/components/map/leaflet-live-map.js +35 -3
  8. package/addon/components/map/order-list-overlay/order.hbs +1 -0
  9. package/addon/components/map/order-list-overlay/order.js +42 -0
  10. package/addon/components/modals/bulk-assign-driver.hbs +2 -2
  11. package/addon/components/order/details/detail.hbs +6 -6
  12. package/addon/components/order/details/notes.js +1 -1
  13. package/addon/components/order/route-editor.hbs +3 -3
  14. package/addon/components/order-tracking-lookup.hbs +1 -1
  15. package/addon/components/service-rate/details.hbs +117 -75
  16. package/addon/components/service-rate/form.hbs +7 -4
  17. package/addon/components/service-rate/form.js +6 -0
  18. package/addon/components/vehicle/pill.hbs +32 -33
  19. package/addon/components/vehicle/pill.js +5 -1
  20. package/addon/controllers/operations/orders/index.js +1 -1
  21. package/addon/controllers/operations/scheduler/index.js +17 -2
  22. package/addon/controllers/operations/service-rates/index/edit.js +1 -7
  23. package/addon/controllers/operations/service-rates/index/new.js +0 -7
  24. package/addon/controllers/operations/service-rates/index.js +10 -2
  25. package/addon/routes/operations/orders/index/details.js +7 -0
  26. package/addon/routes/operations/scheduler/index.js +3 -3
  27. package/addon/services/driver-actions.js +20 -4
  28. package/addon/services/leaflet-routing-control.js +7 -1
  29. package/addon/services/order-list-overlay.js +0 -1
  30. package/addon/services/place-actions.js +20 -4
  31. package/addon/services/service-rate-actions.js +31 -0
  32. package/addon/services/vehicle-actions.js +20 -4
  33. package/addon/templates/operations/scheduler/index.hbs +2 -2
  34. package/addon/utils/create-full-calendar-event-from-order.js +6 -0
  35. package/composer.json +1 -1
  36. package/extension.json +1 -1
  37. package/index.js +0 -11
  38. package/package.json +4 -4
  39. package/server/migrations/2025_12_16_000001_add_subject_created_at_index_to_positions.php +40 -0
  40. package/server/migrations/2025_12_16_000003_add_performance_indexes_to_fleetops_core_tables.php +442 -0
  41. package/server/src/Console/Commands/DispatchAdhocOrders.php +7 -2
  42. package/server/src/Http/Controllers/Api/v1/DriverController.php +2 -0
  43. package/server/src/Http/Controllers/Api/v1/OrderController.php +30 -0
  44. package/server/src/Http/Controllers/Internal/v1/LiveController.php +184 -86
  45. package/server/src/Http/Controllers/Internal/v1/OrderController.php +14 -1
  46. package/server/src/Http/Controllers/Internal/v1/PlaceController.php +5 -0
  47. package/server/src/Http/Filter/DriverFilter.php +10 -0
  48. package/server/src/Http/Filter/OrderFilter.php +8 -1
  49. package/server/src/Http/Resources/v1/Contact.php +2 -2
  50. package/server/src/Http/Resources/v1/Entity.php +1 -1
  51. package/server/src/Http/Resources/v1/Index/Customer.php +33 -0
  52. package/server/src/Http/Resources/v1/Index/Driver.php +45 -0
  53. package/server/src/Http/Resources/v1/Index/Facilitator.php +33 -0
  54. package/server/src/Http/Resources/v1/Index/Order.php +127 -0
  55. package/server/src/Http/Resources/v1/Index/Payload.php +57 -0
  56. package/server/src/Http/Resources/v1/Index/Place.php +45 -0
  57. package/server/src/Http/Resources/v1/Index/TrackingNumber.php +25 -0
  58. package/server/src/Http/Resources/v1/Index/Vehicle.php +50 -0
  59. package/server/src/Http/Resources/v1/Order.php +41 -14
  60. package/server/src/Http/Resources/v1/Place.php +1 -1
  61. package/server/src/Http/Resources/v1/Position.php +2 -1
  62. package/server/src/Http/Resources/v1/ServiceRate.php +4 -4
  63. package/server/src/Http/Resources/v1/ServiceRateFee.php +12 -12
  64. package/server/src/Http/Resources/v1/ServiceRateParcelFee.php +14 -7
  65. package/server/src/Http/Resources/v1/TrackingNumber.php +1 -1
  66. package/server/src/Http/Resources/v1/Waypoint.php +1 -1
  67. package/server/src/Listeners/HandleOrderDispatched.php +5 -0
  68. package/server/src/Models/Contact.php +2 -0
  69. package/server/src/Models/Device.php +2 -0
  70. package/server/src/Models/DeviceEvent.php +2 -0
  71. package/server/src/Models/Driver.php +2 -0
  72. package/server/src/Models/FuelReport.php +2 -0
  73. package/server/src/Models/Issue.php +2 -0
  74. package/server/src/Models/Order.php +12 -5
  75. package/server/src/Models/Place.php +2 -0
  76. package/server/src/Models/Position.php +2 -0
  77. package/server/src/Models/ServiceArea.php +2 -0
  78. package/server/src/Models/ServiceRate.php +5 -1
  79. package/server/src/Models/ServiceRateFee.php +3 -17
  80. package/server/src/Models/ServiceRateParcelFee.php +1 -12
  81. package/server/src/Models/Vehicle.php +2 -0
  82. package/server/src/Models/Vendor.php +2 -0
  83. package/server/src/Models/Zone.php +2 -0
  84. package/server/src/Observers/DriverObserver.php +23 -0
  85. package/server/src/Observers/OrderObserver.php +31 -0
  86. package/server/src/Observers/PlaceObserver.php +31 -0
  87. package/server/src/Observers/ServiceRateObserver.php +0 -18
  88. package/server/src/Observers/VehicleObserver.php +7 -0
  89. package/server/src/Support/LiveCacheService.php +165 -0
  90. package/server/src/Support/OSRM.php +49 -22
  91. package/server/src/Support/OrderTracker.php +100 -28
  92. package/translations/en-us.yaml +3 -1
@@ -75,7 +75,7 @@
75
75
  <ModelSelect
76
76
  @modelName="place"
77
77
  @selectedModel={{waypoint.place}}
78
- @placeholder={{concat (t "order.fields.select-waypoint-placeholder") " " (add index 1)}}
78
+ @placeholder={{concat (t "order.fields.select-waypoint") " " (add index 1)}}
79
79
  @triggerClass="form-select form-input truncate max-w-250px"
80
80
  @infiniteScroll={{false}}
81
81
  @customSearchEndpoint="places/search"
@@ -51,20 +51,20 @@
51
51
  {{/each}}
52
52
  {{/if}}
53
53
 
54
- <ContentPanel @title={{t "fleet-ops.operations.orders.index.new.route"}} @open={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
54
+ <ContentPanel @title={{t "order.fields.route"}} @open={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
55
55
  <div class="lg:col-span-2">
56
56
  <div class="flex flex-row justify-between mb-4">
57
57
  <div class="flex">
58
- <Toggle @isToggled={{this.isMultipleDropoffOrder}} @onToggle={{this.toggleMultiDropOrder}} @label={{t "fleet-ops.operations.orders.index.new.route-label"}} />
58
+ <Toggle @isToggled={{this.isMultipleDropoffOrder}} @onToggle={{this.toggleMultiDropOrder}} @label={{t "order.fields.route-label"}} />
59
59
  </div>
60
60
  <div class="flex flex-1 justify-end space-x-2">
61
61
  {{#if this.isMultipleDropoffOrder}}
62
62
  <Button
63
63
  @icon="map-marked-alt"
64
- @text={{t "fleet-ops.operations.orders.index.new.add-waypoint"}}
64
+ @text={{t "order.fields.add-waypoint"}}
65
65
  @size="sm"
66
66
  @onClick={{this.addWaypoint}}
67
- @helpText={{t "fleet-ops.operations.orders.index.new.add-waypoint-help-text"}}
67
+ @helpText={{t "order.fields.add-waypoint-help-text"}}
68
68
  @permission="fleet-ops create order"
69
69
  />
70
70
  {{/if}}
@@ -84,7 +84,7 @@
84
84
  <ModelSelect
85
85
  @modelName="place"
86
86
  @selectedModel={{waypoint.place}}
87
- @placeholder={{concat (t "fleet-ops.operations.orders.index.new.select-waypoint-placeholder") " " (add index 1)}}
87
+ @placeholder={{concat (t "order.fields.select-waypoint") " " (add index 1)}}
88
88
  @triggerClass="form-select form-input truncate max-w-250px"
89
89
  @infiniteScroll={{false}}
90
90
  @customSearchEndpoint="places/search"
@@ -99,7 +99,7 @@
99
99
  {{#if waypoint.place.hasInvalidCoordinates}}
100
100
  <div class="leading-5 text-sm text-red-400 mt-2">
101
101
  <FaIcon @icon="exclamation-triangle" class="mr-1" />
102
- {{t "fleet-ops.operations.orders.index.new.invalid-coordinates"}}
102
+ {{t "order.fields.invalid-coordinates"}}
103
103
  </div>
104
104
  {{/if}}
105
105
  </div>
@@ -116,7 +116,7 @@
116
116
  <div class="grid grid-cols-1 lg:grid-cols-2 gap-4 lg:gap-2 text-xs dark:text-gray-100">
117
117
  <InputGroup>
118
118
  <div class="flex items-center justify-between">
119
- <label>{{t "fleet-ops.operations.orders.index.new.pickup"}}</label>
119
+ <label>{{t "order.fields.pickup"}}</label>
120
120
  <div class="flex-row space-x-1 pr-0.5">
121
121
  {{#if this.payload.pickup}}
122
122
  <a href="javascript:;" {{on "click" (fn this.editPlace this.payload.pickup)}}>
@@ -131,7 +131,7 @@
131
131
  <ModelSelect
132
132
  @modelName="place"
133
133
  @selectedModel={{this.payload.pickup}}
134
- @placeholder={{t "fleet-ops.operations.orders.index.new.select-pickup"}}
134
+ @placeholder={{t "order.fields.select-pickup"}}
135
135
  @triggerClass="form-select form-input"
136
136
  @infiniteScroll={{false}}
137
137
  @customSearchEndpoint="places/search"
@@ -146,13 +146,13 @@
146
146
  {{#if this.payload.pickup.hasInvalidCoordinates}}
147
147
  <div class="leading-5 text-sm text-red-400 mt-2">
148
148
  <FaIcon @icon="exclamation-triangle" class="mr-1" />
149
- {{t "fleet-ops.operations.orders.index.new.invalid-coordinates"}}
149
+ {{t "order.fields.invalid-coordinates"}}
150
150
  </div>
151
151
  {{/if}}
152
152
  </InputGroup>
153
153
  <InputGroup>
154
154
  <div class="flex items-center justify-between">
155
- <label>{{t "fleet-ops.operations.orders.index.new.dropoff"}}</label>
155
+ <label>{{t "order.fields.dropoff"}}</label>
156
156
  <div class="flex-row space-x-2 pr-0.5">
157
157
  {{#if this.payload.dropoff}}
158
158
  <a href="javascript:;" {{on "click" (fn this.editPlace this.payload.dropoff)}} disaled={{cannot "fleet-ops update place"}}>
@@ -167,7 +167,7 @@
167
167
  <ModelSelect
168
168
  @modelName="place"
169
169
  @selectedModel={{this.payload.dropoff}}
170
- @placeholder={{t "fleet-ops.operations.orders.index.new.select-dropoff"}}
170
+ @placeholder={{t "order.fields.select-dropoff"}}
171
171
  @triggerClass="form-select form-input"
172
172
  @infiniteScroll={{false}}
173
173
  @customSearchEndpoint="places/search"
@@ -182,21 +182,21 @@
182
182
  {{#if this.payload.dropoff.hasInvalidCoordinates}}
183
183
  <div class="leading-5 text-sm text-red-400 mt-2">
184
184
  <FaIcon @icon="exclamation-triangle" class="mr-1" />
185
- {{t "fleet-ops.operations.orders.index.new.invalid-coordinates"}}
185
+ {{t "order.fields.invalid-coordinates"}}
186
186
  </div>
187
187
  {{/if}}
188
188
  </InputGroup>
189
189
  <InputGroup>
190
190
  <div class="flex items-center justify-between">
191
- <label>{{t "fleet-ops.operations.orders.index.new.return"}}</label>
191
+ <label>{{t "order.fields.return"}}</label>
192
192
  {{#if this.payload.return}}
193
- <a href="javascript:;" {{on "click" (fn this.editPlace this.payload.return)}}>{{t "fleet-ops.operations.orders.index.new.edit-address"}}</a>
193
+ <a href="javascript:;" {{on "click" (fn this.editPlace this.payload.return)}}>{{t "order.fields.edit-address"}}</a>
194
194
  {{/if}}
195
195
  </div>
196
196
  <ModelSelect
197
197
  @modelName="place"
198
198
  @selectedModel={{this.payload.return}}
199
- @placeholder={{t "fleet-ops.operations.orders.index.new.select-return"}}
199
+ @placeholder={{t "order.fields.select-return"}}
200
200
  @triggerClass="form-select form-input"
201
201
  @infiniteScroll={{false}}
202
202
  @customSearchEndpoint="places/search"
@@ -211,7 +211,7 @@
211
211
  {{#if this.payload.return.hasInvalidCoordinates}}
212
212
  <div class="leading-5 text-sm text-red-400 mt-2">
213
213
  <FaIcon @icon="exclamation-triangle" class="mr-1" />
214
- {{t "fleet-ops.operations.orders.index.new.invalid-coordinates"}}
214
+ {{t "order.fields.invalid-coordinates"}}
215
215
  </div>
216
216
  {{/if}}
217
217
  </InputGroup>
@@ -225,26 +225,26 @@
225
225
  <div class="lg:col-span-2">
226
226
  <InputGroup>
227
227
  <Toggle
228
- @label={{t "fleet-ops.operations.orders.index.new.require-proof"}}
228
+ @label={{t "order.fields.require-proof"}}
229
229
  @wrapperClass="justify-start-i"
230
230
  @isToggled={{this.order.pod_required}}
231
231
  @onToggle={{this.toggleProofOfDelivery}}
232
232
  @disabled={{cannot "fleet-ops create order"}}
233
- @helpText={{t "fleet-ops.operations.orders.index.new.require-proof-help-text"}}
233
+ @helpText={{t "order.fields.require-proof-help-text"}}
234
234
  />
235
235
  </InputGroup>
236
236
  {{#if this.order.pod_required}}
237
237
  <InputGroup @wrapperClass="mt-4">
238
238
  <InputLabel
239
- @labelText={{t "fleet-ops.operations.orders.index.new.proof-delivery"}}
240
- @helpText={{t "fleet-ops.operations.orders.index.new.proof-delivery-help-text"}}
239
+ @labelText={{t "order.fields.proof-delivery"}}
240
+ @helpText={{t "order.fields.proof-delivery-help-text"}}
241
241
  />
242
242
  <Select
243
243
  class="w-full"
244
244
  @options={{this.podOptions}}
245
245
  @value={{this.order.pod_method}}
246
246
  @onSelect={{fn (mut this.order.pod_method)}}
247
- @placeholder={{t "fleet-ops.operations.orders.index.new.proof-delivery-placeholder"}}
247
+ @placeholder={{t "order.fields.proof-delivery-placeholder"}}
248
248
  @disabled={{cannot "fleet-ops create order"}}
249
249
  />
250
250
  </InputGroup>
@@ -253,13 +253,13 @@
253
253
  </div>
254
254
  </ContentPanel>
255
255
 
256
- <ContentPanel @title={{t "fleet-ops.operations.orders.index.new.payload-entities"}} @open={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
256
+ <ContentPanel @title={{t "order.fields.payload-entities"}} @open={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
257
257
  <div class="flex items-center mb-4">
258
258
  <Button
259
259
  @wrapperClass="mr-2"
260
260
  @icon="square-plus"
261
261
  @iconPrefix="fas"
262
- @text={{t "fleet-ops.operations.orders.index.new.add-item-order"}}
262
+ @text={{t "order.fields.add-item-order"}}
263
263
  @size="sm"
264
264
  @onClick={{this.addEntity}}
265
265
  @permission="fleet-ops create order"
@@ -278,7 +278,7 @@
278
278
  <Input @value={{entity.name}} @type="text" class="w-full form-input form-input-sm" placeholder={{t "common.name"}} />
279
279
  </div>
280
280
  <div>
281
- <Input @value={{entity.sku}} @type="text" class="w-full form-input form-input-sm" placeholder={{t "fleet-ops.operations.orders.index.new.sku"}} />
281
+ <Input @value={{entity.sku}} @type="text" class="w-full form-input form-input-sm" placeholder={{t "order.fields.sku"}} />
282
282
  </div>
283
283
  {{#if this.waypoints.length}}
284
284
  <Select
@@ -286,12 +286,12 @@
286
286
  @options={{this.waypoints}}
287
287
  @optionValue="place.id"
288
288
  @optionLabel="place.address"
289
- @placeholder={{t "fleet-ops.operations.orders.index.new.select-destination"}}
289
+ @placeholder={{t "order.fields.select-destination"}}
290
290
  @onChange={{fn this.setEntityDestionation index}}
291
291
  class="w-full form-input-sm"
292
292
  />
293
293
  {{/if}}
294
- <Button @icon="pencil" @text={{t "fleet-ops.operations.orders.index.new.edit-item"}} @size="sm" @onClick={{fn this.editEntity entity}} />
294
+ <Button @icon="pencil" @text={{t "order.fields.edit-item"}} @size="sm" @onClick={{fn this.editEntity entity}} />
295
295
  </div>
296
296
  <div class="ml-3">
297
297
  <Button
@@ -315,12 +315,12 @@
315
315
  <Spinner
316
316
  class="text-sm dark:text-gray-100 flex flex-row items-center"
317
317
  @iconClass="mr-2"
318
- @loadingMessage={{t "fleet-ops.operations.orders.index.new.loading-message"}}
318
+ @loadingMessage={{t "order.fields.loading-message"}}
319
319
  />
320
320
  </div>
321
321
  {{else}}
322
322
  {{#if this.serviceQuotes}}
323
- <InfoBlock @text={{t "fleet-ops.operations.orders.index.new.info-text"}} class="mb-4" />
323
+ <InfoBlock @text={{t "order.fields.info-text"}} class="mb-4" />
324
324
  {{/if}}
325
325
  <div class="radio-group-condensed -space-y-px">
326
326
  {{#each this.serviceQuotes as |serviceQuote|}}
@@ -343,7 +343,7 @@
343
343
  <table class="table table-fixed flex-1">
344
344
  <thead>
345
345
  <tr>
346
- <th class="text-left">{{t "fleet-ops.operations.orders.index.new.breakdown"}}</th>
346
+ <th class="text-left">{{t "order.fields.breakdown"}}</th>
347
347
  <th></th>
348
348
  </tr>
349
349
  </thead>
@@ -375,8 +375,8 @@
375
375
  @type="warning"
376
376
  @text={{if
377
377
  this.payloadCoordinates.length
378
- (t "fleet-ops.operations.orders.index.new.no-service-info-text")
379
- (t "fleet-ops.operations.orders.index.new.input-order-info-text")
378
+ (t "order.fields.no-service-quotes")
379
+ (t "order.fields.service-quote-info")
380
380
  }}
381
381
  />
382
382
  {{/each}}
@@ -384,17 +384,17 @@
384
384
  {{/if}}
385
385
  </ContentPanel>
386
386
 
387
- <ContentPanel @title={{t "fleet-ops.operations.orders.index.new.notes-title"}} @open={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
387
+ <ContentPanel @title={{t "order.fields.notes-title"}} @open={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
388
388
  <Textarea
389
389
  @value={{this.order.notes}}
390
390
  class="form-input w-full"
391
- placeholder={{t "fleet-ops.operations.orders.index.new.notes-placeholder"}}
391
+ placeholder={{t "order.fields.notes-placeholder"}}
392
392
  disabled={{cannot "fleet-ops create order"}}
393
393
  rows={{4}}
394
394
  />
395
395
  </ContentPanel>
396
396
 
397
- <ContentPanel @title={{t "fleet-ops.operations.orders.index.view.documents-panel-title"}} @open={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
397
+ <ContentPanel @title={{t "order.fields.documents-panel-title"}} @open={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
398
398
  <div class="space-y-4">
399
399
  {{#if this.isUploading}}
400
400
  <div
@@ -155,7 +155,7 @@
155
155
  </div>
156
156
  {{else}}
157
157
  <div class="px-10 py-2 flex items-center justify-center">
158
- <p class="text-red-600 dark:text-red-100">{{t "fleet-ops.operations.orders.index.view.no-order-activity"}}</p>
158
+ <p class="text-red-600 dark:text-red-100">{{t "order.fields.no-order-activity"}}</p>
159
159
  </div>
160
160
  {{/each}}
161
161
  </div>
@@ -179,7 +179,7 @@
179
179
  @value={{group.waypoint.tracking}}
180
180
  class="rounded-md bg-yellow-300 text-yellow-900 px-2 py-0.5 text-xs flex-grow-0 truncate"
181
181
  >
182
- <span>{{t "fleet-ops.operations.orders.index.view.tracking"}}</span>
182
+ <span>{{t "order.fields.tracking"}}</span>
183
183
  <span>{{group.waypoint.tracking}}</span>
184
184
  </ClickToCopy>
185
185
  <Badge @status={{group.waypoint.status_code}} />
@@ -23,7 +23,7 @@
23
23
  <div class="next-dd-menu mt-1 mx-0" aria-labelledby="user-menu">
24
24
  <div class="px-1">
25
25
  <div class="text-sm flex flex-row items-center px-3 py-1 rounded-md my-1 text-gray-300">
26
- {{t "fleet-ops.operations.orders.index.view.waypoint-actions"}}
26
+ {{t "order.fields.waypoint-actions"}}
27
27
  </div>
28
28
  </div>
29
29
  <div class="next-dd-menu-seperator"></div>
@@ -1,17 +1,16 @@
1
- {{#let (or @driver @resource) as |resource|}}
2
- <Pill
3
- @resource={{resource}}
4
- @imageSrc={{avatar-url resource.photo_url}}
5
- @subtitle={{n-a resource.phone "No Phone"}}
6
- @fallbackImageType="driverImage"
7
- @showOnlineIndicator={{true}}
8
- @onClick={{@onClick}}
9
- @anchorClass={{@anchorClass}}
10
- @imageClass={{@imageClass}}
11
- @imageWrapperClass={{@imageWrapperClass}}
12
- @contentWrapperClass={{@contentWrapperClass}}
13
- @titleClass={{@titleClass}}
14
- @subtitleClass={{@subtitleClass}}
15
- ...attributes
16
- />
17
- {{/let}}
1
+ <Pill
2
+ @resource={{this.resource}}
3
+ @imageSrc={{avatar-url this.resource.photo_url}}
4
+ @titleFallback={{or @titleFallback "No driver"}}
5
+ @subtitle={{n-a this.resource.phone (if this.resource "No phone" "-")}}
6
+ @fallbackImageType="driverImage"
7
+ @showOnlineIndicator={{if this.resource true false}}
8
+ @onClick={{@onClick}}
9
+ @anchorClass={{@anchorClass}}
10
+ @imageClass={{@imageClass}}
11
+ @imageWrapperClass={{@imageWrapperClass}}
12
+ @contentWrapperClass={{@contentWrapperClass}}
13
+ @titleClass={{@titleClass}}
14
+ @subtitleClass={{@subtitleClass}}
15
+ ...attributes
16
+ />
@@ -1,3 +1,7 @@
1
1
  import Component from '@glimmer/component';
2
2
 
3
- export default class DriverPillComponent extends Component {}
3
+ export default class DriverPillComponent extends Component {
4
+ get resource() {
5
+ return this.args.driver ?? this.args.resource;
6
+ }
7
+ }
@@ -74,6 +74,10 @@ export default class MapLeafletLiveMapComponent extends Component {
74
74
  this.#createMapContextMenu(map);
75
75
  this.trigger('onLoad', ...arguments);
76
76
  this.load.perform();
77
+
78
+ // Listen for map move/zoom events to trigger viewport-based resource reload
79
+ map.on('moveend', () => this.reloadResourcesInViewport.perform());
80
+ map.on('zoomend', () => this.reloadResourcesInViewport.perform());
77
81
  }
78
82
 
79
83
  @action trigger(name, ...rest) {
@@ -160,11 +164,19 @@ export default class MapLeafletLiveMapComponent extends Component {
160
164
  /** load resources and wait for stuff here and trigger map ready **/
161
165
  @task *load() {
162
166
  try {
167
+ // Get initial map bounds for spatial filtering
168
+ const bounds = this.map ? this.map.getBounds() : null;
169
+ const params = bounds
170
+ ? {
171
+ bounds: [bounds.getSouth(), bounds.getWest(), bounds.getNorth(), bounds.getEast()],
172
+ }
173
+ : {};
174
+
163
175
  const data = yield all([
164
176
  this.loadResource.perform('routes'),
165
- this.loadResource.perform('vehicles'),
166
- this.loadResource.perform('drivers'),
167
- this.loadResource.perform('places'),
177
+ this.loadResource.perform('vehicles', { params }),
178
+ this.loadResource.perform('drivers', { params }),
179
+ this.loadResource.perform('places', { params }),
168
180
  this.loadResource.perform('service-areas'),
169
181
  ]);
170
182
 
@@ -176,6 +188,26 @@ export default class MapLeafletLiveMapComponent extends Component {
176
188
  }
177
189
  }
178
190
 
191
+ @task({ restartable: true }) *reloadResourcesInViewport() {
192
+ if (!this.map) {
193
+ return;
194
+ }
195
+
196
+ // Get current map bounds
197
+ const bounds = this.map.getBounds();
198
+ const params = {
199
+ bounds: [bounds.getSouth(), bounds.getWest(), bounds.getNorth(), bounds.getEast()],
200
+ };
201
+
202
+ // Reload spatially-filtered resources (drivers, vehicles, places)
203
+ // Orders, routes, and service-areas are not spatially filtered
204
+ try {
205
+ yield all([this.loadResource.perform('vehicles', { params }), this.loadResource.perform('drivers', { params }), this.loadResource.perform('places', { params })]);
206
+ } catch (err) {
207
+ debug('Failed to reload resources in viewport: ' + err.message);
208
+ }
209
+ }
210
+
179
211
  @task *loadResource(path, options = {}) {
180
212
  if (this.abilities.cannot(`fleet-ops list ${path}`)) return [];
181
213
 
@@ -5,6 +5,7 @@
5
5
  {{on "dblclick" (fn this.onDoubleClick @order)}}
6
6
  {{on "mouseenter" (fn this.onMouseEnter @order)}}
7
7
  {{on "mouseleave" (fn this.onMouseLeave @order)}}
8
+ {{did-insert this.setupIntersectionObserver}}
8
9
  ...attributes
9
10
  >
10
11
  <div class="order-listing-row">
@@ -1,7 +1,10 @@
1
1
  import Component from '@glimmer/component';
2
2
  import { action } from '@ember/object';
3
+ import { tracked } from '@glimmer/tracking';
3
4
 
4
5
  export default class MapOrderListOverlayOrderComponent extends Component {
6
+ @tracked trackerDataLoaded = false;
7
+ observer = null;
5
8
  @action onClick(order, event) {
6
9
  //Don't run callback if action button is clicked
7
10
  if (event.target.closest('span.order-listing-action-button')) {
@@ -32,4 +35,43 @@ export default class MapOrderListOverlayOrderComponent extends Component {
32
35
  this.args.onMouseLeave(...arguments);
33
36
  }
34
37
  }
38
+
39
+ @action setupIntersectionObserver(element) {
40
+ // Create IntersectionObserver to detect when order becomes visible
41
+ this.observer = new IntersectionObserver(
42
+ (entries) => {
43
+ entries.forEach((entry) => {
44
+ if (entry.isIntersecting && !this.trackerDataLoaded) {
45
+ this.loadTrackerData();
46
+ }
47
+ });
48
+ },
49
+ {
50
+ root: null, // viewport
51
+ rootMargin: '50px', // start loading slightly before visible
52
+ threshold: 0.1, // trigger when 10% visible
53
+ }
54
+ );
55
+
56
+ this.observer.observe(element);
57
+ }
58
+
59
+ loadTrackerData() {
60
+ const { order } = this.args;
61
+
62
+ if (order && typeof order.loadTrackerData === 'function' && !this.trackerDataLoaded) {
63
+ this.trackerDataLoaded = true;
64
+ order.loadTrackerData();
65
+ }
66
+ }
67
+
68
+ willDestroy() {
69
+ super.willDestroy(...arguments);
70
+
71
+ // Clean up observer
72
+ if (this.observer) {
73
+ this.observer.disconnect();
74
+ this.observer = null;
75
+ }
76
+ }
35
77
  }
@@ -1,11 +1,11 @@
1
1
  <Modals::BulkActionModel @options={{@options}} @onConfirm={{@onConfirm}} @onDecline={{@onDecline}} as |options|>
2
2
  <div>
3
- <InputGroup @name={{t "common.select-driver"}} @helpText={{t "fleet-ops.operations.orders.index.bulk-assign-driver-helptext"}}>
3
+ <InputGroup @name={{t "common.select-field" field=(t "resource.driver")}} @helpText={{t "fleet-ops.operations.orders.index.bulk-assign-driver-helptext"}}>
4
4
  <ModelSelect
5
5
  @modelName="driver"
6
6
  @selectedModel={{options.driverAssigned}}
7
7
  @query={{options.driversQuery}}
8
- @placeholder={{t "fleet-ops.operations.orders.index.new.assign-driver-placeholder"}}
8
+ @placeholder={{t "order.fields.assign-driver-placeholder"}}
9
9
  @triggerClass="form-select form-input"
10
10
  @infiniteScroll={{false}}
11
11
  @renderInPlace={{true}}
@@ -16,7 +16,7 @@
16
16
  <span>{{t "order.fields.driver-assigned"}}</span>
17
17
  </div>
18
18
  <div class="field-value">
19
- <Driver::Pill @driver={{@resource.driver_assigned}} @onClick={{this.focusOrderAssignedDriver}} />
19
+ <Driver::Pill @driver={{@resource.driver_assigned}} @onClick={{this.focusOrderAssignedDriver}} @titleFallback="No driver assigned" @titleClass={{unless @resource.driver_assigned "text-gray-300"}} />
20
20
  </div>
21
21
  </div>
22
22
  <div class="field-info-container">
@@ -24,7 +24,7 @@
24
24
  <span>{{t "order.fields.vehicle-assigned"}}</span>
25
25
  </div>
26
26
  <div class="field-value">
27
- <Vehicle::Pill @vehicle={{@resource.vehicle_assigned}} />
27
+ <Vehicle::Pill @vehicle={{@resource.vehicle_assigned}} @titleFallback="No vehicle assigned" @titleClass={{unless @resource.vehicle_assigned "text-gray-300"}} />
28
28
  </div>
29
29
  </div>
30
30
  <div class="field-info-container">
@@ -48,8 +48,8 @@
48
48
  />
49
49
  </div>
50
50
  <div>
51
- <div class="text-sm">{{n-a @resource.customer.name "No Customer"}}</div>
52
- <div class="text-xs text-gray-400 dark:text-gray-500">{{n-a @resource.customer.phone "No Phone"}}</div>
51
+ <div class="text-sm {{unless @resource.customer 'text-gray-300'}}">{{n-a @resource.customer.name "No customer"}}</div>
52
+ <div class="text-xs text-gray-400 dark:text-gray-500">{{n-a @resource.customer.phone "No phone"}}</div>
53
53
  </div>
54
54
  </div>
55
55
  </div>
@@ -70,8 +70,8 @@
70
70
  />
71
71
  </div>
72
72
  <div>
73
- <div class="text-sm">{{n-a @resource.facilitator.name "No Facilitator"}}</div>
74
- <div class="text-xs text-gray-400 dark:text-gray-500">{{n-a @resource.facilitator.phone "No Phone"}}</div>
73
+ <div class="text-sm {{unless @resource.facilitator 'text-gray-300'}}">{{n-a @resource.facilitator.name "No facilitator"}}</div>
74
+ <div class="text-xs text-gray-400 dark:text-gray-500">{{n-a @resource.facilitator.phone "No phone"}}</div>
75
75
  </div>
76
76
  </div>
77
77
  </div>
@@ -29,7 +29,7 @@ export default class OrderDetailsNotesComponent extends Component {
29
29
  @task *save() {
30
30
  try {
31
31
  yield this.args.resource.persistProperty('notes', this.args.resource.notes);
32
- this.notifications.success(this.intl.t('fleet-ops.operations.orders.index.view.order-notes-updated'));
32
+ this.notifications.success(this.intl.t('order.fields.order-notes-updated'));
33
33
  this.isEditing = false;
34
34
  } catch (error) {
35
35
  this.notifications.serverError(error);
@@ -11,11 +11,11 @@
11
11
  <Button
12
12
  @type="magic"
13
13
  @icon="magic"
14
- @text={{t "fleet-ops.operations.orders.index.new.optimize-route"}}
14
+ @text={{t "order.fields.optimize-route"}}
15
15
  @size="sm"
16
16
  @onClick={{perform this.optimizeRoute}}
17
17
  @permission="fleet-ops optimize order"
18
- @helpText={{t "fleet-ops.operations.orders.index.new.optimize-route-help-text"}}
18
+ @helpText={{t "order.fields.optimize-route-help-text"}}
19
19
  @disabled={{lt @resource.payload.waypoints.length 3}}
20
20
  @isLoading={{this.optimizeRoute.isRunning}}
21
21
  />
@@ -46,7 +46,7 @@
46
46
  <ModelSelect
47
47
  @modelName="place"
48
48
  @selectedModel={{waypoint}}
49
- @placeholder={{concat (t "fleet-ops.operations.orders.index.new.select-waypoint-placeholder") " " (add index 1)}}
49
+ @placeholder={{concat (t "order.fields.select-waypoint") " " (add index 1)}}
50
50
  @triggerClass="form-select form-input truncate max-w-300px"
51
51
  @infiniteScroll={{false}}
52
52
  @customSearchEndpoint="places/search"
@@ -144,7 +144,7 @@
144
144
  </div>
145
145
  {{else}}
146
146
  <div class="px-10 py-2 flex items-center justify-center">
147
- <p class="text-red-600 dark:text-red-100">{{t "fleet-ops.operations.orders.index.view.no-order-activity"}}</p>
147
+ <p class="text-red-600 dark:text-red-100">{{t "order.fields.no-order-activity"}}</p>
148
148
  </div>
149
149
  {{/each}}
150
150
  </div>