From 11cb89c94499ed0811838b2c61b3c8982626b627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Cmit=20Tun=C3=A7?= Date: Fri, 22 May 2026 07:36:24 +0300 Subject: [PATCH] feat: implement page template hero functionality with dynamic image handling and localization support for enhanced visual presentation --- .../Pages/Schemas/Sections/ContentSection.php | 1 - .../Schemas/Sections/PageSettingsSection.php | 38 +++--- app/Http/Controllers/PageController.php | 6 +- app/Models/Page.php | 14 +- app/Support/PageTemplateHero.php | 121 ++++++++++++++++++ config/page_templates.php | 26 ++++ database/seeders/AppDevelopmentPageSeeder.php | 1 + lang/en/pages.php | 3 + lang/tr/pages.php | 3 + .../front/page-hero-image.blade.php | 33 +++++ .../services/app-development.blade.php | 20 ++- 11 files changed, 232 insertions(+), 34 deletions(-) create mode 100644 app/Support/PageTemplateHero.php create mode 100644 config/page_templates.php create mode 100644 resources/views/components/front/page-hero-image.blade.php diff --git a/app/Filament/Admin/Resources/Pages/Schemas/Sections/ContentSection.php b/app/Filament/Admin/Resources/Pages/Schemas/Sections/ContentSection.php index 81d0fd5..955a311 100644 --- a/app/Filament/Admin/Resources/Pages/Schemas/Sections/ContentSection.php +++ b/app/Filament/Admin/Resources/Pages/Schemas/Sections/ContentSection.php @@ -35,7 +35,6 @@ class ContentSection RichEditor::make('content') ->label(__('pages.content_field')) - ->required() ->fileAttachmentsDisk('public') ->fileAttachmentsDirectory('pages') ->fileAttachmentsVisibility('public') diff --git a/app/Filament/Admin/Resources/Pages/Schemas/Sections/PageSettingsSection.php b/app/Filament/Admin/Resources/Pages/Schemas/Sections/PageSettingsSection.php index 4c21b3a..bd73498 100644 --- a/app/Filament/Admin/Resources/Pages/Schemas/Sections/PageSettingsSection.php +++ b/app/Filament/Admin/Resources/Pages/Schemas/Sections/PageSettingsSection.php @@ -2,12 +2,14 @@ namespace App\Filament\Admin\Resources\Pages\Schemas\Sections; +use App\Support\PageTemplateHero; use Filament\Forms\Components\Checkbox; use Filament\Forms\Components\DateTimePicker; use Filament\Forms\Components\FileUpload; use Filament\Forms\Components\Select; use Filament\Forms\Components\TextInput; use Filament\Schemas\Components\Section; +use Filament\Schemas\Components\Utilities\Get; class PageSettingsSection { @@ -15,6 +17,13 @@ class PageSettingsSection { return Section::make(__('pages.form_section_page_settings')) ->schema([ + Select::make('template') + ->label(__('pages.template_field')) + ->options(PageTemplateHero::templateFormOptions()) + ->default('default') + ->live() + ->columnSpanFull(), + FileUpload::make('featured_image') ->label(__('pages.featured_image_field')) ->image() @@ -27,7 +36,13 @@ class PageSettingsSection '4:3', '1:1', ]) - ->helperText(__('pages.featured_image_helper_text')) + ->helperText(function (Get $get): string { + if (PageTemplateHero::hasHero($get('template'))) { + return __('pages.featured_image_template_hero_helper'); + } + + return __('pages.featured_image_helper_text'); + }) ->columnSpanFull(), Select::make('author_id') @@ -61,25 +76,8 @@ class PageSettingsSection ->preload() ->helperText(__('pages.parent_helper_text')) ->columnSpanFull(), - - Select::make('template') - ->label(__('pages.template_field')) - ->options([ - 'default' => __('pages.template_default'), - 'landing' => __('pages.template_landing'), - 'blog' => __('pages.template_blog'), - 'contact' => __('pages.template_contact'), - 'home' => 'Home', - 'corporate.testimonials' => 'Müşteri Görüşleri (Kurumsal)', - 'corporate.logos' => 'Logolarımız (Kurumsal)', - 'corporate.partners' => 'Çözüm Ortaklarımız (Kurumsal)', - 'corporate.bank-accounts' => 'Banka Bilgilerimiz (Kurumsal)', - 'corporate.online-payment' => 'Online Ödeme (Kurumsal)', - ]) - ->default('default') - ->columnSpanFull(), - - TextInput::make('sort_order') + + TextInput::make('sort_order') ->label(__('pages.sort_order_field')) ->numeric() ->default(0) diff --git a/app/Http/Controllers/PageController.php b/app/Http/Controllers/PageController.php index 7cb7374..41ef0f1 100644 --- a/app/Http/Controllers/PageController.php +++ b/app/Http/Controllers/PageController.php @@ -8,6 +8,7 @@ use App\Models\HeaderTemplate; use App\Models\FooterTemplate; use App\Services\TemplateService; use App\Support\PageStructuredData; +use App\Support\PageTemplateHero; use Illuminate\Http\Request; use Illuminate\Support\Collection; @@ -145,6 +146,7 @@ class PageController extends Controller return view($view, [ 'page' => $page, + 'pageHero' => $page ? PageTemplateHero::resolveForPage($page) : null, 'settings' => $settings, 'sections' => $page ? ($page->parsed_sections ?? $page->sections ?? $page->data ?? []) : [], 'templatedSections' => $page ? ($page->templated_sections ?? collect([])) : collect([]), @@ -192,8 +194,8 @@ class PageController extends Controller ? ($page->translate('meta_description') ?: ($page->excerpt ?? null)) : ($page->meta_description ?? $page->excerpt ?? null); - if ($page->featured_image) { - $metaImage = asset('storage/' . $page->featured_image); + if ($page->featured_image_url) { + $metaImage = $page->featured_image_url; } } diff --git a/app/Models/Page.php b/app/Models/Page.php index badd38b..fd0f7bf 100644 --- a/app/Models/Page.php +++ b/app/Models/Page.php @@ -3,6 +3,7 @@ namespace App\Models; use App\Models\SectionTemplate; +use App\Support\PageTemplateHero; use App\Traits\HasTranslations; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -115,13 +116,14 @@ class Page extends Model return '/' . $this->slug; } - public function getFeaturedImageUrlAttribute() + public function getFeaturedImageUrlAttribute(): ?string { - if ($this->featured_image) { - return asset('storage/' . $this->featured_image); - } - - return null; + return PageTemplateHero::urlForPage($this); + } + + public function usesTemplateHeroImage(): bool + { + return ! $this->featured_image && PageTemplateHero::hasHero($this->template); } /** diff --git a/app/Support/PageTemplateHero.php b/app/Support/PageTemplateHero.php new file mode 100644 index 0000000..62f123c --- /dev/null +++ b/app/Support/PageTemplateHero.php @@ -0,0 +1,121 @@ +featured_image; + + if (blank($value)) { + return null; + } + + if (is_array($value)) { + $value = $value[0] ?? null; + } + + return is_string($value) && $value !== '' ? $value : null; + } + + public static function urlForPage(Page $page): ?string + { + if ($path = static::featuredStoragePath($page)) { + return asset('storage/' . $path); + } + + return static::url($page->template); + } + + /** + * Hero alanı için görsel verisi (yüklenen öne çıkan görsel veya şablon varsayılanı). + * + * @return array{image: string, webp?: string, width?: int, height?: int, from_upload: bool}|null + */ + public static function resolveForPage(?Page $page, ?string $templateFallback = null): ?array + { + if ($page && ($path = static::featuredStoragePath($page))) { + return [ + 'image' => asset('storage/' . $path), + 'from_upload' => true, + ]; + } + + $template = $page?->template ?? $templateFallback; + $config = static::config($template); + + if (! $config) { + return null; + } + + return array_merge($config, ['from_upload' => false]); + } + + /** + * @return array + */ + public static function templateFormOptions(): array + { + $base = [ + 'default' => __('pages.template_default'), + 'landing' => __('pages.template_landing'), + 'blog' => __('pages.template_blog'), + 'contact' => __('pages.template_contact'), + 'home' => 'Home', + 'corporate.testimonials' => 'Müşteri Görüşleri (Kurumsal)', + 'corporate.logos' => 'Logolarımız (Kurumsal)', + 'corporate.partners' => 'Çözüm Ortaklarımız (Kurumsal)', + 'corporate.bank-accounts' => 'Banka Bilgilerimiz (Kurumsal)', + 'corporate.online-payment' => 'Online Ödeme (Kurumsal)', + ]; + + $fromConfig = collect(config('page_templates.labels', [])) + ->mapWithKeys(fn (string $label, string $key) => [$key => $label]) + ->all(); + + return array_merge($base, $fromConfig); + } +} diff --git a/config/page_templates.php b/config/page_templates.php new file mode 100644 index 0000000..683bb90 --- /dev/null +++ b/config/page_templates.php @@ -0,0 +1,26 @@ + [ + 'services.app-development' => [ + 'image' => 'assets/img/photos/truncgil-mobile-app.png', + 'webp' => 'assets/img/photos/truncgil-mobile-app.webp', + 'width' => 735, + 'height' => 735, + ], + 'neler-yapariz' => [ + 'image' => 'assets/img/illustrations/3d11.png', + 'width' => 800, + 'height' => 1080, + ], + ], + + 'labels' => [ + 'services.app-development' => 'Uygulama Geliştirme', + 'neler-yapariz' => 'Neler Yaparız', + ], +]; diff --git a/database/seeders/AppDevelopmentPageSeeder.php b/database/seeders/AppDevelopmentPageSeeder.php index bdea699..a89540d 100644 --- a/database/seeders/AppDevelopmentPageSeeder.php +++ b/database/seeders/AppDevelopmentPageSeeder.php @@ -117,6 +117,7 @@ class AppDevelopmentPageSeeder extends Seeder 'excerpt' => 'Trunçgil Teknoloji olarak Android ve iOS uygulamalarını Google Play ve App Store\'da yayınlanmaya hazır şekilde titizlikle geliştiriyoruz.', 'content' => '', 'template' => 'services.app-development', + 'featured_image' => null, 'custom_header_blade' => 'partials.header-app-development', 'status' => 'published', 'is_homepage' => false, diff --git a/lang/en/pages.php b/lang/en/pages.php index 682f926..f760f04 100644 --- a/lang/en/pages.php +++ b/lang/en/pages.php @@ -58,6 +58,9 @@ return [ 'slug_helper_text' => 'The part that will appear in the URL. Example: about-us', 'excerpt_helper_text' => 'Page excerpt (maximum 500 characters)', 'featured_image_helper_text' => 'Select a featured image for the page', + 'featured_image_template_hero_helper' => 'If left empty, the selected template\'s hero image is used automatically. Upload a custom image to override.', + 'template_hero_preview_label' => 'Template hero image', + 'template_hero_preview_helper' => 'This image is used as the page featured image and in SEO/meta fields.', 'published_at_helper_text' => 'If left empty, current date will be used', 'parent_helper_text' => 'Select to make this page a sub-page of another page', 'sort_order_helper_text' => 'Display order in menu', diff --git a/lang/tr/pages.php b/lang/tr/pages.php index 80f5cc2..5e78b00 100644 --- a/lang/tr/pages.php +++ b/lang/tr/pages.php @@ -58,6 +58,9 @@ return [ 'slug_helper_text' => 'URL\'de görünecek kısım. Örnek: hakkimizda', 'excerpt_helper_text' => 'Sayfa özeti (maksimum 500 karakter)', 'featured_image_helper_text' => 'Sayfa için öne çıkan görsel seçin', + 'featured_image_template_hero_helper' => 'Boş bırakılırsa seçili şablonun hero görseli otomatik kullanılır. Özel görsel yükleyerek geçersiz kılabilirsiniz.', + 'template_hero_preview_label' => 'Şablon hero görseli', + 'template_hero_preview_helper' => 'Bu görsel sayfa öne çıkan görseli olarak ve SEO/meta alanlarında kullanılır.', 'published_at_helper_text' => 'Boş bırakılırsa şu anki tarih kullanılır', 'parent_helper_text' => 'Bu sayfayı başka bir sayfanın alt sayfası yapmak için seçin', 'sort_order_helper_text' => 'Menüde görünme sırası', diff --git a/resources/views/components/front/page-hero-image.blade.php b/resources/views/components/front/page-hero-image.blade.php new file mode 100644 index 0000000..1843c3d --- /dev/null +++ b/resources/views/components/front/page-hero-image.blade.php @@ -0,0 +1,33 @@ +@props([ + 'hero' => null, + 'alt' => '', +]) + +@if($hero) + @if(!empty($hero['from_upload'])) + merge(['class' => 'w-full max-w-full !h-auto']) }} + src="{{ $hero['image'] }}" + alt="{{ $alt }}" + @if(!empty($hero['width'])) width="{{ $hero['width'] }}" @endif + @if(!empty($hero['height'])) height="{{ $hero['height'] }}" @endif + fetchpriority="high" + decoding="async" + > + @else + + @if(!empty($hero['webp'])) + + @endif + merge(['class' => 'w-full max-w-full !h-auto']) }} + src="{{ asset($hero['image']) }}" + alt="{{ $alt }}" + width="{{ $hero['width'] ?? 735 }}" + height="{{ $hero['height'] ?? 735 }}" + fetchpriority="high" + decoding="async" + > + + @endif +@endif diff --git a/resources/views/templates/services/app-development.blade.php b/resources/views/templates/services/app-development.blade.php index f23679d..8a11585 100644 --- a/resources/views/templates/services/app-development.blade.php +++ b/resources/views/templates/services/app-development.blade.php @@ -1,7 +1,17 @@ @extends('layouts.site', ['header' => 'partials.header-app-development', 'footer' => 'partials.footer']) +@php + $pageHero = $pageHero ?? \App\Support\PageTemplateHero::resolveForPage($page ?? null, 'services.app-development'); +@endphp + @push('head') - +@if($pageHero) + @if(!empty($pageHero['from_upload'])) + + @elseif(!empty($pageHero['webp'])) + + @endif +@endif @endpush @push('styles') @@ -264,10 +274,10 @@
- - - {!! t('Trunçgil Teknoloji çoklu platform uygulama geliştirme') !!} - +