@fleetbase/fleetops-engine 0.6.70 → 0.6.72
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/RELEASE.md +18 -47
- package/addon/components/admin/tracking-page-settings.hbs +56 -0
- package/addon/components/admin/tracking-page-settings.js +67 -0
- package/addon/components/device/form.hbs +2 -2
- package/addon/components/driver/details.hbs +1 -1
- package/addon/components/driver/form.hbs +1 -1
- package/addon/components/equipment/form.hbs +1 -1
- package/addon/components/layout/fleet-ops-sidebar.js +11 -3
- package/addon/components/maintenance/form.hbs +3 -3
- package/addon/components/map/google-live-map.js +2 -2
- package/addon/components/map/leaflet-live-map.js +45 -32
- package/addon/components/orchestrator/phase-builder.js +2 -1
- package/addon/components/orchestrator-workbench.hbs +7 -1
- package/addon/components/orchestrator-workbench.js +16 -2
- package/addon/components/order/details/activity.hbs +8 -6
- package/addon/components/order/details/activity.js +9 -0
- package/addon/components/order/details.hbs +29 -0
- package/addon/components/order/details.js +26 -0
- package/addon/components/order/form/details.hbs +30 -4
- package/addon/components/order/form/details.js +46 -0
- package/addon/components/order/form/route.hbs +5 -3
- package/addon/components/order/form/route.js +24 -1
- package/addon/components/order/form.hbs +12 -0
- package/addon/components/order/form.js +21 -1
- package/addon/components/order/kanban.js +72 -0
- package/addon/components/order-config-manager/activity-flow.js +45 -10
- package/addon/components/order-config-manager/lifecycle.hbs +250 -0
- package/addon/components/order-config-manager/lifecycle.js +358 -0
- package/addon/components/order-config-manager.js +5 -1
- package/addon/components/service-rate/details.hbs +1 -1
- package/addon/components/service-rate/form.hbs +1 -1
- package/addon/components/tracking-page/full-view.hbs +276 -0
- package/addon/components/tracking-page/full-view.js +474 -0
- package/addon/components/tracking-page/locale-selector.js +30 -0
- package/addon/components/tracking-page/map.hbs +44 -0
- package/addon/components/tracking-page/map.js +328 -0
- package/addon/components/tracking-page/order-list.hbs +35 -0
- package/addon/components/tracking-page/public-view.hbs +60 -0
- package/addon/components/tracking-page/settings-preview.hbs +21 -0
- package/addon/components/tracking-page/settings-preview.js +23 -0
- package/addon/components/tracking-page/verify-panel.hbs +109 -0
- package/addon/components/tracking-page.hbs +142 -0
- package/addon/components/tracking-page.js +564 -0
- package/addon/components/trailer/form.hbs +2 -2
- package/addon/components/vehicle/form.hbs +3 -3
- package/addon/components/warranty/form.hbs +2 -2
- package/addon/components/widget/live-fleet.js +54 -6
- package/addon/components/widget/proof-of-delivery.hbs +49 -0
- package/addon/components/widget/proof-of-delivery.js +62 -0
- package/addon/components/work-order/form.hbs +3 -3
- package/addon/controllers/analytics/reports/index.js +10 -1
- package/addon/controllers/connectivity/devices/index/details.js +1 -0
- package/addon/controllers/connectivity/devices/index.js +8 -2
- package/addon/controllers/connectivity/events/index.js +31 -1
- package/addon/controllers/connectivity/sensors/index/details.js +1 -1
- package/addon/controllers/connectivity/sensors/index.js +37 -1
- package/addon/controllers/maintenance/equipment/index/details.js +2 -2
- package/addon/controllers/maintenance/equipment/index.js +28 -9
- package/addon/controllers/maintenance/inspection-forms/index.js +32 -12
- package/addon/controllers/maintenance/inspection-submissions/index/details.js +2 -1
- package/addon/controllers/maintenance/inspection-submissions/index.js +49 -9
- package/addon/controllers/maintenance/maintenances/index/details.js +2 -0
- package/addon/controllers/maintenance/maintenances/index.js +20 -1
- package/addon/controllers/maintenance/parts/index/details.js +2 -2
- package/addon/controllers/maintenance/parts/index.js +28 -8
- package/addon/controllers/maintenance/schedules/index.js +33 -4
- package/addon/controllers/maintenance/work-orders/index/details.js +3 -3
- package/addon/controllers/maintenance/work-orders/index.js +39 -8
- package/addon/controllers/management/contacts/customers.js +13 -0
- package/addon/controllers/management/contacts/index.js +43 -1
- package/addon/controllers/management/drivers/index/details.js +1 -0
- package/addon/controllers/management/drivers/index.js +32 -2
- package/addon/controllers/management/fleets/index/details.js +1 -0
- package/addon/controllers/management/fleets/index.js +39 -1
- package/addon/controllers/management/fuel-reports/index.js +22 -2
- package/addon/controllers/management/fuel-transactions/index.js +43 -7
- package/addon/controllers/management/issues/index/details.js +1 -0
- package/addon/controllers/management/issues/index.js +22 -2
- package/addon/controllers/management/places/index.js +36 -1
- package/addon/controllers/management/trailers/index/details.js +1 -0
- package/addon/controllers/management/trailers/index.js +50 -2
- package/addon/controllers/management/vehicles/index/details.js +1 -0
- package/addon/controllers/management/vehicles/index.js +33 -2
- package/addon/controllers/management/vendors/index.js +26 -2
- package/addon/controllers/operations/orders/index/details.js +50 -7
- package/addon/controllers/operations/orders/index.js +59 -4
- package/addon/controllers/operations/scheduler/index.js +45 -14
- package/addon/controllers/operations/service-rates/index.js +15 -1
- package/addon/controllers/settings/index.js +6 -0
- package/addon/controllers/settings/map.js +75 -0
- package/addon/controllers/settings/orchestrator.js +8 -6
- package/addon/controllers/settings/tracking-page.js +212 -0
- package/addon/extension.js +97 -1
- package/addon/helpers/parcel-box-image.js +16 -0
- package/addon/routes/operations/orders/index/details.js +1 -0
- package/addon/routes/operations/orders/index/new.js +5 -4
- package/addon/routes/operations/scheduler/index.js +1 -1
- package/addon/routes/settings/tracking-page.js +31 -0
- package/addon/routes.js +1 -0
- package/addon/services/contact-actions.js +1 -0
- package/addon/services/customer-actions.js +2 -1
- package/addon/services/device-actions.js +1 -0
- package/addon/services/device-event-actions.js +1 -0
- package/addon/services/driver-actions.js +4 -13
- package/addon/services/equipment-actions.js +1 -0
- package/addon/services/fleet-actions.js +1 -0
- package/addon/services/fuel-report-actions.js +1 -0
- package/addon/services/inspection-form-actions.js +1 -0
- package/addon/services/inspection-submission-actions.js +1 -0
- package/addon/services/integrated-vendor-actions.js +1 -0
- package/addon/services/issue-actions.js +1 -0
- package/addon/services/location.js +69 -21
- package/addon/services/maintenance-actions.js +1 -0
- package/addon/services/maintenance-schedule-actions.js +2 -1
- package/addon/services/map-settings.js +28 -0
- package/addon/services/order-actions.js +7 -0
- package/addon/services/order-allocation.js +1 -1
- package/addon/services/order-presentation.js +129 -0
- package/addon/services/part-actions.js +1 -0
- package/addon/services/place-actions.js +1 -0
- package/addon/services/sensor-actions.js +1 -0
- package/addon/services/service-area-actions.js +1 -0
- package/addon/services/service-rate-actions.js +1 -0
- package/addon/services/telematic-actions.js +1 -0
- package/addon/services/trailer-actions.js +4 -14
- package/addon/services/vehicle-actions.js +4 -13
- package/addon/services/vendor-actions.js +1 -0
- package/addon/services/work-order-actions.js +1 -0
- package/addon/services/zone-actions.js +1 -0
- package/addon/styles/fleetops-engine.css +1391 -0
- package/addon/templates/analytics/reports/index/details.hbs +1 -0
- package/addon/templates/analytics/reports/index.hbs +1 -0
- package/addon/templates/connectivity/devices/index/details.hbs +1 -0
- package/addon/templates/connectivity/devices/index.hbs +1 -0
- package/addon/templates/connectivity/events/index.hbs +1 -0
- package/addon/templates/connectivity/sensors/index/details.hbs +1 -0
- package/addon/templates/connectivity/sensors/index.hbs +1 -0
- package/addon/templates/maintenance/equipment/index/details.hbs +1 -0
- package/addon/templates/maintenance/equipment/index.hbs +2 -0
- package/addon/templates/maintenance/inspection-forms/index/details.hbs +1 -0
- package/addon/templates/maintenance/inspection-forms/index.hbs +1 -0
- package/addon/templates/maintenance/inspection-submissions/index/details.hbs +1 -0
- package/addon/templates/maintenance/inspection-submissions/index.hbs +1 -0
- package/addon/templates/maintenance/maintenances/index/details.hbs +1 -0
- package/addon/templates/maintenance/maintenances/index.hbs +1 -0
- package/addon/templates/maintenance/parts/index/details.hbs +1 -0
- package/addon/templates/maintenance/parts/index.hbs +2 -0
- package/addon/templates/maintenance/schedules/index/details.hbs +1 -0
- package/addon/templates/maintenance/schedules/index.hbs +2 -0
- package/addon/templates/maintenance/work-orders/index/details.hbs +1 -0
- package/addon/templates/maintenance/work-orders/index.hbs +1 -0
- package/addon/templates/management/contacts/customers/details.hbs +1 -0
- package/addon/templates/management/contacts/customers.hbs +1 -0
- package/addon/templates/management/contacts/index/details.hbs +1 -0
- package/addon/templates/management/contacts/index.hbs +1 -0
- package/addon/templates/management/contacts.hbs +1 -0
- package/addon/templates/management/drivers/index/details.hbs +1 -0
- package/addon/templates/management/drivers/index.hbs +2 -0
- package/addon/templates/management/fleets/index/details.hbs +1 -0
- package/addon/templates/management/fleets/index.hbs +1 -0
- package/addon/templates/management/fuel-reports/index/details.hbs +1 -0
- package/addon/templates/management/fuel-reports/index.hbs +1 -0
- package/addon/templates/management/fuel-transactions/index.hbs +1 -0
- package/addon/templates/management/issues/index/details.hbs +1 -0
- package/addon/templates/management/issues/index.hbs +1 -0
- package/addon/templates/management/places/index/details.hbs +1 -0
- package/addon/templates/management/places/index.hbs +1 -0
- package/addon/templates/management/trailers/index/details.hbs +1 -0
- package/addon/templates/management/trailers/index.hbs +2 -0
- package/addon/templates/management/vehicles/index/details.hbs +1 -0
- package/addon/templates/management/vehicles/index.hbs +2 -0
- package/addon/templates/management/vendors/index/details.hbs +1 -0
- package/addon/templates/management/vendors/index.hbs +2 -0
- package/addon/templates/management/vendors/integrated.hbs +1 -0
- package/addon/templates/operations/orders/index/details.hbs +1 -0
- package/addon/templates/operations/orders/index.hbs +1 -0
- package/addon/templates/operations/service-rates/index/details.hbs +1 -0
- package/addon/templates/operations/service-rates/index.hbs +1 -0
- package/addon/templates/settings/map.hbs +46 -0
- package/addon/templates/settings/tracking-page.hbs +318 -0
- package/addon/utils/identity-cell-resource.js +2 -56
- package/addon/utils/map-default-view.js +132 -0
- package/addon/utils/normalize-order-config-flow.js +6 -1
- package/addon/utils/placeholder-images.js +4 -69
- package/addon/utils/resource-descriptors/assets.js +2 -57
- package/addon/utils/resource-descriptors/helpers.js +23 -122
- package/addon/utils/resource-descriptors/operations.js +2 -61
- package/addon/utils/resource-descriptors/people.js +3 -142
- package/addon/utils/resource-descriptors/shared.js +18 -0
- package/addon/utils/tracking-live-source.js +123 -0
- package/addon/utils/tracking-page-theme.js +123 -0
- package/app/components/admin/tracking-page-settings.js +1 -0
- package/app/components/tracking-page/full-view.js +1 -0
- package/app/components/tracking-page/locale-selector.js +1 -0
- package/app/components/tracking-page/map.js +1 -0
- package/app/components/tracking-page/order-list.js +1 -0
- package/app/components/tracking-page/public-view.js +1 -0
- package/app/components/tracking-page/settings-preview.js +1 -0
- package/app/components/tracking-page/verify-panel.js +1 -0
- package/app/components/tracking-page.js +1 -0
- package/app/components/widget/proof-of-delivery.js +1 -0
- package/app/controllers/settings/tracking-page.js +1 -0
- package/app/helpers/parcel-box-image.js +1 -0
- package/app/routes/settings/tracking-page.js +1 -0
- package/app/services/order-presentation.js +1 -0
- package/app/templates/settings/tracking-page.js +1 -0
- package/app/utils/map-default-view.js +1 -0
- package/app/utils/tracking-live-source.js +1 -0
- package/app/utils/tracking-page-theme.js +1 -0
- package/composer.json +2 -2
- package/extension.json +1 -1
- package/package.json +4 -4
- package/scripts/pest-bootstrap.php +4 -0
- package/server/config/fleetops.php +4 -0
- package/server/migrations/2026_10_08_000001_create_tracking_sessions_tables.php +51 -0
- package/server/migrations/2026_10_10_000001_add_trashed_index_to_device_events.php +40 -0
- package/server/migrations/2026_10_10_000002_add_trashed_index_to_positions.php +40 -0
- package/server/migrations/2026_10_12_000001_add_deleted_at_to_tracking_sessions_tables.php +32 -0
- package/server/resources/views/labels/default.php +2 -2
- package/server/resources/views/labels/entity-label.php +2 -2
- package/server/resources/views/labels/waypoint-label.php +2 -2
- package/server/src/Auth/Schemas/FleetOps.php +5 -0
- package/server/src/Console/Commands/ProcessOperationalAlerts.php +1 -0
- package/server/src/Console/Commands/PruneTelematicsData.php +101 -26
- package/server/src/Events/OrderChanged.php +257 -0
- package/server/src/Events/OrderDriverUnassigned.php +68 -0
- package/server/src/Exceptions/OrderEditLockedException.php +32 -0
- package/server/src/Expansions/CompanyExpansion.php +3 -2
- package/server/src/Expansions/UserExpansion.php +12 -10
- package/server/src/Http/Controllers/Api/v1/CustomerController.php +108 -18
- package/server/src/Http/Controllers/Api/v1/DriverController.php +95 -10
- package/server/src/Http/Controllers/Api/v1/EntityController.php +66 -0
- package/server/src/Http/Controllers/Api/v1/FleetController.php +10 -4
- package/server/src/Http/Controllers/Api/v1/OrderController.php +139 -36
- package/server/src/Http/Controllers/Api/v1/PayloadController.php +76 -8
- package/server/src/Http/Controllers/Api/v1/PlaceController.php +3 -3
- package/server/src/Http/Controllers/Api/v1/TrackingNumberController.php +6 -32
- package/server/src/Http/Controllers/Api/v1/VehicleController.php +5 -0
- package/server/src/Http/Controllers/Internal/v1/ContactController.php +3 -0
- package/server/src/Http/Controllers/Internal/v1/DriverController.php +10 -0
- package/server/src/Http/Controllers/Internal/v1/FleetController.php +16 -2
- package/server/src/Http/Controllers/Internal/v1/InspectionSubmissionController.php +13 -8
- package/server/src/Http/Controllers/Internal/v1/IssueController.php +2 -0
- package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +4 -2
- package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +93 -11
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +118 -28
- package/server/src/Http/Controllers/Internal/v1/PositionController.php +22 -0
- package/server/src/Http/Controllers/Internal/v1/ProofController.php +55 -4
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +303 -10
- package/server/src/Http/Controllers/Internal/v1/TrailerController.php +1 -0
- package/server/src/Http/Controllers/Internal/v1/VehicleController.php +1 -0
- package/server/src/Http/Controllers/Public/TrackingController.php +518 -0
- package/server/src/Http/Filter/ContactFilter.php +1 -1
- package/server/src/Http/Filter/InspectionFormFilter.php +23 -0
- package/server/src/Http/Filter/InspectionSubmissionFilter.php +23 -0
- package/server/src/Http/Filter/MaintenanceFilter.php +23 -0
- package/server/src/Http/Filter/OrderFilter.php +39 -4
- package/server/src/Http/Filter/ProofFilter.php +18 -0
- package/server/src/Http/Filter/RouteFilter.php +18 -0
- package/server/src/Http/Filter/TelematicFilter.php +23 -0
- package/server/src/Http/Filter/WarrantyFilter.php +23 -0
- package/server/src/Http/Requests/CancelOrderRequest.php +2 -1
- package/server/src/Http/Resources/v1/Concerns/ResolvesPublicRelationFields.php +14 -1
- package/server/src/Http/Resources/v1/Index/Customer.php +18 -0
- package/server/src/Http/Resources/v1/Index/Order.php +11 -2
- package/server/src/Http/Resources/v1/PublicOrderTracking.php +153 -0
- package/server/src/Http/Resources/v1/TrackingNumber.php +8 -34
- package/server/src/Http/Resources/v1/Zone.php +31 -23
- package/server/src/Jobs/FinalizeApiOrderCreation.php +3 -1
- package/server/src/Jobs/ProcessAllocationJob.php +16 -6
- package/server/src/Jobs/PublishTrackingUpdate.php +50 -0
- package/server/src/Listeners/HandleGeofenceEntered.php +31 -15
- package/server/src/Listeners/HandleOrderCanceled.php +9 -1
- package/server/src/Listeners/HandleUserRemovedFromCompany.php +2 -1
- package/server/src/Listeners/PublishTrackingUpdates.php +27 -0
- package/server/src/Mail/CustomerCredentialsMail.php +4 -2
- package/server/src/Models/AssetConnection.php +3 -0
- package/server/src/Models/Contact.php +3 -0
- package/server/src/Models/Device.php +28 -0
- package/server/src/Models/DeviceEvent.php +7 -0
- package/server/src/Models/Driver.php +20 -7
- package/server/src/Models/Entity.php +12 -4
- package/server/src/Models/Fleet.php +11 -32
- package/server/src/Models/FleetDriver.php +3 -0
- package/server/src/Models/FleetVehicle.php +3 -0
- package/server/src/Models/FuelReport.php +5 -13
- package/server/src/Models/Issue.php +4 -16
- package/server/src/Models/Order.php +234 -11
- package/server/src/Models/OrderConfig.php +129 -0
- package/server/src/Models/Payload.php +227 -35
- package/server/src/Models/Place.php +7 -1
- package/server/src/Models/ServiceArea.php +7 -0
- package/server/src/Models/ServiceRate.php +7 -17
- package/server/src/Models/TrackingNumber.php +109 -6
- package/server/src/Models/TrackingSession.php +33 -0
- package/server/src/Models/TrackingSessionGrant.php +22 -0
- package/server/src/Models/TrackingStatus.php +6 -0
- package/server/src/Models/Vehicle.php +21 -2
- package/server/src/Models/Vendor.php +7 -0
- package/server/src/Models/Waypoint.php +13 -4
- package/server/src/Notifications/OrderAssigned.php +2 -1
- package/server/src/Notifications/OrderCanceled.php +7 -1
- package/server/src/Notifications/OrderChanged.php +87 -0
- package/server/src/Notifications/OrderCompleted.php +2 -1
- package/server/src/Notifications/OrderDispatchFailed.php +2 -1
- package/server/src/Notifications/OrderDispatched.php +2 -1
- package/server/src/Notifications/OrderFailed.php +2 -1
- package/server/src/Notifications/OrderUnassigned.php +66 -0
- package/server/src/Notifications/WaypointCompleted.php +2 -1
- package/server/src/Observers/CompanyUserObserver.php +8 -7
- package/server/src/Observers/DriverObserver.php +6 -1
- package/server/src/Observers/OrderObserver.php +141 -1
- package/server/src/Observers/TrackingNumberObserver.php +18 -6
- package/server/src/Observers/UserObserver.php +2 -1
- package/server/src/Observers/VehicleObserver.php +8 -3
- package/server/src/Orchestration/Engines/RouteSequencingEngine.php +21 -3
- package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +38 -8
- package/server/src/Orchestration/OrchestrationEngineRegistry.php +61 -1
- package/server/src/Orchestration/Support/OrchestratorSettings.php +61 -0
- package/server/src/Orchestration/Support/RouteMetrics.php +165 -0
- package/server/src/Providers/EventServiceProvider.php +11 -0
- package/server/src/Providers/FleetOpsServiceProvider.php +13 -0
- package/server/src/Support/ApiCacheThrottle.php +76 -0
- package/server/src/Support/LiveOrderQuery.php +3 -1
- package/server/src/Support/OrderEdit.php +365 -0
- package/server/src/Support/OrderEditPolicy.php +292 -0
- package/server/src/Support/ProfileAccountManager.php +54 -1
- package/server/src/Support/ResolvesOrderServiceStops.php +5 -0
- package/server/src/Support/SocketChannels.php +346 -0
- package/server/src/Support/Telematics/TelematicService.php +7 -2
- package/server/src/Support/Telematics/Telemetry/Inbox.php +8 -1
- package/server/src/Support/TrackingChannel.php +55 -0
- package/server/src/Support/TrackingCode.php +242 -0
- package/server/src/Support/TrackingPage/TrackingAccess.php +356 -0
- package/server/src/Support/TrackingPage/TrackingPageConfig.php +264 -0
- package/server/src/Support/TrackingPage/TrackingRecipient.php +111 -0
- package/server/src/Support/TrackingPage/TrackingResolver.php +201 -0
- package/server/src/Support/TrackingPage/TrackingTarget.php +40 -0
- package/server/src/Support/TrackingPage/TrackingView.php +543 -0
- package/server/src/Support/TrackingPublisher.php +261 -0
- package/server/src/Support/TrackingScope.php +182 -0
- package/server/src/Support/TrackingUpdate.php +402 -0
- package/server/src/Support/Utils.php +15 -3
- package/server/src/Traits/HasTrackingNumber.php +2 -0
- package/server/src/routes.php +39 -7
- package/server/tests/ApiCustomerControllerContractsTest.php +54 -7
- package/server/tests/ApiDriverPasswordContractsTest.php +25 -21
- package/server/tests/ApiEntityControllerContractsTest.php +84 -0
- package/server/tests/ApiOrderControllerContractsTest.php +245 -0
- package/server/tests/ApiPayloadControllerContractsTest.php +121 -0
- package/server/tests/ApiPlaceControllerContractsTest.php +1 -1
- package/server/tests/ApiTrackingNumberControllerContractsTest.php +6 -9
- package/server/tests/CompactResourceSerializationTest.php +23 -59
- package/server/tests/ControllerFilterContractsTest.php +1 -1
- package/server/tests/CustomerContactIdentitySafetyTest.php +1 -1
- package/server/tests/EventContractsTest.php +26 -60
- package/server/tests/ExpansionContractsTest.php +15 -2
- package/server/tests/Feature/Http/Api/CustomerControllerHelperSeamsTest.php +29 -3
- package/server/tests/Feature/Http/Api/DriverControllerAuthFlowsTest.php +23 -0
- package/server/tests/Feature/Http/Api/OrderControllerNearbyFiltersTest.php +16 -16
- package/server/tests/Feature/Http/Api/OrderControllerQueryFiltersTest.php +41 -14
- package/server/tests/Feature/Http/Api/TrackingNumberControllerHelpersTest.php +57 -22
- package/server/tests/Feature/Http/Internal/DriverControllerCreateRecordTest.php +23 -9
- package/server/tests/Feature/Http/Internal/OrchestrationControllerContractsTest.php +171 -1
- package/server/tests/Feature/Http/Internal/OrderControllerActivityFlowsTest.php +72 -0
- package/server/tests/Feature/Http/Internal/OrderControllerContractsTest.php +110 -14
- package/server/tests/Feature/Http/Internal/OrderControllerCreateRecordTest.php +26 -0
- package/server/tests/Feature/Http/Internal/OrderControllerHelpersTest.php +7 -5
- package/server/tests/Feature/Http/Internal/PositionControllerReplayChannelTest.php +135 -0
- package/server/tests/Feature/Http/Internal/ProofControllerContractsTest.php +3 -3
- package/server/tests/Feature/Http/Internal/ProofControllerRecentTest.php +170 -0
- package/server/tests/Feature/Http/Public/TrackingControllerTest.php +495 -0
- package/server/tests/IndexCustomerPhotoTest.php +151 -0
- package/server/tests/ModelAccessorContractsTest.php +4 -3
- package/server/tests/NotificationAndMailContractsTest.php +5 -8
- package/server/tests/ObserverContractsTest.php +72 -10
- package/server/tests/OrderActivityFlowRegressionTest.php +3 -2
- package/server/tests/OrderConfigLifecycleTest.php +120 -0
- package/server/tests/OrderFilterExecutionTest.php +167 -2
- package/server/tests/ProcessAllocationJobContractsTest.php +20 -0
- package/server/tests/PublicOrderTrackingResourceTest.php +221 -0
- package/server/tests/QueueJobContractsTest.php +2 -1
- package/server/tests/RequestContractsTest.php +1 -1
- package/server/tests/RouteRegistrationExecutionTest.php +26 -0
- package/server/tests/RouteSequencingEngineTest.php +17 -0
- package/server/tests/SettingControllerContractsTest.php +110 -0
- package/server/tests/Support/TrackingPageDatabase.php +200 -0
- package/server/tests/TenantScopingTest.php +206 -0
- package/server/tests/TrackingPageSettingsTest.php +343 -0
- package/server/tests/Unit/Console/PruneTelematicsDataCommandTest.php +51 -0
- package/server/tests/Unit/Events/OrderDispatchFailedTest.php +2 -1
- package/server/tests/Unit/Http/Filter/ContactFilterAddressTest.php +61 -0
- package/server/tests/Unit/Http/Resources/IndexOrderResourceTest.php +30 -0
- package/server/tests/Unit/Jobs/FinalizeApiOrderCreationDispatchGuardTest.php +266 -0
- package/server/tests/Unit/Listeners/HandleGeofenceEnteredTest.php +182 -22
- package/server/tests/Unit/Listeners/HandleUserRemovedFromCompanyTest.php +5 -0
- package/server/tests/Unit/Models/DriverAssignVehicleTest.php +81 -0
- package/server/tests/Unit/Models/FleetTest.php +4 -3
- package/server/tests/Unit/Models/OrderCancelReasonTest.php +143 -0
- package/server/tests/Unit/Models/OrderPayloadConfigAndDistanceTest.php +60 -0
- package/server/tests/Unit/Models/OrderPayloadRouteTest.php +4 -2
- package/server/tests/Unit/Models/PayloadTest.php +18 -4
- package/server/tests/Unit/Models/ServiceRatePersistenceAndServicabilityTest.php +2 -2
- package/server/tests/Unit/Models/TrackingNumberTest.php +67 -6
- package/server/tests/Unit/Models/VehicleTest.php +16 -2
- package/server/tests/Unit/Models/WaypointTest.php +4 -3
- package/server/tests/Unit/Notifications/OrderChangeNotificationsTest.php +59 -0
- package/server/tests/Unit/Notifications/TerminalOrderNotificationsTest.php +3 -3
- package/server/tests/Unit/Observers/DriverDeletionCleanupTest.php +158 -0
- package/server/tests/Unit/Observers/FleetOpsCompanyUserAndServiceAreaObserversTest.php +20 -0
- package/server/tests/Unit/Observers/ObserverListenerSeamsTest.php +3 -3
- package/server/tests/Unit/Observers/OrderAssignmentObserverTest.php +100 -0
- package/server/tests/Unit/Observers/OrderDeleteCascadeTest.php +213 -0
- package/server/tests/Unit/Observers/VehicleObserverDeleteTest.php +67 -0
- package/server/tests/Unit/Orchestration/EngineSelectionAndRouteMetricsTest.php +336 -0
- package/server/tests/Unit/Orchestration/Engines/VroomOrchestrationEngineLifecycleTest.php +8 -1
- package/server/tests/Unit/Support/ApiCacheDependenciesTest.php +347 -0
- package/server/tests/Unit/Support/OrderEditPolicyTest.php +156 -0
- package/server/tests/Unit/Support/OrderOperationsTest.php +514 -0
- package/server/tests/Unit/Support/OsrmProofAndIssueFilterTest.php +4 -3
- package/server/tests/Unit/Support/ProfileAccountManagerTest.php +141 -0
- package/server/tests/Unit/Support/QuerySeamsTest.php +5 -1
- package/server/tests/Unit/Support/SocketChannelsTest.php +396 -0
- package/server/tests/Unit/Support/TrackingChannelTest.php +232 -0
- package/server/tests/Unit/Support/TrackingCodeTest.php +186 -0
- package/server/tests/Unit/Support/TrackingPage/TrackingAccessTest.php +200 -0
- package/server/tests/Unit/Support/TrackingPage/TrackingRecipientTest.php +51 -0
- package/server/tests/Unit/Support/TrackingPage/TrackingResolverTest.php +181 -0
- package/server/tests/Unit/Support/TrackingPage/TrackingViewTest.php +479 -0
- package/server/tests/Unit/Support/TrackingPublisherTest.php +425 -0
- package/server/tests/Unit/Support/TrackingUpdateTest.php +363 -0
- package/server/tests/Unit/Support/UtilsAdditionalTest.php +9 -0
- package/server/tests/Unit/Support/UtilsGeoMatrixAndVendorTest.php +25 -0
- package/server/tests/VehicleResourceContractsTest.php +46 -0
- package/server/tests/VroomOrchestrationEngineTest.php +34 -1
- package/server/tests/ZoneGeofenceSettingsResourceTest.php +121 -0
- package/translations/ar-ae.yml +274 -0
- package/translations/bg-bg.yaml +274 -0
- package/translations/en-us.yaml +347 -0
- package/translations/es-pa.yaml +274 -0
- package/translations/fr-fr.yaml +274 -0
- package/translations/mn-mn.yaml +274 -0
- package/translations/pt-br.yaml +274 -0
- package/translations/ru-ru.yaml +276 -2
- package/translations/uk-ua.yaml +275 -1
- package/translations/vi-vn.yaml +274 -0
- package/addon/components/cell/contact-identity.hbs +0 -1
- package/addon/components/cell/customer-identity.hbs +0 -1
- package/addon/components/cell/driver-identity.hbs +0 -1
- package/addon/components/cell/fleet-identity.hbs +0 -1
- package/addon/components/cell/order-identity.hbs +0 -1
- package/addon/components/cell/place-identity.hbs +0 -1
- package/addon/components/cell/vehicle-identity.hbs +0 -1
- package/addon/components/cell/vendor-identity.hbs +0 -1
- package/addon/components/contact/pill.hbs +0 -24
- package/addon/components/contact/summary.hbs +0 -1
- package/addon/components/customer/pill.hbs +0 -24
- package/addon/components/customer/summary.hbs +0 -1
- package/addon/components/driver/pill.hbs +0 -24
- package/addon/components/driver/summary.hbs +0 -1
- package/addon/components/fleet/pill.hbs +0 -24
- package/addon/components/fleet/summary.hbs +0 -1
- package/addon/components/order/pill.hbs +0 -24
- package/addon/components/order/summary.hbs +0 -1
- package/addon/components/place/pill.hbs +0 -24
- package/addon/components/place/summary.hbs +0 -1
- package/addon/components/select-option/contact.hbs +0 -12
- package/addon/components/select-option/customer.hbs +0 -12
- package/addon/components/select-option/driver.hbs +0 -12
- package/addon/components/select-option/fleet.hbs +0 -12
- package/addon/components/select-option/order.hbs +0 -12
- package/addon/components/select-option/place.hbs +0 -12
- package/addon/components/select-option/vehicle.hbs +0 -12
- package/addon/components/select-option/vendor.hbs +0 -12
- package/addon/components/vehicle/pill.hbs +0 -24
- package/addon/components/vehicle/summary.hbs +0 -1
- package/addon/components/vendor/pill.hbs +0 -24
- package/addon/components/vendor/summary.hbs +0 -1
- package/app/components/cell/contact-identity.js +0 -1
- package/app/components/cell/customer-identity.js +0 -1
- package/app/components/cell/driver-identity.js +0 -1
- package/app/components/cell/fleet-identity.js +0 -1
- package/app/components/cell/order-identity.js +0 -1
- package/app/components/cell/place-identity.js +0 -1
- package/app/components/cell/vehicle-identity.js +0 -1
- package/app/components/cell/vendor-identity.js +0 -1
- package/app/components/contact/pill.js +0 -1
- package/app/components/contact/summary.js +0 -1
- package/app/components/customer/pill.js +0 -1
- package/app/components/customer/summary.js +0 -1
- package/app/components/driver/pill.js +0 -1
- package/app/components/driver/summary.js +0 -1
- package/app/components/fleet/pill.js +0 -1
- package/app/components/fleet/summary.js +0 -1
- package/app/components/order/pill.js +0 -1
- package/app/components/order/summary.js +0 -1
- package/app/components/place/pill.js +0 -1
- package/app/components/place/summary.js +0 -1
- package/app/components/select-option/contact.js +0 -1
- package/app/components/select-option/customer.js +0 -1
- package/app/components/select-option/driver.js +0 -1
- package/app/components/select-option/fleet.js +0 -1
- package/app/components/select-option/order.js +0 -1
- package/app/components/select-option/place.js +0 -1
- package/app/components/select-option/vehicle.js +0 -1
- package/app/components/select-option/vendor.js +0 -1
- package/app/components/vehicle/pill.js +0 -1
- package/app/components/vehicle/summary.js +0 -1
- package/app/components/vendor/pill.js +0 -1
- package/app/components/vendor/summary.js +0 -1
package/RELEASE.md
CHANGED
|
@@ -1,58 +1,29 @@
|
|
|
1
|
-
> v0.6.
|
|
1
|
+
> v0.6.72 ~ "Customer tracking page settings, safer public tracking and structured label codes"
|
|
2
2
|
|
|
3
3
|
---
|
|
4
|
-
##
|
|
5
|
-
- **Telematics Data settings.** A new Fleet-Ops → Settings → Telematics Data page lets each organization decide how long telemetry is kept: device events, raw event payloads, positions, processed and quarantined delivery envelopes, and sync run diagnostics. Administrators set system-wide defaults from the admin console (Fleet-Ops Config → Telematics Data). A value of 0 keeps rows forever.
|
|
6
|
-
- **Scheduled retention sweep.** `fleetops:prune-telematics-data` runs every fifteen minutes and applies each organization's policy in bounded batches, so a large backlog is drained gradually without stalling ingestion. Defaults: events 30 days, raw payloads stripped after 7 days, positions 90 days, processed deliveries 24 hours, quarantined deliveries 7 days, sync runs 7 days.
|
|
7
|
-
- **Storage usage and on-demand cleanup.** The settings page shows rows, oldest row and estimated size per table for your organization, and "Run cleanup now" queues an immediate retention run.
|
|
4
|
+
## Highlights
|
|
8
5
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
## Upgrade notes
|
|
17
|
-
- New indexes on `device_events (company_uuid, created_at)`, `positions (company_uuid, created_at)` and `telematic_sync_runs (telematic_uuid, updated_at)`; on very large tables run `php artisan migrate` in a maintenance window.
|
|
18
|
-
- Retention is enabled by default. Existing rows older than the defaults are removed over successive runs after upgrading; set the values to 0 before upgrading if you need to keep history indefinitely.
|
|
19
|
-
- Deleting rows frees space inside the database files, not on disk. Run `OPTIMIZE TABLE` off-peak to return space to the operating system.
|
|
20
|
-
- Run `php artisan fleetbase:create-permissions` to register the `telematics-settings` permission and the Telematics Settings Manager policy and role.
|
|
6
|
+
- **Tracking Page settings.** A new Fleet-Ops → Settings → Tracking Page lets each organization configure the customer tracking page:
|
|
7
|
+
- its own page at `/t/{slug}`, with a live address check;
|
|
8
|
+
- whether its numbers can be tracked on the shared page;
|
|
9
|
+
- branding: display name, accent colour with a live contrast check, an optional dark-mode accent, support contacts, "Powered by Fleetbase", and languages;
|
|
10
|
+
- how customers verify (text message or email codes, session length, customer sign-in);
|
|
11
|
+
- what verified customers see.
|
|
21
12
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
## What's New
|
|
30
|
-
- **Report on orders, their items and tracking.** The report builder can answer questions like "Which products sold the most this month?" and "What did orders total this month?".
|
|
31
|
-
- Orders expose their ID and Internal ID, and relationships for tracking (tracking number, region, latest status), Order Config, Customer Vendor, Created By, Purchase Rate › Service Quote and Payload Return.
|
|
32
|
-
- `Payload` exposes its items with name, SKU, price, dimensions, metadata and destination. Select an item column for one row per item, or group by one to summarise per product.
|
|
33
|
-
- Summary columns count orders distinctly, so they stay correct when item rows are selected. Distance, duration and transaction amount have totals and averages.
|
|
34
|
-
- `meta` has no fixed shape, so no column assumes a key in it. Read keys with a computed column, for example `CAST(JSON_UNQUOTE(JSON_EXTRACT(payload.entities.meta, '$.quantity')) AS DECIMAL(15,2))`.
|
|
35
|
-
- Column labels drop the table name ("Type", not "Order Type"). Distance and duration are labelled in meters and seconds, and money columns are marked "(minor units)".
|
|
36
|
-
- **A cleaner default dashboard.** Fleet-Ops widgets declare their place on the default dashboard: Radar first, then Active Orders and Drivers Online, a full-width Live Fleet map, and Revenue Trend, Top Drivers and Maintenance side by side.
|
|
37
|
-
- **Live Fleet widget cards match the live map.** Clicking a driver or vehicle on the widget map shows the same card as **Operations › Live Map**.
|
|
13
|
+
Administrators set the shared page's defaults under Fleet-Ops Config → Tracking Page. The new tracking pages themselves arrive in a later release; these settings are what they will read.
|
|
14
|
+
- **Safer public order lookup.** `GET int/v1/fleet-ops/lookup` is rate limited per address and returns a minimal resource: tracking number, status, timeline, ETA, stop coordinates and items. It no longer returns notes, meta, internal ids, files, rates or customer details. The driver's location is included only while the order is under way. Unknown and malformed tracking numbers get the same answer.
|
|
15
|
+
- **Structured label codes.** Tracking-number QR codes now carry a tracking URL holding the tracking number and the owner's public id, which a phone camera opens as the tracking page. Barcodes use Code 128. Every scan endpoint still accepts the uuid codes already printed on parcels.
|
|
16
|
+
- **Fix: signatures on private S3 buckets.** Signatures were not stored on buckets that reject ACLs; they are now written without one and served through signed URLs. Legacy absolute avatar URLs on vehicles, drivers and places are re-signed when read.
|
|
17
|
+
- **Fix: the customer credentials email** linked to an unrouted page; it now links to `/customer-access/{slug}`.
|
|
18
|
+
- **Fix: parcel box images** on the service rate form and details showed broken images in production builds.
|
|
38
19
|
|
|
39
20
|
---
|
|
40
|
-
##
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
- Every report table, and the item join, leaves soft-deleted rows out on core-api v1.6.64 and later.
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
## Dependencies
|
|
47
|
-
- Order items, JSON totals and soft deletes in reports need `fleetbase/core-api` v1.6.64. The schema still registers on older versions.
|
|
48
|
-
- The report builder and the default dashboard order ship in `@fleetbase/ember-ui` v0.4.4.
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
## Testing
|
|
52
|
-
- Every declared report column and join key was checked against the database schema, and order reports were run against real storefront orders on MySQL in strict mode.
|
|
53
|
-
- Tests cover the report schema at 100% line coverage, the Live Fleet map cards and the default dashboard order.
|
|
21
|
+
## Upgrading
|
|
22
|
+
- Run `php artisan fleetbase:create-permissions` to register the new `tracking-page-settings` permission.
|
|
23
|
+
- Private-bucket signature storage pairs with fleetbase/core-api's `File::signStoredUrl()`; on older core-api releases stored avatar URLs pass through unchanged.
|
|
54
24
|
|
|
55
25
|
---
|
|
56
26
|
## Need help?
|
|
57
27
|
- [GitHub Discussions](https://github.com/fleetbase/fleetbase/discussions)
|
|
58
28
|
- [Discord](https://discord.gg/HnTqQ6zAVn)
|
|
29
|
+
---
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<ContentPanel @title="Generic Tracking Page" @open={{true}} @wrapperClass="bordered-classic">
|
|
2
|
+
<div class="space-y-5">
|
|
3
|
+
<div class="flex items-start justify-between gap-4">
|
|
4
|
+
<div>
|
|
5
|
+
<div class="text-sm font-semibold">Enable the generic tracking page</div>
|
|
6
|
+
<div class="text-xs text-gray-600 dark:text-gray-300">Serves /~/track for every organization that allows it. Organizations can also run their own page from their Fleet-Ops settings.</div>
|
|
7
|
+
</div>
|
|
8
|
+
<Toggle @isToggled={{this.genericEnabled}} @onToggle={{fn (mut this.genericEnabled)}} />
|
|
9
|
+
</div>
|
|
10
|
+
<div>
|
|
11
|
+
<label for="admin-tracking-page-name" class="block text-sm font-semibold mb-1">Display name</label>
|
|
12
|
+
<input id="admin-tracking-page-name" type="text" class="form-input w-full" value={{this.displayName}} placeholder="Fleetbase" {{on "input" (fn this.setValue "displayName")}} />
|
|
13
|
+
<p class="mt-1 text-xs text-gray-600 dark:text-gray-300">Shown before a lookup. The logo comes from the instance branding.</p>
|
|
14
|
+
</div>
|
|
15
|
+
<div>
|
|
16
|
+
<label for="admin-tracking-page-accent" class="block text-sm font-semibold mb-1">Accent colour</label>
|
|
17
|
+
<div class="flex items-center gap-3">
|
|
18
|
+
<input id="admin-tracking-page-accent" type="color" class="h-10 w-14 rounded-md border border-gray-300 dark:border-gray-700 bg-transparent cursor-pointer" value={{this.accent}} {{on "input" (fn this.setValue "accent")}} />
|
|
19
|
+
<span class="font-mono text-sm">{{this.accent}}</span>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
23
|
+
<div>
|
|
24
|
+
<label for="admin-tracking-page-phone" class="block text-sm font-semibold mb-1">Support phone</label>
|
|
25
|
+
<input id="admin-tracking-page-phone" type="tel" class="form-input w-full" value={{this.supportPhone}} placeholder="+1 555 010 0100" {{on "input" (fn this.setValue "supportPhone")}} />
|
|
26
|
+
</div>
|
|
27
|
+
<div>
|
|
28
|
+
<label for="admin-tracking-page-email" class="block text-sm font-semibold mb-1">Support email</label>
|
|
29
|
+
<input id="admin-tracking-page-email" type="email" class="form-input w-full" value={{this.supportEmail}} placeholder="support@example.com" {{on "input" (fn this.setValue "supportEmail")}} />
|
|
30
|
+
</div>
|
|
31
|
+
<div>
|
|
32
|
+
<label for="admin-tracking-page-website" class="block text-sm font-semibold mb-1">Website</label>
|
|
33
|
+
<input id="admin-tracking-page-website" type="url" class="form-input w-full" value={{this.website}} placeholder="https://example.com" {{on "input" (fn this.setValue "website")}} />
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="flex items-start justify-between gap-4">
|
|
37
|
+
<div>
|
|
38
|
+
<div class="text-sm font-semibold">Show "Powered by Fleetbase"</div>
|
|
39
|
+
<div class="text-xs text-gray-600 dark:text-gray-300">The default for organizations that haven't chosen.</div>
|
|
40
|
+
</div>
|
|
41
|
+
<Toggle @isToggled={{this.poweredBy}} @onToggle={{fn (mut this.poweredBy)}} />
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</ContentPanel>
|
|
45
|
+
|
|
46
|
+
<EmberWormhole @to="next-view-section-subheader-actions">
|
|
47
|
+
<Button
|
|
48
|
+
@type="primary"
|
|
49
|
+
@size="sm"
|
|
50
|
+
@icon="save"
|
|
51
|
+
@text="Save Changes"
|
|
52
|
+
@onClick={{perform this.saveSettings}}
|
|
53
|
+
@isLoading={{or this.saveSettings.isRunning this.loadSettings.isRunning}}
|
|
54
|
+
@disabled={{this.saveSettings.isRunning}}
|
|
55
|
+
/>
|
|
56
|
+
</EmberWormhole>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { inject as service } from '@ember/service';
|
|
3
|
+
import { tracked } from '@glimmer/tracking';
|
|
4
|
+
import { action } from '@ember/object';
|
|
5
|
+
import { task } from 'ember-concurrency';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Instance-wide defaults for the generic customer tracking page: whether it is on, and the
|
|
9
|
+
* branding it shows before a lookup (and for companies without their own).
|
|
10
|
+
*/
|
|
11
|
+
export default class AdminTrackingPageSettingsComponent extends Component {
|
|
12
|
+
@service fetch;
|
|
13
|
+
@service notifications;
|
|
14
|
+
@tracked genericEnabled = true;
|
|
15
|
+
@tracked displayName = '';
|
|
16
|
+
@tracked accent = '#1F5FA8';
|
|
17
|
+
@tracked supportPhone = '';
|
|
18
|
+
@tracked supportEmail = '';
|
|
19
|
+
@tracked website = '';
|
|
20
|
+
@tracked poweredBy = true;
|
|
21
|
+
|
|
22
|
+
constructor() {
|
|
23
|
+
super(...arguments);
|
|
24
|
+
this.loadSettings.perform();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@task *loadSettings() {
|
|
28
|
+
try {
|
|
29
|
+
const settings = yield this.fetch.get('fleet-ops/settings/admin-tracking-page-settings');
|
|
30
|
+
this.genericEnabled = settings?.generic_page?.enabled ?? true;
|
|
31
|
+
this.displayName = settings?.branding?.display_name ?? '';
|
|
32
|
+
this.accent = settings?.branding?.accent ?? '#1F5FA8';
|
|
33
|
+
this.supportPhone = settings?.branding?.support_phone ?? '';
|
|
34
|
+
this.supportEmail = settings?.branding?.support_email ?? '';
|
|
35
|
+
this.website = settings?.branding?.website ?? '';
|
|
36
|
+
this.poweredBy = settings?.branding?.powered_by ?? true;
|
|
37
|
+
} catch (error) {
|
|
38
|
+
this.notifications.serverError(error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@action setValue(property, event) {
|
|
43
|
+
this[property] = event.target.value;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@task *saveSettings() {
|
|
47
|
+
try {
|
|
48
|
+
yield this.fetch.post('fleet-ops/settings/admin-tracking-page-settings', {
|
|
49
|
+
trackingPage: {
|
|
50
|
+
generic_page: { enabled: this.genericEnabled },
|
|
51
|
+
branding: {
|
|
52
|
+
display_name: this.displayName,
|
|
53
|
+
accent: this.accent,
|
|
54
|
+
support_phone: this.supportPhone,
|
|
55
|
+
support_email: this.supportEmail,
|
|
56
|
+
website: this.website,
|
|
57
|
+
powered_by: this.poweredBy,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
this.notifications.success('Tracking page defaults saved.');
|
|
63
|
+
} catch (error) {
|
|
64
|
+
this.notifications.serverError(error);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
|
|
138
138
|
<InputGroup @name={{t "device.fields.installation-date"}}>
|
|
139
139
|
<DatePicker
|
|
140
|
-
@value={{@resource.installation_date}}
|
|
140
|
+
@value={{format-date-fns @resource.installation_date "yyyy-MM-dd"}}
|
|
141
141
|
@onChange={{fn (mut @resource.installation_date)}}
|
|
142
142
|
@placeholder={{t "device.fields.installation-date-placeholder"}}
|
|
143
143
|
@disabled={{cannot-write @resource}}
|
|
@@ -147,7 +147,7 @@
|
|
|
147
147
|
|
|
148
148
|
<InputGroup @name={{t "device.fields.last-maintenance-date"}}>
|
|
149
149
|
<DatePicker
|
|
150
|
-
@value={{@resource.last_maintenance_date}}
|
|
150
|
+
@value={{format-date-fns @resource.last_maintenance_date "yyyy-MM-dd"}}
|
|
151
151
|
@onChange={{fn (mut @resource.last_maintenance_date)}}
|
|
152
152
|
@placeholder={{t "device.fields.last-maintenance-date-placeholder"}}
|
|
153
153
|
@disabled={{cannot-write @resource}}
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
</div>
|
|
28
28
|
<div class="field-info-container md:col-span-3">
|
|
29
29
|
<div class="field-name">{{t "driver.fields.license-expiry"}}</div>
|
|
30
|
-
<div class="field-value">{{n-a (format-date-fns @resource.license_expiry)}}</div>
|
|
30
|
+
<div class="field-value">{{n-a (format-date-fns @resource.license_expiry "yyyy-MM-dd")}}</div>
|
|
31
31
|
</div>
|
|
32
32
|
<div class="field-info-container">
|
|
33
33
|
<div class="field-name">{{t "resource.vehicle"}}</div>
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
|
|
67
67
|
<InputGroup @name={{t "driver.fields.license-expiry"}}>
|
|
68
68
|
<DatePicker
|
|
69
|
-
@value={{@resource.license_expiry}}
|
|
69
|
+
@value={{format-date-fns @resource.license_expiry "yyyy-MM-dd"}}
|
|
70
70
|
@onChange={{fn (mut @resource.license_expiry)}}
|
|
71
71
|
@placeholder={{t "driver.fields.license-expiry"}}
|
|
72
72
|
@disabled={{cannot-write @resource}}
|
|
@@ -159,7 +159,7 @@
|
|
|
159
159
|
</InputGroup>
|
|
160
160
|
<InputGroup @name="Purchase Date">
|
|
161
161
|
<DatePicker
|
|
162
|
-
@value={{or (format-date-fns @resource.purchased_at "
|
|
162
|
+
@value={{or (format-date-fns @resource.purchased_at "yyyy-MM-dd") null}}
|
|
163
163
|
@onChange={{fn (mut @resource.purchased_at)}}
|
|
164
164
|
@placeholder="DD-MM-YYYY"
|
|
165
165
|
class="filter-date-input w-full flex-1"
|
|
@@ -208,6 +208,13 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
|
|
|
208
208
|
this.createItem('menu.routing', 'route', 'settings.routing', 'fleet-ops view routing-settings', 'fleet-ops see routing-settings'),
|
|
209
209
|
this.createItem('menu.orchestrator', 'circle-nodes', 'settings.orchestrator', 'fleet-ops view routing-settings', 'fleet-ops see routing-settings'),
|
|
210
210
|
this.createItem('menu.scheduling', 'calendar-days', 'settings.scheduling', 'fleet-ops view scheduling-settings', 'fleet-ops see scheduling-settings'),
|
|
211
|
+
this.createItem(
|
|
212
|
+
'tracking-page.settings.menu',
|
|
213
|
+
'magnifying-glass-location',
|
|
214
|
+
'settings.tracking-page',
|
|
215
|
+
'fleet-ops view tracking-page-settings',
|
|
216
|
+
'fleet-ops see tracking-page-settings'
|
|
217
|
+
),
|
|
211
218
|
this.createItem('menu.telematics-settings', 'satellite-dish', 'settings.telematics', 'fleet-ops view telematics-settings', 'fleet-ops see telematics-settings'),
|
|
212
219
|
this.createItem('menu.custom-fields', 'pen-to-square', 'settings.custom-fields', 'fleet-ops list custom-field', 'fleet-ops see custom-field'),
|
|
213
220
|
this.createItem('menu.avatars', 'icons', 'settings.avatars', 'fleet-ops list avatar', 'fleet-ops see avatar'),
|
|
@@ -406,9 +413,10 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
|
|
|
406
413
|
'settings.routing': 5,
|
|
407
414
|
'settings.orchestrator': 6,
|
|
408
415
|
'settings.scheduling': 7,
|
|
409
|
-
'settings.
|
|
410
|
-
'settings.
|
|
411
|
-
'settings.
|
|
416
|
+
'settings.tracking-page': 8,
|
|
417
|
+
'settings.telematics': 9,
|
|
418
|
+
'settings.custom-fields': 10,
|
|
419
|
+
'settings.avatars': 11,
|
|
412
420
|
};
|
|
413
421
|
|
|
414
422
|
return priorities[route] ?? 0;
|
|
@@ -185,7 +185,7 @@
|
|
|
185
185
|
</div>
|
|
186
186
|
<InputGroup @name="Scheduled At">
|
|
187
187
|
<DatePicker
|
|
188
|
-
@value={{or (format-date-fns @resource.scheduled_at "
|
|
188
|
+
@value={{or (format-date-fns @resource.scheduled_at "yyyy-MM-dd") null}}
|
|
189
189
|
@onChange={{fn (mut @resource.scheduled_at)}}
|
|
190
190
|
@placeholder="DD-MM-YYYY"
|
|
191
191
|
class="filter-date-input w-full flex-1"
|
|
@@ -193,7 +193,7 @@
|
|
|
193
193
|
</InputGroup>
|
|
194
194
|
<InputGroup @name="Started At">
|
|
195
195
|
<DatePicker
|
|
196
|
-
@value={{or (format-date-fns @resource.started_at "
|
|
196
|
+
@value={{or (format-date-fns @resource.started_at "yyyy-MM-dd") null}}
|
|
197
197
|
@onChange={{fn (mut @resource.started_at)}}
|
|
198
198
|
@placeholder="DD-MM-YYYY"
|
|
199
199
|
class="filter-date-input w-full flex-1"
|
|
@@ -201,7 +201,7 @@
|
|
|
201
201
|
</InputGroup>
|
|
202
202
|
<InputGroup @name="Completed At">
|
|
203
203
|
<DatePicker
|
|
204
|
-
@value={{or (format-date-fns @resource.completed_at "
|
|
204
|
+
@value={{or (format-date-fns @resource.completed_at "yyyy-MM-dd") null}}
|
|
205
205
|
@onChange={{fn (mut @resource.completed_at)}}
|
|
206
206
|
@placeholder="DD-MM-YYYY"
|
|
207
207
|
class="filter-date-input w-full flex-1"
|
|
@@ -59,7 +59,7 @@ export default class MapGoogleLiveMapComponent extends Component {
|
|
|
59
59
|
const marker = this.mapManager.getMarker(driver.id);
|
|
60
60
|
if (!marker) {
|
|
61
61
|
const createdMarker = await this.mapManager.addMarker(driver.id, coords.lat, coords.lng, {
|
|
62
|
-
iconUrl: driver.vehicle_avatar ?? '/engines-dist/images/
|
|
62
|
+
iconUrl: driver.vehicle_avatar ?? '/engines-dist/images/map-marker.png',
|
|
63
63
|
iconSize: [20, 20],
|
|
64
64
|
title: driver.name,
|
|
65
65
|
tooltip: buildDriverLiveMapContent(driver),
|
|
@@ -89,7 +89,7 @@ export default class MapGoogleLiveMapComponent extends Component {
|
|
|
89
89
|
const marker = this.mapManager.getMarker(vehicle.id);
|
|
90
90
|
if (!marker) {
|
|
91
91
|
const createdMarker = await this.mapManager.addMarker(vehicle.id, coords.lat, coords.lng, {
|
|
92
|
-
iconUrl: vehicle.avatar_url ?? '/engines-dist/images/
|
|
92
|
+
iconUrl: vehicle.avatar_url ?? '/engines-dist/images/map-marker.png',
|
|
93
93
|
iconSize: [20, 20],
|
|
94
94
|
title: vehicle.displayName,
|
|
95
95
|
tooltip: buildVehicleLiveMapContent(vehicle),
|
|
@@ -12,6 +12,7 @@ import { task, timeout } from 'ember-concurrency';
|
|
|
12
12
|
import { next } from '@ember/runloop';
|
|
13
13
|
import getModelName from '@fleetbase/ember-core/utils/get-model-name';
|
|
14
14
|
import ensureLeafletPluginsReady, { hasLeafletPluginsReady } from '../../utils/leaflet-plugin-loader';
|
|
15
|
+
import { FIT_VEHICLES_MAX_ZOOM, isZoom, positionedBounds } from '../../utils/map-default-view';
|
|
15
16
|
|
|
16
17
|
export default class MapLeafletLiveMapComponent extends Component {
|
|
17
18
|
liveViewportReloadDebounceMs = 300;
|
|
@@ -243,9 +244,45 @@ export default class MapLeafletLiveMapComponent extends Component {
|
|
|
243
244
|
this.rebuildMapContextMenu();
|
|
244
245
|
}
|
|
245
246
|
|
|
247
|
+
/**
|
|
248
|
+
* When the company chose "Fit to my vehicles", frame every positioned
|
|
249
|
+
* vehicle before the first viewport-filtered load. With no positioned
|
|
250
|
+
* vehicles the map stays on the default view resolved by the location
|
|
251
|
+
* service.
|
|
252
|
+
*
|
|
253
|
+
* @return {Boolean} whether the view was fitted
|
|
254
|
+
*/
|
|
255
|
+
@task *fitToPositionedVehicles() {
|
|
256
|
+
if (!this.mapSettings.fitMapToVehicles || this.abilities.cannot('fleet-ops list vehicle')) {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
try {
|
|
261
|
+
const vehicles = yield this.fetch.get('fleet-ops/live/vehicles', { limit: this.liveViewportResourceLimit });
|
|
262
|
+
const bounds = positionedBounds(vehicles);
|
|
263
|
+
if (!bounds) {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const options = { maxZoom: FIT_VEHICLES_MAX_ZOOM, padding: [40, 40], animate: false };
|
|
268
|
+
if (typeof this.mapManager.fitBounds === 'function' && this.mapManager.adapter) {
|
|
269
|
+
this.mapManager.fitBounds(bounds, options);
|
|
270
|
+
} else {
|
|
271
|
+
this.map?.fitBounds?.(bounds, options);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return true;
|
|
275
|
+
} catch (err) {
|
|
276
|
+
debug('Failed to fit live map to vehicles: ' + err.message);
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
246
281
|
/** load resources and wait for stuff here and trigger map ready **/
|
|
247
282
|
@task *load() {
|
|
248
283
|
try {
|
|
284
|
+
yield this.fitToPositionedVehicles.perform();
|
|
285
|
+
|
|
249
286
|
// Get initial map bounds for spatial filtering (provider-agnostic)
|
|
250
287
|
const mapBounds = this.mapManager.getBounds?.() ?? (this.map ? this.map.getBounds() : null);
|
|
251
288
|
const bounds = this.#serializeBoundsForRequest(mapBounds);
|
|
@@ -360,6 +397,11 @@ export default class MapLeafletLiveMapComponent extends Component {
|
|
|
360
397
|
if (typeof zoom === 'number' && !isNaN(zoom) && zoom >= 1 && zoom <= 20) {
|
|
361
398
|
return zoom;
|
|
362
399
|
}
|
|
400
|
+
// The company's default view (or a country centre) suggests its own zoom
|
|
401
|
+
const locationZoom = this.location.getZoom?.();
|
|
402
|
+
if (isZoom(locationZoom)) {
|
|
403
|
+
return locationZoom;
|
|
404
|
+
}
|
|
363
405
|
// Return default zoom of 14 if invalid
|
|
364
406
|
return 14;
|
|
365
407
|
}
|
|
@@ -428,6 +470,9 @@ export default class MapLeafletLiveMapComponent extends Component {
|
|
|
428
470
|
if (coordinates && typeof coordinates.latitude === 'number' && typeof coordinates.longitude === 'number') {
|
|
429
471
|
this.latitude = coordinates.latitude;
|
|
430
472
|
this.longitude = coordinates.longitude;
|
|
473
|
+
if (isZoom(coordinates.zoom) && !isZoom(this.args.zoom)) {
|
|
474
|
+
this.zoom = coordinates.zoom;
|
|
475
|
+
}
|
|
431
476
|
|
|
432
477
|
// Update map position if map is loaded
|
|
433
478
|
if (this.map && this.map.setView) {
|
|
@@ -860,36 +905,4 @@ export default class MapLeafletLiveMapComponent extends Component {
|
|
|
860
905
|
|
|
861
906
|
return contextmenuRegistry;
|
|
862
907
|
}
|
|
863
|
-
|
|
864
|
-
/**
|
|
865
|
-
* Safely gets a valid latitude value with fallback to default
|
|
866
|
-
* @returns {number} Valid latitude value
|
|
867
|
-
*/
|
|
868
|
-
#getValidLatitude() {
|
|
869
|
-
const lat = this.location.getLatitude();
|
|
870
|
-
|
|
871
|
-
// Validate latitude is a number and within valid range (-90 to 90)
|
|
872
|
-
if (typeof lat === 'number' && !isNaN(lat) && lat >= -90 && lat <= 90) {
|
|
873
|
-
return lat;
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
// Fallback to default Singapore latitude
|
|
877
|
-
return 1.369;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
/**
|
|
881
|
-
* Safely gets a valid longitude value with fallback to default
|
|
882
|
-
* @returns {number} Valid longitude value
|
|
883
|
-
*/
|
|
884
|
-
#getValidLongitude() {
|
|
885
|
-
const lng = this.location.getLongitude();
|
|
886
|
-
|
|
887
|
-
// Validate longitude is a number and within valid range (-180 to 180)
|
|
888
|
-
if (typeof lng === 'number' && !isNaN(lng) && lng >= -180 && lng <= 180) {
|
|
889
|
-
return lng;
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
// Fallback to default Singapore longitude
|
|
893
|
-
return 103.8864;
|
|
894
|
-
}
|
|
895
908
|
}
|
|
@@ -15,6 +15,7 @@ import { inject as service } from '@ember/service';
|
|
|
15
15
|
*
|
|
16
16
|
* @arg phases - Array of phase objects (managed externally)
|
|
17
17
|
* @arg availableEngines - Array of { id, name } engine options
|
|
18
|
+
* @arg defaultEngine - Engine id selected in Orchestrator settings; new phases start with it
|
|
18
19
|
* @arg onPhasesChange - Action(phases) — called when phases array changes
|
|
19
20
|
* @arg onRunPhases - Action(phases) — called when "Run" is clicked
|
|
20
21
|
*/
|
|
@@ -49,7 +50,7 @@ export default class OrchestratorPhaseBuilderComponent extends Component {
|
|
|
49
50
|
id: crypto.randomUUID(),
|
|
50
51
|
mode,
|
|
51
52
|
label: this.intl.t(`orchestrator.mode-${mode.replace(/_/g, '-')}`),
|
|
52
|
-
engine: 'greedy',
|
|
53
|
+
engine: this.args.defaultEngine ?? 'greedy',
|
|
53
54
|
allocationStrategy: 'route_aware',
|
|
54
55
|
vehiclePacking: 'minimize_vehicles',
|
|
55
56
|
orderStatuses: ['created'],
|
|
@@ -59,7 +59,13 @@
|
|
|
59
59
|
{{! ── Phase builder panel (collapsible, below toolbar) ── }}
|
|
60
60
|
{{#if this.showPhaseBuilder}}
|
|
61
61
|
<div class="workbench-phase-panel border-b dark:border-gray-700 bg-white dark:bg-gray-900 flex-shrink-0 h-80">
|
|
62
|
-
<Orchestrator::PhaseBuilder
|
|
62
|
+
<Orchestrator::PhaseBuilder
|
|
63
|
+
@phases={{this.phases}}
|
|
64
|
+
@availableEngines={{this.availableEngines}}
|
|
65
|
+
@defaultEngine={{this.defaultEngine}}
|
|
66
|
+
@onPhasesChange={{this.onPhasesChange}}
|
|
67
|
+
@onRunPhases={{this.onRunPhases}}
|
|
68
|
+
/>
|
|
63
69
|
</div>
|
|
64
70
|
{{/if}}
|
|
65
71
|
|
|
@@ -49,6 +49,8 @@ export default class OrchestratorWorkbenchComponent extends Component {
|
|
|
49
49
|
@tracked availableVehicles = [];
|
|
50
50
|
@tracked availableDrivers = [];
|
|
51
51
|
@tracked availableEngines = [];
|
|
52
|
+
/** The engine selected in Orchestrator settings; new phases start with it. */
|
|
53
|
+
@tracked defaultEngine = null;
|
|
52
54
|
|
|
53
55
|
// ── Plan state ────────────────────────────────────────────────────────────
|
|
54
56
|
|
|
@@ -204,6 +206,12 @@ export default class OrchestratorWorkbenchComponent extends Component {
|
|
|
204
206
|
} catch {
|
|
205
207
|
this.availableEngines = [{ id: 'greedy', name: 'Greedy (built-in)' }];
|
|
206
208
|
}
|
|
209
|
+
try {
|
|
210
|
+
const settings = yield this.fetch.get('fleet-ops/settings/orchestrator-settings');
|
|
211
|
+
this.defaultEngine = settings?.allocation_engine ?? null;
|
|
212
|
+
} catch {
|
|
213
|
+
this.defaultEngine = null;
|
|
214
|
+
}
|
|
207
215
|
}
|
|
208
216
|
|
|
209
217
|
@task *loadCardFields() {
|
|
@@ -270,7 +278,8 @@ export default class OrchestratorWorkbenchComponent extends Component {
|
|
|
270
278
|
mode: phase.mode,
|
|
271
279
|
order_statuses: phase.orderStatuses ?? ['created'],
|
|
272
280
|
options: {
|
|
273
|
-
|
|
281
|
+
// Without an explicit engine the server runs the one selected in Orchestrator settings.
|
|
282
|
+
engine: phase.engine ?? null,
|
|
274
283
|
allocation_strategy: phase.allocationStrategy ?? 'route_aware',
|
|
275
284
|
vehicle_packing: phase.vehiclePacking ?? 'minimize_vehicles',
|
|
276
285
|
balance_workload: phase.balanceWorkload ?? false,
|
|
@@ -311,6 +320,11 @@ export default class OrchestratorWorkbenchComponent extends Component {
|
|
|
311
320
|
if (result.message) {
|
|
312
321
|
this.orchestratorRunMessage = result.message;
|
|
313
322
|
}
|
|
323
|
+
// The server substituted another engine (e.g. VROOM unavailable) — say so.
|
|
324
|
+
if (result.warning) {
|
|
325
|
+
this.orchestratorRunMessage = result.warning;
|
|
326
|
+
this.notifications.warning(result.warning, { autoClear: true, clearDuration: 10000 });
|
|
327
|
+
}
|
|
314
328
|
// If the run returned zero assignments, surface the error/message to
|
|
315
329
|
// the user immediately — otherwise the right panel stays blank with
|
|
316
330
|
// no feedback (PlanViewer is only rendered when hasProposedPlan).
|
|
@@ -401,7 +415,7 @@ export default class OrchestratorWorkbenchComponent extends Component {
|
|
|
401
415
|
id: 'legacy',
|
|
402
416
|
mode: 'allocate',
|
|
403
417
|
label: 'Allocate',
|
|
404
|
-
engine:
|
|
418
|
+
engine: null,
|
|
405
419
|
allocationStrategy: 'route_aware',
|
|
406
420
|
orderStatuses: ['created'],
|
|
407
421
|
balanceWorkload: false,
|
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
@isLoading={{or @isLoadingActivity this.loadActivity.isRunning @isLoading}}
|
|
6
6
|
@wrapperClass="bordered-top"
|
|
7
7
|
>
|
|
8
|
-
{{
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
<div {{did-update this.onStatusChanged @resource.status}}>
|
|
9
|
+
{{#if (eq this.layout "timeline")}}
|
|
10
|
+
<Order::ActivityTimeline @resource={{@resource}} @activity={{this.parentOrderActivity}} />
|
|
11
|
+
{{else}}
|
|
12
|
+
<Order::ActivityList @resource={{@resource}} @activity={{this.parentOrderActivity}} />
|
|
13
|
+
{{/if}}
|
|
14
|
+
</div>
|
|
15
|
+
</ContentPanel>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Component from '@glimmer/component';
|
|
2
2
|
import { tracked } from '@glimmer/tracking';
|
|
3
3
|
import { inject as service } from '@ember/service';
|
|
4
|
+
import { action } from '@ember/object';
|
|
4
5
|
import { task } from 'ember-concurrency';
|
|
5
6
|
|
|
6
7
|
export default class OrderDetailsActivityComponent extends Component {
|
|
@@ -64,6 +65,14 @@ export default class OrderDetailsActivityComponent extends Component {
|
|
|
64
65
|
];
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
/**
|
|
69
|
+
* A status change recorded outside this panel (a configured lifecycle advancing on save, an
|
|
70
|
+
* extension action, a realtime update) means new tracking statuses exist server-side.
|
|
71
|
+
*/
|
|
72
|
+
@action onStatusChanged() {
|
|
73
|
+
this.loadActivity.perform();
|
|
74
|
+
}
|
|
75
|
+
|
|
67
76
|
@task *loadActivity() {
|
|
68
77
|
if (!this.args.resource) {
|
|
69
78
|
return;
|
|
@@ -44,6 +44,35 @@
|
|
|
44
44
|
)
|
|
45
45
|
)
|
|
46
46
|
}}
|
|
47
|
+
{{else if this.profile}}
|
|
48
|
+
{{#each this.profileSections key="key" as |section|}}
|
|
49
|
+
{{#if section.native}}
|
|
50
|
+
{{#let
|
|
51
|
+
(component
|
|
52
|
+
section.componentName
|
|
53
|
+
resource=@resource
|
|
54
|
+
onChange=section.onChange
|
|
55
|
+
isLoading=@isLoading
|
|
56
|
+
isLoadingActivity=@isLoadingActivity
|
|
57
|
+
reloadToken=this.effectiveProofReloadToken
|
|
58
|
+
)
|
|
59
|
+
as |NativeSection|
|
|
60
|
+
}}
|
|
61
|
+
<NativeSection />
|
|
62
|
+
{{/let}}
|
|
63
|
+
{{else}}
|
|
64
|
+
<LazyEngineComponent @component={{section.component}} as |ProfileSection|>
|
|
65
|
+
<ProfileSection
|
|
66
|
+
@resource={{@resource}}
|
|
67
|
+
@controller={{@controller}}
|
|
68
|
+
@permission={{get-write-permission @resource}}
|
|
69
|
+
@isLoading={{@isLoading}}
|
|
70
|
+
@profile={{this.profile}}
|
|
71
|
+
@onChange={{@onDetailsChanged}}
|
|
72
|
+
/>
|
|
73
|
+
</LazyEngineComponent>
|
|
74
|
+
{{/if}}
|
|
75
|
+
{{/each}}
|
|
47
76
|
{{else}}
|
|
48
77
|
<RegistryYield @registry="fleet-ops:component:order:details:start" as |RegistryComponent|>
|
|
49
78
|
<RegistryComponent @resource={{@resource}} @controller={{@controller}} @permission={{get-write-permission @resource}} @isLoading={{@isLoading}} />
|