Skip to content

Commit 2b97e31

Browse files
committed
Detect current status
1 parent 6393c1e commit 2b97e31

4 files changed

Lines changed: 137 additions & 16 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
namespace App\Livewire;
4+
5+
use App\Support\GitHubOAuth;
6+
use Illuminate\Support\Facades\Cache;
7+
use Livewire\Component;
8+
9+
class GitHubAccessBanner extends Component
10+
{
11+
public bool $inline = false;
12+
13+
public ?string $collaboratorStatus = null;
14+
15+
public function mount(bool $inline = false): void
16+
{
17+
$this->inline = $inline;
18+
$this->checkCollaboratorStatus();
19+
}
20+
21+
public function checkCollaboratorStatus(): void
22+
{
23+
$user = auth()->user();
24+
25+
if (! $user || ! $user->github_username) {
26+
$this->collaboratorStatus = null;
27+
28+
return;
29+
}
30+
31+
// Cache the status for 5 minutes to avoid excessive API calls
32+
$cacheKey = "github_collab_status_{$user->id}";
33+
34+
$this->collaboratorStatus = Cache::remember($cacheKey, 300, function () use ($user) {
35+
$github = GitHubOAuth::make();
36+
37+
return $github->checkCollaboratorStatus($user->github_username);
38+
});
39+
40+
// If they have active access but we haven't recorded it, update our record
41+
if ($this->collaboratorStatus === 'active' && ! $user->mobile_repo_access_granted_at) {
42+
$user->update(['mobile_repo_access_granted_at' => now()]);
43+
}
44+
}
45+
46+
public function refreshStatus(): void
47+
{
48+
$user = auth()->user();
49+
50+
if ($user) {
51+
Cache::forget("github_collab_status_{$user->id}");
52+
}
53+
54+
$this->checkCollaboratorStatus();
55+
}
56+
57+
public function render()
58+
{
59+
return view('livewire.git-hub-access-banner');
60+
}
61+
}

resources/views/customer/licenses/index.blade.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
2828
<x-discounts-banner :inline="true" />
2929
<livewire:wall-of-love-banner :inline="true" />
30-
<x-github-access-banner :inline="true" />
30+
<livewire:git-hub-access-banner :inline="true" />
3131
</div>
3232
</div>
3333

resources/views/components/github-access-banner.blade.php renamed to resources/views/livewire/git-hub-access-banner.blade.php

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
@props(['inline' => false])
2-
1+
<div>
32
@if(auth()->user()->hasActiveMaxLicense())
43
<div @class(['max-w-7xl mx-auto px-4 sm:px-6 lg:px-8' => !$inline])>
54
<div class="bg-gradient-to-r from-gray-50 to-slate-100 dark:from-gray-800 dark:to-slate-900 border border-gray-300 dark:border-gray-600 rounded-lg p-6 h-full">
@@ -11,18 +10,26 @@
1110
</div>
1211
<div class="ml-4 flex-1">
1312
<h3 class="font-medium text-gray-900 dark:text-white">
14-
GitHub Repository Access
13+
<a href="https://github.com/nativephp/mobile" target="_blank" rel="noopener noreferrer" class="hover:underline"><code>nativephp/mobile</code></a> Repo Access
1514
</h3>
1615
<div class="mt-1 text-sm text-gray-600 dark:text-gray-400">
1716
@if(auth()->user()->github_username)
1817
<p>Connected as <span class="font-mono font-medium text-gray-900 dark:text-white">{{ '@' . auth()->user()->github_username }}</span></p>
19-
@if(auth()->user()->mobile_repo_access_granted_at)
18+
19+
@if($collaboratorStatus === 'active')
2020
<p class="mt-1">
2121
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200">
22-
Invitation Sent
22+
Access Granted
2323
</span>
2424
</p>
25-
<p class="mt-1 text-xs">Check your GitHub notifications to accept.</p>
25+
<p class="mt-1 text-xs">You have access to the nativephp/mobile repository.</p>
26+
@elseif($collaboratorStatus === 'pending')
27+
<p class="mt-1">
28+
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200">
29+
Invitation Pending
30+
</span>
31+
</p>
32+
<p class="mt-1 text-xs">Check your GitHub notifications to accept the invitation.</p>
2633
@else
2734
<p>Request access to the nativephp/mobile repository.</p>
2835
@endif
@@ -32,7 +39,16 @@
3239
</div>
3340
<div class="mt-4 flex flex-wrap gap-2">
3441
@if(auth()->user()->github_username)
35-
@if(!auth()->user()->mobile_repo_access_granted_at)
42+
@if($collaboratorStatus === 'active')
43+
<a href="https://github.com/nativephp/mobile" target="_blank" rel="noopener noreferrer" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
44+
View Repo
45+
</a>
46+
@elseif($collaboratorStatus === 'pending')
47+
<button wire:click="refreshStatus" type="button" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
48+
<span wire:loading.remove wire:target="refreshStatus">Check Status</span>
49+
<span wire:loading wire:target="refreshStatus">Checking...</span>
50+
</button>
51+
@else
3652
<form action="{{ route('github.request-access') }}" method="POST">
3753
@csrf
3854
<button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
@@ -58,3 +74,4 @@
5874
</div>
5975
</div>
6076
@endif
77+
</div>

tests/Feature/GitHubIntegrationTest.php

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use App\Models\License;
77
use App\Models\User;
88
use Illuminate\Foundation\Testing\RefreshDatabase;
9+
use Illuminate\Support\Facades\Cache;
910
use Illuminate\Support\Facades\Http;
1011
use Laravel\Pennant\Feature;
1112
use Tests\TestCase;
@@ -19,10 +20,15 @@ protected function setUp(): void
1920
parent::setUp();
2021

2122
Feature::define(ShowAuthButtons::class, true);
23+
24+
// Clear cache between tests to avoid stale collaborator status
25+
Cache::flush();
2226
}
2327

2428
public function test_user_with_active_max_license_sees_github_integration_card(): void
2529
{
30+
Http::fake(['api.github.com/*' => Http::response([], 404)]);
31+
2632
$user = User::factory()->create();
2733
License::factory()->create([
2834
'user_id' => $user->id,
@@ -34,12 +40,15 @@ public function test_user_with_active_max_license_sees_github_integration_card()
3440
$response = $this->actingAs($user)->get('/customer/licenses');
3541

3642
$response->assertStatus(200);
37-
$response->assertSee('GitHub Repository Access');
43+
$response->assertSee('nativephp/mobile');
44+
$response->assertSee('Repo Access');
3845
$response->assertSee('Connect GitHub');
3946
}
4047

4148
public function test_user_without_max_license_does_not_see_github_integration_card(): void
4249
{
50+
Http::fake(['api.github.com/*' => Http::response([], 404)]);
51+
4352
$user = User::factory()->create();
4453
License::factory()->create([
4554
'user_id' => $user->id,
@@ -51,11 +60,13 @@ public function test_user_without_max_license_does_not_see_github_integration_ca
5160
$response = $this->actingAs($user)->get('/customer/licenses');
5261

5362
$response->assertStatus(200);
54-
$response->assertDontSee('GitHub Repository Access');
63+
$response->assertDontSee('Repo Access');
5564
}
5665

5766
public function test_user_with_connected_github_sees_username(): void
5867
{
68+
Http::fake(['api.github.com/*' => Http::response([], 404)]);
69+
5970
$user = User::factory()->create([
6071
'github_username' => 'testuser',
6172
'github_id' => '123456',
@@ -78,7 +89,7 @@ public function test_user_with_connected_github_sees_username(): void
7889
public function test_user_can_request_repo_access_with_active_max_license(): void
7990
{
8091
Http::fake([
81-
'api.github.com/repos/nativephp/mobile/collaborators/*' => Http::response([], 201),
92+
'api.github.com/repos/nativephp/mobile/collaborators/testuser' => Http::response([], 201),
8293
]);
8394

8495
$user = User::factory()->create([
@@ -146,7 +157,7 @@ public function test_user_cannot_request_repo_access_without_connected_github():
146157
public function test_user_can_disconnect_github_account(): void
147158
{
148159
Http::fake([
149-
'api.github.com/repos/nativephp/mobile/collaborators/*' => Http::response([], 204),
160+
'api.github.com/repos/nativephp/mobile/collaborators/testuser' => Http::response([], 204),
150161
]);
151162

152163
$user = User::factory()->create([
@@ -178,7 +189,7 @@ public function test_user_can_disconnect_github_account(): void
178189
public function test_scheduled_command_removes_access_for_expired_max_license(): void
179190
{
180191
Http::fake([
181-
'api.github.com/repos/nativephp/mobile/collaborators/*' => Http::response([], 204),
192+
'api.github.com/repos/nativephp/mobile/collaborators/testuser' => Http::response([], 204),
182193
]);
183194

184195
$user = User::factory()->create([
@@ -226,12 +237,15 @@ public function test_scheduled_command_does_not_remove_access_for_active_max_lic
226237
]);
227238
}
228239

229-
public function test_user_with_granted_access_sees_access_status(): void
240+
public function test_user_with_active_collaborator_status_sees_access_granted(): void
230241
{
242+
Http::fake([
243+
'api.github.com/repos/nativephp/mobile/collaborators/testuser' => Http::response([], 204),
244+
]);
245+
231246
$user = User::factory()->create([
232247
'github_username' => 'testuser',
233248
'github_id' => '123456',
234-
'mobile_repo_access_granted_at' => now()->subHours(2),
235249
]);
236250
License::factory()->create([
237251
'user_id' => $user->id,
@@ -243,8 +257,37 @@ public function test_user_with_granted_access_sees_access_status(): void
243257
$response = $this->actingAs($user)->get('/customer/licenses');
244258

245259
$response->assertStatus(200);
246-
$response->assertSee('Invitation Sent');
260+
$response->assertSee('Access Granted');
247261
$response->assertSee('@testuser');
262+
$response->assertSee('View Repo');
248263
$response->assertDontSee('Request Access');
249264
}
265+
266+
public function test_user_with_pending_invitation_sees_pending_status(): void
267+
{
268+
Http::fake([
269+
'api.github.com/repos/nativephp/mobile/collaborators/testuser' => Http::response([], 404),
270+
'api.github.com/repos/nativephp/mobile/invitations' => Http::response([
271+
['invitee' => ['login' => 'testuser']],
272+
], 200),
273+
]);
274+
275+
$user = User::factory()->create([
276+
'github_username' => 'testuser',
277+
'github_id' => '123456',
278+
]);
279+
License::factory()->create([
280+
'user_id' => $user->id,
281+
'policy_name' => 'max',
282+
'expires_at' => now()->addDays(30),
283+
'is_suspended' => false,
284+
]);
285+
286+
$response = $this->actingAs($user)->get('/customer/licenses');
287+
288+
$response->assertStatus(200);
289+
$response->assertSee('Invitation Pending');
290+
$response->assertSee('@testuser');
291+
$response->assertSee('Check Status');
292+
}
250293
}

0 commit comments

Comments
 (0)