Add Page Section Builder functionality: Introduced a new section builder for dynamic page sections in the Filament Admin panel, allowing users to add various section types with flexible key-value data management. Enhanced the Page model with methods for parsed sections and section data retrieval. Updated localization files to support new section-related terms and added comprehensive documentation for usage and best practices.

This commit is contained in:
Ümit Tunç
2025-10-30 16:28:50 -03:00
parent 0615bbecb1
commit 22b39d94a9
7 changed files with 871 additions and 1 deletions
+115
View File
@@ -210,6 +210,121 @@ $table
**Ö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!
#### **Filament 4.x REPEATER KULLANIM KURALLARI (KRİTİK!):**
Repeater component'ı JSON array formatında tekrarlanan form component'leri oluşturmak için kullanılır.
**Temel Kullanım:**
```php
use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Select;
Repeater::make('members')
->schema([
TextInput::make('name')->required(),
Select::make('role')
->options([
'member' => 'Member',
'administrator' => 'Administrator',
])
->required(),
])
->defaultItems(0)
->addActionLabel('Add Member')
->reorderable()
->collapsible()
->itemLabel(fn (array $state): ?string => $state['name'] ?? null)
```
**Simple Repeater (Tek Field için):**
```php
// ✅ DOĞRU - Filament 4.x'te simple() metodu parametre alır
Repeater::make('tags')
->simple(
TextInput::make('tag')
->label('Tag')
->required()
)
// ❌ YANLIŞ - Bu kullanım HATA VERİR
Repeater::make('tags')
->schema([
TextInput::make('tag')->required(),
])
->simple() // ❌ Parametre gerekli!
```
**Nested Repeater ile Dikkat Edilmesi Gerekenler:**
1. **Aynı İsimli Field'lar Çakışır:**
```php
// ❌ YANLIŞ - Bu HATA VERİR (FileUpload array beklerken string alır)
Repeater::make('data')
->schema([
TextInput::make('value'), // İlk value
FileUpload::make('value'), // İkinci value - ÇAKIŞMA!
Textarea::make('value'), // Üçüncü value - ÇAKIŞMA!
])
// ✅ DOĞRU - dehydrated() ile sadece görünür olan kaydedilir
Repeater::make('data')
->schema([
TextInput::make('value')
->visible(fn ($get) => $get('type') === 'text')
->dehydrated(fn ($get) => $get('type') === 'text'),
FileUpload::make('value')
->visible(fn ($get) => $get('type') === 'image')
->dehydrated(fn ($get) => $get('type') === 'image'),
Textarea::make('value')
->visible(fn ($get) => $get('type') === 'textarea')
->dehydrated(fn ($get) => $get('type') === 'textarea'),
])
```
2. **Conditional Field'lar:**
```php
// Sadece görünür olan field dehydrate edilir
Select::make('type')
->options([...])
->live(), // Değişikliği dinlemek için
TextInput::make('value')
->visible(fn (Get $get) => $get('type') === 'text')
->dehydrated(fn (Get $get) => $get('type') === 'text'),
```
3. **Item Labels:**
```php
// Item'lara dinamik label ekle
Repeater::make('sections')
->schema([...])
->itemLabel(fn (array $state): ?string =>
isset($state['type'])
? __('pages.section_type_' . $state['type'])
: 'Item'
)
```
4. **Repeater Özellikleri:**
```php
Repeater::make('items')
->schema([...])
->defaultItems(0) // Başlangıç item sayısı
->addActionLabel('Add') // Ekleme butonu label'ı
->reorderable() // Sürükle-bırak ile sıralama
->collapsible() // Açılıp kapanabilir
->collapsed() // Başlangıçta kapalı
->cloneable() // Kopyalama butonu
->deletable() // Silme butonu
->minItems(1) // Minimum item sayısı
->maxItems(10) // Maximum item sayısı
```
**Referans:** [Filament Repeater Docs](https://filamentphp.com/docs/4.x/forms/repeater)
### Dil Dosyası Yapısı
<?php
@@ -4,14 +4,20 @@ namespace App\Filament\Admin\Resources\Pages\Schemas;
use App\Filament\Admin\Resources\Components\TranslationTabs;
use Filament\Forms\Components\Checkbox;
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\Utilities\Get;
use Filament\Schemas\Schema;
class PageForm
@@ -164,6 +170,229 @@ class PageForm
->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(fn ($component, $state, Get $get) => $component->state($get('value')))
->dehydrated(false)
->columnSpanFull(),
// Textarea (displays and updates 'value' field)
Textarea::make('_value_textarea')
->label(__('pages.section_data_value'))
->visible(fn (Get $get) => in_array($get('type'), ['textarea', 'json', 'html']))
->rows(fn (Get $get) => $get('type') === 'html' ? 6 : 4)
->placeholder(fn (Get $get) => $get('type') === 'html' ? '<div>HTML kodu girin...</div>' : 'Metin girin...')
->live(onBlur: true)
->afterStateUpdated(fn ($state, callable $set) => $set('value', $state))
->afterStateHydrated(fn ($component, $state, Get $get) => $component->state($get('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(fn ($component, $state, Get $get) => $component->state($get('value')))
->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(fn ($component, $state, Get $get) => $component->state($get('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(fn ($component, $state, Get $get) => $component->state($get('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(fn ($component, $state, Get $get) => $component->state($get('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(fn ($component, $state, Get $get) => $component->state($get('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(fn ($component, $state, Get $get) => $component->state($get('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(fn ($component, $state, Get $get) => $component->state($get('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(fn ($component, $state, Get $get) => $component->state($get('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(fn ($component, $state, Get $get) => $component->state($get('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),
// Çeviri Sekmesi
Section::make('🌍 ' . __('pages.translations_section'))
->schema([
+2 -1
View File
@@ -92,7 +92,8 @@ class PageController extends Controller
? ($page->translate('meta_description') ?: ($page->excerpt ?? null))
: ($page->meta_description ?? $page->excerpt ?? null);
$sections = $page->sections ?? $page->data ?? [];
// Use parsed_sections for easier template usage (key-value format)
$sections = $page->parsed_sections ?? $page->sections ?? $page->data ?? [];
$template = ($slug === 'home' || ($page->is_homepage ?? false))
? 'home'
: ($page->template ?? 'generic');
+36
View File
@@ -88,4 +88,40 @@ class Page extends Model
return null;
}
/**
* Get sections with parsed data as associative array
*/
public function getParsedSectionsAttribute()
{
if (!$this->sections || !is_array($this->sections)) {
return [];
}
return collect($this->sections)->map(function ($section) {
if (!isset($section['data']) || !is_array($section['data'])) {
return $section;
}
// Convert key-value-type array to associative array
$parsedData = collect($section['data'])->pluck('value', 'key')->toArray();
return [
'type' => $section['type'] ?? null,
'data' => $parsedData,
];
})->toArray();
}
/**
* Get a specific section's data by type
*/
public function getSectionData(string $type, int $index = 0): ?array
{
$sections = $this->parsed_sections;
$matchingSections = array_filter($sections, fn($s) => ($s['type'] ?? null) === $type);
$matchingSections = array_values($matchingSections);
return $matchingSections[$index] ?? null;
}
}
+389
View File
@@ -0,0 +1,389 @@
# Page Section Builder - Kullanım Kılavuzu
## 📋 Genel Bakış
Page Section Builder, Filament Admin panelinden sayfalarınıza dinamik bölümler (sections) eklemenizi sağlayan güçlü bir modüldür. Settings modülünden esinlenerek geliştirilmiş olup, esnek bir key-value-type sistemi kullanır.
## ✨ Özellikler
- **Dinamik Section Tipleri**: Hero, Features, Stats, CTA, Gallery, Testimonials ve daha fazlası
- **Esnek Data Yönetimi**: Her section için sınırsız key-value çifti
- **Çoklu Value Tipleri**: Text, Image, HTML, Rich Text, Markdown, Color, Date, Array ve daha fazlası
- **Sürükle-Bırak Sıralama**: Sections ve data fields'ları yeniden sıralayabilme
- **Kolay Entegrasyon**: Mevcut sayfalarda sorunsuz çalışır
## 🎯 Section Tipleri
Aşağıdaki section tipleri mevcuttur:
| Tip | Açıklama | Kullanım Alanı |
|-----|----------|----------------|
| `hero` | Hero (Ana Banner) | Sayfa başlığı, büyük görsel banner |
| `features` | Özellikler | Ürün/hizmet özellikleri listesi |
| `stats` | İstatistikler | Sayısal veriler, başarı metrikleri |
| `cta` | Harekete Geçirme | Kullanıcıyı yönlendirme butonları |
| `content` | İçerik | Genel içerik blokları |
| `gallery` | Galeri | Görsel galeri |
| `testimonials` | Referanslar | Müşteri yorumları |
| `team` | Ekip | Ekip üyeleri |
| `pricing` | Fiyatlandırma | Fiyat tabloları |
| `faq` | SSS | Sık sorulan sorular |
| `contact` | İletişim | İletişim formu |
| `custom` | Özel | Özel içerik |
## 📝 Value Tipleri
Her data field'ı için aşağıdaki value tipleri kullanılabilir:
### Metin Tipleri
- **Text**: Kısa tek satırlık metin (başlık, URL, e-posta, telefon)
- **Textarea**: Uzun metin
- **HTML**: Ham HTML kodu
- **Markdown**: Markdown formatı
- **Rich Text**: WYSIWYG zengin metin editörü
### Medya Tipleri
- **Image**: Görsel yükleme (otomatik image editor ile)
- **File**: Dosya yükleme
- **Color**: Renk seçici
### Veri Tipleri
- **Number**: Sayısal değer
- **Boolean**: Evet/Hayır (Toggle)
- **Date**: Tarih seçici
- **DateTime**: Tarih & saat seçici
- **Array**: Dizi (repeater)
- **JSON**: JSON formatı
## 🔧 Admin Panelinde Kullanım
### 1. Yeni Section Ekleme
1. **Pages** modülünden bir sayfa açın veya yeni sayfa oluşturun
2. **Sayfa Bölümleri** (Page Sections) sekmesine gidin
3. **Yeni Bölüm Ekle** butonuna tıklayın
4. Section tipini seçin (örn: Hero, Features)
5. **Yeni Alan Ekle** ile section data'larını ekleyin
### 2. Data Field Ekleme
Her section için data field'ları eklerken:
1. **Key (Anahtar)**: Data'nın anahtarı (örn: `title`, `subtitle`, `image`)
2. **Type (Değer Tipi)**: Değerin tipini seçin (örn: Text, Image, HTML)
3. **Value (Değer)**: Seçilen tipe göre değeri girin
### 3. Örnek: Hero Section Oluşturma
```
Section Type: Hero (Ana Banner)
Data Fields:
┌─────────────────────────────────────────┐
│ Key: background_image │
│ Type: Image │
│ Value: [Upload Image] │
├─────────────────────────────────────────┤
│ Key: badge │
│ Type: Text │
│ Value: "YENİ PLATFORM" │
├─────────────────────────────────────────┤
│ Key: title │
│ Type: HTML │
│ Value: "Modern <span>Web</span>" │
├─────────────────────────────────────────┤
│ Key: subtitle │
│ Type: Textarea │
│ Value: "Açıklama metni..." │
├─────────────────────────────────────────┤
│ Key: button_text │
│ Type: Text │
│ Value: "Hemen Başla" │
├─────────────────────────────────────────┤
│ Key: button_url │
│ Type: URL │
│ Value: "#features" │
└─────────────────────────────────────────┘
```
## 💻 Kod Kullanımı
### Page Model'de Sections Kullanımı
```php
// Bir sayfayı al
$page = Page::find(1);
// Tüm parsed sections'ları al (key-value formatında)
$sections = $page->parsed_sections;
// Örnek çıktı:
[
[
'type' => 'hero',
'data' => [
'background_image' => 'path/to/image.jpg',
'badge' => 'YENİ PLATFORM',
'title' => 'Modern <span>Web</span>',
'subtitle' => 'Açıklama metni...',
'button_text' => 'Hemen Başla',
'button_url' => '#features',
]
],
[
'type' => 'features',
'data' => [...]
]
]
// Belirli bir section tipinin data'sını al
$heroData = $page->getSectionData('hero'); // İlk hero section
$secondHero = $page->getSectionData('hero', 1); // İkinci hero section
```
### Controller'da Kullanım
```php
public function show($slug)
{
$page = Page::where('slug', $slug)->firstOrFail();
// Parsed sections otomatik olarak view'e gönderilir
$sections = $page->parsed_sections;
return view('templates.home', compact('page', 'sections'));
}
```
### Blade Template'de Kullanım
```blade
@foreach($sections as $section)
@if($section['type'] === 'hero')
<x-blocks.hero :data="$section['data']" />
@elseif($section['type'] === 'features')
<x-blocks.features :data="$section['data']" />
@elseif($section['type'] === 'stats')
<x-blocks.stats :data="$section['data']" />
@endif
@endforeach
```
### Component'te Data Kullanımı
```blade
{{-- resources/views/components/blocks/hero.blade.php --}}
@props(['data'])
<section style="background-image: url({{ asset($data['background_image'] ?? '') }})">
<div class="container">
@if(isset($data['badge']))
<span class="badge">{{ $data['badge'] }}</span>
@endif
@if(isset($data['title']))
<h1>{!! $data['title'] !!}</h1>
@endif
@if(isset($data['subtitle']))
<p>{{ $data['subtitle'] }}</p>
@endif
@if(isset($data['button_text']) && isset($data['button_url']))
<a href="{{ $data['button_url'] }}" class="btn">
{{ $data['button_text'] }}
</a>
@endif
</div>
</section>
```
## 🎨 Best Practices
### 1. Key Naming (Anahtar İsimlendirme)
Tutarlı ve açıklayıcı key isimleri kullanın:
```
✅ İYİ:
- title
- subtitle
- background_image
- primary_button_text
- primary_button_url
❌ KÖTÜ:
- baslik
- resim1
- text
- btn
```
### 2. Value Type Seçimi
Doğru value tipini seçin:
```
✅ title → Text (kısa başlık)
✅ description → Textarea (uzun açıklama)
✅ content → Rich Text (formatlanmış içerik)
✅ banner → Image (görsel)
✅ is_active → Boolean (aktif/pasif)
```
### 3. Section Organization
Benzer section'ları gruplandırın:
```
1. Hero (Giriş)
2. Features (Özellikler)
3. Stats (İstatistikler)
4. CTA (Harekete Geçirme)
5. Contact (İletişim)
```
## 🔄 Mevcut Data Migrasyon
Eğer eski formatınız varsa (örn: PageSeeder'daki gibi), parse edilmiş format otomatik olarak çalışır:
```php
// ESKİ FORMAT (PageSeeder'dan)
'sections' => [
[
'type' => 'hero',
'data' => [
'title' => 'Başlık',
'subtitle' => 'Alt başlık'
]
]
]
// YENİ FORMAT (Admin'den girilince)
'sections' => [
[
'type' => 'hero',
'data' => [
['key' => 'title', 'type' => 'text', 'value' => 'Başlık'],
['key' => 'subtitle', 'type' => 'text', 'value' => 'Alt başlık']
]
]
]
// parsed_sections attribute her ikisini de doğru parse eder!
```
## 🚀 İleri Seviye Özellikler
### Nested Arrays (İç İçe Diziler)
`Array` value tipi ile iç içe veri yapıları oluşturabilirsiniz:
```
Section Type: Features
Data Field:
Key: features
Type: Array
Value: [
{ item: "Hızlı Çözümler" },
{ item: "Güvenli Altyapı" },
{ item: "Uzman Ekip" }
]
```
### JSON Data
Kompleks veri yapıları için JSON kullanabilirsiniz:
```
Key: configuration
Type: JSON
Value: {"layout": "grid", "columns": 3, "gap": 20}
```
### Conditional Rendering
```blade
@foreach($sections as $section)
@php
$data = $section['data'];
$bgClass = $data['bg_class'] ?? '';
@endphp
<section class="{{ $bgClass }}">
@includeIf("components.blocks.{$section['type']}", compact('data'))
</section>
@endforeach
```
## 📚 Dosya Yapısı
```
app/
├── Models/
│ └── Page.php (getParsedSectionsAttribute, getSectionData)
├── Http/Controllers/
│ └── PageController.php (parsed_sections kullanımı)
└── Filament/Admin/Resources/Pages/
└── Schemas/
└── PageForm.php (Section Builder UI)
lang/
├── tr/
│ └── pages.php (Türkçe çeviriler)
└── en/
└── pages.php (İngilizce çeviriler)
resources/views/
├── components/blocks/
│ ├── hero.blade.php
│ ├── features.blade.php
│ ├── stats.blade.php
│ └── cta.blade.php
└── templates/
└── home.blade.php (sections render)
```
## 🐛 Troubleshooting
### Section görünmüyor?
1. `$page->parsed_sections` döndüğünden emin olun
2. Section type'ının doğru yazıldığını kontrol edin
3. Blade component'in var olduğunu doğrulayın
### Data değerleri boş?
1. Admin'de data field'larının key değerlerini kontrol edin
2. Value tipinin doğru seçildiğini doğrulayın
3. Değerlerin kaydedildiğini kontrol edin
### Image görünmüyor?
1. `storage` link'inin oluşturulduğunu kontrol edin: `php artisan storage:link`
2. Image path'inin doğru olduğunu doğrulayın
3. Disk ayarlarını kontrol edin (`config/filesystems.php`)
## 🎓 Örnekler
Daha fazla örnek için:
- `database/seeders/PageSeeder.php` - Mevcut section örnekleri
- `resources/views/components/blocks/` - Component örnekleri
- `resources/views/templates/home.blade.php` - Template örneği
## 📞 Destek
Sorunlarınız için:
1. Bu dokümantasyonu kontrol edin
2. `docs/CURSOR_RULES.md` dosyasına bakın
3. Loglara göz atın: `storage/logs/laravel.log`
---
**Not:** Bu sistem Settings modülünden esinlenerek geliştirilmiş olup, aynı esneklik ve güçle çalışır. Her türlü dinamik sayfa içeriği için kullanılabilir.
+50
View File
@@ -24,6 +24,7 @@ return [
'form_section_content' => 'Content',
'form_section_page_settings' => 'Page Settings',
'form_section_seo_settings' => 'SEO Settings',
'form_section_sections' => 'Page Sections',
'translations_section' => 'Translations',
// Form fields
@@ -95,4 +96,53 @@ return [
'slug_required' => 'The URL slug field is required.',
'slug_unique' => 'This URL slug is already taken.',
'content_required' => 'The content field is required.',
// Section Builder
'sections_field' => 'Page Sections',
'sections_helper_text' => 'Add dynamic sections to your page',
'section_type' => 'Section Type',
'section_type_helper' => 'Select the type of section you want to add',
'section_data' => 'Section Data',
'section_data_helper' => 'Enter the required data for this section',
'section_add' => 'Add New Section',
// Section Types
'section_type_hero' => 'Hero (Main Banner)',
'section_type_features' => 'Features',
'section_type_stats' => 'Statistics',
'section_type_cta' => 'Call to Action',
'section_type_content' => 'Content',
'section_type_gallery' => 'Gallery',
'section_type_testimonials' => 'Testimonials',
'section_type_team' => 'Team',
'section_type_pricing' => 'Pricing',
'section_type_faq' => 'FAQ',
'section_type_contact' => 'Contact',
'section_type_custom' => 'Custom',
// Section Data Fields
'section_data_key' => 'Key',
'section_data_key_helper' => 'Data key (e.g: title, subtitle, image)',
'section_data_value_type' => 'Value Type',
'section_data_value' => 'Value',
'section_data_add_field' => 'Add New Field',
// Value Types
'value_type_text' => 'Text',
'value_type_textarea' => 'Long Text',
'value_type_html' => 'HTML',
'value_type_markdown' => 'Markdown',
'value_type_richtext' => 'Rich Text',
'value_type_image' => 'Image',
'value_type_file' => 'File',
'value_type_url' => 'URL',
'value_type_email' => 'Email',
'value_type_phone' => 'Phone',
'value_type_number' => 'Number',
'value_type_boolean' => 'Yes/No',
'value_type_color' => 'Color',
'value_type_date' => 'Date',
'value_type_datetime' => 'Date & Time',
'value_type_array' => 'Array',
'value_type_json' => 'JSON',
];
+50
View File
@@ -24,6 +24,7 @@ return [
'form_section_content' => 'İçerik',
'form_section_page_settings' => 'Sayfa Ayarları',
'form_section_seo_settings' => 'SEO Ayarları',
'form_section_sections' => 'Sayfa Bölümleri',
'translations_section' => 'Çeviriler',
// Form fields
@@ -95,4 +96,53 @@ return [
'slug_required' => 'URL yolu alanı zorunludur.',
'slug_unique' => 'Bu URL yolu zaten kullanılıyor.',
'content_required' => 'İçerik alanı zorunludur.',
// Section Builder
'sections_field' => 'Sayfa Bölümleri',
'sections_helper_text' => 'Sayfanız için dinamik bölümler ekleyin',
'section_type' => 'Bölüm Tipi',
'section_type_helper' => 'Eklemek istediğiniz bölüm tipini seçin',
'section_data' => 'Bölüm Verileri',
'section_data_helper' => 'Bu bölüm için gerekli verileri girin',
'section_add' => 'Yeni Bölüm Ekle',
// Section Types
'section_type_hero' => 'Hero (Ana Banner)',
'section_type_features' => 'Özellikler',
'section_type_stats' => 'İstatistikler',
'section_type_cta' => 'Harekete Geçirme',
'section_type_content' => 'İçerik',
'section_type_gallery' => 'Galeri',
'section_type_testimonials' => 'Referanslar',
'section_type_team' => 'Ekip',
'section_type_pricing' => 'Fiyatlandırma',
'section_type_faq' => 'SSS',
'section_type_contact' => 'İletişim',
'section_type_custom' => 'Özel',
// Section Data Fields
'section_data_key' => 'Anahtar',
'section_data_key_helper' => 'Veri anahtarı (örn: title, subtitle, image)',
'section_data_value_type' => 'Değer Tipi',
'section_data_value' => 'Değer',
'section_data_add_field' => 'Yeni Alan Ekle',
// Value Types
'value_type_text' => 'Metin',
'value_type_textarea' => 'Uzun Metin',
'value_type_html' => 'HTML',
'value_type_markdown' => 'Markdown',
'value_type_richtext' => 'Zengin Metin',
'value_type_image' => 'Görsel',
'value_type_file' => 'Dosya',
'value_type_url' => 'URL',
'value_type_email' => 'E-posta',
'value_type_phone' => 'Telefon',
'value_type_number' => 'Sayı',
'value_type_boolean' => 'Evet/Hayır',
'value_type_color' => 'Renk',
'value_type_date' => 'Tarih',
'value_type_datetime' => 'Tarih & Saat',
'value_type_array' => 'Dizi',
'value_type_json' => 'JSON',
];