Add Menu Tree functionality and integrate Filament Tree plugin: Introduced a new MenuTree page for managing page structures, added PagesMenuWidget for displaying a tree layout of pages, and updated localization files for menu-related actions. Enhanced Page model with tree management capabilities and included necessary CSS and JS files for the Filament Tree plugin.

This commit is contained in:
Ümit Tunç
2025-11-01 16:13:59 -03:00
parent 1d46c9243d
commit ad20ba575a
13 changed files with 342 additions and 2 deletions
@@ -5,6 +5,7 @@ namespace App\Filament\Admin\Resources\Pages;
use App\Filament\Admin\Resources\Pages\Pages\CreatePage;
use App\Filament\Admin\Resources\Pages\Pages\EditPage;
use App\Filament\Admin\Resources\Pages\Pages\ListPages;
use App\Filament\Admin\Resources\Pages\Pages\MenuTree;
use App\Filament\Admin\Resources\Pages\Schemas\PageForm;
use App\Filament\Admin\Resources\Pages\Tables\PagesTable;
use App\Models\Page;
@@ -60,6 +61,7 @@ class PageResource extends Resource
'index' => ListPages::route('/'),
'create' => CreatePage::route('/create'),
'edit' => EditPage::route('/{record}/edit'),
'menu' => MenuTree::route('/menu'),
];
}
@@ -3,6 +3,7 @@
namespace App\Filament\Admin\Resources\Pages\Pages;
use App\Filament\Admin\Resources\Pages\PageResource;
use Filament\Actions\Action;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
@@ -18,6 +19,11 @@ class ListPages extends ListRecords
protected function getHeaderActions(): array
{
return [
Action::make('menu')
->label(__('pages.menu_tree_title'))
->icon('heroicon-o-bars-3')
->url(PageResource::getUrl('menu'))
->color('gray'),
CreateAction::make()
->label(__('pages.create')),
];
@@ -0,0 +1,46 @@
<?php
namespace App\Filament\Admin\Resources\Pages\Pages;
use App\Filament\Admin\Resources\Pages\PageResource;
use App\Filament\Admin\Widgets\PagesMenuWidget;
use Filament\Actions\Action;
use Filament\Resources\Pages\Page;
class MenuTree extends Page
{
protected static string $resource = PageResource::class;
protected string $view = 'filament.admin.pages.menu-tree';
public function getTitle(): string
{
return __('pages.menu_tree_title');
}
protected function getHeaderActions(): array
{
return [
Action::make('refresh')
->label(__('pages.menu_refresh'))
->icon('heroicon-o-arrow-path')
->color('gray')
->action(function () {
$this->dispatch('$refresh');
}),
Action::make('back_to_pages')
->label(__('pages.menu_back_to_pages'))
->icon('heroicon-o-arrow-left')
->url(PageResource::getUrl('index'))
->color('gray'),
];
}
protected function getHeaderWidgets(): array
{
return [
PagesMenuWidget::class,
];
}
}
@@ -0,0 +1,115 @@
<?php
namespace App\Filament\Admin\Widgets;
use App\Filament\Admin\Resources\Pages\PageResource;
use App\Models\Page;
use Illuminate\Database\Eloquent\Builder;
use SolutionForest\FilamentTree\Actions\EditAction;
use SolutionForest\FilamentTree\Actions\ViewAction;
use SolutionForest\FilamentTree\Widgets\Tree as BaseWidget;
class PagesMenuWidget extends BaseWidget
{
protected static string $model = Page::class;
protected static int $maxDepth = 4;
protected ?string $treeTitle = null;
protected bool $enableTreeTitle = true;
public function mount(): void
{
parent::mount();
$this->treeTitle = __('pages.menu_tree_title');
}
protected function getTreeQuery(): Builder
{
return Page::query()
->where('status', 'published')
->where('show_in_menu', true)
->orderBy('sort_order');
}
public function getTreeRecordTitle(?\Illuminate\Database\Eloquent\Model $record = null): string
{
if (!$record) {
return '';
}
$title = $record->title;
// Add child count indicator
$childCount = $record->children()
->where('status', 'published')
->where('show_in_menu', true)
->count();
if ($childCount > 0) {
$title .= " ({$childCount})";
}
return $title;
}
public function getTreeRecordIcon(?\Illuminate\Database\Eloquent\Model $record = null): ?string
{
if (!$record) {
return null;
}
// Root pages get folder icon (parent_id is null)
if ($record->parent_id === null) {
return 'heroicon-o-folder';
}
// Homepage gets special icon
if ($record->is_homepage) {
return 'heroicon-o-home';
}
// Child pages get document icon
return 'heroicon-o-document-text';
}
protected function hasDeleteAction(): bool
{
return false;
}
protected function hasEditAction(): bool
{
return true;
}
protected function hasViewAction(): bool
{
return true;
}
protected function configureEditAction(EditAction $action): EditAction
{
return $action
->icon('heroicon-o-pencil')
->label(__('pages.edit'))
->url(fn (Page $record): string => PageResource::getUrl('edit', ['record' => $record]))
->openUrlInNewTab(false);
}
protected function configureViewAction(ViewAction $action): ViewAction
{
return $action
->icon('heroicon-o-eye')
->label(__('pages.view'))
->url(fn (Page $record): string => $record->url)
->openUrlInNewTab(true);
}
public function getNodeCollapsedState(?\Illuminate\Database\Eloquent\Model $record = null): bool
{
return true;
}
}
+25 -1
View File
@@ -7,10 +7,11 @@ use App\Traits\HasTranslations;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use SolutionForest\FilamentTree\Concern\ModelTree;
class Page extends Model
{
use HasFactory, SoftDeletes, HasTranslations;
use HasFactory, SoftDeletes, HasTranslations, ModelTree;
protected $fillable = [
'title',
@@ -172,4 +173,27 @@ class Page extends Model
];
})->filter(fn($section) => $section['template'] !== null);
}
/**
* ModelTree - Custom column names
*/
public function determineOrderColumnName(): string
{
return 'sort_order';
}
public function determineParentColumnName(): string
{
return 'parent_id';
}
public function determineTitleColumnName(): string
{
return 'title';
}
public static function defaultParentKey()
{
return null; // Mevcut migration nullable olduğu için null kullanıyoruz
}
}