Implement sticky header actions in edit pages: Added save and cancel actions with localization support to the header of EditBlog, EditCustomer, EditLanguage, EditPage, EditSetting pages. Introduced a new Blade view for sticky actions, enhancing user experience by keeping action buttons accessible during form editing.

This commit is contained in:
Ümit Tunç
2025-10-30 17:09:06 -03:00
parent 8a58e63259
commit 128adb7a9c
14 changed files with 214 additions and 17 deletions
@@ -4,6 +4,7 @@ namespace App\Filament\Admin\Resources\Blogs\Pages;
use App\Filament\Admin\Resources\Blogs\BlogResource; use App\Filament\Admin\Resources\Blogs\BlogResource;
use App\Filament\Admin\Resources\Components\TranslationTabs; use App\Filament\Admin\Resources\Components\TranslationTabs;
use Filament\Actions\Action;
use Filament\Actions\DeleteAction; use Filament\Actions\DeleteAction;
use Filament\Actions\ForceDeleteAction; use Filament\Actions\ForceDeleteAction;
use Filament\Actions\RestoreAction; use Filament\Actions\RestoreAction;
@@ -21,15 +22,34 @@ class EditBlog extends EditRecord
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [
Action::make('save')
->label(__('blog.save'))
->action('save')
->keyBindings(['mod+s'])
->color('primary')
->size('sm'),
Action::make('cancel')
->label(__('blog.cancel'))
->url($this->getResource()::getUrl('index'))
->color('gray')
->size('sm'),
DeleteAction::make() DeleteAction::make()
->label(__('blog.delete')), ->label(__('blog.delete'))
->size('sm'),
RestoreAction::make() RestoreAction::make()
->label(__('blog.restore')), ->label(__('blog.restore'))
->size('sm'),
ForceDeleteAction::make() ForceDeleteAction::make()
->label(__('blog.force_delete')), ->label(__('blog.force_delete'))
->size('sm'),
]; ];
} }
protected function getFormActions(): array
{
return []; // Boş array döndürerek alt taraftaki form action'larını gizliyoruz
}
protected function getRedirectUrl(): string protected function getRedirectUrl(): string
{ {
return $this->getResource()::getUrl('index'); return $this->getResource()::getUrl('index');
@@ -3,6 +3,7 @@
namespace App\Filament\Admin\Resources\Customers\Pages; namespace App\Filament\Admin\Resources\Customers\Pages;
use App\Filament\Admin\Resources\Customers\CustomerResource; use App\Filament\Admin\Resources\Customers\CustomerResource;
use Filament\Actions\Action;
use Filament\Actions\DeleteAction; use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord; use Filament\Resources\Pages\EditRecord;
@@ -13,7 +14,24 @@ class EditCustomer extends EditRecord
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [
DeleteAction::make(), Action::make('save')
->label('Save')
->action('save')
->keyBindings(['mod+s'])
->color('primary')
->size('sm'),
Action::make('cancel')
->label('Cancel')
->url($this->getResource()::getUrl('index'))
->color('gray')
->size('sm'),
DeleteAction::make()
->size('sm'),
]; ];
} }
protected function getFormActions(): array
{
return []; // Boş array döndürerek alt taraftaki form action'larını gizliyoruz
}
} }
@@ -3,6 +3,7 @@
namespace App\Filament\Admin\Resources\Languages\Pages; namespace App\Filament\Admin\Resources\Languages\Pages;
use App\Filament\Admin\Resources\Languages\LanguageResource; use App\Filament\Admin\Resources\Languages\LanguageResource;
use Filament\Actions\Action;
use Filament\Actions\DeleteAction; use Filament\Actions\DeleteAction;
use Filament\Actions\ForceDeleteAction; use Filament\Actions\ForceDeleteAction;
use Filament\Actions\RestoreAction; use Filament\Actions\RestoreAction;
@@ -21,15 +22,34 @@ class EditLanguage extends EditRecord
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [
Action::make('save')
->label(__('language.save'))
->action('save')
->keyBindings(['mod+s'])
->color('primary')
->size('sm'),
Action::make('cancel')
->label(__('language.cancel'))
->url($this->getResource()::getUrl('index'))
->color('gray')
->size('sm'),
DeleteAction::make() DeleteAction::make()
->label(__('language.delete')), ->label(__('language.delete'))
->size('sm'),
RestoreAction::make() RestoreAction::make()
->label(__('language.restore')), ->label(__('language.restore'))
->size('sm'),
ForceDeleteAction::make() ForceDeleteAction::make()
->label(__('language.force_delete')), ->label(__('language.force_delete'))
->size('sm'),
]; ];
} }
protected function getFormActions(): array
{
return []; // Boş array döndürerek alt taraftaki form action'larını gizliyoruz
}
protected function getSavedNotification(): ?Notification protected function getSavedNotification(): ?Notification
{ {
return Notification::make() return Notification::make()
@@ -4,6 +4,7 @@ namespace App\Filament\Admin\Resources\Pages\Pages;
use App\Filament\Admin\Resources\Components\TranslationTabs; use App\Filament\Admin\Resources\Components\TranslationTabs;
use App\Filament\Admin\Resources\Pages\PageResource; use App\Filament\Admin\Resources\Pages\PageResource;
use Filament\Actions\Action;
use Filament\Actions\DeleteAction; use Filament\Actions\DeleteAction;
use Filament\Actions\ForceDeleteAction; use Filament\Actions\ForceDeleteAction;
use Filament\Actions\RestoreAction; use Filament\Actions\RestoreAction;
@@ -21,15 +22,34 @@ class EditPage extends EditRecord
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [
Action::make('save')
->label(__('pages.save'))
->action('save')
->keyBindings(['mod+s'])
->color('primary')
->size('sm'),
Action::make('cancel')
->label(__('pages.cancel'))
->url($this->getResource()::getUrl('index'))
->color('gray')
->size('sm'),
DeleteAction::make() DeleteAction::make()
->label(__('pages.delete')), ->label(__('pages.delete'))
->size('sm'),
ForceDeleteAction::make() ForceDeleteAction::make()
->label(__('pages.force_delete')), ->label(__('pages.force_delete'))
->size('sm'),
RestoreAction::make() RestoreAction::make()
->label(__('pages.restore')), ->label(__('pages.restore'))
->size('sm'),
]; ];
} }
protected function getFormActions(): array
{
return []; // Boş array döndürerek alt taraftaki form action'larını gizliyoruz
}
protected function getRedirectUrl(): string protected function getRedirectUrl(): string
{ {
return $this->getResource()::getUrl('index'); return $this->getResource()::getUrl('index');
@@ -4,6 +4,8 @@ namespace App\Filament\Admin\Resources\Pages\Schemas;
use App\Filament\Admin\Resources\Components\TranslationTabs; use App\Filament\Admin\Resources\Components\TranslationTabs;
use Filament\Forms\Components\Checkbox; use Filament\Forms\Components\Checkbox;
use Filament\Forms\Components\CodeEditor;
use Filament\Forms\Components\CodeEditor\Enums\Language;
use Filament\Forms\Components\ColorPicker; use Filament\Forms\Components\ColorPicker;
use Filament\Forms\Components\DatePicker; use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\DateTimePicker; use Filament\Forms\Components\DateTimePicker;
@@ -261,14 +263,58 @@ class PageForm
// Textarea (displays and updates 'value' field) // Textarea (displays and updates 'value' field)
Textarea::make('_value_textarea') Textarea::make('_value_textarea')
->label(__('pages.section_data_value')) ->label(__('pages.section_data_value'))
->visible(fn (Get $get) => in_array($get('type'), ['textarea', 'json', 'html'])) ->visible(fn (Get $get) => $get('type') === 'textarea')
->rows(fn (Get $get) => $get('type') === 'html' ? 6 : 4) ->rows(4)
->placeholder(fn (Get $get) => $get('type') === 'html' ? '<div>HTML kodu girin...</div>' : 'Metin girin...') ->placeholder('Metin girin...')
->live(onBlur: true) ->live(onBlur: true)
->afterStateUpdated(fn ($state, callable $set) => $set('value', $state)) ->afterStateUpdated(fn ($state, callable $set) => $set('value', $state))
->afterStateHydrated(function ($component, $state, Get $get) { ->afterStateHydrated(function ($component, $state, Get $get) {
// Only hydrate if this field type is active // Only hydrate if this field type is active
if (!in_array($get('type'), ['textarea', 'json', 'html'])) { if ($get('type') !== 'textarea') {
return;
}
$value = $get('value');
if ($value !== null) {
$component->state($value);
}
})
->dehydrated(false)
->columnSpanFull(),
// HTML Code Editor (displays and updates 'value' field)
CodeEditor::make('_value_html')
->label(__('pages.section_data_value'))
->visible(fn (Get $get) => $get('type') === 'html')
->language(Language::Html)
// ->minHeight('300px')
// ->placeholder('<div>HTML kodu girin...</div>')
->live(onBlur: true)
->afterStateUpdated(fn ($state, callable $set) => $set('value', $state))
->afterStateHydrated(function ($component, $state, Get $get) {
// Only hydrate if this field type is html
if ($get('type') !== 'html') {
return;
}
$value = $get('value');
if ($value !== null) {
$component->state($value);
}
})
->dehydrated(false)
->columnSpanFull(),
// JSON Code Editor (displays and updates 'value' field)
CodeEditor::make('_value_json')
->label(__('pages.section_data_value'))
->visible(fn (Get $get) => $get('type') === 'json')
->language(Language::Json)
//->minHeight('300px')
// ->placeholder('{"key": "value"}')
->live(onBlur: true)
->afterStateUpdated(fn ($state, callable $set) => $set('value', $state))
->afterStateHydrated(function ($component, $state, Get $get) {
// Only hydrate if this field type is json
if ($get('type') !== 'json') {
return; return;
} }
$value = $get('value'); $value = $get('value');
@@ -3,6 +3,7 @@
namespace App\Filament\Admin\Resources\Settings\Pages; namespace App\Filament\Admin\Resources\Settings\Pages;
use App\Filament\Admin\Resources\Settings\SettingResource; use App\Filament\Admin\Resources\Settings\SettingResource;
use Filament\Actions\Action;
use Filament\Actions\DeleteAction; use Filament\Actions\DeleteAction;
use Filament\Actions\ForceDeleteAction; use Filament\Actions\ForceDeleteAction;
use Filament\Actions\RestoreAction; use Filament\Actions\RestoreAction;
@@ -20,15 +21,34 @@ class EditSetting extends EditRecord
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [
Action::make('save')
->label(__('settings.save'))
->action('save')
->keyBindings(['mod+s'])
->color('primary')
->size('sm'),
Action::make('cancel')
->label(__('settings.cancel'))
->url($this->getResource()::getUrl('index'))
->color('gray')
->size('sm'),
DeleteAction::make() DeleteAction::make()
->label(__('settings.delete')), ->label(__('settings.delete'))
->size('sm'),
RestoreAction::make() RestoreAction::make()
->label(__('settings.restore')), ->label(__('settings.restore'))
->size('sm'),
ForceDeleteAction::make() ForceDeleteAction::make()
->label(__('settings.force_delete')), ->label(__('settings.force_delete'))
->size('sm'),
]; ];
} }
protected function getFormActions(): array
{
return []; // Boş array döndürerek alt taraftaki form action'larını gizliyoruz
}
protected function getRedirectUrl(): string protected function getRedirectUrl(): string
{ {
return $this->getResource()::getUrl('index'); return $this->getResource()::getUrl('index');
+2
View File
@@ -16,6 +16,8 @@ return [
// Actions // Actions
'create' => 'Create New Blog Post', 'create' => 'Create New Blog Post',
'edit' => 'Edit Blog Post', 'edit' => 'Edit Blog Post',
'save' => 'Save',
'cancel' => 'Cancel',
'delete' => 'Delete Blog Post', 'delete' => 'Delete Blog Post',
'restore' => 'Restore Blog Post', 'restore' => 'Restore Blog Post',
'force_delete' => 'Force Delete', 'force_delete' => 'Force Delete',
+2
View File
@@ -10,6 +10,8 @@ return [
// Actions // Actions
'create' => 'Create Language', 'create' => 'Create Language',
'edit' => 'Edit Language', 'edit' => 'Edit Language',
'save' => 'Save',
'cancel' => 'Cancel',
'delete' => 'Delete Language', 'delete' => 'Delete Language',
'restore' => 'Restore', 'restore' => 'Restore',
'force_delete' => 'Force Delete', 'force_delete' => 'Force Delete',
+2
View File
@@ -16,6 +16,8 @@ return [
// Actions // Actions
'create' => 'Create New Page', 'create' => 'Create New Page',
'edit' => 'Edit Page', 'edit' => 'Edit Page',
'save' => 'Save',
'cancel' => 'Cancel',
'delete' => 'Delete Page', 'delete' => 'Delete Page',
'restore' => 'Restore Page', 'restore' => 'Restore Page',
'force_delete' => 'Force Delete', 'force_delete' => 'Force Delete',
+2
View File
@@ -16,6 +16,8 @@ return [
// Actions // Actions
'create' => 'Yeni Blog Yazısı Oluştur', 'create' => 'Yeni Blog Yazısı Oluştur',
'edit' => 'Blog Yazısını Düzenle', 'edit' => 'Blog Yazısını Düzenle',
'save' => 'Kaydet',
'cancel' => 'İptal',
'delete' => 'Blog Yazısını Sil', 'delete' => 'Blog Yazısını Sil',
'restore' => 'Blog Yazısını Geri Yükle', 'restore' => 'Blog Yazısını Geri Yükle',
'force_delete' => 'Kalıcı Olarak Sil', 'force_delete' => 'Kalıcı Olarak Sil',
+2
View File
@@ -10,6 +10,8 @@ return [
// Actions // Actions
'create' => 'Dil Oluştur', 'create' => 'Dil Oluştur',
'edit' => 'Dil Düzenle', 'edit' => 'Dil Düzenle',
'save' => 'Kaydet',
'cancel' => 'İptal',
'delete' => 'Dil Sil', 'delete' => 'Dil Sil',
'restore' => 'Geri Yükle', 'restore' => 'Geri Yükle',
'force_delete' => 'Kalıcı Olarak Sil', 'force_delete' => 'Kalıcı Olarak Sil',
+2
View File
@@ -16,6 +16,8 @@ return [
// Actions // Actions
'create' => 'Yeni Sayfa Oluştur', 'create' => 'Yeni Sayfa Oluştur',
'edit' => 'Sayfayı Düzenle', 'edit' => 'Sayfayı Düzenle',
'save' => 'Kaydet',
'cancel' => 'İptal',
'delete' => 'Sayfayı Sil', 'delete' => 'Sayfayı Sil',
'restore' => 'Sayfayı Geri Yükle', 'restore' => 'Sayfayı Geri Yükle',
'force_delete' => 'Kalıcı Olarak Sil', 'force_delete' => 'Kalıcı Olarak Sil',
@@ -0,0 +1,41 @@
@php
$hasInlineLabels = $this->hasInlineLabels();
@endphp
<x-filament-panels::page>
<style>
/* Sticky Header with Actions */
.fi-header {
position: sticky;
top: 0;
z-index: 40;
background: white;
border-bottom: 1px solid rgb(229, 231, 235);
padding-bottom: 1rem;
margin-bottom: 1.5rem;
}
.dark .fi-header {
background: rgb(17, 24, 39);
border-bottom-color: rgb(55, 65, 81);
}
/* Action buttons styling */
.fi-header-actions {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
align-items: center;
}
/* Make save button more prominent */
.fi-header-actions button[wire\:click*="save"] {
font-weight: 600;
}
</style>
<form wire:submit="save">
{{ $this->form }}
</form>
</x-filament-panels::page>