Skip to content

Commit

Permalink
Refactored the service rates to utilize the new order config, activit…
Browse files Browse the repository at this point in the history
…y can now require proof of delivery, patched create new order strict validation, added `order_config_uuid` column to service_rates table, few other improvements
  • Loading branch information
roncodes committed Mar 13, 2024
1 parent b0d5547 commit 3f4a0df
Show file tree
Hide file tree
Showing 23 changed files with 474 additions and 148 deletions.
19 changes: 19 additions & 0 deletions addon/components/activity-form-panel.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,25 @@
<span class="dark:text-gray-100 text-sm ml-2">{{t "fleet-ops.component.activity-form-panel.completes-order"}}</span>
</Toggle>
</InputGroup>
<InputGroup @helpText={{t "fleet-ops.component.activity-form-panel.require-pod-help-text"}}>
<Toggle @isToggled={{this.activity.require_pod}} @onToggle={{fn (mut this.activity.require_pod)}} @helpText={{t "fleet-ops.component.activity-form-panel.require-pod-help-text"}}>
<span class="dark:text-gray-100 text-sm mx-2">{{t "fleet-ops.component.activity-form-panel.require-pod"}}</span>
</Toggle>
</InputGroup>
{{#if this.activity.require_pod}}
<InputGroup @name={{t "fleet-ops.component.activity-form-panel.select-pod-method"}}>
<select class="form-select w-full has--placeholder" {{on "change" this.setProofOfDeliveryMethod}}>
<option selected disabled>
{{t "fleet-ops.component.activity-form-panel.pod-method-placeholder"}}
</option>
{{#each this.podOptions as |podOption|}}
<option value={{podOption}} selected={{eq this.activity.pod_method podOption}}>
{{smart-humanize podOption}}
</option>
{{/each}}
</select>
</InputGroup>
{{/if}}
<ActivityLogicBuilder @activity={{this.activity}} @onChange={{this.updateActivityLogic}} class="mb-4" />
<ActivityEventSelector @activity={{this.activity}} @onChange={{this.updateActivityEvents}} />
<Spacer @height="300px" />
Expand Down
18 changes: 18 additions & 0 deletions addon/components/activity-form-panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ export default class ActivityFormPanelComponent extends Component {
*/
@tracked targetActivity;

/**
* Proof of delivery options.
*
* @memberof ActivityFormPanelComponent
*/
@tracked podOptions = ['scan', 'signature', 'photo'];

/**
* Constructor for ActivityFormPanelComponent.
* Applies context component arguments upon instantiation.
Expand All @@ -46,6 +53,17 @@ export default class ActivityFormPanelComponent extends Component {
}
}

/**
* Sets the proof of delivery method to be used for this activity.
*
* @param {Event} event
* @memberof ActivityFormPanelComponent
*/
@action setProofOfDeliveryMethod(event) {
const value = event.target.value;
this.activity.set('pod_method', value);
}

/**
* Action method to set the activity code. It uses the underscore function to format
* the code and updates the status by capitalizing each word.
Expand Down
21 changes: 14 additions & 7 deletions addon/components/live-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,6 @@ export default class LiveMapComponent extends Component {
this.tileSourceUrl = 'https://{s}.tile.jawg.io/jawg-matrix/{z}/{x}/{y}{r}.png?access-token=';
}

this.ready();
this.setupComponent();
}

Expand Down Expand Up @@ -332,7 +331,6 @@ export default class LiveMapComponent extends Component {
})
.finally(() => {
this.listen();
this.ready();
});
}

Expand Down Expand Up @@ -386,14 +384,23 @@ export default class LiveMapComponent extends Component {
* if available, or null if the function is skipped.
*/
async setInitialCoordinates() {
const { latitude, longitude } = await this.location.getUserLocation();
try {
const { latitude, longitude } = await this.location.getUserLocation();

this.latitude = latitude || this.location.DEFAULT_LATITUDE;
this.longitude = longitude || this.location.DEFAULT_LONGITUDE;
} catch (error) {
this.latitude = this.location.DEFAULT_LATITUDE;
this.longitude = this.location.DEFAULT_LONGITUDE;
}

this.latitude = latitude;
this.longitude = longitude;
this.ready();

// trigger that initial coordinates is set to livemap component
this.universe.trigger('fleet-ops.live-map.has_coordinates', { latitude: this.latitude, longitude: this.longitude });
// Trigger that initial coordinates are set to live map component
this.universe.trigger('fleet-ops.live-map.has_coordinates', {
latitude: this.latitude,
longitude: this.longitude,
});
}

/**
Expand Down
5 changes: 5 additions & 0 deletions addon/controllers/operations/orders/index/new.js
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,11 @@ export default class OperationsOrdersIndexNewController extends BaseController {
params.facilitator = this.order.facilitator.public_id;
}

// filter by order config type
if (this.orderConfig) {
params.service_type = this.orderConfig.key;
}

if (shouldCheck) {
try {
serviceRates = await this.fetch.get(`service-rates/for-route`, params);
Expand Down
25 changes: 23 additions & 2 deletions addon/controllers/operations/service-rates/index/new.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ export default class OperationsServiceRatesIndexNewController extends BaseContro
@tracked serviceRate = this.store.createRecord('service-rate', { per_meter_unit: 'm' });

/**
* Different service types available, based on order type.
* Available order configs.
*
* @var {Array}
*/
@tracked serviceTypes = [];
@tracked orderConfigs = [];

/**
* Service areas.
Expand All @@ -75,6 +75,13 @@ export default class OperationsServiceRatesIndexNewController extends BaseContro
*/
@tracked isCreatingServiceRate = false;

/**
* The current selected order config.
*
* @var {OrderConfigModel|null}
*/
@tracked orderConfig;

/**
* True if updating service rate.
*
Expand Down Expand Up @@ -279,6 +286,20 @@ export default class OperationsServiceRatesIndexNewController extends BaseContro
});
}

@action setConfig(event) {
const orderConfigId = event.target.value;
if (!orderConfigId) {
return;
}

const orderConfig = this.store.peekRecord('order-config', orderConfigId);
if (orderConfig) {
this.orderConfig = orderConfig;
this.serviceRate.set('order_config_uuid', orderConfig.id);
this.serviceRate.set('service_type', orderConfig.key);
}
}

/**
* Adds a per drop-off rate fee
*/
Expand Down
19 changes: 14 additions & 5 deletions addon/routes/operations/service-rates/index/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,20 @@ export default class OperationsServiceRatesIndexEditRoute extends Route {
@service currentUser;
@service notifications;

/**
* Re-use the new service rate form template.
*
* @memberof OperationsServiceRatesIndexEditRoute
*/
templateName = 'operations.service-rates.index.new';

/**
* Handle any async error.
*
* @param {*} error
* @return {*}
* @memberof OperationsServiceRatesIndexEditRoute
*/
@action error(error) {
this.notifications.serverError(error);
return this.transitionTo('operations.service-rates.index');
Expand All @@ -33,10 +45,7 @@ export default class OperationsServiceRatesIndexEditRoute extends Route {
controller.parcelFees = model.parcel_fees;
}

const serviceTypes = await this.currentUser.getInstalledOrderConfigs();
const serviceAreas = await this.store.findAll('service-area');

controller.serviceTypes = serviceTypes;
controller.serviceAreas = serviceAreas;
controller.orderConfigs = await this.store.findAll('order-config');
controller.serviceAreas = await this.store.findAll('service-area');
}
}
8 changes: 2 additions & 6 deletions addon/routes/operations/service-rates/index/new.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,9 @@ import { inject as service } from '@ember/service';

export default class OperationsServiceRatesIndexNewRoute extends Route {
@service store;
@service currentUser;

async setupController(controller) {
const serviceTypes = await this.currentUser.getInstalledOrderConfigs();
const serviceAreas = await this.store.findAll('service-area');

controller.serviceTypes = serviceTypes;
controller.serviceAreas = serviceAreas;
controller.orderConfigs = await this.store.findAll('order-config');
controller.serviceAreas = await this.store.findAll('service-area');
}
}
98 changes: 54 additions & 44 deletions addon/services/location.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,66 +78,74 @@ export default class LocationService extends Service {
}

/**
* Attempts to fetch the user's location from various sources.
* Uses cached data, navigator geolocation, or WHOIS data as fallbacks.
* Attempts to fetch the user's location from various sources including cached data,
* navigator geolocation, or WHOIS data. It first tries to get the cached coordinates.
* If not available or outdated, it tries the browser's geolocation API.
* As a fallback, it uses WHOIS data associated with the user's account.
*
* @returns {Promise<Object>} A promise that resolves to an object containing latitude and longitude.
*/
getUserLocation() {
return this.fetch.cachedGet('fleet-ops/live/coordinates', {}, { expirationInterval: 1, expirationIntervalUnit: 'hour' }).then((coordinates) => {
async getUserLocation() {
// If the location has already been located, return the existing coordinates
if (this.located) {
return { latitude: this.latitude, longitude: this.longitude };
}

try {
const coordinates = await this.fetch.cachedGet('fleet-ops/live/coordinates', {}, { expirationInterval: 1, expirationIntervalUnit: 'hour' });

if (isBlank(coordinates)) {
return this.getUserLocationFromNavigator().then((navigatorCoordinates) => {
this.updateLocation(navigatorCoordinates);
return navigatorCoordinates;
});
return await this.getUserLocationFromNavigator();
}

if (isArray(coordinates)) {
const validCoordinates = coordinates.filter((point) => point.coordinates[0] !== 0);
if (isArray(coordinates) && coordinates.length > 0) {
// Ensure the coordinates array contains valid data
const validCoordinates = coordinates.find((point) => point.coordinates[0] !== 0 && point.coordinates[1] !== 0);
if (validCoordinates) {
const [longitude, latitude] = getWithDefault(validCoordinates, '0.coordinates', [0, 0]);
const userCoordinates = {
latitude,
longitude,
};

this.updateLocation(userCoordinates);
return userCoordinates;
const [longitude, latitude] = validCoordinates.coordinates;
this.updateLocation({ latitude, longitude });
return { latitude, longitude };
}
}

return this.getUserLocationFromWhois();
});
return await this.getUserLocationFromWhois();
} catch (error) {
return await this.getUserLocationFromWhois();
}
}

/**
* Retrieves the user's location using the browser's navigator geolocation API.
* @returns {Promise<Object>} A promise that resolves to geolocation coordinates.
* It creates a promise that resolves with the geolocation if successful,
* or with WHOIS data as a fallback in case of failure or absence of navigator geolocation.
*
* @returns {Promise<Object>} A promise that resolves to geolocation coordinates or WHOIS data.
*/
getUserLocationFromNavigator() {
return new Promise((resolve) => {
// eslint-disable-next-line no-undef
if (window.navigator && window.navigator.geolocation) {
// eslint-disable-next-line no-undef
return navigator.geolocation.getCurrentPosition(
({ coords }) => {
this.updateLocation(coords);
return resolve(coords);
},
() => {
// If failed use user whois
return resolve(this.getUserLocationFromWhois());
}
);
async getUserLocationFromNavigator() {
if (window.navigator && window.navigator.geolocation) {
try {
const position = await new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject, {
enableHighAccuracy: true,
timeout: 600,
});
});
const { latitude, longitude } = position.coords;
this.updateLocation({ latitude, longitude });
return { latitude, longitude };
} catch (error) {
return await this.getUserLocationFromWhois();
}

// default to whois lookup coordinates
return resolve(this.getUserLocationFromWhois());
});
} else {
return await this.getUserLocationFromWhois();
}
}

/**
* Retrieves the user's location based on WHOIS data associated with the user's account.
* @returns {Object} An object containing latitude and longitude from WHOIS data.
* Retrieves the user's location based on WHOIS data associated with their account.
* Defaults to predefined coordinates if WHOIS data is not available.
*
* @returns {Object} An object containing latitude and longitude from WHOIS data or default values.
*/
getUserLocationFromWhois() {
const whois = this.currentUser.getOption('whois');
Expand All @@ -151,8 +159,10 @@ export default class LocationService extends Service {
}

/**
* Updates the service's tracked properties with the new location data and triggers an event.
* @param {Object} coordinates - An object containing the latitude and longitude.
* Updates the service's tracked properties with the new location data.
* Triggers an event to notify other parts of the application that the user's location has been updated.
*
* @param {Object} coordinates - An object containing the latitude and longitude to be set.
*/
updateLocation({ latitude, longitude }) {
this.latitude = latitude;
Expand Down
11 changes: 10 additions & 1 deletion addon/templates/operations/service-rates/index/new.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,16 @@

<div class="input-group">
<InputLabel @labelText={{t "fleet-ops.operations.service-rates.index.new.service-order-label"}} @helpText={{t "fleet-ops.operations.service-rates.index.new.service-order-help-text"}} />
<Select class="w-full" @options={{this.serviceTypes}} @value={{this.serviceRate.service_type}} @optionLabel="name" @optionValue="key" @onSelect={{fn (mut this.serviceRate.service_type)}} @placeholder={{t "fleet-ops.operations.service-rates.index.new.service-order-placeholder"}} />
<select class="form-select w-full has--placeholder" {{on "change" this.setConfig}}>
<option selected disabled>
{{t "fleet-ops.operations.service-rates.index.new.service-order-placeholder"}}
</option>
{{#each this.orderConfigs as |orderConfig|}}
<option value={{orderConfig.id}} selected={{eq orderConfig.id this.serviceRate.order_config_uuid}}>
{{orderConfig.name}}
</option>
{{/each}}
</select>
</div>

<div class="input-group">
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fleetbase/fleetops-api",
"version": "0.4.18",
"version": "0.4.19",
"description": "Fleet & Transport Management Extension for Fleetbase",
"keywords": [
"fleetbase-extension",
Expand Down
2 changes: 1 addition & 1 deletion extension.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Fleet-Ops",
"version": "0.4.18",
"version": "0.4.19",
"description": "Fleet & Transport Management Extension for Fleetbase",
"repository": "https://github.com/fleetbase/fleetops",
"license": "MIT",
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@fleetbase/fleetops-engine",
"version": "0.4.18",
"version": "0.4.19",
"description": "Fleet & Transport Management Extension for Fleetbase",
"fleetbase": {
"route": "fleet-ops"
Expand Down Expand Up @@ -44,7 +44,7 @@
"@babel/core": "^7.23.2",
"@fleetbase/ember-core": "^0.2.6",
"@fleetbase/ember-ui": "^0.2.11",
"@fleetbase/fleetops-data": "^0.1.13",
"@fleetbase/fleetops-data": "^0.1.14",
"@fleetbase/leaflet-routing-machine": "^3.2.16",
"@fortawesome/ember-fontawesome": "^0.4.1",
"@fortawesome/fontawesome-svg-core": "^6.4.0",
Expand Down
Loading

0 comments on commit 3f4a0df

Please sign in to comment.