Commit Graph

57 Commits

Author SHA1 Message Date
Developer
62bcf68bdb feat: add partner store analytics, history logs, and global SRP validation 2026-07-01 15:02:43 +08:00
Developer
cc4fd8ba17 feat: early dumping detour mode backend logic 2026-07-01 13:23:39 +08:00
Developer
70741a3c24 feat: live tracking setup and map parsing fixes 2026-07-01 13:04:51 +08:00
Developer
7a63d2c680 Merge branch 'main' of https://gitea-qzlf.srv1050180.hstgr.cloud/admin/Verde-Web 2026-06-30 16:10:43 +08:00
ramram1515
2467633b1b Config 2026-06-30 16:10:19 +08:00
Developer
d66d6e3396 fix: resolve tenant_id scoping issue on QR code batch generation 2026-06-30 15:59:03 +08:00
Developer
6c83d99fd7 Merge branch 'main' of https://gitea-qzlf.srv1050180.hstgr.cloud/admin/Verde-Web 2026-06-30 15:33:50 +08:00
Developer
5959f74746 chore: clean up 2026-06-30 15:32:01 +08:00
Developer
cfb09fd5fb feat: add void and delete endpoints for qr batches, fix batch list status counts 2026-06-30 15:03:25 +08:00
Developer
7452848cf9 fix: resolve duplicate entry error when adding a soft-deleted member back to a household 2026-06-30 14:48:23 +08:00
Developer
bb9825a86b feat: add batch activation endpoint and fix batch generation sequence bug 2026-06-30 14:27:17 +08:00
ramram1515
b8c56585bc UI enhancements and Actionable Notifications 2026-06-30 14:06:21 +08:00
Developer
a4e0fd3408 Merge branch 'main' of https://gitea-qzlf.srv1050180.hstgr.cloud/admin/Verde-Web 2026-06-30 13:35:28 +08:00
Developer
92f8ee95fc feat: Add Geofence Leaflet drawing tool for DOPs and Dumpsites 2026-06-30 13:31:36 +08:00
ramram1515
88caf14aa1 fix(api): force Manila timezone for upcoming pickups evaluation 2026-06-30 13:21:56 +08:00
Developer
7601d721d2 feat(api): add active-trip endpoint for helper and enable Vite dev server CORS 2026-06-30 11:25:31 +08:00
Developer
64d442ec2e feat: add proof of residency upload option during admin household creation 2026-06-30 10:27:49 +08:00
Super Admin
cfd7166a24 feat: implement drop-off points & dumpsites edit/delete and status transition logic 2026-06-29 23:15:55 +08:00
Super Admin
f2c1573f00 feat: Implement Truck edit, delete, and automatic trip cancellation when moved to maintenance/retired status 2026-06-29 22:59:47 +08:00
Super Admin
7a8fe3e7c3 feat: Add mapping, boundaries, and tabbed slide-over management to Partner Stores 2026-06-29 22:32:59 +08:00
Super Admin
31b9bbb026 feat: Implement tabbed slide-over edit modal for households 2026-06-29 19:42:16 +08:00
Developer
7dc074e3c3 Implement household, QR sale, team helpers assignment, and helper endpoints 2026-06-29 16:56:33 +08:00
Developer
b85fc255cf feat: implement barangay and household management with associated tests 2026-06-29 15:02:52 +08:00
ramram1515
9ac2ea7bda chore: apply code review checklist and web design guidelines fixes 2026-06-29 13:54:14 +08:00
Developer
2c7c3528e4 feat: implement interactive Barangay polygon map selector in Service Areas 2026-06-29 13:22:45 +08:00
Developer
4d8ccd3cd4 Merge branch 'main' of https://gitea-qzlf.srv1050180.hstgr.cloud/admin/Verde-Web 2026-06-29 12:45:13 +08:00
Developer
8dbc87b44f feat: add super admin LGU management and global user creator dashboard screens 2026-06-29 12:45:07 +08:00
ramram1515
91df4d05f0 fix: increase per_page validation limit to 500 across all remaining admin endpoints 2026-06-29 12:26:37 +08:00
ramram1515
e76eec1fbd Merge branch 'main' of https://gitea-qzlf.srv1050180.hstgr.cloud/admin/Verde-Web 2026-06-29 12:21:48 +08:00
ramram1515
4b483c093b fix: increase per_page validation limit to 500 on admin endpoints to allow drop-down populating 2026-06-29 12:17:34 +08:00
Developer
82dc044e1a merge: resolve conflicts in EnsureUserHasRole.php and echo.js 2026-06-29 12:05:52 +08:00
ramram1515
8638a80067 feat: implement interactive map location picker and address search with autocomplete suggestions 2026-06-29 11:54:39 +08:00
Developer
167abdc6ad fix: resolve super_admin login, echo crash and GD extension test failures 2026-06-29 11:43:40 +08:00
b1bc73b100 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>
2026-05-01 23:36:43 +08:00
fcf2f9d0bf feat: multi-LGU tenancy — Phase A (foundation + customer-web)
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>
2026-05-01 22:38:35 +08:00
f552bccd1a feat(customer-web): Phase 3 — codes / pickups / history / live tracker
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>
2026-05-01 19:49:04 +08:00
1bc1ea7282 feat(customer-web): Phase 2 — onboarding wizard + household management
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>
2026-05-01 18:55:38 +08:00
306f8fb4e8 feat(backend): customer-site API gaps + CORS
- 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>
2026-05-01 17:55:10 +08:00
968ced302c feat(live): god's-eye view — heading, route, breadcrumb, click-to-detail, push
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>
2026-05-01 17:44:13 +08:00
86ea731eb0 feat(backend): finish all deferred items — Sentry, audit, bulk, FCM, Reverb, PSGC
- **Sentry** wired in bootstrap/app.php via Sentry\Laravel\Integration.
  No-op when SENTRY_LARAVEL_DSN is empty.

- **Audit logging** broadened: LogsActivity applied to Household, QrCode,
  Trip, Payment with tight logOnly whitelists and named log channels
  ('household', 'qr_code', 'trip', 'payment') to keep the activity
  stream useful.

- **Bulk admin actions**:
  POST /admin/households/bulk-approve — skips already-approved /
  no-proof / unknown ids, reports per-id outcomes
  POST /admin/bulk/users/{suspend,activate} — admins protected
  POST /admin/bulk/qr-codes/void — respects state machine transitions

- **FCM push scaffold**: PushDriver contract with LogPushDriver
  (default) and FcmPushDriver (activates when FCM_SERVER_KEY is set).
  PushChannel adapts notifications. RoutesByPreferences now also
  routes via push when push_enabled + fcm_token. toPush() payloads
  added to HouseholdApproved / QrBalanceLow / PickupImminent.

- **Reverb WebSocket broadcast**: laravel/reverb installed.
  TruckLocationBroadcast fires on every TruckTracker::record().
  routes/channels.php authenticates: admins → private-admin.live,
  residents → area.{id}.trucks (only if their household barangay is
  covered by the service area).

- **PSGC seeder** expanded: all 17 PH regions, 4 NCR districts,
  all 17 NCR cities. Sample barangays still carry rectangular
  boundaries for point-in-polygon resolution tests.

- **Conditional SPATIAL INDEX migration** for barangays.boundary —
  safe no-op until every row has a polygon (i.e., after full PSA
  dataset import). Re-running `php artisan migrate` after import
  flips the column to NOT NULL and adds the index.

196 feature tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 17:22:43 +08:00
58a7d3680a feat(backend): hardening sprint — docs, schedulers, validation, email + pickup
1. API docs via dedoc/scramble at /docs/api (scoped to api/v1).
   Linked from the admin sidebar Settings group.

2. Scheduled commands registered in routes/console.php:
   - reports:aggregate (02:00) — daily/weekly/monthly aggregations
   - qr:expire (02:30) — flips past-due allocated/active codes to expired
   - trucks:prune-locations (03:00) — drops history older than retention
     window (default 7 days, config('verde.location_retention_days'))
   All idempotent + withoutOverlapping. --dry flags on qr:expire and
   trucks:prune-locations for safe inspection.

3. Trip double-booking validation: AdminTripController::store rejects
   new trips when the team or truck already has a non-cancelled trip on
   the same date. override_conflicts: true bypasses for emergencies.
   Cancelled trips don't block rebooking.

4a. Email verification: User implements MustVerifyEmail.
    VerifyEmailNotification overrides verificationUrl() for our
    namespaced route. Register sends the link automatically (best
    effort, won't block signup). POST /auth/email/resend (auth) +
    GET /auth/email/verify/{id}/{hash} (signed URL).

4b. Password change while logged in: POST /me/password validates
    current_password, requires the new password to differ, revokes
    every other active token on success — current session stays.

5a. PickupImminent notification: when TripStop -> arrived,
    TripExecutor::notifyAssignedHouseholds() finds households whose
    assigned_drop_off_point_id matches and sends DB + SMS.

5b. Auto-geofence on truck location: TruckTracker::record() now
    auto-fires TripExecutor::arriveAtDumpsite() when an in-progress
    trip's truck pings inside its dumpsite boundary. The executor's
    status guard prevents duplicate timeline events if the driver also
    presses arrive-dumpsite manually.

190 feature tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 17:13:00 +08:00
c2115b767e feat(web): replace remaining "Soon" stubs with real pages
- Live Tracking: Leaflet map (CDN) showing active truck positions,
  auto-refresh every 10s, sidebar list with speed + last update.
- Finance / Payments: filterable list of payments with mark-paid
  action that runs admin fulfillment.
- Settings: notification-preferences form bound to /me/notification-
  preferences.

Backend:
- New admin payments index endpoint
  GET /admin/payments + POST /admin/payments/{uuid}/mark-paid (moved
  out of /admin/live/* for clarity).

Sidebar groups now reflect what's live: Operations (Trips, Live
Tracking), Planning (Routes, Teams, Trucks, DOPs, Dumpsites), People
(Users, Households, Partner Stores), QR, Areas, Finance, Reports,
Settings. Trip Calendar + Incidents remain "Soon" — backends ready,
UI work pending. Dashboard backend-status copy updated to 13/13
modules / 171 tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 14:07:59 +08:00
15d56d0e98 feat(backend): finish Module 13 sub-modules + flow fixes
Notifications: notification_preferences + Laravel notifications inbox.
SmsChannel adapter for our SmsService. RoutesByPreferences trait reads
per-user toggles. HouseholdApproved/Rejected, QrBalanceLow, and
CodesPurchased notifications wired in via auto-discovered listeners
or direct dispatch from controllers/StoreOperations.

Payments: payments table + PaymentDriver interface. ManualPaymentDriver
works out of the box; PayMongoDriver activates when
PAYMONGO_SECRET_KEY is set, falls back to manual otherwise. Resident
initiates code-purchase, admin can mark paid manually, webhook applies
real provider events. Fulfillment runs StoreOperations::sellToHousehold.

Live tracking (HTTP polling): truck_location_history (with SPATIAL
INDEX + 7-day retention plan). Driver POST /driver/trucks/{uuid}/location
writes history, updates trucks.last_known_coordinates, caches in Redis,
flags geofence-trigger when entering active trip dumpsite. Admin
GET /admin/live/trucks returns active truck positions. Reverb broadcast
deferred.

Flow corrections:
- QrAllocator now idempotent — re-approving a household no longer
  re-dispenses free codes.
- arrive-dumpsite enforces dumpsite geofence via ST_Contains; can be
  bypassed with override_geofence: true.

171 feature tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 14:01:38 +08:00
0a2e187f45 feat(backend): complete Module 13 reports + analytics
daily_collection_stats, weekly_route_performance, monthly_store_sales
aggregation tables. Aggregator service is idempotent — wipes the slice
and re-inserts. php artisan reports:aggregate (default: yesterday) for
the nightly cron. Admin endpoints: daily-collection / trip-performance
/ store-sales chart series + totals, compliance.csv stream of dumpsite
releases (DENR-style), POST rebuild for on-demand aggregation.

Payments, notifications, and live tracking sub-modules of Module 13
are deferred per scope.

164 feature tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 02:58:09 +08:00
989f4b87b9 feat(backend): complete Module 12 (partner stores)
partner_stores, store_inventories, store_purchases, store_sales tables.
Promotes qr_code_batches.target_store_id and qr_codes.assigned_to_store_id
to real FKs. StoreOperations service handles wholesale issuance
(generates fresh batch -> codes go allocated to store -> inventory tops
up -> StorePurchase recorded) and resident sales (codes flip allocated
-> active to a household, commission computed at the store's rate).
Admin endpoints: store CRUD, issue-inventory, record-sale.

160 feature tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 02:55:29 +08:00
8643057c81 feat(backend): complete Module 11 (scanning + collection logs)
collection_logs table. ScanService validates code state (active only;
used = duplicate, expired = expired, unassigned/allocated = invalid),
geo proximity (≤200m from DOP via ST_Distance_Sphere), then atomically
transitions code -> Used and writes a CollectionLog. Scans inside a
trip context bump trip_stop.total_scans + emit qr_scanned timeline event.
Bulk scan endpoint processes each scan independently for offline sync.
Triggers QrBalanceLow when household active count drops below threshold.

155 feature tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 02:53:25 +08:00
13148fab97 feat(backend): complete Module 10 (trips + timeline)
trips, trip_stops, trip_timeline_events. Admin: schedule/show/cancel.
Driver: start, arrive/depart/skip stops, report incident, arrive at
dumpsite, release load (creates dumpsite_release row + tallies trip
load), complete. Every action writes a typed timeline event with GPS.
Trip number auto-generated TRIP-YYYYMMDD-NNN. Promotes
dumpsite_releases.trip_id to a real FK now that trips exists.

148 feature tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 02:51:52 +08:00
15f9295d34 feat(backend): complete Module 9 (teams + trucks)
trucks (with optional last_known_coordinates POINT 4326),
collection_teams, team_members. Admin CRUD for both. TeamConflictDetector
flags double-assignment of driver/scanner/truck/helper across active
teams; override_conflicts: true bypasses for emergencies. Promotes
routes.default_team_id to a real FK now that collection_teams exists.

144 feature tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 02:48:40 +08:00
cec29a2fc9 feat(web): full admin shell with sidebar + working pages
Sidebar with all 8 nav groups (Operations / Planning / People / QR /
Areas / Finance / Reports / Settings) per the admin-panel-flow spec.
Disabled items show "Soon" and toast on click.

Working pages (real API):
- Dashboard with live stat cards + recent verification queue
- Households (filter, approve, reject with reason)
- Service Areas (CRUD via slide-over)
- QR Batches (generate, mark printed, link to print PDF)
- QR Code Search (lifecycle lookup by serial)
- Drop-off Points (filter + create)
- Dumpsites, Routes, All Users (filterable lists)

Shared helpers on window.Verde — apiFetch, requireAuth, logout, toast,
escapeHtml, formatDate. Tailwind brand tokens, table/card/badge classes,
slide-over panels.

GenerateBatchRequest now accepts target_area_id by uuid for consistency
with the rest of the public API.

139 tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 02:45:56 +08:00
de77503cdd feat(backend): complete Module 8 (routes)
routes + route_stops tables. Admin CRUD with stops submitted as an
ordered array (drag-reorder is client-side; sequence is array-index).
Cloning produces an inactive copy with -COPY-XXXX suffix. RouteCalculator
recomputes total_distance_km via ST_Distance_Sphere across stop
coordinates + dumpsite, and estimated_duration_minutes from dwell time
+ travel time at config-driven avg speed (default 25 km/h).
default_team_id stays nullable bigint until Module 9 adds the FK.

139 feature tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 02:25:04 +08:00