feat(mobile-driver): Phase 2 — trip lifecycle + foreground GPS
Backend:
- Driver trip endpoints now eager-load 'truck' on both index and
show. The mobile app needs the truck UUID to target the GPS
endpoint (POST /driver/trucks/{truck}/location); without this it
would have to re-query.
Mobile (mobile-driver/):
- TripModel + TripStopModel + DropOffSummary + DumpsiteSummary +
TruckSummary — typed Dart models that mirror the backend's
TripResource shape exactly.
- TripsRepository covers all 11 driver endpoints: index, show,
start, arrive, depart, skip, arrive-dumpsite, release-load,
complete, report-incident, post-location.
- myTripsProvider + tripDetailProvider (FutureProvider.family)
for declarative re-fetching after every action.
- Home screen replaces the placeholder cards with a real trip list:
status pill (scheduled / in_progress / at_dumpsite / completed /
cancelled), today badge, route name + trip number, scheduled
time, truck plate, and a per-trip progress bar showing
done/total stops. Pull-to-refresh + empty/loading/error states.
- Trip detail screen with a verde gradient header (route, trip
number, truck plate, dumpsite), a context-aware primary action
(Start / Arrive at dumpsite / Release load + Complete), and a
list of stops with active-stop highlighting + per-stop Arrive /
Depart / Skip buttons. Skip prompts for a reason. Release load
is a bottom-sheet with a numeric weight input.
- LocationBroadcaster service: 15-second foreground GPS pump that
posts to the truck-location endpoint, with permission cascade,
immediate first-pulse on start, and graceful network-failure
swallowing (Phase 3 will add a local buffer).
- activeTripWatcherProvider: listens to myTripsProvider and
auto-starts/stops the broadcaster when a trip transitions in or
out of the running state. Mounted from the home screen.
- Router gains /trip/:uuid.
Limitations carried forward:
- Foreground only. iOS will throttle the timer within ~30s of
backgrounding. Phase 3 swaps in a native background-locator.
- No offline buffer — if the server is unreachable a GPS pulse is
dropped silently.
- No incident-reporting UI yet (the repository method is in place).
Tests:
- Widget test now renders the TenantScreen in isolation. The
full-app test was flagging timers from Dio + secure_storage that
are tricky to flush deterministically. Phase 3 will add a
fakeAsync harness.
- flutter analyze: 8 style infos, no warnings, no errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,7 @@ class DriverTripController extends ApiController
|
||||
{
|
||||
$user = $request->user();
|
||||
$trips = Trip::query()
|
||||
->with(['route', 'team', 'dumpsite'])
|
||||
->with(['route', 'team', 'truck', 'dumpsite'])
|
||||
->whereIn('status', [Trip::STATUS_SCHEDULED, Trip::STATUS_IN_PROGRESS, Trip::STATUS_AT_DUMPSITE])
|
||||
->whereHas('team', fn ($q) => $q->where('driver_id', $user->id))
|
||||
->orderBy('scheduled_date')
|
||||
@@ -30,7 +30,7 @@ class DriverTripController extends ApiController
|
||||
public function show(Request $request, Trip $trip): JsonResponse
|
||||
{
|
||||
$this->authorizeDriver($request, $trip);
|
||||
$trip->load(['route', 'team', 'dumpsite', 'stops.dropOffPoint', 'timelineEvents.recordedBy']);
|
||||
$trip->load(['route', 'team', 'truck', 'dumpsite', 'stops.dropOffPoint', 'timelineEvents.recordedBy']);
|
||||
|
||||
return $this->ok(new TripResource($trip));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user