Skip to content

Commit 0dd5379

Browse files
simonhampclaude
andauthored
Link new plugin notification to specific plugin page (#314)
* Link new plugin notification email and database action to plugin page Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Link approved/rejected plugin notifications to plugin pages, stack buttons on mobile Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 02d34b0 commit 0dd5379

File tree

8 files changed

+167
-9
lines changed

8 files changed

+167
-9
lines changed

app/Notifications/NewPluginAvailable.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public function toMail(object $notifiable): MailMessage
3434
->subject("New Plugin: {$this->plugin->name}")
3535
->greeting('A new plugin is available!')
3636
->line("**{$this->plugin->name}** has just been added to the NativePHP Plugin Marketplace.")
37-
->action('View Plugin', url('/plugins'))
37+
->action('View Plugin', route('plugins.show', $this->plugin->routeParams()))
3838
->line('You can manage your notification preferences in your account settings.');
3939
}
4040

@@ -48,6 +48,8 @@ public function toArray(object $notifiable): array
4848
'body' => "{$this->plugin->name} has just been added to the NativePHP Plugin Marketplace.",
4949
'plugin_id' => $this->plugin->id,
5050
'plugin_name' => $this->plugin->name,
51+
'action_url' => route('plugins.show', $this->plugin->routeParams()),
52+
'action_label' => 'View Plugin',
5153
];
5254
}
5355
}

app/Notifications/PluginApproved.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public function toMail(object $notifiable): MailMessage
3535
->subject('Your Plugin Has Been Approved!')
3636
->greeting('Great news!')
3737
->line("Your plugin **{$this->plugin->name}** has been approved and is now listed in the NativePHP Plugin Marketplace.")
38-
->action('View Plugin Marketplace', url('/plugins'))
38+
->action('View Plugin', route('plugins.show', $this->plugin->routeParams()))
3939
->line('Thank you for contributing to the NativePHP ecosystem!');
4040
}
4141

@@ -51,6 +51,8 @@ public function toArray(object $notifiable): array
5151
'body' => "{$this->plugin->name} is now listed in the NativePHP Plugin Marketplace.",
5252
'plugin_id' => $this->plugin->id,
5353
'plugin_name' => $this->plugin->name,
54+
'action_url' => route('plugins.show', $this->plugin->routeParams()),
55+
'action_label' => 'View Plugin',
5456
];
5557
}
5658
}

app/Notifications/PluginRejected.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public function toMail(object $notifiable): MailMessage
3737
->line("Unfortunately, your plugin **{$this->plugin->name}** was not approved for the NativePHP Plugin Marketplace.")
3838
->line('**Reason:**')
3939
->line($this->plugin->rejection_reason)
40-
->action('View Your Plugins', route('customer.plugins.index'))
40+
->action('View Plugin', route('customer.plugins.show', $this->plugin->routeParams()))
4141
->line('If you have questions about this decision, please reach out to us.');
4242
}
4343

@@ -54,6 +54,8 @@ public function toArray(object $notifiable): array
5454
'plugin_id' => $this->plugin->id,
5555
'plugin_name' => $this->plugin->name,
5656
'rejection_reason' => $this->plugin->rejection_reason,
57+
'action_url' => route('customer.plugins.show', $this->plugin->routeParams()),
58+
'action_label' => 'View Plugin',
5759
];
5860
}
5961
}

resources/views/livewire/customer/notifications.blade.php

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<div class="mx-auto max-w-2xl">
2-
<div class="mb-6 flex items-center justify-between">
2+
<div class="mb-6 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
33
<div>
44
<flux:heading size="xl">Notifications</flux:heading>
55
<flux:text>Stay up to date with your account activity.</flux:text>
@@ -44,13 +44,19 @@
4444
<flux:text class="mt-1">{{ $notification->data['body'] }}</flux:text>
4545
@endif
4646

47-
@if (is_null($notification->read_at))
48-
<div class="mt-2">
47+
<div class="mt-2 flex items-center gap-2">
48+
@if (! empty($notification->data['action_url']))
49+
<flux:button href="{{ $notification->data['action_url'] }}" variant="primary" size="xs">
50+
{{ $notification->data['action_label'] ?? 'View' }}
51+
</flux:button>
52+
@endif
53+
54+
@if (is_null($notification->read_at))
4955
<flux:button wire:click="markAsRead('{{ $notification->id }}')" variant="ghost" size="xs">
5056
Mark as read
5157
</flux:button>
52-
</div>
53-
@endif
58+
@endif
59+
</div>
5460
</div>
5561
</div>
5662
</flux:card>

resources/views/livewire/team-manager.blade.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
{{-- Seat Management --}}
2626
<flux:card class="mb-6">
27-
<div class="flex items-center justify-between">
27+
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
2828
<div>
2929
<flux:heading size="lg">Seats</flux:heading>
3030
<flux:text class="mt-1">

tests/Feature/Notifications/NewPluginAvailableTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,17 @@ public function test_mail_contains_plugin_name(): void
8989
$this->assertStringContainsString('acme/awesome-plugin', $mail->subject);
9090
}
9191

92+
public function test_mail_action_links_to_plugin_page(): void
93+
{
94+
$user = User::factory()->create();
95+
$plugin = Plugin::factory()->for($user)->create(['name' => 'acme/awesome-plugin']);
96+
97+
$notification = new NewPluginAvailable($plugin);
98+
$mail = $notification->toMail($user);
99+
100+
$this->assertEquals(route('plugins.show', ['vendor' => 'acme', 'package' => 'awesome-plugin']), $mail->actionUrl);
101+
}
102+
92103
public function test_database_notification_contains_plugin_data(): void
93104
{
94105
$user = User::factory()->create();
@@ -100,6 +111,8 @@ public function test_database_notification_contains_plugin_data(): void
100111
$this->assertEquals($plugin->id, $data['plugin_id']);
101112
$this->assertEquals('acme/awesome-plugin', $data['plugin_name']);
102113
$this->assertStringContainsString('acme/awesome-plugin', $data['title']);
114+
$this->assertEquals(route('plugins.show', ['vendor' => 'acme', 'package' => 'awesome-plugin']), $data['action_url']);
115+
$this->assertEquals('View Plugin', $data['action_label']);
103116
}
104117

105118
public function test_new_users_receive_new_plugin_notifications_by_default(): void
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
namespace Tests\Feature\Notifications;
4+
5+
use App\Models\Plugin;
6+
use App\Models\User;
7+
use App\Notifications\PluginApproved;
8+
use Illuminate\Foundation\Testing\RefreshDatabase;
9+
use Tests\TestCase;
10+
11+
class PluginApprovedTest extends TestCase
12+
{
13+
use RefreshDatabase;
14+
15+
public function test_mail_action_links_to_plugin_page(): void
16+
{
17+
$user = User::factory()->create();
18+
$plugin = Plugin::factory()->for($user)->create(['name' => 'acme/awesome-plugin']);
19+
20+
$notification = new PluginApproved($plugin);
21+
$mail = $notification->toMail($user);
22+
23+
$this->assertEquals(route('plugins.show', ['vendor' => 'acme', 'package' => 'awesome-plugin']), $mail->actionUrl);
24+
}
25+
26+
public function test_database_notification_contains_action_url(): void
27+
{
28+
$user = User::factory()->create();
29+
$plugin = Plugin::factory()->for($user)->create(['name' => 'acme/awesome-plugin']);
30+
31+
$notification = new PluginApproved($plugin);
32+
$data = $notification->toArray($user);
33+
34+
$this->assertEquals(route('plugins.show', ['vendor' => 'acme', 'package' => 'awesome-plugin']), $data['action_url']);
35+
$this->assertEquals('View Plugin', $data['action_label']);
36+
}
37+
38+
public function test_mail_contains_plugin_name(): void
39+
{
40+
$user = User::factory()->create();
41+
$plugin = Plugin::factory()->for($user)->create(['name' => 'acme/awesome-plugin']);
42+
43+
$notification = new PluginApproved($plugin);
44+
$mail = $notification->toMail($user);
45+
46+
$this->assertStringContainsString('acme/awesome-plugin', $mail->render()->toHtml());
47+
}
48+
49+
public function test_via_returns_mail_and_database(): void
50+
{
51+
$user = User::factory()->create();
52+
$plugin = Plugin::factory()->for($user)->create();
53+
54+
$notification = new PluginApproved($plugin);
55+
56+
$this->assertEquals(['mail', 'database'], $notification->via($user));
57+
}
58+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
namespace Tests\Feature\Notifications;
4+
5+
use App\Models\Plugin;
6+
use App\Models\User;
7+
use App\Notifications\PluginRejected;
8+
use Illuminate\Foundation\Testing\RefreshDatabase;
9+
use Tests\TestCase;
10+
11+
class PluginRejectedTest extends TestCase
12+
{
13+
use RefreshDatabase;
14+
15+
public function test_mail_action_links_to_dashboard_plugin_page(): void
16+
{
17+
$user = User::factory()->create();
18+
$plugin = Plugin::factory()->for($user)->create(['name' => 'acme/awesome-plugin']);
19+
20+
$notification = new PluginRejected($plugin);
21+
$mail = $notification->toMail($user);
22+
23+
$this->assertEquals(route('customer.plugins.show', ['vendor' => 'acme', 'package' => 'awesome-plugin']), $mail->actionUrl);
24+
}
25+
26+
public function test_database_notification_contains_action_url(): void
27+
{
28+
$user = User::factory()->create();
29+
$plugin = Plugin::factory()->for($user)->create(['name' => 'acme/awesome-plugin']);
30+
31+
$notification = new PluginRejected($plugin);
32+
$data = $notification->toArray($user);
33+
34+
$this->assertEquals(route('customer.plugins.show', ['vendor' => 'acme', 'package' => 'awesome-plugin']), $data['action_url']);
35+
$this->assertEquals('View Plugin', $data['action_label']);
36+
}
37+
38+
public function test_mail_contains_rejection_reason(): void
39+
{
40+
$user = User::factory()->create();
41+
$plugin = Plugin::factory()->for($user)->create([
42+
'name' => 'acme/awesome-plugin',
43+
'rejection_reason' => 'Missing license file',
44+
]);
45+
46+
$notification = new PluginRejected($plugin);
47+
$rendered = $notification->toMail($user)->render()->toHtml();
48+
49+
$this->assertStringContainsString('Missing license file', $rendered);
50+
}
51+
52+
public function test_database_notification_contains_rejection_reason(): void
53+
{
54+
$user = User::factory()->create();
55+
$plugin = Plugin::factory()->for($user)->create([
56+
'name' => 'acme/awesome-plugin',
57+
'rejection_reason' => 'Missing license file',
58+
]);
59+
60+
$notification = new PluginRejected($plugin);
61+
$data = $notification->toArray($user);
62+
63+
$this->assertEquals('Missing license file', $data['rejection_reason']);
64+
}
65+
66+
public function test_via_returns_mail_and_database(): void
67+
{
68+
$user = User::factory()->create();
69+
$plugin = Plugin::factory()->for($user)->create();
70+
71+
$notification = new PluginRejected($plugin);
72+
73+
$this->assertEquals(['mail', 'database'], $notification->via($user));
74+
}
75+
}

0 commit comments

Comments
 (0)