- Eager-load dumpsiteReleases.dumpsite + dumpsiteReleases.releasedByDriver
in AdminTripController::show()
- Add releases[] array to TripResource via whenLoaded('dumpsiteReleases')
- Fields: weight_kg, released_at, dumpsite_name, driver_name,
gate_pass_number, dumpsite_attendant, notes
- Admin Trips detail panel now shows a 'Dumpsite Releases' section
below the timeline with per-release cards
- Load row updated to show release count e.g. '450 kg (2 releases)'
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>
Backend:
- New tenants table with PSGC-derived code, links to
cities_municipalities, optional boundary_polygon override, theme
color, contact info, timezone.
- Adds nullable tenant_id to users / households / drop_off_points /
dumpsites / partner_stores. Foreign-keyed, indexed.
- Tenant model with deriveCode() helper + effectiveBoundary()
fallback chain.
- App\Tenancy\Tenancy — process-level current-tenant register with
withTenant() / withoutScope() helpers for jobs + super-admin.
- App\Tenancy\TenantScope — global Eloquent scope, no-op when no
tenant is set (so seeders + super-admin reads still work).
- App\Tenancy\HasTenant trait — applied to Household, DropOffPoint,
Dumpsite, PartnerStore. Auto-fills tenant_id on create from
Tenancy::current().
- ResolveTenant middleware — reads X-Tenant-Code (or X-Tenant-Id),
validates tenant exists + active, sets Tenancy::current(). Falls
back to authenticated user's tenant_id when header missing.
Registered globally on the api group.
- Login + register now require an active tenant (super-admin
bypasses). Cross-tenant credential reuse is rejected with a
403 + clear message.
- super_admin role added to RoleSeeder + users.role enum.
- Public GET /api/v1/tenants/lookup?code= — no auth, returns tenant
details for the pre-login screen.
Seeders:
- SuperAdminSeeder seeds super@verde.local (tenant_id = null).
- SanPascualTenantSeeder seeds Region IV-A → Batangas → San Pascual
municipality → sample Poblacion barangay → Tenant row with code
SAN-PASCUAL-BAT, then backfills every existing tenant-aware row
(13 users / 2 households / 5 DOPs / 1 dumpsite / 3 stores) so
the dev environment keeps working end-to-end.
- Wired into DatabaseSeeder so migrate:fresh --seed bootstraps
cleanly.
Customer-web:
- New /tenant page — text input, calls public lookup, confirms with
resolved tenant card, stores code + name in cookies (1 year).
"Pilot users: SAN-PASCUAL-BAT" hint as a clickable shortcut.
- /login + /register now redirect to /tenant?next= when no cookie,
show a verde "signing in to <LGU>" pill with a Switch link,
delegate the actual form to client components.
- /api/tenant route — POST sets cookie, DELETE clears.
- apiServer auto-attaches X-Tenant-Code on every API call when the
cookie is present.
- Tenant cookies are non-httpOnly so the client can echo them; the
session token stays httpOnly.
Build: 23 routes (added /tenant), 196 backend tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Backend:
- New GET /api/v1/me/live/trucks. Returns active trucks whose
in-progress trip serves the resident's assigned drop-off point,
with the same shape as the admin endpoint. Empty when household
has no DOP yet.
Frontend (customer-web/):
- /codes — paginated list with status filter pills (all / active /
allocated / used / expired), one-click activate for allocated
codes (in-place state update), inline balance summary + low-balance
warning banner with link to partner stores.
- /pickups — upcoming-trip cards with status pill, route, scheduled
date/time, my stop sequence, driver, truck plate, link to live
tracker. Friendly empty states for "no DOP assigned" and
"no trips scheduled".
- /collections — date-range filtered scan history table (when /
serial / drop-off / weight / waste type), shows total scan count.
- /tracker — Leaflet map polling /me/live/trucks every 10s. Drop-off
pin (verde dot) + truck markers (rotating SVG arrow by heading
degrees, 400ms transition between updates). Diff-update logic:
reuses markers across polls and removes trucks that vanish.
Side list of active trucks with plate, team, speed, last update.
Falls back to NCR center when no DOP coords. Leaflet loaded from
CDN (kept out of bundle) — no SSR concerns.
- Resident nav adds /collections (label "History").
- API client: types LiveTruck + LiveTrucksResponse; me.liveTrucks().
Build: 16 routes, all type-check + lint clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Backend:
- New GET /api/v1/me/household endpoint. Returns the resident's
household (with members + barangay + assigned DOP) or { household:
null }. Used by the wizard to gate-keep the onboarding flow.
Frontend (customer-web/):
- /onboarding — 5-step wizard (Address+map, Size, Proof, Members,
Review). Submits POST /households then uploads proof + adds
members in sequence. Server-side guard redirects to /household
when one already exists.
- /household — full status view:
- Pending: live polling every 30s, "Refresh" button, status pill
- Approved: success state, link back to dashboard
- Rejected: shows reviewer's reason, ResubmitCard re-uploads
proof and flips status back to pending automatically
- Members card with add/remove (head is protected)
- /home now checks household state server-side:
- No household → redirect /onboarding
- Pending/rejected → status banner + link
- Approved → full dashboard with QR balance + next pickup +
quick-action grid
- New components:
- MapPicker — click-to-place pin via Leaflet (CDN-loaded), drag
to fine-tune, returns {lat, lng}
- ProofUpload — drag-and-drop with image preview, jpg/png/pdf,
8MB cap, file size + type validation
- API client adds me.household() and reuses existing household.*
+ uploadProof multipart helpers
Build: 12 routes compile clean, biggest first-load JS 122 kB
(/register), Wizard + Household views 5–7 kB each.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 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>
Live Tracking page upgrades:
- **Heading rotation** on each truck marker via CSS transform driven by
the heading_degrees broadcast field.
- **Click-to-detail** opens a slide-over showing trip number, status,
route, started-at, current load, an ordered stop list color-coded by
status, and the last 20 timeline events.
- **Route overlay** draws planned-route polyline (stops in order →
dumpsite, dashed), stop markers (pending/arrived/completed/skipped
colors), a diamond marker for the dumpsite, and a translucent
geofence polygon when the dumpsite has one configured.
- **Breadcrumb trail** of the last 60 minutes of GPS pings rendered
as a polyline; new pings tack on incrementally while the panel is
open.
- **Real-time push via Reverb** with polling fallback. Subscribes to
private-admin.live, listens for `.truck.location`, and updates
marker position + the trucks-online list immediately. When Reverb
isn't configured or fails, falls back to 10s polling. Status pill
shows live (push) / polling / offline.
Backend additions:
- GET /admin/live/trucks/{uuid}/trail — recent location pings
- GET /admin/live/trucks/{uuid}/active-trip — full trip + stops +
dumpsite + boundary + recent timeline
- Broadcasting auth route now accepts Sanctum bearer tokens (admin
web posts them via Echo's authorizer callback)
Frontend wiring:
- npm: laravel-echo + pusher-js
- window.Verde.getEcho() lazily inits Echo with bearer-token authorizer
- Layout exposes window.VERDE_BROADCAST with reverb config
196 tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>