@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.
@@ -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.rotated-marker @icon={{icon iconUrl=driver.vehicle_avatar iconSize=(array 32 32)}} @location={{point-to-coordinates driver.location}} @onAdd={{fn this.onAction "onDriverAdded" driver}} as |marker|>
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={{true}} @sticky={{true}}>{{driver.name}}</marker.tooltip>
12
- </layers.rotated-marker>
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 32 32)}} @location={{point-to-coordinates place.location}} @riseOnHover={{true}} @title={{place.address}} @alt={{place.address}} as |marker|>
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.uuid}`;
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
- for await (let output of channel) {
751
- const { event, data } = output;
752
-
753
- if (event === 'driver.location_changed') {
754
- driver.location = data.location;
755
- driver.altitude = data.altitude;
756
- driver.heading = data.heading;
757
- driver.speed = data.speed;
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.get('fleet-ops/live/routes').then((routes) => {
833
- this.isLoading = false;
887
+ this.fetch
888
+ .get('fleet-ops/live/routes')
889
+ .then((routes) => {
890
+ this.isLoading = false;
834
891
 
835
- if (typeof this.args.onRoutesLoaded === 'function') {
836
- this.args.onRoutesLoaded(routes);
837
- }
892
+ if (typeof this.args.onRoutesLoaded === 'function') {
893
+ this.args.onRoutesLoaded(routes);
894
+ }
838
895
 
839
- resolve(routes);
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.get('fleet-ops/live/places', { within: center }, { normalizeToEmberData: true, normalizeModelType: 'place' }).then((places) => {
868
- this.isLoading = false;
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
- if (typeof this.args.onPlacesLoaded === 'function') {
871
- this.args.onPlacesLoaded(places);
872
- }
934
+ if (typeof this.args.onPlacesLoaded === 'function') {
935
+ this.args.onPlacesLoaded(places);
936
+ }
873
937
 
874
- resolve(places);
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-console.sockets.watch', `driver.${driver.public_id}`);
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?.createPlace();
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?.liveMap?.hideDrivers();
568
- this.leafletMap?.liveMap?.hideRoutes();
569
-
570
- // track all layers added from this view
571
- this.leafletMap?.on('layeradd', ({ layer }) => {
572
- // disable dragging of layer
573
- layer.dragging?.disable();
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
- if (!this.leafletLayers?.includes(layer)) {
576
- this.leafletLayers.pushObject(layer);
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?.liveMap?.showDrivers();
586
- this.leafletMap?.liveMap?.showRoutes();
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
- const { leafletLayers, leafletMap } = this;
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
- leafletLayers?.forEach((layer) => {
714
+ @action clearAllLayers() {
715
+ if (this.leafletMap) {
680
716
  try {
681
- leafletMap.removeLayer(layer);
717
+ this.leafletMap.eachLayer((layer) => {
718
+ this.leafletMap.removeLayer(layer);
719
+ });
682
720
  } catch (error) {
683
- // silent error just continue with order processing if any
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?.liveMap?.showDrivers();
166
- this.leafletMap?.liveMap?.showRoutes();
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?.liveMap?.hideDrivers();
213
- this.leafletMap?.liveMap?.hideRoutes();
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
- const {
576
- leafletMap: { liveMap },
577
- } = this;
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, { inject as controller } from '@ember/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
+ };
@@ -7,7 +7,9 @@ export default class OperationsOrdersIndexNewRoute extends Route {
7
7
  @service loader;
8
8
 
9
9
  @action willTransition() {
10
- this.controller?.resetForm();
10
+ if (this.controller) {
11
+ this.controller.resetForm();
12
+ }
11
13
  }
12
14
 
13
15
  async setupController(controller) {
@@ -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="700">
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.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.6",
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": {