feat: usr roles and soft deletes and filament
This commit is contained in:
parent
417a1bc53c
commit
ad17c55564
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Enums\UserRole;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class ChangeUserRole extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'user:change-role
|
||||||
|
{userId : The ID of the user}
|
||||||
|
{role : The new role: user, admin, or moderator}';
|
||||||
|
|
||||||
|
protected $description = 'Change a user role by user ID.';
|
||||||
|
|
||||||
|
public function handle(): int
|
||||||
|
{
|
||||||
|
$role = UserRole::tryFrom((string) $this->argument('role'));
|
||||||
|
|
||||||
|
if ($role === null) {
|
||||||
|
$availableRoles = collect(UserRole::cases())
|
||||||
|
->map(fn (UserRole $role): string => $role->value)
|
||||||
|
->implode(', ');
|
||||||
|
|
||||||
|
$this->error("Invalid role. Available roles: {$availableRoles}.");
|
||||||
|
|
||||||
|
return self::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = User::query()->find((string) $this->argument('userId'));
|
||||||
|
|
||||||
|
if ($user === null) {
|
||||||
|
$this->error('User not found.');
|
||||||
|
|
||||||
|
return self::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$user->forceFill([
|
||||||
|
'role' => $role,
|
||||||
|
])->save();
|
||||||
|
|
||||||
|
$this->info("User {$user->getKey()} role changed to {$role->value}.");
|
||||||
|
|
||||||
|
return self::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
use Filament\Support\Contracts\HasColor;
|
||||||
|
use Filament\Support\Contracts\HasLabel;
|
||||||
|
|
||||||
|
enum UserRole: string implements HasColor, HasLabel
|
||||||
|
{
|
||||||
|
case USER = 'user';
|
||||||
|
case ADMIN = 'admin';
|
||||||
|
case MODERATOR = 'moderator';
|
||||||
|
|
||||||
|
public function getLabel(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::USER => __('enums.user_role.user'),
|
||||||
|
self::ADMIN => __('enums.user_role.admin'),
|
||||||
|
self::MODERATOR => __('enums.user_role.moderator'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getColor(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::USER => 'gray',
|
||||||
|
self::ADMIN => 'danger',
|
||||||
|
self::MODERATOR => 'warning',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ namespace App\Filament\Resources\Users\Schemas;
|
||||||
|
|
||||||
use App\Enums\PacePreference;
|
use App\Enums\PacePreference;
|
||||||
use App\Enums\PhysicalActivityLevel;
|
use App\Enums\PhysicalActivityLevel;
|
||||||
|
use App\Enums\UserRole;
|
||||||
use App\Enums\UserSex;
|
use App\Enums\UserSex;
|
||||||
use App\Enums\WeightGoal;
|
use App\Enums\WeightGoal;
|
||||||
use Filament\Forms\Components\DatePicker;
|
use Filament\Forms\Components\DatePicker;
|
||||||
|
|
@ -40,6 +41,11 @@ class UserForm
|
||||||
->options(self::localeOptions())
|
->options(self::localeOptions())
|
||||||
->default('en')
|
->default('en')
|
||||||
->required(),
|
->required(),
|
||||||
|
Select::make('role')
|
||||||
|
->label(__('admin.users.fields.role'))
|
||||||
|
->options(UserRole::class)
|
||||||
|
->default(UserRole::USER->value)
|
||||||
|
->required(),
|
||||||
FileUpload::make('avatar_url')
|
FileUpload::make('avatar_url')
|
||||||
->label(__('admin.users.fields.avatar'))
|
->label(__('admin.users.fields.avatar'))
|
||||||
->image()
|
->image()
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,9 @@ class UserInfolist
|
||||||
TextEntry::make('email')
|
TextEntry::make('email')
|
||||||
->label(__('admin.users.fields.email'))
|
->label(__('admin.users.fields.email'))
|
||||||
->copyable(),
|
->copyable(),
|
||||||
|
TextEntry::make('role')
|
||||||
|
->label(__('admin.users.fields.role'))
|
||||||
|
->badge(),
|
||||||
TextEntry::make('email_verified_at')
|
TextEntry::make('email_verified_at')
|
||||||
->label(__('admin.users.fields.email_verified_at'))
|
->label(__('admin.users.fields.email_verified_at'))
|
||||||
->dateTime()
|
->dateTime()
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ namespace App\Filament\Resources\Users\Tables;
|
||||||
|
|
||||||
use App\Enums\PacePreference;
|
use App\Enums\PacePreference;
|
||||||
use App\Enums\PhysicalActivityLevel;
|
use App\Enums\PhysicalActivityLevel;
|
||||||
|
use App\Enums\UserRole;
|
||||||
use App\Enums\UserSex;
|
use App\Enums\UserSex;
|
||||||
use App\Enums\WeightGoal;
|
use App\Enums\WeightGoal;
|
||||||
use Filament\Actions\BulkActionGroup;
|
use Filament\Actions\BulkActionGroup;
|
||||||
|
|
@ -33,6 +34,10 @@ class UsersTable
|
||||||
TextColumn::make('email')
|
TextColumn::make('email')
|
||||||
->label(__('admin.users.fields.email'))
|
->label(__('admin.users.fields.email'))
|
||||||
->searchable(),
|
->searchable(),
|
||||||
|
TextColumn::make('role')
|
||||||
|
->label(__('admin.users.fields.role'))
|
||||||
|
->badge()
|
||||||
|
->sortable(),
|
||||||
TextColumn::make('email_verified_at')
|
TextColumn::make('email_verified_at')
|
||||||
->label(__('admin.users.fields.email_verified_at'))
|
->label(__('admin.users.fields.email_verified_at'))
|
||||||
->dateTime()
|
->dateTime()
|
||||||
|
|
@ -118,6 +123,9 @@ class UsersTable
|
||||||
->toggleable(isToggledHiddenByDefault: true),
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
])
|
])
|
||||||
->filters([
|
->filters([
|
||||||
|
SelectFilter::make('role')
|
||||||
|
->label(__('admin.users.fields.role'))
|
||||||
|
->options(UserRole::class),
|
||||||
SelectFilter::make('sex')
|
SelectFilter::make('sex')
|
||||||
->label(__('admin.users.fields.sex'))
|
->label(__('admin.users.fields.sex'))
|
||||||
->options(UserSex::class),
|
->options(UserSex::class),
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ class UserResource extends JsonResource
|
||||||
public function toArray(Request $request): array
|
public function toArray(Request $request): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
'id' => $this->id, // à voir si suppression par la suite
|
||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
'email' => $this->email,
|
'email' => $this->email,
|
||||||
'locale' => $this->locale,
|
'locale' => $this->locale,
|
||||||
|
|
@ -24,6 +25,7 @@ class UserResource extends JsonResource
|
||||||
'height' => $this->height,
|
'height' => $this->height,
|
||||||
'avatarUrl' => $this->avatar_url ? asset(Storage::url($this->avatar_url)) : null,
|
'avatarUrl' => $this->avatar_url ? asset(Storage::url($this->avatar_url)) : null,
|
||||||
'bio' => $this->bio,
|
'bio' => $this->bio,
|
||||||
|
'role' => $this->role,
|
||||||
'accountVerified' => $this->account_verified_at !== null,
|
'accountVerified' => $this->account_verified_at !== null,
|
||||||
'physicalActivityLevel' => $this->physical_activity_level,
|
'physicalActivityLevel' => $this->physical_activity_level,
|
||||||
'physicalActivityLevelLabel' => $this->physical_activity_level?->getLabel(),
|
'physicalActivityLevelLabel' => $this->physical_activity_level?->getLabel(),
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ namespace App\Models;
|
||||||
|
|
||||||
use App\Enums\PacePreference;
|
use App\Enums\PacePreference;
|
||||||
use App\Enums\PhysicalActivityLevel;
|
use App\Enums\PhysicalActivityLevel;
|
||||||
|
use App\Enums\UserRole;
|
||||||
use App\Enums\UserSex;
|
use App\Enums\UserSex;
|
||||||
use App\Enums\WeightGoal;
|
use App\Enums\WeightGoal;
|
||||||
use Filament\Models\Contracts\FilamentUser;
|
use Filament\Models\Contracts\FilamentUser;
|
||||||
|
|
@ -12,9 +13,9 @@ use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
use Illuminate\Contracts\Translation\HasLocalePreference;
|
use Illuminate\Contracts\Translation\HasLocalePreference;
|
||||||
use Illuminate\Database\Eloquent\Concerns\HasUlids;
|
use Illuminate\Database\Eloquent\Concerns\HasUlids;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
use Laravel\Sanctum\HasApiTokens;
|
use Laravel\Sanctum\HasApiTokens;
|
||||||
|
|
@ -22,7 +23,7 @@ use Laravel\Sanctum\HasApiTokens;
|
||||||
class User extends Authenticatable implements FilamentUser, HasLocalePreference, MustVerifyEmail
|
class User extends Authenticatable implements FilamentUser, HasLocalePreference, MustVerifyEmail
|
||||||
{
|
{
|
||||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||||
use HasApiTokens, HasFactory, HasUlids, Notifiable;
|
use HasApiTokens, HasFactory, HasUlids, Notifiable, SoftDeletes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attributes that are mass assignable.
|
* The attributes that are mass assignable.
|
||||||
|
|
@ -33,6 +34,7 @@ class User extends Authenticatable implements FilamentUser, HasLocalePreference,
|
||||||
'name',
|
'name',
|
||||||
'email',
|
'email',
|
||||||
'locale',
|
'locale',
|
||||||
|
'role',
|
||||||
'email_verified_at',
|
'email_verified_at',
|
||||||
'password',
|
'password',
|
||||||
'avatar_url',
|
'avatar_url',
|
||||||
|
|
@ -79,6 +81,7 @@ class User extends Authenticatable implements FilamentUser, HasLocalePreference,
|
||||||
'weight_goal' => WeightGoal::class,
|
'weight_goal' => WeightGoal::class,
|
||||||
'pace_preference' => PacePreference::class,
|
'pace_preference' => PacePreference::class,
|
||||||
'sex' => UserSex::class,
|
'sex' => UserSex::class,
|
||||||
|
'role' => UserRole::class,
|
||||||
'date_of_birth' => 'immutable_date',
|
'date_of_birth' => 'immutable_date',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
@ -103,12 +106,6 @@ class User extends Authenticatable implements FilamentUser, HasLocalePreference,
|
||||||
return $this->hasOne(StravaConnection::class);
|
return $this->hasOne(StravaConnection::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function likedMealPosts(): BelongsToMany
|
|
||||||
{
|
|
||||||
return $this->belongsToMany(MealPosts::class, 'meal_post_likes', 'user_id', 'meal_posts_id')
|
|
||||||
->withTimestamps();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function routeNotificationForExpoPush(): array
|
public function routeNotificationForExpoPush(): array
|
||||||
{
|
{
|
||||||
return $this->deviceTokens()
|
return $this->deviceTokens()
|
||||||
|
|
@ -122,15 +119,13 @@ class User extends Authenticatable implements FilamentUser, HasLocalePreference,
|
||||||
return $this->locale ?: 'en';
|
return $this->locale ?: 'en';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isAdmin()
|
public function isAdmin(): bool
|
||||||
{
|
{
|
||||||
return true;
|
return $this->role === UserRole::ADMIN;
|
||||||
// return $this->role === 'admin';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canAccessPanel(Panel $panel): bool
|
public function canAccessPanel(Panel $panel): bool
|
||||||
{
|
{
|
||||||
|
return $this->role === UserRole::ADMIN || $this->role === UserRole::MODERATOR;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ env:
|
||||||
APP_LOCALE: fr
|
APP_LOCALE: fr
|
||||||
APP_ENV: production
|
APP_ENV: production
|
||||||
APP_DEBUG: "false"
|
APP_DEBUG: "false"
|
||||||
|
APP_NAME: "Bowly"
|
||||||
APP_URL: https://bemeal.leonmorival.com
|
APP_URL: https://bemeal.leonmorival.com
|
||||||
SERVER_NAME: ":80"
|
SERVER_NAME: ":80"
|
||||||
APP_RUNTIME: "Laravel\\FrankenPHP\\Runtime"
|
APP_RUNTIME: "Laravel\\FrankenPHP\\Runtime"
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Database\Factories;
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Enums\UserRole;
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
@ -27,6 +28,7 @@ class UserFactory extends Factory
|
||||||
'name' => fake()->name(),
|
'name' => fake()->name(),
|
||||||
'email' => fake()->unique()->safeEmail(),
|
'email' => fake()->unique()->safeEmail(),
|
||||||
'locale' => 'en',
|
'locale' => 'en',
|
||||||
|
'role' => UserRole::USER,
|
||||||
'email_verified_at' => now(),
|
'email_verified_at' => now(),
|
||||||
'password' => static::$password ??= Hash::make('password'),
|
'password' => static::$password ??= Hash::make('password'),
|
||||||
'avatar_url' => null,
|
'avatar_url' => null,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?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::table('users', function (Blueprint $table) {
|
||||||
|
$table->string('role')->default('user');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('role');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?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::table('users', function (Blueprint $table) {
|
||||||
|
$table->softDeletesTz();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->dropSoftDeletesTz();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -19,6 +19,7 @@ return [
|
||||||
'name' => 'Name',
|
'name' => 'Name',
|
||||||
'email' => 'Email address',
|
'email' => 'Email address',
|
||||||
'locale' => 'Language',
|
'locale' => 'Language',
|
||||||
|
'role' => 'Role',
|
||||||
'email_verified_at' => 'Email verified at',
|
'email_verified_at' => 'Email verified at',
|
||||||
'account_verified_at' => 'Account verified at',
|
'account_verified_at' => 'Account verified at',
|
||||||
'avatar' => 'Avatar',
|
'avatar' => 'Avatar',
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,11 @@ return [
|
||||||
'other' => 'Other',
|
'other' => 'Other',
|
||||||
'unknown' => 'Not specified',
|
'unknown' => 'Not specified',
|
||||||
],
|
],
|
||||||
|
'user_role' => [
|
||||||
|
'user' => 'User',
|
||||||
|
'admin' => 'Administrator',
|
||||||
|
'moderator' => 'Moderator',
|
||||||
|
],
|
||||||
'weight_goal' => [
|
'weight_goal' => [
|
||||||
'lose_weight' => 'Lose weight',
|
'lose_weight' => 'Lose weight',
|
||||||
'maintain_weight' => 'Maintain weight',
|
'maintain_weight' => 'Maintain weight',
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ return [
|
||||||
'name' => 'Nom',
|
'name' => 'Nom',
|
||||||
'email' => 'Adresse email',
|
'email' => 'Adresse email',
|
||||||
'locale' => 'Langue',
|
'locale' => 'Langue',
|
||||||
|
'role' => 'Rôle',
|
||||||
'email_verified_at' => 'Email vérifié le',
|
'email_verified_at' => 'Email vérifié le',
|
||||||
'account_verified_at' => 'Compte vérifié le',
|
'account_verified_at' => 'Compte vérifié le',
|
||||||
'avatar' => 'Avatar',
|
'avatar' => 'Avatar',
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,11 @@ return [
|
||||||
'other' => 'Autre',
|
'other' => 'Autre',
|
||||||
'unknown' => 'Non renseigné',
|
'unknown' => 'Non renseigné',
|
||||||
],
|
],
|
||||||
|
'user_role' => [
|
||||||
|
'user' => 'Utilisateur',
|
||||||
|
'admin' => 'Administrateur',
|
||||||
|
'moderator' => 'Modérateur',
|
||||||
|
],
|
||||||
'weight_goal' => [
|
'weight_goal' => [
|
||||||
'lose_weight' => 'Perdre du poids',
|
'lose_weight' => 'Perdre du poids',
|
||||||
'maintain_weight' => 'Maintenir le poids',
|
'maintain_weight' => 'Maintenir le poids',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Enums\UserRole;
|
||||||
|
use App\Filament\Resources\Users\Pages\EditUser;
|
||||||
|
use App\Filament\Resources\Users\Pages\ListUsers;
|
||||||
|
use App\Filament\Resources\Users\Pages\ViewUser;
|
||||||
|
use App\Models\User;
|
||||||
|
use Filament\Facades\Filament;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
|
use Filament\Infolists\Components\TextEntry;
|
||||||
|
use Filament\Tables\Columns\TextColumn;
|
||||||
|
use Filament\Tables\Filters\SelectFilter;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
|
||||||
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
app()->setLocale('fr');
|
||||||
|
|
||||||
|
Filament::setCurrentPanel(Filament::getPanel('dashboard'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('exposes translated user roles in filament user screens', function () {
|
||||||
|
$admin = User::factory()->create([
|
||||||
|
'role' => UserRole::ADMIN,
|
||||||
|
]);
|
||||||
|
$user = User::factory()->create([
|
||||||
|
'role' => UserRole::MODERATOR,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->actingAs($admin);
|
||||||
|
|
||||||
|
$expectedOptions = [
|
||||||
|
'user' => 'Utilisateur',
|
||||||
|
'admin' => 'Administrateur',
|
||||||
|
'moderator' => 'Modérateur',
|
||||||
|
];
|
||||||
|
|
||||||
|
Livewire::test(EditUser::class, ['record' => $user->getKey()])
|
||||||
|
->assertFormFieldExists(
|
||||||
|
'role',
|
||||||
|
null,
|
||||||
|
fn (Select $field): bool => $field->getLabel() === 'Rôle'
|
||||||
|
&& $field->getOptions() === $expectedOptions,
|
||||||
|
);
|
||||||
|
|
||||||
|
Livewire::test(ViewUser::class, ['record' => $user->getKey()])
|
||||||
|
->assertSchemaComponentExists(
|
||||||
|
'role',
|
||||||
|
'infolist',
|
||||||
|
fn (TextEntry $entry): bool => $entry->getLabel() === 'Rôle',
|
||||||
|
);
|
||||||
|
|
||||||
|
Livewire::test(ListUsers::class)
|
||||||
|
->assertTableColumnExists(
|
||||||
|
'role',
|
||||||
|
fn (TextColumn $column): bool => $column->getLabel() === 'Rôle',
|
||||||
|
)
|
||||||
|
->assertTableFilterExists(
|
||||||
|
'role',
|
||||||
|
fn (SelectFilter $filter): bool => $filter->getLabel() === 'Rôle'
|
||||||
|
&& $filter->getOptions() === $expectedOptions,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('changes a user role by id with the artisan command', function () {
|
||||||
|
$user = User::factory()->create([
|
||||||
|
'role' => UserRole::USER,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->artisan('user:change-role', [
|
||||||
|
'userId' => $user->getKey(),
|
||||||
|
'role' => UserRole::ADMIN->value,
|
||||||
|
])
|
||||||
|
->expectsOutput("User {$user->getKey()} role changed to admin.")
|
||||||
|
->assertExitCode(0);
|
||||||
|
|
||||||
|
expect($user->fresh()->role)->toBe(UserRole::ADMIN);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects an invalid user role', function () {
|
||||||
|
$user = User::factory()->create([
|
||||||
|
'role' => UserRole::USER,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->artisan('user:change-role', [
|
||||||
|
'userId' => $user->getKey(),
|
||||||
|
'role' => 'owner',
|
||||||
|
])
|
||||||
|
->expectsOutput('Invalid role. Available roles: user, admin, moderator.')
|
||||||
|
->assertExitCode(1);
|
||||||
|
|
||||||
|
expect($user->fresh()->role)->toBe(UserRole::USER);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects an unknown user id', function () {
|
||||||
|
$this->artisan('user:change-role', [
|
||||||
|
'userId' => '01J00000000000000000000000',
|
||||||
|
'role' => UserRole::ADMIN->value,
|
||||||
|
])
|
||||||
|
->expectsOutput('User not found.')
|
||||||
|
->assertExitCode(1);
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue