feat(register): impementasi module register
-mematikan refresh true vite -menambahkan kolom email_verified_at user
This commit is contained in:
parent
4f41461310
commit
cdc50218e8
92
app/Livewire/Auth/Register.php
Normal file
92
app/Livewire/Auth/Register.php
Normal file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Auth;
|
||||
|
||||
use App\Livewire\Forms\RegisterForm;
|
||||
use Flux\Flux;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Component;
|
||||
|
||||
#[Layout('components.layouts.auth', [
|
||||
'title' => 'Daftar Akun',
|
||||
])]
|
||||
class Register extends Component
|
||||
{
|
||||
public RegisterForm $form;
|
||||
|
||||
public array $steps = [];
|
||||
|
||||
public int $currentStep = 1;
|
||||
|
||||
public int $totalSteps = 2;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->steps = [
|
||||
['id' => 1, 'name' => 'Kredensial'],
|
||||
['id' => 2, 'name' => 'Informasi'],
|
||||
];
|
||||
}
|
||||
|
||||
public function updateStep(int $value)
|
||||
{
|
||||
if ($this->currentStep === 1) {
|
||||
$this->form->validateStep1();
|
||||
}
|
||||
|
||||
$this->currentStep = $value;
|
||||
}
|
||||
|
||||
public function nextStep()
|
||||
{
|
||||
match ($this->currentStep) {
|
||||
1 => $this->form->validateStep1(),
|
||||
2 => $this->form->validateStep2(),
|
||||
default => null,
|
||||
};
|
||||
|
||||
if ($this->currentStep < $this->totalSteps) {
|
||||
$this->currentStep++;
|
||||
}
|
||||
}
|
||||
|
||||
public function prevStep()
|
||||
{
|
||||
if ($this->currentStep > 1) {
|
||||
$this->currentStep--;
|
||||
}
|
||||
}
|
||||
|
||||
public function updated(string $propertyName, $value)
|
||||
{
|
||||
$field = Str::replace('form.', '', $propertyName);
|
||||
|
||||
if (array_key_exists($field, $this->form->rulesStep1())) {
|
||||
$this->validateOnly($propertyName, $this->form->rulesStep1());
|
||||
}
|
||||
|
||||
if (array_key_exists($field, $this->form->rulesStep2())) {
|
||||
$this->validateOnly($propertyName, $this->form->rulesStep2());
|
||||
}
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
$this->form->auth();
|
||||
|
||||
Flux::toast(
|
||||
heading: 'Berhasil',
|
||||
text: 'Selamat, akun Anda berhasil didaftarkan. Silakan cek email Anda untuk melakukan verifikasi.',
|
||||
variant: 'success',
|
||||
duration: 3000
|
||||
);
|
||||
|
||||
$this->redirectIntended('/customer/dashboard/overview', navigate: true);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.auth.register');
|
||||
}
|
||||
}
|
||||
140
app/Livewire/Forms/RegisterForm.php
Normal file
140
app/Livewire/Forms/RegisterForm.php
Normal file
@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Forms;
|
||||
|
||||
use App\Enums\Gender;
|
||||
use App\Enums\UserStatus;
|
||||
use App\Models\Customer;
|
||||
use App\Models\Membership;
|
||||
use App\Models\ReferralCode;
|
||||
use App\Models\ReferralUsage;
|
||||
use App\Models\Tier;
|
||||
use App\Models\User;
|
||||
use App\Rules\PhoneNumber;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
use Livewire\Form;
|
||||
|
||||
class RegisterForm extends Form
|
||||
{
|
||||
public string $email = '';
|
||||
|
||||
public string $username = '';
|
||||
|
||||
public string $password = '';
|
||||
|
||||
public string $name = '';
|
||||
|
||||
public string $phone_number = '';
|
||||
|
||||
public string $gender = '';
|
||||
|
||||
public string $referral_code = '';
|
||||
|
||||
public function rulesStep1(): array
|
||||
{
|
||||
return [
|
||||
'email' => ['required', 'email', 'max:100', Rule::unique('users', 'email')],
|
||||
'username' => ['required', 'alpha_num', 'min:5', 'max:20', Rule::unique('users', 'username')],
|
||||
'password' => [
|
||||
'required',
|
||||
'string',
|
||||
'min:8',
|
||||
Password::min(8)->letters()->mixedCase()
|
||||
->numbers()
|
||||
->symbols()
|
||||
->uncompromised(),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function rulesStep2(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['required', 'string', 'max:50'],
|
||||
'phone_number' => ['required', 'string', new PhoneNumber],
|
||||
'gender' => ['required', Rule::in(Gender::values())],
|
||||
'referral_code' => ['nullable', 'string', Rule::exists('referral_codes', 'code')],
|
||||
];
|
||||
}
|
||||
|
||||
public function validateStep1()
|
||||
{
|
||||
$this->validate($this->rulesStep1());
|
||||
}
|
||||
|
||||
public function validateStep2()
|
||||
{
|
||||
$this->validate($this->rulesStep2());
|
||||
}
|
||||
|
||||
public function validateAll()
|
||||
{
|
||||
$this->validate(array_merge(
|
||||
$this->rulesStep1(),
|
||||
$this->rulesStep2()
|
||||
));
|
||||
}
|
||||
|
||||
public function validationAttributes(): array
|
||||
{
|
||||
return [
|
||||
'username' => 'nama pengguna',
|
||||
'password' => 'kata sandi',
|
||||
'name' => 'nama',
|
||||
'phone_number' => 'nomor telepon',
|
||||
'gender' => 'jenis kelamin',
|
||||
];
|
||||
}
|
||||
|
||||
public function auth()
|
||||
{
|
||||
$this->validateAll();
|
||||
|
||||
$tier = Tier::orderBy('min_points')->first();
|
||||
$maxUser = User::max('id') + 1;
|
||||
$referralCode = ReferralCode::where('code', $this->referral_code)->first();
|
||||
|
||||
DB::transaction(function () use ($tier, $maxUser, $referralCode) {
|
||||
$user = User::create([
|
||||
'email' => $this->email,
|
||||
'username' => $this->username,
|
||||
'password' => Hash::make($this->password),
|
||||
'status' => UserStatus::ACTIVE,
|
||||
]);
|
||||
|
||||
Customer::create([
|
||||
'user_id' => $user->id,
|
||||
'name' => $this->name,
|
||||
'phone_number' => $this->phone_number,
|
||||
'gender' => $this->gender,
|
||||
]);
|
||||
|
||||
Membership::create([
|
||||
'user_id' => $user->id,
|
||||
'tier_id' => $tier->id,
|
||||
'total_points' => 0,
|
||||
]);
|
||||
|
||||
ReferralCode::create([
|
||||
'user_id' => $user->id,
|
||||
'code' => generateReferralCode($this->username, $maxUser),
|
||||
]);
|
||||
|
||||
if ($this->referral_code) {
|
||||
ReferralUsage::create([
|
||||
'user_id' => $user->id,
|
||||
'referral_code_id' => $referralCode->id,
|
||||
]);
|
||||
|
||||
$referralCode->update(['uses_count', $referralCode->uses_count + 1]);
|
||||
}
|
||||
|
||||
auth()->login($user);
|
||||
|
||||
$user->sendEmailVerificationNotification();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -18,6 +18,7 @@ public function up(): void
|
||||
$table->string('username', 20)->unique();
|
||||
$table->string('password', 97);
|
||||
$table->enum('status', [UserStatus::values()])->default(UserStatus::ACTIVE)->comment(UserStatus::comment());
|
||||
$table->timestamp('email_verified_at')->nullable();
|
||||
$table->timestamp('last_login_at')->nullable();
|
||||
$table->timestamp('last_password_change_at')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
@ -36,6 +36,11 @@ class="size-8 rounded shrink-0 bg-accent text-accent-foreground flex items-cente
|
||||
|
||||
<flux:button variant="primary" class="w-full cursor-pointer" wire:click="auth">Masuk</flux:button>
|
||||
</div>
|
||||
<flux:subheading class="text-center">
|
||||
Nikmati berbagai keuntungan! <flux:link href="{{ route('register') }}" wire:navigate.hover>Daftar
|
||||
sekarang.
|
||||
</flux:link>
|
||||
</flux:subheading>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
130
resources/views/livewire/auth/register.blade.php
Normal file
130
resources/views/livewire/auth/register.blade.php
Normal file
@ -0,0 +1,130 @@
|
||||
<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">Halo!</flux:heading>
|
||||
<flux:text>
|
||||
Silakan ikuti langkah-langkah berikut untuk membuat akun dan nikmati keuntungannya dengan menjadi
|
||||
member.
|
||||
</flux:text>
|
||||
|
||||
<ul class="relative flex flex-col md:flex-row gap-5 justify-center py-3">
|
||||
@foreach ($steps as $step)
|
||||
<li class="md:shrink md:basis-0 flex-1 group flex flex-row items-center md:flex-col md:items-center cursor-default"
|
||||
wire:key="step-{{ $step['id'] }}" wire:click="updateStep({{ $step['id'] }})">
|
||||
|
||||
<span
|
||||
class="size-7 flex justify-center items-center shrink-0 font-medium text-gray-800 rounded-full transition-all duration-500 ease-in-out {{ $currentStep === $step['id'] ? 'bg-emerald-500 scale-110 shadow-lg text-white' : 'bg-gray-100 scale-100' }}">
|
||||
{{ $step['id'] }}
|
||||
</span>
|
||||
|
||||
<div
|
||||
class="ml-3 md:ml-0 md:mt-2 text-left md:text-center transition-all duration-300 ease-in-out">
|
||||
<flux:text class="text-gray-500">Step</flux:text>
|
||||
<flux:text
|
||||
class="transition-all duration-500 ease-in-out {{ $currentStep === $step['id'] ? 'text-emerald-600 font-semibold' : '' }}">
|
||||
{{ $step['name'] }}
|
||||
</flux:text>
|
||||
</div>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
|
||||
@if ($currentStep === 1)
|
||||
<div class="flex flex-col gap-6">
|
||||
<flux:field>
|
||||
<flux:label>Email</flux:label>
|
||||
|
||||
<flux:description>Pastikan email aktif karena akan digunakan untuk verifikasi.
|
||||
</flux:description>
|
||||
|
||||
<flux:input placeholder="Masukkan email" wire:model.live.debounce.500ms="form.email" autofocus
|
||||
autocomplete="off" />
|
||||
|
||||
<flux:error name="form.email" />
|
||||
</flux:field>
|
||||
|
||||
<flux:input label="Nama Pengguna" placeholder="Masukkan nama pengguna"
|
||||
wire:model.live.debounce.500ms="form.username" autocomplete="off" />
|
||||
|
||||
<flux:input label="Kata Sandi" placeholder="Masukkan kata sandi" type="password"
|
||||
wire:model.live.debounce.500ms="form.password" autocomplete="off" viewable />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($currentStep === 2)
|
||||
<div class="flex flex-col gap-6">
|
||||
<flux:input label="Nama" placeholder="Masukkan nama" wire:model.live.debounce.500ms="form.name"
|
||||
autofocus autocomplete="off" />
|
||||
|
||||
<flux:input label="Nomor Telepon" placeholder="Masukkan nomor telepon" mask="9999 9999 99999"
|
||||
wire:model.live.debounce.500ms="form.phone_number" autocomplete="off" />
|
||||
|
||||
<flux:radio.group wire:model.live="form.gender" variant="buttons" class="w-full *:flex-1"
|
||||
label="Jenis Kelamin">
|
||||
@foreach (\App\Enums\Gender::cases() as $gender)
|
||||
<flux:radio value="{{ $gender->value }}" icon="{{ $gender->icon() }}">
|
||||
{{ $gender->label() }}
|
||||
</flux:radio>
|
||||
@endforeach
|
||||
</flux:radio.group>
|
||||
|
||||
<flux:input label="Kode Referral" placeholder="Masukkan kode referral"
|
||||
wire:model.live.debounce.500ms="form.referral_code" autocomplete="off" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-8 flex justify-between items-center gap-4">
|
||||
@if ($currentStep > 1)
|
||||
<flux:button wire:click="prevStep" variant="ghost" icon:leading="arrow-left">
|
||||
Sebelumnya
|
||||
</flux:button>
|
||||
@else
|
||||
<div></div>
|
||||
@endif
|
||||
|
||||
@if ($currentStep < $totalSteps)
|
||||
<flux:button wire:click="nextStep" variant="primary" icon:trailing="arrow-right">
|
||||
Selanjutnya
|
||||
</flux:button>
|
||||
@else
|
||||
<flux:button type="submit" variant="primary" icon:trailing="check" wire:click="register">
|
||||
Daftar Sekarang
|
||||
</flux:button>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<flux:subheading class="text-center mt-8">
|
||||
Sudah punya akun? <flux:link href="{{ route('login') }}" wire:navigate.hover>Masuk sekarang.
|
||||
</flux:link>
|
||||
</flux:subheading>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 p-4 max-lg:hidden">
|
||||
<div class="text-white relative rounded-lg h-full w-full bg-zinc-900 flex flex-col items-start justify-end p-16"
|
||||
style="background-image: url('/assets/images/cover.jpg'); background-size: cover">
|
||||
<div class="flex gap-2 mb-4">
|
||||
<flux:icon.star variant="solid" />
|
||||
<flux:icon.star variant="solid" />
|
||||
<flux:icon.star variant="solid" />
|
||||
<flux:icon.star variant="solid" />
|
||||
<flux:icon.star variant="solid" />
|
||||
</div>
|
||||
|
||||
<div class="mb-6 italic font-base text-3xl xl:text-4xl text-white">
|
||||
Bergabunglah dengan ribuan pelanggan yang puas dengan produk parfum berkualitas kami.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
use App\Livewire\Auth\Login;
|
||||
use App\Livewire\Auth\Logout;
|
||||
use App\Livewire\Auth\Register;
|
||||
use App\Livewire\Studio\Catalog\Brand as BrandComponent;
|
||||
use App\Livewire\Studio\Catalog\Category as CategoryComponent;
|
||||
use App\Livewire\Studio\Dashboard\Overview;
|
||||
@ -16,6 +17,8 @@
|
||||
use App\Livewire\Studio\Master\User\Create as UserCreate;
|
||||
use App\Livewire\Studio\Master\User\Edit as UserEdit;
|
||||
use App\Livewire\Studio\Master\User\Index as UserIndex;
|
||||
use Illuminate\Foundation\Auth\EmailVerificationRequest;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
require __DIR__.'/customer.php';
|
||||
@ -28,9 +31,27 @@
|
||||
->prefix('auth')
|
||||
->group(function () {
|
||||
Route::get('login', Login::class)->name('login');
|
||||
Route::get('register', Register::class)->name('register');
|
||||
});
|
||||
|
||||
Route::get('logout', Logout::class)->name('logout')->middleware('auth');
|
||||
|
||||
Route::get('/email/verify', function () {
|
||||
return view('auth.verify-email');
|
||||
})->middleware('auth')->name('verification.notice');
|
||||
|
||||
Route::get('/email/verify/{id}/{hash}', function (EmailVerificationRequest $request) {
|
||||
$request->fulfill();
|
||||
|
||||
return redirect('/');
|
||||
})->middleware(['auth', 'signed'])->name('verification.verify');
|
||||
|
||||
Route::post('/email/verification-notification', function (Request $request) {
|
||||
$request->user()->sendEmailVerificationNotification();
|
||||
|
||||
return back()->with('message', 'Verification link sent!');
|
||||
})->middleware(['auth', 'throttle:6,1'])->name('verification.send');
|
||||
|
||||
Route::middleware('auth')
|
||||
->prefix('studio')
|
||||
->group(function () {
|
||||
|
||||
@ -6,7 +6,7 @@ export default defineConfig({
|
||||
plugins: [
|
||||
laravel({
|
||||
input: ['resources/css/app.css', 'resources/js/app.js'],
|
||||
refresh: true,
|
||||
// refresh: true,
|
||||
}),
|
||||
tailwindcss(),
|
||||
],
|
||||
|
||||
Loading…
Reference in New Issue
Block a user