Commit Graph

7 Commits

Author SHA1 Message Date
Developer
ca4c4e5817 feat: unified crew home screen 2026-06-30 13:46:57 +08:00
Developer
9c1f94d598 Implement role-based conditional navigation for Scanner and Helper 2026-06-30 13:12:55 +08:00
Developer
527f3062fa feat(mobile): add live map routing and helper active trip dashboard 2026-06-30 11:25:22 +08:00
Developer
e1b66034f1 config: update default VERDE_API_URL to local IP 192.168.254.173 2026-06-30 10:36:05 +08:00
Developer
984ce99c72 Implement helper dashboard, providers, assignment check-in, and routing in mobile app 2026-06-29 16:57:29 +08:00
240c5f7adc feat(mobile-scanner): Phase 2 — offline buffer + manual entry
Two production must-haves for field staff: scans don't disappear
when signal drops, and there's a fallback when the camera can't
read a damaged QR.

Offline buffer:
- PendingScan model (local uuid + serial + DOP id + lat/lng +
  timestamp). JSON-encoded.
- ScanQueue: shared_preferences-backed FIFO, capped at 500 items
  (oldest dropped on overflow). API: readAll / add / removeByLocalIds
  / clear / count.
- ScanRepository.scan() now branches on outcome:
  - Server reachable + OK → accepted
  - Server reachable + 4xx with reason → rejected (duplicate /
    expired / invalid). NOT queued — these are permanent answers.
  - DioException / unexpected exception → enqueued + return
    networkError with "Saved offline · {message}".
- ScanSyncer: drains up to 100 items per call to /scanner/scan/bulk,
  removes the batch on success regardless of per-scan acceptance
  (the server has the canonical state now), reports
  (processed, accepted, kept) so the UI can announce the outcome.
- pendingCountProvider state syncs from disk via
  pendingCountInitProvider on home-screen mount.

Home screen surface:
- New amber "X scan(s) waiting to sync" banner appears whenever
  pending > 0. Has a "Sync now" button that runs ScanSyncer.drain()
  and shows a SnackBar with the result.

Manual entry:
- Keyboard icon in the camera-screen top bar opens the
  ManualEntrySheet (bottom sheet with a monospace input).
- Loose client-side validation against the PSGC serial format
  (PH-AREA-YYMM-BBBB-CCCCCC-XX). Strict validation happens server-side.
- Submitting goes through the same scan flow → same outcome
  feedback, same offline-queue behavior.
- Camera is paused while the sheet is up so the same code can't
  double-record.

Bug fix in scan_models:
- ScanResult.scannedAt is non-nullable; the constructor required
  it but had no default — analyzer was angry. Promoted to
  `required this.scannedAt`.

Tests still pass. flutter analyze: 6 style infos, no errors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 23:55:51 +08:00
0b2244fbf7 feat(mobile-scanner): scaffold + Phase 1 — full scan flow
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) <noreply@anthropic.com>
2026-05-01 23:49:20 +08:00