@fleetbase/fleetops-engine 0.6.46 → 0.6.48
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/routing-settings.hbs +71 -0
- package/addon/components/admin/routing-settings.js +124 -0
- package/addon/components/customer/orders.js +1 -1
- package/addon/components/display-place.hbs +1 -1
- package/addon/components/map/order-list-overlay/order.hbs +18 -3
- package/addon/components/map/toolbar.hbs +2 -2
- package/addon/components/map/toolbar.js +10 -2
- package/addon/components/orchestrator/phase-builder.hbs +28 -5
- package/addon/components/orchestrator/phase-builder.js +21 -1
- package/addon/components/orchestrator/resource-panel.hbs +19 -1
- package/addon/components/orchestrator/resource-panel.js +4 -0
- package/addon/components/orchestrator-workbench.js +3 -1
- package/addon/components/order/details/purchase-rate.hbs +19 -22
- package/addon/components/order/details/route.hbs +26 -1
- package/addon/components/order/details/route.js +50 -0
- package/addon/components/order/details/tracking.hbs +171 -27
- package/addon/components/order/details/tracking.js +457 -0
- package/addon/components/order/form/details.hbs +1 -2
- package/addon/components/order/form/details.js +6 -0
- package/addon/components/order/form/route.js +9 -1
- package/addon/components/order/kanban-card.hbs +18 -3
- package/addon/components/order-list-overlay/order.hbs +18 -3
- package/addon/components/order-progress-card.hbs +21 -6
- package/addon/components/order-tracking-lookup.hbs +7 -7
- package/addon/components/order-tracking-lookup.js +1 -1
- package/addon/components/route-list.hbs +78 -8
- package/addon/components/route-list.js +48 -6
- package/addon/components/tracking-stop-progress.hbs +45 -0
- package/addon/components/tracking-stop-progress.js +79 -0
- package/addon/controllers/settings/payments/index.js +1 -1
- package/addon/controllers/settings/routing.js +98 -1
- package/addon/helpers/format-duration.js +38 -0
- package/addon/routes/application.js +6 -0
- package/addon/services/order-list-overlay.js +2 -2
- package/addon/styles/fleetops-engine.css +733 -0
- package/addon/templates/settings/routing.hbs +60 -2
- package/app/components/tracking-stop-progress.js +1 -0
- package/app/helpers/format-duration.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +3 -3
- package/pnpm-workspace.yaml +7 -0
- package/server/config/fleetops.php +32 -11
- package/server/migrations/2026_05_13_000001_repair_customer_contact_user_types.php +86 -0
- package/server/seeders/VroomOrchestrationSeeder.php +402 -0
- package/server/src/Http/Controllers/Api/v1/OrchestrationController.php +64 -0
- package/server/src/Http/Controllers/Api/v1/OrderController.php +26 -26
- package/server/src/Http/Controllers/Api/v1/PayloadController.php +10 -10
- package/server/src/Http/Controllers/Api/v1/ServiceQuoteController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/ContactController.php +41 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +6 -42
- package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +69 -15
- package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +119 -12
- package/server/src/Http/Resources/v1/Index/Payload.php +2 -2
- package/server/src/Models/Contact.php +122 -13
- package/server/src/Models/Order.php +1 -1
- package/server/src/Models/Payload.php +8 -8
- package/server/src/Models/Place.php +0 -3
- package/server/src/Models/ServiceRate.php +1 -1
- package/server/src/Observers/ContactObserver.php +8 -0
- package/server/src/Orchestration/Engines/CapacityAllocationEngine.php +220 -0
- package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +330 -38
- package/server/src/Orchestration/Support/OrchestrationPayloadBuilder.php +259 -51
- package/server/src/Providers/FleetOpsServiceProvider.php +27 -0
- package/server/src/Support/LiveOrderQuery.php +75 -0
- package/server/src/Support/Metrics.php +15 -0
- package/server/src/Support/OSRM.php +11 -6
- package/server/src/Support/OrderTracker.php +7 -582
- package/server/src/Support/Utils.php +1 -1
- package/server/src/Tracking/Contracts/TrackingProviderInterface.php +19 -0
- package/server/src/Tracking/Providers/CalculatedTrackingProvider.php +65 -0
- package/server/src/Tracking/Providers/GoogleRoutesTrackingProvider.php +118 -0
- package/server/src/Tracking/Providers/OsrmTrackingProvider.php +71 -0
- package/server/src/Tracking/README.md +25 -0
- package/server/src/Tracking/Support/FakeTrackingProvider.php +42 -0
- package/server/src/Tracking/TrackingContext.php +63 -0
- package/server/src/Tracking/TrackingContextBuilder.php +204 -0
- package/server/src/Tracking/TrackingIntelligenceService.php +180 -0
- package/server/src/Tracking/TrackingOptions.php +49 -0
- package/server/src/Tracking/TrackingProviderCapabilities.php +30 -0
- package/server/src/Tracking/TrackingProviderManager.php +89 -0
- package/server/src/Tracking/TrackingProviderRegistry.php +35 -0
- package/server/src/Tracking/TrackingProviderResult.php +20 -0
- package/server/src/Tracking/TrackingStop.php +59 -0
- package/server/src/routes.php +11 -0
- package/server/tests/CapacityAllocationEngineTest.php +108 -0
- package/server/tests/LiveOrderQueryTest.php +34 -0
- package/server/tests/OrchestrationPayloadBuilderTest.php +132 -0
- package/server/tests/OrchestratorConsumableApiTest.php +74 -0
- package/server/tests/PingDriverEndpointTest.php +19 -0
- package/server/tests/TrackingIntelligenceTest.php +176 -0
- package/server/tests/VroomOrchestrationEngineTest.php +159 -0
- package/translations/en-us.yaml +4 -0
|
@@ -43,8 +43,7 @@
|
|
|
43
43
|
@renderInPlace={{true}}
|
|
44
44
|
@allowClear={{true}}
|
|
45
45
|
@disabled={{or @resource.hasWaypoints (cannot-write @resource)}}
|
|
46
|
-
@onChange={{
|
|
47
|
-
@onChangeId={{fn (mut @resource.customer_uuid)}}
|
|
46
|
+
@onChange={{this.selectCustomer}}
|
|
48
47
|
as |model|
|
|
49
48
|
>
|
|
50
49
|
<div class="flex items-center">
|
|
@@ -25,6 +25,12 @@ export default class OrderFormDetailsComponent extends Component {
|
|
|
25
25
|
this.args.resource.set('driver', null);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
@action selectCustomer(model) {
|
|
29
|
+
this.args.resource.set('customer', model);
|
|
30
|
+
this.args.resource.set('customer_uuid', model?.uuid ?? model?.id ?? null);
|
|
31
|
+
this.args.resource.set('customer_type', model?.customer_type ? `fleet-ops:${model.customer_type}` : null);
|
|
32
|
+
}
|
|
33
|
+
|
|
28
34
|
@task *selectOrderConfig(orderConfig) {
|
|
29
35
|
if (!orderConfig) return;
|
|
30
36
|
this.args.resource.setProperties({
|
|
@@ -113,7 +113,15 @@ export default class OrderFormRouteComponent extends Component {
|
|
|
113
113
|
if (!this.args.resource.payload.waypoints[index]) return;
|
|
114
114
|
|
|
115
115
|
this.args.resource.payload.waypoints[index].place = place;
|
|
116
|
-
this.args.resource.payload.waypoints[index]?.setProperties(
|
|
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
|
+
});
|
|
117
125
|
this.previewRoute();
|
|
118
126
|
}
|
|
119
127
|
|
|
@@ -15,10 +15,25 @@
|
|
|
15
15
|
<div class="order-listing-row-progress mb-2">
|
|
16
16
|
<OrderProgressBar
|
|
17
17
|
@card={{@card}}
|
|
18
|
-
@progress={{@card.tracker_data.
|
|
19
|
-
@firstWaypointCompleted={{@card.tracker_data.
|
|
20
|
-
@lastWaypointCompleted={{@card.tracker_data.
|
|
18
|
+
@progress={{@card.tracker_data.progress.percentage}}
|
|
19
|
+
@firstWaypointCompleted={{gt @card.tracker_data.progress.completed_stops 0}}
|
|
20
|
+
@lastWaypointCompleted={{eq @card.tracker_data.progress.percentage 100}}
|
|
21
21
|
/>
|
|
22
|
+
{{#if @card.tracker_data.insights.is_location_stale}}
|
|
23
|
+
<div class="flex mt-1">
|
|
24
|
+
<Badge @status="warning" @hideStatusDot={{true}}>Stale GPS</Badge>
|
|
25
|
+
</div>
|
|
26
|
+
{{else if @card.tracker_data.fallback_provider}}
|
|
27
|
+
<div class="flex mt-1">
|
|
28
|
+
<Badge @status="warning" @hideStatusDot={{true}}>Fallback ETA</Badge>
|
|
29
|
+
</div>
|
|
30
|
+
{{else if @card.tracker_data.confidence}}
|
|
31
|
+
{{#unless (eq @card.tracker_data.confidence "high")}}
|
|
32
|
+
<div class="flex mt-1">
|
|
33
|
+
<Badge @status="warning" @hideStatusDot={{true}}>Low ETA Confidence</Badge>
|
|
34
|
+
</div>
|
|
35
|
+
{{/unless}}
|
|
36
|
+
{{/if}}
|
|
22
37
|
</div>
|
|
23
38
|
<div class="order-listing-row-body flex flex-row">
|
|
24
39
|
<div class="flex-1 order-listing-row-body-address start">
|
|
@@ -26,10 +26,25 @@
|
|
|
26
26
|
<div class="order-listing-row-progress">
|
|
27
27
|
<OrderProgressBar
|
|
28
28
|
@order={{@order}}
|
|
29
|
-
@progress={{@order.tracker_data.
|
|
30
|
-
@firstWaypointCompleted={{@order.tracker_data.
|
|
31
|
-
@lastWaypointCompleted={{@order.tracker_data.
|
|
29
|
+
@progress={{@order.tracker_data.progress.percentage}}
|
|
30
|
+
@firstWaypointCompleted={{gt @order.tracker_data.progress.completed_stops 0}}
|
|
31
|
+
@lastWaypointCompleted={{eq @order.tracker_data.progress.percentage 100}}
|
|
32
32
|
/>
|
|
33
|
+
{{#if @order.tracker_data.insights.is_location_stale}}
|
|
34
|
+
<div class="flex mt-1">
|
|
35
|
+
<Badge @status="warning" @hideStatusDot={{true}}>Stale GPS</Badge>
|
|
36
|
+
</div>
|
|
37
|
+
{{else if @order.tracker_data.fallback_provider}}
|
|
38
|
+
<div class="flex mt-1">
|
|
39
|
+
<Badge @status="warning" @hideStatusDot={{true}}>Fallback ETA</Badge>
|
|
40
|
+
</div>
|
|
41
|
+
{{else if @order.tracker_data.confidence}}
|
|
42
|
+
{{#unless (eq @order.tracker_data.confidence "high")}}
|
|
43
|
+
<div class="flex mt-1">
|
|
44
|
+
<Badge @status="warning" @hideStatusDot={{true}}>Low ETA Confidence</Badge>
|
|
45
|
+
</div>
|
|
46
|
+
{{/unless}}
|
|
47
|
+
{{/if}}
|
|
33
48
|
</div>
|
|
34
49
|
<div class="order-listing-row-body">
|
|
35
50
|
<div class="order-listing-row-body-address start">
|
|
@@ -16,11 +16,26 @@
|
|
|
16
16
|
<div class="order-progress-card-progress">
|
|
17
17
|
<OrderProgressBar
|
|
18
18
|
@order={{this.order}}
|
|
19
|
-
@progress={{this.order.tracker_data.
|
|
20
|
-
@firstWaypointCompleted={{this.order.tracker_data.
|
|
21
|
-
@lastWaypointCompleted={{this.order.tracker_data.
|
|
19
|
+
@progress={{this.order.tracker_data.progress.percentage}}
|
|
20
|
+
@firstWaypointCompleted={{gt this.order.tracker_data.progress.completed_stops 0}}
|
|
21
|
+
@lastWaypointCompleted={{eq this.order.tracker_data.progress.percentage 100}}
|
|
22
22
|
/>
|
|
23
23
|
</div>
|
|
24
|
+
{{#if this.order.tracker_data.insights.is_location_stale}}
|
|
25
|
+
<div class="flex px-1 mt-1">
|
|
26
|
+
<Badge @status="warning" @hideStatusDot={{true}}>Stale GPS</Badge>
|
|
27
|
+
</div>
|
|
28
|
+
{{else if this.order.tracker_data.fallback_provider}}
|
|
29
|
+
<div class="flex px-1 mt-1">
|
|
30
|
+
<Badge @status="warning" @hideStatusDot={{true}}>Fallback ETA</Badge>
|
|
31
|
+
</div>
|
|
32
|
+
{{else if this.order.tracker_data.confidence}}
|
|
33
|
+
{{#unless (eq this.order.tracker_data.confidence "high")}}
|
|
34
|
+
<div class="flex px-1 mt-1">
|
|
35
|
+
<Badge @status="warning" @hideStatusDot={{true}}>Low ETA Confidence</Badge>
|
|
36
|
+
</div>
|
|
37
|
+
{{/unless}}
|
|
38
|
+
{{/if}}
|
|
24
39
|
<div class="order-progress-card-details">
|
|
25
40
|
<div class="order-progress-card-details-address start">
|
|
26
41
|
{{#if this.order.payload.isMultiDrop}}
|
|
@@ -41,11 +56,11 @@
|
|
|
41
56
|
<div class="grid grid-cols-2 gap-2 mt-1 px-1">
|
|
42
57
|
<div class="flex flex-col text-xs">
|
|
43
58
|
<div class="font-semibold">{{t "order.fields.current-eta"}}:</div>
|
|
44
|
-
<div class="">{{format-duration this.order.tracker_data.
|
|
59
|
+
<div class="">{{format-duration this.order.tracker_data.eta.active_stop_seconds}}</div>
|
|
45
60
|
</div>
|
|
46
61
|
<div class="flex flex-col text-xs">
|
|
47
62
|
<div class="font-semibold">{{t "order.fields.ect"}}:</div>
|
|
48
|
-
<div class="">{{n-a this.order.tracker_data.
|
|
63
|
+
<div class="">{{n-a this.order.tracker_data.eta.completion_at}}</div>
|
|
49
64
|
</div>
|
|
50
65
|
<div class="flex flex-col text-xs">
|
|
51
66
|
<div class="font-semibold">{{t "order.fields.driver"}}:</div>
|
|
@@ -57,7 +72,7 @@
|
|
|
57
72
|
</div>
|
|
58
73
|
<div class="flex flex-col text-xs">
|
|
59
74
|
<div class="font-semibold">{{t "order.fields.current-destination"}}:</div>
|
|
60
|
-
<div class="truncate">{{n-a this.order.tracker_data.
|
|
75
|
+
<div class="truncate">{{n-a this.order.tracker_data.active_stop.address}}</div>
|
|
61
76
|
</div>
|
|
62
77
|
</div>
|
|
63
78
|
</div>
|
|
@@ -90,27 +90,27 @@
|
|
|
90
90
|
<div class="order-progress-card-progress">
|
|
91
91
|
<OrderProgressBar
|
|
92
92
|
@order={{this.order}}
|
|
93
|
-
@progress={{this.order.tracker_data.
|
|
94
|
-
@firstWaypointCompleted={{this.order.tracker_data.
|
|
95
|
-
@lastWaypointCompleted={{this.order.tracker_data.
|
|
93
|
+
@progress={{this.order.tracker_data.progress.percentage}}
|
|
94
|
+
@firstWaypointCompleted={{gt this.order.tracker_data.progress.completed_stops 0}}
|
|
95
|
+
@lastWaypointCompleted={{eq this.order.tracker_data.progress.percentage 100}}
|
|
96
96
|
/>
|
|
97
97
|
</div>
|
|
98
98
|
<div class="grid grid-cols-2 gap-2 mt-1 px-1">
|
|
99
99
|
<div class="flex flex-col text-xs">
|
|
100
100
|
<div class="font-semibold">Current ETA:</div>
|
|
101
|
-
<div class="">{{format-duration this.order.tracker_data.
|
|
101
|
+
<div class="">{{format-duration this.order.tracker_data.eta.active_stop_seconds}}</div>
|
|
102
102
|
</div>
|
|
103
103
|
<div class="flex flex-col text-xs">
|
|
104
104
|
<div class="font-semibold">ECT:</div>
|
|
105
|
-
<div class="">{{n-a this.order.tracker_data.
|
|
105
|
+
<div class="">{{n-a this.order.tracker_data.eta.completion_at}}</div>
|
|
106
106
|
</div>
|
|
107
107
|
<div class="col-span-2 flex flex-col text-xs">
|
|
108
108
|
<div class="font-semibold">Current Destination:</div>
|
|
109
|
-
<div class="truncate">{{n-a this.order.tracker_data.
|
|
109
|
+
<div class="truncate">{{n-a this.order.tracker_data.active_stop.address}}</div>
|
|
110
110
|
</div>
|
|
111
111
|
<div class="col-span-2 flex flex-col text-xs">
|
|
112
112
|
<div class="font-semibold">Next Destination:</div>
|
|
113
|
-
<div class="truncate">{{n-a this.order.tracker_data.
|
|
113
|
+
<div class="truncate">{{n-a this.order.tracker_data.next_stop.address}}</div>
|
|
114
114
|
</div>
|
|
115
115
|
</div>
|
|
116
116
|
</div>
|
|
@@ -50,7 +50,7 @@ export default class OrderTrackingLookupComponent extends Component {
|
|
|
50
50
|
try {
|
|
51
51
|
this.order = yield this.fetch.get('fleet-ops/lookup', { tracking: this.trackingNumber }, { normalizeToEmberData: true, normalizeModelType: 'order' });
|
|
52
52
|
this.urlSearchParams.addParamToCurrentUrl('order', this.order.tracking);
|
|
53
|
-
const driverCurrentLocation = this.order.get('tracker_data.
|
|
53
|
+
const driverCurrentLocation = this.order.get('tracker_data.driver.location');
|
|
54
54
|
if (driverCurrentLocation) {
|
|
55
55
|
this.latitude = driverCurrentLocation.coordinates[1];
|
|
56
56
|
this.longitude = driverCurrentLocation.coordinates[0];
|
|
@@ -2,10 +2,29 @@
|
|
|
2
2
|
{{#if this.firstStop}}
|
|
3
3
|
<div class="order-route-stop {{@routeStopClass}}">
|
|
4
4
|
<div class="order-route-stop-index {{@routeStopIndexClass}}">
|
|
5
|
-
<div
|
|
5
|
+
<div
|
|
6
|
+
class="index-count fleetops-route-stop-badge {{if this.firstStop.active 'fleetops-route-stop-badge--active'}}"
|
|
7
|
+
style={{this.firstStop.badgeStyle}}
|
|
8
|
+
>{{this.firstStop.label}}</div>
|
|
6
9
|
</div>
|
|
7
10
|
<div class="order-route-location {{@routeLocationClass}} dark:text-gray-100">
|
|
8
|
-
<
|
|
11
|
+
<div class="mb-2 flex flex-wrap items-center gap-1.5">
|
|
12
|
+
{{#if this.firstStop.place.status_code}}
|
|
13
|
+
<Badge @status={{this.firstStop.place.status_code}} />
|
|
14
|
+
{{/if}}
|
|
15
|
+
{{#if this.firstStop.completed}}
|
|
16
|
+
<Badge @status="success" @hideStatusDot={{true}}>Completed</Badge>
|
|
17
|
+
{{else if this.firstStop.active}}
|
|
18
|
+
<Badge @status="indigo" @hideStatusDot={{true}}>Current Stop</Badge>
|
|
19
|
+
{{/if}}
|
|
20
|
+
{{#if this.firstStop.etaSeconds}}
|
|
21
|
+
<Badge @status="pending" @hideStatusDot={{true}}><span class="font-bold">ETA:</span> {{format-duration this.firstStop.etaSeconds}}</Badge>
|
|
22
|
+
{{/if}}
|
|
23
|
+
{{#if this.firstStop.etaAt}}
|
|
24
|
+
<Badge @status="pending" @hideStatusDot={{true}}>{{format-date-fns this.firstStop.etaAt "d MMM HH:mm"}}</Badge>
|
|
25
|
+
{{/if}}
|
|
26
|
+
</div>
|
|
27
|
+
<DisplayPlace @place={{this.firstStop.place}} @waypointActions={{@waypointActions}} @hideBadges={{true}} />
|
|
9
28
|
</div>
|
|
10
29
|
</div>
|
|
11
30
|
{{/if}}
|
|
@@ -24,10 +43,26 @@
|
|
|
24
43
|
{{#each this.middleStops as |stop|}}
|
|
25
44
|
<div class="order-route-stop {{@routeStopClass}}">
|
|
26
45
|
<div class="order-route-stop-index {{@routeStopIndexClass}}">
|
|
27
|
-
<div class="index-count fleetops-route-stop-badge" style={{stop.badgeStyle}}>{{stop.label}}</div>
|
|
46
|
+
<div class="index-count fleetops-route-stop-badge {{if stop.active 'fleetops-route-stop-badge--active'}}" style={{stop.badgeStyle}}>{{stop.label}}</div>
|
|
28
47
|
</div>
|
|
29
48
|
<div class="order-route-location {{@routeLocationClass}} dark:text-gray-100">
|
|
30
|
-
<
|
|
49
|
+
<div class="mb-2 flex flex-wrap items-center gap-1.5">
|
|
50
|
+
{{#if stop.place.status_code}}
|
|
51
|
+
<Badge @status={{stop.place.status_code}} />
|
|
52
|
+
{{/if}}
|
|
53
|
+
{{#if stop.completed}}
|
|
54
|
+
<Badge @status="success" @hideStatusDot={{true}}>Completed</Badge>
|
|
55
|
+
{{else if stop.active}}
|
|
56
|
+
<Badge @status="indigo" @hideStatusDot={{true}}>Current Stop</Badge>
|
|
57
|
+
{{/if}}
|
|
58
|
+
{{#if stop.etaSeconds}}
|
|
59
|
+
<Badge @status="pending" @hideStatusDot={{true}}><span class="font-bold">ETA:</span> {{format-duration stop.etaSeconds}}</Badge>
|
|
60
|
+
{{/if}}
|
|
61
|
+
{{#if stop.etaAt}}
|
|
62
|
+
<Badge @status="pending" @hideStatusDot={{true}}>{{format-date-fns stop.etaAt "d MMM HH:mm"}}</Badge>
|
|
63
|
+
{{/if}}
|
|
64
|
+
</div>
|
|
65
|
+
<DisplayPlace @place={{stop.place}} @waypointActions={{@waypointActions}} @hideBadges={{true}} />
|
|
31
66
|
</div>
|
|
32
67
|
</div>
|
|
33
68
|
{{/each}}
|
|
@@ -36,10 +71,26 @@
|
|
|
36
71
|
{{#each this.middleStops as |stop|}}
|
|
37
72
|
<div class="order-route-stop {{@routeStopClass}}">
|
|
38
73
|
<div class="order-route-stop-index {{@routeStopIndexClass}}">
|
|
39
|
-
<div class="index-count fleetops-route-stop-badge" style={{stop.badgeStyle}}>{{stop.label}}</div>
|
|
74
|
+
<div class="index-count fleetops-route-stop-badge {{if stop.active 'fleetops-route-stop-badge--active'}}" style={{stop.badgeStyle}}>{{stop.label}}</div>
|
|
40
75
|
</div>
|
|
41
76
|
<div class="order-route-location {{@routeLocationClass}} dark:text-gray-100">
|
|
42
|
-
<
|
|
77
|
+
<div class="mb-2 flex flex-wrap items-center gap-1.5">
|
|
78
|
+
{{#if stop.place.status_code}}
|
|
79
|
+
<Badge @status={{stop.place.status_code}} />
|
|
80
|
+
{{/if}}
|
|
81
|
+
{{#if stop.completed}}
|
|
82
|
+
<Badge @status="success" @hideStatusDot={{true}}>Completed</Badge>
|
|
83
|
+
{{else if stop.active}}
|
|
84
|
+
<Badge @status="indigo" @hideStatusDot={{true}}>Current Stop</Badge>
|
|
85
|
+
{{/if}}
|
|
86
|
+
{{#if stop.etaSeconds}}
|
|
87
|
+
<Badge @status="pending" @hideStatusDot={{true}}><span class="font-bold">ETA:</span> {{format-duration stop.etaSeconds}}</Badge>
|
|
88
|
+
{{/if}}
|
|
89
|
+
{{#if stop.etaAt}}
|
|
90
|
+
<Badge @status="pending" @hideStatusDot={{true}}>{{format-date-fns stop.etaAt "d MMM HH:mm"}}</Badge>
|
|
91
|
+
{{/if}}
|
|
92
|
+
</div>
|
|
93
|
+
<DisplayPlace @place={{stop.place}} @waypointActions={{@waypointActions}} @hideBadges={{true}} />
|
|
43
94
|
</div>
|
|
44
95
|
</div>
|
|
45
96
|
{{/each}}
|
|
@@ -48,10 +99,29 @@
|
|
|
48
99
|
{{#if this.lastStop}}
|
|
49
100
|
<div class="order-route-stop {{@routeStopClass}}">
|
|
50
101
|
<div class="order-route-stop-index {{@routeStopIndexClass}}">
|
|
51
|
-
<div
|
|
102
|
+
<div
|
|
103
|
+
class="index-count fleetops-route-stop-badge {{if this.lastStop.active 'fleetops-route-stop-badge--active'}}"
|
|
104
|
+
style={{this.lastStop.badgeStyle}}
|
|
105
|
+
>{{this.lastStop.label}}</div>
|
|
52
106
|
</div>
|
|
53
107
|
<div class="order-route-location {{@routeLocationClass}} dark:text-gray-100">
|
|
54
|
-
<
|
|
108
|
+
<div class="mb-2 flex flex-wrap items-center gap-1.5">
|
|
109
|
+
{{#if this.lastStop.place.status_code}}
|
|
110
|
+
<Badge @status={{this.lastStop.place.status_code}} />
|
|
111
|
+
{{/if}}
|
|
112
|
+
{{#if this.lastStop.completed}}
|
|
113
|
+
<Badge @status="success" @hideStatusDot={{true}}>Completed</Badge>
|
|
114
|
+
{{else if this.lastStop.active}}
|
|
115
|
+
<Badge @status="indigo" @hideStatusDot={{true}}>Current Stop</Badge>
|
|
116
|
+
{{/if}}
|
|
117
|
+
{{#if this.lastStop.etaSeconds}}
|
|
118
|
+
<Badge @status="pending" @hideStatusDot={{true}}><span class="font-bold">ETA:</span> {{format-duration this.lastStop.etaSeconds}}</Badge>
|
|
119
|
+
{{/if}}
|
|
120
|
+
{{#if this.lastStop.etaAt}}
|
|
121
|
+
<Badge @status="pending" @hideStatusDot={{true}}>{{format-date-fns this.lastStop.etaAt "d MMM HH:mm"}}</Badge>
|
|
122
|
+
{{/if}}
|
|
123
|
+
</div>
|
|
124
|
+
<DisplayPlace @place={{this.lastStop.place}} @waypointActions={{@waypointActions}} @hideBadges={{true}} />
|
|
55
125
|
</div>
|
|
56
126
|
</div>
|
|
57
127
|
{{/if}}
|
|
@@ -16,12 +16,22 @@ export default class RouteListComponent extends Component {
|
|
|
16
16
|
get routeStops() {
|
|
17
17
|
const routePoints = buildRoutePointsFromPayload(this.args.order?.payload);
|
|
18
18
|
|
|
19
|
-
return routePoints
|
|
20
|
-
routePoint
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
return routePoints
|
|
20
|
+
.map((routePoint) => ({
|
|
21
|
+
routePoint,
|
|
22
|
+
place: routePoint.place,
|
|
23
|
+
badgeStyle: this.badgeStyleForStop(routePoint),
|
|
24
|
+
trackingStop: this.trackingStopFor(routePoint.place),
|
|
25
|
+
routeLeg: this.routeLegFor(routePoint.place),
|
|
26
|
+
...describeRoutePoint(routePoint, this.routeColor),
|
|
27
|
+
}))
|
|
28
|
+
.map((stop) => ({
|
|
29
|
+
...stop,
|
|
30
|
+
etaSeconds: stop.routeLeg?.eta_seconds ?? stop.routeLeg?.duration_in_traffic_s ?? stop.routeLeg?.duration_s ?? this.legacyEtaFor(stop.place),
|
|
31
|
+
etaAt: stop.routeLeg?.eta_at,
|
|
32
|
+
completed: Boolean(stop.trackingStop?.completed),
|
|
33
|
+
active: this.matchesStop(stop.trackingStop, this.args.order?.tracker_data?.active_stop),
|
|
34
|
+
}));
|
|
25
35
|
}
|
|
26
36
|
|
|
27
37
|
get firstStop() {
|
|
@@ -52,6 +62,38 @@ export default class RouteListComponent extends Component {
|
|
|
52
62
|
return `background-color: ${markerColor}; color: ${textColor};`;
|
|
53
63
|
}
|
|
54
64
|
|
|
65
|
+
trackingStopFor(place) {
|
|
66
|
+
const stops = this.args.order?.tracker_data?.stops ?? [];
|
|
67
|
+
|
|
68
|
+
return stops.find((stop) => this.matchesPlace(stop, place)) ?? null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
routeLegFor(place) {
|
|
72
|
+
const legs = this.args.order?.tracker_data?.route?.legs ?? [];
|
|
73
|
+
|
|
74
|
+
return legs.find((leg) => this.matchesPlace(leg.stop, place)) ?? null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
legacyEtaFor(place) {
|
|
78
|
+
return this.args.eta?.[place?.id] ?? this.args.eta?.[place?.uuid] ?? this.args.eta?.[place?.public_id] ?? null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
matchesPlace(stop, place) {
|
|
82
|
+
if (!stop || !place) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return stop.uuid === place.uuid || stop.public_id === place.public_id || stop.id === place.id;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
matchesStop(stop, activeStop) {
|
|
90
|
+
if (!stop || !activeStop) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return stop.uuid === activeStop.uuid || stop.public_id === activeStop.public_id;
|
|
95
|
+
}
|
|
96
|
+
|
|
55
97
|
@action toggleWaypointsCollapse() {
|
|
56
98
|
this.isWaypointsCollapsed = !this.isWaypointsCollapsed;
|
|
57
99
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<div class="tracking-stop-progress" ...attributes>
|
|
2
|
+
<div class="tracking-stop-progress__header">
|
|
3
|
+
<div class="tracking-stop-progress__title">Between Stops</div>
|
|
4
|
+
<div class="tracking-stop-progress__count">
|
|
5
|
+
<span class="font-semibold text-gray-800 dark:text-gray-100">{{this.completedCount}}</span>
|
|
6
|
+
/
|
|
7
|
+
{{this.totalCount}}
|
|
8
|
+
stops
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div class="tracking-stop-progress__scroller">
|
|
13
|
+
<div class="tracking-stop-progress__rail">
|
|
14
|
+
{{#each this.stops as |stop|}}
|
|
15
|
+
<div class="tracking-stop-progress__stop">
|
|
16
|
+
<div class="tracking-stop-progress__line">
|
|
17
|
+
{{#unless stop.isFirst}}
|
|
18
|
+
<div class="tracking-stop-progress__connector"></div>
|
|
19
|
+
{{/unless}}
|
|
20
|
+
|
|
21
|
+
<div
|
|
22
|
+
class="tracking-stop-progress__dot
|
|
23
|
+
{{if stop.completed 'tracking-stop-progress__dot--done'}}
|
|
24
|
+
{{if stop.active 'tracking-stop-progress__dot--active'}}
|
|
25
|
+
{{if stop.pending 'tracking-stop-progress__dot--pending'}}"
|
|
26
|
+
title={{stop.title}}
|
|
27
|
+
>
|
|
28
|
+
{{stop.label}}
|
|
29
|
+
</div>
|
|
30
|
+
<Attach::Tooltip @class="clean" @animation="scale" @placement="top">
|
|
31
|
+
<InputInfo class="max-w-xs">
|
|
32
|
+
<div class="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">{{stop.title}}</div>
|
|
33
|
+
<DisplayPlace @place={{stop.place}} @addressClass="mt-1 text-xs leading-4 text-gray-900 dark:text-gray-100 not-italic" @hideBadges={{true}} />
|
|
34
|
+
</InputInfo>
|
|
35
|
+
</Attach::Tooltip>
|
|
36
|
+
|
|
37
|
+
{{#unless stop.isLast}}
|
|
38
|
+
<div class="tracking-stop-progress__connector"></div>
|
|
39
|
+
{{/unless}}
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
{{/each}}
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
|
|
3
|
+
export default class TrackingStopProgressComponent extends Component {
|
|
4
|
+
get stops() {
|
|
5
|
+
const stops = this.args.stops ?? [];
|
|
6
|
+
const activeStop = this.args.activeStop;
|
|
7
|
+
|
|
8
|
+
return stops.map((stop, index) => {
|
|
9
|
+
const isActive = this.matches(stop, activeStop);
|
|
10
|
+
const completed = Boolean(stop.completed);
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
...stop,
|
|
14
|
+
index: index + 1,
|
|
15
|
+
isFirst: index === 0,
|
|
16
|
+
isLast: index === stops.length - 1,
|
|
17
|
+
label: this.labelFor(stop, index),
|
|
18
|
+
title: this.titleFor(stop, index),
|
|
19
|
+
locationLabel: this.locationLabelFor(stop, index),
|
|
20
|
+
place: this.placeFor(stop),
|
|
21
|
+
completed,
|
|
22
|
+
active: isActive,
|
|
23
|
+
pending: !completed && !isActive,
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get completedCount() {
|
|
29
|
+
return this.stops.filter((stop) => stop.completed).length;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get totalCount() {
|
|
33
|
+
return this.stops.length;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
labelFor(stop, index) {
|
|
37
|
+
if (stop.type === 'pickup') {
|
|
38
|
+
return 'P';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (stop.type === 'dropoff') {
|
|
42
|
+
return 'D';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return String(index + 1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
titleFor(stop, index) {
|
|
49
|
+
if (stop.type === 'pickup') {
|
|
50
|
+
return 'Pickup';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (stop.type === 'dropoff') {
|
|
54
|
+
return 'Dropoff';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return `Stop ${index + 1}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
locationLabelFor(stop, index) {
|
|
61
|
+
return stop.city || stop.name || stop.address || this.titleFor(stop, index);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
placeFor(stop) {
|
|
65
|
+
return {
|
|
66
|
+
...stop,
|
|
67
|
+
street1: stop.street1 || stop.address || stop.name,
|
|
68
|
+
country_name: stop.country_name || stop.country,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
matches(stop, activeStop) {
|
|
73
|
+
if (!stop || !activeStop) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return stop.uuid === activeStop.uuid || stop.public_id === activeStop.public_id;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -46,7 +46,7 @@ export default class SettingsPaymentsIndexController extends Controller {
|
|
|
46
46
|
];
|
|
47
47
|
|
|
48
48
|
get isStripeEnabled() {
|
|
49
|
-
return
|
|
49
|
+
return !isEmpty(config.stripe.publishableKey);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
@task *lookupStripeConnectAccount() {
|