From 63a2cfa5c0b125e3aaa3504b66e291d77fafcd4b Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 1 May 2026 23:49:20 +0800 Subject: [PATCH] =?UTF-8?q?feat(mobile-scanner):=20scaffold=20+=20Phase=20?= =?UTF-8?q?1=20=E2=80=94=20full=20scan=20flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flutter app for scanner staff at drop-off points, package ph.verde.verde_scanner. Built on the same foundation as mobile-driver — Riverpod 2, Dio, go_router, secure_storage — plus mobile_scanner for native MLKit-backed QR/Code-128 detection. Backend: - DropOffPointResource now exposes db_id alongside the public uuid. The /scanner/scan endpoint takes the integer drop_off_point_id, so the picker screen needs both to resolve a tap → scan target. Auth + tenancy (mirrors driver app): - Tenant gate at /tenant with the same SAN-PASCUAL-BAT shortcut. - Login validates user.role ∈ { scanner, admin } and stashes the bearer in keychain. - ResolveTenant headers attached automatically on every request. DOP picker (new): - /dop screen lists drop-off points within 50 km of current GPS, sorted by distance. Pick one → stored in secure storage as the active session DOP. The router redirects authenticated scanners here until they pick. - GPS prompt screen if location services / permission are missing, with retry. Home screen (new): - "STATIONED AT" banner showing the active DOP with a Change link that returns to the picker. - Premium gradient "Start scanning" CTA. Disabled until a DOP is picked. - Two stat counters (accepted / total) for the current session. - Recent scans list with outcome-colored icons. Camera scan screen (new): - mobile_scanner with QR + Code-128 formats and noDuplicates speed. - Verde reticle overlay with corner brackets, dimmed surroundings. - Bottom translucent banner shows active DOP + accepted count. - Full-screen toast overlay on every scan: green "Accepted" / amber "Duplicate" / red "Expired" / red "Invalid" / gray "Network error". Haptic + system click on accept. 1.5 s cooldown so the same code can't pump. - Torch + camera-flip controls top-right. Data layer: - DopRepository: GET /drop-off-points/nearby, returns DopOption with both id and uuid. - ScanRepository: POST /scanner/scan with current GPS, maps the backend's "rejected: reason[]" response onto a typed ScanOutcome (accepted / duplicate / expired / invalid / networkError). - RecentScansController: in-memory ring buffer capped at 50. Phase 2 will persist + add an offline buffer. Permissions: - iOS Info.plist: NSCameraUsageDescription + NSLocationWhenInUseUsageDescription. - Android manifest: CAMERA + ACCESS_FINE_LOCATION + INTERNET + uses-feature camera. Tests: TenantScreen builds. flutter analyze: 6 style infos, no errors. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/Http/Resources/DropOffPointResource.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Http/Resources/DropOffPointResource.php b/app/Http/Resources/DropOffPointResource.php index dac68ba..3404b9e 100644 --- a/app/Http/Resources/DropOffPointResource.php +++ b/app/Http/Resources/DropOffPointResource.php @@ -11,6 +11,7 @@ class DropOffPointResource extends JsonResource { return [ 'id' => $this->uuid, + 'db_id' => $this->id, 'name' => $this->name, 'code' => $this->code, 'address_line' => $this->address_line,