@fleetbase/fleetops-engine 0.6.45 → 0.6.47
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.
- package/addon/components/admin/map-settings.hbs +26 -0
- package/addon/components/admin/map-settings.js +46 -0
- package/addon/components/admin/routing-settings.hbs +11 -0
- package/addon/components/cell/order-route-type.hbs +46 -0
- package/addon/components/cell/order-route-type.js +90 -0
- package/addon/components/cell/place-address.hbs +1 -0
- package/addon/components/display-place.hbs +45 -56
- package/addon/components/display-place.js +21 -0
- package/addon/components/layout/fleet-ops-sidebar/driver-listing.js +5 -5
- package/addon/components/layout/fleet-ops-sidebar/fleet-listing.js +5 -5
- package/addon/components/layout/fleet-ops-sidebar.js +8 -0
- package/addon/components/maintenance-schedule/form.js +2 -1
- package/addon/components/map/drawer/driver-listing.js +5 -5
- package/addon/components/map/drawer/place-listing.js +5 -5
- package/addon/components/map/drawer/position-listing.js +102 -21
- package/addon/components/map/drawer/vehicle-listing.js +5 -5
- package/addon/components/map/google-live-map.hbs +9 -0
- package/addon/components/map/google-live-map.js +445 -0
- package/addon/components/map/leaflet-live-map.hbs +186 -142
- package/addon/components/map/leaflet-live-map.js +121 -41
- package/addon/components/map/toolbar/visibility-control-panel.js +2 -2
- package/addon/components/map/toolbar/zones-panel.hbs +9 -9
- package/addon/components/map/toolbar.hbs +3 -3
- package/addon/components/map/toolbar.js +9 -1
- package/addon/components/modals/map-layer-form.hbs +132 -63
- package/addon/components/modals/orchestrator-import.hbs +4 -11
- package/addon/components/modals/orchestrator-import.js +17 -1
- package/addon/components/modals/update-order-activity.hbs +1 -1
- package/addon/components/orchestrator/order-pool.js +3 -2
- package/addon/components/orchestrator-workbench.js +29 -41
- package/addon/components/order/details/detail.js +2 -2
- package/addon/components/order/details/payload.hbs +5 -5
- package/addon/components/order/details/payload.js +18 -1
- package/addon/components/order/details/route.hbs +2 -2
- package/addon/components/order/details/route.js +76 -3
- package/addon/components/order/form/details.hbs +1 -2
- package/addon/components/order/form/details.js +6 -0
- package/addon/components/order/form/payload.hbs +33 -0
- package/addon/components/order/form/payload.js +1 -0
- package/addon/components/order/form/route.hbs +8 -8
- package/addon/components/order/form/route.js +86 -18
- package/addon/components/order/form.js +2 -2
- package/addon/components/order/route-editor.hbs +209 -147
- package/addon/components/order/route-editor.js +143 -102
- package/addon/components/place/address.hbs +1 -0
- package/addon/components/place/address.js +12 -0
- package/addon/components/route-list.hbs +43 -55
- package/addon/components/route-list.js +47 -0
- package/addon/components/route-optimization-wizard-panel.js +3 -4
- package/addon/components/vehicle/card.hbs +4 -4
- package/addon/controllers/maintenance/schedules/index.js +2 -1
- package/addon/controllers/operations/orders/index/details.js +110 -37
- package/addon/controllers/operations/orders/index/new.js +1 -1
- package/addon/controllers/operations/orders/index.js +8 -1
- package/addon/controllers/settings/map.js +97 -0
- package/addon/controllers/settings/payments/index.js +1 -1
- package/addon/controllers/settings/routing.js +47 -12
- package/addon/extension.js +11 -0
- package/addon/helpers/place-address-html.js +6 -0
- package/addon/instance-initializers/register-google-routes.js +17 -0
- package/addon/instance-initializers/register-osrm.js +5 -0
- package/addon/routes/application.js +6 -0
- package/addon/routes/operations/orchestrator.js +13 -0
- package/addon/routes/operations/orders/index/details.js +29 -22
- package/addon/routes/operations/orders/index/new.js +9 -0
- package/addon/routes/operations/orders/index.js +0 -1
- package/addon/routes/settings/map.js +3 -0
- package/addon/routes.js +1 -0
- package/addon/services/entity-actions.js +1 -1
- package/addon/services/geofence-event-bus.js +2 -1
- package/addon/services/geofence.js +204 -168
- package/addon/services/google-routes.js +300 -0
- package/addon/services/leaflet-draw-restriction.js +2 -1
- package/addon/services/leaflet-map-manager.js +17 -0
- package/addon/services/map-adapter/google.js +2205 -0
- package/addon/services/map-adapter/leaflet.js +1042 -0
- package/addon/services/map-adapter-interface.js +551 -0
- package/addon/services/map-manager.js +768 -0
- package/addon/services/map-settings.js +75 -0
- package/addon/services/movement-tracker.js +83 -32
- package/addon/services/order-actions.js +11 -1
- package/addon/services/osrm.js +79 -6
- package/addon/services/position-playback.js +135 -62
- package/addon/services/route-engine.js +83 -0
- package/addon/services/service-areas.js +8 -1
- package/addon/styles/fleetops-engine.css +555 -0
- package/addon/templates/management/vehicles/index.hbs +0 -1
- package/addon/templates/operations/orders/index/details.hbs +0 -1
- package/addon/templates/settings/map.hbs +53 -0
- package/addon/templates/settings/routing.hbs +15 -4
- package/addon/utils/fleet-ops-options.js +1 -0
- package/addon/utils/has-valid-coordinates.js +6 -0
- package/addon/utils/map-provider-options.js +6 -0
- package/addon/utils/order-route-editing.js +86 -0
- package/addon/utils/order-route-summary.js +20 -0
- package/addon/utils/place-address-html.js +41 -0
- package/addon/utils/route-colors.js +43 -12
- package/addon/utils/route-visualization.js +93 -0
- package/addon/utils/vendor-integration.js +2 -1
- package/app/components/admin/map-settings.js +1 -0
- package/app/components/admin/routing-settings.js +1 -0
- package/app/components/cell/order-route-type.js +1 -0
- package/app/components/cell/place-address.js +1 -0
- package/app/components/map/google-live-map.js +1 -0
- package/app/components/place/address.js +1 -0
- package/app/controllers/settings/map.js +1 -0
- package/app/helpers/place-address-html.js +1 -0
- package/app/routes/settings/map.js +1 -0
- package/app/services/map-adapter/google.js +1 -0
- package/app/services/map-adapter/leaflet.js +1 -0
- package/app/services/map-adapter-interface.js +1 -0
- package/app/services/map-manager.js +1 -0
- package/app/services/map-settings.js +1 -0
- package/app/templates/settings/map.js +1 -0
- package/app/utils/has-valid-coordinates.js +1 -0
- package/app/utils/order-route-editing.js +2 -0
- package/app/utils/order-route-summary.js +1 -0
- package/app/utils/place-address-html.js +1 -0
- package/app/utils/route-colors.js +1 -1
- package/app/utils/route-visualization.js +1 -0
- package/assets/images/drawing-controls-spritesheet.svg +156 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +4 -4
- package/pnpm-workspace.yaml +7 -0
- package/server/src/Auth/Schemas/FleetOps.php +5 -0
- package/server/src/Http/Controllers/Api/v1/OrderController.php +31 -3
- package/server/src/Http/Controllers/Api/v1/PayloadController.php +14 -1
- package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +2 -0
- package/server/src/Http/Controllers/Api/v1/ServiceQuoteController.php +3 -2
- package/server/src/Http/Controllers/Api/v1/ZoneController.php +2 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +2 -1
- package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +7 -4
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +90 -34
- package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +3 -2
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +106 -3
- package/server/src/Http/Filter/OrderFilter.php +2 -1
- package/server/src/Http/Resources/v1/Index/Payload.php +9 -7
- package/server/src/Http/Resources/v1/Order.php +20 -1
- package/server/src/Models/Order.php +42 -1
- package/server/src/Models/OrderConfig.php +15 -0
- package/server/src/Models/Payload.php +131 -27
- package/server/src/Models/Place.php +182 -48
- package/server/src/Models/ServiceRate.php +4 -3
- package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +59 -23
- package/server/src/Support/Utils.php +36 -2
- package/server/src/routes.php +4 -0
- package/translations/en-us.yaml +1937 -1916
|
@@ -3,11 +3,18 @@ import { tracked } from '@glimmer/tracking';
|
|
|
3
3
|
import { inject as service } from '@ember/service';
|
|
4
4
|
import { action, get } from '@ember/object';
|
|
5
5
|
import { task } from 'ember-concurrency';
|
|
6
|
+
import { colorForId, routeColorForStatus, routeStyleForStatus } from '../../../utils/route-colors';
|
|
7
|
+
import { buildRoutePointMarkerPresentation, buildRoutePointsFromPayload } from '../../../utils/route-visualization';
|
|
8
|
+
|
|
9
|
+
const ORDER_ROUTE_PREVIEW_PADDING_BOTTOM_RIGHT = [420, 0];
|
|
10
|
+
const ORDER_ROUTE_PREVIEW_MAX_ZOOM_TWO_POINTS = 13;
|
|
11
|
+
const ORDER_ROUTE_PREVIEW_MAX_ZOOM_MULTI_POINTS = 12;
|
|
12
|
+
const ORDER_ROUTE_PREVIEW_SINGLE_POINT_PANBY = [10, 0];
|
|
6
13
|
|
|
7
14
|
export default class OrderFormRouteComponent extends Component {
|
|
8
|
-
@service
|
|
15
|
+
@service mapManager;
|
|
16
|
+
@service routeEngine;
|
|
9
17
|
@service routeOptimization;
|
|
10
|
-
@service osrm;
|
|
11
18
|
@service location;
|
|
12
19
|
@service store;
|
|
13
20
|
@service currentUser;
|
|
@@ -17,24 +24,31 @@ export default class OrderFormRouteComponent extends Component {
|
|
|
17
24
|
@tracked routingControl;
|
|
18
25
|
@tracked route;
|
|
19
26
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
focusPlace(place, zoom = 18) {
|
|
28
|
+
if (place?.hasValidCoordinates) {
|
|
29
|
+
this.mapManager.positionWaypoints([[place.latitude, place.longitude]], {
|
|
30
|
+
singlePointZoom: zoom,
|
|
31
|
+
panBy: ORDER_ROUTE_PREVIEW_SINGLE_POINT_PANBY,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
23
35
|
|
|
24
|
-
|
|
36
|
+
get coordinates() {
|
|
37
|
+
return this.routePoints.map(({ place }) => [place.latitude, place.longitude]);
|
|
25
38
|
}
|
|
26
39
|
|
|
27
40
|
get places() {
|
|
28
|
-
|
|
29
|
-
|
|
41
|
+
return this.routePoints.map(({ place }) => place);
|
|
42
|
+
}
|
|
30
43
|
|
|
31
|
-
|
|
44
|
+
get routePoints() {
|
|
45
|
+
return buildRoutePointsFromPayload(this.args.resource.payload);
|
|
32
46
|
}
|
|
33
47
|
|
|
34
48
|
willDestroy() {
|
|
35
49
|
super.willDestroy(...arguments);
|
|
36
50
|
if (this.routingControl) {
|
|
37
|
-
this.
|
|
51
|
+
this.mapManager.removeRoutingControl(this.routingControl);
|
|
38
52
|
}
|
|
39
53
|
}
|
|
40
54
|
|
|
@@ -91,15 +105,23 @@ export default class OrderFormRouteComponent extends Component {
|
|
|
91
105
|
|
|
92
106
|
const waypoint = this.store.createRecord('waypoint', { ...properties, type: 'dropoff' });
|
|
93
107
|
this.args.resource.payload.waypoints.pushObject(waypoint);
|
|
108
|
+
|
|
94
109
|
this.previewRoute();
|
|
95
110
|
}
|
|
96
111
|
|
|
97
112
|
@action setWaypointPlace(index, place) {
|
|
98
|
-
if (!this.args.resource.payload.waypoints[index])
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
113
|
+
if (!this.args.resource.payload.waypoints[index]) return;
|
|
101
114
|
|
|
102
115
|
this.args.resource.payload.waypoints[index].place = place;
|
|
116
|
+
this.args.resource.payload.waypoints[index]?.setProperties({
|
|
117
|
+
street1: place.street1,
|
|
118
|
+
street2: place.street2,
|
|
119
|
+
city: place.city,
|
|
120
|
+
province: place.province,
|
|
121
|
+
postal_code: place.postal_code,
|
|
122
|
+
country: place.country,
|
|
123
|
+
location: place.location,
|
|
124
|
+
});
|
|
103
125
|
this.previewRoute();
|
|
104
126
|
}
|
|
105
127
|
|
|
@@ -129,13 +151,55 @@ export default class OrderFormRouteComponent extends Component {
|
|
|
129
151
|
}
|
|
130
152
|
|
|
131
153
|
@action async previewRoute() {
|
|
132
|
-
if (!this.coordinates.length)
|
|
154
|
+
if (!this.coordinates.length) {
|
|
155
|
+
if (this.routingControl) {
|
|
156
|
+
this.mapManager.removeRoutingControl(this.routingControl);
|
|
157
|
+
this.routingControl = null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
133
162
|
|
|
134
|
-
const
|
|
163
|
+
const order = this.args.resource;
|
|
164
|
+
const orderId = order.public_id ?? order.id ?? 'new-order';
|
|
165
|
+
const routeColor = colorForId(orderId);
|
|
166
|
+
const routeStatus = order.status ?? 'created';
|
|
167
|
+
const statusColor = routeColorForStatus(routeStatus);
|
|
168
|
+
const routeStyles = routeStyleForStatus(routeStatus, statusColor);
|
|
169
|
+
const isSinglePointPreview = this.coordinates.length === 1;
|
|
170
|
+
const fitOptions = isSinglePointPreview
|
|
171
|
+
? {
|
|
172
|
+
paddingBottomRight: [0, 0],
|
|
173
|
+
panBy: ORDER_ROUTE_PREVIEW_SINGLE_POINT_PANBY,
|
|
174
|
+
}
|
|
175
|
+
: {
|
|
176
|
+
paddingBottomRight: ORDER_ROUTE_PREVIEW_PADDING_BOTTOM_RIGHT,
|
|
177
|
+
panBy: [0, 0],
|
|
178
|
+
maxZoom: this.coordinates.length === 2 ? ORDER_ROUTE_PREVIEW_MAX_ZOOM_TWO_POINTS : ORDER_ROUTE_PREVIEW_MAX_ZOOM_MULTI_POINTS,
|
|
179
|
+
};
|
|
180
|
+
const routingControl = await this.mapManager.replaceRoutingControl(this.coordinates, this.routingControl, {
|
|
181
|
+
engine: this.routeEngine.getDisplayEngine('osrm'),
|
|
182
|
+
color: statusColor,
|
|
183
|
+
orderId,
|
|
184
|
+
status: routeStatus,
|
|
185
|
+
fitOptions,
|
|
186
|
+
places: this.places,
|
|
187
|
+
markerWaypoints: this.coordinates,
|
|
188
|
+
polylineOptions: {
|
|
189
|
+
color: statusColor,
|
|
190
|
+
weight: routeStyles.at(-1)?.weight ?? 4,
|
|
191
|
+
opacity: routeStyles.at(-1)?.opacity ?? 0.85,
|
|
192
|
+
styles: routeStyles,
|
|
193
|
+
},
|
|
194
|
+
createMarker: (_waypoint, index) => {
|
|
195
|
+
const routePoint = this.routePoints[index];
|
|
196
|
+
return buildRoutePointMarkerPresentation(routePoint, routeColor);
|
|
197
|
+
},
|
|
135
198
|
onRouteFound: (route) => this.setRoute(route),
|
|
136
199
|
removeOptions: {
|
|
137
|
-
filter: (
|
|
200
|
+
filter: (handle) => handle?.tag === this.args.resource.driver_assigned?.id,
|
|
138
201
|
},
|
|
202
|
+
tag: this.args.resource.driver_assigned?.id,
|
|
139
203
|
});
|
|
140
204
|
|
|
141
205
|
this.routingControl = routingControl;
|
|
@@ -166,9 +230,10 @@ export default class OrderFormRouteComponent extends Component {
|
|
|
166
230
|
const payload = order.payload;
|
|
167
231
|
const waypoints = this.args.resource.payload.waypoints;
|
|
168
232
|
const coordinates = this.coordinates.map((coord) => coord.reverse());
|
|
233
|
+
const service = this.routeEngine.getOptimizationEngine('osrm');
|
|
169
234
|
|
|
170
235
|
try {
|
|
171
|
-
const result = yield this.
|
|
236
|
+
const result = yield this.routeOptimization.optimize(service, {
|
|
172
237
|
context: 'create_order',
|
|
173
238
|
order,
|
|
174
239
|
payload,
|
|
@@ -193,7 +258,10 @@ export default class OrderFormRouteComponent extends Component {
|
|
|
193
258
|
}
|
|
194
259
|
|
|
195
260
|
@action setOptimizedRoute(route, trip, waypoints, engine = 'osrm') {
|
|
196
|
-
let summary = {
|
|
261
|
+
let summary = {
|
|
262
|
+
totalDistance: trip?.distance ?? trip?.totalDistance ?? 0,
|
|
263
|
+
totalTime: trip?.duration ?? trip?.totalTime ?? 0,
|
|
264
|
+
};
|
|
197
265
|
let payload = {
|
|
198
266
|
optimized: true,
|
|
199
267
|
coordinates: route,
|
|
@@ -9,7 +9,7 @@ export default class OrderFormComponent extends Component {
|
|
|
9
9
|
@service store;
|
|
10
10
|
@service orderConfigActions;
|
|
11
11
|
@service customFieldsRegistry;
|
|
12
|
-
@service
|
|
12
|
+
@service mapManager;
|
|
13
13
|
@service currentUser;
|
|
14
14
|
@tracked customFields;
|
|
15
15
|
|
|
@@ -47,7 +47,7 @@ export default class OrderFormComponent extends Component {
|
|
|
47
47
|
debug('Unable to load and set driver vehicle: ' + err.message);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
this.
|
|
50
|
+
this.mapManager.focusResource(driver, 18);
|
|
51
51
|
// if (this.args.resource.is_route_optimized) {
|
|
52
52
|
// this.optimizeRoute.perform();
|
|
53
53
|
// }
|
|
@@ -1,171 +1,233 @@
|
|
|
1
1
|
<div class="order-route-editor" ...attributes>
|
|
2
|
-
<div class="flex flex-row justify-between mb-
|
|
3
|
-
<div
|
|
4
|
-
<
|
|
2
|
+
<div class="flex flex-row items-start justify-between mb-4">
|
|
3
|
+
<div>
|
|
4
|
+
<div class="text-sm font-semibold dark:text-gray-100">Route Structure</div>
|
|
5
|
+
<div class="text-xs text-gray-400 mt-1">{{this.routeSummary}}</div>
|
|
5
6
|
</div>
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
7
|
+
|
|
8
|
+
<div class="flex flex-wrap justify-end gap-2">
|
|
9
|
+
{{#if (gt this.routeOptimization.availableEngines.length 1)}}
|
|
10
|
+
<RouteOptimizationEngineSelectButton
|
|
11
|
+
@onClick={{perform this.optimizeRouteWithService}}
|
|
12
|
+
@isLoading={{this.optimizeRouteWithService.isRunning}}
|
|
13
|
+
@size="sm"
|
|
14
|
+
@disabled={{not this.canOptimizeRoute}}
|
|
15
|
+
/>
|
|
16
|
+
{{else}}
|
|
17
|
+
<Button
|
|
18
|
+
@type="magic"
|
|
19
|
+
@icon="magic"
|
|
20
|
+
@text={{t "order.fields.optimize-route"}}
|
|
21
|
+
@size="sm"
|
|
22
|
+
@onClick={{perform this.optimizeRoute}}
|
|
23
|
+
@permission="fleet-ops optimize order"
|
|
24
|
+
@helpText={{t "order.fields.optimize-route-help-text"}}
|
|
25
|
+
@disabled={{not this.canOptimizeRoute}}
|
|
26
|
+
@isLoading={{this.optimizeRoute.isRunning}}
|
|
27
|
+
/>
|
|
24
28
|
{{/if}}
|
|
29
|
+
|
|
30
|
+
<Button @icon="map-marked-alt" @text={{t "modals.order-route-form.add"}} @size="sm" @onClick={{this.addWaypoint}} @helpText={{t "modals.order-route-form.add-text"}} />
|
|
25
31
|
</div>
|
|
26
32
|
</div>
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
<
|
|
30
|
-
<div
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
{{waypoint-label (add index 1)}}
|
|
36
|
-
</label>
|
|
37
|
-
</div>
|
|
38
|
-
|
|
39
|
-
<div class="flex-1 px-4">
|
|
40
|
-
<ModelSelect
|
|
41
|
-
@modelName="place"
|
|
42
|
-
@selectedModel={{waypoint}}
|
|
43
|
-
@placeholder={{concat (t "order.fields.select-waypoint") " " (add index 1)}}
|
|
44
|
-
@triggerClass="form-select form-input truncate max-w-300px"
|
|
45
|
-
@infiniteScroll={{false}}
|
|
46
|
-
@customSearchEndpoint="places/search"
|
|
47
|
-
@query={{hash geo=true latitude=this.latitude longitude=this.longitude}}
|
|
48
|
-
@renderInPlace={{true}}
|
|
49
|
-
@onChange={{fn this.setWaypointPlace index}}
|
|
50
|
-
as |model|
|
|
51
|
-
>
|
|
52
|
-
{{model.address}}
|
|
53
|
-
</ModelSelect>
|
|
54
|
-
|
|
55
|
-
{{#unless waypoint.isNew}}
|
|
56
|
-
{{#if waypoint.hasInvalidCoordinates}}
|
|
57
|
-
<div class="leading-5 text-sm text-red-400 mt-2">
|
|
58
|
-
<FaIcon @icon="exclamation-triangle" class="mr-1" />
|
|
59
|
-
{{t "modals.order-route-form.invalid"}}
|
|
60
|
-
</div>
|
|
61
|
-
{{/if}}
|
|
62
|
-
{{/unless}}
|
|
63
|
-
</div>
|
|
64
|
-
|
|
65
|
-
<div class="flex items-center space-x-2">
|
|
66
|
-
<Button @icon="edit" @size="sm" @onClick={{fn this.placeActions.modal.edit waypoint}} />
|
|
67
|
-
<Button @type="danger" @icon="trash" @iconPrefix="fas" @size="sm" @onClick={{fn this.removeWaypoint waypoint}} />
|
|
68
|
-
</div>
|
|
34
|
+
<div class="fleetops-route-editor-section mb-3">
|
|
35
|
+
<div class="fleetops-route-editor-section__header">
|
|
36
|
+
<div class="fleetops-route-editor-section__title-group">
|
|
37
|
+
<span class="fleetops-route-stop-badge fleetops-route-editor-section__badge" style={{this.pickupStop.badgeStyle}}>{{this.pickupStop.label}}</span>
|
|
38
|
+
<div>
|
|
39
|
+
<div class="fleetops-route-editor-section__title">Pickup</div>
|
|
40
|
+
<div class="fleetops-route-editor-section__subtitle">Route starting location</div>
|
|
69
41
|
</div>
|
|
70
42
|
</div>
|
|
71
|
-
</DragSortList>
|
|
72
|
-
{{else}}
|
|
73
|
-
<div class="grid grid-cols-1 gap-3 text-xs dark:text-gray-100">
|
|
74
|
-
<div class="input-group">
|
|
75
|
-
<div class="flex items-center justify-between mb-2">
|
|
76
|
-
<label class="mb-0i">{{t "modals.order-route-form.pickup"}}</label>
|
|
77
|
-
|
|
78
|
-
{{#if @resource.payload.pickup}}
|
|
79
|
-
<Button @text={{t "modals.order-route-form.edit-address"}} @icon="edit" @size="xs" @onClick={{fn this.placeActions.modal.edit @resource.payload.pickup}} />
|
|
80
|
-
{{/if}}
|
|
81
|
-
</div>
|
|
82
43
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
@
|
|
86
|
-
@
|
|
87
|
-
@triggerClass="form-select form-input"
|
|
88
|
-
@infiniteScroll={{false}}
|
|
89
|
-
@customSearchEndpoint="places/search"
|
|
90
|
-
@query={{hash geo=true latitude=this.latitude longitude=this.longitude}}
|
|
91
|
-
@renderInPlace={{true}}
|
|
92
|
-
@onChange={{fn this.setPayloadPlace "pickup"}}
|
|
93
|
-
as |model|
|
|
94
|
-
>
|
|
95
|
-
{{model.address}}
|
|
96
|
-
</ModelSelect>
|
|
97
|
-
|
|
98
|
-
{{#if @resource.payload.pickup.hasInvalidCoordinates}}
|
|
99
|
-
<div class="leading-5 text-sm text-red-400 mt-2">
|
|
100
|
-
<FaIcon @icon="exclamation-triangle" class="mr-1" />
|
|
101
|
-
{{t "modals.order-route-form.invalid"}}
|
|
102
|
-
</div>
|
|
44
|
+
<div class="flex items-center gap-2">
|
|
45
|
+
{{#if @resource.payload.pickup}}
|
|
46
|
+
<Button @text={{t "modals.order-route-form.edit-address"}} @icon="edit" @size="xs" @onClick={{fn this.placeActions.modal.edit @resource.payload.pickup}} />
|
|
47
|
+
<Button @type="danger" @icon="trash" @iconPrefix="fas" @size="xs" @onClick={{fn this.clearPayloadPlace "pickup"}} />
|
|
103
48
|
{{/if}}
|
|
104
49
|
</div>
|
|
50
|
+
</div>
|
|
105
51
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
52
|
+
<div class="fleetops-route-editor-section__body">
|
|
53
|
+
<ModelSelect
|
|
54
|
+
@modelName="place"
|
|
55
|
+
@selectedModel={{@resource.payload.pickup}}
|
|
56
|
+
@placeholder={{t "modals.order-route-form.select-pickup"}}
|
|
57
|
+
@triggerClass="form-select form-input"
|
|
58
|
+
@infiniteScroll={{false}}
|
|
59
|
+
@customSearchEndpoint="places/search"
|
|
60
|
+
@renderInPlace={{true}}
|
|
61
|
+
@onChange={{fn this.setPayloadPlace "pickup"}}
|
|
62
|
+
as |place|
|
|
63
|
+
>
|
|
64
|
+
<Place::Address @place={{place}} />
|
|
65
|
+
</ModelSelect>
|
|
66
|
+
|
|
67
|
+
{{#if @resource.payload.pickup.hasInvalidCoordinates}}
|
|
68
|
+
<div class="leading-5 text-sm text-red-400 mt-2">
|
|
69
|
+
<FaIcon @icon="exclamation-triangle" class="mr-1" />
|
|
70
|
+
{{t "modals.order-route-form.invalid"}}
|
|
71
|
+
</div>
|
|
72
|
+
{{/if}}
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
109
75
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
76
|
+
<div class="fleetops-route-editor-section mb-3">
|
|
77
|
+
<div class="fleetops-route-editor-section__header">
|
|
78
|
+
<div class="fleetops-route-editor-section__title-group">
|
|
79
|
+
<span class="fleetops-route-stop-badge fleetops-route-editor-section__badge" style={{this.stopsSectionBadgeStyle}}>{{this.intermediateWaypoints.length}}</span>
|
|
80
|
+
<div>
|
|
81
|
+
<div class="fleetops-route-editor-section__title">Intermediate Stops</div>
|
|
82
|
+
<div class="fleetops-route-editor-section__subtitle">Drag to reorder stops between the fixed endpoints</div>
|
|
113
83
|
</div>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div class="text-xs text-gray-400">{{this.intermediateWaypoints.length}} configured</div>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<div class="fleetops-route-editor-section__body">
|
|
90
|
+
{{#if this.intermediateWaypoints.length}}
|
|
91
|
+
<DragSortList class="overflow-visible" @items={{this.intermediateWaypoints}} @dragEndAction={{this.sortWaypoints}} as |waypoint index|>
|
|
92
|
+
{{#let (get this.intermediateRouteStops index) as |stop|}}
|
|
93
|
+
<div id={{concat "waypoint_" (add index 1)}} class="fleetops-route-editor-stop-row">
|
|
94
|
+
<div class="fleetops-route-editor-stop-row__grip cursor-move">
|
|
95
|
+
<FaIcon @icon="grip-lines" @prefix="fas" class="text-gray-400" />
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<div class="fleetops-route-editor-stop-row__label">
|
|
99
|
+
<span class="fleetops-route-stop-badge fleetops-route-editor-stop-row__badge" style={{stop.badgeStyle}}>{{stop.label}}</span>
|
|
100
|
+
<span class="fleetops-route-editor-stop-row__title">{{stop.title}}</span>
|
|
101
|
+
</div>
|
|
114
102
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
103
|
+
<div class="fleetops-route-editor-stop-row__field">
|
|
104
|
+
{{#let (or waypoint.place waypoint) as |waypointPlace|}}
|
|
105
|
+
<ModelSelect
|
|
106
|
+
@modelName="place"
|
|
107
|
+
@selectedModel={{waypointPlace}}
|
|
108
|
+
@placeholder={{concat (t "order.fields.select-waypoint") " " (add index 1)}}
|
|
109
|
+
@triggerClass="form-select form-input truncate"
|
|
110
|
+
@infiniteScroll={{false}}
|
|
111
|
+
@customSearchEndpoint="places/search"
|
|
112
|
+
@renderInPlace={{true}}
|
|
113
|
+
@onChange={{fn this.setWaypointPlace index}}
|
|
114
|
+
as |place|
|
|
115
|
+
>
|
|
116
|
+
<Place::Address @place={{place}} />
|
|
117
|
+
</ModelSelect>
|
|
118
|
+
|
|
119
|
+
{{#if waypointPlace.hasInvalidCoordinates}}
|
|
120
|
+
<div class="leading-5 text-sm text-red-400 mt-2">
|
|
121
|
+
<FaIcon @icon="exclamation-triangle" class="mr-1" />
|
|
122
|
+
{{t "modals.order-route-form.invalid"}}
|
|
123
|
+
</div>
|
|
124
|
+
{{/if}}
|
|
125
|
+
{{/let}}
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<div class="fleetops-route-editor-stop-row__actions">
|
|
129
|
+
{{#let (or waypoint.place waypoint) as |waypointPlace|}}
|
|
130
|
+
{{#if waypointPlace}}
|
|
131
|
+
<Button @icon="edit" @size="sm" @onClick={{fn this.placeActions.modal.edit waypointPlace}} />
|
|
132
|
+
{{/if}}
|
|
133
|
+
{{/let}}
|
|
134
|
+
<Button @type="danger" @icon="trash" @iconPrefix="fas" @size="sm" @onClick={{fn this.removeWaypoint waypoint}} />
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
{{/let}}
|
|
138
|
+
</DragSortList>
|
|
139
|
+
{{else}}
|
|
140
|
+
<div class="fleetops-route-editor-empty-state">
|
|
141
|
+
<div class="text-sm dark:text-gray-100">No intermediate stops yet.</div>
|
|
142
|
+
<div class="text-xs text-gray-400 mt-1">Add a stop to build a mixed route between pickup and dropoff.</div>
|
|
143
|
+
</div>
|
|
144
|
+
{{/if}}
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<div class="fleetops-route-editor-section mb-3">
|
|
149
|
+
<div class="fleetops-route-editor-section__header">
|
|
150
|
+
<div class="fleetops-route-editor-section__title-group">
|
|
151
|
+
<span class="fleetops-route-stop-badge fleetops-route-editor-section__badge" style={{this.dropoffStop.badgeStyle}}>{{this.dropoffStop.label}}</span>
|
|
152
|
+
<div>
|
|
153
|
+
<div class="fleetops-route-editor-section__title">Dropoff</div>
|
|
154
|
+
<div class="fleetops-route-editor-section__subtitle">Route ending location</div>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<div class="flex items-center gap-2">
|
|
159
|
+
{{#if @resource.payload.dropoff}}
|
|
160
|
+
<Button @text={{t "modals.order-route-form.edit-address"}} @icon="edit" @size="xs" @onClick={{fn this.placeActions.modal.edit @resource.payload.dropoff}} />
|
|
161
|
+
<Button @type="danger" @icon="trash" @iconPrefix="fas" @size="xs" @onClick={{fn this.clearPayloadPlace "dropoff"}} />
|
|
135
162
|
{{/if}}
|
|
136
163
|
</div>
|
|
164
|
+
</div>
|
|
137
165
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
166
|
+
<div class="fleetops-route-editor-section__body">
|
|
167
|
+
<ModelSelect
|
|
168
|
+
@modelName="place"
|
|
169
|
+
@selectedModel={{@resource.payload.dropoff}}
|
|
170
|
+
@placeholder={{t "modals.order-route-form.select-dropoff"}}
|
|
171
|
+
@triggerClass="form-select form-input"
|
|
172
|
+
@infiniteScroll={{false}}
|
|
173
|
+
@customSearchEndpoint="places/search"
|
|
174
|
+
@renderInPlace={{true}}
|
|
175
|
+
@onChange={{fn this.setPayloadPlace "dropoff"}}
|
|
176
|
+
as |place|
|
|
177
|
+
>
|
|
178
|
+
<Place::Address @place={{place}} />
|
|
179
|
+
</ModelSelect>
|
|
141
180
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
181
|
+
{{#if @resource.payload.dropoff.hasInvalidCoordinates}}
|
|
182
|
+
<div class="leading-5 text-sm text-red-400 mt-2">
|
|
183
|
+
<FaIcon @icon="exclamation-triangle" class="mr-1" />
|
|
184
|
+
{{t "modals.order-route-form.invalid"}}
|
|
145
185
|
</div>
|
|
186
|
+
{{/if}}
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
146
189
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
{{
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
{{#if @resource.payload.return.hasInvalidCoordinates}}
|
|
163
|
-
<div class="leading-5 text-sm text-red-400 mt-2">
|
|
164
|
-
<FaIcon @icon="exclamation-triangle" class="mr-1" />
|
|
165
|
-
{{t "modals.order-route-form.invalid"}}
|
|
166
|
-
</div>
|
|
190
|
+
<div class="fleetops-route-editor-section">
|
|
191
|
+
<div class="fleetops-route-editor-section__header">
|
|
192
|
+
<div class="fleetops-route-editor-section__title-group">
|
|
193
|
+
<span class="fleetops-route-stop-badge fleetops-route-editor-section__badge" style={{this.returnStop.badgeStyle}}>{{this.returnStop.label}}</span>
|
|
194
|
+
<div>
|
|
195
|
+
<div class="fleetops-route-editor-section__title">Return</div>
|
|
196
|
+
<div class="fleetops-route-editor-section__subtitle">Optional fallback destination</div>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
<div class="flex items-center gap-2">
|
|
201
|
+
{{#if @resource.payload.return}}
|
|
202
|
+
<Button @text={{t "modals.order-route-form.edit-address"}} @icon="edit" @size="xs" @onClick={{fn this.placeActions.modal.edit @resource.payload.return}} />
|
|
203
|
+
<Button @type="danger" @icon="trash" @iconPrefix="fas" @size="xs" @onClick={{fn this.clearPayloadPlace "return"}} />
|
|
167
204
|
{{/if}}
|
|
168
205
|
</div>
|
|
169
206
|
</div>
|
|
170
|
-
|
|
171
|
-
|
|
207
|
+
|
|
208
|
+
<div class="fleetops-route-editor-section__body">
|
|
209
|
+
<ModelSelect
|
|
210
|
+
@modelName="place"
|
|
211
|
+
@selectedModel={{@resource.payload.return}}
|
|
212
|
+
@placeholder={{t "modals.order-route-form.select-return"}}
|
|
213
|
+
@triggerClass="form-select form-input"
|
|
214
|
+
@infiniteScroll={{false}}
|
|
215
|
+
@customSearchEndpoint="places/search"
|
|
216
|
+
@renderInPlace={{true}}
|
|
217
|
+
@onChange={{fn this.setPayloadPlace "return"}}
|
|
218
|
+
as |place|
|
|
219
|
+
>
|
|
220
|
+
<Place::Address @place={{place}} />
|
|
221
|
+
</ModelSelect>
|
|
222
|
+
|
|
223
|
+
{{#if @resource.payload.return.hasInvalidCoordinates}}
|
|
224
|
+
<div class="leading-5 text-sm text-red-400 mt-2">
|
|
225
|
+
<FaIcon @icon="exclamation-triangle" class="mr-1" />
|
|
226
|
+
{{t "modals.order-route-form.invalid"}}
|
|
227
|
+
</div>
|
|
228
|
+
{{/if}}
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
<Spacer @height="160px" />
|
|
233
|
+
</div>
|