@fleetbase/fleetops-engine
Version:
Fleet & Transport Management Extension for Fleetbase
363 lines (286 loc) • 10.2 kB
JavaScript
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';
import { action, get } from '@ember/object';
import { task, timeout } from 'ember-concurrency';
export default class AiCreateOrderPreviewComponent extends Component {
router;
draft = this.clone(this.args.preview?.draft ?? {});
preview = this.args.preview;
editingField = null;
get payload() {
return this.draft.payload ?? {};
}
get routeStops() {
return this.preview?.route_preview?.stops ?? [];
}
get podMethods() {
return this.preview?.options?.pod_methods ?? ['scan', 'signature', 'photo'];
}
get isReady() {
return this.preview?.ready === true && !this.isTerminal;
}
get isCompleted() {
return Boolean(this.preview?.result);
}
get isTerminal() {
return this.isCompleted || this.isCancelled || Boolean(this.preview?.error);
}
get isCancelled() {
return this.args.task?.status === 'cancelled' || this.preview?.error?.type === 'cancelled';
}
get cancelledMessage() {
return this.preview?.error?.message ?? 'This order creation preview was cancelled.';
}
get completedMessage() {
return this.preview?.result?.message ?? 'Fleet-Ops order was created.';
}
get resultResource() {
return this.preview?.result?.resource;
}
get orderReference() {
return this.resultResource?.id ?? this.resultResource?.uuid ?? 'Created order';
}
get canOpenOrder() {
return Boolean(this.resultResource?.route && (this.resultResource?.models?.length || this.resultResource?.uuid || this.resultResource?.id));
}
get missingFields() {
return this.preview?.missing_fields ?? [];
}
get hasMissingFields() {
return this.missingFields.length > 0;
}
get orderPreviewId() {
return this.args.task?.uuid ?? this.args.task?.id ?? this.preview?.key ?? 'ai-order-preview';
}
get orderTypeLabel() {
return this.titleize(this.draft.type ?? this.draft.order_config_name ?? this.draft.order_config_uuid) ?? 'Select order type';
}
get scheduledAtLabel() {
const scheduledAt = this.draft.scheduled_at;
if (!scheduledAt) {
return 'Add schedule';
}
return String(scheduledAt);
}
get pickupLabel() {
return this.addressLabel(this.payload.pickup) ?? this.payload.pickup_query ?? 'Add pickup';
}
get dropoffLabel() {
return this.addressLabel(this.payload.dropoff) ?? this.payload.dropoff_query ?? 'Add dropoff';
}
get driverLabel() {
return this.draft.driver_query ?? this.draft.driver_name ?? 'Assign driver';
}
get vehicleLabel() {
return this.draft.vehicle_query ?? this.draft.vehicle_name ?? 'Assign vehicle';
}
get notesLabel() {
return this.draft.notes?.trim?.() || 'Add order notes';
}
get podMethodLabel() {
return this.titleize(this.draft.pod_method ?? 'scan');
}
get isEditingOrderType() {
return this.editingField === 'orderType';
}
get isEditingSchedule() {
return this.editingField === 'schedule';
}
get isEditingPickup() {
return this.editingField === 'pickup';
}
get isEditingDropoff() {
return this.editingField === 'dropoff';
}
get isEditingDriver() {
return this.editingField === 'driver';
}
get isEditingVehicle() {
return this.editingField === 'vehicle';
}
get isEditingPodMethod() {
return this.editingField === 'podMethod';
}
get isEditingNotes() {
return this.editingField === 'notes';
}
get isApplyDisabled() {
return !this.isReady || this.args.isApplying || this.refreshPreview.isRunning;
}
addressLabel(place) {
if (!place) {
return null;
}
const values = [place.address, place.street1, place.name, place.query, place.city, place.postal_code, place.country]
.map((value) => (value === null || value === undefined ? null : String(value).trim()))
.filter((value) => value && !['undefined', 'null'].includes(value.toLowerCase()));
return [...new Set(values)].slice(0, 3).join(' - ');
}
clone(value) {
return JSON.parse(JSON.stringify(value ?? {}));
}
modelValue(model, key) {
if (!model) {
return null;
}
if (typeof model.get === 'function') {
return model.get(key);
}
return get(model, key) ?? model[key];
}
serializeModel(model, fallback = {}) {
if (!model) {
return null;
}
if (typeof model.toJSON === 'function') {
return {
...model.toJSON(),
id: this.modelValue(model, 'id'),
uuid: this.modelValue(model, 'uuid') ?? this.modelValue(model, 'id'),
public_id: this.modelValue(model, 'public_id'),
name: this.modelValue(model, 'name'),
address: this.addressLabel(model) ?? this.modelValue(model, 'address'),
street1: this.modelValue(model, 'street1'),
city: this.modelValue(model, 'city'),
postal_code: this.modelValue(model, 'postal_code'),
country: this.modelValue(model, 'country'),
latitude: this.modelValue(model, 'latitude'),
longitude: this.modelValue(model, 'longitude'),
...fallback,
};
}
return { ...model, ...fallback };
}
titleize(value) {
if (!value) {
return null;
}
return String(value)
.replace(/[_-]+/g, ' ')
.replace(/\s+/g, ' ')
.trim()
.replace(/\b\w/g, (char) => char.toUpperCase());
}
updateDraft(updates = {}) {
this.draft = {
...this.draft,
...updates,
payload: {
...(this.draft.payload ?? {}),
...(updates.payload ?? {}),
},
};
}
updateRouteDraft(updates = {}) {
this.updateDraft(updates);
this.refreshPreview.perform();
}
editField(field) {
if (this.isTerminal) {
return;
}
this.editingField = field;
}
closeEditor() {
this.editingField = null;
}
*refreshPreview() {
yield timeout(180);
const refreshed = yield this.args.onRefresh?.(this.args.task, this.preview, { draft: this.draft });
if (refreshed) {
this.preview = {
...this.preview,
...refreshed,
draft: this.draft,
};
}
}
setPayloadPlace(role, place) {
const serialized = this.serializeModel(place);
const payload = { ...(this.draft.payload ?? {}) };
payload[role] = serialized;
payload[`${role}_query`] = this.addressLabel(serialized) ?? serialized?.address ?? serialized?.name ?? null;
if (serialized?.uuid) {
payload[`${role}_uuid`] = serialized.uuid;
} else {
delete payload[`${role}_uuid`];
}
this.updateRouteDraft({ payload });
this.closeEditor();
}
setOrderConfig(orderConfig) {
this.updateDraft({
order_config_uuid: this.modelValue(orderConfig, 'uuid') ?? this.modelValue(orderConfig, 'id'),
order_config_name: this.modelValue(orderConfig, 'name'),
type: this.modelValue(orderConfig, 'key'),
});
this.closeEditor();
}
setScheduledAt(value) {
this.updateDraft({ scheduled_at: value });
}
setDriver(driver) {
this.updateDraft({
driver: this.modelValue(driver, 'uuid') ?? this.modelValue(driver, 'id'),
driver_query: this.modelValue(driver, 'name') ?? this.modelValue(driver, 'public_id'),
});
this.closeEditor();
}
setVehicle(vehicle) {
this.updateDraft({
vehicle_assigned_uuid: this.modelValue(vehicle, 'uuid') ?? this.modelValue(vehicle, 'id'),
vehicle_query: this.modelValue(vehicle, 'display_name') ?? this.modelValue(vehicle, 'name') ?? this.modelValue(vehicle, 'plate_number'),
});
this.closeEditor();
}
setPodRequired(value) {
this.updateDraft({
pod_required: value,
pod_method: value ? (this.draft.pod_method ?? 'scan') : null,
});
}
setPodMethod(value) {
this.updateDraft({ pod_method: value });
this.closeEditor();
}
setPodMethodFromEvent(event) {
this.setPodMethod(event.target.value);
}
setDispatched(value) {
this.updateDraft({ dispatched: value });
}
setNotes(valueOrEvent) {
const value = valueOrEvent?.target ? valueOrEvent.target.value : valueOrEvent;
this.updateDraft({ notes: value });
}
apply() {
if (this.isTerminal) {
return;
}
return this.args.onApply?.(this.args.task, this.preview, { draft: this.draft });
}
cancel() {
if (this.isTerminal) {
return;
}
return this.args.onCancel?.(this.args.task);
}
openOrder() {
if (!this.canOpenOrder) {
return;
}
const models = this.resultResource.models?.length ? this.resultResource.models : [this.resultResource.uuid ?? this.resultResource.id].filter(Boolean);
try {
this.router.transitionTo(this.resultResource.route, ...models);
} catch {
this.router.transitionTo('operations.orders.index.details', ...models);
}
}
updatePreview(preview) {
this.preview = preview;
if (preview?.draft && !this.editingField) {
this.draft = this.clone(preview.draft);
}
}
}