840 lines
51 KiB
PHP
840 lines
51 KiB
PHP
<?php
|
||
|
||
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;
|
||
use Filament\Forms\Components\ColorPicker;
|
||
use Filament\Forms\Components\DatePicker;
|
||
use Filament\Forms\Components\DateTimePicker;
|
||
use Filament\Forms\Components\FileUpload;
|
||
use Filament\Forms\Components\Hidden;
|
||
use Filament\Forms\Components\MarkdownEditor;
|
||
use Filament\Forms\Components\Repeater;
|
||
use Filament\Forms\Components\RichEditor;
|
||
use Filament\Schemas\Components\Section;
|
||
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
|
||
{
|
||
public static function configure(Schema $schema): Schema
|
||
{
|
||
return $schema
|
||
->columns(3)
|
||
->schema([
|
||
// Sol kolon - Ana içerik (2 sütun genişliğinde)
|
||
Section::make(__('pages.form_section_content'))
|
||
->schema([
|
||
TextInput::make('title')
|
||
->label(__('pages.title_field'))
|
||
->required()
|
||
->maxLength(255)
|
||
->live(onBlur: true)
|
||
->afterStateUpdated(function (string $operation, $state, callable $set) {
|
||
if ($operation !== 'create') {
|
||
return;
|
||
}
|
||
$set('slug', \Str::slug($state));
|
||
}),
|
||
|
||
TextInput::make('slug')
|
||
->label(__('pages.slug_field'))
|
||
->required()
|
||
->maxLength(255)
|
||
->unique(ignoreRecord: true)
|
||
->rules(['alpha_dash'])
|
||
->helperText(__('pages.slug_helper_text')),
|
||
|
||
RichEditor::make('content')
|
||
->label(__('pages.content_field'))
|
||
->required()
|
||
->fileAttachmentsDisk('public')
|
||
->fileAttachmentsDirectory('pages')
|
||
->fileAttachmentsVisibility('public')
|
||
->columnSpanFull(),
|
||
|
||
Textarea::make('excerpt')
|
||
->label(__('pages.excerpt_field'))
|
||
->rows(3)
|
||
->maxLength(500)
|
||
->helperText(__('pages.excerpt_helper_text'))
|
||
->columnSpanFull(),
|
||
])
|
||
->columnSpan(2)
|
||
->collapsible(false),
|
||
|
||
// Sağ kolon - Metadata ve öne çıkan görsel (1 sütun genişliğinde)
|
||
Section::make(__('pages.form_section_page_settings'))
|
||
->schema([
|
||
FileUpload::make('featured_image')
|
||
->label(__('pages.featured_image_field'))
|
||
->image()
|
||
->disk('public')
|
||
->directory('pages/featured')
|
||
->visibility('public')
|
||
->imageEditor()
|
||
->imageEditorAspectRatios([
|
||
'16:9',
|
||
'4:3',
|
||
'1:1',
|
||
])
|
||
->helperText(__('pages.featured_image_helper_text'))
|
||
->columnSpanFull(),
|
||
|
||
Select::make('author_id')
|
||
->label(__('pages.author_field'))
|
||
->relationship('author', 'name')
|
||
->default(auth()->id())
|
||
->required()
|
||
->columnSpanFull(),
|
||
|
||
DateTimePicker::make('published_at')
|
||
->label(__('pages.published_at_field'))
|
||
->displayFormat('d.m.Y H:i')
|
||
->helperText(__('pages.published_at_helper_text'))
|
||
->columnSpanFull(),
|
||
|
||
Select::make('status')
|
||
->label(__('pages.status_field'))
|
||
->options([
|
||
'draft' => __('pages.status_draft'),
|
||
'published' => __('pages.status_published'),
|
||
'archived' => __('pages.status_archived'),
|
||
])
|
||
->default('draft')
|
||
->required()
|
||
->columnSpanFull(),
|
||
|
||
Select::make('parent_id')
|
||
->label(__('pages.parent_field'))
|
||
->relationship('parent', 'title')
|
||
->searchable()
|
||
->preload()
|
||
->helperText(__('pages.parent_helper_text'))
|
||
->columnSpanFull(),
|
||
|
||
Select::make('template')
|
||
->label(__('pages.template_field'))
|
||
->options([
|
||
'default' => __('pages.template_default'),
|
||
'landing' => __('pages.template_landing'),
|
||
'blog' => __('pages.template_blog'),
|
||
'contact' => __('pages.template_contact'),
|
||
])
|
||
->default('default')
|
||
->columnSpanFull(),
|
||
|
||
TextInput::make('sort_order')
|
||
->label(__('pages.sort_order_field'))
|
||
->numeric()
|
||
->default(0)
|
||
->helperText(__('pages.sort_order_helper_text'))
|
||
->columnSpanFull(),
|
||
|
||
Checkbox::make('is_homepage')
|
||
->label(__('pages.is_homepage_field'))
|
||
->helperText(__('pages.is_homepage_helper_text'))
|
||
->columnSpanFull(),
|
||
|
||
Checkbox::make('show_in_menu')
|
||
->label(__('pages.show_in_menu_field'))
|
||
->default(true)
|
||
->helperText(__('pages.show_in_menu_helper_text'))
|
||
->columnSpanFull(),
|
||
])
|
||
->columnSpan(1)
|
||
->collapsible(false),
|
||
|
||
// Alt kısım - SEO ayarları (tam genişlik)
|
||
Section::make(__('pages.form_section_seo_settings'))
|
||
->schema([
|
||
TextInput::make('meta_title')
|
||
->label(__('pages.meta_title_field'))
|
||
->maxLength(60)
|
||
->helperText(__('pages.meta_title_helper_text')),
|
||
|
||
Textarea::make('meta_description')
|
||
->label(__('pages.meta_description_field'))
|
||
->rows(3)
|
||
->maxLength(160)
|
||
->helperText(__('pages.meta_description_helper_text'))
|
||
->columnSpanFull(),
|
||
])
|
||
->columns(2)
|
||
->columnSpanFull()
|
||
->collapsible(true)
|
||
->collapsed(true),
|
||
|
||
// Sayfa Bölümleri (Section Builder)
|
||
Section::make(__('pages.form_section_sections'))
|
||
->schema([
|
||
Repeater::make('sections')
|
||
->label(__('pages.sections_field'))
|
||
->helperText(__('pages.sections_helper_text'))
|
||
->schema([
|
||
Select::make('type')
|
||
->label(__('pages.section_type'))
|
||
->helperText(__('pages.section_type_helper'))
|
||
->required()
|
||
->options([
|
||
'hero' => __('pages.section_type_hero'),
|
||
'features' => __('pages.section_type_features'),
|
||
'stats' => __('pages.section_type_stats'),
|
||
'cta' => __('pages.section_type_cta'),
|
||
'content' => __('pages.section_type_content'),
|
||
'gallery' => __('pages.section_type_gallery'),
|
||
'testimonials' => __('pages.section_type_testimonials'),
|
||
'team' => __('pages.section_type_team'),
|
||
'pricing' => __('pages.section_type_pricing'),
|
||
'faq' => __('pages.section_type_faq'),
|
||
'contact' => __('pages.section_type_contact'),
|
||
'custom' => __('pages.section_type_custom'),
|
||
])
|
||
->live()
|
||
->columnSpanFull(),
|
||
|
||
Repeater::make('data')
|
||
->label(__('pages.section_data'))
|
||
->helperText(__('pages.section_data_helper'))
|
||
->schema([
|
||
TextInput::make('key')
|
||
->label(__('pages.section_data_key'))
|
||
->helperText(__('pages.section_data_key_helper'))
|
||
->required()
|
||
->placeholder('title, subtitle, image, button_text, ...')
|
||
->columnSpan(1),
|
||
|
||
Select::make('type')
|
||
->label(__('pages.section_data_value_type'))
|
||
->required()
|
||
->options([
|
||
'text' => __('pages.value_type_text'),
|
||
'textarea' => __('pages.value_type_textarea'),
|
||
'html' => __('pages.value_type_html'),
|
||
'markdown' => __('pages.value_type_markdown'),
|
||
'richtext' => __('pages.value_type_richtext'),
|
||
'image' => __('pages.value_type_image'),
|
||
'file' => __('pages.value_type_file'),
|
||
'url' => __('pages.value_type_url'),
|
||
'email' => __('pages.value_type_email'),
|
||
'phone' => __('pages.value_type_phone'),
|
||
'number' => __('pages.value_type_number'),
|
||
'boolean' => __('pages.value_type_boolean'),
|
||
'color' => __('pages.value_type_color'),
|
||
'date' => __('pages.value_type_date'),
|
||
'datetime' => __('pages.value_type_datetime'),
|
||
'array' => __('pages.value_type_array'),
|
||
'json' => __('pages.value_type_json'),
|
||
])
|
||
->live()
|
||
->columnSpan(1),
|
||
|
||
// Single Hidden Field to store the actual value
|
||
Hidden::make('value'),
|
||
|
||
// Text Input (displays and updates 'value' field)
|
||
TextInput::make('_value_text')
|
||
->label(__('pages.section_data_value'))
|
||
->visible(fn (Get $get) => in_array($get('type'), ['text', 'url', 'email', 'phone', 'number']))
|
||
->numeric(fn (Get $get) => $get('type') === 'number')
|
||
->placeholder('Değer girin...')
|
||
->live(onBlur: true)
|
||
->afterStateUpdated(fn ($state, callable $set) => $set('value', $state))
|
||
->afterStateHydrated(function ($component, $state, Get $get) {
|
||
// Only hydrate if this field type is active
|
||
if (!in_array($get('type'), ['text', 'url', 'email', 'phone', 'number'])) {
|
||
return;
|
||
}
|
||
$value = $get('value');
|
||
if ($value !== null) {
|
||
$component->state($value);
|
||
}
|
||
})
|
||
->dehydrated(false)
|
||
->columnSpanFull(),
|
||
|
||
// Textarea (displays and updates 'value' field)
|
||
Textarea::make('_value_textarea')
|
||
->label(__('pages.section_data_value'))
|
||
->visible(fn (Get $get) => $get('type') === 'textarea')
|
||
->rows(4)
|
||
->placeholder('Metin girin...')
|
||
->live(onBlur: true)
|
||
->afterStateUpdated(fn ($state, callable $set) => $set('value', $state))
|
||
->afterStateHydrated(function ($component, $state, Get $get) {
|
||
// Only hydrate if this field type is active
|
||
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)
|
||
->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)
|
||
->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;
|
||
}
|
||
$value = $get('value');
|
||
if ($value !== null) {
|
||
$component->state($value);
|
||
}
|
||
})
|
||
->dehydrated(false)
|
||
->columnSpanFull(),
|
||
|
||
// Rich Text Editor (displays and updates 'value' field)
|
||
RichEditor::make('_value_richtext')
|
||
->label(__('pages.section_data_value'))
|
||
->visible(fn (Get $get) => $get('type') === 'richtext')
|
||
->fileAttachmentsDisk('public')
|
||
->fileAttachmentsDirectory('pages/sections')
|
||
->toolbarButtons([
|
||
'bold',
|
||
'italic',
|
||
'link',
|
||
'bulletList',
|
||
'orderedList',
|
||
])
|
||
->live(onBlur: true)
|
||
->afterStateUpdated(fn ($state, callable $set) => $set('value', $state))
|
||
->afterStateHydrated(function ($component, $state, Get $get) {
|
||
// Only hydrate if this field type is richtext
|
||
if ($get('type') !== 'richtext') {
|
||
return;
|
||
}
|
||
|
||
$value = $get('value');
|
||
|
||
// Skip if null or empty
|
||
if ($value === null || $value === '' || $value === []) {
|
||
return;
|
||
}
|
||
|
||
// Only set if it's valid TipTap JSON format
|
||
if (is_array($value) && isset($value['type']) && $value['type'] === 'doc') {
|
||
$component->state($value);
|
||
} elseif (is_string($value)) {
|
||
try {
|
||
$decoded = json_decode($value, true);
|
||
if (is_array($decoded) && isset($decoded['type']) && $decoded['type'] === 'doc') {
|
||
$component->state($decoded);
|
||
}
|
||
} catch (\Exception $e) {
|
||
// Invalid JSON, skip
|
||
}
|
||
}
|
||
})
|
||
->dehydrated(false)
|
||
->columnSpanFull(),
|
||
|
||
// Markdown Editor (displays and updates 'value' field)
|
||
MarkdownEditor::make('_value_markdown')
|
||
->label(__('pages.section_data_value'))
|
||
->visible(fn (Get $get) => $get('type') === 'markdown')
|
||
->live(onBlur: true)
|
||
->afterStateUpdated(fn ($state, callable $set) => $set('value', $state))
|
||
->afterStateHydrated(function ($component, $state, Get $get) {
|
||
if ($get('type') !== 'markdown') return;
|
||
$value = $get('value');
|
||
if ($value !== null && is_string($value)) {
|
||
$component->state($value);
|
||
}
|
||
})
|
||
->dehydrated(false)
|
||
->columnSpanFull(),
|
||
|
||
// Image Upload (displays and updates 'value' field)
|
||
FileUpload::make('_value_image')
|
||
->label(__('pages.section_data_value'))
|
||
->visible(fn (Get $get) => $get('type') === 'image')
|
||
->image()
|
||
->disk('public')
|
||
->directory('pages/sections')
|
||
->imageEditor()
|
||
->live()
|
||
->afterStateUpdated(fn ($state, callable $set) => $set('value', $state))
|
||
->afterStateHydrated(function ($component, $state, Get $get) {
|
||
if ($get('type') !== 'image') return;
|
||
$value = $get('value');
|
||
if ($value !== null && is_string($value)) {
|
||
$component->state($value);
|
||
}
|
||
})
|
||
->dehydrated(false)
|
||
->columnSpanFull(),
|
||
|
||
// File Upload (displays and updates 'value' field)
|
||
FileUpload::make('_value_file')
|
||
->label(__('pages.section_data_value'))
|
||
->visible(fn (Get $get) => $get('type') === 'file')
|
||
->disk('public')
|
||
->directory('pages/sections')
|
||
->live()
|
||
->afterStateUpdated(fn ($state, callable $set) => $set('value', $state))
|
||
->afterStateHydrated(function ($component, $state, Get $get) {
|
||
if ($get('type') !== 'file') return;
|
||
$value = $get('value');
|
||
if ($value !== null && is_string($value)) {
|
||
$component->state($value);
|
||
}
|
||
})
|
||
->dehydrated(false)
|
||
->columnSpanFull(),
|
||
|
||
// Boolean Toggle (displays and updates 'value' field)
|
||
Toggle::make('_value_boolean')
|
||
->label(__('pages.section_data_value'))
|
||
->visible(fn (Get $get) => $get('type') === 'boolean')
|
||
->live()
|
||
->afterStateUpdated(fn ($state, callable $set) => $set('value', $state))
|
||
->afterStateHydrated(function ($component, $state, Get $get) {
|
||
if ($get('type') !== 'boolean') return;
|
||
$value = $get('value');
|
||
if ($value !== null && is_bool($value)) {
|
||
$component->state($value);
|
||
}
|
||
})
|
||
->dehydrated(false)
|
||
->columnSpanFull(),
|
||
|
||
// Color Picker (displays and updates 'value' field)
|
||
ColorPicker::make('_value_color')
|
||
->label(__('pages.section_data_value'))
|
||
->visible(fn (Get $get) => $get('type') === 'color')
|
||
->live(onBlur: true)
|
||
->afterStateUpdated(fn ($state, callable $set) => $set('value', $state))
|
||
->afterStateHydrated(function ($component, $state, Get $get) {
|
||
if ($get('type') !== 'color') return;
|
||
$value = $get('value');
|
||
if ($value !== null && is_string($value)) {
|
||
$component->state($value);
|
||
}
|
||
})
|
||
->dehydrated(false)
|
||
->columnSpanFull(),
|
||
|
||
// Date Picker (displays and updates 'value' field)
|
||
DatePicker::make('_value_date')
|
||
->label(__('pages.section_data_value'))
|
||
->visible(fn (Get $get) => $get('type') === 'date')
|
||
->displayFormat('d/m/Y')
|
||
->live()
|
||
->afterStateUpdated(fn ($state, callable $set) => $set('value', $state))
|
||
->afterStateHydrated(function ($component, $state, Get $get) {
|
||
if ($get('type') !== 'date') return;
|
||
$value = $get('value');
|
||
if ($value !== null && is_string($value)) {
|
||
$component->state($value);
|
||
}
|
||
})
|
||
->dehydrated(false)
|
||
->columnSpanFull(),
|
||
|
||
// DateTime Picker (displays and updates 'value' field)
|
||
DateTimePicker::make('_value_datetime')
|
||
->label(__('pages.section_data_value'))
|
||
->visible(fn (Get $get) => $get('type') === 'datetime')
|
||
->displayFormat('d/m/Y H:i')
|
||
->seconds(false)
|
||
->live()
|
||
->afterStateUpdated(fn ($state, callable $set) => $set('value', $state))
|
||
->afterStateHydrated(function ($component, $state, Get $get) {
|
||
if ($get('type') !== 'datetime') return;
|
||
$value = $get('value');
|
||
if ($value !== null && is_string($value)) {
|
||
$component->state($value);
|
||
}
|
||
})
|
||
->dehydrated(false)
|
||
->columnSpanFull(),
|
||
|
||
// Array Repeater (displays and updates 'value' field)
|
||
Repeater::make('_value_array')
|
||
->label(__('pages.section_data_value'))
|
||
->visible(fn (Get $get) => $get('type') === 'array')
|
||
->simple(
|
||
TextInput::make('item')
|
||
->label('Öğe')
|
||
->required()
|
||
)
|
||
->live(onBlur: true)
|
||
->afterStateUpdated(fn ($state, callable $set) => $set('value', $state))
|
||
->afterStateHydrated(function ($component, $state, Get $get) {
|
||
if ($get('type') !== 'array') return;
|
||
$value = $get('value');
|
||
if ($value !== null && is_array($value)) {
|
||
$component->state($value);
|
||
}
|
||
})
|
||
->dehydrated(false)
|
||
->columnSpanFull(),
|
||
])
|
||
->columns(2)
|
||
->defaultItems(0)
|
||
->addActionLabel(__('pages.section_data_add_field'))
|
||
->reorderable()
|
||
->collapsible()
|
||
->itemLabel(fn (array $state): ?string => isset($state['key']) ? $state['key'] . ' (' . ($state['type'] ?? 'text') . ')' : null)
|
||
->columnSpanFull(),
|
||
])
|
||
->defaultItems(0)
|
||
->addActionLabel(__('pages.section_add'))
|
||
->reorderable()
|
||
->collapsible()
|
||
->itemLabel(fn (array $state): ?string => isset($state['type']) ? __('pages.section_type_' . $state['type']) : 'Bölüm')
|
||
->columnSpanFull(),
|
||
])
|
||
->columnSpanFull()
|
||
->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(function ($state, Set $set, Get $get) {
|
||
// Template seçildiğinde default verileri doldur
|
||
if (!$state) {
|
||
return;
|
||
}
|
||
|
||
$template = HeaderTemplate::find($state);
|
||
if (!$template) {
|
||
return;
|
||
}
|
||
|
||
// default_data'yı güvenli bir şekilde al
|
||
$defaultData = $template->default_data;
|
||
if (is_string($defaultData)) {
|
||
$defaultData = json_decode($defaultData, true) ?? [];
|
||
}
|
||
if (!is_array($defaultData) || empty($defaultData)) {
|
||
return;
|
||
}
|
||
|
||
// Mevcut header_data'yı al
|
||
$existingData = $get('header_data') ?? [];
|
||
|
||
// Nested array'leri dot notation'a çeviren recursive fonksiyon
|
||
$flattenDefaultData = function ($array, $prefix = '') use (&$flattenDefaultData) {
|
||
$result = [];
|
||
foreach ($array as $key => $value) {
|
||
$newKey = $prefix ? "{$prefix}.{$key}" : $key;
|
||
if (is_array($value)) {
|
||
$result = array_merge($result, $flattenDefaultData($value, $newKey));
|
||
} else {
|
||
$result[$newKey] = $value;
|
||
}
|
||
}
|
||
return $result;
|
||
};
|
||
|
||
// Default data'yı düzleştir
|
||
$flattenedDefaults = $flattenDefaultData($defaultData);
|
||
|
||
// Her default veri için boş olan alanları doldur
|
||
foreach ($flattenedDefaults as $key => $defaultValue) {
|
||
// Dot notation ile nested değeri kontrol et
|
||
$keys = explode('.', $key);
|
||
$currentValue = $existingData;
|
||
|
||
// Nested değere eriş
|
||
foreach ($keys as $k) {
|
||
$currentValue = $currentValue[$k] ?? null;
|
||
if ($currentValue === null) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Eğer bu alan boşsa veya yoksa, default değeri kullan
|
||
if ($currentValue === null ||
|
||
$currentValue === '' ||
|
||
(is_array($currentValue) && empty($currentValue))) {
|
||
$set("header_data.{$key}", $defaultValue);
|
||
}
|
||
}
|
||
})
|
||
->columnSpanFull(),
|
||
|
||
Group::make()
|
||
->schema(function (Get $get, $record): array {
|
||
$templateId = $get('header_template_id');
|
||
if (!$templateId) {
|
||
return [];
|
||
}
|
||
|
||
$template = HeaderTemplate::find($templateId);
|
||
if (!$template) {
|
||
return [];
|
||
}
|
||
|
||
// Get existing page data
|
||
$existingData = $record?->header_data ?? [];
|
||
$templateDefaults = $template->default_data ?? [];
|
||
|
||
// Pass both existingData and defaultData separately
|
||
// TemplateService will handle empty value fallback
|
||
return TemplateService::generateDynamicFields(
|
||
$template,
|
||
'header_data',
|
||
$existingData,
|
||
$templateDefaults
|
||
);
|
||
})
|
||
->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()
|
||
->afterStateUpdated(function ($state, Set $set, Get $get) {
|
||
// Template seçildiğinde default verileri doldur
|
||
if (!$state) {
|
||
return;
|
||
}
|
||
|
||
$template = SectionTemplate::find($state);
|
||
if (!$template) {
|
||
return;
|
||
}
|
||
|
||
// default_data'yı güvenli bir şekilde al
|
||
$defaultData = $template->default_data;
|
||
if (is_string($defaultData)) {
|
||
$defaultData = json_decode($defaultData, true) ?? [];
|
||
}
|
||
if (!is_array($defaultData) || empty($defaultData)) {
|
||
return;
|
||
}
|
||
|
||
// Mevcut section_data'yı al
|
||
$existingData = $get('section_data') ?? [];
|
||
|
||
// Her default veri için boş olan alanları doldur
|
||
foreach ($defaultData as $key => $defaultValue) {
|
||
// Eğer bu alan boşsa veya yoksa, default değeri kullan
|
||
if (!isset($existingData[$key]) ||
|
||
$existingData[$key] === null ||
|
||
$existingData[$key] === '' ||
|
||
(is_array($existingData[$key]) && empty($existingData[$key]))) {
|
||
$set("section_data.{$key}", $defaultValue);
|
||
}
|
||
}
|
||
})
|
||
->columnSpanFull(),
|
||
|
||
Group::make()
|
||
->schema(function (Get $get, $state): array {
|
||
$templateId = $get('section_template_id');
|
||
if (!$templateId) {
|
||
return [];
|
||
}
|
||
|
||
$template = SectionTemplate::find($templateId);
|
||
if (!$template) {
|
||
return [];
|
||
}
|
||
|
||
// Get existing section data
|
||
$existingData = $get('section_data') ?? [];
|
||
$templateDefaults = $template->default_data ?? [];
|
||
|
||
// Pass both existingData and defaultData separately
|
||
// TemplateService will handle empty value fallback
|
||
return TemplateService::generateDynamicFields(
|
||
$template,
|
||
'section_data',
|
||
$existingData,
|
||
$templateDefaults
|
||
);
|
||
})
|
||
->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(function ($state, Set $set, Get $get) {
|
||
// Template seçildiğinde default verileri doldur
|
||
if (!$state) {
|
||
return;
|
||
}
|
||
|
||
$template = FooterTemplate::find($state);
|
||
if (!$template || empty($template->default_data)) {
|
||
return;
|
||
}
|
||
|
||
// Mevcut footer_data'yı al
|
||
$existingData = $get('footer_data') ?? [];
|
||
|
||
// Her default veri için boş olan alanları doldur
|
||
foreach ($template->default_data as $key => $defaultValue) {
|
||
// Eğer bu alan boşsa veya yoksa, default değeri kullan
|
||
if (!isset($existingData[$key]) ||
|
||
$existingData[$key] === null ||
|
||
$existingData[$key] === '' ||
|
||
(is_array($existingData[$key]) && empty($existingData[$key]))) {
|
||
$set("footer_data.{$key}", $defaultValue);
|
||
}
|
||
}
|
||
})
|
||
->columnSpanFull(),
|
||
|
||
Group::make()
|
||
->schema(function (Get $get, $record): array {
|
||
$templateId = $get('footer_template_id');
|
||
if (!$templateId) {
|
||
return [];
|
||
}
|
||
|
||
$template = FooterTemplate::find($templateId);
|
||
if (!$template) {
|
||
return [];
|
||
}
|
||
|
||
// Get existing page data
|
||
$existingData = $record?->footer_data ?? [];
|
||
$templateDefaults = $template->default_data ?? [];
|
||
|
||
// Pass both existingData and defaultData separately
|
||
// TemplateService will handle empty value fallback
|
||
return TemplateService::generateDynamicFields(
|
||
$template,
|
||
'footer_data',
|
||
$existingData,
|
||
$templateDefaults
|
||
);
|
||
})
|
||
->visible(fn (Get $get): bool => filled($get('footer_template_id')))
|
||
->columnSpanFull(),
|
||
])
|
||
->columnSpanFull()
|
||
->collapsible(true)
|
||
->collapsed(true),
|
||
|
||
// Çeviri Sekmesi
|
||
Section::make('🌍 ' . __('pages.translations_section'))
|
||
->schema([
|
||
TranslationTabs::make([
|
||
'title' => [
|
||
'type' => 'text',
|
||
'label' => __('pages.title_field'),
|
||
'required' => false,
|
||
'maxLength' => 255,
|
||
],
|
||
'content' => [
|
||
'type' => 'richtext',
|
||
'label' => __('pages.content_field'),
|
||
'required' => false,
|
||
],
|
||
'excerpt' => [
|
||
'type' => 'textarea',
|
||
'label' => __('pages.excerpt_field'),
|
||
'required' => false,
|
||
'maxLength' => 500,
|
||
'rows' => 3,
|
||
],
|
||
'meta_title' => [
|
||
'type' => 'text',
|
||
'label' => __('pages.meta_title_field'),
|
||
'required' => false,
|
||
'maxLength' => 60,
|
||
],
|
||
'meta_description' => [
|
||
'type' => 'textarea',
|
||
'label' => __('pages.meta_description_field'),
|
||
'required' => false,
|
||
'maxLength' => 160,
|
||
'rows' => 3,
|
||
],
|
||
]),
|
||
])
|
||
->columnSpanFull()
|
||
->collapsible(true)
|
||
->collapsed(false),
|
||
]);
|
||
}
|
||
}
|