diff --git a/app/Filament/Admin/Resources/MenuTemplates/MenuTemplateResource.php b/app/Filament/Admin/Resources/MenuTemplates/MenuTemplateResource.php new file mode 100644 index 0000000..58196a8 --- /dev/null +++ b/app/Filament/Admin/Resources/MenuTemplates/MenuTemplateResource.php @@ -0,0 +1,86 @@ + ListMenuTemplates::route('/'), + 'create' => CreateMenuTemplate::route('/create'), + 'view' => ViewMenuTemplate::route('/{record}'), + 'edit' => EditMenuTemplate::route('/{record}/edit'), + ]; + } + + public static function getRecordRouteBindingEloquentQuery(): Builder + { + return parent::getRecordRouteBindingEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app/Filament/Admin/Resources/MenuTemplates/Pages/CreateMenuTemplate.php b/app/Filament/Admin/Resources/MenuTemplates/Pages/CreateMenuTemplate.php new file mode 100644 index 0000000..eb32877 --- /dev/null +++ b/app/Filament/Admin/Resources/MenuTemplates/Pages/CreateMenuTemplate.php @@ -0,0 +1,18 @@ +components([ + Section::make(__('menu-templates.section_general')) + ->schema([ + TextInput::make('title') + ->label(__('menu-templates.field_title')) + ->required() + ->maxLength(255) + ->columnSpanFull(), + + CodeEditor::make('html_content') + ->label(__('menu-templates.field_html_content')) + ->required() + ->live(onBlur: true) + ->language(Language::Html) + ->columnSpanFull() + ->helperText(__('menu-templates.field_html_content_help')) + ->extraAttributes([ + 'style' => 'max-height: 400px;overflow-y: auto;', + 'data-field-name' => 'html_content', + ]), + + // Preview Component + View::make('components.menu-template-preview') + ->viewData(function ($record) { + return [ + 'type' => 'menu', + 'fieldName' => 'html_content', + 'recordId' => $record?->id, + ]; + }) + ->columnSpanFull(), + + Toggle::make('is_active') + ->label(__('menu-templates.field_is_active')) + ->default(true) + ->inline(false), + ]) + ->columnSpanFull(), + ]); + } +} diff --git a/app/Filament/Admin/Resources/MenuTemplates/Schemas/MenuTemplateInfolist.php b/app/Filament/Admin/Resources/MenuTemplates/Schemas/MenuTemplateInfolist.php new file mode 100644 index 0000000..7057bf7 --- /dev/null +++ b/app/Filament/Admin/Resources/MenuTemplates/Schemas/MenuTemplateInfolist.php @@ -0,0 +1,17 @@ +components([ + // + ]); + } +} + diff --git a/app/Filament/Admin/Resources/MenuTemplates/Tables/MenuTemplatesTable.php b/app/Filament/Admin/Resources/MenuTemplates/Tables/MenuTemplatesTable.php new file mode 100644 index 0000000..67f903c --- /dev/null +++ b/app/Filament/Admin/Resources/MenuTemplates/Tables/MenuTemplatesTable.php @@ -0,0 +1,65 @@ +columns([ + TextColumn::make('title') + ->label(__('menu-templates.column_title')) + ->searchable() + ->sortable(), + + IconColumn::make('is_active') + ->label(__('menu-templates.column_is_active')) + ->boolean() + ->sortable(), + + TextColumn::make('created_at') + ->label(__('menu-templates.column_created_at')) + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + + TextColumn::make('updated_at') + ->label(__('menu-templates.column_updated_at')) + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + + TextColumn::make('deleted_at') + ->label(__('menu-templates.column_deleted_at')) + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + TrashedFilter::make(), + ]) + ->recordActions([ + ViewAction::make(), + EditAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + ForceDeleteBulkAction::make(), + RestoreBulkAction::make(), + ]), + ]); + } +} diff --git a/app/Http/Controllers/TemplatePreviewController.php b/app/Http/Controllers/TemplatePreviewController.php index 098163a..b10a2e2 100644 --- a/app/Http/Controllers/TemplatePreviewController.php +++ b/app/Http/Controllers/TemplatePreviewController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers; use App\Models\HeaderTemplate; use App\Models\FooterTemplate; use App\Models\SectionTemplate; +use App\Models\MenuTemplate; use App\Services\TemplatePreviewService; use App\Services\TemplateService; use Illuminate\Http\Request; @@ -23,7 +24,7 @@ class TemplatePreviewController extends Controller $type = $request->input('type', 'section'); // header, footer, section // Validate type - if (!in_array($type, ['header', 'footer', 'section'])) { + if (!in_array($type, ['header', 'footer', 'section', 'menu'])) { $type = 'section'; } @@ -70,6 +71,7 @@ class TemplatePreviewController extends Controller 'header' => HeaderTemplate::find($id), 'footer' => FooterTemplate::find($id), 'section' => SectionTemplate::find($id), + 'menu' => MenuTemplate::find($id), default => null, }; } diff --git a/app/Models/MenuTemplate.php b/app/Models/MenuTemplate.php new file mode 100644 index 0000000..673ccf7 --- /dev/null +++ b/app/Models/MenuTemplate.php @@ -0,0 +1,21 @@ + 'boolean', + ]; +} diff --git a/app/Services/MenuService.php b/app/Services/MenuService.php new file mode 100644 index 0000000..c56ee10 --- /dev/null +++ b/app/Services/MenuService.php @@ -0,0 +1,121 @@ + function ($query) { + $query->where('status', 'published') + ->where('show_in_menu', true) + ->orderBy('sort_order', 'asc') + ->orderBy('title', 'asc'); + }]) + ->whereNull('parent_id') + ->where('status', 'published') + ->where('show_in_menu', true) + ->orderBy('sort_order', 'asc') + ->orderBy('title', 'asc') + ->get(); + }); + + // If menu template is provided, use it to wrap the menu + if ($menuTemplate && !empty($menuTemplate['html_content'])) { + $html = $menuTemplate['html_content']; + + // Replace {menu} placeholder with rendered menu structure + $renderedMenu = self::buildMenuStructure($menuItems); + $html = str_replace('{menu}', $renderedMenu, $html); + + return $html; + } + + // Otherwise, return default menu structure + return self::buildMenuStructure($menuItems); + } + + /** + * Build menu structure HTML from menu items + * + * @param \Illuminate\Support\Collection $menuItems + * @return string HTML structure + */ + protected static function buildMenuStructure($menuItems): string + { + if ($menuItems->isEmpty()) { + return ''; + } + + $html = '
'; + + return $html; + } + + /** + * Build submenu structure recursively + * + * @param \Illuminate\Support\Collection $children + * @return string Submenu HTML + */ + protected static function buildSubmenu($children): string + { + $html = ''; + + return $html; + } + + /** + * Clear menu cache + */ + public static function clearCache(): void + { + Cache::forget('menu_items'); + } +} + diff --git a/app/Services/TemplatePreviewService.php b/app/Services/TemplatePreviewService.php index 1f6b301..6634986 100644 --- a/app/Services/TemplatePreviewService.php +++ b/app/Services/TemplatePreviewService.php @@ -214,6 +214,7 @@ class TemplatePreviewService $bodyContent = match($type) { 'header' => $content, 'footer' => $content, + 'menu' => $content, 'section' => '