From 0a956fd9e79f800c9b4b39a2b938aaa6ae444019 Mon Sep 17 00:00:00 2001 From: Constantin Graf Date: Thu, 6 Feb 2025 14:00:59 -0500 Subject: [PATCH] Fixed user create in filament --- .../Commands/Admin/UserCreateCommand.php | 7 +--- app/Filament/Resources/UserResource.php | 22 ++++++++++ .../UserResource/Pages/CreateUser.php | 3 +- app/Models/User.php | 2 +- app/Service/UserService.php | 6 ++- .../Filament/Resources/UserResourceTest.php | 42 +++++++++++++++++++ 6 files changed, 74 insertions(+), 8 deletions(-) diff --git a/app/Console/Commands/Admin/UserCreateCommand.php b/app/Console/Commands/Admin/UserCreateCommand.php index 979278d7..5ac83127 100644 --- a/app/Console/Commands/Admin/UserCreateCommand.php +++ b/app/Console/Commands/Admin/UserCreateCommand.php @@ -57,7 +57,7 @@ class UserCreateCommand extends Command } $user = null; - DB::transaction(function () use (&$user, $name, $email, $password): void { + DB::transaction(function () use (&$user, $name, $email, $password, $verifyEmail): void { $user = app(UserService::class)->createUser( $name, $email, @@ -65,6 +65,7 @@ class UserCreateCommand extends Command 'UTC', Weekday::Monday, 'EUR', + $verifyEmail ); }); /** @var Organization|null $organization */ @@ -73,10 +74,6 @@ class UserCreateCommand extends Command throw new LogicException('User does not have an organization'); } - if ($verifyEmail) { - $user->markEmailAsVerified(); - } - $this->info('Created user "'.$name.'" ("'.$email.'")'); $this->line('ID: '.$user->getKey()); $this->line('Name: '.$name); diff --git a/app/Filament/Resources/UserResource.php b/app/Filament/Resources/UserResource.php index 535a846a..4aaca271 100644 --- a/app/Filament/Resources/UserResource.php +++ b/app/Filament/Resources/UserResource.php @@ -25,6 +25,7 @@ use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; +use Korridor\LaravelModelValidationRules\Rules\UniqueEloquent; use STS\FilamentImpersonate\Tables\Actions\Impersonate; class UserResource extends Resource @@ -39,6 +40,8 @@ class UserResource extends Resource public static function form(Form $form): Form { + /** @var User|null $record */ + $record = $form->getRecord(); return $form ->columns(1) ->schema([ @@ -55,6 +58,13 @@ class UserResource extends Resource Forms\Components\TextInput::make('email') ->label('Email') ->required() + ->rules($record?->is_placeholder ? [] : [ + UniqueEloquent::make(User::class, 'email') + ->ignore($record?->getKey()), + ]) + ->rule([ + 'email', + ]) ->maxLength(255), Forms\Components\Toggle::make('is_placeholder') ->label('Is Placeholder?') @@ -62,7 +72,11 @@ class UserResource extends Resource ->disabledOn(['edit']), Forms\Components\DateTimePicker::make('email_verified_at') ->label('Email Verified At') + ->hiddenOn(['create']) ->nullable(), + Forms\Components\Toggle::make('is_email_verified') + ->label('Email Verified?') + ->visibleOn(['create']), Forms\Components\Select::make('timezone') ->label('Timezone') ->options(fn (): array => app(TimezoneService::class)->getSelectOptions()) @@ -74,8 +88,16 @@ class UserResource extends Resource ->required(), TextInput::make('password') ->password() + ->label('Password') ->dehydrateStateUsing(fn ($state) => Hash::make($state)) ->dehydrated(fn ($state) => filled($state)) + ->hiddenOn(['create']) + ->required(fn (string $context): bool => $context === 'create') + ->maxLength(255), + TextInput::make('password_create') + ->password() + ->label('Password') + ->visibleOn(['create']) ->required(fn (string $context): bool => $context === 'create') ->maxLength(255), Forms\Components\Select::make('currency') diff --git a/app/Filament/Resources/UserResource/Pages/CreateUser.php b/app/Filament/Resources/UserResource/Pages/CreateUser.php index 3ec53fb2..7badbd47 100644 --- a/app/Filament/Resources/UserResource/Pages/CreateUser.php +++ b/app/Filament/Resources/UserResource/Pages/CreateUser.php @@ -20,10 +20,11 @@ class CreateUser extends CreateRecord $user = $userService->createUser( $data['name'], $data['email'], - $data['password'], + $data['password_create'], $data['timezone'], Weekday::from($data['week_start']), $data['currency'], + (bool) $data['is_email_verified'] ); return $user; diff --git a/app/Models/User.php b/app/Models/User.php index b846acee..54906861 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -34,7 +34,7 @@ use OwenIt\Auditing\Contracts\Auditable as AuditableContract; * @property string $id * @property string $name * @property string $email - * @property string|null $email_verified_at + * @property Carbon|null $email_verified_at * @property string|null $password * @property string|null $two_factor_secret * @property string $timezone diff --git a/app/Service/UserService.php b/app/Service/UserService.php index c130eded..92d6e7d2 100644 --- a/app/Service/UserService.php +++ b/app/Service/UserService.php @@ -12,11 +12,12 @@ use App\Models\Organization; use App\Models\ProjectMember; use App\Models\TimeEntry; use App\Models\User; +use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Hash; class UserService { - public function createUser(string $name, string $email, string $password, string $timezone, Weekday $weekStart, string $currency): User + public function createUser(string $name, string $email, string $password, string $timezone, Weekday $weekStart, string $currency, bool $verifyEmail = false): User { $user = new User; $user->name = $name; @@ -24,6 +25,9 @@ class UserService $user->password = Hash::make($password); $user->timezone = $timezone; $user->week_start = $weekStart; + if ($verifyEmail) { + $user->email_verified_at = Carbon::now(); + } $user->save(); $organization = new Organization; diff --git a/tests/Unit/Filament/Resources/UserResourceTest.php b/tests/Unit/Filament/Resources/UserResourceTest.php index ca941e4f..33f789b9 100644 --- a/tests/Unit/Filament/Resources/UserResourceTest.php +++ b/tests/Unit/Filament/Resources/UserResourceTest.php @@ -11,6 +11,7 @@ use App\Models\Organization; use App\Models\User; use App\Service\DeletionService; use Illuminate\Support\Facades\Config; +use Illuminate\Support\Facades\Hash; use Livewire\Livewire; use Mockery\MockInterface; use PHPUnit\Framework\Attributes\UsesClass; @@ -67,6 +68,47 @@ class UserResourceTest extends FilamentTestCase $response->assertSuccessful(); } + public function test_can_see_create_page_of_user(): void + { + // Act + $response = Livewire::test(UserResource\Pages\CreateUser::class); + + // Assert + $response->assertSuccessful(); + } + + public function test_can_create_user(): void + { + // Arrange + $userFake = User::factory()->make(); + + // Act + $response = Livewire::test(UserResource\Pages\CreateUser::class) + ->fillForm([ + 'name' => $userFake->name, + 'email' => $userFake->email, + 'password_create' => 'password', + 'timezone' => $userFake->timezone, + 'week_start' => $userFake->week_start->value, + 'currency' => 'EUR', + ]) + ->call('create') + ->assertHasNoFormErrors(); + + // Assert + $response->assertSuccessful(); + $user = User::where('email', $userFake->email)->first(); + $this->assertNotNull($user); + $this->assertSame($userFake->name, $user->name); + $this->assertSame($userFake->email, $user->email); + $this->assertSame($userFake->timezone, $user->timezone); + $this->assertSame($userFake->week_start->value, $user->week_start->value); + $organization = $user->ownedTeams()->first(); + $this->assertNotNull($organization); + $this->assertSame('EUR', $organization->currency); + $this->assertTrue(Hash::check('password', $user->password)); + } + public function test_can_delete_a_user(): void { // Arrange