diff --git a/.cursorrules b/.cursorrules index 5d7ba98..6c40fa6 100644 --- a/.cursorrules +++ b/.cursorrules @@ -85,6 +85,10 @@ $table DeleteBulkAction::make(), ]), ]); + +// Utilities (Schema içinde kullanım) +use Filament\Schemas\Components\Utilities\Get; // ✅ Doğru - Form state okuma +use Filament\Schemas\Components\Utilities\Set; // ✅ Doğru - Form state yazma ``` #### **Form & Schema Components (DOĞRU):** @@ -103,6 +107,7 @@ use Filament\Schemas\Components\Section; // ✅ Doğru use Filament\Schemas\Components\Grid; // ✅ Doğru use Filament\Schemas\Components\Fieldset; // ✅ Doğru use Filament\Schemas\Components\Flex; // ✅ Doğru +use Filament\Schemas\Components\Group; // ✅ Doğru - Schema içinde gruplamak için // Tabs components (ÖNEMLİ: Schemas namespace!) use Filament\Schemas\Components\Tabs; // ✅ Doğru @@ -189,8 +194,11 @@ public static function form(Schema $schema): Schema use Filament\Tables\Actions\EditAction; // ❌ use Filament\Forms\Components\Section; // ❌ use Filament\Forms\Components\Grid; // ❌ +use Filament\Forms\Components\Group; // ❌ Forms namespace altında Group YOK use Filament\Forms\Components\Tabs; // ❌ Forms namespace altında Tabs YOK use Filament\Resources\Components\Tab; // ❌ Bu class hiç YOK +use Filament\Forms\Get; // ❌ Bu namespace YOK +use Filament\Forms\Set; // ❌ Bu namespace YOK // ❌ YANLIŞ - Eski metod adları $table @@ -204,11 +212,17 @@ $table - ✅ **Table Filters:** `Filament\Tables\Filters\` namespace - ✅ **Form Inputs:** `Filament\Forms\Components\` namespace - ✅ **Layout Components:** `Filament\Schemas\Components\` namespace + - Section, Grid, Fieldset, Flex, Group, Tabs, View vb. - ✅ **Tabs (Tüm Kullanımlar):** `Filament\Schemas\Components\Tabs\Tab` (Her yerde bu!) +- ✅ **Utilities:** `Filament\Schemas\Components\Utilities\` namespace + - Get, Set (Schema içinde form state okuma/yazma) - ✅ **Infolist Entries:** `Filament\Infolists\Components\` namespace - ✅ **Table Methods:** `->recordActions()` ve `->toolbarActions()` -**Önemli Not:** Filament 4.x'te `Filament\Resources\Components\Tab` diye bir class **YOK**. Tüm tab kullanımları `Filament\Schemas\Components\Tabs\Tab` ile yapılır! +**Kritik Notlar:** +- `Filament\Resources\Components\Tab` diye bir class **YOK**. Tüm tab kullanımları `Filament\Schemas\Components\Tabs\Tab` ile yapılır! +- `Filament\Forms\Components\Group` diye bir class **YOK**. Schema içinde gruplamak için `Filament\Schemas\Components\Group` kullan! +- `Filament\Forms\Get` ve `Filament\Forms\Set` diye class'lar **YOK**. `Filament\Schemas\Components\Utilities\Get` ve `Filament\Schemas\Components\Utilities\Set` kullan! #### **Filament 4.x REPEATER KULLANIM KURALLARI (KRİTİK!):** diff --git a/app/Filament/Admin/Resources/FooterTemplates/Schemas/FooterTemplateForm.php b/app/Filament/Admin/Resources/FooterTemplates/Schemas/FooterTemplateForm.php index 3763544..1e1ba8b 100644 --- a/app/Filament/Admin/Resources/FooterTemplates/Schemas/FooterTemplateForm.php +++ b/app/Filament/Admin/Resources/FooterTemplates/Schemas/FooterTemplateForm.php @@ -2,10 +2,14 @@ namespace App\Filament\Admin\Resources\FooterTemplates\Schemas; +use App\Models\FooterTemplate; +use App\Services\TemplateService; use Filament\Forms\Components\CodeEditor; use Filament\Forms\Components\TextInput; use Filament\Forms\Components\Toggle; +use Filament\Schemas\Components\Group; use Filament\Schemas\Components\Section; +use Filament\Schemas\Components\Utilities\Get; use Filament\Schemas\Components\View; use Filament\Schemas\Schema; use Filament\Forms\Components\CodeEditor\Enums\Language; @@ -51,6 +55,38 @@ class FooterTemplateForm ->inline(false), ]) ->columnSpanFull(), + + // Default Data Section - Dynamic fields based on placeholders + Section::make(__('footer-templates.section_default_data')) + ->description(__('footer-templates.section_default_data_desc')) + ->schema([ + Group::make() + ->schema(function (Get $get, $record): array { + $htmlContent = $get('html_content'); + if (empty($htmlContent)) { + return []; + } + + // Create a temporary template object to parse placeholders + $tempTemplate = new FooterTemplate(); + $tempTemplate->html_content = $htmlContent; + + // Get existing default_data if editing + $existingData = $record ? ($record->default_data ?? []) : []; + + return TemplateService::generateDynamicFields( + $tempTemplate, + 'default_data', + $existingData + ); + }) + ->key('default_data_fields') + ->visible(fn (Get $get): bool => filled($get('html_content'))) + ->columnSpanFull(), + ]) + ->columnSpanFull() + ->collapsible(true) + ->collapsed(false), ]); } } diff --git a/app/Filament/Admin/Resources/HeaderTemplates/Schemas/HeaderTemplateForm.php b/app/Filament/Admin/Resources/HeaderTemplates/Schemas/HeaderTemplateForm.php index 9266bd3..270b650 100644 --- a/app/Filament/Admin/Resources/HeaderTemplates/Schemas/HeaderTemplateForm.php +++ b/app/Filament/Admin/Resources/HeaderTemplates/Schemas/HeaderTemplateForm.php @@ -2,10 +2,14 @@ namespace App\Filament\Admin\Resources\HeaderTemplates\Schemas; +use App\Models\HeaderTemplate; +use App\Services\TemplateService; use Filament\Forms\Components\CodeEditor; use Filament\Forms\Components\TextInput; use Filament\Forms\Components\Toggle; +use Filament\Schemas\Components\Group; use Filament\Schemas\Components\Section; +use Filament\Schemas\Components\Utilities\Get; use Filament\Schemas\Components\View; use Filament\Schemas\Schema; use Filament\Forms\Components\CodeEditor\Enums\Language; @@ -50,6 +54,38 @@ class HeaderTemplateForm ->inline(false), ]) ->columnSpanFull(), + + // Default Data Section - Dynamic fields based on placeholders + Section::make(__('header-templates.section_default_data')) + ->description(__('header-templates.section_default_data_desc')) + ->schema([ + Group::make() + ->schema(function (Get $get, $record): array { + $htmlContent = $get('html_content'); + if (empty($htmlContent)) { + return []; + } + + // Create a temporary template object to parse placeholders + $tempTemplate = new HeaderTemplate(); + $tempTemplate->html_content = $htmlContent; + + // Get existing default_data if editing + $existingData = $record ? ($record->default_data ?? []) : []; + + return TemplateService::generateDynamicFields( + $tempTemplate, + 'default_data', + $existingData + ); + }) + ->key('default_data_fields') + ->visible(fn (Get $get): bool => filled($get('html_content'))) + ->columnSpanFull(), + ]) + ->columnSpanFull() + ->collapsible(true) + ->collapsed(false), ]); } } diff --git a/app/Filament/Admin/Resources/Pages/Schemas/PageForm.php b/app/Filament/Admin/Resources/Pages/Schemas/PageForm.php index e316ad9..53a5fa2 100644 --- a/app/Filament/Admin/Resources/Pages/Schemas/PageForm.php +++ b/app/Filament/Admin/Resources/Pages/Schemas/PageForm.php @@ -542,14 +542,43 @@ class PageForm ->options(HeaderTemplate::where('is_active', true)->pluck('title', 'id')) ->searchable() ->live() - ->afterStateUpdated(fn (Set $set) => $set('header_data', [])) + ->afterStateUpdated(function (Set $set, Get $get, $state, $record) { + $template = HeaderTemplate::find($state); + if ($template && $template->default_data) { + // If page doesn't have header_data or it's empty, load defaults + $currentData = $record ? ($record->header_data ?? []) : ($get('header_data') ?? []); + if (empty($currentData)) { + $set('header_data', $template->default_data); + } + } else { + // If no template selected or no defaults, clear data + if (!$record || empty($record->header_data)) { + $set('header_data', []); + } + } + }) ->columnSpanFull(), Group::make() - ->schema(fn (Get $get): array => TemplateService::generateDynamicFields( - HeaderTemplate::find($get('header_template_id')), - 'header_data' - )) + ->schema(function (Get $get, $record): array { + $template = HeaderTemplate::find($get('header_template_id')); + if (!$template) { + return []; + } + + // Get page's header_data or template's default_data + $pageData = $record ? ($record->header_data ?? []) : ($get('header_data') ?? []); + $templateDefaults = $template->default_data ?? []; + + // Merge: page data overrides template defaults + $mergedData = array_merge($templateDefaults, $pageData); + + return TemplateService::generateDynamicFields( + $template, + 'header_data', + $mergedData + ); + }) ->visible(fn (Get $get): bool => filled($get('header_template_id'))) ->columnSpanFull(), ]) @@ -569,13 +598,38 @@ class PageForm ->searchable() ->live() ->required() + ->afterStateUpdated(function (Set $set, Get $get, $state, $record) { + $template = SectionTemplate::find($state); + if ($template && $template->default_data) { + // If section doesn't have section_data or it's empty, load defaults + $currentData = $get('section_data') ?? []; + if (empty($currentData)) { + $set('section_data', $template->default_data); + } + } + }) ->columnSpanFull(), Group::make() - ->schema(fn (Get $get): array => TemplateService::generateDynamicFields( - SectionTemplate::find($get('section_template_id')), - 'section_data' - )) + ->schema(function (Get $get): array { + $template = SectionTemplate::find($get('section_template_id')); + if (!$template) { + return []; + } + + // Get section's data or template's default_data + $sectionData = $get('section_data') ?? []; + $templateDefaults = $template->default_data ?? []; + + // Merge: section data overrides template defaults + $mergedData = array_merge($templateDefaults, $sectionData); + + return TemplateService::generateDynamicFields( + $template, + 'section_data', + $mergedData + ); + }) ->visible(fn (Get $get): bool => filled($get('section_template_id'))) ->columnSpanFull(), ]) @@ -600,14 +654,43 @@ class PageForm ->options(FooterTemplate::where('is_active', true)->pluck('title', 'id')) ->searchable() ->live() - ->afterStateUpdated(fn (Set $set) => $set('footer_data', [])) + ->afterStateUpdated(function (Set $set, Get $get, $state, $record) { + $template = FooterTemplate::find($state); + if ($template && $template->default_data) { + // If page doesn't have footer_data or it's empty, load defaults + $currentData = $record ? ($record->footer_data ?? []) : ($get('footer_data') ?? []); + if (empty($currentData)) { + $set('footer_data', $template->default_data); + } + } else { + // If no template selected or no defaults, clear data + if (!$record || empty($record->footer_data)) { + $set('footer_data', []); + } + } + }) ->columnSpanFull(), Group::make() - ->schema(fn (Get $get): array => TemplateService::generateDynamicFields( - FooterTemplate::find($get('footer_template_id')), - 'footer_data' - )) + ->schema(function (Get $get, $record): array { + $template = FooterTemplate::find($get('footer_template_id')); + if (!$template) { + return []; + } + + // Get page's footer_data or template's default_data + $pageData = $record ? ($record->footer_data ?? []) : ($get('footer_data') ?? []); + $templateDefaults = $template->default_data ?? []; + + // Merge: page data overrides template defaults + $mergedData = array_merge($templateDefaults, $pageData); + + return TemplateService::generateDynamicFields( + $template, + 'footer_data', + $mergedData + ); + }) ->visible(fn (Get $get): bool => filled($get('footer_template_id'))) ->columnSpanFull(), ]) diff --git a/app/Filament/Admin/Resources/SectionTemplates/Schemas/SectionTemplateForm.php b/app/Filament/Admin/Resources/SectionTemplates/Schemas/SectionTemplateForm.php index 8baad7e..cb9df57 100644 --- a/app/Filament/Admin/Resources/SectionTemplates/Schemas/SectionTemplateForm.php +++ b/app/Filament/Admin/Resources/SectionTemplates/Schemas/SectionTemplateForm.php @@ -2,10 +2,14 @@ namespace App\Filament\Admin\Resources\SectionTemplates\Schemas; +use App\Models\SectionTemplate; +use App\Services\TemplateService; use Filament\Forms\Components\CodeEditor; use Filament\Forms\Components\TextInput; use Filament\Forms\Components\Toggle; +use Filament\Schemas\Components\Group; use Filament\Schemas\Components\Section; +use Filament\Schemas\Components\Utilities\Get; use Filament\Schemas\Components\View; use Filament\Schemas\Schema; use Filament\Forms\Components\CodeEditor\Enums\Language; @@ -50,6 +54,38 @@ class SectionTemplateForm ->inline(false), ]) ->columnSpanFull(), + + // Default Data Section - Dynamic fields based on placeholders + Section::make(__('section-templates.section_default_data')) + ->description(__('section-templates.section_default_data_desc')) + ->schema([ + Group::make() + ->schema(function (Get $get, $record): array { + $htmlContent = $get('html_content'); + if (empty($htmlContent)) { + return []; + } + + // Create a temporary template object to parse placeholders + $tempTemplate = new SectionTemplate(); + $tempTemplate->html_content = $htmlContent; + + // Get existing default_data if editing + $existingData = $record ? ($record->default_data ?? []) : []; + + return TemplateService::generateDynamicFields( + $tempTemplate, + 'default_data', + $existingData + ); + }) + ->key('default_data_fields') + ->visible(fn (Get $get): bool => filled($get('html_content'))) + ->columnSpanFull(), + ]) + ->columnSpanFull() + ->collapsible(true) + ->collapsed(false), ]); } } diff --git a/app/Http/Controllers/PageController.php b/app/Http/Controllers/PageController.php index d82eb8a..de6fbf0 100644 --- a/app/Http/Controllers/PageController.php +++ b/app/Http/Controllers/PageController.php @@ -67,18 +67,28 @@ class PageController extends Controller // Render Header Template $renderedHeader = null; if ($page->headerTemplate) { + // Merge template defaults with page data (page data overrides defaults) + $templateDefaults = $page->headerTemplate->default_data ?? []; + $pageData = $page->header_data ?? []; + $mergedHeaderData = array_merge($templateDefaults, $pageData); + $renderedHeader = TemplateService::replacePlaceholders( $page->headerTemplate->html_content, - $page->header_data ?? [] + $mergedHeaderData ); } // Render Footer Template $renderedFooter = null; if ($page->footerTemplate) { + // Merge template defaults with page data (page data overrides defaults) + $templateDefaults = $page->footerTemplate->default_data ?? []; + $pageData = $page->footer_data ?? []; + $mergedFooterData = array_merge($templateDefaults, $pageData); + $renderedFooter = TemplateService::replacePlaceholders( $page->footerTemplate->html_content, - $page->footer_data ?? [] + $mergedFooterData ); } @@ -137,18 +147,28 @@ public function show($slug) // Render Header Template $renderedHeader = null; if ($page->headerTemplate) { + // Merge template defaults with page data (page data overrides defaults) + $templateDefaults = $page->headerTemplate->default_data ?? []; + $pageData = $page->header_data ?? []; + $mergedHeaderData = array_merge($templateDefaults, $pageData); + $renderedHeader = TemplateService::replacePlaceholders( $page->headerTemplate->html_content, - $page->header_data ?? [] + $mergedHeaderData ); } // Render Footer Template $renderedFooter = null; if ($page->footerTemplate) { + // Merge template defaults with page data (page data overrides defaults) + $templateDefaults = $page->footerTemplate->default_data ?? []; + $pageData = $page->footer_data ?? []; + $mergedFooterData = array_merge($templateDefaults, $pageData); + $renderedFooter = TemplateService::replacePlaceholders( $page->footerTemplate->html_content, - $page->footer_data ?? [] + $mergedFooterData ); } diff --git a/app/Models/FooterTemplate.php b/app/Models/FooterTemplate.php index e52b8e1..f79037e 100644 --- a/app/Models/FooterTemplate.php +++ b/app/Models/FooterTemplate.php @@ -13,11 +13,13 @@ class FooterTemplate extends Model protected $fillable = [ 'title', 'html_content', + 'default_data', 'is_active', ]; protected $casts = [ 'is_active' => 'boolean', + 'default_data' => 'array', ]; /** diff --git a/app/Models/HeaderTemplate.php b/app/Models/HeaderTemplate.php index 6808b17..e63ec54 100644 --- a/app/Models/HeaderTemplate.php +++ b/app/Models/HeaderTemplate.php @@ -13,11 +13,13 @@ class HeaderTemplate extends Model protected $fillable = [ 'title', 'html_content', + 'default_data', 'is_active', ]; protected $casts = [ 'is_active' => 'boolean', + 'default_data' => 'array', ]; /** diff --git a/app/Models/SectionTemplate.php b/app/Models/SectionTemplate.php index bfbd172..f8d6bff 100644 --- a/app/Models/SectionTemplate.php +++ b/app/Models/SectionTemplate.php @@ -12,11 +12,13 @@ class SectionTemplate extends Model protected $fillable = [ 'title', 'html_content', + 'default_data', 'is_active', ]; protected $casts = [ 'is_active' => 'boolean', + 'default_data' => 'array', ]; /** diff --git a/app/Services/TemplateService.php b/app/Services/TemplateService.php index 38fae6b..b2b6c59 100644 --- a/app/Services/TemplateService.php +++ b/app/Services/TemplateService.php @@ -16,8 +16,10 @@ class TemplateService { /** * Generate dynamic form fields based on template placeholders + * For default data (template forms), use dataKey = 'default_data' + * For page data (page forms), use dataKey = 'header_data' or 'footer_data' */ - public static function generateDynamicFields($template, string $dataKey): array + public static function generateDynamicFields($template, string $dataKey, ?array $existingData = null): array { if (!$template) { return []; @@ -37,6 +39,9 @@ class TemplateService $label = str($name)->title()->replace('_', ' ')->toString(); + // Get existing value from existingData if provided + $existingValue = $existingData[$placeholder] ?? null; + $field = match($type) { // Text Input Variants 'text' => TextInput::make("{$dataKey}.{$placeholder}") @@ -212,6 +217,11 @@ class TemplateService ->helperText("Unknown type: {$type}"), }; + // Set default value if existingValue is provided + if ($existingValue !== null) { + $field->default($existingValue); + } + $fields[] = $field; } diff --git a/app/View/Components/PageRenderer.php b/app/View/Components/PageRenderer.php index 98a5192..c94b561 100644 --- a/app/View/Components/PageRenderer.php +++ b/app/View/Components/PageRenderer.php @@ -37,9 +37,14 @@ class PageRenderer extends Component return ''; } + // Merge template defaults with page data (page data overrides defaults) + $templateDefaults = $this->page->headerTemplate->default_data ?? []; + $pageData = $this->page->header_data ?? []; + $mergedData = array_merge($templateDefaults, $pageData); + return TemplateService::replacePlaceholders( $this->page->headerTemplate->html_content, - $this->page->header_data ?? [] + $mergedData ); } @@ -55,9 +60,14 @@ class PageRenderer extends Component continue; } + // Merge template defaults with section data (section data overrides defaults) + $templateDefaults = $section['template']->default_data ?? []; + $sectionData = $section['data'] ?? []; + $mergedData = array_merge($templateDefaults, $sectionData); + $output .= TemplateService::replacePlaceholders( $section['template']->html_content, - $section['data'] + $mergedData ); } @@ -73,9 +83,14 @@ class PageRenderer extends Component return ''; } + // Merge template defaults with page data (page data overrides defaults) + $templateDefaults = $this->page->footerTemplate->default_data ?? []; + $pageData = $this->page->footer_data ?? []; + $mergedData = array_merge($templateDefaults, $pageData); + return TemplateService::replacePlaceholders( $this->page->footerTemplate->html_content, - $this->page->footer_data ?? [] + $mergedData ); } } diff --git a/database/migrations/2025_11_01_045504_add_default_data_to_templates_table.php b/database/migrations/2025_11_01_045504_add_default_data_to_templates_table.php new file mode 100644 index 0000000..7560a42 --- /dev/null +++ b/database/migrations/2025_11_01_045504_add_default_data_to_templates_table.php @@ -0,0 +1,44 @@ +json('default_data')->nullable()->after('html_content'); + }); + + Schema::table('footer_templates', function (Blueprint $table) { + $table->json('default_data')->nullable()->after('html_content'); + }); + + Schema::table('section_templates', function (Blueprint $table) { + $table->json('default_data')->nullable()->after('html_content'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('header_templates', function (Blueprint $table) { + $table->dropColumn('default_data'); + }); + + Schema::table('footer_templates', function (Blueprint $table) { + $table->dropColumn('default_data'); + }); + + Schema::table('section_templates', function (Blueprint $table) { + $table->dropColumn('default_data'); + }); + } +}; diff --git a/lang/en/footer-templates.php b/lang/en/footer-templates.php index 032ee29..1ef0b78 100644 --- a/lang/en/footer-templates.php +++ b/lang/en/footer-templates.php @@ -9,6 +9,8 @@ return [ // Sections 'section_general' => 'General Information', + 'section_default_data' => 'Default Values', + 'section_default_data_desc' => 'You can enter default values for placeholders when creating templates. These values will be automatically loaded when the template is selected on pages.', // Form Fields 'field_title' => 'Title', diff --git a/lang/en/header-templates.php b/lang/en/header-templates.php index 9e7e0c0..451ffa1 100644 --- a/lang/en/header-templates.php +++ b/lang/en/header-templates.php @@ -9,6 +9,8 @@ return [ // Sections 'section_general' => 'General Information', + 'section_default_data' => 'Default Values', + 'section_default_data_desc' => 'You can enter default values for placeholders when creating templates. These values will be automatically loaded when the template is selected on pages.', // Form Fields 'field_title' => 'Title', diff --git a/lang/en/section-templates.php b/lang/en/section-templates.php index 53ce862..58b847d 100644 --- a/lang/en/section-templates.php +++ b/lang/en/section-templates.php @@ -9,6 +9,8 @@ return [ // Sections 'section_general' => 'General Information', + 'section_default_data' => 'Default Values', + 'section_default_data_desc' => 'You can enter default values for placeholders when creating templates. These values will be automatically loaded when the template is selected on pages.', // Form Fields 'field_title' => 'Title', diff --git a/lang/tr/footer-templates.php b/lang/tr/footer-templates.php index 179df00..1877a84 100644 --- a/lang/tr/footer-templates.php +++ b/lang/tr/footer-templates.php @@ -9,6 +9,8 @@ return [ // Sections 'section_general' => 'Genel Bilgiler', + 'section_default_data' => 'Varsayılan Değerler', + 'section_default_data_desc' => 'Template oluştururken placeholder\'lar için varsayılan değerleri girebilirsiniz. Bu değerler sayfalarda template seçildiğinde otomatik yüklenecektir.', // Form Fields 'field_title' => 'Başlık', diff --git a/lang/tr/header-templates.php b/lang/tr/header-templates.php index 72946f7..5634a31 100644 --- a/lang/tr/header-templates.php +++ b/lang/tr/header-templates.php @@ -9,6 +9,8 @@ return [ // Sections 'section_general' => 'Genel Bilgiler', + 'section_default_data' => 'Varsayılan Değerler', + 'section_default_data_desc' => 'Template oluştururken placeholder\'lar için varsayılan değerleri girebilirsiniz. Bu değerler sayfalarda template seçildiğinde otomatik yüklenecektir.', // Form Fields 'field_title' => 'Başlık', diff --git a/lang/tr/section-templates.php b/lang/tr/section-templates.php index bfcb656..acc6a24 100644 --- a/lang/tr/section-templates.php +++ b/lang/tr/section-templates.php @@ -9,6 +9,8 @@ return [ // Sections 'section_general' => 'Genel Bilgiler', + 'section_default_data' => 'Varsayılan Değerler', + 'section_default_data_desc' => 'Template oluştururken placeholder\'lar için varsayılan değerleri girebilirsiniz. Bu değerler sayfalarda template seçildiğinde otomatik yüklenecektir.', // Form Fields 'field_title' => 'Başlık',