@fleetbase/fleetops-engine 0.6.48 → 0.6.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addon/components/customer/create-order-form.hbs +2 -2
- package/addon/components/customer/form.hbs +2 -2
- package/addon/components/customer/order-form.hbs +2 -2
- package/addon/components/customer/orders.hbs +2 -2
- package/addon/components/display-place.hbs +2 -2
- package/addon/components/driver/details.hbs +5 -1
- package/addon/components/driver/form.hbs +11 -1
- package/addon/components/fleet-listing-panel.hbs +2 -2
- package/addon/components/fleet-listing-panel.js +10 -0
- package/addon/components/map/google-live-map.js +18 -75
- package/addon/components/map/leaflet-live-map.hbs +187 -37
- package/addon/components/map/leaflet-live-map.js +184 -20
- package/addon/components/modals/order-change-destination.hbs +46 -0
- package/addon/components/modals/order-change-destination.js +106 -0
- package/addon/components/modals/stop-activity.hbs +28 -0
- package/addon/components/modals/stop-activity.js +52 -0
- package/addon/components/modals/update-order-activity.hbs +45 -3
- package/addon/components/modals/update-order-activity.js +143 -5
- package/addon/components/orchestrator/phase-builder.hbs +21 -0
- package/addon/components/orchestrator/phase-builder.js +13 -0
- package/addon/components/orchestrator/resource-panel.hbs +0 -10
- package/addon/components/orchestrator/resource-panel.js +2 -4
- package/addon/components/orchestrator-workbench.js +1 -0
- package/addon/components/order/activity-list.hbs +2 -2
- package/addon/components/order/activity-timeline.hbs +31 -29
- package/addon/components/order/details/activity.hbs +3 -3
- package/addon/components/order/details/activity.js +11 -0
- package/addon/components/order/details/documents.js +2 -0
- package/addon/components/order/details/purchase-rate.hbs +2 -2
- package/addon/components/order/details/route.hbs +11 -3
- package/addon/components/order/details/route.js +91 -14
- package/addon/components/order/details/tracking.hbs +101 -72
- package/addon/components/order/details/tracking.js +149 -2
- package/addon/components/order/form/details.hbs +3 -3
- package/addon/components/order/form/documents.js +11 -4
- package/addon/components/order/form/route.hbs +108 -104
- package/addon/components/order/form/route.js +37 -1
- package/addon/components/order/form/service-rate.hbs +2 -2
- package/addon/components/order/form.hbs +2 -2
- package/addon/components/order-config-manager/activity-flow.hbs +4 -1
- package/addon/components/order-config-manager/activity-flow.js +178 -12
- package/addon/components/place/address.js +1 -1
- package/addon/components/route-list.hbs +17 -17
- package/addon/components/route-list.js +42 -10
- package/addon/components/service-rate/details.hbs +41 -0
- package/addon/components/service-rate/form.hbs +118 -0
- package/addon/components/service-rate/form.js +55 -0
- package/addon/components/vehicle/form.js +1 -1
- package/addon/components/vehicle/pill.hbs +1 -1
- package/addon/components/widget/fleet-ops-key-metrics.hbs +19 -17
- package/addon/components/widget/fleet-ops-key-metrics.js +26 -0
- package/addon/components/widget/fuel-efficiency.hbs +26 -0
- package/addon/components/widget/fuel-efficiency.js +43 -0
- package/addon/components/widget/geofence-violations.hbs +44 -0
- package/addon/components/widget/geofence-violations.js +55 -0
- package/addon/components/widget/issues-insights.hbs +43 -0
- package/addon/components/widget/issues-insights.js +62 -0
- package/addon/components/widget/kpi-active-orders.hbs +10 -0
- package/addon/components/widget/kpi-active-orders.js +5 -0
- package/addon/components/widget/kpi-aov.hbs +10 -0
- package/addon/components/widget/kpi-aov.js +5 -0
- package/addon/components/widget/kpi-distance.hbs +10 -0
- package/addon/components/widget/kpi-distance.js +5 -0
- package/addon/components/widget/kpi-drivers-online.hbs +10 -0
- package/addon/components/widget/kpi-drivers-online.js +5 -0
- package/addon/components/widget/kpi-earnings.hbs +10 -0
- package/addon/components/widget/kpi-earnings.js +5 -0
- package/addon/components/widget/kpi-open-issues.hbs +11 -0
- package/addon/components/widget/kpi-open-issues.js +5 -0
- package/addon/components/widget/kpi-tile.hbs +59 -0
- package/addon/components/widget/kpi-tile.js +168 -0
- package/addon/components/widget/live-fleet.hbs +122 -0
- package/addon/components/widget/live-fleet.js +365 -0
- package/addon/components/widget/maintenance-overview.hbs +45 -0
- package/addon/components/widget/maintenance-overview.js +40 -0
- package/addon/components/widget/on-time-delivery.hbs +27 -0
- package/addon/components/widget/on-time-delivery.js +73 -0
- package/addon/components/widget/operations-pulse.hbs +51 -0
- package/addon/components/widget/operations-pulse.js +173 -0
- package/addon/components/widget/orders-by-status.hbs +26 -0
- package/addon/components/widget/orders-by-status.js +54 -0
- package/addon/components/widget/revenue-trend.hbs +34 -0
- package/addon/components/widget/revenue-trend.js +67 -0
- package/addon/components/widget/top-drivers.hbs +44 -0
- package/addon/components/widget/top-drivers.js +54 -0
- package/addon/controllers/management/vehicles/index/new.js +1 -1
- package/addon/controllers/operations/orders/index/details.js +4 -4
- package/addon/controllers/operations/orders/index.js +14 -0
- package/addon/controllers/settings/routing.js +26 -0
- package/addon/extension.js +172 -3
- package/addon/helpers/place-address-html.js +2 -2
- package/addon/routes/operations/service-rates/index.js +1 -1
- package/addon/services/geofence.js +163 -19
- package/addon/services/leaflet-contextmenu-manager.js +27 -0
- package/addon/services/map-adapter/google.js +13 -2
- package/addon/services/vehicle-actions.js +1 -1
- package/addon/styles/fleetops-engine.css +205 -88
- package/addon/templates/maintenance/equipment/index.hbs +1 -1
- package/addon/templates/maintenance/parts/index.hbs +1 -1
- package/addon/templates/maintenance/schedules/index.hbs +1 -1
- package/addon/templates/management/vehicles/index.hbs +1 -1
- package/addon/templates/settings/avatars.hbs +1 -1
- package/addon/templates/settings/custom-fields.hbs +1 -1
- package/addon/templates/settings/map.hbs +1 -1
- package/addon/templates/settings/navigator-app.hbs +1 -1
- package/addon/templates/settings/notifications.hbs +1 -1
- package/addon/templates/settings/orchestrator.hbs +1 -1
- package/addon/templates/settings/payments/index.hbs +1 -1
- package/addon/templates/settings/payments/onboard.hbs +1 -1
- package/addon/templates/settings/routing.hbs +22 -2
- package/addon/templates/settings/scheduling.hbs +1 -1
- package/addon/utils/fleet-ops-options.js +2 -1
- package/addon/utils/live-map-card-content.js +171 -0
- package/addon/utils/normalize-order-config-flow.js +41 -0
- package/addon/utils/place-address-html.js +3 -2
- package/addon/utils/route-visualization.js +54 -7
- package/app/components/modals/order-change-destination.js +1 -0
- package/app/components/modals/stop-activity.js +1 -0
- package/app/components/widget/fuel-efficiency.js +1 -0
- package/app/components/widget/geofence-violations.js +1 -0
- package/app/components/widget/issues-insights.js +1 -0
- package/app/components/widget/kpi-active-orders.js +1 -0
- package/app/components/widget/kpi-aov.js +1 -0
- package/app/components/widget/kpi-distance.js +1 -0
- package/app/components/widget/kpi-drivers-online.js +1 -0
- package/app/components/widget/kpi-earnings.js +1 -0
- package/app/components/widget/kpi-open-issues.js +1 -0
- package/app/components/widget/kpi-tile.js +1 -0
- package/app/components/widget/live-fleet.js +1 -0
- package/app/components/widget/maintenance-overview.js +1 -0
- package/app/components/widget/on-time-delivery.js +1 -0
- package/app/components/widget/operations-pulse.js +1 -0
- package/app/components/widget/orders-by-status.js +1 -0
- package/app/components/widget/revenue-trend.js +1 -0
- package/app/components/widget/top-drivers.js +1 -0
- package/app/utils/live-map-card-content.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +3 -3
- package/pnpm-workspace.yaml +1 -0
- package/server/config/telematics.php +111 -1
- package/server/migrations/2026_05_18_000001_add_license_expiry_to_drivers_table.php +25 -0
- package/server/migrations/2026_05_21_000001_add_geographic_rules_to_service_rate_fees_table.php +37 -0
- package/server/migrations/2026_05_25_000001_add_endpoint_tracking_refs_to_payloads_table.php +50 -0
- package/server/migrations/2026_05_27_000001_default_driver_vehicle_statuses_to_available.php +48 -0
- package/server/resources/views/labels/default.php +57 -49
- package/server/resources/views/labels/waypoint-label.php +27 -20
- package/server/seeders/Testing/Concerns/SeedsTestingData.php +183 -0
- package/server/seeders/Testing/ConnectivitySeeder.php +213 -0
- package/server/seeders/Testing/FleetSeeder.php +341 -0
- package/server/seeders/Testing/MaintenanceSeeder.php +238 -0
- package/server/seeders/Testing/NetworkSeeder.php +250 -0
- package/server/seeders/Testing/OrdersSeeder.php +198 -0
- package/server/seeders/Testing/TestingSeeder.php +40 -0
- package/server/seeders/{VroomOrchestrationSeeder.php → Testing/VroomOrchestrationSeeder.php} +29 -22
- package/server/src/Console/Commands/AuditCustomerUserConflicts.php +75 -0
- package/server/src/Console/Commands/ProcessOperationalAlerts.php +279 -0
- package/server/src/Exceptions/CustomerUserConflictException.php +13 -0
- package/server/src/Http/Controllers/Api/v1/ContactController.php +17 -2
- package/server/src/Http/Controllers/Api/v1/CustomerController.php +876 -0
- package/server/src/Http/Controllers/Api/v1/DriverController.php +1 -1
- package/server/src/Http/Controllers/Api/v1/OrderConfigController.php +54 -0
- package/server/src/Http/Controllers/Api/v1/OrderController.php +98 -70
- package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +2 -2
- package/server/src/Http/Controllers/Api/v1/VehicleController.php +20 -3
- package/server/src/Http/Controllers/Internal/v1/AnalyticsController.php +119 -0
- package/server/src/Http/Controllers/Internal/v1/ContactController.php +19 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +101 -66
- package/server/src/Http/Controllers/Internal/v1/MetricsController.php +82 -3
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +441 -32
- package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +46 -1
- package/server/src/Http/Controllers/Internal/v1/VehicleController.php +54 -1
- package/server/src/Http/Middleware/AuthenticateCustomerToken.php +34 -0
- package/server/src/Http/Requests/CreateCustomerOrderRequest.php +63 -0
- package/server/src/Http/Requests/CreateCustomerRequest.php +48 -0
- package/server/src/Http/Requests/CreateDriverRequest.php +1 -1
- package/server/src/Http/Requests/CreateVehicleRequest.php +25 -1
- package/server/src/Http/Requests/Internal/CreateDriverRequest.php +1 -1
- package/server/src/Http/Requests/VerifyCreateCustomerRequest.php +29 -0
- package/server/src/Http/Resources/Internal/v1/OrderConfig.php +41 -0
- package/server/src/Http/Resources/v1/Customer.php +79 -0
- package/server/src/Http/Resources/v1/Driver.php +2 -0
- package/server/src/Http/Resources/v1/Fleet.php +4 -0
- package/server/src/Http/Resources/v1/Index/Driver.php +48 -0
- package/server/src/Http/Resources/v1/Index/Vehicle.php +74 -1
- package/server/src/Http/Resources/v1/OrderConfig.php +74 -0
- package/server/src/Http/Resources/v1/Payload.php +23 -20
- package/server/src/Http/Resources/v1/ServiceArea.php +33 -19
- package/server/src/Http/Resources/v1/ServiceRateFee.php +7 -0
- package/server/src/Listeners/HandleOrderDispatched.php +2 -2
- package/server/src/Models/Contact.php +92 -15
- package/server/src/Models/Customer.php +64 -0
- package/server/src/Models/Driver.php +14 -3
- package/server/src/Models/Entity.php +2 -2
- package/server/src/Models/Order.php +29 -4
- package/server/src/Models/OrderConfig.php +14 -11
- package/server/src/Models/Payload.php +17 -1
- package/server/src/Models/ServiceRate.php +273 -1
- package/server/src/Models/ServiceRateFee.php +39 -9
- package/server/src/Models/TrackingNumber.php +8 -2
- package/server/src/Models/Vehicle.php +18 -2
- package/server/src/Models/Waypoint.php +2 -2
- package/server/src/Notifications/LateDeparture.php +48 -0
- package/server/src/Notifications/ProlongedStoppage.php +48 -0
- package/server/src/Notifications/RouteDeviation.php +48 -0
- package/server/src/Observers/ContactObserver.php +2 -0
- package/server/src/Observers/ServiceRateObserver.php +6 -6
- package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +29 -2
- package/server/src/Providers/FleetOpsServiceProvider.php +6 -0
- package/server/src/Providers/NotificationServiceProvider.php +3 -0
- package/server/src/Support/Analytics/AbstractAnalytics.php +77 -0
- package/server/src/Support/Analytics/FuelEfficiency.php +83 -0
- package/server/src/Support/Analytics/GeofenceViolations.php +63 -0
- package/server/src/Support/Analytics/IssuesInsights.php +67 -0
- package/server/src/Support/Analytics/LiveFleet.php +127 -0
- package/server/src/Support/Analytics/MaintenanceOverview.php +70 -0
- package/server/src/Support/Analytics/OnTimeDelivery.php +75 -0
- package/server/src/Support/Analytics/OperationsPulse.php +86 -0
- package/server/src/Support/Analytics/OrdersByStatus.php +64 -0
- package/server/src/Support/Analytics/RevenueTrend.php +78 -0
- package/server/src/Support/Analytics/TopDrivers.php +92 -0
- package/server/src/Support/CustomerAuth.php +98 -0
- package/server/src/Support/LabelPdf.php +52 -0
- package/server/src/Support/Metrics/AbstractMetric.php +170 -0
- package/server/src/Support/Metrics/ActiveLiveOrdersMetric.php +28 -0
- package/server/src/Support/Metrics/AvgOrderValueMetric.php +53 -0
- package/server/src/Support/Metrics/DriversOnlineMetric.php +30 -0
- package/server/src/Support/Metrics/EarningsMetric.php +30 -0
- package/server/src/Support/Metrics/FuelCostsMetric.php +30 -0
- package/server/src/Support/Metrics/MoneyMetric.php +30 -0
- package/server/src/Support/Metrics/OpenIssuesMetric.php +35 -0
- package/server/src/Support/Metrics/OrdersCanceledMetric.php +35 -0
- package/server/src/Support/Metrics/OrdersCompletedMetric.php +35 -0
- package/server/src/Support/Metrics/OrdersInProgressMetric.php +51 -0
- package/server/src/Support/Metrics/OrdersScheduledMetric.php +37 -0
- package/server/src/Support/Metrics/Registry.php +46 -0
- package/server/src/Support/Metrics/ResolvedIssuesMetric.php +35 -0
- package/server/src/Support/Metrics/TotalCustomersMetric.php +29 -0
- package/server/src/Support/Metrics/TotalDistanceTraveledMetric.php +35 -0
- package/server/src/Support/Metrics/TotalDriversMetric.php +28 -0
- package/server/src/Support/Metrics/TotalTimeTraveledMetric.php +35 -0
- package/server/src/Support/Metrics.php +46 -249
- package/server/src/Support/ResolvesOrderServiceStops.php +512 -0
- package/server/src/Support/Telematics/Providers/AfaqyProvider.php +290 -0
- package/server/src/Support/Telematics/Providers/SafeeProvider.php +333 -0
- package/server/src/Tracking/TrackingContextBuilder.php +41 -38
- package/server/src/Tracking/TrackingIntelligenceService.php +109 -9
- package/server/src/Tracking/TrackingStop.php +13 -11
- package/server/src/routes.php +50 -0
- package/server/tests/AnalyticsRoutesTest.php +41 -0
- package/server/tests/CapacityAllocationEngineTest.php +10 -10
- package/server/tests/CustomerContactIdentitySafetyTest.php +54 -0
- package/server/tests/CustomerEndpointTest.php +199 -0
- package/server/tests/DriverVehicleStatusDefaultTest.php +39 -0
- package/server/tests/LabelPdfRenderingTest.php +23 -0
- package/server/tests/LiveControllerViewportTest.php +60 -0
- package/server/tests/MetricsRegistryTest.php +84 -0
- package/server/tests/OrchestrationPayloadBuilderTest.php +2 -2
- package/server/tests/OrderConfigEndpointTest.php +86 -0
- package/server/tests/OrderFileAttachmentTest.php +20 -0
- package/server/tests/ServiceAreaZoneApiShapeTest.php +34 -0
- package/server/tests/VehicleDriverAssignmentTest.php +18 -0
- package/server/tests/VroomOrchestrationEngineTest.php +91 -0
- package/translations/en-us.yaml +38 -0
- package/server/migrations/2026_05_13_000001_repair_customer_contact_user_types.php +0 -86
|
@@ -22,6 +22,10 @@ use Illuminate\Http\Request;
|
|
|
22
22
|
*/
|
|
23
23
|
class LiveController extends Controller
|
|
24
24
|
{
|
|
25
|
+
protected const DEFAULT_VIEWPORT_LIMIT = 500;
|
|
26
|
+
protected const MAX_VIEWPORT_LIMIT = 1000;
|
|
27
|
+
protected const VIEWPORT_BOUNDS_PRECISION = 4;
|
|
28
|
+
|
|
25
29
|
/**
|
|
26
30
|
* Get coordinates for active orders.
|
|
27
31
|
*
|
|
@@ -134,33 +138,19 @@ class LiveController extends Controller
|
|
|
134
138
|
*/
|
|
135
139
|
public function drivers(Request $request)
|
|
136
140
|
{
|
|
137
|
-
$bounds = $
|
|
138
|
-
$
|
|
141
|
+
$bounds = $this->normalizeLiveBounds($request);
|
|
142
|
+
$limit = $this->normalizeLiveLimit($request);
|
|
143
|
+
$cacheParams = ['bounds' => $bounds, 'limit' => $limit];
|
|
139
144
|
|
|
140
|
-
return LiveCacheService::remember('drivers', $cacheParams, function () use ($bounds) {
|
|
145
|
+
return LiveCacheService::remember('drivers', $cacheParams, function () use ($bounds, $limit) {
|
|
141
146
|
$query = Driver::where(['company_uuid' => session('company')])
|
|
142
147
|
->with(['user', 'vehicle'])
|
|
143
148
|
->applyDirectivesForPermissions('fleet-ops list driver');
|
|
144
149
|
|
|
145
|
-
|
|
146
|
-
$
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
AND ST_X(location) BETWEEN -180 AND 180
|
|
150
|
-
AND NOT (ST_X(location) = 0 AND ST_Y(location) = 0)
|
|
151
|
-
');
|
|
152
|
-
|
|
153
|
-
// Apply spatial filtering if bounds are provided
|
|
154
|
-
if ($bounds && is_array($bounds) && count($bounds) === 4) {
|
|
155
|
-
[$south, $west, $north, $east] = $bounds;
|
|
156
|
-
|
|
157
|
-
// Use MySQL spatial functions for POINT column
|
|
158
|
-
// ST_Within checks if location is within the bounding box
|
|
159
|
-
$query->whereRaw(
|
|
160
|
-
'ST_Within(location, ST_MakeEnvelope(POINT(?, ?), POINT(?, ?)))',
|
|
161
|
-
[$west, $south, $east, $north]
|
|
162
|
-
);
|
|
163
|
-
}
|
|
150
|
+
$this->applyLiveLocationGuards($query);
|
|
151
|
+
$this->applyLiveViewportBounds($query, $bounds);
|
|
152
|
+
|
|
153
|
+
$query->orderByDesc('updated_at')->orderByDesc('id')->limit($limit);
|
|
164
154
|
|
|
165
155
|
$drivers = $query->get();
|
|
166
156
|
|
|
@@ -175,34 +165,20 @@ class LiveController extends Controller
|
|
|
175
165
|
*/
|
|
176
166
|
public function vehicles(Request $request)
|
|
177
167
|
{
|
|
178
|
-
$bounds = $
|
|
179
|
-
$
|
|
168
|
+
$bounds = $this->normalizeLiveBounds($request);
|
|
169
|
+
$limit = $this->normalizeLiveLimit($request);
|
|
170
|
+
$cacheParams = ['bounds' => $bounds, 'limit' => $limit];
|
|
180
171
|
|
|
181
|
-
return LiveCacheService::remember('vehicles', $cacheParams, function () use ($bounds) {
|
|
172
|
+
return LiveCacheService::remember('vehicles', $cacheParams, function () use ($bounds, $limit) {
|
|
182
173
|
// Fetch vehicles that are online
|
|
183
174
|
$query = Vehicle::where(['company_uuid' => session('company')])
|
|
184
175
|
->with(['devices', 'driver'])
|
|
185
176
|
->applyDirectivesForPermissions('fleet-ops list vehicle');
|
|
186
177
|
|
|
187
|
-
|
|
188
|
-
$
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
AND ST_X(location) BETWEEN -180 AND 180
|
|
192
|
-
AND NOT (ST_X(location) = 0 AND ST_Y(location) = 0)
|
|
193
|
-
');
|
|
194
|
-
|
|
195
|
-
// Apply spatial filtering if bounds are provided
|
|
196
|
-
if ($bounds && is_array($bounds) && count($bounds) === 4) {
|
|
197
|
-
[$south, $west, $north, $east] = $bounds;
|
|
198
|
-
|
|
199
|
-
// Use MySQL spatial functions for POINT column
|
|
200
|
-
// ST_Within checks if location is within the bounding box
|
|
201
|
-
$query->whereRaw(
|
|
202
|
-
'ST_Within(location, ST_MakeEnvelope(POINT(?, ?), POINT(?, ?)))',
|
|
203
|
-
[$west, $south, $east, $north]
|
|
204
|
-
);
|
|
205
|
-
}
|
|
178
|
+
$this->applyLiveLocationGuards($query);
|
|
179
|
+
$this->applyLiveViewportBounds($query, $bounds);
|
|
180
|
+
|
|
181
|
+
$query->orderByDesc('updated_at')->orderByDesc('id')->limit($limit);
|
|
206
182
|
|
|
207
183
|
$vehicles = $query->get();
|
|
208
184
|
|
|
@@ -217,39 +193,98 @@ class LiveController extends Controller
|
|
|
217
193
|
*/
|
|
218
194
|
public function places(Request $request)
|
|
219
195
|
{
|
|
196
|
+
$bounds = $this->normalizeLiveBounds($request);
|
|
197
|
+
$limit = $this->normalizeLiveLimit($request);
|
|
198
|
+
|
|
220
199
|
// Cache key includes filter parameters
|
|
221
|
-
$cacheParams = $request->only(['query', 'type', 'country',
|
|
200
|
+
$cacheParams = array_merge($request->only(['query', 'type', 'country']), [
|
|
201
|
+
'bounds' => $bounds,
|
|
202
|
+
'limit' => $limit,
|
|
203
|
+
]);
|
|
222
204
|
|
|
223
|
-
return LiveCacheService::remember('places', $cacheParams, function () use ($request) {
|
|
205
|
+
return LiveCacheService::remember('places', $cacheParams, function () use ($request, $bounds, $limit) {
|
|
224
206
|
// Query places based on filters
|
|
225
207
|
$query = Place::where(['company_uuid' => session('company')])
|
|
226
208
|
->filter(new PlaceFilter($request))
|
|
227
209
|
->applyDirectivesForPermissions('fleet-ops list place');
|
|
228
210
|
|
|
229
|
-
|
|
230
|
-
$
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
AND ST_X(location) BETWEEN -180 AND 180
|
|
234
|
-
AND NOT (ST_X(location) = 0 AND ST_Y(location) = 0)
|
|
235
|
-
');
|
|
236
|
-
|
|
237
|
-
// Apply spatial filtering if bounds are provided
|
|
238
|
-
$bounds = $request->input('bounds');
|
|
239
|
-
if ($bounds && is_array($bounds) && count($bounds) === 4) {
|
|
240
|
-
[$south, $west, $north, $east] = $bounds;
|
|
241
|
-
|
|
242
|
-
// Use MySQL spatial functions for POINT column
|
|
243
|
-
// ST_Within checks if location is within the bounding box
|
|
244
|
-
$query->whereRaw(
|
|
245
|
-
'ST_Within(location, ST_MakeEnvelope(POINT(?, ?), POINT(?, ?)))',
|
|
246
|
-
[$west, $south, $east, $north]
|
|
247
|
-
);
|
|
248
|
-
}
|
|
211
|
+
$this->applyLiveLocationGuards($query);
|
|
212
|
+
$this->applyLiveViewportBounds($query, $bounds);
|
|
213
|
+
|
|
214
|
+
$query->orderByDesc('updated_at')->orderByDesc('id')->limit($limit);
|
|
249
215
|
|
|
250
216
|
$places = $query->get();
|
|
251
217
|
|
|
252
218
|
return PlaceIndexResource::collection($places);
|
|
253
219
|
});
|
|
254
220
|
}
|
|
221
|
+
|
|
222
|
+
protected function normalizeLiveBounds(Request $request): ?array
|
|
223
|
+
{
|
|
224
|
+
$bounds = $request->input('bounds');
|
|
225
|
+
|
|
226
|
+
if (!is_array($bounds) || count($bounds) !== 4) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
$bounds = array_values($bounds);
|
|
231
|
+
|
|
232
|
+
if (array_filter($bounds, fn ($value) => !is_numeric($value))) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
[$south, $west, $north, $east] = array_map('floatval', $bounds);
|
|
237
|
+
|
|
238
|
+
if ($south < -90 || $south > 90 || $north < -90 || $north > 90) {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if ($west < -180 || $west > 180 || $east < -180 || $east > 180) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if ($south > $north || $west > $east) {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return array_map(
|
|
251
|
+
fn ($value) => round($value, static::VIEWPORT_BOUNDS_PRECISION),
|
|
252
|
+
[$south, $west, $north, $east]
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
protected function normalizeLiveLimit(Request $request): int
|
|
257
|
+
{
|
|
258
|
+
$limit = (int) $request->input('limit', static::DEFAULT_VIEWPORT_LIMIT);
|
|
259
|
+
|
|
260
|
+
if ($limit < 1) {
|
|
261
|
+
return static::DEFAULT_VIEWPORT_LIMIT;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return min($limit, static::MAX_VIEWPORT_LIMIT);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
protected function applyLiveLocationGuards($query): void
|
|
268
|
+
{
|
|
269
|
+
$query->whereNotNull('location')
|
|
270
|
+
->whereRaw('
|
|
271
|
+
ST_Y(location) BETWEEN -90 AND 90
|
|
272
|
+
AND ST_X(location) BETWEEN -180 AND 180
|
|
273
|
+
AND NOT (ST_X(location) = 0 AND ST_Y(location) = 0)
|
|
274
|
+
');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
protected function applyLiveViewportBounds($query, ?array $bounds): void
|
|
278
|
+
{
|
|
279
|
+
if (!$bounds) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
[$south, $west, $north, $east] = $bounds;
|
|
284
|
+
|
|
285
|
+
$query->whereRaw(
|
|
286
|
+
'ST_Y(location) BETWEEN ? AND ? AND ST_X(location) BETWEEN ? AND ?',
|
|
287
|
+
[$south, $north, $west, $east]
|
|
288
|
+
);
|
|
289
|
+
}
|
|
255
290
|
}
|
|
@@ -3,15 +3,17 @@
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Http\Controllers\Internal\v1;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\FleetOps\Support\Metrics;
|
|
6
|
+
use Fleetbase\FleetOps\Support\Metrics\Registry;
|
|
6
7
|
use Fleetbase\Http\Controllers\Controller;
|
|
7
8
|
use Illuminate\Http\Request;
|
|
9
|
+
use Illuminate\Support\Carbon;
|
|
8
10
|
|
|
9
11
|
class MetricsController extends Controller
|
|
10
12
|
{
|
|
11
13
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
14
|
+
* Legacy bulk endpoint. Returns a flat map of slug → scalar value for the
|
|
15
|
+
* requested period. Preserved for backward compat; the dashboard widgets
|
|
16
|
+
* now prefer per-slug GET /metrics/{slug}.
|
|
15
17
|
*/
|
|
16
18
|
public function all(Request $request)
|
|
17
19
|
{
|
|
@@ -27,4 +29,81 @@ class MetricsController extends Controller
|
|
|
27
29
|
|
|
28
30
|
return response()->json($data);
|
|
29
31
|
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Per-metric endpoint backing <Widget::KpiTile>.
|
|
35
|
+
*
|
|
36
|
+
* Accepts:
|
|
37
|
+
* ?period=7d|30d|90d OR ?start=ISO&end=ISO
|
|
38
|
+
* ?sparkline=true to include a per-day mini-chart series
|
|
39
|
+
* ?compare=true to include period-over-period delta (default: true)
|
|
40
|
+
*
|
|
41
|
+
* Returns: { slug, value, format, currency?, delta_pct?, sparkline? }
|
|
42
|
+
*/
|
|
43
|
+
public function show(Request $request, string $slug)
|
|
44
|
+
{
|
|
45
|
+
$class = Registry::resolve($slug);
|
|
46
|
+
if ($class === null) {
|
|
47
|
+
return response()->json(['error' => "Unknown metric: {$slug}"], 404);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
[$start, $end] = $this->resolvePeriod($request);
|
|
51
|
+
|
|
52
|
+
$metric = $class::forCompany($request->user()->company)->between($start, $end);
|
|
53
|
+
|
|
54
|
+
if ($request->boolean('compare', true)) {
|
|
55
|
+
$duration = $end->getTimestamp() - $start->getTimestamp();
|
|
56
|
+
$compareEnd = (clone $start);
|
|
57
|
+
$compareStart = (clone $start)->modify("-{$duration} seconds");
|
|
58
|
+
$metric->compareTo($compareStart, $compareEnd);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if ($request->boolean('sparkline')) {
|
|
62
|
+
$buckets = (int) $request->input('sparkline_buckets', 14);
|
|
63
|
+
$metric->withSparkline($buckets, 'day');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return response()->json($metric->get());
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Parse ?period=7d/30d/90d into a [start, end] pair, falling back to
|
|
71
|
+
* explicit ?start=&end= ISO dates, then to a 30-day default.
|
|
72
|
+
*/
|
|
73
|
+
private function resolvePeriod(Request $request): array
|
|
74
|
+
{
|
|
75
|
+
$period = $request->string('period')->toString();
|
|
76
|
+
|
|
77
|
+
if ($period !== '') {
|
|
78
|
+
$days = match ($period) {
|
|
79
|
+
'7d' => 7,
|
|
80
|
+
'14d' => 14,
|
|
81
|
+
'30d' => 30,
|
|
82
|
+
'90d' => 90,
|
|
83
|
+
'180d' => 180,
|
|
84
|
+
'365d' => 365,
|
|
85
|
+
default => null,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
if ($days !== null) {
|
|
89
|
+
$end = Carbon::now()->toDateTime();
|
|
90
|
+
$start = Carbon::now()->subDays($days)->toDateTime();
|
|
91
|
+
|
|
92
|
+
return [$start, $end];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
$start = $request->date('start') ?? Carbon::now()->subDays(30)->toDateTime();
|
|
97
|
+
$end = $request->date('end') ?? Carbon::now()->toDateTime();
|
|
98
|
+
|
|
99
|
+
if (!$start instanceof \DateTime) {
|
|
100
|
+
$start = Carbon::parse($start)->toDateTime();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!$end instanceof \DateTime) {
|
|
104
|
+
$end = Carbon::parse($end)->toDateTime();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return [$start, $end];
|
|
108
|
+
}
|
|
30
109
|
}
|