Add Blocks Usage Documentation: Created a comprehensive guide for the block-based page creation system, detailing the available blocks, their usage, and customization options. Included example data and instructions for integrating blocks into the admin panel.

This commit is contained in:
Ümit Tunç
2025-10-30 15:28:54 -03:00
parent 3b238d23cc
commit 1791108b1e
4 changed files with 525 additions and 52 deletions
+424
View File
@@ -0,0 +1,424 @@
# Blok Tabanlı Sayfa Oluşturma Sistemi
## Genel Bakış
Sistemde 9 adet hazır blok bileşeni bulunmaktadır. Bu bloklar admin panelinden seçilip veri girilerek sayfa oluşturulabilir.
## Mevcut Bloklar
### 1. Hero (Ana Başlık)
**Kullanım:** Sayfa başlığı, açıklama, butonlar ve görsel
**Dosya:** `resources/views/components/blocks/hero.blade.php`
**Örnek Veri:**
```json
{
"type": "hero",
"data": {
"background_image": "assets/img/photos/blurry.png",
"badge": "YENİ SÜRÜM",
"title": "Modern ve Çok Amaçlı <span class='text-[#e31e24]'>Web Çözümleri</span>",
"subtitle": "Yenilikçi teknoloji çözümleri ile geleceği şekillendiriyoruz",
"primary_button_text": "Hemen Başla",
"primary_button_url": "#",
"secondary_button_text": "Daha Fazla Bilgi",
"secondary_button_url": "#about",
"hero_image": "assets/img/demos/f1.png"
}
}
```
---
### 2. Features (Özellikler Grid)
**Kullanım:** İkon + başlık + açıklama kartları
**Dosya:** `resources/views/components/blocks/features.blade.php`
**Örnek Veri:**
```json
{
"type": "features",
"data": {
"bg_class": "!bg-[#f0f0f8]",
"section_badge": "ÖZELLİKLER",
"section_title": "Neden Bizi Tercih Etmelisiniz?",
"section_subtitle": "Modern teknolojiler ve uzman ekibimizle projelerinizi hayata geçiriyoruz",
"column_class": "md:w-6/12 lg:w-4/12",
"features": [
{
"icon": "assets/img/icons/fast.svg",
"title": "Hızlı Çözümler",
"description": "Modern teknolojiler kullanarak projelerinizi hızlı ve verimli bir şekilde hayata geçiriyoruz."
},
{
"icon": "assets/img/icons/secure.svg",
"title": "Güvenli Altyapı",
"description": "En son güvenlik standartlarını kullanarak verilerinizi koruma altına alıyoruz."
},
{
"icon": "assets/img/icons/team.svg",
"title": "Uzman Ekip",
"description": "Deneyimli ve uzman ekibimiz ile her zaman yanınızdayız."
}
]
}
}
```
---
### 3. Stats (İstatistikler/Sayaçlar)
**Kullanım:** Sayı + açıklama istatistikleri
**Dosya:** `resources/views/components/blocks/stats.blade.php`
**Örnek Veri:**
```json
{
"type": "stats",
"data": {
"bg_class": "!bg-[#ffffff]",
"column_class": "md:w-6/12 lg:w-3/12",
"stats": [
{
"icon": "assets/img/icons/project.svg",
"number": "500+",
"label": "Tamamlanan Proje"
},
{
"icon": "assets/img/icons/client.svg",
"number": "300+",
"label": "Mutlu Müşteri"
},
{
"icon": "assets/img/icons/award.svg",
"number": "50+",
"label": "Kazanılan Ödül"
},
{
"icon": "assets/img/icons/team-stat.svg",
"number": "25+",
"label": "Ekip Üyesi"
}
]
}
}
```
---
### 4. Testimonials (Referanslar/Yorumlar)
**Kullanım:** Müşteri yorumları
**Dosya:** `resources/views/components/blocks/testimonials.blade.php`
**Örnek Veri:**
```json
{
"type": "testimonials",
"data": {
"bg_class": "!bg-[#f0f0f8]",
"section_badge": "REFERANSLAR",
"section_title": "Müşterilerimiz Ne Diyor?",
"section_subtitle": "Binlerce mutlu müşterimizden bazılarının görüşleri",
"column_class": "md:w-6/12 lg:w-4/12",
"testimonials": [
{
"rating": 5,
"quote": "Harika bir ekip! Projemizi zamanında ve beklentilerimizin üzerinde teslim ettiler.",
"avatar": "assets/img/avatars/te1.jpg",
"name": "Ahmet Yılmaz",
"position": "CEO, ABC Tech"
},
{
"rating": 5,
"quote": "Profesyonel yaklaşım ve mükemmel sonuç. Kesinlikle tavsiye ediyorum!",
"avatar": "assets/img/avatars/te2.jpg",
"name": "Ayşe Demir",
"position": "Kurucu, XYZ Startup"
}
]
}
}
```
---
### 5. Pricing (Fiyatlandırma Tabloları)
**Kullanım:** Fiyat paketleri
**Dosya:** `resources/views/components/blocks/pricing.blade.php`
**Örnek Veri:**
```json
{
"type": "pricing",
"data": {
"bg_class": "!bg-[#ffffff]",
"section_badge": "FİYATLANDIRMA",
"section_title": "Size Uygun Paketi Seçin",
"section_subtitle": "Tüm paketler 30 gün para iade garantisi ile geliyor",
"column_class": "md:w-6/12 lg:w-4/12",
"featured_label": "Önerilen",
"plans": [
{
"name": "Başlangıç",
"currency": "₺",
"price": "999",
"period": "ay",
"features": [
"5 Sayfa",
"Mobil Uyumlu",
"SEO Optimizasyonu",
"7/24 Destek"
],
"button_text": "Başla",
"button_url": "#"
},
{
"name": "Profesyonel",
"currency": "₺",
"price": "2499",
"period": "ay",
"featured": true,
"features": [
"15 Sayfa",
"Mobil Uyumlu",
"SEO Optimizasyonu",
"Yönetim Paneli",
"7/24 Öncelikli Destek"
],
"button_text": "Başla",
"button_url": "#"
},
{
"name": "Kurumsal",
"currency": "₺",
"price": "4999",
"period": "ay",
"features": [
"Sınırsız Sayfa",
"Mobil Uyumlu",
"SEO Optimizasyonu",
"Gelişmiş Yönetim Paneli",
"Özel Entegrasyonlar",
"7/24 Öncelikli Destek"
],
"button_text": "Başla",
"button_url": "#"
}
]
}
}
```
---
### 6. CTA (Call To Action)
**Kullanım:** Dikkat çekici aksiyon çağrısı
**Dosya:** `resources/views/components/blocks/cta.blade.php`
**Örnek Veri:**
```json
{
"type": "cta",
"data": {
"bg_class": "overflow-hidden",
"background_image": "assets/img/photos/blurry.png",
"icon": "assets/img/demos/icon-grape.png",
"title": "Benzersiz düşünün ve <span class='text-[#e31e24]'>fark yaratın</span>",
"subtitle": "Binlerce müşterimiz tarafından güveniliyoruz. Siz de katılın ve kısa sürede çarpıcı web sitenizi kolayca oluşturun.",
"button_text": "Hemen Başla",
"button_url": "#",
"button_icon": "uil uil-arrow-up-right",
"cta_image": "assets/img/demos/f1.png"
}
}
```
---
### 7. Gallery (Galeri/Portfolio)
**Kullanım:** Proje galerisi veya portfolio
**Dosya:** `resources/views/components/blocks/gallery.blade.php`
**Örnek Veri:**
```json
{
"type": "gallery",
"data": {
"bg_class": "!bg-[#f0f0f8]",
"section_badge": "PORTFOLİO",
"section_title": "Projelerimiz",
"section_subtitle": "Son dönemde tamamladığımız bazı projeler",
"column_class": "md:w-6/12 lg:w-4/12",
"items": [
{
"image": "assets/img/portfolio/p1.jpg",
"category": "Web Tasarım",
"title": "E-Ticaret Sitesi",
"description": "Modern ve kullanıcı dostu e-ticaret platformu",
"link": "#"
},
{
"image": "assets/img/portfolio/p2.jpg",
"category": "Mobil Uygulama",
"title": "Fitness Uygulaması",
"description": "iOS ve Android için fitness takip uygulaması",
"link": "#"
}
]
}
}
```
---
### 8. Team (Ekip Üyeleri)
**Kullanım:** Ekip üyeleri tanıtımı
**Dosya:** `resources/views/components/blocks/team.blade.php`
**Örnek Veri:**
```json
{
"type": "team",
"data": {
"bg_class": "!bg-[#ffffff]",
"section_badge": "EKİBİMİZ",
"section_title": "Arkasındaki İnsanlar",
"section_subtitle": "Profesyonel ve deneyimli ekibimizle tanışın",
"column_class": "md:w-6/12 lg:w-3/12",
"members": [
{
"photo": "assets/img/team/t1.jpg",
"name": "Mehmet Kaya",
"position": "CEO & Kurucu",
"bio": "15 yıllık teknoloji tecrübesi",
"social": {
"twitter": "https://twitter.com/",
"linkedin": "https://linkedin.com/",
"facebook": "https://facebook.com/"
}
},
{
"photo": "assets/img/team/t2.jpg",
"name": "Zeynep Şahin",
"position": "CTO",
"bio": "Yazılım mimarisi uzmanı",
"social": {
"twitter": "https://twitter.com/",
"linkedin": "https://linkedin.com/"
}
}
]
}
}
```
---
### 9. Contact (İletişim Formu)
**Kullanım:** İletişim formu ve bilgileri
**Dosya:** `resources/views/components/blocks/contact.blade.php`
**Örnek Veri:**
```json
{
"type": "contact",
"data": {
"bg_class": "!bg-[#f0f0f8]",
"section_badge": "İLETİŞİM",
"title": "Bizimle İletişime Geçin",
"subtitle": "Projeleriniz hakkında konuşmak için bize ulaşın",
"info": {
"address": "Merkez Mah. Teknoloji Cad. No:123 İstanbul",
"phone": "+90 (212) 555 1234",
"email": "info@truncgil.com"
},
"form_action": "/contact/submit",
"button_text": "Gönder"
}
}
```
---
## Admin Panelinde Kullanım
### Page Modelinde `sections` Alanı
Admin panelinde sayfa oluştururken `sections` JSON alanına yukarıdaki blokları ekleyebilirsiniz:
```json
[
{
"type": "hero",
"data": { ... }
},
{
"type": "features",
"data": { ... }
},
{
"type": "pricing",
"data": { ... }
},
{
"type": "cta",
"data": { ... }
}
]
```
### Filament Resource'da Repeater Kullanımı
```php
Repeater::make('sections')
->schema([
Select::make('type')
->options([
'hero' => 'Hero (Ana Başlık)',
'features' => 'Features (Özellikler)',
'stats' => 'Stats (İstatistikler)',
'testimonials' => 'Testimonials (Yorumlar)',
'pricing' => 'Pricing (Fiyatlandırma)',
'cta' => 'CTA (Aksiyon Çağrısı)',
'gallery' => 'Gallery (Galeri)',
'team' => 'Team (Ekip)',
'contact' => 'Contact (İletişim)',
])
->required()
->reactive(),
KeyValue::make('data')
->label('Blok Verileri')
->addActionLabel('Alan Ekle')
])
->collapsible()
->itemLabel(fn (array $state): ?string => $state['type'] ?? null)
```
---
## Özelleştirme
Her blok için `bg_class`, `column_class` gibi parametrelerle Tailwind CSS sınıfları özelleştirilebilir.
**Örnek:**
```json
{
"bg_class": "!bg-gradient-to-br from-[#e31e24] to-[#ff6b6b]",
"column_class": "md:w-4/12 lg:w-3/12"
}
```
---
## Yeni Blok Ekleme
1. `resources/views/components/blocks/` altında yeni blade dosyası oluşturun
2. `@props(['data' => []])` ile veri alın
3. HTML yapısını oluşturun
4. Admin repeater select'ine yeni tipi ekleyin
---
**Son Güncelleme:** {{ now()->format('d.m.Y') }}
@@ -0,0 +1,81 @@
@props(['data' => []])
<section class="wrapper {{ $data['bg_class'] ?? '!bg-[#f0f0f8]' }}">
<div class="container py-24 xl:py-32 lg:py-32 md:py-32">
<div class="flex flex-wrap mx-[-15px]">
<div class="lg:w-6/12 w-full flex-[0_0_auto] !px-[15px] max-w-full">
@if(!empty($data['section_badge']))
<h2 class="text-[0.8rem] tracking-[0.02rem] uppercase text-[#e31e24] !mb-3 !leading-[1.35]">{{ $data['section_badge'] }}</h2>
@endif
@if(!empty($data['title']))
<h2 class="!text-[calc(1.345rem_+_1.14vw)] font-bold !leading-[1.2] xl:!text-[2.2rem] !mb-3">
{{ $data['title'] }}
</h2>
@endif
@if(!empty($data['subtitle']))
<p class="lead !leading-[1.6] !mb-8">{{ $data['subtitle'] }}</p>
@endif
@if(!empty($data['info']))
<div class="!mb-8">
@if(!empty($data['info']['address']))
<div class="flex items-start !mb-4">
<i class="uil uil-location-point text-[#e31e24] text-[1.3rem] !mr-3"></i>
<p class="!mb-0">{{ $data['info']['address'] }}</p>
</div>
@endif
@if(!empty($data['info']['phone']))
<div class="flex items-start !mb-4">
<i class="uil uil-phone-volume text-[#e31e24] text-[1.3rem] !mr-3"></i>
<p class="!mb-0"><a href="tel:{{ $data['info']['phone'] }}" class="hover:text-[#e31e24]">{{ $data['info']['phone'] }}</a></p>
</div>
@endif
@if(!empty($data['info']['email']))
<div class="flex items-start !mb-4">
<i class="uil uil-envelope text-[#e31e24] text-[1.3rem] !mr-3"></i>
<p class="!mb-0"><a href="mailto:{{ $data['info']['email'] }}" class="hover:text-[#e31e24]">{{ $data['info']['email'] }}</a></p>
</div>
@endif
</div>
@endif
</div>
<div class="lg:w-6/12 w-full flex-[0_0_auto] !px-[15px] max-w-full">
<form method="POST" action="{{ $data['form_action'] ?? route('contact.submit') }}" class="contact-form">
@csrf
<div class="messages"></div>
<div class="flex flex-wrap mx-[-10px]">
<div class="xl:w-6/12 lg:w-6/12 md:w-6/12 w-full flex-[0_0_auto] !px-[10px] max-w-full">
<div class="form-floating !mb-4">
<input id="form_name" type="text" name="name" class="form-control !relative block w-full text-[.75rem] font-medium text-[#60697b] bg-[#fefefe] bg-clip-padding border shadow-[0_0_1.25rem_rgba(30,34,40,0.04)] rounded-[0.4rem] border-solid border-[rgba(8,60,130,0.07)] transition-[border-color] duration-[0.15s] ease-in-out focus:text-[#60697b] focus:bg-[rgba(255,255,255,.03)] focus:shadow-[0_0_1.25rem_rgba(30,34,40,0.04),unset] focus:!border-[rgba(63,120,224,0.5)] focus-visible:!border-[rgba(63,120,224,0.5)] focus-visible:!outline-0 placeholder:text-[#959ca9] placeholder:opacity-100 m-0 !pr-9 p-[.6rem_1rem] h-[calc(2.5rem_+_2px)] min-h-[calc(2.5rem_+_2px)] leading-[1.25]" placeholder="Jane" required>
<label for="form_name" class="text-[#959ca9] !mb-0 !ml-[0.05rem] inline-block !text-[.75rem] absolute z-[2] h-full overflow-hidden text-start text-ellipsis whitespace-nowrap pointer-events-none border origin-[0_0] px-4 py-[0.6rem] border-solid border-transparent left-0 top-0 font-Manrope">Ad Soyad *</label>
</div>
</div>
<div class="xl:w-6/12 lg:w-6/12 md:w-6/12 w-full flex-[0_0_auto] !px-[10px] max-w-full">
<div class="form-floating !mb-4">
<input id="form_email" type="email" name="email" class="form-control !relative block w-full text-[.75rem] font-medium text-[#60697b] bg-[#fefefe] bg-clip-padding border shadow-[0_0_1.25rem_rgba(30,34,40,0.04)] rounded-[0.4rem] border-solid border-[rgba(8,60,130,0.07)] transition-[border-color] duration-[0.15s] ease-in-out focus:text-[#60697b] focus:bg-[rgba(255,255,255,.03)] focus:shadow-[0_0_1.25rem_rgba(30,34,40,0.04),unset] focus:!border-[rgba(63,120,224,0.5)] focus-visible:!border-[rgba(63,120,224,0.5)] focus-visible:!outline-0 placeholder:text-[#959ca9] placeholder:opacity-100 m-0 !pr-9 p-[.6rem_1rem] h-[calc(2.5rem_+_2px)] min-h-[calc(2.5rem_+_2px)] leading-[1.25]" placeholder="jane.doe@example.com" required>
<label for="form_email" class="text-[#959ca9] !mb-0 !ml-[0.05rem] inline-block !text-[.75rem] absolute z-[2] h-full overflow-hidden text-start text-ellipsis whitespace-nowrap pointer-events-none border origin-[0_0] px-4 py-[0.6rem] border-solid border-transparent left-0 top-0 font-Manrope">E-posta *</label>
</div>
</div>
<div class="w-full flex-[0_0_auto] !px-[10px] max-w-full">
<div class="form-floating !mb-4">
<textarea id="form_message" name="message" class="form-control !relative block w-full text-[.75rem] font-medium text-[#60697b] bg-[#fefefe] bg-clip-padding border shadow-[0_0_1.25rem_rgba(30,34,40,0.04)] rounded-[0.4rem] border-solid border-[rgba(8,60,130,0.07)] transition-[border-color] duration-[0.15s] ease-in-out focus:text-[#60697b] focus:bg-[rgba(255,255,255,.03)] focus:shadow-[0_0_1.25rem_rgba(30,34,40,0.04),unset] focus:!border-[rgba(63,120,224,0.5)] focus-visible:!border-[rgba(63,120,224,0.5)] focus-visible:!outline-0 placeholder:text-[#959ca9] placeholder:opacity-100 m-0 !pr-9 !h-[calc(2.5rem_+_2px)] !min-h-[calc(8rem_+_2px)] leading-[1.25] p-[.6rem_1rem] resize-none" placeholder="Your message" required></textarea>
<label for="form_message" class="text-[#959ca9] mb-0 ml-[0.05rem] inline-block text-[.75rem] absolute z-[2] h-full overflow-hidden text-start text-ellipsis whitespace-nowrap pointer-events-none border origin-[0_0] px-4 py-[0.6rem] border-solid border-transparent left-0 top-0 font-Manrope">Mesajınız *</label>
</div>
</div>
<div class="w-full flex-[0_0_auto] !px-[10px] max-w-full">
<button type="submit" class="btn btn-grape !text-white !bg-[#e31e24] border-[#e31e24] hover:text-white hover:bg-[#e31e24] hover:border-[#e31e24] focus:shadow-[rgba(92,140,229,1)] active:text-white active:bg-[#e31e24] active:border-[#e31e24] disabled:text-white disabled:bg-[#e31e24] disabled:border-[#e31e24] !rounded-[0.8rem] btn-send !mb-3 hover:translate-y-[-0.15rem] hover:shadow-[0_0.25rem_0.75rem_rgba(30,34,40,0.15)]">
{{ $data['button_text'] ?? 'Gönder' }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</section>
+9 -3
View File
@@ -2,13 +2,19 @@
@section('content')
@php $blocks = $sections ?? []; @endphp
@if(is_array($blocks))
@if(is_array($blocks) && !empty($blocks))
@foreach($blocks as $block)
@php $type = $block['type'] ?? null; @endphp
@if($type && view()->exists("components.templates.$type"))
@include("components.templates.$type", ['data' => $block['props'] ?? $block])
@if($type && view()->exists("components.blocks.$type"))
<x-dynamic-component :component="'blocks.'.$type" :data="$block['data'] ?? $block" />
@endif
@endforeach
@else
{{-- Hiç blok yoksa placeholder göster --}}
<div class="container py-24 text-center">
<h2 class="!text-[2rem] !mb-4">Bu sayfa henüz içerik eklenmemiş</h2>
<p>Admin panelinden bu sayfaya bloklar ekleyebilirsiniz.</p>
</div>
@endif
@endsection
+9 -47
View File
@@ -2,57 +2,19 @@
@section('content')
@php
// Admin verisi varsa ve boş değilse dinamik render, yoksa statik HTML
$pageData = $page->data ?? [];
$hasDynamic = is_array($pageData) && count($pageData) > 0;
// sections varsa blok bazlı render, yoksa index.html fallback
$blocks = $sections ?? [];
$hasBlocks = is_array($blocks) && count($blocks) > 0;
@endphp
@if($hasDynamic)
{{-- Dinamik (admin) veri ile render --}}
<section class="hero">
<div class="container">
<h1>{{ data_get($page->data ?? [], 'hero_title') }}</h1>
<p>{{ data_get($page->data ?? [], 'hero_subtitle') }}</p>
@if($img = data_get($page->data ?? [], 'hero_image'))
<img src="{{ asset($img) }}" alt="hero">
@if($hasBlocks)
{{-- Blok bazlı dinamik render --}}
@foreach($blocks as $block)
@php $type = $block['type'] ?? null; @endphp
@if($type && view()->exists("components.blocks.$type"))
<x-dynamic-component :component="'blocks.'.$type" :data="$block['data'] ?? $block" />
@endif
<div class="hero-actions">
@if($btn = data_get($page->data ?? [], 'hero_primary_url'))
<a href="{{ $btn }}" class="btn">{{ data_get($page->data ?? [], 'hero_primary_text') }}</a>
@endif
@if($btn2 = data_get($page->data ?? [], 'hero_secondary_url'))
<a href="{{ $btn2 }}" class="btn btn-outline">{{ data_get($page->data ?? [], 'hero_secondary_text') }}</a>
@endif
</div>
</div>
</section>
@if($features = data_get($page->data ?? [], 'features', []))
<section class="features">
<div class="container">
<div class="grid">
@foreach($features as $item)
<div class="feature-item">
@if(!empty($item['icon'])) <img src="{{ asset($item['icon']) }}" alt=""> @endif
<h3>{{ $item['title'] ?? '' }}</h3>
<p>{{ $item['text'] ?? '' }}</p>
</div>
@endforeach
</div>
</div>
</section>
@endif
@if(data_get($page->data ?? [], 'cta_title'))
<section class="cta">
<div class="container">
<h2>{{ data_get($page->data ?? [], 'cta_title') }}</h2>
<a class="btn" href="{{ data_get($page->data ?? [], 'cta_button_url', '#') }}">
{{ data_get($page->data ?? [], 'cta_button_text') }}
</a>
</div>
</section>
@endif
@else
{{-- Statik fallback: public/html/index.html içeriğinin <body> bölümü --}}
@php