From b1bc73b1004f8cf3f4efc42e5dd5bd3557c3117f Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 1 May 2026 23:36:43 +0800 Subject: [PATCH] =?UTF-8?q?feat(mobile-driver):=20Phase=202=20=E2=80=94=20?= =?UTF-8?q?trip=20lifecycle=20+=20foreground=20GPS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- app/Http/Controllers/Api/V1/Driver/DriverTripController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/Api/V1/Driver/DriverTripController.php b/app/Http/Controllers/Api/V1/Driver/DriverTripController.php index 147e804..da90c06 100644 --- a/app/Http/Controllers/Api/V1/Driver/DriverTripController.php +++ b/app/Http/Controllers/Api/V1/Driver/DriverTripController.php @@ -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)); }