- GET /partner-stores/nearby — public, residents browse active stores
ranked by distance via ST_Distance_Sphere; PublicPartnerStoreResource
excludes commission rate / owner / permit (only what a buyer needs).
- GET /partner-stores/{uuid} — public details, 404 if not active.
- GET /me/collections — paginated resident QR scan history with
optional from/to date filters.
- GET /me/upcoming-pickups — finds scheduled/in-progress trips whose
route includes the resident's assigned drop-off point. Returns
household_assigned: false when no household yet.
- GET /me/notifications — paginated database notifications inbox
with unread_only filter + unread_count in meta.
- POST /me/notifications/{id}/read, POST .../mark-all-read,
GET .../unread-count.
- config/cors.php — allow CUSTOMER_APP_URL and any EXTRA_CORS_ORIGINS
to call the API with credentials. Same-origin admin web is
unaffected.
202 feature tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
73 lines
2.6 KiB
PHP
73 lines
2.6 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api\V1\Me;
|
|
|
|
use App\Http\Controllers\Api\V1\ApiController;
|
|
use App\Models\Household;
|
|
use App\Models\Trip;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
|
|
class UpcomingPickupsController extends ApiController
|
|
{
|
|
/**
|
|
* Trips scheduled at or after today whose route includes this
|
|
* resident's assigned drop-off point. In-progress trips are
|
|
* always included so the resident sees a "truck on the way"
|
|
* card even mid-day.
|
|
*/
|
|
public function index(Request $request): JsonResponse
|
|
{
|
|
$household = Household::with('assignedDropOffPoint')
|
|
->where('head_user_id', $request->user()->id)
|
|
->first();
|
|
|
|
if (! $household || ! $household->assigned_drop_off_point_id) {
|
|
return $this->ok([
|
|
'household_assigned' => false,
|
|
'pickups' => [],
|
|
]);
|
|
}
|
|
|
|
$dopId = $household->assigned_drop_off_point_id;
|
|
|
|
$trips = Trip::query()
|
|
->with(['route:id,name,code', 'team:id,name,driver_id', 'team.driver:id,first_name,last_name', 'truck:id,plate_number'])
|
|
->whereHas('route.stops', fn ($q) => $q->where('drop_off_point_id', $dopId))
|
|
->where(function ($q) {
|
|
$q->where('scheduled_date', '>=', now()->toDateString())
|
|
->orWhereIn('status', [Trip::STATUS_IN_PROGRESS, Trip::STATUS_AT_DUMPSITE]);
|
|
})
|
|
->whereNotIn('status', [Trip::STATUS_CANCELLED, Trip::STATUS_COMPLETED])
|
|
->orderBy('scheduled_date')
|
|
->orderBy('scheduled_start_time')
|
|
->limit(10)
|
|
->get();
|
|
|
|
$pickups = $trips->map(function (Trip $trip) use ($dopId) {
|
|
$myStop = $trip->route?->stops()->where('drop_off_point_id', $dopId)->first();
|
|
|
|
return [
|
|
'trip_id' => $trip->uuid,
|
|
'trip_number' => $trip->trip_number,
|
|
'status' => $trip->status,
|
|
'scheduled_date' => $trip->scheduled_date?->toDateString(),
|
|
'scheduled_start_time' => $trip->scheduled_start_time,
|
|
'route' => $trip->route?->name,
|
|
'truck_plate' => $trip->truck?->plate_number,
|
|
'driver_name' => $trip->team?->driver?->full_name,
|
|
'my_stop_sequence' => $myStop?->sequence,
|
|
];
|
|
});
|
|
|
|
return $this->ok([
|
|
'household_assigned' => true,
|
|
'drop_off_point' => [
|
|
'id' => $household->assignedDropOffPoint?->uuid,
|
|
'name' => $household->assignedDropOffPoint?->name,
|
|
],
|
|
'pickups' => $pickups,
|
|
]);
|
|
}
|
|
}
|