@fleetbase/fleetops-engine 0.6.49 → 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.
Files changed (271) hide show
  1. package/README.md +185 -67
  2. package/addon/components/contact/panel-header.hbs +11 -48
  3. package/addon/components/customer/create-order-form.hbs +2 -2
  4. package/addon/components/customer/details.hbs +1 -2
  5. package/addon/components/customer/form.hbs +62 -50
  6. package/addon/components/customer/form.js +27 -1
  7. package/addon/components/customer/order-form.hbs +2 -2
  8. package/addon/components/customer/orders.hbs +50 -2
  9. package/addon/components/customer/orders.js +67 -5
  10. package/addon/components/display-place.hbs +2 -2
  11. package/addon/components/driver/form.hbs +1 -1
  12. package/addon/components/home/getting-started-guidance.hbs +113 -0
  13. package/addon/components/home/getting-started-guidance.js +107 -0
  14. package/addon/components/issue/details/comments.hbs +3 -0
  15. package/addon/components/issue/details/comments.js +3 -0
  16. package/addon/components/issue/details/correspondence.hbs +9 -0
  17. package/addon/components/issue/details/correspondence.js +83 -0
  18. package/addon/components/issue/details/documents.hbs +69 -0
  19. package/addon/components/issue/details/documents.js +85 -0
  20. package/addon/components/issue/details.hbs +220 -59
  21. package/addon/components/issue/details.js +245 -0
  22. package/addon/components/issue/form.hbs +19 -1
  23. package/addon/components/issue/panel-header.hbs +43 -0
  24. package/addon/components/issue/panel-header.js +22 -0
  25. package/addon/components/issue/timeline.hbs +40 -0
  26. package/addon/components/issue/timeline.js +45 -0
  27. package/addon/components/leaflet-tracking-marker.js +3 -0
  28. package/addon/components/map/drawer/position-listing.js +3 -0
  29. package/addon/components/map/google-live-map.js +6 -2
  30. package/addon/components/map/leaflet-live-map.hbs +48 -14
  31. package/addon/components/map/leaflet-live-map.js +201 -20
  32. package/addon/components/map/order-list-overlay.hbs +4 -4
  33. package/addon/components/modals/convert-customer-to-vendor.hbs +30 -0
  34. package/addon/components/modals/convert-customer-to-vendor.js +55 -0
  35. package/addon/components/modals/issue-assign.hbs +20 -0
  36. package/addon/components/modals/issue-assign.js +49 -0
  37. package/addon/components/modals/issue-close.hbs +30 -0
  38. package/addon/components/modals/issue-close.js +73 -0
  39. package/addon/components/modals/issue-status.hbs +20 -0
  40. package/addon/components/modals/issue-status.js +43 -0
  41. package/addon/components/modals/order-change-destination.hbs +46 -0
  42. package/addon/components/modals/order-change-destination.js +106 -0
  43. package/addon/components/modals/reset-customer-credentials.js +12 -6
  44. package/addon/components/modals/stop-activity.hbs +28 -0
  45. package/addon/components/modals/stop-activity.js +52 -0
  46. package/addon/components/modals/update-order-activity.hbs +78 -2
  47. package/addon/components/modals/update-order-activity.js +197 -5
  48. package/addon/components/orchestrator/resource-panel.hbs +0 -10
  49. package/addon/components/orchestrator/resource-panel.js +2 -4
  50. package/addon/components/order/activity-list.hbs +2 -2
  51. package/addon/components/order/activity-timeline.hbs +31 -29
  52. package/addon/components/order/details/activity.hbs +3 -3
  53. package/addon/components/order/details/activity.js +13 -2
  54. package/addon/components/order/details/documents.js +2 -0
  55. package/addon/components/order/details/proof.hbs +40 -38
  56. package/addon/components/order/details/proof.js +31 -4
  57. package/addon/components/order/details/purchase-rate.hbs +2 -2
  58. package/addon/components/order/details/route.hbs +11 -3
  59. package/addon/components/order/details/route.js +91 -14
  60. package/addon/components/order/details/tracking.hbs +101 -72
  61. package/addon/components/order/details/tracking.js +149 -2
  62. package/addon/components/order/details.hbs +5 -5
  63. package/addon/components/order/details.js +19 -1
  64. package/addon/components/order/form/details.hbs +3 -3
  65. package/addon/components/order/form/documents.js +11 -4
  66. package/addon/components/order/form/route.hbs +108 -104
  67. package/addon/components/order/form/route.js +37 -1
  68. package/addon/components/order/form/service-rate.hbs +2 -2
  69. package/addon/components/order/header.hbs +1 -1
  70. package/addon/components/order-config-manager/activity-flow.hbs +4 -1
  71. package/addon/components/order-config-manager/activity-flow.js +178 -12
  72. package/addon/components/place/address.js +1 -1
  73. package/addon/components/route-list.hbs +17 -29
  74. package/addon/components/route-list.js +42 -10
  75. package/addon/components/service-rate/details.hbs +42 -1
  76. package/addon/components/service-rate/form.hbs +119 -1
  77. package/addon/components/service-rate/form.js +59 -0
  78. package/addon/components/vehicle/form.js +1 -1
  79. package/addon/components/vehicle/pill.hbs +1 -1
  80. package/addon/components/vendor/personnel-manager.hbs +166 -0
  81. package/addon/components/vendor/personnel-manager.js +143 -0
  82. package/addon/components/widget/live-fleet.hbs +1 -0
  83. package/addon/components/widget/live-fleet.js +2 -7
  84. package/addon/components/widget/revenue-trend.hbs +1 -1
  85. package/addon/components/widget/revenue-trend.js +75 -2
  86. package/addon/controllers/management/contacts/customers/details.js +31 -13
  87. package/addon/controllers/management/contacts/customers.js +16 -5
  88. package/addon/controllers/management/contacts/index/details.js +31 -13
  89. package/addon/controllers/management/contacts/index.js +7 -0
  90. package/addon/controllers/management/issues/index/details.js +52 -6
  91. package/addon/controllers/management/vehicles/index/new.js +1 -1
  92. package/addon/controllers/management/vendors/index/details.js +4 -0
  93. package/addon/controllers/operations/orders/index/details.js +15 -7
  94. package/addon/controllers/operations/orders/index.js +15 -1
  95. package/addon/extension.js +19 -12
  96. package/addon/helpers/format-duration.js +1 -38
  97. package/addon/helpers/place-address-html.js +2 -2
  98. package/addon/initializers/load-leaflet-assets.js +5 -6
  99. package/addon/initializers/patch-ember-leaflet-tooltip-layer.js +51 -0
  100. package/addon/routes/management/issues/index/details.js +1 -1
  101. package/addon/routes/operations/service-rates/index.js +1 -1
  102. package/addon/routes.js +1 -0
  103. package/addon/services/contact-actions.js +231 -1
  104. package/addon/services/geofence.js +163 -19
  105. package/addon/services/getting-started.js +44 -0
  106. package/addon/services/issue-actions.js +71 -0
  107. package/addon/services/leaflet-contextmenu-manager.js +27 -0
  108. package/addon/services/leaflet-map-manager.js +2 -0
  109. package/addon/services/map-adapter/leaflet.js +19 -0
  110. package/addon/services/service-areas.js +3 -0
  111. package/addon/services/vehicle-actions.js +1 -1
  112. package/addon/styles/fleetops-engine.css +1127 -1
  113. package/addon/templates/management/contacts/customers/details.hbs +1 -1
  114. package/addon/templates/management/contacts/index/details.hbs +1 -1
  115. package/addon/templates/management/contacts.hbs +2 -2
  116. package/addon/templates/management/issues/index/details/index.hbs +3 -1
  117. package/addon/templates/management/issues/index/details.hbs +3 -2
  118. package/addon/templates/management/vendors/index/details/personnel.hbs +1 -2
  119. package/addon/templates/operations/orders/index/details/index.hbs +2 -2
  120. package/addon/templates/operations/orders/index.hbs +1 -1
  121. package/addon/templates/operations/service-rates/index/edit.hbs +1 -0
  122. package/addon/templates/operations/service-rates/index/new.hbs +1 -0
  123. package/addon/templates/settings/payments/onboard.hbs +8 -8
  124. package/addon/utils/fleet-ops-options.js +2 -1
  125. package/addon/utils/leaflet-draw-namespace-guard.js +22 -0
  126. package/addon/utils/leaflet-plugin-loader.js +219 -0
  127. package/addon/utils/normalize-order-config-flow.js +41 -0
  128. package/addon/utils/place-address-html.js +3 -2
  129. package/addon/utils/route-visualization.js +54 -7
  130. package/addon/utils/setup-customer-portal.js +4 -64
  131. package/app/components/home/getting-started-guidance.js +1 -0
  132. package/app/components/issue/details/comments.js +1 -0
  133. package/app/components/issue/details/correspondence.js +1 -0
  134. package/app/components/issue/details/documents.js +1 -0
  135. package/app/components/issue/panel-header.js +1 -0
  136. package/app/components/modals/convert-customer-to-vendor.js +1 -0
  137. package/app/components/modals/issue-assign.js +1 -0
  138. package/app/components/modals/issue-close.js +1 -0
  139. package/app/components/modals/issue-status.js +1 -0
  140. package/app/components/modals/order-change-destination.js +1 -0
  141. package/app/components/modals/stop-activity.js +1 -0
  142. package/app/components/vendor/personnel-manager.js +1 -0
  143. package/app/helpers/format-duration.js +1 -1
  144. package/app/services/getting-started.js +1 -0
  145. package/app/utils/leaflet-draw-namespace-guard.js +1 -0
  146. package/app/utils/leaflet-plugin-loader.js +1 -0
  147. package/composer.json +1 -1
  148. package/extension.json +1 -1
  149. package/package.json +5 -5
  150. package/server/migrations/2026_05_21_000001_add_geographic_rules_to_service_rate_fees_table.php +37 -0
  151. package/server/migrations/2026_05_25_000001_add_endpoint_tracking_refs_to_payloads_table.php +50 -0
  152. package/server/migrations/2026_05_27_000001_default_driver_vehicle_statuses_to_available.php +48 -0
  153. package/server/migrations/2026_05_31_000001_add_portal_fields_to_vendor_personnels_table.php +30 -0
  154. package/server/migrations/2026_06_03_000000_ensure_tags_column_on_issues_table.php +31 -0
  155. package/server/migrations/2026_06_03_000001_add_order_uuid_to_issues_table.php +25 -0
  156. package/server/migrations/2026_06_04_000001_backfill_order_transaction_subjects.php +39 -0
  157. package/server/migrations/2026_06_04_000002_backfill_order_transaction_directions.php +24 -0
  158. package/server/resources/views/labels/default.php +57 -49
  159. package/server/resources/views/labels/waypoint-label.php +27 -20
  160. package/server/resources/views/mail/customer-credentials.blade.php +16 -13
  161. package/server/seeders/Testing/ConnectivitySeeder.php +1 -0
  162. package/server/seeders/Testing/FleetSeeder.php +22 -22
  163. package/server/seeders/Testing/OrdersSeeder.php +6 -22
  164. package/server/seeders/Testing/VroomOrchestrationSeeder.php +21 -21
  165. package/server/src/Console/Commands/AuditCustomerUserConflicts.php +75 -0
  166. package/server/src/Exceptions/CustomerUserConflictException.php +13 -0
  167. package/server/src/Expansions/UserFilterExpansion.php +28 -0
  168. package/server/src/Http/Controllers/Api/v1/ContactController.php +17 -2
  169. package/server/src/Http/Controllers/Api/v1/CustomerController.php +876 -0
  170. package/server/src/Http/Controllers/Api/v1/DriverController.php +1 -1
  171. package/server/src/Http/Controllers/Api/v1/IssueController.php +2 -0
  172. package/server/src/Http/Controllers/Api/v1/OrderConfigController.php +54 -0
  173. package/server/src/Http/Controllers/Api/v1/OrderController.php +109 -80
  174. package/server/src/Http/Controllers/Api/v1/PlaceController.php +10 -58
  175. package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +2 -2
  176. package/server/src/Http/Controllers/Api/v1/VehicleController.php +20 -3
  177. package/server/src/Http/Controllers/Internal/v1/ContactController.php +154 -0
  178. package/server/src/Http/Controllers/Internal/v1/CustomerController.php +116 -6
  179. package/server/src/Http/Controllers/Internal/v1/GettingStartedController.php +17 -0
  180. package/server/src/Http/Controllers/Internal/v1/IssueController.php +251 -0
  181. package/server/src/Http/Controllers/Internal/v1/LiveController.php +101 -66
  182. package/server/src/Http/Controllers/Internal/v1/OrderController.php +530 -52
  183. package/server/src/Http/Controllers/Internal/v1/PlaceController.php +10 -68
  184. package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +1 -1
  185. package/server/src/Http/Controllers/Internal/v1/SettingController.php +2 -2
  186. package/server/src/Http/Controllers/Internal/v1/VehicleController.php +54 -1
  187. package/server/src/Http/Controllers/Internal/v1/VendorController.php +89 -0
  188. package/server/src/Http/Middleware/AuthenticateCustomerToken.php +34 -0
  189. package/server/src/Http/Requests/CreateCustomerOrderRequest.php +63 -0
  190. package/server/src/Http/Requests/CreateCustomerRequest.php +48 -0
  191. package/server/src/Http/Requests/CreateDriverRequest.php +1 -1
  192. package/server/src/Http/Requests/CreateIssueRequest.php +4 -0
  193. package/server/src/Http/Requests/CreateVehicleRequest.php +25 -1
  194. package/server/src/Http/Requests/Internal/CreateDriverRequest.php +1 -1
  195. package/server/src/Http/Requests/UpdateIssueRequest.php +4 -0
  196. package/server/src/Http/Requests/VerifyCreateCustomerRequest.php +29 -0
  197. package/server/src/Http/Resources/Internal/v1/OrderConfig.php +41 -0
  198. package/server/src/Http/Resources/v1/Customer.php +79 -0
  199. package/server/src/Http/Resources/v1/Index/Order.php +12 -9
  200. package/server/src/Http/Resources/v1/Issue.php +25 -0
  201. package/server/src/Http/Resources/v1/Order.php +20 -18
  202. package/server/src/Http/Resources/v1/OrderConfig.php +74 -0
  203. package/server/src/Http/Resources/v1/Payload.php +23 -20
  204. package/server/src/Http/Resources/v1/ServiceArea.php +33 -19
  205. package/server/src/Http/Resources/v1/ServiceRateFee.php +7 -0
  206. package/server/src/Listeners/HandleOrderDispatched.php +2 -2
  207. package/server/src/Mail/CustomerCredentialsMail.php +5 -6
  208. package/server/src/Models/Contact.php +122 -66
  209. package/server/src/Models/Customer.php +64 -0
  210. package/server/src/Models/Driver.php +12 -3
  211. package/server/src/Models/Entity.php +2 -2
  212. package/server/src/Models/Issue.php +47 -6
  213. package/server/src/Models/Order.php +62 -10
  214. package/server/src/Models/Payload.php +17 -1
  215. package/server/src/Models/ServiceArea.php +1 -1
  216. package/server/src/Models/ServiceRate.php +273 -1
  217. package/server/src/Models/ServiceRateFee.php +39 -9
  218. package/server/src/Models/TrackingNumber.php +8 -2
  219. package/server/src/Models/Vehicle.php +18 -2
  220. package/server/src/Models/VendorPersonnel.php +10 -1
  221. package/server/src/Models/Waypoint.php +2 -2
  222. package/server/src/Models/Zone.php +1 -1
  223. package/server/src/Notifications/LateDeparture.php +2 -2
  224. package/server/src/Notifications/ProlongedStoppage.php +2 -2
  225. package/server/src/Notifications/RouteDeviation.php +2 -2
  226. package/server/src/Observers/ContactObserver.php +2 -0
  227. package/server/src/Observers/PurchaseRateObserver.php +20 -0
  228. package/server/src/Observers/ServiceRateObserver.php +6 -6
  229. package/server/src/Observers/ZoneObserver.php +56 -0
  230. package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +1 -1
  231. package/server/src/Providers/FleetOpsServiceProvider.php +2 -0
  232. package/server/src/Support/Analytics/AbstractAnalytics.php +6 -6
  233. package/server/src/Support/Analytics/FuelEfficiency.php +2 -2
  234. package/server/src/Support/Analytics/OrdersByStatus.php +1 -1
  235. package/server/src/Support/Analytics/RevenueTrend.php +0 -1
  236. package/server/src/Support/Analytics/TopDrivers.php +10 -2
  237. package/server/src/Support/CustomerAuth.php +98 -0
  238. package/server/src/Support/GettingStarted.php +144 -0
  239. package/server/src/Support/LabelPdf.php +52 -0
  240. package/server/src/Support/Metrics/AbstractMetric.php +4 -4
  241. package/server/src/Support/PlaceSearch.php +282 -0
  242. package/server/src/Support/ResolvesOrderServiceStops.php +522 -0
  243. package/server/src/Tracking/TrackingContextBuilder.php +41 -38
  244. package/server/src/Tracking/TrackingIntelligenceService.php +109 -9
  245. package/server/src/Tracking/TrackingStop.php +13 -11
  246. package/server/src/routes.php +76 -10
  247. package/server/tests/AnalyticsRoutesTest.php +8 -0
  248. package/server/tests/CapacityAllocationEngineTest.php +10 -10
  249. package/server/tests/CustomerContactIdentitySafetyTest.php +95 -0
  250. package/server/tests/CustomerEndpointTest.php +199 -0
  251. package/server/tests/DriverVehicleStatusDefaultTest.php +39 -0
  252. package/server/tests/GettingStartedStatusTest.php +21 -0
  253. package/server/tests/LabelPdfRenderingTest.php +23 -0
  254. package/server/tests/LiveControllerViewportTest.php +60 -0
  255. package/server/tests/MetricsRegistryTest.php +4 -8
  256. package/server/tests/OrchestrationPayloadBuilderTest.php +2 -2
  257. package/server/tests/OrderActivityFlowRegressionTest.php +116 -0
  258. package/server/tests/OrderConfigEndpointTest.php +86 -0
  259. package/server/tests/OrderFileAttachmentTest.php +20 -0
  260. package/server/tests/ServiceAreaZoneApiShapeTest.php +78 -0
  261. package/server/tests/VehicleDriverAssignmentTest.php +18 -0
  262. package/translations/ar-ae.yml +9 -0
  263. package/translations/bg-bg.yaml +9 -0
  264. package/translations/en-us.yaml +12 -0
  265. package/translations/es-pa.yaml +9 -0
  266. package/translations/fr-fr.yaml +9 -0
  267. package/translations/mn-mn.yaml +9 -0
  268. package/translations/pt-br.yaml +9 -0
  269. package/translations/ru-ru.yaml +9 -0
  270. package/translations/vi-vn.yaml +9 -0
  271. package/server/migrations/2026_05_13_000001_repair_customer_contact_user_types.php +0 -86
@@ -0,0 +1,20 @@
1
+ <Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
2
+ <div class="modal-body-container">
3
+ <InputGroup @name="Status" @helpText="Choose the current workflow state for this issue.">
4
+ <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
5
+ <PowerSelect
6
+ @options={{@options.statusOptions}}
7
+ @selected={{this.selectedStatus}}
8
+ @onChange={{fn (mut this.selectedStatus)}}
9
+ @placeholder="Select status"
10
+ @triggerClass="form-select form-input"
11
+ @renderInPlace={{true}}
12
+ as |status|
13
+ >
14
+ <div class="font-semibold normalize-in-trigger">{{status.label}}</div>
15
+ <div class="hide-from-trigger text-xs text-gray-500 dark:text-gray-400">{{status.description}}</div>
16
+ </PowerSelect>
17
+ </div>
18
+ </InputGroup>
19
+ </div>
20
+ </Modal::Default>
@@ -0,0 +1,43 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { inject as service } from '@ember/service';
4
+
5
+ export default class ModalsIssueStatusComponent extends Component {
6
+ @service notifications;
7
+
8
+ @tracked options = {};
9
+ @tracked issue;
10
+ @tracked selectedStatus;
11
+
12
+ constructor(owner, { options }) {
13
+ super(...arguments);
14
+ this.options = options;
15
+ this.issue = options.issue;
16
+ this.selectedStatus = options.statusOptions?.find((status) => status.value === this.issue?.status);
17
+ this.setupOptions();
18
+ }
19
+
20
+ setupOptions() {
21
+ this.options.title = 'Change Issue Status';
22
+ this.options.acceptButtonText = 'Update Status';
23
+ this.options.acceptButtonIcon = 'save';
24
+ this.options.confirm = async (modal) => {
25
+ modal.startLoading();
26
+
27
+ try {
28
+ this.issue.status = this.selectedStatus?.value ?? this.issue.status;
29
+ await this.issue.save();
30
+ this.notifications.success('Issue status updated.');
31
+
32
+ if (typeof this.options.onSaved === 'function') {
33
+ await this.options.onSaved(this.issue);
34
+ }
35
+
36
+ modal.done();
37
+ } catch (error) {
38
+ this.notifications.serverError(error);
39
+ modal.stopLoading();
40
+ }
41
+ };
42
+ }
43
+ }
@@ -0,0 +1,46 @@
1
+ <Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{perform this.confirmSelection}} @decline={{@onDecline}}>
2
+ <div class="modal-body-container">
3
+ <div class="space-y-3">
4
+ {{#each this.waypointOptions as |option|}}
5
+ <button
6
+ type="button"
7
+ class="w-full rounded-md border px-4 py-3 text-left shadow-sm transition {{if option.isCurrent 'border-blue-500 bg-blue-50 dark:border-blue-600 dark:bg-blue-900' (if option.isSelected 'border-green-500 bg-green-50 dark:border-green-700 dark:bg-green-900' 'border-gray-200 bg-white hover:border-blue-300 hover:bg-blue-50 dark:border-gray-700 dark:bg-gray-800 dark:hover:border-blue-700 dark:hover:bg-gray-900')}}"
8
+ disabled={{this.confirmSelection.isRunning}}
9
+ {{on "click" (fn this.selectOption option)}}
10
+ >
11
+ <div class="flex items-start gap-3">
12
+ <div
13
+ class="flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full text-xs font-bold {{if option.isCurrent 'bg-blue-600 text-white' 'bg-gray-200 text-gray-800 dark:bg-gray-700 dark:text-gray-100'}}"
14
+ >
15
+ {{option.label}}
16
+ </div>
17
+ <div class="min-w-0 flex-1">
18
+ <div class="flex items-center justify-between gap-2">
19
+ <div class="truncate text-sm font-semibold dark:text-gray-100">{{option.title}} - {{or option.waypoint.name option.waypoint.id}}</div>
20
+ {{#if option.isCurrent}}
21
+ <Badge @status="info" class="uppercase">Current</Badge>
22
+ {{else if option.isSelected}}
23
+ <Badge @status="success" class="uppercase">Selected</Badge>
24
+ {{else if option.waypoint.complete}}
25
+ <Badge @status="completed" class="uppercase">Complete</Badge>
26
+ {{else if option.waypoint.status_code}}
27
+ <Badge @status={{dasherize option.waypoint.status_code}} class="uppercase">{{option.waypoint.status_code}}</Badge>
28
+ {{else if option.waypoint.status}}
29
+ <Badge @status={{dasherize option.waypoint.status}} class="uppercase">{{option.waypoint.status}}</Badge>
30
+ {{/if}}
31
+ </div>
32
+ <div class="mt-1 truncate text-xs text-gray-600 dark:text-gray-300">{{or option.waypoint.address option.waypoint.street1}}</div>
33
+ {{#if option.tracking}}
34
+ <div class="mt-1 text-xs font-mono text-gray-500 dark:text-gray-400">{{option.tracking}}</div>
35
+ {{/if}}
36
+ </div>
37
+ </div>
38
+ </button>
39
+ {{else}}
40
+ <div class="rounded-md border border-dashed border-gray-300 p-6 text-center text-sm text-gray-500 dark:border-gray-700 dark:text-gray-400">
41
+ No waypoint destinations are available.
42
+ </div>
43
+ {{/each}}
44
+ </div>
45
+ </div>
46
+ </Modal::Default>
@@ -0,0 +1,106 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { inject as service } from '@ember/service';
4
+ import { action } from '@ember/object';
5
+ import { task } from 'ember-concurrency';
6
+ import { buildServiceStopsFromPayload, describeRoutePoint } from '../../utils/route-visualization';
7
+ import { colorForId } from '../../utils/route-colors';
8
+
9
+ export default class ModalsOrderChangeDestinationComponent extends Component {
10
+ @service fetch;
11
+ @service notifications;
12
+ @tracked selectedOption;
13
+
14
+ get order() {
15
+ return this.args.options?.order;
16
+ }
17
+
18
+ get waypoints() {
19
+ const waypoints = this.order?.payload?.waypoints;
20
+ if (typeof waypoints?.toArray === 'function') {
21
+ return waypoints.toArray();
22
+ }
23
+
24
+ return Array.isArray(waypoints) ? waypoints : Array.from(waypoints ?? []);
25
+ }
26
+
27
+ get routeColor() {
28
+ return colorForId(this.order?.public_id ?? this.order?.id ?? 'order-route');
29
+ }
30
+
31
+ get serviceStops() {
32
+ return buildServiceStopsFromPayload(this.order?.payload);
33
+ }
34
+
35
+ get currentWaypointId() {
36
+ const activeStop = this.order?.tracker_data?.active_stop;
37
+ const payload = this.order?.payload;
38
+
39
+ return activeStop?.uuid ?? activeStop?.public_id ?? payload?.current_waypoint_uuid ?? payload?.current_waypoint;
40
+ }
41
+
42
+ get waypointOptions() {
43
+ return this.serviceStops.map((stop, index) => ({
44
+ waypoint: stop.place,
45
+ stop,
46
+ index,
47
+ id: this.waypointId(stop.place),
48
+ isCurrent: this.isCurrentWaypoint(stop.place),
49
+ isSelected: this.isSelectedStop(stop),
50
+ label: describeRoutePoint(stop, this.routeColor).label,
51
+ title: describeRoutePoint(stop, this.routeColor).title,
52
+ tracking: this.trackingForStop(stop),
53
+ }));
54
+ }
55
+
56
+ waypointId(waypoint) {
57
+ return waypoint?.id ?? waypoint?.public_id ?? waypoint?.uuid;
58
+ }
59
+
60
+ isCurrentWaypoint(waypoint) {
61
+ return [waypoint?.id, waypoint?.uuid, waypoint?.public_id, waypoint?.waypoint_public_id].includes(this.currentWaypointId);
62
+ }
63
+
64
+ isSelectedStop(stop) {
65
+ const selectedPlace = this.selectedOption?.waypoint;
66
+
67
+ return [selectedPlace?.id, selectedPlace?.uuid, selectedPlace?.public_id, selectedPlace?.waypoint_public_id].includes(this.waypointId(stop?.place));
68
+ }
69
+
70
+ trackingForStop(stop) {
71
+ return stop?.place?.tracking ?? stop?.place?.tracking_number ?? stop?.trackingNumberUuid ?? this.order?.tracking ?? this.order?.tracking_number;
72
+ }
73
+
74
+ @action selectOption(option) {
75
+ if (!option?.isCurrent) {
76
+ this.selectedOption = option;
77
+ }
78
+ }
79
+
80
+ @task *confirmSelection(modal) {
81
+ const option = this.selectedOption;
82
+ const waypoint = option?.waypoint;
83
+ const waypointId = this.waypointId(waypoint);
84
+ if (!waypointId) {
85
+ modal.stopLoading();
86
+ return this.notifications.warning('Select a destination first.');
87
+ }
88
+
89
+ modal.startLoading();
90
+
91
+ try {
92
+ const order = yield this.fetch.patch(`orders/set-destination/${this.order.id}/${waypointId}`);
93
+ this.notifications.success(`Current destination changed to ${waypoint.name ?? waypoint.address ?? waypointId}.`);
94
+
95
+ if (typeof this.args.options?.onChange === 'function') {
96
+ yield this.args.options.onChange(order, waypoint);
97
+ }
98
+
99
+ modal.done();
100
+ } catch (error) {
101
+ this.notifications.serverError(error);
102
+ } finally {
103
+ modal.stopLoading();
104
+ }
105
+ }
106
+ }
@@ -2,6 +2,8 @@ import Component from '@glimmer/component';
2
2
  import { tracked } from '@glimmer/tracking';
3
3
  import { inject as service } from '@ember/service';
4
4
 
5
+ const INTERNAL_NAMESPACE = 'int/v1';
6
+
5
7
  export default class ModalsResetCustomerCredentialsComponent extends Component {
6
8
  @service fetch;
7
9
  @service notifications;
@@ -26,12 +28,16 @@ export default class ModalsResetCustomerCredentialsComponent extends Component {
26
28
  modal.startLoading();
27
29
 
28
30
  try {
29
- await this.fetch.post('customers/reset-credentials', {
30
- customer: this.customer.id,
31
- password: this.password,
32
- password_confirmation: this.confirmPassword,
33
- send_credentials: this.sendCredentials,
34
- });
31
+ await this.fetch.post(
32
+ 'customers/reset-credentials',
33
+ {
34
+ customer: this.customer.id,
35
+ password: this.password,
36
+ password_confirmation: this.confirmPassword,
37
+ send_credentials: this.sendCredentials,
38
+ },
39
+ { namespace: INTERNAL_NAMESPACE }
40
+ );
35
41
 
36
42
  this.notifications.success('Customer password reset.');
37
43
 
@@ -0,0 +1,28 @@
1
+ <Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
2
+ <div class="modal-body-container">
3
+ <div class="mb-4 rounded-md border border-gray-200 bg-gray-50 px-4 py-3 dark:border-gray-700 dark:bg-gray-800">
4
+ <div class="text-xs font-semibold uppercase text-gray-500 dark:text-gray-400">Stop</div>
5
+ <div class="mt-1 text-sm font-semibold text-gray-900 dark:text-gray-100">{{this.stopName}}</div>
6
+ <div class="mt-1 text-xs text-gray-500 dark:text-gray-400">{{or this.place.address this.place.street1}}</div>
7
+ </div>
8
+
9
+ {{#if this.loadActivity.isRunning}}
10
+ <div class="flex items-center justify-center p-8 text-sm text-gray-500 dark:text-gray-400">
11
+ <FaIcon @icon="spinner" @spin={{true}} class="mr-2" />
12
+ Loading activity...
13
+ </div>
14
+ {{else if this.trackingNumberUuid}}
15
+ <Order::ActivityList @activity={{this.activity}} @resource={{hash status=this.stop.status}} />
16
+ {{else}}
17
+ <div class="min-h-24 pb-4">
18
+ <div class="flex items-center justify-center rounded-xl border border-dashed border-gray-300 bg-gray-50 p-6 dark:border-gray-600 dark:bg-gray-800">
19
+ <div class="text-center">
20
+ <FaIcon @icon="layer-group" @size="2x" class="mb-2 text-gray-400" />
21
+ <div class="text-sm font-medium text-gray-600 dark:text-gray-400">No stop activity</div>
22
+ <div class="mt-1 text-xs text-gray-500 dark:text-gray-500">This stop does not have a separate activity stream.</div>
23
+ </div>
24
+ </div>
25
+ </div>
26
+ {{/if}}
27
+ </div>
28
+ </Modal::Default>
@@ -0,0 +1,52 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { inject as service } from '@ember/service';
4
+ import { task } from 'ember-concurrency';
5
+
6
+ export default class ModalsStopActivityComponent extends Component {
7
+ @service store;
8
+ @service notifications;
9
+ @tracked activity = [];
10
+
11
+ constructor() {
12
+ super(...arguments);
13
+ this.loadActivity.perform();
14
+ }
15
+
16
+ get stop() {
17
+ return this.args.options?.stop;
18
+ }
19
+
20
+ get place() {
21
+ return this.stop?.place ?? this.stop;
22
+ }
23
+
24
+ get order() {
25
+ return this.args.options?.order;
26
+ }
27
+
28
+ get stopName() {
29
+ return this.place?.name ?? this.place?.address ?? this.place?.id;
30
+ }
31
+
32
+ get trackingNumberUuid() {
33
+ return this.stop?.trackingNumberUuid ?? this.stop?.tracking_number_uuid ?? this.stop?.tracking_number?.uuid ?? this.place?.tracking_number_uuid ?? this.place?.tracking_number?.uuid;
34
+ }
35
+
36
+ @task *loadActivity() {
37
+ if (!this.trackingNumberUuid) {
38
+ this.activity = [];
39
+ return [];
40
+ }
41
+
42
+ try {
43
+ const activity = yield this.store.query('tracking-status', {
44
+ tracking_number_uuid: this.trackingNumberUuid,
45
+ });
46
+ this.activity = activity.toArray();
47
+ return activity;
48
+ } catch (error) {
49
+ this.notifications.serverError(error);
50
+ }
51
+ }
52
+ }
@@ -16,6 +16,23 @@
16
16
  </InfoBlock>
17
17
  {{/if}}
18
18
 
19
+ {{#if this.currentWaypoint}}
20
+ <div class="my-4 rounded-md border border-blue-200 bg-blue-50 px-4 py-3 text-blue-900 dark:border-blue-700 dark:bg-blue-900 dark:text-blue-50">
21
+ <div class="text-xs font-semibold uppercase">Updating activity for</div>
22
+ <div class="mt-1 flex items-start justify-between gap-3">
23
+ <div class="min-w-0">
24
+ <div class="truncate text-sm font-semibold">{{or this.currentWaypoint.name this.currentWaypoint.public_id}}</div>
25
+ <div class="truncate text-xs">{{or (place-address-html this.currentWaypoint showTitle=false) this.currentWaypoint.address this.currentWaypoint.street1}}</div>
26
+ </div>
27
+ {{#if this.currentWaypoint.status_code}}
28
+ <Badge @status={{dasherize this.currentWaypoint.status_code}} class="uppercase">{{this.currentWaypoint.status_code}}</Badge>
29
+ {{else if this.currentWaypoint.status}}
30
+ <Badge @status={{dasherize this.currentWaypoint.status}} class="uppercase">{{this.currentWaypoint.status}}</Badge>
31
+ {{/if}}
32
+ </div>
33
+ </div>
34
+ {{/if}}
35
+
19
36
  <fieldset class="my-5">
20
37
  <legend class="sr-only">
21
38
  {{t "modals.order-new-activity.activity-options"}}
@@ -36,14 +53,18 @@
36
53
  </div>
37
54
  <label for={{concat "activity_" index}} class="ml-3 flex flex-col cursor-pointer w-full">
38
55
  <span class="block text-sm font-semibold">
39
- {{activity.status}}
56
+ {{or activity._resolved_status activity.status}}
40
57
  </span>
41
58
  <span class="block text-sm">
42
- {{activity.details}}
59
+ {{or activity._resolved_details activity.details}}
43
60
 
44
61
  {{#if (eq activity.code "dispatched")}}
45
62
  <InfoBlock @type="warning" @size="xs" @icon="exclamation-triangle" @text={{t "modals.order-new-activity.dispatch"}} class="mt-1" />
46
63
  {{/if}}
64
+
65
+ {{#if activity.require_pod}}
66
+ <InfoBlock @type="warning" @size="xs" @icon="camera" @text="Requires photo proof of delivery" class="mt-1" />
67
+ {{/if}}
47
68
  </span>
48
69
  </label>
49
70
  <div>
@@ -88,5 +109,60 @@
88
109
  </div>
89
110
  </div>
90
111
  </fieldset>
112
+
113
+ {{#if this.selectedActivityRequiresPod}}
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>
146
+ {{#if this.proofFiles}}
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>
164
+ {{/if}}
165
+ </div>
166
+ {{/if}}
91
167
  </div>
92
168
  </Modal::Default>