Skip to content

Commit b1dd0b8

Browse files
Add user notification types
1 parent bbeb4d2 commit b1dd0b8

File tree

8 files changed

+206
-2
lines changed

8 files changed

+206
-2
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*/
12+
public function up(): void
13+
{
14+
Schema::create('user_notification_preferences', function (Blueprint $table) {
15+
$table->id();
16+
$table->unsignedBigInteger('user_id');
17+
$table->string('navigation_type');
18+
$table->timestamps();
19+
20+
$table->unique(['user_id', 'navigation_type']);
21+
});
22+
}
23+
24+
/**
25+
* Reverse the migrations.
26+
*/
27+
public function down(): void
28+
{
29+
Schema::dropIfExists('user_notification_preferences');
30+
}
31+
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Backstage\Users\Concerns\Relations;
4+
5+
use Backstage\Users\Models\UserNotificationPreference;
6+
use Illuminate\Database\Eloquent\Relations\HasMany;
7+
8+
trait HasNotificationPreferences
9+
{
10+
public function notificationPreferences(): HasMany
11+
{
12+
return $this->hasMany(UserNotificationPreference::class, 'user_id');
13+
}
14+
}

src/Concerns/Relations/HasRelations.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ trait HasRelations
77
use HasLoginsRelation;
88
use HasTags;
99
use HasTraffic;
10+
use HasNotificationPreferences;
1011
}

src/Enums/NotificationType.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Backstage\Users\Enums;
4+
5+
enum NotificationType: string
6+
{
7+
case Email = 'email';
8+
case SMS = 'sms';
9+
case InApp = 'in_app';
10+
11+
public function label(): string
12+
{
13+
return match ($this) {
14+
self::Email => __('Email'),
15+
self::SMS => __('SMS'),
16+
self::InApp => __('In App'),
17+
};
18+
}
19+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Backstage\Users\Models;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
7+
use Backstage\Users\Enums\NotificationType;
8+
9+
class UserNotificationPreference extends Model
10+
{
11+
protected $fillable = [
12+
'user_id',
13+
'navigation_type',
14+
];
15+
16+
protected $casts = [
17+
'navigation_type' => NotificationType::class,
18+
];
19+
20+
public function user(): BelongsTo
21+
{
22+
return $this->belongsTo(config('backstage.users.eloquent.users.model', User::class), 'user_id');
23+
}
24+
}

src/Pages/Auth/Profile.php

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
namespace Backstage\Users\Pages\Auth;
4+
5+
use Mockery\Matcher\Not;
6+
use Filament\Pages\Auth\EditProfile;
7+
use Filament\Forms\Components\Select;
8+
use Filament\Forms\Components\TextInput;
9+
use Backstage\Users\Enums\NotificationType;
10+
use Backstage\Users\Models\User;
11+
use Backstage\Users\Models\UserNotificationPreference;
12+
use Filament\Facades\Filament;
13+
14+
class Profile extends EditProfile
15+
{
16+
protected function getForms(): array
17+
{
18+
return [
19+
'form' => $this->form(
20+
$this->makeForm()
21+
->schema([
22+
$this->getNameFormComponent(),
23+
$this->getEmailFormComponent(),
24+
static::getNotificationFormComponent(),
25+
$this->getPasswordFormComponent(),
26+
$this->getPasswordConfirmationFormComponent(),
27+
])
28+
->operation('edit')
29+
->model($this->getUser())
30+
->statePath('data')
31+
->inlineLabel(! static::isSimple()),
32+
),
33+
];
34+
}
35+
36+
public static function getNotificationFormComponent(): Select
37+
{
38+
$types = NotificationType::cases();
39+
40+
$options = [];
41+
42+
foreach ($types as $type) {
43+
$options[$type->value] = $type->label();
44+
}
45+
46+
return Select::make('notification_preferences')
47+
->label(__('Notification preferences'))
48+
->options(fn() => $options)
49+
->live()
50+
->placeholder(fn() => ('Select notification preferences'))
51+
->searchingMessage(__('Searching notification types...'))
52+
->searchPrompt(__('Search notification types...'))
53+
->saveRelationshipsUsing(function (User $record, array $state) {
54+
$state = collect($state)->map(fn($value) => NotificationType::from($value));
55+
56+
$state->each(function (NotificationType $type) use ($record) {
57+
if (!$record->notificationPreferences->contains('navigation_type', $type->value)) {
58+
59+
$record->notificationPreferences()->create([
60+
'navigation_type' => $type->value,
61+
]);
62+
}
63+
});
64+
65+
$record->notificationPreferences()->whereNotIn('navigation_type', $state)->delete();
66+
})
67+
->multiple();
68+
}
69+
70+
protected function mutateFormDataBeforeFill(array $data): array
71+
{
72+
$data = parent::mutateFormDataBeforeFill($data);
73+
74+
$user = $this->getUser();
75+
76+
if ($user->notificationPreferences->isNotEmpty()) {
77+
$data['notification_preferences'] = $user->notificationPreferences->pluck('navigation_type')->map(fn(NotificationType $record) => $record->value)->toArray();
78+
}
79+
80+
return $data;
81+
}
82+
}

src/UsersPlugin.php

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,23 @@
55
use Backstage\Users\Components\ToggleSubNavigationType;
66
use Backstage\Users\Http\Middleware\DetectUserTraffic;
77
use Backstage\Users\Http\Middleware\RedirectUnverifiedUsers;
8+
use Backstage\Users\Pages\Auth\Profile;
9+
use Closure;
810
use Filament\Contracts\Plugin;
911
use Filament\Navigation\MenuItem;
1012
use Filament\Panel;
13+
use Filament\Support\Concerns\EvaluatesClosures;
1114
use Filament\View\PanelsRenderHook;
1215
use Livewire\Livewire;
1316

1417
class UsersPlugin implements Plugin
1518
{
19+
use EvaluatesClosures;
20+
21+
public bool | Closure $profile = true;
22+
23+
public bool | Closure $isSimple = true;
24+
1625
public function getId(): string
1726
{
1827
return 'users';
@@ -63,10 +72,14 @@ public function register(Panel $panel): void
6372
$panel->userMenuItems([
6473
MenuItem::make('api_tokens')
6574
->label(__('API Tokens'))
66-
->visible(fn () => config('backstage.users.pages.manage-api-tokens', Pages\ManageApiTokens::class)::canAccess())
75+
->visible(fn() => config('backstage.users.pages.manage-api-tokens', Pages\ManageApiTokens::class)::canAccess())
6776
->icon('heroicon-o-document-text')
68-
->url(fn () => config('backstage.users.pages.manage-api-tokens', Pages\ManageApiTokens::class)::getUrl()),
77+
->url(fn() => config('backstage.users.pages.manage-api-tokens', Pages\ManageApiTokens::class)::getUrl()),
6978
]);
79+
80+
if ($this->isProfileEnabled()) {
81+
$panel->profile(page: Profile::class, isSimple: $this->isSimpleProfile());
82+
}
7083
}
7184

7285
public function boot(Panel $panel): void
@@ -93,4 +106,23 @@ protected function initSubNavigationToggle(Panel $panel)
93106
return Livewire::mount(ToggleSubNavigationType::class, []);
94107
});
95108
}
109+
110+
public function profile(bool | Closure $uses = true, bool | Closure $isSimple = true): self
111+
{
112+
$this->profile = $uses;
113+
114+
$this->isSimple = $isSimple;
115+
116+
return $this;
117+
}
118+
119+
public function isProfileEnabled(): bool
120+
{
121+
return $this->evaluate($this->profile);
122+
}
123+
124+
public function isSimpleProfile(): bool
125+
{
126+
return $this->evaluate($this->isSimple);
127+
}
96128
}

src/UsersServiceProvider.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ protected function getMigrations(): array
214214
'create_users_tags_table',
215215
'user_password_nullable',
216216
'add_sub_navigation_preference_to_users_table',
217+
'create_user_notification_preferences_table'
217218
];
218219
}
219220
}

0 commit comments

Comments
 (0)