@fleetbase/fleetops-engine 0.6.50 → 0.6.51
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/README.md +185 -67
- package/addon/components/contact/panel-header.hbs +11 -48
- package/addon/components/customer/details.hbs +1 -2
- package/addon/components/customer/form.hbs +63 -51
- package/addon/components/customer/form.js +27 -1
- package/addon/components/customer/orders.hbs +48 -0
- package/addon/components/customer/orders.js +67 -5
- package/addon/components/driver/form.hbs +1 -1
- package/addon/components/home/getting-started-guidance.hbs +113 -0
- package/addon/components/home/getting-started-guidance.js +107 -0
- package/addon/components/issue/details/comments.hbs +3 -0
- package/addon/components/issue/details/comments.js +3 -0
- package/addon/components/issue/details/correspondence.hbs +9 -0
- package/addon/components/issue/details/correspondence.js +83 -0
- package/addon/components/issue/details/documents.hbs +69 -0
- package/addon/components/issue/details/documents.js +85 -0
- package/addon/components/issue/details.hbs +220 -59
- package/addon/components/issue/details.js +245 -0
- package/addon/components/issue/form.hbs +19 -1
- package/addon/components/issue/panel-header.hbs +43 -0
- package/addon/components/issue/panel-header.js +22 -0
- package/addon/components/issue/timeline.hbs +40 -0
- package/addon/components/issue/timeline.js +45 -0
- package/addon/components/leaflet-tracking-marker.js +3 -0
- package/addon/components/map/drawer/position-listing.js +3 -0
- package/addon/components/map/leaflet-live-map.hbs +2 -2
- package/addon/components/map/leaflet-live-map.js +17 -0
- package/addon/components/map/order-list-overlay.hbs +4 -4
- package/addon/components/modals/convert-customer-to-vendor.hbs +30 -0
- package/addon/components/modals/convert-customer-to-vendor.js +55 -0
- package/addon/components/modals/issue-assign.hbs +20 -0
- package/addon/components/modals/issue-assign.js +49 -0
- package/addon/components/modals/issue-close.hbs +30 -0
- package/addon/components/modals/issue-close.js +73 -0
- package/addon/components/modals/issue-status.hbs +20 -0
- package/addon/components/modals/issue-status.js +43 -0
- package/addon/components/modals/reset-customer-credentials.js +12 -6
- package/addon/components/modals/update-order-activity.hbs +51 -17
- package/addon/components/modals/update-order-activity.js +59 -5
- package/addon/components/order/details/activity.js +2 -2
- package/addon/components/order/details/proof.hbs +40 -38
- package/addon/components/order/details/proof.js +31 -4
- package/addon/components/order/details.hbs +5 -5
- package/addon/components/order/details.js +19 -1
- package/addon/components/order/header.hbs +1 -1
- package/addon/components/route-list.hbs +0 -12
- package/addon/components/service-rate/details.hbs +1 -1
- package/addon/components/service-rate/form.hbs +1 -1
- package/addon/components/service-rate/form.js +4 -0
- package/addon/components/vendor/personnel-manager.hbs +166 -0
- package/addon/components/vendor/personnel-manager.js +143 -0
- package/addon/components/widget/live-fleet.hbs +1 -0
- package/addon/components/widget/live-fleet.js +2 -7
- package/addon/components/widget/revenue-trend.hbs +1 -1
- package/addon/components/widget/revenue-trend.js +75 -2
- package/addon/controllers/management/contacts/customers/details.js +31 -13
- package/addon/controllers/management/contacts/customers.js +16 -5
- package/addon/controllers/management/contacts/index/details.js +31 -13
- package/addon/controllers/management/contacts/index.js +7 -0
- package/addon/controllers/management/issues/index/details.js +52 -6
- package/addon/controllers/management/vendors/index/details.js +4 -0
- package/addon/controllers/operations/orders/index/details.js +11 -3
- package/addon/controllers/operations/orders/index.js +1 -1
- package/addon/extension.js +19 -12
- package/addon/helpers/format-duration.js +1 -38
- package/addon/initializers/load-leaflet-assets.js +5 -6
- package/addon/initializers/patch-ember-leaflet-tooltip-layer.js +51 -0
- package/addon/routes/management/issues/index/details.js +1 -1
- package/addon/routes.js +1 -0
- package/addon/services/contact-actions.js +231 -1
- package/addon/services/getting-started.js +44 -0
- package/addon/services/issue-actions.js +71 -0
- package/addon/services/leaflet-map-manager.js +2 -0
- package/addon/services/map-adapter/leaflet.js +19 -0
- package/addon/services/service-areas.js +3 -0
- package/addon/styles/fleetops-engine.css +1036 -0
- package/addon/templates/management/contacts/customers/details.hbs +1 -1
- package/addon/templates/management/contacts/index/details.hbs +1 -1
- package/addon/templates/management/contacts.hbs +2 -2
- package/addon/templates/management/issues/index/details/index.hbs +3 -1
- package/addon/templates/management/issues/index/details.hbs +3 -2
- package/addon/templates/management/vendors/index/details/personnel.hbs +1 -2
- package/addon/templates/operations/orders/index/details/index.hbs +2 -2
- package/addon/templates/operations/orders/index.hbs +1 -1
- package/addon/templates/operations/service-rates/index/edit.hbs +1 -0
- package/addon/templates/operations/service-rates/index/new.hbs +1 -0
- package/addon/templates/settings/payments/onboard.hbs +8 -8
- package/addon/utils/leaflet-draw-namespace-guard.js +22 -0
- package/addon/utils/leaflet-plugin-loader.js +219 -0
- package/addon/utils/setup-customer-portal.js +4 -64
- package/app/components/home/getting-started-guidance.js +1 -0
- package/app/components/issue/details/comments.js +1 -0
- package/app/components/issue/details/correspondence.js +1 -0
- package/app/components/issue/details/documents.js +1 -0
- package/app/components/issue/panel-header.js +1 -0
- package/app/components/modals/convert-customer-to-vendor.js +1 -0
- package/app/components/modals/issue-assign.js +1 -0
- package/app/components/modals/issue-close.js +1 -0
- package/app/components/modals/issue-status.js +1 -0
- package/app/components/vendor/personnel-manager.js +1 -0
- package/app/helpers/format-duration.js +1 -1
- package/app/services/getting-started.js +1 -0
- package/app/utils/leaflet-draw-namespace-guard.js +1 -0
- package/app/utils/leaflet-plugin-loader.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +5 -5
- package/server/migrations/2026_05_31_000001_add_portal_fields_to_vendor_personnels_table.php +30 -0
- package/server/migrations/2026_06_03_000000_ensure_tags_column_on_issues_table.php +31 -0
- package/server/migrations/2026_06_03_000001_add_order_uuid_to_issues_table.php +25 -0
- package/server/migrations/2026_06_04_000001_backfill_order_transaction_subjects.php +39 -0
- package/server/migrations/2026_06_04_000002_backfill_order_transaction_directions.php +24 -0
- package/server/resources/views/mail/customer-credentials.blade.php +16 -13
- package/server/src/Expansions/UserFilterExpansion.php +28 -0
- package/server/src/Http/Controllers/Api/v1/IssueController.php +2 -0
- package/server/src/Http/Controllers/Api/v1/OrderController.php +26 -25
- package/server/src/Http/Controllers/Api/v1/PlaceController.php +10 -58
- package/server/src/Http/Controllers/Internal/v1/ContactController.php +135 -0
- package/server/src/Http/Controllers/Internal/v1/CustomerController.php +116 -6
- package/server/src/Http/Controllers/Internal/v1/GettingStartedController.php +17 -0
- package/server/src/Http/Controllers/Internal/v1/IssueController.php +251 -0
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +172 -97
- package/server/src/Http/Controllers/Internal/v1/PlaceController.php +10 -68
- package/server/src/Http/Controllers/Internal/v1/VendorController.php +89 -0
- package/server/src/Http/Requests/CreateIssueRequest.php +4 -0
- package/server/src/Http/Requests/UpdateIssueRequest.php +4 -0
- package/server/src/Http/Resources/v1/Index/Order.php +12 -9
- package/server/src/Http/Resources/v1/Issue.php +25 -0
- package/server/src/Http/Resources/v1/Order.php +20 -18
- package/server/src/Mail/CustomerCredentialsMail.php +5 -6
- package/server/src/Models/Contact.php +30 -51
- package/server/src/Models/Issue.php +47 -6
- package/server/src/Models/Order.php +33 -6
- package/server/src/Models/ServiceArea.php +1 -1
- package/server/src/Models/VendorPersonnel.php +10 -1
- package/server/src/Models/Zone.php +1 -1
- package/server/src/Observers/PurchaseRateObserver.php +20 -0
- package/server/src/Observers/ZoneObserver.php +56 -0
- package/server/src/Providers/FleetOpsServiceProvider.php +1 -0
- package/server/src/Support/Analytics/TopDrivers.php +9 -1
- package/server/src/Support/GettingStarted.php +144 -0
- package/server/src/Support/PlaceSearch.php +282 -0
- package/server/src/Support/ResolvesOrderServiceStops.php +22 -12
- package/server/src/routes.php +33 -1
- package/server/tests/AnalyticsRoutesTest.php +8 -0
- package/server/tests/CustomerContactIdentitySafetyTest.php +41 -0
- package/server/tests/DriverVehicleStatusDefaultTest.php +2 -2
- package/server/tests/GettingStartedStatusTest.php +21 -0
- package/server/tests/OrderActivityFlowRegressionTest.php +116 -0
- package/server/tests/ServiceAreaZoneApiShapeTest.php +44 -0
- package/translations/ar-ae.yml +9 -0
- package/translations/bg-bg.yaml +9 -0
- package/translations/en-us.yaml +11 -0
- package/translations/es-pa.yaml +9 -0
- package/translations/fr-fr.yaml +9 -0
- package/translations/mn-mn.yaml +9 -0
- package/translations/pt-br.yaml +9 -0
- package/translations/ru-ru.yaml +9 -0
- package/translations/vi-vn.yaml +9 -0
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<div class="text-xs font-semibold uppercase">Updating activity for</div>
|
|
22
22
|
<div class="mt-1 flex items-start justify-between gap-3">
|
|
23
23
|
<div class="min-w-0">
|
|
24
|
-
<div class="truncate text-sm font-semibold">{{or this.currentWaypoint.name this.currentWaypoint.
|
|
24
|
+
<div class="truncate text-sm font-semibold">{{or this.currentWaypoint.name this.currentWaypoint.public_id}}</div>
|
|
25
25
|
<div class="truncate text-xs">{{or (place-address-html this.currentWaypoint showTitle=false) this.currentWaypoint.address this.currentWaypoint.street1}}</div>
|
|
26
26
|
</div>
|
|
27
27
|
{{#if this.currentWaypoint.status_code}}
|
|
@@ -111,24 +111,58 @@
|
|
|
111
111
|
</fieldset>
|
|
112
112
|
|
|
113
113
|
{{#if this.selectedActivityRequiresPod}}
|
|
114
|
-
<div class="mb-
|
|
115
|
-
<div class="
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
114
|
+
<div class="mb-4 rounded-md border border-yellow-300 bg-yellow-50 py-2 text-yellow-900 dark:border-yellow-700 dark:bg-yellow-900 dark:text-yellow-50">
|
|
115
|
+
<div class="flex flex-wrap items-center justify-between gap-2 px-3">
|
|
116
|
+
<div class="min-w-0">
|
|
117
|
+
<div class="text-xs font-semibold uppercase">Photo proof required</div>
|
|
118
|
+
{{#if this.bypassProof}}
|
|
119
|
+
<div class="text-xs text-yellow-800 dark:text-yellow-100">Proof bypass enabled.</div>
|
|
120
|
+
{{else if this.hasUploadedProof}}
|
|
121
|
+
<div class="text-xs text-yellow-800 dark:text-yellow-100">Proof uploaded.</div>
|
|
122
|
+
{{else if this.uploadProofFile.isRunning}}
|
|
123
|
+
<div class="text-xs text-yellow-800 dark:text-yellow-100">Uploading proof...</div>
|
|
124
|
+
{{else}}
|
|
125
|
+
<div class="text-xs text-yellow-800 dark:text-yellow-100">Upload proof before submitting.</div>
|
|
126
|
+
{{/if}}
|
|
127
|
+
</div>
|
|
128
|
+
{{#unless this.bypassProof}}
|
|
129
|
+
<FileUpload @name="photos" @accept={{join "," this.acceptedProofFileTypes}} @multiple={{false}} @onFileAdded={{perform this.uploadProofFile}}>
|
|
130
|
+
<a tabindex={{0}} class="btn btn-magic btn-sm cursor-pointer">
|
|
131
|
+
{{#if this.uploadProofFile.isRunning}}
|
|
132
|
+
<Spinner class="mr-1" />
|
|
133
|
+
Uploading
|
|
134
|
+
{{else}}
|
|
135
|
+
<FaIcon @icon="camera" class="mr-1" />
|
|
136
|
+
Select photo
|
|
137
|
+
{{/if}}
|
|
138
|
+
</a>
|
|
139
|
+
</FileUpload>
|
|
140
|
+
{{/unless}}
|
|
141
|
+
</div>
|
|
142
|
+
<div class="px-3 mt-3 flex flex-wrap items-center justify-between gap-2 border-t border-yellow-50 pt-2 dark:border-yellow-700">
|
|
143
|
+
<Toggle @isToggled={{this.bypassProof}} @onToggle={{this.toggleBypassProof}} @label="Bypass proof of delivery" @labelClass="text-xs" />
|
|
144
|
+
<span class="text-xs text-yellow-800 dark:text-yellow-100">Use only when proof cannot be provided from the console.</span>
|
|
145
|
+
</div>
|
|
123
146
|
{{#if this.proofFiles}}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
<
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
147
|
+
<div class="mt-4 mb-1 px-3 space-x-2">
|
|
148
|
+
{{#each this.proofFiles as |file|}}
|
|
149
|
+
<div class="flex items-center justify-between gap-2 text-xs text-yellow-900 dark:text-yellow-50 border border-yellow-50 dark:border-yellow-700 shadow-sm px-2 py-1.5 rounded-md">
|
|
150
|
+
<div class="text-xs flex items-center space-x-2">
|
|
151
|
+
<FaIcon @icon="file" />
|
|
152
|
+
<span class="truncate">{{file.name}}</span>
|
|
153
|
+
</div>
|
|
154
|
+
<span class="flex-shrink-0 font-semibold">
|
|
155
|
+
{{#if this.hasUploadedProof}}
|
|
156
|
+
Uploaded
|
|
157
|
+
{{else}}
|
|
158
|
+
{{round file.progress}}%
|
|
159
|
+
{{/if}}
|
|
160
|
+
</span>
|
|
161
|
+
</div>
|
|
162
|
+
{{/each}}
|
|
163
|
+
</div>
|
|
130
164
|
{{/if}}
|
|
131
165
|
</div>
|
|
132
166
|
{{/if}}
|
|
133
167
|
</div>
|
|
134
|
-
</Modal::Default>
|
|
168
|
+
</Modal::Default>
|
|
@@ -16,6 +16,8 @@ export default class ModalsUpdateOrderActivityComponent extends Component {
|
|
|
16
16
|
@tracked selectedActivity;
|
|
17
17
|
@tracked customActivity = {};
|
|
18
18
|
@tracked proofFiles = [];
|
|
19
|
+
@tracked proof;
|
|
20
|
+
@tracked bypassProof = false;
|
|
19
21
|
|
|
20
22
|
constructor(owner, { options = {} }) {
|
|
21
23
|
super(...arguments);
|
|
@@ -104,6 +106,14 @@ export default class ModalsUpdateOrderActivityComponent extends Component {
|
|
|
104
106
|
return ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
|
|
105
107
|
}
|
|
106
108
|
|
|
109
|
+
get hasUploadedProof() {
|
|
110
|
+
return Boolean(this.proof?.id || this.proof?.public_id);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
get uploadedProofId() {
|
|
114
|
+
return this.proof?.id ?? this.proof?.public_id;
|
|
115
|
+
}
|
|
116
|
+
|
|
107
117
|
@task *loadActivity() {
|
|
108
118
|
if (!this.order?.id) {
|
|
109
119
|
return [];
|
|
@@ -128,14 +138,22 @@ export default class ModalsUpdateOrderActivityComponent extends Component {
|
|
|
128
138
|
return this.notifications.warning(this.intl.t('fleet-ops.operations.orders.index.view.invalid-warning'));
|
|
129
139
|
}
|
|
130
140
|
|
|
131
|
-
if (this.selectedActivityRequiresPod && this.
|
|
141
|
+
if (this.selectedActivityRequiresPod && this.uploadProofFile.isRunning) {
|
|
142
|
+
modal.stopLoading();
|
|
143
|
+
return this.notifications.warning('Photo proof of delivery is still uploading.');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (this.selectedActivityRequiresPod && !this.hasUploadedProof && !this.bypassProof) {
|
|
132
147
|
modal.stopLoading();
|
|
133
|
-
return this.notifications.warning('
|
|
148
|
+
return this.notifications.warning('Upload photo proof of delivery or choose to bypass proof of delivery.');
|
|
134
149
|
}
|
|
135
150
|
|
|
136
151
|
try {
|
|
137
|
-
|
|
138
|
-
|
|
152
|
+
yield this.fetch.patch(`orders/update-activity/${this.order.id}`, {
|
|
153
|
+
activity,
|
|
154
|
+
proof: this.hasUploadedProof ? this.uploadedProofId : null,
|
|
155
|
+
bypass_proof: this.selectedActivityRequiresPod && this.bypassProof,
|
|
156
|
+
});
|
|
139
157
|
|
|
140
158
|
if (typeof this.order?.reload === 'function') {
|
|
141
159
|
yield this.order.reload();
|
|
@@ -150,6 +168,7 @@ export default class ModalsUpdateOrderActivityComponent extends Component {
|
|
|
150
168
|
}
|
|
151
169
|
|
|
152
170
|
this.modalsManager.setOption('activityCreated', activity);
|
|
171
|
+
this.modalsManager.setOption('proofCreated', this.hasUploadedProof ? this.proof : null);
|
|
153
172
|
this.notifications.success(`Order activity has been updated to ${activity.status}`);
|
|
154
173
|
modal.done();
|
|
155
174
|
} catch (error) {
|
|
@@ -161,6 +180,10 @@ export default class ModalsUpdateOrderActivityComponent extends Component {
|
|
|
161
180
|
|
|
162
181
|
@task *capturePhotoProof() {
|
|
163
182
|
const [proofFile] = this.proofFiles;
|
|
183
|
+
if (!proofFile) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
|
|
164
187
|
const headers = this.fetch.getHeaders();
|
|
165
188
|
delete headers['Content-Type'];
|
|
166
189
|
|
|
@@ -179,11 +202,42 @@ export default class ModalsUpdateOrderActivityComponent extends Component {
|
|
|
179
202
|
return response.json();
|
|
180
203
|
}
|
|
181
204
|
|
|
182
|
-
@
|
|
205
|
+
@task *uploadProofFile(file) {
|
|
183
206
|
if (!file) {
|
|
184
207
|
return;
|
|
185
208
|
}
|
|
186
209
|
|
|
210
|
+
this.bypassProof = false;
|
|
211
|
+
this.proof = null;
|
|
187
212
|
this.proofFiles = [file];
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
this.proof = this.normalizeProofResponse(yield this.capturePhotoProof.perform());
|
|
216
|
+
this.notifications.success('Photo proof of delivery uploaded.');
|
|
217
|
+
} catch (error) {
|
|
218
|
+
this.proof = null;
|
|
219
|
+
this.proofFiles = [];
|
|
220
|
+
this.notifications.serverError(error);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
@action toggleBypassProof(value) {
|
|
225
|
+
this.bypassProof = value;
|
|
226
|
+
if (value) {
|
|
227
|
+
this.proofFiles = [];
|
|
228
|
+
this.proof = null;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
normalizeProofResponse(response) {
|
|
233
|
+
if (response?.proof) {
|
|
234
|
+
return response.proof;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (response?.data?.id || response?.data?.public_id) {
|
|
238
|
+
return response.data;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return response;
|
|
188
242
|
}
|
|
189
243
|
}
|
|
@@ -31,9 +31,9 @@ export default class OrderDetailsActivityComponent extends Component {
|
|
|
31
31
|
disabled: this.args.resource.status === 'canceled',
|
|
32
32
|
onClick: () => {
|
|
33
33
|
this.orderActions.updateActivity(this.args.resource, {
|
|
34
|
-
onFinish: (
|
|
34
|
+
onFinish: (options) => {
|
|
35
35
|
if (typeof this.args.onChange === 'function') {
|
|
36
|
-
this.args.onChange(
|
|
36
|
+
this.args.onChange(options);
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
39
|
});
|
|
@@ -1,42 +1,44 @@
|
|
|
1
|
-
<
|
|
2
|
-
{{
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
{{/each}}
|
|
26
|
-
</div>
|
|
27
|
-
{{else}}
|
|
28
|
-
<div class="min-h-24 p-4">
|
|
29
|
-
<div class="flex items-center justify-center border border-dashed border-gray-300 dark:border-gray-600 p-6 rounded-xl bg-gray-50 dark:bg-gray-800">
|
|
30
|
-
<div class="text-center">
|
|
31
|
-
<FaIcon @icon="camera-retro" @size="2x" class="text-gray-400 mb-2" />
|
|
32
|
-
<div class="text-sm text-gray-600 dark:text-gray-400 font-medium">
|
|
33
|
-
No proof of delivery
|
|
1
|
+
<div {{did-update this.reloadProofs @reloadToken}}>
|
|
2
|
+
<ContentPanel @title={{t "order.fields.proof-of-delivery"}} @isLoading={{@isLoading}} @open={{true}} @wrapperClass="bordered-top">
|
|
3
|
+
{{#if this.proofs}}
|
|
4
|
+
<div class="grid grid-cols-1 lg:grid-cols-3 gap-3">
|
|
5
|
+
{{#each this.proofs as |proof|}}
|
|
6
|
+
<div>
|
|
7
|
+
{{#if (eq proof.type "photo")}}
|
|
8
|
+
<a href={{proof.url}} target={{proof.id}}>
|
|
9
|
+
<Image src={{proof.url}} class="w-48 h-48 rounded-md shadow-sm mb-2" />
|
|
10
|
+
</a>
|
|
11
|
+
{{/if}}
|
|
12
|
+
{{#if (eq proof.type "signature")}}
|
|
13
|
+
<a href={{proof.raw}} target={{proof.id}} download>
|
|
14
|
+
<Image src={{proof.raw}} class="w-48 h-48 rounded-md shadow-sm mb-2" />
|
|
15
|
+
</a>
|
|
16
|
+
{{/if}}
|
|
17
|
+
{{#if (eq proof.type "scan")}}
|
|
18
|
+
<div class="flex items-center mb-2">
|
|
19
|
+
<FaIcon @icon="qrcode" @size="2xl" class="text-blue-400" />
|
|
20
|
+
</div>
|
|
21
|
+
{{/if}}
|
|
22
|
+
<div class="font-semibold text-sm mb-0.5">{{proof.remarks}}</div>
|
|
23
|
+
<div class="text-xs">{{proof.public_id}}</div>
|
|
24
|
+
<div class="text-xs">{{format-date-fns proof.created_at}}</div>
|
|
34
25
|
</div>
|
|
35
|
-
|
|
36
|
-
|
|
26
|
+
{{/each}}
|
|
27
|
+
</div>
|
|
28
|
+
{{else}}
|
|
29
|
+
<div class="min-h-24 p-4">
|
|
30
|
+
<div class="flex items-center justify-center border border-dashed border-gray-300 dark:border-gray-600 p-6 rounded-xl bg-gray-50 dark:bg-gray-800">
|
|
31
|
+
<div class="text-center">
|
|
32
|
+
<FaIcon @icon="camera-retro" @size="2x" class="text-gray-400 mb-2" />
|
|
33
|
+
<div class="text-sm text-gray-600 dark:text-gray-400 font-medium">
|
|
34
|
+
No proof of delivery
|
|
35
|
+
</div>
|
|
36
|
+
<div class="text-xs text-gray-500 dark:text-gray-500 mt-1">
|
|
37
|
+
Captured proof of delivery will show up here
|
|
38
|
+
</div>
|
|
37
39
|
</div>
|
|
38
40
|
</div>
|
|
39
41
|
</div>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
</
|
|
42
|
+
{{/if}}
|
|
43
|
+
</ContentPanel>
|
|
44
|
+
</div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Component from '@glimmer/component';
|
|
2
2
|
import { tracked } from '@glimmer/tracking';
|
|
3
|
+
import { action } from '@ember/object';
|
|
3
4
|
import { inject as service } from '@ember/service';
|
|
4
5
|
import { task } from 'ember-concurrency-decorators';
|
|
5
6
|
|
|
@@ -13,12 +14,18 @@ export default class OrderDetailsProofComponent extends Component {
|
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
@task *loadOrderProofs(order) {
|
|
17
|
+
if (!order?.id) {
|
|
18
|
+
this.proofs = [];
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
16
22
|
const proofs = yield this.fetch.get(`orders/${order.id}/proofs`);
|
|
17
23
|
|
|
18
|
-
this.proofs = proofs
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
this.proofs = this.#normalizeProofs(proofs);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@action reloadProofs() {
|
|
28
|
+
this.loadOrderProofs.perform(this.args.resource);
|
|
22
29
|
}
|
|
23
30
|
|
|
24
31
|
#getTypeFromRemarks(remarks = '') {
|
|
@@ -27,4 +34,24 @@ export default class OrderDetailsProofComponent extends Component {
|
|
|
27
34
|
if (remarks.endsWith('Signature')) return 'signature';
|
|
28
35
|
return undefined;
|
|
29
36
|
}
|
|
37
|
+
|
|
38
|
+
#normalizeProofs(proofs = []) {
|
|
39
|
+
const seen = new Set();
|
|
40
|
+
|
|
41
|
+
return proofs
|
|
42
|
+
.filter((proof) => {
|
|
43
|
+
const key = proof.uuid ?? proof.public_id ?? proof.id;
|
|
44
|
+
if (!key || seen.has(key)) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
seen.add(key);
|
|
49
|
+
return true;
|
|
50
|
+
})
|
|
51
|
+
.map((proof) => ({
|
|
52
|
+
...proof,
|
|
53
|
+
type: this.#getTypeFromRemarks(proof.remarks),
|
|
54
|
+
}))
|
|
55
|
+
.sort((a, b) => new Date(b.created_at ?? 0).getTime() - new Date(a.created_at ?? 0).getTime());
|
|
56
|
+
}
|
|
30
57
|
}
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
{{#if (has-block)}}
|
|
3
3
|
{{yield
|
|
4
4
|
(hash
|
|
5
|
-
Activity=(component "order/details/activity" resource=@resource onChange
|
|
5
|
+
Activity=(component "order/details/activity" resource=@resource onChange=this.handleActivityChanged isLoadingActivity=@isLoadingActivity isLoading=@isLoading)
|
|
6
6
|
Detail=(component "order/details/detail" resource=@resource onChange=@onDetailsChanged isLoading=@isLoading)
|
|
7
7
|
CustomFields=(component "order/details/custom-fields" resource=@resource onChange=@onCustomFieldsChanged isLoading=@isLoading)
|
|
8
8
|
PurchaseRate=(component "order/details/purchase-rate" resource=@resource isLoading=@isLoading)
|
|
9
9
|
Tracking=(component "order/details/tracking" resource=@resource isLoading=@isLoading)
|
|
10
|
-
Proof=(component "order/details/proof" resource=@resource isLoading=@isLoading)
|
|
10
|
+
Proof=(component "order/details/proof" resource=@resource reloadToken=this.effectiveProofReloadToken isLoading=@isLoading)
|
|
11
11
|
Notes=(component "order/details/notes" resource=@resource onChange=@onNotesChanged isLoading=@isLoading)
|
|
12
12
|
IntegratedVendorDetails=(component "order/details/integrated-vendor-details" resource=@resource isLoading=@isLoading)
|
|
13
13
|
Route=(component "order/details/route" resource=@resource onChange=@onRouteChanged isLoading=@isLoading)
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
)
|
|
22
22
|
}}
|
|
23
23
|
{{else}}
|
|
24
|
-
<Order::Details::Activity @resource={{@resource}} @onChange={{
|
|
24
|
+
<Order::Details::Activity @resource={{@resource}} @onChange={{this.handleActivityChanged}} @isLoadingActivity={{@isLoadingActivity}} @isLoading={{@isLoading}} />
|
|
25
25
|
<Order::Details::Detail @resource={{@resource}} @onChange={{@onDetailsChanged}} @isLoading={{@isLoading}} />
|
|
26
26
|
<Order::Details::CustomFields @resource={{@resource}} @onChange={{@onCustomFieldsChanged}} @isLoading={{@isLoading}} />
|
|
27
27
|
<Order::Details::PurchaseRate @resource={{@resource}} @isLoading={{@isLoading}} />
|
|
28
28
|
<Order::Details::Tracking @resource={{@resource}} @isLoading={{@isLoading}} />
|
|
29
|
-
<Order::Details::Proof @resource={{@resource}} @isLoading={{@isLoading}} />
|
|
29
|
+
<Order::Details::Proof @resource={{@resource}} @reloadToken={{this.effectiveProofReloadToken}} @isLoading={{@isLoading}} />
|
|
30
30
|
<Order::Details::Notes @resource={{@resource}} @onChange={{@onNotesChanged}} @isLoading={{@isLoading}} />
|
|
31
31
|
<Order::Details::IntegratedVendorDetails @resource={{@resource}} @isLoading={{@isLoading}} />
|
|
32
32
|
<Order::Details::Route @resource={{@resource}} @onChange={{@onRouteChanged}} @isLoading={{@isLoading}} />
|
|
@@ -38,4 +38,4 @@
|
|
|
38
38
|
<Order::Details::Comments @resource={{@resource}} @onChange={{@onCommentsChanged}} @isLoading={{@isLoading}} />
|
|
39
39
|
<Order::Details::Metadata @resource={{@resource}} @onChange={{@onMetadataChanged}} @isLoading={{@isLoading}} />
|
|
40
40
|
{{/if}}
|
|
41
|
-
</div>
|
|
41
|
+
</div>
|
|
@@ -1,3 +1,21 @@
|
|
|
1
1
|
import Component from '@glimmer/component';
|
|
2
|
+
import { action } from '@ember/object';
|
|
3
|
+
import { tracked } from '@glimmer/tracking';
|
|
2
4
|
|
|
3
|
-
export default class OrderDetailsComponent extends Component {
|
|
5
|
+
export default class OrderDetailsComponent extends Component {
|
|
6
|
+
@tracked proofReloadToken = 0;
|
|
7
|
+
|
|
8
|
+
get effectiveProofReloadToken() {
|
|
9
|
+
return `${this.args.proofReloadToken ?? 0}:${this.proofReloadToken}`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@action handleActivityChanged(optionsOrActivity) {
|
|
13
|
+
if (typeof this.args.onActivityChanged === 'function') {
|
|
14
|
+
this.args.onActivityChanged(optionsOrActivity?.activityCreated ?? optionsOrActivity);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (optionsOrActivity?.proofCreated) {
|
|
18
|
+
this.proofReloadToken++;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<Badge @status={{@order.status}} @helpText={{if (eq @order.status (t "order.header.dispatched")) (concat (t "order.header.dispatched-at") @order.dispatchedAt)}} />
|
|
11
11
|
{{#if @order.shouldDisplayDispatchLabel}}
|
|
12
12
|
<Badge @status="info" class="ml-2" @helpText={{t "order.header.dispatched" htmlSafe=true order=@order.dispatchedAt}}>{{t
|
|
13
|
-
"
|
|
13
|
+
"order-status.dispatched"
|
|
14
14
|
}}</Badge>
|
|
15
15
|
{{/if}}
|
|
16
16
|
</div>
|
|
@@ -20,9 +20,6 @@
|
|
|
20
20
|
{{#if this.firstStop.etaSeconds}}
|
|
21
21
|
<Badge @status="pending" @hideStatusDot={{true}}><span class="font-bold">ETA:</span> {{format-duration this.firstStop.etaSeconds}}</Badge>
|
|
22
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
23
|
</div>
|
|
27
24
|
<DisplayPlace @place={{this.firstStop.place}} @waypointActions={{@waypointActions}} @waypointActionContext={{this.firstStop}} @hideBadges={{true}} />
|
|
28
25
|
</div>
|
|
@@ -58,9 +55,6 @@
|
|
|
58
55
|
{{#if stop.etaSeconds}}
|
|
59
56
|
<Badge @status="pending" @hideStatusDot={{true}}><span class="font-bold">ETA:</span> {{format-duration stop.etaSeconds}}</Badge>
|
|
60
57
|
{{/if}}
|
|
61
|
-
{{#if stop.etaAt}}
|
|
62
|
-
<Badge @status="pending" @hideStatusDot={{true}}>{{format-date-fns stop.etaAt "d MMM HH:mm"}}</Badge>
|
|
63
|
-
{{/if}}
|
|
64
58
|
</div>
|
|
65
59
|
<DisplayPlace @place={{stop.place}} @waypointActions={{@waypointActions}} @waypointActionContext={{stop}} @hideBadges={{true}} />
|
|
66
60
|
</div>
|
|
@@ -86,9 +80,6 @@
|
|
|
86
80
|
{{#if stop.etaSeconds}}
|
|
87
81
|
<Badge @status="pending" @hideStatusDot={{true}}><span class="font-bold">ETA:</span> {{format-duration stop.etaSeconds}}</Badge>
|
|
88
82
|
{{/if}}
|
|
89
|
-
{{#if stop.etaAt}}
|
|
90
|
-
<Badge @status="pending" @hideStatusDot={{true}}>{{format-date-fns stop.etaAt "d MMM HH:mm"}}</Badge>
|
|
91
|
-
{{/if}}
|
|
92
83
|
</div>
|
|
93
84
|
<DisplayPlace @place={{stop.place}} @waypointActions={{@waypointActions}} @waypointActionContext={{stop}} @hideBadges={{true}} />
|
|
94
85
|
</div>
|
|
@@ -117,9 +108,6 @@
|
|
|
117
108
|
{{#if this.lastStop.etaSeconds}}
|
|
118
109
|
<Badge @status="pending" @hideStatusDot={{true}}><span class="font-bold">ETA:</span> {{format-duration this.lastStop.etaSeconds}}</Badge>
|
|
119
110
|
{{/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
111
|
</div>
|
|
124
112
|
<DisplayPlace @place={{this.lastStop.place}} @waypointActions={{@waypointActions}} @waypointActionContext={{this.lastStop}} @hideBadges={{true}} />
|
|
125
113
|
</div>
|
|
@@ -199,7 +199,7 @@
|
|
|
199
199
|
<div class="field-info-container">
|
|
200
200
|
<div class="field-name">Algorithm Code</div>
|
|
201
201
|
<div class="field-value">
|
|
202
|
-
<pre class="bg-gray-100 dark:bg-gray-900 font-mono p-3 rounded text-xs overflow-x-auto"><code class="font-mono text-xs">{{n-a @resource.algorithm}}</code></pre>
|
|
202
|
+
<pre class="bg-gray-100 dark:bg-gray-900 font-mono p-3 rounded text-xs overflow-x-auto"><code class="font-mono text-xs whitespace-break-spaces">{{n-a @resource.algorithm}}</code></pre>
|
|
203
203
|
</div>
|
|
204
204
|
</div>
|
|
205
205
|
</ContentPanel>
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
@disabled={{cannot-write @resource}}
|
|
11
11
|
/>
|
|
12
12
|
|
|
13
|
-
<InputGroup @name={{t "service-rate.fields.service-order-label"}} @helpText={{t "service-rate.fields.service-order-help-text"}}>
|
|
13
|
+
<InputGroup @name={{t "service-rate.fields.service-order-label"}} @helpText={{t "service-rate.fields.service-order-help-text"}} @required={{true}}>
|
|
14
14
|
<div class="fleetbase-model-select fleetbase-power-select ember-model-select">
|
|
15
15
|
<PowerSelect
|
|
16
16
|
@options={{this.orderConfigActions.allOrderConfigs}}
|
|
@@ -53,6 +53,7 @@ export default class ServiceRateFormComponent extends Component {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
@action selectRuleGeographyType(rule, { value }) {
|
|
56
|
+
rule.set('selected_geography_type', value);
|
|
56
57
|
rule.set('zone', null);
|
|
57
58
|
rule.set('zone_uuid', null);
|
|
58
59
|
rule.set('service_area', null);
|
|
@@ -61,6 +62,7 @@ export default class ServiceRateFormComponent extends Component {
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
@action selectRuleServiceArea(rule, serviceArea) {
|
|
65
|
+
rule.set('selected_geography_type', 'service_area');
|
|
64
66
|
rule.set('service_area', serviceArea);
|
|
65
67
|
rule.set('service_area_uuid', serviceArea?.id);
|
|
66
68
|
rule.set('zone', null);
|
|
@@ -69,6 +71,7 @@ export default class ServiceRateFormComponent extends Component {
|
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
@action selectRuleZone(rule, zone) {
|
|
74
|
+
rule.set('selected_geography_type', 'zone');
|
|
72
75
|
rule.set('zone', zone);
|
|
73
76
|
rule.set('zone_uuid', zone?.id);
|
|
74
77
|
rule.set('service_area', null);
|
|
@@ -79,6 +82,7 @@ export default class ServiceRateFormComponent extends Component {
|
|
|
79
82
|
@action setRuleFallback(rule, isFallback) {
|
|
80
83
|
rule.set('is_fallback', Boolean(isFallback));
|
|
81
84
|
if (isFallback) {
|
|
85
|
+
rule.set('selected_geography_type', 'fallback');
|
|
82
86
|
rule.set('zone', null);
|
|
83
87
|
rule.set('zone_uuid', null);
|
|
84
88
|
rule.set('service_area', null);
|