fest(auth): implementasi fitur lupa kata sandi

This commit is contained in:
Yoga Pangestu 2025-11-27 08:54:41 +07:00
parent 8a30f8a782
commit 7f7d597116
9 changed files with 257 additions and 2 deletions

View File

@ -0,0 +1,38 @@
<?php
namespace App\Livewire\Auth;
use App\Traits\WithToast;
use Illuminate\Support\Facades\Password;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Validate;
use Livewire\Component;
#[Layout('components.layouts.auth', [
'title' => 'Lupa Kata Sandi',
])]
class ForgotPassword extends Component
{
use WithToast;
#[Validate(['required', 'string'])]
public string $email = '';
public function sendLink()
{
$this->validate();
$status = Password::sendResetLink(
$this->only('email')
);
return $status === Password::ResetLinkSent
? $this->toast(__($status), 'Berhasil')
: $this->toast(__($status), 'Gagal', 'danger');
}
public function render()
{
return view('livewire.auth.forgot-password');
}
}

View File

@ -21,6 +21,18 @@ class Login extends Component
#[Validate('required', attribute: 'kata sandi')]
public string $password = '';
public function mount()
{
if (request()->has('message')) {
$this->toast(request('message'));
$this->js("
const url = new URL(window.location);
url.search = '';
window.history.replaceState({}, '', url);
");
}
}
public function auth()
{
$this->validate();

View File

@ -0,0 +1,47 @@
<?php
namespace App\Livewire\Auth;
use App\Livewire\Forms\Auth\ResetPasswordForm;
use App\Traits\WithToast;
use Illuminate\Support\Facades\Password;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Url;
use Livewire\Component;
#[Layout('components.layouts.auth', [
'title' => 'Atur Ulang Kata Sandi',
])]
class ResetPassword extends Component
{
use WithToast;
public ResetPasswordForm $form;
#[Url]
public $token = '';
#[Url]
public $email = '';
public function mount()
{
$this->form->setTokenAndEmail($this->token, $this->email);
}
public function resetPassword()
{
$status = $this->form->store();
return $status === Password::PasswordReset
? $this->redirectRoute('login', [
'message' => 'Kata sandi berhasil diperbarui, silakan masuk kembali.',
], navigate: true)
: $this->toast(__($status), 'Gagal', 'danger');
}
public function render()
{
return view('livewire.auth.reset-password');
}
}

View File

@ -0,0 +1,64 @@
<?php
namespace App\Livewire\Forms\Auth;
use App\Models\User;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Livewire\Form;
class ResetPasswordForm extends Form
{
public string $token = '';
public string $email = '';
public string $password = '';
public string $password_confirmation = '';
public function rules()
{
return [
'token' => 'required',
'email' => ['required', 'email'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
'password_confirmation' => ['required', 'string', 'min:8', 'same:password'],
];
}
public function validationAttributes(): array
{
return [
'password' => 'kata sandi',
'password_confirmation' => 'konfirmasi kata sandi',
];
}
public function setTokenAndEmail(string $token, string $email)
{
$this->token = $token;
$this->email = $email;
}
public function store()
{
$this->validate();
$status = Password::reset(
$this->only('email', 'password', 'password_confirmation', 'token'),
function (User $user, string $password) {
$user->forceFill([
'password' => Hash::make($password),
]);
$user->save();
event(new PasswordReset($user));
}
);
return $status;
}
}

View File

@ -2,6 +2,8 @@
namespace App\Providers;
use App\Models\User;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
@ -19,6 +21,8 @@ public function register(): void
*/
public function boot(): void
{
//
ResetPassword::createUrlUsing(function (User $user, string $token) {
return config('app.url').'/auth/reset-password?token='.$token.'&email='.$user->email;
});
}
}

View File

@ -0,0 +1,39 @@
<div class="flex min-h-screen">
<div class="flex-1 flex justify-center items-center">
<div class="w-80 md:w-100 space-y-6">
<div class="flex justify-center opacity-50">
<flux:brand href="{{ route('homepage') }}" name="Yadi Parfum" wire:navigate.hover>
<x-slot name="logo">
<div
class="size-8 rounded shrink-0 bg-accent text-accent-foreground flex items-center justify-center">
<i class="font-serif font-bold">YD</i>
</div>
</x-slot>
</flux:brand>
</div>
<flux:heading size="lg" class="mb-1">Lupa Kata Sandi?</flux:heading>
<flux:text>
Silakan masukkan alamat email Anda. Kami akan mengirimkan instruksi untuk mengatur ulang kata sandi.
</flux:text>
<div class="flex flex-col gap-6">
<flux:input wire:model="email" label="Email" type="text" placeholder="info.pangestuyoga@gmail.com"
autocomplete="off" />
<flux:button variant="primary" class="w-full cursor-pointer" wire:click="sendLink">Kirim Tautan
</flux:button>
</div>
<flux:subheading class="text-center">
Sudah ingat kata sandi? <flux:link href="{{ route('login') }}" wire:navigate.hover>Masuk
</flux:link>
</flux:subheading>
</div>
</div>
<div class="flex-1 p-4 max-lg:hidden">
<div class="relative rounded-lg h-full w-full bg-zinc-100 flex flex-col items-start justify-end p-16 bg-cover bg-center bg-no-repeat"
style="background-image: url('/assets/images/cover.jpg')">
</div>
</div>
</div>

View File

@ -25,7 +25,9 @@ class="size-8 rounded shrink-0 bg-accent text-accent-foreground flex items-cente
<div class="mb-3 flex justify-between">
<flux:label>Kata Sandi</flux:label>
<flux:link href="#" variant="subtle" class="text-sm">Lupa kata sandi?</flux:link>
<flux:link href="{{ route('password.request') }}" variant="subtle" class="text-sm"
wire:navigate.hover>Lupa kata
sandi?</flux:link>
</div>
<flux:input wire:model="password" type="password" placeholder="********" viewable />

View File

@ -0,0 +1,43 @@
<div class="flex min-h-screen">
<div class="flex-1 flex justify-center items-center">
<div class="w-80 md:w-100 space-y-6">
<div class="flex justify-center opacity-50">
<flux:brand href="{{ route('homepage') }}" name="Yadi Parfum" wire:navigate.hover>
<x-slot name="logo">
<div
class="size-8 rounded shrink-0 bg-accent text-accent-foreground flex items-center justify-center">
<i class="font-serif font-bold">YD</i>
</div>
</x-slot>
</flux:brand>
</div>
<flux:heading size="lg" class="mb-1">Atur Ulang Kata Sandi</flux:heading>
<flux:text>
Silakan masukkan kata sandi baru Anda untuk mengatur ulang kata sandi Anda.
</flux:text>
<div class="flex flex-col gap-6">
<flux:input label="Kata Sandi" wire:model="form.password" type="password" placeholder="********"
viewable />
<flux:input label="Konfirmasi Kata Sandi" wire:model="form.password_confirmation" type="password"
placeholder="********" viewable />
<flux:button variant="primary" class="w-full cursor-pointer" wire:click="resetPassword">Atur Ulang
</flux:button>
</div>
<flux:subheading class="text-center">
Sudah ingat kata sandi? <flux:link href="{{ route('login') }}" wire:navigate.hover>Masuk
</flux:link>
</flux:subheading>
</div>
</div>
<div class="flex-1 p-4 max-lg:hidden">
<div class="relative rounded-lg h-full w-full bg-zinc-100 flex flex-col items-start justify-end p-16 bg-cover bg-center bg-no-repeat"
style="background-image: url('/assets/images/cover.jpg')">
</div>
</div>
</div>

View File

@ -1,8 +1,10 @@
<?php
use App\Livewire\Auth\ForgotPassword as ForgotPasswordComponent;
use App\Livewire\Auth\Login as LoginComponent;
use App\Livewire\Auth\Logout as LogoutComponent;
use App\Livewire\Auth\Register as RegisterComponent;
use App\Livewire\Auth\ResetPassword as ResetPasswordComponent;
use Illuminate\Foundation\Auth\EmailVerificationRequest;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
@ -12,6 +14,10 @@
->group(function () {
Route::get('login', LoginComponent::class)->name('login');
Route::get('register', RegisterComponent::class)->name('register');
Route::get('forgot-password', ForgotPasswordComponent::class)->name('password.request');
Route::get('reset-password', ResetPasswordComponent::class)->name('password.reset');
});
Route::post('logout', LogoutComponent::class)->name('logout')->middleware('auth');