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:
+115
@@ -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!
|
**Ö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ı
|
### Dil Dosyası Yapısı
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,20 @@ namespace App\Filament\Admin\Resources\Pages\Schemas;
|
|||||||
|
|
||||||
use App\Filament\Admin\Resources\Components\TranslationTabs;
|
use App\Filament\Admin\Resources\Components\TranslationTabs;
|
||||||
use Filament\Forms\Components\Checkbox;
|
use Filament\Forms\Components\Checkbox;
|
||||||
|
use Filament\Forms\Components\ColorPicker;
|
||||||
|
use Filament\Forms\Components\DatePicker;
|
||||||
use Filament\Forms\Components\DateTimePicker;
|
use Filament\Forms\Components\DateTimePicker;
|
||||||
use Filament\Forms\Components\FileUpload;
|
use Filament\Forms\Components\FileUpload;
|
||||||
use Filament\Forms\Components\Hidden;
|
use Filament\Forms\Components\Hidden;
|
||||||
|
use Filament\Forms\Components\MarkdownEditor;
|
||||||
|
use Filament\Forms\Components\Repeater;
|
||||||
use Filament\Forms\Components\RichEditor;
|
use Filament\Forms\Components\RichEditor;
|
||||||
use Filament\Schemas\Components\Section;
|
use Filament\Schemas\Components\Section;
|
||||||
use Filament\Forms\Components\Select;
|
use Filament\Forms\Components\Select;
|
||||||
use Filament\Forms\Components\Textarea;
|
use Filament\Forms\Components\Textarea;
|
||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Forms\Components\Toggle;
|
||||||
|
use Filament\Schemas\Components\Utilities\Get;
|
||||||
use Filament\Schemas\Schema;
|
use Filament\Schemas\Schema;
|
||||||
|
|
||||||
class PageForm
|
class PageForm
|
||||||
@@ -164,6 +170,229 @@ class PageForm
|
|||||||
->collapsible(true)
|
->collapsible(true)
|
||||||
->collapsed(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
|
// Çeviri Sekmesi
|
||||||
Section::make('🌍 ' . __('pages.translations_section'))
|
Section::make('🌍 ' . __('pages.translations_section'))
|
||||||
->schema([
|
->schema([
|
||||||
|
|||||||
@@ -92,7 +92,8 @@ class PageController extends Controller
|
|||||||
? ($page->translate('meta_description') ?: ($page->excerpt ?? null))
|
? ($page->translate('meta_description') ?: ($page->excerpt ?? null))
|
||||||
: ($page->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))
|
$template = ($slug === 'home' || ($page->is_homepage ?? false))
|
||||||
? 'home'
|
? 'home'
|
||||||
: ($page->template ?? 'generic');
|
: ($page->template ?? 'generic');
|
||||||
|
|||||||
@@ -88,4 +88,40 @@ class Page extends Model
|
|||||||
|
|
||||||
return null;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
@@ -24,6 +24,7 @@ return [
|
|||||||
'form_section_content' => 'Content',
|
'form_section_content' => 'Content',
|
||||||
'form_section_page_settings' => 'Page Settings',
|
'form_section_page_settings' => 'Page Settings',
|
||||||
'form_section_seo_settings' => 'SEO Settings',
|
'form_section_seo_settings' => 'SEO Settings',
|
||||||
|
'form_section_sections' => 'Page Sections',
|
||||||
'translations_section' => 'Translations',
|
'translations_section' => 'Translations',
|
||||||
|
|
||||||
// Form fields
|
// Form fields
|
||||||
@@ -95,4 +96,53 @@ return [
|
|||||||
'slug_required' => 'The URL slug field is required.',
|
'slug_required' => 'The URL slug field is required.',
|
||||||
'slug_unique' => 'This URL slug is already taken.',
|
'slug_unique' => 'This URL slug is already taken.',
|
||||||
'content_required' => 'The content field is required.',
|
'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',
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ return [
|
|||||||
'form_section_content' => 'İçerik',
|
'form_section_content' => 'İçerik',
|
||||||
'form_section_page_settings' => 'Sayfa Ayarları',
|
'form_section_page_settings' => 'Sayfa Ayarları',
|
||||||
'form_section_seo_settings' => 'SEO Ayarları',
|
'form_section_seo_settings' => 'SEO Ayarları',
|
||||||
|
'form_section_sections' => 'Sayfa Bölümleri',
|
||||||
'translations_section' => 'Çeviriler',
|
'translations_section' => 'Çeviriler',
|
||||||
|
|
||||||
// Form fields
|
// Form fields
|
||||||
@@ -95,4 +96,53 @@ return [
|
|||||||
'slug_required' => 'URL yolu alanı zorunludur.',
|
'slug_required' => 'URL yolu alanı zorunludur.',
|
||||||
'slug_unique' => 'Bu URL yolu zaten kullanılıyor.',
|
'slug_unique' => 'Bu URL yolu zaten kullanılıyor.',
|
||||||
'content_required' => 'İçerik alanı zorunludur.',
|
'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',
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user