Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('user_notification_preferences', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->string('navigation_type');
$table->timestamps();

$table->unique(['user_id', 'navigation_type']);
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('user_notification_preferences');
}
};
14 changes: 14 additions & 0 deletions src/Concerns/Relations/HasNotificationPreferences.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Backstage\Users\Concerns\Relations;

use Backstage\Users\Models\UserNotificationPreference;
use Illuminate\Database\Eloquent\Relations\HasMany;

trait HasNotificationPreferences
{
public function notificationPreferences(): HasMany
{
return $this->hasMany(UserNotificationPreference::class, 'user_id');
}
}
1 change: 1 addition & 0 deletions src/Concerns/Relations/HasRelations.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ trait HasRelations
use HasLoginsRelation;
use HasTags;
use HasTraffic;
use HasNotificationPreferences;
}
19 changes: 19 additions & 0 deletions src/Enums/NotificationType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace Backstage\Users\Enums;

enum NotificationType: string
{
case Email = 'email';
case SMS = 'sms';
case InApp = 'in_app';

public function label(): string
{
return match ($this) {
self::Email => __('Email'),
self::SMS => __('SMS'),
self::InApp => __('In App'),
};
}
}
24 changes: 24 additions & 0 deletions src/Models/UserNotificationPreference.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Backstage\Users\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Backstage\Users\Enums\NotificationType;

class UserNotificationPreference extends Model
{
protected $fillable = [
'user_id',
'navigation_type',
];

protected $casts = [
'navigation_type' => NotificationType::class,
];

public function user(): BelongsTo
{
return $this->belongsTo(config('backstage.users.eloquent.users.model', User::class), 'user_id');
}
}
82 changes: 82 additions & 0 deletions src/Pages/Auth/Profile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

namespace Backstage\Users\Pages\Auth;

use Mockery\Matcher\Not;
use Filament\Pages\Auth\EditProfile;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Backstage\Users\Enums\NotificationType;
use Backstage\Users\Models\User;
use Backstage\Users\Models\UserNotificationPreference;
use Filament\Facades\Filament;

class Profile extends EditProfile
{
protected function getForms(): array
{
return [
'form' => $this->form(
$this->makeForm()
->schema([
$this->getNameFormComponent(),
$this->getEmailFormComponent(),
static::getNotificationFormComponent(),
$this->getPasswordFormComponent(),
$this->getPasswordConfirmationFormComponent(),
])
->operation('edit')
->model($this->getUser())
->statePath('data')
->inlineLabel(! static::isSimple()),
),
];
}

public static function getNotificationFormComponent(): Select
{
$types = NotificationType::cases();

$options = [];

foreach ($types as $type) {
$options[$type->value] = $type->label();
}

return Select::make('notification_preferences')
->label(__('Notification preferences'))
->options(fn() => $options)
->live()
->placeholder(fn() => ('Select notification preferences'))
->searchingMessage(__('Searching notification types...'))
->searchPrompt(__('Search notification types...'))
->saveRelationshipsUsing(function (User $record, array $state) {
$state = collect($state)->map(fn($value) => NotificationType::from($value));

$state->each(function (NotificationType $type) use ($record) {
if (!$record->notificationPreferences->contains('navigation_type', $type->value)) {

$record->notificationPreferences()->create([
'navigation_type' => $type->value,
]);
}
});

$record->notificationPreferences()->whereNotIn('navigation_type', $state)->delete();
})
->multiple();
}

protected function mutateFormDataBeforeFill(array $data): array
{
$data = parent::mutateFormDataBeforeFill($data);

$user = $this->getUser();

if ($user->notificationPreferences->isNotEmpty()) {
$data['notification_preferences'] = $user->notificationPreferences->pluck('navigation_type')->map(fn(NotificationType $record) => $record->value)->toArray();
}

return $data;
}
}
36 changes: 34 additions & 2 deletions src/UsersPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,23 @@
use Backstage\Users\Components\ToggleSubNavigationType;
use Backstage\Users\Http\Middleware\DetectUserTraffic;
use Backstage\Users\Http\Middleware\RedirectUnverifiedUsers;
use Backstage\Users\Pages\Auth\Profile;
use Closure;
use Filament\Contracts\Plugin;
use Filament\Navigation\MenuItem;
use Filament\Panel;
use Filament\Support\Concerns\EvaluatesClosures;
use Filament\View\PanelsRenderHook;
use Livewire\Livewire;

class UsersPlugin implements Plugin
{
use EvaluatesClosures;

public bool | Closure $profile = true;

public bool | Closure $isSimple = true;

public function getId(): string
{
return 'users';
Expand Down Expand Up @@ -63,10 +72,14 @@ public function register(Panel $panel): void
$panel->userMenuItems([
MenuItem::make('api_tokens')
->label(__('API Tokens'))
->visible(fn () => config('backstage.users.pages.manage-api-tokens', Pages\ManageApiTokens::class)::canAccess())
->visible(fn() => config('backstage.users.pages.manage-api-tokens', Pages\ManageApiTokens::class)::canAccess())
->icon('heroicon-o-document-text')
->url(fn () => config('backstage.users.pages.manage-api-tokens', Pages\ManageApiTokens::class)::getUrl()),
->url(fn() => config('backstage.users.pages.manage-api-tokens', Pages\ManageApiTokens::class)::getUrl()),
]);

if ($this->isProfileEnabled()) {
$panel->profile(page: Profile::class, isSimple: $this->isSimpleProfile());
}
}

public function boot(Panel $panel): void
Expand All @@ -93,4 +106,23 @@ protected function initSubNavigationToggle(Panel $panel)
return Livewire::mount(ToggleSubNavigationType::class, []);
});
}

public function profile(bool | Closure $uses = true, bool | Closure $isSimple = true): self
{
$this->profile = $uses;

$this->isSimple = $isSimple;

return $this;
}

public function isProfileEnabled(): bool
{
return $this->evaluate($this->profile);
}

public function isSimpleProfile(): bool
{
return $this->evaluate($this->isSimple);
}
}
1 change: 1 addition & 0 deletions src/UsersServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ protected function getMigrations(): array
'create_users_tags_table',
'user_password_nullable',
'add_sub_navigation_preference_to_users_table',
'create_user_notification_preferences_table'
];
}
}
Loading