refactor(auth): menggunakan class form, menyesuaikan view dan test atas pemindahan tersebut

This commit is contained in:
Yoga Pangestu 2025-12-08 15:16:07 +07:00
parent 13dfa28d6e
commit 8dcc57673d
13 changed files with 134 additions and 80 deletions

View File

@ -2,10 +2,11 @@
namespace App\Livewire\Auth;
use App\Livewire\Forms\Auth\ForgotPasswordForm;
use App\Traits\WithToast;
use Illuminate\Support\Facades\Password;
use Illuminate\View\View;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Validate;
use Livewire\Component;
#[Layout('components.layouts.auth', [
@ -15,15 +16,14 @@ class ForgotPassword extends Component
{
use WithToast;
#[Validate(['required', 'string'])]
public string $email = '';
public ForgotPasswordForm $form;
public function sendLink()
public function sendLink(): mixed
{
$this->validate();
$this->form->validate();
$status = Password::sendResetLink(
$this->only('email')
['form.email' => $this->form->email]
);
return $status === Password::ResetLinkSent
@ -31,7 +31,7 @@ public function sendLink()
: $this->toast(__($status), 'Gagal', 'danger');
}
public function render()
public function render(): View
{
return view('livewire.auth.forgot-password');
}

View File

@ -3,9 +3,12 @@
namespace App\Livewire\Auth;
use App\Enums\UserStatus;
use App\Livewire\Forms\Auth\LoginForm;
use App\Models\User;
use App\Traits\WithToast;
use Illuminate\Support\Facades\Auth;
use Illuminate\View\View;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Validate;
use Livewire\Component;
#[Layout('components.layouts.auth', [
@ -15,13 +18,9 @@ class Login extends Component
{
use WithToast;
#[Validate(['required', 'string'], attribute: 'nama pengguna atau email')]
public string $login = '';
public LoginForm $form;
#[Validate('required', attribute: 'kata sandi')]
public string $password = '';
public function mount()
public function mount(): void
{
if (! request()->has('message')) {
return;
@ -36,9 +35,9 @@ public function mount()
JS);
}
public function auth()
public function auth(): void
{
$this->validate();
$this->form->validate();
if (! $this->attemptLogin()) {
$this->toast('Oops! Nama pengguna, email, atau kata sandi tidak sesuai.', 'Gagal', 'danger');
@ -49,7 +48,7 @@ public function auth()
$user = auth()->user();
if (! $this->userIsActive($user)) {
auth()->logout();
Auth::logout();
$this->toast(
'Akun Anda belum aktif. Mohon periksa email untuk mengaktifkan akun, dan hubungi kami jika memerlukan bantuan lebih lanjut.',
@ -67,22 +66,22 @@ public function auth()
$this->redirectByRole($user->roles->pluck('name')->toArray());
}
protected function attemptLogin()
protected function attemptLogin(): bool
{
$field = filter_var($this->login, FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
$field = filter_var($this->form->login, FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
return auth()->attempt([
$field => $this->login,
'password' => $this->password,
return Auth::attempt([
$field => $this->form->login,
'password' => $this->form->password,
]);
}
protected function userIsActive($user)
protected function userIsActive(?User $user): bool
{
return $user?->status === UserStatus::ACTIVE;
}
protected function redirectByRole(array $roles)
protected function redirectByRole(array $roles): void
{
if (array_intersect($roles, ['Developer', 'Owner', 'Leader', 'Admin'])) {
$this->redirectIntended('/studio/dashboard/overview', navigate: true);
@ -105,7 +104,7 @@ protected function redirectByRole(array $roles)
$this->redirectIntended('/login', navigate: true);
}
public function render()
public function render(): View
{
return view('livewire.auth.login');
}

View File

@ -2,11 +2,12 @@
namespace App\Livewire\Auth;
use Illuminate\View\View;
use Livewire\Component;
class Logout extends Component
{
public function logout()
public function logout(): void
{
auth()->logout();
@ -16,7 +17,7 @@ public function logout()
$this->redirectRoute('login');
}
public function render()
public function render(): View
{
return view('livewire.auth.logout');
}

View File

@ -9,6 +9,7 @@
use App\Traits\WithToast;
use App\Traits\WithUpdatedData;
use Illuminate\Support\Str;
use Illuminate\View\View;
use Livewire\Attributes\Layout;
use Livewire\Component;
@ -27,7 +28,7 @@ class Register extends Component
public int $totalSteps = 2;
public function mount()
public function mount(): void
{
$this->steps = [
['id' => 1, 'name' => 'Kredensial'],
@ -35,7 +36,7 @@ public function mount()
];
}
public function updateStep(int $value)
public function updateStep(int $value): void
{
if ($this->shouldValidateBeforeChangingStep()) {
$this->form->validateStep1();
@ -44,7 +45,7 @@ public function updateStep(int $value)
$this->currentStep = $value;
}
public function nextStep()
public function nextStep(): void
{
match ($this->currentStep) {
1 => $this->form->validateStep1(),
@ -57,19 +58,19 @@ public function nextStep()
}
}
public function prevStep()
public function prevStep(): void
{
if ($this->currentStep > 1) {
$this->currentStep--;
}
}
public function updated(string $propertyName, $value)
public function updated(string $propertyName, mixed $value): void
{
$this->validateUpdatedField($propertyName);
}
public function register()
public function register(): void
{
$this->form->auth();
@ -78,7 +79,7 @@ public function register()
$this->redirectAfterRegistration();
}
public function render()
public function render(): View
{
return view('livewire.auth.register');
}
@ -86,7 +87,7 @@ public function render()
/**
* Determine whether validation is required before changing step.
*/
protected function shouldValidateBeforeChangingStep()
protected function shouldValidateBeforeChangingStep(): bool
{
return $this->currentStep === 1;
}
@ -94,7 +95,7 @@ protected function shouldValidateBeforeChangingStep()
/**
* Validate updated field based on current form step rules.
*/
protected function validateUpdatedField(string $propertyName)
protected function validateUpdatedField(string $propertyName): void
{
$field = Str::replace('form.', '', $propertyName);
@ -110,7 +111,7 @@ protected function validateUpdatedField(string $propertyName)
/**
* Show success toast after registration.
*/
protected function sendRegistrationSuccessToast()
protected function sendRegistrationSuccessToast(): void
{
$this->toast('Selamat, akun Anda berhasil didaftarkan. Silakan periksa email Anda untuk melakukan verifikasi.');
}
@ -118,7 +119,7 @@ protected function sendRegistrationSuccessToast()
/**
* Dispatch notification to privileged users when a new member registers.
*/
protected function notifyPrivilegedUsersAboutNewMember()
protected function notifyPrivilegedUsersAboutNewMember(): void
{
$userIds = User::role(['Developer', 'Owner'])->pluck('id')->toArray();
@ -135,7 +136,7 @@ protected function notifyPrivilegedUsersAboutNewMember()
/**
* Redirect user after successful registration.
*/
protected function redirectAfterRegistration()
protected function redirectAfterRegistration(): void
{
$this->redirectIntended('/member/overview', navigate: true);
}

View File

@ -5,6 +5,7 @@
use App\Livewire\Forms\Auth\ResetPasswordForm;
use App\Traits\WithToast;
use Illuminate\Support\Facades\Password;
use Illuminate\View\View;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Url;
use Livewire\Component;
@ -19,17 +20,17 @@ class ResetPassword extends Component
public ResetPasswordForm $form;
#[Url]
public $token = '';
public string $token = '';
#[Url]
public $email = '';
public string $email = '';
public function mount()
public function mount(): void
{
$this->form->setTokenAndEmail($this->token, $this->email);
}
public function resetPassword()
public function resetPassword(): mixed
{
$status = $this->form->store();
@ -40,7 +41,7 @@ public function resetPassword()
: $this->toast(__($status), 'Gagal', 'danger');
}
public function render()
public function render(): View
{
return view('livewire.auth.reset-password');
}

View File

@ -0,0 +1,24 @@
<?php
namespace App\Livewire\Forms\Auth;
use Livewire\Form;
class ForgotPasswordForm extends Form
{
public string $email = '';
public function rules(): array
{
return [
'email' => ['required', 'string', 'email'],
];
}
public function validationAttributes(): array
{
return [
'email' => 'email',
];
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Livewire\Forms\Auth;
use Livewire\Form;
class LoginForm extends Form
{
public string $login = '';
public string $password = '';
public function rules(): array
{
return [
'login' => ['required', 'string'],
'password' => ['required'],
];
}
public function validationAttributes(): array
{
return [
'login' => 'nama pengguna atau email',
'password' => 'kata sandi',
];
}
}

View File

@ -62,17 +62,17 @@ public function rulesStep2(): array
];
}
public function validateStep1()
public function validateStep1(): void
{
$this->validate($this->rulesStep1());
}
public function validateStep2()
public function validateStep2(): void
{
$this->validate($this->rulesStep2());
}
public function validateAll()
public function validateAll(): void
{
$this->validate(array_merge(
$this->rulesStep1(),
@ -91,7 +91,7 @@ public function validationAttributes(): array
];
}
public function auth()
public function auth(): string
{
$this->validateAll();
@ -99,7 +99,7 @@ public function auth()
$maxUser = User::max('id') + 1;
$referralCode = ReferralCode::where('code', $this->referral_code)->first();
DB::transaction(function () use ($tier, $maxUser, $referralCode, &$customer) {
DB::transaction(function () use ($tier, $maxUser, $referralCode, &$customer): void {
$user = User::create([
'email' => $this->email,
'username' => $this->username,

View File

@ -18,7 +18,7 @@ class ResetPasswordForm extends Form
public string $password_confirmation = '';
public function rules()
public function rules(): array
{
return [
'token' => 'required',
@ -36,13 +36,13 @@ public function validationAttributes(): array
];
}
public function setTokenAndEmail(string $token, string $email)
public function setTokenAndEmail(string $token, string $email): void
{
$this->token = $token;
$this->email = $email;
}
public function store()
public function store(): string
{
$this->validate();

View File

@ -20,9 +20,9 @@ class="size-8 rounded shrink-0 bg-accent text-accent-foreground flex items-cente
<div class="flex flex-col gap-6">
<flux:field>
<flux:label>Email <span class="text-red-500 ms-1">*</span></flux:label>
<flux:input wire:model="email" type="text" placeholder="info.pangestuyoga@gmail.com"
<flux:input wire:model="form.email" type="text" placeholder="info.pangestuyoga@gmail.com"
autocomplete="off" />
<flux:error name="email" />
<flux:error name="form.email" />
</flux:field>
<flux:button variant="primary" class="w-full cursor-pointer" wire:click="sendLink">Kirim Tautan

View File

@ -20,9 +20,9 @@ class="size-8 rounded shrink-0 bg-accent text-accent-foreground flex items-cente
<div class="flex flex-col gap-6">
<flux:field>
<flux:label>Nama Pengguna atau Email <span class="text-red-500 ms-1">*</span></flux:label>
<flux:input wire:model="login" type="text" placeholder="johndoe / info.pangestuyoga@gmail.com"
<flux:input wire:model="form.login" type="text" placeholder="johndoe / info.pangestuyoga@gmail.com"
autocomplete="off" />
<flux:error name="login" />
<flux:error name="form.login" />
</flux:field>
<flux:field>
@ -34,9 +34,9 @@ class="size-8 rounded shrink-0 bg-accent text-accent-foreground flex items-cente
sandi?</flux:link>
</div>
<flux:input wire:model="password" type="password" placeholder="********" viewable />
<flux:input wire:model="form.password" type="password" placeholder="********" viewable />
<flux:error name="password" />
<flux:error name="form.password" />
</flux:field>
<flux:button variant="primary" class="w-full cursor-pointer" wire:click="auth">Masuk</flux:button>

View File

@ -16,7 +16,7 @@
function attemptSendResetLink(string $email)
{
return Livewire::test(ForgotPassword::class)
->set('email', $email)
->set('form.email', $email)
->call('sendLink');
}
@ -24,7 +24,7 @@ function mockPasswordResetStatus(string $status, string $email = 'user@example.c
{
Password::shouldReceive('sendResetLink')
->once()
->with(['email' => $email])
->with(['form.email' => $email])
->andReturn($status);
}
@ -37,7 +37,7 @@ function mockPasswordResetStatus(string $status, string $email = 'user@example.c
it('renders forgot password component successfully', function () {
Livewire::test(ForgotPassword::class)
->assertViewIs('livewire.auth.forgot-password')
->assertSet('email', '');
->assertSet('form.email', '');
});
/*
@ -48,9 +48,9 @@ function mockPasswordResetStatus(string $status, string $email = 'user@example.c
it('validates email field is required', function () {
Livewire::test(ForgotPassword::class)
->set('email', '')
->set('form.email', '')
->call('sendLink')
->assertHasErrors(['email' => 'required']);
->assertHasErrors(['form.email' => 'required']);
});
/*

View File

@ -46,8 +46,8 @@ function makeUser(?string $role = null, bool $isActive = true): User
function attemptLogin(string $login, string $password)
{
return Livewire::test(Login::class)
->set('login', $login)
->set('password', $password)
->set('form.login', $login)
->set('form.password', $password)
->call('auth');
}
@ -60,14 +60,14 @@ function attemptLogin(string $login, string $password)
it('renders login component successfully', function () {
Livewire::test(Login::class)
->assertViewIs('livewire.auth.login')
->assertSet('login', '')
->assertSet('password', '');
->assertSet('form.login', '')
->assertSet('form.password', '');
});
it('mounts component without query parameters', function () {
Livewire::test(Login::class)
->assertSet('login', '')
->assertSet('password', '');
->assertSet('form.login', '')
->assertSet('form.password', '');
});
it('mounts component with message query parameter', function () {
@ -75,8 +75,8 @@ function attemptLogin(string $login, string $password)
Livewire::withQueryParams(['message' => $message])
->test(Login::class)
->assertSet('login', '')
->assertSet('password', '');
->assertSet('form.login', '')
->assertSet('form.password', '');
});
/*
@ -87,26 +87,26 @@ function attemptLogin(string $login, string $password)
it('validates required fields', function () {
Livewire::test(Login::class)
->set('login', '')
->set('password', '')
->set('form.login', '')
->set('form.password', '')
->call('auth')
->assertHasErrors(['login', 'password']);
->assertHasErrors(['form.login', 'form.password']);
});
it('validates login field is required', function () {
Livewire::test(Login::class)
->set('login', '')
->set('password', 'password123')
->set('form.login', '')
->set('form.password', 'password123')
->call('auth')
->assertHasErrors(['login']);
->assertHasErrors(['form.login']);
});
it('validates password field is required', function () {
Livewire::test(Login::class)
->set('login', 'testuser')
->set('password', '')
->set('form.login', 'testuser')
->set('form.password', '')
->call('auth')
->assertHasErrors(['password']);
->assertHasErrors(['form.password']);
});
/*