@fleetbase/fleetops-engine
Version:
Fleet & Transport Management Extension for Fleetbase
256 lines (218 loc) • 9.44 kB
JavaScript
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { debug } from '@ember/debug';
import { task } from 'ember-concurrency';
import { applyOptimizedIntermediateWaypoints, buildRouteOptimizationInput, canOptimizeIntermediateWaypoints } from '../../../utils/order-route-editing';
import { buildRoutePointsFromPayload } from '../../../utils/route-visualization';
export default class OrderDetailsRouteComponent extends Component {
orderActions;
modalsManager;
fetch;
notifications;
routeEngine;
routeOptimization;
hostRouter;
intl;
constructor() {
super(...arguments);
this.loadTrackerData.perform();
}
get actionButtons() {
return [
this.showOptimizationSelect
? {
component: 'route-optimization-engine-select-button',
onClick: (service) => this.optimizeRouteWithService.perform(service),
isLoading: this.optimizeRouteWithService.isRunning,
disabled: !this.canOptimizeRoute,
}
: {
type: 'magic',
icon: 'magic',
text: this.intl.t('order.fields.optimize-route'),
onClick: () => this.optimizeRoute.perform(),
permission: 'fleet-ops optimize order',
helpText: this.intl.t('order.fields.optimize-route-help-text'),
disabled: !this.canOptimizeRoute,
isLoading: this.optimizeRoute.isRunning,
},
{
icon: 'ellipsis-h',
prefix: 'fas',
triggerClass: 'fleetops-btn-xxs',
items: [
{
text: 'Edit route',
icon: 'pencil',
disabled: !this.args.resource.hasActiveStatus,
onClick: () => {
this.orderActions.editRoute(this.args.resource);
},
},
this.isMultipleWaypointOrder
? {
text: 'Change destination',
icon: 'location-dot',
disabled: !this.args.resource.hasActiveStatus,
onClick: () => {
this.changeDestination();
},
}
: null,
].filter(Boolean),
},
].filter(Boolean);
}
get canOptimizeRoute() {
return canOptimizeIntermediateWaypoints(this.args.resource?.payload);
}
get isMultipleWaypointOrder() {
const waypoints = this.args.resource?.payload?.waypoints;
const waypointCount = typeof waypoints?.toArray === 'function' ? waypoints.toArray().length : Array.isArray(waypoints) ? waypoints.length : (waypoints?.length ?? 0);
return Boolean(this.args.resource?.hasIntermediateWaypoints || waypointCount > 0);
}
get showOptimizationSelect() {
return this.routeOptimization.availableEngines.length > 1;
}
get trackerData() {
return this.args.resource?.tracker_data;
}
get showRouteEtaData() {
if (this.trackerData?.lifecycle?.show_live_eta !== undefined) {
return Boolean(this.trackerData.lifecycle.show_live_eta);
}
const status = String(this.args.resource?.status ?? 'created').toLowerCase();
const hasStarted = Boolean(this.args.resource?.started ?? this.args.resource?.started_at ?? status === 'started');
return hasStarted && !['completed', 'canceled'].includes(status);
}
get hasTrackingRouteSummary() {
return Boolean(this.trackerData?.route || this.trackerData?.eta);
}
get hasTrackingDistance() {
return this.trackerData?.route?.distance_m !== null && this.trackerData?.route?.distance_m !== undefined;
}
get hasCompletionEta() {
return this.showRouteEtaData && Boolean(this.trackerData?.eta?.completion_at);
}
get routeStopsCount() {
return buildRoutePointsFromPayload(this.args.resource?.payload).length;
}
get hasTrackingDuration() {
return this.showRouteEtaData && this.trackerData?.route?.duration_in_traffic_s !== null && this.trackerData?.route?.duration_in_traffic_s !== undefined;
}
get trackingDurationSeconds() {
if (!this.showRouteEtaData) {
return null;
}
return this.trackerData?.route?.duration_in_traffic_s ?? this.trackerData?.route?.duration_s;
}
get hasRouteSummaryLine() {
return this.hasTrackingRouteSummary || this.routeStopsCount > 0;
}
*loadTrackerData() {
if (!this.args.resource || this.args.resource.tracker_data || typeof this.args.resource.loadTrackerData !== 'function') {
return;
}
try {
yield this.args.resource.loadTrackerData();
} catch (err) {
debug('Failed to load order tracker data for route: ' + err.message);
}
}
*optimizeRouteWithService(service) {
const order = this.args.resource;
const optimizationInput = buildRouteOptimizationInput(order);
if (!service) {
this.notifications.error('No route optimization engine was selected.');
return;
}
try {
const result = yield this.routeOptimization.optimize(service, {
context: 'order_details_route',
...optimizationInput,
});
yield this.persistOptimizedRoute(result);
} catch (err) {
this.notifications.error(err.message ?? this.intl.t('fleet-ops.operations.orders.index.new.route-error'));
}
}
*optimizeRoute() {
const order = this.args.resource;
const optimizationInput = buildRouteOptimizationInput(order);
const service = this.routeEngine.getOptimizationEngine('osrm');
try {
const result = yield this.routeOptimization.optimize(service, {
context: 'order_details_route',
...optimizationInput,
});
yield this.persistOptimizedRoute(result);
} catch (_err) {
this.notifications.error(this.intl.t('fleet-ops.operations.orders.index.new.route-error'));
}
}
*persistOptimizedRoute({ sortedWaypoints }) {
applyOptimizedIntermediateWaypoints(this.args.resource.payload, sortedWaypoints);
yield this.orderActions.saveRoute.perform(this.args.resource);
yield this.hostRouter.refresh();
}
changeDestination() {
this.modalsManager.show('modals/order-change-destination', {
title: 'Change destination',
order: this.args.resource,
acceptButtonText: 'Change destination',
declineButtonText: 'Close',
onChange: async () => {
if (typeof this.args.resource?.reload === 'function') {
await this.args.resource.reload();
}
if (typeof this.args.resource?.set === 'function') {
this.args.resource.set('tracker_data', null);
}
if (typeof this.args.resource?.loadTrackerData === 'function') {
await this.args.resource.loadTrackerData();
}
await this.hostRouter.refresh();
},
});
}
viewStopActivity(stop) {
this.modalsManager.show('modals/stop-activity', {
title: 'Stop activity',
stop,
order: this.args.resource,
hideAcceptButton: true,
declineButtonText: 'Close',
});
}
async viewWaypointLabel(stop) {
const waypoint = stop?.place ?? stop;
const waypointId = waypoint?.waypoint_public_id ?? stop?.waypoint_public_id;
if (!waypointId) {
this.notifications.warning('Labels are only available for waypoint stops.');
return;
}
this.modalsManager.show(`modals/order-label`, {
title: 'Waypoint Label',
modalClass: 'modal-xl',
acceptButtonText: 'Done',
hideDeclineButton: true,
});
try {
// eslint-disable-next-line no-undef
const fileReader = new FileReader();
const { data: pdfStream } = await this.fetch.get(`orders/label/${waypointId}?format=base64`);
// eslint-disable-next-line no-undef
const base64 = await fetch(`data:application/pdf;base64,${pdfStream}`);
const blob = await base64.blob();
fileReader.onload = (event) => {
const data = event.target.result;
this.modalsManager.setOption('data', data);
};
fileReader.readAsDataURL(blob);
} catch (err) {
this.notifications.error('Failed to load waypoint label.');
debug('Error loading waypoint label data: ' + err.message);
}
}
}