feat: Implement user settings management with dynamic theme customization, including UX style, primary color, font, and UI preferences, enhancing user experience across the application.
This commit is contained in:
parent
0086ff50d2
commit
56ffcb5508
@ -5,14 +5,17 @@
|
||||
use App\Enums\UxStyle;
|
||||
use App\Filament\Support\CheerfulNotification;
|
||||
use Filament\Auth\Pages\EditProfile;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Schemas\Components\Grid;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Components\Utilities\Get;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Enums\Width;
|
||||
use Filament\Support\Exceptions\Halt;
|
||||
use Filament\Support\Facades\FilamentView;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
@ -24,22 +27,82 @@ public function getMaxContentWidth(): Width
|
||||
return Width::FourExtraLarge;
|
||||
}
|
||||
|
||||
protected function fillForm(): void
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$user->load('settings');
|
||||
|
||||
$data = $user->toArray();
|
||||
$data['settings'] = $user->settings?->toArray() ?? [];
|
||||
|
||||
$this->form->fill($data);
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
try {
|
||||
$this->beginDatabaseTransaction();
|
||||
|
||||
$this->callHook('beforeValidate');
|
||||
|
||||
$data = $this->form->getState();
|
||||
|
||||
$this->callHook('afterValidate');
|
||||
|
||||
$data = $this->mutateFormDataBeforeSave($data);
|
||||
|
||||
$this->callHook('beforeSave');
|
||||
|
||||
$this->handleRecordUpdate($this->getUser(), $data);
|
||||
|
||||
$this->callHook('afterSave');
|
||||
} catch (Halt $exception) {
|
||||
$exception->shouldRollbackDatabaseTransaction() ?
|
||||
$this->rollBackDatabaseTransaction() :
|
||||
$this->commitDatabaseTransaction();
|
||||
|
||||
return;
|
||||
} catch (\Throwable $exception) {
|
||||
$this->rollBackDatabaseTransaction();
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
$this->commitDatabaseTransaction();
|
||||
|
||||
if (request()->hasSession() && array_key_exists('password', $data)) {
|
||||
request()->session()->put([
|
||||
'password_hash_'.Filament::getAuthGuard() => $data['password'],
|
||||
]);
|
||||
}
|
||||
|
||||
$this->data['password'] = null;
|
||||
$this->data['passwordConfirmation'] = null;
|
||||
|
||||
$this->getSavedNotification()?->send();
|
||||
|
||||
if ($redirectUrl = $this->getRedirectUrl()) {
|
||||
$this->redirect($redirectUrl, navigate: FilamentView::hasSpaMode($redirectUrl));
|
||||
}
|
||||
}
|
||||
|
||||
public function form(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
Section::make(fn () => CheerfulNotification::getMessage('Detail Profil 👤✨', 'Profil Pengguna'))
|
||||
->description(fn () => CheerfulNotification::getMessage('Yuk, kelola informasi detail profil Anda agar selalu terkini! 👤🚀', 'Kelola informasi profil dasar Anda di bawah ini.'))
|
||||
->icon(fn () => CheerfulNotification::getUxStyle() === UxStyle::CHEERFUL ? 'heroicon-o-user' : null)
|
||||
Section::make(CheerfulNotification::getMessage('Detail Profil 👤✨', 'Profil Pengguna'))
|
||||
->description(CheerfulNotification::getMessage('Yuk, kelola informasi detail profil Anda agar selalu terkini! 👤🚀', 'Kelola informasi profil dasar Anda di bawah ini.'))
|
||||
->icon(CheerfulNotification::getUxStyle() === UxStyle::CHEERFUL ? 'heroicon-o-user' : null)
|
||||
->schema([
|
||||
TextInput::make('name')
|
||||
->label('Nama Lengkap')
|
||||
->placeholder('Masukkan nama lengkap Anda')
|
||||
->required()
|
||||
->maxLength(100),
|
||||
|
||||
Grid::make(2)
|
||||
->schema([
|
||||
TextInput::make('name')
|
||||
->label('Nama Lengkap')
|
||||
->placeholder('Masukkan nama lengkap Anda')
|
||||
->required()
|
||||
->maxLength(100)
|
||||
->columnSpanFull(),
|
||||
|
||||
TextInput::make('email')
|
||||
->label('Alamat Surel')
|
||||
->placeholder('contoh@email.com')
|
||||
@ -61,19 +124,82 @@ public function form(Schema $schema): Schema
|
||||
->regex('/^[a-zA-Z0-9_.-]+$/')
|
||||
->unique(ignoreRecord: true)
|
||||
->prefixIcon('heroicon-o-at-symbol'),
|
||||
|
||||
Select::make('ux_style')
|
||||
->label('Gaya Bahasa Aplikasi')
|
||||
->options(UxStyle::class)
|
||||
->native(false)
|
||||
->required()
|
||||
->prefixIcon('heroicon-o-chat-bubble-bottom-center-text'),
|
||||
]),
|
||||
]),
|
||||
|
||||
Section::make(fn () => CheerfulNotification::getMessage('Keamanan Akun 🔐✨', 'Ubah Kata Sandi'))
|
||||
->description(fn () => CheerfulNotification::getMessage('Ingin ganti kata sandi? Pastikan pilih yang kuat ya biar makin aman! 🔐💪', 'Silakan masukkan kata sandi baru untuk memperbarui keamanan akun Anda.'))
|
||||
->icon(fn () => CheerfulNotification::getUxStyle() === UxStyle::CHEERFUL ? 'heroicon-o-lock-closed' : null)
|
||||
Section::make(CheerfulNotification::getMessage('Pengaturan Tampilan & UX 🎨✨', 'Preferensi Tampilan'))
|
||||
->description(CheerfulNotification::getMessage('Personalisasi pengalaman aplikasi Anda agar lebih nyaman dan sesuai selera! 🌈🚀', 'Sesuaikan gaya bahasa, warna tema, dan jenis huruf aplikasi Anda.'))
|
||||
->icon(CheerfulNotification::getUxStyle() === UxStyle::CHEERFUL ? 'heroicon-o-swatch' : null)
|
||||
->schema([
|
||||
Grid::make(2)
|
||||
->schema([
|
||||
Select::make('settings.ux_style')
|
||||
->label('Gaya Bahasa')
|
||||
->options(UxStyle::class)
|
||||
->native(false)
|
||||
->required(),
|
||||
|
||||
Select::make('settings.primary_color')
|
||||
->label('Tema Warna')
|
||||
->options([
|
||||
'blue' => '🔵 Biru (Default)',
|
||||
'sky' => '💎 Biru Langit',
|
||||
'cyan' => '🌊 Biru Tosca (Cyan)',
|
||||
'emerald' => '🟢 Emerald (Hijau)',
|
||||
'teal' => '🍃 Teal (Hijau Keunguan)',
|
||||
'lime' => '🍋 Lime (Hijah Muda)',
|
||||
'amber' => '🟡 Amber (Kuning)',
|
||||
'orange' => '🟠 Orange',
|
||||
'rose' => '🔴 Rose (Merah)',
|
||||
'fuchsia' => '🌸 Fuchsia (Pink Cerah)',
|
||||
'violet' => '🟣 Violet (Ungu)',
|
||||
'indigo' => '🌌 Indigo (Ungu Gelap)',
|
||||
])
|
||||
->placeholder('Pilih warna tema')
|
||||
->native(false),
|
||||
|
||||
Select::make('settings.font')
|
||||
->label('Jenis Huruf (Font)')
|
||||
->options([
|
||||
'Inter' => 'Inter (Modern)',
|
||||
'Roboto' => 'Roboto (Clean)',
|
||||
'Poppins' => 'Poppins (Rounder)',
|
||||
'Outfit' => 'Outfit (Premium)',
|
||||
'Montserrat' => 'Montserrat (Classic)',
|
||||
'Lexend' => 'Lexend (Readable)',
|
||||
])
|
||||
->native(false),
|
||||
|
||||
Select::make('settings.content_width')
|
||||
->label('Lebar Konten')
|
||||
->options([
|
||||
'full' => '↔️ Lebar Penuh (Full)',
|
||||
'centered' => '🏢 Terpusat (Centered)',
|
||||
])
|
||||
->native(false),
|
||||
|
||||
Select::make('settings.border_radius')
|
||||
->label('Radius Sudut (Border)')
|
||||
->options([
|
||||
'none' => '📐 Tegas (Sharp)',
|
||||
'md' => '📱 Modern (Default)',
|
||||
'lg' => '🎉 Rounded (Cheerful)',
|
||||
'xl' => '🎈 Extra Round',
|
||||
])
|
||||
->native(false),
|
||||
|
||||
Toggle::make('settings.top_navigation')
|
||||
->label('Navigasi Atas (Top Nav)')
|
||||
->helperText('Pindahkan menu navigasi dari samping ke bagian atas layar.')
|
||||
->onIcon('heroicon-m-window')
|
||||
->offIcon('heroicon-m-arrow-top-right-on-square')
|
||||
->inline(false),
|
||||
]),
|
||||
]),
|
||||
|
||||
Section::make(CheerfulNotification::getMessage('Keamanan Akun 🔐✨', 'Ubah Kata Sandi'))
|
||||
->description(CheerfulNotification::getMessage('Ingin ganti kata sandi? Pastikan pilih yang kuat ya biar makin aman! 🔐💪', 'Silakan masukkan kata sandi baru untuk memperbarui keamanan akun Anda.'))
|
||||
->icon(CheerfulNotification::getUxStyle() === UxStyle::CHEERFUL ? 'heroicon-o-lock-closed' : null)
|
||||
->schema([
|
||||
Grid::make(2)
|
||||
->schema([
|
||||
@ -82,7 +208,6 @@ public function form(Schema $schema): Schema
|
||||
->placeholder('********')
|
||||
->password()
|
||||
->revealable(filament()->arePasswordsRevealable())
|
||||
->required(fn (Get $get) => filled($get('password')) || filled($get('passwordConfirmation')))
|
||||
->rule(Password::default())
|
||||
->showAllValidationMessages()
|
||||
->dehydrated(fn ($state) => filled($state))
|
||||
@ -96,7 +221,6 @@ public function form(Schema $schema): Schema
|
||||
->placeholder('********')
|
||||
->password()
|
||||
->revealable(filament()->arePasswordsRevealable())
|
||||
->required(fn (Get $get) => filled($get('password')) || filled($get('passwordConfirmation')))
|
||||
->dehydrated(false)
|
||||
->prefixIcon('heroicon-o-lock-closed'),
|
||||
]),
|
||||
@ -106,8 +230,17 @@ public function form(Schema $schema): Schema
|
||||
|
||||
protected function handleRecordUpdate(Model $record, array $data): Model
|
||||
{
|
||||
$settingsData = $data['settings'] ?? [];
|
||||
unset($data['settings']);
|
||||
|
||||
$record->update($data);
|
||||
|
||||
if ($record->settings) {
|
||||
$record->settings->update($settingsData);
|
||||
} else {
|
||||
$record->settings()->create($settingsData);
|
||||
}
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
@ -118,8 +251,14 @@ protected function afterSave(): void
|
||||
CheerfulNotification::getMessage('Sip! Detail profil baru Anda sudah tersimpan dengan aman. Terus semangat ya! 💪😊', 'Perubahan pada profil Anda telah berhasil disimpan ke dalam sistem.')
|
||||
)->send();
|
||||
|
||||
if (! filled($this->form->getState()['password'] ?? null)) {
|
||||
$this->redirect($this->getUrl());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (filled($this->form->getState()['password'] ?? null)) {
|
||||
auth()->logout();
|
||||
Filament::auth()->logout();
|
||||
session()->invalidate();
|
||||
session()->regenerateToken();
|
||||
|
||||
|
||||
@ -27,9 +27,9 @@ public static function configure(Schema $schema): Schema
|
||||
Hidden::make('author_id')
|
||||
->default(auth()->id()),
|
||||
|
||||
Section::make(fn () => CheerfulNotification::getMessage('Konten Berita ✍️', 'Konten Berita'))
|
||||
->description(fn () => CheerfulNotification::getMessage('Yuk, tuliskan judul, ringkasan, dan isi lengkap berita Anda biar makin menarik! 🚀😊', 'Silakan tuliskan judul, ringkasan, dan isi lengkap berita Anda.'))
|
||||
->icon(fn () => CheerfulNotification::getUxStyle() === UxStyle::CHEERFUL ? 'heroicon-o-pencil-square' : null)
|
||||
Section::make(CheerfulNotification::getMessage('Konten Berita ✍️', 'Konten Berita'))
|
||||
->description(CheerfulNotification::getMessage('Yuk, tuliskan judul, ringkasan, dan isi lengkap berita Anda biar makin menarik! 🚀😊', 'Silakan tuliskan judul, ringkasan, dan isi lengkap berita Anda.'))
|
||||
->icon(CheerfulNotification::getUxStyle() === UxStyle::CHEERFUL ? 'heroicon-o-pencil-square' : null)
|
||||
->schema([
|
||||
TextInput::make('title')
|
||||
->label('Judul Berita')
|
||||
@ -52,9 +52,9 @@ public static function configure(Schema $schema): Schema
|
||||
->columnSpanFull(),
|
||||
])->columnSpan(2),
|
||||
|
||||
Section::make(fn () => CheerfulNotification::getMessage('Kategori & Publikasi 🏷️', 'Kategori & Publikasi'))
|
||||
->description(fn () => CheerfulNotification::getMessage('Tentukan kategori, tautan media, dan status publikasi biar berita kita gampang dicari! 🏷️✨', 'Tentukan kategori, tautan media, dan status publikasi.'))
|
||||
->icon(fn () => CheerfulNotification::getUxStyle() === UxStyle::CHEERFUL ? 'heroicon-o-tag' : null)
|
||||
Section::make(CheerfulNotification::getMessage('Kategori & Publikasi 🏷️', 'Kategori & Publikasi'))
|
||||
->description(CheerfulNotification::getMessage('Tentukan kategori, tautan media, dan status publikasi biar berita kita gampang dicari! 🏷️✨', 'Tentukan kategori, tautan media, dan status publikasi.'))
|
||||
->icon(CheerfulNotification::getUxStyle() === UxStyle::CHEERFUL ? 'heroicon-o-tag' : null)
|
||||
->schema([
|
||||
TextInput::make('link')
|
||||
->label('Tautan Media (Opsional)')
|
||||
|
||||
@ -21,7 +21,7 @@ public static function make(): Notification
|
||||
public static function getUxStyle(): UxStyle
|
||||
{
|
||||
try {
|
||||
return auth()->user()?->ux_style ?? UxStyle::CHEERFUL;
|
||||
return auth()->user()?->settings?->ux_style ?? UxStyle::CHEERFUL;
|
||||
} catch (\Throwable $e) {
|
||||
return UxStyle::CHEERFUL;
|
||||
}
|
||||
|
||||
97
app/Http/Middleware/DynamicFilamentTheme.php
Normal file
97
app/Http/Middleware/DynamicFilamentTheme.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Support\Colors\Color;
|
||||
use Filament\Support\Facades\FilamentColor;
|
||||
use Filament\Support\Facades\FilamentView;
|
||||
use Filament\View\PanelsRenderHook;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\HtmlString;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class DynamicFilamentTheme
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Closure(Request): (Response) $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
$user = Filament::auth()->user();
|
||||
|
||||
if ($user && $user->settings) {
|
||||
// Register Colors
|
||||
FilamentColor::register([
|
||||
'primary' => match ($user->settings->primary_color) {
|
||||
'blue' => Color::Blue,
|
||||
'sky' => Color::Sky,
|
||||
'cyan' => Color::Cyan,
|
||||
'emerald' => Color::Emerald,
|
||||
'teal' => Color::Teal,
|
||||
'lime' => Color::Lime,
|
||||
'amber' => Color::Amber,
|
||||
'orange' => Color::Orange,
|
||||
'rose' => Color::Rose,
|
||||
'fuchsia' => Color::Fuchsia,
|
||||
'violet' => Color::Violet,
|
||||
'indigo' => Color::Indigo,
|
||||
default => Color::Blue,
|
||||
},
|
||||
]);
|
||||
|
||||
// Register Top Navigation
|
||||
$panel = Filament::getCurrentOrDefaultPanel();
|
||||
if ($panel && method_exists($panel, 'topNavigation')) {
|
||||
$panel->topNavigation((bool) $user->settings->top_navigation);
|
||||
}
|
||||
|
||||
// Register Font & UI Styles
|
||||
$font = $user->settings->font ?? 'Inter';
|
||||
$radius = match ($user->settings->border_radius ?? 'md') {
|
||||
'none' => '0px',
|
||||
'md' => '0.375rem',
|
||||
'lg' => '0.5rem',
|
||||
'xl' => '0.75rem',
|
||||
'2xl' => '1rem',
|
||||
default => '0.5rem',
|
||||
};
|
||||
$maxWidth = match ($user->settings->content_width ?? 'full') {
|
||||
'centered' => '80rem',
|
||||
default => 'none',
|
||||
};
|
||||
|
||||
FilamentView::registerRenderHook(
|
||||
PanelsRenderHook::HEAD_END,
|
||||
fn () => new HtmlString("
|
||||
<link rel='preconnect' href='https://fonts.googleapis.com'>
|
||||
<link rel='preconnect' href='https://fonts.gstatic.com' crossorigin>
|
||||
<link href='https://fonts.googleapis.com/css2?family={$font}:wght@400;500;600;700&display=swap' rel='stylesheet'>
|
||||
<style>
|
||||
:root {
|
||||
--font-family: '{$font}', sans-serif;
|
||||
--c-border-radius: {$radius};
|
||||
}
|
||||
|
||||
/* Override Filament maximum width if centered */
|
||||
.fi-main-ctn {
|
||||
max-width: {$maxWidth} !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
|
||||
/* Apply border radius to common elements */
|
||||
.fi-section, .fi-btn, .fi-input, .fi-card, .fi-modal-window {
|
||||
border-radius: {$radius} !important;
|
||||
}
|
||||
</style>
|
||||
")
|
||||
);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@ -4,11 +4,12 @@
|
||||
|
||||
use App\Enums\IsActive;
|
||||
use App\Enums\RoleEnum;
|
||||
use App\Enums\UxStyle;
|
||||
use App\Enums\VerificationStatus;
|
||||
use App\Observers\UserObserver;
|
||||
use Filament\Models\Contracts\FilamentUser;
|
||||
use Filament\Panel;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
|
||||
use Illuminate\Database\Eloquent\Attributes\Scope;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@ -20,6 +21,7 @@
|
||||
use Spatie\Permission\Traits\HasRoles;
|
||||
use Swindon\FilamentHashids\Traits\HasHashid;
|
||||
|
||||
#[ObservedBy(UserObserver::class)]
|
||||
class User extends Authenticatable implements FilamentUser, MustVerifyEmail
|
||||
{
|
||||
use HasFactory, HasHashid, HasRoles, Notifiable, SoftDeletes;
|
||||
@ -34,7 +36,6 @@ protected function casts(): array
|
||||
'password' => 'hashed',
|
||||
'is_active' => IsActive::class,
|
||||
'email_verified_at' => 'timestamp',
|
||||
'ux_style' => UxStyle::class,
|
||||
];
|
||||
}
|
||||
|
||||
@ -104,6 +105,11 @@ public function verificationReviews(): HasMany
|
||||
return $this->hasMany(VerificationReview::class, 'reviewer_id');
|
||||
}
|
||||
|
||||
public function settings(): HasOne
|
||||
{
|
||||
return $this->hasOne(UserSetting::class);
|
||||
}
|
||||
|
||||
public function visitors(): HasMany
|
||||
{
|
||||
return $this->hasMany(Visitor::class);
|
||||
|
||||
24
app/Models/UserSetting.php
Normal file
24
app/Models/UserSetting.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\UxStyle;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class UserSetting extends Model
|
||||
{
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'ux_style' => UxStyle::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
52
app/Observers/UserObserver.php
Normal file
52
app/Observers/UserObserver.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Enums\UxStyle;
|
||||
use App\Models\User;
|
||||
|
||||
class UserObserver
|
||||
{
|
||||
/**
|
||||
* Handle the User "created" event.
|
||||
*/
|
||||
public function created(User $user): void
|
||||
{
|
||||
$user->settings()->create([
|
||||
'ux_style' => UxStyle::CHEERFUL,
|
||||
'primary_color' => 'blue',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the User "updated" event.
|
||||
*/
|
||||
public function updated(User $user): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the User "deleted" event.
|
||||
*/
|
||||
public function deleted(User $user): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the User "restored" event.
|
||||
*/
|
||||
public function restored(User $user): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the User "force deleted" event.
|
||||
*/
|
||||
public function forceDeleted(User $user): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@ -8,8 +8,10 @@
|
||||
use App\Filament\Pages\Auth\Profile;
|
||||
use App\Filament\Pages\Auth\Registration;
|
||||
use App\Filament\Pages\Dashboard;
|
||||
use App\Http\Middleware\DynamicFilamentTheme;
|
||||
use App\Settings\GeneralSettings;
|
||||
use BezhanSalleh\FilamentShield\FilamentShieldPlugin;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Http\Middleware\Authenticate;
|
||||
use Filament\Http\Middleware\AuthenticateSession;
|
||||
use Filament\Http\Middleware\DisableBladeIconComponents;
|
||||
@ -54,8 +56,8 @@ public function panel(Panel $panel): Panel
|
||||
->default()
|
||||
->id('dashboard')
|
||||
->path('dashboard')
|
||||
->brandLogo(fn () => Storage::disk('public')->url($generalSettings->site_logo))
|
||||
->favicon(fn () => Storage::disk('public')->url($generalSettings->site_icon))
|
||||
->brandLogo(Storage::disk('public')->url($generalSettings->site_logo))
|
||||
->favicon(Storage::disk('public')->url($generalSettings->site_icon))
|
||||
->login(Login::class)
|
||||
->profile(Profile::class)
|
||||
->registration(Registration::class)
|
||||
@ -99,6 +101,7 @@ public function panel(Panel $panel): Panel
|
||||
DisableBladeIconComponents::class,
|
||||
DispatchServingFilamentEvent::class,
|
||||
FilamentHashidsMiddleware::class,
|
||||
DynamicFilamentTheme::class,
|
||||
])
|
||||
->plugins([
|
||||
FilamentShieldPlugin::make()
|
||||
@ -117,7 +120,7 @@ public function panel(Panel $panel): Panel
|
||||
->navigationLabel('Log')
|
||||
->navigationUrl('/system/logs')
|
||||
->pollingTime(null)
|
||||
->authorize(fn (): bool => auth()->user()->can('View:LogTable')),
|
||||
->authorize(fn (): bool => Filament::auth()->user()?->can('View:LogTable') ?? false),
|
||||
])
|
||||
->authMiddleware([
|
||||
Authenticate::class,
|
||||
@ -135,8 +138,6 @@ public function panel(Panel $panel): Panel
|
||||
])
|
||||
->databaseNotifications()
|
||||
->databaseNotificationsPolling('30s')
|
||||
->brandLogo(fn () => Storage::disk('public')->url($generalSettings?->site_logo))
|
||||
->favicon(fn () => Storage::disk('public')->url($generalSettings?->site_icon))
|
||||
->globalSearch(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('user_settings', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->unique()->constrained()->onDelete('cascade');
|
||||
$table->string('ux_style')->default('cheerful');
|
||||
$table->string('primary_color')->nullable(); // For future extension
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
// Migrate existing data from users table (already has ux_style from earlier migration)
|
||||
$users = DB::table('users')->get();
|
||||
foreach ($users as $user) {
|
||||
DB::table('user_settings')->updateOrInsert(
|
||||
['user_id' => $user->id],
|
||||
[
|
||||
'ux_style' => $user->ux_style ?? 'cheerful',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Drop the old column from users
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('ux_style');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('ux_style')->default('cheerful')->after('email_verified_at');
|
||||
});
|
||||
|
||||
// Revert data
|
||||
$settings = DB::table('user_settings')->get();
|
||||
foreach ($settings as $setting) {
|
||||
DB::table('users')
|
||||
->where('id', $setting->user_id)
|
||||
->update(['ux_style' => $setting->ux_style]);
|
||||
}
|
||||
|
||||
Schema::dropIfExists('user_settings');
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('user_settings', function (Blueprint $table) {
|
||||
$table->string('font')->default('Inter')->after('primary_color');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('user_settings', function (Blueprint $table) {
|
||||
$table->dropColumn('font');
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('user_settings', function (Blueprint $table) {
|
||||
$table->string('content_width')->default('full')->after('font');
|
||||
$table->string('border_radius')->default('lg')->after('content_width');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('user_settings', function (Blueprint $table) {
|
||||
$table->dropColumn(['content_width', 'border_radius']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('user_settings', function (Blueprint $table) {
|
||||
$table->boolean('top_navigation')->default(false)->after('border_radius');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('user_settings', function (Blueprint $table) {
|
||||
$table->dropColumn('top_navigation');
|
||||
});
|
||||
}
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user