@fleetbase/fleetops-engine
Version:
Fleet & Transport Management Extension for Fleetbase
228 lines (204 loc) • 9.18 kB
JavaScript
import Controller from '@ember/controller';
import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { task } from 'ember-concurrency';
export default class SettingsRoutingController extends Controller {
fetch;
notifications;
currentUser;
routeEngine;
routeOptimization;
displayEngine = 'osrm';
optimizationEngine = 'osrm';
routingUnit = 'km';
trackingProvider = 'google_routes';
trackingFallbacks = ['osrm', 'calculated'];
trackingTrafficEnabled = true;
trackingCacheTtlSeconds = 60;
trackingRouteCacheTtlSeconds = 600;
trackingStaleLocationThresholdSeconds = 300;
trackingDefaultVehicleSpeedKph = 35;
lateDepartureAlertsEnabled = true;
lateDepartureGracePeriodMinutes = 15;
routeDeviationAlertsEnabled = true;
routeDeviationDistanceThresholdMeters = 500;
prolongedStoppageAlertsEnabled = true;
prolongedStoppageDurationThresholdMinutes = 30;
showTrackingAdvancedSettings = false;
trackingProviderOptions = [
{ value: 'google_routes', label: 'Google Routes' },
{ value: 'osrm', label: 'OSRM' },
{ value: 'calculated', label: 'Calculated' },
];
routingUnitOptions = [
{ label: 'Kilometers', value: 'km' },
{ label: 'Miles', value: 'mi' },
];
saveTasks = new Map();
get hasEngineOverrides() {
return ['vroom', 'valhalla'].includes(this.displayEngine) || ['vroom', 'valhalla'].includes(this.optimizationEngine);
}
constructor() {
super(...arguments);
this.getSettings.perform();
}
/**
* Save routing settings.
*
* @memberof SettingsRoutingController
*/
*saveSettings() {
try {
yield this.fetch.post('fleet-ops/settings/routing-settings', {
display_engine: this.displayEngine,
optimization_engine: this.optimizationEngine,
unit: this.routingUnit,
});
const trackingSettings = this.trackingSettingsPayload;
yield this.fetch.post('fleet-ops/settings/tracking-settings', trackingSettings);
yield this.performAdditionalSaveTasks();
// Save in local memory too
this.currentUser.setOption('routing', {
router: this.displayEngine,
routing_display_engine: this.displayEngine,
routing_optimization_engine: this.optimizationEngine,
unit: this.routingUnit,
});
this.currentUser.setOption('tracking', trackingSettings);
this.notifications.success('Routing and tracking settings saved.');
} catch (error) {
this.notifications.serverError(error);
}
}
/**
* Get routing settings.
*
* @memberof SettingsRoutingController
*/
*getSettings() {
try {
const { router, display_engine, optimization_engine, unit } = yield this.fetch.get('fleet-ops/settings/routing-settings');
this.displayEngine = display_engine ?? router ?? 'osrm';
this.optimizationEngine = optimization_engine ?? display_engine ?? router ?? 'osrm';
this.routingUnit = unit;
const trackingSettings = yield this.fetch.get('fleet-ops/settings/tracking-settings');
this.trackingProvider = trackingSettings.provider ?? 'google_routes';
this.trackingFallbacks = this.normalizeFallbacks(trackingSettings.fallbacks);
this.trackingTrafficEnabled = trackingSettings.traffic_enabled ?? true;
this.trackingCacheTtlSeconds = trackingSettings.cache_ttl_seconds ?? 60;
this.trackingRouteCacheTtlSeconds = trackingSettings.route_cache_ttl_seconds ?? 600;
this.trackingStaleLocationThresholdSeconds = trackingSettings.stale_location_threshold_seconds ?? 300;
this.trackingDefaultVehicleSpeedKph = trackingSettings.default_vehicle_speed_kph ?? 35;
this.lateDepartureAlertsEnabled = trackingSettings.alerts?.late_departures?.enabled ?? true;
this.lateDepartureGracePeriodMinutes = trackingSettings.alerts?.late_departures?.grace_period_minutes ?? 15;
this.routeDeviationAlertsEnabled = trackingSettings.alerts?.route_deviations?.enabled ?? true;
this.routeDeviationDistanceThresholdMeters = trackingSettings.alerts?.route_deviations?.distance_threshold_meters ?? 500;
this.prolongedStoppageAlertsEnabled = trackingSettings.alerts?.prolonged_stoppages?.enabled ?? true;
this.prolongedStoppageDurationThresholdMinutes = trackingSettings.alerts?.prolonged_stoppages?.duration_threshold_minutes ?? 30;
this.trackingProviderOptions = this.normalizeProviderOptions(trackingSettings.providers ?? this.trackingProviderOptions);
} catch (error) {
this.notifications.serverError(error);
}
}
get selectedTrackingFallbackOptions() {
const selected = new Set(this.normalizeFallbacks(this.trackingFallbacks));
return this.trackingProviderOptions.filter((option) => selected.has(this.optionValue(option)));
}
get trackingSettingsPayload() {
return {
provider: this.trackingProvider,
fallbacks: this.normalizeFallbacks(this.trackingFallbacks),
traffic_enabled: this.trackingTrafficEnabled,
cache_ttl_seconds: Number(this.trackingCacheTtlSeconds) || 60,
route_cache_ttl_seconds: Number(this.trackingRouteCacheTtlSeconds) || 600,
stale_location_threshold_seconds: Number(this.trackingStaleLocationThresholdSeconds) || 300,
default_vehicle_speed_kph: Number(this.trackingDefaultVehicleSpeedKph) || 35,
alerts: {
late_departures: {
enabled: this.lateDepartureAlertsEnabled,
grace_period_minutes: Number(this.lateDepartureGracePeriodMinutes) || 15,
},
route_deviations: {
enabled: this.routeDeviationAlertsEnabled,
distance_threshold_meters: Number(this.routeDeviationDistanceThresholdMeters) || 500,
},
prolonged_stoppages: {
enabled: this.prolongedStoppageAlertsEnabled,
duration_threshold_minutes: Number(this.prolongedStoppageDurationThresholdMinutes) || 30,
},
},
};
}
normalizeFallbacks(fallbacks) {
if (Array.isArray(fallbacks)) {
return fallbacks
.map((fallback) => this.optionValue(fallback))
.map((fallback) => String(fallback).trim())
.filter(Boolean);
}
return String(fallbacks ?? '')
.split(',')
.map((fallback) => fallback.trim())
.filter(Boolean);
}
normalizeProviderOptions(options = []) {
return options.map((option) => {
const value = this.optionValue(option);
const label = option?.label ?? option?.name ?? this.providerLabel(value);
return {
...option,
key: option?.key ?? value,
name: option?.name ?? label,
value,
label,
};
});
}
providerLabel(value) {
if (value === 'osrm') {
return 'OSRM';
}
return String(value ?? '')
.replace(/[_-]+/g, ' ')
.replace(/\s+/g, ' ')
.trim()
.replace(/\b\w/g, (character) => character.toUpperCase());
}
optionValue(option) {
return typeof option === 'object' && option !== null ? (option.value ?? option.key) : option;
}
setTrackingFallbacks(options) {
this.trackingFallbacks = this.normalizeFallbacks(options);
}
toggleTrackingAdvancedSettings() {
this.showTrackingAdvancedSettings = !this.showTrackingAdvancedSettings;
}
registerSaveTask(key, task) {
if (!key || typeof task?.perform !== 'function') {
return;
}
this.saveTasks.set(key, task);
}
unregisterSaveTask(key) {
if (!key) {
return;
}
this.saveTasks.delete(key);
}
async performAdditionalSaveTasks() {
for (const task of this.saveTasks.values()) {
try {
await task.perform();
} catch (error) {
// Ignore explicit ember-concurrency task cancellations from components
// that may have unmounted during routing-engine selection changes.
if (typeof error?.name === 'string' && error.name.includes('TaskCancelation')) {
continue;
}
throw error;
}
}
return true;
}
}