Add dynamic template system: Implemented header, footer, and section templates with corresponding models, migrations, and Filament resources. Enhanced Page model to support dynamic templates and updated localization files for new terms. Added TemplateService for managing template data and rendering. Comprehensive documentation included for usage and best practices.
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\FooterTemplates;
|
||||
|
||||
use App\Filament\Admin\Resources\FooterTemplates\Pages\CreateFooterTemplate;
|
||||
use App\Filament\Admin\Resources\FooterTemplates\Pages\EditFooterTemplate;
|
||||
use App\Filament\Admin\Resources\FooterTemplates\Pages\ListFooterTemplates;
|
||||
use App\Filament\Admin\Resources\FooterTemplates\Pages\ViewFooterTemplate;
|
||||
use App\Filament\Admin\Resources\FooterTemplates\Schemas\FooterTemplateForm;
|
||||
use App\Filament\Admin\Resources\FooterTemplates\Schemas\FooterTemplateInfolist;
|
||||
use App\Filament\Admin\Resources\FooterTemplates\Tables\FooterTemplatesTable;
|
||||
use App\Models\FooterTemplate;
|
||||
use BackedEnum;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
|
||||
class FooterTemplateResource extends Resource
|
||||
{
|
||||
protected static ?string $model = FooterTemplate::class;
|
||||
|
||||
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack;
|
||||
|
||||
public static function getNavigationGroup(): string
|
||||
{
|
||||
return __('footer-templates.navigation_group');
|
||||
}
|
||||
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
return __('footer-templates.navigation_label');
|
||||
}
|
||||
|
||||
public static function getModelLabel(): string
|
||||
{
|
||||
return __('footer-templates.model_label');
|
||||
}
|
||||
|
||||
public static function getPluralModelLabel(): string
|
||||
{
|
||||
return __('footer-templates.plural_model_label');
|
||||
}
|
||||
|
||||
public static function form(Schema $schema): Schema
|
||||
{
|
||||
return FooterTemplateForm::configure($schema);
|
||||
}
|
||||
|
||||
public static function infolist(Schema $schema): Schema
|
||||
{
|
||||
return FooterTemplateInfolist::configure($schema);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return FooterTemplatesTable::configure($table);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListFooterTemplates::route('/'),
|
||||
'create' => CreateFooterTemplate::route('/create'),
|
||||
'view' => ViewFooterTemplate::route('/{record}'),
|
||||
'edit' => EditFooterTemplate::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getRecordRouteBindingEloquentQuery(): Builder
|
||||
{
|
||||
return parent::getRecordRouteBindingEloquentQuery()
|
||||
->withoutGlobalScopes([
|
||||
SoftDeletingScope::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\FooterTemplates\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\FooterTemplates\FooterTemplateResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateFooterTemplate extends CreateRecord
|
||||
{
|
||||
protected static string $resource = FooterTemplateResource::class;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\FooterTemplates\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\FooterTemplates\FooterTemplateResource;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Actions\ForceDeleteAction;
|
||||
use Filament\Actions\RestoreAction;
|
||||
use Filament\Actions\ViewAction;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditFooterTemplate extends EditRecord
|
||||
{
|
||||
protected static string $resource = FooterTemplateResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
ViewAction::make(),
|
||||
DeleteAction::make(),
|
||||
ForceDeleteAction::make(),
|
||||
RestoreAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\FooterTemplates\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\FooterTemplates\FooterTemplateResource;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListFooterTemplates extends ListRecords
|
||||
{
|
||||
protected static string $resource = FooterTemplateResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\FooterTemplates\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\FooterTemplates\FooterTemplateResource;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
|
||||
class ViewFooterTemplate extends ViewRecord
|
||||
{
|
||||
protected static string $resource = FooterTemplateResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
EditAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\FooterTemplates\Schemas;
|
||||
|
||||
use Filament\Forms\Components\CodeEditor;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Schema;
|
||||
|
||||
class FooterTemplateForm
|
||||
{
|
||||
public static function configure(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
Section::make(__('footer-templates.section_general'))
|
||||
->schema([
|
||||
TextInput::make('title')
|
||||
->label(__('footer-templates.field_title'))
|
||||
->required()
|
||||
->maxLength(255)
|
||||
->columnSpanFull(),
|
||||
|
||||
CodeEditor::make('html_content')
|
||||
->label(__('footer-templates.field_html_content'))
|
||||
->required()
|
||||
->lineNumbers()
|
||||
->columnSpanFull()
|
||||
->helperText(__('footer-templates.field_html_content_help')),
|
||||
|
||||
Toggle::make('is_active')
|
||||
->label(__('footer-templates.field_is_active'))
|
||||
->default(true)
|
||||
->inline(false),
|
||||
])
|
||||
->columns(2),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\FooterTemplates\Schemas;
|
||||
|
||||
use Filament\Schemas\Schema;
|
||||
|
||||
class FooterTemplateInfolist
|
||||
{
|
||||
public static function configure(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
//
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\FooterTemplates\Tables;
|
||||
|
||||
use Filament\Actions\BulkActionGroup;
|
||||
use Filament\Actions\DeleteBulkAction;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Actions\ForceDeleteBulkAction;
|
||||
use Filament\Actions\RestoreBulkAction;
|
||||
use Filament\Actions\ViewAction;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Filters\TrashedFilter;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class FooterTemplatesTable
|
||||
{
|
||||
public static function configure(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('title')
|
||||
->label(__('footer-templates.column_title'))
|
||||
->searchable()
|
||||
->sortable(),
|
||||
|
||||
IconColumn::make('is_active')
|
||||
->label(__('footer-templates.column_is_active'))
|
||||
->boolean()
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make('pages_count')
|
||||
->label(__('footer-templates.column_pages_count'))
|
||||
->counts('pages')
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make('created_at')
|
||||
->label(__('footer-templates.column_created_at'))
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
|
||||
TextColumn::make('updated_at')
|
||||
->label(__('footer-templates.column_updated_at'))
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
|
||||
TextColumn::make('deleted_at')
|
||||
->label(__('footer-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(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\HeaderTemplates;
|
||||
|
||||
use App\Filament\Admin\Resources\HeaderTemplates\Pages\CreateHeaderTemplate;
|
||||
use App\Filament\Admin\Resources\HeaderTemplates\Pages\EditHeaderTemplate;
|
||||
use App\Filament\Admin\Resources\HeaderTemplates\Pages\ListHeaderTemplates;
|
||||
use App\Filament\Admin\Resources\HeaderTemplates\Pages\ViewHeaderTemplate;
|
||||
use App\Filament\Admin\Resources\HeaderTemplates\Schemas\HeaderTemplateForm;
|
||||
use App\Filament\Admin\Resources\HeaderTemplates\Schemas\HeaderTemplateInfolist;
|
||||
use App\Filament\Admin\Resources\HeaderTemplates\Tables\HeaderTemplatesTable;
|
||||
use App\Models\HeaderTemplate;
|
||||
use BackedEnum;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
|
||||
class HeaderTemplateResource extends Resource
|
||||
{
|
||||
protected static ?string $model = HeaderTemplate::class;
|
||||
|
||||
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack;
|
||||
|
||||
public static function getNavigationGroup(): string
|
||||
{
|
||||
return __('header-templates.navigation_group');
|
||||
}
|
||||
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
return __('header-templates.navigation_label');
|
||||
}
|
||||
|
||||
public static function getModelLabel(): string
|
||||
{
|
||||
return __('header-templates.model_label');
|
||||
}
|
||||
|
||||
public static function getPluralModelLabel(): string
|
||||
{
|
||||
return __('header-templates.plural_model_label');
|
||||
}
|
||||
|
||||
public static function form(Schema $schema): Schema
|
||||
{
|
||||
return HeaderTemplateForm::configure($schema);
|
||||
}
|
||||
|
||||
public static function infolist(Schema $schema): Schema
|
||||
{
|
||||
return HeaderTemplateInfolist::configure($schema);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return HeaderTemplatesTable::configure($table);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListHeaderTemplates::route('/'),
|
||||
'create' => CreateHeaderTemplate::route('/create'),
|
||||
'view' => ViewHeaderTemplate::route('/{record}'),
|
||||
'edit' => EditHeaderTemplate::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getRecordRouteBindingEloquentQuery(): Builder
|
||||
{
|
||||
return parent::getRecordRouteBindingEloquentQuery()
|
||||
->withoutGlobalScopes([
|
||||
SoftDeletingScope::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\HeaderTemplates\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\HeaderTemplates\HeaderTemplateResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateHeaderTemplate extends CreateRecord
|
||||
{
|
||||
protected static string $resource = HeaderTemplateResource::class;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\HeaderTemplates\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\HeaderTemplates\HeaderTemplateResource;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Actions\ForceDeleteAction;
|
||||
use Filament\Actions\RestoreAction;
|
||||
use Filament\Actions\ViewAction;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditHeaderTemplate extends EditRecord
|
||||
{
|
||||
protected static string $resource = HeaderTemplateResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
ViewAction::make(),
|
||||
DeleteAction::make(),
|
||||
ForceDeleteAction::make(),
|
||||
RestoreAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\HeaderTemplates\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\HeaderTemplates\HeaderTemplateResource;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListHeaderTemplates extends ListRecords
|
||||
{
|
||||
protected static string $resource = HeaderTemplateResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\HeaderTemplates\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\HeaderTemplates\HeaderTemplateResource;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
|
||||
class ViewHeaderTemplate extends ViewRecord
|
||||
{
|
||||
protected static string $resource = HeaderTemplateResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
EditAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\HeaderTemplates\Schemas;
|
||||
|
||||
use Filament\Forms\Components\CodeEditor;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Schema;
|
||||
|
||||
class HeaderTemplateForm
|
||||
{
|
||||
public static function configure(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
Section::make(__('header-templates.section_general'))
|
||||
->schema([
|
||||
TextInput::make('title')
|
||||
->label(__('header-templates.field_title'))
|
||||
->required()
|
||||
->maxLength(255)
|
||||
->columnSpanFull(),
|
||||
|
||||
CodeEditor::make('html_content')
|
||||
->label(__('header-templates.field_html_content'))
|
||||
->required()
|
||||
->lineNumbers()
|
||||
->columnSpanFull()
|
||||
->helperText(__('header-templates.field_html_content_help')),
|
||||
|
||||
Toggle::make('is_active')
|
||||
->label(__('header-templates.field_is_active'))
|
||||
->default(true)
|
||||
->inline(false),
|
||||
])
|
||||
->columns(2),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\HeaderTemplates\Schemas;
|
||||
|
||||
use Filament\Schemas\Schema;
|
||||
|
||||
class HeaderTemplateInfolist
|
||||
{
|
||||
public static function configure(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
//
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\HeaderTemplates\Tables;
|
||||
|
||||
use Filament\Actions\BulkActionGroup;
|
||||
use Filament\Actions\DeleteBulkAction;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Actions\ForceDeleteBulkAction;
|
||||
use Filament\Actions\RestoreBulkAction;
|
||||
use Filament\Actions\ViewAction;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Filters\TrashedFilter;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class HeaderTemplatesTable
|
||||
{
|
||||
public static function configure(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('title')
|
||||
->label(__('header-templates.column_title'))
|
||||
->searchable()
|
||||
->sortable(),
|
||||
|
||||
IconColumn::make('is_active')
|
||||
->label(__('header-templates.column_is_active'))
|
||||
->boolean()
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make('pages_count')
|
||||
->label(__('header-templates.column_pages_count'))
|
||||
->counts('pages')
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make('created_at')
|
||||
->label(__('header-templates.column_created_at'))
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
|
||||
TextColumn::make('updated_at')
|
||||
->label(__('header-templates.column_updated_at'))
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
|
||||
TextColumn::make('deleted_at')
|
||||
->label(__('header-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(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,10 @@
|
||||
namespace App\Filament\Admin\Resources\Pages\Schemas;
|
||||
|
||||
use App\Filament\Admin\Resources\Components\TranslationTabs;
|
||||
use App\Models\HeaderTemplate;
|
||||
use App\Models\SectionTemplate;
|
||||
use App\Models\FooterTemplate;
|
||||
use App\Services\TemplateService;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\CodeEditor;
|
||||
use Filament\Forms\Components\CodeEditor\Enums\Language;
|
||||
@@ -19,7 +23,9 @@ use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Schemas\Components\Group;
|
||||
use Filament\Schemas\Components\Utilities\Get;
|
||||
use Filament\Schemas\Components\Utilities\Set;
|
||||
use Filament\Schemas\Schema;
|
||||
|
||||
class PageForm
|
||||
@@ -527,6 +533,88 @@ class PageForm
|
||||
->collapsible(true)
|
||||
->collapsed(false),
|
||||
|
||||
// Dynamic Template System
|
||||
Section::make(__('pages.form_section_header_template'))
|
||||
->description(__('pages.form_section_header_template_desc'))
|
||||
->schema([
|
||||
Select::make('header_template_id')
|
||||
->label(__('pages.header_template_field'))
|
||||
->options(HeaderTemplate::where('is_active', true)->pluck('title', 'id'))
|
||||
->searchable()
|
||||
->live()
|
||||
->afterStateUpdated(fn (Set $set) => $set('header_data', []))
|
||||
->columnSpanFull(),
|
||||
|
||||
Group::make()
|
||||
->schema(fn (Get $get): array => TemplateService::generateDynamicFields(
|
||||
HeaderTemplate::find($get('header_template_id')),
|
||||
'header_data'
|
||||
))
|
||||
->visible(fn (Get $get): bool => filled($get('header_template_id')))
|
||||
->columnSpanFull(),
|
||||
])
|
||||
->columnSpanFull()
|
||||
->collapsible(true)
|
||||
->collapsed(true),
|
||||
|
||||
Section::make(__('pages.form_section_template_sections'))
|
||||
->description(__('pages.form_section_template_sections_desc'))
|
||||
->schema([
|
||||
Repeater::make('sections_data')
|
||||
->label(__('pages.template_sections_field'))
|
||||
->schema([
|
||||
Select::make('section_template_id')
|
||||
->label(__('pages.section_template_field'))
|
||||
->options(SectionTemplate::where('is_active', true)->pluck('title', 'id'))
|
||||
->searchable()
|
||||
->live()
|
||||
->required()
|
||||
->columnSpanFull(),
|
||||
|
||||
Group::make()
|
||||
->schema(fn (Get $get): array => TemplateService::generateDynamicFields(
|
||||
SectionTemplate::find($get('section_template_id')),
|
||||
'section_data'
|
||||
))
|
||||
->visible(fn (Get $get): bool => filled($get('section_template_id')))
|
||||
->columnSpanFull(),
|
||||
])
|
||||
->defaultItems(0)
|
||||
->addActionLabel(__('pages.add_template_section'))
|
||||
->reorderable()
|
||||
->collapsible()
|
||||
->itemLabel(fn (array $state): ?string =>
|
||||
SectionTemplate::find($state['section_template_id'] ?? null)?->title ?? __('pages.template_section')
|
||||
)
|
||||
->columnSpanFull(),
|
||||
])
|
||||
->columnSpanFull()
|
||||
->collapsible(true)
|
||||
->collapsed(true),
|
||||
|
||||
Section::make(__('pages.form_section_footer_template'))
|
||||
->description(__('pages.form_section_footer_template_desc'))
|
||||
->schema([
|
||||
Select::make('footer_template_id')
|
||||
->label(__('pages.footer_template_field'))
|
||||
->options(FooterTemplate::where('is_active', true)->pluck('title', 'id'))
|
||||
->searchable()
|
||||
->live()
|
||||
->afterStateUpdated(fn (Set $set) => $set('footer_data', []))
|
||||
->columnSpanFull(),
|
||||
|
||||
Group::make()
|
||||
->schema(fn (Get $get): array => TemplateService::generateDynamicFields(
|
||||
FooterTemplate::find($get('footer_template_id')),
|
||||
'footer_data'
|
||||
))
|
||||
->visible(fn (Get $get): bool => filled($get('footer_template_id')))
|
||||
->columnSpanFull(),
|
||||
])
|
||||
->columnSpanFull()
|
||||
->collapsible(true)
|
||||
->collapsed(true),
|
||||
|
||||
// Çeviri Sekmesi
|
||||
Section::make('🌍 ' . __('pages.translations_section'))
|
||||
->schema([
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\SectionTemplates\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\SectionTemplates\SectionTemplateResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateSectionTemplate extends CreateRecord
|
||||
{
|
||||
protected static string $resource = SectionTemplateResource::class;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\SectionTemplates\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\SectionTemplates\SectionTemplateResource;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Actions\ForceDeleteAction;
|
||||
use Filament\Actions\RestoreAction;
|
||||
use Filament\Actions\ViewAction;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditSectionTemplate extends EditRecord
|
||||
{
|
||||
protected static string $resource = SectionTemplateResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
ViewAction::make(),
|
||||
DeleteAction::make(),
|
||||
ForceDeleteAction::make(),
|
||||
RestoreAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\SectionTemplates\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\SectionTemplates\SectionTemplateResource;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListSectionTemplates extends ListRecords
|
||||
{
|
||||
protected static string $resource = SectionTemplateResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\SectionTemplates\Pages;
|
||||
|
||||
use App\Filament\Admin\Resources\SectionTemplates\SectionTemplateResource;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
|
||||
class ViewSectionTemplate extends ViewRecord
|
||||
{
|
||||
protected static string $resource = SectionTemplateResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
EditAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\SectionTemplates\Schemas;
|
||||
|
||||
use Filament\Forms\Components\CodeEditor;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Schema;
|
||||
|
||||
class SectionTemplateForm
|
||||
{
|
||||
public static function configure(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
Section::make(__('section-templates.section_general'))
|
||||
->schema([
|
||||
TextInput::make('title')
|
||||
->label(__('section-templates.field_title'))
|
||||
->required()
|
||||
->maxLength(255)
|
||||
->columnSpanFull(),
|
||||
|
||||
CodeEditor::make('html_content')
|
||||
->label(__('section-templates.field_html_content'))
|
||||
->required()
|
||||
->lineNumbers()
|
||||
->columnSpanFull()
|
||||
->helperText(__('section-templates.field_html_content_help')),
|
||||
|
||||
Toggle::make('is_active')
|
||||
->label(__('section-templates.field_is_active'))
|
||||
->default(true)
|
||||
->inline(false),
|
||||
])
|
||||
->columns(2),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\SectionTemplates\Schemas;
|
||||
|
||||
use Filament\Schemas\Schema;
|
||||
|
||||
class SectionTemplateInfolist
|
||||
{
|
||||
public static function configure(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
//
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\SectionTemplates;
|
||||
|
||||
use App\Filament\Admin\Resources\SectionTemplates\Pages\CreateSectionTemplate;
|
||||
use App\Filament\Admin\Resources\SectionTemplates\Pages\EditSectionTemplate;
|
||||
use App\Filament\Admin\Resources\SectionTemplates\Pages\ListSectionTemplates;
|
||||
use App\Filament\Admin\Resources\SectionTemplates\Pages\ViewSectionTemplate;
|
||||
use App\Filament\Admin\Resources\SectionTemplates\Schemas\SectionTemplateForm;
|
||||
use App\Filament\Admin\Resources\SectionTemplates\Schemas\SectionTemplateInfolist;
|
||||
use App\Filament\Admin\Resources\SectionTemplates\Tables\SectionTemplatesTable;
|
||||
use App\Models\SectionTemplate;
|
||||
use BackedEnum;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
|
||||
class SectionTemplateResource extends Resource
|
||||
{
|
||||
protected static ?string $model = SectionTemplate::class;
|
||||
|
||||
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack;
|
||||
|
||||
public static function getNavigationGroup(): string
|
||||
{
|
||||
return __('section-templates.navigation_group');
|
||||
}
|
||||
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
return __('section-templates.navigation_label');
|
||||
}
|
||||
|
||||
public static function getModelLabel(): string
|
||||
{
|
||||
return __('section-templates.model_label');
|
||||
}
|
||||
|
||||
public static function getPluralModelLabel(): string
|
||||
{
|
||||
return __('section-templates.plural_model_label');
|
||||
}
|
||||
|
||||
public static function form(Schema $schema): Schema
|
||||
{
|
||||
return SectionTemplateForm::configure($schema);
|
||||
}
|
||||
|
||||
public static function infolist(Schema $schema): Schema
|
||||
{
|
||||
return SectionTemplateInfolist::configure($schema);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return SectionTemplatesTable::configure($table);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListSectionTemplates::route('/'),
|
||||
'create' => CreateSectionTemplate::route('/create'),
|
||||
'view' => ViewSectionTemplate::route('/{record}'),
|
||||
'edit' => EditSectionTemplate::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getRecordRouteBindingEloquentQuery(): Builder
|
||||
{
|
||||
return parent::getRecordRouteBindingEloquentQuery()
|
||||
->withoutGlobalScopes([
|
||||
SoftDeletingScope::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Admin\Resources\SectionTemplates\Tables;
|
||||
|
||||
use Filament\Actions\BulkActionGroup;
|
||||
use Filament\Actions\DeleteBulkAction;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Actions\ForceDeleteBulkAction;
|
||||
use Filament\Actions\RestoreBulkAction;
|
||||
use Filament\Actions\ViewAction;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Filters\TrashedFilter;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class SectionTemplatesTable
|
||||
{
|
||||
public static function configure(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('title')
|
||||
->label(__('section-templates.column_title'))
|
||||
->searchable()
|
||||
->sortable(),
|
||||
|
||||
IconColumn::make('is_active')
|
||||
->label(__('section-templates.column_is_active'))
|
||||
->boolean()
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make('created_at')
|
||||
->label(__('section-templates.column_created_at'))
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
|
||||
TextColumn::make('updated_at')
|
||||
->label(__('section-templates.column_updated_at'))
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
|
||||
TextColumn::make('deleted_at')
|
||||
->label(__('section-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(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class FooterTemplate extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'html_content',
|
||||
'is_active',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the pages that use this footer template.
|
||||
*/
|
||||
public function pages(): HasMany
|
||||
{
|
||||
return $this->hasMany(Page::class, 'footer_template_id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class HeaderTemplate extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'html_content',
|
||||
'is_active',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the pages that use this header template.
|
||||
*/
|
||||
public function pages(): HasMany
|
||||
{
|
||||
return $this->hasMany(Page::class, 'header_template_id');
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,12 @@ class Page extends Model
|
||||
'show_in_menu',
|
||||
'sections',
|
||||
'data',
|
||||
// Template System
|
||||
'header_template_id',
|
||||
'header_data',
|
||||
'footer_template_id',
|
||||
'footer_data',
|
||||
'sections_data',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -49,6 +55,10 @@ class Page extends Model
|
||||
'sort_order' => 'integer',
|
||||
'sections' => 'array',
|
||||
'data' => 'array',
|
||||
// Template System
|
||||
'header_data' => 'array',
|
||||
'footer_data' => 'array',
|
||||
'sections_data' => 'array',
|
||||
];
|
||||
|
||||
public function author()
|
||||
@@ -66,6 +76,22 @@ class Page extends Model
|
||||
return $this->hasMany(Page::class, 'parent_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the header template for the page.
|
||||
*/
|
||||
public function headerTemplate()
|
||||
{
|
||||
return $this->belongsTo(HeaderTemplate::class, 'header_template_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the footer template for the page.
|
||||
*/
|
||||
public function footerTemplate()
|
||||
{
|
||||
return $this->belongsTo(FooterTemplate::class, 'footer_template_id');
|
||||
}
|
||||
|
||||
public function getRouteKeyName()
|
||||
{
|
||||
return 'slug';
|
||||
@@ -124,4 +150,25 @@ class Page extends Model
|
||||
|
||||
return $matchingSections[$index] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get templated sections with their templates loaded.
|
||||
* Used for new dynamic template system.
|
||||
*/
|
||||
public function getTemplatedSectionsAttribute()
|
||||
{
|
||||
if (!$this->sections_data || !is_array($this->sections_data)) {
|
||||
return collect([]);
|
||||
}
|
||||
|
||||
return collect($this->sections_data)->map(function ($section) {
|
||||
$templateId = $section['section_template_id'] ?? null;
|
||||
$template = $templateId ? SectionTemplate::find($templateId) : null;
|
||||
|
||||
return [
|
||||
'template' => $template,
|
||||
'data' => $section['section_data'] ?? [],
|
||||
];
|
||||
})->filter(fn($section) => $section['template'] !== null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class SectionTemplate extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'html_content',
|
||||
'is_active',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Section templates are used via JSON in pages.sections_data
|
||||
* No direct relation needed, but we can add a helper method
|
||||
*/
|
||||
public function getPagesUsingThisTemplate()
|
||||
{
|
||||
return Page::whereJsonContains('sections_data', [['section_template_id' => $this->id]])->get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\HeaderTemplate;
|
||||
use App\Models\SectionTemplate;
|
||||
use App\Models\FooterTemplate;
|
||||
use Filament\Forms\Components\{
|
||||
TextInput, Textarea, Select, Checkbox, CheckboxList,
|
||||
Radio, Toggle, ToggleButtons, DateTimePicker, DatePicker,
|
||||
TimePicker, FileUpload, RichEditor, MarkdownEditor,
|
||||
ColorPicker, TagsInput, KeyValue, CodeEditor, Hidden, Slider
|
||||
};
|
||||
|
||||
class TemplateService
|
||||
{
|
||||
/**
|
||||
* Generate dynamic form fields based on template placeholders
|
||||
*/
|
||||
public static function generateDynamicFields($template, string $dataKey): array
|
||||
{
|
||||
if (!$template) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$placeholders = self::parsePlaceholders($template->html_content);
|
||||
$fields = [];
|
||||
|
||||
foreach ($placeholders as $placeholder) {
|
||||
$parts = explode('.', $placeholder);
|
||||
|
||||
if (count($parts) !== 2) {
|
||||
continue; // Skip invalid placeholders
|
||||
}
|
||||
|
||||
[$type, $name] = $parts;
|
||||
|
||||
$label = str($name)->title()->replace('_', ' ')->toString();
|
||||
|
||||
$field = match($type) {
|
||||
// Text Input Variants
|
||||
'text' => TextInput::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->maxLength(255),
|
||||
|
||||
'email' => TextInput::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->email()
|
||||
->maxLength(255),
|
||||
|
||||
'url' => TextInput::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->url()
|
||||
->maxLength(500),
|
||||
|
||||
'tel' => TextInput::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->tel()
|
||||
->maxLength(20),
|
||||
|
||||
'number' => TextInput::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->numeric(),
|
||||
|
||||
'password' => TextInput::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->password()
|
||||
->dehydrated(fn ($state) => filled($state))
|
||||
->maxLength(255),
|
||||
|
||||
// Text Area & Editors
|
||||
'textarea' => Textarea::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->rows(4)
|
||||
->maxLength(5000),
|
||||
|
||||
'richtext' => RichEditor::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->toolbarButtons([
|
||||
'bold', 'italic', 'underline', 'link',
|
||||
'bulletList', 'orderedList', 'h2', 'h3',
|
||||
])
|
||||
->maxLength(50000),
|
||||
|
||||
'markdown' => MarkdownEditor::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->toolbarButtons([
|
||||
'bold', 'italic', 'strike', 'link',
|
||||
'heading', 'bulletList', 'orderedList', 'codeBlock',
|
||||
])
|
||||
->maxLength(50000),
|
||||
|
||||
'code' => CodeEditor::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->lineNumbers()
|
||||
->maxLength(50000),
|
||||
|
||||
// Date & Time
|
||||
'date' => DatePicker::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->native(false),
|
||||
|
||||
'datetime' => DateTimePicker::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->native(false)
|
||||
->seconds(false),
|
||||
|
||||
'time' => TimePicker::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->native(false)
|
||||
->seconds(false),
|
||||
|
||||
// File Uploads
|
||||
'image' => FileUpload::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->image()
|
||||
->imageEditor()
|
||||
->imageEditorAspectRatios([null, '16:9', '4:3', '1:1'])
|
||||
->directory('templates/images')
|
||||
->maxSize(5120),
|
||||
|
||||
'images' => FileUpload::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->image()
|
||||
->multiple()
|
||||
->imageEditor()
|
||||
->directory('templates/images')
|
||||
->maxSize(5120)
|
||||
->maxFiles(10),
|
||||
|
||||
'file' => FileUpload::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->directory('templates/files')
|
||||
->maxSize(10240),
|
||||
|
||||
'files' => FileUpload::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->multiple()
|
||||
->directory('templates/files')
|
||||
->maxSize(10240)
|
||||
->maxFiles(10),
|
||||
|
||||
// Selection & Options
|
||||
'select' => Select::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->options(self::getSelectOptions($name))
|
||||
->searchable(),
|
||||
|
||||
'multiselect' => Select::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->multiple()
|
||||
->options(self::getSelectOptions($name))
|
||||
->searchable(),
|
||||
|
||||
'checkbox' => Checkbox::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->inline(false),
|
||||
|
||||
'checkboxlist' => CheckboxList::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->options(self::getSelectOptions($name))
|
||||
->columns(2),
|
||||
|
||||
'radio' => Radio::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->options(self::getSelectOptions($name))
|
||||
->inline()
|
||||
->inlineLabel(false),
|
||||
|
||||
'toggle' => Toggle::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->inline(false),
|
||||
|
||||
'togglebuttons' => ToggleButtons::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->options(self::getSelectOptions($name))
|
||||
->inline()
|
||||
->grouped(),
|
||||
|
||||
// Color & Visual
|
||||
'color' => ColorPicker::make("{$dataKey}.{$placeholder}")
|
||||
->label($label),
|
||||
|
||||
// Structured Data
|
||||
'tags' => TagsInput::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->separator(','),
|
||||
|
||||
'keyvalue' => KeyValue::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->keyLabel('Key')
|
||||
->valueLabel('Value')
|
||||
->reorderable(),
|
||||
|
||||
// Advanced
|
||||
'slider' => Slider::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->minValue(0)
|
||||
->maxValue(100)
|
||||
->step(1),
|
||||
|
||||
'hidden' => Hidden::make("{$dataKey}.{$placeholder}"),
|
||||
|
||||
// Default
|
||||
default => TextInput::make("{$dataKey}.{$placeholder}")
|
||||
->label($label)
|
||||
->maxLength(255)
|
||||
->helperText("Unknown type: {$type}"),
|
||||
};
|
||||
|
||||
$fields[] = $field;
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse placeholders from HTML content
|
||||
* Format: {type.field_name}
|
||||
*/
|
||||
public static function parsePlaceholders(string $html): array
|
||||
{
|
||||
preg_match_all('/\{([a-z]+\.[a-z_]+)\}/i', $html, $matches);
|
||||
return array_unique($matches[1] ?? []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get select options for a field
|
||||
* Can be extended to load from database or config
|
||||
*/
|
||||
protected static function getSelectOptions(string $fieldName): array
|
||||
{
|
||||
return config("template-options.{$fieldName}", [
|
||||
'option_1' => __('Option 1'),
|
||||
'option_2' => __('Option 2'),
|
||||
'option_3' => __('Option 3'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace placeholders in HTML with actual data
|
||||
*/
|
||||
public static function replacePlaceholders(string $html, array $data): string
|
||||
{
|
||||
foreach ($data as $placeholder => $value) {
|
||||
// Handle different value types
|
||||
if (is_array($value)) {
|
||||
$value = implode(', ', $value);
|
||||
} elseif (is_bool($value)) {
|
||||
$value = $value ? 'true' : 'false';
|
||||
}
|
||||
|
||||
$html = str_replace("{{$placeholder}}", $value ?? '', $html);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use App\Models\Page;
|
||||
use App\Services\TemplateService;
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class PageRenderer extends Component
|
||||
{
|
||||
public Page $page;
|
||||
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct(Page $page)
|
||||
{
|
||||
$this->page = $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*/
|
||||
public function render(): View|Closure|string
|
||||
{
|
||||
return view('components.page-renderer');
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the header template with data
|
||||
*/
|
||||
public function renderHeader(): string
|
||||
{
|
||||
if (!$this->page->headerTemplate) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return TemplateService::replacePlaceholders(
|
||||
$this->page->headerTemplate->html_content,
|
||||
$this->page->header_data ?? []
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render all sections with their templates
|
||||
*/
|
||||
public function renderSections(): string
|
||||
{
|
||||
$output = '';
|
||||
|
||||
foreach ($this->page->templated_sections as $section) {
|
||||
if (!$section['template']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$output .= TemplateService::replacePlaceholders(
|
||||
$section['template']->html_content,
|
||||
$section['data']
|
||||
);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the footer template with data
|
||||
*/
|
||||
public function renderFooter(): string
|
||||
{
|
||||
if (!$this->page->footerTemplate) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return TemplateService::replacePlaceholders(
|
||||
$this->page->footerTemplate->html_content,
|
||||
$this->page->footer_data ?? []
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user