@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.
- package/addon/components/customer/create-order-form.hbs +1 -1
- package/addon/components/customer/order-form.hbs +34 -34
- package/addon/components/customer/orders.hbs +2 -2
- package/addon/components/display-place.hbs +1 -1
- package/addon/components/driver/pill.hbs +16 -17
- package/addon/components/driver/pill.js +5 -1
- package/addon/components/map/leaflet-live-map.js +35 -3
- package/addon/components/map/order-list-overlay/order.hbs +1 -0
- package/addon/components/map/order-list-overlay/order.js +42 -0
- package/addon/components/modals/bulk-assign-driver.hbs +2 -2
- package/addon/components/order/details/detail.hbs +6 -6
- package/addon/components/order/details/notes.js +1 -1
- package/addon/components/order/route-editor.hbs +3 -3
- package/addon/components/order-tracking-lookup.hbs +1 -1
- package/addon/components/service-rate/details.hbs +117 -75
- package/addon/components/service-rate/form.hbs +7 -4
- package/addon/components/service-rate/form.js +6 -0
- package/addon/components/vehicle/pill.hbs +32 -33
- package/addon/components/vehicle/pill.js +5 -1
- package/addon/controllers/operations/orders/index.js +1 -1
- package/addon/controllers/operations/scheduler/index.js +17 -2
- package/addon/controllers/operations/service-rates/index/edit.js +1 -7
- package/addon/controllers/operations/service-rates/index/new.js +0 -7
- package/addon/controllers/operations/service-rates/index.js +10 -2
- package/addon/routes/operations/orders/index/details.js +7 -0
- package/addon/routes/operations/scheduler/index.js +3 -3
- package/addon/services/driver-actions.js +20 -4
- package/addon/services/leaflet-routing-control.js +7 -1
- package/addon/services/order-list-overlay.js +0 -1
- package/addon/services/place-actions.js +20 -4
- package/addon/services/service-rate-actions.js +31 -0
- package/addon/services/vehicle-actions.js +20 -4
- package/addon/templates/operations/scheduler/index.hbs +2 -2
- package/addon/utils/create-full-calendar-event-from-order.js +6 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/index.js +0 -11
- package/package.json +4 -4
- package/server/migrations/2025_12_16_000001_add_subject_created_at_index_to_positions.php +40 -0
- package/server/migrations/2025_12_16_000003_add_performance_indexes_to_fleetops_core_tables.php +442 -0
- package/server/src/Console/Commands/DispatchAdhocOrders.php +7 -2
- package/server/src/Http/Controllers/Api/v1/DriverController.php +2 -0
- package/server/src/Http/Controllers/Api/v1/OrderController.php +30 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +184 -86
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +14 -1
- package/server/src/Http/Controllers/Internal/v1/PlaceController.php +5 -0
- package/server/src/Http/Filter/DriverFilter.php +10 -0
- package/server/src/Http/Filter/OrderFilter.php +8 -1
- package/server/src/Http/Resources/v1/Contact.php +2 -2
- package/server/src/Http/Resources/v1/Entity.php +1 -1
- package/server/src/Http/Resources/v1/Index/Customer.php +33 -0
- package/server/src/Http/Resources/v1/Index/Driver.php +45 -0
- package/server/src/Http/Resources/v1/Index/Facilitator.php +33 -0
- package/server/src/Http/Resources/v1/Index/Order.php +127 -0
- package/server/src/Http/Resources/v1/Index/Payload.php +57 -0
- package/server/src/Http/Resources/v1/Index/Place.php +45 -0
- package/server/src/Http/Resources/v1/Index/TrackingNumber.php +25 -0
- package/server/src/Http/Resources/v1/Index/Vehicle.php +50 -0
- package/server/src/Http/Resources/v1/Order.php +41 -14
- package/server/src/Http/Resources/v1/Place.php +1 -1
- package/server/src/Http/Resources/v1/Position.php +2 -1
- package/server/src/Http/Resources/v1/ServiceRate.php +4 -4
- package/server/src/Http/Resources/v1/ServiceRateFee.php +12 -12
- package/server/src/Http/Resources/v1/ServiceRateParcelFee.php +14 -7
- package/server/src/Http/Resources/v1/TrackingNumber.php +1 -1
- package/server/src/Http/Resources/v1/Waypoint.php +1 -1
- package/server/src/Listeners/HandleOrderDispatched.php +5 -0
- package/server/src/Models/Contact.php +2 -0
- package/server/src/Models/Device.php +2 -0
- package/server/src/Models/DeviceEvent.php +2 -0
- package/server/src/Models/Driver.php +2 -0
- package/server/src/Models/FuelReport.php +2 -0
- package/server/src/Models/Issue.php +2 -0
- package/server/src/Models/Order.php +12 -5
- package/server/src/Models/Place.php +2 -0
- package/server/src/Models/Position.php +2 -0
- package/server/src/Models/ServiceArea.php +2 -0
- package/server/src/Models/ServiceRate.php +5 -1
- package/server/src/Models/ServiceRateFee.php +3 -17
- package/server/src/Models/ServiceRateParcelFee.php +1 -12
- package/server/src/Models/Vehicle.php +2 -0
- package/server/src/Models/Vendor.php +2 -0
- package/server/src/Models/Zone.php +2 -0
- package/server/src/Observers/DriverObserver.php +23 -0
- package/server/src/Observers/OrderObserver.php +31 -0
- package/server/src/Observers/PlaceObserver.php +31 -0
- package/server/src/Observers/ServiceRateObserver.php +0 -18
- package/server/src/Observers/VehicleObserver.php +7 -0
- package/server/src/Support/LiveCacheService.php +165 -0
- package/server/src/Support/OSRM.php +49 -22
- package/server/src/Support/OrderTracker.php +100 -28
- 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
|
|
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 "
|
|
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 "
|
|
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 "
|
|
64
|
+
@text={{t "order.fields.add-waypoint"}}
|
|
65
65
|
@size="sm"
|
|
66
66
|
@onClick={{this.addWaypoint}}
|
|
67
|
-
@helpText={{t "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
240
|
-
@helpText={{t "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
318
|
+
@loadingMessage={{t "order.fields.loading-message"}}
|
|
319
319
|
/>
|
|
320
320
|
</div>
|
|
321
321
|
{{else}}
|
|
322
322
|
{{#if this.serviceQuotes}}
|
|
323
|
-
<InfoBlock @text={{t "
|
|
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 "
|
|
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 "
|
|
379
|
-
(t "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
+
/>
|
|
@@ -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
|
|
|
@@ -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 "
|
|
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
|
|
52
|
-
<div class="text-xs text-gray-400 dark:text-gray-500">{{n-a @resource.customer.phone "No
|
|
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
|
|
74
|
-
<div class="text-xs text-gray-400 dark:text-gray-500">{{n-a @resource.facilitator.phone "No
|
|
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('
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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>
|