@fleetbase/fleetops-engine 0.2.2 → 0.2.3
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/leaflet-drift-marker.js +53 -0
- package/addon/components/live-map.hbs +4 -4
- package/addon/components/live-map.js +94 -27
- package/addon/controllers/management/drivers/index.js +1 -1
- package/addon/controllers/operations/orders/index/new.js +67 -20
- package/addon/controllers/operations/orders/index/view.js +15 -4
- package/addon/controllers/operations/orders/index.js +58 -7
- package/addon/controllers/operations/service-rates/index/edit.js +1 -1
- package/addon/controllers/operations/service-rates/index/new.js +10 -11
- package/addon/controllers/operations/service-rates/index.js +8 -0
- package/addon/initializers/load-leaflet-assets.js +6 -1
- package/addon/instance-initializers/register-leaflet-drift-marker.js +17 -0
- package/addon/routes/operations/orders/index/new.js +3 -1
- package/addon/routes/operations/orders/index/view.js +46 -0
- package/addon/routes/operations/service-rates/index/edit.js +7 -0
- package/addon/templates/operations/service-rates/index/new.hbs +2 -2
- package/app/components/leaflet-drift-marker.js +1 -0
- package/assets/images/boxes/large.png +0 -0
- package/assets/images/boxes/medium.png +0 -0
- package/assets/images/boxes/small.png +0 -0
- package/assets/images/boxes/x-large.png +0 -0
- package/index.js +16 -0
- package/package.json +4 -3
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import MarkerLayer from 'ember-leaflet/components/marker-layer';
|
|
2
|
+
|
|
3
|
+
export default class LeafletDriftMarkerComponent extends MarkerLayer {
|
|
4
|
+
leafletOptions = [
|
|
5
|
+
...this.leafletOptions,
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Required, duration im miliseconds marker will take to destination point.
|
|
9
|
+
*
|
|
10
|
+
* @argument duration
|
|
11
|
+
* @type {Number}
|
|
12
|
+
*/
|
|
13
|
+
'duration',
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Makes map view follow marker.
|
|
17
|
+
*
|
|
18
|
+
* @argument keepAtCenter
|
|
19
|
+
* @type {Boolean}
|
|
20
|
+
*/
|
|
21
|
+
'keepAtCenter',
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Rotation angle, in degrees, clockwise.
|
|
25
|
+
*
|
|
26
|
+
* @argument rotationAngle
|
|
27
|
+
* @type {Number}
|
|
28
|
+
*/
|
|
29
|
+
'rotationAngle',
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The rotation center, as a transform-origin CSS rule.
|
|
33
|
+
*
|
|
34
|
+
* @argument rotationOrigin
|
|
35
|
+
* @type {String}
|
|
36
|
+
*/
|
|
37
|
+
'rotationOrigin',
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The Public ID of the driver.
|
|
41
|
+
*
|
|
42
|
+
* @argument publicId
|
|
43
|
+
* @type {String}
|
|
44
|
+
*/
|
|
45
|
+
'publicId',
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
createLayer() {
|
|
49
|
+
const { DriftMarker } = window;
|
|
50
|
+
|
|
51
|
+
return new DriftMarker(...this.requiredOptions, this.options);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -4,18 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
{{#if this.isDriversVisible}}
|
|
6
6
|
{{#each this.drivers as |driver|}}
|
|
7
|
-
<layers.
|
|
7
|
+
<layers.drift-marker @rotationAngle={{driver.heading}} @duration={{300}} @icon={{icon iconUrl=driver.vehicle_avatar iconSize=(array 24 24)}} @location={{point-to-coordinates driver.location}} @publicId={{driver.public_id}} @onAdd={{fn this.onAction "onDriverAdded" driver}} as |marker|>
|
|
8
8
|
<marker.popup @maxWidth="500" @minWidth="225">
|
|
9
9
|
<DriverCard @driver={{driver}} @showCoordinates={{true}} @textClass="text-gray-900 dark:text-gray-100" />
|
|
10
10
|
</marker.popup>
|
|
11
|
-
<marker.tooltip @permanent={{
|
|
12
|
-
</layers.
|
|
11
|
+
<marker.tooltip @permanent={{false}} @sticky={{true}}>{{driver.name}}</marker.tooltip>
|
|
12
|
+
</layers.drift-marker>
|
|
13
13
|
{{/each}}
|
|
14
14
|
{{/if}}
|
|
15
15
|
|
|
16
16
|
{{#if this.isPlacesVisible}}
|
|
17
17
|
{{#each this.places as |place|}}
|
|
18
|
-
<layers.marker @icon={{icon iconUrl="/engines-dist/images/building-marker.png" iconSize=(array
|
|
18
|
+
<layers.marker @icon={{icon iconUrl="/engines-dist/images/building-marker.png" iconSize=(array 16 16)}} @location={{point-to-coordinates place.location}} @riseOnHover={{true}} @title={{place.address}} @alt={{place.address}} as |marker|>
|
|
19
19
|
<marker.popup>
|
|
20
20
|
<div>{{place.address}}</div>
|
|
21
21
|
<div>{{format-point place.location}}</div>
|
|
@@ -112,6 +112,7 @@ export default class LiveMapComponent extends Component {
|
|
|
112
112
|
this.isReady = true;
|
|
113
113
|
|
|
114
114
|
this.watchDrivers(this.drivers);
|
|
115
|
+
this.listenForOrders();
|
|
115
116
|
|
|
116
117
|
if (typeof this.args.onReady === 'function') {
|
|
117
118
|
this.args.onReady(this);
|
|
@@ -334,6 +335,11 @@ export default class LiveMapComponent extends Component {
|
|
|
334
335
|
@action onDriverAdded(driver, event) {
|
|
335
336
|
const { target } = event;
|
|
336
337
|
|
|
338
|
+
// set the marker instance to the driver model
|
|
339
|
+
set(driver, '_marker', target);
|
|
340
|
+
|
|
341
|
+
console.log('onDriverAdded()', ...arguments);
|
|
342
|
+
|
|
337
343
|
this.createContextMenuForDriver(driver, target);
|
|
338
344
|
}
|
|
339
345
|
|
|
@@ -721,6 +727,30 @@ export default class LiveMapComponent extends Component {
|
|
|
721
727
|
* Functions are used to fetch date or handle socket callbacks/initializations.
|
|
722
728
|
*
|
|
723
729
|
*/
|
|
730
|
+
@action async listenForOrders() {
|
|
731
|
+
// setup socket
|
|
732
|
+
const socket = this.socket.instance();
|
|
733
|
+
|
|
734
|
+
// listen on company channel
|
|
735
|
+
const channelId = `company.${this.currentUser.companyId}`;
|
|
736
|
+
const channel = socket.subscribe(channelId);
|
|
737
|
+
|
|
738
|
+
// track channel
|
|
739
|
+
this.channels.pushObject(channel);
|
|
740
|
+
|
|
741
|
+
// listen to channel for events
|
|
742
|
+
await channel.listener('subscribe').once();
|
|
743
|
+
|
|
744
|
+
// get incoming data and console out
|
|
745
|
+
(async () => {
|
|
746
|
+
for await (let output of channel) {
|
|
747
|
+
const { event, data } = output;
|
|
748
|
+
|
|
749
|
+
console.log(`[channel ${channelId}]`, output, event, data);
|
|
750
|
+
}
|
|
751
|
+
})();
|
|
752
|
+
}
|
|
753
|
+
|
|
724
754
|
@action watchDrivers(drivers = []) {
|
|
725
755
|
// setup socket
|
|
726
756
|
const socket = this.socket.instance();
|
|
@@ -734,29 +764,54 @@ export default class LiveMapComponent extends Component {
|
|
|
734
764
|
|
|
735
765
|
@action async listenForDriver(driver, socket) {
|
|
736
766
|
// listen on company channel
|
|
737
|
-
const channelId = `driver.${driver.
|
|
767
|
+
const channelId = `driver.${driver.id}`;
|
|
738
768
|
const channel = socket.subscribe(channelId);
|
|
739
769
|
|
|
740
|
-
// debug
|
|
741
|
-
// console.log(`socket subscribed to ${channelId}`);
|
|
742
|
-
|
|
743
770
|
// track channel
|
|
744
771
|
this.channels.pushObject(channel);
|
|
745
772
|
|
|
746
773
|
// listen to channel for events
|
|
747
774
|
await channel.listener('subscribe').once();
|
|
748
775
|
|
|
776
|
+
// Initialize an empty buffer to store incoming events
|
|
777
|
+
const eventBuffer = [];
|
|
778
|
+
|
|
779
|
+
// Time to wait in milliseconds before processing buffered events
|
|
780
|
+
const bufferTime = 1000 * 10;
|
|
781
|
+
|
|
782
|
+
// Function to process buffered events
|
|
783
|
+
const processBuffer = () => {
|
|
784
|
+
// Sort events by created_at
|
|
785
|
+
eventBuffer.sort((a, b) => new Date(a.created_at) - new Date(b.created_at));
|
|
786
|
+
|
|
787
|
+
// Process sorted events
|
|
788
|
+
for (const output of eventBuffer) {
|
|
789
|
+
const { event, data } = output;
|
|
790
|
+
console.log(`${event} - #${data.additionalData.index} (${output.created_at}) [ ${data.location.coordinates.join(' ')} ]`);
|
|
791
|
+
// update driver heading degree
|
|
792
|
+
driver._marker.setRotationAngle(data.heading);
|
|
793
|
+
// move driver's marker to new coordinates
|
|
794
|
+
driver._marker.slideTo(data.location.coordinates, { duration: 2000 });
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// Clear the buffer
|
|
798
|
+
eventBuffer.length = 0;
|
|
799
|
+
};
|
|
800
|
+
|
|
801
|
+
// Start a timer to process the buffer at intervals
|
|
802
|
+
setInterval(processBuffer, bufferTime);
|
|
803
|
+
|
|
749
804
|
// get incoming data and console out
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
driver.
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
805
|
+
(async () => {
|
|
806
|
+
for await (let output of channel) {
|
|
807
|
+
const { event, data } = output;
|
|
808
|
+
|
|
809
|
+
if (event === 'driver.location_changed' || event === 'driver.simulated_location_changed') {
|
|
810
|
+
// Add the incoming event to the buffer
|
|
811
|
+
eventBuffer.push(output);
|
|
812
|
+
}
|
|
758
813
|
}
|
|
759
|
-
}
|
|
814
|
+
})();
|
|
760
815
|
}
|
|
761
816
|
|
|
762
817
|
@action closeChannels() {
|
|
@@ -829,15 +884,20 @@ export default class LiveMapComponent extends Component {
|
|
|
829
884
|
this.isLoading = true;
|
|
830
885
|
|
|
831
886
|
return new Promise((resolve) => {
|
|
832
|
-
this.fetch
|
|
833
|
-
|
|
887
|
+
this.fetch
|
|
888
|
+
.get('fleet-ops/live/routes')
|
|
889
|
+
.then((routes) => {
|
|
890
|
+
this.isLoading = false;
|
|
834
891
|
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
892
|
+
if (typeof this.args.onRoutesLoaded === 'function') {
|
|
893
|
+
this.args.onRoutesLoaded(routes);
|
|
894
|
+
}
|
|
838
895
|
|
|
839
|
-
|
|
840
|
-
|
|
896
|
+
resolve(routes);
|
|
897
|
+
})
|
|
898
|
+
.catch(() => {
|
|
899
|
+
resolve([]);
|
|
900
|
+
});
|
|
841
901
|
});
|
|
842
902
|
}
|
|
843
903
|
|
|
@@ -854,6 +914,8 @@ export default class LiveMapComponent extends Component {
|
|
|
854
914
|
|
|
855
915
|
resolve(drivers);
|
|
856
916
|
});
|
|
917
|
+
}).catch(() => {
|
|
918
|
+
resolve([]);
|
|
857
919
|
});
|
|
858
920
|
}
|
|
859
921
|
|
|
@@ -864,15 +926,20 @@ export default class LiveMapComponent extends Component {
|
|
|
864
926
|
const center = this.leafletMap.getCenter();
|
|
865
927
|
|
|
866
928
|
return new Promise((resolve) => {
|
|
867
|
-
this.fetch
|
|
868
|
-
|
|
929
|
+
this.fetch
|
|
930
|
+
.get('fleet-ops/live/places', { within: center }, { normalizeToEmberData: true, normalizeModelType: 'place' })
|
|
931
|
+
.then((places) => {
|
|
932
|
+
this.isLoading = false;
|
|
869
933
|
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
934
|
+
if (typeof this.args.onPlacesLoaded === 'function') {
|
|
935
|
+
this.args.onPlacesLoaded(places);
|
|
936
|
+
}
|
|
873
937
|
|
|
874
|
-
|
|
875
|
-
|
|
938
|
+
resolve(places);
|
|
939
|
+
})
|
|
940
|
+
.catch(() => {
|
|
941
|
+
resolve([]);
|
|
942
|
+
});
|
|
876
943
|
});
|
|
877
944
|
}
|
|
878
945
|
|
|
@@ -580,7 +580,7 @@ export default class ManagementDriversIndexController extends Controller {
|
|
|
580
580
|
icon: 'headphones',
|
|
581
581
|
action: () => {
|
|
582
582
|
this.modalsManager.done().then(() => {
|
|
583
|
-
this.hostRouter.transitionTo('console.developers
|
|
583
|
+
this.hostRouter.transitionTo('console.developers.sockets.view', `driver.${driver.public_id}`);
|
|
584
584
|
});
|
|
585
585
|
},
|
|
586
586
|
},
|
|
@@ -5,7 +5,7 @@ import { action, computed, setProperties, set, get } from '@ember/object';
|
|
|
5
5
|
import { not, equal, alias } from '@ember/object/computed';
|
|
6
6
|
import { isArray } from '@ember/array';
|
|
7
7
|
import { dasherize } from '@ember/string';
|
|
8
|
-
import { later } from '@ember/runloop';
|
|
8
|
+
import { later, next } from '@ember/runloop';
|
|
9
9
|
import { OSRMv1, Control as RoutingControl } from '@fleetbase/leaflet-routing-machine';
|
|
10
10
|
import polyline from '@fleetbase/ember-core/utils/polyline';
|
|
11
11
|
import findClosestWaypoint from '@fleetbase/ember-core/utils/find-closest-waypoint';
|
|
@@ -494,7 +494,9 @@ export default class OperationsOrdersIndexNewController extends Controller {
|
|
|
494
494
|
}
|
|
495
495
|
|
|
496
496
|
@action newPlace() {
|
|
497
|
-
this.placesController
|
|
497
|
+
if (this.placesController) {
|
|
498
|
+
return this.placesController.createPlace();
|
|
499
|
+
}
|
|
498
500
|
}
|
|
499
501
|
|
|
500
502
|
@action async getQuotes(service) {
|
|
@@ -564,26 +566,39 @@ export default class OperationsOrdersIndexNewController extends Controller {
|
|
|
564
566
|
}
|
|
565
567
|
|
|
566
568
|
@action setupInterface() {
|
|
567
|
-
this.leafletMap
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
569
|
+
if (this.leafletMap && this.leafletMap.liveMap) {
|
|
570
|
+
this.leafletMap.liveMap.hideDrivers();
|
|
571
|
+
this.leafletMap.liveMap.hideRoutes();
|
|
572
|
+
|
|
573
|
+
// track all layers added from this view
|
|
574
|
+
this.leafletMap.on('layeradd', ({ layer }) => {
|
|
575
|
+
// disable dragging of layer
|
|
576
|
+
if (layer.dragging && typeof layer.dragging.disable === 'function') {
|
|
577
|
+
layer.dragging.disable();
|
|
578
|
+
}
|
|
574
579
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
580
|
+
next(this, function () {
|
|
581
|
+
if (isArray(this.leafletLayers) && !this.leafletLayers.includes(layer)) {
|
|
582
|
+
this.leafletLayers.pushObject(layer);
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
});
|
|
586
|
+
} else {
|
|
587
|
+
// setup interface when livemap is ready
|
|
588
|
+
this.universe.on('livemap.ready', () => {
|
|
589
|
+
this.setupInterface();
|
|
590
|
+
});
|
|
591
|
+
}
|
|
579
592
|
|
|
580
593
|
// switch to map mode
|
|
581
594
|
this.ordersController.setLayoutMode('map');
|
|
582
595
|
}
|
|
583
596
|
|
|
584
597
|
@action resetInterface() {
|
|
585
|
-
this.leafletMap
|
|
586
|
-
|
|
598
|
+
if (this.leafletMap && this.leafletMap.liveMap) {
|
|
599
|
+
this.leafletMap.liveMap.showDrivers();
|
|
600
|
+
this.leafletMap.liveMap.showRoutes();
|
|
601
|
+
}
|
|
587
602
|
}
|
|
588
603
|
|
|
589
604
|
@action getRoute() {
|
|
@@ -674,15 +689,47 @@ export default class OperationsOrdersIndexNewController extends Controller {
|
|
|
674
689
|
}
|
|
675
690
|
|
|
676
691
|
@action clearLayers() {
|
|
677
|
-
|
|
692
|
+
if (this.leafletMap) {
|
|
693
|
+
try {
|
|
694
|
+
this.leafletMap.eachLayer((layer) => {
|
|
695
|
+
if (isArray(this.leafletLayers) && this.leafletLayers.includes(layer)) {
|
|
696
|
+
this.leafletMap.removeLayer(layer);
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
} catch (error) {
|
|
700
|
+
// fallback method with tracked layers
|
|
701
|
+
if (isArray(this.leafletLayers)) {
|
|
702
|
+
this.leafletLayers.forEach((layer) => {
|
|
703
|
+
try {
|
|
704
|
+
this.leafletMap.removeLayer(layer);
|
|
705
|
+
} catch (error) {
|
|
706
|
+
// silent error just continue with order processing if any
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
678
713
|
|
|
679
|
-
|
|
714
|
+
@action clearAllLayers() {
|
|
715
|
+
if (this.leafletMap) {
|
|
680
716
|
try {
|
|
681
|
-
leafletMap.
|
|
717
|
+
this.leafletMap.eachLayer((layer) => {
|
|
718
|
+
this.leafletMap.removeLayer(layer);
|
|
719
|
+
});
|
|
682
720
|
} catch (error) {
|
|
683
|
-
//
|
|
721
|
+
// fallback method with tracked layers
|
|
722
|
+
if (isArray(this.leafletLayers)) {
|
|
723
|
+
this.leafletLayers.forEach((layer) => {
|
|
724
|
+
try {
|
|
725
|
+
this.leafletMap.removeLayer(layer);
|
|
726
|
+
} catch (error) {
|
|
727
|
+
// silent error just continue with order processing if any
|
|
728
|
+
}
|
|
729
|
+
});
|
|
730
|
+
}
|
|
684
731
|
}
|
|
685
|
-
}
|
|
732
|
+
}
|
|
686
733
|
}
|
|
687
734
|
|
|
688
735
|
@action createPlaceArrayFromPayload(payload, waypoints, isMultipleDropoffOrder = false) {
|
|
@@ -81,6 +81,13 @@ export default class OperationsOrdersIndexViewController extends Controller {
|
|
|
81
81
|
*/
|
|
82
82
|
@service hostRouter;
|
|
83
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Inject the `socket` service
|
|
86
|
+
*
|
|
87
|
+
* @var {Service}
|
|
88
|
+
*/
|
|
89
|
+
@service socket;
|
|
90
|
+
|
|
84
91
|
/**
|
|
85
92
|
* Inject the `universe` service
|
|
86
93
|
*
|
|
@@ -162,8 +169,10 @@ export default class OperationsOrdersIndexViewController extends Controller {
|
|
|
162
169
|
}
|
|
163
170
|
|
|
164
171
|
@action resetInterface() {
|
|
165
|
-
this.leafletMap
|
|
166
|
-
|
|
172
|
+
if (this.leafletMap && this.leafletMap.liveMap) {
|
|
173
|
+
this.leafletMap.liveMap.showDrivers();
|
|
174
|
+
this.leafletMap.liveMap.showRoutes();
|
|
175
|
+
}
|
|
167
176
|
}
|
|
168
177
|
|
|
169
178
|
@action removeRoutingControlPreview() {
|
|
@@ -209,8 +218,10 @@ export default class OperationsOrdersIndexViewController extends Controller {
|
|
|
209
218
|
return later(
|
|
210
219
|
this,
|
|
211
220
|
() => {
|
|
212
|
-
this.leafletMap
|
|
213
|
-
|
|
221
|
+
if (this.leafletMap && this.leafletMap.liveMap) {
|
|
222
|
+
this.leafletMap.liveMap.hideDrivers();
|
|
223
|
+
this.leafletMap.liveMap.hideRoutes();
|
|
224
|
+
}
|
|
214
225
|
|
|
215
226
|
// display order route on map
|
|
216
227
|
this.displayOrderRoute();
|
|
@@ -80,6 +80,13 @@ export default class OperationsOrdersIndexController extends Controller {
|
|
|
80
80
|
*/
|
|
81
81
|
@service universe;
|
|
82
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Inject the `socket` service
|
|
85
|
+
*
|
|
86
|
+
* @var {Service}
|
|
87
|
+
*/
|
|
88
|
+
@service socket;
|
|
89
|
+
|
|
83
90
|
/**
|
|
84
91
|
* Queryable parameters for this controller's model
|
|
85
92
|
*
|
|
@@ -537,6 +544,54 @@ export default class OperationsOrdersIndexController extends Controller {
|
|
|
537
544
|
},
|
|
538
545
|
];
|
|
539
546
|
|
|
547
|
+
/**
|
|
548
|
+
* Creates an instance of OperationsOrdersIndexController.
|
|
549
|
+
* @memberof OperationsOrdersIndexController
|
|
550
|
+
*/
|
|
551
|
+
constructor() {
|
|
552
|
+
super(...arguments);
|
|
553
|
+
this.listenForOrderEvents();
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Listen for incoming order events to refresh listing.
|
|
558
|
+
*
|
|
559
|
+
* @memberof OperationsOrdersIndexController
|
|
560
|
+
*/
|
|
561
|
+
@action async listenForOrderEvents() {
|
|
562
|
+
// wait for user to be loaded into service
|
|
563
|
+
this.currentUser.on('user.loaded', () => {
|
|
564
|
+
// Get socket instance
|
|
565
|
+
const socket = this.socket.instance();
|
|
566
|
+
|
|
567
|
+
// The channel ID to listen on
|
|
568
|
+
const channelId = `company.${this.currentUser.companyId}`;
|
|
569
|
+
|
|
570
|
+
// Listed on company channel
|
|
571
|
+
const channel = socket.subscribe(channelId);
|
|
572
|
+
|
|
573
|
+
// Events which should trigger refresh
|
|
574
|
+
const listening = ['order.ready', 'order.driver_assigned'];
|
|
575
|
+
|
|
576
|
+
// Listen for channel subscription
|
|
577
|
+
(async () => {
|
|
578
|
+
for await (let output of channel) {
|
|
579
|
+
const { event } = output;
|
|
580
|
+
|
|
581
|
+
// if an order event refresh orders
|
|
582
|
+
if (typeof event === 'string' && listening.includes(event)) {
|
|
583
|
+
this.hostRouter.refresh();
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
})();
|
|
587
|
+
|
|
588
|
+
// disconnect when transitioning
|
|
589
|
+
this.hostRouter.on('routeWillChange', () => {
|
|
590
|
+
channel.close();
|
|
591
|
+
});
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
|
|
540
595
|
/**
|
|
541
596
|
* The search task.
|
|
542
597
|
*
|
|
@@ -572,13 +627,9 @@ export default class OperationsOrdersIndexController extends Controller {
|
|
|
572
627
|
}
|
|
573
628
|
|
|
574
629
|
@action resetView() {
|
|
575
|
-
|
|
576
|
-
leafletMap
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
if (liveMap) {
|
|
580
|
-
liveMap.hideDrivers();
|
|
581
|
-
liveMap.hideRoutes();
|
|
630
|
+
if (this.leafletMap && this.leafletMap.liveMap) {
|
|
631
|
+
this.leafletMap.liveMap.hideDrivers();
|
|
632
|
+
this.leafletMap.liveMap.hideRoutes();
|
|
582
633
|
}
|
|
583
634
|
}
|
|
584
635
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import OperationsServiceRatesIndexNewController from './new';
|
|
2
2
|
import { tracked } from '@glimmer/tracking';
|
|
3
3
|
import { action } from '@ember/object';
|
|
4
|
-
// import { alias } from '@ember/object/computed';
|
|
5
4
|
|
|
6
5
|
export default class OperationsServiceRatesIndexEditController extends OperationsServiceRatesIndexNewController {
|
|
7
6
|
/**
|
|
@@ -44,6 +43,7 @@ export default class OperationsServiceRatesIndexEditController extends Operation
|
|
|
44
43
|
return this.transitionToRoute('operations.service-rates.index').then(() => {
|
|
45
44
|
this.notifications.success(`Service rate '${serviceRate.service_name}' updated`);
|
|
46
45
|
this.resetForm();
|
|
46
|
+
this.hostRouter.refresh();
|
|
47
47
|
});
|
|
48
48
|
})
|
|
49
49
|
.catch(this.notifications.serverError)
|
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
import Controller
|
|
1
|
+
import Controller from '@ember/controller';
|
|
2
2
|
import { inject as service } from '@ember/service';
|
|
3
3
|
import { tracked } from '@glimmer/tracking';
|
|
4
4
|
import { action, computed } from '@ember/object';
|
|
5
5
|
import { isBlank } from '@ember/utils';
|
|
6
6
|
|
|
7
7
|
export default class OperationsServiceRatesIndexNewController extends Controller {
|
|
8
|
-
/**
|
|
9
|
-
* Inject the `operations.service-rates.index` controller
|
|
10
|
-
*
|
|
11
|
-
* @var {Controller}
|
|
12
|
-
*/
|
|
13
|
-
@controller('operations.service-rates.index') index;
|
|
14
|
-
|
|
15
8
|
/**
|
|
16
9
|
* Inject the `currentUser` service
|
|
17
10
|
*
|
|
@@ -33,12 +26,19 @@ export default class OperationsServiceRatesIndexNewController extends Controller
|
|
|
33
26
|
*/
|
|
34
27
|
@service loader;
|
|
35
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Inject the `hostRouter` service
|
|
31
|
+
*
|
|
32
|
+
* @var {Service}
|
|
33
|
+
*/
|
|
34
|
+
@service hostRouter;
|
|
35
|
+
|
|
36
36
|
/**
|
|
37
37
|
* The service rate being created.
|
|
38
38
|
*
|
|
39
39
|
* @var {ServiceRateModel}
|
|
40
40
|
*/
|
|
41
|
-
@tracked serviceRate = this.store.createRecord('service-rate');
|
|
41
|
+
@tracked serviceRate = this.store.createRecord('service-rate', { per_meter_unit: 'm' });
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* Different service types available, based on order type.
|
|
@@ -300,11 +300,10 @@ export default class OperationsServiceRatesIndexNewController extends Controller
|
|
|
300
300
|
return serviceRate
|
|
301
301
|
.save()
|
|
302
302
|
.then((serviceRate) => {
|
|
303
|
-
this.index.table.addRow(serviceRate);
|
|
304
|
-
|
|
305
303
|
return this.transitionToRoute('operations.service-rates.index').then(() => {
|
|
306
304
|
this.notifications.success(`New Service Rate ${serviceRate.service_name} Created`);
|
|
307
305
|
this.resetForm();
|
|
306
|
+
this.hostRouter.refresh();
|
|
308
307
|
});
|
|
309
308
|
})
|
|
310
309
|
.catch((error) => {
|
|
@@ -56,6 +56,14 @@ export default class OperationsServiceRatesIndexController extends Controller {
|
|
|
56
56
|
*/
|
|
57
57
|
@service modalsManager;
|
|
58
58
|
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Inject the `hostRouter` service
|
|
62
|
+
*
|
|
63
|
+
* @var {Service}
|
|
64
|
+
*/
|
|
65
|
+
@service hostRouter;
|
|
66
|
+
|
|
59
67
|
/**
|
|
60
68
|
* Inject the `loader` service
|
|
61
69
|
*
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
export function initialize() {
|
|
2
2
|
const path = '/engines-dist/leaflet/';
|
|
3
|
-
const scripts = ['leaflet.contextmenu.js', 'leaflet.draw-src.js', 'leaflet.rotatedMarker.js'];
|
|
3
|
+
const scripts = ['leaflet.contextmenu.js', 'leaflet.draw-src.js', 'leaflet.rotatedMarker.js', 'leaflet-drift-marker.js'];
|
|
4
4
|
const stylesheets = ['leaflet.contextmenu.css', 'leaflet.draw.css'];
|
|
5
5
|
|
|
6
|
+
// Define exports on window
|
|
7
|
+
const exportsScript = document.createElement('script');
|
|
8
|
+
exportsScript.innerHTML = 'window.exports = window.exports || {};';
|
|
9
|
+
document.body.appendChild(exportsScript);
|
|
10
|
+
|
|
6
11
|
for (let i = 0; i < scripts.length; i++) {
|
|
7
12
|
const script = document.createElement('script');
|
|
8
13
|
script.src = path + scripts[i];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import LeafletDriftMarkerComponent from '../components/leaflet-drift-marker';
|
|
2
|
+
|
|
3
|
+
export function initialize(owner) {
|
|
4
|
+
let emberLeafletService = owner.lookup('service:ember-leaflet');
|
|
5
|
+
|
|
6
|
+
if (emberLeafletService) {
|
|
7
|
+
// we then invoke the `registerComponent` method
|
|
8
|
+
emberLeafletService.registerComponent('leaflet-drift-marker', {
|
|
9
|
+
as: 'drift-marker',
|
|
10
|
+
component: LeafletDriftMarkerComponent,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default {
|
|
16
|
+
initialize,
|
|
17
|
+
};
|
|
@@ -4,7 +4,9 @@ import { action } from '@ember/object';
|
|
|
4
4
|
|
|
5
5
|
export default class OperationsOrdersIndexViewRoute extends Route {
|
|
6
6
|
@service currentUser;
|
|
7
|
+
@service notifications;
|
|
7
8
|
@service store;
|
|
9
|
+
@service socket;
|
|
8
10
|
|
|
9
11
|
@action willTransition() {
|
|
10
12
|
if (this.controller) {
|
|
@@ -12,6 +14,11 @@ export default class OperationsOrdersIndexViewRoute extends Route {
|
|
|
12
14
|
}
|
|
13
15
|
}
|
|
14
16
|
|
|
17
|
+
@action error(error, transition) {
|
|
18
|
+
this.notifications.serverError(error);
|
|
19
|
+
return this.transitionTo('operations.orders.index');
|
|
20
|
+
}
|
|
21
|
+
|
|
15
22
|
model(order) {
|
|
16
23
|
const { public_id } = order;
|
|
17
24
|
|
|
@@ -22,6 +29,45 @@ export default class OperationsOrdersIndexViewRoute extends Route {
|
|
|
22
29
|
});
|
|
23
30
|
}
|
|
24
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Handle resolved model
|
|
34
|
+
*
|
|
35
|
+
* @param {OrderModel} model
|
|
36
|
+
* @memberof OperationsOrdersIndexViewRoute
|
|
37
|
+
*/
|
|
38
|
+
afterModel(model) {
|
|
39
|
+
this.listenForOrderEvents(model);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Listen to order channel for update events to refresh data
|
|
44
|
+
*
|
|
45
|
+
* @param {OrderModel} model
|
|
46
|
+
* @memberof OperationsOrdersIndexViewRoute
|
|
47
|
+
*/
|
|
48
|
+
async listenForOrderEvents(model) {
|
|
49
|
+
// Get socket instance
|
|
50
|
+
const socket = this.socket.instance();
|
|
51
|
+
|
|
52
|
+
// The channel ID to listen on
|
|
53
|
+
const channelId = `order.${model.public_id}`;
|
|
54
|
+
|
|
55
|
+
// Listed on company channel
|
|
56
|
+
const channel = socket.subscribe(channelId);
|
|
57
|
+
|
|
58
|
+
// Listen for channel subscription
|
|
59
|
+
(async () => {
|
|
60
|
+
for await (let output of channel) {
|
|
61
|
+
this.refresh();
|
|
62
|
+
}
|
|
63
|
+
})();
|
|
64
|
+
|
|
65
|
+
// disconnect when transitioning
|
|
66
|
+
this.on('willTransition', () => {
|
|
67
|
+
channel.close();
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
25
71
|
async setupController(controller, model) {
|
|
26
72
|
super.setupController(controller, model);
|
|
27
73
|
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import Route from '@ember/routing/route';
|
|
2
2
|
import { inject as service } from '@ember/service';
|
|
3
|
+
import { action } from '@ember/object';
|
|
3
4
|
|
|
4
5
|
export default class OperationsServiceRatesIndexEditRoute extends Route {
|
|
5
6
|
@service store;
|
|
6
7
|
@service currentUser;
|
|
8
|
+
@service notifications;
|
|
7
9
|
|
|
8
10
|
templateName = 'operations.service-rates.index.new';
|
|
9
11
|
|
|
12
|
+
@action error(error, transition) {
|
|
13
|
+
this.notifications.serverError(error);
|
|
14
|
+
return this.transitionTo('operations.service-rates.index');
|
|
15
|
+
}
|
|
16
|
+
|
|
10
17
|
model({ public_id }) {
|
|
11
18
|
return this.store.queryRecord('service-rate', {
|
|
12
19
|
public_id,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<Overlay @position="right" @noBackdrop={{true}} @fullHeight={{true}} @width="
|
|
1
|
+
<Overlay @position="right" @noBackdrop={{true}} @isResizable={{true}} @fullHeight={{true}} @width="800px">
|
|
2
2
|
<Overlay::Header @title={{if this.serviceRate.id this.serviceRate.service_name "New service rate"}} @status={{this.serviceRate.public_id}} @hideStatusDot={{true}} @createdAt={{this.serviceRate.createdAt}} @onPressCancel={{this.transitionBack}}>
|
|
3
3
|
{{#if this.serviceRate.id}}
|
|
4
4
|
<Button @icon="save" @type="primary" @text="Save Service Rate" @onClick={{this.updateServiceRate}} @isLoading={{this.isUpdatingServiceRate}} @isSubscriptionRequired={{true}} />
|
|
@@ -179,7 +179,7 @@
|
|
|
179
179
|
<div class="dark:text-gray-100">
|
|
180
180
|
<div class="grid grid-cols-7">
|
|
181
181
|
<div class="flex flex-col items-start justify-start pt-3">
|
|
182
|
-
<img src="/images/boxes/{{parcelFee.size}}.png" alt="parcel size {{parcelFee.size}}" width="44" class="w-11">
|
|
182
|
+
<img src="/engines-dist/images/boxes/{{parcelFee.size}}.png" alt="parcel size {{parcelFee.size}}" width="44" class="w-11">
|
|
183
183
|
<label class="mt-2">{{humanize parcelFee.size}}</label>
|
|
184
184
|
</div>
|
|
185
185
|
<div class="col-span-6 dimensions-box">
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/components/leaflet-drift-marker';
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/index.js
CHANGED
|
@@ -33,6 +33,21 @@ module.exports = buildEngine({
|
|
|
33
33
|
{ package: 'leaflet-contextmenu', include: undefined, exclude: [...alwaysExclude], path: ['dist'] },
|
|
34
34
|
{ package: 'leaflet-draw', include: undefined, exclude: [...alwaysExclude], path: ['dist'] },
|
|
35
35
|
{ package: 'leaflet-rotatedmarker', include: undefined, exclude: [...alwaysExclude], path: [] },
|
|
36
|
+
{
|
|
37
|
+
package: 'leaflet-drift-marker',
|
|
38
|
+
include: ['index.js', 'index.js.map'],
|
|
39
|
+
exclude: [...alwaysExclude],
|
|
40
|
+
path: ['lib'],
|
|
41
|
+
getDestinationPath: (relativePath) => {
|
|
42
|
+
if (relativePath === 'index.js') {
|
|
43
|
+
return 'leaflet-drift-marker.js';
|
|
44
|
+
}
|
|
45
|
+
if (relativePath === 'index.js.map') {
|
|
46
|
+
return 'leaflet-drift-marker.js.map';
|
|
47
|
+
}
|
|
48
|
+
return relativePath;
|
|
49
|
+
},
|
|
50
|
+
},
|
|
36
51
|
];
|
|
37
52
|
const trees = [];
|
|
38
53
|
|
|
@@ -45,6 +60,7 @@ module.exports = buildEngine({
|
|
|
45
60
|
destDir: 'leaflet',
|
|
46
61
|
include: leafletAdddon.include,
|
|
47
62
|
exclude: leafletAdddon.exclude,
|
|
63
|
+
getDestinationPath: leafletAdddon.getDestinationPath,
|
|
48
64
|
})
|
|
49
65
|
);
|
|
50
66
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fleetbase/fleetops-engine",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Fleetbase Fleet-Ops extension provides a fleet management and last mile delivery operations system module.",
|
|
5
5
|
"fleetbase": {
|
|
6
6
|
"route": "fleet-ops",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@fleetbase/ember-core": "^0.1.2",
|
|
43
43
|
"@fleetbase/ember-ui": "^0.1.8",
|
|
44
|
-
"@fleetbase/fleetops-data": "^0.0.
|
|
44
|
+
"@fleetbase/fleetops-data": "^0.0.7",
|
|
45
45
|
"@fleetbase/leaflet-routing-machine": "^3.2.16",
|
|
46
46
|
"@fortawesome/ember-fontawesome": "^0.4.1",
|
|
47
47
|
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
|
@@ -50,16 +50,17 @@
|
|
|
50
50
|
"@zestia/ember-dragula": "^12.0.0",
|
|
51
51
|
"broccoli-funnel": "^3.0.8",
|
|
52
52
|
"broccoli-merge-trees": "^4.2.0",
|
|
53
|
-
"ember-intl": "^6.0.0-beta.6",
|
|
54
53
|
"ember-cli-babel": "^7.26.11",
|
|
55
54
|
"ember-cli-es6-transform": "^1.0.0",
|
|
56
55
|
"ember-cli-htmlbars": "^6.1.0",
|
|
56
|
+
"ember-intl": "^6.0.0-beta.6",
|
|
57
57
|
"ember-leaflet": "^5.1.1",
|
|
58
58
|
"ember-radio-button": "^3.0.0-beta.1",
|
|
59
59
|
"ember-wormhole": "^0.6.0",
|
|
60
60
|
"leaflet": "^1.9.4",
|
|
61
61
|
"leaflet-contextmenu": "^1.4.0",
|
|
62
62
|
"leaflet-draw": "^1.0.4",
|
|
63
|
+
"leaflet-drift-marker": "^2.0.0",
|
|
63
64
|
"leaflet-rotatedmarker": "^0.2.0"
|
|
64
65
|
},
|
|
65
66
|
"devDependencies": {
|