Kustomisasi Halaman Login di Filament 4

Kustomisasi Halaman Login di Filament 4

Kalau kamu udah pernah pakai FilamentPHP, pasti tahu dong tampilan login bawaannya itu simple banget bersih sih, tapi kadang terlalu standar. Nah, gimana kalau kita pengen ubah tampilannya biar lebih kita banget? Misalnya tambahin logo, ubah warna, atau bahkan bikin layout yang lebih keren.
Di artikel ini, kita bahas cara Kustomisasi Halaman Login di Filament 4 dengan gaya santai tapi tetap ngoding.

1. Bikin Komponen Livewire

Pertama, kita bikin komponen baru untuk halaman login kita:

php artisan make:livewire AdminLogin

Perintah ini bakal bikin dua file:

app/Livewire/AdminLogin.php   ← logic dan autentikasi
resources/views/livewire/admin-login.blade.php   ← tampilan login

2. Buat Layout "Guest"

Kita butuh layout khusus untuk halaman login (yang di luar dashboard admin Filament).

Buat file baru:

resources/views/components/layouts/guest.blade.php

Isinya seperti ini:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ $title ?? config('app.name', 'Laravel') }}</title>

    <!-- Fonts -->
    <link rel="preconnect" href="https://fonts.bunny.net">
    <link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />

    <!-- Styles -->
    @filamentStyles
    @vite('resources/css/app.css')
</head>
<body class="font-sans antialiased">
    {{ $slot }}

    @filamentScripts
    @vite('resources/js/app.js')
</body>
</html>

Penjelasan singkat:

  • @filamentStyles dan @filamentScripts = otomatis load semua CSS & JS Filament.
  • @vite('resources/css/app.css') = load Tailwind dari proyek kamu.
  • {{ $slot }} = tempat isi konten login kamu bakal ditampilkan.

3. Buat Logika Login di Livewire

Sekarang buka file:

app/Livewire/AdminLogin.php

Isi seperti ini:

<?php

namespace App\Livewire;

use Filament\Facades\Filament;
use Livewire\Component;
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;

class AdminLogin extends Component
{
    use WithRateLimiting; 

    public $email = '';
    public $password = '';
    public bool $remember = false;

    public function mount()
    {
        // Kalau user udah login, langsung lempar ke dashboard
        if (Filament::auth()->check()) {
            return redirect()->intended(Filament::getUrl());
        }
    }

    public function authenticate()
    {
        // Batasin percobaan login biar gak brute force
        try {
            $this->rateLimit(5);
        } catch (TooManyRequestsException $exception) {
            $this->addError('email', __('Terlalu banyak percobaan login. Coba lagi dalam :seconds detik.', [
                'seconds' => $exception->secondsUntilAvailable,
            ]));
            return;
        }

        // Validasi manual
        $this->validate([
            'email' => 'required|email',
            'password' => 'required|string|min:4',
        ]);

        // Coba login via Filament
        if (!Filament::auth()->attempt([
            'email' => $this->email,
            'password' => $this->password,
        ], $this->remember)) {
            $this->addError('email', 'Email atau password salah.');
            return;
        }

        session()->regenerate();

        return redirect()->intended(Filament::getUrl());
    }

    public function render(): \Illuminate\Contracts\View\View
    {
        return view('livewire.admin-login')->layout('components.layouts.guest');
    }
}

Penjelasan detail:

BagianFungsi
use InteractsWithForms;Supaya bisa pakai sistem form dari Filament.
use WithRateLimiting;Batasin jumlah percobaan login (contoh: 5x per menit).
mount()Cek kalau user udah login → redirect langsung ke dashboard.
authenticate()Proses login: validasi, rate-limit, autentikasi, dan redirect.
Filament::auth()->attempt()Login pakai sistem auth-nya Filament.
ValidationExceptionBiar kalau gagal, bisa munculin pesan error di form.

4. Desain Tampilan Login

Sekarang buka:

resources/views/livewire/admin-login.blade.php
<div class="min-h-screen flex items-center justify-center bg-gradient-to-br from-gray-800 via-gray-700 to-gray-900 relative overflow-hidden">
    {{-- Background image --}}
    <div class="absolute inset-0 bg-[url('https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=1920')] bg-cover bg-center opacity-30"></div>
    <div class="absolute inset-0 bg-gradient-to-br from-gray-900/90 via-gray-800/80 to-gray-900/90"></div>

    <div class="relative z-10 w-full max-w-6xl mx-4 flex">
        {{-- Form --}}
        <div class="w-full lg:w-1/2 bg-gray-800/90 backdrop-blur-lg border border-gray-700/50 rounded-3xl p-10 shadow-2xl">
            {{-- Logo --}}
            <div class="flex items-center gap-2 mb-8">
                <div class="w-8 h-8 bg-blue-500 rounded-full"></div>
                <span class="text-white font-semibold text-lg">{{ config('app.name','Semangatin') }}</span>
            </div>

            {{-- Title --}}
            <div class="mb-8">
                <p class="text-gray-400 text-sm mb-2 uppercase tracking-wider">WELCOME BACK</p>
                <h2 class="text-4xl font-bold text-white mb-3">
                    Sign in to your account<span class="text-blue-500">.</span>
                </h2>
            </div>

            {{-- Form Input --}}
            <form wire:submit="authenticate" class="space-y-5">
                {{-- Email --}}
                <div>
                    <label for="email" class="block text-sm text-gray-300 mb-2">Email</label>
                    <input type="email" id="email" wire:model="email"
                        placeholder="[email protected]"
                        class="w-full px-4 py-3 bg-gray-700/50 border border-gray-600/50 rounded-lg text-white placeholder-gray-400 focus:ring-2 focus:ring-blue-500/50 focus:border-transparent transition-all duration-300 text-sm @error('email') border-red-500 @enderror"
                        required autofocus />
                    @error('email')
                        <p class="mt-1 text-sm text-red-500">{{ $message }}</p>
                    @enderror
                </div>

                {{-- Password --}}
                <div>
                    <label for="password" class="block text-sm text-gray-300 mb-2">Password</label>
                    <input type="password" id="password" wire:model="password"
                        placeholder="••••••••"
                        class="w-full px-4 py-3 bg-gray-700/50 border border-gray-600/50 rounded-lg text-white placeholder-gray-400 focus:ring-2 focus:ring-blue-500/50 focus:border-transparent transition-all duration-300 text-sm @error('password') border-red-500 @enderror"
                        required autocomplete="current-password" />
                    @error('password')
                        <p class="mt-1 text-sm text-red-500">{{ $message }}</p>
                    @enderror
                </div>

                {{-- Forgot Password --}}
                <div class="text-right">
                    <a href="#" class="text-sm text-blue-400 hover:text-blue-300 transition">Forgot password?</a>
                </div>

                {{-- Submit --}}
                <button type="submit"
                    class="w-full py-3 px-6 bg-blue-500 hover:bg-blue-600 text-white font-semibold rounded-lg transition-all duration-300 shadow-lg shadow-blue-500/30 hover:scale-[1.02] active:scale-[0.98]"
                    wire:loading.attr="disabled">
                    <span wire:loading.remove>Sign in</span>
                    <span wire:loading>Signing in...</span>
                </button>
            </form>
        </div>

        {{-- Image Section --}}
        <div class="hidden lg:block w-1/2 relative rounded-r-3xl overflow-hidden">
            <div class="absolute inset-0 bg-gradient-to-br from-gray-700/50 to-gray-900/50"></div>
            <img src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800"
                 alt="Background" class="w-full h-full object-cover">
        </div>
    </div>
</div>

Penjelasan singkat:

ElemenFungsi
bg-gradient-to-brBikin gradasi background elegan dari abu tua ke hitam.
backdrop-blur-lgEfek blur transparan kayak kaca (glassmorphism).
wire:model="data.email"Data binding Livewire (langsung ke $data['email']).
wire:submit="authenticate"Jalankan fungsi authenticate() di PHP saat form dikirim.
Bagian kanan (gambar)Estetika aja, biar login page nggak polos.

5. Hubungkan ke Filament Panel

Sekarang, biar Filament tahu kalau login-nya pakai halaman custom ini,
buka file:

app/Providers/Filament/AdminPanelPanelProvider.php

Ubah / tambahkan baris berikut:

use App\Livewire\AdminLogin;

class AdminPanelPanelProvider extends PanelProvider
{
    public function panel(Panel $panel): Panel
    {
        return $panel
            ->default()
            ->id('admin-panel')
            ->path('admin') // path dashboard kamu
            ->login(AdminLogin::class) // pakai login custom
    }
}
Jika muncul error seperti di atas, maka jalankan perintah berikut:
npm i 
npm run build

Harusnya halaman login custom kamu langsung muncul — bukan login Filament bawaan lagi!