From c2115b767ec984f074f0f644c8e482e7b3e919aa Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 1 May 2026 14:07:59 +0800 Subject: [PATCH] feat(web): replace remaining "Soon" stubs with real pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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) --- .../Api/V1/Payment/PaymentController.php | 47 +++++++ resources/views/admin/dashboard.blade.php | 6 +- resources/views/admin/finance.blade.php | 108 +++++++++++++++ resources/views/admin/live-tracking.blade.php | 126 ++++++++++++++++++ .../views/admin/partials/sidebar.blade.php | 18 +-- resources/views/admin/settings.blade.php | 104 +++++++++++++++ routes/api.php | 7 +- routes/web.php | 26 ++-- .../Api/V1/Payment/PaymentFlowTest.php | 2 +- 9 files changed, 414 insertions(+), 30 deletions(-) create mode 100644 resources/views/admin/finance.blade.php create mode 100644 resources/views/admin/live-tracking.blade.php create mode 100644 resources/views/admin/settings.blade.php diff --git a/app/Http/Controllers/Api/V1/Payment/PaymentController.php b/app/Http/Controllers/Api/V1/Payment/PaymentController.php index b57c577..83374b8 100644 --- a/app/Http/Controllers/Api/V1/Payment/PaymentController.php +++ b/app/Http/Controllers/Api/V1/Payment/PaymentController.php @@ -81,6 +81,53 @@ class PaymentController extends ApiController ]); } + public function adminIndex(Request $request): JsonResponse + { + $request->validate([ + 'status' => ['nullable', 'in:pending,processing,paid,failed,refunded'], + 'purpose' => ['nullable', 'in:resident_code_purchase,store_inventory_purchase'], + 'q' => ['nullable', 'string', 'max:100'], + 'per_page' => ['nullable', 'integer', 'min:1', 'max:100'], + ]); + $perPage = (int) $request->input('per_page', 25); + + $payments = Payment::query() + ->with('payer') + ->when($request->filled('status'), fn ($q) => $q->where('status', $request->string('status'))) + ->when($request->filled('purpose'), fn ($q) => $q->where('purpose', $request->string('purpose'))) + ->when($request->filled('q'), function ($q) use ($request) { + $term = '%'.$request->string('q').'%'; + $q->where(fn ($qq) => $qq->where('uuid', 'like', $term) + ->orWhereHas('payer', fn ($p) => $p->where('email', 'like', $term)->orWhere('phone', 'like', $term))); + }) + ->orderByDesc('id') + ->paginate($perPage); + + return $this->ok( + $payments->getCollection()->map(fn (Payment $p) => [ + 'id' => $p->uuid, + 'payer' => [ + 'name' => $p->payer?->full_name, + 'email' => $p->payer?->email, + ], + 'purpose' => $p->purpose, + 'amount_centavos' => $p->amount_centavos, + 'currency' => $p->currency, + 'provider' => $p->provider, + 'status' => $p->status, + 'paid_at' => $p->paid_at?->toIso8601String(), + 'created_at' => $p->created_at?->toIso8601String(), + ])->all(), + null, + [ + 'page' => $payments->currentPage(), + 'per_page' => $payments->perPage(), + 'total' => $payments->total(), + 'last_page' => $payments->lastPage(), + ], + ); + } + /** * Admin manually marks a payment paid (used for cash-paid resident * purchases or when the manual driver is in effect). Triggers the diff --git a/resources/views/admin/dashboard.blade.php b/resources/views/admin/dashboard.blade.php index cd3ac72..bde4613 100644 --- a/resources/views/admin/dashboard.blade.php +++ b/resources/views/admin/dashboard.blade.php @@ -47,15 +47,15 @@
  • - Backend modules 8 / 13 + Backend modules 13 / 13
  • - Tests passing 139 + Tests passing 171
  • - Operations / Live tracking / Finance ship with later modules. + Reverb live broadcast and FCM push are deferred — polling drives the live map for now.
    diff --git a/resources/views/admin/finance.blade.php b/resources/views/admin/finance.blade.php new file mode 100644 index 0000000..b9d6234 --- /dev/null +++ b/resources/views/admin/finance.blade.php @@ -0,0 +1,108 @@ +@extends('admin.layouts.app', ['pageTitle' => 'Finance']) + +@section('page') +
    +
    +

    Payments

    +

    Resident code purchases + wholesale store buys.

    +
    + +
    + + + + +
    + +
    + + + + + + + + + + + + + + + +
    PaymentPayerPurposeAmountProviderStatusActions
    Loading…
    +
    +
    + + +@endsection diff --git a/resources/views/admin/live-tracking.blade.php b/resources/views/admin/live-tracking.blade.php new file mode 100644 index 0000000..5e22807 --- /dev/null +++ b/resources/views/admin/live-tracking.blade.php @@ -0,0 +1,126 @@ +@extends('admin.layouts.app', ['pageTitle' => 'Live Tracking']) + +@section('page') + + +
    +
    +
    +

    Live Tracking

    +

    Active truck positions, refreshed every 10 seconds.

    +
    +
    + + Loading… +
    +
    + +
    +
    +
    +
    + +
    +

    Trucks online

    +
    +
    Loading…
    +
    +
    +
    +
    + + + +@endsection diff --git a/resources/views/admin/partials/sidebar.blade.php b/resources/views/admin/partials/sidebar.blade.php index 25b968c..cc25830 100644 --- a/resources/views/admin/partials/sidebar.blade.php +++ b/resources/views/admin/partials/sidebar.blade.php @@ -3,20 +3,22 @@ $isActive = fn ($path) => str_starts_with($current, ltrim($path, '/')); $groups = [ 'Operations' => [ - ['href' => '/admin/operations', 'label' => 'Today\'s Trips', 'disabled' => true, 'icon' => 'truck'], + ['href' => '/admin/trips', 'label' => 'Trips', 'icon' => 'truck'], + ['href' => '/admin/operations/live', 'label' => 'Live Tracking', 'icon' => 'map'], ['href' => '/admin/operations/calendar', 'label' => 'Trip Calendar', 'disabled' => true, 'icon' => 'calendar'], - ['href' => '/admin/operations/live', 'label' => 'Live Tracking', 'disabled' => true, 'icon' => 'map'], ['href' => '/admin/operations/incidents', 'label' => 'Incidents', 'disabled' => true, 'icon' => 'alert'], ], 'Planning' => [ ['href' => '/admin/routes', 'label' => 'Routes', 'icon' => 'route'], - ['href' => '/admin/teams', 'label' => 'Teams', 'disabled' => true, 'icon' => 'users'], + ['href' => '/admin/teams', 'label' => 'Teams', 'icon' => 'users'], + ['href' => '/admin/trucks', 'label' => 'Trucks', 'icon' => 'truck'], ['href' => '/admin/drop-off-points', 'label' => 'Drop-off Points', 'icon' => 'pin'], ['href' => '/admin/dumpsites', 'label' => 'Dumpsites', 'icon' => 'trash'], ], 'People' => [ ['href' => '/admin/users', 'label' => 'All Users', 'icon' => 'people'], ['href' => '/admin/households', 'label' => 'Households', 'icon' => 'home'], + ['href' => '/admin/partner-stores', 'label' => 'Partner Stores', 'icon' => 'cash'], ], 'QR Management' => [ ['href' => '/admin/qr-batches', 'label' => 'Code Batches', 'icon' => 'qr'], @@ -26,13 +28,13 @@ ['href' => '/admin/service-areas', 'label' => 'Service Areas', 'icon' => 'globe'], ], 'Finance' => [ - ['href' => '/admin/finance', 'label' => 'Sales & Payouts', 'disabled' => true, 'icon' => 'cash'], + ['href' => '/admin/finance', 'label' => 'Payments', 'icon' => 'cash'], ], 'Reports' => [ - ['href' => '/admin/reports', 'label' => 'Reports', 'disabled' => true, 'icon' => 'chart'], + ['href' => '/admin/reports', 'label' => 'Reports', 'icon' => 'chart'], ], 'Settings' => [ - ['href' => '/admin/settings', 'label' => 'System Config', 'disabled' => true, 'icon' => 'cog'], + ['href' => '/admin/settings', 'label' => 'System Config', 'icon' => 'cog'], ], ]; @@ -55,9 +57,7 @@ 'cog' => '', ]; - $renderIcon = function ($name) use ($icons) { - return $icons[$name] ?? $icons['cog']; - }; + $renderIcon = fn ($name) => $icons[$name] ?? $icons['cog']; @endphp