feat: usr roles and soft deletes and filament

This commit is contained in:
2026-05-22 10:06:47 +02:00
parent 417a1bc53c
commit ad17c55564
16 changed files with 280 additions and 13 deletions
+47
View File
@@ -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;
}
}
+31
View File
@@ -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\PhysicalActivityLevel;
use App\Enums\UserRole;
use App\Enums\UserSex;
use App\Enums\WeightGoal;
use Filament\Forms\Components\DatePicker;
@@ -40,6 +41,11 @@ class UserForm
->options(self::localeOptions())
->default('en')
->required(),
Select::make('role')
->label(__('admin.users.fields.role'))
->options(UserRole::class)
->default(UserRole::USER->value)
->required(),
FileUpload::make('avatar_url')
->label(__('admin.users.fields.avatar'))
->image()
@@ -24,6 +24,9 @@ class UserInfolist
TextEntry::make('email')
->label(__('admin.users.fields.email'))
->copyable(),
TextEntry::make('role')
->label(__('admin.users.fields.role'))
->badge(),
TextEntry::make('email_verified_at')
->label(__('admin.users.fields.email_verified_at'))
->dateTime()
@@ -4,6 +4,7 @@ namespace App\Filament\Resources\Users\Tables;
use App\Enums\PacePreference;
use App\Enums\PhysicalActivityLevel;
use App\Enums\UserRole;
use App\Enums\UserSex;
use App\Enums\WeightGoal;
use Filament\Actions\BulkActionGroup;
@@ -33,6 +34,10 @@ class UsersTable
TextColumn::make('email')
->label(__('admin.users.fields.email'))
->searchable(),
TextColumn::make('role')
->label(__('admin.users.fields.role'))
->badge()
->sortable(),
TextColumn::make('email_verified_at')
->label(__('admin.users.fields.email_verified_at'))
->dateTime()
@@ -118,6 +123,9 @@ class UsersTable
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
SelectFilter::make('role')
->label(__('admin.users.fields.role'))
->options(UserRole::class),
SelectFilter::make('sex')
->label(__('admin.users.fields.sex'))
->options(UserSex::class),
+2
View File
@@ -16,6 +16,7 @@ class UserResource extends JsonResource
public function toArray(Request $request): array
{
return [
'id' => $this->id, // à voir si suppression par la suite
'name' => $this->name,
'email' => $this->email,
'locale' => $this->locale,
@@ -24,6 +25,7 @@ class UserResource extends JsonResource
'height' => $this->height,
'avatarUrl' => $this->avatar_url ? asset(Storage::url($this->avatar_url)) : null,
'bio' => $this->bio,
'role' => $this->role,
'accountVerified' => $this->account_verified_at !== null,
'physicalActivityLevel' => $this->physical_activity_level,
'physicalActivityLevelLabel' => $this->physical_activity_level?->getLabel(),
+8 -13
View File
@@ -4,6 +4,7 @@ namespace App\Models;
use App\Enums\PacePreference;
use App\Enums\PhysicalActivityLevel;
use App\Enums\UserRole;
use App\Enums\UserSex;
use App\Enums\WeightGoal;
use Filament\Models\Contracts\FilamentUser;
@@ -12,9 +13,9 @@ use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Contracts\Translation\HasLocalePreference;
use Illuminate\Database\Eloquent\Concerns\HasUlids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
@@ -22,7 +23,7 @@ use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable implements FilamentUser, HasLocalePreference, MustVerifyEmail
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasApiTokens, HasFactory, HasUlids, Notifiable;
use HasApiTokens, HasFactory, HasUlids, Notifiable, SoftDeletes;
/**
* The attributes that are mass assignable.
@@ -33,6 +34,7 @@ class User extends Authenticatable implements FilamentUser, HasLocalePreference,
'name',
'email',
'locale',
'role',
'email_verified_at',
'password',
'avatar_url',
@@ -79,6 +81,7 @@ class User extends Authenticatable implements FilamentUser, HasLocalePreference,
'weight_goal' => WeightGoal::class,
'pace_preference' => PacePreference::class,
'sex' => UserSex::class,
'role' => UserRole::class,
'date_of_birth' => 'immutable_date',
];
}
@@ -103,12 +106,6 @@ class User extends Authenticatable implements FilamentUser, HasLocalePreference,
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
{
return $this->deviceTokens()
@@ -122,15 +119,13 @@ class User extends Authenticatable implements FilamentUser, HasLocalePreference,
return $this->locale ?: 'en';
}
public function isAdmin()
public function isAdmin(): bool
{
return true;
// return $this->role === 'admin';
return $this->role === UserRole::ADMIN;
}
public function canAccessPanel(Panel $panel): bool
{
return true;
return $this->role === UserRole::ADMIN || $this->role === UserRole::MODERATOR;
}
}