From 3101933ce9022fee88eec1408893ec48da3fc955 Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Tue, 10 Mar 2026 10:52:32 +0000 Subject: [PATCH] Include product purchases in dashboard and purchase history Product licenses (Plugin Dev Kit, Masterclass) were missing from the purchase history page and dashboard total count. Added product license query to PurchaseHistoryController, included count in dashboard totalPurchases, and added amber Product badge/icon to the view. Co-Authored-By: Claude Opus 4.6 --- .../Controllers/CustomerLicenseController.php | 5 +- .../Controllers/PurchaseHistoryController.php | 20 +++ .../customer/purchase-history/index.blade.php | 8 ++ tests/Feature/PurchaseHistoryTest.php | 122 ++++++++++++++++++ 4 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 tests/Feature/PurchaseHistoryTest.php diff --git a/app/Http/Controllers/CustomerLicenseController.php b/app/Http/Controllers/CustomerLicenseController.php index 62cc960a..4f85638a 100644 --- a/app/Http/Controllers/CustomerLicenseController.php +++ b/app/Http/Controllers/CustomerLicenseController.php @@ -63,8 +63,9 @@ public function index(): View default => 'No accounts connected', }; - // Total purchases (licenses + plugins) - $totalPurchases = $licenseCount + $pluginLicenseCount; + // Total purchases (licenses + plugins + products) + $productLicenseCount = $user->productLicenses()->count(); + $totalPurchases = $licenseCount + $pluginLicenseCount + $productLicenseCount; return view('customer.dashboard', compact( 'licenseCount', diff --git a/app/Http/Controllers/PurchaseHistoryController.php b/app/Http/Controllers/PurchaseHistoryController.php index fb2f0b45..e2fd10eb 100644 --- a/app/Http/Controllers/PurchaseHistoryController.php +++ b/app/Http/Controllers/PurchaseHistoryController.php @@ -61,8 +61,28 @@ public function index(): View ]; }); + // Fetch product licenses (e.g. Plugin Dev Kit, Masterclass) + $productLicenses = $user->productLicenses() + ->with('product') + ->orderBy('purchased_at', 'desc') + ->get() + ->map(function ($productLicense) { + return [ + 'type' => 'product', + 'name' => $productLicense->product->name ?? 'Product', + 'description' => $productLicense->product->description ?? null, + 'price' => $productLicense->price_paid, + 'currency' => $productLicense->currency, + 'purchased_at' => $productLicense->purchased_at, + 'expires_at' => null, + 'is_active' => true, + 'href' => $productLicense->product ? route('products.show', $productLicense->product) : null, + ]; + }); + // Combine and sort by purchased_at descending $purchases = $licenses->concat($pluginLicenses) + ->concat($productLicenses) ->sortByDesc('purchased_at') ->values(); diff --git a/resources/views/customer/purchase-history/index.blade.php b/resources/views/customer/purchase-history/index.blade.php index fe821f34..46e1b80f 100644 --- a/resources/views/customer/purchase-history/index.blade.php +++ b/resources/views/customer/purchase-history/index.blade.php @@ -41,6 +41,10 @@
+ @elseif($purchase['type'] === 'product') +
+ +
@else
@@ -63,6 +67,10 @@ License + @elseif($purchase['type'] === 'product') + + Product + @else Plugin diff --git a/tests/Feature/PurchaseHistoryTest.php b/tests/Feature/PurchaseHistoryTest.php new file mode 100644 index 00000000..b79c7191 --- /dev/null +++ b/tests/Feature/PurchaseHistoryTest.php @@ -0,0 +1,122 @@ +get('/customer/purchase-history'); + + $response->assertRedirect('/login'); + } + + public function test_purchase_history_shows_product_licenses(): void + { + $user = User::factory()->create(); + $product = Product::factory()->create(['name' => 'Plugin Dev Kit']); + + ProductLicense::factory()->create([ + 'user_id' => $user->id, + 'product_id' => $product->id, + 'price_paid' => 4900, + 'currency' => 'USD', + 'purchased_at' => now()->subDay(), + ]); + + $response = $this->actingAs($user)->get('/customer/purchase-history'); + + $response->assertStatus(200); + $response->assertSee('Plugin Dev Kit'); + $response->assertSee('Product'); + $response->assertSee('$49.00'); + } + + public function test_purchase_history_shows_multiple_product_types(): void + { + $user = User::factory()->create(); + + $devKit = Product::factory()->create(['name' => 'Plugin Dev Kit']); + $masterclass = Product::factory()->create(['name' => 'The NativePHP Masterclass']); + + ProductLicense::factory()->create([ + 'user_id' => $user->id, + 'product_id' => $devKit->id, + 'price_paid' => 4900, + 'purchased_at' => now()->subDays(2), + ]); + + ProductLicense::factory()->create([ + 'user_id' => $user->id, + 'product_id' => $masterclass->id, + 'price_paid' => 10100, + 'purchased_at' => now()->subDay(), + ]); + + $response = $this->actingAs($user)->get('/customer/purchase-history'); + + $response->assertStatus(200); + $response->assertSee('Plugin Dev Kit'); + $response->assertSee('The NativePHP Masterclass'); + } + + public function test_dashboard_total_purchases_includes_product_licenses(): void + { + $user = User::factory()->create(); + $product = Product::factory()->create(); + + ProductLicense::factory()->create([ + 'user_id' => $user->id, + 'product_id' => $product->id, + ]); + + $response = $this->actingAs($user)->get('/dashboard'); + + $response->assertStatus(200); + $response->assertViewHas('totalPurchases', 1); + } + + public function test_purchase_history_shows_empty_state_when_no_purchases(): void + { + $user = User::factory()->create(); + + $response = $this->actingAs($user)->get('/customer/purchase-history'); + + $response->assertStatus(200); + $response->assertSee('No purchases yet'); + } + + public function test_product_purchases_show_as_active(): void + { + $user = User::factory()->create(); + $product = Product::factory()->create(['name' => 'Plugin Dev Kit']); + + ProductLicense::factory()->create([ + 'user_id' => $user->id, + 'product_id' => $product->id, + 'purchased_at' => now(), + ]); + + $response = $this->actingAs($user)->get('/customer/purchase-history'); + + $response->assertStatus(200); + $response->assertSee('Active'); + } +}